@questpie/admin 3.0.4 → 3.0.5

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.
Files changed (58) hide show
  1. package/dist/client/components/blocks/block-editor-layout.mjs +2 -2
  2. package/dist/client/components/blocks/block-library-sidebar.mjs +89 -61
  3. package/dist/client/components/media/media-grid.mjs +95 -78
  4. package/dist/client/components/primitives/select-multi.mjs +388 -368
  5. package/dist/client/components/primitives/select-single.mjs +344 -331
  6. package/dist/client/components/widgets/chart-widget.mjs +78 -62
  7. package/dist/client/components/widgets/progress-widget.mjs +39 -37
  8. package/dist/client/components/widgets/quick-actions-widget.mjs +111 -90
  9. package/dist/client/components/widgets/recent-items-widget.mjs +40 -38
  10. package/dist/client/components/widgets/table-widget.mjs +4 -3
  11. package/dist/client/components/widgets/timeline-widget.mjs +92 -74
  12. package/dist/client/components/widgets/value-widget.mjs +164 -144
  13. package/dist/client/hooks/use-server-actions.mjs +21 -16
  14. package/dist/client/preview/block-scope-context.d.mts +2 -2
  15. package/dist/client/preview/preview-banner.d.mts +2 -2
  16. package/dist/client/preview/preview-banner.mjs +75 -46
  17. package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
  18. package/dist/client/views/auth/auth-layout.d.mts +2 -2
  19. package/dist/client/views/auth/forgot-password-form.d.mts +2 -2
  20. package/dist/client/views/auth/login-form.d.mts +2 -2
  21. package/dist/client/views/auth/reset-password-form.d.mts +2 -2
  22. package/dist/client/views/auth/setup-form.d.mts +2 -2
  23. package/dist/client/views/collection/table-view.mjs +26 -26
  24. package/dist/client/views/layout/admin-layout.mjs +151 -131
  25. package/dist/client/views/layout/admin-router.mjs +297 -180
  26. package/dist/client/views/layout/admin-sidebar.mjs +126 -116
  27. package/dist/client/views/pages/login-page.d.mts +2 -2
  28. package/dist/client/views/pages/reset-password-page.d.mts +2 -2
  29. package/dist/client/views/pages/setup-page.mjs +67 -51
  30. package/dist/server/i18n/index.mjs +13 -7
  31. package/dist/server/i18n/messages/cs.mjs +391 -1
  32. package/dist/server/i18n/messages/de.mjs +389 -1
  33. package/dist/server/i18n/messages/en.mjs +102 -0
  34. package/dist/server/i18n/messages/es.mjs +389 -1
  35. package/dist/server/i18n/messages/fr.mjs +389 -1
  36. package/dist/server/i18n/messages/pl.mjs +393 -1
  37. package/dist/server/i18n/messages/pt.mjs +386 -1
  38. package/dist/server/i18n/messages/sk.mjs +133 -1
  39. package/dist/server/modules/admin/collections/account.d.mts +50 -50
  40. package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
  41. package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
  42. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +41 -41
  43. package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
  44. package/dist/server/modules/admin/collections/assets.d.mts +20 -20
  45. package/dist/server/modules/admin/collections/session.d.mts +42 -42
  46. package/dist/server/modules/admin/collections/user.d.mts +32 -32
  47. package/dist/server/modules/admin/collections/verification.d.mts +36 -36
  48. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
  49. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  50. package/dist/server/modules/admin/routes/execute-action.mjs +34 -28
  51. package/dist/server/modules/admin/routes/i18n-helpers.mjs +34 -0
  52. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  53. package/dist/server/modules/admin/routes/preview.mjs +25 -17
  54. package/dist/server/modules/admin/routes/route-helpers.mjs +1 -1
  55. package/dist/server/modules/admin/routes/setup.mjs +10 -7
  56. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  57. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +27 -27
  58. package/package.json +3 -3
@@ -1,4 +1,5 @@
1
1
  import { getApp, getAppState, getSession } from "./route-helpers.mjs";
2
+ import { translateAdminMessage } from "./i18n-helpers.mjs";
2
3
  import { z } from "zod";
3
4
  import { route } from "questpie";
4
5
 
