@owlmeans/web-panel 0.1.18-rc.62 → 0.1.18-rc.63
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 +2 -2
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/web-panel/SKILL.md +7 -3
- package/build/auth/entrypoints.d.ts +1 -9
- package/build/auth/entrypoints.d.ts.map +1 -1
- package/build/consent/index.d.ts +1 -1
- package/build/consent/index.d.ts.map +1 -1
- package/build/consent/index.js +1 -1
- package/build/consent/index.js.map +1 -1
- package/build/consent/language.d.ts +7 -17
- package/build/consent/language.d.ts.map +1 -1
- package/build/consent/language.js +7 -45
- package/build/consent/language.js.map +1 -1
- package/build/entrypoints.d.ts +1 -7
- package/build/entrypoints.d.ts.map +1 -1
- package/build/exports.d.ts +1 -0
- package/build/exports.d.ts.map +1 -1
- package/build/exports.js +1 -0
- package/build/exports.js.map +1 -1
- package/build/permission/i18n/be.json +13 -0
- package/build/permission/i18n/de.json +13 -0
- package/build/permission/i18n/en.json +13 -0
- package/build/permission/i18n/es.json +13 -0
- package/build/permission/i18n/fr.json +13 -0
- package/build/permission/i18n/pl.json +13 -0
- package/build/permission/i18n/ru.json +13 -0
- package/build/permission/i18n/uk.json +13 -0
- package/build/permission/i18n.d.ts +2 -0
- package/build/permission/i18n.d.ts.map +1 -0
- package/build/permission/i18n.js +13 -0
- package/build/permission/i18n.js.map +1 -0
- package/build/permission/index.d.ts +6 -0
- package/build/permission/index.d.ts.map +1 -0
- package/build/permission/index.js +27 -0
- package/build/permission/index.js.map +1 -0
- package/build/permission/toast.d.ts +9 -0
- package/build/permission/toast.d.ts.map +1 -0
- package/build/permission/toast.js +8 -0
- package/build/permission/toast.js.map +1 -0
- package/package.json +20 -19
- package/src/consent/index.ts +2 -2
- package/src/consent/language.ts +7 -48
- package/src/exports.ts +1 -0
- package/src/permission/i18n/be.json +13 -0
- package/src/permission/i18n/de.json +13 -0
- package/src/permission/i18n/en.json +13 -0
- package/src/permission/i18n/es.json +13 -0
- package/src/permission/i18n/fr.json +13 -0
- package/src/permission/i18n/pl.json +13 -0
- package/src/permission/i18n/ru.json +13 -0
- package/src/permission/i18n/uk.json +13 -0
- package/src/permission/i18n.ts +13 -0
- package/src/permission/index.ts +27 -0
- package/src/permission/toast.tsx +25 -0
- package/tests/consent-language.spec.ts +18 -69
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ but never started.
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
bun add @owlmeans/web-panel@^0.1.18-rc.
|
|
14
|
+
bun add @owlmeans/web-panel@^0.1.18-rc.63
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Peer requirements (the consuming app provides these): `react`, `react-dom`,
|
|
@@ -496,7 +496,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
496
496
|
your project's skill store (`.agents/skills/`):
|
|
497
497
|
|
|
498
498
|
```sh
|
|
499
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
499
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.40
|
|
500
500
|
```
|
|
501
501
|
|
|
502
502
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/web-panel",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-09-
|
|
4
|
+
"version": "0.1.18-rc.63",
|
|
5
|
+
"generatedAt": "2026-09-25T21:19:38.010Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/web-panel
|
|
9
9
|
|
|
10
10
|
**Layer:** Web (React)
|
|
11
|
-
**Install:** `"@owlmeans/web-panel": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/web-panel": "^0.1.18-rc.63"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Key Exports
|
|
14
14
|
|
|
@@ -330,6 +330,11 @@ export const MainLayout: FC<PropsWithChildren> = ({ children }) => <>
|
|
|
330
330
|
- `sonner` is a dependency of this package, so nothing is required of the consumer — but an app
|
|
331
331
|
raising its own toasts should declare `sonner` too, at a range that resolves to the same copy.
|
|
332
332
|
|
|
333
|
+
`appendPermissionDeniedToast(context)` plugs into the client entrypoint failure service and
|
|
334
|
+
shows a localized access-denied toast for errors carrying the server IAM marker. Mount `Toaster`
|
|
335
|
+
once as usual. The toast exposes the technical message and incident id in a separate details
|
|
336
|
+
section. The app supplies its locale through its i18n context.
|
|
337
|
+
|
|
333
338
|
### Reload prompt — `SocketReloadDialog`
|
|
334
339
|
|
|
335
340
|
`makeContext` calls `appendSocketStatus` from `@owlmeans/client-socket` unconditionally, and
|
|
@@ -499,9 +504,8 @@ menu has already mounted) and because React 18 StrictMode double-invokes mount/c
|
|
|
499
504
|
| Job | Import from `@owlmeans/web-panel/consent` | Where it goes |
|
|
500
505
|
|---|---|---|
|
|
501
506
|
| The consent dialog (and its floating re-open button) | `PanelCookieConsent` | Beside the router — a `PanelApp` child — once |
|
|
502
|
-
| Remember the language only while the visitor granted `functional` cookies | `installConsentLanguage()` | Once, in the bootstrap, BEFORE `prepareI18n` — wires `client-i18n`'s persistence guard, and applies a language that arrived with a link from another OwlMeans domain once the grant is saved |
|
|
503
507
|
| The "Cookie settings" control in a footer or menu | `PanelConsentMenuWidget` (`label`, `className`, `onSelect?` — defaults to `openConsent('reopen')`) | Inside the host's own footer/menu |
|
|
504
|
-
| Hiding the floating button while that control is reachable | `useConsentMenuPresence()` | Called by the always-mounted component that renders the control |
|
|
508
|
+
| Hiding the floating button while that control is reachable | `useConsentMenuPresence()` | Called by the always-mounted component that renders the control — mounted ONLY while the control can actually be reached: a component that is merely hidden by CSS (`xl:hidden`) is still mounted and still claims presence, so the floating button vanishes with nothing else to open the dialog; render it conditionally instead (viable's header mounts its hamburger menu only below the full-row breakpoint) |
|
|
505
509
|
| Registering the presence service | `appendConsentWidgetService(context)` | The app's `context.ts` |
|
|
506
510
|
|
|
507
511
|
A footer is the simplest host, because it is always mounted — the control and the presence call
|
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
export declare const entrypoints: (import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").
|
|
2
|
-
body: import("@owlmeans/auth").AllowanceRequest;
|
|
3
|
-
}, import("@owlmeans/auth").AllowanceResponse>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
4
|
-
body: import("@owlmeans/auth").AuthCredentials;
|
|
5
|
-
}, import("@owlmeans/auth").AuthToken>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
6
|
-
query: Partial<import("@owlmeans/auth").AuthToken>;
|
|
7
|
-
}, undefined>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
8
|
-
query: import("@owlmeans/auth").AuthToken;
|
|
9
|
-
}, undefined>>)[];
|
|
1
|
+
export declare const entrypoints: (import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<any> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<import("@owlmeans/entrypoint").OpenRequest, import("@owlmeans/entrypoint").OpenValue>>)[];
|
|
10
2
|
//# sourceMappingURL=entrypoints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../../src/auth/entrypoints.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../../src/auth/entrypoints.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,mRAAuC,CAAA"}
|
package/build/consent/index.d.ts
CHANGED
|
@@ -5,6 +5,6 @@ export { installConsentLanguage } from './language.js';
|
|
|
5
5
|
export { createConsentWidgetService, appendConsentWidgetService, CONSENT_WIDGET_STATE } from './service.js';
|
|
6
6
|
export { CONSENT_WIDGET_SERVICE } from './consts.js';
|
|
7
7
|
export type { ConsentWidgetPresenceRecord, ConsentWidgetService, ConsentWidgetServiceAppend } from './types.js';
|
|
8
|
-
export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL,
|
|
8
|
+
export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, CONSENT_EVENT, consentBootstrapScript, } from '@owlmeans/web-consent';
|
|
9
9
|
export type { ConsentCategory, ConsentMenuWidgetProps, ConsentOptions, ConsentRecord, ConsentSignal, ConsentState, CookieConsentProps, CookiePolicyProps, } from '@owlmeans/web-consent';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,YAAY,EACV,2BAA2B,EAAE,oBAAoB,EAAE,0BAA0B,EAC9E,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EACtE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,0BAA0B,EACnE,WAAW,EAAE,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,YAAY,EACV,2BAA2B,EAAE,oBAAoB,EAAE,0BAA0B,EAC9E,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EACtE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,0BAA0B,EACnE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EACpE,aAAa,EACb,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,eAAe,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EACrF,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,GACpD,MAAM,uBAAuB,CAAA"}
|
package/build/consent/index.js
CHANGED
|
@@ -4,5 +4,5 @@ export * from './presence.js';
|
|
|
4
4
|
export { installConsentLanguage } from './language.js';
|
|
5
5
|
export { createConsentWidgetService, appendConsentWidgetService, CONSENT_WIDGET_STATE } from './service.js';
|
|
6
6
|
export { CONSENT_WIDGET_SERVICE } from './consts.js';
|
|
7
|
-
export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL,
|
|
7
|
+
export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, CONSENT_EVENT, consentBootstrapScript, } from '@owlmeans/web-consent';
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAIpD,OAAO,EACL,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EACtE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,0BAA0B,EACnE,WAAW,EAAE,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAIpD,OAAO,EACL,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EACtE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,0BAA0B,EACnE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EACpE,aAAa,EACb,sBAAsB,GACvB,MAAM,uBAAuB,CAAA"}
|
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
import type { ConsentOptions } from '@owlmeans/web-consent';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* @deprecated The interface language is strictly necessary storage: `@owlmeans/client-i18n` writes
|
|
4
|
+
* it on every switch and reads it at every start, and `@owlmeans/consent` stores a language that
|
|
5
|
+
* arrives with a link — none of it waits for a cookie decision, so there is nothing left to bind.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* calls `setLanguage`) stops writing `owlmeans-lng` until functional is granted, and a stored
|
|
10
|
-
* language counts as absent while it is not. **Call it BEFORE `prepareI18n`**: the initial
|
|
11
|
-
* language is resolved from storage right there.
|
|
12
|
-
* 2. On every consent change (`CONSENT_EVENT`) writes the choice the visitor made while storage was
|
|
13
|
-
* refused, if it is allowed now — a person who picked a language, then answered the dialog with
|
|
14
|
-
* "accept all", keeps it.
|
|
15
|
-
* 3. On `CONSENT_LANGUAGE_EVENT` — a language that arrived with a link and was waiting for the grant —
|
|
16
|
-
* switches to it now, unless the person has already picked one in this page's life: what they
|
|
17
|
-
* chose outranks what a link carried, and that choice is re-written over the store's own write.
|
|
18
|
-
*
|
|
19
|
-
* Returns a function that undoes all three. Safe on the server (only the guard is installed).
|
|
7
|
+
* Kept as a no-op (returning a no-op cleanup) only so an application written against the earlier,
|
|
8
|
+
* consent-gated API — a generated target that floats to a newer release — still builds. Remove the
|
|
9
|
+
* call.
|
|
20
10
|
*/
|
|
21
|
-
export declare const installConsentLanguage: (
|
|
11
|
+
export declare const installConsentLanguage: (_opts?: ConsentOptions) => (() => void);
|
|
22
12
|
//# sourceMappingURL=language.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"language.d.ts","sourceRoot":"","sources":["../../src/consent/language.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"language.d.ts","sourceRoot":"","sources":["../../src/consent/language.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,WAAY,cAAc,KAAG,CAAC,MAAM,IAAI,CAAoB,CAAA"}
|
|
@@ -1,49 +1,11 @@
|
|
|
1
|
-
import { getExplicitLanguage, persistLanguage, setLanguage, setLanguagePersistence } from '@owlmeans/client-i18n';
|
|
2
|
-
import { CONSENT_EVENT, CONSENT_LANGUAGE_EVENT, functionalGranted } from '@owlmeans/web-consent';
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* @deprecated The interface language is strictly necessary storage: `@owlmeans/client-i18n` writes
|
|
3
|
+
* it on every switch and reads it at every start, and `@owlmeans/consent` stores a language that
|
|
4
|
+
* arrives with a link — none of it waits for a cookie decision, so there is nothing left to bind.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* calls `setLanguage`) stops writing `owlmeans-lng` until functional is granted, and a stored
|
|
11
|
-
* language counts as absent while it is not. **Call it BEFORE `prepareI18n`**: the initial
|
|
12
|
-
* language is resolved from storage right there.
|
|
13
|
-
* 2. On every consent change (`CONSENT_EVENT`) writes the choice the visitor made while storage was
|
|
14
|
-
* refused, if it is allowed now — a person who picked a language, then answered the dialog with
|
|
15
|
-
* "accept all", keeps it.
|
|
16
|
-
* 3. On `CONSENT_LANGUAGE_EVENT` — a language that arrived with a link and was waiting for the grant —
|
|
17
|
-
* switches to it now, unless the person has already picked one in this page's life: what they
|
|
18
|
-
* chose outranks what a link carried, and that choice is re-written over the store's own write.
|
|
19
|
-
*
|
|
20
|
-
* Returns a function that undoes all three. Safe on the server (only the guard is installed).
|
|
6
|
+
* Kept as a no-op (returning a no-op cleanup) only so an application written against the earlier,
|
|
7
|
+
* consent-gated API — a generated target that floats to a newer release — still builds. Remove the
|
|
8
|
+
* call.
|
|
21
9
|
*/
|
|
22
|
-
export const installConsentLanguage = (
|
|
23
|
-
setLanguagePersistence(() => functionalGranted(opts));
|
|
24
|
-
if (typeof window === 'undefined') {
|
|
25
|
-
return () => setLanguagePersistence(null);
|
|
26
|
-
}
|
|
27
|
-
const onConsent = () => {
|
|
28
|
-
persistLanguage();
|
|
29
|
-
};
|
|
30
|
-
const onLanguage = (event) => {
|
|
31
|
-
const carried = event.detail?.language;
|
|
32
|
-
const chosen = getExplicitLanguage();
|
|
33
|
-
const language = chosen ?? carried;
|
|
34
|
-
if (typeof language !== 'string' || language === '') {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
setLanguage(language).catch((error) => {
|
|
38
|
-
console.error('[i18n] could not apply the language that was waiting for consent', error);
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
window.addEventListener(CONSENT_EVENT, onConsent);
|
|
42
|
-
window.addEventListener(CONSENT_LANGUAGE_EVENT, onLanguage);
|
|
43
|
-
return () => {
|
|
44
|
-
window.removeEventListener(CONSENT_EVENT, onConsent);
|
|
45
|
-
window.removeEventListener(CONSENT_LANGUAGE_EVENT, onLanguage);
|
|
46
|
-
setLanguagePersistence(null);
|
|
47
|
-
};
|
|
48
|
-
};
|
|
10
|
+
export const installConsentLanguage = (_opts) => () => undefined;
|
|
49
11
|
//# sourceMappingURL=language.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"language.js","sourceRoot":"","sources":["../../src/consent/language.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"language.js","sourceRoot":"","sources":["../../src/consent/language.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAAsB,EAAgB,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAA"}
|
package/build/entrypoints.d.ts
CHANGED
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
export declare const entrypoints:
|
|
2
|
-
query: import("@owlmeans/auth").AuthToken;
|
|
3
|
-
}, undefined>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
4
|
-
query: import("@owlmeans/auth-common").SurrogateQuery;
|
|
5
|
-
}, undefined>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
6
|
-
body: import("@owlmeans/auth").AuthToken;
|
|
7
|
-
}, import("@owlmeans/auth").AuthToken>>)[];
|
|
1
|
+
export declare const entrypoints: any[];
|
|
8
2
|
//# sourceMappingURL=entrypoints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../src/entrypoints.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../src/entrypoints.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,OAA4C,CAAA"}
|
package/build/exports.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { config } from '@owlmeans/client-context';
|
|
|
4
4
|
export { service } from '@owlmeans/config';
|
|
5
5
|
export { addWebService } from '@owlmeans/client-config';
|
|
6
6
|
export { bind, bindAll, bindScreen, provideRequest, stab } from '@owlmeans/client-entrypoint';
|
|
7
|
+
export { appendPermissionDeniedToast, PermissionDeniedToast } from './permission/index.js';
|
|
7
8
|
export type { ClientEntrypoint as Module } from '@owlmeans/client-entrypoint';
|
|
8
9
|
export { route as croute } from '@owlmeans/client-route';
|
|
9
10
|
export { route, frontend } from '@owlmeans/route';
|
package/build/exports.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAC5G,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACxF,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,YAAY,EAAE,gBAAgB,IAAI,MAAM,EAAE,MAAM,6BAA6B,CAAA;AAC7E,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AAEhG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnG,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACvE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,WAAW,IAAI,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AACtF,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAC5G,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACxF,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC1F,YAAY,EAAE,gBAAgB,IAAI,MAAM,EAAE,MAAM,6BAA6B,CAAA;AAC7E,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AAEhG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnG,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACvE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,WAAW,IAAI,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AACtF,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA"}
|
package/build/exports.js
CHANGED
|
@@ -3,6 +3,7 @@ export { config } from '@owlmeans/client-context';
|
|
|
3
3
|
export { service } from '@owlmeans/config';
|
|
4
4
|
export { addWebService } from '@owlmeans/client-config';
|
|
5
5
|
export { bind, bindAll, bindScreen, provideRequest, stab } from '@owlmeans/client-entrypoint';
|
|
6
|
+
export { appendPermissionDeniedToast, PermissionDeniedToast } from './permission/index.js';
|
|
6
7
|
export { route as croute } from '@owlmeans/client-route';
|
|
7
8
|
export { route, frontend } from '@owlmeans/route';
|
|
8
9
|
export { DEFAULT_ALIAS as DAUTH_GUARD, bindExternalAuthentication } from '@owlmeans/client-auth';
|
package/build/exports.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAE5G,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAE5G,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAE1F,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AAEhG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEpE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAG/D,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnG,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACvE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,WAAW,IAAI,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AACtF,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Дзеянне адхілена",
|
|
3
|
+
"description": "Сервер адхіліў дзеянне паводле бягучых правіл доступу. Калі вы чакалі доступу, звярніцеся да адміністратара.",
|
|
4
|
+
"technical-details": "Тэхнічныя падрабязнасці",
|
|
5
|
+
"endpoint": "Канцавы пункт",
|
|
6
|
+
"status": "Статус",
|
|
7
|
+
"incident": "Ідэнтыфікатар інцыдэнту",
|
|
8
|
+
"area-title": "Доступ забаронены",
|
|
9
|
+
"area-description": "Ваш уліковы запіс не можа адкрыць гэты раздзел. Калі вы чакалі доступу, звярніцеся да адміністратара.",
|
|
10
|
+
"area": "Раздзел",
|
|
11
|
+
"required-permissions": "Патрэбныя дазволы",
|
|
12
|
+
"logout": "Выйсці"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Aktion verweigert",
|
|
3
|
+
"description": "Der Server hat diese Aktion gemäß den aktuellen Berechtigungen verweigert. Wenn Sie Zugriff erwartet haben, fragen Sie eine Administratorin oder einen Administrator.",
|
|
4
|
+
"technical-details": "Technische Details",
|
|
5
|
+
"endpoint": "Endpunkt",
|
|
6
|
+
"status": "Status",
|
|
7
|
+
"incident": "Vorfall-ID",
|
|
8
|
+
"area-title": "Zugriff verweigert",
|
|
9
|
+
"area-description": "Ihr Konto kann diesen Bereich nicht öffnen. Wenn Sie Zugriff erwartet haben, wenden Sie sich an die Administration.",
|
|
10
|
+
"area": "Bereich",
|
|
11
|
+
"required-permissions": "Erforderliche Berechtigungen",
|
|
12
|
+
"logout": "Abmelden"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Action denied",
|
|
3
|
+
"description": "The server denied this action under the current permission rules. If you expected access, ask an administrator.",
|
|
4
|
+
"technical-details": "Technical details",
|
|
5
|
+
"endpoint": "Endpoint",
|
|
6
|
+
"status": "Status",
|
|
7
|
+
"incident": "Incident ID",
|
|
8
|
+
"area-title": "Access denied",
|
|
9
|
+
"area-description": "Your account cannot open this area. Ask an administrator if you expected access.",
|
|
10
|
+
"area": "Area",
|
|
11
|
+
"required-permissions": "Required permissions",
|
|
12
|
+
"logout": "Log out"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Acción denegada",
|
|
3
|
+
"description": "El servidor denegó esta acción según las reglas de permisos actuales. Si esperaba tener acceso, consulte a un administrador.",
|
|
4
|
+
"technical-details": "Detalles técnicos",
|
|
5
|
+
"endpoint": "Punto de acceso",
|
|
6
|
+
"status": "Estado",
|
|
7
|
+
"incident": "ID del incidente",
|
|
8
|
+
"area-title": "Acceso denegado",
|
|
9
|
+
"area-description": "Su cuenta no puede abrir esta sección. Si esperaba tener acceso, consulte a un administrador.",
|
|
10
|
+
"area": "Sección",
|
|
11
|
+
"required-permissions": "Permisos necesarios",
|
|
12
|
+
"logout": "Cerrar sesión"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Action refusée",
|
|
3
|
+
"description": "Le serveur a refusé cette action selon les autorisations actuelles. Si vous pensiez avoir accès, contactez un administrateur.",
|
|
4
|
+
"technical-details": "Détails techniques",
|
|
5
|
+
"endpoint": "Point de terminaison",
|
|
6
|
+
"status": "Statut",
|
|
7
|
+
"incident": "ID de l’incident",
|
|
8
|
+
"area-title": "Accès refusé",
|
|
9
|
+
"area-description": "Votre compte ne peut pas ouvrir cette section. Si vous pensiez avoir accès, contactez un administrateur.",
|
|
10
|
+
"area": "Section",
|
|
11
|
+
"required-permissions": "Autorisations requises",
|
|
12
|
+
"logout": "Se déconnecter"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Działanie odrzucone",
|
|
3
|
+
"description": "Serwer odrzucił to działanie zgodnie z obecnymi uprawnieniami. Jeśli oczekujesz dostępu, skontaktuj się z administratorem.",
|
|
4
|
+
"technical-details": "Szczegóły techniczne",
|
|
5
|
+
"endpoint": "Punkt końcowy",
|
|
6
|
+
"status": "Status",
|
|
7
|
+
"incident": "Identyfikator incydentu",
|
|
8
|
+
"area-title": "Odmowa dostępu",
|
|
9
|
+
"area-description": "Twoje konto nie może otworzyć tego obszaru. Jeśli oczekujesz dostępu, skontaktuj się z administratorem.",
|
|
10
|
+
"area": "Obszar",
|
|
11
|
+
"required-permissions": "Wymagane uprawnienia",
|
|
12
|
+
"logout": "Wyloguj się"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Действие отклонено",
|
|
3
|
+
"description": "Сервер отклонил действие согласно текущим правилам доступа. Если вам нужен доступ, обратитесь к администратору.",
|
|
4
|
+
"technical-details": "Технические подробности",
|
|
5
|
+
"endpoint": "Конечная точка",
|
|
6
|
+
"status": "Статус",
|
|
7
|
+
"incident": "Идентификатор инцидента",
|
|
8
|
+
"area-title": "Доступ запрещён",
|
|
9
|
+
"area-description": "Ваша учётная запись не может открыть этот раздел. Если вам нужен доступ, обратитесь к администратору.",
|
|
10
|
+
"area": "Раздел",
|
|
11
|
+
"required-permissions": "Необходимые разрешения",
|
|
12
|
+
"logout": "Выйти"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Дію відхилено",
|
|
3
|
+
"description": "Сервер відхилив цю дію згідно з поточними правилами доступу. Якщо ви очікували доступу, зверніться до адміністратора.",
|
|
4
|
+
"technical-details": "Технічні подробиці",
|
|
5
|
+
"endpoint": "Кінцева точка",
|
|
6
|
+
"status": "Статус",
|
|
7
|
+
"incident": "Ідентифікатор інциденту",
|
|
8
|
+
"area-title": "Доступ заборонено",
|
|
9
|
+
"area-description": "Ваш обліковий запис не може відкрити цей розділ. Якщо ви очікували доступу, зверніться до адміністратора.",
|
|
10
|
+
"area": "Розділ",
|
|
11
|
+
"required-permissions": "Потрібні дозволи",
|
|
12
|
+
"logout": "Вийти"
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/permission/i18n.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { addI18nLib } from '@owlmeans/i18n';
|
|
2
|
+
import en from './i18n/en.json';
|
|
3
|
+
import pl from './i18n/pl.json';
|
|
4
|
+
import ru from './i18n/ru.json';
|
|
5
|
+
import be from './i18n/be.json';
|
|
6
|
+
import uk from './i18n/uk.json';
|
|
7
|
+
import es from './i18n/es.json';
|
|
8
|
+
import de from './i18n/de.json';
|
|
9
|
+
import fr from './i18n/fr.json';
|
|
10
|
+
for (const [language, chunk] of Object.entries({ en, pl, ru, be, uk, es, de, fr })) {
|
|
11
|
+
addI18nLib(language, 'permission-denied', chunk);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=i18n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/permission/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC/B,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC/B,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC/B,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC/B,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC/B,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC/B,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC/B,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAE/B,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IACnF,UAAU,CAAC,QAAQ,EAAE,mBAAmB,EAAE,KAAK,CAAC,CAAA;AAClD,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import './i18n.js';
|
|
2
|
+
import type { BasicContext } from '@owlmeans/context';
|
|
3
|
+
export { PermissionDeniedToast } from './toast.js';
|
|
4
|
+
/** Opt-in adapter for the context's entrypoint failures; mount one Toaster in the app shell. */
|
|
5
|
+
export declare const appendPermissionDeniedToast: <T extends BasicContext<any>>(context: T) => T;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/permission/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,CAAA;AAGlB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElD,gGAAgG;AAChG,eAAO,MAAM,2BAA2B,GAAI,CAAC,SAAS,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,KAAG,CAerF,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import './i18n.js';
|
|
2
|
+
import { createElement } from 'react';
|
|
3
|
+
import { toast } from 'sonner';
|
|
4
|
+
import { ensureEntrypointFailureService } from '@owlmeans/client-entrypoint';
|
|
5
|
+
import { httpStatusOf, incidentIdOf, isAccessDenied } from '@owlmeans/api/status';
|
|
6
|
+
import { PermissionDeniedToast } from './toast.js';
|
|
7
|
+
export { PermissionDeniedToast } from './toast.js';
|
|
8
|
+
/** Opt-in adapter for the context's entrypoint failures; mount one Toaster in the app shell. */
|
|
9
|
+
export const appendPermissionDeniedToast = (context) => {
|
|
10
|
+
const seen = new Map();
|
|
11
|
+
ensureEntrypointFailureService(context).registerPlugin({
|
|
12
|
+
alias: 'web-panel:permission-denied-toast',
|
|
13
|
+
onFailure: ({ alias, error }) => {
|
|
14
|
+
if (!isAccessDenied(error))
|
|
15
|
+
return;
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
if (now - (seen.get(alias) ?? 0) < 2000)
|
|
18
|
+
return;
|
|
19
|
+
seen.set(alias, now);
|
|
20
|
+
toast.warning(createElement(PermissionDeniedToast, {
|
|
21
|
+
alias, status: httpStatusOf(error), incidentId: incidentIdOf(error),
|
|
22
|
+
}));
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return context;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/permission/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,CAAA;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAE9B,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAA;AAC5E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElD,gGAAgG;AAChG,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAA8B,OAAU,EAAK,EAAE;IACxF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAA;IACtC,8BAA8B,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC;QACrD,KAAK,EAAE,mCAAmC;QAC1C,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;YAC9B,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;gBAAE,OAAM;YAClC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI;gBAAE,OAAM;YAC/C,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;YACpB,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,qBAAqB,EAAE;gBACjD,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,YAAY,CAAC,KAAK,CAAC;aACpE,CAAC,CAAC,CAAA;QACL,CAAC;KACF,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
export interface PermissionDeniedToastProps {
|
|
3
|
+
alias: string;
|
|
4
|
+
status: number | null;
|
|
5
|
+
incidentId: string | null;
|
|
6
|
+
}
|
|
7
|
+
/** A refusal is actionable context for the user, with diagnostics outside the explanation. */
|
|
8
|
+
export declare const PermissionDeniedToast: FC<PermissionDeniedToastProps>;
|
|
9
|
+
//# sourceMappingURL=toast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/permission/toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAG/B,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC,0BAA0B,CAchE,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useI18nLib } from '@owlmeans/client-i18n';
|
|
3
|
+
/** A refusal is actionable context for the user, with diagnostics outside the explanation. */
|
|
4
|
+
export const PermissionDeniedToast = ({ alias, status, incidentId }) => {
|
|
5
|
+
const t = useI18nLib('permission-denied');
|
|
6
|
+
return _jsxs("div", { className: "space-y-2 max-w-sm text-sm", children: [_jsx("p", { className: "font-semibold", children: t('title') }), _jsx("p", { children: t('description') }), _jsxs("details", { className: "text-xs", children: [_jsx("summary", { className: "cursor-pointer", children: t('technical-details') }), _jsxs("dl", { className: "mt-2 grid grid-cols-[auto_1fr] gap-x-2 break-all", children: [_jsx("dt", { children: t('endpoint') }), _jsx("dd", { children: alias }), status != null && _jsxs(_Fragment, { children: [_jsx("dt", { children: t('status') }), _jsx("dd", { children: status })] }), incidentId != null && _jsxs(_Fragment, { children: [_jsx("dt", { children: t('incident') }), _jsx("dd", { children: incidentId })] })] })] })] });
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=toast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toast.js","sourceRoot":"","sources":["../../src/permission/toast.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAQlD,8FAA8F;AAC9F,MAAM,CAAC,MAAM,qBAAqB,GAAmC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE;IACrG,MAAM,CAAC,GAAG,UAAU,CAAC,mBAAmB,CAAC,CAAA;IACzC,OAAO,eAAK,SAAS,EAAC,4BAA4B,aAChD,YAAG,SAAS,EAAC,eAAe,YAAE,CAAC,CAAC,OAAO,CAAC,GAAK,EAC7C,sBAAI,CAAC,CAAC,aAAa,CAAC,GAAK,EACzB,mBAAS,SAAS,EAAC,SAAS,aAC1B,kBAAS,SAAS,EAAC,gBAAgB,YAAE,CAAC,CAAC,mBAAmB,CAAC,GAAW,EACtE,cAAI,SAAS,EAAC,kDAAkD,aAC9D,uBAAK,CAAC,CAAC,UAAU,CAAC,GAAM,EAAA,uBAAK,KAAK,GAAM,EACvC,MAAM,IAAI,IAAI,IAAI,8BAAE,uBAAK,CAAC,CAAC,QAAQ,CAAC,GAAM,EAAA,uBAAK,MAAM,GAAM,IAAG,EAC9D,UAAU,IAAI,IAAI,IAAI,8BAAE,uBAAK,CAAC,CAAC,UAAU,CAAC,GAAM,EAAA,uBAAK,UAAU,GAAM,IAAG,IACtE,IACG,IACN,CAAA;AACR,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/web-panel",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.63",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -51,20 +51,21 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@hookform/resolvers": "^5.2.2",
|
|
54
|
-
"@owlmeans/api-config-client": "^0.1.18-rc.
|
|
54
|
+
"@owlmeans/api-config-client": "^0.1.18-rc.37",
|
|
55
55
|
"@owlmeans/auth": "^0.1.18-rc.36",
|
|
56
|
-
"@owlmeans/auth-common": "^0.1.18-rc.
|
|
57
|
-
"@owlmeans/basic-envelope": "^0.1.18-rc.
|
|
58
|
-
"@owlmeans/client": "^0.1.18-rc.
|
|
59
|
-
"@owlmeans/client-auth": "^0.1.18-rc.
|
|
56
|
+
"@owlmeans/auth-common": "^0.1.18-rc.39",
|
|
57
|
+
"@owlmeans/basic-envelope": "^0.1.18-rc.38",
|
|
58
|
+
"@owlmeans/client": "^0.1.18-rc.43",
|
|
59
|
+
"@owlmeans/client-auth": "^0.1.18-rc.50",
|
|
60
60
|
"@owlmeans/client-config": "^0.1.18-rc.38",
|
|
61
|
-
"@owlmeans/client-context": "^0.1.18-rc.
|
|
62
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
63
|
-
"@owlmeans/
|
|
64
|
-
"@owlmeans/client-
|
|
65
|
-
"@owlmeans/client-
|
|
66
|
-
"@owlmeans/client-
|
|
67
|
-
"@owlmeans/client-
|
|
61
|
+
"@owlmeans/client-context": "^0.1.18-rc.40",
|
|
62
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.39",
|
|
63
|
+
"@owlmeans/api": "^0.1.18-rc.39",
|
|
64
|
+
"@owlmeans/client-flow": "^0.1.18-rc.46",
|
|
65
|
+
"@owlmeans/client-i18n": "^0.1.18-rc.42",
|
|
66
|
+
"@owlmeans/client-panel": "^0.1.18-rc.50",
|
|
67
|
+
"@owlmeans/client-route": "^0.1.18-rc.40",
|
|
68
|
+
"@owlmeans/client-socket": "^0.1.18-rc.41",
|
|
68
69
|
"@owlmeans/config": "^0.1.18-rc.38",
|
|
69
70
|
"@owlmeans/context": "^0.1.18-rc.32",
|
|
70
71
|
"@owlmeans/entrypoint": "^0.1.18-rc.37",
|
|
@@ -73,11 +74,11 @@
|
|
|
73
74
|
"@owlmeans/i18n": "^0.1.18-rc.37",
|
|
74
75
|
"@owlmeans/route": "^0.1.18-rc.33",
|
|
75
76
|
"@owlmeans/state": "^0.1.18-rc.35",
|
|
76
|
-
"@owlmeans/web-client": "^0.1.18-rc.
|
|
77
|
-
"@owlmeans/web-consent": "^0.1.18-rc.
|
|
78
|
-
"@owlmeans/web-db": "^0.1.18-rc.
|
|
79
|
-
"@owlmeans/web-flow": "^0.1.18-rc.
|
|
80
|
-
"@owlmeans/web-router": "^0.1.18-rc.
|
|
77
|
+
"@owlmeans/web-client": "^0.1.18-rc.53",
|
|
78
|
+
"@owlmeans/web-consent": "^0.1.18-rc.34",
|
|
79
|
+
"@owlmeans/web-db": "^0.1.18-rc.40",
|
|
80
|
+
"@owlmeans/web-flow": "^0.1.18-rc.45",
|
|
81
|
+
"@owlmeans/web-router": "^0.1.18-rc.41",
|
|
81
82
|
"i18next-browser-languagedetector": "^8.0.0",
|
|
82
83
|
"qrcode.react": "^4.2.0",
|
|
83
84
|
"react-google-recaptcha": "^3.1.0",
|
|
@@ -103,7 +104,7 @@
|
|
|
103
104
|
},
|
|
104
105
|
"devDependencies": {
|
|
105
106
|
"@owlmeans/dep-config": "workspace:*",
|
|
106
|
-
"@owlmeans/test-ui": "^0.1.18-rc.
|
|
107
|
+
"@owlmeans/test-ui": "^0.1.18-rc.41",
|
|
107
108
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
108
109
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
109
110
|
"@radix-ui/react-label": "^2.1.0",
|
package/src/consent/index.ts
CHANGED
|
@@ -10,8 +10,8 @@ export type {
|
|
|
10
10
|
export {
|
|
11
11
|
useConsent, useConsentCategory, consentStore, openConsent, isConsented,
|
|
12
12
|
readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES,
|
|
13
|
-
CONSENT_KEY, CONSENT_ESSENTIAL,
|
|
14
|
-
CONSENT_EVENT,
|
|
13
|
+
CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING,
|
|
14
|
+
CONSENT_EVENT,
|
|
15
15
|
consentBootstrapScript,
|
|
16
16
|
} from '@owlmeans/web-consent'
|
|
17
17
|
export type {
|
package/src/consent/language.ts
CHANGED
|
@@ -1,53 +1,12 @@
|
|
|
1
|
-
import { getExplicitLanguage, persistLanguage, setLanguage, setLanguagePersistence } from '@owlmeans/client-i18n'
|
|
2
|
-
import { CONSENT_EVENT, CONSENT_LANGUAGE_EVENT, functionalGranted } from '@owlmeans/web-consent'
|
|
3
1
|
import type { ConsentOptions } from '@owlmeans/web-consent'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* @deprecated The interface language is strictly necessary storage: `@owlmeans/client-i18n` writes
|
|
5
|
+
* it on every switch and reads it at every start, and `@owlmeans/consent` stores a language that
|
|
6
|
+
* arrives with a link — none of it waits for a cookie decision, so there is nothing left to bind.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* calls `setLanguage`) stops writing `owlmeans-lng` until functional is granted, and a stored
|
|
13
|
-
* language counts as absent while it is not. **Call it BEFORE `prepareI18n`**: the initial
|
|
14
|
-
* language is resolved from storage right there.
|
|
15
|
-
* 2. On every consent change (`CONSENT_EVENT`) writes the choice the visitor made while storage was
|
|
16
|
-
* refused, if it is allowed now — a person who picked a language, then answered the dialog with
|
|
17
|
-
* "accept all", keeps it.
|
|
18
|
-
* 3. On `CONSENT_LANGUAGE_EVENT` — a language that arrived with a link and was waiting for the grant —
|
|
19
|
-
* switches to it now, unless the person has already picked one in this page's life: what they
|
|
20
|
-
* chose outranks what a link carried, and that choice is re-written over the store's own write.
|
|
21
|
-
*
|
|
22
|
-
* Returns a function that undoes all three. Safe on the server (only the guard is installed).
|
|
8
|
+
* Kept as a no-op (returning a no-op cleanup) only so an application written against the earlier,
|
|
9
|
+
* consent-gated API — a generated target that floats to a newer release — still builds. Remove the
|
|
10
|
+
* call.
|
|
23
11
|
*/
|
|
24
|
-
export const installConsentLanguage = (
|
|
25
|
-
setLanguagePersistence(() => functionalGranted(opts))
|
|
26
|
-
|
|
27
|
-
if (typeof window === 'undefined') {
|
|
28
|
-
return () => setLanguagePersistence(null)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const onConsent = (): void => {
|
|
32
|
-
persistLanguage()
|
|
33
|
-
}
|
|
34
|
-
const onLanguage = (event: Event): void => {
|
|
35
|
-
const carried = (event as CustomEvent<{ language?: string }>).detail?.language
|
|
36
|
-
const chosen = getExplicitLanguage()
|
|
37
|
-
const language = chosen ?? carried
|
|
38
|
-
if (typeof language !== 'string' || language === '') {
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
setLanguage(language).catch((error: unknown) => {
|
|
42
|
-
console.error('[i18n] could not apply the language that was waiting for consent', error)
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
window.addEventListener(CONSENT_EVENT, onConsent)
|
|
46
|
-
window.addEventListener(CONSENT_LANGUAGE_EVENT, onLanguage)
|
|
47
|
-
|
|
48
|
-
return () => {
|
|
49
|
-
window.removeEventListener(CONSENT_EVENT, onConsent)
|
|
50
|
-
window.removeEventListener(CONSENT_LANGUAGE_EVENT, onLanguage)
|
|
51
|
-
setLanguagePersistence(null)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
12
|
+
export const installConsentLanguage = (_opts?: ConsentOptions): (() => void) => () => undefined
|
package/src/exports.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { config } from '@owlmeans/client-context'
|
|
|
5
5
|
export { service } from '@owlmeans/config'
|
|
6
6
|
export { addWebService } from '@owlmeans/client-config'
|
|
7
7
|
export { bind, bindAll, bindScreen, provideRequest, stab } from '@owlmeans/client-entrypoint'
|
|
8
|
+
export { appendPermissionDeniedToast, PermissionDeniedToast } from './permission/index.js'
|
|
8
9
|
export type { ClientEntrypoint as Module } from '@owlmeans/client-entrypoint'
|
|
9
10
|
export { route as croute } from '@owlmeans/client-route'
|
|
10
11
|
export { route, frontend } from '@owlmeans/route'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Дзеянне адхілена",
|
|
3
|
+
"description": "Сервер адхіліў дзеянне паводле бягучых правіл доступу. Калі вы чакалі доступу, звярніцеся да адміністратара.",
|
|
4
|
+
"technical-details": "Тэхнічныя падрабязнасці",
|
|
5
|
+
"endpoint": "Канцавы пункт",
|
|
6
|
+
"status": "Статус",
|
|
7
|
+
"incident": "Ідэнтыфікатар інцыдэнту",
|
|
8
|
+
"area-title": "Доступ забаронены",
|
|
9
|
+
"area-description": "Ваш уліковы запіс не можа адкрыць гэты раздзел. Калі вы чакалі доступу, звярніцеся да адміністратара.",
|
|
10
|
+
"area": "Раздзел",
|
|
11
|
+
"required-permissions": "Патрэбныя дазволы",
|
|
12
|
+
"logout": "Выйсці"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Aktion verweigert",
|
|
3
|
+
"description": "Der Server hat diese Aktion gemäß den aktuellen Berechtigungen verweigert. Wenn Sie Zugriff erwartet haben, fragen Sie eine Administratorin oder einen Administrator.",
|
|
4
|
+
"technical-details": "Technische Details",
|
|
5
|
+
"endpoint": "Endpunkt",
|
|
6
|
+
"status": "Status",
|
|
7
|
+
"incident": "Vorfall-ID",
|
|
8
|
+
"area-title": "Zugriff verweigert",
|
|
9
|
+
"area-description": "Ihr Konto kann diesen Bereich nicht öffnen. Wenn Sie Zugriff erwartet haben, wenden Sie sich an die Administration.",
|
|
10
|
+
"area": "Bereich",
|
|
11
|
+
"required-permissions": "Erforderliche Berechtigungen",
|
|
12
|
+
"logout": "Abmelden"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Action denied",
|
|
3
|
+
"description": "The server denied this action under the current permission rules. If you expected access, ask an administrator.",
|
|
4
|
+
"technical-details": "Technical details",
|
|
5
|
+
"endpoint": "Endpoint",
|
|
6
|
+
"status": "Status",
|
|
7
|
+
"incident": "Incident ID",
|
|
8
|
+
"area-title": "Access denied",
|
|
9
|
+
"area-description": "Your account cannot open this area. Ask an administrator if you expected access.",
|
|
10
|
+
"area": "Area",
|
|
11
|
+
"required-permissions": "Required permissions",
|
|
12
|
+
"logout": "Log out"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Acción denegada",
|
|
3
|
+
"description": "El servidor denegó esta acción según las reglas de permisos actuales. Si esperaba tener acceso, consulte a un administrador.",
|
|
4
|
+
"technical-details": "Detalles técnicos",
|
|
5
|
+
"endpoint": "Punto de acceso",
|
|
6
|
+
"status": "Estado",
|
|
7
|
+
"incident": "ID del incidente",
|
|
8
|
+
"area-title": "Acceso denegado",
|
|
9
|
+
"area-description": "Su cuenta no puede abrir esta sección. Si esperaba tener acceso, consulte a un administrador.",
|
|
10
|
+
"area": "Sección",
|
|
11
|
+
"required-permissions": "Permisos necesarios",
|
|
12
|
+
"logout": "Cerrar sesión"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Action refusée",
|
|
3
|
+
"description": "Le serveur a refusé cette action selon les autorisations actuelles. Si vous pensiez avoir accès, contactez un administrateur.",
|
|
4
|
+
"technical-details": "Détails techniques",
|
|
5
|
+
"endpoint": "Point de terminaison",
|
|
6
|
+
"status": "Statut",
|
|
7
|
+
"incident": "ID de l’incident",
|
|
8
|
+
"area-title": "Accès refusé",
|
|
9
|
+
"area-description": "Votre compte ne peut pas ouvrir cette section. Si vous pensiez avoir accès, contactez un administrateur.",
|
|
10
|
+
"area": "Section",
|
|
11
|
+
"required-permissions": "Autorisations requises",
|
|
12
|
+
"logout": "Se déconnecter"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Działanie odrzucone",
|
|
3
|
+
"description": "Serwer odrzucił to działanie zgodnie z obecnymi uprawnieniami. Jeśli oczekujesz dostępu, skontaktuj się z administratorem.",
|
|
4
|
+
"technical-details": "Szczegóły techniczne",
|
|
5
|
+
"endpoint": "Punkt końcowy",
|
|
6
|
+
"status": "Status",
|
|
7
|
+
"incident": "Identyfikator incydentu",
|
|
8
|
+
"area-title": "Odmowa dostępu",
|
|
9
|
+
"area-description": "Twoje konto nie może otworzyć tego obszaru. Jeśli oczekujesz dostępu, skontaktuj się z administratorem.",
|
|
10
|
+
"area": "Obszar",
|
|
11
|
+
"required-permissions": "Wymagane uprawnienia",
|
|
12
|
+
"logout": "Wyloguj się"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Действие отклонено",
|
|
3
|
+
"description": "Сервер отклонил действие согласно текущим правилам доступа. Если вам нужен доступ, обратитесь к администратору.",
|
|
4
|
+
"technical-details": "Технические подробности",
|
|
5
|
+
"endpoint": "Конечная точка",
|
|
6
|
+
"status": "Статус",
|
|
7
|
+
"incident": "Идентификатор инцидента",
|
|
8
|
+
"area-title": "Доступ запрещён",
|
|
9
|
+
"area-description": "Ваша учётная запись не может открыть этот раздел. Если вам нужен доступ, обратитесь к администратору.",
|
|
10
|
+
"area": "Раздел",
|
|
11
|
+
"required-permissions": "Необходимые разрешения",
|
|
12
|
+
"logout": "Выйти"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Дію відхилено",
|
|
3
|
+
"description": "Сервер відхилив цю дію згідно з поточними правилами доступу. Якщо ви очікували доступу, зверніться до адміністратора.",
|
|
4
|
+
"technical-details": "Технічні подробиці",
|
|
5
|
+
"endpoint": "Кінцева точка",
|
|
6
|
+
"status": "Статус",
|
|
7
|
+
"incident": "Ідентифікатор інциденту",
|
|
8
|
+
"area-title": "Доступ заборонено",
|
|
9
|
+
"area-description": "Ваш обліковий запис не може відкрити цей розділ. Якщо ви очікували доступу, зверніться до адміністратора.",
|
|
10
|
+
"area": "Розділ",
|
|
11
|
+
"required-permissions": "Потрібні дозволи",
|
|
12
|
+
"logout": "Вийти"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { addI18nLib } from '@owlmeans/i18n'
|
|
2
|
+
import en from './i18n/en.json'
|
|
3
|
+
import pl from './i18n/pl.json'
|
|
4
|
+
import ru from './i18n/ru.json'
|
|
5
|
+
import be from './i18n/be.json'
|
|
6
|
+
import uk from './i18n/uk.json'
|
|
7
|
+
import es from './i18n/es.json'
|
|
8
|
+
import de from './i18n/de.json'
|
|
9
|
+
import fr from './i18n/fr.json'
|
|
10
|
+
|
|
11
|
+
for (const [language, chunk] of Object.entries({ en, pl, ru, be, uk, es, de, fr })) {
|
|
12
|
+
addI18nLib(language, 'permission-denied', chunk)
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import './i18n.js'
|
|
2
|
+
import { createElement } from 'react'
|
|
3
|
+
import { toast } from 'sonner'
|
|
4
|
+
import type { BasicContext } from '@owlmeans/context'
|
|
5
|
+
import { ensureEntrypointFailureService } from '@owlmeans/client-entrypoint'
|
|
6
|
+
import { httpStatusOf, incidentIdOf, isAccessDenied } from '@owlmeans/api/status'
|
|
7
|
+
import { PermissionDeniedToast } from './toast.js'
|
|
8
|
+
|
|
9
|
+
export { PermissionDeniedToast } from './toast.js'
|
|
10
|
+
|
|
11
|
+
/** Opt-in adapter for the context's entrypoint failures; mount one Toaster in the app shell. */
|
|
12
|
+
export const appendPermissionDeniedToast = <T extends BasicContext<any>>(context: T): T => {
|
|
13
|
+
const seen = new Map<string, number>()
|
|
14
|
+
ensureEntrypointFailureService(context).registerPlugin({
|
|
15
|
+
alias: 'web-panel:permission-denied-toast',
|
|
16
|
+
onFailure: ({ alias, error }) => {
|
|
17
|
+
if (!isAccessDenied(error)) return
|
|
18
|
+
const now = Date.now()
|
|
19
|
+
if (now - (seen.get(alias) ?? 0) < 2000) return
|
|
20
|
+
seen.set(alias, now)
|
|
21
|
+
toast.warning(createElement(PermissionDeniedToast, {
|
|
22
|
+
alias, status: httpStatusOf(error), incidentId: incidentIdOf(error),
|
|
23
|
+
}))
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
return context
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FC } from 'react'
|
|
2
|
+
import { useI18nLib } from '@owlmeans/client-i18n'
|
|
3
|
+
|
|
4
|
+
export interface PermissionDeniedToastProps {
|
|
5
|
+
alias: string
|
|
6
|
+
status: number | null
|
|
7
|
+
incidentId: string | null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** A refusal is actionable context for the user, with diagnostics outside the explanation. */
|
|
11
|
+
export const PermissionDeniedToast: FC<PermissionDeniedToastProps> = ({ alias, status, incidentId }) => {
|
|
12
|
+
const t = useI18nLib('permission-denied')
|
|
13
|
+
return <div className="space-y-2 max-w-sm text-sm">
|
|
14
|
+
<p className="font-semibold">{t('title')}</p>
|
|
15
|
+
<p>{t('description')}</p>
|
|
16
|
+
<details className="text-xs">
|
|
17
|
+
<summary className="cursor-pointer">{t('technical-details')}</summary>
|
|
18
|
+
<dl className="mt-2 grid grid-cols-[auto_1fr] gap-x-2 break-all">
|
|
19
|
+
<dt>{t('endpoint')}</dt><dd>{alias}</dd>
|
|
20
|
+
{status != null && <><dt>{t('status')}</dt><dd>{status}</dd></>}
|
|
21
|
+
{incidentId != null && <><dt>{t('incident')}</dt><dd>{incidentId}</dd></>}
|
|
22
|
+
</dl>
|
|
23
|
+
</details>
|
|
24
|
+
</div>
|
|
25
|
+
}
|
|
@@ -1,82 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { CONSENT_EVENT, CONSENT_KEY, CONSENT_LANGUAGE_EVENT } from '@owlmeans/web-consent'
|
|
1
|
+
import { beforeEach, describe, expect, test } from 'bun:test'
|
|
2
|
+
import { setLanguage } from '@owlmeans/client-i18n'
|
|
4
3
|
import { installConsentLanguage } from '../src/consent/language.js'
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
const env = () => {
|
|
5
|
+
describe('installConsentLanguage — a deprecated no-op', () => {
|
|
8
6
|
const store = new Map<string, string>()
|
|
9
|
-
const listeners = new Map<string, Set<(e: any) => void>>()
|
|
10
|
-
;(globalThis as any).localStorage = {
|
|
11
|
-
getItem: (k: string) => store.get(k) ?? null,
|
|
12
|
-
setItem: (k: string, v: string) => { store.set(k, v) },
|
|
13
|
-
removeItem: (k: string) => { store.delete(k) },
|
|
14
|
-
}
|
|
15
|
-
;(globalThis as any).window = {
|
|
16
|
-
addEventListener: (t: string, f: any) => { (listeners.get(t) ?? listeners.set(t, new Set()).get(t)!).add(f) },
|
|
17
|
-
removeEventListener: (t: string, f: any) => { listeners.get(t)?.delete(f) },
|
|
18
|
-
dispatchEvent: (e: any) => { listeners.get(e.type)?.forEach(f => f(e)) },
|
|
19
|
-
}
|
|
20
|
-
;(globalThis as any).CustomEvent = class { constructor(public type: string, public init: any) {} get detail() { return this.init?.detail } }
|
|
21
7
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
beforeEach(() => { e = env(); off = installConsentLanguage() })
|
|
31
|
-
afterEach(() => { off(); setLanguagePersistence(null); delete (globalThis as any).window })
|
|
32
|
-
|
|
33
|
-
test('the switcher\'s choice is not remembered without a functional grant', async () => {
|
|
34
|
-
await setLanguage('pl')
|
|
35
|
-
|
|
36
|
-
expect(e.store.has('owlmeans-lng')).toBe(false)
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
store.clear()
|
|
10
|
+
;(globalThis as any).localStorage = {
|
|
11
|
+
getItem: (k: string) => store.get(k) ?? null,
|
|
12
|
+
setItem: (k: string, v: string) => { store.set(k, v) },
|
|
13
|
+
removeItem: (k: string) => { store.delete(k) },
|
|
14
|
+
}
|
|
37
15
|
})
|
|
38
16
|
|
|
39
|
-
test('
|
|
40
|
-
|
|
41
|
-
record(true)
|
|
42
|
-
e.emit(CONSENT_EVENT, { record: {} })
|
|
17
|
+
test('returns a cleanup that can be called, more than once', () => {
|
|
18
|
+
const off = installConsentLanguage()
|
|
43
19
|
|
|
44
|
-
expect(
|
|
20
|
+
expect(typeof off).toBe('function')
|
|
21
|
+
expect(off()).toBeUndefined()
|
|
22
|
+
expect(off()).toBeUndefined()
|
|
45
23
|
})
|
|
46
24
|
|
|
47
|
-
test('a
|
|
25
|
+
test('changes nothing about the language: a choice is stored either way, whatever the decision', async () => {
|
|
26
|
+
installConsentLanguage()
|
|
48
27
|
await setLanguage('pl')
|
|
49
|
-
record(false)
|
|
50
|
-
e.emit(CONSENT_EVENT, { record: {} })
|
|
51
|
-
|
|
52
|
-
expect(e.store.has('owlmeans-lng')).toBe(false)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('a language that was waiting for the grant is applied and stored when it arrives', async () => {
|
|
56
|
-
record(true)
|
|
57
|
-
e.emit(CONSENT_LANGUAGE_EVENT, { language: 'de' })
|
|
58
|
-
await new Promise(resolve => setTimeout(resolve, 0))
|
|
59
|
-
|
|
60
|
-
expect(e.store.get('owlmeans-lng')).toBe('de')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
test('what the person picked in this page life outranks the language the link carried', async () => {
|
|
64
|
-
await setLanguage('fr')
|
|
65
|
-
record(true)
|
|
66
|
-
e.emit(CONSENT_EVENT, { record: {} })
|
|
67
|
-
e.emit(CONSENT_LANGUAGE_EVENT, { language: 'de' })
|
|
68
|
-
await new Promise(resolve => setTimeout(resolve, 0))
|
|
69
|
-
|
|
70
|
-
expect(e.store.get('owlmeans-lng')).toBe('fr')
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
test('the returned function removes the listeners and the guard', async () => {
|
|
74
|
-
off()
|
|
75
|
-
expect(e.listeners.get(CONSENT_EVENT)?.size ?? 0).toBe(0)
|
|
76
|
-
expect(e.listeners.get(CONSENT_LANGUAGE_EVENT)?.size ?? 0).toBe(0)
|
|
77
28
|
|
|
78
|
-
|
|
79
|
-
expect(e.store.get('owlmeans-lng')).toBe('es')
|
|
80
|
-
expect(persistLanguage()).toBe(false)
|
|
29
|
+
expect(store.get('owlmeans-lng')).toBe('pl')
|
|
81
30
|
})
|
|
82
31
|
})
|