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;
|
box-sizing: border-box;
|
||||||
min-height: 110px;
|
min-height: 110px;
|
||||||
padding: $unit-4x;
|
padding: $unit-4x;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
@include breakpoint('phone') {
|
@include breakpoint('phone') {
|
||||||
padding: $unit-3x;
|
padding: $unit-3x;
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
|
|
||||||
function switchToEssay() {
|
function switchToEssay() {
|
||||||
const contentParam = content ? encodeURIComponent(JSON.stringify(content)) : ''
|
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 {
|
function generateSlug(title: string): string {
|
||||||
|
|
@ -151,8 +151,8 @@
|
||||||
|
|
||||||
function handleMediaSelect(media: Media | Media[]) {
|
function handleMediaSelect(media: Media | Media[]) {
|
||||||
const mediaArray = Array.isArray(media) ? media : [media]
|
const mediaArray = Array.isArray(media) ? media : [media]
|
||||||
const currentIds = attachedPhotos.map(p => p.id)
|
const currentIds = attachedPhotos.map((p) => p.id)
|
||||||
const newMedia = mediaArray.filter(m => !currentIds.includes(m.id))
|
const newMedia = mediaArray.filter((m) => !currentIds.includes(m.id))
|
||||||
attachedPhotos = [...attachedPhotos, ...newMedia]
|
attachedPhotos = [...attachedPhotos, ...newMedia]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function removePhoto(photoId: number) {
|
function removePhoto(photoId: number) {
|
||||||
attachedPhotos = attachedPhotos.filter(p => p.id !== photoId)
|
attachedPhotos = attachedPhotos.filter((p) => p.id !== photoId)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePhotoClick(photo: Media) {
|
function handlePhotoClick(photo: Media) {
|
||||||
|
|
@ -176,7 +176,7 @@
|
||||||
|
|
||||||
function handleMediaUpdate(updatedMedia: Media) {
|
function handleMediaUpdate(updatedMedia: Media) {
|
||||||
// Update the photo in the attachedPhotos array
|
// Update the photo in the attachedPhotos array
|
||||||
attachedPhotos = attachedPhotos.map(photo =>
|
attachedPhotos = attachedPhotos.map((photo) =>
|
||||||
photo.id === updatedMedia.id ? updatedMedia : photo
|
photo.id === updatedMedia.id ? updatedMedia : photo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +206,7 @@
|
||||||
let postData: any = {
|
let postData: any = {
|
||||||
content,
|
content,
|
||||||
status: 'published',
|
status: 'published',
|
||||||
attachedPhotos: attachedPhotos.map(photo => photo.id)
|
attachedPhotos: attachedPhotos.map((photo) => photo.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postType === 'essay') {
|
if (postType === 'essay') {
|
||||||
|
|
@ -234,9 +234,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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', {
|
const response = await fetch('/api/posts', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers,
|
||||||
body: JSON.stringify(postData)
|
body: JSON.stringify(postData)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -330,7 +336,6 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
{#if attachedPhotos.length > 0}
|
{#if attachedPhotos.length > 0}
|
||||||
<div class="attached-photos">
|
<div class="attached-photos">
|
||||||
{#each attachedPhotos as photo}
|
{#each attachedPhotos as photo}
|
||||||
|
|
@ -340,11 +345,7 @@
|
||||||
onclick={() => handlePhotoClick(photo)}
|
onclick={() => handlePhotoClick(photo)}
|
||||||
title="View media details"
|
title="View media details"
|
||||||
>
|
>
|
||||||
<img
|
<img src={photo.url} alt={photo.altText || ''} class="photo-preview" />
|
||||||
src={photo.url}
|
|
||||||
alt={photo.altText || ''}
|
|
||||||
class="photo-preview"
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="remove-photo"
|
class="remove-photo"
|
||||||
|
|
@ -580,7 +581,6 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
{#if attachedPhotos.length > 0}
|
{#if attachedPhotos.length > 0}
|
||||||
<div class="attached-photos">
|
<div class="attached-photos">
|
||||||
{#each attachedPhotos as photo}
|
{#each attachedPhotos as photo}
|
||||||
|
|
@ -590,11 +590,7 @@
|
||||||
onclick={() => handlePhotoClick(photo)}
|
onclick={() => handlePhotoClick(photo)}
|
||||||
title="View media details"
|
title="View media details"
|
||||||
>
|
>
|
||||||
<img
|
<img src={photo.url} alt={photo.altText || ''} class="photo-preview" />
|
||||||
src={photo.url}
|
|
||||||
alt={photo.altText || ''}
|
|
||||||
class="photo-preview"
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="remove-photo"
|
class="remove-photo"
|
||||||
|
|
@ -903,11 +899,8 @@
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: $unit-2x;
|
border-radius: $unit-2x;
|
||||||
border: 1px solid $grey-80;
|
border: 1px solid $grey-80;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.composer-body {
|
.composer-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -997,7 +990,7 @@
|
||||||
|
|
||||||
.photo-item {
|
.photo-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.photo-button {
|
.photo-button {
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
|
|
@ -1010,7 +1003,7 @@
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.photo-preview) {
|
:global(.photo-preview) {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
|
|
@ -1018,7 +1011,7 @@
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-photo {
|
.remove-photo {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -6px;
|
top: -6px;
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,7 @@
|
||||||
{viewMode === 'grid' ? '📋' : '🖼️'}
|
{viewMode === 'grid' ? '📋' : '🖼️'}
|
||||||
{viewMode === 'grid' ? 'List' : 'Grid'}
|
{viewMode === 'grid' ? 'List' : 'Grid'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="primary" size="large" onclick={openUploadModal}>Upload Media</Button>
|
<Button variant="primary" size="large" onclick={openUploadModal}>Upload...</Button>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</AdminHeader>
|
</AdminHeader>
|
||||||
|
|
||||||
|
|
@ -650,7 +650,7 @@
|
||||||
<!-- Media Upload Modal -->
|
<!-- Media Upload Modal -->
|
||||||
<MediaUploadModal
|
<MediaUploadModal
|
||||||
bind:isOpen={isUploadModalOpen}
|
bind:isOpen={isUploadModalOpen}
|
||||||
onClose={() => isUploadModalOpen = false}
|
onClose={() => (isUploadModalOpen = false)}
|
||||||
onUploadComplete={handleUploadComplete}
|
onUploadComplete={handleUploadComplete}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
import AdminHeader from '$lib/components/admin/AdminHeader.svelte'
|
import AdminHeader from '$lib/components/admin/AdminHeader.svelte'
|
||||||
import AdminFilters from '$lib/components/admin/AdminFilters.svelte'
|
import AdminFilters from '$lib/components/admin/AdminFilters.svelte'
|
||||||
import PostListItem from '$lib/components/admin/PostListItem.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 LoadingSpinner from '$lib/components/admin/LoadingSpinner.svelte'
|
||||||
import Select from '$lib/components/admin/Select.svelte'
|
import Select from '$lib/components/admin/Select.svelte'
|
||||||
|
import UniverseComposer from '$lib/components/admin/UniverseComposer.svelte'
|
||||||
|
|
||||||
interface Post {
|
interface Post {
|
||||||
id: number
|
id: number
|
||||||
|
|
@ -36,6 +36,9 @@
|
||||||
// Filter state
|
// Filter state
|
||||||
let selectedFilter = $state<string>('all')
|
let selectedFilter = $state<string>('all')
|
||||||
|
|
||||||
|
// Composer state
|
||||||
|
let showInlineComposer = $state(true)
|
||||||
|
|
||||||
// Create filter options
|
// Create filter options
|
||||||
const filterOptions = $derived([
|
const filterOptions = $derived([
|
||||||
{ value: 'all', label: 'All posts' },
|
{ value: 'all', label: 'All posts' },
|
||||||
|
|
@ -140,18 +143,30 @@
|
||||||
applyFilter()
|
applyFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleComposerSaved() {
|
||||||
|
// Reload posts when a new post is created
|
||||||
|
loadPosts()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AdminPage>
|
<AdminPage>
|
||||||
<AdminHeader title="Universe" slot="header">
|
<AdminHeader title="Universe" slot="header" />
|
||||||
{#snippet actions()}
|
|
||||||
<PostDropdown />
|
|
||||||
{/snippet}
|
|
||||||
</AdminHeader>
|
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<div class="error-message">{error}</div>
|
<div class="error-message">{error}</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
<!-- Inline Composer -->
|
||||||
|
{#if showInlineComposer}
|
||||||
|
<div class="composer-section">
|
||||||
|
<UniverseComposer
|
||||||
|
isOpen={true}
|
||||||
|
initialMode="page"
|
||||||
|
initialPostType="post"
|
||||||
|
on:saved={handleComposerSaved}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<!-- Filters -->
|
<!-- Filters -->
|
||||||
<AdminFilters>
|
<AdminFilters>
|
||||||
{#snippet left()}
|
{#snippet left()}
|
||||||
|
|
@ -241,5 +256,8 @@
|
||||||
gap: $unit-2x;
|
gap: $unit-2x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.composer-section {
|
||||||
|
margin-bottom: $unit-4x;
|
||||||
|
padding: 0 $unit;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue