diff --git a/.env.example b/.env.example deleted file mode 100644 index b38780d..0000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -NEXT_PUBLIC_APPWRITE_ENDPOINT= -NEXT_PUBLIC_APPWRITE_PROJECT_ID= -NEXT_PUBLIC_APPWRITE_PROJECT_NAME= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 372683d..c7fce78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@appwrite.io/pink-icons": "^1.0.0", "@tailwindcss/postcss": "^4.0.14", "appwrite": "^16.1.0", + "date-fns": "^4.1.0", "next": "14.2.15", "postcss": "^8.5.3", "react": "^18", @@ -472,6 +473,15 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", diff --git a/package.json b/package.json index 2660285..415085b 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@appwrite.io/pink-icons": "^1.0.0", "@tailwindcss/postcss": "^4.0.14", "appwrite": "^16.1.0", + "date-fns": "^4.1.0", "next": "14.2.15", "postcss": "^8.5.3", "react": "^18", diff --git a/src/app/page.js b/src/app/page.js index e6926f5..2e37d4d 100644 --- a/src/app/page.js +++ b/src/app/page.js @@ -3,309 +3,756 @@ import "./app.css"; import "@appwrite.io/pink-icons"; import { useState, useEffect, useRef, useCallback } from "react"; -import { client } from "@/lib/appwrite"; -import { AppwriteException } from "appwrite"; +import { client, databases, storage } from "@/lib/appwrite"; +import { AppwriteException, ID } from "appwrite"; import NextjsLogo from "../static/nextjs-icon.svg"; import AppwriteLogo from "../static/appwrite-icon.svg"; import Image from "next/image"; export default function Home() { - const [detailHeight, setDetailHeight] = useState(55); - const [logs, setLogs] = useState([]); - const [status, setStatus] = useState("idle"); - const [showLogs, setShowLogs] = useState(false); + const [title, setTitle] = useState(''); + const [successMessage, setSuccessMessage] = useState(''); + const [completeMessage, setCompleteMessage] = useState(''); + const [failedMessage, setFailedMessage] = useState(''); + const [deleteMessage, setDeleteMessage] = useState(''); + const [editMessage, setEditMessage] = useState(''); + const [isEditing, setIsEditing] = useState(''); + const [editTodo, setEditTodo] = useState(null); + const [tab, setTab] = useState('hi'); + const [todos, setTodos] = useState([]); - const detailsRef = useRef(null); - const updateHeight = useCallback(() => { - if (detailsRef.current) { - setDetailHeight(detailsRef.current.clientHeight); - } - }, [logs, showLogs]); + const fileInputRef = useRef(null); - useEffect(() => { - updateHeight(); - window.addEventListener("resize", updateHeight); - return () => window.removeEventListener("resize", updateHeight); - }, [updateHeight]); + // post + const handleSubmit = async (e) => { + e.preventDefault(); - useEffect(() => { - if (!detailsRef.current) return; - detailsRef.current.addEventListener("toggle", updateHeight); + const file = fileInputRef.current?.files?.[0]; - return () => { - if (!detailsRef.current) return; - detailsRef.current.removeEventListener("toggle", updateHeight); - }; - }, []); + if (!title.trim()) { + alert('Please enter a title.'); + return; + } + + if (!file) { + alert('Please attach a media file.'); + return; + } - async function sendPing() { - if (status === "loading") return; - setStatus("loading"); try { - const result = await client.ping(); - const log = { - date: new Date(), - method: "GET", - path: "/v1/ping", - status: 200, - response: JSON.stringify(result), - }; - setLogs((prevLogs) => [log, ...prevLogs]); - setStatus("success"); + const results = await storage.createFile( + (process.env.NEXT_PUBLIC_APPWRITE_BUCKET_ID), // bucketId + ID.unique(), // fileId + file, // file + ); + + const result = await databases.createDocument( + (process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID), // databaseId + (process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_ID), + ID.unique(), // documentId + { //data + title: title, + media: [results.$id], + status: 'pending' + }, + ); + + // Set success message + setSuccessMessage('Todo added successfully!'); + + // clear form fields + setTitle(''); + if (fileInputRef.current) fileInputRef.current.value = null; + + setTimeout(() => setSuccessMessage(''), 5000); + fetchTodos(); + } catch (err) { - const log = { - date: new Date(), - method: "GET", - path: "/v1/ping", - status: err instanceof AppwriteException ? err.code : 500, - response: - err instanceof AppwriteException - ? err.message - : "Something went wrong", - }; - setLogs((prevLogs) => [log, ...prevLogs]); - setStatus("error"); + console.error('Error uploading:', err); + alert('An error occurred.'); + } + }; + + // fetch + const fetchTodos = async () => { + try { + const response = await databases.listDocuments( //fetches multiple documents from a collection. + (process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID), // databaseId + (process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_ID), + ); + setTodos(response.documents); + } catch (error) { + console.error('Failed to fetch todos:', error); + } + }; + + // delete + const handleDelete = async (id) => { + try { + await databases.deleteDocument( + (process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID), // databaseId + (process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_ID), + // ID.unique(), // documentId + id + ); + // Set success message + setDeleteMessage('Todo Deleted'); + setTimeout(() => setDeleteMessage(''), 5000); + // Refresh the list + fetchTodos(); + } catch (error) { + console.error('Failed to delete todo:', error); + } + }; + + const completeTodos = todos.filter((todo) => todo.status === 'completed'); + const failedTodos = todos.filter((todo) => todo.status === 'failed'); + const pendingTodos = todos.filter((todo) => todo.status === 'pending'); + + + // complete status + const handleComplete = async (id) => { + if (!id) { + console.error("Missing document ID"); + return; + } + + try { + await databases.updateDocument( + (process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID), // databaseId + (process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_ID), + id, // Document ID + { status: "completed" } // Fields to update + ); + + // Set success message + setCompleteMessage('Todo marked completed!'); + + setTimeout(() => setCompleteMessage(''), 5000); + fetchTodos(); // Refresh the todo list + } catch (error) { + console.error("Failed to mark todo as complete:", error); } - setShowLogs(true); - } + }; + + // failed status + const handleFailed = async (id) => { + if (!id) { + console.error("Missing document ID"); + return; + } + try { + await databases.updateDocument( + (process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID), // databaseId + (process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_ID), + id, // Document ID + { status: "failed" } // Fields to update + ); + // Set success message + setFailedMessage('Todo marked failed!'); + setTimeout(() => setFailedMessage(''), 5000); + fetchTodos(); // Refresh the todo list + } catch (error) { + console.error("Failed to mark todo as fail:", error); + } + }; + + // edit + const handleEdit = (todo) => { + setIsEditing(true); + setEditTodo(todo); + setTitle(todo.title); + }; + + const handleUpdate = async (e) => { + e.preventDefault(); + if (!editTodo) return; + + try { + await databases.updateDocument( // service that lets you update an existing document inside a collection + (process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID), // databaseId + (process.env.NEXT_PUBLIC_APPWRITE_COLLECTION_ID), + editTodo.$id, + { + title, + } + ); + // Set success message + setEditMessage('Todo Updated'); + setTimeout(() => setEditMessage(''), 5000); + setTitle(''); + setEditTodo(null); + setIsEditing(false); + fetchTodos(); + } catch (error) { + console.error('Failed to update todo:', error); + } + }; + + + useEffect(() => { + fetchTodos(); + }, []); return (
-
-
-
- {"Next.js -
+
+

Todo Masterpiece

+

Organise your tasks with style and efficiency

+
+ {/* top */} +
+
+
{pendingTodos.length}
+
Total
-
-
-
-
+
+
{pendingTodos.length}
+
Pending
-
-
- {"Appwrite -
+
+
{completeTodos.length}
+
Completed
+
+ +
+
{failedTodos.length}
+
Failed
-
- {status === "loading" ? ( -
-
- - Loading... -
- Waiting for connection... -
- ) : status === "success" ? ( -

- Congratulations! -

- ) : ( -

- Check connection -

- )} - -

- {status === "success" ? ( - You connected your app successfully. - ) : status === "error" || status === "idle" ? ( - Send a ping to verify the connection - ) : null} -

- - -
- -
-
-

Edit your app

-

- Edit{" "} - app/page.js to - get started with building your app. -

+ {/* tabs */} +
+
+ +
- -
-
-

- Go to console -

- -
-

- Navigate to the console to control and oversee the Appwrite - services. -

-
-
- - -
-
-

- Explore docs -

- -
-

- Discover the full power of Appwrite by diving into our - documentation. -

-
-
- -
+ )} + + + + ); } diff --git a/src/lib/appwrite.js b/src/lib/appwrite.js index 537ab08..aeefa94 100644 --- a/src/lib/appwrite.js +++ b/src/lib/appwrite.js @@ -1,10 +1,13 @@ -import { Client, Account, Databases } from "appwrite"; +import { Client, Account, Databases, Storage } from "appwrite"; const client = new Client() - .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT) - .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID); +.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT ?? "") +.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID ?? ""); + // .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT) + // .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID); const account = new Account(client); const databases = new Databases(client); +const storage = new Storage(client); -export { client, account, databases }; +export { client, account, databases , storage};