From 588cbe00f358df1c74bcd72348ea5f2cb7804884 Mon Sep 17 00:00:00 2001
From: shafi54 <108669266+shafi-aviz@users.noreply.github.com>
Date: Fri, 31 Jul 2026 22:08:00 +0530
Subject: [PATCH] enh
---
.commandcode/taste/taste.md | 4 +
.../manage-orders/orders/index_old.tsx | 801 ------------------
.../app/(drawer)/products/add_old.tsx | 82 --
.../src/components/ProductForm_old.tsx | 262 ------
4 files changed, 4 insertions(+), 1145 deletions(-)
create mode 100644 .commandcode/taste/taste.md
delete mode 100644 apps/admin-ui/app/(drawer)/manage-orders/orders/index_old.tsx
delete mode 100644 apps/admin-ui/app/(drawer)/products/add_old.tsx
delete mode 100644 apps/admin-ui/src/components/ProductForm_old.tsx
diff --git a/.commandcode/taste/taste.md b/.commandcode/taste/taste.md
new file mode 100644
index 0000000..f562cac
--- /dev/null
+++ b/.commandcode/taste/taste.md
@@ -0,0 +1,4 @@
+# Taste (Continuously Learned by [CommandCode][cmd])
+
+[cmd]: https://commandcode.ai/
+
diff --git a/apps/admin-ui/app/(drawer)/manage-orders/orders/index_old.tsx b/apps/admin-ui/app/(drawer)/manage-orders/orders/index_old.tsx
deleted file mode 100644
index fd17855..0000000
--- a/apps/admin-ui/app/(drawer)/manage-orders/orders/index_old.tsx
+++ /dev/null
@@ -1,801 +0,0 @@
-import React, { useState , useEffect } from 'react';
-import { View, TouchableOpacity, Alert, TextInput, ActivityIndicator, Linking } from 'react-native';
-import { AppContainer, MyText, tw, MyFlatList, BottomDialog, BottomDropdown, Checkbox, theme, MyTextInput } from 'common-ui';
-import { trpc } from '@/src/trpc-client';
-import { useRouter, useLocalSearchParams } from 'expo-router';
-import dayjs from 'dayjs';
-import MaterialIcons from '@expo/vector-icons/MaterialIcons';
-import { Entypo } from '@expo/vector-icons';
-import CancelOrderDialog from '@/components/CancelOrderDialog';
-import { OrderOptionsMenu } from '@/components/OrderOptionsMenu';
-import * as Location from 'expo-location';
-
-const AdminNotesForm = ({ orderId, existingNotes, onClose, refetch }: { orderId: string; existingNotes?: string | null; onClose: () => void; refetch: () => void }) => {
- const [notesText, setNotesText] = useState(existingNotes || '');
- const updateNotesMutation = trpc.admin.order.updateNotes.useMutation();
-
- return (
-
- Admin Notes
-
-
- {
- updateNotesMutation.mutate(
- { orderId: parseInt(orderId), adminNotes: notesText },
- {
- onSuccess: () => {
- onClose();
- Alert.alert('Success', 'Notes updated successfully');
- refetch();
- },
- onError: (error: any) => {
- Alert.alert('Error', error.message || 'Failed to update notes');
- },
- }
- );
- }}
- >
- Save
-
-
-
- );
-};
-
-
-interface OrderType {
- id: number;
- orderId: string;
- readableId: number;
- customerName: string | null;
- customerMobile?: string | null;
- address: string;
- addressId: number;
- latitude: number | null;
- longitude: number | null;
- totalAmount: number;
- deliveryCharge: number;
- items: {
- id?: number;
- name: string;
- quantity: number;
- price: number;
- amount: number;
- unit: string;
- isPackaged?: boolean;
- isPackageVerified?: boolean;
- productSize: number;
- }[];
- createdAt: string;
- deliveryTime: string | null;
- status: 'pending' | 'delivered' | 'cancelled';
- isPackaged: boolean;
- isDelivered: boolean;
- isCod: boolean;
- isFlashDelivery: boolean;
- couponCode?: string;
- couponDescription?: string;
- discountAmount?: number;
- adminNotes?: string | null;
- userNotes?: string | null;
- userNegativityScore?: number;
-}
-
-const OrderItem = ({ order, refetch }: { order: OrderType; refetch: () => void }) => {
- const id = order.orderId;
- const router = useRouter();
- const [menuOpen, setMenuOpen] = useState(false);
- const [itemsDialogOpen, setItemsDialogOpen] = useState(false);
- const [notesDialogOpen, setNotesDialogOpen] = useState(false);
- const [cancelDialogOpen, setCancelDialogOpen] = useState(false);
- const [userNotesDialogOpen, setUserNotesDialogOpen] = useState(false);
- const [adminNotesDialogOpen, setAdminNotesDialogOpen] = useState(false);
- const [updatingItems, setUpdatingItems] = useState>(new Set());
-
- const updatePackagedMutation = trpc.admin.order.updatePackaged.useMutation();
- const updateDeliveredMutation = trpc.admin.order.updateDelivered.useMutation();
- const updateItemPackagingMutation = trpc.admin.order.updateOrderItemPackaging.useMutation();
-
- const handleOrderPress = () => {
- router.push(`/manage-orders/order-details/${order.orderId}` as any);
- };
-
- const handleMenuOption = () => {
- setMenuOpen(false);
- router.push(`/manage-orders/order-details/${order.orderId}` as any);
- };
-
- const handleMarkPackaged = (isPackaged: boolean) => {
- updatePackagedMutation.mutate(
- { orderId: order.orderId.toString(), isPackaged },
- {
- onSuccess: () => {
- refetch();
- },
- }
- );
- };
-
- const handleMarkDelivered = (isDelivered: boolean) => {
- updateDeliveredMutation.mutate(
- { orderId: order.orderId.toString(), isDelivered },
- {
- onSuccess: () => {
- refetch();
- },
- }
- );
- };
-
- const handleItemPackagingToggle = (itemId: number, field: 'isPackaged' | 'isPackageVerified', value: boolean) => {
- setUpdatingItems(prev => new Set(prev).add(itemId));
-
- updateItemPackagingMutation.mutate(
- { orderItemId: itemId, [field]: value },
- {
- onSuccess: () => {
- setUpdatingItems(prev => {
- const newSet = new Set(prev);
- newSet.delete(itemId);
- return newSet;
- });
- refetch();
- },
- onError: (error: any) => {
- setUpdatingItems(prev => {
- const newSet = new Set(prev);
- newSet.delete(itemId);
- return newSet;
- });
- Alert.alert("Error", error.message || "Failed to update packaging status");
- },
- }
- );
- };
-
- return (
- <>
-
- {/* Header Section */}
-
-
-
-
- 0 ? 'text-yellow-600' : 'text-gray-900')}`}>
- {order.customerName || order.customerMobile || 'Unknown Customer'}
-
-
- #{order.readableId}
-
- {order.isFlashDelivery && (
-
-
- FLASH
-
- )}
-
-
-
-
- {dayjs(order.createdAt).format('MMM D, h:mm A')}
-
- {order.userNegativityScore && order.userNegativityScore > 0 && (
-
-
- Negative Customer
-
- )}
-
-
-
- setMenuOpen(true)}
- style={tw`p-2 -mr-2 -mt-2 rounded-full`}
- >
-
-
-
-
-
- {/* Main Content */}
-
- {/* Status Badges */}
-
- {/*
- {order.status}
- */}
- {/* {order.isCod && (
-
- COD
-
- )} */}
-
- Packaged
- handleMarkPackaged(!order.isPackaged)}
- onPress={() => {}}
- size={18}
- fillColor={theme.colors.gray500}
- checkColor="#FFFFFF"
- />
-
-
- Delivered
- handleMarkDelivered(!order.isDelivered)}
- size={18}
- fillColor="#10B981"
- checkColor="#FFFFFF"
- />
-
- {order.status === 'cancelled' && (
-
- CANCELLED
-
- )}
-
-
- {/* Delivery Info */}
-
-
-
- Delivery Address
-
- {order.address}
-
-
-
-
- {order.isFlashDelivery ? "1 Hr Delivery:" : "Slot:"} {order.isFlashDelivery ? dayjs(order.createdAt).add(30, 'minutes').format('MMM D, h:mm A') : order.deliveryTime ? dayjs(order.deliveryTime).format("ddd, MMM D • h:mm A") : 'Not scheduled'}
-
-
- {order.isFlashDelivery && (
-
-
-
- 1 Hour Delivery • High Priority
-
-
- )}
-
-
-
- {/* Items Summary & Total */}
-
-
-
- setItemsDialogOpen(true)}
- style={tw`flex-row items-center py-2 px-3 bg-blue-50 rounded-lg flex-1 mr-3`}
- >
-
-
- {order.items.length} {order.items.length === 1 ? 'item' : 'items'}
-
- {order.isFlashDelivery && (
-
- ⚡
-
- )}
-
-
- Total:
- ₹{order.totalAmount}
-
-
-
-
-
- {/* Coupons */}
- {order.couponCode && (
-
- Applied Coupons
-
-
- {order.couponCode}
-
- {order.couponDescription && (
-
- {order.couponDescription}
-
- )}
- {order.discountAmount && (
-
- Discount: ₹{order.discountAmount}
-
- )}
-
-
- )}
-
- {/* Notes Section */}
-
- {order.userNotes && (
- setUserNotesDialogOpen(true)}
- >
-
-
- User Notes
-
-
- )}
- {order.adminNotes && (
- setNotesDialogOpen(true)}
- >
-
-
- Admin Notes
-
-
- )}
-
-
- {/* Footer / Delivery Charge */}
- {order.deliveryCharge > 0 && (
-
-
- Delivery Charge
- ₹{order.deliveryCharge}
-
-
- )}
-
-
-
- setMenuOpen(false)}
- order={{
- id: order.id,
- readableId: order.readableId,
- isPackaged: order.isPackaged,
- isDelivered: order.isDelivered,
- isFlashDelivery: order.isFlashDelivery,
- address: order.address,
- addressId: order.addressId,
- adminNotes: order.adminNotes,
- userNotes: order.userNotes,
- latitude: order.latitude,
- longitude: order.longitude,
- status: order.status,
- }}
- onViewDetails={handleMenuOption}
- onTogglePackaged={() => handleMarkPackaged(!order.isPackaged)}
- onToggleDelivered={() => handleMarkDelivered(!order.isDelivered)}
- onOpenAdminNotes={() => {
- setMenuOpen(false);
- setNotesDialogOpen(true);
- }}
- onCancelOrder={() => {
- setMenuOpen(false);
- setCancelDialogOpen(true);
- }}
- onAttachLocation={() => refetch()}
- onWhatsApp={() => {}}
- onDial={() => {}}
- />
-
- setItemsDialogOpen(false)}>
-
-
-
- Order Items
-
- {order.isFlashDelivery && (
-
-
- FLASH
-
- )}
-
-
- Total: ₹{order.totalAmount}
-
- {order.items.map((item, idx) => (
-
-
-
- {item.quantity * item.productSize } {item.unit}
-
-
- {item.name.length > 30 ? `${item.name.substring(0, 30)}...` : item.name}
-
- {item.isPackaged !== undefined && item.isPackageVerified !== undefined && (
- <>
-
- pkg
- handleItemPackagingToggle(item.id!, 'isPackaged', !item.isPackaged)}
- size={18}
- fillColor={updatingItems.has(item.id!) ? "#F59E0B" : "#10B981"}
- checkColor="#FFFFFF"
- />
-
-
- verf
- handleItemPackagingToggle(item.id!, 'isPackageVerified', !item.isPackageVerified)}
- size={18}
- fillColor={updatingItems.has(item.id!) ? "#F59E0B" : "#10B981"}
- checkColor="#FFFFFF"
- />
-
- {updatingItems.has(item.id!) && (
-
- )}
- >
- )}
-
-
- ))}
-
-
-
- setNotesDialogOpen(false)}>
- setNotesDialogOpen(false)} refetch={refetch} />
-
-
- setCancelDialogOpen(false)}
- onSuccess={refetch}
- />
-
- setUserNotesDialogOpen(false)}>
-
-
- User Notes
-
-
-
- {order.userNotes}
-
-
-
-
-
- setAdminNotesDialogOpen(false)}>
-
-
- Admin Notes
-
-
-
- {order.adminNotes}
-
-
-
-
- >
- );
- };
-
-export default function Orders() {
- const router = useRouter();
- const { filter } = useLocalSearchParams<{ filter?: string }>();
- const [selectedSlot, setSelectedSlot] = useState(null);
- const [selectedSlotType, setSelectedSlotType] = useState<'slot' | 'flash' | null>(null);
- const [packagedFilter, setPackagedFilter] = useState<'all' | 'packaged' | 'not_packaged'>('all');
- const [packagedChecked, setPackagedChecked] = useState(false);
- const [notPackagedChecked, setNotPackagedChecked] = useState(false);
- const [deliveredFilter, setDeliveredFilter] = useState<'all' | 'delivered' | 'not_delivered'>('all');
- const [deliveredChecked, setDeliveredChecked] = useState(false);
- const [notDeliveredChecked, setNotDeliveredChecked] = useState(false);
- const [cancellationFilter, setCancellationFilter] = useState<'all' | 'cancelled' | 'not_cancelled'>('all');
- const [cancelledChecked, setCancelledChecked] = useState(false);
- const [notCancelledChecked, setNotCancelledChecked] = useState(false);
- const [flashDeliveryFilter, setFlashDeliveryFilter] = useState<'all' | 'flash' | 'regular'>('all');
- const [flashChecked, setFlashChecked] = useState(false);
- const [regularChecked, setRegularChecked] = useState(false);
- const [filterDialogOpen, setFilterDialogOpen] = useState(false);
-
- // Handle initial filter from URL params
- useEffect(() => {
- if (filter === 'flash') {
- setSelectedSlotType('flash');
- setFlashDeliveryFilter('flash');
- setFlashChecked(true);
- setRegularChecked(false);
- }
- }, [filter]);
- const { data: slotsData } = trpc.admin.slots.getAll.useQuery();
- const { data, isLoading, isFetchingNextPage, fetchNextPage, hasNextPage, refetch } = trpc.admin.order.getAll.useInfiniteQuery(
- {
- limit: 20,
- slotId: selectedSlotType === 'slot' ? selectedSlot : null,
- packagedFilter,
- deliveredFilter,
- cancellationFilter,
- flashDeliveryFilter: selectedSlotType === 'flash' ? 'flash' : flashDeliveryFilter
- },
- {
- getNextPageParam: (lastPage) => lastPage?.nextCursor,
- }
- );
-
- const orders = data?.pages.flatMap(page => page?.orders) || [];
-
- if (isLoading) {
- return (
-
-
- Loading orders...
-
- );
- }
-
- const slotOptions = [
- { label: '⚡ Flash Deliveries', value: 'flash' },
- ...(slotsData?.slots?.map(slot => ({
- label: dayjs(slot.deliveryTime).format('ddd DD MMM, h:mm a'),
- value: slot.id.toString(),
- })) || [])
- ];
-
-
- return (
- <>
- item!.orderId}
- renderItem={({ item }) => item ? : null}
- onEndReached={() => {
- if (hasNextPage && !isFetchingNextPage) {
- fetchNextPage();
- }
- }}
- onEndReachedThreshold={0.5}
- onRefresh={() => refetch()}
- ListHeaderComponent={
- <>
-
-
- {
- if (val === 'flash') {
- setSelectedSlotType('flash');
- setSelectedSlot(null);
- setFlashDeliveryFilter('flash');
- // Reset other filters when switching to flash
- setPackagedFilter('all');
- setPackagedChecked(false);
- setNotPackagedChecked(false);
- setDeliveredFilter('all');
- setDeliveredChecked(false);
- setNotDeliveredChecked(false);
- setCancellationFilter('all');
- setCancelledChecked(false);
- setNotCancelledChecked(false);
- } else {
- setSelectedSlotType('slot');
- setSelectedSlot(val ? Number(val) : null);
- setFlashDeliveryFilter('all');
- }
- }}
- placeholder="All slots"
- />
-
- setFilterDialogOpen(true)}
- style={tw`p-2`}
- >
-
-
-
- {!isLoading && selectedSlotType && (
-
-
- {selectedSlotType === 'flash'
- ? `${orders.length} Flash delivery orders`
- : `${orders.length} Orders in slot`
- }
-
-
- )}
- >
- }
- ListFooterComponent={
- isFetchingNextPage ? (
-
-
- Loading more...
-
- ) : null
- }
- />
-
- setFilterDialogOpen(false)}>
-
-
- Packaged Status
-
- {
- const newValue = !packagedChecked;
- setPackagedChecked(newValue);
- if (newValue && notPackagedChecked) {
- setPackagedFilter('all');
- } else if (newValue) {
- setPackagedFilter('packaged');
- } else if (notPackagedChecked) {
- setPackagedFilter('not_packaged');
- } else {
- setPackagedFilter('all');
- }
- }}
- />
- Packaged
-
-
- {
- const newValue = !notPackagedChecked;
- setNotPackagedChecked(newValue);
- if (packagedChecked && newValue) {
- setPackagedFilter('all');
- } else if (newValue) {
- setPackagedFilter('not_packaged');
- } else if (packagedChecked) {
- setPackagedFilter('packaged');
- } else {
- setPackagedFilter('all');
- }
- }}
- />
- Not Packaged
-
-
-
- Delivered Status
-
- {
- const newValue = !deliveredChecked;
- setDeliveredChecked(newValue);
- if (newValue && notDeliveredChecked) {
- setDeliveredFilter('all');
- } else if (newValue) {
- setDeliveredFilter('delivered');
- } else if (notDeliveredChecked) {
- setDeliveredFilter('not_delivered');
- } else {
- setDeliveredFilter('all');
- }
- }}
- />
- Delivered
-
-
- {
- const newValue = !notDeliveredChecked;
- setNotDeliveredChecked(newValue);
- if (deliveredChecked && newValue) {
- setDeliveredFilter('all');
- } else if (newValue) {
- setDeliveredFilter('not_delivered');
- } else if (deliveredChecked) {
- setDeliveredFilter('delivered');
- } else {
- setDeliveredFilter('all');
- }
- }}
- />
- Not Delivered
-
-
-
- Cancellation Status
-
- {
- const newValue = !cancelledChecked;
- setCancelledChecked(newValue);
- if (newValue && notCancelledChecked) {
- setCancellationFilter('all');
- } else if (newValue) {
- setCancellationFilter('cancelled');
- } else if (notCancelledChecked) {
- setCancellationFilter('not_cancelled');
- } else {
- setCancellationFilter('all');
- }
- }}
- />
- Cancelled
-
-
- {
- const newValue = !notCancelledChecked;
- setNotCancelledChecked(newValue);
- if (cancelledChecked && newValue) {
- setCancellationFilter('all');
- } else if (newValue) {
- setCancellationFilter('not_cancelled');
- } else if (cancelledChecked) {
- setCancellationFilter('cancelled');
- } else {
- setCancellationFilter('all');
- }
- }}
- />
- Not Cancelled
-
-
-
- Delivery Type
-
- {
- const newValue = !flashChecked;
- setFlashChecked(newValue);
- if (newValue && regularChecked) {
- setFlashDeliveryFilter('all');
- } else if (newValue) {
- setFlashDeliveryFilter('flash');
- } else if (regularChecked) {
- setFlashDeliveryFilter('regular');
- } else {
- setFlashDeliveryFilter('all');
- }
- }}
- />
- ⚡ 1 Hr Delivery
-
-
- {
- const newValue = !regularChecked;
- setRegularChecked(newValue);
- if (flashChecked && newValue) {
- setFlashDeliveryFilter('all');
- } else if (newValue) {
- setFlashDeliveryFilter('regular');
- } else if (flashChecked) {
- setFlashDeliveryFilter('flash');
- } else {
- setFlashDeliveryFilter('all');
- }
- }}
- />
- Regular Delivery
-
-
-
-
- >
- );
-}
diff --git a/apps/admin-ui/app/(drawer)/products/add_old.tsx b/apps/admin-ui/app/(drawer)/products/add_old.tsx
deleted file mode 100644
index 33b9a71..0000000
--- a/apps/admin-ui/app/(drawer)/products/add_old.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import React from 'react';
-import { Alert } from 'react-native';
-import { AppContainer, ImageUploaderNeoPayload } from 'common-ui';
-import ProductForm from '@/src/components/ProductForm';
-import { trpc } from '@/src/trpc-client';
-import { useUploadToObjectStorage } from '@/hooks/useUploadToObjectStore';
-
-export default function AddProduct() {
- const createProduct = trpc.admin.product.createProduct.useMutation();
- const { refetch: refetchProducts } = trpc.admin.product.getProducts.useQuery(undefined, {
- enabled: false,
- });
- const { upload, isUploading } = useUploadToObjectStorage();
-
- const handleSubmit = async (values: any, images: ImageUploaderNeoPayload[]) => {
- try {
- let uploadUrls: string[] = [];
-
- if (images.length > 0) {
- const blobs = await Promise.all(
- images.map(async (img) => {
- const response = await fetch(img.url);
- const blob = await response.blob();
- return { blob, mimeType: img.mimeType || 'image/jpeg' };
- })
- );
-
- const result = await upload({ images: blobs, contextString: 'product_info' });
- uploadUrls = result.presignedUrls;
- }
-
- await createProduct.mutateAsync({
- name: values.name,
- shortDescription: values.shortDescription,
- longDescription: values.longDescription,
- unitId: parseInt(values.unitId),
- storeId: parseInt(values.storeId),
- price: parseFloat(values.price),
- marketPrice: values.marketPrice ? parseFloat(values.marketPrice) : undefined,
- incrementStep: 1,
- productQuantity: values.productQuantity || 1,
- isSuspended: values.isSuspended || false,
- isFlashAvailable: values.isFlashAvailable || false,
- flashPrice: values.flashPrice ? parseFloat(values.flashPrice) : undefined,
- uploadUrls,
- tagIds: values.tagIds || [],
- });
-
- await refetchProducts();
- Alert.alert('Success', 'Product created successfully!');
- } catch (error: any) {
- Alert.alert('Error', error.message || 'Failed to create product');
- }
- };
-
- const initialValues = {
- name: '',
- shortDescription: '',
- longDescription: '',
- unitId: 0,
- price: '',
- storeId: 1,
- marketPrice: '',
- deals: [{ quantity: '', price: '', validTill: new Date() }],
- tagIds: [],
- isSuspended: false,
- isFlashAvailable: false,
- flashPrice: '',
- productQuantity: 1,
- };
-
- return (
-
-
-
- );
-}
diff --git a/apps/admin-ui/src/components/ProductForm_old.tsx b/apps/admin-ui/src/components/ProductForm_old.tsx
deleted file mode 100644
index ac8d4e3..0000000
--- a/apps/admin-ui/src/components/ProductForm_old.tsx
+++ /dev/null
@@ -1,262 +0,0 @@
-import React, { useState, useEffect, useCallback, forwardRef, useImperativeHandle, useMemo } from 'react';
-import { View, TouchableOpacity } from 'react-native';
-import { Formik, FieldArray } from 'formik';
-import * as Yup from 'yup';
-import { MyTextInput, BottomDropdown, MyText, useTheme, DatePicker, tw, useFocusCallback, Checkbox, ImageUploaderNeo, ImageUploaderNeoItem, ImageUploaderNeoPayload } from 'common-ui';
-import MaterialIcons from '@expo/vector-icons/MaterialIcons';
-import { trpc } from '../trpc-client';
-
-interface ProductFormData {
- name: string;
- shortDescription: string;
- longDescription: string;
- unitId: number;
- storeId: number;
- price: string;
- marketPrice: string;
- isSuspended: boolean;
- isFlashAvailable: boolean;
- flashPrice: string;
- deals: Deal[];
- tagIds: number[];
- productQuantity: number;
-}
-
-interface Deal {
- quantity: string;
- price: string;
- validTill: Date | null;
-}
-
-export interface ProductFormRef {
- clearImages: () => void;
-}
-
-interface ProductFormProps {
- mode: 'create' | 'edit';
- initialValues: ProductFormData;
- onSubmit: (values: ProductFormData, images: ImageUploaderNeoPayload[], imagesToDelete: string[]) => void;
- isLoading: boolean;
- existingImages?: ImageUploaderNeoItem[];
- existingImageKeys?: string[];
-}
-
-const unitOptions = [
- { label: 'Kg', value: 1 },
- { label: 'Litre', value: 2 },
- { label: 'Dozen', value: 3 },
- { label: 'Unit Piece', value: 4 },
-];
-
-const ProductForm = forwardRef(({
- mode,
- initialValues,
- onSubmit,
- isLoading,
- existingImages:existingImagesRaw,
- existingImageKeys = [],
-}, ref) => {
- const { theme } = useTheme();
- const [images, setImages] = useState([]);
-
- const existingImages = existingImagesRaw || []
- // Sync images state when existingImages prop changes (e.g., when async query data arrives)
- useEffect(() => {
- setImages(existingImages);
- }, [existingImagesRaw]);
-
- const { data: storesData } = trpc.common.getStoresSummary.useQuery();
- const storeOptions = storesData?.stores.map(store => ({
- label: store.name,
- value: store.id,
- })) || [];
-
- const { data: tagsData } = trpc.admin.product.getProductTags.useQuery();
- const tagOptions = tagsData?.tags.map(tag => ({
- label: tag.tagName,
- value: tag.id.toString(),
- })) || [];
-
- // Build signed URL -> S3 key mapping for existing images
- const signedUrlToKey = useMemo(() => {
- const map: Record = {};
- existingImages.forEach((img, i) => {
- if (existingImageKeys[i]) {
- map[img.imgUrl] = existingImageKeys[i];
- }
- });
- return map;
- }, [existingImages, existingImageKeys]);
-
- return (
- {
- // New images have mimeType set, existing images have mimeType === null
- const newImages = images.filter(img => img.mimeType !== null);
- const deletedImageKeys = existingImages
- .filter(existing => !images.some(current => current.imgUrl === existing.imgUrl))
- .map(deleted => signedUrlToKey[deleted.imgUrl])
- .filter(Boolean);
-
- onSubmit(
- values,
- newImages.map(img => ({ url: img.imgUrl, mimeType: img.mimeType })),
- deletedImageKeys,
- );
- }}
- enableReinitialize
- >
- {({ handleChange, handleSubmit, values, setFieldValue, resetForm }) => {
- const clearForm = useCallback(() => {
- setImages([]);
- resetForm();
- }, [resetForm]);
-
- useFocusCallback(clearForm);
-
- useImperativeHandle(ref, () => ({
- clearImages: clearForm,
- }), [clearForm]);
-
- const submit = () => handleSubmit();
-
- return (
-
-
-
-
-
- setImages(prev => [...prev, ...payloads.map(p => ({ imgUrl: p.url, mimeType: p.mimeType }))])}
- onImageRemove={(payload) => setImages(prev => prev.filter(img => img.imgUrl !== payload.url))}
- allowMultiple={true}
- />
-
- setFieldValue('unitId', value)}
- placeholder="Select unit"
- style={{ marginBottom: 16 }}
- />
- setFieldValue('productQuantity', text)}
- style={{ marginBottom: 16 }}
- />
- setFieldValue('storeId', value)}
- placeholder="Select store"
- style={{ marginBottom: 16 }}
- />
- id.toString())}
- options={tagOptions}
- onValueChange={(value) => setFieldValue('tagIds', (value as string[]).map(id => parseInt(id)))}
- multiple={true}
- placeholder="Select tags"
- style={{ marginBottom: 16 }}
- />
-
-
-
-
- setFieldValue('isSuspended', !values.isSuspended)}
- style={tw`mr-3`}
- />
- Suspend Product
-
-
-
- {
- setFieldValue('isFlashAvailable', !values.isFlashAvailable);
- if (values.isFlashAvailable) setFieldValue('flashPrice', '');
- }}
- style={tw`mr-3`}
- />
- Flash Available
-
-
- {values.isFlashAvailable && (
-
- )}
-
-
-
- {isLoading ? (mode === 'create' ? 'Creating...' : 'Updating...') : (mode === 'create' ? 'Create Product' : 'Update Product')}
-
-
-
- );
- }}
-
- );
-});
-
-ProductForm.displayName = 'ProductForm';
-
-export default ProductForm;