From 2bfa01e0d4c0af87cf7de7a5fbfa00a10b68191b Mon Sep 17 00:00:00 2001 From: poduck Date: Mon, 22 Dec 2025 18:21:52 -0500 Subject: [PATCH] Add image picker to site builder components and fix media gallery bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ImagePickerField to all site builder components with image URLs: - Marketing: Hero, SplitContent, LogoCloud, GalleryGrid, Testimonials, ContentBlocks, Header, Footer - Email: EmailImage, EmailHeader - Fix media gallery issues: - Change API endpoint from /media/ to /media-files/ to avoid URL conflict - Fix album file_count annotation conflict with model property - Fix image URLs to use absolute paths for cross-domain access - Add clipboard fallback for non-HTTPS copy URL - Show permission slugs in plan feature editor - Fix branding settings access for tenants with custom_branding feature - Fix EntitlementService method call in StorageQuotaService 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- frontend/src/api/media.ts | 14 +++--- .../src/billing/components/FeaturePicker.tsx | 18 +++++-- frontend/src/hooks/usePlanFeatures.ts | 2 + frontend/src/layouts/SettingsLayout.tsx | 4 +- frontend/src/pages/MediaGalleryPage.tsx | 48 ++++++++++++++----- .../src/puck/components/email/EmailHeader.tsx | 5 +- .../src/puck/components/email/EmailImage.tsx | 5 +- .../components/marketing/ContentBlocks.tsx | 3 +- .../src/puck/components/marketing/Footer.tsx | 5 +- .../puck/components/marketing/GalleryGrid.tsx | 3 +- .../src/puck/components/marketing/Header.tsx | 5 +- .../src/puck/components/marketing/Hero.tsx | 3 +- .../puck/components/marketing/LogoCloud.tsx | 3 +- .../components/marketing/SplitContent.tsx | 3 +- .../components/marketing/Testimonials.tsx | 3 +- frontend/src/types.ts | 1 + .../smoothschedule/identity/core/services.py | 8 ++-- .../scheduling/schedule/api_views.py | 1 + .../scheduling/schedule/serializers.py | 46 +++++++++++------- .../scheduling/schedule/urls.py | 2 +- .../scheduling/schedule/views.py | 2 +- 21 files changed, 120 insertions(+), 64 deletions(-) diff --git a/frontend/src/api/media.ts b/frontend/src/api/media.ts index 2c87001d..771141bb 100644 --- a/frontend/src/api/media.ts +++ b/frontend/src/api/media.ts @@ -123,7 +123,7 @@ export async function deleteAlbum(id: number): Promise { */ export async function listMediaFiles(albumId?: number | 'null'): Promise { const params = albumId !== undefined ? { album: albumId } : {}; - const response = await apiClient.get('/media/', { params }); + const response = await apiClient.get('/media-files/', { params }); return response.data; } @@ -131,7 +131,7 @@ export async function listMediaFiles(albumId?: number | 'null'): Promise { - const response = await apiClient.get(`/media/${id}/`); + const response = await apiClient.get(`/media-files/${id}/`); return response.data; } @@ -152,7 +152,7 @@ export async function uploadMediaFile( formData.append('alt_text', altText); } - const response = await apiClient.post('/media/', formData, { + const response = await apiClient.post('/media-files/', formData, { headers: { 'Content-Type': 'multipart/form-data', }, @@ -167,7 +167,7 @@ export async function updateMediaFile( id: number, data: MediaFileUpdatePayload ): Promise { - const response = await apiClient.patch(`/media/${id}/`, data); + const response = await apiClient.patch(`/media-files/${id}/`, data); return response.data; } @@ -175,7 +175,7 @@ export async function updateMediaFile( * Delete a media file */ export async function deleteMediaFile(id: number): Promise { - await apiClient.delete(`/media/${id}/`); + await apiClient.delete(`/media-files/${id}/`); } /** @@ -185,7 +185,7 @@ export async function bulkMoveFiles( fileIds: number[], albumId: number | null ): Promise<{ updated: number }> { - const response = await apiClient.post('/media/bulk_move/', { + const response = await apiClient.post('/media-files/bulk_move/', { file_ids: fileIds, album_id: albumId, }); @@ -196,7 +196,7 @@ export async function bulkMoveFiles( * Delete multiple files */ export async function bulkDeleteFiles(fileIds: number[]): Promise<{ deleted: number }> { - const response = await apiClient.post('/media/bulk_delete/', { + const response = await apiClient.post('/media-files/bulk_delete/', { file_ids: fileIds, }); return response.data; diff --git a/frontend/src/billing/components/FeaturePicker.tsx b/frontend/src/billing/components/FeaturePicker.tsx index 37b6d671..a8f4f62a 100644 --- a/frontend/src/billing/components/FeaturePicker.tsx +++ b/frontend/src/billing/components/FeaturePicker.tsx @@ -171,6 +171,9 @@ export const FeaturePicker: React.FC = ({ {feature.name} + + {feature.code} + {feature.description && ( {feature.description} @@ -207,17 +210,22 @@ export const FeaturePicker: React.FC = ({ : 'border-gray-200 dark:border-gray-700' }`} > -