@buerokratt-ria/menu 0.0.1
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/.eslintrc.json +18 -0
- package/CHANGELOG.md +7 -0
- package/MAKING_CHANGES.md +8 -0
- package/README.md +70 -0
- package/buerokratt-ria-menu-0.0.1.tgz +0 -0
- package/package.json +42 -0
- package/project.json +52 -0
- package/src/index.ts +3 -0
- package/src/menu/components/icons/icon/icon.tsx +32 -0
- package/src/menu/components/icons/icon.scss +17 -0
- package/src/menu/data/menu-structure.json +343 -0
- package/src/menu/index.tsx +153 -0
- package/src/menu/main-navigation.scss +119 -0
- package/tsconfig.base.json +21 -0
- package/tsconfig.json +14 -0
- package/tsconfig.spec.json +19 -0
- package/vite.config.ts +67 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
|
3
|
+
"ignorePatterns": ["!**/*"],
|
|
4
|
+
"overrides": [
|
|
5
|
+
{
|
|
6
|
+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
7
|
+
"rules": {}
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"files": ["*.ts", "*.tsx"],
|
|
11
|
+
"rules": {}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"files": ["*.js", "*.jsx"],
|
|
15
|
+
"rules": {}
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## Making Changes
|
|
2
|
+
|
|
3
|
+
#### If you want to make changes to export menu component:
|
|
4
|
+
* Make necessary changes
|
|
5
|
+
* Update version in file [package.json](package.json)
|
|
6
|
+
* Template: `major v.mid v.minor v`
|
|
7
|
+
* Add brief description to [changelog file](CHANGELOG.md)
|
|
8
|
+
* Proceed with deploying or testing package locally by information provided by [readme](README.md)
|
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
## MainNavigation component
|
|
2
|
+
|
|
3
|
+
### General information
|
|
4
|
+
|
|
5
|
+
Changelog file could be found here [link](CHANGELOG.md)
|
|
6
|
+
|
|
7
|
+
## Creating package
|
|
8
|
+
|
|
9
|
+
To create npm package for future usage:
|
|
10
|
+
* Navigate to the root directory of the package.
|
|
11
|
+
* Run `npm pack` command, to create package file.
|
|
12
|
+
* If you made updates to the package please relate to this [file](MAKING_CHANGES.md) before creating package
|
|
13
|
+
|
|
14
|
+
To publish created package:
|
|
15
|
+
* Run `npm publish --access public`
|
|
16
|
+
* Authorize in npm and package would be published
|
|
17
|
+
|
|
18
|
+
## Adding dependency from remote
|
|
19
|
+
- Since this package currently being deployed to @buerokratt-ria account therefore it would need to be related as @buerokratt-ria
|
|
20
|
+
- Add to `package.json` @buerokratt-ria/menu: followed by version, list of available version could be found [here](CHANGELOG.md)
|
|
21
|
+
|
|
22
|
+
## Adding dependency as local package
|
|
23
|
+
- When you build the package file, put it in `root` directory of the application
|
|
24
|
+
- Add to `package.json` @buerokratt-ria/header: file:name-of-the-generated-package
|
|
25
|
+
- If having import issues like `NOT FOUND` try adding to `vite.config.ts`
|
|
26
|
+
`resolve: {
|
|
27
|
+
alias: {
|
|
28
|
+
'@buerokratt-ria': `${path.resolve(__dirname, 'node_modules/@buerokratt-ria/menu/src')}`
|
|
29
|
+
},
|
|
30
|
+
}`
|
|
31
|
+
|
|
32
|
+
## Using package
|
|
33
|
+
* Importing component
|
|
34
|
+
* `import { MainNavigation } from '@buerokratt-ria/menu/src'` for Header and Menu
|
|
35
|
+
* If you want to use local package, put created package to the root of react app and add dependency like "@buerokratt-ria/header": "file:buerokratt-ria-menu-0.0.5.tgz" (use proper version)
|
|
36
|
+
### Using MainNavigation component
|
|
37
|
+
* In Layout component you need to provide fetching and caching the `menu.json` file, code snippet would be:
|
|
38
|
+
```
|
|
39
|
+
const CACHE_NAME = 'mainmenu-cache';
|
|
40
|
+
const [MainMenuItems, setMainMenuItems] = useState([])
|
|
41
|
+
const {data, isLoading, status} = useQuery({
|
|
42
|
+
queryKey: [import.meta.env.REACT_APP_MENU_URL + import.meta.env.REACT_APP_MENU_PATH],
|
|
43
|
+
onSuccess: (res: any) => {
|
|
44
|
+
try {
|
|
45
|
+
setMainMenuItems(res);
|
|
46
|
+
localStorage.setItem(CACHE_NAME, JSON.stringify(res));
|
|
47
|
+
} catch (e) {
|
|
48
|
+
console.log(e);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
onError: (error: any) => {
|
|
52
|
+
setMainMenuItems(getCache());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
function getCache(): any {
|
|
58
|
+
const cache = localStorage.getItem(CACHE_NAME) || '{}';
|
|
59
|
+
return JSON.parse(cache);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
* Then pass this MainMenu items to menu component `<MainNavigation serviceId={import.meta.env.REACT_APP_SERVICE_ID.split(',')} items={MainMenuItems}/>`
|
|
63
|
+
* If you want to use only local file provided by package then pass empty array instead `[]`
|
|
64
|
+
* REACT_APP_SERVICE_ID set of values seperated by comma resembling current module, examples of this value could
|
|
65
|
+
be found in following links
|
|
66
|
+
|
|
67
|
+
### Implemented examples:
|
|
68
|
+
* https://github.com/buerokratt/Training-Module
|
|
69
|
+
* https://github.com/buerokratt/Service-Module
|
|
70
|
+
* https://github.com/buerokratt/Analytics-Module
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@buerokratt-ria/menu",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Generic MainNavigation component that would be injected as dependency.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {},
|
|
7
|
+
"author": "ExiRain",
|
|
8
|
+
"license": "ISC",
|
|
9
|
+
"peerDependencies": {
|
|
10
|
+
"@radix-ui/react-accessible-icon": "^1.0.3",
|
|
11
|
+
"@radix-ui/react-dialog": "^1.0.4",
|
|
12
|
+
"@radix-ui/react-switch": "^1.0.3",
|
|
13
|
+
"@radix-ui/react-toast": "^1.1.4",
|
|
14
|
+
"@tanstack/react-query": "^4.32.1",
|
|
15
|
+
"clsx": "^1.2.1",
|
|
16
|
+
"i18next": "^23.2.3",
|
|
17
|
+
"i18next-browser-languagedetector": "^7.1.0",
|
|
18
|
+
"path": "^0.12.7",
|
|
19
|
+
"react": "^18.2.0",
|
|
20
|
+
"react-cookie": "^4.1.1",
|
|
21
|
+
"react-dom": "^18.2.0",
|
|
22
|
+
"react-hook-form": "^7.45.4",
|
|
23
|
+
"react-i18next": "^12.1.1",
|
|
24
|
+
"react-icons": "^4.10.1",
|
|
25
|
+
"react-idle-timer": "^5.7.2",
|
|
26
|
+
"react-router-dom": "^6.14.2",
|
|
27
|
+
"rxjs": "^7.8.1",
|
|
28
|
+
"tslib": "^2.3.0",
|
|
29
|
+
"vite-plugin-dts": "^3.5.2",
|
|
30
|
+
"vite-plugin-svgr": "^3.2.0",
|
|
31
|
+
"zustand": "^4.4.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"react": "^18.2.0",
|
|
35
|
+
"@types/react": "^18.2.21",
|
|
36
|
+
"@buerokratt-ria/styles": "^0.0.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/react": "^18.0.26",
|
|
40
|
+
"@types/react-dom": "^18.0.9"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/project.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "header",
|
|
3
|
+
"sourceRoot": "header/src",
|
|
4
|
+
"projectType": "library",
|
|
5
|
+
"tags": ["scope:header"],
|
|
6
|
+
"targets": {
|
|
7
|
+
"lint": {
|
|
8
|
+
"executor": "@nrwl/linter:eslint",
|
|
9
|
+
"outputs": ["{options.outputFile}"],
|
|
10
|
+
"options": {
|
|
11
|
+
"lintFilePatterns": ["header/**/*.{ts,tsx,js,jsx}"]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"build": {
|
|
15
|
+
"executor": "@nrwl/vite:build",
|
|
16
|
+
"outputs": ["{options.outputPath}"],
|
|
17
|
+
"defaultConfiguration": "production",
|
|
18
|
+
"options": {
|
|
19
|
+
"project": "./ng-package.json",
|
|
20
|
+
"outputPath": "dist/header"
|
|
21
|
+
},
|
|
22
|
+
"configurations": {
|
|
23
|
+
"production": {
|
|
24
|
+
"tsConfig": "./tsconfig.lib.prod.json",
|
|
25
|
+
"mode": "production"
|
|
26
|
+
},
|
|
27
|
+
"development": {
|
|
28
|
+
"tsConfig": "./tsconfig.lib.json",
|
|
29
|
+
"mode": "development"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"test": {
|
|
34
|
+
"executor": "@nrwl/vite:test",
|
|
35
|
+
"outputs": ["{projectRoot}/coverage"],
|
|
36
|
+
"options": {
|
|
37
|
+
"passWithNoTests": true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"version": {
|
|
41
|
+
"executor": "@jscutlery/semver:version",
|
|
42
|
+
"options": {
|
|
43
|
+
"preset": "angular",
|
|
44
|
+
"baseBranch": "main",
|
|
45
|
+
"push": true,
|
|
46
|
+
"noVerify": true,
|
|
47
|
+
"commitMessageFormat": "chore(${projectName}): release version ${version} [skip ci]",
|
|
48
|
+
"skipCommitTypes": ["chore", "ci", "docs", "style", "test", "format"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
CSSProperties,
|
|
3
|
+
forwardRef,
|
|
4
|
+
ReactNode,
|
|
5
|
+
StyleHTMLAttributes,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import * as AccessibleIcon from '@radix-ui/react-accessible-icon';
|
|
8
|
+
import clsx from 'clsx';
|
|
9
|
+
|
|
10
|
+
import '../icon.scss';
|
|
11
|
+
|
|
12
|
+
type IconProps = StyleHTMLAttributes<CSSProperties> & {
|
|
13
|
+
label?: string | null;
|
|
14
|
+
icon: ReactNode;
|
|
15
|
+
size?: 'small' | 'medium';
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const IconComponent = forwardRef<HTMLSpanElement, IconProps>(
|
|
19
|
+
({ label, icon, size = 'small', ...rest }, ref) => {
|
|
20
|
+
const iconClasses = clsx('icon', `icon--${size}`);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<AccessibleIcon.Root label={label ?? ''}>
|
|
24
|
+
<span ref={ref} className={iconClasses} style={rest.style}>
|
|
25
|
+
{icon}
|
|
26
|
+
</span>
|
|
27
|
+
</AccessibleIcon.Root>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export default IconComponent;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@import '@buerokratt-ria/styles/styles/tools/spacing';
|
|
2
|
+
|
|
3
|
+
.icon {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
|
|
8
|
+
&--small {
|
|
9
|
+
width: get-spacing(haapsalu);
|
|
10
|
+
height: get-spacing(haapsalu);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&--medium {
|
|
14
|
+
width: get-spacing(kuressaare);
|
|
15
|
+
height: get-spacing(kuressaare);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "conversations",
|
|
4
|
+
"label": {
|
|
5
|
+
"et": "Vestlused",
|
|
6
|
+
"en": "Conversations"
|
|
7
|
+
},
|
|
8
|
+
"path": "/chat",
|
|
9
|
+
"children": [
|
|
10
|
+
{
|
|
11
|
+
"label": {
|
|
12
|
+
"et": "Vastamata",
|
|
13
|
+
"en": "Unanswered"
|
|
14
|
+
},
|
|
15
|
+
"path": "/unanswered"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"label": {
|
|
19
|
+
"et": "Aktiivsed",
|
|
20
|
+
"en": "Active"
|
|
21
|
+
},
|
|
22
|
+
"path": "/active"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"label": {
|
|
26
|
+
"et": "Ajalugu",
|
|
27
|
+
"en": "History"
|
|
28
|
+
},
|
|
29
|
+
"path": "/history"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "training",
|
|
35
|
+
"label": {
|
|
36
|
+
"et": "Treening",
|
|
37
|
+
"en": "Training"
|
|
38
|
+
},
|
|
39
|
+
"path": "/training",
|
|
40
|
+
"children": [
|
|
41
|
+
{
|
|
42
|
+
"label": {
|
|
43
|
+
"et": "Treening",
|
|
44
|
+
"en": "Training"
|
|
45
|
+
},
|
|
46
|
+
"path": "/training",
|
|
47
|
+
"children": [
|
|
48
|
+
{
|
|
49
|
+
"label": {
|
|
50
|
+
"et": "Teemad",
|
|
51
|
+
"en": "Themes"
|
|
52
|
+
},
|
|
53
|
+
"path": "/training/intents"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"label": {
|
|
57
|
+
"et": "Avalikud teemad",
|
|
58
|
+
"en": "Public themes"
|
|
59
|
+
},
|
|
60
|
+
"path": "/training/common-intents"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"label": {
|
|
64
|
+
"et": "Teemade järeltreenimine",
|
|
65
|
+
"en": "Post training themes"
|
|
66
|
+
},
|
|
67
|
+
"path": "/training/intents-followup-training"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"label": {
|
|
71
|
+
"et": "Vastused",
|
|
72
|
+
"en": "Answers"
|
|
73
|
+
},
|
|
74
|
+
"path": "/training/responses"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"label": {
|
|
78
|
+
"et": "Kasutuslood",
|
|
79
|
+
"en": "User Stories"
|
|
80
|
+
},
|
|
81
|
+
"path": "/training/stories"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"label": {
|
|
85
|
+
"et": "Konfiguratsioon",
|
|
86
|
+
"en": "Configuration"
|
|
87
|
+
},
|
|
88
|
+
"path": "/training/configuration"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"label": {
|
|
92
|
+
"et": "Vormid",
|
|
93
|
+
"en": "Forms"
|
|
94
|
+
},
|
|
95
|
+
"path": "/training/forms"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"label": {
|
|
99
|
+
"et": "Mälukohad",
|
|
100
|
+
"en": "Slots"
|
|
101
|
+
},
|
|
102
|
+
"path": "/training/slots"
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"label": {
|
|
108
|
+
"et": "Ajaloolised vestlused",
|
|
109
|
+
"en": "Historical conversations"
|
|
110
|
+
},
|
|
111
|
+
"path": "/history",
|
|
112
|
+
"children": [
|
|
113
|
+
{
|
|
114
|
+
"label": {
|
|
115
|
+
"et": "Ajalugu",
|
|
116
|
+
"en": "History"
|
|
117
|
+
},
|
|
118
|
+
"path": "/history/history"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"label": {
|
|
122
|
+
"et": "Pöördumised",
|
|
123
|
+
"en": "Appeals"
|
|
124
|
+
},
|
|
125
|
+
"path": "/history/appeal"
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"label": {
|
|
131
|
+
"et": "Mudelipank ja analüütika",
|
|
132
|
+
"en": "Modelbank and analytics"
|
|
133
|
+
},
|
|
134
|
+
"path": "/analytics",
|
|
135
|
+
"children": [
|
|
136
|
+
{
|
|
137
|
+
"label": {
|
|
138
|
+
"et": "Teemade ülevaade",
|
|
139
|
+
"en": "Overview of topics"
|
|
140
|
+
},
|
|
141
|
+
"path": "/analytics/overview"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"label": {
|
|
145
|
+
"et": "Mudelite võrdlus",
|
|
146
|
+
"en": "Comparison of models"
|
|
147
|
+
},
|
|
148
|
+
"path": "/analytics/models"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"label": {
|
|
152
|
+
"et": "Testlood",
|
|
153
|
+
"en": "testTracks"
|
|
154
|
+
},
|
|
155
|
+
"path": "/analytics/testcases"
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"label": {
|
|
161
|
+
"et": "Treeni uus mudel",
|
|
162
|
+
"en": "Train new model"
|
|
163
|
+
},
|
|
164
|
+
"path": "/training/train-new-model"
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "analytics",
|
|
170
|
+
"label": {
|
|
171
|
+
"et": "Analüütika",
|
|
172
|
+
"en": "Analytics"
|
|
173
|
+
},
|
|
174
|
+
"path": "/analytics",
|
|
175
|
+
"children": [
|
|
176
|
+
{
|
|
177
|
+
"label": {
|
|
178
|
+
"et": "Ülevaade",
|
|
179
|
+
"en": "Overview"
|
|
180
|
+
},
|
|
181
|
+
"path": "/overview"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"label": {
|
|
185
|
+
"et": "Vestlused",
|
|
186
|
+
"en": "Chats"
|
|
187
|
+
},
|
|
188
|
+
"path": "/chats"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"label": {
|
|
192
|
+
"et": "Bürokratt",
|
|
193
|
+
"en": "Burokratt"
|
|
194
|
+
},
|
|
195
|
+
"path": "/burokratt"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"label": {
|
|
199
|
+
"et": "Tagasiside",
|
|
200
|
+
"en": "Feedback"
|
|
201
|
+
},
|
|
202
|
+
"path": "/feedback"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"label": {
|
|
206
|
+
"et": "Nõustajad",
|
|
207
|
+
"en": "Advisors"
|
|
208
|
+
},
|
|
209
|
+
"path": "/advisors"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"label": {
|
|
213
|
+
"et": "Avaandmed",
|
|
214
|
+
"en": "Reports"
|
|
215
|
+
},
|
|
216
|
+
"path": "/reports"
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"id": "services",
|
|
222
|
+
"label": {
|
|
223
|
+
"et": "Teenused",
|
|
224
|
+
"en": "Services"
|
|
225
|
+
},
|
|
226
|
+
"path": "/services",
|
|
227
|
+
"children": [
|
|
228
|
+
{
|
|
229
|
+
"label": {
|
|
230
|
+
"et": "Ülevaade",
|
|
231
|
+
"en": "Overview"
|
|
232
|
+
},
|
|
233
|
+
"path": "/overview"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"label": {
|
|
237
|
+
"et": "Uus teenus",
|
|
238
|
+
"en": "New Service"
|
|
239
|
+
},
|
|
240
|
+
"path": "/newService"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"label": {
|
|
244
|
+
"et": "Teemade järeltreenimine",
|
|
245
|
+
"en": "Followup Training"
|
|
246
|
+
},
|
|
247
|
+
"path": "/followupTraining"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"label": {
|
|
251
|
+
"et": "Probleemsed teenused",
|
|
252
|
+
"en": "Faulty Services"
|
|
253
|
+
},
|
|
254
|
+
"path": "/faultyServices"
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"id": "settings",
|
|
260
|
+
"label": {
|
|
261
|
+
"et": "Haldus",
|
|
262
|
+
"en": "Administration"
|
|
263
|
+
},
|
|
264
|
+
"path": "/settings",
|
|
265
|
+
"children": [
|
|
266
|
+
{
|
|
267
|
+
"label": {
|
|
268
|
+
"et": "Kasutajad",
|
|
269
|
+
"en": "Users"
|
|
270
|
+
},
|
|
271
|
+
"path": "/users"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"label": {
|
|
275
|
+
"et": "Vestlusbot",
|
|
276
|
+
"en": "Chatbot"
|
|
277
|
+
},
|
|
278
|
+
"path": "/chatbot",
|
|
279
|
+
"children": [
|
|
280
|
+
{
|
|
281
|
+
"label": {
|
|
282
|
+
"et": "Seaded",
|
|
283
|
+
"en": "Settings"
|
|
284
|
+
},
|
|
285
|
+
"path": "/chatbot/settings"
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"label": {
|
|
289
|
+
"et": "Tervitussõnum",
|
|
290
|
+
"en": "Welcome message"
|
|
291
|
+
},
|
|
292
|
+
"path": "/chatbot/welcome-message"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"label": {
|
|
296
|
+
"et": "Välimus ja käitumine",
|
|
297
|
+
"en": "Appearance and behavior"
|
|
298
|
+
},
|
|
299
|
+
"path": "/chatbot/appearance"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"label": {
|
|
303
|
+
"et": "Erakorralised teated",
|
|
304
|
+
"en": "Emergency notices"
|
|
305
|
+
},
|
|
306
|
+
"path": "/chatbot/emergency-notices"
|
|
307
|
+
}
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"label": {
|
|
312
|
+
"et": "Asutuse tööaeg",
|
|
313
|
+
"en": "Office opening hours"
|
|
314
|
+
},
|
|
315
|
+
"path": "/working-time"
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"label": {
|
|
319
|
+
"et": "Sessiooni pikkus",
|
|
320
|
+
"en": "Session length"
|
|
321
|
+
},
|
|
322
|
+
"path": "/session-length"
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"id": "monitoring",
|
|
328
|
+
"label": {
|
|
329
|
+
"et": "Seire",
|
|
330
|
+
"en": "Monitoring"
|
|
331
|
+
},
|
|
332
|
+
"path": "/monitoring",
|
|
333
|
+
"children": [
|
|
334
|
+
{
|
|
335
|
+
"label": {
|
|
336
|
+
"et": "Aktiivaeg",
|
|
337
|
+
"en": "Working hours"
|
|
338
|
+
},
|
|
339
|
+
"path": "/uptime"
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
]
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import React, {FC, MouseEvent, useEffect, useState} from 'react';
|
|
2
|
+
|
|
3
|
+
import {NavLink, useLocation} from 'react-router-dom';
|
|
4
|
+
import {MdClose, MdKeyboardArrowDown, MdMiscellaneousServices} from 'react-icons/md';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
import { MdOutlineForum, MdOutlineAdb, MdOutlineEqualizer, MdSettings, MdOutlineMonitorWeight } from 'react-icons/md';
|
|
7
|
+
import Icon from './components/icons/icon/icon';
|
|
8
|
+
import './main-navigation.scss';
|
|
9
|
+
import { useQuery } from '@tanstack/react-query';
|
|
10
|
+
import {useTranslation} from "react-i18next";
|
|
11
|
+
import menuStructure from './data/menu-structure.json';
|
|
12
|
+
|
|
13
|
+
interface MenuItem {
|
|
14
|
+
id?: string;
|
|
15
|
+
label: TranslatedLabel;
|
|
16
|
+
path?: string;
|
|
17
|
+
target?: '_blank' | '_self';
|
|
18
|
+
children?: MenuItem[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface TranslatedLabel {
|
|
22
|
+
[lang: string] : string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MainNavigation: FC<{items: MenuItem[], serviceId: string[]}> = ( {items, serviceId}) => {
|
|
26
|
+
if(!items.isArray || items.length === 0) {
|
|
27
|
+
items = menuStructure;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { t, i18n } = useTranslation();
|
|
31
|
+
const currentlySelectedLanguage = i18n.language;
|
|
32
|
+
const [menuItems, setMenuItems] = useState<MenuItem[]>([]);
|
|
33
|
+
const menuData = [
|
|
34
|
+
{
|
|
35
|
+
id: 'conversations',
|
|
36
|
+
icon: <MdOutlineForum />,
|
|
37
|
+
url: import.meta.env.REACT_APP_CONVERSATIONS_BASE_URL,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'training',
|
|
41
|
+
icon: <MdOutlineAdb />,
|
|
42
|
+
url: import.meta.env.REACT_APP_TRAINING_BASE_URL,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'analytics',
|
|
46
|
+
icon: <MdOutlineEqualizer />,
|
|
47
|
+
url: import.meta.env.REACT_APP_ANALYTICS_BASE_URL,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "services",
|
|
51
|
+
icon: <MdMiscellaneousServices />,
|
|
52
|
+
url: import.meta.env.REACT_APP_SERVICES_BASE_URL,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'settings',
|
|
56
|
+
icon: <MdSettings />,
|
|
57
|
+
url: import.meta.env.REACT_APP_SETTINGS_BASE_URL,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'monitoring',
|
|
61
|
+
icon: <MdOutlineMonitorWeight />,
|
|
62
|
+
url: import.meta.env.REACT_APP_MONITORING_BASE_URL,
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
let activeMenuId;
|
|
67
|
+
|
|
68
|
+
const { data } = useQuery({
|
|
69
|
+
queryKey: ['cs-get-user-role', 'prod'],
|
|
70
|
+
onSuccess: (res: any) => {
|
|
71
|
+
const filteredItems = items.filter((item) => {
|
|
72
|
+
const role = res.data.get_user[0].authorities[0]
|
|
73
|
+
switch (role) {
|
|
74
|
+
case 'ROLE_ADMINISTRATOR': return item.id
|
|
75
|
+
case 'ROLE_SERVICE_MANAGER': return item.id != 'settings' && item.id != 'training'
|
|
76
|
+
case 'ROLE_CUSTOMER_SUPPORT_AGENT': return item.id != 'settings' && item.id != 'analytics'
|
|
77
|
+
case 'ROLE_CHATBOT_TRAINER': return item.id != 'settings' && item.id != 'conversations'
|
|
78
|
+
case 'ROLE_ANALYST': return item.id == 'analytics'
|
|
79
|
+
case 'ROLE_UNAUTHENTICATED': return
|
|
80
|
+
default: return
|
|
81
|
+
}
|
|
82
|
+
}) ?? []
|
|
83
|
+
setMenuItems(filteredItems)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const location = useLocation();
|
|
89
|
+
const [navCollapsed, setNavCollapsed] = useState(false);
|
|
90
|
+
|
|
91
|
+
const handleNavToggle = (event: MouseEvent) => {
|
|
92
|
+
const isExpanded = event.currentTarget.getAttribute('aria-expanded') === 'true';
|
|
93
|
+
event.currentTarget.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const renderMenuTree = (menuItems: MenuItem[]) => {
|
|
97
|
+
return menuItems.map((menuItem) => (
|
|
98
|
+
<li key={menuItem.label[currentlySelectedLanguage]}>
|
|
99
|
+
{!!menuItem.children ? (
|
|
100
|
+
<>
|
|
101
|
+
<button
|
|
102
|
+
className={clsx('nav__toggle', { 'nav__toggle--icon': !!menuItem.id })}
|
|
103
|
+
aria-expanded={menuItem.path && (isSameRoot(menuItem)) ? 'true' : 'false'}
|
|
104
|
+
onClick={handleNavToggle}
|
|
105
|
+
>
|
|
106
|
+
{ menuItem.id && (
|
|
107
|
+
<Icon icon={menuData.find(dataItem => dataItem.id === menuItem.id)?.icon} />
|
|
108
|
+
)}
|
|
109
|
+
|
|
110
|
+
<span>{menuItem.label[currentlySelectedLanguage]}</span>
|
|
111
|
+
<Icon icon={<MdKeyboardArrowDown />} />
|
|
112
|
+
</button>
|
|
113
|
+
<ul className='nav__submenu'>
|
|
114
|
+
{renderMenuTree(menuItem.children.map((item) => ({id: menuItem.id, ...item})))}
|
|
115
|
+
</ul>
|
|
116
|
+
</>
|
|
117
|
+
) : (
|
|
118
|
+
|
|
119
|
+
(serviceId.includes(menuItem.id)) ?
|
|
120
|
+
<NavLink to={menuItem.path || '#'}>{menuItem.label[currentlySelectedLanguage] }</NavLink> :
|
|
121
|
+
<a href={menuData.find(dataItem => dataItem.id === menuItem.id)?.url + menuItem.path}>{menuItem.label[currentlySelectedLanguage]}</a>
|
|
122
|
+
|
|
123
|
+
)}
|
|
124
|
+
</li>),
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const base = window.location.pathname.split("/")[1];
|
|
129
|
+
const currentService = base === 'chat' ? serviceId : [base];
|
|
130
|
+
const isSameRoot = (menuItem) => {
|
|
131
|
+
let result = false;
|
|
132
|
+
if(currentService.includes(menuItem.id)){
|
|
133
|
+
result = menuItem.children.some((item: MenuItem) => item.path?.includes("/" + window.location.pathname.split("/")[2]));
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!menuItems) return null;
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<nav className={clsx('nav', { 'nav--collapsed': navCollapsed })}>
|
|
142
|
+
<button className='nav__menu-toggle' onClick={() => setNavCollapsed(!navCollapsed)}>
|
|
143
|
+
<Icon icon={<MdClose />} />
|
|
144
|
+
{t('mainMenu.closeMenu')}
|
|
145
|
+
</button>
|
|
146
|
+
<ul className='nav__menu'>
|
|
147
|
+
{renderMenuTree(menuItems)}
|
|
148
|
+
</ul>
|
|
149
|
+
</nav>
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export default MainNavigation;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
@import '@buerokratt-ria/styles/styles/tools/spacing';
|
|
2
|
+
@import '@buerokratt-ria/styles/styles/tools/color';
|
|
3
|
+
@import '@buerokratt-ria/styles/styles/settings/variables/typography';
|
|
4
|
+
|
|
5
|
+
.nav {
|
|
6
|
+
$self: &;
|
|
7
|
+
width: 208px;
|
|
8
|
+
background-color: get-color(sapphire-blue-10);
|
|
9
|
+
overflow: auto;
|
|
10
|
+
scrollbar-width: none;
|
|
11
|
+
transition: width .1s ease-out;
|
|
12
|
+
|
|
13
|
+
&::-webkit-scrollbar {
|
|
14
|
+
display: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
li, a, .nav__toggle, .nav__menu-toggle {
|
|
18
|
+
font-size: 14px;
|
|
19
|
+
line-height: 1.5;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
&__menu-toggle {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
|
|
26
|
+
&:hover {
|
|
27
|
+
background-color: get-color(sapphire-blue-8);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&:active {
|
|
31
|
+
background-color: get-color(sapphire-blue-7);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
a, .nav__toggle {
|
|
36
|
+
width: 100%;
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: get-spacing(paldiski);
|
|
40
|
+
color: get-color(black-coral-0);
|
|
41
|
+
padding: 14px 8px 14px 32px;
|
|
42
|
+
box-shadow: inset 0 -1px 0 get-color(sapphire-blue-14);
|
|
43
|
+
|
|
44
|
+
span:not(.icon) {
|
|
45
|
+
flex: 1;
|
|
46
|
+
display: block;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&:hover {
|
|
50
|
+
background-color: get-color(sapphire-blue-8);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&:active {
|
|
54
|
+
background-color: get-color(sapphire-blue-7);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&.active {
|
|
58
|
+
font-weight: 700;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&:focus {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&__toggle {
|
|
66
|
+
&[aria-expanded=true] {
|
|
67
|
+
font-weight: 700;
|
|
68
|
+
|
|
69
|
+
.icon {
|
|
70
|
+
transform: rotate(180deg);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
+ ul {
|
|
74
|
+
display: block;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&.nav__toggle--icon {
|
|
79
|
+
padding-left: 8px;
|
|
80
|
+
|
|
81
|
+
.icon:first-child {
|
|
82
|
+
transform: none;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
&__toggle-icon {
|
|
88
|
+
margin-left: auto;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&__menu-toggle {
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
gap: get-spacing(paldiski);
|
|
95
|
+
width: 100%;
|
|
96
|
+
color: get-color(white);
|
|
97
|
+
padding: 14px 8px;
|
|
98
|
+
box-shadow: inset 0 -1px 0 get-color(sapphire-blue-14);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
&__submenu {
|
|
102
|
+
display: none;
|
|
103
|
+
|
|
104
|
+
a, .nav__toggle {
|
|
105
|
+
background-color: get-color(sapphire-blue-14);
|
|
106
|
+
box-shadow: inset 0 -1px 0 get-color(sapphire-blue-17);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#{$self} {
|
|
110
|
+
&__submenu {
|
|
111
|
+
a {
|
|
112
|
+
background-color: get-color(sapphire-blue-17);
|
|
113
|
+
box-shadow: inset 0 -1px 0 get-color(black);
|
|
114
|
+
padding: 14px 48px 14px 40px;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compileOnSave": false,
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": ".",
|
|
5
|
+
"sourceMap": true,
|
|
6
|
+
"declaration": false,
|
|
7
|
+
"moduleResolution": "node",
|
|
8
|
+
"emitDecoratorMetadata": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"experimentalDecorators": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"importHelpers": true,
|
|
13
|
+
"target": "es2015",
|
|
14
|
+
"module": "esnext",
|
|
15
|
+
"lib": ["es2017", "dom", "dom.Iterable"],
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"skipDefaultLibCheck": true,
|
|
18
|
+
"baseUrl": ".",
|
|
19
|
+
},
|
|
20
|
+
"exclude": ["node_modules", "tmp"]
|
|
21
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"esModuleInterop": true,
|
|
4
|
+
"resolveJsonModule": true,
|
|
5
|
+
"jsx": "react-jsx",
|
|
6
|
+
"allowJs": false,
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"types": ["vite/client"]
|
|
10
|
+
},
|
|
11
|
+
"files": [],
|
|
12
|
+
"include": [],
|
|
13
|
+
"extends": "./tsconfig.base.json"
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"types": ["vitest/globals", "node"]
|
|
6
|
+
},
|
|
7
|
+
"include": [
|
|
8
|
+
"vite.config.ts",
|
|
9
|
+
"src/**/*.test.ts",
|
|
10
|
+
"src/**/*.spec.ts",
|
|
11
|
+
"src/**/*.test.tsx",
|
|
12
|
+
"src/**/*.spec.tsx",
|
|
13
|
+
"src/**/*.test.js",
|
|
14
|
+
"src/**/*.spec.js",
|
|
15
|
+
"src/**/*.test.jsx",
|
|
16
|
+
"src/**/*.spec.jsx",
|
|
17
|
+
"src/**/*.d.ts"
|
|
18
|
+
]
|
|
19
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/// <reference types="vitest" />
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
import react from '@vitejs/plugin-react';
|
|
4
|
+
import tsconfigPaths from 'vite-tsconfig-paths';
|
|
5
|
+
import dts from 'vite-plugin-dts';
|
|
6
|
+
import { join, path } from 'path';
|
|
7
|
+
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
plugins: [
|
|
10
|
+
dts({
|
|
11
|
+
tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
|
|
12
|
+
// Faster builds by skipping tests. Set this to false to enable type checking.
|
|
13
|
+
skipDiagnostics: true,
|
|
14
|
+
}),
|
|
15
|
+
react(),
|
|
16
|
+
tsconfigPaths({
|
|
17
|
+
root: './',
|
|
18
|
+
}),
|
|
19
|
+
],
|
|
20
|
+
resolve: {
|
|
21
|
+
alias: {
|
|
22
|
+
'@': `${path.resolve(__dirname, './src')}`,
|
|
23
|
+
'~@fontsource': path.resolve(__dirname, 'node_modules/@fontsource'),
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
css: {
|
|
27
|
+
preprocessorOptions: {
|
|
28
|
+
scss: {
|
|
29
|
+
additionalData: `@import "@/styles/main.scss";`,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// Configuration for building your library.
|
|
35
|
+
// See: https://vitejs.dev/guide/build.html#library-mode
|
|
36
|
+
build: {
|
|
37
|
+
lib: {
|
|
38
|
+
// Could also be a dictionary or array of multiple entry points.
|
|
39
|
+
entry: 'src/index.ts',
|
|
40
|
+
name: 'header',
|
|
41
|
+
fileName: 'index',
|
|
42
|
+
// Change this to the formats you want to support.
|
|
43
|
+
// Don't forgot to update your package.json as well.
|
|
44
|
+
formats: ['es', 'cjs'],
|
|
45
|
+
},
|
|
46
|
+
rollupOptions: {
|
|
47
|
+
// External packages that should not be bundled into your library.
|
|
48
|
+
// external: ['react','react-dom', 'react/jsx-runtime'],
|
|
49
|
+
output: {
|
|
50
|
+
globals: {
|
|
51
|
+
react: 'React',
|
|
52
|
+
'react-dom': 'ReactDOM',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
test: {
|
|
60
|
+
globals: true,
|
|
61
|
+
cache: {
|
|
62
|
+
dir: '../../node_modules/.vitest',
|
|
63
|
+
},
|
|
64
|
+
environment: 'jsdom',
|
|
65
|
+
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
|
66
|
+
},
|
|
67
|
+
});
|