Compare commits
	
		
			3 Commits
		
	
	
		
			d9bfaba977
			...
			49dead566c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 49dead566c | ||
|   | 2fa6b95012 | ||
|   | f5acde78de | 
| @@ -55,14 +55,14 @@ export default function AddOrEditCategoryForm({ onAdd, initialData, onCancel, ma | |||||||
| const tagLabelById = useMemo(() => { | const tagLabelById = useMemo(() => { | ||||||
|   const map = {}; |   const map = {}; | ||||||
|   for (const t of allTags) { |   for (const t of allTags) { | ||||||
|     const key = t._id || t.id; |     const key = t._id; | ||||||
|     map[key] = t.tagName || t.name || key; |     map[key] = t.tagName || t.name || key; | ||||||
|   } |   } | ||||||
|   return map; |   return map; | ||||||
| }, [allTags]); | }, [allTags]); | ||||||
|  |  | ||||||
|   const [form, setForm] = useState({ |   const [form, setForm] = useState({ | ||||||
|     _Id: '', |     _id: '', | ||||||
|     id: '', |     id: '', | ||||||
|     tenantId: '', |     tenantId: '', | ||||||
|     tagName: '', |     tagName: '', | ||||||
| @@ -107,9 +107,9 @@ const tagLabelById = useMemo(() => { | |||||||
|     // Build a case-insensitive name -> id map |     // Build a case-insensitive name -> id map | ||||||
|     const nameToId = new Map( |     const nameToId = new Map( | ||||||
|       allTags.map(t => { |       allTags.map(t => { | ||||||
|         const id = t._id || t.id; |         const _id = t._id; | ||||||
|         const label = (t.tagName || t.name || '').toLowerCase(); |         const label = (t.tagName || t.name || '').toLowerCase(); | ||||||
|         return [label, id]; |         return [label, _id]; | ||||||
|       }) |       }) | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -126,11 +126,9 @@ const tagLabelById = useMemo(() => { | |||||||
|   // set inicial |   // set inicial | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (initialData) { |     if (initialData) { | ||||||
|       const _Id = initialData._id || initialData._Id || ''; |  | ||||||
|       const id = initialData.id || initialData.Id || _Id || ''; |  | ||||||
|       setForm({ |       setForm({ | ||||||
|         _Id, |         _id: initialData._id, | ||||||
|         id, |         id: initialData.id, | ||||||
|         tenantId: initialData.tenantId || extractTenantId(token) || '', |         tenantId: initialData.tenantId || extractTenantId(token) || '', | ||||||
|         tagName: initialData.tagName || initialData.name || '', |         tagName: initialData.tagName || initialData.name || '', | ||||||
|         typeId: initialData.typeId || '', |         typeId: initialData.typeId || '', | ||||||
| @@ -146,7 +144,7 @@ const tagLabelById = useMemo(() => { | |||||||
|       }); |       }); | ||||||
|     } else { |     } else { | ||||||
|       setForm({ |       setForm({ | ||||||
|         _Id: '', |         _id: '', | ||||||
|         id: '', |         id: '', | ||||||
|         tenantId: extractTenantId(token) || '', |         tenantId: extractTenantId(token) || '', | ||||||
|         tagName: '', |         tagName: '', | ||||||
| @@ -164,7 +162,7 @@ const tagLabelById = useMemo(() => { | |||||||
|     } |     } | ||||||
|   }, [initialData]); |   }, [initialData]); | ||||||
|  |  | ||||||
|   const isEdit = Boolean(form._Id || form.id); |   const isEdit = Boolean(form._id); | ||||||
|   const isAdd = !isEdit; |   const isAdd = !isEdit; | ||||||
|  |  | ||||||
|   const setVal = (name, value) => setForm(p => ({ ...p, [name]: value })); |   const setVal = (name, value) => setForm(p => ({ ...p, [name]: value })); | ||||||
| @@ -172,7 +170,7 @@ const tagLabelById = useMemo(() => { | |||||||
|   const handleChange = (e) => { |   const handleChange = (e) => { | ||||||
|     const { name, value } = e.target; |     const { name, value } = e.target; | ||||||
|     setVal(name, value); |     setVal(name, value); | ||||||
|     if (name === 'tagName' && !form._Id) { |     if (name === 'tagName' && !form._id) { | ||||||
|       setVal('slug', slugify(value)); |       setVal('slug', slugify(value)); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| @@ -187,6 +185,7 @@ const tagLabelById = useMemo(() => { | |||||||
|       if (!tenantId) throw new Error('TenantId not found in token'); |       if (!tenantId) throw new Error('TenantId not found in token'); | ||||||
|  |  | ||||||
|       const base = { |       const base = { | ||||||
|  |         id: form.id?.trim() || undefined, | ||||||
|         tagName: form.tagName.trim(), |         tagName: form.tagName.trim(), | ||||||
|         typeId: form.typeId, |         typeId: form.typeId, | ||||||
|         parentTagId: form.parentTagId, |         parentTagId: form.parentTagId, | ||||||
| @@ -197,15 +196,16 @@ const tagLabelById = useMemo(() => { | |||||||
|         tenantId, // requerido por backend (400 si falta) |         tenantId, // requerido por backend (400 si falta) | ||||||
|       }; |       }; | ||||||
|  |  | ||||||
|       if (form._Id) { |       if (form._id) { | ||||||
|         // UPDATE |         const idForUpdate = Boolean(form._id) ? String(form._id) : null; | ||||||
|  |         if (!idForUpdate) throw new Error('Missing _id for update'); | ||||||
|         const payload = { |         const payload = { | ||||||
|           id: form.id || form._Id, // backend acepta GUID; si no hay, mandamos _id |           _id: idForUpdate, | ||||||
|           ...base, |           ...base, | ||||||
|         }; |         }; | ||||||
|  |         console.log('[CategoryForm] SUBMIT (edit) with _id:', idForUpdate, 'payload:', payload); | ||||||
|         await api.update(payload); |         await api.update(payload); | ||||||
|       } else { |       } else { | ||||||
|         // CREATE |  | ||||||
|         await api.create(base); |         await api.create(base); | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -221,10 +221,9 @@ const tagLabelById = useMemo(() => { | |||||||
|  |  | ||||||
|   const handleDelete = async () => { |   const handleDelete = async () => { | ||||||
|     try { |     try { | ||||||
|       // Try to use Mongo _Id (24-hex); if not present, fall back to GUID `id`. |       const idToUse = form._id; | ||||||
|       const hex = typeof form._Id === 'string' && /^[0-9a-f]{24}$/i.test(form._Id) ? form._Id : null; |       if (!idToUse) throw new Error('Missing _id to delete'); | ||||||
|       const idToUse = hex || form.id; |       console.debug('[CategoryForm] DELETE with _id:', idToUse); | ||||||
|       if (!idToUse) throw new Error('Missing id to delete'); |  | ||||||
|       await api.changeStatus({ id: idToUse, status: 'Inactive' }); |       await api.changeStatus({ id: idToUse, status: 'Inactive' }); | ||||||
|       if (onAdd) { |       if (onAdd) { | ||||||
|         await onAdd(); |         await onAdd(); | ||||||
| @@ -238,7 +237,7 @@ const tagLabelById = useMemo(() => { | |||||||
|   return ( |   return ( | ||||||
|     <Paper sx={{ p: 2 }}> |     <Paper sx={{ p: 2 }}> | ||||||
|       <Typography variant="subtitle1" sx={{ mb: 2 }}> |       <Typography variant="subtitle1" sx={{ mb: 2 }}> | ||||||
|         {form._Id ? 'Edit Category' : 'Add Category'} |         {form._id ? 'Edit Category' : 'Add Category'} | ||||||
|       </Typography> |       </Typography> | ||||||
|  |  | ||||||
|       {isAdd && ( |       {isAdd && ( | ||||||
| @@ -273,7 +272,7 @@ const tagLabelById = useMemo(() => { | |||||||
|         required |         required | ||||||
|       > |       > | ||||||
|         {types.map((t) => { |         {types.map((t) => { | ||||||
|           const value = t._id || t.id; // prefer Mongo _id for 1:1 mapping |           const value = t._id; | ||||||
|           const label = t.typeName || value; |           const label = t.typeName || value; | ||||||
|           return ( |           return ( | ||||||
|             <MenuItem key={value} value={value}> |             <MenuItem key={value} value={value}> | ||||||
| @@ -307,7 +306,7 @@ const tagLabelById = useMemo(() => { | |||||||
|         sx={{ mb: 2 }} |         sx={{ mb: 2 }} | ||||||
|       > |       > | ||||||
|         {allTags.map((t) => { |         {allTags.map((t) => { | ||||||
|           const value = t._id || t.id; |           const value = t._id; | ||||||
|           const label = t.tagName || t.name || value; |           const label = t.tagName || t.name || value; | ||||||
|           return ( |           return ( | ||||||
|             <MenuItem key={value} value={value}> |             <MenuItem key={value} value={value}> | ||||||
| @@ -359,7 +358,7 @@ const tagLabelById = useMemo(() => { | |||||||
|         <MenuItem value="Inactive">Inactive</MenuItem> |         <MenuItem value="Inactive">Inactive</MenuItem> | ||||||
|       </TextField> |       </TextField> | ||||||
|  |  | ||||||
|       {form._Id || form.id ? ( |       {form._id ? ( | ||||||
|         <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2, mt: 2 }}> |         <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2, mt: 2 }}> | ||||||
|           <TextField label="Created At" value={formatDateSafe(form.createdAt)} InputProps={{ readOnly: true }} fullWidth /> |           <TextField label="Created At" value={formatDateSafe(form.createdAt)} InputProps={{ readOnly: true }} fullWidth /> | ||||||
|           <TextField label="Created By" value={form.createdBy ?? '—'} InputProps={{ readOnly: true }} fullWidth /> |           <TextField label="Created By" value={form.createdBy ?? '—'} InputProps={{ readOnly: true }} fullWidth /> | ||||||
| @@ -369,7 +368,7 @@ const tagLabelById = useMemo(() => { | |||||||
|       ) : null} |       ) : null} | ||||||
|  |  | ||||||
|       <Box display="flex" justifyContent="space-between" gap={1} mt={3}> |       <Box display="flex" justifyContent="space-between" gap={1} mt={3}> | ||||||
|         {(form._Id || form.id) ? ( |         {form._id ? ( | ||||||
|           <Button color="error" onClick={handleDelete}>Delete</Button> |           <Button color="error" onClick={handleDelete}>Delete</Button> | ||||||
|         ) : <span />} |         ) : <span />} | ||||||
|         <Box sx={{ display: 'flex', gap: 1 }}> |         <Box sx={{ display: 'flex', gap: 1 }}> | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; | |||||||
| import AddOrEditCategoryForm from './AddOrEditCategoryForm'; | import AddOrEditCategoryForm from './AddOrEditCategoryForm'; | ||||||
| import CategoriesApi from '../../api/CategoriesApi'; | import CategoriesApi from '../../api/CategoriesApi'; | ||||||
| import { useAuth } from '../../context/AuthContext'; | import { useAuth } from '../../context/AuthContext'; | ||||||
| import TagTypeApi from '../../api/TagTypeApi'; |  | ||||||
|  |  | ||||||
| export default function Categories() { | export default function Categories() { | ||||||
|   const { user } = useAuth(); |   const { user } = useAuth(); | ||||||
| @@ -41,21 +40,25 @@ export default function Categories() { | |||||||
|  |  | ||||||
|       setAllTags(list); |       setAllTags(list); | ||||||
|  |  | ||||||
|       // Build a map of parentId -> array of child tagNames |       // Build a map of tagId -> tagName to resolve parent names | ||||||
|       const parentToChildren = {}; |       const idToName = {}; | ||||||
|       for (const item of list) { |       for (const item of list) { | ||||||
|         const parents = Array.isArray(item?.parentTagId) ? item.parentTagId : []; |         const key = item?._id || item?.id; | ||||||
|         for (const pid of parents) { |         if (key) idToName[key] = item?.tagName || item?.name || ''; | ||||||
|           if (!parentToChildren[pid]) parentToChildren[pid] = []; |  | ||||||
|           if (item?.tagName) parentToChildren[pid].push(item.tagName); |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       // Enrich each row with `material` (children whose parentTagId includes this _id) |       // Enrich each row with `materialNames`: names of the parents referenced by parentTagId | ||||||
|       const enriched = list.map((r) => ({ |       const enriched = list.map((r) => { | ||||||
|  |         const parents = Array.isArray(r?.parentTagId) ? r.parentTagId : []; | ||||||
|  |         const materialNames = parents | ||||||
|  |           .map((pid) => idToName[pid]) | ||||||
|  |           .filter(Boolean); | ||||||
|  |  | ||||||
|  |         return { | ||||||
|           ...r, |           ...r, | ||||||
|         material: Array.isArray(parentToChildren[r?._id]) ? parentToChildren[r._id].join(', ') : '', |           materialNames, // array of strings | ||||||
|       })); |         }; | ||||||
|  |       }); | ||||||
|  |  | ||||||
|       setRows(enriched); |       setRows(enriched); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
| @@ -73,8 +76,8 @@ export default function Categories() { | |||||||
|     const r = params?.row; |     const r = params?.row; | ||||||
|     if (!r) return; |     if (!r) return; | ||||||
|     setEditingCategory({ |     setEditingCategory({ | ||||||
|       _Id: r._id || r._Id || '', |       _id: String(r._id || ''), | ||||||
|       id: r.id || r.Id || '', |       id: String(r.id || ''), | ||||||
|       tagName: r.tagName || r.name || '', |       tagName: r.tagName || r.name || '', | ||||||
|       typeId: r.typeId || '', |       typeId: r.typeId || '', | ||||||
|       parentTagId: Array.isArray(r.parentTagId) ? r.parentTagId : [], |       parentTagId: Array.isArray(r.parentTagId) ? r.parentTagId : [], | ||||||
| @@ -82,9 +85,11 @@ export default function Categories() { | |||||||
|       displayOrder: Number(r.displayOrder ?? 0), |       displayOrder: Number(r.displayOrder ?? 0), | ||||||
|       icon: r.icon || '', |       icon: r.icon || '', | ||||||
|       status: r.status ?? 'Active', |       status: r.status ?? 'Active', | ||||||
|       materialNames: typeof r.material === 'string' |       materialNames: Array.isArray(r.materialNames) | ||||||
|  |         ? r.materialNames | ||||||
|  |         : (typeof r.material === 'string' | ||||||
|           ? r.material.split(',').map(s => s.trim()).filter(Boolean) |           ? r.material.split(',').map(s => s.trim()).filter(Boolean) | ||||||
|         : Array.isArray(r.material) ? r.material : [], |           : []), | ||||||
|       createdAt: r.createdAt ?? null, |       createdAt: r.createdAt ?? null, | ||||||
|       createdBy: r.createdBy ?? null, |       createdBy: r.createdBy ?? null, | ||||||
|       updatedAt: r.updatedAt ?? null, |       updatedAt: r.updatedAt ?? null, | ||||||
| @@ -100,7 +105,7 @@ export default function Categories() { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const pickHexId = (r) => |   const pickHexId = (r) => | ||||||
|     [r?._id, r?._Id, r?.id, r?.Id] |     [r?._id, r?.id] | ||||||
|       .filter(Boolean) |       .filter(Boolean) | ||||||
|       .find((x) => typeof x === 'string' && /^[0-9a-f]{24}$/i.test(x)) || null; |       .find((x) => typeof x === 'string' && /^[0-9a-f]{24}$/i.test(x)) || null; | ||||||
|  |  | ||||||
| @@ -178,7 +183,40 @@ export default function Categories() { | |||||||
|     { field: 'tagName', headerName: 'Name', flex: 1.2, minWidth: 180 }, |     { field: 'tagName', headerName: 'Name', flex: 1.2, minWidth: 180 }, | ||||||
|     { field: 'slug', headerName: 'Slug', flex: 1.0, minWidth: 160 }, |     { field: 'slug', headerName: 'Slug', flex: 1.0, minWidth: 160 }, | ||||||
|     { field: 'icon', headerName: 'Icon', flex: 0.7, minWidth: 250 }, |     { field: 'icon', headerName: 'Icon', flex: 0.7, minWidth: 250 }, | ||||||
|  |  | ||||||
|  |     /* | ||||||
|     { field: 'material', headerName: 'Material', flex: 1.2, minWidth: 200 }, |     { field: 'material', headerName: 'Material', flex: 1.2, minWidth: 200 }, | ||||||
|  |     */ | ||||||
|  |     { | ||||||
|  |       field: 'materialNames', | ||||||
|  |       headerName: 'Material', | ||||||
|  |       flex: 1.2, | ||||||
|  |       minWidth: 220, | ||||||
|  |       renderCell: (params) => { | ||||||
|  |         const vals = Array.isArray(params?.row?.materialNames) ? params.row.materialNames : []; | ||||||
|  |         return ( | ||||||
|  |           <Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', alignItems: 'center' }}> | ||||||
|  |             {vals.length ? vals.map((m) => ( | ||||||
|  |               <span | ||||||
|  |                 key={m} | ||||||
|  |                 style={{ | ||||||
|  |                   padding: '2px 8px', | ||||||
|  |                   borderRadius: '12px', | ||||||
|  |                   background: '#DFCCBC', | ||||||
|  |                   color: '#26201A', | ||||||
|  |                   fontSize: 12, | ||||||
|  |                   lineHeight: '18px', | ||||||
|  |                 }} | ||||||
|  |               > | ||||||
|  |                 {m} | ||||||
|  |               </span> | ||||||
|  |             )) : '—'} | ||||||
|  |           </Box> | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |       sortable: false, | ||||||
|  |       filterable: false, | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       field: 'createdAt', |       field: 'createdAt', | ||||||
|       headerName: 'Created Date', |       headerName: 'Created Date', | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user