@nside/wefa 0.3.0 → 0.4.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/README.md +46 -3
- package/dist/LegalConsent-9nOroDoA.cjs +1 -0
- package/dist/LegalConsent-CrPVZOxx.js +151 -0
- package/dist/LegalDocument-CVJiGmPJ.cjs +109 -0
- package/dist/{LegalDocument-BhoEpJ2O.js → LegalDocument-DwVhwjIf.js} +236 -215
- package/dist/{LoginView-kH440cCh.js → LoginView-DUPa_PsC.js} +3 -3
- package/dist/{LoginView-IIkXXw3R.cjs → LoginView-Dihs8n_X.cjs} +1 -1
- package/dist/{LogoutView-DGqh4bP7.js → LogoutView-DAh7MrFi.js} +3 -3
- package/dist/{LogoutView-B90MA-_Q.cjs → LogoutView-Fl3nfeJ0.cjs} +1 -1
- package/dist/{apiClient-DJdAL3tN.cjs → apiClient-BUS5ZclN.cjs} +1 -1
- package/dist/{apiClient-D-kcx_S1.js → apiClient-BbJl566D.js} +1 -1
- package/dist/axios-CZvsFspN.js +1887 -0
- package/dist/axios-DMqeKDaq.cjs +6 -0
- package/dist/containers.cjs +590 -5
- package/dist/containers.d.ts +39 -0
- package/dist/containers.js +3803 -977
- package/dist/{index-Coos428-.js → index--_rUTrqU.js} +308 -282
- package/dist/{index-B4vneBZh.cjs → index-B4oFnh1T.cjs} +6 -6
- package/dist/index-BHSxFTgZ.js +49 -0
- package/dist/{index-BSfhC_wu.cjs → index-BaA_oL1s.cjs} +1 -1
- package/dist/{index-CJmnkrIs.cjs → index-Becfy0pF.cjs} +1 -1
- package/dist/{index-Dj5oTSEE.js → index-C09d0pI4.js} +15 -15
- package/dist/{index-BXrnPbjr.cjs → index-CbQWytWd.cjs} +4 -4
- package/dist/{index-DmVIgb18.js → index-CgAb-gZi.js} +11 -11
- package/dist/{index-B53YL3vD.cjs → index-DFOQKDki.cjs} +2 -2
- package/dist/index-DFSkcsx-.cjs +943 -0
- package/dist/{index-CEz0St1t.js → index-DQFN7qxo.js} +7 -7
- package/dist/index-DRozw3P8.js +167 -0
- package/dist/index-DfCQoHSf.cjs +146 -0
- package/dist/index-DkuJMEY1.js +6731 -0
- package/dist/{index-bRjoenrr.js → index-Dv6jyKbT.js} +12 -12
- package/dist/{index-Bl3JVLei.cjs → index-EDm9-cRY.cjs} +1 -1
- package/dist/index-IGN7_cyg.cjs +2 -0
- package/dist/{index-DGvdYnh3.js → index-lFl6UsTa.js} +7 -7
- package/dist/index-lQmq7gxp.cjs +54 -0
- package/dist/{index-FS8xE7Mo.js → index-xUb0UC07.js} +5 -5
- package/dist/lib-C3DWunRS.js +26376 -0
- package/dist/lib-COvHzA2Y.cjs +2104 -0
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.ts +160 -7
- package/dist/lib.js +33 -30
- package/dist/libRoutes-B-H3e9wZ.js +22 -0
- package/dist/libRoutes-Cl3TklhN.cjs +1 -0
- package/dist/network.cjs +1 -1
- package/dist/network.d.ts +19 -0
- package/dist/network.js +3 -3
- package/dist/router.cjs +1 -1
- package/dist/router.d.ts +26 -4
- package/dist/router.js +10 -10
- package/package.json +55 -48
- package/src/assets/main.css +2 -2
- package/src/components/AutoroutedBreadcrumb/AutoroutedBreadcrumb.mdx +8 -8
- package/src/components/AutoroutedBreadcrumb/AutoroutedBreadcrumb.spec.ts +86 -45
- package/src/components/AutoroutedBreadcrumb/AutoroutedBreadcrumb.vue +29 -21
- package/src/components/AvatarComponent/AvatarComponent.mdx +63 -0
- package/src/components/AvatarComponent/AvatarComponent.stories.ts +98 -0
- package/src/components/AvatarComponent/AvatarComponent.vue +115 -0
- package/src/components/GanttChartComponent/GanttChartComponent.mdx +143 -0
- package/src/components/GanttChartComponent/GanttChartComponent.spec.ts +257 -0
- package/src/components/GanttChartComponent/GanttChartComponent.stories.ts +253 -0
- package/src/components/GanttChartComponent/GanttChartComponent.vue +220 -0
- package/src/components/GanttChartComponent/GanttChartGrid.vue +66 -0
- package/src/components/GanttChartComponent/GanttChartHeaderGrid.vue +167 -0
- package/src/components/GanttChartComponent/GanttChartHeaderLabel.vue +23 -0
- package/src/components/GanttChartComponent/GanttChartLinksOverlay.vue +105 -0
- package/src/components/GanttChartComponent/GanttChartRowGrid.vue +288 -0
- package/src/components/GanttChartComponent/GanttChartRowLabel.vue +32 -0
- package/src/components/GanttChartComponent/composables/useGanttLinks.ts +212 -0
- package/src/components/GanttChartComponent/composables/useGanttSizing.ts +42 -0
- package/src/components/GanttChartComponent/ganttChartLayout.ts +211 -0
- package/src/components/GanttChartComponent/ganttChartTypes.ts +24 -0
- package/src/components/GanttChartComponent/index.ts +1 -0
- package/src/components/NetworkButton/ApiMutationButton.vue +7 -5
- package/src/components/NetworkButton/ApiQueryButton.vue +6 -4
- package/src/components/PlotlyComponent/PlotlyComponent.stories.ts +74 -45
- package/src/containers/BareContainer/BareContainer.mdx +1 -1
- package/src/containers/LayoutContainer/LayoutContainer.mdx +128 -0
- package/src/containers/LayoutContainer/LayoutContainer.spec.ts +151 -0
- package/src/containers/LayoutContainer/LayoutContainer.stories.ts +292 -0
- package/src/containers/LayoutContainer/LayoutContainer.vue +53 -0
- package/src/containers/LayoutContainer/MobileNavigationComponent/MobileNavigationComponent.spec.ts +139 -0
- package/src/containers/LayoutContainer/MobileNavigationComponent/MobileNavigationComponent.vue +63 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/BottomComponent/BottomComponent.spec.ts +39 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/BottomComponent/BottomComponent.vue +9 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/MainComponent/MainComponent.spec.ts +175 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/MainComponent/MainComponent.vue +163 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/MainComponent/NavigationLinkComponent.spec.ts +105 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/MainComponent/NavigationLinkComponent.vue +45 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/SideNavigationComponent.spec.ts +78 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/SideNavigationComponent.vue +29 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/TopComponent/TopComponent.spec.ts +60 -0
- package/src/containers/LayoutContainer/SideNavigationComponent/TopComponent/TopComponent.vue +56 -0
- package/src/containers/LayoutContainer/UserMenuTriggerComponent/UserMenuTriggerComponent.spec.ts +96 -0
- package/src/containers/LayoutContainer/UserMenuTriggerComponent/UserMenuTriggerComponent.vue +80 -0
- package/src/containers/LayoutContainer/index.ts +1 -0
- package/src/containers/NavbarContainer/NavbarContainer.mdx +1 -1
- package/src/containers/RoutedTabsComponent/RoutedTabsComponent.mdx +3 -3
- package/src/containers/SideMenuContainer/SideMenuContainer.mdx +1 -1
- package/src/containers/helpers.ts +6 -3
- package/src/containers/index.ts +2 -0
- package/src/containers/storybook/PlaceholderView.vue +1 -1
- package/src/containers/storybook/PrimeComponents.stories.ts +17 -0
- package/src/containers/storybook/PrimeComponentsShowcase.vue +587 -0
- package/src/containers/storybook/overview.mdx +36 -36
- package/src/demo/App.vue +7 -0
- package/src/{demo.ts → demo/main.ts} +8 -9
- package/src/demo/router.ts +65 -19
- package/src/demo/views/PlaygroundView.vue +86 -0
- package/src/demo/views/ShowcaseView.vue +41 -0
- package/src/lib.ts +3 -1
- package/src/locales/Translation.mdx +2 -2
- package/src/locales/en/avatar.json +3 -0
- package/src/locales/en/gantt_chart.json +6 -0
- package/src/locales/en/navigation.json +3 -1
- package/src/locales/index.ts +0 -4
- package/src/plugins/legalConsent/views/__tests__/LegalConsent.test.ts +12 -7
- package/src/router/guards.ts +4 -4
- package/src/router/libRoutes.ts +6 -2
- package/src/router/router.mdx +107 -66
- package/src/router/types.ts +24 -3
- package/src/stores/__tests__/backend/jwt.test.ts +4 -4
- package/src/stores/__tests__/backend/oauth.test.ts +104 -0
- package/src/stores/__tests__/backend/token.test.ts +4 -4
- package/src/stores/authentication.mdx +138 -0
- package/src/stores/backend/common.ts +89 -0
- package/src/stores/backend/constants.ts +22 -0
- package/src/stores/backend/schemes/jwt.ts +208 -0
- package/src/stores/backend/schemes/oauth.ts +142 -0
- package/src/stores/backend/schemes/token.ts +122 -0
- package/src/stores/backend/types.ts +96 -0
- package/src/stores/backend.ts +21 -427
- package/src/stores/index.ts +6 -0
- package/src/theme/index.ts +2 -0
- package/src/theme/nside.ts +157 -0
- package/src/utils/color.spec.ts +24 -0
- package/src/utils/color.ts +100 -0
- package/src/utils/translations.ts +0 -4
- package/dist/LegalConsent-CEcXZml6.cjs +0 -1
- package/dist/LegalConsent-Dzq3fdnt.js +0 -277
- package/dist/LegalDocument-CS3MnOcV.cjs +0 -109
- package/dist/axios-ClRPr3Xn.js +0 -1777
- package/dist/axios-Dcidtc2l.cjs +0 -6
- package/dist/index-Bc699sOR.js +0 -4997
- package/dist/index-CL_OJMNr.cjs +0 -55
- package/dist/index-CTNsucOq.cjs +0 -147
- package/dist/index-CwLAV8WF.js +0 -210
- package/dist/index-FrfvunRp.cjs +0 -146
- package/dist/lib-BBJH9d11.cjs +0 -2792
- package/dist/lib-Y8FPgwH4.js +0 -20886
- package/dist/libRoutes-BsneoQ4G.js +0 -18
- package/dist/libRoutes-BzeZrIaK.cjs +0 -1
- package/src/demo/DemoApp.vue +0 -13
- package/src/demo/ShowcaseView.vue +0 -39
- package/src/demo/demo.css +0 -15
- /package/src/demo/{DemoContent.vue → views/DemoContent.vue} +0 -0
- /package/src/demo/{DemoView.vue → views/DemoView.vue} +0 -0
package/src/stores/backend.ts
CHANGED
|
@@ -1,61 +1,19 @@
|
|
|
1
1
|
import { defineStore, type Pinia, type StoreDefinition } from 'pinia'
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* on some collection.
|
|
18
|
-
*/
|
|
19
|
-
export type AuthenticationType = 'TOKEN' | 'JWT' | 'SESSION'
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Represents a set of credentials consisting of a username and password.
|
|
23
|
-
* Typically used for authentication purposes.
|
|
24
|
-
*/
|
|
25
|
-
export interface Credentials {
|
|
26
|
-
username: string
|
|
27
|
-
password: string
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface BackendStore {
|
|
31
|
-
authenticated: Ref<boolean>
|
|
32
|
-
axiosInstance: AxiosInstance
|
|
33
|
-
login: (credentials: Credentials) => Promise<AxiosResponse>
|
|
34
|
-
logout: () => void
|
|
35
|
-
setPostLogin: (fn: () => void) => void
|
|
36
|
-
setPostLogout: (fn: () => void) => void
|
|
37
|
-
setupAuthRouteGuard: (router: Router) => void
|
|
38
|
-
unsetAuthRouteGuard: () => void
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface BackendStoreOptions {
|
|
42
|
-
authenticationType: AuthenticationType
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* A constant variable used as the key for storing and retrieving the authentication token
|
|
47
|
-
* in the browser's localStorage. This key is utilized to maintain user session or
|
|
48
|
-
* authentication state across browser sessions by securely saving the token locally.
|
|
49
|
-
*/
|
|
50
|
-
const localStorageKey = 'authenticationToken'
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Constants used as keys for storing and retrieving JWT authentication tokens
|
|
54
|
-
* in the browser's localStorage. These keys are utilized to maintain user session or
|
|
55
|
-
* authentication state across browser sessions by securely saving the tokens locally.
|
|
56
|
-
*/
|
|
57
|
-
const jwtAccessTokenKey = 'jwtAccessToken'
|
|
58
|
-
const jwtRefreshTokenKey = 'jwtRefreshToken'
|
|
2
|
+
import type { BackendStore, BackendStoreOptions } from './backend/types'
|
|
3
|
+
import { jwtAuthenticationBackendStoreSetup } from './backend/schemes/jwt.ts'
|
|
4
|
+
import { oauthAuthenticationBackendStoreSetup } from './backend/schemes/oauth.ts'
|
|
5
|
+
import { tokenAuthenticationBackendStoreSetup } from './backend/schemes/token.ts'
|
|
6
|
+
|
|
7
|
+
export type {
|
|
8
|
+
AuthenticationEndpoints,
|
|
9
|
+
AuthenticationType,
|
|
10
|
+
BackendStore,
|
|
11
|
+
BackendStoreOptions,
|
|
12
|
+
Credentials,
|
|
13
|
+
JwtEndpoints,
|
|
14
|
+
OAuthEndpoints,
|
|
15
|
+
TokenEndpoints,
|
|
16
|
+
} from './backend/types'
|
|
59
17
|
|
|
60
18
|
export const useBackendStore = (
|
|
61
19
|
options: BackendStoreOptions,
|
|
@@ -76,378 +34,14 @@ export const defineBackendStore = (backendStoreOptions: BackendStoreOptions): St
|
|
|
76
34
|
function getBackendStoreSetup(backendStoreOptions: BackendStoreOptions): BackendStore {
|
|
77
35
|
switch (backendStoreOptions.authenticationType) {
|
|
78
36
|
case 'TOKEN':
|
|
79
|
-
return tokenAuthenticationBackendStoreSetup()
|
|
37
|
+
return tokenAuthenticationBackendStoreSetup(backendStoreOptions)
|
|
80
38
|
case 'JWT':
|
|
81
|
-
return jwtAuthenticationBackendStoreSetup()
|
|
39
|
+
return jwtAuthenticationBackendStoreSetup(backendStoreOptions)
|
|
40
|
+
case 'OAUTH':
|
|
41
|
+
return oauthAuthenticationBackendStoreSetup(backendStoreOptions)
|
|
42
|
+
case 'SESSION':
|
|
43
|
+
throw new Error('SESSION authentication is not yet supported.')
|
|
82
44
|
default:
|
|
83
45
|
throw new Error(`Unknown authentication type: ${backendStoreOptions.authenticationType}`)
|
|
84
46
|
}
|
|
85
47
|
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Creates common authentication functions that can be shared between different authentication implementations.
|
|
89
|
-
* @param authenticated - Reactive reference to the authentication state
|
|
90
|
-
* @param postLogin - Reactive reference to the post-login callback
|
|
91
|
-
* @param postLogout - Reactive reference to the post-logout callback
|
|
92
|
-
* @returns An object containing common authentication functions
|
|
93
|
-
*/
|
|
94
|
-
function createCommonAuthFunctions(
|
|
95
|
-
authenticated: Ref<boolean>,
|
|
96
|
-
postLogin: Ref<() => void>,
|
|
97
|
-
postLogout: Ref<() => void>
|
|
98
|
-
) {
|
|
99
|
-
let authRouteGuardWatcher: WatchHandle | null = null
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Sets the function to be executed after login.
|
|
103
|
-
* @param fn - A callback function to execute post-login.
|
|
104
|
-
*/
|
|
105
|
-
function setPostLogin(fn: () => void): void {
|
|
106
|
-
postLogin.value = fn
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Sets the postLogout function to be called after a logout action.
|
|
111
|
-
* @param fn - A function to be executed post-logout.
|
|
112
|
-
*/
|
|
113
|
-
function setPostLogout(fn: () => void): void {
|
|
114
|
-
postLogout.value = fn
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Sets up a route guard that responds to authentication status changes
|
|
119
|
-
*
|
|
120
|
-
* This function should be installed in the root component (App.vue) setup script to ensure
|
|
121
|
-
* proper route reevaluation when authentication status changes. It forces the router to
|
|
122
|
-
* reevaluate the current route, allowing navigation guards to run again and potentially
|
|
123
|
-
* redirect the user based on the new authentication state.
|
|
124
|
-
* @param router - Vue Router instance
|
|
125
|
-
* @example
|
|
126
|
-
* // In App.vue setup script, given you have a router setup
|
|
127
|
-
* import router from '@/router'
|
|
128
|
-
* import { useBackendStore } from '@/stores/backend'
|
|
129
|
-
*
|
|
130
|
-
* const backendStore = useBackendStore()
|
|
131
|
-
*
|
|
132
|
-
* // Set up auth route guard to respond to authentication changes
|
|
133
|
-
* backendStore.setupAuthRouteGuard(router)
|
|
134
|
-
*/
|
|
135
|
-
function setupAuthRouteGuard(router: Router) {
|
|
136
|
-
authRouteGuardWatcher = watch(authenticated, (value, oldValue) => {
|
|
137
|
-
if (value != oldValue) {
|
|
138
|
-
// Force a reevaluation of the current route, so router guards apply
|
|
139
|
-
router.push({ path: router.currentRoute.value.path, force: true }).then(() => {})
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Removes the authentication route guard previously set up by `setupAuthRouteGuard`.
|
|
146
|
-
*
|
|
147
|
-
* This function cleans up the navigation guard registered with the Vue Router instance,
|
|
148
|
-
* preventing any authentication-related redirects. It's useful when you need to
|
|
149
|
-
* disable authentication checks, such as during application teardown or when switching
|
|
150
|
-
* authentication strategies.
|
|
151
|
-
* @see setupAuthRouteGuard - The corresponding function that sets up the route guard.
|
|
152
|
-
*/
|
|
153
|
-
function unsetAuthRouteGuard(): void {
|
|
154
|
-
if (authRouteGuardWatcher) authRouteGuardWatcher()
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
setPostLogin,
|
|
159
|
-
setPostLogout,
|
|
160
|
-
setupAuthRouteGuard,
|
|
161
|
-
unsetAuthRouteGuard,
|
|
162
|
-
authRouteGuardWatcher,
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Configures and sets up a token-based authentication backend store for managing API authentication.
|
|
168
|
-
*
|
|
169
|
-
* The method initializes authentication states, such as tokens, login/logout handlers, and
|
|
170
|
-
* authentication interceptors on the axiosInstance for requests and responses.
|
|
171
|
-
* It also provides helper functions for handling authentication processes, like login, logout,
|
|
172
|
-
* and post-login/logout callbacks. Additionally, it enables the setting of route guards to
|
|
173
|
-
* respond to authentication status changes in a Vue.js application.
|
|
174
|
-
* @returns An object containing authentication state, API client instance, and helper functions:
|
|
175
|
-
* - `axiosInstance`: Pre-configured axios instance with authentication support
|
|
176
|
-
* - `authenticated`: Reactive flag representing the authentication status
|
|
177
|
-
* - `login`: Function to authenticate users using credentials
|
|
178
|
-
* - `logout`: Function to deauthenticate users and clear authentication tokens
|
|
179
|
-
* - `setPostLogin`: Method to set a callback function invoked after successful login
|
|
180
|
-
* - `setPostLogout`: Method to set a callback function invoked after logout
|
|
181
|
-
* - `setupAuthRouteGuard`: Function to set up route guard for managing authentication-driven route reevaluations
|
|
182
|
-
*/
|
|
183
|
-
function tokenAuthenticationBackendStoreSetup(): BackendStore {
|
|
184
|
-
/**
|
|
185
|
-
* A reactive variable that indicates whether a user is authenticated or not.
|
|
186
|
-
*
|
|
187
|
-
* The value is `false` by default, representing an unauthenticated state.
|
|
188
|
-
* This variable can be updated dynamically to reflect the authentication status
|
|
189
|
-
* of the user within the application.
|
|
190
|
-
*/
|
|
191
|
-
const authenticated = ref(false)
|
|
192
|
-
const _token: Ref<string | null> = ref(null)
|
|
193
|
-
const _postLogout: Ref<() => void> = ref(() => {})
|
|
194
|
-
const _postLogin: Ref<() => void> = ref(() => {})
|
|
195
|
-
const _fromLocalStorage = localStorage.getItem(localStorageKey)
|
|
196
|
-
|
|
197
|
-
// Create common authentication functions
|
|
198
|
-
const commonAuth = createCommonAuthFunctions(authenticated, _postLogin, _postLogout)
|
|
199
|
-
|
|
200
|
-
if (_fromLocalStorage) {
|
|
201
|
-
_token.value = _fromLocalStorage
|
|
202
|
-
authenticated.value = true
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
watch(_token, () => {
|
|
206
|
-
localStorage.setItem(localStorageKey, _token.value ?? '')
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
axiosInstance.defaults.withCredentials = true
|
|
210
|
-
axiosInstance.defaults.headers.common['Content-Type'] = 'application/json'
|
|
211
|
-
|
|
212
|
-
axiosInstance.interceptors.request.use(
|
|
213
|
-
async (config) => {
|
|
214
|
-
if (authenticated.value && _token.value) {
|
|
215
|
-
config.headers['Authorization'] = `Token ${_token.value}`
|
|
216
|
-
}
|
|
217
|
-
return config
|
|
218
|
-
},
|
|
219
|
-
(error) => Promise.reject(error)
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
axiosInstance.interceptors.response.use(
|
|
223
|
-
(response) => response,
|
|
224
|
-
async (error) => {
|
|
225
|
-
if (error.response.status === 403) {
|
|
226
|
-
const data = error.response.data
|
|
227
|
-
if (data?.detail === 'Authentication credentials were not provided.') {
|
|
228
|
-
logout()
|
|
229
|
-
}
|
|
230
|
-
return Promise.reject(error)
|
|
231
|
-
} else {
|
|
232
|
-
return Promise.reject(error)
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Authenticates a user by sending their credentials to the server.
|
|
239
|
-
* @param credentials - The user's login credentials, typically including a username and password.
|
|
240
|
-
* @returns A promise that resolves to the Axios response object returned from the authentication request.
|
|
241
|
-
*/
|
|
242
|
-
async function login(credentials: Credentials): Promise<AxiosResponse> {
|
|
243
|
-
const response = await axiosInstance.post('/api/token-auth/', credentials)
|
|
244
|
-
if (response.status === 200) {
|
|
245
|
-
authenticated.value = true
|
|
246
|
-
_token.value = response.data.token
|
|
247
|
-
}
|
|
248
|
-
_postLogin.value()
|
|
249
|
-
return response
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Logs the user out of the application by performing the following actions:
|
|
254
|
-
* - Removes the user's authentication token from localStorage.
|
|
255
|
-
* - Updates the application's authenticated state to false.
|
|
256
|
-
* - Resets the stored authentication token to null.
|
|
257
|
-
* - Executes any additional post-logout logic defined in the application.
|
|
258
|
-
*/
|
|
259
|
-
function logout(): void {
|
|
260
|
-
localStorage.removeItem(localStorageKey)
|
|
261
|
-
authenticated.value = false
|
|
262
|
-
_token.value = null
|
|
263
|
-
_postLogout.value()
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return {
|
|
267
|
-
axiosInstance,
|
|
268
|
-
authenticated,
|
|
269
|
-
login,
|
|
270
|
-
logout,
|
|
271
|
-
setPostLogin: commonAuth.setPostLogin,
|
|
272
|
-
setPostLogout: commonAuth.setPostLogout,
|
|
273
|
-
setupAuthRouteGuard: commonAuth.setupAuthRouteGuard,
|
|
274
|
-
unsetAuthRouteGuard: commonAuth.unsetAuthRouteGuard,
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Configures and sets up a JWT-based authentication backend store for managing API authentication.
|
|
280
|
-
*
|
|
281
|
-
* The method initializes authentication states, such as access and refresh tokens, login/logout handlers, and
|
|
282
|
-
* authentication interceptors on the axios instance for requests and responses.
|
|
283
|
-
* It also provides helper functions for handling authentication processes, like login, logout,
|
|
284
|
-
* and post-login/logout callbacks. Additionally, it enables the setting of route guards to
|
|
285
|
-
* respond to authentication status changes in a Vue.js application.
|
|
286
|
-
*
|
|
287
|
-
* This implementation is designed to work with a Django backend using the "Simple JWT" library.
|
|
288
|
-
* @returns An object containing authentication state, API client instance, and helper functions:
|
|
289
|
-
* - `axiosInstance`: Pre-configured axios instance with authentication support
|
|
290
|
-
* - `authenticated`: Reactive flag representing the authentication status
|
|
291
|
-
* - `login`: Function to authenticate users using credentials
|
|
292
|
-
* - `logout`: Function to deauthenticate users and clear authentication tokens
|
|
293
|
-
* - `setPostLogin`: Method to set a callback function invoked after successful login
|
|
294
|
-
* - `setPostLogout`: Method to set a callback function invoked after logout
|
|
295
|
-
* - `setupAuthRouteGuard`: Function to set up route guard for managing authentication-driven route reevaluations
|
|
296
|
-
*/
|
|
297
|
-
function jwtAuthenticationBackendStoreSetup(): BackendStore {
|
|
298
|
-
/**
|
|
299
|
-
* A reactive variable that indicates whether a user is authenticated or not.
|
|
300
|
-
*
|
|
301
|
-
* The value is `false` by default, representing an unauthenticated state.
|
|
302
|
-
* This variable can be updated dynamically to reflect the authentication status
|
|
303
|
-
* of the user within the application.
|
|
304
|
-
*/
|
|
305
|
-
const authenticated = ref(false)
|
|
306
|
-
const _accessToken: Ref<string | null> = ref(null)
|
|
307
|
-
const _refreshToken: Ref<string | null> = ref(null)
|
|
308
|
-
const _postLogout: Ref<() => void> = ref(() => {})
|
|
309
|
-
const _postLogin: Ref<() => void> = ref(() => {})
|
|
310
|
-
const _accessTokenFromLocalStorage = localStorage.getItem(jwtAccessTokenKey)
|
|
311
|
-
const _refreshTokenFromLocalStorage = localStorage.getItem(jwtRefreshTokenKey)
|
|
312
|
-
const refreshUrl = '/api/token/refresh/'
|
|
313
|
-
|
|
314
|
-
// Create common authentication functions
|
|
315
|
-
const commonAuth = createCommonAuthFunctions(authenticated, _postLogin, _postLogout)
|
|
316
|
-
|
|
317
|
-
if (_accessTokenFromLocalStorage && _refreshTokenFromLocalStorage) {
|
|
318
|
-
_accessToken.value = _accessTokenFromLocalStorage
|
|
319
|
-
_refreshToken.value = _refreshTokenFromLocalStorage
|
|
320
|
-
authenticated.value = true
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
watch(_accessToken, () => {
|
|
324
|
-
localStorage.setItem(jwtAccessTokenKey, _accessToken.value ?? '')
|
|
325
|
-
})
|
|
326
|
-
|
|
327
|
-
watch(_refreshToken, () => {
|
|
328
|
-
localStorage.setItem(jwtRefreshTokenKey, _refreshToken.value ?? '')
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
axiosInstance.defaults.withCredentials = true
|
|
332
|
-
axiosInstance.defaults.headers.common['Content-Type'] = 'application/json'
|
|
333
|
-
|
|
334
|
-
axiosInstance.interceptors.request.use(
|
|
335
|
-
async (config) => {
|
|
336
|
-
if (authenticated.value && _accessToken.value) {
|
|
337
|
-
config.headers['Authorization'] = `Bearer ${_accessToken.value}`
|
|
338
|
-
}
|
|
339
|
-
return config
|
|
340
|
-
},
|
|
341
|
-
(error) => Promise.reject(error)
|
|
342
|
-
)
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Checks if the error is related to token authentication issues
|
|
346
|
-
* @param error - The Axios error to check for token-related issues
|
|
347
|
-
* @returns True if the error is related to token authentication, false otherwise
|
|
348
|
-
*/
|
|
349
|
-
function isTokenError(error: AxiosError): boolean {
|
|
350
|
-
return !!(
|
|
351
|
-
error.response &&
|
|
352
|
-
[401, 403].includes(error.response.status) &&
|
|
353
|
-
((error.response?.data as { code?: string })?.code === 'token_not_valid' ||
|
|
354
|
-
!_accessToken.value ||
|
|
355
|
-
!_refreshToken.value)
|
|
356
|
-
)
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Attempts to refresh the JWT token and retry the original request
|
|
361
|
-
* @param originalRequest - The original Axios request configuration to retry after token refresh
|
|
362
|
-
* @returns A promise that resolves to the Axios response from the retried request
|
|
363
|
-
*/
|
|
364
|
-
async function handleTokenRefresh(
|
|
365
|
-
originalRequest: InternalAxiosRequestConfig
|
|
366
|
-
): Promise<AxiosResponse> {
|
|
367
|
-
const response = await axiosInstance.post(refreshUrl, {
|
|
368
|
-
refresh: _refreshToken.value,
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
if (response.status === 200) {
|
|
372
|
-
// Update the tokens
|
|
373
|
-
_accessToken.value = response.data.access
|
|
374
|
-
if (response.data.refresh) {
|
|
375
|
-
_refreshToken.value = response.data.refresh
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Retry the original request with the new token
|
|
379
|
-
originalRequest.headers['Authorization'] = `Bearer ${_accessToken.value}`
|
|
380
|
-
return axiosInstance(originalRequest)
|
|
381
|
-
}
|
|
382
|
-
throw new Error('Token refresh failed')
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
axiosInstance.interceptors.response.use(
|
|
386
|
-
(response) => response,
|
|
387
|
-
async (error) => {
|
|
388
|
-
if (!isTokenError(error)) {
|
|
389
|
-
return Promise.reject(error)
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const originalRequest = error.config
|
|
393
|
-
const isRefreshRequest = originalRequest.url === refreshUrl
|
|
394
|
-
|
|
395
|
-
// Prevent infinite loops
|
|
396
|
-
if (!isRefreshRequest && _refreshToken.value) {
|
|
397
|
-
try {
|
|
398
|
-
return await handleTokenRefresh(originalRequest)
|
|
399
|
-
} catch (refreshError) {
|
|
400
|
-
logout()
|
|
401
|
-
return Promise.reject(refreshError)
|
|
402
|
-
}
|
|
403
|
-
} else {
|
|
404
|
-
logout()
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
return Promise.reject(error)
|
|
408
|
-
}
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Authenticates a user by sending their credentials to the server.
|
|
413
|
-
* @param credentials - The user's login credentials, typically including a username and password.
|
|
414
|
-
* @returns A promise that resolves to the Axios response object returned from the authentication request.
|
|
415
|
-
*/
|
|
416
|
-
async function login(credentials: Credentials): Promise<AxiosResponse> {
|
|
417
|
-
const response = await axiosInstance.post('/api/token/', credentials)
|
|
418
|
-
if (response.status === 200) {
|
|
419
|
-
authenticated.value = true
|
|
420
|
-
_accessToken.value = response.data.access
|
|
421
|
-
_refreshToken.value = response.data.refresh
|
|
422
|
-
}
|
|
423
|
-
_postLogin.value()
|
|
424
|
-
return response
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* Logs the user out of the application by performing the following actions:
|
|
429
|
-
* - Removes the user's authentication tokens from localStorage.
|
|
430
|
-
* - Updates the application's authenticated state to false.
|
|
431
|
-
* - Resets the stored authentication tokens to null.
|
|
432
|
-
* - Executes any additional post-logout logic defined in the application.
|
|
433
|
-
*/
|
|
434
|
-
function logout(): void {
|
|
435
|
-
localStorage.removeItem(jwtAccessTokenKey)
|
|
436
|
-
localStorage.removeItem(jwtRefreshTokenKey)
|
|
437
|
-
authenticated.value = false
|
|
438
|
-
_accessToken.value = null
|
|
439
|
-
_refreshToken.value = null
|
|
440
|
-
_postLogout.value()
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return {
|
|
444
|
-
axiosInstance,
|
|
445
|
-
authenticated,
|
|
446
|
-
login,
|
|
447
|
-
logout,
|
|
448
|
-
setPostLogin: commonAuth.setPostLogin,
|
|
449
|
-
setPostLogout: commonAuth.setPostLogout,
|
|
450
|
-
setupAuthRouteGuard: commonAuth.setupAuthRouteGuard,
|
|
451
|
-
unsetAuthRouteGuard: commonAuth.unsetAuthRouteGuard,
|
|
452
|
-
}
|
|
453
|
-
}
|
package/src/stores/index.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export {
|
|
2
2
|
useBackendStore,
|
|
3
|
+
type AuthenticationEndpoints,
|
|
3
4
|
type BackendStore,
|
|
4
5
|
type Credentials,
|
|
5
6
|
type AuthenticationType,
|
|
7
|
+
type JwtEndpoints,
|
|
8
|
+
type OAuthEndpoints,
|
|
9
|
+
type TokenEndpoints,
|
|
6
10
|
} from './backend.ts'
|
|
11
|
+
|
|
12
|
+
export { useDarkModeStore } from './darkMode.ts'
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import Aura from '@primeuix/themes/aura'
|
|
2
|
+
import { definePreset, palette } from '@primeuix/themes'
|
|
3
|
+
|
|
4
|
+
const SCALE_KEYS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const
|
|
5
|
+
type ScaleKey = (typeof SCALE_KEYS)[number]
|
|
6
|
+
|
|
7
|
+
// Ensures a full Tailwind-like scale (50–950) by overlaying explicit brand steps
|
|
8
|
+
// on top of a generated palette while keeping every scale key present.
|
|
9
|
+
const mergeScale = (base: Record<ScaleKey, string>, overrides: Partial<Record<ScaleKey, string>>) =>
|
|
10
|
+
SCALE_KEYS.reduce<Record<ScaleKey, string>>(
|
|
11
|
+
(acc, key) => {
|
|
12
|
+
acc[key] = overrides[key] ?? base[key]
|
|
13
|
+
return acc
|
|
14
|
+
},
|
|
15
|
+
{} as Record<ScaleKey, string>
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const greenPalette = mergeScale(palette('#8CBB13') as Record<ScaleKey, string>, {
|
|
19
|
+
50: '#F1F6E2',
|
|
20
|
+
100: '#D4E5A7',
|
|
21
|
+
300: '#B7D46C',
|
|
22
|
+
500: '#8CBB13',
|
|
23
|
+
700: '#70A100',
|
|
24
|
+
900: '#0A3B00',
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const tealPalette = mergeScale(palette('#00ACC2') as Record<ScaleKey, string>, {
|
|
28
|
+
50: '#DFF5F7',
|
|
29
|
+
100: '#9FE0E8',
|
|
30
|
+
300: '#40C1D1',
|
|
31
|
+
500: '#00ACC2',
|
|
32
|
+
700: '#0090A7',
|
|
33
|
+
900: '#002A41',
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const orangePalette = mergeScale(palette('#F4A74F') as Record<ScaleKey, string>, {
|
|
37
|
+
50: '#FEF4E9',
|
|
38
|
+
100: '#FBDEBD',
|
|
39
|
+
300: '#F7BD7B',
|
|
40
|
+
500: '#F4A74F',
|
|
41
|
+
700: '#D48C37',
|
|
42
|
+
900: '#752800',
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const pinkPalette = mergeScale(palette('#D63487') as Record<ScaleKey, string>, {
|
|
46
|
+
50: '#FAE6F0',
|
|
47
|
+
100: '#F0B3D2',
|
|
48
|
+
300: '#E067A5',
|
|
49
|
+
500: '#D63487',
|
|
50
|
+
700: '#B80870',
|
|
51
|
+
900: '#570008',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const purplePalette = mergeScale(palette('#A83A8D') as Record<ScaleKey, string>, {
|
|
55
|
+
50: '#F4E6F1',
|
|
56
|
+
100: '#DEB5D4',
|
|
57
|
+
300: '#BE6BAA',
|
|
58
|
+
500: '#A83A8D',
|
|
59
|
+
700: '#8A1B73',
|
|
60
|
+
900: '#8A1B73',
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const redPalette = mergeScale(palette('#C6243D') as Record<ScaleKey, string>, {
|
|
64
|
+
50: '#F8E4E7',
|
|
65
|
+
100: '#EAADB6',
|
|
66
|
+
300: '#D45B6E',
|
|
67
|
+
500: '#C6243D',
|
|
68
|
+
700: '#A50027',
|
|
69
|
+
900: '#600000',
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const grayPalette = mergeScale(palette('#535F6B') as Record<ScaleKey, string>, {
|
|
73
|
+
50: '#FAFCFE',
|
|
74
|
+
100: '#F8F9FA',
|
|
75
|
+
200: '#E9ECEF',
|
|
76
|
+
300: '#DEE2E6',
|
|
77
|
+
400: '#CED4DA',
|
|
78
|
+
500: '#ADB5BD',
|
|
79
|
+
600: '#868E96',
|
|
80
|
+
700: '#495057',
|
|
81
|
+
800: '#343A40',
|
|
82
|
+
900: '#212529',
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const surfaceLightBase = grayPalette
|
|
86
|
+
|
|
87
|
+
const surfaceLight = {
|
|
88
|
+
0: '#FFFFFF',
|
|
89
|
+
...surfaceLightBase,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const surfaceDark = {
|
|
93
|
+
0: '#E7EBF5',
|
|
94
|
+
...mergeScale(palette('#162950') as Record<ScaleKey, string>, {}),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type NsidePrimaryColor = 'green' | 'orange' | 'blue'
|
|
98
|
+
|
|
99
|
+
export const createNsideTheme = (primary: NsidePrimaryColor = 'green') =>
|
|
100
|
+
definePreset(Aura, {
|
|
101
|
+
primitive: {
|
|
102
|
+
borderRadius: {
|
|
103
|
+
none: '0',
|
|
104
|
+
xs: '3px',
|
|
105
|
+
sm: '6px',
|
|
106
|
+
md: '8px',
|
|
107
|
+
lg: '10px',
|
|
108
|
+
xl: '14px',
|
|
109
|
+
},
|
|
110
|
+
green: greenPalette,
|
|
111
|
+
teal: tealPalette,
|
|
112
|
+
cyan: tealPalette,
|
|
113
|
+
blue: tealPalette,
|
|
114
|
+
sky: tealPalette,
|
|
115
|
+
yellow: orangePalette,
|
|
116
|
+
orange: orangePalette,
|
|
117
|
+
red: redPalette,
|
|
118
|
+
pink: pinkPalette,
|
|
119
|
+
purple: purplePalette,
|
|
120
|
+
gray: grayPalette,
|
|
121
|
+
},
|
|
122
|
+
css: `
|
|
123
|
+
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');
|
|
124
|
+
|
|
125
|
+
:root,
|
|
126
|
+
:host {
|
|
127
|
+
font-size: 14px;
|
|
128
|
+
font-family: 'Poppins', sans-serif;
|
|
129
|
+
--nside-gradient-teal-purple: linear-gradient(135deg, #00ACC2 0%, #807FBD 100%);
|
|
130
|
+
--nside-gradient-orange-pink: linear-gradient(135deg, #F4A74F 0%, #EE4D9B 100%);
|
|
131
|
+
--nside-gradient-green-blue: linear-gradient(135deg, #8CBB13 0%, #71CFEC 100%);
|
|
132
|
+
}
|
|
133
|
+
`,
|
|
134
|
+
semantic: {
|
|
135
|
+
primary: palette(`{${primary}}`),
|
|
136
|
+
colorScheme: {
|
|
137
|
+
light: {
|
|
138
|
+
surface: surfaceLight,
|
|
139
|
+
text: {
|
|
140
|
+
color: '#535F6B',
|
|
141
|
+
hoverColor: '#162950',
|
|
142
|
+
mutedColor: '#66728C',
|
|
143
|
+
hoverMutedColor: '#535F6B',
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
dark: {
|
|
147
|
+
surface: surfaceDark,
|
|
148
|
+
text: {
|
|
149
|
+
color: '#E7EBF5',
|
|
150
|
+
hoverColor: '#FFFFFF',
|
|
151
|
+
mutedColor: '#A5BEDB',
|
|
152
|
+
hoverMutedColor: '#E7EBF5',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getAccessibleTextColor, getRelativeLuminance, hashString, hslToRgb } from './color'
|
|
3
|
+
|
|
4
|
+
describe('color utilities', () => {
|
|
5
|
+
it('hashes strings deterministically', () => {
|
|
6
|
+
expect(hashString('alice')).toBe(hashString('alice'))
|
|
7
|
+
expect(hashString('alice')).not.toBe(hashString('bob'))
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('converts hsl values to rgb', () => {
|
|
11
|
+
expect(hslToRgb(0, 100, 50)).toEqual({ r: 255, g: 0, b: 0 })
|
|
12
|
+
expect(hslToRgb(120, 100, 50)).toEqual({ r: 0, g: 255, b: 0 })
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('computes luminance in expected bounds', () => {
|
|
16
|
+
expect(getRelativeLuminance(0, 0, 0)).toBe(0)
|
|
17
|
+
expect(getRelativeLuminance(255, 255, 255)).toBe(1)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('chooses readable text color for background contrast', () => {
|
|
21
|
+
expect(getAccessibleTextColor({ r: 0, g: 0, b: 0 })).toBe('#FFFFFF')
|
|
22
|
+
expect(getAccessibleTextColor({ r: 255, g: 255, b: 255 })).toBe('#0F172A')
|
|
23
|
+
})
|
|
24
|
+
})
|