@airoom/nextmin-react 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/components/AddressAutocomplete.d.ts +4 -1
- package/dist/components/AddressAutocomplete.js +2 -2
- package/dist/components/AdminApp.js +86 -1
- package/dist/components/FileUploader.d.ts +3 -0
- package/dist/components/FileUploader.js +84 -28
- package/dist/components/PasswordInput.d.ts +2 -0
- package/dist/components/PasswordInput.js +2 -2
- package/dist/components/PhoneInput.d.ts +2 -0
- package/dist/components/PhoneInput.js +2 -2
- package/dist/components/QRCodeModal.d.ts +10 -0
- package/dist/components/QRCodeModal.js +153 -0
- package/dist/components/RefSelect.d.ts +4 -1
- package/dist/components/RefSelect.js +163 -65
- package/dist/components/SchemaForm.d.ts +2 -1
- package/dist/components/SchemaForm.js +132 -55
- package/dist/components/Sidebar.js +96 -7
- package/dist/components/TableFilters.js +1 -1
- package/dist/components/editor/TiptapEditor.js +46 -13
- package/dist/components/editor/Toolbar.js +195 -62
- package/dist/components/editor/components/CustomFontSizeModal.d.ts +13 -0
- package/dist/components/editor/components/CustomFontSizeModal.js +81 -0
- package/dist/components/editor/components/DistrictGridModal.js +1 -1
- package/dist/components/editor/components/SchemaInsertionModal.js +1 -1
- package/dist/components/editor/editor.css +2 -0
- package/dist/components/editor/extensions/FontSize.d.ts +19 -0
- package/dist/components/editor/extensions/FontSize.js +41 -0
- package/dist/components/viewer/DynamicViewer.js +50 -15
- package/dist/components/viewer/useViewDrawer.js +42 -2
- package/dist/hooks/useAppNavigation.d.ts +3 -0
- package/dist/hooks/useAppNavigation.js +37 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/lib/QueryBuilder.d.ts +3 -1
- package/dist/lib/QueryBuilder.js +10 -0
- package/dist/lib/api.d.ts +6 -0
- package/dist/lib/api.js +45 -24
- package/dist/lib/schemaUtils.js +1 -1
- package/dist/lib/types.d.ts +6 -1
- package/dist/nextmin.css +1 -1
- package/dist/providers/NextMinProvider.d.ts +5 -1
- package/dist/providers/NextMinProvider.js +12 -2
- package/dist/providers/themeResolver.d.ts +25 -0
- package/dist/providers/themeResolver.js +174 -0
- package/dist/router/NextMinRouter.d.ts +0 -1
- package/dist/router/NextMinRouter.js +0 -1
- package/dist/views/CreateEditPage.js +20 -11
- package/dist/views/DashboardPage.js +1 -1
- package/dist/views/ListPage.js +37 -6
- package/dist/views/SettingsEdit.js +1 -1
- package/dist/views/list/DataTableHero.d.ts +5 -5
- package/dist/views/list/DataTableHero.js +154 -41
- package/dist/views/list/ListHeader.js +9 -1
- package/package.json +5 -2
|
@@ -248,7 +248,7 @@ function toJsonString(v) {
|
|
|
248
248
|
return typeof v === 'string' ? v : JSON.stringify(v);
|
|
249
249
|
}
|
|
250
250
|
/** --------------------------------------------------------------------------------------- **/
|
|
251
|
-
export function SchemaForm({ model, schemaOverride, initialValues, submitLabel = 'Save', busy = false, showReset = true, onSubmit, fieldErrors, }) {
|
|
251
|
+
export function SchemaForm({ model, schemaOverride, initialValues, submitLabel = 'Save', busy = false, showReset = true, onSubmit, fieldErrors, isEdit: isEditProp, }) {
|
|
252
252
|
const mapsKey = useGoogleMapsKey();
|
|
253
253
|
const formUid = useId();
|
|
254
254
|
const { items } = useSelector((s) => s.schemas);
|
|
@@ -286,6 +286,16 @@ export function SchemaForm({ model, schemaOverride, initialValues, submitLabel =
|
|
|
286
286
|
// For auto-slugging
|
|
287
287
|
const [manuallyEditedSlugs, setManuallyEditedSlugs] = useState(new Set());
|
|
288
288
|
const [pendingSlugChecks, setPendingSlugChecks] = useState({});
|
|
289
|
+
const [internalFieldErrors, setInternalFieldErrors] = useState(fieldErrors ?? []);
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
setInternalFieldErrors(fieldErrors ?? []);
|
|
292
|
+
}, [fieldErrors]);
|
|
293
|
+
const getFieldError = (fieldName) => {
|
|
294
|
+
const match = internalFieldErrors.find((fe) => fe &&
|
|
295
|
+
(fe.field === fieldName ||
|
|
296
|
+
fe.field.toLowerCase() === fieldName.toLowerCase()));
|
|
297
|
+
return match?.message;
|
|
298
|
+
};
|
|
289
299
|
useEffect(() => {
|
|
290
300
|
const timer = setTimeout(async () => {
|
|
291
301
|
const entries = Object.entries(pendingSlugChecks);
|
|
@@ -308,8 +318,9 @@ export function SchemaForm({ model, schemaOverride, initialValues, submitLabel =
|
|
|
308
318
|
return () => clearTimeout(timer);
|
|
309
319
|
}, [pendingSlugChecks, model, form.id, form._id, form]);
|
|
310
320
|
const [error, setError] = useState();
|
|
311
|
-
const isEdit = useMemo(() =>
|
|
312
|
-
(initialValues
|
|
321
|
+
const isEdit = useMemo(() => isEditProp ??
|
|
322
|
+
!!(initialValues &&
|
|
323
|
+
(initialValues.id || initialValues._id)), [initialValues, isEditProp]);
|
|
313
324
|
useEffect(() => {
|
|
314
325
|
const next = { ...(initialValues ?? {}) };
|
|
315
326
|
if ('password' in next)
|
|
@@ -333,13 +344,19 @@ export function SchemaForm({ model, schemaOverride, initialValues, submitLabel =
|
|
|
333
344
|
.map(toName)
|
|
334
345
|
.filter((s) => !!s)
|
|
335
346
|
.map((s) => s.toLowerCase());
|
|
336
|
-
const result = configured.length > 0 ? configured : ['admin', 'superadmin'];
|
|
347
|
+
const result = configured.length > 0 ? configured : ['admin', 'superadmin', 'super-admin', 'super admin', 'developer'];
|
|
337
348
|
return result;
|
|
338
349
|
}, [schema]);
|
|
339
350
|
const canBypassPrivacy = useMemo(() => {
|
|
340
|
-
const userRolesLC = sessionRole.map((r) => r.toLowerCase());
|
|
341
|
-
const
|
|
342
|
-
|
|
351
|
+
const userRolesLC = sessionRole.map((r) => r.toLowerCase().replace(/[\s\-_]+/g, ''));
|
|
352
|
+
const normalizedBypass = bypassRoles.map(r => r.toLowerCase().replace(/[\s\-_]+/g, ''));
|
|
353
|
+
if (userRolesLC.some((r) => normalizedBypass.includes(r))) {
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
if (userRolesLC.some(r => r.includes('admin') || r.includes('developer') || r.includes('owner') || r.includes('super'))) {
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
343
360
|
}, [sessionRole, bypassRoles]);
|
|
344
361
|
const fields = useMemo(() => {
|
|
345
362
|
const allFields = Object.entries(schema.attributes);
|
|
@@ -401,6 +418,8 @@ export function SchemaForm({ model, schemaOverride, initialValues, submitLabel =
|
|
|
401
418
|
}, [schema, canBypassPrivacy, isEdit]);
|
|
402
419
|
const gridClass = 'grid gap-4 grid-cols-2';
|
|
403
420
|
const handleChange = (name, value) => {
|
|
421
|
+
setInternalFieldErrors((prev) => prev.filter((fe) => fe.field !== name &&
|
|
422
|
+
fe.field.toLowerCase() !== name.toLowerCase()));
|
|
404
423
|
setForm((f) => {
|
|
405
424
|
const next = { ...f, [name]: value };
|
|
406
425
|
if (schema) {
|
|
@@ -493,7 +512,7 @@ export function SchemaForm({ model, schemaOverride, initialValues, submitLabel =
|
|
|
493
512
|
[fieldName]: fieldValue,
|
|
494
513
|
};
|
|
495
514
|
handleChange(name, next);
|
|
496
|
-
}, disabled: busy, inputClassNames: inputClassNames, selectClassNames: selectClassNames, mapsKey: mapsKey, availableSchemas: items, formState: it }) }, `${name}-${idx}-${k}`));
|
|
515
|
+
}, disabled: busy, inputClassNames: inputClassNames, selectClassNames: selectClassNames, mapsKey: mapsKey, availableSchemas: items, formState: it, isEdit: isEdit, canBypassPrivacy: canBypassPrivacy, isInvalid: Boolean(getFieldError(`${name}.${idx}.${k}`) || getFieldError(`${name}.${k}`) || getFieldError(k)), errorMessage: getFieldError(`${name}.${idx}.${k}`) || getFieldError(`${name}.${k}`) || getFieldError(k) }) }, `${name}-${idx}-${k}`));
|
|
497
516
|
}), _jsx("div", { className: "col-span-2 flex justify-between", children: _jsx(Button, { size: "sm", variant: "flat", color: "danger", isDisabled: !canRemove, onPress: () => {
|
|
498
517
|
const next = items.slice();
|
|
499
518
|
next.splice(idx, 1);
|
|
@@ -513,7 +532,7 @@ export function SchemaForm({ model, schemaOverride, initialValues, submitLabel =
|
|
|
513
532
|
return (_jsx("div", { className: isRich ? 'col-span-2' : 'col-span-1', children: _jsx(SchemaField, { uid: `${formUid}-${name}`, name: k, attr: a, value: obj?.[k], onChange: (fieldName, fieldValue) => {
|
|
514
533
|
const next = { ...(obj || {}), [fieldName]: fieldValue };
|
|
515
534
|
handleChange(name, next);
|
|
516
|
-
}, disabled: busy, inputClassNames: inputClassNames, selectClassNames: selectClassNames, mapsKey: mapsKey, availableSchemas: items, formState: obj }) }, `${name}-${k}`));
|
|
535
|
+
}, disabled: busy, inputClassNames: inputClassNames, selectClassNames: selectClassNames, mapsKey: mapsKey, availableSchemas: items, formState: obj, isEdit: isEdit, canBypassPrivacy: canBypassPrivacy, isInvalid: Boolean(getFieldError(`${name}.${k}`) || getFieldError(k)), errorMessage: getFieldError(`${name}.${k}`) || getFieldError(k) }) }, `${name}-${k}`));
|
|
517
536
|
}) })] }, name));
|
|
518
537
|
}
|
|
519
538
|
// let password reflect local state, but never prefill
|
|
@@ -525,58 +544,94 @@ export function SchemaForm({ model, schemaOverride, initialValues, submitLabel =
|
|
|
525
544
|
// If it's a rich text field, force full width
|
|
526
545
|
const isRich = attr?.rich === true;
|
|
527
546
|
const dynamicColClass = isRich ? 'col-span-2' : colClass;
|
|
547
|
+
const fieldErr = getFieldError(name);
|
|
528
548
|
return (_jsx("div", { className: dynamicColClass, children: _jsx(SchemaField, { uid: formUid, name: name, attr: attr, inputClassNames: inputClassNames, selectClassNames: selectClassNames, value: baseValue, onChange: handleChange, disabled: busy,
|
|
529
549
|
// pass schemas for slash cmd
|
|
530
|
-
availableSchemas: items, mapsKey: mapsKey, formState: form }) }, name));
|
|
550
|
+
availableSchemas: items, mapsKey: mapsKey, formState: form, isEdit: isEdit, canBypassPrivacy: canBypassPrivacy, isInvalid: Boolean(fieldErr), errorMessage: fieldErr }) }, name));
|
|
531
551
|
}), error && (_jsx("div", { className: "col-span-2", children: _jsx("div", { className: "text-danger text-sm", children: error }) })), _jsxs("div", { className: "flex gap-2 col-span-2", children: [_jsx(Button, { type: "submit", color: "primary", isLoading: busy, children: submitLabel }), showReset && (_jsx(Button, { type: "reset", variant: "flat", isDisabled: busy, children: "Reset" }))] })] }));
|
|
532
552
|
}
|
|
533
|
-
function
|
|
553
|
+
function isAttrReadOnly(attr, isEdit = false) {
|
|
554
|
+
if (!attr)
|
|
555
|
+
return false;
|
|
556
|
+
const a = Array.isArray(attr) ? attr[0] : attr;
|
|
557
|
+
if (a?.readOnly === true)
|
|
558
|
+
return true;
|
|
559
|
+
if (typeof a?.readOnly === 'object' && a?.readOnly !== null) {
|
|
560
|
+
return isEdit ? !!a.readOnly.edit : !!a.readOnly.create;
|
|
561
|
+
}
|
|
562
|
+
if (typeof a?.editable === 'boolean') {
|
|
563
|
+
return isEdit ? !a.editable : false;
|
|
564
|
+
}
|
|
565
|
+
if (typeof a?.editable === 'object' && a?.editable !== null) {
|
|
566
|
+
return isEdit ? !a.editable.edit : !a.editable.create;
|
|
567
|
+
}
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNames, selectClassNames, mapsKey, availableSchemas, formState, isEdit = false, canBypassPrivacy = false, isInvalid = false, errorMessage, }) {
|
|
534
571
|
const id = `${uid}-${name}`;
|
|
535
572
|
const label = attr?.label ?? formatLabel(name);
|
|
536
|
-
const
|
|
573
|
+
const isReadOnly = isAttrReadOnly(attr, isEdit);
|
|
574
|
+
const isEffectiveDisabled = disabled || isReadOnly;
|
|
575
|
+
const isPasswordField = name.toLowerCase() === 'password' ||
|
|
576
|
+
attr?.format === 'password' ||
|
|
577
|
+
attr?.writeOnly === true;
|
|
578
|
+
let required = !!attr?.required ||
|
|
537
579
|
name.toLowerCase() === 'slug' ||
|
|
538
580
|
!!attr?.populateSlugFrom;
|
|
581
|
+
if (isEdit) {
|
|
582
|
+
if (isPasswordField) {
|
|
583
|
+
required = false;
|
|
584
|
+
}
|
|
585
|
+
else if (canBypassPrivacy && attr?.private === true) {
|
|
586
|
+
required = false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
539
589
|
const description = attr?.description;
|
|
540
590
|
// Prefer ref over enum. Only use enum when there is NO ref.
|
|
541
|
-
const enumVals = attr?.ref
|
|
591
|
+
const enumVals = attr?.ref || attr?.refPath
|
|
542
592
|
? undefined
|
|
543
593
|
: attr?.enum;
|
|
544
|
-
const isPasswordField = name.toLowerCase() === 'password' ||
|
|
545
|
-
attr?.format === 'password' ||
|
|
546
|
-
attr?.writeOnly === true;
|
|
547
594
|
// RICH TEXT EDITOR CHECK
|
|
548
595
|
if (attr?.rich === true) {
|
|
549
596
|
// Extract speciality slug from form state
|
|
550
597
|
const specialitySlug = formState?.speciality?.slug || formState?.slug || '';
|
|
551
|
-
return (_jsx(TiptapEditor, { value: value, onChange: (html) => onChange(name, html), placeholder: label, availableSchemas: availableSchemas, currentSpeciality: specialitySlug }));
|
|
598
|
+
return (_jsxs("div", { className: "w-full flex flex-col gap-1", children: [_jsx(TiptapEditor, { value: value, onChange: (html) => onChange(name, html), placeholder: label, availableSchemas: availableSchemas, currentSpeciality: specialitySlug, className: isInvalid ? 'border-danger ring-1 ring-danger rounded-lg' : undefined }), errorMessage && (_jsx("p", { className: "text-danger text-xs mt-1", children: errorMessage }))] }));
|
|
552
599
|
}
|
|
553
600
|
// --- 1) Array of references → multi select
|
|
554
|
-
const refArray = getRefArraySpec(attr);
|
|
601
|
+
const refArray = getRefArraySpec(attr, formState);
|
|
555
602
|
if (refArray) {
|
|
556
|
-
return (_jsx(RefSelect, { name: name, label: attr[0]?.label ?? label, refModel: refArray.ref, showKey: refArray.show ?? 'name', description: attr?.description, value:
|
|
603
|
+
return (_jsx(RefSelect, { name: name, label: attr[0]?.label ?? label, refModel: refArray.ref, showKey: refArray.show ?? 'name', description: attr?.description, value: value, onChange: (ids) => onChange(name, ids), multiple: true, disabled: disabled || isReadOnly || !refArray.ref, required: attr[0]?.required, pageSize: attr[0]?.pageSize, where: attr[0]?.where || attr?.where, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
557
604
|
}
|
|
558
605
|
// --- 2) Single reference → single select
|
|
559
|
-
const refSingle = getRefSingleSpec(attr);
|
|
606
|
+
const refSingle = getRefSingleSpec(attr, formState);
|
|
560
607
|
if (refSingle) {
|
|
561
|
-
return (_jsx(RefSelect, { name: name, label: label, refModel: refSingle.ref, showKey: refSingle.show ?? 'name', description: attr?.description, value:
|
|
608
|
+
return (_jsx(RefSelect, { name: name, label: label, refModel: refSingle.ref, showKey: refSingle.show ?? 'name', description: attr?.description, value: value, onChange: (id) => onChange(name, id), multiple: false, disabled: disabled || isReadOnly || !refSingle.ref, required: required, pageSize: attr?.pageSize, where: attr?.where, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
562
609
|
}
|
|
563
610
|
const head = Array.isArray(attr) ? (attr?.[0] ?? {}) : (attr ?? {});
|
|
564
611
|
const isPhoneField = isPhoneAttr(name, attr);
|
|
565
612
|
const rawMask = typeof head?.mask === 'string' ? head.mask : '';
|
|
566
613
|
const hasSlots = /[Xx9#_]/.test(rawMask);
|
|
567
614
|
const phoneMask = hasSlots ? rawMask : 'xxx-xxxx-xxxx';
|
|
615
|
+
const fieldType = String(attr?.fieldType || attr?.filedType || '').toLowerCase();
|
|
568
616
|
const isFileField = String(attr?.format || '').toLowerCase() === 'file' ||
|
|
569
|
-
|
|
617
|
+
fieldType === 'file' ||
|
|
618
|
+
fieldType === 'image' ||
|
|
619
|
+
/(file|image|avatar|photo|picture|logo|siteLogo|sitelogo|profilepicture|bannerImage|catalog|brochure|document|attachment|pdf|upload|floorplan|floorPlan)s?$/i.test(name);
|
|
570
620
|
if (isFileField) {
|
|
571
621
|
const head = Array.isArray(attr) ? attr[0] : attr;
|
|
572
|
-
const multiple = Array.isArray(attr) || Boolean(head?.multiple);
|
|
622
|
+
const multiple = Array.isArray(attr) || String(head?.type || '').toLowerCase() === 'array' || Boolean(head?.multiple);
|
|
573
623
|
const fileTypes = head?.fileTypes;
|
|
574
624
|
const maxFileSize = typeof head?.maxFileSize === 'number' ? head.maxFileSize : undefined;
|
|
575
625
|
const maxFilesCount = multiple && typeof head?.maxFilesCount === 'number'
|
|
576
626
|
? Math.max(1, head.maxFilesCount)
|
|
577
627
|
: undefined;
|
|
578
|
-
|
|
628
|
+
let isRequired = !!attr?.required ||
|
|
579
629
|
(Array.isArray(attr) ? !!head?.required : false);
|
|
630
|
+
if (isEdit && canBypassPrivacy) {
|
|
631
|
+
if (attr?.private === true || head?.private === true) {
|
|
632
|
+
isRequired = false;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
580
635
|
const controlledValue = multiple
|
|
581
636
|
? Array.isArray(value)
|
|
582
637
|
? value
|
|
@@ -586,16 +641,16 @@ function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNam
|
|
|
586
641
|
: typeof value === 'string'
|
|
587
642
|
? value
|
|
588
643
|
: null;
|
|
589
|
-
return (_jsx(FileUploader, { name: name, label: label, value: controlledValue, onChange: (v) => onChange(name, v), required: isRequired, disabled: disabled, accept: fileTypes, maxSizeBytes: maxFileSize, multiple: multiple, maxFilesCount: maxFilesCount, description: attr?.description, className: "w-full" }));
|
|
644
|
+
return (_jsx(FileUploader, { name: name, label: label, value: controlledValue, onChange: (v) => onChange(name, v), required: isRequired, disabled: disabled, accept: fileTypes, maxSizeBytes: maxFileSize, multiple: multiple, maxFilesCount: maxFilesCount, isImage: fieldType === 'image' || /(image|photo|avatar|logo|picture|thumbnail|icon)/i.test(name), description: attr?.description, className: "w-full", isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
590
645
|
}
|
|
591
646
|
if (isPasswordField) {
|
|
592
|
-
return (_jsx(PasswordInput, { id: id, name: name, label: label, value: typeof value === 'string' ? value : '', onChange: (v) => onChange(name, v), disabled: disabled, required: required, description: description, className: "w-full", classNames: inputClassNames, autoComplete: "new-password" }));
|
|
647
|
+
return (_jsx(PasswordInput, { id: id, name: name, label: label, value: typeof value === 'string' ? value : '', onChange: (v) => onChange(name, v), disabled: disabled, required: required, description: description, className: "w-full", classNames: inputClassNames, autoComplete: "new-password", isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
593
648
|
}
|
|
594
649
|
if (isPhoneField) {
|
|
595
650
|
const rawDigits = typeof value === 'string' || typeof value === 'number'
|
|
596
651
|
? String(value).replace(/\D/g, '')
|
|
597
652
|
: '';
|
|
598
|
-
return (_jsx(PhoneInput, { id: id, name: name, label: label, mask: phoneMask, value: rawDigits, onChange: (raw) => onChange(name, raw), disabled: disabled, required: required, description: description, className: "w-full", classNames: inputClassNames, minLength: head?.minLength, maxLength: head?.maxLength, regex: head?.pattern }));
|
|
653
|
+
return (_jsx(PhoneInput, { id: id, name: name, label: label, mask: phoneMask, value: rawDigits, onChange: (raw) => onChange(name, raw), disabled: disabled, required: required, description: description, className: "w-full", classNames: inputClassNames, minLength: head?.minLength, maxLength: head?.maxLength, regex: head?.pattern, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
599
654
|
}
|
|
600
655
|
const populateField = getPopulateTarget(attr);
|
|
601
656
|
// Address → Autocomplete
|
|
@@ -610,7 +665,7 @@ function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNam
|
|
|
610
665
|
if (populateField) {
|
|
611
666
|
onChange(populateField, `${latlng.lat.toFixed(6)},${latlng.lng.toFixed(6)}`);
|
|
612
667
|
}
|
|
613
|
-
}, disabled: disabled, required: required, className: "w-full", classNames: inputClassNames, countryCodes: attr?.countryCodes, limit: attr?.limit ?? 8 }));
|
|
668
|
+
}, disabled: disabled, required: required, className: "w-full", classNames: inputClassNames, countryCodes: attr?.countryCodes, limit: attr?.limit ?? 8, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
614
669
|
}
|
|
615
670
|
/** ----------------------- NEW: DATE / TIME / RANGE RENDERERS ----------------------- **/
|
|
616
671
|
// 1) TIME-ONLY RANGE → two TimeInput fields (AM/PM UI)
|
|
@@ -630,7 +685,7 @@ function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNam
|
|
|
630
685
|
end: serializeTime(t),
|
|
631
686
|
});
|
|
632
687
|
};
|
|
633
|
-
return (_jsx("div", { className: "w-full", children: _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(TimeInput, { variant: "bordered", label: `${label} (Start)`, labelPlacement: "outside", className: "w-full", isDisabled: disabled, isRequired: required, description: description, value: startT ?? undefined, onChange: setStart, granularity: "minute", hourCycle: 12 }), _jsx(TimeInput, { variant: "bordered", label: `${label} (End)`, labelPlacement: "outside", className: "w-full", isDisabled: disabled, isRequired: required, description: description, value: endT ?? undefined, onChange: setEnd, granularity: "minute", hourCycle: 12 })] }) }));
|
|
688
|
+
return (_jsx("div", { className: "w-full", children: _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(TimeInput, { variant: "bordered", label: `${label} (Start)`, labelPlacement: "outside", className: "w-full", isDisabled: disabled, isRequired: required, description: description, value: startT ?? undefined, onChange: setStart, granularity: "minute", hourCycle: 12, isInvalid: isInvalid, errorMessage: errorMessage }), _jsx(TimeInput, { variant: "bordered", label: `${label} (End)`, labelPlacement: "outside", className: "w-full", isDisabled: disabled, isRequired: required, description: description, value: endT ?? undefined, onChange: setEnd, granularity: "minute", hourCycle: 12, isInvalid: isInvalid })] }) }));
|
|
634
689
|
}
|
|
635
690
|
// 2) DATE RANGE (default; supports withTime)
|
|
636
691
|
if (isRangeType(attr)) {
|
|
@@ -643,7 +698,7 @@ function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNam
|
|
|
643
698
|
return onChange(name, null);
|
|
644
699
|
const { start, end } = val;
|
|
645
700
|
onChange(name, serializeRangeOut(start, end, withTime));
|
|
646
|
-
}, granularity: withTime ? 'minute' : 'day', hourCycle: withTime ? 24 : undefined }));
|
|
701
|
+
}, granularity: withTime ? 'minute' : 'day', hourCycle: withTime ? 24 : undefined, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
647
702
|
}
|
|
648
703
|
// 3) SINGLE DATE (supports withTime)
|
|
649
704
|
if (isDateType(attr)) {
|
|
@@ -651,12 +706,12 @@ function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNam
|
|
|
651
706
|
const parsed = parseDateString(value, withTime);
|
|
652
707
|
return (_jsx(DatePicker, { variant: "bordered", label: label, labelPlacement: "outside", className: "w-full", isDisabled: disabled, isRequired: required, description: description, value: parsed ?? null, onChange: (v) => onChange(name, v
|
|
653
708
|
? serializeDate(v, withTime)
|
|
654
|
-
: ''), granularity: withTime ? 'minute' : 'day', hourCycle: withTime ? 24 : undefined }));
|
|
709
|
+
: ''), granularity: withTime ? 'minute' : 'day', hourCycle: withTime ? 24 : undefined, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
655
710
|
}
|
|
656
711
|
// 4) SINGLE TIME (uses your existing isTimeType)
|
|
657
712
|
if (isTimeType(attr)) {
|
|
658
713
|
const tVal = parseTimeString(value);
|
|
659
|
-
return (_jsx(TimeInput, { variant: "bordered", label: label, labelPlacement: "outside", className: "w-full", isDisabled: disabled, isRequired: required, description: description, value: tVal ?? undefined, onChange: (t) => onChange(name, serializeTime(t)), granularity: "minute", hourCycle: 12 }));
|
|
714
|
+
return (_jsx(TimeInput, { variant: "bordered", label: label, labelPlacement: "outside", className: "w-full", isDisabled: disabled, isRequired: required, description: description, value: tVal ?? undefined, onChange: (t) => onChange(name, serializeTime(t)), granularity: "minute", hourCycle: 12, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
660
715
|
}
|
|
661
716
|
const normalize = (s) => (s ?? '').toLowerCase().trim();
|
|
662
717
|
const canSeeOption = (opt) => {
|
|
@@ -690,10 +745,10 @@ function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNam
|
|
|
690
745
|
// Enum → RadioGroup (≤4) or Select (>4)
|
|
691
746
|
if (enumVals && enumVals.length > 0) {
|
|
692
747
|
if (enumVals.length <= 4) {
|
|
693
|
-
return (_jsx(RadioGroup, { id: id, name: name, label: label, orientation: "horizontal", value: (value ?? ''), onValueChange: (v) => onChange(name, v), isDisabled:
|
|
748
|
+
return (_jsx(RadioGroup, { id: id, name: name, label: label, orientation: "horizontal", value: (value ?? ''), onValueChange: (v) => onChange(name, v), isDisabled: isEffectiveDisabled, description: description, isRequired: required, className: "w-full", isInvalid: isInvalid, errorMessage: errorMessage, children: enumVals.map((opt) => {
|
|
694
749
|
if (!canSeeOption(opt))
|
|
695
750
|
return null;
|
|
696
|
-
return (_jsx(Radio, { value: opt, children: opt }, opt));
|
|
751
|
+
return (_jsx(Radio, { value: opt, isDisabled: isEffectiveDisabled, children: opt }, opt));
|
|
697
752
|
}) }));
|
|
698
753
|
}
|
|
699
754
|
return (_jsx(Select, { isVirtualized: true, variant: "bordered", classNames: selectClassNames, labelPlacement: "outside", id: id, name: name, label: label, selectedKeys: value ? new Set([String(value)]) : new Set(), onSelectionChange: (keys) => {
|
|
@@ -701,29 +756,25 @@ function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNam
|
|
|
701
756
|
return;
|
|
702
757
|
const v = Array.from(keys)[0];
|
|
703
758
|
onChange(name, v != null ? String(v) : '');
|
|
704
|
-
}, isDisabled:
|
|
759
|
+
}, isDisabled: isEffectiveDisabled, description: description, className: "w-full", selectionMode: "single", isRequired: required, isInvalid: isInvalid, errorMessage: errorMessage, children: enumVals.map((opt) => (_jsx(SelectItem, { textValue: opt, children: opt }, opt))) }));
|
|
705
760
|
}
|
|
706
761
|
// Boolean → Checkbox
|
|
707
762
|
if (inputTypeFor(attr) === 'checkbox') {
|
|
708
|
-
return (_jsxs("div", { className: "w-full", children: [_jsx(Checkbox, { id: id, name: name, isSelected: !!value, onValueChange: (v) => onChange(name, v), isDisabled:
|
|
763
|
+
return (_jsxs("div", { className: "w-full", children: [_jsx(Checkbox, { id: id, name: name, isSelected: !!value, onValueChange: (v) => onChange(name, v), isDisabled: isEffectiveDisabled, isRequired: required, isInvalid: isInvalid, children: label }), errorMessage ? (_jsx("p", { className: "text-danger text-xs mt-1", children: errorMessage })) : description ? (_jsx("p", { className: "text-default-500 text-xs mt-1", children: description })) : null] }));
|
|
709
764
|
}
|
|
710
765
|
// Textarea
|
|
711
766
|
if (inputTypeFor(attr) === 'textarea') {
|
|
712
|
-
return (_jsx(Textarea, { variant: "bordered", classNames: inputClassNames, id: id, name: name, label: label, labelPlacement: "outside", value: value ?? '', onChange: (e) => onChange(name, e.target.value), isDisabled:
|
|
767
|
+
return (_jsx(Textarea, { variant: "bordered", classNames: inputClassNames, id: id, name: name, label: label, labelPlacement: "outside", value: value ?? '', onChange: (e) => onChange(name, e.target.value), isDisabled: isEffectiveDisabled, minRows: 3, isReadOnly: isReadOnly, description: description, className: "w-full", isRequired: required, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
713
768
|
}
|
|
714
769
|
function attrHead(attr) {
|
|
715
770
|
return Array.isArray(attr) ? attr[0] : attr;
|
|
716
771
|
}
|
|
717
|
-
function isAttrReadOnly(attr) {
|
|
718
|
-
const a = attrHead(attr);
|
|
719
|
-
return !!a?.readOnly;
|
|
720
|
-
}
|
|
721
772
|
// Array (non-ref) → comma-separated input
|
|
722
773
|
if (Array.isArray(attr)) {
|
|
723
|
-
return (_jsx(Input, { variant: "bordered", classNames: inputClassNames, id: id, name: name, label: label, isReadOnly:
|
|
774
|
+
return (_jsx(Input, { variant: "bordered", classNames: inputClassNames, id: id, name: name, label: label, isReadOnly: isReadOnly, labelPlacement: "outside-top", placeholder: "comma-separated values", value: Array.isArray(value) ? value.join(', ') : (value ?? ''), onChange: (e) => onChange(name, e.target.value
|
|
724
775
|
.split(',')
|
|
725
776
|
.map((s) => s.trim())
|
|
726
|
-
.filter(Boolean)), isDisabled:
|
|
777
|
+
.filter(Boolean)), isDisabled: isEffectiveDisabled, description: description ?? 'Enter values separated by commas.', className: "w-full", isRequired: required, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
727
778
|
}
|
|
728
779
|
function pickInputType(x, fieldName) {
|
|
729
780
|
if (fieldName.toLowerCase() === 'password')
|
|
@@ -741,9 +792,9 @@ function SchemaField({ uid, name, attr, value, onChange, disabled, inputClassNam
|
|
|
741
792
|
}
|
|
742
793
|
}
|
|
743
794
|
const inputType = pickInputType(inputTypeFor(attr), name);
|
|
744
|
-
return (_jsx(Input, { variant: "bordered", classNames: inputClassNames, id: id, name: name, label: label, isReadOnly:
|
|
795
|
+
return (_jsx(Input, { variant: "bordered", classNames: inputClassNames, id: id, name: name, label: label, isReadOnly: isReadOnly, labelPlacement: "outside-top", type: inputType, value: value ?? '', onChange: (e) => onChange(name, inputType === 'number'
|
|
745
796
|
? numberOrEmpty(e.target.value)
|
|
746
|
-
: e.target.value), isDisabled:
|
|
797
|
+
: e.target.value), isDisabled: isEffectiveDisabled, description: description, className: "w-full", isRequired: required, minLength: head?.minLength, maxLength: head?.maxLength, pattern: head?.pattern, autoComplete: name.toLowerCase() === 'password' ? 'new-password' : undefined, isInvalid: isInvalid, errorMessage: errorMessage }));
|
|
747
798
|
}
|
|
748
799
|
function isHiddenAttr(attr, isEdit) {
|
|
749
800
|
const head = Array.isArray(attr) ? (attr?.[0] ?? {}) : (attr ?? {});
|
|
@@ -773,25 +824,51 @@ function isPasswordAttr(name, attr) {
|
|
|
773
824
|
const byWriteOnly = attr?.writeOnly === true;
|
|
774
825
|
return byName || byFormat || byWriteOnly;
|
|
775
826
|
}
|
|
776
|
-
function getRefArraySpec(attr) {
|
|
827
|
+
function getRefArraySpec(attr, formState) {
|
|
777
828
|
if (!Array.isArray(attr) || attr.length === 0)
|
|
778
829
|
return null;
|
|
779
830
|
const head = attr[0];
|
|
780
|
-
if (head
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
831
|
+
if (!head || typeof head !== 'object')
|
|
832
|
+
return null;
|
|
833
|
+
const t = String(head.type || '').toLowerCase();
|
|
834
|
+
const isRefType = t === 'objectid' || t === 'ref' || !!head.ref || !!head.refPath;
|
|
835
|
+
if (!isRefType)
|
|
836
|
+
return null;
|
|
837
|
+
let targetRef = typeof head.ref === 'string' && head.ref.trim() ? head.ref.trim() : undefined;
|
|
838
|
+
if (head.refPath && formState) {
|
|
839
|
+
const rawVal = formState[head.refPath];
|
|
840
|
+
if (typeof rawVal === 'string' && rawVal.trim()) {
|
|
841
|
+
targetRef = rawVal.trim();
|
|
842
|
+
}
|
|
843
|
+
else if (rawVal && typeof rawVal === 'object') {
|
|
844
|
+
targetRef = rawVal.modelName || rawVal.name || rawVal.slug || (rawVal.id || rawVal._id ? undefined : String(rawVal));
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (head.refPath || head.ref) {
|
|
848
|
+
return { ref: targetRef || '', show: head.show };
|
|
785
849
|
}
|
|
786
850
|
return null;
|
|
787
851
|
}
|
|
788
|
-
function getRefSingleSpec(attr) {
|
|
852
|
+
function getRefSingleSpec(attr, formState) {
|
|
789
853
|
if (!attr || typeof attr !== 'object' || Array.isArray(attr))
|
|
790
854
|
return null;
|
|
791
855
|
const a = attr;
|
|
792
856
|
const t = String(a.type || '').toLowerCase();
|
|
793
|
-
|
|
794
|
-
|
|
857
|
+
const isRefType = t === 'objectid' || t === 'ref' || !!a.ref || !!a.refPath;
|
|
858
|
+
if (!isRefType)
|
|
859
|
+
return null;
|
|
860
|
+
let targetRef = typeof a.ref === 'string' && a.ref.trim() ? a.ref.trim() : undefined;
|
|
861
|
+
if (a.refPath && formState) {
|
|
862
|
+
const rawVal = formState[a.refPath];
|
|
863
|
+
if (typeof rawVal === 'string' && rawVal.trim()) {
|
|
864
|
+
targetRef = rawVal.trim();
|
|
865
|
+
}
|
|
866
|
+
else if (rawVal && typeof rawVal === 'object') {
|
|
867
|
+
targetRef = rawVal.modelName || rawVal.name || rawVal.slug || (rawVal.id || rawVal._id ? undefined : String(rawVal));
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (a.refPath || a.ref) {
|
|
871
|
+
return { ref: targetRef || '', show: a.show };
|
|
795
872
|
}
|
|
796
873
|
return null;
|
|
797
874
|
}
|
|
@@ -877,7 +954,7 @@ async function findUniqueSlug(model, baseSlug, currentId) {
|
|
|
877
954
|
const checkSlug = attempt === 0 ? baseSlug : `${baseSlug}-${attempt}`;
|
|
878
955
|
try {
|
|
879
956
|
// Check if slug exists in the model
|
|
880
|
-
const res = await api.list(model, { page: 0, limit: 1, where: { slug: checkSlug } });
|
|
957
|
+
const res = await api.list(model, { page: 0, limit: 1, where: { slug: checkSlug }, select: 'id,slug' });
|
|
881
958
|
const existing = res.data?.[0];
|
|
882
959
|
if (!existing ||
|
|
883
960
|
(currentId && String(existing.id || existing._id) === String(currentId))) {
|
|
@@ -2,15 +2,21 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { useDispatch, useSelector } from 'react-redux';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
5
|
+
import Link from 'next/link';
|
|
6
|
+
import { usePathname, useRouter } from 'next/navigation';
|
|
7
7
|
import { Button } from '@heroui/react';
|
|
8
|
+
import { clearSession } from '../state/sessionSlice';
|
|
8
9
|
import { AuthClient } from '../lib/AuthClient';
|
|
9
10
|
import { RealtimeClient } from '../lib/RealtimeClient';
|
|
10
|
-
import
|
|
11
|
+
import clsx from 'clsx';
|
|
12
|
+
import { isTailwindClass, } from '../providers/themeResolver';
|
|
13
|
+
import { useNextMin } from '../providers/NextMinProvider';
|
|
14
|
+
import { useAppNavigation } from '../hooks/useAppNavigation';
|
|
11
15
|
export function Sidebar() {
|
|
12
16
|
const router = useRouter();
|
|
13
17
|
const dispatch = useDispatch();
|
|
18
|
+
const pathname = usePathname();
|
|
19
|
+
const navigateTo = useAppNavigation();
|
|
14
20
|
const system = useSelector((s) => s.nextMin.system);
|
|
15
21
|
const systemStatus = useSelector((s) => s.nextMin.status);
|
|
16
22
|
const { items, status, error } = useSelector((s) => s.schemas);
|
|
@@ -68,13 +74,96 @@ export function Sidebar() {
|
|
|
68
74
|
const first = arr.find((u) => typeof u === 'string' && u.trim().length > 0);
|
|
69
75
|
return first || undefined;
|
|
70
76
|
}, [system]);
|
|
71
|
-
|
|
77
|
+
const { theme } = useNextMin();
|
|
78
|
+
const sidebarBgClass = theme?.sidebarColor && isTailwindClass(theme.sidebarColor)
|
|
79
|
+
? theme.sidebarColor
|
|
80
|
+
: '';
|
|
81
|
+
const sidebarWidthClass = theme?.sidebarWidth && isTailwindClass(theme.sidebarWidth)
|
|
82
|
+
? theme.sidebarWidth
|
|
83
|
+
: 'w-64';
|
|
84
|
+
const borderClass = theme?.sidebarPosition === 'right' ? 'border-l' : 'border-r';
|
|
85
|
+
// Active configurations
|
|
86
|
+
const activeBgClass = theme?.activeMenuBgColor && isTailwindClass(theme.activeMenuBgColor)
|
|
87
|
+
? theme.activeMenuBgColor
|
|
88
|
+
: 'bg-primary/10 text-primary font-medium';
|
|
89
|
+
const activeBgStyle = theme?.activeMenuBgColor && !isTailwindClass(theme.activeMenuBgColor)
|
|
90
|
+
? { backgroundColor: 'var(--nm-active-menu-bg)' }
|
|
91
|
+
: {};
|
|
92
|
+
const activeTextClass = theme?.activeMenuTextColor && isTailwindClass(theme.activeMenuTextColor)
|
|
93
|
+
? theme.activeMenuTextColor
|
|
94
|
+
: '';
|
|
95
|
+
const activeTextStyle = theme?.activeMenuTextColor && !isTailwindClass(theme.activeMenuTextColor)
|
|
96
|
+
? { color: 'var(--nm-active-menu-text)' }
|
|
97
|
+
: {};
|
|
98
|
+
// Inactive configurations
|
|
99
|
+
const inactiveTextClass = theme?.menuTextColor && isTailwindClass(theme.menuTextColor)
|
|
100
|
+
? theme.menuTextColor
|
|
101
|
+
: 'text-gray-700';
|
|
102
|
+
const inactiveTextStyle = theme?.menuTextColor && !isTailwindClass(theme.menuTextColor)
|
|
103
|
+
? { color: 'var(--nm-menu-text)' }
|
|
104
|
+
: {};
|
|
105
|
+
// Hover configurations
|
|
106
|
+
const hoverBgClass = theme?.menuHoverBgColor && isTailwindClass(theme.menuHoverBgColor)
|
|
107
|
+
? theme.menuHoverBgColor
|
|
108
|
+
: 'hover:bg-gray-200';
|
|
109
|
+
const hoverTextClass = theme?.menuHoverTextColor && isTailwindClass(theme.menuHoverTextColor)
|
|
110
|
+
? theme.menuHoverTextColor
|
|
111
|
+
: '';
|
|
112
|
+
const renderMenuItem = (href, label) => {
|
|
113
|
+
const isRootOrDashboard = href === '/admin' || href === '/admin/dashboard';
|
|
114
|
+
const isActive = isRootOrDashboard
|
|
115
|
+
? pathname === href
|
|
116
|
+
: pathname === href || pathname?.startsWith(href + '/');
|
|
117
|
+
const handleClick = (e) => {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
if (pathname === href)
|
|
120
|
+
return;
|
|
121
|
+
navigateTo(href);
|
|
122
|
+
};
|
|
123
|
+
return (_jsx(Link, { href: href, onClick: handleClick, className: clsx("w-full px-3 py-2 rounded-md inline-flex items-center gap-2 transition-colors", isActive
|
|
124
|
+
? clsx(activeBgClass, activeTextClass)
|
|
125
|
+
: clsx(hoverBgClass, hoverTextClass, inactiveTextClass)), style: {
|
|
126
|
+
...(isActive ? { ...activeBgStyle, ...activeTextStyle } : inactiveTextStyle)
|
|
127
|
+
}, children: label }));
|
|
128
|
+
};
|
|
129
|
+
return (_jsxs("aside", { className: clsx("flex h-full min-h-screen flex-col flex-shrink-0", borderClass, sidebarBgClass, sidebarWidthClass), style: {
|
|
130
|
+
width: theme?.sidebarWidth && !isTailwindClass(theme.sidebarWidth) ? 'var(--nm-sidebar-width)' : undefined,
|
|
131
|
+
backgroundColor: theme?.sidebarColor && !isTailwindClass(theme.sidebarColor) ? 'var(--nm-sidebar-bg)' : undefined,
|
|
132
|
+
maxWidth: '100%'
|
|
133
|
+
}, children: [_jsx(Link, { href: "/admin/dashboard", onClick: (e) => {
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
if (pathname === '/admin/dashboard')
|
|
136
|
+
return;
|
|
137
|
+
navigateTo('/admin/dashboard');
|
|
138
|
+
}, className: clsx("px-3 border-b flex items-center justify-center flex-shrink-0", theme?.topNavHeight && isTailwindClass(theme.topNavHeight) ? theme.topNavHeight : "h-20"), style: {
|
|
139
|
+
height: theme?.topNavHeight && !isTailwindClass(theme.topNavHeight) ? 'var(--nm-top-nav-height)' : undefined,
|
|
140
|
+
maxHeight: '100%'
|
|
141
|
+
}, children: logoUrl ? (_jsx("img", { src: logoUrl, alt: siteName, className: "h-10 max-w-[160px] object-contain" })) : (_jsx("div", { className: "text-lg font-semibold truncate", children: siteName })) }), _jsxs("div", { className: "flex-1 overflow-y-auto p-2", children: [isLoading && _jsx("div", { children: "Loading\u2026" }), status === 'failed' && error && (_jsx("div", { className: "text-red-600", children: error })), status === 'succeeded' && (_jsxs("ul", { className: "list-none space-y-2", children: [_jsx("li", { children: renderMenuItem("/admin/dashboard", "Dashboard") }, "dashboard"), orderedItems.map((s) => {
|
|
72
142
|
const slug = String(s.modelName || '').toLowerCase();
|
|
73
143
|
// Hide auto Settings model from the list; we link a fixed Settings route below
|
|
74
144
|
if (slug === 'settings')
|
|
75
145
|
return null;
|
|
76
|
-
return (_jsx("li", { children:
|
|
77
|
-
}), _jsx("li", { children:
|
|
146
|
+
return (_jsx("li", { children: renderMenuItem(`/admin/${slug}`, s.modelName) }, s.modelName));
|
|
147
|
+
}), _jsx("li", { children: renderMenuItem("/admin/system/update", "Settings") }, "settings"), !items.length && (_jsx("li", { className: "text-gray-500", children: "No schemas found" }))] }))] }), _jsxs("div", { className: "mt-auto border-t p-4 space-y-2 flex-shrink-0", children: [_jsx(Link, { href: "/admin/profile", onClick: (e) => {
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
if (pathname === '/admin/profile')
|
|
150
|
+
return;
|
|
151
|
+
navigateTo('/admin/profile');
|
|
152
|
+
}, className: clsx("w-full block rounded-md transition-colors", pathname === '/admin/profile'
|
|
153
|
+
? clsx(activeBgClass, activeTextClass)
|
|
154
|
+
: clsx(hoverBgClass, hoverTextClass, inactiveTextClass)), style: {
|
|
155
|
+
...(pathname === '/admin/profile' ? { ...activeBgStyle, ...activeTextStyle } : inactiveTextStyle)
|
|
156
|
+
}, children: _jsxs("div", { className: "flex items-center justify-between gap-3 p-2", children: [user && (_jsxs("div", { className: clsx("text-sm text-left", pathname === '/admin/profile'
|
|
157
|
+
? ""
|
|
158
|
+
: (theme?.sidebarTextColor && isTailwindClass(theme.sidebarTextColor) ? theme.sidebarTextColor : "text-gray-700")), style: {
|
|
159
|
+
color: pathname === '/admin/profile'
|
|
160
|
+
? "inherit"
|
|
161
|
+
: (theme?.sidebarTextColor && !isTailwindClass(theme.sidebarTextColor) ? 'var(--nm-sidebar-text)' : undefined)
|
|
162
|
+
}, children: [_jsx("div", { className: "font-semibold", children: `${user.firstName ?? ''} ${user.lastName ?? ''}`.trim() ||
|
|
78
163
|
user.name ||
|
|
79
|
-
'' }), _jsx("div", { className:
|
|
164
|
+
'' }), _jsx("div", { className: clsx(pathname === '/admin/profile'
|
|
165
|
+
? "opacity-75"
|
|
166
|
+
: (theme?.sidebarTextColor && isTailwindClass(theme.sidebarTextColor) ? "opacity-75" : "text-gray-500")), style: {
|
|
167
|
+
color: pathname === '/admin/profile' ? 'inherit' : undefined
|
|
168
|
+
}, children: user.email })] })), _jsx("svg", { "aria-hidden": "true", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-default-500 flex-shrink-0", children: _jsx("path", { d: "M9 6l6 6-6 6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })] }) }), _jsx(Button, { fullWidth: true, variant: "flat", onPress: logout, children: "Log out" })] })] }));
|
|
80
169
|
}
|
|
@@ -34,7 +34,7 @@ export function TableFilters({ model, value, onChange, busy, columns, visibleCol
|
|
|
34
34
|
setQ('');
|
|
35
35
|
onChange({});
|
|
36
36
|
};
|
|
37
|
-
return (_jsxs(Form, { onSubmit: apply, onReset: clear, className: "grid gap-3 md:grid-cols-5 grid-cols-1 items-end", children: [_jsx(Select, { placeholder: "Search Field", "aria-label": "Search Field", variant: "bordered", size: "sm", selectedKeys: searchKey ? new Set([searchKey]) : new Set(), onSelectionChange: (s) => {
|
|
37
|
+
return (_jsxs(Form, { onSubmit: apply, onReset: clear, className: "nextmin-table-filters-form grid gap-3 md:grid-cols-5 grid-cols-1 items-end w-full", children: [_jsx(Select, { placeholder: "Search Field", "aria-label": "Search Field", variant: "bordered", size: "sm", selectedKeys: searchKey ? new Set([searchKey]) : new Set(), onSelectionChange: (s) => {
|
|
38
38
|
if (s === 'all')
|
|
39
39
|
return;
|
|
40
40
|
const v = Array.from(s)[0];
|