From e52ea39e607e402842b8e4785889ca8e22dcc2d0 Mon Sep 17 00:00:00 2001 From: hosseintaromi Date: Mon, 1 Sep 2025 01:21:29 +0330 Subject: [PATCH] feat(shipping-methods): add full CRUD scaffolding (list + form), API routes, query keys and React Query hooks; improve dropdown selected contrast; numeric inputs with thousand separators and no default zeros --- src/App.tsx | 9 ++ src/components/layout/Sidebar.tsx | 6 + src/components/ui/MultiSelectAutocomplete.tsx | 80 +++++++--- src/components/ui/Table.tsx | 18 ++- src/constant/routes.ts | 10 ++ .../DiscountCodeFormPage.tsx | 141 ++++++++++++----- src/pages/orders/core/_requests.ts | 2 +- src/pages/shipping-methods/core/_hooks.ts | 84 +++++++++++ src/pages/shipping-methods/core/_models.ts | 28 ++++ src/pages/shipping-methods/core/_requests.ts | 58 +++++++ .../ShippingMethodFormPage.tsx | 142 ++++++++++++++++++ .../ShippingMethodsListPage.tsx | 136 +++++++++++++++++ src/pages/users-admin/core/_requests.ts | 4 +- .../user-admin-form/UserAdminFormPage.tsx | 18 ++- .../users-admin-list/UsersAdminListPage.tsx | 19 ++- src/types/index.ts | 1 + src/utils/query-key.ts | 7 + 17 files changed, 686 insertions(+), 77 deletions(-) create mode 100644 src/pages/shipping-methods/core/_hooks.ts create mode 100644 src/pages/shipping-methods/core/_models.ts create mode 100644 src/pages/shipping-methods/core/_requests.ts create mode 100644 src/pages/shipping-methods/shipping-method-form/ShippingMethodFormPage.tsx create mode 100644 src/pages/shipping-methods/shipping-methods-list/ShippingMethodsListPage.tsx diff --git a/src/App.tsx b/src/App.tsx index 7ea2a78..2246af2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -63,6 +63,10 @@ const ProductDetailPage = lazy(() => import('./pages/products/product-detail/Pro // Landing Hero Page const HeroSliderPage = lazy(() => import('./pages/landing-hero/HeroSliderPage')); +// Shipping Methods Pages +const ShippingMethodsListPage = lazy(() => import('./pages/shipping-methods/shipping-methods-list/ShippingMethodsListPage')); +const ShippingMethodFormPage = lazy(() => import('./pages/shipping-methods/shipping-method-form/ShippingMethodFormPage')); + const ProtectedRoute = ({ children }: { children: any }) => { const { user, isLoading } = useAuth(); @@ -138,6 +142,11 @@ const AppRoutes = () => { {/* Landing Hero Route */} } /> + {/* Shipping Methods Routes */} + } /> + } /> + } /> + {/* Products Routes */} } /> } /> diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index e90ef5e..126085d 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -15,6 +15,7 @@ import { BadgePercent, ShoppingCart, Users, + Truck, X } from 'lucide-react'; import { useAuth } from '../../contexts/AuthContext'; @@ -98,6 +99,11 @@ const menuItems: MenuItem[] = [ icon: Sliders, path: '/landing-hero', }, + { + title: 'روش‌های ارسال', + icon: Truck, + path: '/shipping-methods', + }, ] } ]; diff --git a/src/components/ui/MultiSelectAutocomplete.tsx b/src/components/ui/MultiSelectAutocomplete.tsx index 6e83e33..b3297b8 100644 --- a/src/components/ui/MultiSelectAutocomplete.tsx +++ b/src/components/ui/MultiSelectAutocomplete.tsx @@ -16,6 +16,10 @@ interface MultiSelectAutocompleteProps { error?: string; isLoading?: boolean; disabled?: boolean; + onSearchChange?: (query: string) => void; + onLoadMore?: () => void; + hasMore?: boolean; + loadingMore?: boolean; } export const MultiSelectAutocomplete: React.FC = ({ @@ -27,17 +31,25 @@ export const MultiSelectAutocomplete: React.FC = ( error, isLoading = false, disabled = false, + onSearchChange, + onLoadMore, + hasMore = false, + loadingMore = false, }) => { const [isOpen, setIsOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const dropdownRef = useRef(null); const inputRef = useRef(null); + const listRef = useRef(null); const filteredOptions = options.filter(option => option.title.toLowerCase().includes(searchTerm.toLowerCase()) || (option.description && option.description.toLowerCase().includes(searchTerm.toLowerCase())) ); + // If parent provides onSearchChange, assume server-side filtering and use options as-is + const displayedOptions = onSearchChange ? options : filteredOptions; + const selectedOptions = options.filter(option => selectedValues.includes(option.id)); useEffect(() => { @@ -100,7 +112,7 @@ export const MultiSelectAutocomplete: React.FC = ( key={option.id} className="inline-flex items-center gap-1 px-2 py-1 bg-primary-100 dark:bg-primary-800 text-primary-800 dark:text-primary-100 text-xs rounded-md" > - {option.title} + {option.title || option.description || `#${option.id}`}