includio-cms 0.5.3 → 0.5.7

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 (109) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/ROADMAP.md +33 -0
  3. package/dist/admin/api/rest/handler.d.ts +7 -0
  4. package/dist/admin/api/rest/handler.js +116 -0
  5. package/dist/admin/api/rest/middleware/apiKey.d.ts +6 -0
  6. package/dist/admin/api/rest/middleware/apiKey.js +45 -0
  7. package/dist/admin/api/rest/routes/collections.d.ts +5 -0
  8. package/dist/admin/api/rest/routes/collections.js +104 -0
  9. package/dist/admin/api/rest/routes/entries.d.ts +2 -0
  10. package/dist/admin/api/rest/routes/entries.js +37 -0
  11. package/dist/admin/api/rest/routes/languages.d.ts +1 -0
  12. package/dist/admin/api/rest/routes/languages.js +5 -0
  13. package/dist/admin/api/rest/routes/schema.d.ts +2 -0
  14. package/dist/admin/api/rest/routes/schema.js +78 -0
  15. package/dist/admin/api/rest/routes/singletons.d.ts +3 -0
  16. package/dist/admin/api/rest/routes/singletons.js +60 -0
  17. package/dist/admin/auth-client.d.ts +48 -48
  18. package/dist/admin/client/admin/admin-after-login-layout.svelte +5 -3
  19. package/dist/admin/client/admin/admin-layout.svelte +0 -2
  20. package/dist/admin/client/collection/bulk-actions-bar.svelte +3 -1
  21. package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +1 -1
  22. package/dist/admin/client/collection/collection-entries.svelte +334 -166
  23. package/dist/admin/client/collection/collection-page.svelte +6 -3
  24. package/dist/admin/client/collection/data-table.svelte +127 -18
  25. package/dist/admin/client/collection/data-table.svelte.d.ts +2 -0
  26. package/dist/admin/client/collection/row-actions.svelte +3 -1
  27. package/dist/admin/client/collection/row-actions.svelte.d.ts +1 -1
  28. package/dist/admin/client/entry/entry-page.svelte +5 -4
  29. package/dist/admin/client/form/form-config-wrapper.svelte +5 -3
  30. package/dist/admin/client/form/form-page.svelte +5 -5
  31. package/dist/admin/client/form/form-submission/form-submission-page.svelte +30 -32
  32. package/dist/admin/components/accessibility/accessibility-overview.svelte +7 -5
  33. package/dist/admin/components/accessibility/accessibility-overview.svelte.d.ts +2 -17
  34. package/dist/admin/components/dashboard/a11y-gauge.svelte +7 -4
  35. package/dist/admin/components/dashboard/a11y-gauge.svelte.d.ts +2 -17
  36. package/dist/admin/components/dashboard/accessibility-hub.svelte +7 -4
  37. package/dist/admin/components/dashboard/accessibility-hub.svelte.d.ts +2 -17
  38. package/dist/admin/components/dashboard/form-submissions-widget.svelte +8 -3
  39. package/dist/admin/components/dashboard/form-submissions-widget.svelte.d.ts +2 -17
  40. package/dist/admin/components/dashboard/recent-activity.svelte +8 -3
  41. package/dist/admin/components/dashboard/recent-activity.svelte.d.ts +2 -17
  42. package/dist/admin/components/dashboard/recent-entries.svelte +8 -3
  43. package/dist/admin/components/dashboard/recent-entries.svelte.d.ts +2 -17
  44. package/dist/admin/components/fields/image-field.svelte +21 -20
  45. package/dist/admin/components/fields/media-field.svelte +45 -44
  46. package/dist/admin/components/fields/relation-field.svelte +428 -149
  47. package/dist/admin/components/fields/relation-picker-dialog.svelte +112 -0
  48. package/dist/admin/components/fields/relation-picker-dialog.svelte.d.ts +22 -0
  49. package/dist/admin/components/fields/seo-field.svelte +66 -10
  50. package/dist/admin/components/icons/icon-map.d.ts +4 -0
  51. package/dist/admin/components/icons/icon-map.js +143 -0
  52. package/dist/admin/components/layout/header-actions.svelte +0 -10
  53. package/dist/admin/components/layout/nav-collections.svelte +55 -57
  54. package/dist/admin/components/layout/nav-collections.svelte.d.ts +2 -17
  55. package/dist/admin/components/layout/nav-forms.svelte +49 -49
  56. package/dist/admin/components/layout/nav-forms.svelte.d.ts +2 -17
  57. package/dist/admin/components/layout/nav-search.svelte +15 -22
  58. package/dist/admin/components/layout/nav-singletons.svelte +35 -38
  59. package/dist/admin/components/layout/nav-singletons.svelte.d.ts +2 -17
  60. package/dist/admin/components/media/file/file-preview.svelte +92 -91
  61. package/dist/admin/components/media/file-preview.svelte +17 -18
  62. package/dist/admin/components/media/media-library.svelte +39 -41
  63. package/dist/admin/components/media/media-selector.svelte +8 -8
  64. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +6 -8
  65. package/dist/admin/components/tiptap/content-editor.svelte +2 -2
  66. package/dist/admin/components/tiptap/editor-toolbar.svelte +1 -1
  67. package/dist/admin/components/tiptap/inline-block-node.d.ts +1 -0
  68. package/dist/admin/components/tiptap/inline-block-node.js +12 -1
  69. package/dist/admin/components/tiptap/tiptap-editor.svelte +1 -1
  70. package/dist/admin/remote/entry.remote.d.ts +20 -1
  71. package/dist/admin/remote/entry.remote.js +24 -3
  72. package/dist/admin/remote/invite.d.ts +2 -2
  73. package/dist/cli/scaffold/admin.js +8 -0
  74. package/dist/cms/runtime/remote.d.ts +8 -0
  75. package/dist/cms/runtime/remote.js +7 -0
  76. package/dist/cms/runtime/types.d.ts +1 -1
  77. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  78. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
  79. package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
  80. package/dist/components/ui/sonner/sonner.svelte +1 -2
  81. package/dist/core/cms.d.ts +2 -1
  82. package/dist/core/cms.js +2 -0
  83. package/dist/core/server/entries/operations/get.d.ts +11 -0
  84. package/dist/core/server/entries/operations/get.js +63 -1
  85. package/dist/core/server/entries/operations/update.d.ts +1 -0
  86. package/dist/core/server/entries/operations/update.js +5 -1
  87. package/dist/core/server/fields/resolveUrlFields.js +49 -18
  88. package/dist/db-postgres/schema/entry.d.ts +17 -0
  89. package/dist/db-postgres/schema/entry.js +4 -2
  90. package/dist/server/auth.d.ts +4 -4
  91. package/dist/sveltekit/components/structured-content.svelte +18 -5
  92. package/dist/sveltekit/server/handle.js +1 -0
  93. package/dist/types/cms.d.ts +7 -0
  94. package/dist/types/collections.d.ts +2 -0
  95. package/dist/types/config.d.ts +2 -2
  96. package/dist/types/entries.d.ts +7 -1
  97. package/dist/types/forms.d.ts +2 -0
  98. package/dist/types/index.d.ts +2 -1
  99. package/dist/types/index.js +1 -0
  100. package/dist/updates/0.5.4/index.d.ts +2 -0
  101. package/dist/updates/0.5.4/index.js +15 -0
  102. package/dist/updates/0.5.5/index.d.ts +2 -0
  103. package/dist/updates/0.5.5/index.js +20 -0
  104. package/dist/updates/0.5.6/index.d.ts +2 -0
  105. package/dist/updates/0.5.6/index.js +17 -0
  106. package/dist/updates/0.5.7/index.d.ts +2 -0
  107. package/dist/updates/0.5.7/index.js +14 -0
  108. package/dist/updates/index.js +5 -1
  109. package/package.json +6 -2
