@saasicat/ui-vue 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/{chunk-66BUSVC5.js → chunk-CRUUZMMN.js} +1 -1
- package/dist/{chunk-TY2WYKME.js → chunk-MGKJCVDU.js} +32 -5
- package/dist/{chunk-TBLXKSTR.js → chunk-TZ2NGQXK.js} +48 -15
- package/dist/client/index.cjs +50 -15
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +6 -2
- package/dist/index.cjs +74 -20
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +9 -3
- package/dist/{messages-BIYw32u1.d.ts → messages-DetB75lX.d.cts} +15 -3
- package/dist/{messages-BIYw32u1.d.cts → messages-DetB75lX.d.ts} +15 -3
- package/dist/quasar/index.cjs +66 -4
- package/dist/quasar/index.d.cts +2 -2
- package/dist/quasar/index.d.ts +2 -2
- package/dist/quasar/index.js +2 -2
- package/dist/{use-super-admin-i18n-CWNu_W3u.d.ts → use-super-admin-i18n-BPC-H9jz.d.ts} +30 -4
- package/dist/{use-super-admin-i18n-DL_qI1BY.d.cts → use-super-admin-i18n-DokHhkS_.d.cts} +30 -4
- package/package.json +2 -2
- package/src/client/i18n/locale.ts +15 -0
- package/src/client/i18n/messages/shell.ts +2 -0
- package/src/client/types.ts +51 -14
- package/src/components/LocaleSwitcher.vue +68 -0
- package/src/pages-standard/AdminLayout.vue +2 -0
- package/src/pages-standard/SuperAdminLoginPage.vue +9 -0
- package/src/pages-standard/SuperAdminSetupWizard.vue +7 -0
- package/src/vue/use-super-admin-i18n.ts +74 -8
|
@@ -25,8 +25,11 @@ interface KvStore {
|
|
|
25
25
|
remove(key: string): void;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Returns a `KvStore` wrapper around `localStorage
|
|
29
|
-
* Tests may pass an in-memory stub.
|
|
28
|
+
* Returns a `KvStore` wrapper around `localStorage`, or a no-op store when the
|
|
29
|
+
* environment has none or refuses access. Tests may pass an in-memory stub.
|
|
30
|
+
*
|
|
31
|
+
* Every persistence built on this is a convenience — losing it must never take
|
|
32
|
+
* the app down, so no failure mode is allowed to escape.
|
|
30
33
|
*/
|
|
31
34
|
declare function defaultKvStore(): KvStore;
|
|
32
35
|
/**
|
|
@@ -40,6 +43,14 @@ declare const SA_LOCALES: readonly SaLocale[];
|
|
|
40
43
|
declare const DEFAULT_SA_LOCALE: SaLocale;
|
|
41
44
|
/** BCP-47 tags used for `Intl`/`toLocaleString` formatting per UI locale. */
|
|
42
45
|
declare const SA_INTL_LOCALES: Record<SaLocale, string>;
|
|
46
|
+
/**
|
|
47
|
+
* Names for the language switcher. Endonyms — a language is listed in its own
|
|
48
|
+
* language, so the entries stay readable no matter which locale is active and
|
|
49
|
+
* never need translating.
|
|
50
|
+
*/
|
|
51
|
+
declare const SA_LOCALE_LABELS: Record<SaLocale, string>;
|
|
52
|
+
/** Narrows untrusted input (persisted preference, query parameter) to a locale. */
|
|
53
|
+
declare function isSaLocale(value: unknown): value is SaLocale;
|
|
43
54
|
|
|
44
55
|
/** Consumer implementation; receives the action inputs as a generic object. */
|
|
45
56
|
type ActionHandler<TInput = unknown, TResult = unknown> = (input: TInput) => Promise<TResult>;
|
|
@@ -1405,6 +1416,7 @@ declare const shellMessages: Record<SaLocale, {
|
|
|
1405
1416
|
subtitle: string;
|
|
1406
1417
|
roleBadge: string;
|
|
1407
1418
|
logout: string;
|
|
1419
|
+
language: string;
|
|
1408
1420
|
};
|
|
1409
1421
|
drawer: {
|
|
1410
1422
|
docs: string;
|
|
@@ -1582,4 +1594,4 @@ declare const SA_MESSAGES: Record<SaLocale, SaMessages>;
|
|
|
1582
1594
|
*/
|
|
1583
1595
|
declare function resolveMessages(locale: SaLocale, overrides?: SaMessagesOverrides): SaMessages;
|
|
1584
1596
|
|
|
1585
|
-
export { type ActionHandler as A, DEFAULT_SA_LOCALE as D, type HttpClient as H, type KvStore as K, type MessageTree as M, type PartialMessages as P, type ResolvedAction as R, type SaLocale as S, type TranslationOf as T, type SaMessages as a, type SaMessagesOverrides as b, ActionRegistry as c, ActionDefNotInManifestError as d, type HttpResponse as e, MissingHandlerError as f, SA_INTL_LOCALES as g, SA_LOCALES as h,
|
|
1597
|
+
export { type ActionHandler as A, DEFAULT_SA_LOCALE as D, type HttpClient as H, type KvStore as K, type MessageTree as M, type PartialMessages as P, type ResolvedAction as R, type SaLocale as S, type TranslationOf as T, type SaMessages as a, type SaMessagesOverrides as b, ActionRegistry as c, ActionDefNotInManifestError as d, type HttpResponse as e, MissingHandlerError as f, SA_INTL_LOCALES as g, SA_LOCALES as h, SA_LOCALE_LABELS as i, SA_MESSAGES as j, defaultHttpClient as k, defaultKvStore as l, defineMessages as m, isSaLocale as n, mergeMessages as o, resolveMessages as r };
|
|
@@ -25,8 +25,11 @@ interface KvStore {
|
|
|
25
25
|
remove(key: string): void;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Returns a `KvStore` wrapper around `localStorage
|
|
29
|
-
* Tests may pass an in-memory stub.
|
|
28
|
+
* Returns a `KvStore` wrapper around `localStorage`, or a no-op store when the
|
|
29
|
+
* environment has none or refuses access. Tests may pass an in-memory stub.
|
|
30
|
+
*
|
|
31
|
+
* Every persistence built on this is a convenience — losing it must never take
|
|
32
|
+
* the app down, so no failure mode is allowed to escape.
|
|
30
33
|
*/
|
|
31
34
|
declare function defaultKvStore(): KvStore;
|
|
32
35
|
/**
|
|
@@ -40,6 +43,14 @@ declare const SA_LOCALES: readonly SaLocale[];
|
|
|
40
43
|
declare const DEFAULT_SA_LOCALE: SaLocale;
|
|
41
44
|
/** BCP-47 tags used for `Intl`/`toLocaleString` formatting per UI locale. */
|
|
42
45
|
declare const SA_INTL_LOCALES: Record<SaLocale, string>;
|
|
46
|
+
/**
|
|
47
|
+
* Names for the language switcher. Endonyms — a language is listed in its own
|
|
48
|
+
* language, so the entries stay readable no matter which locale is active and
|
|
49
|
+
* never need translating.
|
|
50
|
+
*/
|
|
51
|
+
declare const SA_LOCALE_LABELS: Record<SaLocale, string>;
|
|
52
|
+
/** Narrows untrusted input (persisted preference, query parameter) to a locale. */
|
|
53
|
+
declare function isSaLocale(value: unknown): value is SaLocale;
|
|
43
54
|
|
|
44
55
|
/** Consumer implementation; receives the action inputs as a generic object. */
|
|
45
56
|
type ActionHandler<TInput = unknown, TResult = unknown> = (input: TInput) => Promise<TResult>;
|
|
@@ -1405,6 +1416,7 @@ declare const shellMessages: Record<SaLocale, {
|
|
|
1405
1416
|
subtitle: string;
|
|
1406
1417
|
roleBadge: string;
|
|
1407
1418
|
logout: string;
|
|
1419
|
+
language: string;
|
|
1408
1420
|
};
|
|
1409
1421
|
drawer: {
|
|
1410
1422
|
docs: string;
|
|
@@ -1582,4 +1594,4 @@ declare const SA_MESSAGES: Record<SaLocale, SaMessages>;
|
|
|
1582
1594
|
*/
|
|
1583
1595
|
declare function resolveMessages(locale: SaLocale, overrides?: SaMessagesOverrides): SaMessages;
|
|
1584
1596
|
|
|
1585
|
-
export { type ActionHandler as A, DEFAULT_SA_LOCALE as D, type HttpClient as H, type KvStore as K, type MessageTree as M, type PartialMessages as P, type ResolvedAction as R, type SaLocale as S, type TranslationOf as T, type SaMessages as a, type SaMessagesOverrides as b, ActionRegistry as c, ActionDefNotInManifestError as d, type HttpResponse as e, MissingHandlerError as f, SA_INTL_LOCALES as g, SA_LOCALES as h,
|
|
1597
|
+
export { type ActionHandler as A, DEFAULT_SA_LOCALE as D, type HttpClient as H, type KvStore as K, type MessageTree as M, type PartialMessages as P, type ResolvedAction as R, type SaLocale as S, type TranslationOf as T, type SaMessages as a, type SaMessagesOverrides as b, ActionRegistry as c, ActionDefNotInManifestError as d, type HttpResponse as e, MissingHandlerError as f, SA_INTL_LOCALES as g, SA_LOCALES as h, SA_LOCALE_LABELS as i, SA_MESSAGES as j, defaultHttpClient as k, defaultKvStore as l, defineMessages as m, isSaLocale as n, mergeMessages as o, resolveMessages as r };
|
package/dist/quasar/index.cjs
CHANGED
|
@@ -33,6 +33,45 @@ var import_pinia = require("pinia");
|
|
|
33
33
|
var import_vue_router = require("vue-router");
|
|
34
34
|
|
|
35
35
|
// src/client/types.ts
|
|
36
|
+
var NOOP_KV_STORE = {
|
|
37
|
+
get: () => null,
|
|
38
|
+
set: () => {
|
|
39
|
+
},
|
|
40
|
+
remove: () => {
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function defaultKvStore() {
|
|
44
|
+
const ls = resolveLocalStorage();
|
|
45
|
+
if (!ls) return NOOP_KV_STORE;
|
|
46
|
+
return {
|
|
47
|
+
get: (k) => {
|
|
48
|
+
try {
|
|
49
|
+
return ls.getItem(k);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
set: (k, v) => {
|
|
55
|
+
try {
|
|
56
|
+
ls.setItem(k, v);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
remove: (k) => {
|
|
61
|
+
try {
|
|
62
|
+
ls.removeItem(k);
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function resolveLocalStorage() {
|
|
69
|
+
try {
|
|
70
|
+
return globalThis.localStorage ?? null;
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
36
75
|
function defaultHttpClient() {
|
|
37
76
|
return (url, init) => fetch(url, init);
|
|
38
77
|
}
|
|
@@ -93,11 +132,15 @@ var SUPER_ADMIN_NOTIFY_KEY = /* @__PURE__ */ Symbol.for(
|
|
|
93
132
|
var import_vue = require("vue");
|
|
94
133
|
|
|
95
134
|
// src/client/i18n/locale.ts
|
|
135
|
+
var SA_LOCALES = ["de", "en"];
|
|
96
136
|
var DEFAULT_SA_LOCALE = "de";
|
|
97
137
|
var SA_INTL_LOCALES = {
|
|
98
138
|
de: "de-DE",
|
|
99
139
|
en: "en-US"
|
|
100
140
|
};
|
|
141
|
+
function isSaLocale(value) {
|
|
142
|
+
return typeof value === "string" && SA_LOCALES.includes(value);
|
|
143
|
+
}
|
|
101
144
|
|
|
102
145
|
// src/client/i18n/define.ts
|
|
103
146
|
function defineMessages(de, en) {
|
|
@@ -2777,7 +2820,8 @@ var shellMessages = defineMessages(
|
|
|
2777
2820
|
productionWarning: "Aktionen wirken sich sofort auf alle Mandanten aus.",
|
|
2778
2821
|
subtitle: "SuperAdmin \xB7 Plattform-Verwaltung",
|
|
2779
2822
|
roleBadge: "SUPER ADMIN",
|
|
2780
|
-
logout: "Abmelden"
|
|
2823
|
+
logout: "Abmelden",
|
|
2824
|
+
language: "Sprache"
|
|
2781
2825
|
},
|
|
2782
2826
|
drawer: {
|
|
2783
2827
|
docs: "Doku \xF6ffnen"
|
|
@@ -2845,7 +2889,8 @@ var shellMessages = defineMessages(
|
|
|
2845
2889
|
productionWarning: "Actions take effect immediately for all tenants.",
|
|
2846
2890
|
subtitle: "SuperAdmin \xB7 Platform administration",
|
|
2847
2891
|
roleBadge: "SUPER ADMIN",
|
|
2848
|
-
logout: "Sign out"
|
|
2892
|
+
logout: "Sign out",
|
|
2893
|
+
language: "Language"
|
|
2849
2894
|
},
|
|
2850
2895
|
drawer: {
|
|
2851
2896
|
docs: "Open documentation"
|
|
@@ -3119,16 +3164,33 @@ function resolveMessages(locale, overrides) {
|
|
|
3119
3164
|
}
|
|
3120
3165
|
|
|
3121
3166
|
// src/vue/use-super-admin-i18n.ts
|
|
3167
|
+
var SA_LOCALE_STORAGE_KEY = "sa:locale";
|
|
3122
3168
|
var SUPER_ADMIN_I18N_KEY = /* @__PURE__ */ Symbol.for(
|
|
3123
3169
|
"@saasicat/ui-vue/SUPER_ADMIN_I18N"
|
|
3124
3170
|
);
|
|
3125
3171
|
function createSuperAdminI18n(options = {}) {
|
|
3126
|
-
const locale = (0, import_vue.isRef)(options.locale) ? options.locale : (
|
|
3172
|
+
const locale = (0, import_vue.isRef)(options.locale) ? options.locale : createOwnedLocale(options);
|
|
3127
3173
|
const messages = (0, import_vue.computed)(
|
|
3128
3174
|
() => resolveMessages(locale.value, options.overrides?.[locale.value])
|
|
3129
3175
|
);
|
|
3130
3176
|
const intlLocale = (0, import_vue.computed)(() => SA_INTL_LOCALES[locale.value]);
|
|
3131
|
-
|
|
3177
|
+
const writable = !(0, import_vue.isReadonly)(locale);
|
|
3178
|
+
return {
|
|
3179
|
+
locale,
|
|
3180
|
+
messages,
|
|
3181
|
+
intlLocale,
|
|
3182
|
+
switcherEnabled: (options.switcher ?? true) && writable
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
function createOwnedLocale(options) {
|
|
3186
|
+
if (options.persist === false) {
|
|
3187
|
+
return (0, import_vue.ref)(options.locale ?? DEFAULT_SA_LOCALE);
|
|
3188
|
+
}
|
|
3189
|
+
const storage = options.storage ?? defaultKvStore();
|
|
3190
|
+
const stored = storage.get(SA_LOCALE_STORAGE_KEY);
|
|
3191
|
+
const locale = (0, import_vue.ref)(isSaLocale(stored) ? stored : options.locale ?? DEFAULT_SA_LOCALE);
|
|
3192
|
+
(0, import_vue.watch)(locale, (next) => storage.set(SA_LOCALE_STORAGE_KEY, next));
|
|
3193
|
+
return locale;
|
|
3132
3194
|
}
|
|
3133
3195
|
|
|
3134
3196
|
// src/quasar/notify.ts
|
package/dist/quasar/index.d.cts
CHANGED
|
@@ -2,8 +2,8 @@ import { Component, App } from 'vue';
|
|
|
2
2
|
import { QuasarPluginOptions } from 'quasar';
|
|
3
3
|
import { Pinia } from 'pinia';
|
|
4
4
|
import { RouteRecordRaw, RouterHistory, Router } from 'vue-router';
|
|
5
|
-
import { H as HttpClient } from '../messages-
|
|
6
|
-
import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, I as InstallPlugin, d as SuperAdminI18nOptions, e as SuperAdminI18n } from '../use-super-admin-i18n-
|
|
5
|
+
import { H as HttpClient } from '../messages-DetB75lX.cjs';
|
|
6
|
+
import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, I as InstallPlugin, d as SuperAdminI18nOptions, e as SuperAdminI18n } from '../use-super-admin-i18n-DokHhkS_.cjs';
|
|
7
7
|
import '@saasicat/types';
|
|
8
8
|
|
|
9
9
|
interface CreateSuperAdminAppOptions extends SuperAdminGuardOptions {
|
package/dist/quasar/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { Component, App } from 'vue';
|
|
|
2
2
|
import { QuasarPluginOptions } from 'quasar';
|
|
3
3
|
import { Pinia } from 'pinia';
|
|
4
4
|
import { RouteRecordRaw, RouterHistory, Router } from 'vue-router';
|
|
5
|
-
import { H as HttpClient } from '../messages-
|
|
6
|
-
import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, I as InstallPlugin, d as SuperAdminI18nOptions, e as SuperAdminI18n } from '../use-super-admin-i18n-
|
|
5
|
+
import { H as HttpClient } from '../messages-DetB75lX.js';
|
|
6
|
+
import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, I as InstallPlugin, d as SuperAdminI18nOptions, e as SuperAdminI18n } from '../use-super-admin-i18n-BPC-H9jz.js';
|
|
7
7
|
import '@saasicat/types';
|
|
8
8
|
|
|
9
9
|
interface CreateSuperAdminAppOptions extends SuperAdminGuardOptions {
|
package/dist/quasar/index.js
CHANGED
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
SUPER_ADMIN_NOTIFY_KEY,
|
|
11
11
|
buildNavigationGuard,
|
|
12
12
|
createSuperAdminI18n
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-MGKJCVDU.js";
|
|
14
14
|
import {
|
|
15
15
|
defaultHttpClient
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-TZ2NGQXK.js";
|
|
17
17
|
|
|
18
18
|
// src/quasar/create-super-admin-app.ts
|
|
19
19
|
import { createApp } from "vue";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Component, App, InjectionKey, Ref, ComputedRef } from 'vue';
|
|
2
2
|
import { NavigationGuardWithThis } from 'vue-router';
|
|
3
3
|
import { ActionKey, ComponentKey, AdminManifest } from '@saasicat/types';
|
|
4
|
-
import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaMessagesOverrides } from './messages-
|
|
4
|
+
import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaMessagesOverrides, K as KvStore } from './messages-DetB75lX.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* App-specific branding data that the platform `AdminLayout` and other
|
|
@@ -182,16 +182,42 @@ interface SuperAdminI18n {
|
|
|
182
182
|
messages: ComputedRef<SaMessages>;
|
|
183
183
|
/** BCP-47 tag for `Intl`/`toLocaleString` formatting in the active locale. */
|
|
184
184
|
intlLocale: ComputedRef<string>;
|
|
185
|
+
/**
|
|
186
|
+
* Whether the shell renders its language switcher. `false` when the app
|
|
187
|
+
* opted out or when `locale` cannot be written to — a switcher that cannot
|
|
188
|
+
* change anything is worse than none.
|
|
189
|
+
*/
|
|
190
|
+
switcherEnabled: boolean;
|
|
185
191
|
}
|
|
186
192
|
interface SuperAdminI18nOptions {
|
|
187
193
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
194
|
+
* Starting UI locale, default `'de'` — the shell's switcher lets the user
|
|
195
|
+
* pick another one, and a stored pick outranks this. Pass a `Ref` when the
|
|
196
|
+
* app wants to keep control (e.g. a user-profile setting).
|
|
190
197
|
*/
|
|
191
198
|
locale?: SaLocale | Ref<SaLocale>;
|
|
192
199
|
/** Per-locale string overrides layered over the platform catalog. */
|
|
193
200
|
overrides?: Partial<Record<SaLocale, SaMessagesOverrides>>;
|
|
201
|
+
/**
|
|
202
|
+
* Remembers the locale picked in the header switcher across reloads,
|
|
203
|
+
* default `true`. Ignored when `locale` is a `Ref`: the app owns that value
|
|
204
|
+
* and persists it wherever it belongs (user profile, its own store).
|
|
205
|
+
*/
|
|
206
|
+
persist?: boolean;
|
|
207
|
+
/**
|
|
208
|
+
* Storage adapter for `persist`. Defaults to `defaultKvStore()`. Pass a
|
|
209
|
+
* key-prefixing wrapper when several apps share one origin.
|
|
210
|
+
*/
|
|
211
|
+
storage?: KvStore;
|
|
212
|
+
/**
|
|
213
|
+
* Renders the language switcher in the shell chrome, default `true`. Set
|
|
214
|
+
* `false` for a deployment that ships one language. A readonly `locale`
|
|
215
|
+
* ref disables it on its own — no need to set this as well.
|
|
216
|
+
*/
|
|
217
|
+
switcher?: boolean;
|
|
194
218
|
}
|
|
219
|
+
/** `KvStore` key holding the locale the user picked in the switcher. */
|
|
220
|
+
declare const SA_LOCALE_STORAGE_KEY = "sa:locale";
|
|
195
221
|
declare const SUPER_ADMIN_I18N_KEY: InjectionKey<SuperAdminI18n>;
|
|
196
222
|
declare function createSuperAdminI18n(options?: SuperAdminI18nOptions): SuperAdminI18n;
|
|
197
223
|
/**
|
|
@@ -207,4 +233,4 @@ declare function useSuperAdminI18n(): SuperAdminI18n;
|
|
|
207
233
|
*/
|
|
208
234
|
declare function useSaMessages<K extends keyof SaMessages>(namespace: K): ComputedRef<SaMessages[K]>;
|
|
209
235
|
|
|
210
|
-
export { type ActionsMap as A, type ExtensionsMap as E, type InstallPlugin as I, type SuperAdminGuardOptions as S, type UiNotify as U, type SuperAdminBrand as a, type SuperAdminEndpoints as b, type SuperAdminLoginAdapter as c, type SuperAdminI18nOptions as d, type SuperAdminI18n as e, type ExtensionLoader as f,
|
|
236
|
+
export { type ActionsMap as A, type ExtensionsMap as E, type InstallPlugin as I, type SuperAdminGuardOptions as S, type UiNotify as U, type SuperAdminBrand as a, type SuperAdminEndpoints as b, type SuperAdminLoginAdapter as c, type SuperAdminI18nOptions as d, type SuperAdminI18n as e, type ExtensionLoader as f, SA_LOCALE_STORAGE_KEY as g, SUPER_ADMIN_ACTIONS_KEY as h, SUPER_ADMIN_BRAND_KEY as i, SUPER_ADMIN_ENDPOINTS_KEY as j, SUPER_ADMIN_EXTENSIONS_KEY as k, SUPER_ADMIN_HTTP_KEY as l, SUPER_ADMIN_I18N_KEY as m, SUPER_ADMIN_LOGIN_ADAPTER_KEY as n, SUPER_ADMIN_MANIFEST_KEY as o, SUPER_ADMIN_NOTIFY_KEY as p, type SuperAdminAuthGuardOptions as q, type SuperAdminLoginResult as r, type SuperAdminManifestGuardOptions as s, type UiNotifyKind as t, type UiNotifyOptions as u, buildNavigationGuard as v, createSuperAdminI18n as w, useSaMessages as x, useSuperAdminI18n as y };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Component, App, InjectionKey, Ref, ComputedRef } from 'vue';
|
|
2
2
|
import { NavigationGuardWithThis } from 'vue-router';
|
|
3
3
|
import { ActionKey, ComponentKey, AdminManifest } from '@saasicat/types';
|
|
4
|
-
import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaMessagesOverrides } from './messages-
|
|
4
|
+
import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaMessagesOverrides, K as KvStore } from './messages-DetB75lX.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* App-specific branding data that the platform `AdminLayout` and other
|
|
@@ -182,16 +182,42 @@ interface SuperAdminI18n {
|
|
|
182
182
|
messages: ComputedRef<SaMessages>;
|
|
183
183
|
/** BCP-47 tag for `Intl`/`toLocaleString` formatting in the active locale. */
|
|
184
184
|
intlLocale: ComputedRef<string>;
|
|
185
|
+
/**
|
|
186
|
+
* Whether the shell renders its language switcher. `false` when the app
|
|
187
|
+
* opted out or when `locale` cannot be written to — a switcher that cannot
|
|
188
|
+
* change anything is worse than none.
|
|
189
|
+
*/
|
|
190
|
+
switcherEnabled: boolean;
|
|
185
191
|
}
|
|
186
192
|
interface SuperAdminI18nOptions {
|
|
187
193
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
194
|
+
* Starting UI locale, default `'de'` — the shell's switcher lets the user
|
|
195
|
+
* pick another one, and a stored pick outranks this. Pass a `Ref` when the
|
|
196
|
+
* app wants to keep control (e.g. a user-profile setting).
|
|
190
197
|
*/
|
|
191
198
|
locale?: SaLocale | Ref<SaLocale>;
|
|
192
199
|
/** Per-locale string overrides layered over the platform catalog. */
|
|
193
200
|
overrides?: Partial<Record<SaLocale, SaMessagesOverrides>>;
|
|
201
|
+
/**
|
|
202
|
+
* Remembers the locale picked in the header switcher across reloads,
|
|
203
|
+
* default `true`. Ignored when `locale` is a `Ref`: the app owns that value
|
|
204
|
+
* and persists it wherever it belongs (user profile, its own store).
|
|
205
|
+
*/
|
|
206
|
+
persist?: boolean;
|
|
207
|
+
/**
|
|
208
|
+
* Storage adapter for `persist`. Defaults to `defaultKvStore()`. Pass a
|
|
209
|
+
* key-prefixing wrapper when several apps share one origin.
|
|
210
|
+
*/
|
|
211
|
+
storage?: KvStore;
|
|
212
|
+
/**
|
|
213
|
+
* Renders the language switcher in the shell chrome, default `true`. Set
|
|
214
|
+
* `false` for a deployment that ships one language. A readonly `locale`
|
|
215
|
+
* ref disables it on its own — no need to set this as well.
|
|
216
|
+
*/
|
|
217
|
+
switcher?: boolean;
|
|
194
218
|
}
|
|
219
|
+
/** `KvStore` key holding the locale the user picked in the switcher. */
|
|
220
|
+
declare const SA_LOCALE_STORAGE_KEY = "sa:locale";
|
|
195
221
|
declare const SUPER_ADMIN_I18N_KEY: InjectionKey<SuperAdminI18n>;
|
|
196
222
|
declare function createSuperAdminI18n(options?: SuperAdminI18nOptions): SuperAdminI18n;
|
|
197
223
|
/**
|
|
@@ -207,4 +233,4 @@ declare function useSuperAdminI18n(): SuperAdminI18n;
|
|
|
207
233
|
*/
|
|
208
234
|
declare function useSaMessages<K extends keyof SaMessages>(namespace: K): ComputedRef<SaMessages[K]>;
|
|
209
235
|
|
|
210
|
-
export { type ActionsMap as A, type ExtensionsMap as E, type InstallPlugin as I, type SuperAdminGuardOptions as S, type UiNotify as U, type SuperAdminBrand as a, type SuperAdminEndpoints as b, type SuperAdminLoginAdapter as c, type SuperAdminI18nOptions as d, type SuperAdminI18n as e, type ExtensionLoader as f,
|
|
236
|
+
export { type ActionsMap as A, type ExtensionsMap as E, type InstallPlugin as I, type SuperAdminGuardOptions as S, type UiNotify as U, type SuperAdminBrand as a, type SuperAdminEndpoints as b, type SuperAdminLoginAdapter as c, type SuperAdminI18nOptions as d, type SuperAdminI18n as e, type ExtensionLoader as f, SA_LOCALE_STORAGE_KEY as g, SUPER_ADMIN_ACTIONS_KEY as h, SUPER_ADMIN_BRAND_KEY as i, SUPER_ADMIN_ENDPOINTS_KEY as j, SUPER_ADMIN_EXTENSIONS_KEY as k, SUPER_ADMIN_HTTP_KEY as l, SUPER_ADMIN_I18N_KEY as m, SUPER_ADMIN_LOGIN_ADAPTER_KEY as n, SUPER_ADMIN_MANIFEST_KEY as o, SUPER_ADMIN_NOTIFY_KEY as p, type SuperAdminAuthGuardOptions as q, type SuperAdminLoginResult as r, type SuperAdminManifestGuardOptions as s, type UiNotifyKind as t, type UiNotifyOptions as u, buildNavigationGuard as v, createSuperAdminI18n as w, useSaMessages as x, useSuperAdminI18n as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasicat/ui-vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Vue 3 components, resource clients and composables for the SuperAdmin UI shell. Provides boot and manifest loaders, navigation, actions and standard pages.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"src"
|
|
54
54
|
],
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@saasicat/types": "^0.
|
|
56
|
+
"@saasicat/types": "^0.16.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"pinia": "^2.0.0 || ^3.0.0",
|
|
@@ -13,3 +13,18 @@ export const SA_INTL_LOCALES: Record<SaLocale, string> = {
|
|
|
13
13
|
de: 'de-DE',
|
|
14
14
|
en: 'en-US',
|
|
15
15
|
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Names for the language switcher. Endonyms — a language is listed in its own
|
|
19
|
+
* language, so the entries stay readable no matter which locale is active and
|
|
20
|
+
* never need translating.
|
|
21
|
+
*/
|
|
22
|
+
export const SA_LOCALE_LABELS: Record<SaLocale, string> = {
|
|
23
|
+
de: 'Deutsch',
|
|
24
|
+
en: 'English',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Narrows untrusted input (persisted preference, query parameter) to a locale. */
|
|
28
|
+
export function isSaLocale(value: unknown): value is SaLocale {
|
|
29
|
+
return typeof value === 'string' && (SA_LOCALES as readonly string[]).includes(value);
|
|
30
|
+
}
|
|
@@ -10,6 +10,7 @@ export const shellMessages = defineMessages(
|
|
|
10
10
|
subtitle: 'SuperAdmin · Plattform-Verwaltung',
|
|
11
11
|
roleBadge: 'SUPER ADMIN',
|
|
12
12
|
logout: 'Abmelden',
|
|
13
|
+
language: 'Sprache',
|
|
13
14
|
},
|
|
14
15
|
drawer: {
|
|
15
16
|
docs: 'Doku öffnen',
|
|
@@ -81,6 +82,7 @@ export const shellMessages = defineMessages(
|
|
|
81
82
|
subtitle: 'SuperAdmin · Platform administration',
|
|
82
83
|
roleBadge: 'SUPER ADMIN',
|
|
83
84
|
logout: 'Sign out',
|
|
85
|
+
language: 'Language',
|
|
84
86
|
},
|
|
85
87
|
drawer: {
|
|
86
88
|
docs: 'Open documentation',
|
package/src/client/types.ts
CHANGED
|
@@ -28,27 +28,64 @@ export interface KvStore {
|
|
|
28
28
|
remove(key: string): void;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/** Stand-in for when there is no usable `localStorage`. */
|
|
32
|
+
const NOOP_KV_STORE: KvStore = {
|
|
33
|
+
get: () => null,
|
|
34
|
+
set: () => {},
|
|
35
|
+
remove: () => {},
|
|
36
|
+
};
|
|
37
|
+
|
|
31
38
|
/**
|
|
32
|
-
* Returns a `KvStore` wrapper around `localStorage
|
|
33
|
-
* Tests may pass an in-memory stub.
|
|
39
|
+
* Returns a `KvStore` wrapper around `localStorage`, or a no-op store when the
|
|
40
|
+
* environment has none or refuses access. Tests may pass an in-memory stub.
|
|
41
|
+
*
|
|
42
|
+
* Every persistence built on this is a convenience — losing it must never take
|
|
43
|
+
* the app down, so no failure mode is allowed to escape.
|
|
34
44
|
*/
|
|
35
45
|
export function defaultKvStore(): KvStore {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
get: () => null,
|
|
40
|
-
set: () => {},
|
|
41
|
-
remove: () => {},
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
const ls = globalThis.localStorage;
|
|
46
|
+
const ls = resolveLocalStorage();
|
|
47
|
+
if (!ls) return NOOP_KV_STORE;
|
|
45
48
|
return {
|
|
46
|
-
get: (k) =>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
get: (k) => {
|
|
50
|
+
try {
|
|
51
|
+
return ls.getItem(k);
|
|
52
|
+
} catch {
|
|
53
|
+
// See resolveLocalStorage: access can be revoked per call too.
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
set: (k, v) => {
|
|
58
|
+
try {
|
|
59
|
+
ls.setItem(k, v);
|
|
60
|
+
} catch {
|
|
61
|
+
// Full quota or Safari's private mode — the value is simply
|
|
62
|
+
// not remembered.
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
remove: (k) => {
|
|
66
|
+
try {
|
|
67
|
+
ls.removeItem(k);
|
|
68
|
+
} catch {
|
|
69
|
+
// Nothing to clean up if the store is unreachable.
|
|
70
|
+
}
|
|
71
|
+
},
|
|
49
72
|
};
|
|
50
73
|
}
|
|
51
74
|
|
|
75
|
+
/**
|
|
76
|
+
* `localStorage` is a throwing getter, not a plain property: a browser that
|
|
77
|
+
* blocks site data (cookie blocking, a sandboxed iframe) raises `SecurityError`
|
|
78
|
+
* on read, and `typeof` evaluates the getter rather than guarding it. The
|
|
79
|
+
* access therefore has to happen inside `try`.
|
|
80
|
+
*/
|
|
81
|
+
function resolveLocalStorage(): Storage | null {
|
|
82
|
+
try {
|
|
83
|
+
return globalThis.localStorage ?? null;
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
52
89
|
/**
|
|
53
90
|
* Default `HttpClient` over `fetch`. Consumers pass their own variant
|
|
54
91
|
* through when they need auth headers / tenant headers / retry logic.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<q-btn
|
|
3
|
+
v-if="visible"
|
|
4
|
+
class="sa-locale-switcher"
|
|
5
|
+
flat
|
|
6
|
+
dense
|
|
7
|
+
no-caps
|
|
8
|
+
icon="language"
|
|
9
|
+
:label="SA_LOCALE_LABELS[locale]"
|
|
10
|
+
:aria-label="msg.header.language"
|
|
11
|
+
>
|
|
12
|
+
<q-menu auto-close anchor="bottom right" self="top right">
|
|
13
|
+
<q-list dense class="sa-locale-switcher__list">
|
|
14
|
+
<q-item
|
|
15
|
+
v-for="option in SA_LOCALES"
|
|
16
|
+
:key="option"
|
|
17
|
+
clickable
|
|
18
|
+
:active="option === locale"
|
|
19
|
+
@click="select(option)"
|
|
20
|
+
>
|
|
21
|
+
<q-item-section>{{ SA_LOCALE_LABELS[option] }}</q-item-section>
|
|
22
|
+
<q-item-section v-if="option === locale" side>
|
|
23
|
+
<q-icon name="check" size="16px" />
|
|
24
|
+
</q-item-section>
|
|
25
|
+
</q-item>
|
|
26
|
+
</q-list>
|
|
27
|
+
</q-menu>
|
|
28
|
+
</q-btn>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import { SA_LOCALES, SA_LOCALE_LABELS, type SaLocale } from '../client/i18n/index.js';
|
|
33
|
+
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
34
|
+
|
|
35
|
+
// Language switcher for the shell chrome. Writes straight to the shared i18n
|
|
36
|
+
// locale, so every catalog text, the sidebar labels and `Intl` formatting
|
|
37
|
+
// follow on the next render.
|
|
38
|
+
//
|
|
39
|
+
// The button inherits its color from the surrounding chrome (`currentColor`),
|
|
40
|
+
// so it needs no variant prop — each host sets the color it wants.
|
|
41
|
+
//
|
|
42
|
+
// It renders nothing when the app opted out of the switcher or handed over a
|
|
43
|
+
// locale it does not allow writing to; hosts therefore embed it unconditionally
|
|
44
|
+
// and never have to repeat that check.
|
|
45
|
+
|
|
46
|
+
const { locale, switcherEnabled } = useSuperAdminI18n();
|
|
47
|
+
|
|
48
|
+
// Only an explicit `false` hides the switcher. The injection key is a
|
|
49
|
+
// `Symbol.for`, so a context created by an older copy of this package resolves
|
|
50
|
+
// here just as well — and that one has no `switcherEnabled` at all. Treating
|
|
51
|
+
// the missing field as "off" would make every switcher vanish after such an
|
|
52
|
+
// upgrade, without a warning anywhere. Chrome fails open.
|
|
53
|
+
const visible = switcherEnabled !== false;
|
|
54
|
+
const msg = useSaMessages('shell');
|
|
55
|
+
|
|
56
|
+
function select(next: SaLocale): void {
|
|
57
|
+
locale.value = next;
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<style scoped>
|
|
62
|
+
.sa-locale-switcher {
|
|
63
|
+
color: inherit;
|
|
64
|
+
}
|
|
65
|
+
.sa-locale-switcher__list {
|
|
66
|
+
min-width: 160px;
|
|
67
|
+
}
|
|
68
|
+
</style>
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
</q-toolbar-title>
|
|
15
15
|
<q-space />
|
|
16
16
|
<slot name="header-actions" />
|
|
17
|
+
<LocaleSwitcher class="q-mr-sm" />
|
|
17
18
|
<q-badge class="sa-admin-badge q-mr-sm">{{ msg.header.roleBadge }}</q-badge>
|
|
18
19
|
<div class="sa-admin-user">
|
|
19
20
|
<q-avatar size="32px" class="sa-admin-user__avatar">
|
|
@@ -97,6 +98,7 @@ import { computed, inject, ref } from 'vue';
|
|
|
97
98
|
import { useRoute } from 'vue-router';
|
|
98
99
|
import type { AdminManifest, StandardPageKey } from '@saasicat/types';
|
|
99
100
|
import { buildRoutes, buildSidebar, defaultSectionOrder } from '../client/nav-builder.js';
|
|
101
|
+
import LocaleSwitcher from '../components/LocaleSwitcher.vue';
|
|
100
102
|
import { SUPER_ADMIN_BRAND_KEY, SUPER_ADMIN_MANIFEST_KEY } from '../vue/super-admin-context.js';
|
|
101
103
|
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
102
104
|
import { ADMIN_UI_VERSION } from '../client/version.js';
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
<div class="sa-login-brand__name">{{ brandName }}</div>
|
|
19
19
|
<div class="sa-login-brand__tag">{{ tagText }}</div>
|
|
20
20
|
</div>
|
|
21
|
+
<LocaleSwitcher class="sa-login-locale" />
|
|
21
22
|
</div>
|
|
22
23
|
|
|
23
24
|
<h1 class="sa-login-title">{{ msg.login.signIn }}</h1>
|
|
@@ -98,6 +99,7 @@ import {
|
|
|
98
99
|
} from '../vue/use-super-admin-context.js';
|
|
99
100
|
import { getJson } from '../client/http-json.js';
|
|
100
101
|
import { useSaMessages } from '../vue/use-super-admin-i18n.js';
|
|
102
|
+
import LocaleSwitcher from '../components/LocaleSwitcher.vue';
|
|
101
103
|
import SuperAdminSetupWizard from './SuperAdminSetupWizard.vue';
|
|
102
104
|
|
|
103
105
|
interface Props {
|
|
@@ -193,6 +195,13 @@ async function handleSubmit(): Promise<void> {
|
|
|
193
195
|
</script>
|
|
194
196
|
|
|
195
197
|
<style scoped>
|
|
198
|
+
.sa-login-locale {
|
|
199
|
+
/* In the card's flow rather than floating over it: a fixed overlay lands
|
|
200
|
+
on top of the centered card on short viewports. */
|
|
201
|
+
margin-left: auto;
|
|
202
|
+
align-self: flex-start;
|
|
203
|
+
color: var(--sa-login-tag-color, #475569);
|
|
204
|
+
}
|
|
196
205
|
.sa-login-wrap {
|
|
197
206
|
min-height: 100vh;
|
|
198
207
|
display: flex;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<div class="sa-setup-title">{{ msg.setup.title }}</div>
|
|
8
8
|
<div class="sa-setup-sub">{{ subtitleText }}</div>
|
|
9
9
|
</div>
|
|
10
|
+
<LocaleSwitcher class="sa-setup-locale" />
|
|
10
11
|
</div>
|
|
11
12
|
|
|
12
13
|
<q-banner v-if="errorMessage" class="sa-setup-error" rounded>
|
|
@@ -160,6 +161,7 @@ import {
|
|
|
160
161
|
import { HttpJsonError, postJson as httpPostJson } from '../client/http-json.js';
|
|
161
162
|
import { formatMessage } from '../client/i18n/format.js';
|
|
162
163
|
import { useSaMessages } from '../vue/use-super-admin-i18n.js';
|
|
164
|
+
import LocaleSwitcher from '../components/LocaleSwitcher.vue';
|
|
163
165
|
|
|
164
166
|
interface Props {
|
|
165
167
|
/** Display name + badge abbreviation (override the app branding, e.g. from PublicBoot). */
|
|
@@ -288,6 +290,11 @@ async function submitConfirm(): Promise<void> {
|
|
|
288
290
|
gap: 12px;
|
|
289
291
|
margin-bottom: 20px;
|
|
290
292
|
}
|
|
293
|
+
.sa-setup-locale {
|
|
294
|
+
margin-left: auto;
|
|
295
|
+
align-self: flex-start;
|
|
296
|
+
color: #475569;
|
|
297
|
+
}
|
|
291
298
|
.sa-setup-badge {
|
|
292
299
|
width: 44px;
|
|
293
300
|
height: 44px;
|