diff --git a/app/flashcards/create/page.tsx b/app/flashcards/create/page.tsx new file mode 100644 index 0000000..755fa3a --- /dev/null +++ b/app/flashcards/create/page.tsx @@ -0,0 +1,341 @@ +"use client"; + +import React, { useState } from "react"; +import { useRouter } from "next/navigation"; +import { Form, Input, Upload, Button, message, Card, Row, Col, Select, Typography } from "antd"; +import { UploadOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"; +import { motion, AnimatePresence } from "framer-motion"; + +const { Text } = Typography; + +type FlashcardFormValues = { + deckTitle: string; + flashcards: { + question: string; + answer: string; + questionImageUrl?: string; + answerImageUrl?: string; + }[]; +}; + +/** + * Example image-upload function. + * Discuss with Melih & Shak how to do image upload logic + * in backend (endpoint URLs, form data, etc.). + */ +async function handleImageUpload(_file: File): Promise { + // e.g.: + // const formData = new FormData(); + // formData.append("file", file); + // const res = await apiService.post<{ imageUrl: string }>("/flashcards/upload-image", formData); + // return res.imageUrl; + + // For now, return a placeholder for demonstration: + console.log("Simulating image upload for file:", _file); + return "https://placekitten.com/200/300"; +} + +const CreateDeckPage: React.FC = () => { + const router = useRouter(); + const [form] = Form.useForm(); + const [bulkCount, setBulkCount] = useState(1); // for the “Add X flashcards” dropdown + + /** + * Called when user submits (clicks "Save Deck"). + * gather deckTitle + flashcards array and pass them to backend. + * + * Discuss with Melih & Shak how to store this deck in backend: + * - Endpoint: e.g. POST /decks + * - Body: { deckTitle, flashcards: [...] } + */ + const onFinish = async (values: FlashcardFormValues) => { + try { + // Just log values so it's used and not flagged by the linter: + console.log("Submitted values:", values); + + // e.g.: + // await apiService.post('/decks', values); + + message.success("Deck created successfully!"); + router.push("/home"); // Return to decks listing page + } catch (err) { + console.error(err); + message.error("Error creating deck."); + } + }; + + /** + * Because we use beforeUpload in , we intercept the file + * and manually handle the upload to get a URL, then store it in form state. + * + * Discuss with Melih & Shak how backend expects these uploads: + * - Auth headers? + * - Specific file field name? + */ + const handleBeforeUpload = async ( + file: File, + fieldName: [number, string] + ) => { + console.log("Uploading file:", file); + try { + const url = await handleImageUpload(file); + form.setFieldValue(["flashcards", fieldName[0], fieldName[1]], url); + message.success("Image uploaded!"); + } catch (err) { + console.error(err); + message.error("Image upload failed."); + } + return false; + }; + + return ( +
+

+ Create a New Deck +

+ +
+
+ Deck Title} + name="deckTitle" + rules={[{ required: true, message: "Please enter a deck title!" }]} + > + + + + + { + if (!flashcards || flashcards.length < 1) { + return Promise.reject(new Error("At least one flashcard is required.")); + } + }, + }, + ]} + > + {(fields, { add, remove }) => ( + <> + + {fields.map(({ key, name, ...restField }, index) => ( + + + + +
+ + Flashcard #{index + 1} + + {fields.length > 1 && ( + remove(name)} + style={{ + fontSize: 18, + cursor: "pointer", + color: "red", + }} + /> + )} +
+ + + + Topic / Question} + name={[name, "question"]} + rules={[{ required: true, message: "A question is required." }]} + > + + + + Question Image (Optional)} + name={[name, "questionImageUrl"]} + > + + handleBeforeUpload(file, [name, "questionImageUrl"]) + } + maxCount={1} + > + + + + + + + Answer / Memory} + name={[name, "answer"]} + rules={[{ required: true, message: "An answer is required." }]} + > + + + + Answer Image (Optional)} + name={[name, "answerImageUrl"]} + > + + handleBeforeUpload(file, [name, "answerImageUrl"]) + } + maxCount={1} + > + + + + +
+
+
+ ))} +
+ +
+ Add + +