From ba7911d04b1dad4246e1b95163b243f561e7d4fc Mon Sep 17 00:00:00 2001 From: ferdiansyah783 Date: Wed, 13 Aug 2025 23:53:03 +0700 Subject: [PATCH 1/2] fix: product and inventory --- .../ecommerce/products/[id]/edit/page.tsx | 10 +- .../(private)/dashboards/overview/page.tsx | 18 +-- .../(private)/dashboards/profit-loss/page.tsx | 25 +++- .../layout/vertical/VerticalMenu.tsx | 2 +- src/data/dictionaries/ar.json | 2 +- src/data/dictionaries/en.json | 2 +- src/data/dictionaries/fr.json | 2 +- src/redux-store/slices/product.ts | 1 - src/redux-store/slices/productRecipe.ts | 59 ++++++++- src/services/mutations/productRecipes.ts | 17 ++- src/services/queries/products.ts | 5 +- src/types/services/inventory.ts | 1 + src/types/services/product.ts | 1 - src/views/Login.tsx | 23 +++- src/views/NotFound.tsx | 9 +- .../orders/details/BillingAddressCard.tsx | 8 +- .../orders/details/OrderDetailsCard.tsx | 4 +- .../apps/ecommerce/orders/details/index.tsx | 6 - .../apps/ecommerce/orders/list/index.tsx | 4 +- .../products/add/ProductAddHeader.tsx | 2 +- .../products/add/ProductInformation.tsx | 35 +++-- .../products/add/ProductOrganize.tsx | 63 +++++---- .../products/detail/AddRecipeDrawer.tsx | 55 +++++--- .../products/detail/ProductDetail.tsx | 121 ++++++++++++++++-- .../products/list/ProductListTable.tsx | 12 +- .../ecommerce/products/list/TableFilters.tsx | 98 +++++++------- .../stock/adjustment/StockListTable.tsx | 42 ++---- .../ecommerce/stock/list/StockListTable.tsx | 12 +- 28 files changed, 435 insertions(+), 204 deletions(-) diff --git a/src/app/[lang]/(dashboard)/(private)/apps/ecommerce/products/[id]/edit/page.tsx b/src/app/[lang]/(dashboard)/(private)/apps/ecommerce/products/[id]/edit/page.tsx index 6392314..ad2d2f4 100644 --- a/src/app/[lang]/(dashboard)/(private)/apps/ecommerce/products/[id]/edit/page.tsx +++ b/src/app/[lang]/(dashboard)/(private)/apps/ecommerce/products/[id]/edit/page.tsx @@ -3,12 +3,11 @@ import Grid from '@mui/material/Grid2' // Component Imports import ProductAddHeader from '@views/apps/ecommerce/products/add/ProductAddHeader' -import ProductInformation from '@views/apps/ecommerce/products/add/ProductInformation' import ProductImage from '@views/apps/ecommerce/products/add/ProductImage' -import ProductVariants from '@views/apps/ecommerce/products/add/ProductVariants' -import ProductInventory from '@views/apps/ecommerce/products/add/ProductInventory' -import ProductPricing from '@views/apps/ecommerce/products/add/ProductPricing' +import ProductInformation from '@views/apps/ecommerce/products/add/ProductInformation' import ProductOrganize from '@views/apps/ecommerce/products/add/ProductOrganize' +import ProductPricing from '@views/apps/ecommerce/products/add/ProductPricing' +import ProductVariants from '@views/apps/ecommerce/products/add/ProductVariants' const eCommerceProductsEdit = () => { return ( @@ -27,9 +26,6 @@ const eCommerceProductsEdit = () => { - - - diff --git a/src/app/[lang]/(dashboard)/(private)/dashboards/overview/page.tsx b/src/app/[lang]/(dashboard)/(private)/dashboards/overview/page.tsx index f8d347f..e251c66 100644 --- a/src/app/[lang]/(dashboard)/(private)/dashboards/overview/page.tsx +++ b/src/app/[lang]/(dashboard)/(private)/dashboards/overview/page.tsx @@ -14,12 +14,12 @@ const DashboardOverview = () => { if (isLoading) return - const MetricCard = ({ iconClass, title, value, subtitle, bgColor = 'bg-blue-500' }: any) => ( + const MetricCard = ({ iconClass, title, value, subtitle, bgColor = 'bg-blue-500', isCurrency = false }: any) => (

{title}

-

{value}

+

{isCurrency ? 'Rp ' + value : value}