@@ -149,8 +149,10 @@
149
149
  async function onTagUpdate(tagIds: string[]) {
150
150
  if (currentFile) {
151
151
  await remotes.setMediaFileTags({ fileId: currentFile.id, tagIds });
152
- const tags = (await tagsQuery) as MediaTag[];
153
- currentFile = { ...currentFile, tags: tags.filter((t) => tagIds.includes(t.id)) };
152
+ const tags = tagsQuery.current as MediaTag[] | undefined;
153
+ if (tags) {
154
+ currentFile = { ...currentFile, tags: tags.filter((t) => tagIds.includes(t.id)) };
155
+ }
154
156
  await filesQuery.refresh();
155
157
  await allFilesQuery.refresh();
156
158
  }
@@ -216,17 +218,17 @@
216
218
  <div class="flex h-full overflow-hidden" bind:this={dropZoneRef}>
217
219
  <!-- Tag sidebar (192px) -->
218
220
  <aside class="w-48 min-w-48 shrink-0 border-r bg-card flex flex-col overflow-hidden" aria-label="Filtry tagów">
219
- {#await Promise.all([tagsQuery, allFilesQuery]) then [tags, allFiles]}
221
+ {#if tagsQuery.ready && allFilesQuery.ready && tagsQuery.current && allFilesQuery.current}
220
222
  <TagSidebar
221
- {tags}
222
- files={allFiles}
223
+ tags={tagsQuery.current}
224
+ files={allFilesQuery.current}
223
225
  activeFilter={activeTagFilter}
224
226
  onFilterChange={(f) => (activeTagFilter = f)}
225
227
  onCreateTag={handleCreateTag}
226
228
  onUpdateTag={handleUpdateTag}
227
229
  onDeleteTag={handleDeleteTag}
228
230
  />
229
- {/await}
231
+ {/if}
230
232
  </aside>
231
233
 
232
234
  <!-- Main content (flex-1) -->
@@ -259,20 +261,20 @@
259
261
 
260
262
  <!-- File grid -->
261
263
  <div class="flex-1 overflow-y-auto px-5 pt-4 pb-24 scrollbar-thin" role="listbox" aria-multiselectable="true" aria-label="Siatka plików">
262
- <div class="grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-3">
263
- {#await filesQuery}
264
+ <div class="grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-3" class:opacity-60={filesQuery.loading} style="transition: opacity 200ms">
265
+ {#if !filesQuery.ready}
264
266
  {#each Array(8) as _}
265
267
  <Skeleton class="block h-[168px] rounded-xl" />
266
268
  {/each}
267
- {:then files}
269
+ {:else if filesQuery.current}
268
270
  <FilesList
269
- {files}
271
+ files={filesQuery.current}
270
272
  selected={selectedFileIds.length > 0 ? selectedFileIds : (selected ?? '')}
271
273
  onSelect={handleFileSelect}
272
274
  onRangeSelect={handleRangeSelect}
273
275
  {selectionMode}
274
276
  />
275
- {/await}
277
+ {/if}
276
278
  </div>
277
279
  </div>
278
280
  </section>
@@ -280,22 +282,20 @@
280
282
  <!-- Detail panel (384px) -->
281
283
  <aside class="w-96 min-w-96 shrink-0 border-l bg-card flex flex-col overflow-hidden" aria-label="Szczegóły pliku">
282
284
  {#if selectionMode}
283
- {#await filesQuery then allFiles}
284
- {#await tagsQuery then tags}
285
- <MultiFileSummary
286
- files={allFiles.filter((f) => selectedFileIds.includes(f.id))}
287
- allTags={tags}
288
- onBulkTag={handleBulkTag}
289
- onBulkDelete={handleBulkDelete}
290
- />
291
- {/await}
292
- {/await}
285
+ {#if filesQuery.ready && filesQuery.current && tagsQuery.ready && tagsQuery.current}
286
+ <MultiFileSummary
287
+ files={filesQuery.current.filter((f) => selectedFileIds.includes(f.id))}
288
+ allTags={tagsQuery.current}
289
+ onBulkTag={handleBulkTag}
290
+ onBulkDelete={handleBulkDelete}
291
+ />
292
+ {/if}
293
293
  {:else if currentFile}
294
294
  {#key currentFile}
295
- {#await tagsQuery then tags}
295
+ {#if tagsQuery.ready && tagsQuery.current}
296
296
  <FileDetails
297
297
  file={currentFile}
298
- allTags={tags}
298
+ allTags={tagsQuery.current}
299
299
  onDelete={deleteFileCommand}
300
300
  onReplace={(updated) => {
301
301
  currentFile = updated;
@@ -305,7 +305,7 @@
305
305
  {onTagUpdate}
306
306
  {onNameUpdate}
307
307
  />
308
- {/await}
308
+ {/if}
309
309
  {/key}
310
310
  {:else}
311
311
  <!-- Empty state -->
@@ -323,21 +323,19 @@
323
323
  </div>
324
324
 
325
325
  <!-- Bulk action bar -->
326
- {#if selectedFileIds.length > 0}
327
- {#await Promise.all([tagsQuery, allFilesQuery]) then [tags, allFiles]}
328
- <BulkActionBar
329
- selectedCount={selectedFileIds.length}
330
- totalCount={allFiles.length}
331
- {tags}
332
- onBulkTag={handleBulkTag}
333
- onBulkDelete={handleBulkDelete}
334
- onClear={exitSelectionMode}
335
- onSelectAll={() => { selectedFileIds = allFiles.map((f) => f.id); }}
336
- onCreateTag={handleCreateTag}
337
- onUpdateTagColor={async (id, color) => {
338
- const tag = tags.find((t) => t.id === id);
339
- if (tag) await handleUpdateTag(id, tag.name, color);
340
- }}
341
- />
342
- {/await}
326
+ {#if selectedFileIds.length > 0 && tagsQuery.ready && tagsQuery.current && allFilesQuery.ready && allFilesQuery.current}
327
+ <BulkActionBar
328
+ selectedCount={selectedFileIds.length}
329
+ totalCount={allFilesQuery.current.length}
330
+ tags={tagsQuery.current}
331
+ onBulkTag={handleBulkTag}
332
+ onBulkDelete={handleBulkDelete}
333
+ onClear={exitSelectionMode}
334
+ onSelectAll={() => { selectedFileIds = allFilesQuery.current!.map((f) => f.id); }}
335
+ onCreateTag={handleCreateTag}
336
+ onUpdateTagColor={async (id, color) => {
337
+ const tag = tagsQuery.current?.find((t) => t.id === id);
338
+ if (tag) await handleUpdateTag(id, tag.name, color);
339
+ }}
340
+ />
343
341
  {/if}
@@ -112,9 +112,9 @@
112
112
  <div class="flex min-h-0 flex-1 overflow-hidden">
113
113
  <!-- Tag sidebar -->
114
114
  <div class="w-44 shrink-0 border-r p-3 overflow-y-auto">
115
- {#await tagsQuery then tags}
115
+ {#if tagsQuery.ready && tagsQuery.current}
116
116
  <TagSidebar
117
- {tags}
117
+ tags={tagsQuery.current}
118
118
  files={[]}
119
119
  activeFilter={activeTagFilter}
120
120
  onFilterChange={(f) => (activeTagFilter = f)}
@@ -131,7 +131,7 @@
131
131
  await tagsQuery.refresh();
132
132
  }}
133
133
  />
134
- {/await}
134
+ {/if}
135
135
  </div>
136
136
 
137
137
  <!-- Main content -->
@@ -149,14 +149,14 @@
149
149
 
150
150
  <!-- Grid z plikami -->
151
151
  <div class="flex-1 overflow-y-auto pr-2">
152
- <div class="grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-3 p-4">
153
- {#await filesQuery}
152
+ <div class="grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-3 p-4" class:opacity-60={filesQuery.loading} style="transition: opacity 200ms">
153
+ {#if !filesQuery.ready}
154
154
  {#each Array(8) as _}
155
155
  <Skeleton class="block aspect-square rounded-2xl" />
156
156
  {/each}
157
- {:then files}
157
+ {:else if filesQuery.current}
158
158
  <FilesList
159
- {files}
159
+ files={filesQuery.current}
160
160
  selected={stagedSelection}
161
161
  onSelect={(file) => {
162
162
  currentFile = file;
@@ -169,7 +169,7 @@
169
169
  }
170
170
  }}
171
171
  />
172
- {/await}
172
+ {/if}
173
173
  </div>
174
174
  </div>
175
175
  </div>
@@ -14,10 +14,11 @@
14
14
  import { onMount, onDestroy } from 'svelte';
15
15
  import type { FormPathLeaves } from 'sveltekit-superforms';
16
16
  import { evaluateCondition } from '../../utils/fieldCondition.js';
17
+ import { isRecentlyInserted } from './inline-block-node.js';
17
18
 
18
- let { node, updateAttributes, editor, getPos, deleteNode, selected }: NodeViewProps = $props();
19
+ let { node, updateAttributes, editor, getPos, deleteNode }: NodeViewProps = $props();
19
20
 
20
- let collapsed = $state(false);
21
+ let collapsed = $state(!isRecentlyInserted(node.attrs.blockId));
21
22
 
22
23
  const contentLanguage = getContentLanguage();
23
24
 
@@ -243,7 +244,7 @@
243
244
  }
244
245
  </script>
245
246
 
246
- <NodeViewWrapper class="inline-block-node {selected ? 'selected' : ''}" data-drag-handle>
247
+ <NodeViewWrapper class="inline-block-node" data-drag-handle>
247
248
  <div class="inline-block-wrapper">
248
249
  <!-- Header -->
249
250
  <div class="inline-block-header">
@@ -308,17 +309,13 @@
308
309
  border-radius: 12px;
309
310
  overflow: hidden;
310
311
  margin: 0.5rem 0;
311
- background: var(--surface, var(--background));
312
+ background: var(--surface, var(--muted));
312
313
  }
313
314
 
314
315
  .inline-block-wrapper:hover {
315
316
  border-color: color-mix(in oklch, var(--primary) 50%, transparent);
316
317
  }
317
318
 
318
- :global(.selected) .inline-block-wrapper {
319
- border-color: color-mix(in oklch, var(--primary) 50%, transparent);
320
- }
321
-
322
319
  .inline-block-header {
323
320
  display: flex;
324
321
  align-items: center;
@@ -441,4 +438,5 @@
441
438
  font-size: 0.8125rem;
442
439
  font-style: italic;
443
440
  }
441
+
444
442
  </style>
@@ -126,7 +126,7 @@
126
126
  </script>
127
127
 
128
128
  <Tooltip.Provider>
129
- <div class="border-input bg-background rounded-lg border">
129
+ <div class="border-input bg-card rounded-lg border">
130
130
  <!-- Toolbar — no code view for content editor -->
131
131
  {#if ed}
132
132
  <EditorToolbar
@@ -267,7 +267,7 @@
267
267
  <!-- Editor content -->
268
268
  <div
269
269
  bind:this={element}
270
- class="prose prose-sm dark:prose-invert max-w-none p-4 focus:outline-none [&_.ProseMirror]:min-h-[200px] [&_.ProseMirror]:outline-none [&_.ProseMirror]:scroll-padding-top-12"
270
+ class="prose prose-sm dark:prose-invert max-w-none p-4 focus:outline-none overflow-y-auto max-h-[70vh] [&_.ProseMirror]:min-h-[200px] [&_.ProseMirror]:outline-none [&_.ProseMirror]:scroll-padding-top-12"
271
271
  ></div>
272
272
  </div>
273
273
  </Tooltip.Provider>
@@ -57,7 +57,7 @@
57
57
  </script>
58
58
 
59
59
  <Tooltip.Provider>
60
- <div class="flex flex-wrap items-center gap-0.5 border-b p-1 sticky top-0 z-10 bg-background">
60
+ <div class="flex flex-wrap items-center gap-0.5 border-b p-1 sticky top-0 z-10 bg-muted/50">
61
61
  <!-- Headings -->
62
62
  <ToolbarButton
63
63
  label="Nagłówek 2"
@@ -1,5 +1,6 @@
1
1
  import { Node } from '@tiptap/core';
2
2
  import type { ObjectField } from '../../../types/fields.js';
3
+ export declare function isRecentlyInserted(id: string): boolean;
3
4
  export interface InlineBlockOptions {
4
5
  HTMLAttributes: Record<string, string>;
5
6
  inlineBlocks: ObjectField[];
@@ -1,6 +1,14 @@
1
1
  import { Node, mergeAttributes } from '@tiptap/core';
2
2
  import { SvelteNodeViewRenderer } from 'svelte-tiptap';
3
3
  import InlineBlockNodeView from './InlineBlockNodeView.svelte';
4
+ const recentlyInsertedIds = new Set();
5
+ export function isRecentlyInserted(id) {
6
+ if (recentlyInsertedIds.has(id)) {
7
+ recentlyInsertedIds.delete(id);
8
+ return true;
9
+ }
10
+ return false;
11
+ }
4
12
  /** Build default data for an inline block from its field definitions. */
5
13
  export function buildDefaultData(blockType, inlineBlocks) {
6
14
  const def = inlineBlocks.find((b) => b.slug === blockType);
@@ -50,6 +58,7 @@ export const InlineBlockNode = Node.create({
50
58
  name: 'inlineBlock',
51
59
  group: 'block',
52
60
  atom: true,
61
+ selectable: false,
53
62
  draggable: true,
54
63
  isolating: true,
55
64
  addOptions() {
@@ -96,12 +105,14 @@ export const InlineBlockNode = Node.create({
96
105
  return {
97
106
  insertInlineBlock: ({ blockType, blockData }) => ({ commands }) => {
98
107
  const data = blockData ?? buildDefaultData(blockType, this.options.inlineBlocks);
108
+ const blockId = crypto.randomUUID();
109
+ recentlyInsertedIds.add(blockId);
99
110
  return commands.insertContent({
100
111
  type: this.name,
101
112
  attrs: {
102
113
  blockType,
103
114
  blockData: JSON.stringify(data),
104
- blockId: crypto.randomUUID()
115
+ blockId
105
116
  }
106
117
  });
107
118
  }
@@ -231,7 +231,7 @@
231
231
  <!-- Editor content -->
232
232
  <div
233
233
  bind:this={element}
234
- class="prose prose-sm dark:prose-invert max-w-none p-4 focus:outline-none [&_.ProseMirror]:min-h-[200px] [&_.ProseMirror]:outline-none {isCodeViewActive && 'hidden'}"
234
+ class="prose prose-sm dark:prose-invert max-w-none p-4 focus:outline-none overflow-y-auto max-h-[70vh] [&_.ProseMirror]:min-h-[200px] [&_.ProseMirror]:outline-none {isCodeViewActive && 'hidden'}"
235
235
  ></div>
236
236
 
237
237
  <!-- HTML textarea -->
@@ -6,7 +6,7 @@ export declare const getRawEntries: import("@sveltejs/kit").RemoteQueryFunction<
6
6
  limit?: number | undefined;
7
7
  offset?: number | undefined;
8
8
  orderBy?: {
9
- column: "createdAt" | "updatedAt";
9
+ column: "createdAt" | "updatedAt" | "sortOrder";
10
10
  direction: "asc" | "desc";
11
11
  } | undefined;
12
12
  }, {
@@ -20,7 +20,22 @@ export declare const getEntries: import("@sveltejs/kit").RemoteQueryFunction<{
20
20
  language?: string | undefined;
21
21
  status?: "draft" | "published" | "scheduled" | "archived" | undefined;
22
22
  slug?: string | undefined;
23
+ orderBy?: {
24
+ column: "createdAt" | "updatedAt" | "sortOrder";
25
+ direction: "asc" | "desc";
26
+ } | undefined;
23
27
  }, import("../../types/entries.js").Entry[]>;
28
+ export declare const getEntryLabels: import("@sveltejs/kit").RemoteQueryFunction<{
29
+ slug: string;
30
+ ids?: string[] | undefined;
31
+ search?: string | undefined;
32
+ limit?: number | undefined;
33
+ status?: "draft" | "published" | "all" | undefined;
34
+ }, {
35
+ id: string;
36
+ label: string;
37
+ total: number;
38
+ }[]>;
24
39
  export declare const getEntry: import("@sveltejs/kit").RemoteQueryFunction<{
25
40
  id?: string | undefined;
26
41
  dataValues?: Record<string, unknown> | undefined;
@@ -53,11 +68,15 @@ export declare const updateEntryCommand: import("@sveltejs/kit").RemoteCommand<{
53
68
  publishedAt?: Date | null | undefined;
54
69
  publishedVersionId?: string | null | undefined;
55
70
  publishedBy?: string | null | undefined;
71
+ sortOrder?: number | null | undefined;
56
72
  };
57
73
  }, Promise<import("../../types/entries.js").DbEntry>>;
58
74
  export declare const archiveEntryCommand: import("@sveltejs/kit").RemoteCommand<string, Promise<import("../../types/entries.js").DbEntry>>;
59
75
  export declare const unarchiveEntryCommand: import("@sveltejs/kit").RemoteCommand<string, Promise<import("../../types/entries.js").DbEntry>>;
60
76
  export declare const deleteEntryCommand: import("@sveltejs/kit").RemoteCommand<string, Promise<void>>;
77
+ export declare const reorderEntriesCommand: import("@sveltejs/kit").RemoteCommand<{
78
+ orderedIds: string[];
79
+ }, Promise<void>>;
61
80
  export declare const getEntryVersion: import("@sveltejs/kit").RemoteQueryFunction<{
62
81
  id: string;
63
82
  language: string;
@@ -1,6 +1,6 @@
1
1
  import { command, query } from '$app/server';
2
2
  import { createEntry as createEntryOperation, createEntrySchema, createEntryVersion } from '../../core/server/entries/operations/create.js';
3
- import { getRawEntries as getRawEntriesOperation, countRawEntries as countRawEntriesOperation, getRawEntry as getRawEntryOperation, getRawEntryOrThrow, getDbEntry, getDbEntryOrThrow, getEntries as getEntriesOperation, getEntry as getEntryOperation, getEntryVersion as getEntryVersionOperation } from '../../core/server/entries/operations/get.js';
3
+ import { getRawEntries as getRawEntriesOperation, countRawEntries as countRawEntriesOperation, getRawEntry as getRawEntryOperation, getRawEntryOrThrow, getDbEntry, getDbEntryOrThrow, getEntries as getEntriesOperation, getEntry as getEntryOperation, getEntryVersion as getEntryVersionOperation, getEntryLabels as getEntryLabelsOperation } from '../../core/server/entries/operations/get.js';
4
4
  import { getCMS } from '../../core/cms.js';
5
5
  import { pruneOldDraftVersions, unpublishEntry, upsertDraftVersion, updateEntry, updateEntrySchema, updateEntryVersionCommandTypes } from '../../core/server/entries/operations/update.js';
6
6
  import z from 'zod';
@@ -14,7 +14,7 @@ export const getRawEntries = query(z.object({
14
14
  offset: z.number().int().nonnegative().optional(),
15
15
  orderBy: z
16
16
  .object({
17
- column: z.enum(['createdAt', 'updatedAt']),
17
+ column: z.enum(['createdAt', 'updatedAt', 'sortOrder']),
18
18
  direction: z.enum(['asc', 'desc'])
19
19
  })
20
20
  .optional()
@@ -32,10 +32,25 @@ export const getEntries = query(z.object({
32
32
  dataLike: z.record(z.string(), z.unknown()).optional(),
33
33
  language: z.string().optional(),
34
34
  status: z.enum(entryStatuses).optional(),
35
- slug: z.string().optional()
35
+ slug: z.string().optional(),
36
+ orderBy: z
37
+ .object({
38
+ column: z.enum(['createdAt', 'updatedAt', 'sortOrder']),
39
+ direction: z.enum(['asc', 'desc'])
40
+ })
41
+ .optional()
36
42
  }), async (input) => {
37
43
  return getEntriesOperation(input);
38
44
  });
45
+ export const getEntryLabels = query(z.object({
46
+ slug: z.string(),
47
+ ids: z.array(z.string()).optional(),
48
+ search: z.string().optional(),
49
+ limit: z.number().int().positive().optional(),
50
+ status: z.enum(['published', 'draft', 'all']).optional()
51
+ }), async (input) => {
52
+ return getEntryLabelsOperation(input);
53
+ });
39
54
  export const getEntry = query(z.object({
40
55
  id: z.string().uuid().optional(),
41
56
  dataValues: z.record(z.string(), z.unknown()).optional(),
@@ -172,6 +187,12 @@ export const deleteEntryCommand = command(z.string().uuid(), async (id) => {
172
187
  }
173
188
  return getCMS().databaseAdapter.deleteEntry({ id });
174
189
  });
190
+ export const reorderEntriesCommand = command(z.object({
191
+ orderedIds: z.array(z.string().uuid())
192
+ }), async ({ orderedIds }) => {
193
+ requireAuth();
194
+ await Promise.all(orderedIds.map((id, index) => updateEntry(id, { sortOrder: index })));
195
+ });
175
196
  export const getEntryVersion = query(z.object({
176
197
  id: z.string().uuid(),
177
198
  language: z.string()
@@ -1,11 +1,11 @@
1
1
  import type { UserRole } from '../../types/roles.js';
2
2
  export declare function createInvitation(email: string, role: UserRole, createdBy: string): Promise<{
3
+ id: string;
4
+ role: string;
3
5
  createdAt: Date;
4
6
  email: string;
5
- id: string;
6
7
  expiresAt: Date;
7
8
  token: string;
8
- role: string;
9
9
  createdBy: string;
10
10
  usedAt: Date | null;
11
11
  }>;
@@ -156,6 +156,14 @@ export * from 'includio-cms/admin/remote';
156
156
  import { createAdminApiHandler } from 'includio-cms/admin/api/handler';
157
157
 
158
158
  export const { GET, POST, PATCH, PUT, DELETE } = createAdminApiHandler();
159
+ `
160
+ },
161
+ {
162
+ path: 'admin/api/rest/[...restPath]/+server.ts',
163
+ content: `${GENERATED_COMMENT_TS}
164
+ import { createRestApiHandler } from 'includio-cms/admin/api/rest/handler';
165
+
166
+ export const { GET, POST, PUT, DELETE } = createRestApiHandler();
159
167
  `
160
168
  }
161
169
  ];
@@ -0,0 +1,8 @@
1
+ export declare const submitContactCommand: import("@sveltejs/kit").RemoteCommand<{
2
+ name: string;
3
+ email: string;
4
+ phone: string;
5
+ subject: string;
6
+ message: string;
7
+ consent: boolean;
8
+ }, Promise<void>>;
@@ -0,0 +1,7 @@
1
+ // This file is auto-generated. Do not edit directly.
2
+ import { command } from '$app/server';
3
+ import { submitForm } from './api';
4
+ import { contactFormSchema } from './schemas';
5
+ export const submitContactCommand = command(contactFormSchema, async (data) => {
6
+ await submitForm('contact', data);
7
+ });
@@ -142,7 +142,7 @@ export interface Contact {
142
142
  };
143
143
  publishedAt: Date | null;
144
144
  }
145
- export type FormMap = {
145
+ export type FormEntryMap = {
146
146
  contact: Contact;
147
147
  };
148
148
  export type SiteLanguage = "en" | "pl";
@@ -8,6 +8,6 @@ type Props = WithElementRef<Omit<HTMLInputAttributes, "type"> & ({
8
8
  type?: InputType;
9
9
  files?: undefined;
10
10
  })>;
11
- declare const Input: import("svelte").Component<Props, {}, "ref" | "value" | "files">;
11
+ declare const Input: import("svelte").Component<Props, {}, "files" | "ref" | "value">;
12
12
  type Input = ReturnType<typeof Input>;
13
13
  export default Input;
@@ -2,7 +2,7 @@ declare const InputGroupInput: import("svelte").Component<(Omit<import("svelte/e
2
2
  type: "file";
3
3
  files?: FileList;
4
4
  } | {
5
- type?: "number" | "image" | "url" | "text" | "date" | "radio" | "email" | "checkbox" | "hidden" | "password" | (string & {}) | "reset" | "search" | "time" | "color" | "button" | "submit" | "tel" | "datetime-local" | "month" | "range" | "week";
5
+ type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "hidden" | "password" | "reset" | "time" | "submit" | "tel" | "datetime-local" | "month" | "range" | "week";
6
6
  files?: undefined;
7
7
  })) & {
8
8
  ref?: HTMLElement | null | undefined;
@@ -2,7 +2,7 @@ declare const SidebarInput: import("svelte").Component<(Omit<import("svelte/elem
2
2
  type: "file";
3
3
  files?: FileList;
4
4
  } | {
5
- type?: "number" | "image" | "url" | "text" | "date" | "radio" | "email" | "checkbox" | "hidden" | "password" | (string & {}) | "reset" | "search" | "time" | "color" | "button" | "submit" | "tel" | "datetime-local" | "month" | "range" | "week";
5
+ type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "hidden" | "password" | "reset" | "time" | "submit" | "tel" | "datetime-local" | "month" | "range" | "week";
6
6
  files?: undefined;
7
7
  })) & {
8
8
  ref?: HTMLElement | null | undefined;
@@ -1,12 +1,11 @@
1
1
  <script lang="ts">
2
2
  import { Toaster as Sonner, type ToasterProps as SonnerProps } from "svelte-sonner";
3
- import { mode } from "mode-watcher";
4
3
 
5
4
  let { richColors = false, ...restProps }: SonnerProps = $props();
6
5
  </script>
7
6
 
8
7
  <Sonner
9
- theme={mode.current}
8
+ theme="light"
10
9
  class="toaster group"
11
10
  {richColors}
12
11
  style="
@@ -1,6 +1,6 @@
1
1
  import type { DatabaseAdapter } from '../types/adapters/db.js';
2
2
  import type { FilesAdapter } from '../types/adapters/files.js';
3
- import type { CMSConfig, ICMS, MediaConfig } from '../types/cms.js';
3
+ import type { ApiKeyConfig, CMSConfig, ICMS, MediaConfig } from '../types/cms.js';
4
4
  import type { CollectionConfigWithType } from '../types/collections.js';
5
5
  import type { Language } from '../types/languages.js';
6
6
  import type { SingleConfigWithType } from '../types/singles.js';
@@ -22,6 +22,7 @@ export declare class CMS implements ICMS {
22
22
  languages: Language[];
23
23
  mediaConfig: MediaConfig;
24
24
  plugins: PluginConfig[];
25
+ apiKeys: ApiKeyConfig[];
25
26
  constructor(config: CMSConfig);
26
27
  getBySlug(slug: string): CollectionConfigWithType | SingleConfigWithType;
27
28
  getFormBySlug(slug: string): FormConfig;
package/dist/core/cms.js CHANGED
@@ -11,6 +11,7 @@ export class CMS {
11
11
  languages;
12
12
  mediaConfig;
13
13
  plugins = [];
14
+ apiKeys = [];
14
15
  constructor(config) {
15
16
  this.config = config;
16
17
  this.databaseAdapter = config.db;
@@ -40,6 +41,7 @@ export class CMS {
40
41
  };
41
42
  });
42
43
  this.languages = config.languages || [];
44
+ this.apiKeys = config.apiKeys || [];
43
45
  if (config.plugins) {
44
46
  this.plugins = config.plugins;
45
47
  }
@@ -10,6 +10,17 @@ export declare const getRawEntryOrThrow: (options: getRawEntryOptions) => Promis
10
10
  export declare const getEntries: (options?: GetEntriesOptions) => Promise<Entry[]>;
11
11
  export declare const getEntry: (options?: GetEntryOptions) => Promise<Entry | null>;
12
12
  export declare const getEntryOrThrow: (options?: GetEntryOptions) => Promise<Entry>;
13
+ export declare const getEntryLabels: (options: {
14
+ slug: string;
15
+ ids?: string[];
16
+ search?: string;
17
+ limit?: number;
18
+ status?: "published" | "draft" | "all";
19
+ }) => Promise<{
20
+ id: string;
21
+ label: string;
22
+ total: number;
23
+ }[]>;
13
24
  export declare const getDbEntryVersions: (options: GetDbEntryVersionsOptions) => Promise<DbEntryVersion[]>;
14
25
  export declare const getDbEntryVersion: (options: GetDbEntryVersionOptions) => Promise<DbEntryVersion | null>;
15
26
  export declare const getDbEntryVersionOrThrow: (options: GetDbEntryVersionOptions) => Promise<DbEntryVersion>;