feat(page): wire image upload state, base64 conversion, and API integration into home page
This commit is contained in:
@@ -21,6 +21,14 @@ import { EventsList } from "@/components/events-list";
|
|||||||
import { EventDialog } from "@/components/event-dialog";
|
import { EventDialog } from "@/components/event-dialog";
|
||||||
import { DragDropContainer } from "@/components/drag-drop-container";
|
import { DragDropContainer } from "@/components/drag-drop-container";
|
||||||
|
|
||||||
|
const fileToBase64 = (file: File): Promise<string> =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => resolve(reader.result as string);
|
||||||
|
reader.onerror = () => reject(new Error("Failed to read file"));
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const [events, setEvents] = useState<CalendarEvent[]>([]);
|
const [events, setEvents] = useState<CalendarEvent[]>([]);
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
@@ -45,6 +53,10 @@ export default function HomePage() {
|
|||||||
const [summary, setSummary] = useState<string | null>(null);
|
const [summary, setSummary] = useState<string | null>(null);
|
||||||
const [summaryUpdated, setSummaryUpdated] = useState<string | null>(null);
|
const [summaryUpdated, setSummaryUpdated] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Image
|
||||||
|
const [imageBase64, setImageBase64] = useState<string | null>(null);
|
||||||
|
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const stored = await getAllEvents();
|
const stored = await getAllEvents();
|
||||||
@@ -66,6 +78,20 @@ export default function HomePage() {
|
|||||||
setRecurrenceRule(undefined);
|
setRecurrenceRule(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleImageSelect = async (file: File) => {
|
||||||
|
const base64 = await fileToBase64(file);
|
||||||
|
setImageBase64(base64);
|
||||||
|
setImagePreview(URL.createObjectURL(file));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImageClear = () => {
|
||||||
|
if (imagePreview) {
|
||||||
|
URL.revokeObjectURL(imagePreview);
|
||||||
|
}
|
||||||
|
setImageBase64(null);
|
||||||
|
setImagePreview(null);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
const eventData: CalendarEvent = {
|
const eventData: CalendarEvent = {
|
||||||
id: editingId || nanoid(),
|
id: editingId || nanoid(),
|
||||||
@@ -130,7 +156,7 @@ export default function HomePage() {
|
|||||||
|
|
||||||
// AI Create Event
|
// AI Create Event
|
||||||
const handleAiCreate = async () => {
|
const handleAiCreate = async () => {
|
||||||
if (!aiPrompt.trim()) return;
|
if (!aiPrompt.trim() && !imageBase64) return;
|
||||||
setAiLoading(true);
|
setAiLoading(true);
|
||||||
|
|
||||||
const promise = (): Promise<{ message: string }> =>
|
const promise = (): Promise<{ message: string }> =>
|
||||||
@@ -139,7 +165,10 @@ export default function HomePage() {
|
|||||||
const res = await fetch("/api/ai-event", {
|
const res = await fetch("/api/ai-event", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ prompt: aiPrompt }),
|
body: JSON.stringify({
|
||||||
|
prompt: aiPrompt,
|
||||||
|
imageBase64: imageBase64 || undefined,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 401) {
|
if (res.status === 401) {
|
||||||
@@ -154,7 +183,6 @@ export default function HomePage() {
|
|||||||
|
|
||||||
if (Array.isArray(data) && data.length > 0) {
|
if (Array.isArray(data) && data.length > 0) {
|
||||||
if (data.length === 1) {
|
if (data.length === 1) {
|
||||||
// Prefill dialog directly (same as before)
|
|
||||||
const ev = data[0];
|
const ev = data[0];
|
||||||
setTitle(ev.title || "");
|
setTitle(ev.title || "");
|
||||||
setDescription(ev.description || "");
|
setDescription(ev.description || "");
|
||||||
@@ -167,11 +195,11 @@ export default function HomePage() {
|
|||||||
setAiPrompt("");
|
setAiPrompt("");
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
setRecurrenceRule(ev.recurrenceRule || undefined);
|
setRecurrenceRule(ev.recurrenceRule || undefined);
|
||||||
|
handleImageClear();
|
||||||
resolve({
|
resolve({
|
||||||
message: "Event has been created!",
|
message: "Event has been created!",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Save them all directly to DB
|
|
||||||
for (const ev of data) {
|
for (const ev of data) {
|
||||||
const newEvent = {
|
const newEvent = {
|
||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
@@ -186,8 +214,9 @@ export default function HomePage() {
|
|||||||
setAiPrompt("");
|
setAiPrompt("");
|
||||||
setSummary(`Added ${data.length} AI-generated events.`);
|
setSummary(`Added ${data.length} AI-generated events.`);
|
||||||
setSummaryUpdated(new Date().toLocaleString());
|
setSummaryUpdated(new Date().toLocaleString());
|
||||||
|
handleImageClear();
|
||||||
resolve({
|
resolve({
|
||||||
message: "Event has been created!",
|
message: "Events have been created!",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -264,6 +293,7 @@ export default function HomePage() {
|
|||||||
isDragOver={isDragOver}
|
isDragOver={isDragOver}
|
||||||
setIsDragOver={setIsDragOver}
|
setIsDragOver={setIsDragOver}
|
||||||
onImport={handleImport}
|
onImport={handleImport}
|
||||||
|
onImageDrop={handleImageSelect}
|
||||||
>
|
>
|
||||||
<AIToolbar
|
<AIToolbar
|
||||||
isAuthenticated={!!session?.user}
|
isAuthenticated={!!session?.user}
|
||||||
@@ -271,6 +301,9 @@ export default function HomePage() {
|
|||||||
aiPrompt={aiPrompt}
|
aiPrompt={aiPrompt}
|
||||||
setAiPrompt={setAiPrompt}
|
setAiPrompt={setAiPrompt}
|
||||||
aiLoading={aiLoading}
|
aiLoading={aiLoading}
|
||||||
|
imagePreview={imagePreview}
|
||||||
|
onImageSelect={handleImageSelect}
|
||||||
|
onImageClear={handleImageClear}
|
||||||
onAiCreate={handleAiCreate}
|
onAiCreate={handleAiCreate}
|
||||||
onAiSummarize={handleAiSummarize}
|
onAiSummarize={handleAiSummarize}
|
||||||
summary={summary}
|
summary={summary}
|
||||||
|
|||||||
Reference in New Issue
Block a user