Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend tagging & search #44

Merged
merged 67 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
3bbfc59
fix: only allow BITS email ids
skoriop Aug 4, 2023
a1cc577
feat: add DopyImage and Tag models to prisma
skoriop Aug 5, 2023
8c826c6
feat: add image tagging endpoint
skoriop Aug 5, 2023
29283d1
feat: add user roles
skoriop Aug 5, 2023
18db6b0
fix: restrict tagImages endpoint to admins only
skoriop Aug 5, 2023
ed82dbf
build: expose db port, remove unnecessary port, change bind mount dir
pnicto Aug 5, 2023
0a7b8d8
build: remove line erroring the build process as the file does not exist
pnicto Aug 5, 2023
58b87cd
build: create uploads directory and give necessary perms
pnicto Aug 5, 2023
53065fd
build: change exposed ports for nextauth in prod, simplify bind mount…
pnicto Aug 5, 2023
82d3dad
docs: add info about running the prod image
pnicto Aug 5, 2023
f4fab59
feat: modfied profile page for dopy accounts
skoriop Aug 6, 2023
e4642c0
feat: modify image upload for dopy
skoriop Aug 6, 2023
cb58c89
refactor: remove userData variable
skoriop Aug 6, 2023
7308bc0
refactor: move login page to pages folder
skoriop Aug 6, 2023
7e2df15
refactor: rename db table names
skoriop Aug 6, 2023
bc45b35
feat: add update tags endpoint
skoriop Aug 6, 2023
27b2d66
Revert "feat: modify image upload for dopy"
skoriop Aug 13, 2023
88ad7ba
Revert "feat: modfied profile page for dopy accounts"
skoriop Aug 13, 2023
0c9608b
fix: update tag model in prisma
skoriop Aug 13, 2023
f4f078d
fix: change updateTags endpoint to use tag values
skoriop Aug 14, 2023
7c26f24
feat: connect frontend to update tags endpoint
skoriop Aug 14, 2023
74a6f9a
fix: make case of tag value uniform
skoriop Aug 14, 2023
fadad00
fix: button is adjacent to text input sometimes
skoriop Aug 16, 2023
24e43ce
feat: display tag update status
skoriop Aug 16, 2023
8b52651
fix: prevent adding duplicate tags
skoriop Aug 16, 2023
81bc502
refactor: store route data in array
skoriop Aug 16, 2023
eb3fcaf
feat: hide current page in navbar
skoriop Aug 16, 2023
13aa4b6
docs: update project status
skoriop Aug 16, 2023
dcc9a74
fix: add key prop to fix eslint error
skoriop Aug 16, 2023
57620c0
refactor: add filepath to dopy image schema
pnicto Aug 19, 2023
5774642
fix: replace id with filepath for dopyimage
skoriop Aug 19, 2023
aedbf7f
test: add testing endpoint
skoriop Aug 19, 2023
4ddd86c
test: add tagged users to test data
skoriop Aug 19, 2023
4f24ee3
feat: finish search by event and uid
pnicto Aug 19, 2023
0f5d050
feat: fetch events from database
pnicto Aug 19, 2023
db58e7e
test: add test event year
pnicto Aug 19, 2023
455aad2
feat: add the request to get the images for given filters
pnicto Aug 19, 2023
27bf0b8
refactor: add event year as part of label
pnicto Aug 19, 2023
5a61845
refactor: add event field to event schema
pnicto Aug 19, 2023
9cd67f6
refactor: replace logo with login button
pnicto Aug 19, 2023
1fae507
fix: make the navbar responsive
pnicto Aug 19, 2023
354f431
refactor: replace id with filepath as per the schema update
pnicto Aug 19, 2023
1c31fd2
feat: integrate search endpoint with frontend
skoriop Aug 19, 2023
e910478
Merge branch 'frontend-tagging' of https://github.com/crux-bphc/snap-…
skoriop Aug 19, 2023
47e6b14
fix: searchImages should use `some`, not `every`
skoriop Aug 19, 2023
62e1ffb
fix: modal image previews on other browsers that are not firefox
pnicto Aug 20, 2023
0668751
Revert "refactor: add event year as part of label"
pnicto Aug 20, 2023
f326f8a
feat: create event year table
pnicto Aug 20, 2023
3cc9d6f
feat: finish endpoint to get available events for a given year
pnicto Aug 20, 2023
c72c227
feat: finish request to get available events for a given year for the…
pnicto Aug 20, 2023
d5ea2e2
refactor: change the flow to show event year selection first
pnicto Aug 20, 2023
08a9afb
feat: fetch events button to make the fetch available events request
pnicto Aug 20, 2023
f47638d
refactor: disable multiselect if there are no events
pnicto Aug 20, 2023
e14a978
refactor: remove event year as only event id is used for finding images
pnicto Aug 20, 2023
f9d4529
test: add more events with years
pnicto Aug 20, 2023
97b3f4b
style: fix formatting with prettier
pnicto Aug 20, 2023
6179cba
refactor: change the req from post to get
pnicto Aug 20, 2023
7c8f72d
test: add random tags to images
pnicto Aug 20, 2023
0e0f25d
fix: return tags along with images
pnicto Aug 20, 2023
6719710
Revert "feat: fetch events button to make the fetch available events …
pnicto Aug 20, 2023
bebf5eb
feat: fetch events on year change instead of an extra button click
pnicto Aug 20, 2023
1ecbc9c
fix: reset selected events when the new available events are fetched
pnicto Aug 20, 2023
95aa80c
feat: add regex validation for uid input field
pnicto Aug 20, 2023
04bd1b5
test: reset db before adding test data
skoriop Aug 20, 2023
c1ee8b6
fix: pass tags to image modal
skoriop Aug 20, 2023
91823b8
refactor: use `filter` instead of `for...of`
skoriop Aug 20, 2023
5f36019
refactor: use 'add tag' instead of 'create tag'
skoriop Aug 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
34 changes: 23 additions & 11 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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`
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
87 changes: 67 additions & 20 deletions frontend/components/ImageWithModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -40,23 +61,17 @@ export default function ImagePreviewModal({
alt={imageUrl}
src={imageUrl}
onClick={open}
imageProps={{ onLoad: () => URL.revokeObjectURL(imageUrl) }}
className="cursor-pointer"
/>

<Modal centered opened={opened} onClose={close} size={"auto"}>
<section className="px-2 py-4 sm:flex sm:gap-x-2">
<Image
alt={imageUrl}
src={imageUrl}
onClick={open}
imageProps={{ onLoad: () => URL.revokeObjectURL(imageUrl) }}
/>
<Image alt={imageUrl} src={imageUrl} onClick={open} />
<div>
<Group position="center" className="mt-2">
{tags.map((tag, badgeIndex) => (
<Badge
className="m-1 capitalize"
className="m-1 normal-case"
key={badgeIndex}
rightSection={
<ActionIcon
Expand All @@ -75,22 +90,54 @@ export default function ImagePreviewModal({
))}
</Group>

<Group position="center" className="py-2">
<Stack align="center" className="my-2">
<TextInput
className="mt-4"
value={newTag}
placeholder="New tag"
onChange={(event) => setNewTag(event.currentTarget.value)}
/>
<Button
type="button"
disabled={newTag.length === 0}
onClick={() => setTags([...tags, newTag])}>
Create tag
</Button>
<Button type="button" onClick={handleUpdateTags}>
Update tags
</Button>
</Group>
<Group position="center" className="px-5" noWrap>
<Button
type="button"
disabled={newTag.length === 0}
onClick={() => {
if (!tags.includes(newTag)) {
setTags([...tags, newTag]);
}
setNewTag("");
}}>
Add tag
</Button>
<Button type="button" onClick={handleUpdateTags}>
Update tags
</Button>
</Group>
{updateStatus === "success" && (
<Alert
className="px-5 pt-5"
icon={<Icon icon="mdi:check-circle-outline" />}
title="Updated successfully!"
color="green"
radius="md"
withCloseButton
onClose={() => setUpdateStatus("")}>
{}
</Alert>
)}
{updateStatus === "failure" && (
<Alert
className="px-5 pt-5"
icon={<Icon icon="mdi:alert-circle-outline" />}
title="Tag update failed!"
color="red"
radius="md"
withCloseButton
onClose={() => setUpdateStatus("")}>
{}
</Alert>
)}
</Stack>
</div>
</section>
</Modal>
Expand Down
83 changes: 54 additions & 29 deletions frontend/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<nav className="flex items-center justify-between px-10">
<nav className="flex items-center justify-center px-10 sm:justify-end">
{/* TODO: Add crux logo here for home route */}
<Anchor component={Link} href={"/"}>
Logo
</Anchor>
<ul className="flex list-none px-0">
<li className="mx-2">
<Button
component={Link}
href={"/announcements"}
className="capitalize">
announcements
{router.pathname !== "/login" && session?.user === undefined && (
<Button component={Link} href={"/"} className="mx-2 capitalize">
login
</Button>
</li>
)}
{unprotectedRoutes.map(
(route, index) =>
router.pathname !== route.path && (
<li key={index} className="mx-2">
<Button
component={Link}
href={route.path}
className="capitalize">
{route.name}
</Button>
</li>
)
)}
{session?.user && (
<>
<li className="mx-2">
<Button
component={Link}
href={"/app/profile"}
className=" capitalize">
profile
</Button>
</li>
<li className="mx-2">
<Button
component={Link}
href={"/app/search"}
className=" capitalize">
search
</Button>
</li>
{protectedRoutes.map(
(route, index) =>
router.pathname !== route.path && (
<li key={index} className="mx-2">
<Button
component={Link}
href={route.path}
className="capitalize">
{route.name}
</Button>
</li>
)
)}
<li className="mx-2">
<Button
onClick={() => signOut({ callbackUrl: "/login" })}
className=" capitalize">
className="capitalize">
logout
</Button>
</li>
Expand Down
5 changes: 4 additions & 1 deletion frontend/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
callbacks: {
session: ({ session, user }) => {
if (session?.user) session.user.id = user.id;
if (session?.user) {
session.user.id = user.id;
session.user.role = user.role;
}
return session;
},
},
Expand Down
Loading