Compare commits
10 Commits
9cdb76273d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7adaf1b18 | ||
|
|
b3209a4019 | ||
|
|
efdb48919f | ||
|
|
6b8d5acc0d | ||
|
|
01a19b9144 | ||
|
|
74d6a8b269 | ||
|
|
c33de6ada5 | ||
|
|
15107a48bd | ||
|
|
73699009fc | ||
|
|
55dc96085d |
@@ -52,4 +52,15 @@ export default class ProductsApi {
|
|||||||
if (!res.ok) throw new Error(`Delete error ${res.status}: ${await res.text()}`);
|
if (!res.ok) throw new Error(`Delete error ${res.status}: ${await res.text()}`);
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async changeStatusVariant(payload) {
|
||||||
|
// If your API is change status, reuse updateVariant.
|
||||||
|
const res = await fetch(`${this.baseUrl}/ChangeStatus`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: this.headers(),
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error(`ChangeStatus error ${res.status}: ${await res.text()}`);
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ function formatDateSafe(value) {
|
|||||||
}).format(d);
|
}).format(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AddOrEditCategoryForm({ onAdd, initialData, onCancel, materials: materialsProp = [], initialMaterialNames = [] }) {
|
export default function AddOrEditCategoryForm({ onAdd, initialData, onCancel, materials: materialsProp = [], initialMaterialNames = [], viewOnly = false }) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const token = user?.thalosToken || localStorage.getItem('thalosToken');
|
const token = user?.thalosToken || localStorage.getItem('thalosToken');
|
||||||
const api = useMemo(() => new CategoriesApi(token), [token]);
|
const api = useMemo(() => new CategoriesApi(token), [token]);
|
||||||
@@ -248,6 +248,7 @@ const tagLabelById = useMemo(() => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
InputProps={{ readOnly: true }}
|
InputProps={{ readOnly: true }}
|
||||||
|
disabled={viewOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -259,6 +260,7 @@ const tagLabelById = useMemo(() => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
required
|
required
|
||||||
|
disabled={viewOnly}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -270,6 +272,7 @@ const tagLabelById = useMemo(() => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
required
|
required
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{types.map((t) => {
|
{types.map((t) => {
|
||||||
const value = t._id;
|
const value = t._id;
|
||||||
@@ -304,6 +307,7 @@ const tagLabelById = useMemo(() => {
|
|||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{allTags.map((t) => {
|
{allTags.map((t) => {
|
||||||
const value = t._id;
|
const value = t._id;
|
||||||
@@ -323,6 +327,7 @@ const tagLabelById = useMemo(() => {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
fullWidth
|
fullWidth
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
|
disabled={viewOnly}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -333,6 +338,7 @@ const tagLabelById = useMemo(() => {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
fullWidth
|
fullWidth
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
|
disabled={viewOnly}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -343,6 +349,7 @@ const tagLabelById = useMemo(() => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
required
|
required
|
||||||
|
disabled={viewOnly}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -353,6 +360,7 @@ const tagLabelById = useMemo(() => {
|
|||||||
select
|
select
|
||||||
fullWidth
|
fullWidth
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
<MenuItem value="Active">Active</MenuItem>
|
<MenuItem value="Active">Active</MenuItem>
|
||||||
<MenuItem value="Inactive">Inactive</MenuItem>
|
<MenuItem value="Inactive">Inactive</MenuItem>
|
||||||
@@ -368,12 +376,14 @@ 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 && !viewOnly ? (
|
||||||
<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 }}>
|
||||||
<Button onClick={onCancel} className="button-transparent">Cancel</Button>
|
<Button onClick={onCancel} className="button-transparent">{viewOnly ? 'Close' : 'Cancel'}</Button>
|
||||||
|
{!viewOnly && (
|
||||||
<Button variant="contained" onClick={handleSubmit} className="button-gold">Save</Button>
|
<Button variant="contained" onClick={handleSubmit} className="button-gold">Save</Button>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
import { DataGrid } from '@mui/x-data-grid';
|
import { DataGrid } from '@mui/x-data-grid';
|
||||||
import EditRoundedIcon from '@mui/icons-material/EditRounded';
|
import EditRoundedIcon from '@mui/icons-material/EditRounded';
|
||||||
import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
|
import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
|
||||||
|
import VisibilityRoundedIcon from '@mui/icons-material/VisibilityRounded';
|
||||||
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';
|
||||||
@@ -22,6 +23,7 @@ export default function Categories() {
|
|||||||
const [editingCategory, setEditingCategory] = useState(null);
|
const [editingCategory, setEditingCategory] = useState(null);
|
||||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||||
const [rowToDelete, setRowToDelete] = useState(null);
|
const [rowToDelete, setRowToDelete] = useState(null);
|
||||||
|
const [viewOnly, setViewOnly] = useState(false);
|
||||||
const hasLoaded = useRef(false);
|
const hasLoaded = useRef(false);
|
||||||
|
|
||||||
const pageSize = 100; // Número de filas por página
|
const pageSize = 100; // Número de filas por página
|
||||||
@@ -68,11 +70,13 @@ export default function Categories() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleAddClick = () => {
|
const handleAddClick = () => {
|
||||||
|
setViewOnly(false);
|
||||||
setEditingCategory(null);
|
setEditingCategory(null);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditClick = (params) => {
|
const handleEditClick = (params) => {
|
||||||
|
setViewOnly(false);
|
||||||
const r = params?.row;
|
const r = params?.row;
|
||||||
if (!r) return;
|
if (!r) return;
|
||||||
setEditingCategory({
|
setEditingCategory({
|
||||||
@@ -145,7 +149,7 @@ export default function Categories() {
|
|||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
headerName: '',
|
headerName: '',
|
||||||
width: 130,
|
width: 150,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
disableExport: true,
|
disableExport: true,
|
||||||
@@ -164,6 +168,44 @@ export default function Categories() {
|
|||||||
>
|
>
|
||||||
<EditRoundedIcon fontSize="small" />
|
<EditRoundedIcon fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#E3F2FD',
|
||||||
|
color: '#1565C0',
|
||||||
|
'&:hover': { backgroundColor: '#BBDEFB' },
|
||||||
|
borderRadius: 2,
|
||||||
|
p: 1,
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
const r = params?.row;
|
||||||
|
if (!r) return;
|
||||||
|
setEditingCategory({
|
||||||
|
_id: String(r._id || ''),
|
||||||
|
id: String(r.id || ''),
|
||||||
|
tagName: r.tagName || r.name || '',
|
||||||
|
typeId: r.typeId || '',
|
||||||
|
parentTagId: Array.isArray(r.parentTagId) ? r.parentTagId : [],
|
||||||
|
slug: r.slug || '',
|
||||||
|
displayOrder: Number(r.displayOrder ?? 0),
|
||||||
|
icon: r.icon || '',
|
||||||
|
status: r.status ?? 'Active',
|
||||||
|
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,
|
||||||
|
updatedBy: r.updatedBy ?? null,
|
||||||
|
});
|
||||||
|
setViewOnly(true);
|
||||||
|
setOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VisibilityRoundedIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
sx={{
|
sx={{
|
||||||
@@ -254,7 +296,7 @@ export default function Categories() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 2, flexWrap: 'wrap' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 2, flexWrap: 'wrap' }}>
|
||||||
<Typography variant="h6">Categories</Typography>
|
<Typography color='text.primary' variant="h6">Categories</Typography>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
@@ -319,6 +361,7 @@ export default function Categories() {
|
|||||||
initialMaterialNames={editingCategory?.materialNames || []}
|
initialMaterialNames={editingCategory?.materialNames || []}
|
||||||
onAdd={handleFormDone}
|
onAdd={handleFormDone}
|
||||||
onCancel={() => { setOpen(false); setEditingCategory(null); }}
|
onCancel={() => { setOpen(false); setEditingCategory(null); }}
|
||||||
|
viewOnly={viewOnly}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@@ -327,8 +370,8 @@ export default function Categories() {
|
|||||||
<DialogTitle>Delete Category</DialogTitle>
|
<DialogTitle>Delete Category</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Box sx={{ display: 'flex', gap: 1, mt: 2, mb: 1 }}>
|
<Box sx={{ display: 'flex', gap: 1, mt: 2, mb: 1 }}>
|
||||||
<Button onClick={() => setConfirmOpen(false)}>Cancel</Button>
|
<Button onClick={() => setConfirmOpen(false)} className='button-transparent'>Cancel</Button>
|
||||||
<Button color="error" variant="contained" onClick={confirmDelete}>Delete</Button>
|
<Button color="error" variant="contained" onClick={confirmDelete} className="button-gold">Delete</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// src/private/furniture/AddOrEditFurnitureVariantForm.jsx
|
// src/private/furniture/AddOrEditFurnitureVariantForm.jsx
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { Box, Button, TextField, MenuItem, Grid, CircularProgress } from '@mui/material';
|
import { Box, Button, TextField, MenuItem, CircularProgress } from '@mui/material';
|
||||||
import FurnitureVariantApi from '../../../api/ProductsApi';
|
import FurnitureVariantApi from '../../../api/ProductsApi';
|
||||||
import CategoriesApi from '../../../api/CategoriesApi';
|
import CategoriesApi from '../../../api/CategoriesApi';
|
||||||
import TagTypeApi from '../../../api/TagTypeApi';
|
import TagTypeApi from '../../../api/TagTypeApi';
|
||||||
@@ -19,7 +19,7 @@ const TYPE_NAMES = {
|
|||||||
origin: 'Origin',
|
origin: 'Origin',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AddOrEditProductCollectionForm({ initialData, onAdd, onCancel }) {
|
export default function AddOrEditProductCollectionForm({ initialData, onAdd, onCancel, viewOnly = false }) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const token = user?.thalosToken || localStorage.getItem('thalosToken');
|
const token = user?.thalosToken || localStorage.getItem('thalosToken');
|
||||||
|
|
||||||
@@ -197,16 +197,18 @@ export default function AddOrEditProductCollectionForm({ initialData, onAdd, onC
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Grid container spacing={2}>
|
<Box display="flex" flexDirection="column" gap={2}>
|
||||||
<Grid item xs={12} md={6}>
|
{/* Name */}
|
||||||
<TextField label="Model Id" fullWidth value={form.modelId} onChange={(e) => setVal('modelId', e.target.value)} />
|
<TextField
|
||||||
</Grid>
|
label="Name"
|
||||||
<Grid item xs={12} md={6}>
|
fullWidth
|
||||||
<TextField label="Name" fullWidth value={form.name} onChange={(e) => setVal('name', e.target.value)} />
|
value={form.name}
|
||||||
</Grid>
|
onChange={(e) => setVal('name', e.target.value)}
|
||||||
|
sx={{ mt: 1 }}
|
||||||
|
disabled={viewOnly}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Clasificación */}
|
{/* Category / Provider */}
|
||||||
<Grid item xs={12} md={6}>
|
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
label="Category"
|
label="Category"
|
||||||
@@ -214,141 +216,145 @@ export default function AddOrEditProductCollectionForm({ initialData, onAdd, onC
|
|||||||
value={form.categoryId}
|
value={form.categoryId}
|
||||||
onChange={(e) => setVal('categoryId', e.target.value)}
|
onChange={(e) => setVal('categoryId', e.target.value)}
|
||||||
helperText="Se envía el tagName por ahora"
|
helperText="Se envía el tagName por ahora"
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
||||||
{!loadingTags && options.categories.filter(t => t.status !== 'Inactive').map(tag => (
|
{!loadingTags && options.categories.filter(t => t.status !== 'Inactive').map(tag => (
|
||||||
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12} md={6}>
|
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
label="Provider"
|
label="Provider"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={form.providerId}
|
value={form.providerId}
|
||||||
onChange={(e) => setVal('providerId', e.target.value)}
|
onChange={(e) => setVal('providerId', e.target.value)}
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
||||||
{!loadingTags && options.providers.filter(t => t.status !== 'Inactive').map(tag => (
|
{!loadingTags && options.providers.filter(t => t.status !== 'Inactive').map(tag => (
|
||||||
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Específicos de variante */}
|
<Box display="flex" gap={2}>
|
||||||
<Grid item xs={12} md={4}>
|
{/* Color */}
|
||||||
|
<Box flex={1}>
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
label="Color"
|
label="Color"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={form.color}
|
value={form.color}
|
||||||
onChange={(e) => setVal('color', e.target.value)}
|
onChange={(e) => setVal('color', e.target.value)}
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
||||||
{!loadingTags && options.colors.filter(t => t.status !== 'Inactive').map(tag => (
|
{!loadingTags && options.colors.filter(t => t.status !== 'Inactive').map(tag => (
|
||||||
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Box>
|
||||||
|
{/* Line */}
|
||||||
<Grid item xs={12} md={4}>
|
<Box flex={1}>
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
label="Line"
|
label="Line"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={form.line}
|
value={form.line}
|
||||||
onChange={(e) => setVal('line', e.target.value)}
|
onChange={(e) => setVal('line', e.target.value)}
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
||||||
{!loadingTags && options.lines.filter(t => t.status !== 'Inactive').map(tag => (
|
{!loadingTags && options.lines.filter(t => t.status !== 'Inactive').map(tag => (
|
||||||
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Box>
|
||||||
|
{/* Material */}
|
||||||
<Grid item xs={12} md={4}>
|
<Box flex={1}>
|
||||||
<TextField
|
|
||||||
select
|
|
||||||
label="Currency"
|
|
||||||
fullWidth
|
|
||||||
value={form.currency}
|
|
||||||
onChange={(e) => setVal('currency', e.target.value)}
|
|
||||||
>
|
|
||||||
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
|
||||||
{!loadingTags && options.currencies.filter(t => t.status !== 'Inactive').map(tag => (
|
|
||||||
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
|
||||||
))}
|
|
||||||
</TextField>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Atributos como catálogos */}
|
|
||||||
<Grid item xs={12} md={4}>
|
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
label="Material"
|
label="Material"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={form.attributes.material}
|
value={form.attributes.material}
|
||||||
onChange={(e) => setVal('attributes.material', e.target.value)}
|
onChange={(e) => setVal('attributes.material', e.target.value)}
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
||||||
{!loadingTags && options.materials.filter(t => t.status !== 'Inactive').map(tag => (
|
{!loadingTags && options.materials.filter(t => t.status !== 'Inactive').map(tag => (
|
||||||
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Grid item xs={12} md={4}>
|
<Box display="flex" gap={2}>
|
||||||
|
{/* Price */}
|
||||||
|
<Box flex={1}>
|
||||||
|
<TextField label="Price" type="number" fullWidth value={form.price} onChange={(e) => setVal('price', e.target.value)} disabled={viewOnly} />
|
||||||
|
</Box>
|
||||||
|
{/* Stock */}
|
||||||
|
<Box flex={1}>
|
||||||
|
<TextField label="Stock" type="number" fullWidth value={form.stock} onChange={(e) => setVal('stock', e.target.value)} disabled={viewOnly} />
|
||||||
|
</Box>
|
||||||
|
{/* Currency */}
|
||||||
|
<Box flex={1}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
label="Currency"
|
||||||
|
fullWidth
|
||||||
|
value={form.currency}
|
||||||
|
onChange={(e) => setVal('currency', e.target.value)}
|
||||||
|
disabled={viewOnly}
|
||||||
|
>
|
||||||
|
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
||||||
|
{!loadingTags && options.currencies.filter(t => t.status !== 'Inactive').map(tag => (
|
||||||
|
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Attributes */}
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
label="Legs"
|
label="Legs"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={form.attributes.legs}
|
value={form.attributes.legs}
|
||||||
onChange={(e) => setVal('attributes.legs', e.target.value)}
|
onChange={(e) => setVal('attributes.legs', e.target.value)}
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
||||||
{!loadingTags && options.legs.filter(t => t.status !== 'Inactive').map(tag => (
|
{!loadingTags && options.legs.filter(t => t.status !== 'Inactive').map(tag => (
|
||||||
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12} md={4}>
|
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
label="Origin"
|
label="Origin"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={form.attributes.origin}
|
value={form.attributes.origin}
|
||||||
onChange={(e) => setVal('attributes.origin', e.target.value)}
|
onChange={(e) => setVal('attributes.origin', e.target.value)}
|
||||||
|
disabled={viewOnly}
|
||||||
>
|
>
|
||||||
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
{loadingTags && <MenuItem value="" disabled><CircularProgress size={16} /> Cargando…</MenuItem>}
|
||||||
{!loadingTags && options.origins.filter(t => t.status !== 'Inactive').map(tag => (
|
{!loadingTags && options.origins.filter(t => t.status !== 'Inactive').map(tag => (
|
||||||
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
<MenuItem key={tag._id?.$oid || tag._id || tag.id} value={tag.tagName}>{tag.tagName}</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Números */}
|
|
||||||
<Grid item xs={12} md={4}>
|
|
||||||
<TextField label="Stock" type="number" fullWidth value={form.stock} onChange={(e) => setVal('stock', e.target.value)} />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={4}>
|
|
||||||
<TextField label="Price" type="number" fullWidth value={form.price} onChange={(e) => setVal('price', e.target.value)} />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Status */}
|
{/* Status */}
|
||||||
<Grid item xs={12} md={4}>
|
<TextField select label="Status" fullWidth value={form.status} onChange={(e) => setVal('status', e.target.value)} disabled={viewOnly}>
|
||||||
<TextField select label="Status" fullWidth value={form.status} onChange={(e) => setVal('status', e.target.value)}>
|
|
||||||
<MenuItem value="Active">Active</MenuItem>
|
<MenuItem value="Active">Active</MenuItem>
|
||||||
<MenuItem value="Inactive">Inactive</MenuItem>
|
<MenuItem value="Inactive">Inactive</MenuItem>
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Box>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Box display="flex" justifyContent="flex-end" mt={3} gap={1}>
|
<Box display="flex" justifyContent="flex-end" mt={3} gap={1}>
|
||||||
<Button onClick={onCancel} className="button-transparent">Cancel</Button>
|
<Button onClick={onCancel} className="button-transparent">{viewOnly ? 'Close' : 'Cancel'}</Button>
|
||||||
|
{!viewOnly && (
|
||||||
<Button variant="contained" onClick={handleSubmit} className="button-gold">Save</Button>
|
<Button variant="contained" onClick={handleSubmit} className="button-gold">Save</Button>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { useEffect, useMemo, useState } from 'react';
|
|||||||
import { DataGrid } from '@mui/x-data-grid';
|
import { DataGrid } from '@mui/x-data-grid';
|
||||||
import {
|
import {
|
||||||
Typography, Button, Dialog, DialogTitle, DialogContent,
|
Typography, Button, Dialog, DialogTitle, DialogContent,
|
||||||
IconButton, Box, FormControlLabel, Switch, Tooltip
|
IconButton, Box, ToggleButtonGroup, ToggleButton
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import EditRoundedIcon from '@mui/icons-material/EditRounded';
|
import EditRoundedIcon from '@mui/icons-material/EditRounded';
|
||||||
import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
|
import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
|
||||||
import AddRoundedIcon from '@mui/icons-material/AddRounded';
|
import AddRoundedIcon from '@mui/icons-material/AddRounded';
|
||||||
|
import VisibilityRoundedIcon from '@mui/icons-material/VisibilityRounded';
|
||||||
import AddOrEditProductCollectionForm from './AddOrEditProductCollectionForm';
|
import AddOrEditProductCollectionForm from './AddOrEditProductCollectionForm';
|
||||||
import FurnitureVariantApi from '../../../api/ProductsApi';
|
import FurnitureVariantApi from '../../../api/ProductsApi';
|
||||||
import CategoriesApi from '../../../api/CategoriesApi';
|
import CategoriesApi from '../../../api/CategoriesApi';
|
||||||
@@ -42,15 +43,20 @@ export default function ProductCollections() {
|
|||||||
const tagTypeApi = useMemo(() => new TagTypeApi(token), [token]);
|
const tagTypeApi = useMemo(() => new TagTypeApi(token), [token]);
|
||||||
const categoriesApi = useMemo(() => new CategoriesApi(token), [token]);
|
const categoriesApi = useMemo(() => new CategoriesApi(token), [token]);
|
||||||
|
|
||||||
const toast = useApiToast();
|
const { handleError } = useApiToast();
|
||||||
|
|
||||||
const [rows, setRows] = useState([]);
|
const [rows, setRows] = useState([]);
|
||||||
const [rawRows, setRawRows] = useState([]);
|
const [rawRows, setRawRows] = useState([]);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [editRow, setEditRow] = useState(null);
|
const [editRow, setEditRow] = useState(null);
|
||||||
const [showInactive, setShowInactive] = useState(false);
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const [statusFilter, setStatusFilter] = useState('All');
|
||||||
|
|
||||||
|
const [viewOnly, setViewOnly] = useState(false);
|
||||||
|
|
||||||
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
const [loadingTags, setLoadingTags] = useState(true);
|
const [loadingTags, setLoadingTags] = useState(true);
|
||||||
const [typeMap, setTypeMap] = useState({});
|
const [typeMap, setTypeMap] = useState({});
|
||||||
@@ -80,15 +86,6 @@ export default function ProductCollections() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
// Cargar TagTypes + Tags
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let mounted = true;
|
let mounted = true;
|
||||||
@@ -154,10 +151,9 @@ export default function ProductCollections() {
|
|||||||
createdBy: r.createdBy ?? null,
|
createdBy: r.createdBy ?? null,
|
||||||
}));
|
}));
|
||||||
setRawRows(normalized);
|
setRawRows(normalized);
|
||||||
setRows(normalized.filter(r => showInactive ? true : r.status !== 'Inactive'));
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
toast.error(err?.message || 'Error loading variants');
|
handleError(err, 'Error loading product collections');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -165,108 +161,215 @@ export default function ProductCollections() {
|
|||||||
|
|
||||||
useEffect(() => { load(); /* eslint-disable-next-line */ }, []);
|
useEffect(() => { load(); /* eslint-disable-next-line */ }, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRows(rawRows.filter(r => showInactive ? true : r.status !== 'Inactive'));
|
if (statusFilter === 'All') {
|
||||||
}, [showInactive, rawRows]);
|
setRows(rawRows);
|
||||||
|
} else {
|
||||||
|
const want = statusFilter.toLowerCase();
|
||||||
|
setRows(rawRows.filter(r => String(r.status ?? 'Active').toLowerCase() === want));
|
||||||
|
}
|
||||||
|
}, [statusFilter, rawRows]);
|
||||||
|
|
||||||
|
const handleDeleteClick = (row) => {
|
||||||
|
setEditRow(row);
|
||||||
|
setConfirmOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmDelete = async () => {
|
||||||
|
try {
|
||||||
|
if (!editRow?.id) return;
|
||||||
|
await api.changeStatusVariant({ mongoId: editRow._Id, status: 'Inactive' });
|
||||||
|
await load();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
setConfirmOpen(false);
|
||||||
|
setEditRow(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ field: 'modelId', headerName: 'Model Id', width: 220 },
|
{
|
||||||
{ field: 'name', headerName: 'Name', width: 200 },
|
field: 'actions',
|
||||||
{ field: 'categoryId', headerName: 'Category', width: 170, valueGetter: (p) => labelCategory(p?.row?.categoryId) },
|
headerName: '',
|
||||||
{ field: 'providerId', headerName: 'Provider', width: 170, valueGetter: (p) => labelProvider(p?.row?.providerId) },
|
width: 190,
|
||||||
{ field: 'color', headerName: 'Color', width: 130, valueGetter: (p) => labelColor(p?.row?.color) },
|
sortable: false,
|
||||||
{ field: 'line', headerName: 'Line', width: 130, valueGetter: (p) => labelLine(p?.row?.line) },
|
filterable: false,
|
||||||
|
disableExport: true,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<Box display="flex" alignItems="center" justifyContent="flex-start" height="100%" gap={1}>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#DFCCBC',
|
||||||
|
color: '#26201A',
|
||||||
|
'&:hover': { backgroundColor: '#C2B2A4' },
|
||||||
|
borderRadius: 2,
|
||||||
|
p: 1,
|
||||||
|
}}
|
||||||
|
onClick={() => { setEditRow(params.row); setViewOnly(false); setOpen(true); }}
|
||||||
|
>
|
||||||
|
<EditRoundedIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#E3F2FD',
|
||||||
|
color: '#1565C0',
|
||||||
|
'&:hover': { backgroundColor: '#BBDEFB' },
|
||||||
|
borderRadius: 2,
|
||||||
|
p: 1,
|
||||||
|
}}
|
||||||
|
onClick={() => { setEditRow(params.row); setOpen(true); setViewOnly(true); }}
|
||||||
|
>
|
||||||
|
<VisibilityRoundedIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#FBE9E7',
|
||||||
|
color: '#C62828',
|
||||||
|
'&:hover': { backgroundColor: '#EF9A9A' },
|
||||||
|
borderRadius: 2,
|
||||||
|
p: 1,
|
||||||
|
}}
|
||||||
|
onClick={() => { setViewOnly(false); handleDeleteClick(params?.row); }}
|
||||||
|
>
|
||||||
|
<DeleteRoundedIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ field: 'name', headerName: 'Name', flex: 1, minWidth: 160 },
|
||||||
|
{ field: 'categoryId', headerName: 'Category', flex: 1, minWidth: 160 },
|
||||||
|
{ field: 'providerId', headerName: 'Provider', flex: 1, minWidth: 160 },
|
||||||
|
{ field: 'color', headerName: 'Color', flex: 1, minWidth: 160 },
|
||||||
|
{ field: 'line', headerName: 'Line', flex: 1, minWidth: 160 },
|
||||||
{
|
{
|
||||||
field: 'price',
|
field: 'price',
|
||||||
headerName: 'Price',
|
headerName: 'Price',
|
||||||
width: 130,
|
flex: 1,
|
||||||
|
minWidth: 160,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
valueGetter: (p) => parsePrice(p?.row?.price),
|
valueGetter: (p) => parsePrice(p?.row?.price),
|
||||||
renderCell: (p) => {
|
renderCell: (p) => {
|
||||||
const currency = labelCurrency(p?.row?.currency || 'USD');
|
|
||||||
const val = parsePrice(p?.row?.price);
|
const val = parsePrice(p?.row?.price);
|
||||||
|
const currency = p?.row?.currency || 'USD';
|
||||||
try {
|
try {
|
||||||
return new Intl.NumberFormat(undefined, { style: 'currency', currency: currency || 'USD' }).format(val);
|
return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(val);
|
||||||
} catch {
|
} catch {
|
||||||
return `${currency} ${val.toFixed(2)}`;
|
return `${currency} ${val.toFixed(2)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ field: 'currency', headerName: 'Currency', width: 120, valueGetter: (p) => labelCurrency(p?.row?.currency) },
|
{ field: 'currency', headerName: 'Currency', flex: 1, minWidth: 160 },
|
||||||
{ field: 'stock', headerName: 'Stock', width: 100, type: 'number', valueGetter: (p) => Number(p?.row?.stock ?? 0) },
|
{ field: 'stock', headerName: 'Stock', flex: 1, minWidth: 160 },
|
||||||
{ 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',
|
field: 'attributes',
|
||||||
headerName: '',
|
headerName: 'Attributes',
|
||||||
|
minWidth: 300,
|
||||||
|
flex: 1.5,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
width: 110,
|
filterable: false,
|
||||||
renderCell: (p) => (
|
renderCell: (params) => {
|
||||||
<Box display="flex" gap={1}>
|
const a = params?.row?.attributes || {};
|
||||||
<Tooltip title="Edit">
|
const chips = Object.entries(a).filter(([, v]) => !!v);
|
||||||
<IconButton size="small" onClick={() => { setEditRow(p.row); setOpen(true); }}>
|
return (
|
||||||
<EditRoundedIcon fontSize="small" />
|
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>
|
||||||
</IconButton>
|
{chips.map(([key, value]) => (
|
||||||
</Tooltip>
|
<Box
|
||||||
<Tooltip title={p.row.status === 'Active' ? 'Deactivate' : 'Activate'}>
|
key={key}
|
||||||
<IconButton
|
component="span"
|
||||||
size="small"
|
sx={{
|
||||||
onClick={async () => {
|
px: 1.5,
|
||||||
try {
|
py: 0.5,
|
||||||
const updated = { ...p.row, status: p.row.status === 'Active' ? 'Inactive' : 'Active' };
|
borderRadius: '12px',
|
||||||
await api.updateVariant(updated);
|
backgroundColor: '#DFCCBC',
|
||||||
setRawRows(prev => prev.map(r => r.id === p.row.id ? updated : r));
|
fontSize: 12,
|
||||||
} catch (err) {
|
color: '#26201A',
|
||||||
console.error(err);
|
lineHeight: '18px',
|
||||||
toast.error(err?.message || 'Error updating status');
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeleteRoundedIcon fontSize="small" />
|
{key}: {value}
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
))}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
{ field: 'status', headerName: 'Status', flex: 1, minWidth: 160 }
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionContainer>
|
<Box
|
||||||
<Box display="flex" alignItems="center" justifyContent="space-between" mb={2}>
|
sx={{
|
||||||
<Typography variant="h6">Furniture Variants</Typography>
|
height: 'calc(100vh - 64px - 64px)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box display="flex" alignItems="center" justifyContent="space-between" mb={2} flexWrap="wrap" gap={2}>
|
||||||
|
<Typography color='text.primary' variant="h6">Product Collection</Typography>
|
||||||
<Box display="flex" alignItems="center" gap={2}>
|
<Box display="flex" alignItems="center" gap={2}>
|
||||||
<FormControlLabel control={<Switch checked={showInactive} onChange={(_, v) => setShowInactive(v)} />} label="Show Inactive" />
|
<ToggleButtonGroup
|
||||||
<Button variant="contained" className="button-gold" startIcon={<AddRoundedIcon />} onClick={() => { setEditRow(null); setOpen(true); }}>
|
value={statusFilter}
|
||||||
Add Variant
|
exclusive
|
||||||
|
onChange={(_, v) => v && setStatusFilter(v)}
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<ToggleButton value="Active">Active</ToggleButton>
|
||||||
|
<ToggleButton value="All">All</ToggleButton>
|
||||||
|
<ToggleButton value="Inactive">Inactive</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
<Button variant="contained" className="button-gold" startIcon={<AddRoundedIcon />} onClick={() => { setEditRow(null); setViewOnly(false); setOpen(true); }}>
|
||||||
|
Add Product Collection
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ width: '100%', height: 560 }}>
|
<Box sx={{ flex: 1, minHeight: 0 }}>
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
disableRowSelectionOnClick
|
disableRowSelectionOnClick
|
||||||
loading={loading || loadingTags}
|
loading={loading || loadingTags}
|
||||||
pageSizeOptions={[10, 25, 50]}
|
pageSizeOptions={[50, 100, 200]}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: { paginationModel: { pageSize: 10 } },
|
pagination: { paginationModel: { pageSize: 50 } },
|
||||||
columns: { columnVisibilityModel: { id: false, _Id: false } },
|
columns: { columnVisibilityModel: { id: false, _Id: false } },
|
||||||
}}
|
}}
|
||||||
getRowHeight={() => 'auto'}
|
getRowHeight={() => 'auto'}
|
||||||
|
columnBuffer={0}
|
||||||
sx={{
|
sx={{
|
||||||
'& .MuiDataGrid-cell': { display: 'flex', alignItems: 'center' },
|
height: '100%',
|
||||||
'& .MuiDataGrid-columnHeader': { display: 'flex', alignItems: 'center' },
|
'& .MuiDataGrid-cell, & .MuiDataGrid-columnHeader': {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
'& .MuiDataGrid-filler': {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
slots={{
|
||||||
|
noRowsOverlay: () => (
|
||||||
|
<Box sx={{ p: 2 }}>No product collection found. Try switching the status filter to "All".</Box>
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Dialog open={open} onClose={() => setOpen(false)} maxWidth="md" fullWidth>
|
<Dialog open={open} onClose={() => setOpen(false)} maxWidth="md" fullWidth>
|
||||||
<DialogTitle>{editRow ? 'Edit Product Collection' : 'Add Product Collection'}</DialogTitle>
|
<DialogTitle>
|
||||||
|
{viewOnly
|
||||||
|
? 'View Product Collection'
|
||||||
|
: editRow
|
||||||
|
? 'Edit Product Collection'
|
||||||
|
: 'Add Product Collection'}
|
||||||
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<AddOrEditProductCollectionForm
|
<AddOrEditProductCollectionForm
|
||||||
initialData={editRow}
|
initialData={editRow}
|
||||||
|
viewOnly={viewOnly}
|
||||||
onAdd={(saved) => {
|
onAdd={(saved) => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
if (editRow) {
|
if (editRow) {
|
||||||
@@ -279,6 +382,16 @@ export default function ProductCollections() {
|
|||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</SectionContainer>
|
|
||||||
|
<Dialog open={confirmOpen} onClose={() => setConfirmOpen(false)}>
|
||||||
|
<DialogTitle>Delete Product Collection</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Box sx={{ display: 'flex', gap: 1, mt: 2, mb: 1 }}>
|
||||||
|
<Button onClick={() => setConfirmOpen(false)} className='button-transparent'>Cancel</Button>
|
||||||
|
<Button color="error" variant="contained" onClick={confirmDelete} className="button-gold">Delete</Button>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user