From 515e22bb1744bcff4ee88cbafa333222a01fd652 Mon Sep 17 00:00:00 2001 From: PokeJofeJr4th Date: Sun, 21 Apr 2024 15:08:59 -0400 Subject: [PATCH 1/6] add cursed factory for api wrapper --- next/app/go/GoLink.tsx | 34 +-- next/app/go/MakeNewGoLink.tsx | 307 ++++++++++++++----------- next/app/go/page.tsx | 57 ++--- next/lib/api.ts | 107 +++++++++ next/lib/middlewares/authentication.ts | 9 +- 5 files changed, 315 insertions(+), 199 deletions(-) create mode 100644 next/lib/api.ts diff --git a/next/app/go/GoLink.tsx b/next/app/go/GoLink.tsx index ff08c6f5..acc71fc6 100644 --- a/next/app/go/GoLink.tsx +++ b/next/app/go/GoLink.tsx @@ -2,6 +2,7 @@ import { GoLinkIcon } from "@/components/common/Icons"; import { GoLinkStar } from "@/components/common/Icons"; import { GoLinkEdit } from "@/components/common/Icons"; import { GoLinkDelete } from "@/components/common/Icons"; +import { deleteGolink, fetchAuthLevel, updateGolink } from "@/lib/api"; import { useEffectAsync } from "@/lib/utils"; import { useSession } from "next-auth/react"; import { useCallback, useEffect, useState } from "react"; @@ -35,19 +36,13 @@ const GoLink: React.FC = ({ id, goUrl, url, description, pinned, fe const handleEdit = async () => { try { - const response = await fetch(`http://localhost:3000/api/golinks`, { - method: 'PUT', // Assuming you are using PUT method for editing - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - id: id, - golink: newTitle, - url: newUrl, - description: newDescription, - isPinned: newPinned, - isPublic: !officer - }), + const response = await updateGolink({ + id: id, + golink: newTitle, + url: newUrl, + description: newDescription, + isPinned: newPinned, + isPublic: !officer }); if (response.ok) { @@ -59,15 +54,7 @@ const GoLink: React.FC = ({ id, goUrl, url, description, pinned, fe const handleDelete = async () => { try { - const response = await fetch(`http://localhost:3000/api/golinks`, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - id: id - }), - }); + const response = await deleteGolink(id); if (response.ok) { handleCancel(); @@ -226,8 +213,7 @@ const EditAndDelete: React.FC = ({ id, goUrl, url, description, pin const [isOfficer, setIsOfficer] = useState(false); useEffectAsync(async() => { - const response = await fetch("http://localhost:3000/api/authLevel"); - const data = await response.json(); + const data = await fetchAuthLevel(); setIsOfficer(data.isOfficer); }, []); diff --git a/next/app/go/MakeNewGoLink.tsx b/next/app/go/MakeNewGoLink.tsx index c92707c9..07246086 100644 --- a/next/app/go/MakeNewGoLink.tsx +++ b/next/app/go/MakeNewGoLink.tsx @@ -2,79 +2,67 @@ import { useSession } from "next-auth/react"; import { useCallback, useEffect, useState } from "react"; import { CreateGoLinkProps } from "./page"; import { useEffectAsync } from "@/lib/utils"; - -export const GoLinkButton: React.FC = ({fetchData}) => { - const { data: session } : any= useSession() - const [title, setTitle] = useState(""); - const [url, setUrl] = useState(""); - const [description, setDescription] = useState(""); - const [pinned, setPinned] = useState(false); - const [officer, setOfficer] = useState(false); - - const handleSetTitle = (givenTitle: string) => { - const title = givenTitle.toLowerCase().split(' ').join('-') - setTitle(title) - } - - const handleCancel = () => { - setTitle(""); - setUrl(""); - setDescription(""); - setPinned(false); - setOfficer(false); - }; - - const handleCreate = async () => { - try { - const response = await fetch('http://localhost:3000/api/golinks', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + session?.accessToken - }, - // In body, make sure parameter names MATCH the ones in api/golinks/route.ts for the POST request - // Left is backend parameter names, right is our front end names - // golink = backend parameter name --- title = frontend parameter name - // - // I messed up when I did this with the edit API call, it wasn't saving the title because of that. - body: JSON.stringify({ - golink: title, - url: url, - description: description, - isPinned: pinned, - isPublic: !officer // If it is officer, it is not public - }), - }); - - if (response.ok) { - handleCancel(); - (document.getElementById('create-golink') as HTMLDialogElement).close(); - fetchData(); +import { createGolink, fetchAuthLevel } from "@/lib/api"; + +export const GoLinkButton: React.FC = ({ fetchData }) => { + const { data: session }: any = useSession(); + const [title, setTitle] = useState(""); + const [url, setUrl] = useState(""); + const [description, setDescription] = useState(""); + const [pinned, setPinned] = useState(false); + const [officer, setOfficer] = useState(false); + + const handleSetTitle = (givenTitle: string) => { + const title = givenTitle.toLowerCase().split(" ").join("-"); + setTitle(title); + }; + + const handleCancel = () => { + setTitle(""); + setUrl(""); + setDescription(""); + setPinned(false); + setOfficer(false); + }; + + const handleCreate = async () => { + try { + const response = await createGolink({ + golink: title, + url: url, + description: description, + isPinned: pinned, + isPublic: !officer, // If it is officer, it is not public + }); + + if (response.ok) { + handleCancel(); + (document.getElementById("create-golink") as HTMLDialogElement).close(); + fetchData(); + } + } catch (error) {} + }; + + const [isOfficer, setIsOfficer] = useState(false); + useEffectAsync(async () => { + const data = await fetchAuthLevel(); + console.log(data); + setIsOfficer(data.isOfficer); + }, []); + + if (isOfficer) { + return ( + <> + - - -
-

Create GoLink

- - - - - - -
- -
- -
- -
- -
- - -
-
- -
-
- - - -
-
- -
-
-
-
-
- - - ); - } -} - -export default GoLinkButton; \ No newline at end of file + > + Create Go Link + + + +
+

Create GoLink

+ + + + + + +
+ +
+ +
+ +
+ +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+ + ); + } +}; + +export default GoLinkButton; diff --git a/next/app/go/page.tsx b/next/app/go/page.tsx index 76b799d0..37e14d84 100644 --- a/next/app/go/page.tsx +++ b/next/app/go/page.tsx @@ -1,43 +1,44 @@ -"use client" +"use client"; import GoLinksContainer from "@/app/go/GoLinksContainer"; import { GoLinkProps } from "./GoLink"; import { useCallback, useEffect, useState } from "react"; +import { fetchGolinks } from "@/lib/api"; export interface CreateGoLinkProps { - fetchData: () => Promise; + fetchData: () => Promise; } export interface GoLinksContainerProps { - goLinkData: GoLinkProps[]; - fetchData: () => Promise; + goLinkData: GoLinkProps[]; + fetchData: () => Promise; } const GoLinksPage = () => { - const [goLinkData, setGoLinkData] = useState([]); - const fetchData = useCallback(async() => { - const response = await fetch("http://localhost:3000/api/golinks/public"); - const data = await response.json(); - setGoLinkData(data.map((item: { id: number, golink: string; url: string; description: string; isPinned: boolean; }) => ({ - id: item.id, - goUrl: item.golink, - url: item.url, - description: item.description ?? '', - pinned: item.isPinned, - }))); - }, []) - useEffect(() => { - fetchData() - }, [fetchData]); + const [goLinkData, setGoLinkData]: [any[], any] = useState([]); + const fetchData = useCallback(async () => { + const data = await fetchGolinks(); + setGoLinkData( + data.map((item) => ({ + id: item.id, + goUrl: item.golink, + url: item.url, + description: item.description ?? "", + pinned: item.isPinned, + })) + ); + }, []); + useEffect(() => { + fetchData(); + }, [fetchData]); - useEffect(() => {}, [goLinkData]); - - return ( - <> - - - ) -} + useEffect(() => {}, [goLinkData]); + return ( + <> + + + ); +}; -export default GoLinksPage; \ No newline at end of file +export default GoLinksPage; diff --git a/next/lib/api.ts b/next/lib/api.ts new file mode 100644 index 00000000..0e97db2d --- /dev/null +++ b/next/lib/api.ts @@ -0,0 +1,107 @@ +export type NoId = { + [P in keyof T as Exclude]: T[P]; +}; + +export type GoLink = { + id: number; + golink: string; + url: string; + description: string; + isPinned: boolean; + isPublic: boolean; +}; + +export type Skill = { + id: number; + skill: string; +}; + +export type Api = { + create: (item: NoId) => Promise; + fetch: () => Promise; + update: (item: T) => Promise; + delete: (id: number) => Promise; +}; + +function routeFactory(route: string): Api { + return { + create: (item): Promise => + fetch(process.env.NEXTAUTH_URL + "/api/" + route, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(item), + }), + fetch: async () => { + const response = await fetch(process.env.NEXTAUTH_URL + "/api/" + route); + const data = await response.json(); + return data; + }, + update: (item) => + fetch(process.env.NEXTAUTH_URL + "/api/" + route, { + method: "PUT", + body: JSON.stringify(item), + }), + delete: (id) => + fetch(process.env.NEXTAUTH_URL + "/api/" + route, { + method: "DELETE", + body: JSON.stringify({ id }), + }), + }; +} + +export const skillsApi: Api = routeFactory("skills"); + +export async function createGolink(golink: NoId): Promise { + return await fetch(process.env.NEXTAUTH_URL + "/api/golinks", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(golink), + }); +} + +export async function fetchGolinks(): Promise { + const response = await fetch( + process.env.NEXTAUTH_URL + "/api/golinks/public" + ); + const data = await response.json(); + return data; +} + +export async function updateGolink(golink: GoLink): Promise { + return await fetch(process.env.NEXTAUTH_URL + `/api/golinks`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(golink), + }); +} + +export async function deleteGolink(id: number): Promise { + return await fetch(process.env.NEXTAUTH_URL + `/api/golinks`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: id, + }), + }); +} + +export type AuthLevel = { + isUser: boolean; + isMember: boolean; + isMentor: boolean; + isOfficer: boolean; +}; + +export async function fetchAuthLevel(): Promise { + const response = await fetch(process.env.NEXTAUTH_URL + "/api/authLevel"); + const data = await response.json(); + return data; +} diff --git a/next/lib/middlewares/authentication.ts b/next/lib/middlewares/authentication.ts index 3348aa80..356cc144 100644 --- a/next/lib/middlewares/authentication.ts +++ b/next/lib/middlewares/authentication.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from "next/server"; +import { fetchAuthLevel } from "../api"; /** * A function to verify if a request should be let through. This function should handle the required authLevel @@ -22,13 +23,7 @@ const authVerifierFactory = ( // get the token from the cookie const token = request.cookies.get("next-auth.session-token")?.value; // fetch permissions from the API - const permissions = await fetch( - process.env.NEXTAUTH_URL + "/api/authLevel", - { - body: JSON.stringify({ token }), - method: "PUT", - } - ).then(async (res) => await res.json()); + const permissions = await fetchAuthLevel(); // console.log(permissions); return verifier(permissions); }; From 1299705a908e8fcd8e16066927111f3466458360 Mon Sep 17 00:00:00 2001 From: PokeJofeJr4th Date: Sun, 21 Apr 2024 15:09:42 -0400 Subject: [PATCH 2/6] fix auth middleware --- next/lib/middlewares/authentication.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/next/lib/middlewares/authentication.ts b/next/lib/middlewares/authentication.ts index 356cc144..3348aa80 100644 --- a/next/lib/middlewares/authentication.ts +++ b/next/lib/middlewares/authentication.ts @@ -1,5 +1,4 @@ import { NextRequest, NextResponse } from "next/server"; -import { fetchAuthLevel } from "../api"; /** * A function to verify if a request should be let through. This function should handle the required authLevel @@ -23,7 +22,13 @@ const authVerifierFactory = ( // get the token from the cookie const token = request.cookies.get("next-auth.session-token")?.value; // fetch permissions from the API - const permissions = await fetchAuthLevel(); + const permissions = await fetch( + process.env.NEXTAUTH_URL + "/api/authLevel", + { + body: JSON.stringify({ token }), + method: "PUT", + } + ).then(async (res) => await res.json()); // console.log(permissions); return verifier(permissions); }; From 55eaab23ddd642ccaa15f36adcb4962f4a937f6c Mon Sep 17 00:00:00 2001 From: PokeJofeJr4th Date: Sun, 21 Apr 2024 15:24:39 -0400 Subject: [PATCH 3/6] move golink api wrapper functions into an API object --- next/app/go/GoLink.tsx | 528 ++++++++++++++++++------------- next/app/go/GoLinksContainer.tsx | 202 ++++++------ next/app/go/MakeNewGoLink.tsx | 4 +- next/app/go/page.tsx | 4 +- next/lib/api.ts | 78 ++--- 5 files changed, 447 insertions(+), 369 deletions(-) diff --git a/next/app/go/GoLink.tsx b/next/app/go/GoLink.tsx index acc71fc6..35efe537 100644 --- a/next/app/go/GoLink.tsx +++ b/next/app/go/GoLink.tsx @@ -2,77 +2,83 @@ import { GoLinkIcon } from "@/components/common/Icons"; import { GoLinkStar } from "@/components/common/Icons"; import { GoLinkEdit } from "@/components/common/Icons"; import { GoLinkDelete } from "@/components/common/Icons"; -import { deleteGolink, fetchAuthLevel, updateGolink } from "@/lib/api"; +import { fetchAuthLevel, goLinksApi } from "@/lib/api"; import { useEffectAsync } from "@/lib/utils"; import { useSession } from "next-auth/react"; import { useCallback, useEffect, useState } from "react"; export interface GoLinkProps { - id: number; - goUrl: string; - url: string; - description: string; - pinned: boolean; - fetchData: () => Promise; + id: number; + goUrl: string; + url: string; + description: string; + pinned: boolean; + fetchData: () => Promise; } -const GoLink: React.FC = ({ id, goUrl, url, description, pinned, fetchData }) => { - const [newTitle, setTitle] = useState(goUrl); - const [newUrl, setUrl] = useState(url); - const [newDescription, setDescription] = useState(description); - const [newPinned, setPinned] = useState(pinned); - const [officer, setOfficer] = useState(false); - - const handleCancel = () => { - setTitle(goUrl); - setUrl(url); - setDescription(description); - setPinned(pinned); - setOfficer(false); - }; - - const editModalId = `edit-golink-${id}`; - const deleteModalId = `delete-golink-${id}`; - - const handleEdit = async () => { - try { - const response = await updateGolink({ - id: id, - golink: newTitle, - url: newUrl, - description: newDescription, - isPinned: newPinned, - isPublic: !officer - }); - - if (response.ok) { - (document.getElementById(editModalId) as HTMLDialogElement).close(); - fetchData(); - } - } catch (error) {} - } - - const handleDelete = async () => { - try { - const response = await deleteGolink(id); - - if (response.ok) { - handleCancel(); - (document.getElementById(deleteModalId) as HTMLDialogElement).close(); - fetchData(); - } - } catch (error) {} - } +const GoLink: React.FC = ({ + id, + goUrl, + url, + description, + pinned, + fetchData, +}) => { + const [newTitle, setTitle] = useState(goUrl); + const [newUrl, setUrl] = useState(url); + const [newDescription, setDescription] = useState(description); + const [newPinned, setPinned] = useState(pinned); + const [officer, setOfficer] = useState(false); + const handleCancel = () => { + setTitle(goUrl); + setUrl(url); + setDescription(description); + setPinned(pinned); + setOfficer(false); + }; - return ( - <> - {console.log(url)} - - -
+
-
- {pinned && } -

- {goUrl} -

-
-

{description}

-
-
- - - - - - -
-
- -
-

Create GoLink

- - - - - - -
- -
- -
- -
- -
- - -
-
- -
-
- - - -
-
- -
-
-
-
-
- -
-
- -
-

- Are you sure you want to delete this GoLink? -

-
- - -
-
- -
-
- - - -
-
- -
-
-
-
-
- - ); -} + " + > +
+ {pinned && } +

{goUrl}

+
+

{description}

+
+
+ + + + + + +
+ + +
+

Create GoLink

-const EditAndDelete: React.FC = ({ id, goUrl, url, description, pinned } ) => { - const { data: session } = useSession(); - const [isOfficer, setIsOfficer] = useState(false); - - useEffectAsync(async() => { - const data = await fetchAuthLevel(); - setIsOfficer(data.isOfficer); - }, []); - - if(isOfficer) { - return ( -
-
-
- -
-
- -
-
-
- ) - } -} + + + + + +
+ +
+ +
+ +
+ +
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+ +
+
+ +
+

+ Are you sure you want to delete this GoLink? +

+
+ + +
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+ + ); +}; + +const EditAndDelete: React.FC = ({ + id, + goUrl, + url, + description, + pinned, +}) => { + const { data: session } = useSession(); + const [isOfficer, setIsOfficer] = useState(false); + + useEffectAsync(async () => { + const data = await fetchAuthLevel(); + setIsOfficer(data.isOfficer); + }, []); + + if (isOfficer) { + return ( +
+
+
+ +
+
+ +
+
+
+ ); + } +}; export default GoLink; diff --git a/next/app/go/GoLinksContainer.tsx b/next/app/go/GoLinksContainer.tsx index 6e996f9d..47f46fff 100644 --- a/next/app/go/GoLinksContainer.tsx +++ b/next/app/go/GoLinksContainer.tsx @@ -1,78 +1,78 @@ -'use client' +"use client"; -import React, { useEffect, useState } from 'react'; -import GoLink, { GoLinkProps } from './GoLink'; +import React, { useEffect, useState } from "react"; +import GoLink, { GoLinkProps } from "./GoLink"; import { GoLinksContainerProps } from "@/app/go/page"; -import { filterGoLinks } from '@/lib/filter'; -import {GoLinkButton} from '@/app/go/MakeNewGoLink' +import { filterGoLinks } from "@/lib/filter"; +import { GoLinkButton } from "@/app/go/MakeNewGoLink"; -const GoLinksContainer: React.FC = ({ goLinkData, fetchData }) => { - const pinnedGoLinks = goLinkData - .filter(data => data.pinned === true) +const GoLinksContainer: React.FC = ({ + goLinkData, + fetchData, +}) => { + const pinnedGoLinks = goLinkData + .filter((data) => data.pinned === true) .map((data, index) => ( - + )); - const unpinnedGoLinks = goLinkData - .filter(data => !data.pinned) + const unpinnedGoLinks = goLinkData + .filter((data) => !data.pinned) .map((data, index) => ( - + )); - const [goLinkList, setGoLinkList] = useState([]); + const [goLinkList, setGoLinkList] = useState([]); - const updateGoLinkList = (givenFilter: string) => { - if (givenFilter === "" || givenFilter === null) { - setGoLinkList([...pinnedGoLinks, ...unpinnedGoLinks]); - } else { - const filteredGoLinkData = filterGoLinks(givenFilter, goLinkData); - setGoLinkList(filteredGoLinkData.map((data, index) => ( - - ))); - } - }; + const updateGoLinkList = (givenFilter: string) => { + if (givenFilter === "" || givenFilter === null) { + setGoLinkList([...pinnedGoLinks, ...unpinnedGoLinks]); + } else { + const filteredGoLinkData = filterGoLinks(givenFilter, goLinkData); + setGoLinkList( + filteredGoLinkData.map((data, index) => ( + + )) + ); + } + }; - useEffect(() => { - updateGoLinkList(""); - }, [goLinkData]); + useEffect(() => { + updateGoLinkList(""); + }, [goLinkData]); - const handleFilterChange = (event: React.ChangeEvent) => { - const givenFilter = event.target.value; - updateGoLinkList(givenFilter); - }; + const handleFilterChange = (event: React.ChangeEvent) => { + const givenFilter = event.target.value; + updateGoLinkList(givenFilter); + }; - if (goLinkData.length === 0) { - return ( -
-
-

+
+

- Go Links -

- -

- GoLinks are a type of URL shortcut that allow you to access the SSE's frequently used - external websites or resources. Important and/or relevant golinks are marked with a gold star. -

-
-
- handleFilterChange(event)} /> -
-
+ GoLinks are a type of URL shortcut that allow you to access the + SSE's frequently used external websites or resources. Important + and/or relevant golinks are marked with a gold star. +

+
+
+ handleFilterChange(event)} + /> +
+
- -
-
- Loading... -
-

- ) - } - else{ - return ( -
-
-

+ +

+
Loading...
+
+ ); + } else { + return ( +
+
+

- Go Links -

- -

- GoLinks are a type of URL shortcut that allow you to access the SSE's frequently used - external websites or resources. Important and/or relevant golinks are marked with a gold star. -

-
- -
- handleFilterChange(event)} /> -
-
+ GoLinks are a type of URL shortcut that allow you to access the + SSE's frequently used external websites or resources. Important + and/or relevant golinks are marked with a gold star. +

+
+ +
+ handleFilterChange(event)} + /> +
+
- - {goLinkList} -
-
- ) - } + " + > + + {goLinkList} +
+ + ); + } }; -export default GoLinksContainer; \ No newline at end of file +export default GoLinksContainer; diff --git a/next/app/go/MakeNewGoLink.tsx b/next/app/go/MakeNewGoLink.tsx index 07246086..4215369f 100644 --- a/next/app/go/MakeNewGoLink.tsx +++ b/next/app/go/MakeNewGoLink.tsx @@ -2,7 +2,7 @@ import { useSession } from "next-auth/react"; import { useCallback, useEffect, useState } from "react"; import { CreateGoLinkProps } from "./page"; import { useEffectAsync } from "@/lib/utils"; -import { createGolink, fetchAuthLevel } from "@/lib/api"; +import { goLinksApi, fetchAuthLevel } from "@/lib/api"; export const GoLinkButton: React.FC = ({ fetchData }) => { const { data: session }: any = useSession(); @@ -27,7 +27,7 @@ export const GoLinkButton: React.FC = ({ fetchData }) => { const handleCreate = async () => { try { - const response = await createGolink({ + const response = await goLinksApi.create({ golink: title, url: url, description: description, diff --git a/next/app/go/page.tsx b/next/app/go/page.tsx index 37e14d84..d7e8bc2a 100644 --- a/next/app/go/page.tsx +++ b/next/app/go/page.tsx @@ -3,7 +3,7 @@ import GoLinksContainer from "@/app/go/GoLinksContainer"; import { GoLinkProps } from "./GoLink"; import { useCallback, useEffect, useState } from "react"; -import { fetchGolinks } from "@/lib/api"; +import { goLinksApi } from "@/lib/api"; export interface CreateGoLinkProps { fetchData: () => Promise; @@ -17,7 +17,7 @@ export interface GoLinksContainerProps { const GoLinksPage = () => { const [goLinkData, setGoLinkData]: [any[], any] = useState([]); const fetchData = useCallback(async () => { - const data = await fetchGolinks(); + const data = await goLinksApi.fetch(); setGoLinkData( data.map((item) => ({ id: item.id, diff --git a/next/lib/api.ts b/next/lib/api.ts index 0e97db2d..1fb6d53c 100644 --- a/next/lib/api.ts +++ b/next/lib/api.ts @@ -33,11 +33,10 @@ function routeFactory(route: string): Api { }, body: JSON.stringify(item), }), - fetch: async () => { - const response = await fetch(process.env.NEXTAUTH_URL + "/api/" + route); - const data = await response.json(); - return data; - }, + fetch: async () => + fetch(process.env.NEXTAUTH_URL + "/api/" + route).then((response) => + response.json() + ), update: (item) => fetch(process.env.NEXTAUTH_URL + "/api/" + route, { method: "PUT", @@ -53,45 +52,38 @@ function routeFactory(route: string): Api { export const skillsApi: Api = routeFactory("skills"); -export async function createGolink(golink: NoId): Promise { - return await fetch(process.env.NEXTAUTH_URL + "/api/golinks", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(golink), - }); -} - -export async function fetchGolinks(): Promise { - const response = await fetch( - process.env.NEXTAUTH_URL + "/api/golinks/public" - ); - const data = await response.json(); - return data; -} - -export async function updateGolink(golink: GoLink): Promise { - return await fetch(process.env.NEXTAUTH_URL + `/api/golinks`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(golink), - }); -} - -export async function deleteGolink(id: number): Promise { - return await fetch(process.env.NEXTAUTH_URL + `/api/golinks`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - id: id, +export const goLinksApi: Api = { + create: (golink) => + fetch(process.env.NEXTAUTH_URL + "/api/golinks", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(golink), }), - }); -} + fetch: () => + fetch(process.env.NEXTAUTH_URL + "/api/golinks/public").then((response) => + response.json() + ), + update: (golink) => + fetch(process.env.NEXTAUTH_URL + `/api/golinks`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(golink), + }), + delete: (id) => + fetch(process.env.NEXTAUTH_URL + `/api/golinks`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: id, + }), + }), +}; export type AuthLevel = { isUser: boolean; From 84235b160f1e4b267d19d936f5d71306ab9a3c4e Mon Sep 17 00:00:00 2001 From: PokeJofeJr4th Date: Sun, 21 Apr 2024 15:34:24 -0400 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=A4=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next/lib/api.ts | 54 ++++++++++--------------------------------------- 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/next/lib/api.ts b/next/lib/api.ts index 1fb6d53c..3d00aa4a 100644 --- a/next/lib/api.ts +++ b/next/lib/api.ts @@ -25,25 +25,20 @@ export type Api = { function routeFactory(route: string): Api { return { - create: (item): Promise => - fetch(process.env.NEXTAUTH_URL + "/api/" + route, { + create: async (item) => + fetch("/api/" + route, { method: "POST", - headers: { - "Content-Type": "application/json", - }, body: JSON.stringify(item), }), fetch: async () => - fetch(process.env.NEXTAUTH_URL + "/api/" + route).then((response) => - response.json() - ), - update: (item) => - fetch(process.env.NEXTAUTH_URL + "/api/" + route, { + fetch("/api/" + route).then((response) => response.json()), + update: async (item) => + fetch("/api/" + route, { method: "PUT", body: JSON.stringify(item), }), - delete: (id) => - fetch(process.env.NEXTAUTH_URL + "/api/" + route, { + delete: async (id) => + fetch("/api/" + route, { method: "DELETE", body: JSON.stringify({ id }), }), @@ -53,36 +48,9 @@ function routeFactory(route: string): Api { export const skillsApi: Api = routeFactory("skills"); export const goLinksApi: Api = { - create: (golink) => - fetch(process.env.NEXTAUTH_URL + "/api/golinks", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(golink), - }), - fetch: () => - fetch(process.env.NEXTAUTH_URL + "/api/golinks/public").then((response) => - response.json() - ), - update: (golink) => - fetch(process.env.NEXTAUTH_URL + `/api/golinks`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(golink), - }), - delete: (id) => - fetch(process.env.NEXTAUTH_URL + `/api/golinks`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - id: id, - }), - }), + ...routeFactory("golinks"), + fetch: async () => + fetch("/api/golinks/public").then((response) => response.json()), }; export type AuthLevel = { @@ -93,7 +61,7 @@ export type AuthLevel = { }; export async function fetchAuthLevel(): Promise { - const response = await fetch(process.env.NEXTAUTH_URL + "/api/authLevel"); + const response = await fetch("/api/authLevel"); const data = await response.json(); return data; } From 5d5a0a00c451664ecbcbb66d61721b32df6efe2f Mon Sep 17 00:00:00 2001 From: PokeJofeJr4th Date: Sun, 21 Apr 2024 15:54:32 -0400 Subject: [PATCH 5/6] start documenting API wrappers --- next/lib/api.ts | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/next/lib/api.ts b/next/lib/api.ts index 3d00aa4a..961112ae 100644 --- a/next/lib/api.ts +++ b/next/lib/api.ts @@ -1,7 +1,17 @@ +/** + * Remove the "id" key from an object type + */ export type NoId = { [P in keyof T as Exclude]: T[P]; }; +/** + * Make every key except "id" optional + */ +export type NonExhaustive = { + [P in keyof T]?: T[P]; +} & { id: number }; + export type GoLink = { id: number; golink: string; @@ -16,14 +26,25 @@ export type Skill = { skill: string; }; -export type Api = { +/** + * A collection of functions to wrap API methods working with a given type + * + * create: create a new item + * + * fetch: fetch all items + * + * update: update an existing item + * + * delete: remove an item + */ +export type ApiWrapper = { create: (item: NoId) => Promise; fetch: () => Promise; - update: (item: T) => Promise; + update: (item: NonExhaustive) => Promise; delete: (id: number) => Promise; }; -function routeFactory(route: string): Api { +function apiWrapperFactory(route: string): ApiWrapper { return { create: async (item) => fetch("/api/" + route, { @@ -45,10 +66,10 @@ function routeFactory(route: string): Api { }; } -export const skillsApi: Api = routeFactory("skills"); +export const skillsApi: ApiWrapper = apiWrapperFactory("skills"); -export const goLinksApi: Api = { - ...routeFactory("golinks"), +export const goLinksApi: ApiWrapper = { + ...apiWrapperFactory("golinks"), fetch: async () => fetch("/api/golinks/public").then((response) => response.json()), }; From ee04335bb2e834bc58572578bacea6230228f00e Mon Sep 17 00:00:00 2001 From: PokeJofeJr4th Date: Sat, 27 Apr 2024 11:35:03 -0400 Subject: [PATCH 6/6] ALL THE TYPES --- next/lib/api.ts | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/next/lib/api.ts b/next/lib/api.ts index 961112ae..7bfa6a61 100644 --- a/next/lib/api.ts +++ b/next/lib/api.ts @@ -1,3 +1,5 @@ +import { DateTime } from "next-auth/providers/kakao"; + /** * Remove the "id" key from an object type */ @@ -26,6 +28,32 @@ export type Skill = { skill: string; }; +export type HourBlock = { + id: number; + weekday: string; + startTime: DateTime; +}; + +export type Mentor = { + id: number; + userId: number; + expirationDate: DateTime; + isActive: boolean; +}; + +export type MentorRead = Mentor & {}; + +export type Schedule = { + id: number; + mentorId: number; + hourBlockId: number; +}; + +export type ScheduleRead = Schedule & { + mentor: Mentor; + hourBlock: HourBlock; +}; + /** * A collection of functions to wrap API methods working with a given type * @@ -37,14 +65,14 @@ export type Skill = { * * delete: remove an item */ -export type ApiWrapper = { - create: (item: NoId) => Promise; - fetch: () => Promise; - update: (item: NonExhaustive) => Promise; +export type ApiWrapper> = { + create: (item: W) => Promise; + fetch: () => Promise; + update: (item: NonExhaustive) => Promise; delete: (id: number) => Promise; }; -function apiWrapperFactory(route: string): ApiWrapper { +function apiWrapperFactory(route: string): ApiWrapper { return { create: async (item) => fetch("/api/" + route, { @@ -67,6 +95,8 @@ function apiWrapperFactory(route: string): ApiWrapper { } export const skillsApi: ApiWrapper = apiWrapperFactory("skills"); +export const hourBlockApi: ApiWrapper = + apiWrapperFactory("hourBlocks"); export const goLinksApi: ApiWrapper = { ...apiWrapperFactory("golinks"), @@ -74,6 +104,9 @@ export const goLinksApi: ApiWrapper = { fetch("/api/golinks/public").then((response) => response.json()), }; +export const scheduleApi: ApiWrapper = + apiWrapperFactory("schedule"); + export type AuthLevel = { isUser: boolean; isMember: boolean;