Add media gallery with album organization and Puck integration
Backend: - Add Album and MediaFile models for tenant-scoped media storage - Add TenantStorageUsage model for per-tenant storage quota tracking - Create StorageQuotaService with EntitlementService integration - Add AlbumViewSet, MediaFileViewSet with bulk operations - Add StorageUsageView for quota monitoring Frontend: - Create MediaGalleryPage with album management and file upload - Add drag-and-drop upload with storage quota validation - Create ImagePickerField custom Puck field for gallery integration - Update Image, Testimonial components to use ImagePicker - Add background image picker to Puck design controls - Add gallery to sidebar navigation Also includes: - Puck marketing components (Hero, SplitContent, etc.) - Enhanced ContactForm and BusinessHours components - Platform login page improvements - Site builder draft/preview enhancements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,7 @@ const TrialExpired = React.lazy(() => import('./pages/TrialExpired'));
|
||||
const Upgrade = React.lazy(() => import('./pages/Upgrade'));
|
||||
|
||||
// Import platform pages
|
||||
const PlatformLoginPage = React.lazy(() => import('./pages/platform/PlatformLoginPage'));
|
||||
const PlatformDashboard = React.lazy(() => import('./pages/platform/PlatformDashboard'));
|
||||
const PlatformBusinesses = React.lazy(() => import('./pages/platform/PlatformBusinesses'));
|
||||
const PlatformSupportPage = React.lazy(() => import('./pages/platform/PlatformSupport'));
|
||||
@@ -115,6 +116,7 @@ const PageEditor = React.lazy(() => import('./pages/PageEditor')); // Import Pag
|
||||
const PublicPage = React.lazy(() => import('./pages/PublicPage')); // Import PublicPage
|
||||
const BookingFlow = React.lazy(() => import('./pages/BookingFlow')); // Import Booking Flow
|
||||
const Locations = React.lazy(() => import('./pages/Locations')); // Import Locations management page
|
||||
const MediaGalleryPage = React.lazy(() => import('./pages/MediaGalleryPage')); // Import Media Gallery page
|
||||
|
||||
// Settings pages
|
||||
const SettingsLayout = React.lazy(() => import('./layouts/SettingsLayout'));
|
||||
@@ -368,7 +370,28 @@ const AppContent: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
// For root domain or platform subdomain, show marketing site / login
|
||||
// For platform subdomain, only /platform/login exists - everything else renders nothing
|
||||
if (isPlatformSubdomain) {
|
||||
const path = window.location.pathname;
|
||||
const allowedPaths = ['/platform/login', '/mfa-verify', '/verify-email'];
|
||||
|
||||
// If not an allowed path, render nothing
|
||||
if (!allowedPaths.includes(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<LoadingScreen />}>
|
||||
<Routes>
|
||||
<Route path="/platform/login" element={<PlatformLoginPage />} />
|
||||
<Route path="/mfa-verify" element={<MFAVerifyPage />} />
|
||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
// For root domain, show marketing site with business user login
|
||||
return (
|
||||
<Suspense fallback={<LoadingScreen />}>
|
||||
<Routes>
|
||||
@@ -660,6 +683,13 @@ const AppContent: React.FC = () => {
|
||||
return (
|
||||
<Suspense fallback={<LoadingScreen />}>
|
||||
<Routes>
|
||||
{/* Public routes outside BusinessLayout */}
|
||||
<Route path="/" element={<PublicPage />} />
|
||||
<Route path="/book" element={<BookingFlow />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/sign/:token" element={<ContractSigning />} />
|
||||
|
||||
{/* Dashboard routes inside BusinessLayout */}
|
||||
<Route
|
||||
element={
|
||||
<BusinessLayout
|
||||
@@ -672,9 +702,6 @@ const AppContent: React.FC = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{/* Redirect root to dashboard */}
|
||||
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
||||
|
||||
{/* Trial and Upgrade Routes */}
|
||||
<Route path="/dashboard/trial-expired" element={<TrialExpired />} />
|
||||
<Route path="/dashboard/upgrade" element={<Upgrade />} />
|
||||
@@ -902,6 +929,16 @@ const AppContent: React.FC = () => {
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/dashboard/gallery"
|
||||
element={
|
||||
hasAccess(['owner', 'manager']) ? (
|
||||
<MediaGalleryPage />
|
||||
) : (
|
||||
<Navigate to="/dashboard" />
|
||||
)
|
||||
}
|
||||
/>
|
||||
{/* Settings Routes with Nested Layout */}
|
||||
{hasAccess(['owner']) ? (
|
||||
<Route path="/dashboard/settings" element={<SettingsLayout />}>
|
||||
@@ -925,8 +962,10 @@ const AppContent: React.FC = () => {
|
||||
)}
|
||||
<Route path="/dashboard/profile" element={<ProfileSettings />} />
|
||||
<Route path="/dashboard/verify-email" element={<VerifyEmail />} />
|
||||
<Route path="*" element={<Navigate to="/dashboard" />} />
|
||||
</Route>
|
||||
|
||||
{/* Catch-all redirects to home */}
|
||||
<Route path="*" element={<Navigate to="/" />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user