fix(admin): make filters reactive in Svelte 5
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
42be8ebcfc
commit
c67dbeaf38
3 changed files with 139 additions and 81 deletions
|
|
@ -14,6 +14,10 @@
|
|||
variant?: 'default' | 'minimal'
|
||||
fullWidth?: boolean
|
||||
pill?: boolean
|
||||
onchange?: (event: Event) => void
|
||||
oninput?: (event: Event) => void
|
||||
onfocus?: (event: FocusEvent) => void
|
||||
onblur?: (event: FocusEvent) => void
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -23,6 +27,10 @@
|
|||
variant = 'default',
|
||||
fullWidth = false,
|
||||
pill = true,
|
||||
onchange,
|
||||
oninput,
|
||||
onfocus,
|
||||
onblur,
|
||||
class: className = '',
|
||||
...restProps
|
||||
}: Props = $props()
|
||||
|
|
@ -34,6 +42,10 @@
|
|||
class="select select-{size} select-{variant} {className}"
|
||||
class:select-full-width={fullWidth}
|
||||
class:select-pill={pill}
|
||||
onchange={(e) => onchange?.(e)}
|
||||
oninput={(e) => oninput?.(e)}
|
||||
onfocus={(e) => onfocus?.(e)}
|
||||
onblur={(e) => onblur?.(e)}
|
||||
{...restProps}
|
||||
>
|
||||
{#each options as option}
|
||||
|
|
|
|||
|
|
@ -12,18 +12,19 @@
|
|||
import type { PageData } from './$types'
|
||||
import type { AdminPost } from '$lib/types/admin'
|
||||
|
||||
const { data, form } = $props<{ data: PageData; form?: { message?: string } }>()
|
||||
const { data, form } = $props<{ data: PageData; form?: { message?: string } }>()
|
||||
|
||||
let showInlineComposer = $state(true)
|
||||
let showDeleteConfirmation = $state(false)
|
||||
let postToDelete = $state<AdminPost | null>(null)
|
||||
let showInlineComposer = true
|
||||
let showDeleteConfirmation = false
|
||||
let postToDelete: AdminPost | null = null
|
||||
|
||||
let selectedTypeFilter = $state<string>('all')
|
||||
let selectedStatusFilter = $state<string>('all')
|
||||
let sortBy = $state<string>('newest')
|
||||
let selectedTypeFilter = 'all'
|
||||
let selectedStatusFilter = 'all'
|
||||
let sortBy = 'newest'
|
||||
|
||||
const actionError = $derived(form?.message ?? '')
|
||||
const posts = $derived(data.items ?? [])
|
||||
const actionError = form?.message ?? ''
|
||||
const posts = data.items ?? []
|
||||
let filteredPosts = $state<AdminPost[]>([...posts])
|
||||
|
||||
let toggleForm: HTMLFormElement | null = null
|
||||
let toggleIdField: HTMLInputElement | null = null
|
||||
|
|
@ -33,17 +34,17 @@
|
|||
let deleteForm: HTMLFormElement | null = null
|
||||
let deleteIdField: HTMLInputElement | null = null
|
||||
|
||||
const typeFilterOptions = $derived([
|
||||
{ value: 'all', label: 'All posts' },
|
||||
{ value: 'post', label: 'Posts' },
|
||||
{ value: 'essay', label: 'Essays' }
|
||||
])
|
||||
const typeFilterOptions = [
|
||||
{ value: 'all', label: 'All posts' },
|
||||
{ value: 'post', label: 'Posts' },
|
||||
{ value: 'essay', label: 'Essays' }
|
||||
]
|
||||
|
||||
const statusFilterOptions = $derived([
|
||||
const statusFilterOptions = [
|
||||
{ value: 'all', label: 'All statuses' },
|
||||
{ value: 'published', label: 'Published' },
|
||||
{ value: 'draft', label: 'Draft' }
|
||||
])
|
||||
]
|
||||
|
||||
const sortOptions = [
|
||||
{ value: 'newest', label: 'Newest first' },
|
||||
|
|
@ -54,47 +55,61 @@
|
|||
{ value: 'status-draft', label: 'Draft first' }
|
||||
]
|
||||
|
||||
const filteredPosts = $derived(() => {
|
||||
let next = [...posts]
|
||||
function applyFilterAndSort() {
|
||||
let next = [...posts]
|
||||
|
||||
if (selectedTypeFilter !== 'all') {
|
||||
next = next.filter((post) => post.postType === selectedTypeFilter)
|
||||
}
|
||||
if (selectedTypeFilter !== 'all') {
|
||||
next = next.filter((post) => post.postType === selectedTypeFilter)
|
||||
}
|
||||
|
||||
if (selectedStatusFilter !== 'all') {
|
||||
next = next.filter((post) => post.status === selectedStatusFilter)
|
||||
}
|
||||
if (selectedStatusFilter !== 'all') {
|
||||
next = next.filter((post) => post.status === selectedStatusFilter)
|
||||
}
|
||||
|
||||
switch (sortBy) {
|
||||
case 'oldest':
|
||||
next.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
|
||||
break
|
||||
case 'title-asc':
|
||||
next.sort((a, b) => (a.title || '').localeCompare(b.title || ''))
|
||||
break
|
||||
case 'title-desc':
|
||||
next.sort((a, b) => (b.title || '').localeCompare(a.title || ''))
|
||||
break
|
||||
case 'status-published':
|
||||
next.sort((a, b) => {
|
||||
if (a.status === b.status) return 0
|
||||
return a.status === 'published' ? -1 : 1
|
||||
})
|
||||
break
|
||||
case 'status-draft':
|
||||
next.sort((a, b) => {
|
||||
if (a.status === b.status) return 0
|
||||
return a.status === 'draft' ? -1 : 1
|
||||
})
|
||||
break
|
||||
case 'newest':
|
||||
default:
|
||||
next.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||
break
|
||||
}
|
||||
switch (sortBy) {
|
||||
case 'oldest':
|
||||
next.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
|
||||
break
|
||||
case 'title-asc':
|
||||
next.sort((a, b) => (a.title || '').localeCompare(b.title || ''))
|
||||
break
|
||||
case 'title-desc':
|
||||
next.sort((a, b) => (b.title || '').localeCompare(a.title || ''))
|
||||
break
|
||||
case 'status-published':
|
||||
next.sort((a, b) => {
|
||||
if (a.status === b.status) return 0
|
||||
return a.status === 'published' ? -1 : 1
|
||||
})
|
||||
break
|
||||
case 'status-draft':
|
||||
next.sort((a, b) => {
|
||||
if (a.status === b.status) return 0
|
||||
return a.status === 'draft' ? -1 : 1
|
||||
})
|
||||
break
|
||||
case 'newest':
|
||||
default:
|
||||
next.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||
break
|
||||
}
|
||||
|
||||
return next
|
||||
})
|
||||
filteredPosts = next
|
||||
}
|
||||
|
||||
applyFilterAndSort()
|
||||
|
||||
function handleTypeFilterChange() {
|
||||
applyFilterAndSort()
|
||||
}
|
||||
|
||||
function handleStatusFilterChange() {
|
||||
applyFilterAndSort()
|
||||
}
|
||||
|
||||
function handleSortChange() {
|
||||
applyFilterAndSort()
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener('click', handleOutsideClick)
|
||||
|
|
@ -184,16 +199,24 @@
|
|||
options={typeFilterOptions}
|
||||
size="small"
|
||||
variant="minimal"
|
||||
onchange={handleTypeFilterChange}
|
||||
/>
|
||||
<Select
|
||||
bind:value={selectedStatusFilter}
|
||||
options={statusFilterOptions}
|
||||
size="small"
|
||||
variant="minimal"
|
||||
onchange={handleStatusFilterChange}
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet right()}
|
||||
<Select bind:value={sortBy} options={sortOptions} size="small" variant="minimal" />
|
||||
<Select
|
||||
bind:value={sortBy}
|
||||
options={sortOptions}
|
||||
size="small"
|
||||
variant="minimal"
|
||||
onchange={handleSortChange}
|
||||
/>
|
||||
{/snippet}
|
||||
</AdminFilters>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,15 +13,16 @@
|
|||
|
||||
const { data, form } = $props<{ data: PageData; form?: { message?: string } }>()
|
||||
|
||||
let showDeleteModal = $state(false)
|
||||
let projectToDelete = $state<AdminProject | null>(null)
|
||||
let showDeleteModal = false
|
||||
let projectToDelete: AdminProject | null = null
|
||||
|
||||
let selectedTypeFilter = $state<string>('all')
|
||||
let selectedStatusFilter = $state<string>('all')
|
||||
let sortBy = $state<string>('newest')
|
||||
let selectedTypeFilter: string = 'all'
|
||||
let selectedStatusFilter: string = 'all'
|
||||
let sortBy: string = 'newest'
|
||||
|
||||
const actionError = $derived(form?.message ?? '')
|
||||
const projects = $derived(data.items ?? [])
|
||||
const actionError = form?.message ?? ''
|
||||
const projects = data.items ?? []
|
||||
let filteredProjects = $state<AdminProject[]>([...projects])
|
||||
|
||||
let toggleForm: HTMLFormElement | null = null
|
||||
let toggleIdField: HTMLInputElement | null = null
|
||||
|
|
@ -31,17 +32,17 @@
|
|||
let deleteForm: HTMLFormElement | null = null
|
||||
let deleteIdField: HTMLInputElement | null = null
|
||||
|
||||
const typeFilterOptions = $derived([
|
||||
const typeFilterOptions = [
|
||||
{ value: 'all', label: 'All projects' },
|
||||
{ value: 'work', label: 'Work' },
|
||||
{ value: 'labs', label: 'Labs' }
|
||||
])
|
||||
]
|
||||
|
||||
const statusFilterOptions = $derived([
|
||||
const statusFilterOptions = [
|
||||
{ value: 'all', label: 'All statuses' },
|
||||
{ value: 'published', label: 'Published' },
|
||||
{ value: 'draft', label: 'Draft' }
|
||||
])
|
||||
]
|
||||
|
||||
const sortOptions = [
|
||||
{ value: 'newest', label: 'Newest first' },
|
||||
|
|
@ -54,7 +55,7 @@
|
|||
{ value: 'status-draft', label: 'Draft first' }
|
||||
]
|
||||
|
||||
const filteredProjects = $derived(() => {
|
||||
function applyFilterAndSort() {
|
||||
let next = [...projects]
|
||||
|
||||
if (selectedStatusFilter !== 'all') {
|
||||
|
|
@ -99,8 +100,22 @@
|
|||
break
|
||||
}
|
||||
|
||||
return next
|
||||
})
|
||||
filteredProjects = next
|
||||
}
|
||||
|
||||
applyFilterAndSort()
|
||||
|
||||
function handleTypeFilterChange() {
|
||||
applyFilterAndSort()
|
||||
}
|
||||
|
||||
function handleStatusFilterChange() {
|
||||
applyFilterAndSort()
|
||||
}
|
||||
|
||||
function handleSortChange() {
|
||||
applyFilterAndSort()
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener('click', handleOutsideClick)
|
||||
|
|
@ -169,21 +184,29 @@
|
|||
|
||||
<AdminFilters>
|
||||
{#snippet left()}
|
||||
<Select
|
||||
bind:value={selectedTypeFilter}
|
||||
options={typeFilterOptions}
|
||||
size="small"
|
||||
variant="minimal"
|
||||
/>
|
||||
<Select
|
||||
bind:value={selectedStatusFilter}
|
||||
options={statusFilterOptions}
|
||||
size="small"
|
||||
variant="minimal"
|
||||
/>
|
||||
<Select
|
||||
bind:value={selectedTypeFilter}
|
||||
options={typeFilterOptions}
|
||||
size="small"
|
||||
variant="minimal"
|
||||
onchange={handleTypeFilterChange}
|
||||
/>
|
||||
<Select
|
||||
bind:value={selectedStatusFilter}
|
||||
options={statusFilterOptions}
|
||||
size="small"
|
||||
variant="minimal"
|
||||
onchange={handleStatusFilterChange}
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet right()}
|
||||
<Select bind:value={sortBy} options={sortOptions} size="small" variant="minimal" />
|
||||
<Select
|
||||
bind:value={sortBy}
|
||||
options={sortOptions}
|
||||
size="small"
|
||||
variant="minimal"
|
||||
onchange={handleSortChange}
|
||||
/>
|
||||
{/snippet}
|
||||
</AdminFilters>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue