solved merge conflicts
This commit is contained in:
@@ -7,7 +7,6 @@ export type BadgeVariant =
|
||||
| "draft"
|
||||
| "inactive"
|
||||
| "disabled"
|
||||
| "inactive"
|
||||
| "published"
|
||||
| "pending"
|
||||
| "archived"
|
||||
@@ -25,7 +24,6 @@ const BADGE_STYLES: Record<BadgeVariant, string> = {
|
||||
draft: "bg-slate-100 text-slate-700 border border-slate-200",
|
||||
inactive: "bg-zinc-100 text-zinc-500 border border-zinc-200",
|
||||
disabled: "bg-gray-100 text-gray-500 border border-gray-200",
|
||||
inactive: "bg-gray-100 text-gray-500 border border-gray-200",
|
||||
published: "bg-primary-light text-primary-dark border border-primary/20",
|
||||
pending: "bg-amber-100 text-amber-700 border border-amber-200",
|
||||
archived: "bg-slate-100 text-slate-500 border border-slate-200",
|
||||
@@ -44,7 +42,6 @@ const DOT_COLORS: Record<BadgeVariant, string> = {
|
||||
draft: "bg-slate-400",
|
||||
inactive: "bg-zinc-400",
|
||||
disabled: "bg-gray-400",
|
||||
inactive: "bg-gray-400",
|
||||
published: "bg-primary",
|
||||
pending: "bg-amber-500",
|
||||
archived: "bg-slate-400",
|
||||
|
||||
@@ -9,8 +9,6 @@ export interface AssetFamily {
|
||||
createdAt: string;
|
||||
assetTypes?: AssetType[];
|
||||
assetTypeIds?: string[];
|
||||
assetTypes?: any[];
|
||||
assetTypeIds?: string[];
|
||||
}
|
||||
export type AssetFamilyCreateRequest = Omit<AssetFamily, 'id' | 'createdAt'>;
|
||||
export type AssetFamilyUpdateRequest = Partial<AssetFamilyCreateRequest>;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useChannel } from '../../channels/hook/useChannel';
|
||||
import { useWorkflow } from '../../workflow/hook/useWorkflow';
|
||||
import { StageTimeline } from '../../workflow/components/StageTimeline';
|
||||
import { Search, X, Layers, Activity } from 'lucide-react';
|
||||
import { useUnit } from '../../units/hook/useUnit';
|
||||
import { useAttributeSet } from '../../attribute-sets/hook/useAttributeSet';
|
||||
import { useAssetFamily } from '../../asset-families/hook/useAssetFamily';
|
||||
import { useBrand } from '../../brands/hook/useBrand';
|
||||
@@ -236,6 +237,7 @@ export default function NewFamily() {
|
||||
const { items: attributeSetsList, fetchItems: fetchAttributeSets, loading: setsLoading } = useAttributeSet();
|
||||
|
||||
const { brands, fetchBrands, loading: brandsLoading } = useBrand();
|
||||
const { units, fetchUnits, loading: unitsLoading } = useUnit();
|
||||
|
||||
const [familyLoading, setFamilyLoading] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState('basic');
|
||||
@@ -289,6 +291,7 @@ export default function NewFamily() {
|
||||
attributes: [] as string[],
|
||||
variantAxes: [] as string[],
|
||||
allowedBrands: [] as string[],
|
||||
allowedUnits: [] as string[],
|
||||
channels: [] as string[],
|
||||
assetRequirements: [] as string[],
|
||||
completenessRules: {
|
||||
@@ -331,7 +334,8 @@ export default function NewFamily() {
|
||||
fetchWorkflows();
|
||||
fetchAttributeSets();
|
||||
fetchBrands();
|
||||
}, [fetchCategories, fetchAttributes, fetchChannels, fetchAssetFamilies, fetchWorkflows, fetchAttributeSets, fetchBrands]);
|
||||
fetchUnits();
|
||||
}, [fetchCategories, fetchAttributes, fetchChannels, fetchAssetFamilies, fetchWorkflows, fetchAttributeSets, fetchBrands, fetchUnits]);
|
||||
|
||||
// Load family details in Edit mode
|
||||
useEffect(() => {
|
||||
@@ -354,6 +358,7 @@ export default function NewFamily() {
|
||||
channels: Array.isArray(data.channels) ? data.channels.map((c: any) => c.channelCode || c.channel_code || c) : [],
|
||||
assetRequirements: Array.isArray(data.assetRequirements) ? data.assetRequirements.map((r: any) => r.id || r) : [],
|
||||
allowedBrands: Array.isArray(data.allowedBrands) ? data.allowedBrands.map((b: any) => b.id || b) : [],
|
||||
allowedUnits: Array.isArray(data.allowedUnits) ? data.allowedUnits.map((u: any) => u.id || u) : [],
|
||||
completenessRules: data.completenessRules || {
|
||||
required_attributes: 40,
|
||||
at_least_one_image: 20,
|
||||
@@ -386,7 +391,7 @@ export default function NewFamily() {
|
||||
}, [formik.values.attributeSetId, loadBlueprintPreview]);
|
||||
|
||||
const activeIndex = TABS.findIndex(t => t.id === activeTab);
|
||||
const isLoading = familyLoading || categoriesLoading || attributesLoading || channelsLoading || assetFamiliesLoading || workflowsLoading || setsLoading || brandsLoading;
|
||||
const isLoading = familyLoading || categoriesLoading || attributesLoading || channelsLoading || assetFamiliesLoading || workflowsLoading || setsLoading || brandsLoading || unitsLoading;
|
||||
|
||||
// Selected attributes list for Step 3 Axis Filtering
|
||||
const selectedAttributesList = attributes.filter(attr =>
|
||||
@@ -682,8 +687,16 @@ export default function NewFamily() {
|
||||
|
||||
{/* Row 3 — Description last */}
|
||||
<div className="col-span-2">
|
||||
<label className={labelClass}>Description</label>
|
||||
<textarea name="description" value={formik.values.description} onChange={formik.handleChange} placeholder="Describe this product family..." rows={4} className={`${inputClass} resize-y`} style={{ minHeight: '120px' }} />
|
||||
<label className={labelClass}>Allowed Units <span className="text-red-400">*</span></label>
|
||||
<SearchableMultiSelect
|
||||
title="Allowed Units"
|
||||
items={units}
|
||||
selectedIds={formik.values.allowedUnits}
|
||||
onChange={(ids) => formik.setFieldValue('allowedUnits', ids)}
|
||||
searchPlaceholder="Search units by name, code or symbol..."
|
||||
getDisplayInfo={(u: any) => ({ id: u.id, name: u.name, code: u.code, badgeText: u.symbol, status: u.status })}
|
||||
error={formik.touched.allowedUnits && formik.errors.allowedUnits ? String(formik.errors.allowedUnits) : undefined}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -9,17 +9,16 @@ import { useBrand } from '../../brands/hook/useBrand';
|
||||
import { useUnit } from '../../units/hook/useUnit';
|
||||
import { useProductFamilyConfiguration } from '../hook/useProductFamilyConfiguration';
|
||||
import { VariantsTab } from '../components/variants/VariantsTab';
|
||||
import { ProductGeneralSection } from '../components/ProductGeneralSection';
|
||||
import { ProductCategorySection } from '../components/ProductCategorySection';
|
||||
import { ProductBrandSection } from '../components/ProductBrandSection';
|
||||
import { DynamicAttributesSection } from '../components/DynamicAttributesSection';
|
||||
import { StageTimeline } from '../../workflow/components/StageTimeline';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Box, LayoutGrid, Tags, Globe, Eye, Save, Send, Copy, Image as ImageIcon, Info, FolderTree, AlertCircle, RefreshCw, Check, Plus } from 'lucide-react';
|
||||
import { notify } from '../../../services/toast';
|
||||
import { Box, LayoutGrid, Tags, Globe, Eye, Save, Send, Copy, Image as ImageIcon, Info, FolderTree, Check, Plus, CheckCircle2 } from 'lucide-react';
|
||||
import { PageWrapper } from '../../../components/layouts/PageWrapper';
|
||||
import { ProtectedRoute } from '../../../components/layouts/ProtectedRoute';
|
||||
import { Breadcrumb } from '../../../components/layouts/Breadcrumb';
|
||||
import { FamilyCard } from '../../../components/customs/FamilyCard';
|
||||
import { Radio, RadioGroup } from '../../../components/customs/Radio';
|
||||
import { Select } from '../../../components/customs/Select';
|
||||
|
||||
const TABS = [
|
||||
{ id: 'general', label: 'General', icon: Box, step: 1 },
|
||||
@@ -77,9 +76,6 @@ export default function NewProduct() {
|
||||
groups: attributeGroups,
|
||||
attributes: attributesList,
|
||||
workflow: inheritedWorkflow,
|
||||
loading: configLoading,
|
||||
error: configError,
|
||||
retry: configRetry
|
||||
} = useProductFamilyConfiguration(selectedFamily || undefined);
|
||||
|
||||
const selectedFamilyObj = useMemo(() => {
|
||||
@@ -185,15 +181,15 @@ export default function NewProduct() {
|
||||
try {
|
||||
if (isEdit && id) {
|
||||
await productService.update(id, values as any);
|
||||
toast.success("Product updated successfully!");
|
||||
notify.success("Product updated successfully!");
|
||||
navigate('/products');
|
||||
} else {
|
||||
const created = await productService.create({ ...values, family_id: selectedFamily } as any);
|
||||
toast.success("Product created successfully!");
|
||||
notify.success("Product created successfully!");
|
||||
navigate(`/products/${created.id}/edit`);
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast.error(err.message || 'Failed to save product');
|
||||
notify.error(err);
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
@@ -247,7 +243,7 @@ export default function NewProduct() {
|
||||
setSelectedFamily(productData.family_id || (productData.family ? productData.family.id : null));
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast.error(err.message || 'Failed to load product details');
|
||||
notify.error(err);
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
@@ -551,9 +547,9 @@ export default function NewProduct() {
|
||||
metadata: updatedMetadata
|
||||
} as any);
|
||||
formik.setFieldValue('metadata', updatedMetadata);
|
||||
toast.success(`Product advanced to stage: ${nextStage.name}`);
|
||||
notify.success(`Product advanced to stage: ${nextStage.name}`);
|
||||
} catch (err: any) {
|
||||
toast.error(err.message || 'Failed to advance stage');
|
||||
notify.error(err);
|
||||
}
|
||||
}}
|
||||
className="px-3 py-1.5 bg-primary hover:bg-primary-hover text-white text-[11px] font-bold rounded-lg transition-all shadow-xs"
|
||||
@@ -671,7 +667,7 @@ export default function NewProduct() {
|
||||
onChange={formik.handleChange}
|
||||
>
|
||||
<option value="">Select brand</option>
|
||||
{selectedFamilyObj?.allowedBrands?.map((b: any) => (
|
||||
{activeAllowedBrandsList?.map((b: any) => (
|
||||
<option key={b.id} value={b.id}>{b.name}</option>
|
||||
))}
|
||||
</Select>
|
||||
@@ -684,7 +680,7 @@ export default function NewProduct() {
|
||||
onChange={formik.handleChange}
|
||||
>
|
||||
<option value="">Select unit</option>
|
||||
{selectedFamilyObj?.allowedUnits?.map((u: any) => (
|
||||
{activeAllowedUnitsList?.map((u: any) => (
|
||||
<option key={u.id} value={u.id}>
|
||||
{u.name} {u.symbol ? `(${u.symbol})` : `(${u.code})`}
|
||||
</option>
|
||||
|
||||
@@ -13,8 +13,8 @@ export const SystemMessages = {
|
||||
timeout: (): BusinessNotification =>
|
||||
createError("Request Timed Out", "The server took too long to respond. Please try again."),
|
||||
|
||||
internalServerError: (httpStatus?: number): BusinessNotification =>
|
||||
createError("Server Error", "An internal server error occurred. Please try again later.", { httpStatus }),
|
||||
internalServerError: (msg?: string, httpStatus?: number): BusinessNotification =>
|
||||
createError("Server Error", msg ?? "An internal server error occurred. Please try again later.", { httpStatus }),
|
||||
|
||||
unauthorized: (httpStatus = 401): BusinessNotification =>
|
||||
createWarning("Unauthorized", "Your session has expired. Please log in again.", { httpStatus }),
|
||||
|
||||
@@ -77,7 +77,7 @@ export function normalizeError(error: unknown): BusinessNotification {
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
return SystemMessages.internalServerError(status);
|
||||
return SystemMessages.internalServerError(backendMessage, status);
|
||||
default:
|
||||
return SystemMessages.unknownError(backendMessage, status);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user