diff --git a/src/admin/orders/OrderModal.jsx b/src/admin/orders/OrderModal.jsx
index 089e59a..d42e581 100644
--- a/src/admin/orders/OrderModal.jsx
+++ b/src/admin/orders/OrderModal.jsx
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
-import { X, Save, FileText } from 'lucide-react';
+import { X, Save, FileText, Camera, X as XIcon } from 'lucide-react';
+import { useOrderPhotos } from './useOrderPhotos';
import { pdf } from '@react-pdf/renderer';
import { supabase } from '../../lib/supabase';
import { useAuth } from '../auth/useAuth';
@@ -51,6 +52,7 @@ const OrderModal = ({ order, onClose, onSaved, mode = 'edit' }) => {
address: order?.address || '',
});
const [events, setEvents] = useState([]);
+ const { photos, uploading, uploadPhotos, deletePhoto } = useOrderPhotos(mode === 'edit' ? order?.id : null);
const [saving, setSaving] = useState(false);
const [downloading, setDownloading] = useState(false);
const [error, setError] = useState('');
@@ -277,6 +279,60 @@ const OrderModal = ({ order, onClose, onSaved, mode = 'edit' }) => {
+ {mode === 'edit' && (
+
+
+
+ {/* Thumbnail grid */}
+ {photos.length > 0 && (
+
+ {photos.map((photo) => (
+
+

+
+
+ ))}
+
+ )}
+
+ {/* Upload button */}
+
+
+ )}
+
{error && {error}
}
diff --git a/src/admin/orders/useOrderPhotos.js b/src/admin/orders/useOrderPhotos.js
new file mode 100644
index 0000000..3a9a9b1
--- /dev/null
+++ b/src/admin/orders/useOrderPhotos.js
@@ -0,0 +1,47 @@
+import { useState, useEffect, useCallback } from 'react';
+import { supabase } from '../../lib/supabase';
+
+const BUCKET = 'order-photos';
+
+export function useOrderPhotos(orderId) {
+ const [photos, setPhotos] = useState([]);
+ const [uploading, setUploading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const fetchPhotos = useCallback(async () => {
+ if (!orderId) return;
+ const { data, error: err } = await supabase.storage.from(BUCKET).list(`${orderId}/`);
+ if (err) { setError(err.message); return; }
+ const urls = (data || [])
+ .filter((f) => f.name !== '.emptyFolderPlaceholder')
+ .map((f) => ({
+ name: f.name,
+ url: supabase.storage.from(BUCKET).getPublicUrl(`${orderId}/${f.name}`).data.publicUrl,
+ }));
+ setPhotos(urls);
+ }, [orderId]);
+
+ useEffect(() => { fetchPhotos(); }, [fetchPhotos]);
+
+ const uploadPhotos = async (files) => {
+ if (!orderId || !files.length) return;
+ setUploading(true);
+ setError(null);
+ for (const file of Array.from(files)) {
+ const ext = file.name.split('.').pop();
+ const path = `${orderId}/${Date.now()}-${Math.random().toString(36).slice(2)}.${ext}`;
+ const { error: upErr } = await supabase.storage.from(BUCKET).upload(path, file, { upsert: false });
+ if (upErr) { setError(upErr.message); }
+ }
+ setUploading(false);
+ await fetchPhotos();
+ };
+
+ const deletePhoto = async (name) => {
+ const { error: delErr } = await supabase.storage.from(BUCKET).remove([`${orderId}/${name}`]);
+ if (delErr) { setError(delErr.message); return; }
+ setPhotos((prev) => prev.filter((p) => p.name !== name));
+ };
+
+ return { photos, uploading, error, uploadPhotos, deletePhoto };
+}
diff --git a/supabase/migrations/003_order_photos_bucket.sql b/supabase/migrations/003_order_photos_bucket.sql
new file mode 100644
index 0000000..379e442
--- /dev/null
+++ b/supabase/migrations/003_order_photos_bucket.sql
@@ -0,0 +1,22 @@
+-- Create storage bucket for order photos
+-- Run in Supabase SQL Editor
+INSERT INTO storage.buckets (id, name, public)
+VALUES ('order-photos', 'order-photos', true)
+ON CONFLICT (id) DO NOTHING;
+
+-- Allow authenticated users to upload
+CREATE POLICY "Authenticated users can upload order photos"
+ON storage.objects FOR INSERT
+TO authenticated
+WITH CHECK (bucket_id = 'order-photos');
+
+-- Allow authenticated users to delete own uploads
+CREATE POLICY "Authenticated users can delete order photos"
+ON storage.objects FOR DELETE
+TO authenticated
+USING (bucket_id = 'order-photos');
+
+-- Public read
+CREATE POLICY "Public read order photos"
+ON storage.objects FOR SELECT
+USING (bucket_id = 'order-photos');