create-hhmi-example 1.0.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/README.md +78 -0
- package/copy-template.js +76 -0
- package/index.js +254 -0
- package/package.json +17 -0
- package/template/hhmiExample.Server/Program.cs +167 -0
- package/template/hhmiExample.Server/Properties/launchSettings.json +44 -0
- package/template/hhmiExample.Server/appsettings.Development.json +8 -0
- package/template/hhmiExample.Server/appsettings.json +9 -0
- package/template/hhmiExample.Server/hhmiExample.Server.csproj +50 -0
- package/template/hhmiExample.Server/hhmiExample.Server.http +6 -0
- package/template/hhmiExample.sln +33 -0
- package/template/hhmiexample.client/eslint.config.js +23 -0
- package/template/hhmiexample.client/hhmiexample.client.esproj +12 -0
- package/template/hhmiexample.client/index.html +13 -0
- package/template/hhmiexample.client/package-lock.json +6490 -0
- package/template/hhmiexample.client/package.json +42 -0
- package/template/hhmiexample.client/prompts/README.md +12 -0
- package/template/hhmiexample.client/prompts/REQUIREMENTS.md +113 -0
- package/template/hhmiexample.client/public/favicon.ico +0 -0
- package/template/hhmiexample.client/public/vite.svg +1 -0
- package/template/hhmiexample.client/src/App.css +11 -0
- package/template/hhmiexample.client/src/App.tsx +147 -0
- package/template/hhmiexample.client/src/assets/logo-black.png +0 -0
- package/template/hhmiexample.client/src/assets/logo-white.png +0 -0
- package/template/hhmiexample.client/src/assets/react.svg +1 -0
- package/template/hhmiexample.client/src/components/AppFrame/AppFrame.tsx +796 -0
- package/template/hhmiexample.client/src/components/AppFrame/Theme.tsx +98 -0
- package/template/hhmiexample.client/src/components/AppFrame/UserSettingPage.tsx +91 -0
- package/template/hhmiexample.client/src/components/AppFrame/UserSettings.tsx +146 -0
- package/template/hhmiexample.client/src/components/AppFrame/modules/ExampleConfig.tsx +86 -0
- package/template/hhmiexample.client/src/components/AppFrame/modules/index.ts +8 -0
- package/template/hhmiexample.client/src/components/AppFrame/types.ts +48 -0
- package/template/hhmiexample.client/src/components/Global/HHMIControls.tsx +567 -0
- package/template/hhmiexample.client/src/components/Global/Quill.tsx +60 -0
- package/template/hhmiexample.client/src/index.css +11 -0
- package/template/hhmiexample.client/src/main.tsx +17 -0
- package/template/hhmiexample.client/src/pages/Example/ExampleConfigurationPage.tsx +24 -0
- package/template/hhmiexample.client/src/pages/Example/ExampleHomePage.tsx +23 -0
- package/template/hhmiexample.client/src/pages/LandingPage.tsx +36 -0
- package/template/hhmiexample.client/src/pages/NotAuthorizedPage.tsx +18 -0
- package/template/hhmiexample.client/src/services/AppService.ts +297 -0
- package/template/hhmiexample.client/src/types/IExampleUser.ts +19 -0
- package/template/hhmiexample.client/src/types/IMessageLocation.ts +8 -0
- package/template/hhmiexample.client/src/vite-env.d.ts +4 -0
- package/template/hhmiexample.client/tsconfig.app.json +27 -0
- package/template/hhmiexample.client/tsconfig.json +11 -0
- package/template/hhmiexample.client/tsconfig.node.json +25 -0
- package/template/hhmiexample.client/vite.config.ts +61 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createDarkTheme, createLightTheme, type BrandVariants, type Theme } from "@fluentui/react-components";
|
|
2
|
+
|
|
3
|
+
const colorNeutralForegroundDisabledLight = '#888';
|
|
4
|
+
const colorNeutralForegroundDisabledDark = '#999';
|
|
5
|
+
|
|
6
|
+
const brandThemeDev: BrandVariants = {
|
|
7
|
+
10: "#0A0102",
|
|
8
|
+
20: "#1A0408",
|
|
9
|
+
30: "#2A070E",
|
|
10
|
+
40: "#3A0A14",
|
|
11
|
+
50: "#4A0D1A",
|
|
12
|
+
60: "#5A1020",
|
|
13
|
+
70: "#6A1326",
|
|
14
|
+
80: "#7A162C",
|
|
15
|
+
90: "#8A1932",
|
|
16
|
+
100: "#9A1C38",
|
|
17
|
+
110: "#AA1F3E",
|
|
18
|
+
120: "#BA2244",
|
|
19
|
+
130: "#CA254A",
|
|
20
|
+
140: "#DA2850",
|
|
21
|
+
150: "#EA2B56",
|
|
22
|
+
160: "#FA2E5C"
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const lightThemeDev: Theme = {
|
|
26
|
+
...createLightTheme(brandThemeDev),
|
|
27
|
+
colorNeutralForegroundDisabled: colorNeutralForegroundDisabledLight
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const darkThemeDev: Theme = {
|
|
31
|
+
...createDarkTheme(brandThemeDev),
|
|
32
|
+
colorNeutralForegroundDisabled: colorNeutralForegroundDisabledDark
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const brandThemeTest: BrandVariants = {
|
|
36
|
+
10: "#000C1A",
|
|
37
|
+
20: "#001A33",
|
|
38
|
+
30: "#00284D",
|
|
39
|
+
40: "#003666",
|
|
40
|
+
50: "#004480",
|
|
41
|
+
60: "#005299",
|
|
42
|
+
70: "#0060B3",
|
|
43
|
+
80: "#006ECC",
|
|
44
|
+
90: "#007CE6",
|
|
45
|
+
100: "#004EFF",
|
|
46
|
+
110: "#004EFF",
|
|
47
|
+
120: "#1A66FF",
|
|
48
|
+
130: "#337EFF",
|
|
49
|
+
140: "#4D96FF",
|
|
50
|
+
150: "#66AEFF",
|
|
51
|
+
160: "#80C6FF"
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const lightThemeTest: Theme = {
|
|
55
|
+
...createLightTheme(brandThemeTest),
|
|
56
|
+
colorBrandBackground: brandThemeTest[110],
|
|
57
|
+
colorBrandBackground2: brandThemeTest[120],
|
|
58
|
+
colorNeutralForegroundDisabled: colorNeutralForegroundDisabledLight
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const darkThemeTest: Theme = {
|
|
62
|
+
...createDarkTheme(brandThemeTest),
|
|
63
|
+
colorBrandBackground: brandThemeTest[110],
|
|
64
|
+
colorBrandBackground2: brandThemeTest[120],
|
|
65
|
+
colorNeutralForegroundDisabled: colorNeutralForegroundDisabledDark
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const brandTheme: BrandVariants = {
|
|
69
|
+
10: "#020404",
|
|
70
|
+
20: "#0F1B1C",
|
|
71
|
+
30: "#142D2F",
|
|
72
|
+
40: "#163A3D",
|
|
73
|
+
50: "#17484C",
|
|
74
|
+
60: "#17565B",
|
|
75
|
+
70: "#16656B",
|
|
76
|
+
80: "#13747B",
|
|
77
|
+
90: "#358187",
|
|
78
|
+
100: "#4E8E93",
|
|
79
|
+
110: "#659BA0",
|
|
80
|
+
120: "#7AA9AD",
|
|
81
|
+
130: "#90B6B9",
|
|
82
|
+
140: "#A5C4C6",
|
|
83
|
+
150: "#BAD2D4",
|
|
84
|
+
160: "#CFE0E1"
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const lightTheme: Theme = {
|
|
88
|
+
...createLightTheme(brandTheme),
|
|
89
|
+
colorNeutralForegroundDisabled: colorNeutralForegroundDisabledLight,
|
|
90
|
+
colorBrandBackgroundInvertedHover: brandTheme[100],
|
|
91
|
+
colorBrandBackground2: brandTheme[60]
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const darkTheme: Theme = {
|
|
95
|
+
...createDarkTheme(brandTheme),
|
|
96
|
+
colorNeutralForegroundDisabled: colorNeutralForegroundDisabledDark,
|
|
97
|
+
colorBrandBackground2: brandTheme[60]
|
|
98
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
makeStyles,
|
|
3
|
+
tokens,
|
|
4
|
+
Title2,
|
|
5
|
+
Text,
|
|
6
|
+
Avatar,
|
|
7
|
+
Switch
|
|
8
|
+
} from "@fluentui/react-components";
|
|
9
|
+
import type { IExampleUser } from "../../types/IExampleUser";
|
|
10
|
+
|
|
11
|
+
const useStyles = makeStyles({
|
|
12
|
+
container: {
|
|
13
|
+
padding: '24px',
|
|
14
|
+
maxWidth: '600px',
|
|
15
|
+
margin: '0 auto'
|
|
16
|
+
},
|
|
17
|
+
header: {
|
|
18
|
+
display: 'flex',
|
|
19
|
+
alignItems: 'center',
|
|
20
|
+
gap: '16px',
|
|
21
|
+
marginBottom: '32px'
|
|
22
|
+
},
|
|
23
|
+
backButton: {
|
|
24
|
+
minWidth: '40px'
|
|
25
|
+
},
|
|
26
|
+
profileSection: {
|
|
27
|
+
display: 'flex',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
gap: '16px',
|
|
30
|
+
marginBottom: '32px',
|
|
31
|
+
padding: '24px',
|
|
32
|
+
backgroundColor: tokens.colorNeutralBackground2,
|
|
33
|
+
borderRadius: tokens.borderRadiusLarge
|
|
34
|
+
},
|
|
35
|
+
profileInfo: {
|
|
36
|
+
display: 'flex',
|
|
37
|
+
flexDirection: 'column',
|
|
38
|
+
gap: '4px'
|
|
39
|
+
},
|
|
40
|
+
settingInfo: {
|
|
41
|
+
display: 'flex',
|
|
42
|
+
flexDirection: 'column',
|
|
43
|
+
gap: '4px'
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export default function UserSettingsPage(props: { currentUser: IExampleUser; setCurrentUser: React.Dispatch<React.SetStateAction<IExampleUser | undefined>>; }) {
|
|
48
|
+
const styles = useStyles();
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className={styles.container}>
|
|
52
|
+
<div className={styles.header}>
|
|
53
|
+
<Title2>User Settings</Title2>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div className={styles.profileSection}>
|
|
57
|
+
<Avatar
|
|
58
|
+
name={props.currentUser.Name}
|
|
59
|
+
size={128}
|
|
60
|
+
color="colorful"
|
|
61
|
+
image={{ src: props.currentUser.PhotoURL }}
|
|
62
|
+
/>
|
|
63
|
+
<div className={styles.profileInfo}>
|
|
64
|
+
<Text size={500} weight="semibold">{props.currentUser.Name}</Text>
|
|
65
|
+
<Text size={300} style={{ color: tokens.colorNeutralForeground3 }}>
|
|
66
|
+
{props.currentUser.UserId}
|
|
67
|
+
</Text>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div className={styles.profileSection} style={{ justifyContent: 'space-between' }}>
|
|
71
|
+
<div className={styles.settingInfo}>
|
|
72
|
+
<Text weight="semibold">{props.currentUser.IsDarkTheme === true ? "Dark" : "Light"} Mode</Text>
|
|
73
|
+
<Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
|
|
74
|
+
Switch between light and dark themes
|
|
75
|
+
</Text>
|
|
76
|
+
</div>
|
|
77
|
+
<Switch
|
|
78
|
+
checked={props.currentUser.IsDarkTheme}
|
|
79
|
+
aria-label="Toggle dark mode"
|
|
80
|
+
onChange={(_ev, data) => {
|
|
81
|
+
const updatedUser = {
|
|
82
|
+
...props.currentUser,
|
|
83
|
+
IsDarkTheme: data.checked
|
|
84
|
+
};
|
|
85
|
+
props.setCurrentUser(updatedUser);
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Avatar,
|
|
3
|
+
makeStyles,
|
|
4
|
+
Text,
|
|
5
|
+
tokens,
|
|
6
|
+
Menu,
|
|
7
|
+
MenuTrigger,
|
|
8
|
+
MenuPopover,
|
|
9
|
+
MenuList,
|
|
10
|
+
MenuItem,
|
|
11
|
+
MenuDivider
|
|
12
|
+
} from "@fluentui/react-components";
|
|
13
|
+
import { DarkTheme20Regular } from "@fluentui/react-icons";
|
|
14
|
+
import { useNavigate } from "react-router";
|
|
15
|
+
import React from "react";
|
|
16
|
+
import type { IExampleUser } from "../../types/IExampleUser";
|
|
17
|
+
|
|
18
|
+
interface IUserSettingsProps {
|
|
19
|
+
setCurrentUser: React.Dispatch<React.SetStateAction<IExampleUser | undefined>>;
|
|
20
|
+
currentUser: IExampleUser;
|
|
21
|
+
isProd: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function UserSettings(props: IUserSettingsProps) {
|
|
25
|
+
const useStyles = makeStyles({
|
|
26
|
+
headerRight: {
|
|
27
|
+
display: 'flex',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
gap: '16px'
|
|
30
|
+
},
|
|
31
|
+
userName: {
|
|
32
|
+
'@media (max-width: 768px)': {
|
|
33
|
+
display: 'none'
|
|
34
|
+
},
|
|
35
|
+
color: props.isProd ? tokens.colorNeutralForeground1 : tokens.colorNeutralForegroundOnBrand,
|
|
36
|
+
},
|
|
37
|
+
userProfile: {
|
|
38
|
+
display: 'flex',
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
gap: '8px',
|
|
41
|
+
padding: '8px',
|
|
42
|
+
borderRadius: tokens.borderRadiusMedium,
|
|
43
|
+
cursor: 'pointer',
|
|
44
|
+
':hover': {
|
|
45
|
+
backgroundColor: tokens.colorNeutralBackground1Hover
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
menuTrigger: {
|
|
49
|
+
border: 'none',
|
|
50
|
+
background: 'none',
|
|
51
|
+
padding: 0,
|
|
52
|
+
cursor: 'pointer',
|
|
53
|
+
borderRadius: tokens.borderRadiusMedium,
|
|
54
|
+
':focus': {
|
|
55
|
+
outline: 'none'
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
userInfo: {
|
|
59
|
+
padding: '12px 16px',
|
|
60
|
+
display: 'flex',
|
|
61
|
+
flexDirection: 'column',
|
|
62
|
+
gap: '4px'
|
|
63
|
+
},
|
|
64
|
+
userEmail: {
|
|
65
|
+
fontSize: tokens.fontSizeBase200,
|
|
66
|
+
color: tokens.colorNeutralForeground3
|
|
67
|
+
},
|
|
68
|
+
mobileUserButton: {
|
|
69
|
+
backgroundColor: 'transparent',
|
|
70
|
+
border: 'none',
|
|
71
|
+
'@media (min-width: 769px)': {
|
|
72
|
+
display: 'none'
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
desktopUserMenu: {
|
|
76
|
+
'@media (max-width: 768px)': {
|
|
77
|
+
display: 'none'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const styles = useStyles();
|
|
82
|
+
|
|
83
|
+
const navigate = useNavigate();
|
|
84
|
+
|
|
85
|
+
const handleMobileUserClick = () => {
|
|
86
|
+
navigate('/user-settings');
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className={styles.headerRight}>
|
|
91
|
+
<button
|
|
92
|
+
className={`${styles.userProfile} ${styles.mobileUserButton}`}
|
|
93
|
+
onClick={handleMobileUserClick}
|
|
94
|
+
aria-label="Go to user settings"
|
|
95
|
+
>
|
|
96
|
+
<Avatar
|
|
97
|
+
name={props.currentUser.Name}
|
|
98
|
+
size={32}
|
|
99
|
+
color="colorful"
|
|
100
|
+
image={{ src: props.currentUser.PhotoURL }}
|
|
101
|
+
/>
|
|
102
|
+
<Text className={styles.userName}>{props.currentUser.Name}</Text>
|
|
103
|
+
</button>
|
|
104
|
+
|
|
105
|
+
<div className={styles.desktopUserMenu}>
|
|
106
|
+
<Menu>
|
|
107
|
+
<MenuTrigger disableButtonEnhancement>
|
|
108
|
+
<button className={styles.menuTrigger} aria-label="User menu">
|
|
109
|
+
<div className={styles.userProfile}>
|
|
110
|
+
<Avatar
|
|
111
|
+
name={props.currentUser.Name}
|
|
112
|
+
size={32}
|
|
113
|
+
color="colorful"
|
|
114
|
+
image={{ src: props.currentUser.PhotoURL }}
|
|
115
|
+
/>
|
|
116
|
+
<Text className={styles.userName}>{props.currentUser.Name}</Text>
|
|
117
|
+
</div>
|
|
118
|
+
</button>
|
|
119
|
+
</MenuTrigger>
|
|
120
|
+
<MenuPopover>
|
|
121
|
+
<MenuList>
|
|
122
|
+
<div className={styles.userInfo}>
|
|
123
|
+
<Text weight="semibold">{props.currentUser.Name}</Text>
|
|
124
|
+
<Text className={styles.userEmail}>{props.currentUser.UserId}</Text>
|
|
125
|
+
</div>
|
|
126
|
+
<MenuDivider />
|
|
127
|
+
<MenuItem
|
|
128
|
+
icon={<DarkTheme20Regular />}
|
|
129
|
+
onClick={() => {
|
|
130
|
+
const updatedUser = {
|
|
131
|
+
...props.currentUser,
|
|
132
|
+
IsDarkTheme: !props.currentUser.IsDarkTheme
|
|
133
|
+
};
|
|
134
|
+
props.setCurrentUser(updatedUser);
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
Switch to {props.currentUser.IsDarkTheme === true ? 'Light Mode' : 'Dark Mode'}
|
|
138
|
+
</MenuItem>
|
|
139
|
+
<MenuDivider />
|
|
140
|
+
</MenuList>
|
|
141
|
+
</MenuPopover>
|
|
142
|
+
</Menu>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Cube20Regular, Home20Regular, Shield20Regular } from "@fluentui/react-icons";
|
|
2
|
+
import type { IModuleConfig, IUserPermissions, INavItem } from "../types";
|
|
3
|
+
import type { IExampleUser } from "../../../types/IExampleUser";
|
|
4
|
+
|
|
5
|
+
export const ExampleNavItems = {
|
|
6
|
+
Home: "ExampleHome",
|
|
7
|
+
Admin: "ExampleAdmin",
|
|
8
|
+
AdminConfiguration: "ExampleAdminConfiguration",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface IExamplePermissions extends IUserPermissions {
|
|
12
|
+
isDeveloper: boolean;
|
|
13
|
+
isAppManager: boolean;
|
|
14
|
+
isPowerUser: boolean;
|
|
15
|
+
isSuperUser: boolean;
|
|
16
|
+
isDevOrAppManager: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const resolveExamplePermissions = (user: IExampleUser): IExamplePermissions => {
|
|
20
|
+
const isDeveloper = user.IsDeveloper;
|
|
21
|
+
const isAppManager = user.IsAppManager;
|
|
22
|
+
const isPowerUser = user.IsPowerUser;
|
|
23
|
+
const isSuperUser = user.IsSuperUser;
|
|
24
|
+
const isDevOrAppManager = isDeveloper || isAppManager;
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
isDeveloper,
|
|
28
|
+
isAppManager,
|
|
29
|
+
isPowerUser,
|
|
30
|
+
isSuperUser,
|
|
31
|
+
isDevOrAppManager,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const canViewExampleAdmin = (permissions: IUserPermissions): boolean => {
|
|
36
|
+
const perms = permissions as IExamplePermissions;
|
|
37
|
+
return perms.isDevOrAppManager;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const ExampleConfig: IModuleConfig = {
|
|
41
|
+
id: "Example",
|
|
42
|
+
label: "Example",
|
|
43
|
+
icon: Cube20Regular,
|
|
44
|
+
href: "/example",
|
|
45
|
+
navItems: ExampleNavItems,
|
|
46
|
+
resolvePermissions: resolveExamplePermissions,
|
|
47
|
+
canViewAdmin: canViewExampleAdmin,
|
|
48
|
+
getAdminNavItems: (activeModule: string, permissions: IUserPermissions) => {
|
|
49
|
+
const perms = permissions as IExamplePermissions;
|
|
50
|
+
const navItems: INavItem[] = [];
|
|
51
|
+
if (perms.isDevOrAppManager) {
|
|
52
|
+
const activeModuleLower = activeModule.toLowerCase();
|
|
53
|
+
navItems.push({
|
|
54
|
+
id: ExampleNavItems.AdminConfiguration,
|
|
55
|
+
label: "Configuration",
|
|
56
|
+
icon: Shield20Regular,
|
|
57
|
+
href: `/${activeModuleLower}/configuration`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return navItems;
|
|
61
|
+
},
|
|
62
|
+
getRegularNavItems: (activeModule: string, _permissions: IUserPermissions) => {
|
|
63
|
+
const activeModuleLower = activeModule.toLowerCase();
|
|
64
|
+
const navItems: INavItem[] = [
|
|
65
|
+
{
|
|
66
|
+
id: ExampleNavItems.Home,
|
|
67
|
+
label: "Home",
|
|
68
|
+
icon: Home20Regular,
|
|
69
|
+
href: `/${activeModuleLower}`,
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
return navItems;
|
|
73
|
+
},
|
|
74
|
+
getMobileAdminNavItem: (activeModule: string, canViewAdmin: boolean) => {
|
|
75
|
+
return canViewAdmin
|
|
76
|
+
? [
|
|
77
|
+
{
|
|
78
|
+
id: ExampleNavItems.Admin,
|
|
79
|
+
label: "Configuration",
|
|
80
|
+
icon: Shield20Regular,
|
|
81
|
+
href: `/${activeModule.toLowerCase()}/configuration`,
|
|
82
|
+
},
|
|
83
|
+
]
|
|
84
|
+
: [];
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { IExampleUser } from "../../types/IExampleUser";
|
|
3
|
+
|
|
4
|
+
export interface INavItemBase {
|
|
5
|
+
id: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface INavLinkItem extends INavItemBase {
|
|
9
|
+
label: string;
|
|
10
|
+
icon: React.ElementType;
|
|
11
|
+
href: string;
|
|
12
|
+
isDivider?: false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface INavDividerItem extends INavItemBase {
|
|
16
|
+
isDivider: true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type INavItem = INavLinkItem | INavDividerItem;
|
|
20
|
+
|
|
21
|
+
export interface IModuleNavItems {
|
|
22
|
+
[key: string]: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface IUserPermissions {
|
|
26
|
+
[key: string]: boolean | undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type PermissionResolver = (user: IExampleUser) => IUserPermissions;
|
|
30
|
+
|
|
31
|
+
export type CanViewAdminResolver = (permissions: IUserPermissions) => boolean;
|
|
32
|
+
|
|
33
|
+
export interface IModuleConfig {
|
|
34
|
+
id: string;
|
|
35
|
+
label: string;
|
|
36
|
+
icon: React.ElementType;
|
|
37
|
+
href: string;
|
|
38
|
+
navItems: IModuleNavItems;
|
|
39
|
+
resolvePermissions: PermissionResolver;
|
|
40
|
+
canViewAdmin: CanViewAdminResolver;
|
|
41
|
+
getAdminNavItems: (activeModule: string, permissions: IUserPermissions) => INavItem[];
|
|
42
|
+
getRegularNavItems: (activeModule: string, permissions: IUserPermissions) => INavItem[];
|
|
43
|
+
getMobileAdminNavItem: (activeModule: string, canViewAdmin: boolean) => INavItem[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface IModuleRegistry {
|
|
47
|
+
[moduleId: string]: IModuleConfig;
|
|
48
|
+
}
|