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