@@ -98,9 +99,10 @@ function serializeActionFormFields(fields) {
98
99
  async function executeAction(app, request, session) {
99
100
  const { collection: collectionSlug, actionId, itemId, itemIds, data, locale } = request;
100
101
  const collection$1 = getAppState(app).collections?.[collectionSlug];
102
+ const t = (key, params) => translateAdminMessage(locale, key, params);
101
103
  if (!collection$1) return {
102
104
  success: false,
103
- error: `Collection "${collectionSlug}" not found`
105
+ error: t("action.collectionNotFound", { collection: collectionSlug })
104
106
  };
105
107
  const actionsConfig = (collection$1.state || collection$1).adminActions;
106
108
  if ((actionsConfig?.builtin || [
@@ -124,10 +126,13 @@ async function executeAction(app, request, session) {
124
126
  const customAction = actionsConfig?.custom?.find((a) => a.id === actionId);
125
127
  if (!customAction) return {
126
128
  success: false,
127
- error: `Action "${actionId}" not found on collection "${collectionSlug}"`
129
+ error: t("action.notFound", {
130
+ action: actionId,
131
+ collection: collectionSlug
132
+ })
128
133
  };
129
134
  if (customAction.form) {
130
- const validationError = validateActionFormData(customAction.form.fields, data || {});
135
+ const validationError = validateActionFormData(customAction.form.fields, data || {}, t);
131
136
  if (validationError) return {
132
137
  success: false,
133
138
  result: {
@@ -160,7 +165,7 @@ async function executeAction(app, request, session) {
160
165
  success: false,
161
166
  result: {
162
167
  type: "error",
163
- toast: { message: error instanceof Error ? error.message : "Action execution failed" }
168
+ toast: { message: error instanceof Error ? error.message : t("action.executionFailed") }
164
169
  }
165
170
  };
166
171
  }
@@ -169,7 +174,8 @@ async function executeAction(app, request, session) {
169
174
  * Execute a built-in action.
170
175
  */
171
176
  async function executeBuiltinAction(app, params) {
172
- const { collectionSlug, actionId, itemId, itemIds, data } = params;
177
+ const { collectionSlug, actionId, itemId, itemIds, data, locale } = params;
178
+ const t = (key, messageParams) => translateAdminMessage(locale, key, messageParams);
173
179
  const appRec = app;
174
180
  const collectionCrud = appRec.api?.collections?.[collectionSlug];
175
181
  const crudContext = {
@@ -185,7 +191,7 @@ async function executeBuiltinAction(app, params) {
185
191
  success: true,
186
192
  result: {
187
193
  type: "success",
188
- toast: { message: "Item created successfully" },
194
+ toast: { message: t("action.itemCreated") },
189
195
  effects: {
190
196
  invalidate: [collectionSlug],
191
197
  redirect: `/admin/collections/${collectionSlug}/${result.id}`
@@ -198,7 +204,7 @@ async function executeBuiltinAction(app, params) {
198
204
  success: false,
199
205
  result: {
200
206
  type: "error",
201
- toast: { message: "Item ID is required for save action" }
207
+ toast: { message: t("action.itemIdRequired.save") }
202
208
  }
203
209
  };
204
210
  await appRec.update(collectionSlug, itemId, data || {});
@@ -206,7 +212,7 @@ async function executeBuiltinAction(app, params) {
206
212
  success: true,
207
213
  result: {
208
214
  type: "success",
209
- toast: { message: "Item saved successfully" },
215
+ toast: { message: t("action.itemSaved") },
210
216
  effects: { invalidate: [collectionSlug] }
211
217
  }
212
218
  };
@@ -215,7 +221,7 @@ async function executeBuiltinAction(app, params) {
215
221
  success: false,
216
222
  result: {
217
223
  type: "error",
218
- toast: { message: "Item ID is required for delete action" }
224
+ toast: { message: t("action.itemIdRequired.delete") }
219
225
  }
220
226
  };
221
227
  await appRec.delete(collectionSlug, itemId);
@@ -223,7 +229,7 @@ async function executeBuiltinAction(app, params) {
223
229
  success: true,
224
230
  result: {
225
231
  type: "success",
226
- toast: { message: "Item deleted successfully" },
232
+ toast: { message: t("action.itemDeleted") },
227
233
  effects: {
228
234
  invalidate: [collectionSlug],
229
235
  redirect: `/admin/collections/${collectionSlug}`
@@ -235,7 +241,7 @@ async function executeBuiltinAction(app, params) {
235
241
  success: false,
236
242
  result: {
237
243
  type: "error",
238
- toast: { message: "Item IDs are required for bulk delete action" }
244
+ toast: { message: t("action.itemIdsRequired.bulkDelete") }
239
245
  }
240
246
  };
241
247
  await Promise.all(itemIds.map((id) => appRec.delete(collectionSlug, id)));
@@ -243,7 +249,7 @@ async function executeBuiltinAction(app, params) {
243
249
  success: true,
244
250
  result: {
245
251
  type: "success",
246
- toast: { message: `${itemIds.length} items deleted successfully` },
252
+ toast: { message: t("action.itemsDeleted", { count: itemIds.length }) },
247
253
  effects: { invalidate: [collectionSlug] }
248
254
  }
249
255
  };
@@ -252,7 +258,7 @@ async function executeBuiltinAction(app, params) {
252
258
  success: false,
253
259
  result: {
254
260
  type: "error",
255
- toast: { message: "Item ID is required for restore action" }
261
+ toast: { message: t("action.itemIdRequired.restore") }
256
262
  }
257
263
  };
258
264
  if (typeof appRec.restore === "function") await appRec.restore(collectionSlug, itemId);
@@ -261,14 +267,14 @@ async function executeBuiltinAction(app, params) {
261
267
  success: false,
262
268
  result: {
263
269
  type: "error",
264
- toast: { message: "Restore is not supported for this collection" }
270
+ toast: { message: t("action.restoreUnsupported") }
265
271
  }
266
272
  };
267
273
  return {
268
274
  success: true,
269
275
  result: {
270
276
  type: "success",
271
- toast: { message: "Item restored successfully" },
277
+ toast: { message: t("action.itemRestored") },
272
278
  effects: {
273
279
  invalidate: [collectionSlug],
274
280
  redirect: `/admin/collections/${collectionSlug}/${itemId}`
@@ -280,7 +286,7 @@ async function executeBuiltinAction(app, params) {
280
286
  success: false,
281
287
  result: {
282
288
  type: "error",
283
- toast: { message: "Item IDs are required for bulk restore action" }
289
+ toast: { message: t("action.itemIdsRequired.bulkRestore") }
284
290
  }
285
291
  };
286
292
  if (typeof appRec.restore === "function") await Promise.all(itemIds.map((id) => appRec.restore(collectionSlug, id)));
@@ -296,7 +302,7 @@ async function executeBuiltinAction(app, params) {
296
302
  success: true,
297
303
  result: {
298
304
  type: "success",
299
- toast: { message: `${itemIds.length} items restored successfully` },
305
+ toast: { message: t("action.itemsRestored", { count: itemIds.length }) },
300
306
  effects: { invalidate: [collectionSlug] }
301
307
  }
302
308
  };
@@ -305,7 +311,7 @@ async function executeBuiltinAction(app, params) {
305
311
  success: false,
306
312
  result: {
307
313
  type: "error",
308
- toast: { message: "Item ID is required for duplicate action" }
314
+ toast: { message: t("action.itemIdRequired.duplicate") }
309
315
  }
310
316
  };
311
317
  const original = await appRec.findById(collectionSlug, itemId);
@@ -313,7 +319,7 @@ async function executeBuiltinAction(app, params) {
313
319
  success: false,
314
320
  result: {
315
321
  type: "error",
316
- toast: { message: "Item not found" }
322
+ toast: { message: t("action.itemNotFound") }
317
323
  }
318
324
  };
319
325
  const { id, createdAt, updatedAt, ...duplicateData } = original;
@@ -322,7 +328,7 @@ async function executeBuiltinAction(app, params) {
322
328
  success: true,
323
329
  result: {
324
330
  type: "success",
325
- toast: { message: "Item duplicated successfully" },
331
+ toast: { message: t("action.itemDuplicated") },
326
332
  effects: {
327
333
  invalidate: [collectionSlug],
328
334
  redirect: `/admin/collections/${collectionSlug}/${duplicated.id}`
@@ -335,7 +341,7 @@ async function executeBuiltinAction(app, params) {
335
341
  success: false,
336
342
  result: {
337
343
  type: "error",
338
- toast: { message: "Item ID is required for transition action" }
344
+ toast: { message: t("action.itemIdRequired.transition") }
339
345
  }
340
346
  };
341
347
  const stage = data?.stage;
@@ -343,7 +349,7 @@ async function executeBuiltinAction(app, params) {
343
349
  success: false,
344
350
  result: {
345
351
  type: "error",
346
- toast: { message: "Target stage is required for transition action" }
352
+ toast: { message: t("action.targetStageRequired") }
347
353
  }
348
354
  };
349
355
  if (collectionCrud?.transitionStage) {
@@ -358,14 +364,14 @@ async function executeBuiltinAction(app, params) {
358
364
  success: false,
359
365
  result: {
360
366
  type: "error",
361
- toast: { message: "Workflow transitions are not supported for this collection" }
367
+ toast: { message: t("action.workflowUnsupported") }
362
368
  }
363
369
  };
364
370
  return {
365
371
  success: true,
366
372
  result: {
367
373
  type: "success",
368
- toast: { message: `Transitioned to "${stage}" successfully` },
374
+ toast: { message: t("workflow.transitionSuccess", { stage }) },
369
375
  effects: { invalidate: [collectionSlug] }
370
376
  }
371
377
  };
@@ -374,7 +380,7 @@ async function executeBuiltinAction(app, params) {
374
380
  success: false,
375
381
  result: {
376
382
  type: "error",
377
- toast: { message: `Unknown built-in action: ${actionId}` }
383
+ toast: { message: t("action.unknownBuiltin", { action: actionId }) }
378
384
  }
379
385
  };
380
386
  }
@@ -384,7 +390,7 @@ async function executeBuiltinAction(app, params) {
384
390
  success: false,
385
391
  result: {
386
392
  type: "error",
387
- toast: { message: error instanceof Error ? error.message : "Action execution failed" }
393
+ toast: { message: error instanceof Error ? error.message : t("action.executionFailed") }
388
394
  }
389
395
  };
390
396
  }
@@ -406,8 +412,8 @@ function isFieldRequired(field$1) {
406
412
  * Validate form data against action form fields.
407
413
  * Returns error message if validation fails, null if valid.
408
414
  */
409
- function validateActionFormData(fields, data) {
410
- for (const [fieldName, fieldConfig] of Object.entries(fields)) if (isFieldRequired(fieldConfig) && !data[fieldName]) return `Field "${fieldName}" is required`;
415
+ function validateActionFormData(fields, data, t) {
416
+ for (const [fieldName, fieldConfig] of Object.entries(fields)) if (isFieldRequired(fieldConfig) && !data[fieldName]) return t("action.fieldRequired", { field: fieldName });
411
417
  return null;
412
418
  }
413
419
  const executeActionRequestSchema = z.object({
@@ -0,0 +1,34 @@
1
+ import { getAdminMessagesForLocale } from "../../../i18n/index.mjs";
2
+
3
+ //#region src/server/modules/admin/routes/i18n-helpers.ts
4
+ function getPluralCount(params) {
5
+ if (!params) return 1;
6
+ for (const key of [
7
+ "count",
8
+ "total",
9
+ "length"
10
+ ]) if (typeof params[key] === "number") return params[key];
11
+ return 1;
12
+ }
13
+ function selectMessage(value, locale, params) {
14
+ if (typeof value === "string") return value;
15
+ const count = getPluralCount(params);
16
+ try {
17
+ return value[new Intl.PluralRules(locale).select(count)] ?? value.other;
18
+ } catch {
19
+ return count === 1 ? value.one : value.other;
20
+ }
21
+ }
22
+ function interpolate(template, params) {
23
+ if (!params) return template;
24
+ return template.replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key) => params[key] === void 0 ? `{{${key}}}` : String(params[key]));
25
+ }
26
+ function translateAdminMessage(locale, key, params) {
27
+ const resolvedLocale = locale ?? "en";
28
+ const message = getAdminMessagesForLocale(resolvedLocale)[key];
29
+ if (message === void 0) return key;
30
+ return interpolate(selectMessage(message, resolvedLocale, params), params);
31
+ }
32
+
33
+ //#endregion
34
+ export { translateAdminMessage };
@@ -1,4 +1,4 @@
1
- import * as questpie97 from "questpie";
1
+ import * as questpie125 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/preview.d.ts
4
4
 
@@ -21,13 +21,13 @@ interface PreviewTokenPayload {
21
21
  * @returns Object with preview functions
22
22
  */
23
23
  declare function createPreviewFunctions(secret: string): {
24
- mintPreviewToken: questpie97.JsonRouteDefinition<{
24
+ mintPreviewToken: questpie125.JsonRouteDefinition<{
25
25
  path: string;
26
26
  ttlMs?: number | undefined;
27
- }, any, questpie97.JsonRouteParams>;
28
- verifyPreviewToken: questpie97.JsonRouteDefinition<{
27
+ }, any, questpie125.JsonRouteParams>;
28
+ verifyPreviewToken: questpie125.JsonRouteDefinition<{
29
29
  token: string;
30
- }, any, questpie97.JsonRouteParams>;
30
+ }, any, questpie125.JsonRouteParams>;
31
31
  };
32
32
  /**
33
33
  * Verify a preview token without RPC.
@@ -63,18 +63,18 @@ declare function createPreviewTokenVerifier(secret?: string): (token: string) =>
63
63
  * Used by the `adminModule` to register preview RPC functions.
64
64
  */
65
65
  declare const previewFunctions: {
66
- getPreviewUrl: questpie97.JsonRouteDefinition<{
66
+ getPreviewUrl: questpie125.JsonRouteDefinition<{
67
67
  collection: string;
68
68
  record: Record<string, unknown>;
69
69
  locale?: string | undefined;
70
- }, any, questpie97.JsonRouteParams>;
71
- mintPreviewToken: questpie97.JsonRouteDefinition<{
70
+ }, any, questpie125.JsonRouteParams>;
71
+ mintPreviewToken: questpie125.JsonRouteDefinition<{
72
72
  path: string;
73
73
  ttlMs?: number | undefined;
74
- }, any, questpie97.JsonRouteParams>;
75
- verifyPreviewToken: questpie97.JsonRouteDefinition<{
74
+ }, any, questpie125.JsonRouteParams>;
75
+ verifyPreviewToken: questpie125.JsonRouteDefinition<{
76
76
  token: string;
77
- }, any, questpie97.JsonRouteParams>;
77
+ }, any, questpie125.JsonRouteParams>;
78
78
  };
79
79
  //#endregion
80
80
  export { PreviewTokenPayload, createPreviewFunctions, createPreviewTokenVerifier, previewFunctions, verifyPreviewTokenDirect };
@@ -1,4 +1,5 @@
1
- import { getApp, getCollectionState, getSession } from "./route-helpers.mjs";
1
+ import { getApp, getCollectionState, getLocale, getSession } from "./route-helpers.mjs";
2
+ import { translateAdminMessage } from "./i18n-helpers.mjs";
2
3
  import { getPreviewSecret } from "../../../../shared/preview-utils.mjs";
3
4
  import { z } from "zod";
4
5
  import { ApiError, route } from "questpie";
@@ -23,7 +24,7 @@ function base64UrlDecode(input) {
23
24
  return Buffer.from(base64, "base64").toString("utf8");
24
25
  }
25
26
  const mintPreviewTokenSchema = z.object({
26
- path: z.string().min(1, "Path is required"),
27
+ path: z.string().min(1),
27
28
  ttlMs: z.number().positive().optional()
28
29
  });
29
30
  const mintPreviewTokenOutputSchema = z.object({
@@ -50,7 +51,9 @@ function createPreviewFunctions(secret) {
50
51
  return {
51
52
  mintPreviewToken: route().post().schema(mintPreviewTokenSchema).outputSchema(mintPreviewTokenOutputSchema).handler(async (ctx) => {
52
53
  const { input } = ctx;
53
- if (!getSession(ctx)) throw ApiError.unauthorized("Admin session required");
54
+ const locale = getLocale(ctx);
55
+ const t = (key, params) => translateAdminMessage(locale, key, params);
56
+ if (!getSession(ctx)) throw ApiError.unauthorized(t("preview.adminSessionRequired"));
54
57
  const { path, ttlMs = DEFAULT_TTL_MS } = input;
55
58
  const expiresAt = Date.now() + ttlMs;
56
59
  const payload = {
@@ -63,37 +66,40 @@ function createPreviewFunctions(secret) {
63
66
  expiresAt
64
67
  };
65
68
  }),
66
- verifyPreviewToken: route().post().schema(verifyPreviewTokenSchema).outputSchema(verifyPreviewTokenOutputSchema).handler(async ({ input }) => {
69
+ verifyPreviewToken: route().post().schema(verifyPreviewTokenSchema).outputSchema(verifyPreviewTokenOutputSchema).handler(async (ctx) => {
70
+ const { input } = ctx;
71
+ const locale = getLocale(ctx);
72
+ const t = (key, params) => translateAdminMessage(locale, key, params);
67
73
  const { token } = input;
68
74
  const [encodedPayload, signature] = token.split(".");
69
75
  if (!encodedPayload || !signature) return {
70
76
  valid: false,
71
- error: "Invalid token format"
77
+ error: t("preview.invalidTokenFormat")
72
78
  };
73
79
  const expectedSignature = signPayload(encodedPayload);
74
80
  const signatureBuffer = Uint8Array.from(Buffer.from(signature));
75
81
  const expectedBuffer = Uint8Array.from(Buffer.from(expectedSignature));
76
82
  if (signatureBuffer.length !== expectedBuffer.length) return {
77
83
  valid: false,
78
- error: "Invalid signature"
84
+ error: t("preview.invalidSignature")
79
85
  };
80
86
  if (!timingSafeEqual(signatureBuffer, expectedBuffer)) return {
81
87
  valid: false,
82
- error: "Invalid signature"
88
+ error: t("preview.invalidSignature")
83
89
  };
84
90
  try {
85
91
  const payload = JSON.parse(base64UrlDecode(encodedPayload));
86
92
  if (!payload?.exp || typeof payload.exp !== "number") return {
87
93
  valid: false,
88
- error: "Invalid payload"
94
+ error: t("preview.invalidPayload")
89
95
  };
90
96
  if (payload.exp < Date.now()) return {
91
97
  valid: false,
92
- error: "Token expired"
98
+ error: t("preview.tokenExpired")
93
99
  };
94
100
  if (!payload.path || typeof payload.path !== "string") return {
95
101
  valid: false,
96
- error: "Invalid path"
102
+ error: t("preview.invalidPath")
97
103
  };
98
104
  return {
99
105
  valid: true,
@@ -102,7 +108,7 @@ function createPreviewFunctions(secret) {
102
108
  } catch {
103
109
  return {
104
110
  valid: false,
105
- error: "Invalid payload"
111
+ error: t("preview.invalidPayload")
106
112
  };
107
113
  }
108
114
  })
@@ -160,7 +166,7 @@ function createPreviewTokenVerifier(secret) {
160
166
  };
161
167
  }
162
168
  const getPreviewUrlSchema = z.object({
163
- collection: z.string().min(1, "Collection name is required"),
169
+ collection: z.string().min(1),
164
170
  record: z.record(z.string(), z.unknown()),
165
171
  locale: z.string().optional()
166
172
  });
@@ -186,24 +192,26 @@ const getPreviewUrlOutputSchema = z.object({
186
192
  */
187
193
  const getPreviewUrl = route().post().schema(getPreviewUrlSchema).outputSchema(getPreviewUrlOutputSchema).handler(async (ctx) => {
188
194
  const { input } = ctx;
195
+ const messageLocale = getLocale(ctx) ?? input.locale;
196
+ const t = (key, params) => translateAdminMessage(messageLocale, key, params);
189
197
  if (!getSession(ctx)) return {
190
198
  url: null,
191
- error: "Unauthorized: Admin session required"
199
+ error: t("preview.adminSessionRequired")
192
200
  };
193
201
  const { collection: collectionName, record, locale } = input;
194
202
  const collection$1 = getApp(ctx).getCollections()[collectionName];
195
203
  if (!collection$1) return {
196
204
  url: null,
197
- error: `Collection '${collectionName}' not found`
205
+ error: t("preview.collectionNotFound", { collection: collectionName })
198
206
  };
199
207
  const previewConfig = getCollectionState(collection$1).adminPreview;
200
208
  if (!previewConfig?.url) return {
201
209
  url: null,
202
- error: "No preview URL configured for this collection"
210
+ error: t("preview.noUrlConfigured")
203
211
  };
204
212
  if (previewConfig.enabled === false) return {
205
213
  url: null,
206
- error: "Preview is disabled for this collection"
214
+ error: t("preview.disabledForCollection")
207
215
  };
208
216
  try {
209
217
  return { url: previewConfig.url({
@@ -213,7 +221,7 @@ const getPreviewUrl = route().post().schema(getPreviewUrlSchema).outputSchema(ge
213
221
  } catch (err) {
214
222
  return {
215
223
  url: null,
216
- error: `Failed to generate preview URL: ${err instanceof Error ? err.message : "Unknown error"}`
224
+ error: t("preview.generateUrlFailed", { message: err instanceof Error ? err.message : t("error.unknown") })
217
225
  };
218
226
  }
219
227
  });
@@ -73,4 +73,4 @@ function buildServerContext(ctx) {
73
73
  }
74
74
 
75
75
  //#endregion
76
- export { buildServerContext, getAccessContext, getAdminConfig, getApp, getAppState, getCollectionState, getGlobalState, getSession };
76
+ export { buildServerContext, getAccessContext, getAdminConfig, getApp, getAppState, getCollectionState, getGlobalState, getLocale, getSession };
@@ -1,4 +1,5 @@
1
- import { getApp } from "./route-helpers.mjs";
1
+ import { getApp, getLocale } from "./route-helpers.mjs";
2
+ import { translateAdminMessage } from "./i18n-helpers.mjs";
2
3
  import { z } from "zod";
3
4
  import { route } from "questpie";
4
5
  import { eq, sql as sql$1 } from "questpie/drizzle";
@@ -14,9 +15,9 @@ import { eq, sql as sql$1 } from "questpie/drizzle";
14
15
  const isSetupRequiredSchema = z.object({});
15
16
  const isSetupRequiredOutputSchema = z.object({ required: z.boolean() });
16
17
  const createFirstAdminSchema = z.object({
17
- email: z.string().email("Invalid email address"),
18
- password: z.string().min(8, "Password must be at least 8 characters"),
19
- name: z.string().min(2, "Name must be at least 2 characters")
18
+ email: z.string().email(),
19
+ password: z.string().min(8),
20
+ name: z.string().min(2)
20
21
  });
21
22
  const createFirstAdminOutputSchema = z.object({
22
23
  success: z.boolean(),
@@ -67,11 +68,13 @@ const isSetupRequired = route().post().schema(isSetupRequiredSchema).outputSchem
67
68
  */
68
69
  const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSchema(createFirstAdminOutputSchema).handler(async (ctx) => {
69
70
  const app = getApp(ctx);
71
+ const locale = getLocale(ctx);
72
+ const t = (key, params) => translateAdminMessage(locale, key, params);
70
73
  const input = ctx.input;
71
74
  const userCollection = app.getCollectionConfig("user");
72
75
  if ((await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userCollection.table.role, "admin")))[0].count > 0) return {
73
76
  success: false,
74
- error: "Setup already completed - admin users exist in the system"
77
+ error: t("auth.setupAlreadyCompleted")
75
78
  };
76
79
  try {
77
80
  const signUpResult = await app.auth.api.signUpEmail({ body: {
@@ -81,7 +84,7 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
81
84
  } });
82
85
  if (!signUpResult.user) return {
83
86
  success: false,
84
- error: "Failed to create user account"
87
+ error: t("auth.failedToCreateUserAccount")
85
88
  };
86
89
  await app.db.update(userCollection.table).set({
87
90
  role: "admin",
@@ -98,7 +101,7 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
98
101
  } catch (error) {
99
102
  return {
100
103
  success: false,
101
- error: error instanceof Error ? error.message : "An unexpected error occurred"
104
+ error: error instanceof Error ? error.message : t("error.unexpectedError")
102
105
  };
103
106
  }
104
107
  });
@@ -1,4 +1,4 @@
1
- import * as questpie93 from "questpie";
1
+ import * as questpie69 from "questpie";
2
2
 
3
3
  //#region src/server/modules/admin/routes/widget-data.d.ts
4
4
 
@@ -20,13 +20,13 @@ import * as questpie93 from "questpie";
20
20
  * const data = await client.routes.fetchWidgetData({ widgetId: "my-widget" });
21
21
  * ```
22
22
  */
23
- declare const fetchWidgetData: questpie93.JsonRouteDefinition<{
23
+ declare const fetchWidgetData: questpie69.JsonRouteDefinition<{
24
24
  widgetId: string;
25
- }, any, questpie93.JsonRouteParams>;
25
+ }, any, questpie69.JsonRouteParams>;
26
26
  declare const widgetDataFunctions: {
27
- readonly fetchWidgetData: questpie93.JsonRouteDefinition<{
27
+ readonly fetchWidgetData: questpie69.JsonRouteDefinition<{
28
28
  widgetId: string;
29
- }, any, questpie93.JsonRouteParams>;
29
+ }, any, questpie69.JsonRouteParams>;
30
30
  };
31
31
  //#endregion
32
32
  export { fetchWidgetData, widgetDataFunctions };
@@ -1,8 +1,8 @@
1
1
  import { FilterOperator, FilterRule, SortConfig, ViewConfiguration } from "../../../../shared/types/saved-views.types.mjs";
2
2
  import * as questpie_shared15 from "questpie/shared";
3
- import * as questpie107 from "questpie";
4
- import * as questpie_src_server_modules_core_fields_email_js2 from "questpie/src/server/modules/core/fields/email.js";
5
- import * as questpie_src_server_modules_core_fields_json_js2 from "questpie/src/server/modules/core/fields/json.js";
3
+ import * as questpie73 from "questpie";
4
+ import * as questpie_src_server_modules_core_fields_email_js1 from "questpie/src/server/modules/core/fields/email.js";
5
+ import * as questpie_src_server_modules_core_fields_json_js1 from "questpie/src/server/modules/core/fields/json.js";
6
6
  import * as drizzle_orm_pg_core22 from "drizzle-orm/pg-core";
7
7
  import * as drizzle_orm5 from "drizzle-orm";
8
8
 
@@ -33,22 +33,22 @@ import * as drizzle_orm5 from "drizzle-orm";
33
33
  * });
34
34
  * ```
35
35
  */
36
- declare const savedViewsCollection: questpie107.CollectionBuilder<questpie_shared15.Override<questpie_shared15.Override<questpie107.EmptyCollectionState<"admin_saved_views", undefined, {
37
- readonly text: typeof questpie107.text;
38
- readonly textarea: typeof questpie107.textarea;
39
- readonly email: typeof questpie_src_server_modules_core_fields_email_js2.email;
40
- readonly url: typeof questpie107.url;
41
- readonly number: typeof questpie107.number;
42
- readonly boolean: typeof questpie107.boolean;
43
- readonly date: typeof questpie107.date;
44
- readonly datetime: typeof questpie107.datetime;
45
- readonly time: typeof questpie107.time;
46
- readonly select: typeof questpie107.select;
47
- readonly upload: typeof questpie107.upload;
48
- readonly relation: typeof questpie107.relation;
49
- readonly object: typeof questpie107.object;
50
- readonly json: typeof questpie_src_server_modules_core_fields_json_js2.json;
51
- readonly from: typeof questpie107.from;
36
+ declare const savedViewsCollection: questpie73.CollectionBuilder<questpie_shared15.Override<questpie_shared15.Override<questpie73.EmptyCollectionState<"admin_saved_views", undefined, {
37
+ readonly text: typeof questpie73.text;
38
+ readonly textarea: typeof questpie73.textarea;
39
+ readonly email: typeof questpie_src_server_modules_core_fields_email_js1.email;
40
+ readonly url: typeof questpie73.url;
41
+ readonly number: typeof questpie73.number;
42
+ readonly boolean: typeof questpie73.boolean;
43
+ readonly date: typeof questpie73.date;
44
+ readonly datetime: typeof questpie73.datetime;
45
+ readonly time: typeof questpie73.time;
46
+ readonly select: typeof questpie73.select;
47
+ readonly upload: typeof questpie73.upload;
48
+ readonly relation: typeof questpie73.relation;
49
+ readonly object: typeof questpie73.object;
50
+ readonly json: typeof questpie_src_server_modules_core_fields_json_js1.json;
51
+ readonly from: typeof questpie73.from;
52
52
  }>, {
53
53
  fields: {
54
54
  readonly userId: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
@@ -59,31 +59,31 @@ declare const savedViewsCollection: questpie107.CollectionBuilder<questpie_share
59
59
  };
60
60
  localized: readonly string[];
61
61
  fieldDefinitions: {
62
- readonly userId: questpie107.FieldWithMethods<Omit<questpie107.TextFieldState, "notNull" | "column"> & {
62
+ readonly userId: questpie73.FieldWithMethods<Omit<questpie73.TextFieldState, "notNull" | "column"> & {
63
63
  notNull: true;
64
64
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
65
65
  } & {
66
66
  label: questpie_shared15.I18nText;
67
- }, questpie107.TextFieldMethods>;
68
- readonly collectionName: questpie107.FieldWithMethods<Omit<questpie107.TextFieldState, "notNull" | "column"> & {
67
+ }, questpie73.TextFieldMethods>;
68
+ readonly collectionName: questpie73.FieldWithMethods<Omit<questpie73.TextFieldState, "notNull" | "column"> & {
69
69
  notNull: true;
70
70
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
71
71
  } & {
72
72
  label: questpie_shared15.I18nText;
73
- }, questpie107.TextFieldMethods>;
74
- readonly name: questpie107.FieldWithMethods<Omit<questpie107.TextFieldState, "notNull" | "column"> & {
73
+ }, questpie73.TextFieldMethods>;
74
+ readonly name: questpie73.FieldWithMethods<Omit<questpie73.TextFieldState, "notNull" | "column"> & {
75
75
  notNull: true;
76
76
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
77
77
  } & {
78
78
  label: questpie_shared15.I18nText;
79
- }, questpie107.TextFieldMethods>;
80
- readonly configuration: questpie107.Field<Omit<questpie107.JsonFieldState, "notNull" | "column"> & {
79
+ }, questpie73.TextFieldMethods>;
80
+ readonly configuration: questpie73.Field<Omit<questpie73.JsonFieldState, "notNull" | "column"> & {
81
81
  notNull: true;
82
82
  column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgJsonbBuilder>;
83
83
  } & {
84
84
  label: questpie_shared15.I18nText;
85
85
  }>;
86
- readonly isDefault: questpie107.Field<Omit<questpie107.BooleanFieldState, "column" | "hasDefault"> & {
86
+ readonly isDefault: questpie73.Field<Omit<questpie73.BooleanFieldState, "column" | "hasDefault"> & {
87
87
  hasDefault: true;
88
88
  column: drizzle_orm5.HasDefault<drizzle_orm_pg_core22.PgBooleanBuilder>;
89
89
  } & {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@questpie/admin",
3
- "version": "3.0.4",
3
+ "version": "3.0.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/questpie/questpie.git",
@@ -62,7 +62,7 @@
62
62
  "@fontsource-variable/jetbrains-mono": "^5.2.8",
63
63
  "@hookform/resolvers": "^5.1.0",
64
64
  "@iconify/react": "^6.0.2",
65
- "@questpie/tanstack-query": "^3.0.4",
65
+ "@questpie/tanstack-query": "^3.0.5",
66
66
  "@tailwindcss/vite": "^4.0.6",
67
67
  "@tiptap/core": "^2.x",
68
68
  "@tiptap/extension-character-count": "^2.x",
@@ -88,7 +88,7 @@
88
88
  "date-fns": "^4.1.0",
89
89
  "lowlight": "^3.x",
90
90
  "next-themes": "^0.4.6",
91
- "questpie": "^3.0.4",
91
+ "questpie": "^3.0.5",
92
92
  "react-day-picker": "^9.12.0",
93
93
  "react-hook-form": "^7.54.0",
94
94
  "react-resizable-panels": "^4.4.2",