Revize a69da1e3
Přidáno uživatelem Václav Honzík před téměř 3 roky(ů)
frontend/package.json | ||
---|---|---|
9 | 9 |
"@faker-js/faker": "^6.0.0", |
10 | 10 |
"@mui/icons-material": "^5.5.1", |
11 | 11 |
"@mui/material": "^5.5.2", |
12 |
"@testing-library/jest-dom": "^5.14.1", |
|
13 |
"@testing-library/react": "^12.0.0", |
|
14 |
"@testing-library/user-event": "^13.2.1", |
|
15 |
"@types/jest": "^27.0.1", |
|
16 |
"@types/node": "^16.7.13", |
|
17 |
"@types/react": "^17.0.20", |
|
18 |
"@types/react-dom": "^17.0.9", |
|
19 | 12 |
"axios": "^0.26.0", |
20 | 13 |
"dotenv": "^16.0.0", |
21 | 14 |
"formik": "^2.2.9", |
... | ... | |
25 | 18 |
"react-router-dom": "^6.2.2", |
26 | 19 |
"react-scripts": "5.0.0", |
27 | 20 |
"redux": "^4.1.2", |
21 |
"redux-persist": "^6.0.0", |
|
28 | 22 |
"swagger-typescript-api": "^9.3.1", |
29 | 23 |
"ts-node": "^10.7.0", |
30 | 24 |
"typescript": "^4.4.2", |
... | ... | |
54 | 48 |
"last 1 firefox version", |
55 | 49 |
"last 1 safari version" |
56 | 50 |
] |
51 |
}, |
|
52 |
"devDependencies": { |
|
53 |
"@types/react-redux": "^7.1.23", |
|
54 |
"@types/redux-persist": "^4.3.1", |
|
55 |
"@testing-library/jest-dom": "^5.14.1", |
|
56 |
"@testing-library/react": "^12.0.0", |
|
57 |
"@testing-library/user-event": "^13.2.1", |
|
58 |
"@types/jest": "^27.0.1", |
|
59 |
"@types/node": "^16.7.13", |
|
60 |
"@types/react": "^17.0.20", |
|
61 |
"@types/react-dom": "^17.0.9" |
|
57 | 62 |
} |
58 | 63 |
} |
frontend/src/api/axiosInstance.ts | ||
---|---|---|
16 | 16 |
axiosInstance.interceptors.request.use(config => { |
17 | 17 |
config.headers = { |
18 | 18 |
...config.headers, |
19 |
Authorization: store.getState().auth.token
|
|
19 |
Authorization: store.getState().user.accessToken ?? ''
|
|
20 | 20 |
} |
21 | 21 |
}) |
22 | 22 |
|
23 | 23 |
axiosInstance.interceptors.response.use(response => response, error => { |
24 | 24 |
|
25 | 25 |
if (error?.response?.status === 401) { |
26 |
const refreshToken = store.getState().auth.refreshToken
|
|
26 |
const refreshToken = store.getState().user.refreshToken
|
|
27 | 27 |
// TODO send the refresh token correctly |
28 | 28 |
console.log('401 called they want their token refreshed'); |
29 | 29 |
} |
frontend/src/features/Auth/authReducer.ts | ||
---|---|---|
1 |
import { AnyAction } from 'redux' |
|
2 |
import { persist, load } from '../../utils/statePersistence' |
|
3 |
|
|
4 |
export interface UserInfo { |
|
5 |
accessToken: string | undefined // to send api requests |
|
6 |
refreshToken: string | undefined // to refresh the api key |
|
7 |
username: string // to display the username |
|
8 |
roles: string[] |
|
9 |
} |
|
10 |
|
|
11 |
export interface AuthState extends UserInfo { |
|
12 |
isAuthenticated: boolean // if this is false all other values should be ignored |
|
13 |
} |
|
14 |
|
|
15 |
const statePersistName = 'auth' |
|
16 |
|
|
17 |
// Initial state when the user first starts the application |
|
18 |
const initialState: AuthState = (load(statePersistName) as AuthState) || { |
|
19 |
isAuthenticated: false, |
|
20 |
username: '', |
|
21 |
roles: [], |
|
22 |
} |
|
23 |
|
|
24 |
// All possible actions |
|
25 |
export enum AuthStateActions { |
|
26 |
LOG_IN = 'LOG_IN', |
|
27 |
LOG_OUT = 'LOG_OUT', |
|
28 |
UPDATE_ACCESS_TOKEN = 'REFRESH_ACCESS_TOKEN', |
|
29 |
UPDATE_REFRESH_TOKEN = 'UPDATE_REFRESH_TOKEN', |
|
30 |
UPDATE_TOKENS = 'UPDATE_TOKENS', |
|
31 |
} |
|
32 |
|
|
33 |
// Actions |
|
34 |
const authReducer = (state: AuthState = initialState, action: AnyAction) => { |
|
35 |
switch (action.type) { |
|
36 |
case AuthStateActions.LOG_IN: |
|
37 |
return persist(statePersistName, { |
|
38 |
...action.payload, |
|
39 |
isAuthenticated: true, |
|
40 |
}) |
|
41 |
|
|
42 |
case AuthStateActions.LOG_OUT: |
|
43 |
return persist(statePersistName, initialState) |
|
44 |
|
|
45 |
case AuthStateActions.UPDATE_ACCESS_TOKEN: |
|
46 |
return persist(statePersistName, { |
|
47 |
...state, |
|
48 |
accessToken: action.payload, |
|
49 |
}) |
|
50 |
|
|
51 |
case AuthStateActions.UPDATE_REFRESH_TOKEN: |
|
52 |
return persist(statePersistName, { |
|
53 |
...state, |
|
54 |
refreshToken: action.payload, |
|
55 |
}) |
|
56 |
|
|
57 |
case AuthStateActions.UPDATE_TOKENS: |
|
58 |
return persist(statePersistName, { ...state, ...action.payload }) |
|
59 |
|
|
60 |
default: |
|
61 |
return state |
|
62 |
} |
|
63 |
} |
|
64 |
|
|
65 |
export default authReducer |
frontend/src/features/Auth/userReducer.ts | ||
---|---|---|
1 |
import { AnyAction } from 'redux' |
|
2 |
import persistReducer from 'redux-persist/es/persistReducer' |
|
3 |
import storage from 'redux-persist/lib/storage' |
|
4 |
|
|
5 |
export interface UserState { |
|
6 |
accessToken?: string |
|
7 |
refreshToken?: string |
|
8 |
username: string |
|
9 |
roles: string[] |
|
10 |
isLoggedIn: boolean |
|
11 |
} |
|
12 |
|
|
13 |
const initialState: UserState = { |
|
14 |
roles: [], |
|
15 |
isLoggedIn: false, |
|
16 |
username: '', |
|
17 |
} |
|
18 |
|
|
19 |
// All possible actions |
|
20 |
export enum AuthStateActions { |
|
21 |
LOG_IN = 'LOG_IN', |
|
22 |
LOG_OUT = 'LOG_OUT', |
|
23 |
UPDATE_ACCESS_TOKEN = 'REFRESH_ACCESS_TOKEN', |
|
24 |
UPDATE_REFRESH_TOKEN = 'UPDATE_REFRESH_TOKEN', |
|
25 |
UPDATE_TOKENS = 'UPDATE_TOKENS', |
|
26 |
} |
|
27 |
|
|
28 |
// Only needed if the state is to be persisted |
|
29 |
const persistConfig = { |
|
30 |
key: 'auth', |
|
31 |
storage |
|
32 |
} |
|
33 |
|
|
34 |
const _authReducer = ( |
|
35 |
state: UserState = initialState, |
|
36 |
action: AnyAction |
|
37 |
): UserState => { |
|
38 |
switch (action.type) { |
|
39 |
case AuthStateActions.LOG_IN: |
|
40 |
return { |
|
41 |
...action.payload, |
|
42 |
isAuthenticated: true, |
|
43 |
} |
|
44 |
case AuthStateActions.LOG_OUT: |
|
45 |
return initialState |
|
46 |
|
|
47 |
case AuthStateActions.UPDATE_ACCESS_TOKEN: |
|
48 |
return { |
|
49 |
...state, |
|
50 |
accessToken: action.payload, |
|
51 |
} |
|
52 |
|
|
53 |
case AuthStateActions.UPDATE_REFRESH_TOKEN: |
|
54 |
return { |
|
55 |
...state, |
|
56 |
refreshToken: action.payload, |
|
57 |
} |
|
58 |
|
|
59 |
case AuthStateActions.UPDATE_TOKENS: |
|
60 |
return { ...state, ...action.payload } |
|
61 |
|
|
62 |
default: |
|
63 |
return state |
|
64 |
} |
|
65 |
} |
|
66 |
|
|
67 |
const authReducer = persistReducer(persistConfig, _authReducer) |
|
68 |
|
|
69 |
export default authReducer |
frontend/src/features/Catalog/Catalog.tsx | ||
---|---|---|
8 | 8 |
Stack, |
9 | 9 |
TextField, |
10 | 10 |
} from '@mui/material' |
11 |
import { CatalogDto } from '../../swagger/data-contracts' |
|
11 |
import { CatalogItemDto } from '../../swagger/data-contracts'
|
|
12 | 12 |
import CatalogTable from './CatalogTable' |
13 | 13 |
import { faker } from '@faker-js/faker' |
14 | 14 |
import { useState } from 'react' |
... | ... | |
20 | 20 |
} |
21 | 21 |
|
22 | 22 |
// Creates a generic row for the table |
23 |
const createRow = (): CatalogDto => ({ |
|
23 |
const createRow = (): CatalogItemDto => ({
|
|
24 | 24 |
name: faker.commerce.product(), |
25 | 25 |
certainty: faker.random.number({ min: 1, max: 1000 }), |
26 | 26 |
longitude: faker.address.latitude() as unknown as number, |
... | ... | |
31 | 31 |
countries: [faker.address.country()], |
32 | 32 |
alternativeNames: [faker.commerce.productName()], |
33 | 33 |
}) |
34 |
const data: CatalogDto[] = Array(33) |
|
34 |
const data: CatalogItemDto[] = Array(33)
|
|
35 | 35 |
.fill({}) |
36 | 36 |
.map(() => createRow()) |
37 | 37 |
|
frontend/src/features/Catalog/CatalogTable.tsx | ||
---|---|---|
2 | 2 |
import { |
3 | 3 |
Box, |
4 | 4 |
Paper, |
5 |
Stack, |
|
6 | 5 |
Table, |
7 | 6 |
TableBody, |
8 | 7 |
TableCell, |
9 | 8 |
TableContainer, |
10 |
TableFooter, |
|
11 | 9 |
TableHead, |
12 | 10 |
TablePagination, |
13 | 11 |
TableRow, |
14 | 12 |
Typography, |
15 | 13 |
} from '@mui/material' |
16 |
import TablePaginationActions, {
|
|
14 |
import { |
|
17 | 15 |
TablePaginationActionsProps, |
18 | 16 |
} from '@mui/material/TablePagination/TablePaginationActions' |
19 | 17 |
import { FunctionComponent, useState } from 'react' |
... | ... | |
22 | 20 |
import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft' |
23 | 21 |
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight' |
24 | 22 |
import LastPageIcon from '@mui/icons-material/LastPage' |
25 |
import { CatalogDto } from '../../swagger/data-contracts' |
|
23 |
import { CatalogItemDto } from '../../swagger/data-contracts'
|
|
26 | 24 |
|
27 | 25 |
// Currently adapted from https://mui.com/components/tables/ |
28 | 26 |
|
... | ... | |
129 | 127 |
].sort((a, b) => (a.calories < b.calories ? -1 : 1)) |
130 | 128 |
|
131 | 129 |
export interface CatalogTableProps { |
132 |
data: CatalogDto[] |
|
130 |
data: CatalogItemDto[]
|
|
133 | 131 |
} |
134 | 132 |
|
135 | 133 |
const CatalogTable: FunctionComponent<CatalogTableProps> = ({ data }) => { |
frontend/src/features/redux/store.ts | ||
---|---|---|
1 |
// Store that holds the state of the application |
|
2 | 1 |
|
3 | 2 |
import { combineReducers, createStore } from 'redux' |
4 |
import authReducer from '../Auth/authReducer' |
|
3 |
import { persistStore } from 'redux-persist' |
|
4 |
import userReducer from '../Auth/userReducer' |
|
5 | 5 |
import themeReducer from '../Theme/themeReducer' |
6 | 6 |
|
7 |
// Store holds shared state in the application |
|
7 | 8 |
const store = createStore( |
8 |
combineReducers({ auth: authReducer, theme: themeReducer }),
|
|
9 |
combineReducers({ user: userReducer, theme: themeReducer }),
|
|
9 | 10 |
{} |
10 | 11 |
) |
11 | 12 |
|
12 | 13 |
export default store |
13 |
|
|
14 |
export const persistor = persistStore(store) |
|
14 | 15 |
export type AppStore = typeof store |
15 |
|
|
16 | 16 |
export type RootState = ReturnType<typeof store.getState> |
17 |
|
|
18 | 17 |
export type AppDispatch = typeof store.dispatch |
frontend/src/index.tsx | ||
---|---|---|
4 | 4 |
import App from './App' |
5 | 5 |
import reportWebVitals from './reportWebVitals' |
6 | 6 |
import { BrowserRouter } from 'react-router-dom' |
7 |
import store from './features/redux/store' |
|
7 |
import store, { persistor } from './features/redux/store'
|
|
8 | 8 |
import { Provider } from 'react-redux' |
9 | 9 |
import { injectStore } from './api/axiosInstance' |
10 |
import { PersistGate } from 'redux-persist/integration/react' |
|
10 | 11 |
|
11 | 12 |
// Injects store to the axios instance in ./api/axiosInstance |
12 | 13 |
injectStore(store) |
13 | 14 |
|
14 | 15 |
ReactDOM.render( |
15 | 16 |
<Provider store={store}> |
16 |
<React.StrictMode> |
|
17 |
<BrowserRouter> |
|
18 |
<App /> |
|
19 |
</BrowserRouter> |
|
20 |
</React.StrictMode> |
|
17 |
<PersistGate loading={null} persistor={persistor}> |
|
18 |
<React.StrictMode> |
|
19 |
<BrowserRouter> |
|
20 |
<App /> |
|
21 |
</BrowserRouter> |
|
22 |
</React.StrictMode> |
|
23 |
</PersistGate> |
|
21 | 24 |
</Provider>, |
22 | 25 |
document.getElementById('root') |
23 | 26 |
) |
frontend/src/swagger/Catalog.ts | ||
---|---|---|
9 | 9 |
* --------------------------------------------------------------- |
10 | 10 |
*/ |
11 | 11 |
|
12 |
import { CatalogDto } from "./data-contracts"; |
|
12 |
import { CatalogItemDto } from "./data-contracts";
|
|
13 | 13 |
import { ContentType, HttpClient, RequestParams } from "./http-client"; |
14 | 14 |
|
15 | 15 |
export class Catalog<SecurityDataType = unknown> extends HttpClient<SecurityDataType> { |
... | ... | |
20 | 20 |
* @name UpdateCatalogEntry |
21 | 21 |
* @request PUT:/catalog/{id} |
22 | 22 |
*/ |
23 |
updateCatalogEntry = (id: string, data: CatalogDto, params: RequestParams = {}) => |
|
23 |
updateCatalogEntry = (id: string, data: CatalogItemDto, params: RequestParams = {}) =>
|
|
24 | 24 |
this.request<void, any>({ |
25 | 25 |
path: `/catalog/${id}`, |
26 | 26 |
method: "PUT", |
... | ... | |
49 | 49 |
* @request GET:/catalog |
50 | 50 |
*/ |
51 | 51 |
getAllUsers1 = (params: RequestParams = {}) => |
52 |
this.request<CatalogDto[], any>({ |
|
52 |
this.request<CatalogItemDto[], any>({
|
|
53 | 53 |
path: `/catalog`, |
54 | 54 |
method: "GET", |
55 | 55 |
...params, |
... | ... | |
61 | 61 |
* @name AddCatalogEntry |
62 | 62 |
* @request POST:/catalog |
63 | 63 |
*/ |
64 |
addCatalogEntry = (data: CatalogDto, params: RequestParams = {}) => |
|
64 |
addCatalogEntry = (data: CatalogItemDto, params: RequestParams = {}) =>
|
|
65 | 65 |
this.request<void, any>({ |
66 | 66 |
path: `/catalog`, |
67 | 67 |
method: "POST", |
frontend/src/swagger/CatalogItems.ts | ||
---|---|---|
1 |
/* eslint-disable */ |
|
2 |
/* tslint:disable */ |
|
3 |
/* |
|
4 |
* --------------------------------------------------------------- |
|
5 |
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## |
|
6 |
* ## ## |
|
7 |
* ## AUTHOR: acacode ## |
|
8 |
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ## |
|
9 |
* --------------------------------------------------------------- |
|
10 |
*/ |
|
11 |
|
|
12 |
import { CatalogItemDto } from "./data-contracts"; |
|
13 |
import { ContentType, HttpClient, RequestParams } from "./http-client"; |
|
14 |
|
|
15 |
export class CatalogItems<SecurityDataType = unknown> extends HttpClient<SecurityDataType> { |
|
16 |
/** |
|
17 |
* No description |
|
18 |
* |
|
19 |
* @tags catalog-controller |
|
20 |
* @name UpdateCatalogItem |
|
21 |
* @summary updates catalog item with given ID |
|
22 |
* @request PUT:/catalog-items/{id} |
|
23 |
*/ |
|
24 |
updateCatalogItem = (id: string, data: CatalogItemDto, params: RequestParams = {}) => |
|
25 |
this.request<void, any>({ |
|
26 |
path: `/catalog-items/${id}`, |
|
27 |
method: "PUT", |
|
28 |
body: data, |
|
29 |
type: ContentType.Json, |
|
30 |
...params, |
|
31 |
}); |
|
32 |
/** |
|
33 |
* No description |
|
34 |
* |
|
35 |
* @tags catalog-controller |
|
36 |
* @name DeleteCatalogItem |
|
37 |
* @summary deletes catalog item with given ID |
|
38 |
* @request DELETE:/catalog-items/{id} |
|
39 |
*/ |
|
40 |
deleteCatalogItem = (id: string, params: RequestParams = {}) => |
|
41 |
this.request<void, any>({ |
|
42 |
path: `/catalog-items/${id}`, |
|
43 |
method: "DELETE", |
|
44 |
...params, |
|
45 |
}); |
|
46 |
/** |
|
47 |
* No description |
|
48 |
* |
|
49 |
* @tags catalog-controller |
|
50 |
* @name GetCatalog |
|
51 |
* @summary returns catalog items based on filter |
|
52 |
* @request GET:/catalog-items |
|
53 |
*/ |
|
54 |
getCatalog = (query?: { name?: string; country?: string; type?: string }, params: RequestParams = {}) => |
|
55 |
this.request<CatalogItemDto[], any>({ |
|
56 |
path: `/catalog-items`, |
|
57 |
method: "GET", |
|
58 |
query: query, |
|
59 |
...params, |
|
60 |
}); |
|
61 |
/** |
|
62 |
* No description |
|
63 |
* |
|
64 |
* @tags catalog-controller |
|
65 |
* @name AddCatalogItem |
|
66 |
* @summary creates new catalog item |
|
67 |
* @request POST:/catalog-items |
|
68 |
*/ |
|
69 |
addCatalogItem = (data: CatalogItemDto, params: RequestParams = {}) => |
|
70 |
this.request<void, any>({ |
|
71 |
path: `/catalog-items`, |
|
72 |
method: "POST", |
|
73 |
body: data, |
|
74 |
type: ContentType.Json, |
|
75 |
...params, |
|
76 |
}); |
|
77 |
} |
frontend/src/swagger/Users.ts | ||
---|---|---|
9 | 9 |
* --------------------------------------------------------------- |
10 | 10 |
*/ |
11 | 11 |
|
12 |
import { UserDto } from "./data-contracts"; |
|
13 |
import { HttpClient, RequestParams } from "./http-client"; |
|
12 |
import { PasswordDto, PermissionDto, UserDto } from "./data-contracts";
|
|
13 |
import { ContentType, HttpClient, RequestParams } from "./http-client";
|
|
14 | 14 |
|
15 | 15 |
export class Users<SecurityDataType = unknown> extends HttpClient<SecurityDataType> { |
16 | 16 |
/** |
... | ... | |
18 | 18 |
* |
19 | 19 |
* @tags user-controller |
20 | 20 |
* @name GetAllUsers |
21 |
* @summary returns all users |
|
21 | 22 |
* @request GET:/users |
22 | 23 |
*/ |
23 | 24 |
getAllUsers = (params: RequestParams = {}) => |
... | ... | |
26 | 27 |
method: "GET", |
27 | 28 |
...params, |
28 | 29 |
}); |
30 |
/** |
|
31 |
* No description |
|
32 |
* |
|
33 |
* @tags user-controller |
|
34 |
* @name RegisterNewUser |
|
35 |
* @summary registers new user |
|
36 |
* @request POST:/users |
|
37 |
*/ |
|
38 |
registerNewUser = (data: UserDto, params: RequestParams = {}) => |
|
39 |
this.request<void, any>({ |
|
40 |
path: `/users`, |
|
41 |
method: "POST", |
|
42 |
body: data, |
|
43 |
type: ContentType.Json, |
|
44 |
...params, |
|
45 |
}); |
|
46 |
/** |
|
47 |
* No description |
|
48 |
* |
|
49 |
* @tags user-controller |
|
50 |
* @name UpdatePermissions |
|
51 |
* @summary changes permissions to given user |
|
52 |
* @request PATCH:/users/{username}/permissions |
|
53 |
*/ |
|
54 |
updatePermissions = (username: string, data: PermissionDto, params: RequestParams = {}) => |
|
55 |
this.request<void, any>({ |
|
56 |
path: `/users/${username}/permissions`, |
|
57 |
method: "PATCH", |
|
58 |
body: data, |
|
59 |
type: ContentType.Json, |
|
60 |
...params, |
|
61 |
}); |
|
62 |
/** |
|
63 |
* No description |
|
64 |
* |
|
65 |
* @tags user-controller |
|
66 |
* @name ResetPassword |
|
67 |
* @summary changes password to given user |
|
68 |
* @request PATCH:/users/{username}/password |
|
69 |
*/ |
|
70 |
resetPassword = (username: string, data: PasswordDto, params: RequestParams = {}) => |
|
71 |
this.request<void, any>({ |
|
72 |
path: `/users/${username}/password`, |
|
73 |
method: "PATCH", |
|
74 |
body: data, |
|
75 |
type: ContentType.Json, |
|
76 |
...params, |
|
77 |
}); |
|
78 |
/** |
|
79 |
* No description |
|
80 |
* |
|
81 |
* @tags user-controller |
|
82 |
* @name ChangePassword |
|
83 |
* @summary changes password to logged-in user |
|
84 |
* @request PATCH:/users/password |
|
85 |
*/ |
|
86 |
changePassword = (query: { oldPassword: string }, data: PasswordDto, params: RequestParams = {}) => |
|
87 |
this.request<void, any>({ |
|
88 |
path: `/users/password`, |
|
89 |
method: "PATCH", |
|
90 |
query: query, |
|
91 |
body: data, |
|
92 |
type: ContentType.Json, |
|
93 |
...params, |
|
94 |
}); |
|
95 |
/** |
|
96 |
* No description |
|
97 |
* |
|
98 |
* @tags user-controller |
|
99 |
* @name RefreshToken |
|
100 |
* @summary returns a new access token and a refresh token to user |
|
101 |
* @request GET:/users/token |
|
102 |
*/ |
|
103 |
refreshToken = (params: RequestParams = {}) => |
|
104 |
this.request<void, any>({ |
|
105 |
path: `/users/token`, |
|
106 |
method: "GET", |
|
107 |
...params, |
|
108 |
}); |
|
109 |
/** |
|
110 |
* No description |
|
111 |
* |
|
112 |
* @tags user-controller |
|
113 |
* @name DeleteUser |
|
114 |
* @summary deletes user with given username |
|
115 |
* @request DELETE:/users/{username} |
|
116 |
*/ |
|
117 |
deleteUser = (username: string, params: RequestParams = {}) => |
|
118 |
this.request<void, any>({ |
|
119 |
path: `/users/${username}`, |
|
120 |
method: "DELETE", |
|
121 |
...params, |
|
122 |
}); |
|
29 | 123 |
} |
frontend/src/swagger/data-contracts.ts | ||
---|---|---|
9 | 9 |
* --------------------------------------------------------------- |
10 | 10 |
*/ |
11 | 11 |
|
12 |
export interface UserDto { |
|
13 |
name?: string; |
|
14 |
email: string; |
|
15 |
canRead?: boolean; |
|
16 |
canWrite?: boolean; |
|
17 |
canDelete?: boolean; |
|
18 |
} |
|
19 |
|
|
20 |
export interface CatalogDto { |
|
12 |
export interface CatalogItemDto { |
|
21 | 13 |
/** @format uuid */ |
22 | 14 |
id?: string; |
23 | 15 |
name?: string; |
... | ... | |
36 | 28 |
alternativeNames?: string[]; |
37 | 29 |
types?: string[]; |
38 | 30 |
} |
31 |
|
|
32 |
export interface PasswordDto { |
|
33 |
password: string; |
|
34 |
confirmationPassword: string; |
|
35 |
} |
|
36 |
|
|
37 |
export interface PermissionDto { |
|
38 |
canRead?: boolean; |
|
39 |
canWrite?: boolean; |
|
40 |
canDelete?: boolean; |
|
41 |
} |
|
42 |
|
|
43 |
export interface UserDto { |
|
44 |
name?: string; |
|
45 |
email: string; |
|
46 |
permissions?: PermissionDto; |
|
47 |
passwords: PasswordDto; |
|
48 |
} |
|
49 |
|
|
50 |
export interface TitlePage { |
|
51 |
title?: string; |
|
52 |
content?: string; |
|
53 |
} |
Také k dispozici: Unified diff
re #9367 - redux-persist for data persistence