@cosmicdrift/kumiko-bundled-features 0.132.0 → 0.134.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.132.0",
3
+ "version": "0.134.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>",
@@ -107,11 +107,11 @@
107
107
  "./step-dispatcher": "./src/step-dispatcher/index.ts"
108
108
  },
109
109
  "dependencies": {
110
- "@cosmicdrift/kumiko-dispatcher-live": "0.132.0",
111
- "@cosmicdrift/kumiko-framework": "0.132.0",
112
- "@cosmicdrift/kumiko-headless": "0.132.0",
113
- "@cosmicdrift/kumiko-renderer": "0.132.0",
114
- "@cosmicdrift/kumiko-renderer-web": "0.132.0",
110
+ "@cosmicdrift/kumiko-dispatcher-live": "0.134.0",
111
+ "@cosmicdrift/kumiko-framework": "0.134.0",
112
+ "@cosmicdrift/kumiko-headless": "0.134.0",
113
+ "@cosmicdrift/kumiko-renderer": "0.134.0",
114
+ "@cosmicdrift/kumiko-renderer-web": "0.134.0",
115
115
  "@mollie/api-client": "^4.5.0",
116
116
  "@node-rs/argon2": "^2.0.2",
117
117
  "@types/nodemailer": "^8.0.0",
@@ -1,5 +1,6 @@
1
1
  // @runtime client
2
2
  import { usePrimitives } from "@cosmicdrift/kumiko-renderer";
3
+ import { FormScreenShell } from "@cosmicdrift/kumiko-renderer-web";
3
4
  import type { ReactNode } from "react";
4
5
 
5
6
  export type OverviewCard = {
@@ -32,7 +33,7 @@ export function OverviewLayout({
32
33
  const gridClass = columns === 2 ? "sm:grid-cols-2" : "sm:grid-cols-3";
33
34
 
34
35
  return (
35
- <div data-testid={testId} className="flex max-w-4xl flex-col gap-6 p-6">
36
+ <FormScreenShell testId={testId} maxWidth="4xl" className="flex flex-col gap-6">
36
37
  <Heading variant="page">{title}</Heading>
37
38
 
38
39
  {state.kind === "loading" && (
@@ -63,6 +64,6 @@ export function OverviewLayout({
63
64
  ))}
64
65
  </div>
65
66
  )}
66
- </div>
67
+ </FormScreenShell>
67
68
  );
68
69
  }
@@ -59,7 +59,7 @@ export function DeliveryLogScreen(): ReactNode {
59
59
  }
60
60
 
61
61
  return (
62
- <FormScreenShell testId="delivery-log-screen" className="flex max-w-5xl flex-col gap-6">
62
+ <FormScreenShell testId="delivery-log-screen" maxWidth="4xl" className="flex flex-col gap-6">
63
63
  <Heading variant="page">{t("delivery.log.title")}</Heading>
64
64
 
65
65
  <Card options={{ padded: false }}>
@@ -69,7 +69,7 @@ export function ToggleAdminScreen(): ReactNode {
69
69
  }
70
70
 
71
71
  return (
72
- <FormScreenShell testId="toggle-admin-screen" className="flex max-w-5xl flex-col gap-6">
72
+ <FormScreenShell testId="toggle-admin-screen" maxWidth="4xl" className="flex flex-col gap-6">
73
73
  <Heading variant="page">{t("feature-toggles.admin.title")}</Heading>
74
74
 
75
75
  {actionError !== null && <Banner variant="error">{actionError}</Banner>}
@@ -12,6 +12,7 @@ import {
12
12
  useQuery,
13
13
  useTranslation,
14
14
  } from "@cosmicdrift/kumiko-renderer";
15
+ import { FormScreenShell } from "@cosmicdrift/kumiko-renderer-web";
15
16
  import { type ReactNode, useState } from "react";
16
17
  import { PatHandlers, PatQueries } from "../constants";
17
18
  import { parseGrant } from "../scopes";
@@ -133,8 +134,8 @@ export function PatTokensScreen({
133
134
  return parts.join(" · ");
134
135
  };
135
136
 
136
- return (
137
- <div className={embedded ? "flex flex-col gap-6" : "flex max-w-3xl flex-col gap-6 p-6"}>
137
+ const content = (
138
+ <>
138
139
  <Heading>{t("pat.title")}</Heading>
139
140
 
140
141
  {minted && (
@@ -250,6 +251,15 @@ export function PatTokensScreen({
250
251
  );
251
252
  })}
252
253
  </div>
253
- </div>
254
+ </>
255
+ );
256
+
257
+ if (embedded) {
258
+ return <div className="flex flex-col gap-6">{content}</div>;
259
+ }
260
+ return (
261
+ <FormScreenShell testId="pat-tokens-screen" maxWidth="3xl">
262
+ {content}
263
+ </FormScreenShell>
254
264
  );
255
265
  }
@@ -9,7 +9,7 @@ import {
9
9
  PrimitivesProvider,
10
10
  } from "@cosmicdrift/kumiko-renderer";
11
11
  import { defaultPrimitives } from "@cosmicdrift/kumiko-renderer-web";
12
- import { render, screen } from "@testing-library/react";
12
+ import { act, fireEvent, render, screen } from "@testing-library/react";
13
13
  import type { ReactNode } from "react";
14
14
  import { textContentClient } from "../client-plugin";
15
15
 
@@ -20,18 +20,23 @@ mock.module("@cosmicdrift/kumiko-bundled-features/auth-email-password/web", () =
20
20
  const actual_renderer = await import("@cosmicdrift/kumiko-renderer");
21
21
  mock.module("@cosmicdrift/kumiko-renderer", () => ({
22
22
  ...actual_renderer,
23
- useDispatcher: mock(() => ({
24
- write: mock(),
25
- query: mock(),
26
- })),
23
+ useDispatcher: mock(),
27
24
  useQuery: mock(() => ({
28
- data: { slug: "imprint", lang: "de", title: "Impressum", body: "Inhalt" },
25
+ data: {
26
+ slug: "imprint",
27
+ lang: "de",
28
+ title: "Impressum",
29
+ body: "Inhalt",
30
+ folder: "legal",
31
+ },
29
32
  loading: false,
30
33
  error: null,
31
34
  refetch: mock(),
32
35
  })),
33
36
  }));
34
37
 
38
+ const { useDispatcher } = await import("@cosmicdrift/kumiko-renderer");
39
+
35
40
  const TARGET = {
36
41
  featureId: "text-content",
37
42
  action: "edit",
@@ -109,3 +114,25 @@ describe("TextContentEditor — role-based write-access", () => {
109
114
  expect(screen.queryByRole("button", { name: /^speichern/i })).toBeNull();
110
115
  });
111
116
  });
117
+
118
+ describe("TextContentEditor — handleSave", () => {
119
+ test("reicht das geladene folder unverändert an den Write-Payload durch (#898)", async () => {
120
+ // biome-ignore lint/suspicious/noExplicitAny: Bun mock function
121
+ (useShellUser as any).mockReturnValue({ id: "u1", roles: ["TenantAdmin"] });
122
+ const write = mock(() => Promise.resolve({ isSuccess: true, data: { isNew: false } }));
123
+ // biome-ignore lint/suspicious/noExplicitAny: Bun mock function
124
+ (useDispatcher as any).mockReturnValue({ write, query: mock() });
125
+
126
+ const Editor = getEditor();
127
+ render(<Editor target={TARGET} onClose={() => {}} />, { wrapper: Wrapper });
128
+
129
+ await act(async () => {
130
+ fireEvent.click(screen.getByRole("button", { name: /speichern/i }));
131
+ await Promise.resolve();
132
+ });
133
+
134
+ expect(write).toHaveBeenCalledTimes(1);
135
+ const [, payload] = write.mock.calls[0] as unknown as [unknown, { folder: string | null }];
136
+ expect(payload.folder).toBe("legal");
137
+ });
138
+ });
@@ -211,6 +211,7 @@ type TextBlock = {
211
211
  readonly lang: string;
212
212
  readonly title: string;
213
213
  readonly body: string | null;
214
+ readonly folder: string | null;
214
215
  readonly updatedAt: string;
215
216
  };
216
217
 
@@ -287,6 +288,7 @@ function TextContentEditor({
287
288
  lang,
288
289
  title,
289
290
  body: body.length > 0 ? body : null,
291
+ folder: loaded?.folder ?? null,
290
292
  ...(tenantIdOverride !== undefined && { tenantIdOverride }),
291
293
  });
292
294
  if (result.isSuccess) {
@@ -1,25 +0,0 @@
1
- import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
2
- import type { DbRunner } from "@cosmicdrift/kumiko-framework/db";
3
- import type { Temporal } from "temporal-polyfill";
4
-
5
- export async function updateFeatureToggleOptimistic(
6
- db: DbRunner,
7
- params: {
8
- readonly enabled: boolean;
9
- readonly updatedBy: string;
10
- readonly updatedAt: Temporal.Instant;
11
- readonly featureName: string;
12
- readonly expectedVersion: number;
13
- },
14
- ): Promise<readonly unknown[]> {
15
- return asRawClient(db).unsafe(
16
- 'UPDATE "read_global_feature_state" SET enabled = $1, version = version + 1, updated_by = $2, updated_at = $3 WHERE feature_name = $4 AND version = $5 RETURNING *',
17
- [
18
- params.enabled,
19
- params.updatedBy,
20
- params.updatedAt,
21
- params.featureName,
22
- params.expectedVersion,
23
- ],
24
- );
25
- }
@@ -1 +0,0 @@
1
- export { securePageHeaders } from "../page-render/security-headers";
@@ -1,5 +0,0 @@
1
- // Re-exports aus den schema/<entity>.ts Files. Eine Datei pro Entity
2
- // — wenn das Feature später weitere Entities hinzubekommt, kommen sie
3
- // als zusätzliche Datei rein und werden hier exportiert.
4
-
5
- export * from "./user-session";
@@ -1,5 +0,0 @@
1
- // Re-exports aus den schema/<entity>.ts Files. Eine Datei pro Entity
2
- // — wenn das Feature später weitere Entities hinzubekommt, kommen sie
3
- // als zusätzliche Datei rein und werden hier exportiert.
4
-
5
- export * from "./tenant";
@@ -1,5 +0,0 @@
1
- // Re-exports aus den schema/<entity>.ts Files. Eine Datei pro Entity
2
- // — wenn das Feature später weitere Entities hinzubekommt, kommen sie
3
- // als zusätzliche Datei rein und werden hier exportiert.
4
-
5
- export * from "./user";