diff --git a/src/routes/admin/media/+page.svelte b/src/routes/admin/media/+page.svelte index 9b0a7ab..98aeab0 100644 --- a/src/routes/admin/media/+page.svelte +++ b/src/routes/admin/media/+page.svelte @@ -6,8 +6,11 @@ import Input from '$lib/components/admin/Input.svelte' import Select from '$lib/components/admin/Select.svelte' import Button from '$lib/components/admin/Button.svelte' + import DropdownMenuContainer from '$lib/components/admin/DropdownMenuContainer.svelte' + import DropdownItem from '$lib/components/admin/DropdownItem.svelte' import MediaDetailsModal from '$lib/components/admin/MediaDetailsModal.svelte' import MediaUploadModal from '$lib/components/admin/MediaUploadModal.svelte' + import ChevronDown from '$icons/chevron-down.svg' import type { Media } from '@prisma/client' let media = $state([]) @@ -20,9 +23,10 @@ // Filter states let filterType = $state('all') - let photographyFilter = $state('all') + let publishedFilter = $state('all') let searchQuery = $state('') let searchTimeout: ReturnType + let sortBy = $state('newest') // Filter options const typeFilterOptions = [ @@ -30,13 +34,23 @@ { value: 'image', label: 'Images' }, { value: 'video', label: 'Videos' }, { value: 'audio', label: 'Audio' }, - { value: 'application/pdf', label: 'PDFs' } + { value: 'vector', label: 'Vectors' } ] - const photographyFilterOptions = [ - { value: 'all', label: 'All media' }, - { value: 'true', label: 'Photography only' }, - { value: 'false', label: 'Non-photography' } + const publishedFilterOptions = [ + { value: 'all', label: 'Published in' }, + { value: 'photos', label: 'Photos' }, + { value: 'universe', label: 'Universe' }, + { value: 'unpublished', label: 'Unpublished' } + ] + + const sortOptions = [ + { value: 'newest', label: 'Newest first' }, + { value: 'oldest', label: 'Oldest first' }, + { value: 'name-asc', label: 'Name (A-Z)' }, + { value: 'name-desc', label: 'Name (Z-A)' }, + { value: 'size-asc', label: 'Size (smallest)' }, + { value: 'size-desc', label: 'Size (largest)' } ] // Modal states @@ -48,6 +62,9 @@ let selectedMediaIds = $state>(new Set()) let isMultiSelectMode = $state(false) let isDeleting = $state(false) + + // Dropdown state + let isDropdownOpen = $state(false) onMount(async () => { await loadMedia() @@ -73,12 +90,15 @@ if (filterType !== 'all') { url += `&mimeType=${filterType}` } - if (photographyFilter !== 'all') { - url += `&isPhotography=${photographyFilter}` + if (publishedFilter !== 'all') { + url += `&publishedFilter=${publishedFilter}` } if (searchQuery) { url += `&search=${encodeURIComponent(searchQuery)}` } + if (sortBy) { + url += `&sort=${sortBy}` + } const response = await fetch(url, { headers: { Authorization: `Basic ${auth}` } @@ -113,6 +133,11 @@ loadMedia(1) } + function handleSortChange() { + currentPage = 1 + loadMedia(1) + } + function formatFileSize(bytes: number): string { if (bytes === 0) return '0 Bytes' const k = 1024 @@ -154,11 +179,36 @@ function openUploadModal() { isUploadModalOpen = true + isDropdownOpen = false } + function handleDropdownToggle(e: MouseEvent) { + e.stopPropagation() + isDropdownOpen = !isDropdownOpen + } + + function handleClickOutside(event: MouseEvent) { + const target = event.target as HTMLElement + if (!target.closest('.actions-dropdown')) { + isDropdownOpen = false + } + } + + function handleAuditStorage() { + window.location.href = '/admin/media/audit' + } + + $effect(() => { + if (isDropdownOpen) { + document.addEventListener('click', handleClickOutside) + return () => document.removeEventListener('click', handleClickOutside) + } + }) + // Multiselect functions function toggleMultiSelectMode() { isMultiSelectMode = !isMultiSelectMode + isDropdownOpen = false if (!isMultiSelectMode) { selectedMediaIds.clear() selectedMediaIds = new Set() @@ -318,15 +368,28 @@ {#snippet actions()} - - +
+ + + + {#if isDropdownOpen} + + + {isMultiSelectMode ? 'Exit Select' : 'Select Files'} + + + Audit Storage + + + {/if} +
{/snippet}
@@ -344,14 +407,21 @@ onchange={handleFilterChange} />