From 9ba787cd8b90e7d74ea5195741b71dae22755672 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Mon, 2 Jun 2025 17:00:52 -0700 Subject: [PATCH] Add dropdown to change object publish state and fix z-index --- src/lib/components/Lightbox.svelte | 2 +- src/lib/components/PhotoLightbox.svelte | 2 +- .../admin/AdminSegmentedController.svelte | 2 +- src/lib/components/admin/AlbumListItem.svelte | 2 +- src/lib/components/admin/DropdownMenu.svelte | 2 +- .../admin/DropdownMenuContainer.svelte | 2 +- .../admin/GenericMetadataPopover.svelte | 2 +- .../components/admin/MetadataPopover.svelte | 2 +- src/lib/components/admin/Modal.svelte | 2 +- src/lib/components/admin/PostDropdown.svelte | 2 +- src/lib/components/admin/ProjectForm.svelte | 42 ++-- src/lib/server/cloudinary.ts | 31 ++- .../admin/albums/[id]/edit/+page.svelte | 25 ++- src/routes/admin/posts/[id]/edit/+page.svelte | 205 ++++++++++-------- src/routes/admin/posts/new/+page.svelte | 2 +- 15 files changed, 180 insertions(+), 145 deletions(-) diff --git a/src/lib/components/Lightbox.svelte b/src/lib/components/Lightbox.svelte index 5aa30e2..3564561 100644 --- a/src/lib/components/Lightbox.svelte +++ b/src/lib/components/Lightbox.svelte @@ -131,7 +131,7 @@ position: fixed; inset: 0; background: rgba(0, 0, 0, 0.9); - z-index: 1000; + z-index: 1400; display: flex; align-items: center; justify-content: center; diff --git a/src/lib/components/PhotoLightbox.svelte b/src/lib/components/PhotoLightbox.svelte index a7550f8..2614f43 100644 --- a/src/lib/components/PhotoLightbox.svelte +++ b/src/lib/components/PhotoLightbox.svelte @@ -173,7 +173,7 @@ display: flex; align-items: center; justify-content: center; - z-index: 1000; + z-index: 1400; padding: $unit-2x; @include breakpoint('phone') { diff --git a/src/lib/components/admin/AdminSegmentedController.svelte b/src/lib/components/admin/AdminSegmentedController.svelte index 3cd680b..1eb962b 100644 --- a/src/lib/components/admin/AdminSegmentedController.svelte +++ b/src/lib/components/admin/AdminSegmentedController.svelte @@ -237,7 +237,7 @@ border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); min-width: 150px; - z-index: 1000; + z-index: 1050; overflow: hidden; animation: slideDown 0.2s ease; } diff --git a/src/lib/components/admin/AlbumListItem.svelte b/src/lib/components/admin/AlbumListItem.svelte index 23afafd..09f8e9a 100644 --- a/src/lib/components/admin/AlbumListItem.svelte +++ b/src/lib/components/admin/AlbumListItem.svelte @@ -272,7 +272,7 @@ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); overflow: hidden; min-width: 180px; - z-index: 10; + z-index: 1050; } .dropdown-item { diff --git a/src/lib/components/admin/DropdownMenu.svelte b/src/lib/components/admin/DropdownMenu.svelte index 55c6a51..833612d 100644 --- a/src/lib/components/admin/DropdownMenu.svelte +++ b/src/lib/components/admin/DropdownMenu.svelte @@ -97,7 +97,7 @@ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); overflow: hidden; min-width: 180px; - z-index: 1000; + z-index: 1050; } .dropdown-item { diff --git a/src/lib/components/admin/DropdownMenuContainer.svelte b/src/lib/components/admin/DropdownMenuContainer.svelte index 91559cc..ff63d57 100644 --- a/src/lib/components/admin/DropdownMenuContainer.svelte +++ b/src/lib/components/admin/DropdownMenuContainer.svelte @@ -23,6 +23,6 @@ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); overflow: hidden; min-width: 180px; - z-index: 10; + z-index: 1050; } diff --git a/src/lib/components/admin/GenericMetadataPopover.svelte b/src/lib/components/admin/GenericMetadataPopover.svelte index 224eb68..f6bd96e 100644 --- a/src/lib/components/admin/GenericMetadataPopover.svelte +++ b/src/lib/components/admin/GenericMetadataPopover.svelte @@ -106,7 +106,7 @@ popoverElement.style.position = 'fixed' popoverElement.style.top = `${top}px` popoverElement.style.left = `${left}px` - popoverElement.style.zIndex = '1000' + popoverElement.style.zIndex = '1200' } function handleFieldUpdate(key: string, value: any) { diff --git a/src/lib/components/admin/MetadataPopover.svelte b/src/lib/components/admin/MetadataPopover.svelte index fe4970a..d5da20b 100644 --- a/src/lib/components/admin/MetadataPopover.svelte +++ b/src/lib/components/admin/MetadataPopover.svelte @@ -90,7 +90,7 @@ popoverElement.style.position = 'fixed' popoverElement.style.top = `${top}px` popoverElement.style.left = `${left}px` - popoverElement.style.zIndex = '1000' + popoverElement.style.zIndex = '1200' } onMount(() => { diff --git a/src/lib/components/admin/Modal.svelte b/src/lib/components/admin/Modal.svelte index e0f9fd7..23bfeee 100644 --- a/src/lib/components/admin/Modal.svelte +++ b/src/lib/components/admin/Modal.svelte @@ -89,7 +89,7 @@ display: flex; justify-content: center; align-items: center; - z-index: 1000; + z-index: 1400; padding: $unit-2x; } diff --git a/src/lib/components/admin/PostDropdown.svelte b/src/lib/components/admin/PostDropdown.svelte index e801de3..f10ab6f 100644 --- a/src/lib/components/admin/PostDropdown.svelte +++ b/src/lib/components/admin/PostDropdown.svelte @@ -146,7 +146,7 @@ border-radius: $unit-2x; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); min-width: 140px; - z-index: 100; + z-index: 1050; overflow: hidden; margin: 0; padding: 0; diff --git a/src/lib/components/admin/ProjectForm.svelte b/src/lib/components/admin/ProjectForm.svelte index 4fa1bd5..b9c14ce 100644 --- a/src/lib/components/admin/ProjectForm.svelte +++ b/src/lib/components/admin/ProjectForm.svelte @@ -220,28 +220,26 @@
{#if !isLoading} - {#if formData.status === 'published'} - - {:else} - - {/if} + {/if}
diff --git a/src/lib/server/cloudinary.ts b/src/lib/server/cloudinary.ts index 0e90a2e..53c24d9 100644 --- a/src/lib/server/cloudinary.ts +++ b/src/lib/server/cloudinary.ts @@ -77,8 +77,11 @@ export async function uploadFile( customOptions?: any ): Promise { try { + // TEMPORARY: Force Cloudinary usage for testing + const FORCE_CLOUDINARY_IN_DEV = true; // Toggle this to test + // Use local storage in development or when Cloudinary is not configured - if (dev || !isCloudinaryConfigured()) { + if ((dev && !FORCE_CLOUDINARY_IN_DEV) || !isCloudinaryConfigured()) { logger.info('Using local storage for file upload') const localResult = await uploadFileLocally(file, type) @@ -123,14 +126,13 @@ export async function uploadFile( } // Log upload attempt for debugging - if (isSvg) { - logger.info('Attempting SVG upload with options:', { - filename: file.name, - mimeType: file.type, - size: file.size, - uploadOptions - }) - } + logger.info('Attempting file upload:', { + filename: file.name, + mimeType: file.type, + size: file.size, + isSvg, + uploadOptions + }) // Upload to Cloudinary const result = await new Promise((resolve, reject) => { @@ -168,6 +170,17 @@ export async function uploadFile( } catch (error) { logger.error('Cloudinary upload failed', error as Error) logger.mediaUpload(file.name, file.size, file.type, false) + + // Enhanced error logging + if (error instanceof Error) { + logger.error('Upload error details:', { + filename: file.name, + mimeType: file.type, + size: file.size, + errorMessage: error.message, + errorStack: error.stack + }) + } return { success: false, diff --git a/src/routes/admin/albums/[id]/edit/+page.svelte b/src/routes/admin/albums/[id]/edit/+page.svelte index dd78a55..e63b98e 100644 --- a/src/routes/admin/albums/[id]/edit/+page.svelte +++ b/src/routes/admin/albums/[id]/edit/+page.svelte @@ -10,7 +10,7 @@ import MediaLibraryModal from '$lib/components/admin/MediaLibraryModal.svelte' import MediaDetailsModal from '$lib/components/admin/MediaDetailsModal.svelte' import GalleryUploader from '$lib/components/admin/GalleryUploader.svelte' - import SaveActionsGroup from '$lib/components/admin/SaveActionsGroup.svelte' + import StatusDropdown from '$lib/components/admin/StatusDropdown.svelte' import AlbumMetadataPopover from '$lib/components/admin/AlbumMetadataPopover.svelte' // Form state @@ -93,7 +93,7 @@ } } - async function handleSave(publishStatus?: 'draft' | 'published') { + async function handleSave(newStatus?: string) { if (!title.trim()) { error = 'Title is required' return @@ -122,7 +122,7 @@ location: location.trim() || null, isPhotography, showInUniverse, - status: publishStatus || status + status: newStatus || status } const response = await fetch(`/api/albums/${album.id}`, { @@ -142,8 +142,8 @@ const updatedAlbum = await response.json() album = updatedAlbum - if (publishStatus) { - status = publishStatus + if (newStatus) { + status = newStatus } } catch (err) { error = err instanceof Error ? err.message : 'Failed to update album' @@ -591,12 +591,19 @@ /> {/if} - {/if} diff --git a/src/routes/admin/posts/[id]/edit/+page.svelte b/src/routes/admin/posts/[id]/edit/+page.svelte index efdd953..7432f6c 100644 --- a/src/routes/admin/posts/[id]/edit/+page.svelte +++ b/src/routes/admin/posts/[id]/edit/+page.svelte @@ -8,7 +8,7 @@ import PostMetadataPopover from '$lib/components/admin/PostMetadataPopover.svelte' import DeleteConfirmationModal from '$lib/components/admin/DeleteConfirmationModal.svelte' import Button from '$lib/components/admin/Button.svelte' - import SaveActionsGroup from '$lib/components/admin/SaveActionsGroup.svelte' + import StatusDropdown from '$lib/components/admin/StatusDropdown.svelte' import type { JSONContent } from '@tiptap/core' let post = $state(null) @@ -48,49 +48,55 @@ type: 'paragraph', content: block.content ? [{ type: 'text', text: block.content }] : [] } - + case 'heading': return { type: 'heading', attrs: { level: block.level || 1 }, content: block.content ? [{ type: 'text', text: block.content }] : [] } - + case 'bulletList': case 'ul': return { type: 'bulletList', content: (block.content || []).map((item: any) => ({ type: 'listItem', - content: [{ - type: 'paragraph', - content: [{ type: 'text', text: item.content || item }] - }] + content: [ + { + type: 'paragraph', + content: [{ type: 'text', text: item.content || item }] + } + ] })) } - + case 'orderedList': case 'ol': return { type: 'orderedList', content: (block.content || []).map((item: any) => ({ type: 'listItem', - content: [{ - type: 'paragraph', - content: [{ type: 'text', text: item.content || item }] - }] + content: [ + { + type: 'paragraph', + content: [{ type: 'text', text: item.content || item }] + } + ] })) } - + case 'blockquote': return { type: 'blockquote', - content: [{ - type: 'paragraph', - content: [{ type: 'text', text: block.content || '' }] - }] + content: [ + { + type: 'paragraph', + content: [{ type: 'text', text: block.content || '' }] + } + ] } - + case 'codeBlock': case 'code': return { @@ -98,7 +104,7 @@ attrs: { language: block.language || '' }, content: [{ type: 'text', text: block.content || '' }] } - + case 'image': return { type: 'image', @@ -108,11 +114,11 @@ title: block.caption || '' } } - + case 'hr': case 'horizontalRule': return { type: 'horizontalRule' } - + default: // Default to paragraph for unknown types return { @@ -134,66 +140,74 @@ return { blocks: [] } } - const blocks = tiptapContent.content.map((node: any) => { - switch (node.type) { - case 'paragraph': - const text = extractTextFromNode(node) - return text ? { type: 'paragraph', content: text } : null - - case 'heading': - return { - type: 'heading', - level: node.attrs?.level || 1, - content: extractTextFromNode(node) - } - - case 'bulletList': - return { - type: 'bulletList', - content: node.content?.map((item: any) => { - const itemText = extractTextFromNode(item.content?.[0]) - return itemText - }).filter(Boolean) || [] - } - - case 'orderedList': - return { - type: 'orderedList', - content: node.content?.map((item: any) => { - const itemText = extractTextFromNode(item.content?.[0]) - return itemText - }).filter(Boolean) || [] - } - - case 'blockquote': - return { - type: 'blockquote', - content: extractTextFromNode(node.content?.[0]) - } - - case 'codeBlock': - return { - type: 'codeBlock', - language: node.attrs?.language || '', - content: node.content?.[0]?.text || '' - } - - case 'image': - return { - type: 'image', - src: node.attrs?.src || '', - alt: node.attrs?.alt || '', - caption: node.attrs?.title || '' - } - - case 'horizontalRule': - return { type: 'hr' } - - default: - // Skip unknown types - return null - } - }).filter(Boolean) + const blocks = tiptapContent.content + .map((node: any) => { + switch (node.type) { + case 'paragraph': + const text = extractTextFromNode(node) + return text ? { type: 'paragraph', content: text } : null + + case 'heading': + return { + type: 'heading', + level: node.attrs?.level || 1, + content: extractTextFromNode(node) + } + + case 'bulletList': + return { + type: 'bulletList', + content: + node.content + ?.map((item: any) => { + const itemText = extractTextFromNode(item.content?.[0]) + return itemText + }) + .filter(Boolean) || [] + } + + case 'orderedList': + return { + type: 'orderedList', + content: + node.content + ?.map((item: any) => { + const itemText = extractTextFromNode(item.content?.[0]) + return itemText + }) + .filter(Boolean) || [] + } + + case 'blockquote': + return { + type: 'blockquote', + content: extractTextFromNode(node.content?.[0]) + } + + case 'codeBlock': + return { + type: 'codeBlock', + language: node.attrs?.language || '', + content: node.content?.[0]?.text || '' + } + + case 'image': + return { + type: 'image', + src: node.attrs?.src || '', + alt: node.attrs?.alt || '', + caption: node.attrs?.title || '' + } + + case 'horizontalRule': + return { type: 'hr' } + + default: + // Skip unknown types + return null + } + }) + .filter(Boolean) return { blocks } } @@ -244,7 +258,7 @@ status = post.status || 'draft' slug = post.slug || '' excerpt = post.excerpt || '' - + // Convert blocks format to Tiptap format if needed if (post.content && post.content.blocks) { content = convertBlocksToTiptap(post.content) @@ -253,7 +267,7 @@ } else { content = { type: 'doc', content: [] } } - + tags = post.tags || [] } else { if (response.status === 404) { @@ -283,7 +297,7 @@ tags = tags.filter((t) => t !== tag) } - async function handleSave(publishStatus?: 'draft' | 'published') { + async function handleSave(newStatus?: string) { const auth = localStorage.getItem('admin_auth') if (!auth) { goto('/admin/login') @@ -291,18 +305,18 @@ } saving = true - + // Convert content to blocks format if it's in Tiptap format let saveContent = content if (config?.showContent && content && content.type === 'doc') { saveContent = convertTiptapToBlocks(content) } - + const postData = { title: config?.showTitle ? title : null, slug, type: postType, - status: publishStatus || status, + status: newStatus || status, content: config?.showContent ? saveContent : null, excerpt: postType === 'essay' ? excerpt : undefined, link_url: undefined, @@ -322,8 +336,8 @@ if (response.ok) { post = await response.json() - if (publishStatus) { - status = publishStatus + if (newStatus) { + status = newStatus } } } catch (error) { @@ -431,12 +445,15 @@ /> {/if} - {/if} @@ -591,7 +608,7 @@ border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); min-width: 150px; - z-index: 100; + z-index: 1050; overflow: hidden; } diff --git a/src/routes/admin/posts/new/+page.svelte b/src/routes/admin/posts/new/+page.svelte index 0819280..a20f5b6 100644 --- a/src/routes/admin/posts/new/+page.svelte +++ b/src/routes/admin/posts/new/+page.svelte @@ -276,7 +276,7 @@ border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); min-width: 150px; - z-index: 100; + z-index: 1050; overflow: hidden; }