chore: build
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
||||
ScrollView,
|
||||
RefreshControl,
|
||||
TouchableOpacity,
|
||||
useWindowDimensions,
|
||||
} from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { spacing, fontSize } from "../../theme";
|
||||
@@ -18,23 +17,13 @@ import LoadingSpinner from "../../components/ui/LoadingSpinner";
|
||||
import Card from "../../components/ui/Card";
|
||||
|
||||
type Period = "day" | "week" | "month";
|
||||
type Metric = "count" | "revenue";
|
||||
|
||||
const BAR_MAX_HEIGHT = 110;
|
||||
const BAR_WIDTH = 36;
|
||||
const BAR_GAP = 8;
|
||||
|
||||
function BarChart({
|
||||
data,
|
||||
metric,
|
||||
colors,
|
||||
}: {
|
||||
data: StatPoint[];
|
||||
metric: Metric;
|
||||
colors: any;
|
||||
}) {
|
||||
const values = data.map((d) => (metric === "count" ? d.count : d.revenue));
|
||||
const maxVal = Math.max(...values, 1);
|
||||
function BarChart({ data, colors }: { data: StatPoint[]; colors: any }) {
|
||||
const maxVal = Math.max(...data.map((d) => d.count), 1);
|
||||
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
@@ -51,7 +40,7 @@ function BarChart({
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ marginTop: spacing.m }}>
|
||||
<View style={{ flexDirection: "row", alignItems: "flex-end", paddingBottom: spacing.s, paddingHorizontal: 4 }}>
|
||||
{data.map((point, i) => {
|
||||
const val = metric === "count" ? point.count : point.revenue;
|
||||
const val = point.count;
|
||||
const barH = Math.max(4, (val / maxVal) * BAR_MAX_HEIGHT);
|
||||
const isLast = i === data.length - 1;
|
||||
return (
|
||||
@@ -64,9 +53,7 @@ function BarChart({
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: colors.textMuted, fontSize: 9, marginBottom: 3 }}>
|
||||
{metric === "count"
|
||||
? val > 0 ? String(val) : ""
|
||||
: val > 0 ? (val >= 1000 ? `${(val / 1000).toFixed(1)}k` : `${Math.round(val)}`) : ""}
|
||||
{val > 0 ? String(val) : ""}
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
@@ -105,7 +92,6 @@ export default function StatsScreen() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [period, setPeriod] = useState<Period>("week");
|
||||
const [metric, setMetric] = useState<Metric>("count");
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
try {
|
||||
@@ -137,18 +123,13 @@ export default function StatsScreen() {
|
||||
const completed = deliveries.filter((d) => d.status === "livre" || d.status === "approved").length;
|
||||
const inProgress = deliveries.filter((d) => d.status === "en_route").length;
|
||||
const pending = deliveries.filter((d) => d.status === "assigned").length;
|
||||
const totalRevenue = deliveries
|
||||
.filter((d) => d.status === "livre" || d.status === "approved")
|
||||
.reduce((s, d) => s + (d.total_prix - (d.referral_used ?? 0)), 0);
|
||||
|
||||
const chartData = period === "day" ? byDay : period === "week" ? byWeek : byMonth;
|
||||
|
||||
const periodTotal = useMemo(() => {
|
||||
return chartData.reduce(
|
||||
(acc, p) => ({ count: acc.count + p.count, revenue: acc.revenue + p.revenue }),
|
||||
{ count: 0, revenue: 0 },
|
||||
);
|
||||
}, [chartData]);
|
||||
const periodTotal = useMemo(
|
||||
() => chartData.reduce((acc, p) => acc + p.count, 0),
|
||||
[chartData],
|
||||
);
|
||||
|
||||
const periodLabels: Record<Period, string> = {
|
||||
day: "30 derniers jours",
|
||||
@@ -175,22 +156,6 @@ export default function StatsScreen() {
|
||||
marginTop: spacing.xs,
|
||||
textAlign: "center",
|
||||
},
|
||||
revenueCard: {
|
||||
alignItems: "center",
|
||||
paddingVertical: spacing.l,
|
||||
marginTop: spacing.m,
|
||||
},
|
||||
revenueValue: {
|
||||
fontSize: 28,
|
||||
fontWeight: "800",
|
||||
color: colors.success,
|
||||
marginTop: spacing.s,
|
||||
},
|
||||
revenueLabel: {
|
||||
color: colors.textMuted,
|
||||
fontSize: fontSize.sm,
|
||||
marginTop: spacing.xs,
|
||||
},
|
||||
sectionTitle: {
|
||||
color: colors.textWhite,
|
||||
fontSize: fontSize.lg,
|
||||
@@ -222,26 +187,6 @@ export default function StatsScreen() {
|
||||
color: colors.textMuted,
|
||||
},
|
||||
periodBtnTextActive: { color: colors.accent },
|
||||
metricRow: {
|
||||
flexDirection: "row",
|
||||
gap: spacing.s,
|
||||
marginBottom: spacing.s,
|
||||
},
|
||||
metricBtn: {
|
||||
flex: 1,
|
||||
paddingVertical: spacing.xs,
|
||||
borderRadius: 6,
|
||||
alignItems: "center",
|
||||
backgroundColor: colors.bgCard,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
metricBtnActive: {
|
||||
backgroundColor: colors.success + "22",
|
||||
borderColor: colors.success,
|
||||
},
|
||||
metricBtnText: { fontSize: fontSize.xs, fontWeight: "600", color: colors.textMuted },
|
||||
metricBtnTextActive: { color: colors.success },
|
||||
chartCard: { paddingBottom: spacing.s },
|
||||
summaryRow: {
|
||||
flexDirection: "row",
|
||||
@@ -287,15 +232,6 @@ export default function StatsScreen() {
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* Revenu total all-time */}
|
||||
<Card style={styles.revenueCard}>
|
||||
<Ionicons name="cash-outline" size={28} color={colors.success} />
|
||||
<Text style={styles.revenueValue}>
|
||||
{totalRevenue.toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} €
|
||||
</Text>
|
||||
<Text style={styles.revenueLabel}>Revenu total généré</Text>
|
||||
</Card>
|
||||
|
||||
{/* Section graphiques */}
|
||||
<Text style={styles.sectionTitle}>Évolution</Text>
|
||||
|
||||
@@ -314,44 +250,17 @@ export default function StatsScreen() {
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* Sélecteur métrique */}
|
||||
<View style={styles.metricRow}>
|
||||
<TouchableOpacity
|
||||
style={[styles.metricBtn, metric === "count" && styles.metricBtnActive]}
|
||||
onPress={() => setMetric("count")}
|
||||
>
|
||||
<Text style={[styles.metricBtnText, metric === "count" && styles.metricBtnTextActive]}>
|
||||
Commandes
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.metricBtn, metric === "revenue" && styles.metricBtnActive]}
|
||||
onPress={() => setMetric("revenue")}
|
||||
>
|
||||
<Text style={[styles.metricBtnText, metric === "revenue" && styles.metricBtnTextActive]}>
|
||||
Revenus (€)
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<Card style={styles.chartCard}>
|
||||
<Text style={styles.periodHint}>{periodLabels[period]}</Text>
|
||||
|
||||
<BarChart data={chartData} metric={metric} colors={colors} />
|
||||
<BarChart data={chartData} colors={colors} />
|
||||
|
||||
{/* Totaux de la période */}
|
||||
{chartData.length > 0 && (
|
||||
<View style={styles.summaryRow}>
|
||||
<View style={styles.summaryItem}>
|
||||
<Text style={styles.summaryValue}>{periodTotal.count}</Text>
|
||||
<Text style={styles.summaryValue}>{periodTotal}</Text>
|
||||
<Text style={styles.summaryLabel}>livraisons</Text>
|
||||
</View>
|
||||
<View style={[styles.summaryItem, { borderLeftWidth: 1, borderLeftColor: colors.border }]}>
|
||||
<Text style={[styles.summaryValue, { color: colors.success }]}>
|
||||
{periodTotal.revenue.toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} €
|
||||
</Text>
|
||||
<Text style={styles.summaryLabel}>revenus</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user