Refactor: Remove photo reordering

This commit is contained in:
gpt-engineer-app[bot]
2025-10-02 14:07:12 +00:00
parent d00b0ba71a
commit 19a191e6a1
2 changed files with 65 additions and 113 deletions

View File

@@ -2,6 +2,13 @@ import { useState, useEffect } from 'react';
import { Camera, Upload, LogIn, Settings } from 'lucide-react'; import { Camera, Upload, LogIn, Settings } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { useAuth } from '@/hooks/useAuth'; import { useAuth } from '@/hooks/useAuth';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { UppyPhotoSubmissionUpload } from '@/components/upload/UppyPhotoSubmissionUpload'; import { UppyPhotoSubmissionUpload } from '@/components/upload/UppyPhotoSubmissionUpload';
@@ -35,10 +42,11 @@ export function EntityPhotoGallery({
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [selectedPhotoIndex, setSelectedPhotoIndex] = useState<number | null>(null); const [selectedPhotoIndex, setSelectedPhotoIndex] = useState<number | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [sortBy, setSortBy] = useState<'newest' | 'oldest'>('newest');
useEffect(() => { useEffect(() => {
fetchPhotos(); fetchPhotos();
}, [entityId, entityType]); }, [entityId, entityType, sortBy]);
const fetchPhotos = async () => { const fetchPhotos = async () => {
try { try {
@@ -50,8 +58,7 @@ export function EntityPhotoGallery({
.select('id, cloudflare_image_url, title, caption, submitted_by, created_at, order_index') .select('id, cloudflare_image_url, title, caption, submitted_by, created_at, order_index')
.eq('entity_type', entityType) .eq('entity_type', entityType)
.eq('entity_id', entityId) .eq('entity_id', entityId)
.order('order_index', { ascending: true }) .order('created_at', { ascending: sortBy === 'oldest' });
.order('created_at', { ascending: false });
console.log('📷 [FETCH PHOTOS] Query result:', { photoData, error, count: photoData?.length }); console.log('📷 [FETCH PHOTOS] Query result:', { photoData, error, count: photoData?.length });
@@ -132,34 +139,52 @@ export function EntityPhotoGallery({
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Header with Upload and Management Buttons */} {/* Header with Upload and Management Buttons */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 sm:gap-0 space-y-3 sm:space-y-0"> <div className="flex flex-col gap-3">
<div> <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<h3 className="text-base sm:text-lg font-semibold">Photo Gallery</h3> <div>
<p className="text-sm text-muted-foreground hidden sm:block"> <h3 className="text-base sm:text-lg font-semibold">Photo Gallery</h3>
Share your photos of {entityName} <p className="text-sm text-muted-foreground hidden sm:block">
</p> Share your photos of {entityName}
</div> </p>
<div className="flex flex-col sm:flex-row gap-2 w-full sm:w-auto"> </div>
{isModerator && photos.length > 0 && ( <div className="flex flex-col sm:flex-row gap-2 w-full sm:w-auto">
<Button onClick={() => setShowManagement(true)} variant="outline" className="gap-2 w-full sm:w-auto"> {isModerator && photos.length > 0 && (
<Settings className="w-4 h-4" /> <Button onClick={() => setShowManagement(true)} variant="outline" className="gap-2 w-full sm:w-auto">
<span className="sm:inline">Manage</span> <Settings className="w-4 h-4" />
</Button> <span className="sm:inline">Manage</span>
)} </Button>
<Button onClick={handleUploadClick} className="gap-2 w-full sm:w-auto">
{user ? (
<>
<Upload className="w-4 h-4" />
<span className="sm:inline">Upload Photos</span>
</>
) : (
<>
<LogIn className="w-4 h-4" />
<span className="sm:inline">Sign in to Upload</span>
</>
)} )}
</Button> <Button onClick={handleUploadClick} className="gap-2 w-full sm:w-auto">
{user ? (
<>
<Upload className="w-4 h-4" />
<span className="sm:inline">Upload Photos</span>
</>
) : (
<>
<LogIn className="w-4 h-4" />
<span className="sm:inline">Sign in to Upload</span>
</>
)}
</Button>
</div>
</div> </div>
{/* Sort Dropdown */}
{photos.length > 0 && (
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">Sort by:</span>
<Select value={sortBy} onValueChange={(value: 'newest' | 'oldest') => setSortBy(value)}>
<SelectTrigger className="w-full sm:w-[180px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="newest">Newest First</SelectItem>
<SelectItem value="oldest">Oldest First</SelectItem>
</SelectContent>
</Select>
</div>
)}
</div> </div>
{/* Photo Management Dialog */} {/* Photo Management Dialog */}
@@ -183,16 +208,11 @@ export function EntityPhotoGallery({
className="w-full h-48 sm:h-56 md:h-48 object-cover transition-transform cursor-pointer touch-manipulation active:opacity-80 sm:hover:scale-105" className="w-full h-48 sm:h-56 md:h-48 object-cover transition-transform cursor-pointer touch-manipulation active:opacity-80 sm:hover:scale-105"
onClick={() => handlePhotoClick(index)} onClick={() => handlePhotoClick(index)}
/> />
{(photo.title || photo.caption) && ( {photo.caption && (
<div className="p-2 sm:p-3"> <div className="p-2 sm:p-3">
{photo.title && ( <p className="text-[10px] sm:text-xs text-muted-foreground line-clamp-2">
<h4 className="font-medium text-xs sm:text-sm line-clamp-2">{photo.title}</h4> {photo.caption}
)} </p>
{photo.caption && (
<p className="text-[10px] sm:text-xs text-muted-foreground line-clamp-2 mt-1">
{photo.caption}
</p>
)}
</div> </div>
)} )}
</CardContent> </CardContent>

View File

@@ -13,7 +13,7 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { useToast } from '@/hooks/use-toast'; import { useToast } from '@/hooks/use-toast';
import { ArrowUp, ArrowDown, Trash2, Pencil } from 'lucide-react'; import { Trash2, Pencil } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
interface Photo { interface Photo {
@@ -75,53 +75,6 @@ export function PhotoManagementDialog({
} }
}; };
const movePhoto = async (photoId: string, direction: 'up' | 'down') => {
const currentIndex = photos.findIndex((p) => p.id === photoId);
if (
(direction === 'up' && currentIndex === 0) ||
(direction === 'down' && currentIndex === photos.length - 1)
) {
return;
}
const newPhotos = [...photos];
const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
[newPhotos[currentIndex], newPhotos[targetIndex]] = [
newPhotos[targetIndex],
newPhotos[currentIndex],
];
// Update order_index for both photos
const updates = [
{ id: newPhotos[currentIndex].id, order_index: currentIndex },
{ id: newPhotos[targetIndex].id, order_index: targetIndex },
];
try {
for (const update of updates) {
const { error } = await supabase
.from('photos')
.update({ order_index: update.order_index })
.eq('id', update.id);
if (error) throw error;
}
setPhotos(newPhotos);
toast({
title: 'Success',
description: 'Photo order updated',
});
onUpdate?.();
} catch (error) {
console.error('Error updating photo order:', error);
toast({
title: 'Error',
description: 'Failed to update photo order',
variant: 'destructive',
});
}
};
const deletePhoto = async (photoId: string) => { const deletePhoto = async (photoId: string) => {
@@ -227,7 +180,7 @@ export function PhotoManagementDialog({
<DialogHeader> <DialogHeader>
<DialogTitle>Manage Photos</DialogTitle> <DialogTitle>Manage Photos</DialogTitle>
<DialogDescription> <DialogDescription>
Reorder, edit, or delete photos for this entity Edit or delete photos for this entity
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -262,45 +215,24 @@ export function PhotoManagementDialog({
</p> </p>
</div> </div>
<div className="grid grid-cols-2 sm:flex sm:flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<Button
size="sm"
variant="outline"
onClick={() => movePhoto(photo.id, 'up')}
disabled={index === 0}
className="justify-start sm:justify-center"
>
<ArrowUp className="w-4 h-4 mr-2" />
<span className="sm:hidden">Move Up</span>
</Button>
<Button
size="sm"
variant="outline"
onClick={() => movePhoto(photo.id, 'down')}
disabled={index === photos.length - 1}
className="justify-start sm:justify-center"
>
<ArrowDown className="w-4 h-4 mr-2" />
<span className="sm:hidden">Move Down</span>
</Button>
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
onClick={() => setEditingPhoto(photo)} onClick={() => setEditingPhoto(photo)}
className="justify-start sm:justify-center" className="flex-1 sm:flex-initial"
> >
<Pencil className="w-4 h-4 mr-2" /> <Pencil className="w-4 h-4 mr-2" />
<span className="sm:hidden">Edit Caption</span> Edit
<span className="hidden sm:inline">Edit</span>
</Button> </Button>
<Button <Button
size="sm" size="sm"
variant="destructive" variant="destructive"
onClick={() => deletePhoto(photo.id)} onClick={() => deletePhoto(photo.id)}
className="justify-start sm:justify-center" className="flex-1 sm:flex-initial"
> >
<Trash2 className="w-4 h-4 mr-2" /> <Trash2 className="w-4 h-4 mr-2" />
<span className="sm:hidden">Delete Photo</span> Delete
</Button> </Button>
</div> </div>
</div> </div>