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:
poduck
2025-12-13 19:59:31 -05:00
parent e7733449dd
commit fbefccf436
58 changed files with 11590 additions and 477 deletions

View File

@@ -10,6 +10,7 @@ export interface TestUser {
role: string;
label: string;
color: string;
category: 'platform' | 'business' | 'customer';
}
const testUsers: TestUser[] = [
@@ -19,6 +20,7 @@ const testUsers: TestUser[] = [
role: 'SUPERUSER',
label: 'Platform Superuser',
color: 'bg-purple-600 hover:bg-purple-700',
category: 'platform',
},
{
email: 'manager@platform.com',
@@ -26,6 +28,7 @@ const testUsers: TestUser[] = [
role: 'PLATFORM_MANAGER',
label: 'Platform Manager',
color: 'bg-blue-600 hover:bg-blue-700',
category: 'platform',
},
{
email: 'sales@platform.com',
@@ -33,6 +36,7 @@ const testUsers: TestUser[] = [
role: 'PLATFORM_SALES',
label: 'Platform Sales',
color: 'bg-green-600 hover:bg-green-700',
category: 'platform',
},
{
email: 'support@platform.com',
@@ -40,6 +44,7 @@ const testUsers: TestUser[] = [
role: 'PLATFORM_SUPPORT',
label: 'Platform Support',
color: 'bg-yellow-600 hover:bg-yellow-700',
category: 'platform',
},
{
email: 'owner@demo.com',
@@ -47,6 +52,7 @@ const testUsers: TestUser[] = [
role: 'TENANT_OWNER',
label: 'Business Owner',
color: 'bg-indigo-600 hover:bg-indigo-700',
category: 'business',
},
{
email: 'manager@demo.com',
@@ -54,6 +60,7 @@ const testUsers: TestUser[] = [
role: 'TENANT_MANAGER',
label: 'Business Manager',
color: 'bg-pink-600 hover:bg-pink-700',
category: 'business',
},
{
email: 'staff@demo.com',
@@ -61,6 +68,7 @@ const testUsers: TestUser[] = [
role: 'TENANT_STAFF',
label: 'Staff Member',
color: 'bg-teal-600 hover:bg-teal-700',
category: 'business',
},
{
email: 'customer@demo.com',
@@ -68,14 +76,18 @@ const testUsers: TestUser[] = [
role: 'CUSTOMER',
label: 'Customer',
color: 'bg-orange-600 hover:bg-orange-700',
category: 'customer',
},
];
type UserFilter = 'all' | 'platform' | 'business';
interface DevQuickLoginProps {
embedded?: boolean;
filter?: UserFilter;
}
export function DevQuickLogin({ embedded = false }: DevQuickLoginProps) {
export function DevQuickLogin({ embedded = false, filter = 'all' }: DevQuickLoginProps) {
const queryClient = useQueryClient();
const [loading, setLoading] = useState<string | null>(null);
const [isMinimized, setIsMinimized] = useState(false);
@@ -85,6 +97,14 @@ export function DevQuickLogin({ embedded = false }: DevQuickLoginProps) {
return null;
}
// Filter users based on the filter prop
const filteredUsers = testUsers.filter((user) => {
if (filter === 'all') return true;
if (filter === 'platform') return user.category === 'platform';
if (filter === 'business') return user.category === 'business';
return true;
});
const handleQuickLogin = async (user: TestUser) => {
setLoading(user.email);
try {
@@ -174,7 +194,7 @@ export function DevQuickLogin({ embedded = false }: DevQuickLoginProps) {
</div>
<div className="grid grid-cols-2 gap-2">
{testUsers.map((user) => (
{filteredUsers.map((user) => (
<button
key={user.email}
onClick={() => handleQuickLogin(user)}