@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.
Files changed (66) hide show
  1. package/.babelrc +12 -0
  2. package/.eslintrc.json +18 -0
  3. package/README.md +7 -0
  4. package/jest.config.ts +11 -0
  5. package/package.json +4 -0
  6. package/project.json +43 -0
  7. package/src/index.ts +1 -0
  8. package/src/lib/app.module.css +1 -0
  9. package/src/lib/app.spec.tsx +17 -0
  10. package/src/lib/app.tsx +374 -0
  11. package/src/lib/components/error-page/404.tsx +16 -0
  12. package/src/lib/components/routes/main-routes.tsx +26 -0
  13. package/src/lib/layouts/blank/blank-layout.tsx +10 -0
  14. package/src/lib/layouts/main/app-bar.tsx +192 -0
  15. package/src/lib/layouts/main/app-layout.tsx +52 -0
  16. package/src/lib/layouts/main/content.tsx +10 -0
  17. package/src/lib/layouts/main/footer-bar.tsx +76 -0
  18. package/src/lib/layouts/main/navbar-item.module.css +49 -0
  19. package/src/lib/layouts/main/navbar-item.tsx +129 -0
  20. package/src/lib/layouts/main/navbar.tsx +38 -0
  21. package/src/lib/layouts/main/search-bar.tsx +44 -0
  22. package/src/lib/layouts/main/shell-spotlight.tsx +58 -0
  23. package/src/lib/models/app-jit.ts +6 -0
  24. package/src/lib/pages/general/fluxo-plugin-loader.tsx +22 -0
  25. package/src/lib/pages/login/account-confirmation.tsx +86 -0
  26. package/src/lib/pages/login/change-password.tsx +58 -0
  27. package/src/lib/pages/login/forgot-password.tsx +53 -0
  28. package/src/lib/pages/login/login-page.tsx +70 -0
  29. package/src/lib/pages/login/reset-password.tsx +91 -0
  30. package/src/lib/pages/settings/apps/add-jit-app.tsx +79 -0
  31. package/src/lib/pages/settings/apps/app-item.tsx +157 -0
  32. package/src/lib/pages/settings/apps/app-jit-item.tsx +105 -0
  33. package/src/lib/pages/settings/apps/apps-list.tsx +171 -0
  34. package/src/lib/pages/settings/apps/embedded-module-item.tsx +66 -0
  35. package/src/lib/pages/settings/apps/install-or-upgrade-app.tsx +105 -0
  36. package/src/lib/pages/settings/menu-designer/custom-node.module.css +34 -0
  37. package/src/lib/pages/settings/menu-designer/custom-node.tsx +160 -0
  38. package/src/lib/pages/settings/menu-designer/menu-create-form.tsx +109 -0
  39. package/src/lib/pages/settings/menu-designer/menu-designer.module.css +19 -0
  40. package/src/lib/pages/settings/menu-designer/menu-designer.tsx +463 -0
  41. package/src/lib/pages/settings/menu-designer/menu-item-edit.tsx +115 -0
  42. package/src/lib/pages/settings/menu-designer/placeholder.module.css +8 -0
  43. package/src/lib/pages/settings/menu-designer/placeholder.tsx +13 -0
  44. package/src/lib/pages/settings/security/components/domani-role/domain-role-form.tsx +67 -0
  45. package/src/lib/pages/settings/security/components/domani-role/domain-role-grid.tsx +139 -0
  46. package/src/lib/pages/settings/security/components/permission/create-custom-permission-form.tsx +64 -0
  47. package/src/lib/pages/settings/security/components/permission/create-scope-form.tsx +158 -0
  48. package/src/lib/pages/settings/security/components/permission/permission-grid.tsx +304 -0
  49. package/src/lib/pages/settings/security/components/permission/permission-navigator.tsx +320 -0
  50. package/src/lib/pages/settings/security/components/permission/update-permission.tsx +54 -0
  51. package/src/lib/pages/settings/security/components/route-rule/route-rule-create-form.tsx +59 -0
  52. package/src/lib/pages/settings/security/components/route-rule/route-rule-grid.tsx +229 -0
  53. package/src/lib/pages/settings/security/components/user/user-form.tsx +123 -0
  54. package/src/lib/pages/settings/security/components/user/users-grid.tsx +127 -0
  55. package/src/lib/pages/settings/settings.tsx +298 -0
  56. package/src/lib/pages/settings/token/token-row.tsx +69 -0
  57. package/src/lib/pages/settings/token/tokens.tsx +73 -0
  58. package/src/lib/pages/settings/ui/ui.tsx +149 -0
  59. package/src/lib/services/app-jit-manager.ts +92 -0
  60. package/src/lib/services/app-menu.service.ts +35 -0
  61. package/src/lib/utils/icon-factory.tsx +25 -0
  62. package/src/lib/utils/security/only-not-logged-route.tsx +17 -0
  63. package/src/lib/utils/security/protected-route.tsx +47 -0
  64. package/tsconfig.json +20 -0
  65. package/tsconfig.lib.json +24 -0
  66. package/tsconfig.spec.json +20 -0
