@smartbooks-ai/layout 0.0.3
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/package/tsconfig.layout.tsbuildinfo +1 -0
- package/package.json +48 -0
- package/src/components/PageHeader/PageHeader.tsx +15 -0
- package/src/components/PageHeader/index.ts +1 -0
- package/src/components/PageHeader/styles.ts +34 -0
- package/src/components/PageWithMenuLayout/AppSelect/index.tsx +66 -0
- package/src/components/PageWithMenuLayout/AppSelect/styles.ts +33 -0
- package/src/components/PageWithMenuLayout/LogoHeaderImage.tsx +44 -0
- package/src/components/PageWithMenuLayout/LogoHeaderText.tsx +48 -0
- package/src/components/PageWithMenuLayout/MenuItemWithChildren/MenuItemWithChildren.tsx +149 -0
- package/src/components/PageWithMenuLayout/MenuItemWithChildren/styles.ts +179 -0
- package/src/components/PageWithMenuLayout/MenuSelect/index.tsx +78 -0
- package/src/components/PageWithMenuLayout/MenuSelect/styles.ts +97 -0
- package/src/components/PageWithMenuLayout/MultiSubscriptionsMenuItems/ConsolidationIcon.tsx +6 -0
- package/src/components/PageWithMenuLayout/MultiSubscriptionsMenuItems/MultiSubscriptionsMenuItems.tsx +120 -0
- package/src/components/PageWithMenuLayout/MultiSubscriptionsMenuItems/consolidation.svg +8 -0
- package/src/components/PageWithMenuLayout/MultiSubscriptionsMenuItems/index.ts +1 -0
- package/src/components/PageWithMenuLayout/MultiSubscriptionsMenuItems/styles.ts +10 -0
- package/src/components/PageWithMenuLayout/PageWithMenuLayout.tsx +106 -0
- package/src/components/PageWithMenuLayout/UserProfileSelect/index.tsx +64 -0
- package/src/components/PageWithMenuLayout/UserProfileSelect/styles.ts +8 -0
- package/src/components/PageWithMenuLayout/index.ts +8 -0
- package/src/components/PageWithMenuLayout/styles.ts +110 -0
- package/src/components/PageWithMenuLayout/types.ts +7 -0
- package/src/components/index.ts +3 -0
- package/src/emotion.d.ts +76 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useIsAuthorized.ts +35 -0
- package/src/hooks/useToggle.ts +27 -0
- package/src/index.ts +7 -0
- package/src/package-isolation.test.ts +60 -0
- package/src/security/AuthorizedContent/index.tsx +77 -0
- package/src/security/AuthorizedContent/state.ts +8 -0
- package/src/security/AuthorizedContent/useAuthorizationState.ts +42 -0
- package/src/security/ProfileContext/ProfileContext.tsx +37 -0
- package/src/security/ProfileContext/index.ts +4 -0
- package/src/security/ProfileContext/types.ts +7 -0
- package/src/security/ProfileContext/useProfile.tsx +7 -0
- package/src/security/UserProfile.ts +48 -0
- package/src/security/index.ts +2 -0
- package/src/theme/colorPrimitives.ts +107 -0
- package/src/theme/colors.ts +78 -0
- package/src/theme/font.ts +27 -0
- package/src/theme/globalStyles.tsx +55 -0
- package/src/theme/index.tsx +228 -0
- package/src/theme/radius.ts +12 -0
- package/src/theme/spacing.ts +12 -0
- package/src/theme/typography.ts +40 -0
- package/src/utils/assertNever.ts +14 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/shouldNotForwardPropsWithKeys.ts +7 -0
- package/tsconfig.json +34 -0
- package/tsconfig.layout.tsbuildinfo +1 -0
- package/vitest.config.ts +10 -0
package/src/emotion.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import '@emotion/react';
|
|
2
|
+
import { ThemeOptions as MuiThemeOptions } from '@mui/material/styles';
|
|
3
|
+
|
|
4
|
+
type PaletteShade = 'main' | 'light' | 'dark' | 'contrast';
|
|
5
|
+
|
|
6
|
+
type CommonColorShade = '50' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | '950';
|
|
7
|
+
type SignalColorShade = '100' | '300' | '600' | '900' | '950';
|
|
8
|
+
|
|
9
|
+
type ColorPrimitives = {
|
|
10
|
+
darkNavy: Record<CommonColorShade, string>;
|
|
11
|
+
coolMint: Record<CommonColorShade, string>;
|
|
12
|
+
coral: Record<CommonColorShade, string>;
|
|
13
|
+
navy: Record<CommonColorShade, string>;
|
|
14
|
+
skyBlue: Record<CommonColorShade, string>;
|
|
15
|
+
grey: Record<CommonColorShade, string>;
|
|
16
|
+
common: Record<'black' | 'white', string>;
|
|
17
|
+
warning: Record<SignalColorShade, string>;
|
|
18
|
+
error: Record<SignalColorShade, string>;
|
|
19
|
+
success: Record<SignalColorShade, string>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type ColorPalette = {
|
|
23
|
+
primitives: ColorPrimitives;
|
|
24
|
+
text: Record<'main' | 'light' | 'lighter' | 'disabled' | 'placeholder' | 'white', string>;
|
|
25
|
+
primary: Record<PaletteShade, string>;
|
|
26
|
+
secondary: Record<PaletteShade, string>;
|
|
27
|
+
tertiary: Record<PaletteShade, string>;
|
|
28
|
+
quaternary: Record<PaletteShade, string>;
|
|
29
|
+
quinary: Record<PaletteShade, string>;
|
|
30
|
+
neutral: Record<PaletteShade, string>;
|
|
31
|
+
warning: Record<PaletteShade, string>;
|
|
32
|
+
error: Record<PaletteShade, string>;
|
|
33
|
+
success: Record<PaletteShade, string>;
|
|
34
|
+
background: Record<
|
|
35
|
+
'white' | 'lightGrey' | 'transparent' | 'light' | 'hover' | 'hoverDark' | 'hoverDarkNonTransparent',
|
|
36
|
+
string
|
|
37
|
+
>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type FontStyleName =
|
|
41
|
+
| 'h1'
|
|
42
|
+
| 'h2'
|
|
43
|
+
| 'h3'
|
|
44
|
+
| 'h4'
|
|
45
|
+
| 'h5'
|
|
46
|
+
| 'h6'
|
|
47
|
+
| 'subtitle1'
|
|
48
|
+
| 'subtitle2'
|
|
49
|
+
| 'body1'
|
|
50
|
+
| 'body2'
|
|
51
|
+
| 'body3'
|
|
52
|
+
| 'highlight1'
|
|
53
|
+
| 'highlight2'
|
|
54
|
+
| 'highlight3'
|
|
55
|
+
| 'button1'
|
|
56
|
+
| 'button2'
|
|
57
|
+
| 'caption'
|
|
58
|
+
| 'overline'
|
|
59
|
+
| 'label'
|
|
60
|
+
| 'nav1'
|
|
61
|
+
| 'nav2';
|
|
62
|
+
|
|
63
|
+
type ThemeOptions = {
|
|
64
|
+
my: {
|
|
65
|
+
colors: ColorPalette;
|
|
66
|
+
spacing: Record<'xxxs' | 'xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', string>;
|
|
67
|
+
radius: Record<'sq' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'xxxl', string>;
|
|
68
|
+
font: Record<FontStyleName, string>;
|
|
69
|
+
};
|
|
70
|
+
} & MuiThemeOptions;
|
|
71
|
+
|
|
72
|
+
declare module '@emotion/react' {
|
|
73
|
+
// This declaration is necessary to merge the custom theme with the default theme
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/consistent-type-definitions
|
|
75
|
+
export interface Theme extends ThemeOptions {}
|
|
76
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { CompanyRole, GlobalRole, ResourceType, TenantRole } from '@smartbooks-ai/api-client';
|
|
4
|
+
|
|
5
|
+
import { useProfile } from '../security/ProfileContext';
|
|
6
|
+
|
|
7
|
+
type AuthorizationParams = {
|
|
8
|
+
companyRole?: CompanyRole;
|
|
9
|
+
tenantCode?: string;
|
|
10
|
+
companyCode?: string;
|
|
11
|
+
tenantRole?: TenantRole;
|
|
12
|
+
globalRole?: GlobalRole;
|
|
13
|
+
resourceType?: ResourceType;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type Output = (params: AuthorizationParams) => boolean | null;
|
|
17
|
+
|
|
18
|
+
export const useIsAuthorized = (): Output => {
|
|
19
|
+
const { profile } = useProfile();
|
|
20
|
+
|
|
21
|
+
return useCallback(
|
|
22
|
+
({ companyCode, companyRole, globalRole, tenantCode, tenantRole, resourceType }) =>
|
|
23
|
+
profile &&
|
|
24
|
+
(globalRole === undefined || profile.globalRoles.includes(globalRole)) &&
|
|
25
|
+
(tenantRole === undefined ||
|
|
26
|
+
!!profile.allowedTenants.find(({ code }) => code === tenantCode)?.roles.includes(tenantRole)) &&
|
|
27
|
+
(companyRole === undefined ||
|
|
28
|
+
!!profile.allowedCompanies.find(({ code }) => code === companyCode)?.roles.includes(companyRole)) &&
|
|
29
|
+
(resourceType === undefined ||
|
|
30
|
+
!profile.allowedCompanies
|
|
31
|
+
.find(({ code }) => code === companyCode)
|
|
32
|
+
?.resources?.find(({ resource }) => resource === resourceType)?.isRestricted),
|
|
33
|
+
[profile],
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
type Output = {
|
|
4
|
+
value: boolean;
|
|
5
|
+
switchOn: () => void;
|
|
6
|
+
switchOff: () => void;
|
|
7
|
+
toggle: () => void;
|
|
8
|
+
set: (value: boolean) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const useToggle = (defaultValue = false): Output => {
|
|
12
|
+
const [value, setValue] = useState(defaultValue);
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
value,
|
|
16
|
+
switchOn: useCallback(() => {
|
|
17
|
+
setValue(true);
|
|
18
|
+
}, []),
|
|
19
|
+
switchOff: useCallback(() => {
|
|
20
|
+
setValue(false);
|
|
21
|
+
}, []),
|
|
22
|
+
toggle: useCallback(() => {
|
|
23
|
+
setValue((previous) => !previous);
|
|
24
|
+
}, []),
|
|
25
|
+
set: setValue,
|
|
26
|
+
};
|
|
27
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { glob } from 'glob';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
describe('package isolation', () => {
|
|
7
|
+
it('should not reference files outside of src folder', async () => {
|
|
8
|
+
expect.assertions(1);
|
|
9
|
+
|
|
10
|
+
// Find all TypeScript files in src directory
|
|
11
|
+
const srcFiles = await glob('src/**/*.{ts,tsx}', {
|
|
12
|
+
cwd: path.resolve(__dirname, '..'),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const violations: string[] = [];
|
|
16
|
+
|
|
17
|
+
for (const file of srcFiles) {
|
|
18
|
+
const filePath = path.resolve(__dirname, '..', file);
|
|
19
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
20
|
+
|
|
21
|
+
// Extract import statements using regex
|
|
22
|
+
const importRegex = /^import\s+.*?from\s+['"]([^'"]+)['"]/gm;
|
|
23
|
+
let match;
|
|
24
|
+
|
|
25
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
26
|
+
const importPath = match[1];
|
|
27
|
+
|
|
28
|
+
// Check for relative imports that go outside the src folder
|
|
29
|
+
if (importPath.startsWith('../')) {
|
|
30
|
+
const resolvedPath = path.resolve(path.dirname(filePath), importPath);
|
|
31
|
+
const srcRoot = path.resolve(__dirname, '..', 'src');
|
|
32
|
+
|
|
33
|
+
// Only flag as violation if the resolved path is outside the src folder
|
|
34
|
+
if (!resolvedPath.startsWith(srcRoot + path.sep) && resolvedPath !== srcRoot) {
|
|
35
|
+
violations.push(`${file}: ${importPath}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Also check for dynamic imports that go outside the src folder
|
|
41
|
+
const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
42
|
+
while ((match = dynamicImportRegex.exec(content)) !== null) {
|
|
43
|
+
const importPath = match[1];
|
|
44
|
+
|
|
45
|
+
if (importPath.startsWith('../')) {
|
|
46
|
+
const resolvedPath = path.resolve(path.dirname(filePath), importPath);
|
|
47
|
+
const srcRoot = path.resolve(__dirname, '..', 'src');
|
|
48
|
+
|
|
49
|
+
if (!resolvedPath.startsWith(srcRoot + path.sep) && resolvedPath !== srcRoot) {
|
|
50
|
+
violations.push(`${file}: ${importPath} (dynamic import)`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(violations);
|
|
57
|
+
|
|
58
|
+
expect(violations).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { JSX, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
import { CompanyRole, GlobalRole, TenantRole } from '@smartbooks-ai/api-client';
|
|
4
|
+
|
|
5
|
+
import { State } from './state';
|
|
6
|
+
import { useAuthorizationState } from './useAuthorizationState';
|
|
7
|
+
|
|
8
|
+
import { ProfileState, useProfile } from '../../security/ProfileContext';
|
|
9
|
+
import { assertNever } from '../../utils/assertNever';
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
requireCompanyRole?: CompanyRole;
|
|
13
|
+
requireTenantRole?: TenantRole;
|
|
14
|
+
requireGlobalRole?: GlobalRole;
|
|
15
|
+
companyCode?: string;
|
|
16
|
+
tenantCode?: string;
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
errorElement?: JSX.Element;
|
|
19
|
+
loadingElement?: JSX.Element;
|
|
20
|
+
notLoggedInElement?: JSX.Element;
|
|
21
|
+
unauthorizedElement?: React.ReactNode;
|
|
22
|
+
shouldInitiateLogin?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const AuthorizedContent: React.FC<Props> = ({
|
|
26
|
+
children,
|
|
27
|
+
companyCode,
|
|
28
|
+
errorElement,
|
|
29
|
+
shouldInitiateLogin,
|
|
30
|
+
loadingElement,
|
|
31
|
+
notLoggedInElement,
|
|
32
|
+
requireCompanyRole,
|
|
33
|
+
requireGlobalRole,
|
|
34
|
+
requireTenantRole,
|
|
35
|
+
tenantCode,
|
|
36
|
+
unauthorizedElement,
|
|
37
|
+
}) => {
|
|
38
|
+
const { state, initiateLogin } = useProfile();
|
|
39
|
+
|
|
40
|
+
const authorizationState = useAuthorizationState({
|
|
41
|
+
companyCode,
|
|
42
|
+
companyRole: requireCompanyRole,
|
|
43
|
+
globalRole: requireGlobalRole,
|
|
44
|
+
tenantCode,
|
|
45
|
+
tenantRole: requireTenantRole,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (state === ProfileState.notLoggedIn && shouldInitiateLogin) {
|
|
50
|
+
initiateLogin();
|
|
51
|
+
}
|
|
52
|
+
}, [initiateLogin, shouldInitiateLogin, state]);
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (authorizationState === State.needsProfileLinking) {
|
|
56
|
+
window.location.href = '/link-identities'; // Cannot use navigate outside of a RouterContext
|
|
57
|
+
}
|
|
58
|
+
}, [authorizationState]);
|
|
59
|
+
|
|
60
|
+
switch (authorizationState) {
|
|
61
|
+
case State.loading:
|
|
62
|
+
case State.needsProfileLinking:
|
|
63
|
+
return loadingElement;
|
|
64
|
+
case State.authorized:
|
|
65
|
+
return children;
|
|
66
|
+
case State.notLoggedIn:
|
|
67
|
+
return notLoggedInElement;
|
|
68
|
+
case State.unauthorized:
|
|
69
|
+
return unauthorizedElement;
|
|
70
|
+
case State.error:
|
|
71
|
+
return errorElement;
|
|
72
|
+
default:
|
|
73
|
+
return assertNever(authorizationState);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { AuthorizedContent };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CompanyRole, GlobalRole, TenantRole } from '@smartbooks-ai/api-client';
|
|
2
|
+
|
|
3
|
+
import { State } from './state';
|
|
4
|
+
|
|
5
|
+
import { useIsAuthorized } from '../../hooks/useIsAuthorized';
|
|
6
|
+
import { ProfileState, useProfile } from '../../security/ProfileContext';
|
|
7
|
+
|
|
8
|
+
type Input = {
|
|
9
|
+
companyRole?: CompanyRole;
|
|
10
|
+
tenantCode?: string;
|
|
11
|
+
companyCode?: string;
|
|
12
|
+
tenantRole?: TenantRole;
|
|
13
|
+
globalRole?: GlobalRole;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const useAuthorizationState = ({
|
|
17
|
+
companyCode,
|
|
18
|
+
companyRole,
|
|
19
|
+
globalRole,
|
|
20
|
+
tenantCode,
|
|
21
|
+
tenantRole,
|
|
22
|
+
}: Input): State => {
|
|
23
|
+
const { profile, state } = useProfile();
|
|
24
|
+
|
|
25
|
+
const getIsAuthorized = useIsAuthorized();
|
|
26
|
+
|
|
27
|
+
if (profile) {
|
|
28
|
+
return getIsAuthorized({ companyCode, companyRole, globalRole, tenantCode, tenantRole })
|
|
29
|
+
? State.authorized
|
|
30
|
+
: State.unauthorized;
|
|
31
|
+
}
|
|
32
|
+
switch (state) {
|
|
33
|
+
case ProfileState.notLoggedIn:
|
|
34
|
+
return State.notLoggedIn;
|
|
35
|
+
case ProfileState.loading:
|
|
36
|
+
return State.loading;
|
|
37
|
+
case ProfileState.needsLinking:
|
|
38
|
+
return State.needsProfileLinking;
|
|
39
|
+
default:
|
|
40
|
+
return State.error;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import { ProfileState } from './types';
|
|
4
|
+
|
|
5
|
+
import { UserProfile } from '../UserProfile';
|
|
6
|
+
|
|
7
|
+
export type InitiateLoginArgs = {
|
|
8
|
+
forceSignUp?: boolean;
|
|
9
|
+
forceSignIn?: boolean;
|
|
10
|
+
returnTo?: string;
|
|
11
|
+
loginHint?: string;
|
|
12
|
+
connection?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type PrimaryProfileInformation = {
|
|
16
|
+
connection: string;
|
|
17
|
+
canUseLoginHint: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ProfileContextValue = {
|
|
21
|
+
profile: UserProfile | null;
|
|
22
|
+
state: ProfileState;
|
|
23
|
+
primaryProfileInformation?: PrimaryProfileInformation;
|
|
24
|
+
initiateLogin: (args?: InitiateLoginArgs) => void;
|
|
25
|
+
reload: () => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const unsetFn = () => {
|
|
29
|
+
throw new Error('Unable interact before initializing the profile provider');
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const ProfileContext = createContext<ProfileContextValue>({
|
|
33
|
+
profile: null,
|
|
34
|
+
state: ProfileState.loading,
|
|
35
|
+
initiateLogin: unsetFn,
|
|
36
|
+
reload: unsetFn,
|
|
37
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AvailableCompany, AvailableTenant, GlobalRole, Profile } from '@smartbooks-ai/api-client';
|
|
2
|
+
|
|
3
|
+
export class UserProfile {
|
|
4
|
+
displayName: string;
|
|
5
|
+
email: string;
|
|
6
|
+
allowedTenants: AvailableTenant[];
|
|
7
|
+
allowedCompanies: AvailableCompany[];
|
|
8
|
+
globalRoles: GlobalRole[];
|
|
9
|
+
userId: string;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
userId: string,
|
|
13
|
+
displayName: string,
|
|
14
|
+
email: string,
|
|
15
|
+
allowedTenants: AvailableTenant[],
|
|
16
|
+
globalRoles: GlobalRole[],
|
|
17
|
+
) {
|
|
18
|
+
this.userId = userId;
|
|
19
|
+
this.displayName = displayName;
|
|
20
|
+
this.email = email;
|
|
21
|
+
this.allowedTenants = allowedTenants;
|
|
22
|
+
this.globalRoles = globalRoles;
|
|
23
|
+
this.allowedCompanies = allowedTenants
|
|
24
|
+
.flatMap((t) => t.companies)
|
|
25
|
+
.sort((a, b) => a.description.localeCompare(b.description));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static create(
|
|
29
|
+
given_name: string | undefined,
|
|
30
|
+
family_name: string | undefined,
|
|
31
|
+
email_address: string | undefined,
|
|
32
|
+
profileResponse: Profile,
|
|
33
|
+
) {
|
|
34
|
+
const firstName = given_name;
|
|
35
|
+
const lastName = family_name;
|
|
36
|
+
const email = email_address || 'Email Unknown';
|
|
37
|
+
|
|
38
|
+
const displayName = firstName || lastName ? `${firstName} ${lastName}`.trim() : email;
|
|
39
|
+
|
|
40
|
+
return new UserProfile(
|
|
41
|
+
profileResponse.userId,
|
|
42
|
+
displayName,
|
|
43
|
+
email,
|
|
44
|
+
profileResponse.tenants,
|
|
45
|
+
profileResponse.globalRoles,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ColorPrimitives } from '../emotion';
|
|
2
|
+
|
|
3
|
+
export const colorPrimitives: ColorPrimitives = {
|
|
4
|
+
darkNavy: {
|
|
5
|
+
'50': '#E6E8EC',
|
|
6
|
+
'100': '#CBCCD9',
|
|
7
|
+
'200': '#B2B5C6',
|
|
8
|
+
'300': '#989BB2',
|
|
9
|
+
'400': '#7E82A0',
|
|
10
|
+
'500': '#65698B',
|
|
11
|
+
'600': '#4E5376',
|
|
12
|
+
'700': '#373B60',
|
|
13
|
+
'800': '#20244A',
|
|
14
|
+
'900': '#120A32',
|
|
15
|
+
'950': '#0E0828',
|
|
16
|
+
},
|
|
17
|
+
coolMint: {
|
|
18
|
+
'50': '#ECF7F7',
|
|
19
|
+
'100': '#DAEEEF',
|
|
20
|
+
'200': '#C7E6E7',
|
|
21
|
+
'300': '#B5DEDF',
|
|
22
|
+
'400': '#A2D6D7',
|
|
23
|
+
'500': '#8FCDCF',
|
|
24
|
+
'600': '#7DC5C7',
|
|
25
|
+
'700': '#6ABDBF',
|
|
26
|
+
'800': '#58B4B7',
|
|
27
|
+
'900': '#45ACAF',
|
|
28
|
+
'950': '#3E9B9E',
|
|
29
|
+
},
|
|
30
|
+
coral: {
|
|
31
|
+
'50': '#FFF2ED',
|
|
32
|
+
'100': '#FEE5DB',
|
|
33
|
+
'200': '#FED8C9',
|
|
34
|
+
'300': '#FDCBB7',
|
|
35
|
+
'400': '#FDBEA6',
|
|
36
|
+
'500': '#FCB194',
|
|
37
|
+
'600': '#FCA482',
|
|
38
|
+
'700': '#FB9770',
|
|
39
|
+
'800': '#FB8A5E',
|
|
40
|
+
'900': '#FA7D4C',
|
|
41
|
+
'950': '#E17144',
|
|
42
|
+
},
|
|
43
|
+
navy: {
|
|
44
|
+
'50': '#E8EDF9',
|
|
45
|
+
'100': '#D0DCF3',
|
|
46
|
+
'200': '#B9CAEC',
|
|
47
|
+
'300': '#A1B9E6',
|
|
48
|
+
'400': '#8AA7E0',
|
|
49
|
+
'500': '#7395DA',
|
|
50
|
+
'600': '#5B84D4',
|
|
51
|
+
'700': '#4472CD',
|
|
52
|
+
'800': '#2C61C7',
|
|
53
|
+
'900': '#154FC1',
|
|
54
|
+
'950': '#1347AE',
|
|
55
|
+
},
|
|
56
|
+
skyBlue: {
|
|
57
|
+
'50': '#ECF9FB',
|
|
58
|
+
'100': '#D9F2F8',
|
|
59
|
+
'200': '#C6ECF4',
|
|
60
|
+
'300': '#B3E5F1',
|
|
61
|
+
'400': '#A0DFED',
|
|
62
|
+
'500': '#8CD9E9',
|
|
63
|
+
'600': '#79D2E6',
|
|
64
|
+
'700': '#66CCE2',
|
|
65
|
+
'800': '#53C5DF',
|
|
66
|
+
'900': '#40BFDB',
|
|
67
|
+
'950': '#3AACC5',
|
|
68
|
+
},
|
|
69
|
+
grey: {
|
|
70
|
+
'50': '#FAFAFA',
|
|
71
|
+
'100': '#FAF9F9',
|
|
72
|
+
'200': '#F0EFEF',
|
|
73
|
+
'300': '#E1E0E0',
|
|
74
|
+
'400': '#C8C7C7',
|
|
75
|
+
'500': '#AFAEAE',
|
|
76
|
+
'600': '#969595',
|
|
77
|
+
'700': '#7D7D7D',
|
|
78
|
+
'800': '#4E4E4E',
|
|
79
|
+
'900': '#383838',
|
|
80
|
+
'950': '#222222',
|
|
81
|
+
},
|
|
82
|
+
common: {
|
|
83
|
+
black: '#000000',
|
|
84
|
+
white: '#ffffff',
|
|
85
|
+
},
|
|
86
|
+
warning: {
|
|
87
|
+
'100': '#FEF7F0',
|
|
88
|
+
'300': '#FCE6CF',
|
|
89
|
+
'600': '#F7B46E',
|
|
90
|
+
'900': '#DF770C',
|
|
91
|
+
'950': '#B25F0A',
|
|
92
|
+
},
|
|
93
|
+
error: {
|
|
94
|
+
'100': '#FDF2F1',
|
|
95
|
+
'300': '#F9D2D2',
|
|
96
|
+
'600': '#EE7877',
|
|
97
|
+
'900': '#D11C1A',
|
|
98
|
+
'950': '#A71615',
|
|
99
|
+
},
|
|
100
|
+
success: {
|
|
101
|
+
'100': '#F1F8F6',
|
|
102
|
+
'300': '#D9F2E8',
|
|
103
|
+
'600': '#88DDBC',
|
|
104
|
+
'900': '#26A675',
|
|
105
|
+
'950': '#1E855E',
|
|
106
|
+
},
|
|
107
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Theme } from '@emotion/react';
|
|
2
|
+
|
|
3
|
+
import { colorPrimitives } from './colorPrimitives';
|
|
4
|
+
|
|
5
|
+
export const colors: Theme['my']['colors'] = {
|
|
6
|
+
primitives: colorPrimitives,
|
|
7
|
+
text: {
|
|
8
|
+
main: colorPrimitives.darkNavy[800],
|
|
9
|
+
light: colorPrimitives.darkNavy[600],
|
|
10
|
+
lighter: colorPrimitives.darkNavy[400],
|
|
11
|
+
disabled: colorPrimitives.grey[600],
|
|
12
|
+
placeholder: colorPrimitives.grey[400],
|
|
13
|
+
white: colorPrimitives.common.white,
|
|
14
|
+
},
|
|
15
|
+
primary: {
|
|
16
|
+
main: colorPrimitives.darkNavy[900],
|
|
17
|
+
light: colorPrimitives.darkNavy[600],
|
|
18
|
+
dark: colorPrimitives.darkNavy[950],
|
|
19
|
+
contrast: colorPrimitives.common.white,
|
|
20
|
+
},
|
|
21
|
+
secondary: {
|
|
22
|
+
main: colorPrimitives.coolMint[900],
|
|
23
|
+
light: colorPrimitives.coolMint[600],
|
|
24
|
+
dark: colorPrimitives.coolMint[950],
|
|
25
|
+
contrast: colorPrimitives.common.white,
|
|
26
|
+
},
|
|
27
|
+
tertiary: {
|
|
28
|
+
main: colorPrimitives.coral[900],
|
|
29
|
+
light: colorPrimitives.coral[600],
|
|
30
|
+
dark: colorPrimitives.coral[950],
|
|
31
|
+
contrast: colorPrimitives.common.white,
|
|
32
|
+
},
|
|
33
|
+
quaternary: {
|
|
34
|
+
main: colorPrimitives.navy[900],
|
|
35
|
+
light: colorPrimitives.navy[600],
|
|
36
|
+
dark: colorPrimitives.navy[950],
|
|
37
|
+
contrast: colorPrimitives.common.white,
|
|
38
|
+
},
|
|
39
|
+
quinary: {
|
|
40
|
+
main: colorPrimitives.skyBlue[900],
|
|
41
|
+
light: colorPrimitives.skyBlue[600],
|
|
42
|
+
dark: colorPrimitives.skyBlue[950],
|
|
43
|
+
contrast: colorPrimitives.common.white,
|
|
44
|
+
},
|
|
45
|
+
neutral: {
|
|
46
|
+
main: colorPrimitives.grey[900],
|
|
47
|
+
light: colorPrimitives.grey[500],
|
|
48
|
+
dark: colorPrimitives.grey[950],
|
|
49
|
+
contrast: colorPrimitives.common.white,
|
|
50
|
+
},
|
|
51
|
+
warning: {
|
|
52
|
+
main: colorPrimitives.warning[900],
|
|
53
|
+
light: colorPrimitives.warning[600],
|
|
54
|
+
dark: colorPrimitives.warning[950],
|
|
55
|
+
contrast: colorPrimitives.common.white,
|
|
56
|
+
},
|
|
57
|
+
error: {
|
|
58
|
+
main: colorPrimitives.error[900],
|
|
59
|
+
light: colorPrimitives.error[600],
|
|
60
|
+
dark: colorPrimitives.error[950],
|
|
61
|
+
contrast: colorPrimitives.common.white,
|
|
62
|
+
},
|
|
63
|
+
success: {
|
|
64
|
+
main: colorPrimitives.success[900],
|
|
65
|
+
light: colorPrimitives.success[600],
|
|
66
|
+
dark: colorPrimitives.success[950],
|
|
67
|
+
contrast: colorPrimitives.common.white,
|
|
68
|
+
},
|
|
69
|
+
background: {
|
|
70
|
+
white: colorPrimitives.common.white,
|
|
71
|
+
lightGrey: colorPrimitives.grey[50],
|
|
72
|
+
transparent: 'transparent',
|
|
73
|
+
light: colorPrimitives.darkNavy[50],
|
|
74
|
+
hover: 'rgb(255 255 255 / 8%)',
|
|
75
|
+
hoverDark: 'rgb(0 0 0 / 8%)',
|
|
76
|
+
hoverDarkNonTransparent: '#ebebeb',
|
|
77
|
+
},
|
|
78
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Theme } from '@emotion/react';
|
|
2
|
+
|
|
3
|
+
export const FONT_FAMILY = 'Arial, sans-serif';
|
|
4
|
+
|
|
5
|
+
export const font: Theme['my']['font'] = {
|
|
6
|
+
h1: ` 600 1.5rem/2 ${FONT_FAMILY}`,
|
|
7
|
+
h2: ` 700 1.25rem/1.75 ${FONT_FAMILY}`,
|
|
8
|
+
h3: ` 700 1.125rem/1.5 ${FONT_FAMILY}`,
|
|
9
|
+
h4: ` 700 1rem/1.25 ${FONT_FAMILY}`,
|
|
10
|
+
h5: ` 700 0.875rem/1.25 ${FONT_FAMILY}`,
|
|
11
|
+
h6: ` 700 0.8125rem/1 ${FONT_FAMILY}`,
|
|
12
|
+
subtitle1: ` 500 1.25rem/1.75 ${FONT_FAMILY}`,
|
|
13
|
+
subtitle2: ` 500 1rem/1.5 ${FONT_FAMILY}`,
|
|
14
|
+
body1: ` 400 1rem/1.5 ${FONT_FAMILY}`,
|
|
15
|
+
body2: ` 400 0.875rem/1.25 ${FONT_FAMILY}`,
|
|
16
|
+
body3: ` 400 0.8125rem/1 ${FONT_FAMILY}`,
|
|
17
|
+
highlight1: `700 1rem/1.5 ${FONT_FAMILY}`,
|
|
18
|
+
highlight2: `700 0.875rem/1.25 ${FONT_FAMILY}`,
|
|
19
|
+
highlight3: `500 0.8125rem/1 ${FONT_FAMILY}`,
|
|
20
|
+
button1: ` 500 0.875rem/1.5 ${FONT_FAMILY}`,
|
|
21
|
+
button2: ` 500 0.875rem/1.25 ${FONT_FAMILY}`,
|
|
22
|
+
caption: ` 400 1rem/1.5 ${FONT_FAMILY}`,
|
|
23
|
+
overline: ` 500 1rem/1.5 ${FONT_FAMILY}`,
|
|
24
|
+
label: ` 500 0.875rem/1.25 ${FONT_FAMILY}`,
|
|
25
|
+
nav1: ` 400 0.8125rem/1.25 ${FONT_FAMILY}`,
|
|
26
|
+
nav2: ` 700 0.8125rem/1.25 ${FONT_FAMILY}`,
|
|
27
|
+
};
|