@dashadmin/dash-admin 1.3.44 → 1.3.46
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/dist/components/i18n/BridgedLocalesMenuButton.js +1 -2
- package/dist/components/menu/DarkToggleMode.js +1 -1
- package/dist/components/serviceAccount/ApiKeyRevealDialog.js +110 -0
- package/dist/components/serviceAccount/ServiceAccountStatusSwitch.js +94 -0
- package/dist/components/subscription/PlanPricesSettings.js +4 -2
- package/dist/components/tenancy/GlobalTenantWrapper.js +64 -0
- package/dist/components/tenancy/TenantSwitcher.js +389 -0
- package/dist/contexts/com/components/EchoNotificationsCenter.js +40 -0
- package/dist/default-theme/menu/AppMenuComponents/expanded/SidebarItem.js +1 -0
- package/dist/index.js +60 -40
- package/dist/pages/Profile.js +32 -38
- package/dist/providers/i18n/en.js +108 -0
- package/dist/providers/i18n/es.js +108 -0
- package/dist/providers/i18n/languages.js +3 -1
- package/dist/providers/i18n/mergeTranslations.js +18 -0
- package/dist/resources/User/components/AvatarUploadField.js +61 -0
- package/dist/resources/User/components/PasswordFields.js +67 -0
- package/dist/resources/User/getProfileMenu.js +36 -0
- package/dist/resources/User/profileResource.js +33 -0
- package/dist/resources/User/userLayout.js +58 -0
- package/dist/resources/User/userResource.js +90 -0
- package/dist/resources/User/userSchema.js +206 -0
- package/dist/resources/User/usersFilters.js +22 -0
- package/dist/systemResources.js +30 -19
- package/dist/templates/ResourceTemplateCreate.js +5 -2
- package/package.json +4 -1
- package/src/components/i18n/BridgedLocalesMenuButton.tsx +1 -2
- package/src/components/menu/DarkToggleMode.tsx +1 -1
- package/src/components/serviceAccount/ApiKeyRevealDialog.tsx +129 -0
- package/src/components/serviceAccount/ServiceAccountStatusSwitch.tsx +99 -0
- package/src/components/subscription/PlanPricesSettings.tsx +2 -2
- package/src/components/tenancy/GlobalTenantWrapper.tsx +84 -0
- package/src/components/tenancy/TenantSwitcher.tsx +485 -0
- package/src/contexts/com/components/EchoNotificationsCenter.tsx +65 -0
- package/src/default-theme/menu/AppMaterialMenu.tsx +1 -1
- package/src/default-theme/menu/AppMenuComponents/expanded/SidebarItem.tsx +1 -0
- package/src/default-theme/menu/SidebarActions.tsx +1 -1
- package/src/index.tsx +15 -0
- package/src/pages/Profile.tsx +69 -107
- package/src/providers/i18n/en.ts +113 -0
- package/src/providers/i18n/es.ts +113 -0
- package/src/providers/i18n/languages.tsx +3 -0
- package/src/providers/i18n/mergeTranslations.ts +53 -0
- package/src/resources/User/components/AvatarUploadField.tsx +57 -0
- package/src/resources/User/components/PasswordFields.tsx +73 -0
- package/src/resources/User/getProfileMenu.ts +57 -0
- package/src/resources/User/profileResource.tsx +51 -0
- package/src/resources/User/userLayout.tsx +62 -0
- package/src/resources/User/userResource.tsx +111 -0
- package/src/resources/User/userSchema.tsx +213 -0
- package/src/resources/User/usersFilters.tsx +21 -0
- package/src/systemResources.tsx +31 -20
- package/src/templates/ResourceTemplateCreate.tsx +17 -2
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Card, Grid } from "@mui/material";
|
|
3
|
+
const userLayout = (render) => {
|
|
4
|
+
return /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 1, children: [
|
|
5
|
+
/* @__PURE__ */ jsx(Grid, { size: { xs: 12, md: 4 }, children: /* @__PURE__ */ jsxs(Grid, { container: true, children: [
|
|
6
|
+
/* @__PURE__ */ jsx(Grid, { size: 12, children: /* @__PURE__ */ jsxs(Card, { sx: { mb: 1 }, className: "dash-widget", children: [
|
|
7
|
+
/* @__PURE__ */ jsx(
|
|
8
|
+
Box,
|
|
9
|
+
{
|
|
10
|
+
sx: { p: 1 },
|
|
11
|
+
style: { display: "flex", alignItems: "center" },
|
|
12
|
+
children: /* @__PURE__ */ jsx("span", { className: "card-title", children: "Avatar" })
|
|
13
|
+
}
|
|
14
|
+
),
|
|
15
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 1 }, children: render("Avatar") })
|
|
16
|
+
] }) }),
|
|
17
|
+
/* @__PURE__ */ jsx(Grid, { size: 12, children: /* @__PURE__ */ jsxs(Card, { sx: { mb: 1 }, className: "dash-widget", children: [
|
|
18
|
+
/* @__PURE__ */ jsx(
|
|
19
|
+
Box,
|
|
20
|
+
{
|
|
21
|
+
sx: { p: 1 },
|
|
22
|
+
style: { display: "flex", alignItems: "center" },
|
|
23
|
+
children: /* @__PURE__ */ jsx("span", { className: "card-title", children: "Credenciales" })
|
|
24
|
+
}
|
|
25
|
+
),
|
|
26
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 1 }, children: render("Credenciales") })
|
|
27
|
+
] }) })
|
|
28
|
+
] }) }),
|
|
29
|
+
/* @__PURE__ */ jsx(Grid, { size: { xs: 12, md: 8 }, children: /* @__PURE__ */ jsxs(Grid, { container: true, children: [
|
|
30
|
+
/* @__PURE__ */ jsx(Grid, { size: 12, children: /* @__PURE__ */ jsxs(Card, { sx: { mb: 1 }, className: "dash-widget", children: [
|
|
31
|
+
/* @__PURE__ */ jsx(
|
|
32
|
+
Box,
|
|
33
|
+
{
|
|
34
|
+
sx: { p: 1 },
|
|
35
|
+
style: { display: "flex", alignItems: "center" },
|
|
36
|
+
children: /* @__PURE__ */ jsx("span", { className: "card-title", children: "Datos del usuario" })
|
|
37
|
+
}
|
|
38
|
+
),
|
|
39
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 1 }, children: render("Datos del usuario") })
|
|
40
|
+
] }) }),
|
|
41
|
+
/* @__PURE__ */ jsx(Grid, { size: 12, children: /* @__PURE__ */ jsxs(Card, { sx: { mb: 1 }, className: "dash-widget", children: [
|
|
42
|
+
/* @__PURE__ */ jsx(
|
|
43
|
+
Box,
|
|
44
|
+
{
|
|
45
|
+
sx: { p: 1 },
|
|
46
|
+
style: { display: "flex", alignItems: "center" },
|
|
47
|
+
children: /* @__PURE__ */ jsx("span", { className: "card-title", children: "Configuraci\xF3n" })
|
|
48
|
+
}
|
|
49
|
+
),
|
|
50
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 1 }, children: render("Configuraci\xF3n") })
|
|
51
|
+
] }) })
|
|
52
|
+
] }) })
|
|
53
|
+
] });
|
|
54
|
+
};
|
|
55
|
+
var userLayout_default = userLayout;
|
|
56
|
+
export {
|
|
57
|
+
userLayout_default as default
|
|
58
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import ResourceTemplate from "../../templates/ResourceTemplate";
|
|
3
|
+
import Settings from "@mui/icons-material/Settings";
|
|
4
|
+
import { PaginationComponent } from "dash-components";
|
|
5
|
+
import { DASHAppConstants } from "dash-constants";
|
|
6
|
+
import { DASHAdminSystemConstants } from "dash-constants";
|
|
7
|
+
import userLayout from "./userLayout";
|
|
8
|
+
import userSchema from "./userSchema";
|
|
9
|
+
import { NoResults } from "../../components/misc/NoResults";
|
|
10
|
+
import usersFilters from "./usersFilters";
|
|
11
|
+
const userResource = {
|
|
12
|
+
filterWithSubmit: true,
|
|
13
|
+
roles: [
|
|
14
|
+
DASHAdminSystemConstants.system.DASH_SYSTEM_ROLE,
|
|
15
|
+
DASHAppConstants.system.TENANT_ROLE
|
|
16
|
+
],
|
|
17
|
+
component: ResourceTemplate,
|
|
18
|
+
trash: true,
|
|
19
|
+
model: "tenant/user",
|
|
20
|
+
schema: userSchema,
|
|
21
|
+
icon: /* @__PURE__ */ jsx(Settings, {}),
|
|
22
|
+
group: "resource.groups.configuration",
|
|
23
|
+
label: "resource.ecommerce.users.label",
|
|
24
|
+
menu: [
|
|
25
|
+
{
|
|
26
|
+
title: "Usuarios",
|
|
27
|
+
redirect: "/tenant/user"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
title: "\u{1F5D1}",
|
|
31
|
+
redirect: "/tenant/user/trash"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
resourceMenuDisabled: false,
|
|
35
|
+
refreshAfter: true,
|
|
36
|
+
/*customListActions: (props) => {
|
|
37
|
+
const { children } = props;
|
|
38
|
+
return <Card sx={{ mb: 2 }} className='dash-card-content dash-module top-toolbar'>{children}</Card>;
|
|
39
|
+
},*/
|
|
40
|
+
isFormData: true,
|
|
41
|
+
/** @deprecated TODO: side effects? seems is not implemented anywhere */
|
|
42
|
+
listEditButton: { enabled: true },
|
|
43
|
+
postFormatter: (params, _method) => {
|
|
44
|
+
if (!params.avatar) {
|
|
45
|
+
delete params.avatar;
|
|
46
|
+
}
|
|
47
|
+
return params;
|
|
48
|
+
},
|
|
49
|
+
dataGridProps: { stickyHeader: true, empty: /* @__PURE__ */ jsx(NoResults, {}), bulkActionButtons: false },
|
|
50
|
+
listProps: { perPage: DASHAdminSystemConstants.system.DEFAULT_PER_PAGE || 50, empty: false },
|
|
51
|
+
dataGridWrapper: (props) => {
|
|
52
|
+
const { children } = props;
|
|
53
|
+
return /* @__PURE__ */ jsx("div", { className: "sticky-header-wrapper", children });
|
|
54
|
+
},
|
|
55
|
+
redirectAfterUpdate: false,
|
|
56
|
+
createLayout: userLayout,
|
|
57
|
+
editLayout: userLayout,
|
|
58
|
+
formGroupMode: "layout",
|
|
59
|
+
/*exporter: (clients) => {
|
|
60
|
+
const clientsForExport = clients.map((client) => {
|
|
61
|
+
return client;
|
|
62
|
+
});
|
|
63
|
+
jsonExport(
|
|
64
|
+
clientsForExport,
|
|
65
|
+
{
|
|
66
|
+
//headers: ['id', 'title', 'author_name', 'body'] // order fields in the export
|
|
67
|
+
},
|
|
68
|
+
(err, csv) => {
|
|
69
|
+
downloadCSV(csv, 'clientes');
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
},*/
|
|
73
|
+
create: true,
|
|
74
|
+
edit: true,
|
|
75
|
+
view: false,
|
|
76
|
+
listViewButton: { enabled: false },
|
|
77
|
+
toolbarCreateButton: { enabled: true },
|
|
78
|
+
toolbarEditButton: { enabled: false },
|
|
79
|
+
toolbarExportButton: { enabled: false },
|
|
80
|
+
toolbarListButton: { enabled: true },
|
|
81
|
+
listDeleteButton: { enabled: true, props: { confirm: true } },
|
|
82
|
+
Pagination: PaginationComponent,
|
|
83
|
+
drawer: false,
|
|
84
|
+
mutationMode: "pessimistic",
|
|
85
|
+
referenceFilters: usersFilters
|
|
86
|
+
};
|
|
87
|
+
var userResource_default = userResource;
|
|
88
|
+
export {
|
|
89
|
+
userResource_default as default
|
|
90
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { SelectArrayInput } from "react-admin";
|
|
2
|
+
import { RutValidator } from "../../utils/validators";
|
|
3
|
+
import AvatarUploadField from "./components/AvatarUploadField";
|
|
4
|
+
import PasswordFields from "./components/PasswordFields";
|
|
5
|
+
const userSchema = [
|
|
6
|
+
{
|
|
7
|
+
tab: "Datos del usuario",
|
|
8
|
+
attribute: "id",
|
|
9
|
+
label: "Id",
|
|
10
|
+
type: String,
|
|
11
|
+
sortable: true,
|
|
12
|
+
inEdit: false,
|
|
13
|
+
inCreate: false
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
tab: "Datos del usuario",
|
|
17
|
+
attribute: "public_id",
|
|
18
|
+
label: "Rut",
|
|
19
|
+
type: String,
|
|
20
|
+
fieldOptions: { required: true, showDiv: true, fullWidth: true },
|
|
21
|
+
sortable: true,
|
|
22
|
+
inEdit: true,
|
|
23
|
+
inCreate: true,
|
|
24
|
+
inShow: true,
|
|
25
|
+
inList: false,
|
|
26
|
+
validate: (value) => {
|
|
27
|
+
if (!value) throw Error("El campo es requerido");
|
|
28
|
+
if (!RutValidator(value)) {
|
|
29
|
+
throw Error("El Rut es inv\xE1lido");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
tab: "Datos del usuario",
|
|
35
|
+
attribute: "name",
|
|
36
|
+
label: "Nombre",
|
|
37
|
+
type: String,
|
|
38
|
+
sortable: true,
|
|
39
|
+
inEdit: true,
|
|
40
|
+
inCreate: true,
|
|
41
|
+
inShow: false,
|
|
42
|
+
inList: false,
|
|
43
|
+
fieldOptions: { required: true }
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
tab: "Datos del usuario",
|
|
47
|
+
attribute: "lastname",
|
|
48
|
+
label: "Apellido",
|
|
49
|
+
type: String,
|
|
50
|
+
sortable: true,
|
|
51
|
+
inEdit: true,
|
|
52
|
+
inCreate: true,
|
|
53
|
+
inShow: true,
|
|
54
|
+
inList: false,
|
|
55
|
+
fieldOptions: { required: true }
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
tab: "Datos del usuario",
|
|
59
|
+
attribute: "full_name",
|
|
60
|
+
label: "Nombre",
|
|
61
|
+
type: String,
|
|
62
|
+
sortable: true,
|
|
63
|
+
inEdit: false,
|
|
64
|
+
inShow: true,
|
|
65
|
+
inCreate: false,
|
|
66
|
+
fieldOptions: { required: true }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
tab: "Datos del usuario",
|
|
70
|
+
attribute: "email",
|
|
71
|
+
label: "Email",
|
|
72
|
+
type: String,
|
|
73
|
+
sortable: true,
|
|
74
|
+
inEdit: true,
|
|
75
|
+
inShow: true,
|
|
76
|
+
inCreate: true,
|
|
77
|
+
fieldOptions: { required: true }
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
tab: "Datos del usuario",
|
|
81
|
+
attribute: "phone",
|
|
82
|
+
label: "Telefono",
|
|
83
|
+
type: String,
|
|
84
|
+
sortable: true,
|
|
85
|
+
inEdit: true,
|
|
86
|
+
inCreate: true,
|
|
87
|
+
inShow: true,
|
|
88
|
+
inList: false
|
|
89
|
+
},
|
|
90
|
+
/*{
|
|
91
|
+
tab: 'Configuración',
|
|
92
|
+
attribute: 'active',
|
|
93
|
+
type: Boolean,
|
|
94
|
+
inList: true,
|
|
95
|
+
inCreate: false,
|
|
96
|
+
label: 'Activo',
|
|
97
|
+
showLabel: false,
|
|
98
|
+
processor: 'Boolean',
|
|
99
|
+
},*/
|
|
100
|
+
{
|
|
101
|
+
tab: "Credenciales",
|
|
102
|
+
attribute: "password",
|
|
103
|
+
label: "Contrase\xF1a",
|
|
104
|
+
type: String,
|
|
105
|
+
sortable: true,
|
|
106
|
+
inEdit: false,
|
|
107
|
+
inCreate: false,
|
|
108
|
+
inShow: false,
|
|
109
|
+
inList: false
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
tab: "Credenciales",
|
|
113
|
+
attribute: "password_confirmation",
|
|
114
|
+
label: "Confirma Contrase\xF1a",
|
|
115
|
+
type: String,
|
|
116
|
+
sortable: true,
|
|
117
|
+
inEdit: false,
|
|
118
|
+
inCreate: false,
|
|
119
|
+
inShow: false,
|
|
120
|
+
inList: false
|
|
121
|
+
},
|
|
122
|
+
/*{
|
|
123
|
+
tab: 'Credenciales',
|
|
124
|
+
attribute: 'role_id',
|
|
125
|
+
type: 'system/role/forSelect.name',
|
|
126
|
+
label: 'Rol de usuario',
|
|
127
|
+
componentProps: {
|
|
128
|
+
filter: { substitutable: true } // Shows only substituable roles for current user, backend logic.
|
|
129
|
+
},
|
|
130
|
+
sortable: true,
|
|
131
|
+
inEdit: true,
|
|
132
|
+
inCreate: true,
|
|
133
|
+
inShow: true,
|
|
134
|
+
inList: false,
|
|
135
|
+
multiple: false,
|
|
136
|
+
},*/
|
|
137
|
+
{
|
|
138
|
+
tab: "Credenciales",
|
|
139
|
+
label: "Roles",
|
|
140
|
+
attribute: "role_ids",
|
|
141
|
+
//listAttribute: 'name',
|
|
142
|
+
// system/role is core-owned and works for every domain (kitchntabs-*, vanexa-*),
|
|
143
|
+
// unlike tenant/role which only exists in kitchntabs-backend-domain
|
|
144
|
+
// (Domain\App\Http\Controllers\API\ECommerce\RoleController — an empty
|
|
145
|
+
// Domain\App\Models\ECommerce\Role subclass of the same core Role/roles table,
|
|
146
|
+
// with equivalent privilege-level-based visibility filtering).
|
|
147
|
+
type: "system/role/forSelect.name",
|
|
148
|
+
//type: Object,
|
|
149
|
+
// TODO Agregar un filtro, solo para traer los roles de cliente.
|
|
150
|
+
pagination: false,
|
|
151
|
+
multiple: true,
|
|
152
|
+
//custom: true,
|
|
153
|
+
//componentProps:{ options: {fullwidth: true} },
|
|
154
|
+
component: SelectArrayInput
|
|
155
|
+
//searchField: "subdomain"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
// @TODO Solo mostrar cuando se está creando:
|
|
159
|
+
label: "Genear Contrase\xF1a Aleatorea Autom\xE1ticamente",
|
|
160
|
+
attribute: "generate_password",
|
|
161
|
+
type: Boolean,
|
|
162
|
+
inList: false,
|
|
163
|
+
inEdit: false,
|
|
164
|
+
tab: "Configuraci\xF3n"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
attribute: "active",
|
|
168
|
+
type: Boolean,
|
|
169
|
+
inList: true,
|
|
170
|
+
label: "Activo",
|
|
171
|
+
tab: "Configuraci\xF3n",
|
|
172
|
+
processor: "Boolean"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
tab: "Avatar",
|
|
176
|
+
attribute: "avatar",
|
|
177
|
+
listAttribute: "image_url",
|
|
178
|
+
label: "Foto Usuario",
|
|
179
|
+
type: String,
|
|
180
|
+
sortable: true,
|
|
181
|
+
inEdit: true,
|
|
182
|
+
inCreate: true,
|
|
183
|
+
inShow: true,
|
|
184
|
+
inList: false,
|
|
185
|
+
custom: true,
|
|
186
|
+
component: AvatarUploadField,
|
|
187
|
+
processor: "File"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
tab: "Credenciales",
|
|
191
|
+
attribute: "password",
|
|
192
|
+
label: "Contrase\xF1a",
|
|
193
|
+
type: String,
|
|
194
|
+
sortable: true,
|
|
195
|
+
inList: false,
|
|
196
|
+
inEdit: true,
|
|
197
|
+
inCreate: true,
|
|
198
|
+
custom: true,
|
|
199
|
+
inShow: false,
|
|
200
|
+
component: PasswordFields
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
var userSchema_default = userSchema;
|
|
204
|
+
export {
|
|
205
|
+
userSchema_default as default
|
|
206
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const usersReferenceFilters = [
|
|
2
|
+
{
|
|
3
|
+
id: "q",
|
|
4
|
+
label: "Nombres/Email/Busqueda General",
|
|
5
|
+
source: "q",
|
|
6
|
+
alwaysOn: true,
|
|
7
|
+
reference: null,
|
|
8
|
+
optionText: null
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "email",
|
|
12
|
+
label: "Email",
|
|
13
|
+
source: "email",
|
|
14
|
+
alwaysOn: true,
|
|
15
|
+
reference: null,
|
|
16
|
+
optionText: null
|
|
17
|
+
}
|
|
18
|
+
];
|
|
19
|
+
var usersFilters_default = usersReferenceFilters;
|
|
20
|
+
export {
|
|
21
|
+
usersFilters_default as default
|
|
22
|
+
};
|
package/dist/systemResources.js
CHANGED
|
@@ -29,6 +29,8 @@ import Avatar from "./components/avatar/Avatar";
|
|
|
29
29
|
import SystemUpdateAlt from "@mui/icons-material/SystemUpdateAlt";
|
|
30
30
|
import Person from "@mui/icons-material/Person";
|
|
31
31
|
import CardMembershipIcon from "@mui/icons-material/CardMembership";
|
|
32
|
+
import PaidIcon from "@mui/icons-material/Paid";
|
|
33
|
+
import TranslateIcon from "@mui/icons-material/Translate";
|
|
32
34
|
import { lazy } from "react";
|
|
33
35
|
import BusinessIcon from "@mui/icons-material/Business";
|
|
34
36
|
import Chip from "@mui/material/Chip";
|
|
@@ -878,12 +880,19 @@ const systemResources = [
|
|
|
878
880
|
redirectAfterUpdate: false,
|
|
879
881
|
redirectAfterCreate: true
|
|
880
882
|
}, drawerSettings),
|
|
883
|
+
// ============ Currency Management (core shared master data) ============
|
|
884
|
+
// Backed by dash-backend core's system/currency route (App\Http\Controllers\
|
|
885
|
+
// API\System\CurrencyController). Previously pointed at the domain's
|
|
886
|
+
// ecommerce/currency route — currencies moved to core as "shared master
|
|
887
|
+
// data" (see CLAUDE.md's core/domain split), so every app using this
|
|
888
|
+
// shared systemResources manifest (kitchntabs-*, vanexa-*) now gets a
|
|
889
|
+
// working Currencies page with no domain layer required.
|
|
881
890
|
{
|
|
882
891
|
roles: [DASHAppConstants.system.SYSTEM_ROLE],
|
|
883
892
|
component: ResourceTemplate,
|
|
884
893
|
trash: true,
|
|
885
|
-
model: "
|
|
886
|
-
label: "resource.
|
|
894
|
+
model: "system/currency",
|
|
895
|
+
label: "resource.system.currencies.label",
|
|
887
896
|
schema: [
|
|
888
897
|
{
|
|
889
898
|
label: "C\xF3digo",
|
|
@@ -920,23 +929,20 @@ const systemResources = [
|
|
|
920
929
|
component: FormatCurrency
|
|
921
930
|
}
|
|
922
931
|
],
|
|
923
|
-
|
|
924
|
-
//references: [{ reference: 'roles', target: 'id', schema: roleSchema }],
|
|
925
|
-
icon: /* @__PURE__ */ jsx(Person, {}),
|
|
926
|
-
// group: "resource.groups.system_resources",
|
|
932
|
+
icon: /* @__PURE__ */ jsx(PaidIcon, {}),
|
|
927
933
|
group: "System",
|
|
928
934
|
menu: [
|
|
929
935
|
{
|
|
930
|
-
title: "resource.
|
|
931
|
-
redirect: "/
|
|
936
|
+
title: "resource.system.currencies.menu_list",
|
|
937
|
+
redirect: "/system/currency"
|
|
932
938
|
},
|
|
933
939
|
{
|
|
934
940
|
title: "\u{1F5D1}",
|
|
935
|
-
redirect: "/
|
|
941
|
+
redirect: "/system/currency/trash"
|
|
936
942
|
}
|
|
937
943
|
],
|
|
938
944
|
mainAction: {
|
|
939
|
-
title: "resource.
|
|
945
|
+
title: "resource.system.currencies.main_action",
|
|
940
946
|
mode: "create",
|
|
941
947
|
fn: "virtualhash",
|
|
942
948
|
redirect: "inline/create"
|
|
@@ -950,16 +956,21 @@ const systemResources = [
|
|
|
950
956
|
mutationMode: "pessimistic",
|
|
951
957
|
saveButtonAlwaysEnabled: true,
|
|
952
958
|
listProps: { storeKey: false, filterDefaultValues: { show_disabled: true } },
|
|
953
|
-
// deshabilita persistencia deel estado, cache de los valores seleccionados sort, page, etc.
|
|
954
959
|
resetSelectedIdsOnLoad: true
|
|
955
960
|
},
|
|
956
|
-
// ============ Language Management ============
|
|
961
|
+
// ============ Language Management (core shared master data) ============
|
|
962
|
+
// Backed by dash-backend core's system/language route (App\Http\Controllers\
|
|
963
|
+
// API\System\LanguageController). Previously pointed at the domain's
|
|
964
|
+
// common/language route — languages moved to core as "shared master data"
|
|
965
|
+
// (see CLAUDE.md's core/domain split), so every app using this shared
|
|
966
|
+
// systemResources manifest (kitchntabs-*, vanexa-*) now gets a working
|
|
967
|
+
// Languages page with no domain layer required.
|
|
957
968
|
{
|
|
958
969
|
roles: [DASHAppConstants.system.SYSTEM_ROLE],
|
|
959
970
|
component: ResourceTemplate,
|
|
960
971
|
trash: true,
|
|
961
|
-
model: "
|
|
962
|
-
label: "resource.
|
|
972
|
+
model: "system/language",
|
|
973
|
+
label: "resource.system.languages.label",
|
|
963
974
|
schema: [
|
|
964
975
|
{
|
|
965
976
|
label: "Code",
|
|
@@ -1003,20 +1014,20 @@ const systemResources = [
|
|
|
1003
1014
|
}
|
|
1004
1015
|
}
|
|
1005
1016
|
],
|
|
1006
|
-
icon: /* @__PURE__ */ jsx(
|
|
1017
|
+
icon: /* @__PURE__ */ jsx(TranslateIcon, {}),
|
|
1007
1018
|
group: "System",
|
|
1008
1019
|
menu: [
|
|
1009
1020
|
{
|
|
1010
|
-
title: "resource.
|
|
1011
|
-
redirect: "/
|
|
1021
|
+
title: "resource.system.languages.menu_list",
|
|
1022
|
+
redirect: "/system/language"
|
|
1012
1023
|
},
|
|
1013
1024
|
{
|
|
1014
1025
|
title: "\u{1F5D1}",
|
|
1015
|
-
redirect: "/
|
|
1026
|
+
redirect: "/system/language/trash"
|
|
1016
1027
|
}
|
|
1017
1028
|
],
|
|
1018
1029
|
mainAction: {
|
|
1019
|
-
title: "resource.
|
|
1030
|
+
title: "resource.system.languages.main_action",
|
|
1020
1031
|
mode: "create",
|
|
1021
1032
|
fn: "virtualhash",
|
|
1022
1033
|
redirect: "inline/create"
|
|
@@ -40,15 +40,18 @@ const ResourceTemplateCreate = (props) => {
|
|
|
40
40
|
return `/${path}`.replace(/\/+/g, "/");
|
|
41
41
|
};
|
|
42
42
|
const onCreate = (data) => {
|
|
43
|
+
var _a, _b;
|
|
43
44
|
const translatedLabel = translate(resourceConfig.label, { _: resourceConfig.label });
|
|
44
45
|
if (_showNotifyAfterSubmit) {
|
|
45
46
|
notify("dash.resource.created", { type: "success" });
|
|
46
47
|
}
|
|
47
48
|
if (_showDialogAfterSubmit) {
|
|
49
|
+
const successOverride = (_a = resourceConfig == null ? void 0 : resourceConfig.createSuccessDialog) == null ? void 0 : _a.call(resourceConfig, data);
|
|
50
|
+
const overrideTitle = typeof (successOverride == null ? void 0 : successOverride.title) === "string" ? translate(successOverride.title, { _: successOverride.title }) : successOverride == null ? void 0 : successOverride.title;
|
|
48
51
|
dialog({
|
|
49
52
|
variant: "info",
|
|
50
|
-
title: translate("dash.resource.created"),
|
|
51
|
-
content: translate("dash.resource.created_message", { label: translatedLabel }),
|
|
53
|
+
title: overrideTitle != null ? overrideTitle : translate("dash.resource.created"),
|
|
54
|
+
content: (_b = successOverride == null ? void 0 : successOverride.content) != null ? _b : translate("dash.resource.created_message", { label: translatedLabel }),
|
|
52
55
|
onConfirm: () => {
|
|
53
56
|
switch (resourceConfig == null ? void 0 : resourceConfig.redirectAfterCreate) {
|
|
54
57
|
case false:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dashadmin/dash-admin",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.46",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@capacitor/core": "^7.4.2",
|
|
29
29
|
"@capacitor/push-notifications": "^7.0.1",
|
|
30
|
+
"antd": "latest",
|
|
31
|
+
"@tanstack/react-query": "latest",
|
|
32
|
+
"@tanstack/react-query-persist-client": "latest",
|
|
30
33
|
"@emotion/react": "latest",
|
|
31
34
|
"@emotion/styled": "latest",
|
|
32
35
|
"@mui/material": "^7.3.10",
|
|
@@ -111,8 +111,7 @@ const BridgedLocalesMenuButton: React.FC<BridgedLocalesMenuButtonProps> = ({
|
|
|
111
111
|
<Tooltip title={`Language: ${currentLocaleName}`}>
|
|
112
112
|
<IconButton
|
|
113
113
|
onClick={handleClick}
|
|
114
|
-
|
|
115
|
-
size="small"
|
|
114
|
+
className="dash-icon-button-color dash-icon-button-bg"
|
|
116
115
|
aria-label="change language"
|
|
117
116
|
aria-controls={open ? 'locale-menu' : undefined}
|
|
118
117
|
aria-haspopup="true"
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import Alert from "@mui/material/Alert";
|
|
3
|
+
import Box from "@mui/material/Box";
|
|
4
|
+
import IconButton from "@mui/material/IconButton";
|
|
5
|
+
import Tooltip from "@mui/material/Tooltip";
|
|
6
|
+
import Typography from "@mui/material/Typography";
|
|
7
|
+
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
|
8
|
+
import CheckIcon from "@mui/icons-material/Check";
|
|
9
|
+
import { useI18nBridge } from "../../contexts/I18nBridgeContext";
|
|
10
|
+
|
|
11
|
+
export interface IApiKeyRevealContentProps {
|
|
12
|
+
/** The plaintext token, shown exactly once. */
|
|
13
|
+
token: string;
|
|
14
|
+
/** Name of the key, for context. */
|
|
15
|
+
name?: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One-time reveal of a newly issued API key.
|
|
20
|
+
*
|
|
21
|
+
* The plaintext credential is never persisted in recoverable form - only a
|
|
22
|
+
* short display prefix is stored - so this is the single moment the operator
|
|
23
|
+
* can copy it.
|
|
24
|
+
*
|
|
25
|
+
* Designed to be passed as the `content` of the framework dialog via a
|
|
26
|
+
* resource's `createSuccessDialog`, so it inherits the app's dialog chrome
|
|
27
|
+
* rather than introducing a second dialog system:
|
|
28
|
+
*
|
|
29
|
+
* createSuccessDialog: (data) => data?.plain_text_token
|
|
30
|
+
* ? { title: '...', content: <ApiKeyRevealContent token={data.plain_text_token} name={data.name} /> }
|
|
31
|
+
* : null,
|
|
32
|
+
*/
|
|
33
|
+
export const ApiKeyRevealContent: React.FC<IApiKeyRevealContentProps> = ({ token, name }) => {
|
|
34
|
+
// DialogServiceProvider is mounted ABOVE <Admin> in DASHAppProviders, so
|
|
35
|
+
// dialog content renders outside React Admin's i18n context and
|
|
36
|
+
// useTranslate() would silently fall through to the English defaults.
|
|
37
|
+
// The bridge is the supported way to translate from out there - same
|
|
38
|
+
// pattern as AppMaterialMenu.
|
|
39
|
+
const { i18nProvider, locale } = useI18nBridge();
|
|
40
|
+
const [copied, setCopied] = useState(false);
|
|
41
|
+
|
|
42
|
+
const translate = React.useCallback(
|
|
43
|
+
(key: string, options?: any) => {
|
|
44
|
+
if (i18nProvider?.translate) {
|
|
45
|
+
try {
|
|
46
|
+
return i18nProvider.translate(key, options);
|
|
47
|
+
} catch {
|
|
48
|
+
return options?._ ?? key;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return options?._ ?? key;
|
|
52
|
+
},
|
|
53
|
+
// locale is a dependency so the text re-renders on a language switch.
|
|
54
|
+
[i18nProvider, locale]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const handleCopy = async () => {
|
|
58
|
+
try {
|
|
59
|
+
await navigator.clipboard.writeText(token);
|
|
60
|
+
setCopied(true);
|
|
61
|
+
setTimeout(() => setCopied(false), 2000);
|
|
62
|
+
} catch {
|
|
63
|
+
// Clipboard API unavailable (insecure context or denied permission).
|
|
64
|
+
// The value stays selectable so the operator can still copy it by
|
|
65
|
+
// hand - never leave them without a path to the secret.
|
|
66
|
+
setCopied(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Box>
|
|
72
|
+
<Alert severity="warning" sx={{ mb: 2 }}>
|
|
73
|
+
{translate("serviceAccount.reveal.warning", {
|
|
74
|
+
_: "This is the only time this key will be shown. It cannot be recovered later — if you lose it, you will have to issue a new one.",
|
|
75
|
+
})}
|
|
76
|
+
</Alert>
|
|
77
|
+
|
|
78
|
+
{name ? (
|
|
79
|
+
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
80
|
+
{translate("serviceAccount.reveal.forKey", { _: "Key" })}: <strong>{name}</strong>
|
|
81
|
+
</Typography>
|
|
82
|
+
) : null}
|
|
83
|
+
|
|
84
|
+
<Box
|
|
85
|
+
sx={{
|
|
86
|
+
display: "flex",
|
|
87
|
+
alignItems: "center",
|
|
88
|
+
gap: 1,
|
|
89
|
+
p: 1.5,
|
|
90
|
+
borderRadius: 1,
|
|
91
|
+
border: (theme) => `1px solid ${theme.palette.divider}`,
|
|
92
|
+
bgcolor: (theme) =>
|
|
93
|
+
theme.palette.mode === "dark" ? "rgba(255,255,255,0.04)" : "rgba(0,0,0,0.04)",
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
<Box
|
|
97
|
+
component="code"
|
|
98
|
+
sx={{
|
|
99
|
+
flex: 1,
|
|
100
|
+
fontFamily: "monospace",
|
|
101
|
+
fontSize: "0.875rem",
|
|
102
|
+
wordBreak: "break-all",
|
|
103
|
+
userSelect: "all",
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
{token}
|
|
107
|
+
</Box>
|
|
108
|
+
|
|
109
|
+
<Tooltip
|
|
110
|
+
title={
|
|
111
|
+
copied
|
|
112
|
+
? translate("serviceAccount.reveal.copied", { _: "Copied" })
|
|
113
|
+
: translate("serviceAccount.reveal.copy", { _: "Copy" })
|
|
114
|
+
}
|
|
115
|
+
>
|
|
116
|
+
<IconButton onClick={handleCopy} size="small" aria-label="copy api key">
|
|
117
|
+
{copied ? (
|
|
118
|
+
<CheckIcon color="success" fontSize="small" />
|
|
119
|
+
) : (
|
|
120
|
+
<ContentCopyIcon fontSize="small" />
|
|
121
|
+
)}
|
|
122
|
+
</IconButton>
|
|
123
|
+
</Tooltip>
|
|
124
|
+
</Box>
|
|
125
|
+
</Box>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export default ApiKeyRevealContent;
|