@zauru-sdk/components 6.9.0 → 6.11.0
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/CHANGELOG.md +30 -0
- package/dist/AccessControl/WebappEmployeeAccess.d.ts +36 -0
- package/dist/AccessControl/WebappRoleManagement.d.ts +22 -0
- package/dist/AccessControl/index.d.ts +2 -0
- package/dist/NavBar/NavBar.d.ts +1 -1
- package/dist/NavBar/NavBar.types.d.ts +1 -0
- package/dist/Tab/WebappSettingsChrome.d.ts +12 -0
- package/dist/Tab/index.d.ts +1 -0
- package/dist/esm/AccessControl/WebappEmployeeAccess.js +103 -0
- package/dist/esm/AccessControl/WebappRoleManagement.js +77 -0
- package/dist/esm/AccessControl/index.js +2 -0
- package/dist/esm/Form/SelectField/index.js +11 -5
- package/dist/esm/NavBar/NavBar.js +6 -5
- package/dist/esm/Tab/PermissionMatrix.js +1 -1
- package/dist/esm/Tab/WebappSettingsChrome.js +24 -0
- package/dist/esm/Tab/index.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/index.d.ts +1 -0
- package/package.json +7 -7
- package/src/AccessControl/WebappEmployeeAccess.tsx +275 -0
- package/src/AccessControl/WebappRoleManagement.tsx +207 -0
- package/src/AccessControl/index.ts +2 -0
- package/src/Form/SelectField/index.tsx +35 -16
- package/src/NavBar/NavBar.tsx +43 -3
- package/src/NavBar/NavBar.types.ts +1 -0
- package/src/Tab/PermissionMatrix.tsx +2 -0
- package/src/Tab/WebappSettingsChrome.tsx +54 -0
- package/src/Tab/index.ts +1 -0
- package/src/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,36 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [6.11.0](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.10.0...@zauru-sdk/components@6.11.0) (2026-10-05)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* asociar etiquetas del selector con su campo ([1626bbd](https://github.com/intuitiva/zauru-typescript-sdk/commit/1626bbdd58971c353bf03b07533a171a05ec8571))
|
|
12
|
+
* identificar controles de la matriz de permisos ([472c956](https://github.com/intuitiva/zauru-typescript-sdk/commit/472c9565f2dc167e8566706f58c27ca55c6c9d09))
|
|
13
|
+
* limitar altura de selecciones múltiples ([b0e578d](https://github.com/intuitiva/zauru-typescript-sdk/commit/b0e578df1d7ec2198abfd57515b6dc8409227c4d))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* exportar gestión de acceso de empleados ([df97155](https://github.com/intuitiva/zauru-typescript-sdk/commit/df97155d1370cdb624cc3ceb3905111a42edf65f))
|
|
19
|
+
* exportar gestión de roles ([f0ee633](https://github.com/intuitiva/zauru-typescript-sdk/commit/f0ee63301583591b72faba93b8eab2d7ec8e735f))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# [6.10.0](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.9.0...@zauru-sdk/components@6.10.0) (2026-10-05)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* menú Configuración genérico y extras de asignación fuera del SDK ([2691957](https://github.com/intuitiva/zauru-typescript-sdk/commit/2691957c9dba23226a4b8d371fee26b221e126f0))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
6
36
|
# [6.9.0](https://github.com/intuitiva/zauru-typescript-sdk/compare/@zauru-sdk/components@6.8.3...@zauru-sdk/components@6.9.0) (2026-10-05)
|
|
7
37
|
|
|
8
38
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { EmployeeGraphQL, WebAppRowGraphQL, WebappEmployeeRole, WebappRole } from "@zauru-sdk/types";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export type WebappEmployeeAssignmentChange<TAssignment extends WebappEmployeeRole> = {
|
|
4
|
+
employee: EmployeeGraphQL;
|
|
5
|
+
assignment?: WebAppRowGraphQL<TAssignment>;
|
|
6
|
+
roleId: number | null;
|
|
7
|
+
};
|
|
8
|
+
export type WebappEmployeeAssignmentDetails<TAssignment extends WebappEmployeeRole> = {
|
|
9
|
+
employee: EmployeeGraphQL;
|
|
10
|
+
assignment?: WebAppRowGraphQL<TAssignment>;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
};
|
|
13
|
+
type Props<TAssignment extends WebappEmployeeRole> = {
|
|
14
|
+
employees: EmployeeGraphQL[];
|
|
15
|
+
roles: WebAppRowGraphQL<WebappRole>[];
|
|
16
|
+
assignments: WebAppRowGraphQL<TAssignment>[];
|
|
17
|
+
canAssign?: boolean;
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
description?: string;
|
|
20
|
+
superAdminLabel?: string;
|
|
21
|
+
addEmployeeTitle?: string;
|
|
22
|
+
addEmployeeDescription?: string;
|
|
23
|
+
noEmployeesToAddText?: string;
|
|
24
|
+
noRolesText?: string;
|
|
25
|
+
assignmentDetailsColumn?: {
|
|
26
|
+
name: string;
|
|
27
|
+
minWidth?: string;
|
|
28
|
+
grow?: number;
|
|
29
|
+
};
|
|
30
|
+
renderAssignmentDetails?: (details: WebappEmployeeAssignmentDetails<TAssignment>) => ReactNode;
|
|
31
|
+
superAdminEmailSuffixes?: readonly string[];
|
|
32
|
+
onAssignRole: (change: WebappEmployeeAssignmentChange<TAssignment>) => void;
|
|
33
|
+
onAddEmployee: (employeeId: number, roleId: number) => void;
|
|
34
|
+
};
|
|
35
|
+
export declare function WebappEmployeeAccess<TAssignment extends WebappEmployeeRole = WebappEmployeeRole>({ employees, roles, assignments, canAssign, loading, description, superAdminLabel, addEmployeeTitle, addEmployeeDescription, noEmployeesToAddText, noRolesText, assignmentDetailsColumn, renderAssignmentDetails, superAdminEmailSuffixes, onAssignRole, onAddEmployee, }: Props<TAssignment>): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { WebAppRowGraphQL, WebappRole } from "@zauru-sdk/types";
|
|
2
|
+
export type WebappRoleValues = {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
};
|
|
6
|
+
type Props = {
|
|
7
|
+
roles: WebAppRowGraphQL<WebappRole>[];
|
|
8
|
+
canCreate?: boolean;
|
|
9
|
+
canEdit?: boolean;
|
|
10
|
+
canDelete?: boolean;
|
|
11
|
+
loading?: boolean;
|
|
12
|
+
createTitle?: string;
|
|
13
|
+
createButtonLabel?: string;
|
|
14
|
+
editTitle?: string;
|
|
15
|
+
deleteTitle?: string;
|
|
16
|
+
deleteDescription?: string;
|
|
17
|
+
onCreate: (values: WebappRoleValues) => void;
|
|
18
|
+
onUpdate: (roleId: number, values: WebappRoleValues) => void;
|
|
19
|
+
onDelete: (roleId: number) => void;
|
|
20
|
+
};
|
|
21
|
+
export declare function WebappRoleManagement({ roles, canCreate, canEdit, canDelete, loading, createTitle, createButtonLabel, editTitle, deleteTitle, deleteDescription, onCreate, onUpdate, onDelete, }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
22
|
+
export {};
|
package/dist/NavBar/NavBar.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { NavBarProps } from "./NavBar.types.js";
|
|
2
|
-
export declare const NavBar: ({ title, loggedIn, items, selectedColor, version, reloadCatalogsOption, showDarkModeToggle, }: NavBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const NavBar: ({ title, loggedIn, items, selectedColor, version, reloadCatalogsOption, settingsOption, showDarkModeToggle, }: NavBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type Props = {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
showEmployees?: boolean;
|
|
5
|
+
showRoles?: boolean;
|
|
6
|
+
showPermissions?: boolean;
|
|
7
|
+
basePath?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const WebappSettingsChrome: ({ children, showEmployees, showRoles, showPermissions, basePath, title, description, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
package/dist/Tab/index.d.ts
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { isSuperAdminEmail } from "@zauru-sdk/utils";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { Button } from "../Buttons/Button.js";
|
|
6
|
+
import { ButtonSectionContainer } from "../Containers/ButtonSectionContainer.js";
|
|
7
|
+
import { DoubleFieldContainer } from "../Form/FieldContainer/DoubleFieldContainer.js";
|
|
8
|
+
import { ReactZodForm } from "../Form/ReactZodForm/index.js";
|
|
9
|
+
import { SelectField } from "../Form/SelectField/index.js";
|
|
10
|
+
import { ZauruTable } from "../Table/ZauruTable.js";
|
|
11
|
+
const addEmployeeSchema = z.object({
|
|
12
|
+
employee_id: z.coerce.number().min(1, "El empleado es requerido"),
|
|
13
|
+
role_id: z.coerce.number().min(1, "El rol es requerido"),
|
|
14
|
+
});
|
|
15
|
+
export function WebappEmployeeAccess({ employees, roles, assignments, canAssign = false, loading = false, description = "Solo aparecen quienes ya tienen acceso: rol asignado o super admin. Quitar el rol los saca de la lista.", superAdminLabel = "Super admin (todos los permisos)", addEmployeeTitle = "Agregar empleado", addEmployeeDescription = "Busca entre quienes aún no están en la lista y asígnales un rol.", noEmployeesToAddText = "No quedan empleados activos por agregar.", noRolesText = "Crea al menos un rol antes de agregar empleados.", assignmentDetailsColumn, renderAssignmentDetails, superAdminEmailSuffixes, onAssignRole, onAddEmployee, }) {
|
|
16
|
+
const roleOptions = useMemo(() => [
|
|
17
|
+
{ value: "", label: "Sin rol" },
|
|
18
|
+
...roles.map((role) => ({
|
|
19
|
+
value: String(role.id),
|
|
20
|
+
label: role.data.name,
|
|
21
|
+
})),
|
|
22
|
+
], [roles]);
|
|
23
|
+
const addRoleOptions = useMemo(() => roles.map((role) => ({
|
|
24
|
+
value: String(role.id),
|
|
25
|
+
label: role.data.name,
|
|
26
|
+
})), [roles]);
|
|
27
|
+
const assignmentByEmployee = useMemo(() => {
|
|
28
|
+
const map = new Map();
|
|
29
|
+
for (const row of assignments) {
|
|
30
|
+
map.set(Number(row.data.employee_id), row);
|
|
31
|
+
}
|
|
32
|
+
return map;
|
|
33
|
+
}, [assignments]);
|
|
34
|
+
const listedEmployees = useMemo(() => employees
|
|
35
|
+
.filter((employee) => isSuperAdminEmail(employee.email, superAdminEmailSuffixes) ||
|
|
36
|
+
assignmentByEmployee.has(employee.id))
|
|
37
|
+
.sort((a, b) => a.name.localeCompare(b.name)), [assignmentByEmployee, employees, superAdminEmailSuffixes]);
|
|
38
|
+
const addEmployeeOptions = useMemo(() => {
|
|
39
|
+
const listedIds = new Set(listedEmployees.map((employee) => employee.id));
|
|
40
|
+
return employees
|
|
41
|
+
.filter((employee) => !listedIds.has(employee.id))
|
|
42
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
43
|
+
.map((employee) => ({
|
|
44
|
+
value: employee.id,
|
|
45
|
+
label: employee.email
|
|
46
|
+
? `${employee.name} (${employee.email})`
|
|
47
|
+
: employee.name,
|
|
48
|
+
}));
|
|
49
|
+
}, [employees, listedEmployees]);
|
|
50
|
+
const columns = [
|
|
51
|
+
{
|
|
52
|
+
name: "Empleado",
|
|
53
|
+
sortable: true,
|
|
54
|
+
grow: 1,
|
|
55
|
+
minWidth: "12rem",
|
|
56
|
+
selector: (employee) => employee.name,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "Email",
|
|
60
|
+
sortable: true,
|
|
61
|
+
grow: 1,
|
|
62
|
+
minWidth: "14rem",
|
|
63
|
+
selector: (employee) => employee.email || "—",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "Rol",
|
|
67
|
+
minWidth: "14rem",
|
|
68
|
+
cell: (employee) => {
|
|
69
|
+
const assignment = assignmentByEmployee.get(employee.id);
|
|
70
|
+
const superAdmin = isSuperAdminEmail(employee.email, superAdminEmailSuffixes);
|
|
71
|
+
const currentRole = assignment
|
|
72
|
+
? String(assignment.data.role_id)
|
|
73
|
+
: "";
|
|
74
|
+
return (_jsx("div", { className: "w-56 py-2", children: superAdmin ? (_jsx("p", { className: "text-sm text-gray-600", children: superAdminLabel })) : (_jsx(SelectField, { name: `role_${employee.id}`, options: roleOptions, defaultValue: roleOptions.find((option) => option.value === currentRole) ?? roleOptions[0], disabled: !canAssign || loading, onChange: (option) => {
|
|
75
|
+
const value = option?.value?.toString() ?? "";
|
|
76
|
+
onAssignRole({
|
|
77
|
+
employee,
|
|
78
|
+
assignment,
|
|
79
|
+
roleId: value ? Number(value) : null,
|
|
80
|
+
});
|
|
81
|
+
} })) }));
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
...(assignmentDetailsColumn && renderAssignmentDetails
|
|
85
|
+
? [
|
|
86
|
+
{
|
|
87
|
+
name: assignmentDetailsColumn.name,
|
|
88
|
+
minWidth: assignmentDetailsColumn.minWidth ?? "16rem",
|
|
89
|
+
grow: assignmentDetailsColumn.grow ?? 1,
|
|
90
|
+
cell: (employee) => {
|
|
91
|
+
const assignment = assignmentByEmployee.get(employee.id);
|
|
92
|
+
return renderAssignmentDetails({
|
|
93
|
+
employee,
|
|
94
|
+
assignment,
|
|
95
|
+
disabled: !canAssign || !assignment || loading,
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
: []),
|
|
101
|
+
];
|
|
102
|
+
return (_jsxs("div", { className: "mt-4 space-y-6", children: [_jsx("p", { className: "text-sm text-gray-600", children: description }), _jsx(ZauruTable, { columns: columns, data: listedEmployees, loading: loading, offlineSearch: ["name", "email"] }), canAssign ? (roles.length === 0 ? (_jsx("p", { className: "text-sm text-gray-600", role: "status", children: noRolesText })) : addEmployeeOptions.length > 0 ? (_jsx("div", { className: "rounded-md border border-gray-200 bg-white p-4", children: _jsxs(ReactZodForm, { schema: addEmployeeSchema, onSubmit: (values) => onAddEmployee(Number(values.employee_id), Number(values.role_id)), children: [_jsx("h2", { className: "text-base font-semibold text-gray-900", children: addEmployeeTitle }), _jsx("p", { className: "mb-4 mt-1 text-sm text-gray-600", children: addEmployeeDescription }), _jsxs(DoubleFieldContainer, { alignCenter: false, children: [_jsx(SelectField, { name: "employee_id", title: "Empleado", hint: "Escribe para filtrar por nombre o correo", options: addEmployeeOptions, required: true }), _jsx(SelectField, { name: "role_id", title: "Rol", options: addRoleOptions, required: true })] }), _jsx(ButtonSectionContainer, { children: _jsx(Button, { loading: loading, title: "Agregar" }) })] }) })) : (_jsx("p", { className: "text-sm text-gray-600", role: "status", children: noEmployeesToAddText }))) : null] }));
|
|
103
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PencilSvg, TrashSvg } from "@zauru-sdk/icons";
|
|
3
|
+
import { useSyncExternalStore } from "react";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { Button } from "../Buttons/Button.js";
|
|
6
|
+
import { ButtonSectionContainer } from "../Containers/ButtonSectionContainer.js";
|
|
7
|
+
import { ReactZodForm } from "../Form/ReactZodForm/index.js";
|
|
8
|
+
import { TextField } from "../Form/TextField/index.js";
|
|
9
|
+
import { createModal } from "../Modal/Modal.js";
|
|
10
|
+
import { ZauruTable } from "../Table/ZauruTable.js";
|
|
11
|
+
const roleSchema = z.object({
|
|
12
|
+
name: z.string().min(1, "El nombre es requerido"),
|
|
13
|
+
description: z.string().optional(),
|
|
14
|
+
});
|
|
15
|
+
const subscribeToBrowser = () => () => undefined;
|
|
16
|
+
const getBrowserSnapshot = () => true;
|
|
17
|
+
const getServerSnapshot = () => false;
|
|
18
|
+
export function WebappRoleManagement({ roles, canCreate = false, canEdit = false, canDelete = false, loading = false, createTitle = "Crear rol", createButtonLabel = "Crear rol", editTitle = "Editar rol", deleteTitle = "¿Eliminar este rol?", deleteDescription = "No se puede eliminar si hay empleados asignados.", onCreate, onUpdate, onDelete, }) {
|
|
19
|
+
const isClient = useSyncExternalStore(subscribeToBrowser, getBrowserSnapshot, getServerSnapshot);
|
|
20
|
+
const editRole = async (role) => {
|
|
21
|
+
let name = role.data.name;
|
|
22
|
+
let description = role.data.description ?? "";
|
|
23
|
+
const response = await createModal({
|
|
24
|
+
title: editTitle,
|
|
25
|
+
okButtonText: "Guardar",
|
|
26
|
+
description: (_jsxs(_Fragment, { children: [_jsx(TextField, { title: "Nombre", name: "name", defaultValue: name, onChange: (value) => {
|
|
27
|
+
name = value;
|
|
28
|
+
} }), _jsx(TextField, { className: "mt-4", title: "Descripci\u00F3n", name: "description", defaultValue: description, onChange: (value) => {
|
|
29
|
+
description = value;
|
|
30
|
+
} })] })),
|
|
31
|
+
});
|
|
32
|
+
if (response === "OK") {
|
|
33
|
+
onUpdate(role.id, { name, description });
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const confirmDelete = async (roleId) => {
|
|
37
|
+
const response = await createModal({
|
|
38
|
+
title: deleteTitle,
|
|
39
|
+
description: deleteDescription,
|
|
40
|
+
});
|
|
41
|
+
if (response === "OK") {
|
|
42
|
+
onDelete(roleId);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const columns = [
|
|
46
|
+
{
|
|
47
|
+
name: "Nombre",
|
|
48
|
+
sortable: true,
|
|
49
|
+
grow: 1,
|
|
50
|
+
selector: (role) => role.data.name,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "Descripción",
|
|
54
|
+
grow: 2,
|
|
55
|
+
selector: (role) => role.data.description || "—",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "Permisos",
|
|
59
|
+
width: "8%",
|
|
60
|
+
selector: (role) => role.data.permission_keys?.length ?? 0,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "",
|
|
64
|
+
grow: 0,
|
|
65
|
+
minWidth: "90px",
|
|
66
|
+
maxWidth: "90px",
|
|
67
|
+
cell: (role) => (_jsxs("div", { className: "flex gap-2", children: [canDelete ? (_jsx("button", { type: "button", title: "Eliminar", "aria-label": `Eliminar rol ${role.data.name}`, disabled: loading, className: "cursor-pointer rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", onClick: () => void confirmDelete(role.id), children: _jsx(TrashSvg, {}) })) : null, canEdit ? (_jsx("button", { type: "button", title: "Editar", "aria-label": `Editar rol ${role.data.name}`, disabled: loading, className: "cursor-pointer rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", onClick: () => void editRole(role), children: _jsx(PencilSvg, {}) })) : null] })),
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
if (!isClient) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return (_jsxs("div", { className: "mt-4 space-y-6", children: [_jsx(ZauruTable, { columns: columns, data: [...roles].sort((a, b) => a.data.name.localeCompare(b.data.name)), loading: loading, offlineSearch: ["data.name", "data.description"] }), canCreate ? (_jsx("div", { className: "rounded-md border border-gray-200 bg-white p-4", children: _jsxs(ReactZodForm, { schema: roleSchema, onSubmit: (values) => onCreate({
|
|
74
|
+
name: String(values.name ?? ""),
|
|
75
|
+
description: String(values.description ?? ""),
|
|
76
|
+
}), children: [_jsx("h2", { className: "mb-4 text-base font-semibold text-gray-900", children: createTitle }), _jsx(TextField, { name: "name", title: "Nombre del rol", required: true }), _jsx(TextField, { className: "mt-4", name: "description", title: "Descripci\u00F3n" }), _jsx(ButtonSectionContainer, { children: _jsx(Button, { loading: loading, title: createButtonLabel }) })] }) })) : null] }));
|
|
77
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { IdeaIconSVG } from "@zauru-sdk/icons";
|
|
3
|
-
import { useEffect, useState, useRef, useCallback, } from "react";
|
|
3
|
+
import { useEffect, useState, useRef, useId, useCallback, } from "react";
|
|
4
4
|
import { LoadingInputSkeleton } from "../../Skeletons/index.js";
|
|
5
5
|
import { useFormContext } from "react-hook-form";
|
|
6
6
|
// Simple comparison helper to check if two SelectFieldOption arrays contain the
|
|
@@ -30,6 +30,10 @@ export const SelectField = (props) => {
|
|
|
30
30
|
const lastSyncedDefaultValues = useRef(defaultValueMulti.map((option) => option.value));
|
|
31
31
|
const [isTabPressed, setIsTabPressed] = useState(false);
|
|
32
32
|
const [isSearching, setIsSearching] = useState(false);
|
|
33
|
+
const generatedInputId = useId();
|
|
34
|
+
const inputId = id ?? (name ? `${name}-input` : generatedInputId);
|
|
35
|
+
const errorId = `${inputId}-error`;
|
|
36
|
+
const hintId = `${inputId}-hint`;
|
|
33
37
|
// Use refs to store stable references to callbacks
|
|
34
38
|
const onChangeRef = useRef(onChange);
|
|
35
39
|
const onChangeMultiRef = useRef(onChangeMulti);
|
|
@@ -275,11 +279,13 @@ export const SelectField = (props) => {
|
|
|
275
279
|
options,
|
|
276
280
|
]);
|
|
277
281
|
if (loading) {
|
|
278
|
-
return (_jsxs(_Fragment, { children: [title && (_jsx("
|
|
282
|
+
return (_jsxs(_Fragment, { children: [title && (_jsx("span", { className: `block text-sm font-medium text-${color}-700 dark:text-${color}-500`, children: title })), _jsx(LoadingInputSkeleton, {}), helpText && (_jsx("p", { className: `mt-2 italic text-sm text-${color}-500 dark:text-${color}-400`, children: helpText }))] }));
|
|
279
283
|
}
|
|
280
|
-
return (_jsxs("div", { className: `col-span-6 sm:col-span-3 ${className}`, ref: selectRef, children: [title && (_jsxs("label", { htmlFor:
|
|
284
|
+
return (_jsxs("div", { className: `col-span-6 sm:col-span-3 ${className}`, ref: selectRef, children: [title && (_jsxs("label", { htmlFor: inputId, className: `block text-sm font-medium ${color === "red"
|
|
281
285
|
? "text-red-700 dark:text-red-500"
|
|
282
|
-
: "text-gray-700 dark:text-gray-500"}`, children: [title, required && _jsx("span", { className: "text-red-500", children: "*" })] })), _jsxs("div", { className: "relative", children: [_jsxs("div", { className: "flex items-center", children: [_jsx("input", { type: "text", id:
|
|
286
|
+
: "text-gray-700 dark:text-gray-500"}`, children: [title, required && _jsx("span", { className: "text-red-500", children: "*" })] })), _jsxs("div", { className: "relative", children: [_jsxs("div", { className: "flex items-center", children: [_jsx("input", { type: "text", id: inputId, value: inputValue, onFocus: () => setIsOpen(true), onKeyDown: handleKeyDown, readOnly: isReadOnly, disabled: disabled, className: `block w-full rounded-md ${bgColor} ${borderColor} ${textColor} shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm pr-10`, placeholder: isMulti ? "Select options..." : "Select an option...", autoComplete: "off", onChange: handleInputChange, onBlur: handleBlur, required: required, "aria-invalid": Boolean(error), "aria-describedby": error ? errorId : hint ? hintId : undefined }), isClearable && (value || valueMulti.length > 0) && (_jsx("button", { type: "button", onClick: handleClear, "aria-label": isMulti
|
|
287
|
+
? "Limpiar todas las opciones seleccionadas"
|
|
288
|
+
: "Limpiar la opción seleccionada", className: "absolute inset-y-0 right-0 flex cursor-pointer items-center pr-3 text-gray-600 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2", children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })), helpText && (_jsx("div", { className: "flex items-center relative ml-3", children: _jsxs("div", { className: "relative cursor-pointer", onMouseEnter: () => setShowTooltip(true), onMouseLeave: () => setShowTooltip(false), children: [_jsx(IdeaIconSVG, {}), showTooltip && (_jsx("div", { className: "absolute -left-48 top-0 mt-8 p-2 bg-white border rounded shadow text-black z-50", children: helpText }))] }) }))] }), _jsx("input", { type: "hidden", ...(register ?? {}), name: name, value: isMulti
|
|
283
289
|
? valueMulti.map((v) => v.value).join(",")
|
|
284
290
|
: value?.value || "" }), isOpen && !isReadOnly && (_jsx("ul", { ref: optionsRef, className: "absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm", children: filteredOptions.map((option, index) => {
|
|
285
291
|
const isDisabled = Boolean(option.disabled);
|
|
@@ -294,5 +300,5 @@ export const SelectField = (props) => {
|
|
|
294
300
|
: index === highlightedIndex
|
|
295
301
|
? "cursor-pointer text-black bg-sky-200"
|
|
296
302
|
: "cursor-pointer text-gray-900"}`, onClick: () => !isDisabled && handleOptionClick(option), onMouseEnter: () => !isDisabled && setHighlightedIndex(index), onMouseLeave: () => setHighlightedIndex(-1), children: [option.label, isDisabled && disabledLabel && (_jsxs("span", { className: "ml-2 text-xs text-gray-400", children: ["(", disabledLabel, ")"] }))] }, `${option.value}-${index}`));
|
|
297
|
-
}) }))] }), isMulti && valueMulti.length > 0 && (_jsx("
|
|
303
|
+
}) }))] }), isMulti && valueMulti.length > 0 && (_jsx("ul", { className: "mt-2 flex max-h-20 flex-wrap gap-1.5 overflow-auto overscroll-contain rounded-md border border-blue-100 bg-blue-50/40 p-1.5", "aria-label": "Opciones seleccionadas", children: valueMulti.map((option, index) => (_jsxs("li", { className: "inline-flex max-w-full shrink-0 items-center rounded bg-blue-100 py-0.5 pl-2.5 pr-0.5 text-xs font-semibold text-blue-800", children: [_jsx("span", { className: "whitespace-nowrap", children: option.label }), _jsx("button", { type: "button", onClick: () => handleRemoveMultiValue(option), "aria-label": `Quitar ${option.label}`, className: "ml-1 inline-flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded text-blue-700 hover:bg-blue-200 hover:text-blue-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-1", children: _jsx("span", { "aria-hidden": "true", children: "\u00D7" }) })] }, `${option.value}-${index}`))) })), error && (_jsxs("p", { id: errorId, className: `mt-2 text-sm text-${color}-600 dark:text-${color}-500`, children: [_jsx("span", { className: "font-medium", children: "Oops!" }), " ", error?.message?.toString() || "Error desconocido"] })), !error && hint && (_jsx("p", { id: hintId, className: `mt-2 italic text-sm text-${color}-500 dark:text-${color}-400`, children: hint }))] }));
|
|
298
304
|
};
|
|
@@ -4,9 +4,9 @@ import { DropDownArrowSvgIcon, LogoutDropDownSvgIcon, MenuAlt4Svg, OpcionButtonS
|
|
|
4
4
|
import { COLORS } from "./NavBar.utils.js";
|
|
5
5
|
import { Link, useNavigate, useLocation } from "@remix-run/react";
|
|
6
6
|
import { useAppSelector } from "@zauru-sdk/redux";
|
|
7
|
-
const OptionsDropDownButton = ({ color, options, name }) => {
|
|
7
|
+
const OptionsDropDownButton = ({ color, options, name, ariaLabel, }) => {
|
|
8
8
|
const [showOptionsMenu, setShowOptionsMenu] = useState(true);
|
|
9
|
-
return (_jsx("div", { className: "nav-item ml-auto", children: _jsx("div", { className: "flex justify-center", children: _jsxs("div", { className: "relative inline-block", children: [_jsxs("button", { onClick: () => setShowOptionsMenu(!showOptionsMenu), className: `relative flex cursor-pointer items-center p-2 text-xs text-white ${color.bg700} active:${color.bg900} border border-transparent rounded-full uppercase focus:ring-
|
|
9
|
+
return (_jsx("div", { className: "nav-item ml-auto", children: _jsx("div", { className: "flex justify-center", children: _jsxs("div", { className: "relative inline-block", children: [_jsxs("button", { type: "button", "aria-label": ariaLabel ?? (typeof name === "string" ? name : "Menú"), "aria-haspopup": "menu", "aria-expanded": !showOptionsMenu, onClick: () => setShowOptionsMenu(!showOptionsMenu), className: `relative flex cursor-pointer items-center p-2 text-xs text-white ${color.bg700} active:${color.bg900} border border-transparent rounded-full uppercase focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2`, children: [name ?? _jsx(OpcionButtonSvgIcon, {}), _jsx(DropDownArrowSvgIcon, {})] }), _jsx("div", { className: "absolute right-0 z-20 w-56 py-2 mt-2 overflow-hidden bg-white rounded-md shadow-xl dark:bg-gray-800", hidden: showOptionsMenu, onMouseLeave: () => setShowOptionsMenu(true), children: options.map((option, index) => (_jsx(React.Fragment, { children: option }, index))) })] }) }) }));
|
|
10
10
|
};
|
|
11
11
|
const NavItem = ({ name, link, icon, selectedColor, childrens = [], reduxNotificationBadge, }) => {
|
|
12
12
|
const location = useLocation();
|
|
@@ -30,7 +30,7 @@ const NavItem = ({ name, link, icon, selectedColor, childrens = [], reduxNotific
|
|
|
30
30
|
// De lo contrario, usamos el color normal (bg700)
|
|
31
31
|
className: `${isActive ? specialColor.bg900 : specialColor.bg700} container text-white w-full sm:w-auto h-10 text-sm py-1 uppercase shadow hover:shadow-lg outline-none rounded-full focus:outline-none my-auto sm:my-0 sm:mr-1 mb-1 ease-linear transition-all duration-150`, children: _jsxs(Link, { className: "px-3 flex items-center text-xs leading-snug text-white uppercase hover:opacity-75 relative", to: link, children: [_jsxs("div", { className: "mx-auto pt-2", children: [icon, _jsx("span", { children: name })] }), notificationBadge !== undefined && (_jsx("span", { className: "absolute -top-2 -right-2 bg-red-500 text-white text-xs font-bold rounded-full flex items-center justify-center w-5 h-5", children: notificationBadge }))] }) }) }));
|
|
32
32
|
};
|
|
33
|
-
export const NavBar = ({ title, loggedIn, items, selectedColor, version, reloadCatalogsOption, showDarkModeToggle = false, }) => {
|
|
33
|
+
export const NavBar = ({ title, loggedIn, items, selectedColor, version, reloadCatalogsOption, settingsOption, showDarkModeToggle = false, }) => {
|
|
34
34
|
const color = COLORS[selectedColor];
|
|
35
35
|
const [NavBarOpen, setNavBarOpen] = useState(false);
|
|
36
36
|
const [currentVersion, setCurrentVersion] = useState(version);
|
|
@@ -56,11 +56,12 @@ export const NavBar = ({ title, loggedIn, items, selectedColor, version, reloadC
|
|
|
56
56
|
const options = useMemo(() => {
|
|
57
57
|
return (_jsxs(_Fragment, { children: [_jsx("ul", { className: "w-full lg:flex lg:items-center", children: renderNavItems(items
|
|
58
58
|
.filter((item) => item.loggedIn === loggedIn)
|
|
59
|
-
.filter((item) => item.hide !== true)) }), _jsx("ul", { className: "sm:flex sm:flex-col lg:flex-row ml-auto", children: loggedIn && (_jsx(OptionsDropDownButton, { color: color, options: [
|
|
59
|
+
.filter((item) => item.hide !== true)) }), _jsx("ul", { className: "sm:flex sm:flex-col lg:flex-row ml-auto", children: loggedIn && (_jsx(OptionsDropDownButton, { color: color, ariaLabel: "Men\u00FA de usuario", options: [
|
|
60
|
+
settingsOption && (_jsx(Link, { className: "block cursor-pointer px-4 py-3 text-sm text-gray-600 capitalize transition-colors duration-200 transform dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 dark:hover:text-white", to: "/configuracion", children: _jsxs("div", { className: "mx-auto pt-2", children: [_jsxs("svg", { className: "h-5 w-5 mr-1", width: "24", height: "24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [_jsx("path", { d: "M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7Z" }), _jsx("path", { d: "M19.4 13a7.7 7.7 0 0 0 .06-2l2.1-1.64-2-3.46-2.55 1a7.7 7.7 0 0 0-1.73-1L14.7 2H9.3l-.58 2.9a7.7 7.7 0 0 0-1.73 1l-2.55-1-2 3.46L4.54 11a7.7 7.7 0 0 0 0 2l-2.1 1.64 2 3.46 2.55-1a7.7 7.7 0 0 0 1.73 1L9.3 22h5.4l.58-2.9a7.7 7.7 0 0 0 1.73-1l2.55 1 2-3.46Z" })] }), _jsx("span", { children: "Configuraci\u00F3n" })] }) }, "configuracion")),
|
|
60
61
|
reloadCatalogsOption && (_jsx(Link, { className: `block px-4 py-3 text-sm text-gray-600 capitalize transition-colors duration-200 transform dark:text-gray-300 hover:bg-red-100 dark:hover:bg-gray-700 dark:hover:text-white`, to: "/reload-catalogs", children: _jsxs("div", { className: "mx-auto pt-2", children: [_jsxs("svg", { className: `h-5 w-5 mr-1`, width: "24", height: "24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M20.5 8c-1.392-3.179-4.823-5-8.522-5C7.299 3 3.453 6.552 3 11.1" }), _jsx("path", { d: "M16.489 8.4h3.97A.54.54 0 0 0 21 7.86V3.9M3.5 16c1.392 3.179 4.823 5 8.522 5 4.679 0 8.525-3.552 8.978-8.1" }), _jsx("path", { d: "M7.511 15.6h-3.97a.54.54 0 0 0-.541.54v3.96" })] }), _jsx("span", { children: "Recargar cat\u00E1logos" })] }) }, "recargar-catalogos")),
|
|
61
62
|
showDarkModeToggle && (_jsx("button", { type: "button", className: "block w-full cursor-pointer text-left px-4 py-3 text-sm text-gray-600 capitalize transition-colors duration-200 transform dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 dark:hover:text-white", onClick: () => setIsDarkMode((value) => !value), children: _jsx("div", { className: "mx-auto pt-2", children: _jsx("span", { children: isDarkMode ? "Modo claro" : "Modo oscuro" }) }) }, "dark-mode-toggle")),
|
|
62
63
|
_jsx(Link, { className: `block px-4 py-3 text-sm text-gray-600 capitalize transition-colors duration-200 transform dark:text-gray-300 hover:bg-red-100 dark:hover:bg-gray-700 dark:hover:text-white`, to: "/logout", children: _jsxs("div", { className: "mx-auto pt-2", children: [_jsx(LogoutDropDownSvgIcon, {}), _jsx("span", { children: "Cerrar sesi\u00F3n" })] }) }, "cerrar-sesion"),
|
|
63
64
|
] })) })] }));
|
|
64
|
-
}, [items, loggedIn, hiddenItemsChange, showDarkModeToggle, isDarkMode]);
|
|
65
|
+
}, [items, loggedIn, hiddenItemsChange, reloadCatalogsOption, settingsOption, showDarkModeToggle, isDarkMode]);
|
|
65
66
|
return (_jsx("nav", { className: `py-3 ${color.bg600}`, children: _jsxs("div", { className: "flex items-center justify-between ml-5 mr-5", children: [_jsxs("div", { className: "flex justify-between items-center w-full lg:w-auto", children: [_jsxs(Link, { className: "text-sm font-bold leading-relaxed inline-block mr-4 py-2 whitespace-nowrap uppercase text-white", to: "/home", children: [_jsx("div", { className: "inline-block mr-2 mb-2 align-middle", children: _jsx("img", { className: "w-auto h-7", src: "/logo.png", alt: "logo-zauru" }) }), title] }), version !== currentVersion && (_jsx("button", { className: `ml-2 px-2 py-1 text-xs text-white ${color.bg700} rounded-full hover:${color.bg900} transition-colors duration-200`, onClick: refreshPage, children: "\uD83D\uDD04 Actualizar versi\u00F3n" })), _jsx("button", { className: `rounded lg:hidden focus:outline-none focus:ring focus:${color.ring600} focus:ring-opacity-50`, "aria-label": "Toggle mobile menu", type: "button", onClick: () => setNavBarOpen(!NavBarOpen), children: _jsx(MenuAlt4Svg, { open: NavBarOpen }) })] }), _jsx("div", { className: `lg:hidden fixed top-0 left-0 z-50 w-64 h-full ${color.bg700} dark:bg-gray-900 shadow-lg transform ${NavBarOpen ? "translate-x-0" : "-translate-x-full"} transition-transform duration-300 ease-in-out overflow-y-auto`, children: _jsx("div", { className: "p-4", children: options }) }), _jsx("div", { className: "hidden lg:flex lg:items-center w-full lg:w-auto", children: options })] }) }));
|
|
66
67
|
};
|
|
@@ -22,7 +22,7 @@ function GroupRows({ group, roles, sectionKeys, disabled, pendingCells, onToggle
|
|
|
22
22
|
const granted = role.permissionKeys.includes(action.key);
|
|
23
23
|
const cellKey = permissionMatrixCellKey(role.id, action.key);
|
|
24
24
|
const pending = pendingCells?.has(cellKey) ?? false;
|
|
25
|
-
return (_jsx("td", { className: "border-b border-l border-gray-100 px-3 py-2 text-center", children: _jsx("input", { type: "checkbox", className: checkboxClass, checked: granted, disabled: disabled || pending, "aria-label": `${role.name}: ${action.label}`, onChange: () => onToggle(role.id, action.key, !granted) }) }, role.id));
|
|
25
|
+
return (_jsx("td", { className: "border-b border-l border-gray-100 px-3 py-2 text-center", children: _jsx("input", { type: "checkbox", id: `permission-${role.id}-${action.key}`, name: `permission-${role.id}-${action.key}`, className: checkboxClass, checked: granted, disabled: disabled || pending, "aria-label": `${role.name}: ${action.label}`, onChange: () => onToggle(role.id, action.key, !granted) }) }, role.id));
|
|
26
26
|
})] }, action.key))), onBulkSection ? (_jsxs("tr", { children: [_jsx("td", { className: "sticky left-0 z-10 border-b border-gray-200 bg-white px-3 py-1 text-xs text-gray-500", children: "Secci\u00F3n" }), roles.map((role) => {
|
|
27
27
|
const sectionGranted = sectionKeys.every((key) => role.permissionKeys.includes(key));
|
|
28
28
|
return (_jsx("td", { className: "border-b border-l border-gray-200 px-3 py-1 text-center", children: _jsx("button", { type: "button", className: "cursor-pointer text-xs font-medium text-indigo-700 hover:text-indigo-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 rounded disabled:cursor-not-allowed disabled:opacity-50", disabled: disabled, onClick: () => onBulkSection(role.id, group.id, sectionGranted ? "remove" : "add"), children: sectionGranted ? "Quitar sección" : "Marcar sección" }) }, role.id));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MainContainer } from "../Containers/MainContainer.js";
|
|
3
|
+
import { SubContainer } from "../Containers/SubContainer.js";
|
|
4
|
+
import { RouteTabNav } from "./RouteTabNav.js";
|
|
5
|
+
export const WebappSettingsChrome = ({ children, showEmployees = true, showRoles = true, showPermissions = true, basePath = "/configuracion", title = "Configuración", description = "Roles, empleados y permisos de esta aplicación.", }) => {
|
|
6
|
+
const base = basePath.replace(/\/$/, "");
|
|
7
|
+
return (_jsx(MainContainer, { children: _jsxs(SubContainer, { title: title, description: description, children: [_jsx(RouteTabNav, { ariaLabel: "Secciones de configuraci\u00F3n de accesos", selectedColor: "indigo", items: [
|
|
8
|
+
{
|
|
9
|
+
to: `${base}/empleados`,
|
|
10
|
+
label: "Empleados",
|
|
11
|
+
hide: !showEmployees,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
to: `${base}/roles`,
|
|
15
|
+
label: "Roles",
|
|
16
|
+
hide: !showRoles,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
to: `${base}/permisos`,
|
|
20
|
+
label: "Permisos",
|
|
21
|
+
hide: !showPermissions,
|
|
22
|
+
},
|
|
23
|
+
] }), children] }) }));
|
|
24
|
+
};
|
package/dist/esm/Tab/index.js
CHANGED
package/dist/esm/index.js
CHANGED
package/dist/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zauru-sdk/components",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.11.0",
|
|
4
4
|
"description": "Componentes reutilizables en las WebApps de Zauru.",
|
|
5
5
|
"main": "./dist/esm/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"@reduxjs/toolkit": "^2.2.1",
|
|
45
45
|
"@remix-run/node": "^2.8.1",
|
|
46
46
|
"@remix-run/react": "^2.8.1",
|
|
47
|
-
"@zauru-sdk/common": "^6.8.
|
|
48
|
-
"@zauru-sdk/hooks": "^6.3.
|
|
47
|
+
"@zauru-sdk/common": "^6.8.5",
|
|
48
|
+
"@zauru-sdk/hooks": "^6.3.18",
|
|
49
49
|
"@zauru-sdk/icons": "^6.1.0",
|
|
50
|
-
"@zauru-sdk/redux": "^6.5.
|
|
51
|
-
"@zauru-sdk/types": "^6.
|
|
52
|
-
"@zauru-sdk/utils": "^6.
|
|
50
|
+
"@zauru-sdk/redux": "^6.5.16",
|
|
51
|
+
"@zauru-sdk/types": "^6.23.0",
|
|
52
|
+
"@zauru-sdk/utils": "^6.22.0",
|
|
53
53
|
"browser-image-compression": "^2.0.2",
|
|
54
54
|
"framer-motion": "^12.0.0",
|
|
55
55
|
"jsonwebtoken": "^9.0.2",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"styled-components": "^5.3.5",
|
|
63
63
|
"zod": "^3.23.8"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "885c1d44afb4c5717ae823664b37ac48b30283db"
|
|
66
66
|
}
|