@cosmicdrift/kumiko-bundled-features 0.126.0 → 0.128.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 +6 -6
- package/src/audit/__tests__/audit-security.integration.test.ts +50 -0
- package/src/audit/i18n.ts +9 -0
- package/src/audit/web/audit-log-screen.tsx +112 -2
- package/src/billing-foundation/__tests__/billing-foundation.integration.test.ts +195 -2
- package/src/billing-foundation/entities.ts +28 -2
- package/src/billing-foundation/events.ts +6 -2
- package/src/billing-foundation/feature.ts +2 -0
- package/src/billing-foundation/get-subscription-for-tenant.ts +16 -5
- package/src/billing-foundation/handlers/create-portal-session.write.ts +17 -1
- package/src/billing-foundation/handlers/list-subscriptions.query.ts +15 -1
- package/src/billing-foundation/handlers/process-event.write.ts +39 -3
- package/src/billing-foundation/tenant-destroy-hook.ts +30 -7
- package/src/cap-counter/entity.ts +1 -1
- package/src/cap-counter/feature.ts +10 -2
- package/src/cap-counter/i18n.ts +12 -0
- package/src/compliance-profiles/feature.ts +3 -0
- package/src/compliance-profiles/i18n.ts +6 -0
- package/src/config/feature.ts +3 -0
- package/src/config/i18n.ts +9 -0
- package/src/delivery/feature.ts +3 -0
- package/src/delivery/i18n.ts +6 -0
- package/src/feature-toggles/feature.ts +3 -0
- package/src/feature-toggles/i18n.ts +6 -0
- package/src/jobs/i18n.ts +4 -0
- package/src/jobs/web/job-runs-screen.tsx +9 -9
- package/src/managed-pages/i18n.ts +5 -0
- package/src/managed-pages/screens/page-screens.ts +5 -1
- package/src/personal-access-tokens/feature.ts +2 -0
- package/src/personal-access-tokens/i18n.ts +5 -0
- package/src/personal-access-tokens/web/pat-tokens-screen.tsx +7 -3
- package/src/subscription-stripe/feature.ts +2 -0
- package/src/tags/feature.ts +2 -0
- package/src/tags/i18n.ts +5 -0
- package/src/tenant/__tests__/tenant-security.integration.test.ts +20 -5
- package/src/tenant/feature.ts +3 -0
- package/src/tenant/handlers/members.query.ts +19 -4
- package/src/tenant/i18n.ts +28 -0
- package/src/tenant/index.ts +1 -1
- package/src/tenant/schema/tenant.ts +2 -2
- package/src/tenant/screens.ts +3 -2
- package/src/tenant/web/members-screen.tsx +8 -3
- package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +161 -1
- package/src/tenant-lifecycle/feature.ts +1 -0
- package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +2 -0
- package/src/tenant-lifecycle/handlers/request-destruction.write.ts +2 -0
- package/src/tenant-lifecycle/lifecycle-gate.ts +58 -0
- package/src/tenant-lifecycle/run-tenant-destroy.ts +50 -39
- package/src/tenant-lifecycle/stages.ts +36 -6
- package/src/tier-engine/feature.ts +2 -0
- package/src/tier-engine/i18n.ts +8 -0
- package/src/tier-engine/web/tier-admin-screen.tsx +20 -14
- package/src/user/feature.ts +3 -0
- package/src/user/i18n.ts +20 -0
- package/src/user/schema/user.ts +1 -0
- package/src/user/screens.ts +3 -15
- package/src/user-data-rights/feature.ts +17 -0
- package/src/user-data-rights/i18n.ts +32 -33
- package/src/user-data-rights/schema/export-job.ts +2 -0
- package/src/user-data-rights/screens.ts +2 -1
- package/src/user-data-rights/web/i18n.ts +9 -0
|
@@ -37,7 +37,7 @@ type Status =
|
|
|
37
37
|
|
|
38
38
|
export function TierAdminScreen(): ReactNode {
|
|
39
39
|
const t = useTranslation();
|
|
40
|
-
const {
|
|
40
|
+
const { Field, Input, Button, Banner, Form, Text } = usePrimitives();
|
|
41
41
|
const dispatcher = useDispatcher();
|
|
42
42
|
|
|
43
43
|
// ponytail: nur die erste Seite (default-limit, nextCursor ignoriert) —
|
|
@@ -92,8 +92,7 @@ export function TierAdminScreen(): ReactNode {
|
|
|
92
92
|
const canSubmit = tenantId !== "" && tier !== "" && !submitting;
|
|
93
93
|
|
|
94
94
|
return (
|
|
95
|
-
<FormScreenShell testId="tier-admin-screen" className="flex
|
|
96
|
-
<Heading variant="page">{t("tier-admin.title")}</Heading>
|
|
95
|
+
<FormScreenShell testId="tier-admin-screen" className="flex flex-col gap-6">
|
|
97
96
|
<Text variant="small">{t("tier-admin.explainer")}</Text>
|
|
98
97
|
|
|
99
98
|
{tenantsQuery.error !== null && (
|
|
@@ -107,7 +106,23 @@ export function TierAdminScreen(): ReactNode {
|
|
|
107
106
|
</Banner>
|
|
108
107
|
)}
|
|
109
108
|
|
|
110
|
-
<
|
|
109
|
+
<Form
|
|
110
|
+
testId="tier-admin-form"
|
|
111
|
+
onSubmit={(e) => {
|
|
112
|
+
e?.preventDefault();
|
|
113
|
+
void onSave();
|
|
114
|
+
}}
|
|
115
|
+
actions={
|
|
116
|
+
<Button
|
|
117
|
+
type="submit"
|
|
118
|
+
disabled={!canSubmit}
|
|
119
|
+
loading={submitting}
|
|
120
|
+
testId="tier-admin-submit"
|
|
121
|
+
>
|
|
122
|
+
{t("tier-admin.submit")}
|
|
123
|
+
</Button>
|
|
124
|
+
}
|
|
125
|
+
>
|
|
111
126
|
<Field id="tier-admin-tenant" label={t("tier-admin.tenant.label")} required>
|
|
112
127
|
<Input
|
|
113
128
|
kind="select"
|
|
@@ -147,16 +162,7 @@ export function TierAdminScreen(): ReactNode {
|
|
|
147
162
|
{t(status.messageKey)}
|
|
148
163
|
</Banner>
|
|
149
164
|
)}
|
|
150
|
-
|
|
151
|
-
<Button
|
|
152
|
-
onClick={() => void onSave()}
|
|
153
|
-
disabled={!canSubmit}
|
|
154
|
-
loading={submitting}
|
|
155
|
-
testId="tier-admin-submit"
|
|
156
|
-
>
|
|
157
|
-
{t("tier-admin.submit")}
|
|
158
|
-
</Button>
|
|
159
|
-
</Section>
|
|
165
|
+
</Form>
|
|
160
166
|
</FormScreenShell>
|
|
161
167
|
);
|
|
162
168
|
}
|
package/src/user/feature.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { findForAuthQuery } from "./handlers/find-for-auth.query";
|
|
|
5
5
|
import { listQuery } from "./handlers/list.query";
|
|
6
6
|
import { meQuery } from "./handlers/me.query";
|
|
7
7
|
import { updateWrite } from "./handlers/update.write";
|
|
8
|
+
import { USER_I18N } from "./i18n";
|
|
8
9
|
import { userEntity } from "./schema/user";
|
|
9
10
|
import { userEditScreen, userListScreen } from "./screens";
|
|
10
11
|
|
|
@@ -43,6 +44,8 @@ export function createUserFeature(): FeatureDefinition {
|
|
|
43
44
|
r.screen(userListScreen);
|
|
44
45
|
r.screen(userEditScreen);
|
|
45
46
|
|
|
47
|
+
r.translations({ keys: USER_I18N });
|
|
48
|
+
|
|
46
49
|
return { handlers, queries };
|
|
47
50
|
});
|
|
48
51
|
}
|
package/src/user/i18n.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type LocalizedString = { readonly de: string; readonly en: string };
|
|
2
|
+
|
|
3
|
+
export const USER_I18N: Readonly<Record<string, LocalizedString>> = {
|
|
4
|
+
"screen:user-list.title": { de: "Benutzer", en: "Users" },
|
|
5
|
+
"screen:user-edit.title": { de: "Benutzer bearbeiten", en: "Edit user" },
|
|
6
|
+
"user:entity:user:field:email": { de: "E-Mail", en: "Email" },
|
|
7
|
+
"user:entity:user:field:displayName": { de: "Anzeigename", en: "Display name" },
|
|
8
|
+
"user:entity:user:field:status": { de: "Status", en: "Status" },
|
|
9
|
+
"user:entity:user:field:emailVerified": { de: "E-Mail bestätigt", en: "Email verified" },
|
|
10
|
+
"user:entity:user:field:locale": { de: "Sprache", en: "Locale" },
|
|
11
|
+
"user:entity:user:field:status:option:active": { de: "Aktiv", en: "Active" },
|
|
12
|
+
"user:entity:user:field:status:option:restricted": { de: "Eingeschränkt", en: "Restricted" },
|
|
13
|
+
"user:entity:user:field:status:option:deletionRequested": {
|
|
14
|
+
de: "Löschung angefordert",
|
|
15
|
+
en: "Deletion requested",
|
|
16
|
+
},
|
|
17
|
+
"user:entity:user:field:status:option:deleted": { de: "Gelöscht", en: "Deleted" },
|
|
18
|
+
"user:entity:user:field:emailVerified:option:true": { de: "Ja", en: "Yes" },
|
|
19
|
+
"user:entity:user:field:emailVerified:option:false": { de: "Nein", en: "No" },
|
|
20
|
+
};
|
package/src/user/schema/user.ts
CHANGED
package/src/user/screens.ts
CHANGED
|
@@ -7,10 +7,6 @@ import type {
|
|
|
7
7
|
// feature runs with `r.systemScope()`, the entityList query returns every
|
|
8
8
|
// user across all tenants — the SystemAdmin platform roster. Both screens are
|
|
9
9
|
// SystemAdmin-gated and stay inert until an app navs them (no auto-nav).
|
|
10
|
-
//
|
|
11
|
-
// Field labels come from the renderer's humanizeSlug fallback (no i18n keys
|
|
12
|
-
// registered) — "Display Name", "Email Verified" etc. Apps can override via
|
|
13
|
-
// their own translations under the `user:entity:user:field:*` convention.
|
|
14
10
|
|
|
15
11
|
export const userListScreen: EntityListScreenDefinition = {
|
|
16
12
|
id: "user-list",
|
|
@@ -26,9 +22,9 @@ export const userListScreen: EntityListScreenDefinition = {
|
|
|
26
22
|
entityId: "id",
|
|
27
23
|
},
|
|
28
24
|
],
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
searchable:
|
|
25
|
+
defaultSort: { field: "status", dir: "asc" },
|
|
26
|
+
// ponytail: screen searchable but email is encrypted PII — list search uses non-encrypted columns only.
|
|
27
|
+
searchable: true,
|
|
32
28
|
access: { roles: ["SystemAdmin"] },
|
|
33
29
|
};
|
|
34
30
|
|
|
@@ -44,14 +40,6 @@ export const userEditScreen: EntityEditScreenDefinition = {
|
|
|
44
40
|
},
|
|
45
41
|
],
|
|
46
42
|
},
|
|
47
|
-
// `roles` is deliberately NOT editable here: it is a raw JSON text column
|
|
48
|
-
// (`["SystemAdmin"]`) — a free-text input would let a typo corrupt the
|
|
49
|
-
// privilege column on a live platform. Role management needs a dedicated
|
|
50
|
-
// surface; the list still shows status for triage.
|
|
51
|
-
//
|
|
52
|
-
// Create dispatches user:write:user:create (email + displayName required —
|
|
53
|
-
// both in the form). Delete is suppressed: there is no user:write:user:delete
|
|
54
|
-
// — user removal is the GDPR status/forget flow, not a hard delete.
|
|
55
43
|
allowDelete: false,
|
|
56
44
|
access: { roles: ["SystemAdmin"] },
|
|
57
45
|
};
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import { requestExportWrite } from "./handlers/request-export.write";
|
|
29
29
|
import { restrictAccountWrite } from "./handlers/restrict-account.write";
|
|
30
30
|
import { createRunForgetCleanupHandler } from "./handlers/run-forget-cleanup.write";
|
|
31
|
+
import { USER_DATA_RIGHTS_I18N } from "./i18n";
|
|
31
32
|
import {
|
|
32
33
|
type GdprMailDefaults,
|
|
33
34
|
isMailTransportAvailable,
|
|
@@ -257,6 +258,22 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
257
258
|
r.screen(exportJobDetailScreen);
|
|
258
259
|
r.screen(downloadAttemptListScreen);
|
|
259
260
|
|
|
261
|
+
r.nav({
|
|
262
|
+
id: "export-job-list",
|
|
263
|
+
label: "user-data-rights:nav.exportJobs",
|
|
264
|
+
icon: "download",
|
|
265
|
+
screen: "user-data-rights:screen:export-job-list",
|
|
266
|
+
order: 25,
|
|
267
|
+
access: { roles: ["SystemAdmin"] },
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
r.translations({
|
|
271
|
+
keys: {
|
|
272
|
+
...USER_DATA_RIGHTS_I18N,
|
|
273
|
+
"user-data-rights:nav.exportJobs": { de: "DSGVO-Exporte", en: "GDPR exports" },
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
260
277
|
// Dormant Self-Service-Screen (Art. 15/17/18/20): Export, Aktivitäts-
|
|
261
278
|
// protokoll, Einschränkung, Löschung in einem Screen. Kein r.nav — die
|
|
262
279
|
// App platziert ihn im eingeloggten Bereich. Die React-Component kommt
|
|
@@ -1,37 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// Default-Translations fuer user-data-rights Error-Wire-Keys (S2.U3 Atom 4b.fix3).
|
|
4
|
-
//
|
|
5
|
-
// Wire-Errors aus den download-handlers tragen i18nKeys statt fixe
|
|
6
|
-
// Strings — UI rendert die Keys ueber den Renderer-LocaleProvider. Apps
|
|
7
|
-
// koennen einzelne Keys via `userDataRightsClient({ translations: { de:
|
|
8
|
-
// {...} } })` ueberschreiben (Pattern matched auth-email-password).
|
|
9
|
-
//
|
|
10
|
-
// **Scope 4b.fix3:** nur die download-error-keys. Andere Keys (UI-
|
|
11
|
-
// Texte fuer Forget-Pfad, Status-Labels, Banner) kommen mit Atom 6+
|
|
12
|
-
// (UI-Integration).
|
|
1
|
+
type LocalizedString = { readonly de: string; readonly en: string };
|
|
13
2
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export
|
|
17
|
-
de:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
3
|
+
export const USER_DATA_RIGHTS_I18N: Readonly<Record<string, LocalizedString>> = {
|
|
4
|
+
"screen:export-job-list.title": { de: "DSGVO-Exporte", en: "GDPR exports" },
|
|
5
|
+
"screen:export-job-detail.title": { de: "Export-Job", en: "Export job" },
|
|
6
|
+
"screen:download-attempt-list.title": { de: "Download-Versuche", en: "Download attempts" },
|
|
7
|
+
"screen:privacy-center.title": { de: "Datenschutz", en: "Privacy" },
|
|
8
|
+
"user-data-rights:entity:export-job:field:userId": { de: "Benutzer", en: "User" },
|
|
9
|
+
"user-data-rights:entity:export-job:field:status": { de: "Status", en: "Status" },
|
|
10
|
+
"user-data-rights:entity:export-job:field:requestedAt": { de: "Angefordert", en: "Requested" },
|
|
11
|
+
"user-data-rights:entity:export-job:field:completedAt": { de: "Abgeschlossen", en: "Completed" },
|
|
12
|
+
"user-data-rights:entity:export-job:field:expiresAt": { de: "Läuft ab", en: "Expires" },
|
|
13
|
+
"user-data-rights:entity:export-job:field:requestedFromTenantId": {
|
|
14
|
+
de: "Mandant",
|
|
15
|
+
en: "Tenant",
|
|
16
|
+
},
|
|
17
|
+
"user-data-rights:entity:export-job:field:startedAt": { de: "Gestartet", en: "Started" },
|
|
18
|
+
"user-data-rights:entity:export-job:field:downloadStorageKey": {
|
|
19
|
+
de: "Speicher-Schlüssel",
|
|
20
|
+
en: "Storage key",
|
|
21
|
+
},
|
|
22
|
+
"user-data-rights:entity:export-job:field:bytesWritten": { de: "Bytes", en: "Bytes" },
|
|
23
|
+
"user-data-rights:entity:export-job:field:errorMessage": { de: "Fehler", en: "Error" },
|
|
24
|
+
"user-data-rights:entity:download-attempt:field:attemptedAt": {
|
|
25
|
+
de: "Zeitpunkt",
|
|
26
|
+
en: "Attempted at",
|
|
26
27
|
},
|
|
27
|
-
en:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"The export is not yet ready or has failed. Please check the status endpoint.",
|
|
34
|
-
"userDataRights.errors.download.signedUrlNotSupported":
|
|
35
|
-
"The download is currently unavailable due to a server configuration issue. The operator has been notified.",
|
|
28
|
+
"user-data-rights:entity:download-attempt:field:result": { de: "Ergebnis", en: "Result" },
|
|
29
|
+
"user-data-rights:entity:download-attempt:field:via": { de: "Via", en: "Via" },
|
|
30
|
+
"user-data-rights:entity:download-attempt:field:ip": { de: "IP", en: "IP" },
|
|
31
|
+
"user-data-rights:entity:download-attempt:field:attemptedByUserId": {
|
|
32
|
+
de: "Benutzer",
|
|
33
|
+
en: "User",
|
|
36
34
|
},
|
|
35
|
+
"user-data-rights:entity:download-attempt:field:jobId": { de: "Job", en: "Job" },
|
|
37
36
|
};
|
|
@@ -73,6 +73,7 @@ export const exportJobEntity = createEntity({
|
|
|
73
73
|
// nur Storage.
|
|
74
74
|
userId: createTextField({
|
|
75
75
|
required: true,
|
|
76
|
+
searchable: true,
|
|
76
77
|
}),
|
|
77
78
|
|
|
78
79
|
// **requestedFromTenantId** — der Tenant aus dem der User den Antrag
|
|
@@ -102,6 +103,7 @@ export const exportJobEntity = createEntity({
|
|
|
102
103
|
// entsteht ohne Klick.
|
|
103
104
|
requestedAt: createTimestampField({
|
|
104
105
|
required: true,
|
|
106
|
+
sortable: true,
|
|
105
107
|
}),
|
|
106
108
|
|
|
107
109
|
// Wann hat der Worker mit dem Pickup angefangen. NULL solange
|
|
@@ -141,5 +141,14 @@ export const defaultTranslations: TranslationsByLocale = {
|
|
|
141
141
|
"userDataRights.privacyCenter.deletion.dialogTitle": "Delete your account?",
|
|
142
142
|
"userDataRights.privacyCenter.deletion.dialogDescription":
|
|
143
143
|
"Confirming starts the deletion grace period. You can cancel the deletion until it ends.",
|
|
144
|
+
|
|
145
|
+
"userDataRights.errors.download.notFound":
|
|
146
|
+
"The download link is invalid or belongs to a different account.",
|
|
147
|
+
"userDataRights.errors.download.expired":
|
|
148
|
+
"Your download has expired. Please request a new export.",
|
|
149
|
+
"userDataRights.errors.download.unavailable":
|
|
150
|
+
"The export is not yet ready or has failed. Please check the status endpoint.",
|
|
151
|
+
"userDataRights.errors.download.signedUrlNotSupported":
|
|
152
|
+
"The download is currently unavailable due to a server configuration issue. The operator has been notified.",
|
|
144
153
|
},
|
|
145
154
|
};
|