@cosmicdrift/kumiko-samples 0.364.0 → 0.365.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/LICENSE +1 -1
- package/package.json +1 -1
- package/packages/bundled-features/package.json +1 -1
- package/packages/bundled-features/src/audit/changes.json +12 -0
- package/packages/bundled-features/src/audit/feature.ts +1 -1
- package/packages/bundled-features/src/audit/run-escape-hatch-retention.ts +63 -11
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +1 -3
- package/packages/bundled-features/src/auth-email-password/changes.json +18 -0
- package/packages/bundled-features/src/auth-email-password/feature.ts +4 -7
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +2 -3
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +6 -4
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +7 -3
- package/packages/bundled-features/src/auth-email-password/mail-i18n.ts +10 -0
- package/packages/bundled-features/src/billing-foundation/changes.json +12 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +8 -1
- package/packages/bundled-features/src/billing-foundation/i18n.ts +6 -0
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +10 -2
- package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +20 -8
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +7 -2
- package/packages/bundled-features/src/cap-counter/changes.json +13 -0
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +2 -0
- package/packages/bundled-features/src/cap-overview/changes.json +6 -0
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
- package/packages/bundled-features/src/cap-overview/handlers/tenant-options.query.ts +2 -1
- package/packages/bundled-features/src/channel-discord/discord-channel.ts +4 -2
- package/packages/bundled-features/src/channel-email/changes.json +18 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +30 -20
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +7 -1
- package/packages/bundled-features/src/channel-slack/slack-channel.ts +4 -2
- package/packages/bundled-features/src/channel-teams/teams-channel.ts +4 -2
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +7 -1
- package/packages/bundled-features/src/custom-fields/db/queries/retention.ts +2 -2
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +4 -5
- package/packages/bundled-features/src/delivery/attachment-ref.ts +18 -1
- package/packages/bundled-features/src/delivery/changes.json +12 -0
- package/packages/bundled-features/src/delivery/delivery-service.ts +93 -92
- package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
- package/packages/bundled-features/src/feature-toggles/changes.json +7 -0
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +26 -12
- package/packages/bundled-features/src/files-tenant-data/index.ts +3 -1
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +12 -3
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/jobs/changes.json +12 -0
- package/packages/bundled-features/src/jobs/job-run-logger.ts +5 -2
- package/packages/bundled-features/src/metrics/changes.json +6 -0
- package/packages/bundled-features/src/metrics/filters.ts +28 -12
- package/packages/bundled-features/src/personal-access-tokens/changes.json +6 -0
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +7 -8
- package/packages/bundled-features/src/secrets/changes.json +6 -0
- package/packages/bundled-features/src/secrets/handlers/delete.write.ts +3 -1
- package/packages/bundled-features/src/secrets/write-gate.ts +8 -2
- package/packages/bundled-features/src/shared/index.ts +8 -0
- package/packages/bundled-features/src/shared/resource-image-formats.ts +31 -0
- package/packages/bundled-features/src/shared/sharp-input-limits.ts +7 -8
- package/packages/bundled-features/src/step-dispatcher/webhook-runner.ts +2 -2
- package/packages/bundled-features/src/template-resolver/changes.json +81 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +78 -12
- package/packages/bundled-features/src/template-resolver/compile-steps-block.ts +68 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +11 -1
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +3 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +7 -1
- package/packages/bundled-features/src/template-resolver/document-steps.ts +114 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +12 -0
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +81 -39
- package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +4 -3
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +111 -61
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +1 -0
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +25 -15
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +12 -1
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +12 -2
- package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +48 -9
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +4 -1
- package/packages/bundled-features/src/template-resolver/index.ts +6 -1
- package/packages/bundled-features/src/template-resolver/layout-appearance.ts +25 -9
- package/packages/bundled-features/src/template-resolver/mail-constants.ts +4 -0
- package/packages/bundled-features/src/template-resolver/resource-image.ts +9 -16
- package/packages/bundled-features/src/template-resolver/seeding.ts +2 -0
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +390 -124
- package/packages/bundled-features/src/template-resolver/template-bundle.ts +4 -0
- package/packages/bundled-features/src/template-resolver/template-draft.ts +1 -1
- package/packages/bundled-features/src/template-resolver/template-meta.ts +8 -0
- package/packages/bundled-features/src/template-resolver/template-preview.ts +1 -1
- package/packages/bundled-features/src/template-resolver/template-resources.ts +2 -43
- package/packages/bundled-features/src/template-resolver/template-validation.ts +15 -9
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +34 -0
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +3 -2
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +56 -11
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +6 -3
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +7 -0
- package/packages/bundled-features/src/template-resolver/web/index.ts +1 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +14 -5
- package/packages/bundled-features/src/tenant/changes.json +6 -0
- package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +3 -8
- package/packages/bundled-features/src/tenant/seeding.ts +33 -27
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +14 -3
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +9 -1
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +3 -2
- package/packages/bundled-features/src/tier-engine/changes.json +12 -0
- package/packages/bundled-features/src/tier-engine/constants.ts +9 -0
- package/packages/bundled-features/src/tier-engine/entity.ts +9 -4
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +8 -5
- package/packages/bundled-features/src/tier-engine/index.ts +2 -0
- package/packages/bundled-features/src/user/constants.ts +1 -0
- package/packages/bundled-features/src/user/invited-global-roles.ts +2 -4
- package/packages/bundled-features/src/user-data-rights/changes.json +6 -0
- package/packages/bundled-features/src/user-data-rights/email-templates.ts +0 -1
- package/packages/bundled-features/src/user-data-rights/i18n.ts +5 -0
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +13 -2
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +7 -1
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/feature.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +35 -3
- package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +2 -17
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +33 -9
- package/packages/bundled-features/src/waitlist/i18n.ts +9 -0
- package/packages/bundled-features/src/waitlist/lib.ts +20 -0
- package/packages/bundled-features/src/workflow-runner/feature.ts +1 -1
- package/samples/apps/ui-walkthrough/src/run-config.ts +14 -1
- package/samples/apps/use-all-bundled/README.md +3 -13
- package/samples/apps/use-all-bundled/feature-manifest.json +3 -3
- package/samples/apps/use-all-bundled/scripts/check-coverage.ts +34 -118
- package/samples/apps/use-all-bundled/src/app/notes-feature.ts +1 -1
- package/samples/apps/use-all-bundled/src/run-config.ts +0 -3
- package/samples/recipes/auth-mfa/README.md +2 -1
- package/samples/recipes/auth-mfa/src/feature.ts +1 -0
- package/samples/recipes/custom-handlers/README.md +4 -3
- package/samples/recipes/custom-handlers/src/feature.ts +21 -12
- package/samples/recipes/dashboard-stats/README.md +38 -0
- package/samples/recipes/dashboard-stats/package.json +8 -0
- package/samples/recipes/dashboard-stats/src/feature.ts +104 -0
- package/samples/recipes/legal-pages/README.md +2 -2
- package/samples/recipes/managed-pages/README.md +2 -2
- package/samples/recipes/pdf-audit-export/README.md +2 -1
- package/samples/recipes/pdf-audit-export/package.json +1 -1
- package/samples/recipes/pdf-audit-export/src/feature.ts +68 -48
- package/samples/recipes/workflow-engine/src/db/queries/suspended-runs.ts +2 -2
- package/samples/apps/use-all-bundled/schema/generate.ts +0 -617
- package/samples/apps/use-all-bundled/schema/migrations/meta/0000_snapshot.json +0 -3055
- package/samples/apps/use-all-bundled/schema/migrations/meta/_journal.json +0 -13
- package/samples/apps/use-all-bundled/schema/schema.generated.ts +0 -94
- package/samples/apps/use-all-bundled/schema/schema.ts +0 -9
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Dashboard Stats Sample
|
|
2
|
+
// Shows: a dashboard screen with a stat-group and a single stat, backed by one
|
|
3
|
+
// query handler that counts in the database.
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
createEntity,
|
|
7
|
+
createEntityExecutor,
|
|
8
|
+
createSelectField,
|
|
9
|
+
createTextField,
|
|
10
|
+
defineFeature,
|
|
11
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
export const TICKET_STATUSES = ["open", "closed"] as const;
|
|
15
|
+
|
|
16
|
+
export const ticketEntity = createEntity({
|
|
17
|
+
table: "read_sample_tickets",
|
|
18
|
+
fields: {
|
|
19
|
+
title: createTextField({ personal: false, reason: "is_business_data", required: true }),
|
|
20
|
+
status: createSelectField({ options: TICKET_STATUSES, default: "open", required: true }),
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const TICKET_STATS_QUERY = "tickets:query:ticket:stats";
|
|
25
|
+
|
|
26
|
+
const readerRoles = { roles: ["Admin", "User"] } as const;
|
|
27
|
+
|
|
28
|
+
export const ticketFeature = defineFeature("tickets", (r) => {
|
|
29
|
+
const { executor: ticketExecutor } = createEntityExecutor("ticket", ticketEntity);
|
|
30
|
+
|
|
31
|
+
r.crud("ticket", ticketEntity, {
|
|
32
|
+
write: { access: { roles: ["Admin"] } },
|
|
33
|
+
read: { access: readerRoles },
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Flat result: each dashboard stat reads one field via its valueField.
|
|
37
|
+
// limit: 1 keeps the page tiny; totalCount counts over the whole WHERE in
|
|
38
|
+
// the database, so no rows are loaded just to be counted.
|
|
39
|
+
r.queryHandler(
|
|
40
|
+
"ticket:stats",
|
|
41
|
+
z.object({}),
|
|
42
|
+
async (query, ctx) => {
|
|
43
|
+
const countWithStatus = async (status: (typeof TICKET_STATUSES)[number]): Promise<number> => {
|
|
44
|
+
const page = await ticketExecutor.list(
|
|
45
|
+
{ filter: { field: "status", op: "eq", value: status }, limit: 1, totalCount: true },
|
|
46
|
+
query.user,
|
|
47
|
+
ctx.db,
|
|
48
|
+
);
|
|
49
|
+
return page.total ?? 0;
|
|
50
|
+
};
|
|
51
|
+
const [open, closed] = await Promise.all([
|
|
52
|
+
countWithStatus("open"),
|
|
53
|
+
countWithStatus("closed"),
|
|
54
|
+
]);
|
|
55
|
+
return { open, closed, total: open + closed };
|
|
56
|
+
},
|
|
57
|
+
{ access: readerRoles },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
r.translations({
|
|
61
|
+
keys: {
|
|
62
|
+
"tickets.dashboard.group": { en: "Tickets by status" },
|
|
63
|
+
"tickets.dashboard.open": { en: "Open" },
|
|
64
|
+
"tickets.dashboard.closed": { en: "Closed" },
|
|
65
|
+
"tickets.dashboard.total": { en: "All tickets" },
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
r.screen({
|
|
70
|
+
id: "ticket-dashboard",
|
|
71
|
+
type: "dashboard",
|
|
72
|
+
panels: [
|
|
73
|
+
{
|
|
74
|
+
kind: "stat-group",
|
|
75
|
+
id: "by-status",
|
|
76
|
+
label: "tickets.dashboard.group",
|
|
77
|
+
stats: [
|
|
78
|
+
{
|
|
79
|
+
kind: "stat",
|
|
80
|
+
id: "open",
|
|
81
|
+
label: "tickets.dashboard.open",
|
|
82
|
+
query: TICKET_STATS_QUERY,
|
|
83
|
+
valueField: "open",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
kind: "stat",
|
|
87
|
+
id: "closed",
|
|
88
|
+
label: "tickets.dashboard.closed",
|
|
89
|
+
query: TICKET_STATS_QUERY,
|
|
90
|
+
valueField: "closed",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
kind: "stat",
|
|
96
|
+
id: "total",
|
|
97
|
+
label: "tickets.dashboard.total",
|
|
98
|
+
query: TICKET_STATS_QUERY,
|
|
99
|
+
valueField: "total",
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
access: readerRoles,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -87,8 +87,8 @@ imprint" decision.
|
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
# In the app workspace:
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
bunx kumiko-schema generate <name> # detects text-block entity → SQL migration
|
|
91
|
+
bunx kumiko-schema apply # one-time (pre-deploy step in prod)
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
### 3. Initial seed of the required blocks
|
|
@@ -81,8 +81,8 @@ the recipe lists `createConfigFeature()` explicitly (see `src/feature.ts`).
|
|
|
81
81
|
### 2. Create the table
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
bunx kumiko-schema generate <name> # detects the `page` entity → SQL migration
|
|
85
|
+
bunx kumiko-schema apply
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
### 3. Authoring + branding
|
|
@@ -5,7 +5,8 @@ A write handler exports tenant data as a PDF and keeps it as a FileRef.
|
|
|
5
5
|
## What it shows
|
|
6
6
|
|
|
7
7
|
- `renderTablePdf` (`@cosmicdrift/kumiko-bundled-features/renderer-pdf`) draws a table document in pure JavaScript: repeating header, weighted columns, page footer. The handler passes ready-formatted strings.
|
|
8
|
-
- `
|
|
8
|
+
- The export is a `beforeTransaction` handler: it reads the rows (reads work in that phase), renders the PDF and calls `uploadFileBytesBeforeTransaction` (`@cosmicdrift/kumiko-framework/files`), which validates the bytes and writes them to the tenant-prefixed storage key. The upload runs before the transaction because storage I/O inside it would hold a pool connection for as long as the upload takes.
|
|
9
|
+
- The handler itself calls `createFileRefFromUploadedBytes(ctx, event.prepared)` and creates the `file_refs` row and the `fileRef.created` event in the transaction. If the transaction does not commit, the phase's `release` deletes the uploaded bytes again (only when no row exists for the storage key). A crash between upload and commit leaves the bytes in storage; nothing sweeps them.
|
|
9
10
|
- The result is a normal FileRef: `GET /api/files/:id` serves it with `Content-Type: application/pdf`, other tenants get 404.
|
|
10
11
|
|
|
11
12
|
## Source
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-sample-pdf-audit-export",
|
|
3
|
-
"description": "Exports an audit table as a PDF: a write handler renders rows with renderTablePdf and
|
|
3
|
+
"description": "Exports an audit table as a PDF: a write handler renders rows with renderTablePdf and uploads the bytes before the transaction (uploadFileBytesBeforeTransaction) and stores them as a FileRef (createFileRefFromUploadedBytes).",
|
|
4
4
|
"private": true,
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@cosmicdrift/kumiko-bundled-features": "workspace:*",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// PDF Audit Export Sample
|
|
2
2
|
//
|
|
3
3
|
// A write handler turns tenant data into a PDF and keeps it as a regular
|
|
4
|
-
// FileRef: renderTablePdf draws the table (pure JS, no browser)
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// FileRef: renderTablePdf draws the table (pure JS, no browser). The storage
|
|
5
|
+
// write runs in the beforeTransaction phase, so the transaction holds no pool
|
|
6
|
+
// connection while the bytes upload; the handler then only creates the
|
|
7
|
+
// fileRef row from event.prepared.
|
|
7
8
|
|
|
8
9
|
import { renderTablePdf } from "@cosmicdrift/kumiko-bundled-features/renderer-pdf";
|
|
9
10
|
import {
|
|
@@ -13,8 +14,13 @@ import {
|
|
|
13
14
|
createSelectField,
|
|
14
15
|
createTextField,
|
|
15
16
|
defineFeature,
|
|
17
|
+
defineWriteHandler,
|
|
16
18
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
17
|
-
import {
|
|
19
|
+
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
20
|
+
import {
|
|
21
|
+
createFileRefFromUploadedBytes,
|
|
22
|
+
uploadFileBytesBeforeTransaction,
|
|
23
|
+
} from "@cosmicdrift/kumiko-framework/files";
|
|
18
24
|
import * as z from "zod";
|
|
19
25
|
|
|
20
26
|
export const INSPECTION_RESULTS = ["ok", "defect"] as const;
|
|
@@ -44,7 +50,8 @@ function toFileNameSegment(text: string): string {
|
|
|
44
50
|
return text.replaceAll(/[^A-Za-z0-9_-]+/g, "-");
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
// One row above the limit tells "exactly full" from "cut off"; entity lists cap at 200 rows.
|
|
54
|
+
const AUDIT_ROW_LIMIT = 100;
|
|
48
55
|
|
|
49
56
|
const access = { access: { roles: ["Admin", "User"] } } as const;
|
|
50
57
|
|
|
@@ -65,50 +72,63 @@ export const pdfAuditExportFeature = defineFeature("pdf-audit-export", (r) => {
|
|
|
65
72
|
);
|
|
66
73
|
|
|
67
74
|
r.writeHandler(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
defineWriteHandler({
|
|
76
|
+
name: "export-audit",
|
|
77
|
+
schema: z.object({ assetName: z.string().min(1), from: z.iso.date(), to: z.iso.date() }),
|
|
78
|
+
access: access.access,
|
|
79
|
+
beforeTransaction: async (event, ctx) => {
|
|
80
|
+
const { assetName, from, to } = event.payload;
|
|
81
|
+
const inspections = await inspectionExecutor.list(
|
|
82
|
+
{
|
|
83
|
+
filters: [
|
|
84
|
+
{ field: "assetName", op: "eq", value: assetName },
|
|
85
|
+
{ field: "inspectedOn", op: "gte", value: from },
|
|
86
|
+
{ field: "inspectedOn", op: "lte", value: to },
|
|
87
|
+
],
|
|
88
|
+
sort: "inspectedOn",
|
|
89
|
+
sortDirection: "asc",
|
|
90
|
+
limit: AUDIT_ROW_LIMIT + 1,
|
|
91
|
+
},
|
|
92
|
+
event.user,
|
|
93
|
+
ctx.db,
|
|
94
|
+
);
|
|
95
|
+
// A protocol that silently drops inspections would look complete.
|
|
96
|
+
if (inspections.rows.length > AUDIT_ROW_LIMIT) {
|
|
97
|
+
return writeFailure(
|
|
98
|
+
new UnprocessableError("audit_period_too_large", {
|
|
99
|
+
details: { limit: AUDIT_ROW_LIMIT },
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
86
103
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
const { pdfBytes } = await renderTablePdf({
|
|
105
|
+
title: `Prüfprotokoll ${assetName}`,
|
|
106
|
+
meta: [
|
|
107
|
+
{ label: "Zeitraum", value: `${formatGermanDate(from)} bis ${formatGermanDate(to)}` },
|
|
108
|
+
{ label: "Prüfungen", value: String(inspections.rows.length) },
|
|
109
|
+
],
|
|
110
|
+
columns: [{ header: "Datum" }, { header: "Ergebnis" }, { header: "Prüfer", weight: 2 }],
|
|
111
|
+
rows: inspections.rows.map((row) => [
|
|
112
|
+
formatGermanDate(String(row["inspectedOn"])),
|
|
113
|
+
RESULT_LABELS[z.enum(INSPECTION_RESULTS).parse(row["result"])],
|
|
114
|
+
String(row["inspector"] ?? ""),
|
|
115
|
+
]),
|
|
116
|
+
emptyText: "Keine Prüfungen im Zeitraum",
|
|
117
|
+
footerText: `Prüfbericht ${assetName}`,
|
|
118
|
+
pageLabel: "Seite {page} von {pages}",
|
|
119
|
+
});
|
|
103
120
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
return uploadFileBytesBeforeTransaction(ctx, {
|
|
122
|
+
fileName: `audit-${toFileNameSegment(assetName)}.pdf`,
|
|
123
|
+
mimeType: "application/pdf",
|
|
124
|
+
data: pdfBytes,
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
handler: async (event, ctx) => {
|
|
128
|
+
const created = await createFileRefFromUploadedBytes(ctx, event.prepared);
|
|
129
|
+
if (!created.isSuccess) return created;
|
|
130
|
+
return { isSuccess: true as const, data: { fileRefId: created.data.id } };
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
113
133
|
);
|
|
114
134
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import type
|
|
2
|
+
import { type DbRunner, toPostgresArrayLiteral } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
|
|
4
4
|
export async function selectExpiredSuspensionEvents(
|
|
5
5
|
db: DbRunner,
|
|
@@ -9,6 +9,6 @@ export async function selectExpiredSuspensionEvents(
|
|
|
9
9
|
`SELECT * FROM "kumiko_events"
|
|
10
10
|
WHERE "type" = ANY($1::text[])
|
|
11
11
|
AND (("payload"->>'wakeAt')::timestamptz < now() OR ("payload"->>'timeoutAt')::timestamptz < now())`,
|
|
12
|
-
[eventTypes
|
|
12
|
+
[toPostgresArrayLiteral(eventTypes)],
|
|
13
13
|
)) as Record<string, unknown>[];
|
|
14
14
|
}
|