components/activite-CommandeRowCompact-reference.tsx

component·mobile·3.9 KB · 91 lignes· Voir l'itinéraire

Annotation

Ce composant React Native affiche une ligne compacte résumant une commande dans une liste d'activités. Il expose un composant `CommandeRowCompact` qui prend une commande et une fonction de rappel optionnelle pour le clic. Ce composant est utilisé pour représenter chaque commande dans une interface utilisateur mobile, en affichant des informations clés comme le nom du vendeur, le produit principal, le statut, la date et le total.

1 export

default

Code source· tsx

import React, { useMemo } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { Image } from 'expo-image';
import { useTheme } from '@/hooks/useTheme';
import { FONTS, RADIUS, SPACING } from '@/lib/constants';
import type { CommandeMobile } from '@/hooks/useActivite';

type Props = { commande: CommandeMobile; onPress?: () => void };

const STATUT_LABEL: Record<CommandeMobile['statut'], string> = {
  EN_ATTENTE: 'En attente',
  CONFIRMEE: 'Confirmée',
  EN_PREPARATION: 'En préparation',
  EXPEDIE: 'Expédiée',
  LIVRE: 'Livrée',
  ANNULE: 'Annulée',
};

function statutColor(s: CommandeMobile['statut'], colors: ReturnType<typeof useTheme>['colors']) {
  if (s === 'EN_ATTENTE') return colors.warning;
  if (s === 'CONFIRMEE') return colors.info;
  if (s === 'EN_PREPARATION') return colors.info;
  if (s === 'EXPEDIE') return colors.primary;
  if (s === 'LIVRE') return colors.success;
  return colors.error;
}

export default function CommandeRowCompact({ commande, onPress }: Props) {
  const { colors } = useTheme();
  const styles = useMemo(() => makeStyles(colors), [colors]);
  const sc = statutColor(commande.statut, colors);
  const date = new Date(commande.createdAt).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' });

  return (
    <TouchableOpacity style={styles.row} onPress={onPress} activeOpacity={0.75}>
      <View style={styles.thumbWrap}>
        {commande.premiereLigne?.imageUrl ? (
          <Image source={{ uri: commande.premiereLigne.imageUrl }} style={styles.thumb} contentFit="cover" />
        ) : (
          <View style={[styles.thumb, styles.thumbFallback]}>
            <Text style={styles.thumbFallbackText}>{commande.tenant?.nom?.charAt(0).toUpperCase() ?? '?'}</Text>
          </View>
        )}
      </View>

      <View style={styles.content}>
        <Text style={styles.tenant} numberOfLines={1}>{commande.tenant?.nom ?? 'Vitrine'}</Text>
        <Text style={styles.product} numberOfLines={1}>
          {commande.premiereLigne?.nom ?? 'Commande'}
          {commande.nbLignes > 1 ? ` +${commande.nbLignes - 1}` : ''}
        </Text>
        <View style={styles.metaRow}>
          <View style={[styles.statutDot, { backgroundColor: sc }]} />
          <Text style={[styles.statut, { color: sc }]}>{STATUT_LABEL[commande.statut]}</Text>
          <Text style={styles.dot}>·</Text>
          <Text style={styles.date}>{date}</Text>
          <Text style={styles.dot}>·</Text>
          <Text style={styles.total}>{commande.total.toLocaleString('fr-FR')} {commande.devise}</Text>
        </View>
      </View>
    </TouchableOpacity>
  );
}

function makeStyles(colors: ReturnType<typeof useTheme>['colors']) {
  return StyleSheet.create({
    row: {
      flexDirection: 'row',
      gap: SPACING.md,
      backgroundColor: colors.bgCard,
      borderRadius: RADIUS.lg,
      borderWidth: 1,
      borderColor: colors.border,
      padding: SPACING.sm,
    },
    thumbWrap: { width: 56, height: 56, borderRadius: RADIUS.md, overflow: 'hidden' },
    thumb: { width: '100%', height: '100%' },
    thumbFallback: { backgroundColor: colors.bgElevated, alignItems: 'center', justifyContent: 'center' },
    thumbFallbackText: { fontSize: FONTS.lg, fontWeight: FONTS.bold, color: colors.primary },
    content: { flex: 1, justifyContent: 'center', gap: 2 },
    tenant: { fontSize: FONTS.xs, color: colors.textMuted, fontWeight: FONTS.medium },
    product: { fontSize: FONTS.sm, color: colors.text, fontWeight: FONTS.semibold },
    metaRow: { flexDirection: 'row', alignItems: 'center', gap: 4, marginTop: 2 },
    statutDot: { width: 8, height: 8, borderRadius: 4 },
    statut: { fontSize: FONTS.xs, fontWeight: FONTS.semibold },
    dot: { color: colors.textMuted, fontSize: FONTS.xs },
    date: { color: colors.textSecondary, fontSize: FONTS.xs },
    total: { color: colors.text, fontSize: FONTS.xs, fontWeight: FONTS.semibold },
  });
}