{subtitle &&

{subtitle}

}
@@ -52,12 +52,6 @@ const DashboardOverview = () => { {/* Overview Metrics */}
- { subtitle={`${salesData.overview.voided_orders} voided, ${salesData.overview.refunded_orders} refunded`} bgColor='bg-blue-500' /> + { function formatMetricName(metric: string): string { const nameMap: { [key: string]: string } = { revenue: 'Revenue', - net_profit: 'Net Profit', + net_profit: 'Net Profit' } return nameMap[metric] || metric.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) @@ -61,15 +61,25 @@ const DashboardProfitloss = () => { ] } - const MetricCard = ({ iconClass, title, value, subtitle, bgColor = 'bg-blue-500', isNegative = false }: any) => ( + const MetricCard = ({ + iconClass, + title, + value, + subtitle, + bgColor = 'bg-blue-500', + isNegative = false, + isCurrency = false + }: any) => (

{title}

-

{value}

+

+ {isCurrency ? 'Rp ' + value : value} +

{subtitle &&

{subtitle}

}
-
+
@@ -95,12 +105,14 @@ const DashboardProfitloss = () => { title='Total Revenue' value={formatShortCurrency(profitData.summary.total_revenue)} bgColor='bg-green-500' + isCurrency={true} /> { subtitle={`Margin: ${formatPercentage(profitData.summary.gross_profit_margin)}`} bgColor='bg-blue-500' isNegative={profitData.summary.gross_profit < 0} + isCurrency={true} /> {

Net Profit

- {formatShortCurrency(profitData.summary.net_profit)} + Rp {formatShortCurrency(profitData.summary.net_profit)}

Margin: {formatPercentage(profitData.summary.net_profit_margin)}

@@ -144,7 +157,7 @@ const DashboardProfitloss = () => {

Tax & Discount

- {formatShortCurrency(profitData.summary.total_tax + profitData.summary.total_discount)} + Rp {formatShortCurrency(profitData.summary.total_tax + profitData.summary.total_discount)}

Tax: {formatShortCurrency(profitData.summary.total_tax)} | Discount:{' '} diff --git a/src/components/layout/vertical/VerticalMenu.tsx b/src/components/layout/vertical/VerticalMenu.tsx index c47bc40..5002fe6 100644 --- a/src/components/layout/vertical/VerticalMenu.tsx +++ b/src/components/layout/vertical/VerticalMenu.tsx @@ -90,7 +90,7 @@ const VerticalMenu = ({ dictionary, scrollMenu }: Props) => { {dictionary['navigation'].paymentMethods} - }> + }> {/* {dictionary['navigation'].dashboard} */} {dictionary['navigation'].list} diff --git a/src/data/dictionaries/ar.json b/src/data/dictionaries/ar.json index c99d8e2..b005a82 100644 --- a/src/data/dictionaries/ar.json +++ b/src/data/dictionaries/ar.json @@ -2,7 +2,7 @@ "navigation": { "dashboards": "لوحات القيادة", "analytics": "تحليلات", - "eCommerce": "التجارة الإلكترونية", + "eCommerce": "تجزئة الكترونية", "stock": "المخزون", "academy": "أكاديمية", "logistics": "اللوجستية", diff --git a/src/data/dictionaries/en.json b/src/data/dictionaries/en.json index 18771ed..47b8b99 100644 --- a/src/data/dictionaries/en.json +++ b/src/data/dictionaries/en.json @@ -2,7 +2,7 @@ "navigation": { "dashboards": "Dashboards", "analytics": "Analytics", - "eCommerce": "eCommerce", + "eCommerce": "Inventory", "stock": "Stock", "academy": "Academy", "logistics": "Logistics", diff --git a/src/data/dictionaries/fr.json b/src/data/dictionaries/fr.json index 3215e14..6f8786a 100644 --- a/src/data/dictionaries/fr.json +++ b/src/data/dictionaries/fr.json @@ -2,7 +2,7 @@ "navigation": { "dashboards": "Tableaux de bord", "analytics": "Analytique", - "eCommerce": "commerce électronique", + "eCommerce": "Inventaire", "stock": "Stock", "academy": "Académie", "logistics": "Logistique", diff --git a/src/redux-store/slices/product.ts b/src/redux-store/slices/product.ts index 9b2ec59..af9df26 100644 --- a/src/redux-store/slices/product.ts +++ b/src/redux-store/slices/product.ts @@ -13,7 +13,6 @@ const initialState: { productRequest: ProductRequest } = { sku: '', name: '', description: '', - barcode: '', price: 0, cost: 0, printer_type: '', diff --git a/src/redux-store/slices/productRecipe.ts b/src/redux-store/slices/productRecipe.ts index 5dc1c7a..de89da4 100644 --- a/src/redux-store/slices/productRecipe.ts +++ b/src/redux-store/slices/productRecipe.ts @@ -1,20 +1,71 @@ // Third-party Imports import type { PayloadAction } from '@reduxjs/toolkit' import { createSlice } from '@reduxjs/toolkit' +import { ProductRecipe } from '../../types/services/productRecipe' // Type Imports // Data Imports -const initialState: { currentProductRecipe: any } = { - currentProductRecipe: {} +const initialState: { currentVariant: any, currentProductRecipe: ProductRecipe } = { + currentVariant: {}, + currentProductRecipe: { + id: '', + organization_id: '', + outlet_id: null, + product_id: '', + variant_id: null, + ingredient_id: '', + quantity: 0, + created_at: '', + updated_at: '', + product: { + ID: '', + OrganizationID: '', + CategoryID: '', + SKU: '', + Name: '', + Description: null, + Price: 0, + Cost: 0, + BusinessType: '', + ImageURL: '', + PrinterType: '', + UnitID: null, + HasIngredients: false, + Metadata: {}, + IsActive: false, + CreatedAt: '', + UpdatedAt: '' + }, + ingredient: { + id: '', + organization_id: '', + outlet_id: null, + name: '', + unit_id: '', + cost: 0, + stock: 0, + is_semi_finished: false, + is_active: false, + metadata: {}, + created_at: '', + updated_at: '' + } + } } export const productRecipeSlice = createSlice({ name: 'productRecipe', initialState, reducers: { - setProductRecipe: (state, action: PayloadAction) => { + setProductVariant: (state, action: PayloadAction) => { + state.currentVariant = action.payload + }, + resetProductVariant: state => { + state.currentVariant = initialState.currentVariant + }, + setProductRecipe: (state, action: PayloadAction) => { state.currentProductRecipe = action.payload }, resetProductRecipe: state => { @@ -23,6 +74,6 @@ export const productRecipeSlice = createSlice({ } }) -export const { setProductRecipe, resetProductRecipe } = productRecipeSlice.actions +export const { setProductVariant, resetProductVariant, setProductRecipe, resetProductRecipe } = productRecipeSlice.actions export default productRecipeSlice.reducer diff --git a/src/services/mutations/productRecipes.ts b/src/services/mutations/productRecipes.ts index 4ccf3de..fbd428d 100644 --- a/src/services/mutations/productRecipes.ts +++ b/src/services/mutations/productRecipes.ts @@ -38,8 +38,23 @@ export const useProductRecipesMutation = () => { } }) + const deleteProductRecipe = useMutation({ + mutationFn: async (id: string) => { + const response = await api.delete(`/product-recipes/${id}`) + return response.data + }, + onSuccess: () => { + toast.success('Product Recipe deleted successfully!') + queryClient.invalidateQueries({ queryKey: ['product-recipes'] }) + }, + onError: (error: any) => { + toast.error(error.response?.data?.errors?.[0]?.cause || 'Delete failed') + } + }) + return { createProductRecipe, - updateProductRecipe + updateProductRecipe, + deleteProductRecipe } } diff --git a/src/services/queries/products.ts b/src/services/queries/products.ts index c2cf7c8..93ea235 100644 --- a/src/services/queries/products.ts +++ b/src/services/queries/products.ts @@ -1,15 +1,14 @@ import { useQuery } from '@tanstack/react-query' import { Product, Products } from '../../types/services/product' import { api } from '../api' -import { ProductRecipe } from '../../types/services/productRecipe' -interface ProductsQueryParams { +export interface ProductsQueryParams { page?: number limit?: number search?: string // Add other filter parameters as needed category_id?: string - is_active?: boolean + is_active?: boolean | string } export function useProducts(params: ProductsQueryParams = {}) { diff --git a/src/types/services/inventory.ts b/src/types/services/inventory.ts index 4f5f0d8..7d22234 100644 --- a/src/types/services/inventory.ts +++ b/src/types/services/inventory.ts @@ -13,6 +13,7 @@ export interface Inventory { quantity: number reorder_level: number is_low_stock: boolean + product: any updated_at: string // ISO 8601 timestamp } diff --git a/src/types/services/product.ts b/src/types/services/product.ts index bb7002d..0cc3f8e 100644 --- a/src/types/services/product.ts +++ b/src/types/services/product.ts @@ -47,7 +47,6 @@ export type ProductRequest = { sku: string name: string description: string - barcode: string price: number cost: number printer_type: string diff --git a/src/views/Login.tsx b/src/views/Login.tsx index 9be34de..75bbe6e 100644 --- a/src/views/Login.tsx +++ b/src/views/Login.tsx @@ -133,11 +133,22 @@ const Login = ({ mode }: { mode: SystemMode }) => { const handleClickShowPassword = () => setIsPasswordShown(show => !show) const onSubmit: SubmitHandler = async (data: FormData) => { - login.mutate(data) + login.mutate(data, { + onSuccess: (data: any) => { + if (data?.user?.role === 'admin') { + const redirectURL = searchParams.get('redirectTo') ?? '/dashboards/overview' - const redirectURL = searchParams.get('redirectTo') ?? '/dashboards/overview' + router.replace(getLocalizedUrl(redirectURL, locale as Locale)) + } else { + const redirectURL = searchParams.get('redirectTo') ?? '/sa/organizations/list' - router.replace(getLocalizedUrl(redirectURL, locale as Locale)) + router.replace(getLocalizedUrl(redirectURL, locale as Locale)) + } + }, + onError: (error: any) => { + setErrorState(error.response.data) + } + }) } return ( @@ -243,7 +254,11 @@ const Login = ({ mode }: { mode: SystemMode }) => {

New on our platform? - + Create an account
diff --git a/src/views/NotFound.tsx b/src/views/NotFound.tsx index b335aa7..e34e573 100644 --- a/src/views/NotFound.tsx +++ b/src/views/NotFound.tsx @@ -17,6 +17,7 @@ import type { SystemMode } from '@core/types' // Hook Imports import { useImageVariant } from '@core/hooks/useImageVariant' +import { useAuth } from '../contexts/authContext' // Styled Components const MaskImg = styled('img')({ @@ -29,6 +30,8 @@ const MaskImg = styled('img')({ }) const NotFound = ({ mode }: { mode: SystemMode }) => { + const { currentUser } = useAuth() + // Vars const darkImg = '/images/pages/misc-mask-dark.png' const lightImg = '/images/pages/misc-mask-light.png' @@ -48,7 +51,11 @@ const NotFound = ({ mode }: { mode: SystemMode }) => { Page Not Found ⚠️ we couldn't find the page you are looking for.
- {
- Payment Details ({data.payments.length} {data.payments.length === 1 ? 'Payment' : 'Payments'}) + Payment Details ({data?.payments?.length ?? 0} {data?.payments?.length === 1 ? 'Payment' : 'Payments'})
- {data.payments.map((payment, index) => ( + {data?.payments?.length ? data.payments.map((payment, index) => (
@@ -74,7 +74,9 @@ const BillingAddress = ({ data }: { data: Order }) => {
- ))} + )) : ( + No payments found + )} ) diff --git a/src/views/apps/ecommerce/orders/details/OrderDetailsCard.tsx b/src/views/apps/ecommerce/orders/details/OrderDetailsCard.tsx index 536f496..aeb5dab 100644 --- a/src/views/apps/ecommerce/orders/details/OrderDetailsCard.tsx +++ b/src/views/apps/ecommerce/orders/details/OrderDetailsCard.tsx @@ -276,10 +276,10 @@ const OrderDetailsCard = ({ data }: { data: Order }) => {
- + Total: - + {formatCurrency(data.total_amount)}
diff --git a/src/views/apps/ecommerce/orders/details/index.tsx b/src/views/apps/ecommerce/orders/details/index.tsx index efc8883..e41e612 100644 --- a/src/views/apps/ecommerce/orders/details/index.tsx +++ b/src/views/apps/ecommerce/orders/details/index.tsx @@ -39,9 +39,6 @@ const OrderDetails = () => { - {/* - - */}
@@ -49,9 +46,6 @@ const OrderDetails = () => { - {/* - - */} diff --git a/src/views/apps/ecommerce/orders/list/index.tsx b/src/views/apps/ecommerce/orders/list/index.tsx index da49cea..30385a8 100644 --- a/src/views/apps/ecommerce/orders/list/index.tsx +++ b/src/views/apps/ecommerce/orders/list/index.tsx @@ -12,9 +12,9 @@ import OrderListTable from './OrderListTable' const OrderList = () => { return ( - + {/* - + */} diff --git a/src/views/apps/ecommerce/products/add/ProductAddHeader.tsx b/src/views/apps/ecommerce/products/add/ProductAddHeader.tsx index 2ead91e..38ea2c6 100644 --- a/src/views/apps/ecommerce/products/add/ProductAddHeader.tsx +++ b/src/views/apps/ecommerce/products/add/ProductAddHeader.tsx @@ -55,7 +55,7 @@ const ProductAddHeader = () => { - + {/* */} + + - ))} + )) + ) : ( + + + + No ingredients found for this variant + + + + )} {/* Variant Summary */} - {productRecipe?.length && ( + {productRecipe?.length ? ( @@ -215,6 +265,13 @@ const ProductDetail = () => { + ) : ( + + + + Total Ingredients: + + )} + + - ))} + )) + ) : ( + + + + No ingredients found for this variant + + + + )} {/* Variant Summary */} - {productRecipe?.length && ( + {productRecipe?.length ? ( @@ -357,6 +442,13 @@ const ProductDetail = () => { + ) : ( + + + + Total Ingredients: + + )} diff --git a/src/views/apps/ecommerce/stock/list/StockListTable.tsx b/src/views/apps/ecommerce/stock/list/StockListTable.tsx index 0fd21d8..ecbec50 100644 --- a/src/views/apps/ecommerce/stock/list/StockListTable.tsx +++ b/src/views/apps/ecommerce/stock/list/StockListTable.tsx @@ -107,11 +107,13 @@ const StockListTable = () => { const [openConfirm, setOpenConfirm] = useState(false) const [productId, setProductId] = useState('') const [addInventoryOpen, setAddInventoryOpen] = useState(false) + const [search, setSearch] = useState('') // Fetch products with pagination and search const { data, isLoading, error, isFetching } = useInventories({ page: currentPage, - limit: pageSize + limit: pageSize, + search }) const { mutate: deleteInventory, isPending: isDeleting } = useInventoriesMutation().deleteInventory @@ -162,7 +164,7 @@ const StockListTable = () => { }, columnHelper.accessor('product_id', { header: 'Product', - cell: ({ row }) => {row.original.product_id} + cell: ({ row }) => {row.original.product?.name} }), columnHelper.accessor('is_low_stock', { header: 'Status', @@ -241,8 +243,8 @@ const StockListTable = () => {
console.log(value)} + value={search} + onChange={value => setSearch(value as string)} placeholder='Search Product' className='max-sm:is-full' /> @@ -271,7 +273,7 @@ const StockListTable = () => { onClick={() => setAddInventoryOpen(!addInventoryOpen)} startIcon={} > - Add Inventory + Add Stock
From d34d5a92cd65353c32d1e61a184737ece125a62c Mon Sep 17 00:00:00 2001 From: ferdiansyah783 Date: Wed, 13 Aug 2025 23:58:37 +0700 Subject: [PATCH 2/2] fix: organization input --- .../(guest-only)/organization/page.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/[lang]/(blank-layout-pages)/(guest-only)/organization/page.tsx b/src/app/[lang]/(blank-layout-pages)/(guest-only)/organization/page.tsx index 8821739..362d1e8 100644 --- a/src/app/[lang]/(blank-layout-pages)/(guest-only)/organization/page.tsx +++ b/src/app/[lang]/(blank-layout-pages)/(guest-only)/organization/page.tsx @@ -352,7 +352,7 @@ const CreateOrganization = () => { required label='Organization Name' placeholder='My Company Ltd.' - value={formData.organization_name} + value={formData.organization_name || ''} onChange={handleInputChange('organization_name')} error={!!errors.organization_name} helperText={errors.organization_name} @@ -395,7 +395,7 @@ const CreateOrganization = () => { required label='Admin Name' placeholder='John Doe' - value={formData.admin_name} + value={formData.admin_name || ''} onChange={handleInputChange('admin_name')} error={!!errors.admin_name} helperText={errors.admin_name} @@ -408,7 +408,7 @@ const CreateOrganization = () => { label='Admin Email' placeholder='admin@mycompany.com' type='email' - value={formData.admin_email} + value={formData.admin_email || ''} onChange={handleInputChange('admin_email')} error={!!errors.admin_email} helperText={errors.admin_email} @@ -421,7 +421,7 @@ const CreateOrganization = () => { type='password' label='Admin Password' placeholder='Minimum 6 characters' - value={formData.admin_password} + value={formData.admin_password || ''} onChange={handleInputChange('admin_password')} error={!!errors.admin_password} helperText={errors.admin_password} @@ -443,7 +443,7 @@ const CreateOrganization = () => { required label='Outlet Name' placeholder='Main Store' - value={formData.outlet_name} + value={formData.outlet_name || ''} onChange={handleInputChange('outlet_name')} error={!!errors.outlet_name} helperText={errors.outlet_name}