Inline composer

This commit is contained in:
Justin Edmund 2025-06-02 04:56:09 -07:00
parent 4407a85dec
commit e7a7e7cd1e
4 changed files with 46 additions and 34 deletions

View file

@ -51,6 +51,7 @@
box-sizing: border-box;
min-height: 110px;
padding: $unit-4x;
display: flex;
@include breakpoint('phone') {
padding: $unit-3x;

View file

@ -86,7 +86,7 @@
function switchToEssay() {
const contentParam = content ? encodeURIComponent(JSON.stringify(content)) : ''
goto(`/admin/universe/compose?type=essay${contentParam ? `&content=${contentParam}` : ''}`)
goto(`/admin/posts/new?type=essay${contentParam ? `&content=${contentParam}` : ''}`)
}
function generateSlug(title: string): string {
@ -151,8 +151,8 @@
function handleMediaSelect(media: Media | Media[]) {
const mediaArray = Array.isArray(media) ? media : [media]
const currentIds = attachedPhotos.map(p => p.id)
const newMedia = mediaArray.filter(m => !currentIds.includes(m.id))
const currentIds = attachedPhotos.map((p) => p.id)
const newMedia = mediaArray.filter((m) => !currentIds.includes(m.id))
attachedPhotos = [...attachedPhotos, ...newMedia]
}
@ -161,7 +161,7 @@
}
function removePhoto(photoId: number) {
attachedPhotos = attachedPhotos.filter(p => p.id !== photoId)
attachedPhotos = attachedPhotos.filter((p) => p.id !== photoId)
}
function handlePhotoClick(photo: Media) {
@ -176,7 +176,7 @@
function handleMediaUpdate(updatedMedia: Media) {
// Update the photo in the attachedPhotos array
attachedPhotos = attachedPhotos.map(photo =>
attachedPhotos = attachedPhotos.map((photo) =>
photo.id === updatedMedia.id ? updatedMedia : photo
)
}
@ -206,7 +206,7 @@
let postData: any = {
content,
status: 'published',
attachedPhotos: attachedPhotos.map(photo => photo.id)
attachedPhotos: attachedPhotos.map((photo) => photo.id)
}
if (postType === 'essay') {
@ -234,9 +234,15 @@
}
try {
const auth = localStorage.getItem('admin_auth')
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
if (auth) {
headers.Authorization = `Basic ${auth}`
}
const response = await fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
headers,
body: JSON.stringify(postData)
})
@ -330,7 +336,6 @@
</div>
{/if}
{#if attachedPhotos.length > 0}
<div class="attached-photos">
{#each attachedPhotos as photo}
@ -340,11 +345,7 @@
onclick={() => handlePhotoClick(photo)}
title="View media details"
>
<img
src={photo.url}
alt={photo.altText || ''}
class="photo-preview"
/>
<img src={photo.url} alt={photo.altText || ''} class="photo-preview" />
</button>
<button
class="remove-photo"
@ -580,7 +581,6 @@
</div>
{/if}
{#if attachedPhotos.length > 0}
<div class="attached-photos">
{#each attachedPhotos as photo}
@ -590,11 +590,7 @@
onclick={() => handlePhotoClick(photo)}
title="View media details"
>
<img
src={photo.url}
alt={photo.altText || ''}
class="photo-preview"
/>
<img src={photo.url} alt={photo.altText || ''} class="photo-preview" />
</button>
<button
class="remove-photo"
@ -903,11 +899,8 @@
background: white;
border-radius: $unit-2x;
border: 1px solid $grey-80;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
overflow: hidden;
width: 100%;
max-width: 800px;
margin: 0 auto;
.composer-body {
display: flex;
@ -997,7 +990,7 @@
.photo-item {
position: relative;
.photo-button {
border: none;
background: none;
@ -1010,7 +1003,7 @@
transform: scale(1.05);
}
}
:global(.photo-preview) {
width: 64px;
height: 64px;
@ -1018,7 +1011,7 @@
border-radius: 12px;
display: block;
}
.remove-photo {
position: absolute;
top: -6px;

View file

@ -335,7 +335,7 @@
{viewMode === 'grid' ? '📋' : '🖼️'}
{viewMode === 'grid' ? 'List' : 'Grid'}
</Button>
<Button variant="primary" size="large" onclick={openUploadModal}>Upload Media</Button>
<Button variant="primary" size="large" onclick={openUploadModal}>Upload...</Button>
{/snippet}
</AdminHeader>
@ -650,7 +650,7 @@
<!-- Media Upload Modal -->
<MediaUploadModal
bind:isOpen={isUploadModalOpen}
onClose={() => isUploadModalOpen = false}
onClose={() => (isUploadModalOpen = false)}
onUploadComplete={handleUploadComplete}
/>

View file

@ -5,9 +5,9 @@
import AdminHeader from '$lib/components/admin/AdminHeader.svelte'
import AdminFilters from '$lib/components/admin/AdminFilters.svelte'
import PostListItem from '$lib/components/admin/PostListItem.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'
import UniverseComposer from '$lib/components/admin/UniverseComposer.svelte'
interface Post {
id: number
@ -36,6 +36,9 @@
// Filter state
let selectedFilter = $state<string>('all')
// Composer state
let showInlineComposer = $state(true)
// Create filter options
const filterOptions = $derived([
{ value: 'all', label: 'All posts' },
@ -140,18 +143,30 @@
applyFilter()
}
function handleComposerSaved() {
// Reload posts when a new post is created
loadPosts()
}
</script>
<AdminPage>
<AdminHeader title="Universe" slot="header">
{#snippet actions()}
<PostDropdown />
{/snippet}
</AdminHeader>
<AdminHeader title="Universe" slot="header" />
{#if error}
<div class="error-message">{error}</div>
{:else}
<!-- Inline Composer -->
{#if showInlineComposer}
<div class="composer-section">
<UniverseComposer
isOpen={true}
initialMode="page"
initialPostType="post"
on:saved={handleComposerSaved}
/>
</div>
{/if}
<!-- Filters -->
<AdminFilters>
{#snippet left()}
@ -241,5 +256,8 @@
gap: $unit-2x;
}
.composer-section {
margin-bottom: $unit-4x;
padding: 0 $unit;
}
</style>