import SectionContainer from '../../components/SectionContainer'; import { useEffect, useMemo, useState } from 'react'; import { DataGrid } from '@mui/x-data-grid'; import { Typography, Button, Dialog, DialogTitle, DialogContent, IconButton, Box, FormControlLabel, Switch, Tooltip } from '@mui/material'; import EditRoundedIcon from '@mui/icons-material/EditRounded'; import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; import AddRoundedIcon from '@mui/icons-material/AddRounded'; import AddOrEditProductCollectionForm from './AddOrEditProductCollectionForm'; import FurnitureVariantApi from '../../api/furnitureVariantApi'; import CategoriesApi from '../../api/CategoriesApi'; import TagTypeApi from '../../api/TagTypeApi'; import { useAuth } from '../../context/AuthContext'; import useApiToast from '../../hooks/useApiToast'; const parsePrice = (p) => { if (p == null) return 0; if (typeof p === 'number') return p; if (typeof p === 'string') return Number(p) || 0; if (typeof p === 'object' && p.$numberDecimal) return Number(p.$numberDecimal) || 0; return 0; }; const TYPE_NAMES = { category: 'Furniture category', provider: 'Provider', color: 'Color', line: 'Line', currency: 'Currency', material: 'Material', legs: 'Legs', origin: 'Origin', }; export default function ProductCollections() { const { user } = useAuth(); const token = user?.thalosToken || localStorage.getItem('thalosToken'); const api = useMemo(() => new FurnitureVariantApi(token), [token]); const tagTypeApi = useMemo(() => new TagTypeApi(token), [token]); const categoriesApi = useMemo(() => new CategoriesApi(token), [token]); const toast = useApiToast(); const [rows, setRows] = useState([]); const [rawRows, setRawRows] = useState([]); const [open, setOpen] = useState(false); const [editRow, setEditRow] = useState(null); const [showInactive, setShowInactive] = useState(false); const [loading, setLoading] = useState(true); // Tags const [loadingTags, setLoadingTags] = useState(true); const [typeMap, setTypeMap] = useState({}); const [byType, setByType] = useState({ [TYPE_NAMES.category]: [], [TYPE_NAMES.provider]: [], [TYPE_NAMES.color]: [], [TYPE_NAMES.line]: [], [TYPE_NAMES.currency]: [], [TYPE_NAMES.material]: [], [TYPE_NAMES.legs]: [], [TYPE_NAMES.origin]: [], }); const buildLabelResolver = (typeName) => { const list = byType[typeName] || []; return (value) => { if (!value && value !== 0) return '—'; if (list.some(t => t.tagName === value)) return value; // ya es tagName const found = list.find(t => t.id === value || t._id === value || t._id?.$oid === value || t.slug === value ); return found?.tagName || String(value); }; }; const labelCategory = useMemo(() => buildLabelResolver(TYPE_NAMES.category), [byType]); const labelProvider = useMemo(() => buildLabelResolver(TYPE_NAMES.provider), [byType]); const labelColor = useMemo(() => buildLabelResolver(TYPE_NAMES.color), [byType]); const labelLine = useMemo(() => buildLabelResolver(TYPE_NAMES.line), [byType]); const labelCurrency = useMemo(() => buildLabelResolver(TYPE_NAMES.currency), [byType]); const labelMaterial = useMemo(() => buildLabelResolver(TYPE_NAMES.material), [byType]); const labelLegs = useMemo(() => buildLabelResolver(TYPE_NAMES.legs), [byType]); const labelOrigin = useMemo(() => buildLabelResolver(TYPE_NAMES.origin), [byType]); // Cargar TagTypes + Tags useEffect(() => { let mounted = true; (async () => { try { const [types, tags] = await Promise.all([tagTypeApi.getAll(), categoriesApi.getAll()]); const tmap = {}; types?.forEach(t => { if (!t?.typeName || !t?._id) return; tmap[t.typeName] = t._id; }); const group = (tname) => { const tid = tmap[tname]; if (!tid) return []; return (tags || []).filter(tag => tag?.typeId === tid); }; if (mounted) { setTypeMap(tmap); setByType({ [TYPE_NAMES.category]: group(TYPE_NAMES.category), [TYPE_NAMES.provider]: group(TYPE_NAMES.provider), [TYPE_NAMES.color]: group(TYPE_NAMES.color), [TYPE_NAMES.line]: group(TYPE_NAMES.line), [TYPE_NAMES.currency]: group(TYPE_NAMES.currency), [TYPE_NAMES.material]: group(TYPE_NAMES.material), [TYPE_NAMES.legs]: group(TYPE_NAMES.legs), [TYPE_NAMES.origin]: group(TYPE_NAMES.origin), }); } } catch (e) { console.error('Failed loading TagTypes/Tags', e); } finally { if (mounted) setLoadingTags(false); } })(); return () => { mounted = false; }; }, [tagTypeApi, categoriesApi]); // Cargar variants const load = async () => { try { setLoading(true); const data = await api.getAllVariants(); const normalized = (data || []).map((r, idx) => ({ id: r.id || r._id || `row-${idx}`, _Id: r._id || r._Id || '', modelId: r.modelId ?? '', name: r.name ?? '', categoryId: r.categoryId ?? '', providerId: r.providerId ?? '', color: r.color ?? '', line: r.line ?? '', stock: Number(r.stock ?? 0), price: parsePrice(r.price), currency: r.currency ?? 'USD', attributes: { material: r?.attributes?.material ?? '', legs: r?.attributes?.legs ?? '', origin: r?.attributes?.origin ?? '', }, status: r.status ?? 'Active', createdAt: r.createdAt ?? null, createdBy: r.createdBy ?? null, })); setRawRows(normalized); setRows(normalized.filter(r => showInactive ? true : r.status !== 'Inactive')); } catch (err) { console.error(err); toast.error(err?.message || 'Error loading variants'); } finally { setLoading(false); } }; useEffect(() => { load(); /* eslint-disable-next-line */ }, []); useEffect(() => { setRows(rawRows.filter(r => showInactive ? true : r.status !== 'Inactive')); }, [showInactive, rawRows]); const columns = [ { field: 'modelId', headerName: 'Model Id', width: 220 }, { field: 'name', headerName: 'Name', width: 200 }, { field: 'categoryId', headerName: 'Category', width: 170, valueGetter: (p) => labelCategory(p?.row?.categoryId) }, { field: 'providerId', headerName: 'Provider', width: 170, valueGetter: (p) => labelProvider(p?.row?.providerId) }, { field: 'color', headerName: 'Color', width: 130, valueGetter: (p) => labelColor(p?.row?.color) }, { field: 'line', headerName: 'Line', width: 130, valueGetter: (p) => labelLine(p?.row?.line) }, { field: 'price', headerName: 'Price', width: 130, type: 'number', valueGetter: (p) => parsePrice(p?.row?.price), renderCell: (p) => { const currency = labelCurrency(p?.row?.currency || 'USD'); const val = parsePrice(p?.row?.price); try { return new Intl.NumberFormat(undefined, { style: 'currency', currency: currency || 'USD' }).format(val); } catch { return `${currency} ${val.toFixed(2)}`; } } }, { field: 'currency', headerName: 'Currency', width: 120, valueGetter: (p) => labelCurrency(p?.row?.currency) }, { field: 'stock', headerName: 'Stock', width: 100, type: 'number', valueGetter: (p) => Number(p?.row?.stock ?? 0) }, { field: 'attributes.material', headerName: 'Material', width: 150, valueGetter: (p) => labelMaterial(p?.row?.attributes?.material) }, { field: 'attributes.legs', headerName: 'Legs', width: 140, valueGetter: (p) => labelLegs(p?.row?.attributes?.legs) }, { field: 'attributes.origin', headerName: 'Origin', width: 150, valueGetter: (p) => labelOrigin(p?.row?.attributes?.origin) }, { field: 'status', headerName: 'Status', width: 120 }, { field: 'actions', headerName: '', sortable: false, width: 110, renderCell: (p) => ( { setEditRow(p.row); setOpen(true); }}> { try { const updated = { ...p.row, status: p.row.status === 'Active' ? 'Inactive' : 'Active' }; await api.updateVariant(updated); setRawRows(prev => prev.map(r => r.id === p.row.id ? updated : r)); } catch (err) { console.error(err); toast.error(err?.message || 'Error updating status'); } }} > ) }, ]; return ( Furniture Variants setShowInactive(v)} />} label="Show Inactive" /> 'auto'} sx={{ '& .MuiDataGrid-cell': { display: 'flex', alignItems: 'center' }, '& .MuiDataGrid-columnHeader': { display: 'flex', alignItems: 'center' }, }} /> setOpen(false)} maxWidth="md" fullWidth> {editRow ? 'Edit Product Collection' : 'Add Product Collection'} { setOpen(false); if (editRow) { setRawRows(prev => prev.map(r => (r.id === editRow.id ? { ...saved } : r))); } else { setRawRows(prev => [{ ...saved, id: saved.id || saved._id || `row-${Date.now()}` }, ...prev]); } }} onCancel={() => setOpen(false)} /> ); }