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:
@@ -119,6 +119,18 @@ export const useIsAuthenticated = (): boolean => {
|
||||
return !isLoading && !!user;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the redirect path based on user role
|
||||
* Tenant users go to /dashboard/, platform users go to /
|
||||
*/
|
||||
const getRedirectPathForRole = (role: string): string => {
|
||||
const tenantRoles = ['tenant_owner', 'tenant_manager', 'tenant_staff'];
|
||||
if (tenantRoles.includes(role)) {
|
||||
return '/dashboard/';
|
||||
}
|
||||
return '/';
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to masquerade as another user
|
||||
*/
|
||||
@@ -154,6 +166,7 @@ export const useMasquerade = () => {
|
||||
}
|
||||
|
||||
const needsRedirect = targetSubdomain && currentHostname !== `${targetSubdomain}.${baseDomain}`;
|
||||
const redirectPath = getRedirectPathForRole(user.role);
|
||||
|
||||
if (needsRedirect) {
|
||||
// CRITICAL: Clear the session cookie BEFORE redirect
|
||||
@@ -170,17 +183,17 @@ export const useMasquerade = () => {
|
||||
|
||||
// Pass tokens AND masquerading stack in URL (for cross-domain transfer)
|
||||
const stackEncoded = encodeURIComponent(JSON.stringify(data.masquerade_stack || []));
|
||||
const redirectUrl = buildSubdomainUrl(targetSubdomain, `/?access_token=${data.access}&refresh_token=${data.refresh}&masquerade_stack=${stackEncoded}`);
|
||||
const redirectUrl = buildSubdomainUrl(targetSubdomain, `${redirectPath}?access_token=${data.access}&refresh_token=${data.refresh}&masquerade_stack=${stackEncoded}`);
|
||||
|
||||
window.location.href = redirectUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
// If no redirect needed (same subdomain), we can just set cookies and reload
|
||||
// If no redirect needed (same subdomain), we can just set cookies and navigate
|
||||
setCookie('access_token', data.access, 7);
|
||||
setCookie('refresh_token', data.refresh, 7);
|
||||
queryClient.setQueryData(['currentUser'], data.user);
|
||||
window.location.reload();
|
||||
window.location.href = redirectPath;
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -227,6 +240,7 @@ export const useStopMasquerade = () => {
|
||||
}
|
||||
|
||||
const needsRedirect = targetSubdomain && currentHostname !== `${targetSubdomain}.${baseDomain}`;
|
||||
const redirectPath = getRedirectPathForRole(user.role);
|
||||
|
||||
if (needsRedirect) {
|
||||
// CRITICAL: Clear the session cookie BEFORE redirect
|
||||
@@ -242,17 +256,17 @@ export const useStopMasquerade = () => {
|
||||
|
||||
// Pass tokens AND masquerading stack in URL (for cross-domain transfer)
|
||||
const stackEncoded = encodeURIComponent(JSON.stringify(data.masquerade_stack || []));
|
||||
const redirectUrl = buildSubdomainUrl(targetSubdomain, `/?access_token=${data.access}&refresh_token=${data.refresh}&masquerade_stack=${stackEncoded}`);
|
||||
const redirectUrl = buildSubdomainUrl(targetSubdomain, `${redirectPath}?access_token=${data.access}&refresh_token=${data.refresh}&masquerade_stack=${stackEncoded}`);
|
||||
|
||||
window.location.href = redirectUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
// If no redirect needed (same subdomain), we can just set cookies and reload
|
||||
// If no redirect needed (same subdomain), we can just set cookies and navigate
|
||||
setCookie('access_token', data.access, 7);
|
||||
setCookie('refresh_token', data.refresh, 7);
|
||||
queryClient.setQueryData(['currentUser'], data.user);
|
||||
window.location.reload();
|
||||
window.location.href = redirectPath;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user