fix: address all code review findings

- Extract shared permission logic into boardPermissions.ts utility
- Fix DnD drag revert: add dirty flag to prevent  overwrite
- Wrap OAuth group sync in Prisma transaction (N+1 fix)
- Add empty widgetIds validation in widget reorder API
- Add invalidateAll() after guest toggle PATCH
- Replace console.error with user-visible error banners
- Extract WidgetCreationForm component (DraggableSection was 448 lines)
- Remove unused boardId prop from DraggableSection
- Always include OAuth state parameter + validate in callback
- Clean up copyLink timer on component destroy
- Add type-specific widget config validation in addWidget action
This commit is contained in:
2026-03-25 00:03:32 +03:00
parent 5a6002be76
commit cba160ecb8
15 changed files with 588 additions and 447 deletions
+19 -5
View File
@@ -53,19 +53,25 @@
}: Props = $props();
let sections = $state<SectionData[]>([...initialSections]);
let dirty = $state(false);
let errorMessage = $state('');
// Keep local state in sync when parent data changes
// Keep local state in sync when parent data changes (skip during drag)
$effect(() => {
sections = [...initialSections];
if (!dirty) {
sections = [...initialSections];
}
});
const flipDurationMs = 200;
function handleConsider(e: CustomEvent<{ items: SectionData[] }>) {
dirty = true;
sections = e.detail.items;
}
async function handleFinalize(e: CustomEvent<{ items: SectionData[] }>) {
dirty = true;
sections = e.detail.items;
const sectionIds = sections.map((s) => s.id);
@@ -76,12 +82,15 @@
body: JSON.stringify({ sectionIds })
});
} catch (err) {
console.error('Failed to persist section reorder:', err);
errorMessage = err instanceof Error ? err.message : 'Failed to persist section reorder';
} finally {
dirty = false;
}
}
async function handleWidgetsUpdate(sectionId: string, widgets: WidgetData[]) {
// Update local state
dirty = true;
sections = sections.map((s) => (s.id === sectionId ? { ...s, widgets } : s));
const widgetIds = widgets.map((w) => w.id);
@@ -93,11 +102,17 @@
body: JSON.stringify({ widgetIds })
});
} catch (err) {
console.error('Failed to persist widget reorder:', err);
errorMessage = err instanceof Error ? err.message : 'Failed to persist widget reorder';
} finally {
dirty = false;
}
}
</script>
{#if errorMessage}
<p class="mb-2 text-sm text-destructive">{errorMessage}</p>
{/if}
{#if sections.length === 0}
<div class="rounded-xl border border-border bg-card/50 p-8 text-center">
<p class="text-muted-foreground">{$t('board.no_sections')}</p>
@@ -113,7 +128,6 @@
<div>
<DraggableSection
{section}
{boardId}
{apps}
onWidgetsUpdate={handleWidgetsUpdate}
{addWidgetSectionId}