@cosmicdrift/kumiko-bundled-features 0.147.3 → 0.148.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-bundled-features",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.148.0",
|
|
4
4
|
"description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -114,11 +114,11 @@
|
|
|
114
114
|
"./step-dispatcher": "./src/step-dispatcher/index.ts"
|
|
115
115
|
},
|
|
116
116
|
"dependencies": {
|
|
117
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.
|
|
118
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
119
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
120
|
-
"@cosmicdrift/kumiko-renderer": "0.
|
|
121
|
-
"@cosmicdrift/kumiko-renderer-web": "0.
|
|
117
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.148.0",
|
|
118
|
+
"@cosmicdrift/kumiko-framework": "0.148.0",
|
|
119
|
+
"@cosmicdrift/kumiko-headless": "0.148.0",
|
|
120
|
+
"@cosmicdrift/kumiko-renderer": "0.148.0",
|
|
121
|
+
"@cosmicdrift/kumiko-renderer-web": "0.148.0",
|
|
122
122
|
"@mollie/api-client": "^4.5.0",
|
|
123
123
|
"imapflow": "^1.3.3",
|
|
124
124
|
"mailparser": "^3.9.8",
|
|
@@ -15,7 +15,7 @@ describe("audit log screen + handler access alignment", () => {
|
|
|
15
15
|
test("audit-log screen is custom, access.admin-gated", () => {
|
|
16
16
|
const audit = createAuditFeature();
|
|
17
17
|
const screen = audit.screens[AUDIT_LOG_SCREEN_ID];
|
|
18
|
-
if (
|
|
18
|
+
if (screen?.type !== "custom") {
|
|
19
19
|
throw new Error(`expected a custom screen for ${AUDIT_LOG_SCREEN_ID}, got ${screen?.type}`);
|
|
20
20
|
}
|
|
21
21
|
if (!("access" in screen) || !screen.access || !("roles" in screen.access)) {
|
|
@@ -27,7 +27,7 @@ describe("audit log screen + handler access alignment", () => {
|
|
|
27
27
|
test("audit-log-detail screen is custom, admin-gated, breadcrumb-linked to list", () => {
|
|
28
28
|
const audit = createAuditFeature();
|
|
29
29
|
const screen = audit.screens[AUDIT_LOG_DETAIL_SCREEN_ID];
|
|
30
|
-
if (
|
|
30
|
+
if (screen?.type !== "custom") {
|
|
31
31
|
throw new Error(
|
|
32
32
|
`expected a custom screen for ${AUDIT_LOG_DETAIL_SCREEN_ID}, got ${screen?.type}`,
|
|
33
33
|
);
|
package/src/auth-mfa/web/i18n.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { TranslationsByLocale } from "@cosmicdrift/kumiko-renderer";
|
|
|
7
7
|
|
|
8
8
|
export const defaultTranslations: TranslationsByLocale = {
|
|
9
9
|
de: {
|
|
10
|
+
"screen:auth-mfa-enable.title": "Zwei-Faktor-Authentifizierung",
|
|
10
11
|
"auth.mfa.verify.title": "Zwei-Faktor-Bestätigung",
|
|
11
12
|
"auth.mfa.verify.subtitle": "Gib den 6-stelligen Code aus deiner Authenticator-App ein.",
|
|
12
13
|
"auth.mfa.verify.code": "Code",
|
|
@@ -36,6 +37,7 @@ export const defaultTranslations: TranslationsByLocale = {
|
|
|
36
37
|
"auth.mfa.enable.success": "Zwei-Faktor-Authentifizierung ist jetzt aktiv.",
|
|
37
38
|
},
|
|
38
39
|
en: {
|
|
40
|
+
"screen:auth-mfa-enable.title": "Two-factor authentication",
|
|
39
41
|
"auth.mfa.verify.title": "Two-factor verification",
|
|
40
42
|
"auth.mfa.verify.subtitle": "Enter the 6-digit code from your authenticator app.",
|
|
41
43
|
"auth.mfa.verify.code": "Code",
|
|
@@ -39,7 +39,7 @@ export type MfaEnableScreenProps = { readonly embedded?: boolean };
|
|
|
39
39
|
|
|
40
40
|
export function MfaEnableScreen({ embedded = false }: MfaEnableScreenProps = {}): ReactNode {
|
|
41
41
|
const t = useTranslation();
|
|
42
|
-
const { Button, Banner, Field, Input,
|
|
42
|
+
const { Button, Banner, Field, Input, Section, Heading } = usePrimitives();
|
|
43
43
|
const dispatcher = useDispatcher();
|
|
44
44
|
const session = useSession();
|
|
45
45
|
|
|
@@ -98,22 +98,43 @@ export function MfaEnableScreen({ embedded = false }: MfaEnableScreenProps = {})
|
|
|
98
98
|
{error !== null && <Banner variant="error">{t(`auth.mfa.errors.${error}`)}</Banner>}
|
|
99
99
|
|
|
100
100
|
{!setup && !enabled && (
|
|
101
|
-
<
|
|
101
|
+
<Section
|
|
102
|
+
testId="mfa-enable-intro"
|
|
103
|
+
actions={
|
|
104
|
+
<Button
|
|
105
|
+
variant="primary"
|
|
106
|
+
onClick={() => void startSetup()}
|
|
107
|
+
loading={busy}
|
|
108
|
+
disabled={busy}
|
|
109
|
+
>
|
|
110
|
+
{t("auth.mfa.enable.start")}
|
|
111
|
+
</Button>
|
|
112
|
+
}
|
|
113
|
+
>
|
|
102
114
|
<span className="text-sm text-muted-foreground">{t("auth.mfa.enable.intro")}</span>
|
|
103
|
-
|
|
104
|
-
variant="primary"
|
|
105
|
-
onClick={() => void startSetup()}
|
|
106
|
-
loading={busy}
|
|
107
|
-
disabled={busy}
|
|
108
|
-
>
|
|
109
|
-
{t("auth.mfa.enable.start")}
|
|
110
|
-
</Button>
|
|
111
|
-
</Card>
|
|
115
|
+
</Section>
|
|
112
116
|
)}
|
|
113
117
|
|
|
114
118
|
{setup && (
|
|
115
|
-
<
|
|
116
|
-
|
|
119
|
+
<Section
|
|
120
|
+
testId="mfa-enable-setup"
|
|
121
|
+
actions={
|
|
122
|
+
<>
|
|
123
|
+
<Button variant="secondary" onClick={() => setSetup(null)} disabled={busy}>
|
|
124
|
+
{t("auth.mfa.enable.cancel")}
|
|
125
|
+
</Button>
|
|
126
|
+
<Button
|
|
127
|
+
variant="primary"
|
|
128
|
+
onClick={() => void confirmSetup()}
|
|
129
|
+
loading={busy}
|
|
130
|
+
disabled={busy || !acknowledged || code.length < 6}
|
|
131
|
+
>
|
|
132
|
+
{t("auth.mfa.enable.confirm")}
|
|
133
|
+
</Button>
|
|
134
|
+
</>
|
|
135
|
+
}
|
|
136
|
+
>
|
|
137
|
+
<div className="flex flex-col items-center gap-2 text-center">
|
|
117
138
|
<span className="text-sm font-semibold">{t("auth.mfa.enable.scanTitle")}</span>
|
|
118
139
|
{/* qrcode's own SVG string output, not user input — safe to inline */}
|
|
119
140
|
<div
|
|
@@ -124,7 +145,7 @@ export function MfaEnableScreen({ embedded = false }: MfaEnableScreenProps = {})
|
|
|
124
145
|
<span className="text-xs text-muted-foreground">
|
|
125
146
|
{t("auth.mfa.enable.manualEntry")}
|
|
126
147
|
</span>
|
|
127
|
-
<code className="block break-all rounded bg-muted px-3 py-2 font-mono text-sm">
|
|
148
|
+
<code className="inline-block break-all rounded bg-muted px-3 py-2 font-mono text-sm">
|
|
128
149
|
{setup.secretParam}
|
|
129
150
|
</code>
|
|
130
151
|
</div>
|
|
@@ -159,21 +180,7 @@ export function MfaEnableScreen({ embedded = false }: MfaEnableScreenProps = {})
|
|
|
159
180
|
autoComplete="one-time-code"
|
|
160
181
|
/>
|
|
161
182
|
</Field>
|
|
162
|
-
|
|
163
|
-
<div className="flex justify-end gap-2">
|
|
164
|
-
<Button variant="secondary" onClick={() => setSetup(null)} disabled={busy}>
|
|
165
|
-
{t("auth.mfa.enable.cancel")}
|
|
166
|
-
</Button>
|
|
167
|
-
<Button
|
|
168
|
-
variant="primary"
|
|
169
|
-
onClick={() => void confirmSetup()}
|
|
170
|
-
loading={busy}
|
|
171
|
-
disabled={busy || !acknowledged || code.length < 6}
|
|
172
|
-
>
|
|
173
|
-
{t("auth.mfa.enable.confirm")}
|
|
174
|
-
</Button>
|
|
175
|
-
</div>
|
|
176
|
-
</Card>
|
|
183
|
+
</Section>
|
|
177
184
|
)}
|
|
178
185
|
</div>
|
|
179
186
|
);
|