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,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hhmiexample.client",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc -b && vite build",
|
|
9
|
+
"lint": "eslint .",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@fluentui-contrib/react-data-grid-react-window": "^1.3.2",
|
|
14
|
+
"@fluentui/react-components": "^9.70.0",
|
|
15
|
+
"@fluentui/react-datepicker-compat": "^0.6.14",
|
|
16
|
+
"@fluentui/react-icons": "^2.0.309",
|
|
17
|
+
"dayjs": "^1.11.18",
|
|
18
|
+
"jspdf": "^4.2.0",
|
|
19
|
+
"quill": "^2.0.3",
|
|
20
|
+
"react": "18.2.0",
|
|
21
|
+
"react-dom": "18.2.0",
|
|
22
|
+
"react-quill": "^2.0.0",
|
|
23
|
+
"react-router": "^7.8.2",
|
|
24
|
+
"react-router-dom": "^7.9.1",
|
|
25
|
+
"xlsx": "^0.18.5"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@eslint/js": "^9.33.0",
|
|
29
|
+
"@types/node": "^22.18.10",
|
|
30
|
+
"@types/react": "18.2.0",
|
|
31
|
+
"@types/react-dom": "18.2.0",
|
|
32
|
+
"@vitejs/plugin-react": "^5.0.0",
|
|
33
|
+
"eslint": "^9.33.0",
|
|
34
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
35
|
+
"eslint-plugin-react-refresh": "^0.4.20",
|
|
36
|
+
"globals": "^16.3.0",
|
|
37
|
+
"typescript": "~5.8.3",
|
|
38
|
+
"typescript-eslint": "^8.39.1",
|
|
39
|
+
"vite": "^7.1.2",
|
|
40
|
+
"vite-plugin-svgr": "^4.5.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Template prompts
|
|
2
|
+
|
|
3
|
+
This folder contains documentation for the **Example App** template (reusable HHMI scaffold).
|
|
4
|
+
|
|
5
|
+
- **REQUIREMENTS.md** — Describes what the template does and how it is structured (product and technical). Use it to understand the app before customizing or generating new projects from this template.
|
|
6
|
+
- Replace or extend this doc when you turn the template into a real project.
|
|
7
|
+
|
|
8
|
+
## Running the template
|
|
9
|
+
|
|
10
|
+
- **Client:** From `template/hhmiexample.client`, run `npm install` then `npm run dev`.
|
|
11
|
+
- **Full stack:** Open `template/hhmiExample.sln` in Visual Studio and run the **hhmiExample.Server** project (it will proxy to the client dev server).
|
|
12
|
+
- The template expects backend endpoints `/api/HeartBeat`, `/api/Environment`, and `/api/SyncData`. User loading uses placeholder stored procedures (`example.p_Get_Example_User`, `example.p_Example_Manage_UserSettings`); implement these in your own database when building a real app.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Template Requirements: HHMI Example App Scaffold
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document describes the **generic template** used to spin up new HHMI-style applications. The template is a reusable scaffold: same look, feel, and patterns as the reference app, but with **universal naming** (Example, hhmiExample, hhmiexample) and **minimal, generic content**. It has no product-specific branding or features—only what is needed to reuse the shell, navigation, permissioning, and API patterns for future apps.
|
|
6
|
+
|
|
7
|
+
**Audience:** Technical and non-technical readers; new projects can start from this doc and then replace it with project-specific requirements.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Purpose
|
|
12
|
+
|
|
13
|
+
- Provide a **consistent starting point** for new HHMI collaboration apps.
|
|
14
|
+
- Preserve **AppFrame** (header, sidebar, module switcher, theme, mobile nav), **auth and environment** integration, and **permission model**.
|
|
15
|
+
- Allow each new app to **substitute the generic name** (e.g. "Example") with a project name and add their own pages, modules, and stored procedures.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Architecture / High-Level Structure
|
|
20
|
+
|
|
21
|
+
- **Entry:** `main.tsx` → `App.tsx` with `BrowserRouter` and `FluentProvider`. Theme is derived from user preference and environment (Dev/Test/Prod).
|
|
22
|
+
- **Layout:** `AppFrame` wraps all authenticated content: top header (logo, app title "Example App", module switcher, user menu), collapsible sidebar (module nav + Configuration section when permitted), and main content area. Mobile uses bottom nav and a module dropdown.
|
|
23
|
+
- **Routing:**
|
|
24
|
+
- `/` — Landing page (single tile to the Example module).
|
|
25
|
+
- `/example` — Home (blank placeholder).
|
|
26
|
+
- `/example/configuration` — Configuration (blank placeholder; shown only if user can view admin).
|
|
27
|
+
- `/user-settings` — User settings (theme, profile display).
|
|
28
|
+
- `/not-authorized` — Shown when the user is not loaded or not enabled.
|
|
29
|
+
- **Single module:** The module registry has one module, **Example**, with nav items **Home** and **Configuration**. Future apps can add more modules or replace this one.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Authentication and Authorization
|
|
34
|
+
|
|
35
|
+
- **Auth:** In non-development, the app uses Azure auth (e.g. `/.auth/me`, `/.auth/refresh`). `getADUser()` returns the current user identity; the client then calls `getExampleUser(userId)` to load app-specific profile and permissions.
|
|
36
|
+
- **Access:** A user must be **IsEnabled** to use the app. If not enabled or not loaded, the app shows the Not Authorized page.
|
|
37
|
+
- **Permission model (exactly four roles plus access):**
|
|
38
|
+
- **IsEnabled** — Can use the app at all.
|
|
39
|
+
- **IsDeveloper** — Developer role (e.g. can see Configuration).
|
|
40
|
+
- **IsAppManager** — App manager role (e.g. can see Configuration).
|
|
41
|
+
- **IsPowerUser** — Power user role (for future feature gating).
|
|
42
|
+
- **IsSuperUser** — Super user role (for future feature gating).
|
|
43
|
+
- **Configuration visibility:** Users with **IsDeveloper** or **IsAppManager** see the Configuration section in the sidebar and the Configuration nav item; others do not. Route `/example/configuration` is effectively admin-only.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Key Features / Modules
|
|
48
|
+
|
|
49
|
+
### Landing Page
|
|
50
|
+
|
|
51
|
+
- Single view with the same layout and nav pattern as the reference app.
|
|
52
|
+
- One card/tile: **Example**, which navigates to `/example`.
|
|
53
|
+
- Generic title/copy (e.g. "Welcome to the Example App"). No product-specific wording.
|
|
54
|
+
|
|
55
|
+
### Example Module
|
|
56
|
+
|
|
57
|
+
- **Home** (`/example`): Blank page with a short placeholder (e.g. "Home" heading and generic text). Replace with real content per project.
|
|
58
|
+
- **Configuration** (`/example/configuration`): Blank admin/configuration page with the same shell (sidebar, header). No user management or other product-specific UI—just a placeholder for future config screens.
|
|
59
|
+
|
|
60
|
+
### User Settings
|
|
61
|
+
|
|
62
|
+
- Page at `/user-settings`: Profile display (avatar, name, userId) and **Dark/Light** theme toggle. Changes are persisted via `manageExampleUserSettings` (debounced).
|
|
63
|
+
|
|
64
|
+
### Not Authorized
|
|
65
|
+
|
|
66
|
+
- Shown when the user is not loaded or not enabled. Same behavior and styling as the reference app; copy is generic.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Data and Services
|
|
71
|
+
|
|
72
|
+
- **Shared API (unchanged):** Auth (`getADUser`, `AuthMe`, `AuthRefresh`), `getCollabEnvironment`, `HeartBeat`, `syncData`. These are shared infrastructure; do not remove or rename.
|
|
73
|
+
- **App-specific API (generic names):**
|
|
74
|
+
- **getExampleUser(userId)** — Loads the current user profile and permissions. Uses stored procedure **`example.p_Get_Example_User`** (schema **example**; replace with your own backend per project).
|
|
75
|
+
- **manageExampleUserSettings(user, currentUserId)** — Saves user preferences (e.g. theme). Uses stored procedure **`example.p_Example_Manage_UserSettings`**.
|
|
76
|
+
- **Stubs (template only):** `searchUserByEmailWDHCM` and `getAllActiveJaneliaLabs` return empty arrays so that global components (e.g. HHMIPeoplePicker) build and run. Replace with real implementations per project if needed.
|
|
77
|
+
- **Types:** **IExampleUser** (UserId, Name, Title, Email, SupOrgCode, PhotoURL, IsDarkTheme, IsEnabled, IsDeveloper, IsAppManager, IsPowerUser, IsSuperUser) and **IExampleUserSettings** (UserId, IsDarkTheme). All client code uses these; no MTA/StatusViewer-specific types remain.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Shared UI and Behavior
|
|
82
|
+
|
|
83
|
+
- **Theme:** Fluent UI theme; light/dark and Dev/Test/Prod variants (same tokens and setup as reference). Theme is driven by `currentUser.IsDarkTheme` and `CollabEnvironment`.
|
|
84
|
+
- **AppFrame:** Header (logo, "Example App" title, environment badge when not Prod), module switcher, sidebar (collapsible), Configuration section for eligible users, and mobile bottom nav. All labels and types use generic naming.
|
|
85
|
+
- **Global components:** HHMIControls (e.g. HHMIHomeIcons, HHMIPeoplePicker, buttons, dialogs, data grid), Quill editor. Kept for reuse; HHMIPeoplePicker uses the stubbed search in the template.
|
|
86
|
+
- **Module registry:** Single entry **IExample: ExampleConfig** (mirrors the reference pattern). ExampleConfig defines id **Example**, label **Example**, href **/example**, and nav items Home and Configuration with permission resolution from the four roles and IsEnabled.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Backend (hhmiExample.Server)
|
|
91
|
+
|
|
92
|
+
- **Minimal API:** Endpoints `/api/HeartBeat`, `/api/Environment`, `/api/SyncData` only. Same behavior as reference (e.g. SyncData executes a stored procedure by name; no MTA/StatusViewer logic).
|
|
93
|
+
- **Naming:** Project and solution use **hhmiExample** (e.g. `hhmiExample.Server`, `hhmiExample.sln`). SpaRoot points to **hhmiexample.client**.
|
|
94
|
+
- **Stored procedures:** The template does not implement `example.p_Get_Example_User` or `example.p_Example_Manage_UserSettings`; each new app configures its own database and procedures.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## References
|
|
99
|
+
|
|
100
|
+
| Area | Location (template) |
|
|
101
|
+
|-----------------|---------------------|
|
|
102
|
+
| Entry / routing | `src/App.tsx`, `src/main.tsx` |
|
|
103
|
+
| Layout | `src/components/AppFrame/AppFrame.tsx` |
|
|
104
|
+
| Module config | `src/components/AppFrame/modules/ExampleConfig.tsx`, `modules/index.ts` |
|
|
105
|
+
| User types | `src/types/IExampleUser.ts` |
|
|
106
|
+
| Services | `src/services/AppService.ts` |
|
|
107
|
+
| Pages | `src/pages/LandingPage.tsx`, `src/pages/Example/ExampleHomePage.tsx`, `src/pages/Example/ExampleConfigurationPage.tsx`, `src/pages/NotAuthorizedPage.tsx`, User Settings in `AppFrame/UserSettingPage.tsx` |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Summary
|
|
112
|
+
|
|
113
|
+
The template is a **generic, runnable scaffold** with one module (Example), blank Home and Configuration pages, and a permission model of **IsEnabled** plus **IsDeveloper**, **IsAppManager**, **IsPowerUser**, and **IsSuperUser**. Placeholder stored procedures use the **example** schema. Use it as the source for generating new apps (e.g. via an npm create flow) and substitute the generic name with the project name where appropriate.
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { FluentProvider, type Theme } from "@fluentui/react-components";
|
|
2
|
+
import AppFrame from "./components/AppFrame/AppFrame";
|
|
3
|
+
import { Navigate, Route, Routes } from "react-router";
|
|
4
|
+
import React, { useEffect } from "react";
|
|
5
|
+
import { darkTheme, darkThemeDev, darkThemeTest, lightTheme, lightThemeDev, lightThemeTest } from "./components/AppFrame/Theme";
|
|
6
|
+
import UserSettingsPage from "./components/AppFrame/UserSettingPage";
|
|
7
|
+
import type { IExampleUser, IExampleUserSettings } from "./types/IExampleUser";
|
|
8
|
+
import { getADUser, getCollabEnvironment, getExampleUser, manageExampleUserSettings } from "./services/AppService";
|
|
9
|
+
import LandingPage from "./pages/LandingPage";
|
|
10
|
+
import NotAuthorizedPage from "./pages/NotAuthorizedPage";
|
|
11
|
+
import ExampleHomePage from "./pages/Example/ExampleHomePage";
|
|
12
|
+
import ExampleConfigurationPage from "./pages/Example/ExampleConfigurationPage";
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line react-refresh/only-export-components
|
|
15
|
+
export const CollabEnvironment = {
|
|
16
|
+
Dev: "Dev",
|
|
17
|
+
Test: "Test",
|
|
18
|
+
Prod: "Prod"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function App() {
|
|
22
|
+
const [collabEnvironment, setCollabEnvironment] = React.useState<string>("");
|
|
23
|
+
const [dataLoaded, setDataLoaded] = React.useState<boolean>(false);
|
|
24
|
+
const [currentTheme, setCurrentTheme] = React.useState<Theme | undefined>(undefined);
|
|
25
|
+
const [currentUser, setCurrentUser] = React.useState<IExampleUser | undefined>(undefined);
|
|
26
|
+
|
|
27
|
+
const pageNotFound = <>Error: Page Not Found</>;
|
|
28
|
+
|
|
29
|
+
const getTheme = React.useCallback((user: IExampleUser): Theme | undefined => {
|
|
30
|
+
switch (collabEnvironment) {
|
|
31
|
+
case CollabEnvironment.Dev:
|
|
32
|
+
return user.IsDarkTheme ? darkThemeDev : lightThemeDev;
|
|
33
|
+
case CollabEnvironment.Test:
|
|
34
|
+
return user.IsDarkTheme ? darkThemeTest : lightThemeTest;
|
|
35
|
+
case CollabEnvironment.Prod:
|
|
36
|
+
return user.IsDarkTheme ? darkTheme : lightTheme;
|
|
37
|
+
default:
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}, [collabEnvironment]);
|
|
41
|
+
|
|
42
|
+
const initializeData = React.useCallback(async (): Promise<void> => {
|
|
43
|
+
const environment: string = await getCollabEnvironment();
|
|
44
|
+
setCollabEnvironment(environment);
|
|
45
|
+
|
|
46
|
+
if (process.env.NODE_ENV !== "development") {
|
|
47
|
+
getADUser().then((authMe) => {
|
|
48
|
+
if (authMe) {
|
|
49
|
+
getExampleUser(authMe.user_id).then((user: IExampleUser | null) => {
|
|
50
|
+
if (user && user.IsEnabled) {
|
|
51
|
+
setCurrentUser(user);
|
|
52
|
+
setCurrentTheme(getTheme(user));
|
|
53
|
+
}
|
|
54
|
+
}).catch((error) => {
|
|
55
|
+
console.log("Failed to get user:", error);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}).catch((error) => {
|
|
59
|
+
console.log("Failed to get user authentication:", error);
|
|
60
|
+
});
|
|
61
|
+
} else {
|
|
62
|
+
console.log('Local dev mode');
|
|
63
|
+
setCurrentTheme(darkThemeDev);
|
|
64
|
+
setCurrentUser({
|
|
65
|
+
UserId: "guest@test.org",
|
|
66
|
+
Name: "Guest User",
|
|
67
|
+
Title: "Guest Title",
|
|
68
|
+
Email: "guest@test.org",
|
|
69
|
+
SupOrgCode: "",
|
|
70
|
+
IsEnabled: true,
|
|
71
|
+
IsDeveloper: false,
|
|
72
|
+
IsAppManager: false,
|
|
73
|
+
IsPowerUser: false,
|
|
74
|
+
IsSuperUser: false,
|
|
75
|
+
PhotoURL: "",
|
|
76
|
+
IsDarkTheme: true,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}, [getTheme]);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
initializeData().then(() => {
|
|
83
|
+
setDataLoaded(true);
|
|
84
|
+
});
|
|
85
|
+
}, [initializeData]);
|
|
86
|
+
|
|
87
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
88
|
+
|
|
89
|
+
function debounce(func: (updatedUser: IExampleUserSettings, currentUserId: string) => Promise<IExampleUserSettings>, updatedUser: IExampleUserSettings, currentUserId: string, time: number) {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
timer = setTimeout(() => {
|
|
92
|
+
func(updatedUser, currentUserId);
|
|
93
|
+
}, time);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const updateUserSettings = (updatedUser: IExampleUser, currentUserId: string) => {
|
|
97
|
+
debounce(manageExampleUserSettings, updatedUser, currentUserId, 1000);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const updateCurrentUser = (user: IExampleUser) => {
|
|
101
|
+
setCurrentUser(user);
|
|
102
|
+
setCurrentTheme(getTheme(user));
|
|
103
|
+
updateUserSettings(user, user.UserId);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (dataLoaded && currentTheme && !currentUser) {
|
|
107
|
+
return (
|
|
108
|
+
<FluentProvider theme={currentTheme}>
|
|
109
|
+
<NotAuthorizedPage />
|
|
110
|
+
</FluentProvider>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return (dataLoaded && currentTheme && currentUser) ? (
|
|
115
|
+
<FluentProvider theme={currentTheme}>
|
|
116
|
+
<Routes>
|
|
117
|
+
<Route path="/not-authorized" element={<NotAuthorizedPage />} />
|
|
118
|
+
|
|
119
|
+
<Route element={
|
|
120
|
+
<AppFrame
|
|
121
|
+
currentUser={currentUser}
|
|
122
|
+
environment={collabEnvironment}
|
|
123
|
+
setCurrentUser={(user) => {
|
|
124
|
+
if (user) {
|
|
125
|
+
updateCurrentUser(user as IExampleUser);
|
|
126
|
+
}
|
|
127
|
+
}}
|
|
128
|
+
/>
|
|
129
|
+
}>
|
|
130
|
+
<Route index element={<LandingPage currentUser={currentUser} />} />
|
|
131
|
+
<Route path="user-settings" element={
|
|
132
|
+
<UserSettingsPage currentUser={currentUser} setCurrentUser={(user) => {
|
|
133
|
+
if (user) { updateCurrentUser(user as IExampleUser); }
|
|
134
|
+
}} />
|
|
135
|
+
} />
|
|
136
|
+
<Route path="example">
|
|
137
|
+
<Route index element={<ExampleHomePage />} />
|
|
138
|
+
<Route path="configuration" element={(currentUser.IsDeveloper || currentUser.IsAppManager) ? <ExampleConfigurationPage currentUser={currentUser} /> : <Navigate to="/not-authorized" replace />} />
|
|
139
|
+
</Route>
|
|
140
|
+
<Route path="*" element={pageNotFound} />
|
|
141
|
+
</Route>
|
|
142
|
+
</Routes>
|
|
143
|
+
</FluentProvider>
|
|
144
|
+
) : <></>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default App;
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|