From 5e066093d80795d32d74b629314d5fe70c7e0f09 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Mon, 2 Jun 2025 02:23:50 -0700 Subject: [PATCH] styles --- src/assets/icons/chevron-down.svg | 3 + src/lib/components/admin/AdminHeader.svelte | 39 +++ src/lib/components/admin/AdminPage.svelte | 2 + src/lib/components/admin/PostDropdown.svelte | 107 ++++--- src/lib/components/admin/Select.svelte | 286 +++++++++++-------- src/routes/admin/albums/+page.svelte | 118 ++------ src/routes/admin/media/+page.svelte | 192 +++++++------ src/routes/admin/posts/+page.svelte | 190 ++++++------ src/routes/admin/projects/+page.svelte | 153 ++++++---- 9 files changed, 559 insertions(+), 531 deletions(-) create mode 100644 src/assets/icons/chevron-down.svg create mode 100644 src/lib/components/admin/AdminHeader.svelte diff --git a/src/assets/icons/chevron-down.svg b/src/assets/icons/chevron-down.svg new file mode 100644 index 0000000..ff0c1a0 --- /dev/null +++ b/src/assets/icons/chevron-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/lib/components/admin/AdminHeader.svelte b/src/lib/components/admin/AdminHeader.svelte new file mode 100644 index 0000000..dd45b72 --- /dev/null +++ b/src/lib/components/admin/AdminHeader.svelte @@ -0,0 +1,39 @@ + + +
+

{title}