@@ -0,0 +1,192 @@
1
+ import React, {useEffect} from 'react';
2
+ import {useSelector} from "react-redux";
3
+ import {Link, useLocation, useNavigate} from "react-router-dom";
4
+ import SearchBar from "./search-bar";
5
+ import {FluxoClientSys, FluxoStateDispatcher} from "@fluxo-dev/api-client-base";
6
+ import {
7
+ Group,
8
+ Avatar,
9
+ Stack,
10
+ Burger,
11
+ ActionIcon, Drawer, NavLink, Badge, AppShell
12
+ } from "@mantine/core";
13
+ import {
14
+ IconApps,
15
+ IconChevronRight,
16
+ IconLink,
17
+ IconRefresh
18
+ } from '@tabler/icons-react';
19
+ import { useMantineTheme } from '@mantine/core';
20
+ import {IFluxoRootState} from "@fluxo-dev/state";
21
+ import {AppJit} from "../../models/app-jit";
22
+ import {AppJitManager} from "../../services/app-jit-manager";
23
+ import {showNotification} from "@mantine/notifications";
24
+ import {DataCard} from "@fluxo-dev/ui-components";
25
+ import {useHotkeys} from "@mantine/hooks";
26
+
27
+ export interface AppBarProps {
28
+ previewMode?: {
29
+ logo?: string,
30
+ logoHeight: number,
31
+ headerColor: string,
32
+ }
33
+ }
34
+ function AppBar(props: AppBarProps) {
35
+
36
+ const {userInfo} = useSelector((state: any) => state.user)
37
+ const {navbarOpened, configuration} = useSelector((state: IFluxoRootState) => state.layout);
38
+
39
+ const [jitApps, setJitApps] = React.useState<AppJit[]>([]);
40
+ const [jitAppsLoaded, setJitAppsLoaded] = React.useState<boolean>(false);
41
+ const [jitAppsDrawerOpened, setJitAppsDrawerOpened] = React.useState<boolean>(false);
42
+
43
+ const fluxoTheme = useMantineTheme();
44
+ const navigate = useNavigate();
45
+ const location = useLocation();
46
+
47
+ const [developmentMode, setDevelopmentMode] = React.useState<boolean | undefined>(undefined);
48
+ const [lastLocation, setLastLocation] = React.useState<string | undefined>(undefined);
49
+
50
+ useHotkeys([
51
+ ['mod + o', () => {
52
+ if(navbarOpened)
53
+ FluxoStateDispatcher.dispatchCloseNavbar()
54
+ else
55
+ FluxoStateDispatcher.dispatchOpenNavbar()
56
+ }]
57
+ ]);
58
+
59
+ useEffect(() => {
60
+
61
+ if(!jitAppsLoaded)
62
+ return;
63
+
64
+ const splittedLocation = location.pathname.split('/');
65
+
66
+ if(splittedLocation.length < 2) {
67
+ setDevelopmentMode(false);
68
+ return;
69
+ }
70
+
71
+ const found = jitApps.filter(a => a.developmentRoute === splittedLocation[1]);
72
+ if(found.length > 0) {
73
+ setDevelopmentMode(true);
74
+ } else {
75
+ setDevelopmentMode(false);
76
+ }
77
+
78
+ if(lastLocation) {
79
+ const splittedLastLocation = lastLocation.split('/');
80
+ const foundInLastLocation = jitApps.filter(a => a.developmentRoute === splittedLastLocation[1]);
81
+ if((foundInLastLocation.length > 0 && found.length === 0) || (foundInLastLocation.length === 0 && found.length > 0)) {
82
+ navigate(0);
83
+ }
84
+ }
85
+
86
+ setLastLocation(location.pathname);
87
+
88
+ }, [location, jitApps])
89
+
90
+
91
+ const appJitManager = new AppJitManager();
92
+
93
+ useEffect(() => {
94
+ setJitApps(appJitManager.loadAppConfigurations());
95
+ setJitAppsLoaded(true);
96
+ }, [])
97
+
98
+ const logout = () => {
99
+ FluxoClientSys.Instance.auth.logout();
100
+ }
101
+
102
+ const refreshJitApps = (app: AppJit) => {
103
+ appJitManager.refreshApp(app)
104
+ .then((result: boolean) => {
105
+ if(result) {
106
+ setJitApps(appJitManager.loadAppConfigurations());
107
+ showNotification({
108
+ title: 'Applicazione aggiornata',
109
+ message: `L'applicazione ${app.name} è stata aggiornata correttamente`,
110
+ color: 'green',
111
+ icon: <IconRefresh />,
112
+ })
113
+ } else {
114
+ showNotification({
115
+ title: 'Errore',
116
+ message: `L'applicazione ${app.name} non è stata aggiornata. Verifica che la JIT App sia attiva e raggiungibile e che il file di configurazione sia corretto.`,
117
+ color: 'red',
118
+ icon: <IconRefresh />,
119
+ })
120
+ }
121
+ })
122
+ }
123
+
124
+ return (
125
+ <AppShell.Header
126
+ id={'fluxo-app-bar'}
127
+ style={{
128
+ borderBlockEnd: 'none',
129
+ position: props.previewMode ? 'relative' : 'fixed',
130
+ zIndex: props.previewMode ? 1 : 200,
131
+ }}
132
+ >
133
+ <Stack gap={0}>
134
+ <Group px={16} justify={'space-between'} wrap="nowrap" style={{height: '48px', backgroundColor: props.previewMode?.headerColor ?? fluxoTheme.colors['brand'][7],}}>
135
+ <Group>
136
+ <Burger
137
+ disabled={props.previewMode !== undefined}
138
+ color={'#fff'}
139
+ opened={navbarOpened}
140
+ title={'Menu'}
141
+ onClick={() => {
142
+ if(navbarOpened)
143
+ FluxoStateDispatcher.dispatchCloseNavbar()
144
+ else
145
+ FluxoStateDispatcher.dispatchOpenNavbar()
146
+ }}
147
+ />
148
+ <Link to={props.previewMode ? '' : '/'} style={{maxHeight: props.previewMode?.logoHeight ?? configuration.logoHeight}} >
149
+ <img src={props.previewMode?.logo ? props.previewMode.logo : configuration.logo} style={{maxHeight: props.previewMode?.logoHeight ?? configuration.logoHeight}} />
150
+ </Link>
151
+ </Group>
152
+ <Group wrap="nowrap">
153
+ {developmentMode &&
154
+ <Badge color={'red'}>Development mode</Badge>
155
+ }
156
+ {jitApps && jitApps.length > 0 &&
157
+ <>
158
+ <ActionIcon variant={'transparent'} onClick={() => setJitAppsDrawerOpened(true)}><IconApps color={'white'} size={32}/></ActionIcon>
159
+
160
+ <Drawer title={'Navigazione (App JIT)'} position={'right'} opened={jitAppsDrawerOpened} onClose={() => setJitAppsDrawerOpened(false)}>
161
+ <div>
162
+ {jitApps.map((app, index) => {
163
+ return (
164
+ <DataCard padded={false} key={index} title={app.name} actions={[{icon: <IconRefresh size={18} />, onClick: () => refreshJitApps(app),}]}>
165
+ {app.menu.map((group, index) => {
166
+ return (
167
+ <React.Fragment key={`${group.name}_${index}`}>
168
+ <NavLink leftSection={<Badge size="xs" variant="filled" color={fluxoTheme.colors['brand'][7]} w={16} h={16} p={0}>{group.items ? group.items?.length : 0}</Badge>} rightSection={<IconChevronRight size={16} />} label={group.name}>
169
+ {group.items?.map((feature, index) => {
170
+ return (
171
+ <NavLink key={`${feature.name}_${index}`} onClick={() => navigate(`${app.developmentRoute}/${feature.route}`)} label={feature.name} leftSection={<IconLink size={16} />} />
172
+ )})}
173
+ </NavLink>
174
+ </React.Fragment>
175
+ )})}
176
+ </DataCard>
177
+ )
178
+ })}
179
+ </div>
180
+ </Drawer>
181
+ </>
182
+ }
183
+ <SearchBar disabled={props.previewMode !== undefined}/>
184
+ <Avatar onClick={() => props.previewMode ? undefined : logout()} color="cyan" radius="xl" size={32}>{userInfo.FirstName.substr(0, 1)}{userInfo.LastName.substr(0, 1)}</Avatar>
185
+ </Group>
186
+ </Group>
187
+ </Stack>
188
+ </AppShell.Header>
189
+ )
190
+ }
191
+
192
+ export default AppBar;
@@ -0,0 +1,52 @@
1
+ import React, {Suspense} from 'react';
2
+ import AppBar from "./app-bar";
3
+ import AppContent from "./content";
4
+ import FooterBar from "./footer-bar";
5
+ import {AppShell, Loader, ScrollArea} from '@mantine/core';
6
+ import {Navbar} from "./navbar";
7
+ import {useSelector} from "react-redux";
8
+ import {IFluxoRootState} from "@fluxo-dev/state";
9
+ import ShellSpotlight from './shell-spotlight';
10
+
11
+ export interface AppLayoutProps {
12
+ appVersion: string;
13
+ }
14
+
15
+ function AppLayout(props: AppLayoutProps) {
16
+
17
+ const {navbarOpened} = useSelector((state: IFluxoRootState) => state.layout);
18
+
19
+ return (
20
+ <>
21
+ <AppShell
22
+ padding={0}
23
+ styles={{main: {backgroundColor: (1 == 1) ? '#edeff3' : '#f9f9f9'}}}
24
+ navbar={{width: 248, breakpoint: 'sm', collapsed: {mobile: !navbarOpened, desktop: !navbarOpened}}}
25
+ header={{height: 48}}
26
+ footer={{height: 40}}
27
+ transitionDuration={0}
28
+ layout={'default'}
29
+ >
30
+ <AppBar/>
31
+
32
+ <Navbar/>
33
+
34
+
35
+ <AppShell.Main>
36
+ <Suspense fallback={<Loader/>}>
37
+ <AppContent/>
38
+ </Suspense>
39
+ </AppShell.Main>
40
+
41
+ <AppShell.Footer>
42
+ <FooterBar appVersion={props.appVersion} />
43
+ </AppShell.Footer>
44
+
45
+ </AppShell>
46
+
47
+ <ShellSpotlight/>
48
+ </>
49
+ )
50
+ }
51
+
52
+ export default AppLayout;
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import {Outlet} from "react-router-dom";
3
+
4
+ function AppContent() {
5
+ return (
6
+ <Outlet />
7
+ )
8
+ }
9
+
10
+ export default AppContent;
@@ -0,0 +1,76 @@
1
+ import React, {useEffect} from 'react';
2
+ import {IFluxoRootState, useAppSelector} from "@fluxo-dev/state";
3
+ import {Group, Space, Divider, Text, Anchor, Breadcrumbs, Tooltip, AppShell} from "@mantine/core";
4
+ import {IconChecks, IconUser} from '@tabler/icons-react';
5
+ import {FluxoStateDispatcher} from "@fluxo-dev/api-client-base";
6
+ import {useIdle} from "@mantine/hooks";
7
+ import {Link} from 'react-router-dom';
8
+
9
+ export interface FooterBarProps {
10
+ appVersion: string;
11
+ }
12
+
13
+ function FooterBar(props: FooterBarProps) {
14
+
15
+ const {userInfo} = useAppSelector((state: IFluxoRootState) => state.user);
16
+ const {userIdle} = useAppSelector((state: IFluxoRootState) => state.layout);
17
+
18
+ const idle = useIdle(5000);
19
+
20
+ useEffect(() => {
21
+ if (idle)
22
+ FluxoStateDispatcher.dispatchSetUserIdle();
23
+ else
24
+ FluxoStateDispatcher.dispatchUnsetUserIdle();
25
+ }, [idle])
26
+
27
+ const items = [
28
+ {title: 'Home', href: '#'},
29
+ {title: 'Prodotti', href: '#'},
30
+ ].map((item, index) => (
31
+ <Anchor href={item.href} key={index}>
32
+ {item.title}
33
+ </Anchor>
34
+ ));
35
+
36
+ return (
37
+ <AppShell.Footer style={{
38
+ height: 40
39
+ }}>
40
+ <Group px={20} justify='space-between' wrap="nowrap" style={{height: '100%'}}>
41
+ <Group>
42
+ <Tooltip label={'Stato del sistema'} position={'top'} withArrow color={'gray'}>
43
+ <span>
44
+ <IconChecks color={'green'} size={16}/>
45
+ </span>
46
+ </Tooltip>
47
+ <Divider orientation={'vertical'}/>
48
+ <Tooltip label={'Stato utente'} position={'top'} withArrow color={'gray'}>
49
+ <span>
50
+ {userIdle ?
51
+ <IconUser size={16} color={'orange'}/> :
52
+ <IconUser size={16} color={'green'}/>
53
+ }
54
+ </span>
55
+ </Tooltip>
56
+ <Divider orientation={'vertical'}/>
57
+ <Text c="dimmed" fz="xs">{userInfo.FirstName} {userInfo.LastName}</Text>
58
+ <Divider orientation={'vertical'}/>
59
+ <Breadcrumbs separator="→" fz={'xs'}>{items}</Breadcrumbs>
60
+ </Group>
61
+ <Space h={'md'}/>
62
+ <Group>
63
+ <Link to={'/settings'} style={{textDecoration: 'none'}}>
64
+ <Text c="dimmed" fz="xs">Amministrazione</Text>
65
+ </Link>
66
+ <Divider orientation={'vertical'}/>
67
+ <Text c="dimmed" fz="xs">Fluxo ©2022</Text>
68
+ <Divider orientation={'vertical'}/>
69
+ <Text c="dimmed" fz="xs">V. {props.appVersion}</Text>
70
+ </Group>
71
+ </Group>
72
+ </AppShell.Footer>
73
+ )
74
+ }
75
+
76
+ export default FooterBar;
@@ -0,0 +1,49 @@
1
+ .control {
2
+ font-weight: 500;
3
+ height: auto;
4
+ display: block;
5
+ width: 100%;
6
+ color: black;
7
+ font-size: 14px;
8
+ padding-left: 8px;
9
+ padding-right: 8px;
10
+ &:hover {
11
+ background-color: #ececec;
12
+ color: black;
13
+ };
14
+ }
15
+
16
+ .controlSub {
17
+ font-weight: 500;
18
+ width: calc(100% - 30px);
19
+ display: block;
20
+ text-decoration: none;
21
+ padding: 8px 10px 8px 24px;
22
+ margin-left: 30px;
23
+ font-size: 18px;
24
+ color: black;
25
+ border-left: #ececec;
26
+
27
+ &:hover {
28
+ background-color: black;
29
+ color: black;
30
+ }
31
+ }
32
+
33
+ .link {
34
+ font-weight: 500;
35
+ display: block;
36
+ text-decoration: none;
37
+ padding: 8px 16px;
38
+ padding-left: 24px;
39
+ margin-left: 30px;
40
+ font-size: 14px;
41
+ &:hover {
42
+ background-color: #ececec;
43
+ color: black;
44
+ }
45
+ }
46
+
47
+ .chevron {
48
+ transition: transform 200ms ease;
49
+ }
@@ -0,0 +1,129 @@
1
+ import { useState } from 'react';
2
+ import {
3
+ Group,
4
+ Box,
5
+ Collapse,
6
+ ThemeIcon,
7
+ UnstyledButton, useMantineTheme, useMantineColorScheme
8
+ } from '@mantine/core';
9
+ import {IconChevronLeft, IconH6} from '@tabler/icons-react';
10
+ import {IFluxoRootState} from "@fluxo-dev/state";
11
+ import {useSelector} from "react-redux";
12
+ import {Link, useNavigate} from "react-router-dom";
13
+ import {AppMenuItem, UserService} from "@fluxo-dev/api-client-base";
14
+ import style from './navbar-item.module.css';
15
+
16
+ interface NavBarItemProps {
17
+ icon: JSX.Element;
18
+ label: string;
19
+ routerLink?: string;
20
+ initiallyOpened?: boolean;
21
+ links?: AppMenuItem[];
22
+ level: number;
23
+ }
24
+
25
+ export function NavBarItem({ icon: Icon, label, routerLink, initiallyOpened, links, level }: NavBarItemProps) {
26
+
27
+ const hasLinks = Array.isArray(links);
28
+ const [opened, setOpened] = useState(initiallyOpened || false);
29
+ const {navbarOpened} = useSelector((state: IFluxoRootState) => state.layout);
30
+ const navigate = useNavigate();
31
+
32
+ const theme = useMantineTheme();
33
+ const colorScheme = useMantineColorScheme();
34
+ const ChevronIcon = IconChevronLeft;
35
+ const userService = new UserService();
36
+
37
+ const items = (hasLinks ? links : []).map((link, i) => {
38
+
39
+ if(link.hasPermissions) {
40
+
41
+ if(!userService.currentUserHasPermissions(link.permissions!)) {
42
+ return null;
43
+ }
44
+ }
45
+
46
+ if(link.isGroup) {
47
+ return (
48
+ <NavBarItem
49
+ key={link.label + '_' + i}
50
+ label={link.label}
51
+ icon={<IconH6/>}
52
+ links={link.children}
53
+ routerLink={link.routerLink}
54
+ initiallyOpened={false}
55
+ level={level + 1}
56
+ />
57
+ )
58
+ }
59
+
60
+ if(link.featureIdentifier === 'webclient.routing.externallink') {
61
+ return (
62
+ <Link
63
+ className={style['link']}
64
+ style={{
65
+ color: colorScheme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.colors.gray[7],
66
+ borderLeft: `1px solid ${colorScheme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[3]}`
67
+ }}
68
+ key={link.label + '_' + i}
69
+ to={link.routerLink!}
70
+ target={'_blank'}>
71
+ {link.label}
72
+ </Link>
73
+ );
74
+ } else {
75
+ return (
76
+ <Link
77
+ className={style['link']}
78
+ style={{
79
+ color: colorScheme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.colors.gray[7],
80
+ borderLeft: `1px solid ${colorScheme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[3]}`
81
+ }}
82
+ key={link.label + '_' + i}
83
+ to={'/' + link.routerLink}>
84
+ {link.label}
85
+ </Link>
86
+ );
87
+ }
88
+ });
89
+
90
+ return (
91
+ <>
92
+ {navbarOpened ?
93
+ <>
94
+ <UnstyledButton onClick={() => {
95
+ if(hasLinks)
96
+ setOpened((o) => !o)
97
+ else
98
+ navigate('/' + routerLink)
99
+ }} className={level === 0 ? style['control'] : style['controlSub']}>
100
+ <Group justify='space-between' gap={0}>
101
+ <Box style={{ display: 'flex', alignItems: 'center' }}>
102
+ {level === 0 &&
103
+ <ThemeIcon variant="light" size={30}>
104
+ {Icon}
105
+ </ThemeIcon>
106
+ }
107
+ <Box ml="md" style={{marginLeft: level === 0 ? '1rem' : 0}}>{label}</Box>
108
+ </Box>
109
+ {hasLinks && (
110
+ <ChevronIcon
111
+ className={style['chevron']}
112
+ size={14}
113
+ stroke={1.5}
114
+ style={{
115
+ transform: opened ? `rotate(90deg)` : 'none',
116
+ }}
117
+ />
118
+ )}
119
+ </Group>
120
+ </UnstyledButton>
121
+ {hasLinks ? <Collapse style={{width: '100%', marginBottom: 8}} in={opened}>{items}</Collapse> : null}
122
+ </> :
123
+ <>
124
+ {/*<NavBarItemMinimal leftSection={Icon} label={label} initiallyOpened={initiallyOpened} links={links} />*/}
125
+ </>
126
+ }
127
+ </>
128
+ );
129
+ }
@@ -0,0 +1,38 @@
1
+ import React from "react";
2
+ import {Burger, Center, AppShell, Stack, ScrollArea, useMantineTheme} from "@mantine/core";
3
+ import {NavBarItem} from "./navbar-item";
4
+ import {IconBadge} from '@tabler/icons-react';
5
+ import {IFluxoRootState} from "@fluxo-dev/state";
6
+ import {useSelector} from "react-redux";
7
+ import {IconFactory} from "../../utils/icon-factory";
8
+ import {AppMenuItem, UserService} from "@fluxo-dev/api-client-base";
9
+
10
+ export function Navbar() {
11
+
12
+ const {navbarOpened, appMenu} = useSelector((state: IFluxoRootState) => state.layout);
13
+ const fluxoTheme = useMantineTheme();
14
+
15
+ const getPermittedNavBarItems = () => {
16
+ const userService = new UserService();
17
+
18
+ return (appMenu as AppMenuItem[])?.filter(a => !a.hasPermissions || userService.currentUserHasPermissions(a.permissions ?? []));
19
+ }
20
+
21
+ return (
22
+ <AppShell.Navbar>
23
+ <AppShell.Section grow my="md" component={ScrollArea}>
24
+ <Stack justify="center" align={'center'} gap={8}>
25
+ {getPermittedNavBarItems().map((e, i) => {
26
+ const IconComponent = e.icon ?
27
+ IconFactory.getIcon(e.icon) :
28
+ IconFactory.getIcon('Dashboard');
29
+
30
+ return (
31
+ <NavBarItem level={0} key={i} routerLink={e.routerLink} icon={IconComponent} label={e.label} links={e.children && e.children.length > 0 ? e.children : undefined}/>
32
+ )
33
+ })}
34
+ </Stack>
35
+ </AppShell.Section>
36
+ </AppShell.Navbar>
37
+ )
38
+ }
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import {Text, TextInput} from "@mantine/core";
3
+ import {IconSearch} from '@tabler/icons-react';
4
+ import {openSpotlight} from "@mantine/spotlight";
5
+ import {useOs} from "@mantine/hooks";
6
+
7
+ export interface SearchBarProps {
8
+ disabled: boolean,
9
+ }
10
+ function SearchBar(props: SearchBarProps) {
11
+
12
+ const currentOs = useOs();
13
+
14
+ const rightSection = (
15
+ <div style={{display: 'flex', alignItems: 'center'}}>
16
+ <Text style={{backgroundColor: '#f4f4f4', fontSize: 11, fontWeight: 700, padding: '2px 7px', borderRadius: 4, border: '1px solid #e9ecef'}}>
17
+ {currentOs === "macos" ?
18
+ <>⌘</> :
19
+ <>Ctrl</>
20
+ }
21
+ <span style={{margin: '0 5px'}}>+</span>
22
+ K
23
+ </Text>
24
+ </div>
25
+ );
26
+
27
+ return (
28
+ <div>
29
+ <TextInput
30
+ disabled={props.disabled}
31
+ size={'xs'}
32
+ placeholder="Cerca..."
33
+ onClick={() => props.disabled ? undefined : openSpotlight()}
34
+ leftSection={<IconSearch size={16}/>}
35
+ rightSectionWidth={90}
36
+ width={150}
37
+ rightSection={rightSection}
38
+ styles={{input: {cursor: 'pointer', borderRadius: 4}, section: {pointerEvents: 'none'}}}
39
+ />
40
+ </div>
41
+ )
42
+ }
43
+
44
+ export default SearchBar;
@@ -0,0 +1,58 @@
1
+ import {useSelector} from "react-redux";
2
+ import {IFluxoRootState} from "@fluxo-dev/state";
3
+ import React, {useEffect, useState} from "react";
4
+ import {Spotlight, SpotlightActionGroupData} from "@mantine/spotlight";
5
+ import {AppMenuService} from "../../services/app-menu.service";
6
+ import {useNavigate} from "react-router-dom";
7
+ import {FluxoStateDispatcher} from "@fluxo-dev/api-client-base";
8
+ import {IconSearch} from "@tabler/icons-react";
9
+ import {SpotlightActions} from "@mantine/spotlight/lib/Spotlight";
10
+ import { rem } from "@mantine/core";
11
+
12
+ function ShellSpotlight() {
13
+
14
+ const {configuration, appMenu} = useSelector((state: IFluxoRootState) => state.layout);
15
+ const [spotlightActions, setSpotlightActions] = useState<SpotlightActionGroupData[] | SpotlightActions[]>([]);
16
+ const navigateTo = useNavigate();
17
+
18
+ useEffect(() => {
19
+
20
+ const appMenuService = new AppMenuService();
21
+
22
+ if(appMenu) {
23
+ appMenuService.getSpotlightMenu(appMenu)
24
+ .then((res) => {
25
+
26
+ const result: SpotlightActionGroupData[] = res.map((group) => {
27
+ return {
28
+ ...group,
29
+ actions: group.actions.map((action) => {
30
+ return {
31
+ ...action,
32
+ onClick: () => navigateTo(action.id),
33
+ }
34
+ })
35
+ }
36
+ });
37
+
38
+ setSpotlightActions(result)
39
+ })
40
+ }
41
+ }, [configuration, appMenu, navigateTo])
42
+
43
+ return (
44
+ <Spotlight
45
+ onSpotlightOpen={() => FluxoStateDispatcher.dispatchOpenSearchPanel()}
46
+ onSpotlightClose={() => FluxoStateDispatcher.dispatchCloseSearchPanel()}
47
+ actions={spotlightActions}
48
+ searchProps={{
49
+ leftSection: <IconSearch style={{ width: rem(20), height: rem(20) }} stroke={1.5} />,
50
+ placeholder: "Cerca funzione..."
51
+ }}
52
+ shortcut="mod + k"
53
+ nothingFound="Nessun risultato..."
54
+ />
55
+ )
56
+ }
57
+
58
+ export default ShellSpotlight;
@@ -0,0 +1,6 @@
1
+ import {FluxoFrontendAppConfiguration} from "@fluxo-dev/api-client-base";
2
+
3
+ export interface AppJit extends FluxoFrontendAppConfiguration {
4
+ developmentRoute: string;
5
+ developmentUrl: string;
6
+ }
@@ -0,0 +1,22 @@
1
+ import React, {Suspense, useState} from 'react';
2
+ import { ModuleLoader } from "@fluxo-fe/shell/mfe";
3
+
4
+ export interface FluxoPluginLoaderProps {
5
+ url: string,
6
+ scope: string,
7
+ module: string
8
+ }
9
+ function FluxoPluginLoader(props: FluxoPluginLoaderProps) {
10
+
11
+ const [remote, setRemote] = useState<any>({url: props.url, scope: props.scope, module: props.module});
12
+
13
+ return (
14
+ <Suspense fallback={'Loading . . . '}>
15
+ {
16
+ remote && <ModuleLoader url={remote.url} scope={remote.scope} module={remote.module} />
17
+ }
18
+ </Suspense>
19
+ )
20
+ }
21
+
22
+ export default FluxoPluginLoader;