refactor(admin): drive media page from server data
This commit is contained in:
parent
3a588fdf89
commit
878c0ae248
2 changed files with 73 additions and 28 deletions
60
src/routes/admin/media/+page.server.ts
Normal file
60
src/routes/admin/media/+page.server.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import type { Actions, PageServerLoad } from './$types'
|
||||||
|
import { adminFetchJson } from '$lib/server/admin/authenticated-fetch'
|
||||||
|
import type { Media } from '@prisma/client'
|
||||||
|
|
||||||
|
interface MediaResponse {
|
||||||
|
media: Media[]
|
||||||
|
pagination: {
|
||||||
|
page: number
|
||||||
|
totalPages: number
|
||||||
|
total: number
|
||||||
|
limit: number
|
||||||
|
hasNext: boolean
|
||||||
|
hasPrev: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildQueryString(url: URL) {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
|
||||||
|
const page = url.searchParams.get('page')
|
||||||
|
if (page) params.set('page', page)
|
||||||
|
|
||||||
|
const limit = url.searchParams.get('limit')
|
||||||
|
if (limit) params.set('limit', limit)
|
||||||
|
|
||||||
|
const mimeType = url.searchParams.get('mimeType')
|
||||||
|
if (mimeType) params.set('mimeType', mimeType)
|
||||||
|
|
||||||
|
const publishedFilter = url.searchParams.get('publishedFilter')
|
||||||
|
if (publishedFilter) params.set('publishedFilter', publishedFilter)
|
||||||
|
|
||||||
|
const sort = url.searchParams.get('sort')
|
||||||
|
if (sort) params.set('sort', sort)
|
||||||
|
|
||||||
|
const search = url.searchParams.get('search')
|
||||||
|
if (search) params.set('search', search)
|
||||||
|
|
||||||
|
const albumId = url.searchParams.get('albumId')
|
||||||
|
if (albumId) params.set('albumId', albumId)
|
||||||
|
|
||||||
|
const unused = url.searchParams.get('unused')
|
||||||
|
if (unused) params.set('unused', unused)
|
||||||
|
|
||||||
|
return params.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const load = (async (event) => {
|
||||||
|
event.depends('admin:media')
|
||||||
|
|
||||||
|
const query = buildQueryString(event.url)
|
||||||
|
const base = query ? `/api/media?${query}` : '/api/media'
|
||||||
|
const data = await adminFetchJson<MediaResponse>(event, base)
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: data.media,
|
||||||
|
pagination: data.pagination
|
||||||
|
}
|
||||||
|
}) satisfies PageServerLoad
|
||||||
|
|
||||||
|
export const actions = {} satisfies Actions
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte'
|
|
||||||
import { goto } from '$app/navigation'
|
import { goto } from '$app/navigation'
|
||||||
import AdminPage from '$lib/components/admin/AdminPage.svelte'
|
import AdminPage from '$lib/components/admin/AdminPage.svelte'
|
||||||
import AdminHeader from '$lib/components/admin/AdminHeader.svelte'
|
import AdminHeader from '$lib/components/admin/AdminHeader.svelte'
|
||||||
|
|
@ -15,13 +14,16 @@
|
||||||
import ChevronDown from '$icons/chevron-down.svg?component'
|
import ChevronDown from '$icons/chevron-down.svg?component'
|
||||||
import PlayIcon from '$icons/play.svg?component'
|
import PlayIcon from '$icons/play.svg?component'
|
||||||
import type { Media } from '@prisma/client'
|
import type { Media } from '@prisma/client'
|
||||||
|
import type { PageData } from './$types'
|
||||||
|
|
||||||
let media = $state<Media[]>([])
|
const { data } = $props<{ data: PageData }>()
|
||||||
let isLoading = $state(true)
|
|
||||||
|
let media = $state<Media[]>(data.items ?? [])
|
||||||
|
let isLoading = $state(false)
|
||||||
let error = $state('')
|
let error = $state('')
|
||||||
let currentPage = $state(1)
|
let currentPage = $state(data.pagination?.page ?? 1)
|
||||||
let totalPages = $state(1)
|
let totalPages = $state(data.pagination?.totalPages ?? 1)
|
||||||
let total = $state(0)
|
let total = $state(data.pagination?.total ?? (data.items?.length ?? 0))
|
||||||
// Only using grid view
|
// Only using grid view
|
||||||
|
|
||||||
// Filter states
|
// Filter states
|
||||||
|
|
@ -70,10 +72,6 @@
|
||||||
// Dropdown state
|
// Dropdown state
|
||||||
let isDropdownOpen = $state(false)
|
let isDropdownOpen = $state(false)
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
await loadMedia()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Watch for search query changes with debounce
|
// Watch for search query changes with debounce
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (searchQuery !== undefined) {
|
if (searchQuery !== undefined) {
|
||||||
|
|
@ -87,9 +85,6 @@
|
||||||
async function loadMedia(page = 1) {
|
async function loadMedia(page = 1) {
|
||||||
try {
|
try {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
const auth = localStorage.getItem('admin_auth')
|
|
||||||
if (!auth) return
|
|
||||||
|
|
||||||
let url = `/api/media?page=${page}&limit=24`
|
let url = `/api/media?page=${page}&limit=24`
|
||||||
if (filterType !== 'all') {
|
if (filterType !== 'all') {
|
||||||
url += `&mimeType=${filterType}`
|
url += `&mimeType=${filterType}`
|
||||||
|
|
@ -105,7 +100,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: { Authorization: `Basic ${auth}` }
|
credentials: 'same-origin'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) throw new Error('Failed to load media')
|
if (!response.ok) throw new Error('Failed to load media')
|
||||||
|
|
@ -252,15 +247,12 @@
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isDeleting = true
|
isDeleting = true
|
||||||
const auth = localStorage.getItem('admin_auth')
|
|
||||||
if (!auth) return
|
|
||||||
|
|
||||||
const response = await fetch('/api/media/bulk-delete', {
|
const response = await fetch('/api/media/bulk-delete', {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${auth}`,
|
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
credentials: 'same-origin',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
mediaIds: Array.from(selectedMediaIds)
|
mediaIds: Array.from(selectedMediaIds)
|
||||||
})
|
})
|
||||||
|
|
@ -269,8 +261,7 @@
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to delete media files')
|
throw new Error('Failed to delete media files')
|
||||||
}
|
}
|
||||||
|
await response.json()
|
||||||
const result = await response.json()
|
|
||||||
|
|
||||||
// Remove deleted media from the list
|
// Remove deleted media from the list
|
||||||
media = media.filter((m) => !selectedMediaIds.has(m.id))
|
media = media.filter((m) => !selectedMediaIds.has(m.id))
|
||||||
|
|
@ -294,17 +285,14 @@
|
||||||
if (selectedMediaIds.size === 0) return
|
if (selectedMediaIds.size === 0) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = localStorage.getItem('admin_auth')
|
|
||||||
if (!auth) return
|
|
||||||
|
|
||||||
// Update each selected media item
|
// Update each selected media item
|
||||||
const promises = Array.from(selectedMediaIds).map(async (mediaId) => {
|
const promises = Array.from(selectedMediaIds).map(async (mediaId) => {
|
||||||
const response = await fetch(`/api/media/${mediaId}`, {
|
const response = await fetch(`/api/media/${mediaId}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${auth}`,
|
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
credentials: 'same-origin',
|
||||||
body: JSON.stringify({ isPhotography: true })
|
body: JSON.stringify({ isPhotography: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -335,17 +323,14 @@
|
||||||
if (selectedMediaIds.size === 0) return
|
if (selectedMediaIds.size === 0) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = localStorage.getItem('admin_auth')
|
|
||||||
if (!auth) return
|
|
||||||
|
|
||||||
// Update each selected media item
|
// Update each selected media item
|
||||||
const promises = Array.from(selectedMediaIds).map(async (mediaId) => {
|
const promises = Array.from(selectedMediaIds).map(async (mediaId) => {
|
||||||
const response = await fetch(`/api/media/${mediaId}`, {
|
const response = await fetch(`/api/media/${mediaId}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${auth}`,
|
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
credentials: 'same-origin',
|
||||||
body: JSON.stringify({ isPhotography: false })
|
body: JSON.stringify({ isPhotography: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue