@fluxo-dev/shell 0.0.8
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/.babelrc +12 -0
- package/.eslintrc.json +18 -0
- package/README.md +7 -0
- package/jest.config.ts +11 -0
- package/package.json +4 -0
- package/project.json +43 -0
- package/src/index.ts +1 -0
- package/src/lib/app.module.css +1 -0
- package/src/lib/app.spec.tsx +17 -0
- package/src/lib/app.tsx +374 -0
- package/src/lib/components/error-page/404.tsx +16 -0
- package/src/lib/components/routes/main-routes.tsx +26 -0
- package/src/lib/layouts/blank/blank-layout.tsx +10 -0
- package/src/lib/layouts/main/app-bar.tsx +192 -0
- package/src/lib/layouts/main/app-layout.tsx +52 -0
- package/src/lib/layouts/main/content.tsx +10 -0
- package/src/lib/layouts/main/footer-bar.tsx +76 -0
- package/src/lib/layouts/main/navbar-item.module.css +49 -0
- package/src/lib/layouts/main/navbar-item.tsx +129 -0
- package/src/lib/layouts/main/navbar.tsx +38 -0
- package/src/lib/layouts/main/search-bar.tsx +44 -0
- package/src/lib/layouts/main/shell-spotlight.tsx +58 -0
- package/src/lib/models/app-jit.ts +6 -0
- package/src/lib/pages/general/fluxo-plugin-loader.tsx +22 -0
- package/src/lib/pages/login/account-confirmation.tsx +86 -0
- package/src/lib/pages/login/change-password.tsx +58 -0
- package/src/lib/pages/login/forgot-password.tsx +53 -0
- package/src/lib/pages/login/login-page.tsx +70 -0
- package/src/lib/pages/login/reset-password.tsx +91 -0
- package/src/lib/pages/settings/apps/add-jit-app.tsx +79 -0
- package/src/lib/pages/settings/apps/app-item.tsx +157 -0
- package/src/lib/pages/settings/apps/app-jit-item.tsx +105 -0
- package/src/lib/pages/settings/apps/apps-list.tsx +171 -0
- package/src/lib/pages/settings/apps/embedded-module-item.tsx +66 -0
- package/src/lib/pages/settings/apps/install-or-upgrade-app.tsx +105 -0
- package/src/lib/pages/settings/menu-designer/custom-node.module.css +34 -0
- package/src/lib/pages/settings/menu-designer/custom-node.tsx +160 -0
- package/src/lib/pages/settings/menu-designer/menu-create-form.tsx +109 -0
- package/src/lib/pages/settings/menu-designer/menu-designer.module.css +19 -0
- package/src/lib/pages/settings/menu-designer/menu-designer.tsx +463 -0
- package/src/lib/pages/settings/menu-designer/menu-item-edit.tsx +115 -0
- package/src/lib/pages/settings/menu-designer/placeholder.module.css +8 -0
- package/src/lib/pages/settings/menu-designer/placeholder.tsx +13 -0
- package/src/lib/pages/settings/security/components/domani-role/domain-role-form.tsx +67 -0
- package/src/lib/pages/settings/security/components/domani-role/domain-role-grid.tsx +139 -0
- package/src/lib/pages/settings/security/components/permission/create-custom-permission-form.tsx +64 -0
- package/src/lib/pages/settings/security/components/permission/create-scope-form.tsx +158 -0
- package/src/lib/pages/settings/security/components/permission/permission-grid.tsx +304 -0
- package/src/lib/pages/settings/security/components/permission/permission-navigator.tsx +320 -0
- package/src/lib/pages/settings/security/components/permission/update-permission.tsx +54 -0
- package/src/lib/pages/settings/security/components/route-rule/route-rule-create-form.tsx +59 -0
- package/src/lib/pages/settings/security/components/route-rule/route-rule-grid.tsx +229 -0
- package/src/lib/pages/settings/security/components/user/user-form.tsx +123 -0
- package/src/lib/pages/settings/security/components/user/users-grid.tsx +127 -0
- package/src/lib/pages/settings/settings.tsx +298 -0
- package/src/lib/pages/settings/token/token-row.tsx +69 -0
- package/src/lib/pages/settings/token/tokens.tsx +73 -0
- package/src/lib/pages/settings/ui/ui.tsx +149 -0
- package/src/lib/services/app-jit-manager.ts +92 -0
- package/src/lib/services/app-menu.service.ts +35 -0
- package/src/lib/utils/icon-factory.tsx +25 -0
- package/src/lib/utils/security/only-not-logged-route.tsx +17 -0
- package/src/lib/utils/security/protected-route.tsx +47 -0
- package/tsconfig.json +20 -0
- package/tsconfig.lib.json +24 -0
- package/tsconfig.spec.json +20 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataCard,
|
|
3
|
+
ShellPage,
|
|
4
|
+
SideMenuGroup,
|
|
5
|
+
WaitData,
|
|
6
|
+
} from "@fluxo-dev/ui-components";
|
|
7
|
+
import { Text } from '@mantine/core';
|
|
8
|
+
import {
|
|
9
|
+
IconApi, IconApps, IconCode,
|
|
10
|
+
IconDashboard,
|
|
11
|
+
IconKey,
|
|
12
|
+
IconLockAccess,
|
|
13
|
+
IconMenu, IconPrinter, IconRoute, IconUsers, IconUsersGroup,
|
|
14
|
+
IconVariable,
|
|
15
|
+
} from '@tabler/icons-react';
|
|
16
|
+
import React, { useEffect, useState } from 'react';
|
|
17
|
+
import { Ui } from './ui/ui';
|
|
18
|
+
import { MenuDesigner } from './menu-designer/menu-designer';
|
|
19
|
+
import { RestApiDocumentation } from '@fluxo-fe/shell/korebi';
|
|
20
|
+
import {Documentation, EmbeddedModule, FluxoClientSys} from "@fluxo-dev/api-client-base";
|
|
21
|
+
import { ErrorPage } from "@fluxo-dev/ui-components";
|
|
22
|
+
import Tokens from './token/tokens';
|
|
23
|
+
import {useNavigate} from "react-router-dom";
|
|
24
|
+
import {AppsList} from "./apps/apps-list";
|
|
25
|
+
import {PermissionGrid} from "./security/components/permission/permission-grid";
|
|
26
|
+
import {DomainRoleGrid} from "./security/components/domani-role/domain-role-grid";
|
|
27
|
+
import {UsersGrid} from "./security/components/user/users-grid";
|
|
28
|
+
import {RouteRuleGrid} from "./security/components/route-rule/route-rule-grid";
|
|
29
|
+
import {PrintServerList} from "@fluxo-fe/shell/printing";
|
|
30
|
+
|
|
31
|
+
export interface SettingsProps {
|
|
32
|
+
pluginEndpoint: string;
|
|
33
|
+
modules: EmbeddedModule[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function Settings(props: SettingsProps) {
|
|
37
|
+
const [currentTab, setCurrentTab] = useState('ui');
|
|
38
|
+
const [apiRestDoc, setApiRestDoc] = useState<Documentation>();
|
|
39
|
+
const navigate = useNavigate();
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const hash = window.location.hash.slice(1);
|
|
44
|
+
if (hash) {
|
|
45
|
+
setCurrentTab(hash);
|
|
46
|
+
}
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
FluxoClientSys.Instance.korebi.documentation.getRestApiDocumentation().then((res) => {
|
|
51
|
+
setApiRestDoc(res);
|
|
52
|
+
});
|
|
53
|
+
}, []);
|
|
54
|
+
|
|
55
|
+
const getSideMenuGroups = (): SideMenuGroup[] => {
|
|
56
|
+
return [
|
|
57
|
+
{
|
|
58
|
+
name: "Interfaccia",
|
|
59
|
+
items: [
|
|
60
|
+
{
|
|
61
|
+
key: 'ui',
|
|
62
|
+
title: (
|
|
63
|
+
<Text>
|
|
64
|
+
Brand
|
|
65
|
+
</Text>
|
|
66
|
+
),
|
|
67
|
+
onClick: () => {
|
|
68
|
+
window.location.hash = 'ui';
|
|
69
|
+
setCurrentTab('ui')
|
|
70
|
+
},
|
|
71
|
+
disabled: false,
|
|
72
|
+
icon: <IconDashboard size={18} />,
|
|
73
|
+
description:
|
|
74
|
+
'Gestisci colori logo e altri impostazioni di interfaccia',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
key: 'menu',
|
|
78
|
+
title: (
|
|
79
|
+
<Text>
|
|
80
|
+
Menu e ricerca
|
|
81
|
+
</Text>
|
|
82
|
+
),
|
|
83
|
+
onClick: () => {
|
|
84
|
+
window.location.hash = 'menu';
|
|
85
|
+
setCurrentTab('menu')
|
|
86
|
+
},
|
|
87
|
+
disabled: false,
|
|
88
|
+
icon: <IconMenu size={18} />,
|
|
89
|
+
description: 'Gestione del menu laterale e delle azioni veloci',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'Sicurezza',
|
|
95
|
+
items: [
|
|
96
|
+
{
|
|
97
|
+
key: 'users',
|
|
98
|
+
title: (
|
|
99
|
+
<Text>
|
|
100
|
+
Utenti
|
|
101
|
+
</Text>
|
|
102
|
+
),
|
|
103
|
+
onClick: () => {
|
|
104
|
+
window.location.hash = 'users';
|
|
105
|
+
setCurrentTab('users');
|
|
106
|
+
},
|
|
107
|
+
disabled: false,
|
|
108
|
+
icon: <IconUsers size={18} />,
|
|
109
|
+
description: 'Crea, modifica, elimina utenti',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
key: 'roles',
|
|
113
|
+
title: (
|
|
114
|
+
<Text>
|
|
115
|
+
Ruoli
|
|
116
|
+
</Text>
|
|
117
|
+
),
|
|
118
|
+
onClick: () => {
|
|
119
|
+
window.location.hash = 'roles';
|
|
120
|
+
setCurrentTab('roles')
|
|
121
|
+
},
|
|
122
|
+
disabled: false,
|
|
123
|
+
icon: <IconUsersGroup size={18} />,
|
|
124
|
+
description: 'Crea, modifica, elimina ruoli',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
key: 'permissions',
|
|
128
|
+
title: (
|
|
129
|
+
<Text>
|
|
130
|
+
Permessi
|
|
131
|
+
</Text>
|
|
132
|
+
),
|
|
133
|
+
onClick: () => {
|
|
134
|
+
window.location.hash = 'permissions';
|
|
135
|
+
setCurrentTab('permissions')
|
|
136
|
+
},
|
|
137
|
+
disabled: false,
|
|
138
|
+
icon: <IconKey size={18} />,
|
|
139
|
+
description: 'Gestisci i permessi utente',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
key: 'routes',
|
|
143
|
+
title: (
|
|
144
|
+
<Text>
|
|
145
|
+
Regole sulle rotte <small><b>(Avanzato)</b></small>
|
|
146
|
+
</Text>
|
|
147
|
+
),
|
|
148
|
+
onClick: () => {
|
|
149
|
+
window.location.hash = 'routes';
|
|
150
|
+
setCurrentTab('routes')
|
|
151
|
+
},
|
|
152
|
+
disabled: false,
|
|
153
|
+
icon: <IconRoute size={18} />,
|
|
154
|
+
description: 'Gestisci le regole sulle rotte',
|
|
155
|
+
},
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'Stampe',
|
|
160
|
+
items: [
|
|
161
|
+
{
|
|
162
|
+
key: 'print-server',
|
|
163
|
+
title: (
|
|
164
|
+
<Text>
|
|
165
|
+
Print server
|
|
166
|
+
</Text>
|
|
167
|
+
),
|
|
168
|
+
onClick: () => {
|
|
169
|
+
window.location.hash = 'print-server';
|
|
170
|
+
setCurrentTab('print-server')
|
|
171
|
+
},
|
|
172
|
+
disabled: false,
|
|
173
|
+
icon: <IconPrinter size={18} />,
|
|
174
|
+
description: 'Gestisci i print server',
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'Sviluppatori',
|
|
180
|
+
items: [
|
|
181
|
+
{
|
|
182
|
+
key: 'tokens',
|
|
183
|
+
title: (
|
|
184
|
+
<Text>
|
|
185
|
+
Access tokens <small>(account)</small>
|
|
186
|
+
</Text>
|
|
187
|
+
),
|
|
188
|
+
onClick: () => {
|
|
189
|
+
window.location.hash = 'tokens';
|
|
190
|
+
setCurrentTab('tokens')
|
|
191
|
+
},
|
|
192
|
+
disabled: false,
|
|
193
|
+
icon: <IconLockAccess size={18} />,
|
|
194
|
+
description: 'Gestisci i tuoi access tokens',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
key: 'environment',
|
|
198
|
+
title: (
|
|
199
|
+
<Text>
|
|
200
|
+
Variabili d'ambiente <small>(Globale)</small>
|
|
201
|
+
</Text>
|
|
202
|
+
),
|
|
203
|
+
onClick: () => {
|
|
204
|
+
window.location.hash = 'environment';
|
|
205
|
+
setCurrentTab('environment')
|
|
206
|
+
},
|
|
207
|
+
disabled: false,
|
|
208
|
+
icon: <IconVariable size={18} />,
|
|
209
|
+
description: "Crea, modifica o elimina le variabili d'ambiente",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
key: 'plugin',
|
|
213
|
+
title: (
|
|
214
|
+
<Text>
|
|
215
|
+
Apps <small>(Globale)</small>
|
|
216
|
+
</Text>
|
|
217
|
+
),
|
|
218
|
+
onClick: () => {
|
|
219
|
+
window.location.hash = 'plugin';
|
|
220
|
+
setCurrentTab('plugin')
|
|
221
|
+
},
|
|
222
|
+
disabled: false,
|
|
223
|
+
icon: <IconApps size={18} />,
|
|
224
|
+
description: 'Installa o gestisci le apps',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
key: 'apidoc',
|
|
228
|
+
title: (
|
|
229
|
+
<Text>
|
|
230
|
+
API <small>REST</small>
|
|
231
|
+
</Text>
|
|
232
|
+
),
|
|
233
|
+
onClick: () => {
|
|
234
|
+
window.location.hash = 'apidoc';
|
|
235
|
+
setCurrentTab('apidoc')
|
|
236
|
+
},
|
|
237
|
+
disabled: false,
|
|
238
|
+
icon: <IconApi size={18} />,
|
|
239
|
+
description: 'Documentazione delle API REST',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
key: 'ide',
|
|
243
|
+
title: (
|
|
244
|
+
<Text>
|
|
245
|
+
Ambiente di sviluppo <small>KOREBI</small>
|
|
246
|
+
</Text>
|
|
247
|
+
),
|
|
248
|
+
onClick: () => navigate('/korebi-ide'),
|
|
249
|
+
disabled: false,
|
|
250
|
+
icon: <IconCode size={18} />,
|
|
251
|
+
description: 'Accedi all\'ambiente di sviluppo',
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
];
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const renderCurrentSection = (tab: string) => {
|
|
259
|
+
switch (tab) {
|
|
260
|
+
case 'ui':
|
|
261
|
+
return <Ui />;
|
|
262
|
+
case 'menu':
|
|
263
|
+
return <MenuDesigner />;
|
|
264
|
+
case 'tokens':
|
|
265
|
+
return <Tokens />;
|
|
266
|
+
case 'users':
|
|
267
|
+
return <UsersGrid />;
|
|
268
|
+
case 'roles':
|
|
269
|
+
return <DomainRoleGrid />;
|
|
270
|
+
case 'permissions':
|
|
271
|
+
return <PermissionGrid />;
|
|
272
|
+
case 'routes':
|
|
273
|
+
return <RouteRuleGrid />;
|
|
274
|
+
case 'print-server':
|
|
275
|
+
return <PrintServerList />;
|
|
276
|
+
case 'apidoc':
|
|
277
|
+
return apiRestDoc ? (
|
|
278
|
+
<DataCard title={'Documentazione API REST'}>
|
|
279
|
+
<RestApiDocumentation documentation={apiRestDoc} />
|
|
280
|
+
</DataCard>
|
|
281
|
+
) : (
|
|
282
|
+
<WaitData />
|
|
283
|
+
);
|
|
284
|
+
case 'plugin':
|
|
285
|
+
return <AppsList pluginEndpoint={props.pluginEndpoint} modules={props.modules} />;
|
|
286
|
+
default:
|
|
287
|
+
return <ErrorPage />;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<ShellPage
|
|
293
|
+
headerProps={{ title: 'Impostazioni', subtitle: '' }}
|
|
294
|
+
contextSidebarProps={{ groups: getSideMenuGroups(), activeKey: currentTab }}>
|
|
295
|
+
{renderCurrentSection(currentTab)}
|
|
296
|
+
</ShellPage>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ApiKey, ApiKeyService } from "@fluxo-dev/api-client-base";
|
|
2
|
+
import { Badge, Button, Group, Stack } from "@mantine/core";
|
|
3
|
+
import { showNotification } from "@mantine/notifications";
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
|
+
|
|
6
|
+
interface TokenRowProps {
|
|
7
|
+
token: ApiKey;
|
|
8
|
+
onChange: () => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function TokenRow(props: TokenRowProps) {
|
|
12
|
+
const apiKeyService = new ApiKeyService();
|
|
13
|
+
|
|
14
|
+
const enable = () => {
|
|
15
|
+
apiKeyService.enable(props.token.id)
|
|
16
|
+
.then((res) => {
|
|
17
|
+
showNotification({
|
|
18
|
+
title: '',
|
|
19
|
+
message: `Token abilitato`,
|
|
20
|
+
color: 'green',
|
|
21
|
+
});
|
|
22
|
+
props.onChange();
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const disable = () => {
|
|
27
|
+
apiKeyService.disable(props.token.id)
|
|
28
|
+
.then((res) => {
|
|
29
|
+
showNotification({
|
|
30
|
+
title: '',
|
|
31
|
+
message: `Token disabilitato`,
|
|
32
|
+
color: 'green',
|
|
33
|
+
});
|
|
34
|
+
props.onChange();
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const deletion = () => {
|
|
39
|
+
apiKeyService.delete(props.token.id)
|
|
40
|
+
.then((res) => {
|
|
41
|
+
showNotification({
|
|
42
|
+
title: 'Cancellazione completata',
|
|
43
|
+
message: `Il token è stato cancellato con successo`,
|
|
44
|
+
color: 'green',
|
|
45
|
+
});
|
|
46
|
+
props.onChange();
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Group justify='space-between' style={{ border: '1px solid #dedede', padding: 15 }}>
|
|
52
|
+
<Stack gap={0}>
|
|
53
|
+
<span>{props.token.description}</span>
|
|
54
|
+
<small>{dayjs(props.token.expirationDate).format('DD/MM/YYYY')}</small>
|
|
55
|
+
</Stack>
|
|
56
|
+
<Group>
|
|
57
|
+
{props.token.isActive ? <Badge color="green">Attivo</Badge> : <Badge color="red">Disattivo</Badge>}
|
|
58
|
+
<Button variant="light" size="xs" onClick={props.token.isActive ? disable : enable}>
|
|
59
|
+
{props.token.isActive ? 'Disabilita' : 'Abilita'}
|
|
60
|
+
</Button>
|
|
61
|
+
<Button variant="light" color="red" size="xs" onClick={deletion}>
|
|
62
|
+
Cancella
|
|
63
|
+
</Button>
|
|
64
|
+
</Group>
|
|
65
|
+
</Group>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default TokenRow;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { DataCard } from "@fluxo-dev/ui-components";
|
|
2
|
+
import { ApiKey, ApiKeyService } from "@fluxo-dev/api-client-base";
|
|
3
|
+
import { Button, Modal, Stack, TextInput } from "@mantine/core";
|
|
4
|
+
import {DatePickerInput} from '@mantine/dates';
|
|
5
|
+
import { showNotification } from "@mantine/notifications";
|
|
6
|
+
import { IconFilePlus, IconPlus } from '@tabler/icons-react';
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
import TokenRow from "./token-row";
|
|
9
|
+
|
|
10
|
+
function Tokens() {
|
|
11
|
+
const [tokens, setTokens] = useState<ApiKey[]>([]);
|
|
12
|
+
const [newTokenDescr, setNewTokenDescr] = useState<string>('');
|
|
13
|
+
const [newTokenDate, setNewTokenDate] = useState<Date | null>();
|
|
14
|
+
const [newTokenModalOpen, setNewTokenModalOpen] = useState<boolean>(false);
|
|
15
|
+
const apiKeyService = new ApiKeyService();
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
getApiKeys();
|
|
19
|
+
}, [])
|
|
20
|
+
|
|
21
|
+
const getApiKeys = () => {
|
|
22
|
+
apiKeyService.getApiKeys().then(res => {
|
|
23
|
+
setTokens(res);
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const create = () => {
|
|
28
|
+
apiKeyService.create({ description: newTokenDescr, expirationDate: newTokenDate! })
|
|
29
|
+
.then((res) => {
|
|
30
|
+
showNotification({
|
|
31
|
+
title: 'Salvataggio completato',
|
|
32
|
+
message: `Il menu è stato salvato con successo`,
|
|
33
|
+
color: 'green',
|
|
34
|
+
});
|
|
35
|
+
getApiKeys();
|
|
36
|
+
setNewTokenModalOpen(false);
|
|
37
|
+
setNewTokenDescr('');
|
|
38
|
+
setNewTokenDate(undefined);
|
|
39
|
+
}).catch((err) => {
|
|
40
|
+
showNotification({
|
|
41
|
+
title: 'Errore',
|
|
42
|
+
message: `Errore durante il salvataggio del token. ${err.message}`,
|
|
43
|
+
color: 'red',
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<DataCard
|
|
51
|
+
title={'Access token'}
|
|
52
|
+
actions={[
|
|
53
|
+
{ icon: <IconPlus size={18} />, label: 'Genera un nuovo token', onClick: () => setNewTokenModalOpen(true) },
|
|
54
|
+
]}>
|
|
55
|
+
<Stack gap={16}>
|
|
56
|
+
{
|
|
57
|
+
tokens.map((v, i) => <TokenRow key={i} token={v} onChange={getApiKeys} />)
|
|
58
|
+
}
|
|
59
|
+
</Stack>
|
|
60
|
+
</DataCard>
|
|
61
|
+
|
|
62
|
+
<Modal centered opened={newTokenModalOpen} onClose={() => setNewTokenModalOpen(false)} title="Nuovo token">
|
|
63
|
+
<Stack align="flex-start" justify="flex-start">
|
|
64
|
+
<DatePickerInput w='100%' label="Data scadenza" onChange={(v) => setNewTokenDate(v)} />
|
|
65
|
+
<TextInput w='100%' label='Descrizione' onChange={(v) => setNewTokenDescr(v.currentTarget.value)} />
|
|
66
|
+
<Button onClick={create} leftSection={<IconFilePlus />} ml='auto'>Salva</Button>
|
|
67
|
+
</Stack>
|
|
68
|
+
</Modal>
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default Tokens;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {DataCard} from "@fluxo-dev/ui-components";
|
|
2
|
+
import {
|
|
3
|
+
Alert,
|
|
4
|
+
Box,
|
|
5
|
+
Button,
|
|
6
|
+
ColorInput,
|
|
7
|
+
ColorSwatch,
|
|
8
|
+
Divider,
|
|
9
|
+
FileButton,
|
|
10
|
+
Group,
|
|
11
|
+
NumberInput,
|
|
12
|
+
Stack,
|
|
13
|
+
Text, useMantineTheme
|
|
14
|
+
} from "@mantine/core";
|
|
15
|
+
import AppBar from "../../../layouts/main/app-bar";
|
|
16
|
+
import {IconDeviceFloppy, IconInfoSquare, IconUpload} from '@tabler/icons-react';
|
|
17
|
+
import {useEffect, useState} from "react";
|
|
18
|
+
import {IFluxoRootState, useAppSelector} from "@fluxo-dev/state";
|
|
19
|
+
import {showNotification} from "@mantine/notifications";
|
|
20
|
+
import {FluxoClientSys} from "@fluxo-dev/api-client-base";
|
|
21
|
+
|
|
22
|
+
export function Ui() {
|
|
23
|
+
|
|
24
|
+
const {configuration} = useAppSelector((state: IFluxoRootState) => state.layout);
|
|
25
|
+
const theme = useMantineTheme();
|
|
26
|
+
|
|
27
|
+
const [newLogo, setNewLogo] = useState<File | null>(null);
|
|
28
|
+
const [newLogoBase64, setNewLogoBase64] = useState<string | undefined>(configuration.logo);
|
|
29
|
+
const [newLogoHeight, setNewLogoHeight] = useState<number>(configuration.logoHeight);
|
|
30
|
+
const [newBrandColors, setNewBrandColors] = useState(configuration.colors);
|
|
31
|
+
|
|
32
|
+
const swatches = Object.keys(theme.colors).map((color) => (
|
|
33
|
+
<ColorSwatch
|
|
34
|
+
key={color}
|
|
35
|
+
color={theme.colors[color][6]}
|
|
36
|
+
onClick={() => {
|
|
37
|
+
setNewBrandColors(theme.colors[color])
|
|
38
|
+
}}
|
|
39
|
+
/>
|
|
40
|
+
));
|
|
41
|
+
|
|
42
|
+
const saveClientUIConfiguration = () => {
|
|
43
|
+
if(newLogoBase64 && newLogoHeight && newLogoHeight > 0 && newBrandColors && newBrandColors.length === 10) {
|
|
44
|
+
|
|
45
|
+
FluxoClientSys.Instance.configuration.updateClientUi({
|
|
46
|
+
colors: newBrandColors,
|
|
47
|
+
logo: newLogoBase64,
|
|
48
|
+
logoHeight: newLogoHeight
|
|
49
|
+
})
|
|
50
|
+
.then((res) => {
|
|
51
|
+
showNotification({
|
|
52
|
+
title: 'Salvataggio completato',
|
|
53
|
+
message: 'La configurazione è stata salvata correttamente.',
|
|
54
|
+
color: 'green',
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
.catch((err) => {
|
|
58
|
+
showNotification({
|
|
59
|
+
title: 'Errore',
|
|
60
|
+
message: err.message,
|
|
61
|
+
color: 'red',
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
} else {
|
|
66
|
+
showNotification({
|
|
67
|
+
title: 'Errore',
|
|
68
|
+
message: 'Compilare tutti i campi richiesti',
|
|
69
|
+
color: 'red',
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const toBase64 = (file: File) => new Promise<string>((resolve, reject) => {
|
|
75
|
+
const reader = new FileReader();
|
|
76
|
+
reader.readAsDataURL(file);
|
|
77
|
+
reader.onload = () => resolve(reader.result?.toString() ?? '');
|
|
78
|
+
reader.onerror = error => reject(error);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const elaborateColorsArray = (colors: any, position: number, value: string) => {
|
|
82
|
+
const tmpColors = [...colors];
|
|
83
|
+
tmpColors[position] = value;
|
|
84
|
+
return tmpColors;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if(newLogo) {
|
|
89
|
+
toBase64(newLogo)
|
|
90
|
+
.then((res) => {
|
|
91
|
+
setNewLogoBase64(res);
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
}, [newLogo])
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Stack align={'stretch'} >
|
|
98
|
+
|
|
99
|
+
<div>
|
|
100
|
+
<DataCard title={'Anteprima'}>
|
|
101
|
+
<Stack>
|
|
102
|
+
<Box style={{border: '0px solid #888'}} p={0}>
|
|
103
|
+
<AppBar previewMode={{logo: newLogoBase64, logoHeight: newLogoHeight, headerColor: newBrandColors[6]}} />
|
|
104
|
+
</Box>
|
|
105
|
+
</Stack>
|
|
106
|
+
</DataCard>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div>
|
|
110
|
+
<DataCard title={'Logo'} actions={[{icon: <IconDeviceFloppy />, onClick: () => console.log}]}>
|
|
111
|
+
<Stack align={'end'}>
|
|
112
|
+
<FileButton onChange={setNewLogo} accept="image/png,image/jpeg">
|
|
113
|
+
{(props) => <Button w={'100%'} leftSection={<IconUpload />} {...props}>Carica nuovo logo</Button>}
|
|
114
|
+
</FileButton>
|
|
115
|
+
<NumberInput max={50} min={10} w={'100%'} label={'Altezza del logo'} defaultValue={newLogoHeight} onChange={(v) => setNewLogoHeight(v !== '' ? +v! : 10)} />
|
|
116
|
+
<Divider color={'#e9ecef'} w={'100%'} />
|
|
117
|
+
<Button onClick={() => saveClientUIConfiguration()} leftSection={<IconDeviceFloppy />}>Salva</Button>
|
|
118
|
+
</Stack>
|
|
119
|
+
</DataCard>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div>
|
|
123
|
+
<DataCard title={'Colori del brand'}>
|
|
124
|
+
<Stack align={'end'}>
|
|
125
|
+
<Alert w={'100%'} icon={<IconInfoSquare size={18} />} title="Informazioni">
|
|
126
|
+
Nota che tutti i colori dovrebbero sempre avere 10 sfumature. <a href={'https://smart-swatch.netlify.app/'} target={'_blank'}>Clicca qui</a> per generare la tua palette personalizzata.
|
|
127
|
+
</Alert>
|
|
128
|
+
<Text w={'100%'}>Preset</Text>
|
|
129
|
+
<Group w={'100%'} justify="left" gap="xs">
|
|
130
|
+
{swatches}
|
|
131
|
+
</Group>
|
|
132
|
+
<ColorInput w={'100%'} placeholder="Colore 1" label="Colore 1" value={newBrandColors[0]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 0, v)])} />
|
|
133
|
+
<ColorInput w={'100%'} placeholder="Colore 2" label="Colore 2" format={'hex'} value={newBrandColors[1]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 1, v)])} />
|
|
134
|
+
<ColorInput w={'100%'} placeholder="Colore 3" label="Colore 3" format={'hex'} value={newBrandColors[2]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 2, v)])} />
|
|
135
|
+
<ColorInput w={'100%'} placeholder="Colore 4" label="Colore 4" format={'hex'} value={newBrandColors[3]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 3, v)])} />
|
|
136
|
+
<ColorInput w={'100%'} placeholder="Colore 5" label="Colore 5" format={'hex'} value={newBrandColors[4]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 4, v)])} />
|
|
137
|
+
<ColorInput w={'100%'} placeholder="Colore 6" label="Colore 6" format={'hex'} value={newBrandColors[5]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 5, v)])} />
|
|
138
|
+
<ColorInput w={'100%'} placeholder="Colore 7" label="Colore 7" format={'hex'} value={newBrandColors[6]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 6, v)])} description={'Primario, headerbar'} />
|
|
139
|
+
<ColorInput w={'100%'} placeholder="Colore 8" label="Colore 8" format={'hex'} value={newBrandColors[7]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 7, v)])} />
|
|
140
|
+
<ColorInput w={'100%'} placeholder="Colore 9" label="Colore 9" format={'hex'} value={newBrandColors[8]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 8, v)])} />
|
|
141
|
+
<ColorInput w={'100%'} placeholder="Colore 10" label="Colore 10" format={'hex'} value={newBrandColors[9]} onChange={(v) => setNewBrandColors((o: any) => [...elaborateColorsArray(o, 9, v)])} />
|
|
142
|
+
<Divider color={'#e9ecef'} w={'100%'} />
|
|
143
|
+
<Button onClick={() => saveClientUIConfiguration()} leftSection={<IconDeviceFloppy />}>Salva</Button>
|
|
144
|
+
</Stack>
|
|
145
|
+
</DataCard>
|
|
146
|
+
</div>
|
|
147
|
+
</Stack>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {AppJit} from "../models/app-jit";
|
|
2
|
+
import {AppState, AppType, FluxoFrontendAppConfiguration} from "@fluxo-dev/api-client-base";
|
|
3
|
+
|
|
4
|
+
export class AppJitManager {
|
|
5
|
+
|
|
6
|
+
public loadAppConfigurations(): AppJit[] {
|
|
7
|
+
const apps = localStorage.getItem('fluxo-jit-apps');
|
|
8
|
+
|
|
9
|
+
if (apps) {
|
|
10
|
+
const parsedApps: AppJit[] = JSON.parse(apps);
|
|
11
|
+
return parsedApps;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public refreshApp(app: AppJit): Promise<boolean> {
|
|
18
|
+
return fetch(`${app.developmentUrl}/fluxo-fe-app-configuration.json`)
|
|
19
|
+
.then((response) => {
|
|
20
|
+
return response.json()
|
|
21
|
+
.then((json) => {
|
|
22
|
+
|
|
23
|
+
this.removeAppConfiguration(json.scope, json.name);
|
|
24
|
+
this.addAppConfiguration(json, app.developmentUrl, app.developmentRoute);
|
|
25
|
+
|
|
26
|
+
return true;
|
|
27
|
+
});
|
|
28
|
+
})
|
|
29
|
+
.catch((error) => {
|
|
30
|
+
|
|
31
|
+
return false;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public checkIfAppExists(app: AppJit): boolean {
|
|
36
|
+
const apps = this.loadAppConfigurations();
|
|
37
|
+
return !!apps.find(x => x.scope === app.scope);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public addAppConfiguration(app: FluxoFrontendAppConfiguration, devUrl: string, devRoute: string) {
|
|
41
|
+
const apps = this.loadAppConfigurations();
|
|
42
|
+
|
|
43
|
+
if(apps.find(x => x.scope === app.scope && x.name === app.name)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const a: AppJit = {
|
|
48
|
+
name: app?.name || '',
|
|
49
|
+
type: AppType.Frontend,
|
|
50
|
+
version: app?.version || '',
|
|
51
|
+
author: app?.author || '',
|
|
52
|
+
scope: app?.scope || '',
|
|
53
|
+
state: AppState.Active,
|
|
54
|
+
description: app?.description || '',
|
|
55
|
+
developmentRoute: devRoute,
|
|
56
|
+
installationDate: new Date(),
|
|
57
|
+
installationDirectory: '',
|
|
58
|
+
developmentUrl: devUrl,
|
|
59
|
+
id: 0,
|
|
60
|
+
menu: app?.menu || [],
|
|
61
|
+
isEmbedded: false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
apps.push(a);
|
|
65
|
+
localStorage.setItem('fluxo-jit-apps', JSON.stringify(apps));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public setAppEnabled(appScope: string, appName: string, enabled: boolean) {
|
|
69
|
+
const apps = this.loadAppConfigurations();
|
|
70
|
+
const app = apps.find(x => x.scope === appScope && x.name === appName);
|
|
71
|
+
|
|
72
|
+
if(!app) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
app.state = enabled ? AppState.Active : AppState.Inactive;
|
|
77
|
+
localStorage.setItem('fluxo-jit-apps', JSON.stringify(apps));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public removeAppConfiguration(appScope: string, appName: string) {
|
|
81
|
+
const apps = this.loadAppConfigurations();
|
|
82
|
+
const app = apps.find(x => x.scope === appScope && x.name === appName);
|
|
83
|
+
|
|
84
|
+
if(!app) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const index = apps.indexOf(app);
|
|
89
|
+
apps.splice(index, 1);
|
|
90
|
+
localStorage.setItem('fluxo-jit-apps', JSON.stringify(apps));
|
|
91
|
+
}
|
|
92
|
+
}
|