mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 02:51:12 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
230
src-old/components/lists/ListItemEditor.tsx
Normal file
230
src-old/components/lists/ListItemEditor.tsx
Normal file
@@ -0,0 +1,230 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { UserTopList, UserTopListItem } from "@/types/database";
|
||||
import { supabase } from "@/lib/supabaseClient";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { GripVertical, Trash2, Plus } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { ListSearch } from "./ListSearch";
|
||||
import { getErrorMessage } from "@/lib/errorHandler";
|
||||
|
||||
interface ListItemEditorProps {
|
||||
list: UserTopList;
|
||||
onUpdate: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function ListItemEditor({ list, onUpdate, onClose }: ListItemEditorProps) {
|
||||
const [items, setItems] = useState<UserTopListItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showSearch, setShowSearch] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchItems();
|
||||
}, [list.id]);
|
||||
|
||||
const fetchItems = async () => {
|
||||
setLoading(true);
|
||||
const { data, error } = await supabase
|
||||
.from("user_top_list_items")
|
||||
.select("*")
|
||||
.eq("list_id", list.id)
|
||||
.order("position", { ascending: true });
|
||||
|
||||
if (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
toast.error("Failed to load list items", {
|
||||
description: errorMessage
|
||||
});
|
||||
} else {
|
||||
setItems(data as UserTopListItem[]);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleAddItem = async (entityType: string, entityId: string, entityName: string) => {
|
||||
const newPosition = items.length + 1;
|
||||
|
||||
const { error } = await supabase
|
||||
.from("user_top_list_items")
|
||||
.insert({
|
||||
list_id: list.id,
|
||||
entity_type: entityType,
|
||||
entity_id: entityId,
|
||||
position: newPosition,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
if (error.code === "23505") {
|
||||
toast.error("This item is already in your list");
|
||||
} else {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
toast.error("Failed to add item", {
|
||||
description: errorMessage
|
||||
});
|
||||
}
|
||||
} else {
|
||||
toast.success(`Added ${entityName} to list`);
|
||||
fetchItems();
|
||||
onUpdate();
|
||||
setShowSearch(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveItem = async (itemId: string) => {
|
||||
const { error } = await supabase
|
||||
.from("user_top_list_items")
|
||||
.delete()
|
||||
.eq("id", itemId);
|
||||
|
||||
if (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
toast.error("Failed to remove item", {
|
||||
description: errorMessage
|
||||
});
|
||||
} else {
|
||||
toast.success("Item removed");
|
||||
// Reorder remaining items
|
||||
const remainingItems = items.filter(i => i.id !== itemId);
|
||||
await reorderItems(remainingItems);
|
||||
fetchItems();
|
||||
onUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateNotes = async (itemId: string, notes: string) => {
|
||||
const { error } = await supabase
|
||||
.from("user_top_list_items")
|
||||
.update({ notes })
|
||||
.eq("id", itemId);
|
||||
|
||||
if (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
toast.error("Failed to update notes", {
|
||||
description: errorMessage
|
||||
});
|
||||
} else {
|
||||
setItems(items.map(i => i.id === itemId ? { ...i, notes } : i));
|
||||
}
|
||||
};
|
||||
|
||||
const handleMoveItem = async (itemId: string, direction: "up" | "down") => {
|
||||
const currentIndex = items.findIndex(i => i.id === itemId);
|
||||
if (
|
||||
(direction === "up" && currentIndex === 0) ||
|
||||
(direction === "down" && currentIndex === items.length - 1)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newItems = [...items];
|
||||
const swapIndex = direction === "up" ? currentIndex - 1 : currentIndex + 1;
|
||||
[newItems[currentIndex], newItems[swapIndex]] = [newItems[swapIndex], newItems[currentIndex]];
|
||||
|
||||
await reorderItems(newItems);
|
||||
setItems(newItems);
|
||||
onUpdate();
|
||||
};
|
||||
|
||||
const reorderItems = async (orderedItems: UserTopListItem[]) => {
|
||||
const updates = orderedItems.map((item, index) => ({
|
||||
id: item.id,
|
||||
position: index + 1,
|
||||
}));
|
||||
|
||||
for (const update of updates) {
|
||||
await supabase
|
||||
.from("user_top_list_items")
|
||||
.update({ position: update.position })
|
||||
.eq("id", update.id);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div className="text-center py-4">Loading items...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<h3 className="font-semibold">Edit List Items</h3>
|
||||
<div className="space-x-2">
|
||||
<Button size="sm" onClick={() => setShowSearch(!showSearch)}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add Item
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={onClose}>
|
||||
Done
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showSearch && (
|
||||
<ListSearch
|
||||
listType={list.list_type}
|
||||
onSelect={handleAddItem}
|
||||
onClose={() => setShowSearch(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{items.length === 0 ? (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
No items in this list yet. Click "Add Item" to get started.
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{items.map((item, index) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="flex items-start gap-2 p-3 border rounded-lg bg-card"
|
||||
>
|
||||
<div className="flex flex-col gap-1 mt-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6"
|
||||
onClick={() => handleMoveItem(item.id, "up")}
|
||||
disabled={index === 0}
|
||||
>
|
||||
<GripVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
<span className="text-xs text-muted-foreground text-center">
|
||||
{index + 1}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex justify-between items-start">
|
||||
<div>
|
||||
<p className="font-medium">{item.entity_type} - {item.entity_id}</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => handleRemoveItem(item.id)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor={`notes-${item.id}`} className="text-xs">
|
||||
Notes (optional)
|
||||
</Label>
|
||||
<Textarea
|
||||
id={`notes-${item.id}`}
|
||||
value={item.notes || ""}
|
||||
onChange={(e) => handleUpdateNotes(item.id, e.target.value)}
|
||||
placeholder="Add personal notes about this item..."
|
||||
className="h-16 text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user