) => { id: string; label: string; layoutId: string }[];
+ metadata: WidgetMetadata;
+ previewComponent?: React.ComponentType
;
+ getNestedLayouts?: (props: P) => { id: string; label: string; layoutId: string }[];
}
class WidgetRegistry {
- private widgets = new Map();
+ private widgets = new Map>();
- register(definition: WidgetDefinition) {
+ register>(definition: WidgetDefinition
) {
if (this.widgets.has(definition.metadata.id)) {
// Allow overwriting for HMR/Dynamic loading, just log info if needed
// console.debug(`Updating existing widget registration: '${definition.metadata.id}'`);
}
- this.widgets.set(definition.metadata.id, definition);
+ this.widgets.set(definition.metadata.id, definition as WidgetDefinition);
}
get(id: string): WidgetDefinition | undefined {
diff --git a/packages/ui/src/pages/Collections.tsx b/packages/ui/src/pages/Collections.tsx
index deacaf28..308083d6 100644
--- a/packages/ui/src/pages/Collections.tsx
+++ b/packages/ui/src/pages/Collections.tsx
@@ -32,12 +32,13 @@ import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Progress } from '@/components/ui/progress';
import { GenericCanvas } from '@/components/hmi/GenericCanvas';
+import { LayoutProvider } from '@/contexts/LayoutContext';
interface Collection {
id: string;
name: string;
description: string;
- slug: string;
+ slug: string;
user_id: string;
is_public: boolean;
created_at: string;
@@ -55,7 +56,7 @@ interface Picture {
comments: { count: number }[];
}
-const Collections = () => {
+const CollectionsContent = () => {
const { userId, slug } = useParams();
const { user } = useAuth();
const navigate = useNavigate();
@@ -76,7 +77,7 @@ const Collections = () => {
const [isLayoutEditMode, setIsLayoutEditMode] = useState(false);
const isOwner = user?.id === userId;
-
+
interface UploadingFile {
id: string;
file: File;
@@ -99,7 +100,7 @@ const Collections = () => {
const completedCount = uploadingFiles.filter(f => f.status === 'complete').length;
const errorCount = uploadingFiles.filter(f => f.status === 'error').length;
const totalProcessed = completedCount + errorCount;
-
+
if (uploadingFiles.length > 0 && totalProcessed === uploadingFiles.length) {
if (completedCount > 0) {
toast({
@@ -115,7 +116,7 @@ const Collections = () => {
variant: "destructive"
});
}
-
+
setTimeout(() => {
setUploadingFiles([]);
}, 5000);
@@ -125,56 +126,56 @@ const Collections = () => {
const uploadAndProcessFiles = (filesToUpload: UploadingFile[]) => {
filesToUpload.forEach(async (fileToUpload) => {
- try {
- if (!user || !collection) return;
+ try {
+ if (!user || !collection) return;
- setUploadingFiles(prev => prev.map(f => f.id === fileToUpload.id ? { ...f, status: 'uploading', progress: 10 } : f));
+ setUploadingFiles(prev => prev.map(f => f.id === fileToUpload.id ? { ...f, status: 'uploading', progress: 10 } : f));
- const filePath = `${user.id}/${collection.id}/${Date.now()}_${fileToUpload.file.name.replace(/[^a-zA-Z0-9.\-_]/g, '')}`;
- const { error: uploadError } = await supabase.storage
- .from('pictures')
- .upload(filePath, fileToUpload.file, {
- cacheControl: '3600',
- upsert: false,
- });
+ const filePath = `${user.id}/${collection.id}/${Date.now()}_${fileToUpload.file.name.replace(/[^a-zA-Z0-9.\-_]/g, '')}`;
+ const { error: uploadError } = await supabase.storage
+ .from('pictures')
+ .upload(filePath, fileToUpload.file, {
+ cacheControl: '3600',
+ upsert: false,
+ });
- if (uploadError) throw uploadError;
-
- setUploadingFiles(prev => prev.map(f => f.id === fileToUpload.id ? { ...f, progress: 90, status: 'processing' } : f));
+ if (uploadError) throw uploadError;
- const { data: { publicUrl } } = supabase.storage.from('pictures').getPublicUrl(filePath);
+ setUploadingFiles(prev => prev.map(f => f.id === fileToUpload.id ? { ...f, progress: 90, status: 'processing' } : f));
- if (!publicUrl) throw new Error('Could not get public URL');
+ const { data: { publicUrl } } = supabase.storage.from('pictures').getPublicUrl(filePath);
- const { data: newPicture, error: insertPictureError } = await supabase
- .from('pictures')
- .insert({
- user_id: user.id,
- image_url: publicUrl,
- thumbnail_url: publicUrl,
- title: '',
- description: '',
- })
- .select()
- .single();
+ if (!publicUrl) throw new Error('Could not get public URL');
- if (insertPictureError) throw insertPictureError;
+ const { data: newPicture, error: insertPictureError } = await supabase
+ .from('pictures')
+ .insert({
+ user_id: user.id,
+ image_url: publicUrl,
+ thumbnail_url: publicUrl,
+ title: '',
+ description: '',
+ })
+ .select()
+ .single();
- const { error: insertCollectionPictureError } = await supabase
- .from('collection_pictures')
- .insert({
- collection_id: collection.id,
- picture_id: newPicture.id,
- });
+ if (insertPictureError) throw insertPictureError;
- if (insertCollectionPictureError) throw insertCollectionPictureError;
+ const { error: insertCollectionPictureError } = await supabase
+ .from('collection_pictures')
+ .insert({
+ collection_id: collection.id,
+ picture_id: newPicture.id,
+ });
- setUploadingFiles(prev => prev.map(f => f.id === fileToUpload.id ? { ...f, status: 'complete', progress: 100 } : f));
+ if (insertCollectionPictureError) throw insertCollectionPictureError;
- } catch (error: any) {
- console.error('Error uploading file:', error);
- setUploadingFiles(prev => prev.map(f => f.id === fileToUpload.id ? { ...f, status: 'error', error: error.message, progress: 100 } : f));
- }
+ setUploadingFiles(prev => prev.map(f => f.id === fileToUpload.id ? { ...f, status: 'complete', progress: 100 } : f));
+
+ } catch (error: any) {
+ console.error('Error uploading file:', error);
+ setUploadingFiles(prev => prev.map(f => f.id === fileToUpload.id ? { ...f, status: 'error', error: error.message, progress: 100 } : f));
+ }
});
};
@@ -185,15 +186,15 @@ const Collections = () => {
if (imageFiles.length === 0) return;
const newFiles: UploadingFile[] = imageFiles.map(file => ({
- id: `${file.name}-${file.lastModified}-${Math.random()}`,
- file,
- preview: URL.createObjectURL(file),
- progress: 0,
- status: 'pending',
+ id: `${file.name}-${file.lastModified}-${Math.random()}`,
+ file,
+ preview: URL.createObjectURL(file),
+ progress: 0,
+ status: 'pending',
}));
setUploadingFiles(prev => [...prev, ...newFiles]);
-
+
uploadAndProcessFiles(newFiles);
};
@@ -206,7 +207,7 @@ const Collections = () => {
const fetchCollection = async () => {
try {
setLoading(true);
-
+
// Fetch collection
const { data: collectionData, error: collectionError } = await supabase
.from('collections')
@@ -247,7 +248,7 @@ const Collections = () => {
const flattenedPictures = picturesData
.map(item => item.pictures)
.filter(Boolean) as Picture[];
-
+
// Add comment counts for each picture
const picturesWithCommentCounts = await Promise.all(
flattenedPictures.map(async (picture) => {
@@ -255,11 +256,11 @@ const Collections = () => {
.from('comments')
.select('*', { count: 'exact', head: true })
.eq('picture_id', picture.id);
-
+
return { ...picture, comments: [{ count: count || 0 }] };
})
);
-
+
setPictures(picturesWithCommentCounts);
} catch (error) {
console.error('Error:', error);
@@ -375,7 +376,7 @@ const Collections = () => {
});
setEditDialogOpen(false);
-
+
// Navigate to new slug if it changed
if (newSlug !== slug) {
navigate(`/collections/${userId}/${newSlug}`);
@@ -487,12 +488,12 @@ const Collections = () => {