From 78ae1d075608ad03f75dadd3353a37282a241424 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 09:15:09 -0700 Subject: [PATCH 01/14] Update empty states --- src/lib/components/UniverseFeed.svelte | 29 ++++++++++++++++++----- src/routes/labs/+page.svelte | 32 ++++++++++++++++++-------- src/routes/photos/+page.svelte | 2 +- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/lib/components/UniverseFeed.svelte b/src/lib/components/UniverseFeed.svelte index 12873e7..ffcc5f5 100644 --- a/src/lib/components/UniverseFeed.svelte +++ b/src/lib/components/UniverseFeed.svelte @@ -16,8 +16,11 @@ {/if} {/each} {:else} -
-

No content found in the universe yet.

+
+
+

No posts yet

+

Posts will be added to Universe soon

+
{/if}
@@ -29,14 +32,28 @@ gap: $unit-3x; } - .empty-state { + .empty-container { + display: flex; + justify-content: center; + align-items: center; + min-height: 60vh; + } + + .empty-message { text-align: center; - padding: $unit-6x $unit-3x; - color: $grey-40; + max-width: 500px; + + h2 { + font-size: 1.5rem; + font-weight: 600; + margin: 0 0 $unit-2x; + color: $grey-10; + } p { margin: 0; - font-size: 1.125rem; + color: $grey-40; + line-height: 1.5; } } diff --git a/src/routes/labs/+page.svelte b/src/routes/labs/+page.svelte index 15b3406..ad79b89 100644 --- a/src/routes/labs/+page.svelte +++ b/src/routes/labs/+page.svelte @@ -10,14 +10,18 @@
{#if error} -
-

Unable to load projects

-

{error}

+
+
+

Unable to load projects

+

{error}

+
{:else if projects.length === 0} -
-

No projects yet

-

Labs projects will appear here once published.

+
+
+

No projects yet

+

Projects will be added to Labs soon

+
{:else}
@@ -49,10 +53,18 @@ } } + .error-container, + .empty-container { + display: flex; + justify-content: center; + align-items: center; + min-height: 60vh; + } + .error-message, .empty-message { text-align: center; - padding: $unit-6x $unit-3x; + max-width: 500px; h2 { font-size: 1.5rem; @@ -68,7 +80,9 @@ } } - .error-message h2 { - color: $red-60; + .error-message { + h2 { + color: $red-60; + } } diff --git a/src/routes/photos/+page.svelte b/src/routes/photos/+page.svelte index deeb722..0e2916f 100644 --- a/src/routes/photos/+page.svelte +++ b/src/routes/photos/+page.svelte @@ -20,7 +20,7 @@

No photos yet

-

Photography albums will appear here once published.

+

Photos and albums will be added soon

{:else} From 204cba0e6dabb65e2bcf4b34729e919146141e6c Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 09:21:39 -0700 Subject: [PATCH 02/14] Fix nesting errors --- src/lib/components/LabCard.svelte | 40 +++++++++++++++++++------------ src/lib/components/Mention.svelte | 22 ++++++++++++----- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/lib/components/LabCard.svelte b/src/lib/components/LabCard.svelte index 9acfe46..df43118 100644 --- a/src/lib/components/LabCard.svelte +++ b/src/lib/components/LabCard.svelte @@ -40,13 +40,16 @@ {#if isClickable} - window.location.href = projectUrl} + on:keydown={(e) => e.key === 'Enter' && (window.location.href = projectUrl)} + role="button" + tabindex="0" style:transform >
@@ -62,7 +65,7 @@ target="_blank" rel="noopener noreferrer" iconPosition="right" - onclick={(e) => e.stopPropagation()} + on:click={(e) => e.stopPropagation()} > Visit site Password Protected
{/if} -
+
{:else}
@@ -187,14 +190,12 @@ transform-style: preserve-3d; will-change: transform; - // Prevent overflow issues with 3D transforms - -webkit-mask-image: -webkit-radial-gradient(white, black); - mask-image: radial-gradient(white, black); + // Remove mask-image to allow shadows to render properly &:hover { box-shadow: - 0 10px 30px rgba(0, 0, 0, 0.12), - 0 2px 10px rgba(0, 0, 0, 0.08); + 0 10px 30px rgba(0, 0, 0, 0.1), + 0 1px 8px rgba(0, 0, 0, 0.06); .project-title { color: $red-60; @@ -203,6 +204,15 @@ &.clickable { cursor: pointer; + + &:focus { + outline: 2px solid $red-60; + outline-offset: 2px; + } + + &:focus:not(:focus-visible) { + outline: none; + } } @include breakpoint('phone') { diff --git a/src/lib/components/Mention.svelte b/src/lib/components/Mention.svelte index b10176f..62c5dd2 100644 --- a/src/lib/components/Mention.svelte +++ b/src/lib/components/Mention.svelte @@ -1,9 +1,19 @@ -
  • From 315a4ba102a8d2e1085f23a9ec02604bb36061f0 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 09:44:23 -0700 Subject: [PATCH 03/14] Mobile fixes for horizontal scrolling --- src/app.html | 2 +- src/assets/styles/globals.scss | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app.html b/src/app.html index 8013721..2f0b73f 100644 --- a/src/app.html +++ b/src/app.html @@ -3,7 +3,7 @@ - + diff --git a/src/assets/styles/globals.scss b/src/assets/styles/globals.scss index 3090706..8f0ace0 100644 --- a/src/assets/styles/globals.scss +++ b/src/assets/styles/globals.scss @@ -9,6 +9,14 @@ body { font-family: 'cstd', 'Helvetica Neue', Arial, sans-serif; font-weight: 400; line-height: 1.4; + overflow-x: hidden; + width: 100%; +} + +// Prevent horizontal scroll +html { + overflow-x: hidden; + width: 100%; } // Heading font weights From 7869d05e1efbc6fece485a7c2494953a0b5882e7 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 09:44:27 -0700 Subject: [PATCH 04/14] Remove log --- src/routes/api/lastfm/+server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/api/lastfm/+server.ts b/src/routes/api/lastfm/+server.ts index a290fd2..ca17a5f 100644 --- a/src/routes/api/lastfm/+server.ts +++ b/src/routes/api/lastfm/+server.ts @@ -21,7 +21,7 @@ export const GET: RequestHandler = async ({ url }) => { // const albums = await getWeeklyAlbumChart(client, USERNAME) const albums = await getRecentAlbums(client, USERNAME, ALBUM_LIMIT) - console.log(albums) + // console.log(albums) const enrichedAlbums = await Promise.all( albums.slice(0, ALBUM_LIMIT).map(async (album) => { try { From f2a6bf3f997cf96ca5cefb212aa69aa2189930e9 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 09:44:34 -0700 Subject: [PATCH 05/14] Add dropdown for mobile nav --- src/lib/components/Header.svelte | 45 ++++- src/lib/components/NavDropdown.svelte | 237 ++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 7 deletions(-) create mode 100644 src/lib/components/NavDropdown.svelte diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte index 5a93286..a26bc1f 100644 --- a/src/lib/components/Header.svelte +++ b/src/lib/components/Header.svelte @@ -1,6 +1,7 @@ + + + + From 205244584f81ef60435b6f68b2fbf7fa60ad6c51 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 10:22:08 -0700 Subject: [PATCH 06/14] Mobile layout adjustments --- src/lib/components/Header.svelte | 5 +- src/lib/components/Page.svelte | 9 +- src/lib/components/ProjectList.svelte | 5 + src/lib/components/UniverseFeed.svelte | 3 +- src/lib/components/admin/Button.svelte | 4 +- src/routes/about/+page.svelte | 95 +++++++++------- src/routes/labs/+page.svelte | 3 +- src/routes/labs/[slug]/+page.svelte | 143 +++++++++++++----------- src/routes/photos/+page.svelte | 9 +- src/routes/universe/+page.svelte | 12 +- src/routes/universe/[slug]/+page.svelte | 41 ++++--- src/routes/work/[slug]/+page.svelte | 3 +- 12 files changed, 185 insertions(+), 147 deletions(-) diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte index a26bc1f..258ce55 100644 --- a/src/lib/components/Header.svelte +++ b/src/lib/components/Header.svelte @@ -89,16 +89,13 @@ .header-content { display: flex; align-items: center; + justify-content: center; gap: $unit-3x; pointer-events: auto; width: 100%; - max-width: 900px; - margin: 0 auto; @include breakpoint('phone') { gap: $unit-2x; - max-width: none; - justify-content: center; } } diff --git a/src/lib/components/Page.svelte b/src/lib/components/Page.svelte index 508e566..6152cd9 100644 --- a/src/lib/components/Page.svelte +++ b/src/lib/components/Page.svelte @@ -1,8 +1,13 @@ -
    +
    diff --git a/src/lib/components/ProjectList.svelte b/src/lib/components/ProjectList.svelte index 54736ff..cb0872d 100644 --- a/src/lib/components/ProjectList.svelte +++ b/src/lib/components/ProjectList.svelte @@ -56,6 +56,11 @@ justify-content: center; width: 100%; + @include breakpoint('phone') { + box-sizing: border-box; + padding: 0 $unit-2x; + } + ul { display: flex; flex-direction: column; diff --git a/src/lib/components/UniverseFeed.svelte b/src/lib/components/UniverseFeed.svelte index ffcc5f5..4aae7c3 100644 --- a/src/lib/components/UniverseFeed.svelte +++ b/src/lib/components/UniverseFeed.svelte @@ -29,7 +29,8 @@ .universe-feed { display: flex; flex-direction: column; - gap: $unit-3x; + gap: $unit-2x; + padding: 0 $unit-2x; } .empty-container { diff --git a/src/lib/components/admin/Button.svelte b/src/lib/components/admin/Button.svelte index a6d7ce1..55f9aaf 100644 --- a/src/lib/components/admin/Button.svelte +++ b/src/lib/components/admin/Button.svelte @@ -170,7 +170,7 @@ align-items: center; justify-content: center; gap: $unit; - font-weight: 500; + font-weight: 400; border: none; cursor: pointer; transition: all 0.15s ease; @@ -211,7 +211,7 @@ } .btn-medium { - padding: $unit $unit-2x; + padding: ($unit * 1.5) $unit-2x; font-size: 14px; border-radius: 24px; min-height: 36px; diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte index 7f77695..7ca11a5 100644 --- a/src/routes/about/+page.svelte +++ b/src/routes/about/+page.svelte @@ -12,47 +12,48 @@ $: ({ albums, games, error } = data) - - -

    A little about me

    -
    +
    + + +

    A little about me

    +
    -
    -

    - Hello! My name is Justin Edmund. I'm a software designer and developer living in San - Francisco. -

    -

    - Right now, I'm spending my free time building a hobby journaling app called Maitsu. I've spent time at several companies over the last 11 years, but you might know me from - Pinterest, where I was the first - design hire. -

    -

    - I was born and raised in New York City and spend a lot of time in Tokyo. I graduated from Carnegie Mellon University in 2011 with a Bachelors of Arts in Communication Design. -

    -
    -
    - - -

    Notable mentions

    -
    +
    +

    + Hello! My name is Justin Edmund. I'm a software designer and developer living in + San Francisco. +

    +

    + Right now, I'm spending my free time building a hobby journaling app called Maitsu. I've spent time at several companies over the last 11 years, but you might know me from + Pinterest, where I was the first + design hire. +

    +

    + I was born and raised in New York City and spend a lot of time in Tokyo. I graduated from Carnegie Mellon University in 2011 with a Bachelors of Arts in Communication Design. +

    +
    +
    + + +

    Notable mentions

    +
    - -
    - - -

    Now playing

    -
    + +
    + + +

    Now playing

    +
    - + - -
    + +
    diff --git a/src/routes/photos/[albumSlug]/[photoId]/+page.svelte b/src/routes/photos/[albumSlug]/[photoId]/+page.svelte index ad0d194..f4e334b 100644 --- a/src/routes/photos/[albumSlug]/[photoId]/+page.svelte +++ b/src/routes/photos/[albumSlug]/[photoId]/+page.svelte @@ -445,7 +445,7 @@ } .photo-caption { - font-size: 1.125rem; + font-size: 1rem; color: $grey-20; margin: 0 0 $unit-3x; line-height: 1.5; diff --git a/src/routes/photos/[slug]/+page.svelte b/src/routes/photos/[slug]/+page.svelte index 4f5b75b..854bb6e 100644 --- a/src/routes/photos/[slug]/+page.svelte +++ b/src/routes/photos/[slug]/+page.svelte @@ -158,7 +158,7 @@ } .album-description { - font-size: 1.125rem; + font-size: 1rem; color: $grey-30; margin: 0 0 $unit-4x; line-height: 1.5; diff --git a/src/routes/universe/+page.svelte b/src/routes/universe/+page.svelte index 08aaae2..1f4c44c 100644 --- a/src/routes/universe/+page.svelte +++ b/src/routes/universe/+page.svelte @@ -37,7 +37,7 @@ p { margin: 0; - font-size: 1.125rem; + font-size: 1rem; } } From 90735d2c831072de0cda8e2a8c1027a9460d4279 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 12:06:02 -0700 Subject: [PATCH 08/14] Spacing and mobile design fixes --- src/lib/components/DynamicPostContent.svelte | 8 +- src/lib/components/Page.svelte | 18 +++-- .../components/ProjectHeaderContent.svelte | 8 +- src/routes/about/+page.svelte | 18 +++-- src/routes/labs/[slug]/+page.svelte | 76 ++++++++++--------- src/routes/work/[slug]/+page.svelte | 8 +- 6 files changed, 79 insertions(+), 57 deletions(-) diff --git a/src/lib/components/DynamicPostContent.svelte b/src/lib/components/DynamicPostContent.svelte index 8599805..3fd448f 100644 --- a/src/lib/components/DynamicPostContent.svelte +++ b/src/lib/components/DynamicPostContent.svelte @@ -96,10 +96,10 @@ max-width: 784px; gap: $unit-3x; margin: 0 auto; - padding: 0 $unit-3x; @include breakpoint('phone') { - padding: 0 $unit-2x; + gap: $unit-2x; + padding: $unit-half 0; } // Post type styles @@ -319,10 +319,6 @@ } } - .post-footer { - padding-bottom: $unit-2x; - } - .back-button { color: $red-60; background-color: transparent; diff --git a/src/lib/components/Page.svelte b/src/lib/components/Page.svelte index 6152cd9..e12f243 100644 --- a/src/lib/components/Page.svelte +++ b/src/lib/components/Page.svelte @@ -1,18 +1,26 @@
    -
    - -
    + {#if header} +
    + {@render header()} +
    + {/if} - + {#if children} + {@render children()} + {/if}
    diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte index a6951aa..06c8c6d 100644 --- a/src/routes/about/+page.svelte +++ b/src/routes/about/+page.svelte @@ -7,16 +7,18 @@ import type { PageData } from './$types' - export let data: PageData + let { data } = $props<{ data: PageData }>() - $: ({ albums, games, error } = data) + let albums = $derived(data.albums) + let games = $derived(data.games) + let error = $derived(data.error)
    - + {#snippet header()}

    A little about me

    -
    + {/snippet}

    @@ -40,16 +42,16 @@

    - + {#snippet header()}

    Notable mentions

    -
    + {/snippet}
    - + {#snippet header()}

    Now playing

    -
    + {/snippet} diff --git a/src/routes/labs/[slug]/+page.svelte b/src/routes/labs/[slug]/+page.svelte index a5813b0..490050d 100644 --- a/src/routes/labs/[slug]/+page.svelte +++ b/src/routes/labs/[slug]/+page.svelte @@ -14,9 +14,11 @@
    {#if error} -
    -

    Error

    -
    + {#snippet header()} +
    +

    Error

    +
    + {/snippet}

    {error}

    ← Back to labs @@ -28,9 +30,11 @@ {:else if project.status === 'list-only'} -
    -

    Project Not Available

    -
    + {#snippet header()} +
    +

    Project Not Available

    +
    + {/snippet}

    This project is not yet available for viewing. Please check back later.

    ← Back to labs @@ -38,46 +42,50 @@ {:else if project.status === 'password-protected'} + {#snippet header()} +
    + {#if project.logoUrl} + + {/if} +

    {project.title}

    + {#if project.subtitle} +

    {project.subtitle}

    + {/if} +
    + {/snippet} {#snippet children()} -
    - {#if project.logoUrl} - - {/if} -

    {project.title}

    - {#if project.subtitle} -

    {project.subtitle}

    - {/if} -
    {/snippet}
    {:else} -
    - {#if project.logoUrl} - - {/if} -

    {project.title}

    - {#if project.subtitle} -

    {project.subtitle}

    - {/if} -
    + {#snippet header()} +
    + {#if project.logoUrl} + + {/if} +

    {project.title}

    + {#if project.subtitle} +

    {project.subtitle}

    + {/if} +
    + {/snippet}
    {/if} diff --git a/src/routes/work/[slug]/+page.svelte b/src/routes/work/[slug]/+page.svelte index 700596c..ee54671 100644 --- a/src/routes/work/[slug]/+page.svelte +++ b/src/routes/work/[slug]/+page.svelte @@ -95,9 +95,11 @@ {/if}
    -
    - -
    + {#snippet header()} +
    + +
    + {/snippet} {#if project.status === 'password-protected'} Date: Tue, 10 Jun 2025 19:09:23 -0700 Subject: [PATCH 09/14] Button unification --- src/lib/components/LabCard.svelte | 4 +- .../components/ProjectHeaderContent.svelte | 4 +- src/routes/labs/[slug]/+page.svelte | 168 ++++++------------ 3 files changed, 59 insertions(+), 117 deletions(-) diff --git a/src/lib/components/LabCard.svelte b/src/lib/components/LabCard.svelte index a29863c..f92c189 100644 --- a/src/lib/components/LabCard.svelte +++ b/src/lib/components/LabCard.svelte @@ -67,7 +67,7 @@ iconPosition="right" on:click={(e) => e.stopPropagation()} > - Visit site + Visit Visit @@ -75,7 +75,7 @@ align-items: center; gap: $unit; padding: ($unit * 1.5) $unit-2x; - background: var(--button-bg, $grey-10); + background: var(--button-bg, $red-60); color: var(--button-color, white); text-decoration: none; border-radius: 50px; diff --git a/src/routes/labs/[slug]/+page.svelte b/src/routes/labs/[slug]/+page.svelte index 490050d..a499a7a 100644 --- a/src/routes/labs/[slug]/+page.svelte +++ b/src/routes/labs/[slug]/+page.svelte @@ -1,6 +1,7 @@ -
    - {#if error} - - {#snippet header()} -
    -

    Error

    -
    - {/snippet} -
    -

    {error}

    - ← Back to labs +{#if error} + + {#snippet header()} +
    +

    Error

    -
    - {:else if !project} - -
    Loading project...
    -
    - {:else if project.status === 'list-only'} - - {#snippet header()} -
    -

    Project Not Available

    -
    - {/snippet} -
    -

    This project is not yet available for viewing. Please check back later.

    - ← Back to labs + {/snippet} +
    +

    {error}

    + ← Back to labs +
    + +{:else if !project} + +
    Loading project...
    +
    +{:else if project.status === 'list-only'} + + {#snippet header()} +
    +

    Project Not Available

    -
    - {:else if project.status === 'password-protected'} + {/snippet} +
    +

    This project is not yet available for viewing. Please check back later.

    + ← Back to labs +
    + +{:else if project.status === 'password-protected' || project.status === 'published'} +
    {#snippet header()}
    - {#if project.logoUrl} - - {/if} -

    {project.title}

    - {#if project.subtitle} -

    {project.subtitle}

    - {/if} +
    {/snippet} - - {#snippet children()} - - {/snippet} - + {#if project.status === 'password-protected'} + + {#snippet children()} + + {/snippet} + + {:else} + + {/if}
    - {:else} - - {#snippet header()} -
    - {#if project.logoUrl} - - {/if} -

    {project.title}

    - {#if project.subtitle} -

    {project.subtitle}

    - {/if} -
    - {/snippet} - -
    - {/if} -
    +
    +{/if} From 5edc7eb33b3f8a718709e0a7f34419a660a6a434 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 19:47:14 -0700 Subject: [PATCH 10/14] Fix filtering for projects --- src/routes/api/projects/+server.ts | 8 ++- src/routes/api/projects/[id]/+server.ts | 83 ++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/routes/api/projects/+server.ts b/src/routes/api/projects/+server.ts index cdf037b..b2d3f9e 100644 --- a/src/routes/api/projects/+server.ts +++ b/src/routes/api/projects/+server.ts @@ -22,6 +22,9 @@ export const GET: RequestHandler = async (event) => { const { page, limit } = getPaginationParams(event.url) const skip = (page - 1) * limit + // Check if admin is authenticated + const isAdmin = checkAdminAuth(event) + // Get filter parameters const status = event.url.searchParams.get('status') const projectType = event.url.searchParams.get('projectType') @@ -34,8 +37,8 @@ export const GET: RequestHandler = async (event) => { if (status) { where.status = status - } else { - // Default behavior: determine which statuses to include + } else if (!isAdmin) { + // For non-admin users: only show published projects by default const allowedStatuses = ['published'] if (includeListOnly) { @@ -48,6 +51,7 @@ export const GET: RequestHandler = async (event) => { where.status = { in: allowedStatuses } } + // For admin users: show all projects (no status filter applied) if (projectType) { where.projectType = projectType diff --git a/src/routes/api/projects/[id]/+server.ts b/src/routes/api/projects/[id]/+server.ts index 702c86a..a70191c 100644 --- a/src/routes/api/projects/[id]/+server.ts +++ b/src/routes/api/projects/[id]/+server.ts @@ -151,7 +151,7 @@ export const PUT: RequestHandler = async (event) => { }) if (usageReferences.length > 0) { - await trackMediaUsage(usageReferences) + await updateMediaUsage(usageReferences) } } catch (error) { logger.warn('Failed to update media usage tracking for project', { projectId: id, error }) @@ -166,6 +166,87 @@ export const PUT: RequestHandler = async (event) => { } } +// PATCH /api/projects/[id] - Partially update a project +export const PATCH: RequestHandler = async (event) => { + // Check authentication + if (!checkAdminAuth(event)) { + return errorResponse('Unauthorized', 401) + } + + const id = parseInt(event.params.id) + if (isNaN(id)) { + return errorResponse('Invalid project ID', 400) + } + + try { + const body = await parseRequestBody(event.request) + if (!body) { + return errorResponse('Invalid request body', 400) + } + + // Check if project exists + const existing = await prisma.project.findUnique({ + where: { id } + }) + + if (!existing) { + return errorResponse('Project not found', 404) + } + + // Build update data object with only provided fields + const updateData: any = {} + + // Handle status update specially + if (body.status !== undefined) { + updateData.status = body.status + // Set publishedAt if changing to published for the first time + if (body.status === 'published' && !existing.publishedAt) { + updateData.publishedAt = new Date() + } + // Clear publishedAt if changing to draft + else if (body.status === 'draft') { + updateData.publishedAt = null + } + } + + // Add other fields if provided + if (body.title !== undefined) updateData.title = body.title + if (body.subtitle !== undefined) updateData.subtitle = body.subtitle + if (body.description !== undefined) updateData.description = body.description + if (body.year !== undefined) updateData.year = body.year + if (body.client !== undefined) updateData.client = body.client + if (body.role !== undefined) updateData.role = body.role + if (body.featuredImage !== undefined) updateData.featuredImage = body.featuredImage + if (body.logoUrl !== undefined) updateData.logoUrl = body.logoUrl + if (body.gallery !== undefined) updateData.gallery = body.gallery + if (body.externalUrl !== undefined) updateData.externalUrl = body.externalUrl + if (body.caseStudyContent !== undefined) updateData.caseStudyContent = body.caseStudyContent + if (body.backgroundColor !== undefined) updateData.backgroundColor = body.backgroundColor + if (body.highlightColor !== undefined) updateData.highlightColor = body.highlightColor + if (body.projectType !== undefined) updateData.projectType = body.projectType + if (body.displayOrder !== undefined) updateData.displayOrder = body.displayOrder + if (body.password !== undefined) updateData.password = body.password + + // Handle slug update if provided + if (body.slug && body.slug !== existing.slug) { + updateData.slug = await ensureUniqueSlug(body.slug, 'project', id) + } + + // Update project + const project = await prisma.project.update({ + where: { id }, + data: updateData + }) + + logger.info('Project partially updated', { id: project.id, fields: Object.keys(updateData) }) + + return jsonResponse(project) + } catch (error) { + logger.error('Failed to update project', error as Error) + return errorResponse('Failed to update project', 500) + } +} + // DELETE /api/projects/[id] - Delete a project export const DELETE: RequestHandler = async (event) => { // Check authentication From b3c9529e3f773ec0395f4ef877a20500bb36d750 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 10 Jun 2025 20:48:02 -0700 Subject: [PATCH 11/14] Fix image handling in forms --- .../components/admin/FormFieldWrapper.svelte | 7 +- src/lib/components/admin/ImageUploader.svelte | 3 + .../admin/ProjectBrandingForm.svelte | 99 ++++++++++--- src/lib/components/admin/ProjectForm.svelte | 16 +-- .../admin/ProjectMetadataForm.svelte | 134 +++++++++++++++--- src/lib/components/admin/Select.svelte | 2 + src/lib/components/admin/SelectField.svelte | 60 ++++++++ src/lib/types/project.ts | 3 - src/routes/api/albums/[id]/+server.ts | 10 +- src/routes/api/media/[id]/+server.ts | 6 +- src/routes/api/projects/[id]/+server.ts | 34 ++--- 11 files changed, 293 insertions(+), 81 deletions(-) create mode 100644 src/lib/components/admin/SelectField.svelte diff --git a/src/lib/components/admin/FormFieldWrapper.svelte b/src/lib/components/admin/FormFieldWrapper.svelte index 81f9313..3084d61 100644 --- a/src/lib/components/admin/FormFieldWrapper.svelte +++ b/src/lib/components/admin/FormFieldWrapper.svelte @@ -18,12 +18,12 @@ {/if} + {@render children?.()} + {#if helpText}

    {helpText}

    {/if} - {@render children?.()} - {#if error}

    {error}

    {/if} @@ -39,7 +39,8 @@ &.has-error { :global(input), - :global(textarea) { + :global(textarea), + :global(select) { border-color: #c33; } } diff --git a/src/lib/components/admin/ImageUploader.svelte b/src/lib/components/admin/ImageUploader.svelte index 17db29d..be7ea2d 100644 --- a/src/lib/components/admin/ImageUploader.svelte +++ b/src/lib/components/admin/ImageUploader.svelte @@ -11,6 +11,7 @@ label: string value?: Media | null onUpload: (media: Media) => void + onRemove?: () => void aspectRatio?: string // e.g., "16:9", "1:1" required?: boolean error?: string @@ -26,6 +27,7 @@ label, value = $bindable(), onUpload, + onRemove, aspectRatio, required = false, error, @@ -182,6 +184,7 @@ altTextValue = '' descriptionValue = '' uploadError = null + onRemove?.() } // Update alt text on server diff --git a/src/lib/components/admin/ProjectBrandingForm.svelte b/src/lib/components/admin/ProjectBrandingForm.svelte index dbab34b..de52b1a 100644 --- a/src/lib/components/admin/ProjectBrandingForm.svelte +++ b/src/lib/components/admin/ProjectBrandingForm.svelte @@ -1,21 +1,26 @@

    Branding

    - + {#if !showLogoSection && (!formData.logoUrl || formData.logoUrl.trim() === '')} + + {:else} +
    +
    +

    Project Logo

    +
    + +
    + {/if}
    diff --git a/src/lib/components/admin/ProjectForm.svelte b/src/lib/components/admin/ProjectForm.svelte index b9c14ce..a04cd51 100644 --- a/src/lib/components/admin/ProjectForm.svelte +++ b/src/lib/components/admin/ProjectForm.svelte @@ -7,7 +7,6 @@ import Editor from './Editor.svelte' import ProjectMetadataForm from './ProjectMetadataForm.svelte' import ProjectBrandingForm from './ProjectBrandingForm.svelte' - import ProjectGalleryForm from './ProjectGalleryForm.svelte' import ProjectStylingForm from './ProjectStylingForm.svelte' import Button from './Button.svelte' import StatusDropdown from './StatusDropdown.svelte' @@ -60,11 +59,10 @@ role: data.role || '', projectType: data.projectType || 'work', externalUrl: data.externalUrl || '', - featuredImage: data.featuredImage || null, + featuredImage: data.featuredImage && data.featuredImage.trim() !== '' ? data.featuredImage : null, backgroundColor: data.backgroundColor || '', highlightColor: data.highlightColor || '', - logoUrl: data.logoUrl || '', - gallery: data.gallery || null, + logoUrl: data.logoUrl && data.logoUrl.trim() !== '' ? data.logoUrl : '', status: data.status || 'draft', password: data.password || '', caseStudyContent: data.caseStudyContent || { @@ -142,9 +140,8 @@ role: formData.role, projectType: formData.projectType, externalUrl: formData.externalUrl, - featuredImage: formData.featuredImage, - logoUrl: formData.logoUrl, - gallery: formData.gallery && formData.gallery.length > 0 ? formData.gallery : null, + featuredImage: formData.featuredImage && formData.featuredImage !== '' ? formData.featuredImage : null, + logoUrl: formData.logoUrl && formData.logoUrl !== '' ? formData.logoUrl : null, backgroundColor: formData.backgroundColor, highlightColor: formData.highlightColor, status: formData.status, @@ -266,9 +263,8 @@ handleSave() }} > - - - + +
    diff --git a/src/lib/components/admin/ProjectMetadataForm.svelte b/src/lib/components/admin/ProjectMetadataForm.svelte index d8fce21..6f18c74 100644 --- a/src/lib/components/admin/ProjectMetadataForm.svelte +++ b/src/lib/components/admin/ProjectMetadataForm.svelte @@ -1,19 +1,71 @@
    @@ -34,7 +86,7 @@ placeholder="Short description for project cards" /> - + {#if !showFeaturedImage} + + {:else if showFeaturedImage} +
    +
    +

    Featured Image

    +
    + +
    + {/if} {#if formData.status === 'password-protected'} diff --git a/src/lib/components/admin/Select.svelte b/src/lib/components/admin/Select.svelte index 345b288..8721db3 100644 --- a/src/lib/components/admin/Select.svelte +++ b/src/lib/components/admin/Select.svelte @@ -49,6 +49,7 @@ .select-wrapper { position: relative; display: inline-block; + width: 100%; } .select { @@ -59,6 +60,7 @@ transition: all 0.2s ease; appearance: none; padding-right: 36px; + width: 100%; &:focus { outline: none; diff --git a/src/lib/components/admin/SelectField.svelte b/src/lib/components/admin/SelectField.svelte new file mode 100644 index 0000000..cd831b5 --- /dev/null +++ b/src/lib/components/admin/SelectField.svelte @@ -0,0 +1,60 @@ + + + + {#snippet children()} +
    - + diff --git a/src/lib/components/admin/Input.svelte b/src/lib/components/admin/Input.svelte index 5fb6978..3c30420 100644 --- a/src/lib/components/admin/Input.svelte +++ b/src/lib/components/admin/Input.svelte @@ -1,8 +1,7 @@
    @@ -161,32 +132,17 @@ > {/if} - {#if type === 'textarea' && isTextarea(restProps)} -