Inline composer
This commit is contained in:
parent
4407a85dec
commit
e7a7e7cd1e
4 changed files with 46 additions and 34 deletions
|
|
@ -51,6 +51,7 @@
|
|||
box-sizing: border-box;
|
||||
min-height: 110px;
|
||||
padding: $unit-4x;
|
||||
display: flex;
|
||||
|
||||
@include breakpoint('phone') {
|
||||
padding: $unit-3x;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue