astro-tractstack 2.0.20 → 2.0.22
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 +1 -1
- package/templates/src/components/compositor/preview/PanesPreviewGenerator.tsx +0 -1
- package/templates/src/components/edit/pane/AddPanePanel.tsx +28 -15
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +138 -125
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +225 -269
- package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +0 -2
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +3 -1
- package/templates/src/utils/compositor/designLibraryHelper.ts +378 -281
- package/templates/src/utils/compositor/reduceNodesClassNames.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useMemo } from 'react';
|
|
1
|
+
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { useStore } from '@nanostores/react';
|
|
3
3
|
import {
|
|
4
4
|
Dialog,
|
|
@@ -59,7 +59,6 @@ const TemplatePreviewItem = ({
|
|
|
59
59
|
} | null>(null);
|
|
60
60
|
|
|
61
61
|
const fragmentRequest = useMemo((): PanePreviewRequest[] => {
|
|
62
|
-
// This preview logic is correct: it creates a *temporary* context.
|
|
63
62
|
const ctx = new NodesContext();
|
|
64
63
|
ctx.addNode(createEmptyStorykeep('tmp'));
|
|
65
64
|
ctx.addTemplatePane('tmp', template);
|
|
@@ -145,6 +144,7 @@ export const RestylePaneModal = () => {
|
|
|
145
144
|
keys: ['isRestyleModalOpen', 'paneToRestyleId'],
|
|
146
145
|
});
|
|
147
146
|
const designLibrary = brandConfigStore.get()?.DESIGN_LIBRARY || [];
|
|
147
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
148
148
|
|
|
149
149
|
const [selectedCategory, setSelectedCategory] = useState<string>('all');
|
|
150
150
|
const [searchTerm, setSearchTerm] = useState('');
|
|
@@ -157,6 +157,33 @@ export const RestylePaneModal = () => {
|
|
|
157
157
|
return ['all', ...Array.from(allCategories)];
|
|
158
158
|
}, [designLibrary]);
|
|
159
159
|
|
|
160
|
+
const [targetMarkdownCount, setTargetMarkdownCount] = useState<number>(0);
|
|
161
|
+
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (!paneToRestyleId || !isRestyleModalOpen) {
|
|
164
|
+
setTargetMarkdownCount(0);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const paneChildIds = ctx.getChildNodeIDs(paneToRestyleId);
|
|
168
|
+
const nodesMap = ctx.allNodes.get();
|
|
169
|
+
const gridNodeId = paneChildIds.find(
|
|
170
|
+
(id) => nodesMap.get(id)?.nodeType === 'GridLayoutNode'
|
|
171
|
+
);
|
|
172
|
+
if (gridNodeId) {
|
|
173
|
+
const columns = ctx.getChildNodeIDs(gridNodeId);
|
|
174
|
+
setTargetMarkdownCount(columns.length);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const markdownNodeId = paneChildIds.find(
|
|
178
|
+
(id) => nodesMap.get(id)?.nodeType === 'Markdown'
|
|
179
|
+
);
|
|
180
|
+
if (markdownNodeId) {
|
|
181
|
+
setTargetMarkdownCount(1);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
setTargetMarkdownCount(0);
|
|
185
|
+
}, [paneToRestyleId, isRestyleModalOpen]);
|
|
186
|
+
|
|
160
187
|
const originalPaneData = useMemo(() => {
|
|
161
188
|
if (!paneToRestyleId) return null;
|
|
162
189
|
const paneNode = ctx.allNodes.get().get(paneToRestyleId) as PaneNode;
|
|
@@ -183,9 +210,10 @@ export const RestylePaneModal = () => {
|
|
|
183
210
|
return designLibrary.filter(
|
|
184
211
|
(entry: DesignLibraryEntry) =>
|
|
185
212
|
(selectedCategory === 'all' || entry.category === selectedCategory) &&
|
|
186
|
-
entry.title.toLowerCase().includes(searchTerm.toLowerCase())
|
|
213
|
+
entry.title.toLowerCase().includes(searchTerm.toLowerCase()) &&
|
|
214
|
+
entry.markdownCount === targetMarkdownCount
|
|
187
215
|
);
|
|
188
|
-
}, [designLibrary, selectedCategory, searchTerm]);
|
|
216
|
+
}, [designLibrary, selectedCategory, searchTerm, targetMarkdownCount]);
|
|
189
217
|
|
|
190
218
|
const paginatedEntries = useMemo(() => {
|
|
191
219
|
const start = (currentPage - 1) * PAGE_SIZE;
|
|
@@ -224,123 +252,64 @@ export const RestylePaneModal = () => {
|
|
|
224
252
|
setSelectedCategory('all');
|
|
225
253
|
};
|
|
226
254
|
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
{
|
|
233
|
-
templateToApply: template,
|
|
234
|
-
originalPaneData: originalPaneData,
|
|
235
|
-
}
|
|
236
|
-
);
|
|
255
|
+
const handleDialogStateChange = (details: { open: boolean }) => {
|
|
256
|
+
if (!details.open) {
|
|
257
|
+
handleClose();
|
|
258
|
+
}
|
|
259
|
+
};
|
|
237
260
|
|
|
261
|
+
const handleSelectTemplate = (template: TemplatePane) => {
|
|
238
262
|
if (!originalPaneData) {
|
|
239
|
-
console.error(
|
|
240
|
-
'%cDEBUG: handleSelectTemplate FAILED: originalPaneData is null.',
|
|
241
|
-
'color: red; font-weight: bold;'
|
|
242
|
-
);
|
|
243
263
|
return;
|
|
244
264
|
}
|
|
245
265
|
|
|
246
266
|
const originalPane = originalPaneData.paneNode;
|
|
247
267
|
const originalPaneId = originalPane.id;
|
|
248
268
|
|
|
249
|
-
|
|
250
|
-
console.log(
|
|
251
|
-
`%cDEBUG: STEP 1 - HOLLOWING OUT original pane ${originalPaneId}`,
|
|
252
|
-
'color: #A0A; font-weight: bold;'
|
|
253
|
-
);
|
|
254
|
-
const oldChildrenNodes = ctx
|
|
255
|
-
.getChildNodeIDs(originalPaneId)
|
|
256
|
-
.map((id) => ctx.allNodes.get().get(id));
|
|
257
|
-
if (VERBOSE)
|
|
258
|
-
console.log(
|
|
259
|
-
'%cDEBUG: Original pane children BEFORE delete:',
|
|
260
|
-
oldChildrenNodes
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
const deletedChildren = ctx.deleteChildren(originalPaneId); // This deletes *children*, not the pane itself
|
|
264
|
-
|
|
265
|
-
const childrenAfterDelete = ctx.getChildNodeIDs(originalPaneId);
|
|
266
|
-
if (VERBOSE) {
|
|
267
|
-
console.log(
|
|
268
|
-
`%cDEBUG: Deleted ${deletedChildren.length} old child nodes.`,
|
|
269
|
-
'color: #A0A;'
|
|
270
|
-
);
|
|
271
|
-
console.log(
|
|
272
|
-
`%cDEBUG: Original pane children IDs AFTER delete: [${childrenAfterDelete.join(', ')}]`,
|
|
273
|
-
'color: #A0A;'
|
|
274
|
-
);
|
|
275
|
-
console.log(
|
|
276
|
-
`%cDEBUG: STEP 2 - REFILLING pane with new nodes...`,
|
|
277
|
-
'color: #0A0; font-weight: bold;'
|
|
278
|
-
);
|
|
279
|
-
}
|
|
269
|
+
ctx.deleteChildren(originalPaneId);
|
|
280
270
|
|
|
281
271
|
const newNodesToAdd: BaseNode[] = [];
|
|
282
272
|
const newMarkdown = template.markdown as TemplateMarkdown | undefined;
|
|
273
|
+
const newGridLayout = template.gridLayout;
|
|
283
274
|
const newBgPane = template.bgPane;
|
|
284
275
|
|
|
285
276
|
if (newMarkdown) {
|
|
286
|
-
// Re-parent the new Markdown node to the original pane
|
|
287
277
|
newMarkdown.parentId = originalPaneId;
|
|
288
278
|
newNodesToAdd.push(newMarkdown);
|
|
289
|
-
|
|
290
|
-
// The markdown.nodes are already parented to the newMarkdown.id, which is correct.
|
|
291
|
-
// We just need to add them to the context.
|
|
292
279
|
if (newMarkdown.nodes) {
|
|
293
280
|
newNodesToAdd.push(...newMarkdown.nodes);
|
|
294
281
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (newGridLayout) {
|
|
285
|
+
newGridLayout.parentId = originalPaneId;
|
|
286
|
+
newNodesToAdd.push(newGridLayout);
|
|
287
|
+
|
|
288
|
+
if (newGridLayout.nodes) {
|
|
289
|
+
newGridLayout.nodes.forEach((column) => {
|
|
290
|
+
column.parentId = newGridLayout.id;
|
|
291
|
+
newNodesToAdd.push(column);
|
|
292
|
+
|
|
293
|
+
if (column.nodes) {
|
|
294
|
+
newNodesToAdd.push(...column.nodes);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
301
297
|
}
|
|
302
298
|
}
|
|
303
299
|
|
|
304
300
|
if (newBgPane) {
|
|
305
|
-
// Re-parent the new BgPane node to the original pane
|
|
306
301
|
newBgPane.parentId = originalPaneId;
|
|
307
302
|
newNodesToAdd.push(newBgPane);
|
|
308
|
-
if (VERBOSE) console.log(`%cDEBUG: Prepared new BgPane:`, newBgPane);
|
|
309
303
|
}
|
|
310
304
|
|
|
311
|
-
ctx.addNodes(newNodesToAdd);
|
|
305
|
+
ctx.addNodes(newNodesToAdd);
|
|
312
306
|
|
|
313
|
-
const childrenAfterAdd = ctx.getChildNodeIDs(originalPaneId);
|
|
314
|
-
const childrenNodesAfterAdd = childrenAfterAdd.map((id) =>
|
|
315
|
-
ctx.allNodes.get().get(id)
|
|
316
|
-
);
|
|
317
|
-
if (VERBOSE) {
|
|
318
|
-
console.log(
|
|
319
|
-
`%cDEBUG: Original pane children IDs AFTER add: [${childrenAfterAdd.join(', ')}]`,
|
|
320
|
-
'color: #0A0;'
|
|
321
|
-
);
|
|
322
|
-
console.log(
|
|
323
|
-
`%cDEBUG: Original pane children nodes AFTER add:`,
|
|
324
|
-
childrenNodesAfterAdd
|
|
325
|
-
);
|
|
326
|
-
console.log(
|
|
327
|
-
`%cDEBUG: STEP 3 - UPDATING original pane properties...`,
|
|
328
|
-
'color: #00F; font-weight: bold;'
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// We must get a fresh reference from the store to modify
|
|
333
307
|
const paneToUpdate = ctx.allNodes.get().get(originalPaneId) as PaneNode;
|
|
334
308
|
|
|
335
309
|
if (!paneToUpdate) {
|
|
336
|
-
console.error(
|
|
337
|
-
`%cDEBUG: FAILED TO FIND PANE ${originalPaneId} IN STORE FOR FINAL UPDATE.`,
|
|
338
|
-
'color: red; font-weight: bold;'
|
|
339
|
-
);
|
|
340
310
|
return;
|
|
341
311
|
}
|
|
342
312
|
|
|
343
|
-
// Copy all style/config properties from the template, but keep the original ID, parentId, slug, title
|
|
344
313
|
paneToUpdate.bgColour = template.bgColour;
|
|
345
314
|
paneToUpdate.isDecorative = template.isDecorative;
|
|
346
315
|
paneToUpdate.heightOffsetDesktop = template.heightOffsetDesktop;
|
|
@@ -349,25 +318,9 @@ export const RestylePaneModal = () => {
|
|
|
349
318
|
paneToUpdate.heightRatioDesktop = template.heightRatioDesktop;
|
|
350
319
|
paneToUpdate.heightRatioMobile = template.heightRatioMobile;
|
|
351
320
|
paneToUpdate.heightRatioTablet = template.heightRatioTablet;
|
|
352
|
-
paneToUpdate.isChanged = true;
|
|
353
|
-
|
|
354
|
-
if (VERBOSE)
|
|
355
|
-
console.log(
|
|
356
|
-
`%cDEBUG: Calling modifyNodes with this pane object:`,
|
|
357
|
-
paneToUpdate
|
|
358
|
-
);
|
|
359
|
-
ctx.modifyNodes([paneToUpdate]); // This will save the changes and notify the UI
|
|
321
|
+
paneToUpdate.isChanged = true;
|
|
360
322
|
|
|
361
|
-
|
|
362
|
-
console.log(
|
|
363
|
-
'%cDEBUG: handleSelectTemplate FINISHED.',
|
|
364
|
-
'color: #00A; font-weight: bold;'
|
|
365
|
-
);
|
|
366
|
-
console.log(
|
|
367
|
-
'%cDEBUG: Notifying ROOT_NODE to force re-render.',
|
|
368
|
-
'color: green; font-weight: bold;'
|
|
369
|
-
);
|
|
370
|
-
}
|
|
323
|
+
ctx.modifyNodes([paneToUpdate]);
|
|
371
324
|
ctx.notifyNode('root');
|
|
372
325
|
|
|
373
326
|
handleClose();
|
|
@@ -394,176 +347,179 @@ export const RestylePaneModal = () => {
|
|
|
394
347
|
);
|
|
395
348
|
|
|
396
349
|
return (
|
|
397
|
-
<Dialog.Root
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
350
|
+
<Dialog.Root
|
|
351
|
+
open={isRestyleModalOpen}
|
|
352
|
+
onOpenChange={handleDialogStateChange}
|
|
353
|
+
modal={false}
|
|
354
|
+
>
|
|
355
|
+
<Dialog.Backdrop className="z-103 fixed inset-0 bg-black/70" />
|
|
356
|
+
<Dialog.Positioner className="z-104 fixed inset-0 flex items-center justify-center">
|
|
357
|
+
<Dialog.Content
|
|
358
|
+
ref={contentRef}
|
|
359
|
+
className="flex flex-col rounded-lg bg-white shadow-2xl"
|
|
360
|
+
style={{ maxHeight: '90vw', width: '90vw' }}
|
|
361
|
+
>
|
|
362
|
+
<header className="flex items-center justify-between border-b p-4">
|
|
363
|
+
<Dialog.Title className="text-xl font-bold">
|
|
364
|
+
Restyle Pane from Design Library
|
|
365
|
+
</Dialog.Title>
|
|
366
|
+
<Dialog.CloseTrigger
|
|
367
|
+
type="button"
|
|
368
|
+
className="rounded-full p-1 text-gray-600 hover:bg-gray-100"
|
|
369
|
+
>
|
|
370
|
+
<XMarkIcon className="h-6 w-6" />
|
|
371
|
+
</Dialog.CloseTrigger>
|
|
372
|
+
</header>
|
|
373
|
+
|
|
374
|
+
<nav className="flex items-center gap-x-4 border-b bg-gray-50 p-4">
|
|
375
|
+
<Select.Root
|
|
376
|
+
collection={selectCollection}
|
|
377
|
+
value={[selectedCategory]}
|
|
378
|
+
onValueChange={(details: SelectValueChangeDetails) =>
|
|
379
|
+
setSelectedCategory(details.value[0])
|
|
380
|
+
}
|
|
381
|
+
className="w-48"
|
|
382
|
+
positioning={{ gutter: 4 }}
|
|
383
|
+
>
|
|
384
|
+
<Select.Label className="mb-1 text-sm font-bold">
|
|
385
|
+
Category
|
|
386
|
+
</Select.Label>
|
|
387
|
+
<Select.Control>
|
|
388
|
+
<Select.Trigger className="flex w-full items-center justify-between rounded border bg-white p-2 text-left">
|
|
389
|
+
<Select.ValueText />
|
|
390
|
+
<Select.Indicator>▼</Select.Indicator>
|
|
391
|
+
</Select.Trigger>
|
|
392
|
+
</Select.Control>
|
|
393
|
+
<Portal>
|
|
394
|
+
<Select.Positioner>
|
|
395
|
+
<Select.Content className="z-105 rounded border bg-white shadow-lg">
|
|
396
|
+
{categories.map((c) => (
|
|
397
|
+
<Select.Item
|
|
398
|
+
key={c}
|
|
399
|
+
item={{ label: c, value: c }}
|
|
400
|
+
className="cursor-pointer p-2 hover:bg-gray-100"
|
|
401
|
+
>
|
|
402
|
+
<Select.ItemText>{c}</Select.ItemText>
|
|
403
|
+
</Select.Item>
|
|
404
|
+
))}
|
|
405
|
+
</Select.Content>
|
|
406
|
+
</Select.Positioner>
|
|
407
|
+
</Portal>
|
|
408
|
+
</Select.Root>
|
|
409
|
+
|
|
410
|
+
<Combobox.Root
|
|
411
|
+
collection={comboboxCollection}
|
|
412
|
+
onInputValueChange={(e: ComboboxInputValueChangeDetails) =>
|
|
413
|
+
setSearchTerm(e.inputValue)
|
|
414
|
+
}
|
|
415
|
+
className="flex-1"
|
|
416
|
+
positioning={{ gutter: 4 }}
|
|
417
|
+
>
|
|
418
|
+
<Combobox.Label className="mb-1 text-sm font-bold">
|
|
419
|
+
Filter by Title
|
|
420
|
+
</Combobox.Label>
|
|
421
|
+
<Combobox.Control>
|
|
422
|
+
<Combobox.Input
|
|
423
|
+
placeholder="Search by title..."
|
|
424
|
+
className="w-full rounded border p-2"
|
|
425
|
+
/>
|
|
426
|
+
</Combobox.Control>
|
|
427
|
+
<Portal>
|
|
428
|
+
<Combobox.Positioner>
|
|
429
|
+
<Combobox.Content className="z-105 rounded border bg-white shadow-lg">
|
|
430
|
+
{filteredEntries.map((entry: DesignLibraryEntry) => (
|
|
431
|
+
<Combobox.Item
|
|
432
|
+
key={entry.title}
|
|
433
|
+
item={entry}
|
|
434
|
+
className="cursor-pointer p-2 hover:bg-gray-100"
|
|
435
|
+
>
|
|
436
|
+
<Combobox.ItemText>{entry.title}</Combobox.ItemText>
|
|
437
|
+
</Combobox.Item>
|
|
438
|
+
))}
|
|
439
|
+
</Combobox.Content>
|
|
440
|
+
</Combobox.Positioner>
|
|
441
|
+
</Portal>
|
|
442
|
+
</Combobox.Root>
|
|
443
|
+
</nav>
|
|
444
|
+
|
|
445
|
+
<main className="flex-1 overflow-y-auto bg-gray-100 p-6">
|
|
446
|
+
{mergedTemplates.length === 0 ? (
|
|
447
|
+
<div className="flex h-full items-center justify-center">
|
|
448
|
+
<p className="text-gray-500">No designs found.</p>
|
|
449
|
+
</div>
|
|
450
|
+
) : (
|
|
451
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
452
|
+
{mergedTemplates.map(({ template }) => (
|
|
453
|
+
<TemplatePreviewItem
|
|
454
|
+
key={template.id}
|
|
455
|
+
template={template}
|
|
456
|
+
onClick={() => handleSelectTemplate(template)}
|
|
457
|
+
/>
|
|
458
|
+
))}
|
|
459
|
+
</div>
|
|
460
|
+
)}
|
|
461
|
+
</main>
|
|
462
|
+
|
|
463
|
+
{totalPages > 1 && (
|
|
464
|
+
<footer className="flex items-center justify-center border-t p-4">
|
|
465
|
+
<Pagination.Root
|
|
466
|
+
count={totalPages * PAGE_SIZE}
|
|
467
|
+
pageSize={PAGE_SIZE}
|
|
468
|
+
siblingCount={1}
|
|
469
|
+
page={currentPage}
|
|
470
|
+
onPageChange={(details: PaginationPageChangeDetails) =>
|
|
471
|
+
setCurrentPage(details.page)
|
|
423
472
|
}
|
|
424
|
-
className="
|
|
425
|
-
positioning={{ gutter: 4 }}
|
|
473
|
+
className="flex items-center gap-x-2"
|
|
426
474
|
>
|
|
427
|
-
<
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
475
|
+
<Pagination.PrevTrigger
|
|
476
|
+
type="button"
|
|
477
|
+
className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
|
|
478
|
+
disabled={currentPage === 1}
|
|
479
|
+
>
|
|
480
|
+
Previous
|
|
481
|
+
</Pagination.PrevTrigger>
|
|
482
|
+
<Pagination.Context>
|
|
483
|
+
{(pagination) =>
|
|
484
|
+
pagination.pages.map((page, index: number) =>
|
|
485
|
+
page.type === 'page' ? (
|
|
486
|
+
<Pagination.Item
|
|
487
|
+
key={index}
|
|
488
|
+
{...page}
|
|
489
|
+
type="page"
|
|
490
|
+
className={classNames(
|
|
491
|
+
'flex h-9 w-9 items-center justify-center rounded text-sm',
|
|
492
|
+
page.value === currentPage
|
|
493
|
+
? 'bg-blue-600 font-bold text-white'
|
|
494
|
+
: 'hover:bg-gray-100'
|
|
495
|
+
)}
|
|
444
496
|
>
|
|
445
|
-
|
|
446
|
-
</
|
|
447
|
-
)
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
<Combobox.Root
|
|
454
|
-
collection={comboboxCollection}
|
|
455
|
-
onInputValueChange={(e: ComboboxInputValueChangeDetails) =>
|
|
456
|
-
setSearchTerm(e.inputValue)
|
|
457
|
-
}
|
|
458
|
-
className="flex-1"
|
|
459
|
-
positioning={{ gutter: 4 }}
|
|
460
|
-
>
|
|
461
|
-
<Combobox.Label className="mb-1 text-sm font-bold">
|
|
462
|
-
Filter by Title
|
|
463
|
-
</Combobox.Label>
|
|
464
|
-
<Combobox.Control>
|
|
465
|
-
<Combobox.Input
|
|
466
|
-
placeholder="Search by title..."
|
|
467
|
-
className="w-full rounded border p-2"
|
|
468
|
-
/>
|
|
469
|
-
</Combobox.Control>
|
|
470
|
-
<Portal>
|
|
471
|
-
<Combobox.Positioner>
|
|
472
|
-
<Combobox.Content className="z-105 rounded border bg-white shadow-lg">
|
|
473
|
-
{filteredEntries.map((entry: DesignLibraryEntry) => (
|
|
474
|
-
<Combobox.Item
|
|
475
|
-
key={entry.title}
|
|
476
|
-
item={entry}
|
|
477
|
-
className="cursor-pointer p-2 hover:bg-gray-100"
|
|
497
|
+
{page.value}
|
|
498
|
+
</Pagination.Item>
|
|
499
|
+
) : (
|
|
500
|
+
<Pagination.Ellipsis
|
|
501
|
+
key={index}
|
|
502
|
+
index={index}
|
|
503
|
+
className="px-2 text-sm"
|
|
478
504
|
>
|
|
479
|
-
|
|
480
|
-
</
|
|
481
|
-
)
|
|
482
|
-
|
|
483
|
-
</Combobox.Positioner>
|
|
484
|
-
</Portal>
|
|
485
|
-
</Combobox.Root>
|
|
486
|
-
</nav>
|
|
487
|
-
|
|
488
|
-
<main className="flex-1 overflow-y-auto bg-gray-100 p-6">
|
|
489
|
-
{mergedTemplates.length === 0 ? (
|
|
490
|
-
<div className="flex h-full items-center justify-center">
|
|
491
|
-
<p className="text-gray-500">No designs found.</p>
|
|
492
|
-
</div>
|
|
493
|
-
) : (
|
|
494
|
-
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
495
|
-
{mergedTemplates.map(({ template }) => (
|
|
496
|
-
<TemplatePreviewItem
|
|
497
|
-
key={template.id}
|
|
498
|
-
template={template}
|
|
499
|
-
onClick={() => handleSelectTemplate(template)}
|
|
500
|
-
/>
|
|
501
|
-
))}
|
|
502
|
-
</div>
|
|
503
|
-
)}
|
|
504
|
-
</main>
|
|
505
|
-
|
|
506
|
-
{totalPages > 1 && (
|
|
507
|
-
<footer className="flex items-center justify-center border-t p-4">
|
|
508
|
-
<Pagination.Root
|
|
509
|
-
count={totalPages * PAGE_SIZE}
|
|
510
|
-
pageSize={PAGE_SIZE}
|
|
511
|
-
siblingCount={1}
|
|
512
|
-
page={currentPage}
|
|
513
|
-
onPageChange={(details: PaginationPageChangeDetails) =>
|
|
514
|
-
setCurrentPage(details.page)
|
|
505
|
+
...
|
|
506
|
+
</Pagination.Ellipsis>
|
|
507
|
+
)
|
|
508
|
+
)
|
|
515
509
|
}
|
|
516
|
-
|
|
510
|
+
</Pagination.Context>
|
|
511
|
+
<Pagination.NextTrigger
|
|
512
|
+
type="button"
|
|
513
|
+
className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
|
|
514
|
+
disabled={currentPage === totalPages}
|
|
517
515
|
>
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
<Pagination.Context>
|
|
526
|
-
{(pagination) =>
|
|
527
|
-
pagination.pages.map((page, index: number) =>
|
|
528
|
-
page.type === 'page' ? (
|
|
529
|
-
<Pagination.Item
|
|
530
|
-
key={index}
|
|
531
|
-
{...page}
|
|
532
|
-
type="page"
|
|
533
|
-
className={classNames(
|
|
534
|
-
'flex h-9 w-9 items-center justify-center rounded text-sm',
|
|
535
|
-
page.value === currentPage
|
|
536
|
-
? 'bg-blue-600 font-bold text-white'
|
|
537
|
-
: 'hover:bg-gray-100'
|
|
538
|
-
)}
|
|
539
|
-
>
|
|
540
|
-
{page.value}
|
|
541
|
-
</Pagination.Item>
|
|
542
|
-
) : (
|
|
543
|
-
<Pagination.Ellipsis
|
|
544
|
-
key={index}
|
|
545
|
-
index={index}
|
|
546
|
-
className="px-2 text-sm"
|
|
547
|
-
>
|
|
548
|
-
...
|
|
549
|
-
</Pagination.Ellipsis>
|
|
550
|
-
)
|
|
551
|
-
)
|
|
552
|
-
}
|
|
553
|
-
</Pagination.Context>
|
|
554
|
-
<Pagination.NextTrigger
|
|
555
|
-
type="button"
|
|
556
|
-
className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
|
|
557
|
-
disabled={currentPage === totalPages}
|
|
558
|
-
>
|
|
559
|
-
Next
|
|
560
|
-
</Pagination.NextTrigger>
|
|
561
|
-
</Pagination.Root>
|
|
562
|
-
</footer>
|
|
563
|
-
)}
|
|
564
|
-
</Dialog.Content>
|
|
565
|
-
</Dialog.Positioner>
|
|
566
|
-
</Portal>
|
|
516
|
+
Next
|
|
517
|
+
</Pagination.NextTrigger>
|
|
518
|
+
</Pagination.Root>
|
|
519
|
+
</footer>
|
|
520
|
+
)}
|
|
521
|
+
</Dialog.Content>
|
|
522
|
+
</Dialog.Positioner>
|
|
567
523
|
</Dialog.Root>
|
|
568
524
|
);
|
|
569
525
|
};
|
|
@@ -30,7 +30,6 @@ import { classNames } from '@/utils/helpers';
|
|
|
30
30
|
|
|
31
31
|
const PAGE_SIZE = 12;
|
|
32
32
|
|
|
33
|
-
// --- Sub-component for rendering a single preview item ---
|
|
34
33
|
interface TemplatePreviewItemProps {
|
|
35
34
|
storageTemplate: StoragePane;
|
|
36
35
|
onClick: () => void;
|
|
@@ -224,7 +223,6 @@ export const DesignLibraryStep = ({ onSelect }: DesignLibraryStepProps) => {
|
|
|
224
223
|
2. Choose a Design
|
|
225
224
|
</label>
|
|
226
225
|
|
|
227
|
-
{/* --- Filters --- */}
|
|
228
226
|
<nav className="flex items-center gap-x-4 rounded-md border bg-white p-3">
|
|
229
227
|
<Select.Root
|
|
230
228
|
collection={selectCollection}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, useMemo, useEffect } from 'react';
|
|
2
2
|
import { CheckIcon } from '@heroicons/react/20/solid';
|
|
3
3
|
import { savePaneToLibrary } from '@/utils/compositor/designLibraryHelper';
|
|
4
|
+
import { convertToBackendFormat } from '@/utils/api/brandHelpers';
|
|
4
5
|
import StringInput from '@/components/form/StringInput';
|
|
5
6
|
import { brandConfigStore } from '@/stores/storykeep';
|
|
6
7
|
|
|
@@ -87,8 +88,9 @@ export function SaveToLibraryModal({
|
|
|
87
88
|
formData
|
|
88
89
|
);
|
|
89
90
|
if (newBrandConfig) {
|
|
91
|
+
const backendDTO = convertToBackendFormat(newBrandConfig);
|
|
90
92
|
brandConfigStore.set({
|
|
91
|
-
...
|
|
93
|
+
...backendDTO, // Use the converted DTO
|
|
92
94
|
TENANT_ID: brandConfig.TENANT_ID,
|
|
93
95
|
});
|
|
94
96
|
setSaveState('saved');
|