@owlmeans/web-panel 0.1.18-rc.55 → 0.1.18-rc.57
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 +3 -3
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/web-panel/SKILL.md +19 -24
- package/build/components/login/screen.d.ts.map +1 -1
- package/build/components/login/screen.js +3 -4
- package/build/components/login/screen.js.map +1 -1
- package/build/components/login/terms.d.ts +0 -8
- package/build/components/login/terms.d.ts.map +1 -1
- package/build/components/login/terms.js +23 -42
- package/build/components/login/terms.js.map +1 -1
- package/build/components/socket/i18n/be.json +4 -2
- package/build/components/socket/i18n/de.json +4 -2
- package/build/components/socket/i18n/en.json +4 -2
- package/build/components/socket/i18n/es.json +4 -2
- package/build/components/socket/i18n/fr.json +4 -2
- package/build/components/socket/i18n/pl.json +4 -2
- package/build/components/socket/i18n/ru.json +4 -2
- package/build/components/socket/i18n/uk.json +4 -2
- package/build/components/socket/reload-dialog.d.ts +8 -4
- package/build/components/socket/reload-dialog.d.ts.map +1 -1
- package/build/components/socket/reload-dialog.js +15 -7
- package/build/components/socket/reload-dialog.js.map +1 -1
- package/build/types.d.ts +1 -1
- package/package.json +29 -29
- package/src/components/login/screen.tsx +2 -4
- package/src/components/login/terms.tsx +33 -80
- package/src/components/socket/i18n/be.json +4 -2
- package/src/components/socket/i18n/de.json +4 -2
- package/src/components/socket/i18n/en.json +4 -2
- package/src/components/socket/i18n/es.json +4 -2
- package/src/components/socket/i18n/fr.json +4 -2
- package/src/components/socket/i18n/pl.json +4 -2
- package/src/components/socket/i18n/ru.json +4 -2
- package/src/components/socket/i18n/uk.json +4 -2
- package/src/components/socket/reload-dialog.tsx +23 -7
- package/src/types.ts +1 -1
- package/tests/harness/mount.tsx +14 -17
- package/tests/login.spec.ts +2 -74
- package/tests/socket-dialog.spec.ts +85 -0
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.57
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Peer requirements (the consuming app provides these): `react`, `react-dom`,
|
|
@@ -86,7 +86,7 @@ import { commonConfig, MY_APP_WEB } from 'my-app-common'
|
|
|
86
86
|
import type { Config } from './types.js'
|
|
87
87
|
|
|
88
88
|
const cfg: Config = config(MY_APP_WEB, commonConfig as Config)
|
|
89
|
-
// Block the screen with a "reload the page" prompt once
|
|
89
|
+
// Block the screen with a "try again / reload the page" prompt once a socket has given up reconnecting.
|
|
90
90
|
cfg.socket = { ...cfg.socket, reloadDialog: true }
|
|
91
91
|
|
|
92
92
|
export default cfg
|
|
@@ -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.34
|
|
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.57",
|
|
5
|
+
"generatedAt": "2026-09-22T20:45:24.070Z",
|
|
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.57"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Key Exports
|
|
14
14
|
|
|
@@ -19,9 +19,9 @@ user-invocable: false
|
|
|
19
19
|
| `TopNav` / `SideNav` / `MobileNav` / `Footer` | The shell's pieces, mountable on their own |
|
|
20
20
|
| `Toaster` | The application's toast surface — mounted once, in the layout |
|
|
21
21
|
| `ThemeToggle` / `useColorScheme` | The light/dark switcher (`ThemeToggleProps`, `ThemeToggleLabels`) and the hook behind it (`ColorSchemeModel`) — see *Light and dark* below |
|
|
22
|
-
| `SocketReloadDialog` | The global "
|
|
22
|
+
| `SocketReloadDialog` | The global "connection lost — try again / reload" prompt (opt in via `cfg.socket.reloadDialog`) `PanelApp` mounts automatically — see below. The hooks it reads, `useSocketStatus` and `useSocketRetry`, are imported from `@owlmeans/client-socket`, not from this package |
|
|
23
23
|
| `Link` | An `<a>` addressing an entrypoint alias (or a literal `src`), with the label taken from i18n |
|
|
24
|
-
| `LoginScreen` / `LocalizedLoginScreen` / `appendLoginScreen` | The identity-provider choice screen — see `login-methods
|
|
24
|
+
| `LoginScreen` / `LocalizedLoginScreen` / `appendLoginScreen` | The identity-provider choice screen — see `login-methods` |
|
|
25
25
|
| `render(context, opts?)` | Mounts the tree inside `PanelApp`, with the browser language detector installed on the i18n instance. `opts` is `RenderOptions` plus `rootClassName` |
|
|
26
26
|
| `PanelApp` | That wrapper on its own — the themed root `div` plus the i18n provider — for a host that mounts the tree itself |
|
|
27
27
|
| `useContext<C, T>()` | The current context, from React. `AppContext` adds `context.flow()` over `@owlmeans/web-client`'s |
|
|
@@ -341,12 +341,22 @@ on every navigation. Neither does anything unless an app opts in:
|
|
|
341
341
|
cfg.socket = { reloadDialog: true }
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
-
Once
|
|
344
|
+
Once any `ws()`/`useWs()` connection in the app has exhausted its own retry budget
|
|
345
345
|
(`useSocketStatus() === 'lost'`), a global, blocking `AlertDialog` covers the screen — no Escape,
|
|
346
|
-
no outside click,
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
(`
|
|
346
|
+
no outside click, two actions:
|
|
347
|
+
|
|
348
|
+
- **"Try again"** (`data-socket-retry-action`) — `useSocketRetry().retry()` revives every lost
|
|
349
|
+
connection (see the `client-socket` skill). The dialog stays up with the button disabled and
|
|
350
|
+
reading "Reconnecting…" while they retry, closes once the aggregate is `'online'`, and offers
|
|
351
|
+
both buttons again if it falls back to `'lost'`.
|
|
352
|
+
- **"Reload page"** (`data-socket-reload-action`) — `window.location.reload()`, the fallback that
|
|
353
|
+
always works.
|
|
354
|
+
|
|
355
|
+
The status service also retries by itself when the tab becomes visible, the window gains focus or
|
|
356
|
+
the browser comes back online, so a socket that died in a background tab is usually back before
|
|
357
|
+
anyone presses anything — an open dialog shows "Reconnecting…" through that too. Strings are
|
|
358
|
+
lib-tier (`useI18nLib('socket', 'reload')`: `title`, `description`, `action`, `retry`,
|
|
359
|
+
`retrying`), 8 languages, under `src/components/socket/i18n/`.
|
|
350
360
|
|
|
351
361
|
Leave `cfg.socket.reloadDialog` unset (or `false`) for an app that would rather show its own
|
|
352
362
|
inline "reconnecting…" state — `useSocketStatus()` from `@owlmeans/client-socket` serves that,
|
|
@@ -411,21 +421,6 @@ import { Form, TextInput, SubmitButton, Button } from '@owlmeans/web-panel'
|
|
|
411
421
|
`contained` and the rest `outlined`, calling `onSelect(option)`. `name` prefixes each option's
|
|
412
422
|
label key as `<name>.<option>`.
|
|
413
423
|
|
|
414
|
-
### The terms confirmation — `LoginTerms`
|
|
415
|
-
|
|
416
|
-
`components/login/terms.tsx` renders `LoginTermsModel` (`@owlmeans/client-panel/auth`) via
|
|
417
|
-
`termsSentence` (`@owlmeans/client-auth/login`) rather than re-deriving link/label pairs itself.
|
|
418
|
-
**`[data-login-terms]` marks exactly one checkbox, always** — an e2e suite elsewhere in the platform
|
|
419
|
-
treats it as a strict locator, so a change here must never add a second one, whatever
|
|
420
|
-
billing/product/custom documents a configuration adds. `[data-login-privacy]` is a SIBLING
|
|
421
|
-
paragraph of the checkbox's `<label>`, never nested inside it — a privacy disclosure is not
|
|
422
|
-
something the checkbox consents to. A linked document/notice fragment carries
|
|
423
|
-
`data-login-document="<key>"`. `[data-login-revised]` renders only when the resolved terms carry a
|
|
424
|
-
`revisedAt` (i.e. the configuration set `showRevision: true` and at least one document has its own
|
|
425
|
-
revision date). `LoginTerms` takes `locale?: string` — from `LoginScreen`'s own `locale` prop, never
|
|
426
|
-
read from context directly — for `Intl.ListFormat` and a custom document's locale-keyed `labelMap`.
|
|
427
|
-
Pinned by `tests/login.spec.ts` → the "with billing, product and custom documents configured" block.
|
|
428
|
-
|
|
429
424
|
## Subpath: `./consent`
|
|
430
425
|
|
|
431
426
|
`PanelCookieConsent` and `PanelCookiePolicy` — `@owlmeans/web-consent`'s components bound to this
|
|
@@ -536,7 +531,7 @@ in a linked workspace, so it is the reliable scan target in both modes.
|
|
|
536
531
|
## Depends On
|
|
537
532
|
|
|
538
533
|
- `@owlmeans/web-client`, `@owlmeans/client-panel`, `@owlmeans/client-i18n`, `@owlmeans/web-router`
|
|
539
|
-
- `@owlmeans/client-socket` — `appendSocketStatus`, `useSocketStatus`, behind `SocketReloadDialog`
|
|
534
|
+
- `@owlmeans/client-socket` — `appendSocketStatus`, `useSocketStatus`, `useSocketRetry`, behind `SocketReloadDialog`
|
|
540
535
|
- Peers (app-provided): `react`, `react-dom`, `react-hook-form`, `tailwindcss`, `tailwind-merge`,
|
|
541
536
|
`clsx`, `class-variance-authority`, `lucide-react`, `ajv`, and the `@radix-ui/react-*` primitives
|
|
542
537
|
(`alert-dialog`, `dialog`, `label`, `navigation-menu`, `progress`, `separator`, `slot`). No MUI,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../../src/components/login/screen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAQ/B,OAAO,KAAK,EAAe,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAWhF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CA4F5C,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../../src/components/login/screen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAQ/B,OAAO,KAAK,EAAe,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAWhF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CA4F5C,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,gBAAgB,CAOrD,CAAA"}
|
|
@@ -4,7 +4,7 @@ import { Loader2 } from 'lucide-react';
|
|
|
4
4
|
import { Button } from '../../@/components/ui/button.js';
|
|
5
5
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '../../@/components/ui/card.js';
|
|
6
6
|
import { cn } from '../../@/lib/utils.js';
|
|
7
|
-
import { useI18nLib
|
|
7
|
+
import { useI18nLib } from '@owlmeans/client-i18n';
|
|
8
8
|
import { useLoginMethods } from '@owlmeans/client-panel/auth';
|
|
9
9
|
import { loginAttemptError } from '@owlmeans/client-auth/login';
|
|
10
10
|
import { LoginMethodIcon } from './icons.js';
|
|
@@ -56,7 +56,7 @@ export const LoginScreen = props => {
|
|
|
56
56
|
// visibly actionable independently of a consumer's shadcn setup.
|
|
57
57
|
className: cn('w-full justify-center gap-2 cursor-pointer', model.blocked && 'opacity-60'), autoFocus: method.id === model.primary?.id, onClick: () => model.select(method), children: [model.busy === method.id
|
|
58
58
|
? _jsx(Loader2, { className: "size-4 animate-spin" })
|
|
59
|
-
: _jsx(LoginMethodIcon, { name: method.icon, className: "size-4" }), method.label ?? t(`login.method.${method.i18nKey ?? method.id}`, method.id)] }, method.id)) }), model.terms.required && _jsx(LoginTerms, { model: model.terms, translate: t
|
|
59
|
+
: _jsx(LoginMethodIcon, { name: method.icon, className: "size-4" }), method.label ?? t(`login.method.${method.i18nKey ?? method.id}`, method.id)] }, method.id)) }), model.terms.required && _jsx(LoginTerms, { model: model.terms, translate: t }), (model.error ?? attemptError) != null
|
|
60
60
|
&& _jsx("p", { role: "alert", "data-login-error": true, className: "text-sm text-destructive text-center", children: model.error ?? t(attemptError, 'Sign-in did not complete. Please try again.') })] }), _jsx("div", { className: "px-6", children: props.footer ?? _jsx(LoginCredit, { model: model.credit, translate: t }) })] }) });
|
|
61
61
|
};
|
|
62
62
|
/**
|
|
@@ -68,7 +68,6 @@ export const LoginScreen = props => {
|
|
|
68
68
|
*/
|
|
69
69
|
export const LocalizedLoginScreen = props => {
|
|
70
70
|
const t = useI18nLib('auth');
|
|
71
|
-
|
|
72
|
-
return _jsx(LoginScreen, { ...props, translate: props.translate ?? ((key, defaultValue) => t(key, { defaultValue })), locale: props.locale ?? lng });
|
|
71
|
+
return _jsx(LoginScreen, { ...props, translate: props.translate ?? ((key, defaultValue) => t(key, { defaultValue })) });
|
|
73
72
|
};
|
|
74
73
|
//# sourceMappingURL=screen.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen.js","sourceRoot":"","sources":["../../../src/components/login/screen.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAA;AACxD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AACzG,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"screen.js","sourceRoot":"","sources":["../../../src/components/login/screen.tsx"],"names":[],"mappings":"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"}
|
|
@@ -3,15 +3,7 @@ import type { LoginTermsModel } from '@owlmeans/client-panel/auth';
|
|
|
3
3
|
export interface LoginTermsProps {
|
|
4
4
|
model: LoginTermsModel;
|
|
5
5
|
translate: (key: string, defaultValue: string) => string;
|
|
6
|
-
/** The current language, for `Intl.ListFormat` and a document's own locale-keyed label. */
|
|
7
|
-
locale?: string;
|
|
8
6
|
className?: string;
|
|
9
7
|
}
|
|
10
|
-
/**
|
|
11
|
-
* The sign-in screen's terms confirmation: one consented checkbox for `documents` (terms, plus
|
|
12
|
-
* billing/product/custom when configured), and a SEPARATE, non-consented disclosure line for
|
|
13
|
-
* `notices` (privacy, plus cookies per its own rule) — never nested inside the checkbox's label,
|
|
14
|
-
* because nothing is being agreed to there.
|
|
15
|
-
*/
|
|
16
8
|
export declare const LoginTerms: FC<LoginTermsProps>;
|
|
17
9
|
//# sourceMappingURL=terms.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terms.d.ts","sourceRoot":"","sources":["../../../src/components/login/terms.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAA;AAE1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"terms.d.ts","sourceRoot":"","sources":["../../../src/components/login/terms.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAA;AAE1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAElE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,CAAA;IACtB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACxD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAuBD,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,CA0C1C,CAAA"}
|
|
@@ -1,52 +1,33 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from '../../@/lib/utils.js';
|
|
3
|
-
import { termsSentence } from '@owlmeans/client-auth/login';
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
4
|
+
* Split a translated sentence around its two link placeholders.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* The sentence is translated as ONE string with `{{terms}}` and `{{privacy}}` in it, rather than
|
|
7
|
+
* assembled from fragments, because word order differs between languages and an assembled sentence
|
|
8
|
+
* forces every translator into English's. Splitting the already-translated string keeps that
|
|
9
|
+
* freedom while keeping the i18n contract at `(key, defaultValue) => string` — no ReactNode ever
|
|
10
|
+
* comes out of a translation, so an app with no i18n provider still renders a correct sentence.
|
|
9
11
|
*/
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* afterwards, since the `translate` contract this whole package shares is a plain
|
|
21
|
-
* `(key, defaultValue) => string` with no interpolation option of its own.
|
|
22
|
-
*/
|
|
23
|
-
const resolveLabelFor = (translate, locale) => (doc) => {
|
|
24
|
-
const fromMap = doc.labelMap != null
|
|
25
|
-
? (locale != null ? doc.labelMap[locale] : undefined) ?? Object.values(doc.labelMap)[0]
|
|
26
|
-
: undefined;
|
|
27
|
-
let label = doc.label ?? fromMap
|
|
28
|
-
?? (doc.i18nKey != null ? translate(doc.i18nKey, DEFAULT_LABEL[doc.key] ?? doc.key) : doc.key);
|
|
29
|
-
if (doc.params != null) {
|
|
30
|
-
for (const [key, value] of Object.entries(doc.params)) {
|
|
31
|
-
label = label.split(`{{${key}}}`).join(value);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return label;
|
|
35
|
-
};
|
|
36
|
-
const renderParts = (parts) => parts.map((part, index) => part.href != null
|
|
37
|
-
? _jsx("a", { href: part.href, target: "_blank", rel: "noreferrer noopener", "data-login-document": part.documentKey, className: "underline underline-offset-2 hover:text-foreground", children: part.text }, index)
|
|
38
|
-
: _jsx("span", { children: part.text }, index));
|
|
39
|
-
/**
|
|
40
|
-
* The sign-in screen's terms confirmation: one consented checkbox for `documents` (terms, plus
|
|
41
|
-
* billing/product/custom when configured), and a SEPARATE, non-consented disclosure line for
|
|
42
|
-
* `notices` (privacy, plus cookies per its own rule) — never nested inside the checkbox's label,
|
|
43
|
-
* because nothing is being agreed to there.
|
|
44
|
-
*/
|
|
45
|
-
export const LoginTerms = ({ model, translate, locale, className }) => {
|
|
46
|
-
const resolveLabel = resolveLabelFor(translate, locale);
|
|
12
|
+
const interpolate = (sentence, slots) => sentence
|
|
13
|
+
.split(/(\{\{[a-z]+\}\})/i)
|
|
14
|
+
.map((part, index) => {
|
|
15
|
+
const match = /^\{\{([a-z]+)\}\}$/i.exec(part);
|
|
16
|
+
return match != null
|
|
17
|
+
? _jsx("span", { children: slots[match[1]] ?? part }, index)
|
|
18
|
+
: _jsx("span", { children: part }, index);
|
|
19
|
+
});
|
|
20
|
+
export const LoginTerms = ({ model, translate, className }) => {
|
|
21
|
+
const link = (href, label) => _jsx("a", { href: href, target: "_blank", rel: "noreferrer noopener", className: "underline underline-offset-2 hover:text-foreground", children: label });
|
|
47
22
|
// Centred, like every other row in the card. The checkbox stays at the start of the sentence
|
|
48
23
|
// rather than above it, so `justify-center` centres the pair and `text-center` centres the
|
|
49
24
|
// wrapped lines within it.
|
|
50
|
-
return _jsxs("div", { className: cn('flex flex-col items-center gap-1.5 text-center', className), children: [_jsxs("label", { className: "flex items-start justify-center gap-2 text-sm text-muted-foreground cursor-pointer", children: [_jsx("input", { type: "checkbox", "data-login-terms": true, className: "mt-0.5 size-4 shrink-0 accent-primary", checked: model.accepted, "aria-invalid": model.attempted && !model.accepted, onChange: event => model.accept(event.target.checked) }), _jsx("span", { children:
|
|
25
|
+
return _jsxs("div", { className: cn('flex flex-col items-center gap-1.5 text-center', className), children: [_jsxs("label", { className: "flex items-start justify-center gap-2 text-sm text-muted-foreground cursor-pointer", children: [_jsx("input", { type: "checkbox", "data-login-terms": true, className: "mt-0.5 size-4 shrink-0 accent-primary", checked: model.accepted, "aria-invalid": model.attempted && !model.accepted, onChange: event => model.accept(event.target.checked) }), _jsx("span", { children: interpolate(translate('login.terms.agreement', 'I have read and agree to the {{terms}} and the {{privacy}}.'), {
|
|
26
|
+
terms: link(model.urls.terms, translate('login.terms.terms', 'Terms & Conditions')),
|
|
27
|
+
privacy: link(model.urls.privacy, translate('login.terms.privacy', 'Privacy Policy')),
|
|
28
|
+
cookies: model.urls.cookies != null
|
|
29
|
+
? link(model.urls.cookies, translate('login.terms.cookies', 'Cookie Policy'))
|
|
30
|
+
: null,
|
|
31
|
+
}) })] }), model.attempted && !model.accepted && _jsx("p", { role: "alert", className: "text-sm text-destructive", children: translate('login.terms.required', 'Please confirm the Terms & Conditions and the Privacy Policy to continue.') })] });
|
|
51
32
|
};
|
|
52
33
|
//# sourceMappingURL=terms.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terms.js","sourceRoot":"","sources":["../../../src/components/login/terms.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"terms.js","sourceRoot":"","sources":["../../../src/components/login/terms.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AASzC;;;;;;;;GAQG;AACH,MAAM,WAAW,GAAG,CAClB,QAAgB,EAAE,KAAgC,EACrC,EAAE,CAAC,QAAQ;KACvB,KAAK,CAAC,mBAAmB,CAAC;KAC1B,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;IACnB,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAE9C,OAAO,KAAK,IAAI,IAAI;QAClB,CAAC,CAAC,yBAAmB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,IAA/B,KAAK,CAAkC;QACpD,CAAC,CAAC,yBAAmB,IAAI,IAAZ,KAAK,CAAe,CAAA;AACrC,CAAC,CAAC,CAAA;AAEJ,MAAM,CAAC,MAAM,UAAU,GAAwB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE;IACjF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,KAAa,EAAa,EAAE,CACtD,YAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAC,QAAQ,EAAC,GAAG,EAAC,qBAAqB,EACtD,SAAS,EAAC,oDAAoD,YAAE,KAAK,GAAK,CAAA;IAE9E,6FAA6F;IAC7F,2FAA2F;IAC3F,2BAA2B;IAC3B,OAAO,eAAK,SAAS,EAAE,EAAE,CAAC,gDAAgD,EAAE,SAAS,CAAC,aACpF,iBAAO,SAAS,EAAC,oFAAoF,aAOnG,gBACE,IAAI,EAAC,UAAU,4BACf,SAAS,EAAC,uCAAuC,EACjD,OAAO,EAAE,KAAK,CAAC,QAAQ,kBACT,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,QAAQ,EAChD,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,GACrD,EACF,yBACG,WAAW,CACV,SAAS,CAAC,uBAAuB,EAC/B,6DAA6D,CAAC,EAChE;4BACE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,mBAAmB,EAAE,oBAAoB,CAAC,CAAC;4BACnF,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,qBAAqB,EAAE,gBAAgB,CAAC,CAAC;4BACrF,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI;gCACjC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,qBAAqB,EAAE,eAAe,CAAC,CAAC;gCAC7E,CAAC,CAAC,IAAI;yBACT,CACF,GACI,IACD,EACP,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,YAAG,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,0BAA0B,YACxF,SAAS,CAAC,sBAAsB,EAC/B,2EAA2E,CAAC,GAC5E,IACA,CAAA;AACR,CAAC,CAAA"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"reload": {
|
|
3
3
|
"title": "Злучэнне страчана",
|
|
4
|
-
"description": "Не ўдалося аднавіць злучэнне пасля некалькіх спробаў.
|
|
5
|
-
"action": "Перазагрузіць старонку"
|
|
4
|
+
"description": "Не ўдалося аднавіць злучэнне пасля некалькіх спробаў. Паспрабуйце яшчэ раз або перазагрузіце старонку, каб працягнуць.",
|
|
5
|
+
"action": "Перазагрузіць старонку",
|
|
6
|
+
"retry": "Паспрабаваць яшчэ раз",
|
|
7
|
+
"retrying": "Паўторнае падключэнне…"
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"reload": {
|
|
3
3
|
"title": "Verbindung verloren",
|
|
4
|
-
"description": "Die Verbindung konnte nach mehreren Versuchen nicht wiederhergestellt werden.
|
|
5
|
-
"action": "Seite neu laden"
|
|
4
|
+
"description": "Die Verbindung konnte nach mehreren Versuchen nicht wiederhergestellt werden. Versuche es erneut oder lade die Seite neu, um fortzufahren.",
|
|
5
|
+
"action": "Seite neu laden",
|
|
6
|
+
"retry": "Erneut versuchen",
|
|
7
|
+
"retrying": "Verbindung wird wiederhergestellt…"
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"reload": {
|
|
3
3
|
"title": "Connection lost",
|
|
4
|
-
"description": "We couldn't restore the connection after several attempts.
|
|
5
|
-
"action": "Reload page"
|
|
4
|
+
"description": "We couldn't restore the connection after several attempts. Try again, or reload the page to continue.",
|
|
5
|
+
"action": "Reload page",
|
|
6
|
+
"retry": "Try again",
|
|
7
|
+
"retrying": "Reconnecting…"
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"reload": {
|
|
3
3
|
"title": "Conexión perdida",
|
|
4
|
-
"description": "No se pudo restablecer la conexión tras varios intentos.
|
|
5
|
-
"action": "Recargar página"
|
|
4
|
+
"description": "No se pudo restablecer la conexión tras varios intentos. Vuelve a intentarlo o recarga la página para continuar.",
|
|
5
|
+
"action": "Recargar página",
|
|
6
|
+
"retry": "Reintentar",
|
|
7
|
+
"retrying": "Reconectando…"
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"reload": {
|
|
3
3
|
"title": "Connexion perdue",
|
|
4
|
-
"description": "Nous n’avons pas pu rétablir la connexion après plusieurs tentatives.
|
|
5
|
-
"action": "Actualiser la page"
|
|
4
|
+
"description": "Nous n’avons pas pu rétablir la connexion après plusieurs tentatives. Réessayez ou actualisez la page pour continuer.",
|
|
5
|
+
"action": "Actualiser la page",
|
|
6
|
+
"retry": "Réessayer",
|
|
7
|
+
"retrying": "Reconnexion…"
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"reload": {
|
|
3
3
|
"title": "Utracono połączenie",
|
|
4
|
-
"description": "Nie udało się przywrócić połączenia mimo kilku prób.
|
|
5
|
-
"action": "Odśwież stronę"
|
|
4
|
+
"description": "Nie udało się przywrócić połączenia mimo kilku prób. Spróbuj ponownie lub odśwież stronę, aby kontynuować.",
|
|
5
|
+
"action": "Odśwież stronę",
|
|
6
|
+
"retry": "Spróbuj ponownie",
|
|
7
|
+
"retrying": "Ponowne łączenie…"
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"reload": {
|
|
3
3
|
"title": "Соединение потеряно",
|
|
4
|
-
"description": "Не удалось восстановить соединение после нескольких попыток.
|
|
5
|
-
"action": "Перезагрузить страницу"
|
|
4
|
+
"description": "Не удалось восстановить соединение после нескольких попыток. Попробуйте снова или перезагрузите страницу, чтобы продолжить.",
|
|
5
|
+
"action": "Перезагрузить страницу",
|
|
6
|
+
"retry": "Попробовать снова",
|
|
7
|
+
"retrying": "Переподключение…"
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"reload": {
|
|
3
3
|
"title": "З'єднання втрачено",
|
|
4
|
-
"description": "Не вдалося відновити з'єднання після кількох спроб.
|
|
5
|
-
"action": "Перезавантажити сторінку"
|
|
4
|
+
"description": "Не вдалося відновити з'єднання після кількох спроб. Спробуйте ще раз або перезавантажте сторінку, щоб продовжити.",
|
|
5
|
+
"action": "Перезавантажити сторінку",
|
|
6
|
+
"retry": "Спробувати ще раз",
|
|
7
|
+
"retrying": "Перепідключення…"
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { FC } from 'react';
|
|
2
2
|
/**
|
|
3
|
-
* A global, blocking "
|
|
3
|
+
* A global, blocking "connection lost" prompt — opt in per app with `cfg.socket.reloadDialog`.
|
|
4
4
|
*
|
|
5
5
|
* Mount it once, above the router (`PanelApp` does), exactly like `PanelCookieConsent`: a dialog
|
|
6
6
|
* mounted inside a route is torn down on every navigation, and this one is meant to survive them
|
|
7
7
|
* all. It opens the moment `useSocketStatus()` reports `'lost'` — every `ws()`/`useWs()`
|
|
8
|
-
* connection in the app has exhausted its own retry budget (10 minutes by default)
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* connection in the app has exhausted its own retry budget (10 minutes by default).
|
|
9
|
+
*
|
|
10
|
+
* Two exits: "Try again" revives every lost connection (`useSocketRetry()`), and the dialog stays
|
|
11
|
+
* up showing progress until they are back online — or offers both buttons again if they are lost
|
|
12
|
+
* again. "Reload page" is the fallback that always works. The status service also retries by
|
|
13
|
+
* itself when the tab or window becomes active, so a socket that died in a background tab is
|
|
14
|
+
* usually back before anyone has to press anything.
|
|
11
15
|
*
|
|
12
16
|
* Non-dismissible on two levels: Radix's `AlertDialogContent` (unlike its plain `Dialog` sibling)
|
|
13
17
|
* never treats an outside click as a request to close, so there is no `onPointerDownOutside` to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reload-dialog.d.ts","sourceRoot":"","sources":["../../../src/components/socket/reload-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"reload-dialog.d.ts","sourceRoot":"","sources":["../../../src/components/socket/reload-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAS/B;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,EAAE,EAgChC,CAAA"}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useSocketStatus } from '@owlmeans/client-socket';
|
|
2
|
+
import { useSocketRetry, useSocketStatus } from '@owlmeans/client-socket';
|
|
3
3
|
import { useI18nLib } from '@owlmeans/client-i18n';
|
|
4
4
|
import { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '../../@/components/ui/alert-dialog.js';
|
|
5
|
+
import { Button } from '../../@/components/ui/button.js';
|
|
5
6
|
/**
|
|
6
|
-
* A global, blocking "
|
|
7
|
+
* A global, blocking "connection lost" prompt — opt in per app with `cfg.socket.reloadDialog`.
|
|
7
8
|
*
|
|
8
9
|
* Mount it once, above the router (`PanelApp` does), exactly like `PanelCookieConsent`: a dialog
|
|
9
10
|
* mounted inside a route is torn down on every navigation, and this one is meant to survive them
|
|
10
11
|
* all. It opens the moment `useSocketStatus()` reports `'lost'` — every `ws()`/`useWs()`
|
|
11
|
-
* connection in the app has exhausted its own retry budget (10 minutes by default)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* connection in the app has exhausted its own retry budget (10 minutes by default).
|
|
13
|
+
*
|
|
14
|
+
* Two exits: "Try again" revives every lost connection (`useSocketRetry()`), and the dialog stays
|
|
15
|
+
* up showing progress until they are back online — or offers both buttons again if they are lost
|
|
16
|
+
* again. "Reload page" is the fallback that always works. The status service also retries by
|
|
17
|
+
* itself when the tab or window becomes active, so a socket that died in a background tab is
|
|
18
|
+
* usually back before anyone has to press anything.
|
|
14
19
|
*
|
|
15
20
|
* Non-dismissible on two levels: Radix's `AlertDialogContent` (unlike its plain `Dialog` sibling)
|
|
16
21
|
* never treats an outside click as a request to close, so there is no `onPointerDownOutside` to
|
|
@@ -19,8 +24,11 @@ import { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescript
|
|
|
19
24
|
*/
|
|
20
25
|
export const SocketReloadDialog = () => {
|
|
21
26
|
const status = useSocketStatus();
|
|
27
|
+
const { retry, retrying } = useSocketRetry();
|
|
22
28
|
const t = useI18nLib('socket', 'reload');
|
|
23
|
-
const open = status === 'lost';
|
|
24
|
-
|
|
29
|
+
const open = status === 'lost' || (retrying && status === 'reconnecting');
|
|
30
|
+
// Closing still animates the content out — keep the retry in its busy state until it is gone.
|
|
31
|
+
const busy = retrying || !open;
|
|
32
|
+
return _jsx(AlertDialog, { open: open, children: _jsxs(AlertDialogContent, { "data-socket-reload-dialog": true, onEscapeKeyDown: event => event.preventDefault(), children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: t('title') }), _jsx(AlertDialogDescription, { children: t('description') })] }), _jsxs(AlertDialogFooter, { children: [_jsx(Button, { variant: "outline", "data-socket-reload-action": true, onClick: () => window.location.reload(), children: t('action') }), _jsx(AlertDialogAction, { "data-socket-retry-action": true, disabled: busy, "aria-busy": busy, onClick: retry, children: busy ? t('retrying') : t('retry') })] })] }) });
|
|
25
33
|
};
|
|
26
34
|
//# sourceMappingURL=reload-dialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reload-dialog.js","sourceRoot":"","sources":["../../../src/components/socket/reload-dialog.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"reload-dialog.js","sourceRoot":"","sources":["../../../src/components/socket/reload-dialog.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EACL,WAAW,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAC1E,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EACvD,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAA;AAExD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAO,GAAG,EAAE;IACzC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAA;IAChC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,cAAc,EAAE,CAAA;IAC5C,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACxC,MAAM,IAAI,GAAG,MAAM,KAAK,MAAM,IAAI,CAAC,QAAQ,IAAI,MAAM,KAAK,cAAc,CAAC,CAAA;IACzE,8FAA8F;IAC9F,MAAM,IAAI,GAAG,QAAQ,IAAI,CAAC,IAAI,CAAA;IAE9B,OAAO,KAAC,WAAW,IAAC,IAAI,EAAE,IAAI,YAC5B,MAAC,kBAAkB,uCAEjB,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,aAEhD,MAAC,iBAAiB,eAChB,KAAC,gBAAgB,cAAE,CAAC,CAAC,OAAO,CAAC,GAAoB,EACjD,KAAC,sBAAsB,cAAE,CAAC,CAAC,aAAa,CAAC,GAA0B,IACjD,EACpB,MAAC,iBAAiB,eAChB,KAAC,MAAM,IAAC,OAAO,EAAC,SAAS,qCAA2B,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,YACxF,CAAC,CAAC,QAAQ,CAAC,GACL,EACT,KAAC,iBAAiB,sCAEhB,QAAQ,EAAE,IAAI,eACH,IAAI,EACf,OAAO,EAAE,KAAK,YAEb,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAChB,IACF,IACD,GACT,CAAA;AAChB,CAAC,CAAA"}
|
package/build/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { SocketClientSettings, SocketStatusServiceAppend } from '@owlmeans/
|
|
|
5
5
|
export interface AppConfig extends Config, WithFlowConfig {
|
|
6
6
|
socket?: SocketClientSettings & {
|
|
7
7
|
/**
|
|
8
|
-
* Show `SocketReloadDialog` — a global, blocking "reload the page" prompt — once every
|
|
8
|
+
* Show `SocketReloadDialog` — a global, blocking "try again / reload the page" prompt — once every
|
|
9
9
|
* socket in the app has given up reconnecting (`useSocketStatus() === 'lost'`). Off by
|
|
10
10
|
* default: an app opts in once it has decided that outcome deserves a full-page prompt
|
|
11
11
|
* rather than the caller's own fallback UI.
|
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.57",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -51,33 +51,33 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@hookform/resolvers": "^5.2.2",
|
|
54
|
-
"@owlmeans/api-config-client": "^0.1.18-rc.
|
|
55
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
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.
|
|
60
|
-
"@owlmeans/client-config": "^0.1.18-rc.
|
|
61
|
-
"@owlmeans/client-context": "^0.1.18-rc.
|
|
62
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
63
|
-
"@owlmeans/client-flow": "^0.1.18-rc.
|
|
64
|
-
"@owlmeans/client-i18n": "^0.1.18-rc.
|
|
65
|
-
"@owlmeans/client-panel": "^0.1.18-rc.
|
|
66
|
-
"@owlmeans/client-route": "^0.1.18-rc.
|
|
67
|
-
"@owlmeans/client-socket": "^0.1.18-rc.
|
|
68
|
-
"@owlmeans/config": "^0.1.18-rc.
|
|
69
|
-
"@owlmeans/context": "^0.1.18-rc.
|
|
70
|
-
"@owlmeans/entrypoint": "^0.1.18-rc.
|
|
71
|
-
"@owlmeans/error": "^0.1.18-rc.
|
|
72
|
-
"@owlmeans/flow": "^0.1.18-rc.
|
|
73
|
-
"@owlmeans/i18n": "^0.1.18-rc.
|
|
74
|
-
"@owlmeans/route": "^0.1.18-rc.
|
|
75
|
-
"@owlmeans/state": "^0.1.18-rc.
|
|
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.
|
|
54
|
+
"@owlmeans/api-config-client": "^0.1.18-rc.32",
|
|
55
|
+
"@owlmeans/auth": "^0.1.18-rc.32",
|
|
56
|
+
"@owlmeans/auth-common": "^0.1.18-rc.34",
|
|
57
|
+
"@owlmeans/basic-envelope": "^0.1.18-rc.33",
|
|
58
|
+
"@owlmeans/client": "^0.1.18-rc.37",
|
|
59
|
+
"@owlmeans/client-auth": "^0.1.18-rc.44",
|
|
60
|
+
"@owlmeans/client-config": "^0.1.18-rc.34",
|
|
61
|
+
"@owlmeans/client-context": "^0.1.18-rc.35",
|
|
62
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.34",
|
|
63
|
+
"@owlmeans/client-flow": "^0.1.18-rc.40",
|
|
64
|
+
"@owlmeans/client-i18n": "^0.1.18-rc.36",
|
|
65
|
+
"@owlmeans/client-panel": "^0.1.18-rc.44",
|
|
66
|
+
"@owlmeans/client-route": "^0.1.18-rc.35",
|
|
67
|
+
"@owlmeans/client-socket": "^0.1.18-rc.35",
|
|
68
|
+
"@owlmeans/config": "^0.1.18-rc.34",
|
|
69
|
+
"@owlmeans/context": "^0.1.18-rc.30",
|
|
70
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.33",
|
|
71
|
+
"@owlmeans/error": "^0.1.18-rc.32",
|
|
72
|
+
"@owlmeans/flow": "^0.1.18-rc.35",
|
|
73
|
+
"@owlmeans/i18n": "^0.1.18-rc.33",
|
|
74
|
+
"@owlmeans/route": "^0.1.18-rc.31",
|
|
75
|
+
"@owlmeans/state": "^0.1.18-rc.31",
|
|
76
|
+
"@owlmeans/web-client": "^0.1.18-rc.47",
|
|
77
|
+
"@owlmeans/web-consent": "^0.1.18-rc.28",
|
|
78
|
+
"@owlmeans/web-db": "^0.1.18-rc.35",
|
|
79
|
+
"@owlmeans/web-flow": "^0.1.18-rc.39",
|
|
80
|
+
"@owlmeans/web-router": "^0.1.18-rc.35",
|
|
81
81
|
"i18next-browser-languagedetector": "^8.0.0",
|
|
82
82
|
"qrcode.react": "^4.2.0",
|
|
83
83
|
"react-google-recaptcha": "^3.1.0",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@owlmeans/dep-config": "workspace:*",
|
|
106
|
-
"@owlmeans/test-ui": "^0.1.18-rc.
|
|
106
|
+
"@owlmeans/test-ui": "^0.1.18-rc.35",
|
|
107
107
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
108
108
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
109
109
|
"@radix-ui/react-label": "^2.1.0",
|