+ {#if actions} +
+ {@render actions()} +
+ {/if} +
+ + \ No newline at end of file diff --git a/src/lib/components/admin/AdminPage.svelte b/src/lib/components/admin/AdminPage.svelte index 45374bc..55874b1 100644 --- a/src/lib/components/admin/AdminPage.svelte +++ b/src/lib/components/admin/AdminPage.svelte @@ -48,6 +48,8 @@ } .page-header { + box-sizing: border-box; + min-height: 110px; padding: $unit-4x; @include breakpoint('phone') { diff --git a/src/lib/components/admin/PostDropdown.svelte b/src/lib/components/admin/PostDropdown.svelte index 534d917..1f52ffb 100644 --- a/src/lib/components/admin/PostDropdown.svelte +++ b/src/lib/components/admin/PostDropdown.svelte @@ -2,6 +2,7 @@ import { goto } from '$app/navigation' import UniverseComposer from './UniverseComposer.svelte' import Button from './Button.svelte' + import ChevronDownIcon from '$icons/chevron-down.svg?raw' let isOpen = $state(false) let buttonRef: HTMLElement @@ -54,6 +55,7 @@ @@ -86,39 +82,49 @@ > {#snippet icon()} + {#if type.value === 'essay'} + + + + + + + {:else if type.value === 'post'} + + + + + + {/if} + {/snippet} {type.label} @@ -142,22 +148,11 @@ position: relative; } - // Button styles are now handled by the Button component - // Override primary button color to match original design - :global(.dropdown-container .btn-primary) { - background-color: $grey-10; - - &:hover:not(:disabled) { - background-color: $grey-20; - } - - &:active:not(:disabled) { - background-color: $grey-30; - } - } - .chevron { transition: transform 0.2s ease; + display: flex; + align-items: center; + justify-content: center; } .dropdown-menu { diff --git a/src/lib/components/admin/Select.svelte b/src/lib/components/admin/Select.svelte index 2d8cd33..1271547 100644 --- a/src/lib/components/admin/Select.svelte +++ b/src/lib/components/admin/Select.svelte @@ -1,162 +1,212 @@ -
- {#if label} - - {/if} - -
- -
- - - -
+
+ +
+ {@html ChevronDownIcon}
- - {#if error} -
{error}
- {/if} - - {#if helpText && !error} -
{helpText}
- {/if}
\ No newline at end of file + diff --git a/src/routes/admin/albums/+page.svelte b/src/routes/admin/albums/+page.svelte index 32eaaa2..54b66c7 100644 --- a/src/routes/admin/albums/+page.svelte +++ b/src/routes/admin/albums/+page.svelte @@ -2,8 +2,10 @@ import { goto } from '$app/navigation' import { onMount } from 'svelte' import AdminPage from '$lib/components/admin/AdminPage.svelte' + import AdminHeader from '$lib/components/admin/AdminHeader.svelte' import DataTable from '$lib/components/admin/DataTable.svelte' import Button from '$lib/components/admin/Button.svelte' + import Select from '$lib/components/admin/Select.svelte' import LoadingSpinner from '$lib/components/admin/LoadingSpinner.svelte' // State @@ -16,6 +18,13 @@ // Filter state let photographyFilter = $state('all') + // Filter options + const filterOptions = [ + { value: 'all', label: 'All albums' }, + { value: 'true', label: 'Photography albums' }, + { value: 'false', label: 'Regular albums' } + ] + const columns = [ { key: 'title', @@ -100,11 +109,10 @@ // Calculate album type counts const counts: Record = { all: albums.length, - photography: albums.filter(a => a.isPhotography).length, - regular: albums.filter(a => !a.isPhotography).length + photography: albums.filter((a) => a.isPhotography).length, + regular: albums.filter((a) => !a.isPhotography).length } albumTypeCounts = counts - } catch (err) { error = 'Failed to load albums' console.error(err) @@ -127,41 +135,24 @@ -
-

Albums

-
- -
-
+ + {#snippet actions()} + + {/snippet} + {#if error}
{error}
{:else} - -
-
- {albumTypeCounts.all || 0} - Total albums -
-
- {albumTypeCounts.photography || 0} - Photography albums -
-
- {albumTypeCounts.regular || 0} - Regular albums -
-
-
- + @@ -357,8 +337,19 @@ prefixIcon wrapperClass="search-input-wrapper" > - - + +
@@ -367,14 +358,14 @@ {#if isMultiSelectMode && media.length > 0}
- - - - {/if}
@@ -423,8 +416,8 @@
{#if isMultiSelectMode}
- toggleMediaSelection(item.id)} id="media-{item.id}" @@ -432,15 +425,19 @@
{/if} -
{#if !isMultiSelectMode} - - + + {/if}
@@ -648,25 +677,6 @@ flex-wrap: wrap; } - .media-stats { - .stat { - display: flex; - flex-direction: column; - gap: $unit-half; - - .stat-value { - font-size: 1.5rem; - font-weight: 700; - color: $grey-10; - } - - .stat-label { - font-size: 0.875rem; - color: $grey-40; - } - } - } - .filters { display: flex; gap: $unit-2x; @@ -758,7 +768,7 @@ .file-type { font-size: 0.875rem; color: $grey-40; - } + } } .media-info { @@ -842,7 +852,7 @@ border-radius: $unit; font-size: 0.75rem; color: $grey-40; - } + } } .media-details { @@ -905,7 +915,7 @@ border: 1px solid $grey-80; border-radius: 50px; font-size: 0.75rem; - color: $grey-30; + color: $grey-30; cursor: pointer; transition: all 0.2s ease; @@ -999,7 +1009,7 @@ left: $unit; z-index: 10; - input[type="checkbox"] { + input[type='checkbox'] { opacity: 0; position: absolute; pointer-events: none; diff --git a/src/routes/admin/posts/+page.svelte b/src/routes/admin/posts/+page.svelte index ece7cb6..31d3b42 100644 --- a/src/routes/admin/posts/+page.svelte +++ b/src/routes/admin/posts/+page.svelte @@ -2,8 +2,10 @@ import { onMount } from 'svelte' import { goto } from '$app/navigation' import AdminPage from '$lib/components/admin/AdminPage.svelte' + import AdminHeader from '$lib/components/admin/AdminHeader.svelte' import PostDropdown from '$lib/components/admin/PostDropdown.svelte' import LoadingSpinner from '$lib/components/admin/LoadingSpinner.svelte' + import Select from '$lib/components/admin/Select.svelte' interface Post { id: number @@ -28,10 +30,17 @@ let error = $state('') let total = $state(0) let postTypeCounts = $state>({}) - + // Filter state let selectedFilter = $state('all') + // Create filter options + const filterOptions = $derived([ + { value: 'all', label: 'All posts' }, + { value: 'post', label: 'Posts' }, + { value: 'essay', label: 'Essays' } + ]) + const postTypeIcons: Record = { post: '💭', essay: '📝', @@ -88,7 +97,7 @@ post: 0, essay: 0 } - + posts.forEach((post) => { // Normalize legacy types to simplified types if (post.postType === 'blog') { @@ -115,15 +124,13 @@ if (selectedFilter === 'all') { filteredPosts = posts } else if (selectedFilter === 'post') { - filteredPosts = posts.filter(post => + filteredPosts = posts.filter((post) => ['post', 'microblog', 'link', 'photo'].includes(post.postType) ) } else if (selectedFilter === 'essay') { - filteredPosts = posts.filter(post => - ['essay', 'blog'].includes(post.postType) - ) + filteredPosts = posts.filter((post) => ['essay', 'blog'].includes(post.postType)) } else { - filteredPosts = posts.filter(post => post.postType === selectedFilter) + filteredPosts = posts.filter((post) => post.postType === selectedFilter) } } @@ -144,7 +151,7 @@ // Try to extract text from content JSON if (post.content) { let textContent = '' - + if (typeof post.content === 'object' && post.content.content) { // BlockNote/TipTap format function extractText(node: any): string { @@ -166,7 +173,9 @@ // Fallback to link description for link posts if (post.linkDescription) { - return post.linkDescription.length > 150 ? post.linkDescription.substring(0, 150) + '...' : post.linkDescription + return post.linkDescription.length > 150 + ? post.linkDescription.substring(0, 150) + '...' + : post.linkDescription } // Default fallback @@ -186,8 +195,8 @@ } else if (diffDays < 7) { return `${diffDays} days ago` } else { - return date.toLocaleDateString('en-US', { - month: 'short', + return date.toLocaleDateString('en-US', { + month: 'short', day: 'numeric', year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined }) @@ -196,7 +205,7 @@ function getDisplayTitle(post: Post): string { if (post.title) return post.title - + // For posts without titles, create a meaningful display title if (post.linkUrl) { try { @@ -206,46 +215,38 @@ return 'Link post' } } - + const snippet = getPostSnippet(post) - if (snippet && snippet !== `${postTypeLabels[post.postType] || post.postType} without content`) { + if ( + snippet && + snippet !== `${postTypeLabels[post.postType] || post.postType} without content` + ) { return snippet.length > 50 ? snippet.substring(0, 50) + '...' : snippet } - + return `${postTypeLabels[post.postType] || post.postType}` } -
-

Universe

-
- + + {#snippet actions()} -
-
+ {/snippet} + {#if error}
{error}
{:else} - -
-
- {postTypeCounts.all || 0} - Total posts -
-
- {postTypeCounts.post || 0} - Posts -
-
- {postTypeCounts.essay || 0} - Essays -
+ +
+
+ + + {#if isLoading} +
+
+

Loading projects...

+
+ {:else if filteredProjects.length === 0} +
+

+ {#if selectedFilter === 'all'} + No projects found. Create your first project! + {:else} + No {selectedFilter} projects found. Try a different filter or create a new project. + {/if} +

+
+ {:else} +
+ {#each filteredProjects as project} + + {/each} +
+ {/if} {/if}