Polish mobile app for App Store review and expand CRUD.
Default to beenvoice.soconnor.dev with server settings hidden behind Advanced; add Entities tab with clients/businesses, invoice creation, UI fixes for dashboard layout, date fields, FAB position, and card-matched button radius. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+40
-25
@@ -128,40 +128,50 @@ export default function DashboardScreen() {
|
||||
</View>
|
||||
|
||||
<View style={styles.statsGrid}>
|
||||
<StatCard label="Total revenue" value={formatCurrency(stats.totalRevenue)} />
|
||||
<StatCard label="Pending" value={formatCurrency(stats.pendingAmount)} />
|
||||
<StatCard
|
||||
label="Overdue"
|
||||
value={String(stats.overdueCount)}
|
||||
hint={stats.overdueCount === 1 ? "invoice" : "invoices"}
|
||||
/>
|
||||
<StatCard label="Clients" value={String(stats.totalClients)} hint={revenueChange} />
|
||||
<View style={styles.statCell}>
|
||||
<StatCard label="Total revenue" value={formatCurrency(stats.totalRevenue)} />
|
||||
</View>
|
||||
<View style={styles.statCell}>
|
||||
<StatCard label="Pending" value={formatCurrency(stats.pendingAmount)} />
|
||||
</View>
|
||||
<View style={styles.statCell}>
|
||||
<StatCard
|
||||
label="Overdue"
|
||||
value={String(stats.overdueCount)}
|
||||
hint={stats.overdueCount === 1 ? "invoice" : "invoices"}
|
||||
/>
|
||||
</View>
|
||||
<Pressable style={styles.statCell} onPress={() => router.push("/(app)/entities")}>
|
||||
<StatCard
|
||||
label="Clients"
|
||||
value={String(stats.totalClients)}
|
||||
hint={revenueChange}
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<Card title="Revenue (6 months)">
|
||||
<View style={styles.chart}>
|
||||
{stats.revenueChartData.map((point) => (
|
||||
<View key={point.month} style={styles.chartColumn}>
|
||||
<View style={styles.chartBarTrack}>
|
||||
<View
|
||||
style={[
|
||||
styles.chartBar,
|
||||
{ height: `${Math.max(8, (point.revenue / maxRevenue) * 100)}%` },
|
||||
]}
|
||||
/>
|
||||
{stats.revenueChartData.map((point) => {
|
||||
const barHeight = Math.max(4, (point.revenue / maxRevenue) * 80);
|
||||
return (
|
||||
<View key={point.month} style={styles.chartColumn}>
|
||||
<View style={styles.chartBarTrack}>
|
||||
<View style={[styles.chartBar, { height: barHeight }]} />
|
||||
</View>
|
||||
<Text style={styles.chartLabel}>{point.monthLabel}</Text>
|
||||
<Text style={styles.chartValue}>
|
||||
{point.revenue > 0 ? formatCurrency(point.revenue) : "—"}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={styles.chartLabel}>{point.monthLabel}</Text>
|
||||
<Text style={styles.chartValue}>
|
||||
{point.revenue > 0 ? formatCurrency(point.revenue) : "—"}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</Card>
|
||||
|
||||
<Card title="Recent invoices">
|
||||
{stats.recentInvoices.length === 0 ? (
|
||||
<Text style={styles.empty}>No invoices yet. Create one on the web app.</Text>
|
||||
<Text style={styles.empty}>No invoices yet. Create one from the Invoices tab.</Text>
|
||||
) : (
|
||||
stats.recentInvoices.map((invoice) => {
|
||||
const status = getInvoiceStatus(invoice);
|
||||
@@ -262,12 +272,17 @@ const createDashboardStyles = (colors: ThemeColors, isDark: boolean) =>
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
gap: spacing.md,
|
||||
alignContent: "flex-start",
|
||||
},
|
||||
statCell: {
|
||||
flexGrow: 0,
|
||||
flexShrink: 0,
|
||||
flexBasis: "47%",
|
||||
},
|
||||
chart: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
gap: spacing.xs,
|
||||
minHeight: 140,
|
||||
},
|
||||
chartColumn: {
|
||||
flex: 1,
|
||||
|
||||
Reference in New Issue
Block a user