diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 71a206a..1aadd9e 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -12,16 +12,17 @@ services: - ./frontend/.env.development ports: - 3000:3000 - - 5555:5555 volumes: - ./frontend/pages:/app/pages - ./frontend/components:/app/components - ./frontend/styles:/app/styles - - ./images/uploads:/app/uploads + - ./uploads:/app/uploads db: image: postgres:latest container_name: snap-sorter-database-dev restart: always + ports: + - 5432:5432 env_file: - ./frontend/.env.development volumes: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index b06c7d2..5f491df 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -10,16 +10,19 @@ services: env_file: - ./frontend/.env.production ports: - - 80:3000 + - 3000:3000 volumes: - ./frontend/pages:/app/pages - ./frontend/components:/app/components - ./frontend/styles:/app/styles + - ./uploads:/app/uploads image: snap-sorter-frontend-prod db: image: postgres:latest container_name: snap-sorter-database-prod restart: always + ports: + - 5432:5432 env_file: - ./frontend/.env.production volumes: diff --git a/frontend/README.md b/frontend/README.md index a610bb4..aa0503f 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -6,8 +6,8 @@ #### Components -- [ ] Navbar - - [ ] Logo which takes to `search` page when logged in else to `login` +- [x] Navbar + - [x] Logo which takes to `search` page when logged in else to `login` - [x] Links to each page (profile, search, announcements) - [x] Make it dynamic when showing the pages i.e., do not display announcements route when on announcements page, show other routes only when logged in. - [ ] Footer @@ -20,7 +20,7 @@ - [x] Login page - [x] Login button which initiates the oauth flow - [x] Announcements - - [ ] Integrate with backend + - [x] Integrate with backend - [x] Get data from announcements endpoint and display sorted by latest - [x] Profile - [x] Dropzone from mantine to collect images @@ -42,7 +42,7 @@ #### Endpoints -- [ ] Announcements +- [x] Announcements - Get announcements from DB - [x] Store images - Take the images and dump them in some folder called `/temp/images` as `studentuid_imagename.extension` @@ -52,21 +52,22 @@ - Return images from the `Unknown schema joined with Dopy Image` table with tags - [x] Auth - Nextauth will do the needful (more deep dive on what exactly is happening might be needed) -- [ ] Update tags for a given image id +- [x] Update tags for a given image id - Endpoint will be a `PATCH` request which takes image id and the updated tags and updates them in the db. #### Database - [x] Prisma or Drizzle with postgresql -- [ ] Announcement schema - {uuid, title, description, created at time stamp} +- [x] Announcement schema - {uuid, title, description, created at time stamp} - [x] Upload Image - {uuid, fk to student uid, image path} -- [ ] Some Unknown schema to store tagged image - {fk to student uid, image id} -- [ ] Dopy Image - {uuid, image path, event name, tags} +- [x] Tag - {uuid, tag value, fk to image uids} +- [x] Event - {uuid, event name, fk to image uids} +- [x] Dopy Image - {uuid, image path, fk to event uid, fk to tag uids, fk to tagged student uids} ### Config & Misc -- [ ] Docker files - - [ ] Production +- [x] Docker files + - [x] Production - [x] Development - [x] Config - [x] Prettier @@ -78,7 +79,7 @@ ### Development -1. Rename `.env.example` as `.env` or `.env.development` and add your `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` from google cloud console. +1. Rename `.env.example` as `.env.development` and add your `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` from google cloud console. Callback url on google cloud console is `http://localhost:3000/api/auth/callback/google`. 2. Run the following command and visit http://localhost:3000 ```bash docker compose -f docker-compose.dev.yml up @@ -88,3 +89,14 @@ pnpm install pnpm prisma generate ``` + +### Production + +1. Rename `.env.example` as `.env`(for prisma's sake as of now) and `.env.production`. +2. Add your `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` from google cloud console. Callback url on google cloud console is `http://localhost:3000/api/auth/callback/google`. Add a secret to `NEXTAUTH_SECRET`. +3. Run the following command and visit http://localhost:3000 + ```bash + docker compose -f docker-compose.prod.yml up + ``` +4. As of now locally you have to run `npx --yes prisma migrate deploy` to migrate your schema to the database. +5. Because of how bind mounts work the `uploads` folder ownership needs to be changed. So run `chown -R 1001:1001 uploads` on the server/host. diff --git a/frontend/components/ImageWithModal.tsx b/frontend/components/ImageWithModal.tsx index 062b7c5..35c21cb 100644 --- a/frontend/components/ImageWithModal.tsx +++ b/frontend/components/ImageWithModal.tsx @@ -7,31 +7,52 @@ import { Icon } from "@iconify/react"; import { ActionIcon, + Alert, Badge, Button, Group, Image, Modal, + Stack, TextInput, } from "@mantine/core"; import { useDisclosure } from "@mantine/hooks"; import { useState } from "react"; type Props = { + id: string; imageUrl: string; tagsFromDatabase: string[]; }; export default function ImagePreviewModal({ + id, imageUrl, tagsFromDatabase, }: Props) { const [opened, { open, close }] = useDisclosure(false); const [tags, setTags] = useState(tagsFromDatabase); const [newTag, setNewTag] = useState(""); + const [updateStatus, setUpdateStatus] = useState(""); async function handleUpdateTags() { - console.log(tags); + const add = tags.filter((tag) => !tagsFromDatabase.includes(tag)); + const remove = tagsFromDatabase.filter((tag) => !tags.includes(tag)); + + const response = await fetch("/api/updateTags", { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + image: id, + add, + remove, + }), + }); + + setUpdateStatus(response.ok ? "success" : "failure"); } return ( @@ -40,23 +61,17 @@ export default function ImagePreviewModal({ alt={imageUrl} src={imageUrl} onClick={open} - imageProps={{ onLoad: () => URL.revokeObjectURL(imageUrl) }} className="cursor-pointer" />
- {imageUrl} URL.revokeObjectURL(imageUrl) }} - /> + {imageUrl}
{tags.map((tag, badgeIndex) => ( - + setNewTag(event.currentTarget.value)} /> - - - + + + + + {updateStatus === "success" && ( + } + title="Updated successfully!" + color="green" + radius="md" + withCloseButton + onClose={() => setUpdateStatus("")}> + {} + + )} + {updateStatus === "failure" && ( + } + title="Tag update failed!" + color="red" + radius="md" + withCloseButton + onClose={() => setUpdateStatus("")}> + {} + + )} +
diff --git a/frontend/components/Navbar.tsx b/frontend/components/Navbar.tsx index 8000077..add3907 100644 --- a/frontend/components/Navbar.tsx +++ b/frontend/components/Navbar.tsx @@ -1,49 +1,74 @@ // Pitfalls: // Since tailwind preflight is disabled some of these styles overwrite the default styles or make use of the default styles // For example here 'px-0` is needed to remove the default unordered list padding -import { Anchor, Button } from "@mantine/core"; +import { Button } from "@mantine/core"; import { signOut, useSession } from "next-auth/react"; import Link from "next/link"; +import { useRouter } from "next/router"; export default function Navbar() { const { data: session } = useSession(); + const router = useRouter(); + + const protectedRoutes = [ + { + name: "profile", + path: "/app/profile", + }, + { + name: "search", + path: "/app/search", + }, + ]; + + const unprotectedRoutes = [ + { + name: "announcements", + path: "/announcements", + }, + ]; + return ( -