From c962fb4867eed88025a8f94bd0585465938aea16 Mon Sep 17 00:00:00 2001 From: Shogo NAKAMURA <104970808+naka-12@users.noreply.github.com> Date: Wed, 9 Oct 2024 08:31:17 +0900 Subject: [PATCH 001/131] =?UTF-8?q?=E6=8E=88=E6=A5=ADID=E3=82=92=E9=81=B8?= =?UTF-8?q?=E6=8A=9E=E6=99=82=E3=81=AB=E8=A1=A8=E7=A4=BA=20(#464)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 授業IDがないと判別できないケースが多いため選択時に表示するようにした image ## 補足 ## レビューリクエストを出す前にチェック! - [x] 改めてセルフレビューしたか - [x] 手動での動作検証を行ったか - [x] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [x] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [x] わかりやすいPRになっているか --- .../course/components/SelectCourseDialog.tsx | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/web/src/components/course/components/SelectCourseDialog.tsx b/web/src/components/course/components/SelectCourseDialog.tsx index 2a625369..046a3475 100644 --- a/web/src/components/course/components/SelectCourseDialog.tsx +++ b/web/src/components/course/components/SelectCourseDialog.tsx @@ -69,21 +69,35 @@ export default function SelectCourseDialog({ <> - - - 現在の授業: {currentEdit?.course?.name ?? "-"} - - { - if (!currentEdit?.course?.id) return; + + + 現在の授業 + {currentEdit?.course ? ( + + + + {currentEdit?.course?.name ?? "-"} + + {`${currentEdit?.course?.teacher ?? "-"} / ${ + currentEdit?.course?.id ?? "-" + }`} + + { + if (!currentEdit?.course?.id) return; - setNewCourse(currentEdit.course); - setIsDeleteConfirmDialogOpen(true); - }} - > - - + setNewCourse(currentEdit.course); + setIsDeleteConfirmDialogOpen(true); + }} + > + + + + ) : ( + 未登録 + )} + { @@ -111,7 +125,10 @@ export default function SelectCourseDialog({ setIsConfirmDialogOpen(true); }} > - {`${course.name}(${course.teacher})`} + + {course.name} + {`${course.teacher} / ${course.id}`} + ))} From d7683f22a81abebaee67eb165d18117929038b10 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:48:40 +0900 Subject: [PATCH 002/131] =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E7=B7=A8=E9=9B=86=E4=B8=AD=E3=81=AB=20ctrl=20+=20ente?= =?UTF-8?q?r=20=E3=81=A7=E7=A2=BA=E5=AE=9A=E3=81=99=E3=82=8B=20(#471)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 closes #459 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- web/src/components/chat/RoomWindow.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/src/components/chat/RoomWindow.tsx b/web/src/components/chat/RoomWindow.tsx index 4a271dc3..93df4c51 100644 --- a/web/src/components/chat/RoomWindow.tsx +++ b/web/src/components/chat/RoomWindow.tsx @@ -203,6 +203,11 @@ export function RoomWindow() { setEditedContent(e.target.value)} + onKeyDown={(e) => { + if ((e.metaKey || e.ctrlKey) && e.key === "Enter") { + commitEdit(editingMessageId, editedContent); + } + }} fullWidth variant="outlined" multiline From 62a3a94039f3d822adbc3ae4d1d7374a33b1b806 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:56:49 +0900 Subject: [PATCH 003/131] =?UTF-8?q?keep-alive=20=E3=81=AE=E3=82=B9?= =?UTF-8?q?=E3=83=91=E3=83=B3=E3=82=92=205=20=E5=88=86=E3=81=8B=E3=82=89?= =?UTF-8?q?=202=20=E5=88=86=E3=81=AB=E7=9F=AD=E7=B8=AE=20(#470)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [x] わかりやすいPRになっているか --- .github/workflows/keep-alive.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/keep-alive.yml b/.github/workflows/keep-alive.yml index 31cd7b58..2c172179 100644 --- a/.github/workflows/keep-alive.yml +++ b/.github/workflows/keep-alive.yml @@ -2,7 +2,7 @@ name: Keep Alive on: schedule: - - cron: "*/5 * * * *" + - cron: "*/2 * * * *" workflow_dispatch: env: From 7aae429d8a8b09199535d57e06e48901f152a60e Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:57:09 +0900 Subject: [PATCH 004/131] =?UTF-8?q?[bugfix]=20=E6=9C=80=E5=88=9D=E3=81=AE?= =?UTF-8?q?=E3=83=81=E3=83=A3=E3=83=83=E3=83=88=E3=81=AE=E3=83=A1=E3=83=83?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=82=B8=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=AA=E3=81=84=E4=B8=8D=E5=85=B7=E5=90=88=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#469)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit before: ![before](https://github.com/user-attachments/assets/0f0bbebb-22b3-4e8b-a479-42384ce4ce13) after: ![after](https://github.com/user-attachments/assets/9af4afb8-6ced-4fbb-9097-816ed76e55cd) --- web/src/components/chat/RoomWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/chat/RoomWindow.tsx b/web/src/components/chat/RoomWindow.tsx index 93df4c51..8d7209d2 100644 --- a/web/src/components/chat/RoomWindow.tsx +++ b/web/src/components/chat/RoomWindow.tsx @@ -177,7 +177,7 @@ export function RoomWindow() { overflowY: "auto", }} > - {messages ? ( + {messages && messages.length > 0 ? ( Date: Fri, 11 Oct 2024 17:16:02 +0900 Subject: [PATCH 005/131] =?UTF-8?q?=E3=82=88=E3=81=8F=E3=81=82=E3=82=8B?= =?UTF-8?q?=E8=B3=AA=E5=95=8F=E3=82=92Settings=E7=94=BB=E9=9D=A2=E3=81=AB?= =?UTF-8?q?=E3=82=82=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F=20(#477)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 close #463 ## 具体的な変更内容 スクリーンショット 2024-10-11 14 59 39 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [x] 改めてセルフレビューしたか - [x] 手動での動作検証を行ったか - [x] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [x] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [x] わかりやすいPRになっているか --- web/src/routes/tabs/settings/settings.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/src/routes/tabs/settings/settings.tsx b/web/src/routes/tabs/settings/settings.tsx index 18143274..e214b73a 100644 --- a/web/src/routes/tabs/settings/settings.tsx +++ b/web/src/routes/tabs/settings/settings.tsx @@ -34,6 +34,10 @@ export default function Settings() { + navigate("/faq")}> + + + navigate("/settings/aboutUs")}> From fe4bf7f796ed1794a0d23a9735ac2d5c3c669e64 Mon Sep 17 00:00:00 2001 From: KaichiManabe <154493010+KaichiManabe@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:00:45 +0900 Subject: [PATCH 006/131] =?UTF-8?q?Header=E3=81=AECourseMateIcon=E3=82=92?= =?UTF-8?q?=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=E3=81=99=E3=82=8B=E3=81=A8?= =?UTF-8?q?=E3=80=81/home=E3=81=AB=E9=A3=9B=E3=81=B6=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F=20(#478)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 close #453 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- web/src/components/Header.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index be8e16ca..2b05d6c5 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -21,7 +21,13 @@ export default function Header(props: Props) { > - + { + navigate("/home"); + }} + > + + Date: Tue, 22 Oct 2024 12:48:20 +0900 Subject: [PATCH 007/131] freeze lockfile (#485) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 closes #479 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [x] わかりやすいPRになっているか --- Makefile | 6 +++--- web/bun.lockb | Bin 118394 -> 118736 bytes 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 419a455e..9c75503f 100644 --- a/Makefile +++ b/Makefile @@ -95,16 +95,16 @@ spell-check: # Sync (install/update packages, generate prisma, etc) sync-web: - cd web; bun install + cd web; bun install --frozen-lockfile # copy .env.sample -> .env only if .env is not there sync-server: - cd server; bun install + cd server; bun install --frozen-lockfile cd server; bunx prisma generate # copy .env.sample -> .env only if .env is not there sync-root: - bun install + bun install --frozen-lockfile # Static checks diff --git a/web/bun.lockb b/web/bun.lockb index bb70b69a855fa55eab1976c75d89ac48b9f80211..3a9e6d19b0f2885328bd7c3021e327483a07031e 100755 GIT binary patch delta 21078 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 C%fa3N delta 20829 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 a2XvvUCw6|2&N?%(H-pJLV>8%-l>Y(eaCDgf From af5548db78e32630c00f789ddf297f7e7f5ff40b Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:42:37 +0900 Subject: [PATCH 008/131] cache token (#476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 close #475 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- web/src/firebase/auth/lib.ts | 44 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/web/src/firebase/auth/lib.ts b/web/src/firebase/auth/lib.ts index 2ba1fdcf..064eceb8 100644 --- a/web/src/firebase/auth/lib.ts +++ b/web/src/firebase/auth/lib.ts @@ -1,29 +1,29 @@ import { getAuth, onAuthStateChanged } from "firebase/auth"; import type { User } from "firebase/auth"; import type { IDToken } from "../../common/types"; +import { app } from "../config"; export class ErrUnauthorized extends Error {} -export async function getIdToken(): Promise { - const auth = getAuth(); - const user = await new Promise((resolve) => { - const unsubscribe = onAuthStateChanged(auth, (user: User | null) => { - if (user != null) { - resolve(user); - unsubscribe(); - } else { - console.error("getIdToken: user is null"); - } - }); - }); - - if (user == null) { - throw new Error( - "Client Error: firebase/auth/lib.ts: current user not found", - ); +let user: User; +let token: string; + +const auth = getAuth(app); +onAuthStateChanged(auth, async (u: User | null) => { + if (u != null) { + user = u; + token = await user.getIdToken(); } +}); + +async function refreshToken() { + token = await user.getIdToken(true); +} - return await user.getIdToken(true); +export async function getIdToken(): Promise { + if (token) return token; + await refreshToken(); + return token; } type RequestMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; @@ -48,9 +48,11 @@ export async function credFetch( }; } - const res = await fetch(`${path}?token=${idToken}`, init); + let res = await fetch(`${path}?token=${idToken}`, init); + if (res.status === 401) { + await refreshToken(); + res = await fetch(`${path}?token=${idToken}`); + } - // if (res.status === 401) throw new ErrUnauthorized(); - // if (!res.ok) throw new Error("response was not ok"); return res; } From 18bf215f5cd154f28d9347a9de99092aa6901ec6 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Wed, 23 Oct 2024 23:45:49 +0900 Subject: [PATCH 009/131] =?UTF-8?q?HOME=20=E3=81=A7=20REJECT=20=E3=81=97?= =?UTF-8?q?=E3=81=9F/=E3=81=95=E3=82=8C=E3=81=9F=E4=BA=BA=E3=82=92?= =?UTF-8?q?=E5=87=BA=E3=81=95=E3=81=AA=E3=81=84=20(#508)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 close #481 ## レビューリクエストを出す前にチェック! - [x] 改めてセルフレビューしたか - [x] 手動での動作検証を行ったか - [x] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [x] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [x] わかりやすいPRになっているか --- server/prisma/sql/recommend.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/prisma/sql/recommend.sql b/server/prisma/sql/recommend.sql index 2d7b3839..beaf2c52 100644 --- a/server/prisma/sql/recommend.sql +++ b/server/prisma/sql/recommend.sql @@ -8,13 +8,13 @@ AS overlap FROM "User" recv WHERE recv.id <> $1 AND NOT EXISTS ( - SELECT * FROM "Relationship" rel + SELECT 1 FROM "Relationship" rel WHERE rel."sendingUserId" IN ($1, recv.id) AND rel."receivingUserId" IN ($1, recv.id) - AND status = 'MATCHED' + AND (status = 'MATCHED' OR status = 'REJECTED') ) AND NOT EXISTS ( - SELECT * FROM "Relationship" rel_pd + SELECT 1 FROM "Relationship" rel_pd WHERE rel_pd."sendingUserId" = $1 AND rel_pd."receivingUserId" = recv.id AND status = 'PENDING' ) From 2370cac3947b8e8781e6e7feb6eee397576bfc66 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Sun, 27 Oct 2024 14:01:16 +0900 Subject: [PATCH 010/131] Fix zod error (#506) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 zod エラーの解消 closes #370 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- web/bun.lockb | Bin 118736 -> 119070 bytes web/package.json | 1 + web/src/api/chat/chat.ts | 19 ++++++++++++++++--- web/src/hooks/useSWR.ts | 22 +++++++++++++++------- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/web/bun.lockb b/web/bun.lockb index 3a9e6d19b0f2885328bd7c3021e327483a07031e..870ae427d6ecd0b62c08df27cfd69eff83abf329 100755 GIT binary patch delta 14231 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 delta 14067 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∋hUH}Y!S zXlE^$cJj^m4Pd&l1~y=|rp*85M7V8%i-fy|M!3(WYJ%=pqwLwn5l(#)urabx~B zz?Apl#%J>cm_zv#m;>dkFv>?j|Nhf1f_afy@Fg${x?;wY8N7-cJ^S2@Cv$||GG%gg z$W@s6w^(og-%|KJ1Aii&4plexEzV`2HWEV3g#TmC{jU_vzHebVL}p*dfZ02-U?#bA z8T}t;MzjW3K|(jC{ti>cgBuI#38vf|Hx|?%%z{!FX)?#kAjs^3A!fYJ9DjF2{7k0q zP%{IW!Qr@>ahVgUTr^$)WDt>q8Pzhhm* zzWx*IA7mS3=Ut0d4yg5R4A5!Ok zYalcQ7eoJB1L+O%Us;mS@~^C|{=ct*V$d{ydJRnq#L z^N0uJ%z0ipW1g?P0x3tfQeGLSeB~155mRL`q>GS}=6l40a>0DBoIl@J-hz}XyDjj_ zt_ytS<^>+?tKNk4Eu=vUJ=k5#U+9(V7y3%yMIP~(Oj+cW{TKPl-H@J;j>TSCezC6{ zyVxUU%3Y9(AO$b+h^J)c60aP!#8)1HG)o37^-BMxzVgAP9`TGk2)_uykI0w1AQeFhUhfebW#)SLw;ujM+AIU|;a@)d%lC+_@*t!G zkis{3M1joN0RJ|?KS+f#bR+!R2>&*E#1451(tD8FZ1RYma^@!Zw+a41+9g|UhJTyk z-)4_^Q5HkG2q|fc2frOH*aH8yz&}VY%h~ALu3O>XRu6s#xVaVnK^nBpgT43sZSZd! z{44N?H)Kiy{40QekoHT*bMWsu`1hPg9F)5t6+sFv^oT<;vk?9j!aqnyWWaX#w;ld% z_lTqNAfyA3!gqMYahbCN{_TK&klvP|&wIr?aysrO?O_xI#^+)v3? zMewW$o)vk-8ChK974OUTyS(B9xd8XG@&@i7%5E=s#W}eG_w({5?iXaw7ro*mnUDL& zQoQ69|CTAZen5eD+As{ z$GnM-dDA1R$b*m$Kng$P5!GbQA#}_kbPS{#GW0M8-eC;9!yf$PathLWklGyah}v@I z5e&Q|7b~}cFcg(kN^RbE* zWseViMEAnuXMVJa@$&fDN9=ZN+ByowyG?9NvsdbbodPM|4be517H&TOjPRR?-#993 zS{`2#ytp{pUfA=abvD1TsmAZTdc2vmrO@}2_v0!IsY^Y4C55PlA)-z0?H`ScakY@d zR9_0XESamUUPd;3Lyti&`TCo2{$K{VY;&7&0cIROlu s$<6S12~aJHEoy{Xhw4N z{J5Dp$c*Dg-v?B~6>P?F(aY}&rqLH>;eOM3fN3FS+$aWcU2LE_*Aw?Uxsliv;QKTT zXa+O~!U68Yd;xq3dk;5B;3&Xt!2*EWgd9HH zKp6m}0s{eVtngD~3NRIz20RGxbLBqZet;WxmjP~a+kvvcN3i`d@NeJ~;8WGUp{Om6 zsg#ByFqeC*PXXLAeHO?C@_^|8H-6to4h~%Ypzt0r3vt|!Qay6 z@kxODDo+DX0sjD=0oDPl013=t$+Hod56lDR0{;XQun<@PtOOPTRZV#rcqy>Wtpkgtw7 z4i1hU8n-g@rO0`jZ{?Z*^ZEgNtMYBjw{UI1gM4(7de&gED#~U$7mfs6cV(#;B1G8E za}lDh*y$fB(&~y$Dt({`2(peE-ngr9+OYK1&)dWcjg1w>vBqlCJ;Fc8I;?o{(6Dy@ zy7p`vn@Ed|aK=VNMSk5_or8wZRPG=VAS$RD$s$QKQR&GdS~%6*WHDHDQOQG))oUJ^@1q@c1wHG@<|?s|h%#E*&mI*K ztGU%PeMEGib!2$e==<->xt$t}JW&zOXjomSK7diNT73gO+u0VXd0*ilXr0qN{rs5C zwR0X?3;mdgNLFH+8r)X|1R7_GaRxbd@Y)v5ugrx4>PUq-O7=znCaV3U-s*d*TPKgF zoC&FVcvyIjS?88Gl%QJlgMxL`_~i*r&RovzUK0wgh^QE}pG_Ttf_PGiF(N?C>nCD_ z9u+N$)Q?E9osCv?ykdl?qo#YI*hDP?1zIPLFIB1+Q1RhLQ7{O1S-~82lzOphYbL0p zs-L2p5ALrMOA%{SPfCeu3@Fe#JA7&M)W;pITP`c9+eWnv3bEFq<2P%4=U1W1jgut` z*0JQ)yPoRvz2ETXN>Y5(6&4YuZm@X07D-lRj5-BcXP6U{`z9yNl6y)ro0T@|cZZFi z95PgFCll05S;F6TIYCuT6}8lp14K}ub=Y}Gw_z3Un%Va>3|oUw?Hz#XtXF49Ybn+C ziNtAX=Jq!RR?V`p7+9KDbx1`A>l1*VdOB6~63f(wjM7JZP1R(eXoWB*9wF6Kk}+GT z0|P~MbFOG$O%j~BDq&4f$FEVf=?!`xt}3h))5H?x{4*76W-~PshWu2?pD6}eC$s||>)#>Rp7svfFA8HT z4r}YulVt5O&X{uQ+F+}d0#qrRKc6I(dTNMB3cAq2I3S;&9DIE7oD(m?G#+gZ+b=q( z%IWAylg*Y@?b1ba(`C>T>0%`4TDqt&2B>O7#Uk538<(Q;*VekfWtOnnauw9bFK%k>;FamK;nC3%jQ8X$u~TS^VaYB?{J& z^0Jdo49|M&3tLIbUiBcd23lv(=S&YudFcMHnwBW|r~)X6hUyUY61x~@&Z}HF?ri?U zp&v^0($tTvgLUY==fbO3+butSyF|e{7XL~6M&-Zi(DC(>lp>Wh9B(XRJ_)i;tv5Tl zYU1%R&kfWY8oh`wNy>-P(FqeL3TnkrIin8=+>66wMsj{q+xk zyfC#FmW*lFPhB4=d;+aA{K2!kj~ljJK424JoSJYoP*p~uUe=-iHv7sq?OlHVjuHiZ z$~DXwb8^kH`fItPnvIMoMW>DGS{B~oKX`jbiU$@cg%0RPVRgau2mmpicVsP3LP!te;Dw?1=YB{q_~QIi7VEBGF;l2v1aLsWmvgd z>jQtags<#|YUdctpVogqM9&#EW$%*Z58A}pG-HMPO`58QH({XlzY;tB-+2GnvD+WR z5+`qGjOMR;XNl;R)_*@`&+K}8`J4TIHb`cdM%6k-_@r6#i|f z^lR2%7@bTnD~uIU74GFe0IqFWs`FTM?Bj^66XP}Bc#p=#9u|`;-#%w5#U0&wVyxKG Mc<1x~6r&RU132|!v;Y7A diff --git a/web/package.json b/web/package.json index f07c77dc..53c29a8d 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,7 @@ "@fontsource/roboto": "^5.0.13", "@mui/icons-material": "^5.16.7", "@mui/material": "^5.15.20", + "devalue": "^5.1.1", "firebase": "^10.12.2", "framer-motion": "^11.3.23", "notistack": "^3.0.1", diff --git a/web/src/api/chat/chat.ts b/web/src/api/chat/chat.ts index 182ee85a..c161e1ba 100644 --- a/web/src/api/chat/chat.ts +++ b/web/src/api/chat/chat.ts @@ -54,7 +54,16 @@ export async function updateMessage( export async function overview(): Promise { const res = await credFetch("GET", endpoints.roomOverview); if (res.status === 401) throw new ErrUnauthorized(); - return await res.json(); + const json: RoomOverview[] = await res.json(); + + if (!Array.isArray(json)) return json; + + for (const room of json) { + if (!room.lastMsg) continue; + room.lastMsg.createdAt = new Date(room.lastMsg.createdAt); + } + + return json; } //// DM関連 //// @@ -74,7 +83,6 @@ export async function sendDM( return res.json(); } -// WARNING: don't use this outside of api/ export async function getDM(friendId: UserID): Promise { const res = await credFetch("GET", endpoints.dmWith(friendId)); if (res.status === 401) throw new ErrUnauthorized(); @@ -82,7 +90,12 @@ export async function getDM(friendId: UserID): Promise { throw new Error( `getDM() failed: expected status code 200, got ${res.status}`, ); - return res.json(); + const json: DMRoom = await res.json(); + if (!Array.isArray(json?.messages)) return json; + for (const m of json.messages) { + m.createdAt = new Date(m.createdAt); + } + return json; } ////グループチャット関連//// diff --git a/web/src/hooks/useSWR.ts b/web/src/hooks/useSWR.ts index 0ce481af..83d3906d 100644 --- a/web/src/hooks/useSWR.ts +++ b/web/src/hooks/useSWR.ts @@ -1,3 +1,4 @@ +import { parse, stringify } from "devalue"; import { useCallback, useEffect, useState } from "react"; import type { ZodSchema } from "zod"; @@ -72,15 +73,16 @@ export function useSWR( const result = schema.safeParse(data); if (!result.success) { console.error( - `WARNING: useSWR: UNEXPECTED ZOD PARSE ERROR: Schema Parse Error: ${result.error.message}`, + `useSWR: Schema Parse Error | in incoming data | at schema ${CACHE_KEY} | Error: ${result.error.message}`, ); + console.log("data:", data); } setState({ data: data, current: "success", error: null, }); - localStorage.setItem(CACHE_KEY, JSON.stringify(data)); + localStorage.setItem(CACHE_KEY, stringify(data)); } catch (e) { setState({ data: null, @@ -92,7 +94,7 @@ export function useSWR( const write = useCallback( (data: T) => { - localStorage.setItem(CACHE_KEY, JSON.stringify(data)); + localStorage.setItem(CACHE_KEY, stringify(data)); }, [CACHE_KEY], ); @@ -115,10 +117,16 @@ function loadOldData( const oldData = localStorage.getItem(CACHE_KEY); if (oldData) { try { - const data = JSON.parse(oldData); - const parse = schema.safeParse(data); - if (!parse.success) - console.error(`useSWR: zodParseError: ${parse.error}`); + const data = parse(oldData); + const parsed = schema.safeParse(data); + if (!parsed.success) { + console.error( + `useSWR: zodParseError | in stale data | at schema ${CACHE_KEY} | ${parsed.error}`, + ); + console.log("data:", data); + // because loading old data isn't critical to the application and wrong stale data may cause several problems, + throw ""; + } return { current: "stale", data, From 6a4fe055400d25ffef05eff2ef408e450d2bfb1b Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Sat, 2 Nov 2024 00:10:36 +0900 Subject: [PATCH 011/131] =?UTF-8?q?prepare-deploy-server=20=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#505)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [x] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 9c75503f..72b7c243 100644 --- a/Makefile +++ b/Makefile @@ -45,10 +45,9 @@ test: dev-db prepare-deploy-web: copy-common cd web; bun install; bun run build -prepare-deploy-server: copy-common - cd server; bun install; npx prisma generate; +prepare-deploy-server: copy-common sync-server generate-sql deploy-server: - cd server; bun src/index.ts + cd server; bun src/main.ts docker: copy-common @# deferring `docker compose down`. https://qiita.com/KEINOS/items/532dc395fe0f89c2b574 From d21df4d3529efb82e214468f4a526dfd5c918f19 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Sat, 2 Nov 2024 10:32:47 +0900 Subject: [PATCH 012/131] Scraper (#487) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 closes #486 ## 具体的な変更内容 後期学部はカバー 大学院はカバーしていない ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- .cspell.json | 10 +- .github/workflows/rust.yml | 32 + biome.json | 2 +- flake.lock | 40 + flake.nix | 23 +- nix/prisma.nix | 12 + nix/rust-toolchain.nix | 5 + package.json | 2 +- scraper/.gitignore | 5 + scraper/Cargo.lock | 1907 +++++++++++++++++++++++++++++++++++ scraper/Cargo.toml | 16 + scraper/readme.md | 25 + scraper/rust-toolchain.toml | 4 + scraper/src/io.rs | 36 + scraper/src/logger.rs | 43 + scraper/src/main.rs | 99 ++ scraper/src/parser.rs | 57 ++ scraper/src/types.rs | 15 + scraper/src/urls.rs | 42 + web/bun.lockb | Bin 119070 -> 119070 bytes 20 files changed, 2360 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/rust.yml create mode 100644 nix/prisma.nix create mode 100644 nix/rust-toolchain.nix create mode 100644 scraper/.gitignore create mode 100644 scraper/Cargo.lock create mode 100644 scraper/Cargo.toml create mode 100644 scraper/readme.md create mode 100644 scraper/rust-toolchain.toml create mode 100644 scraper/src/io.rs create mode 100644 scraper/src/logger.rs create mode 100644 scraper/src/main.rs create mode 100644 scraper/src/parser.rs create mode 100644 scraper/src/types.rs create mode 100644 scraper/src/urls.rs diff --git a/.cspell.json b/.cspell.json index 98a900be..1f059cd5 100644 --- a/.cspell.json +++ b/.cspell.json @@ -15,7 +15,9 @@ "qiita", "safify", "supabase", - "swiper" + "swiper", + "reqwest", + "chrono" ], "dictionaries": [ "softwareTerms", @@ -31,6 +33,7 @@ ], "ignorePaths": [ "flake.*", + "nix/*", "**/.git/**", "**/node_modules/**", "server/target/**", @@ -39,6 +42,9 @@ "**/bun.lockb", "**/*.svg", "**/migration.sql", - "**/data.json" + "**/data.json", + "**/Cargo.*", + "scraper/target", + "**/rust-toolchain.toml" ] } diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..4e53c038 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,32 @@ +name: Rust +on: + pull_request: + push: + branches: [main] + +jobs: + all: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + RUSTFLAGS: -D warnings + defaults: + run: + working-directory: scraper + steps: + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/ + ~/.rustup/ + scraper/target + key: cargo-cache-${{ github.job }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: clippy,rustfmt + - run: cargo build + - run: cargo build --release + - run: cargo test + - run: cargo clippy + - run: cargo fmt --check diff --git a/biome.json b/biome.json index f16f6128..b2ca8999 100644 --- a/biome.json +++ b/biome.json @@ -10,6 +10,6 @@ "useIgnoreFile": true }, "files": { - "ignore": ["bun.lockb", "server/target", "data.json"] + "ignore": ["bun.lockb", "server/target", "data.json", "scraper/target"] } } diff --git a/flake.lock b/flake.lock index 6ccba3b6..98ba3177 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,27 @@ { "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1727764514, + "narHash": "sha256-tvN9v5gTxLI5zOKsNvYl1aUxIitHm8Nj3vKdXNfJo50=", + "owner": "nix-community", + "repo": "fenix", + "rev": "a9d2e5fa8d77af05240230c9569bbbddd28ccfaf", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "monthly", + "repo": "fenix", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -89,11 +111,29 @@ }, "root": { "inputs": { + "fenix": "fenix", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "prisma-utils": "prisma-utils" } }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1727706011, + "narHash": "sha256-xxgUHwwJ+1xQQoUWvLDo807IZ0MDldkfr9N1G4fvNJU=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "28830ff2f1158ee92f4852ef3ec35af0935d1562", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index 7c1a0a42..25c451e5 100644 --- a/flake.nix +++ b/flake.nix @@ -4,21 +4,18 @@ nixpkgs.url = "github:NixOS/nixpkgs/master"; flake-utils.url = "github:numtide/flake-utils"; prisma-utils.url = "github:VanCoding/nix-prisma-utils"; + fenix = { + url = "github:nix-community/fenix/monthly"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { nixpkgs, flake-utils, prisma-utils, ... }: + outputs = { nixpkgs, flake-utils, prisma-utils, fenix, ... }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; - - prisma = (prisma-utils.lib.prisma-factory { - nixpkgs = pkgs; - prisma-fmt-hash = "sha256-atD5GZfmeU86mF1V6flAshxg4fFR2ews7EwaJWZZzbc="; # just copy these hashes for now, and then change them when nix complains about the mismatch - query-engine-hash = "sha256-8FTZaKmQCf9lrDQvkF5yWPeZ7TSVfFjTbjdbWWEHgq4="; - libquery-engine-hash = "sha256-USIdaum87ekGY6F6DaL/tKH0BAZvHBDK7zjmCLo//kM="; - schema-engine-hash = "sha256-k5MkxXViEqojbkkcW/4iBFNdfhb9PlMEF1M2dyhfOok="; - }).fromNpmLock - ./server/package-lock.json; # <--- path to our package-lock.json file that contains the version of prisma-engines + prisma = import ./nix/prisma.nix { inherit prisma-utils pkgs; }; + rust-pkgs = import ./nix/rust-toolchain.nix { inherit fenix system; }; in { devShell = pkgs.mkShell { @@ -28,10 +25,14 @@ gnumake bun biome + pkg-config + openssl + ] ++ [ + rust-pkgs ]; shellHook = '' export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${pkgs.stdenv.cc.cc.lib}/lib - '' + (if pkgs.system == "x86_64-linux" then prisma.shellHook else ""); + '' + prisma.shellHook; }; }); } diff --git a/nix/prisma.nix b/nix/prisma.nix new file mode 100644 index 00000000..563d59d3 --- /dev/null +++ b/nix/prisma.nix @@ -0,0 +1,12 @@ +{ prisma-utils, pkgs }: rec { + prisma = (prisma-utils.lib.prisma-factory { + nixpkgs = pkgs; + prisma-fmt-hash = "sha256-atD5GZfmeU86mF1V6flAshxg4fFR2ews7EwaJWZZzbc="; # just copy these hashes for now, and then change them when nix complains about the mismatch + query-engine-hash = "sha256-8FTZaKmQCf9lrDQvkF5yWPeZ7TSVfFjTbjdbWWEHgq4="; + libquery-engine-hash = "sha256-USIdaum87ekGY6F6DaL/tKH0BAZvHBDK7zjmCLo//kM="; + schema-engine-hash = "sha256-k5MkxXViEqojbkkcW/4iBFNdfhb9PlMEF1M2dyhfOok="; + }).fromNpmLock + ./../server/package-lock.json; # <--- path to our package-lock.json file that contains the version of prisma-engines + + shellHook = (if pkgs.system == "x86_64-linux" then prisma.shellHook else ""); +} diff --git a/nix/rust-toolchain.nix b/nix/rust-toolchain.nix new file mode 100644 index 00000000..5a919688 --- /dev/null +++ b/nix/rust-toolchain.nix @@ -0,0 +1,5 @@ +{ fenix, system }: +fenix.packages.${system}.fromToolchainFile { + file = ../scraper/rust-toolchain.toml; + sha256 = "sha256-yMuSb5eQPO/bHv+Bcf/US8LVMbf/G/0MSfiPwBhiPpk="; +} diff --git a/package.json b/package.json index 8b60397f..d12a6c1e 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,6 @@ "zod": "^3.23.8" }, "lint-staged": { - "*.{js,jsx,ts,tsx}": ["biome check --write"] + "*.{js,jsx,ts,tsx,json}": ["biome check --write"] } } diff --git a/scraper/.gitignore b/scraper/.gitignore new file mode 100644 index 00000000..c5903a8d --- /dev/null +++ b/scraper/.gitignore @@ -0,0 +1,5 @@ +/target +/data.json +.cache +/course-mate-scraper + diff --git a/scraper/Cargo.lock b/scraper/Cargo.lock new file mode 100644 index 00000000..19fb391b --- /dev/null +++ b/scraper/Cargo.lock @@ -0,0 +1,1907 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + +[[package]] +name = "cc" +version = "1.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "course-mate-scraper" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "futures", + "lazy_static", + "reqwest", + "scraper", + "serde", + "serde_json", + "sha2", + "tokio", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.11.2", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "ego-tree" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "html5ever" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" +dependencies = [ + "log", + "phf 0.11.2", + "phf_codegen 0.11.2", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro2" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "reqwest" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scraper" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90460b31bfe1fc07be8262e42c665ad97118d4585869de9345a84d501a9eaf0" +dependencies = [ + "ahash", + "cssparser", + "ego-tree", + "getopts", + "html5ever", + "once_cell", + "selectors", + "tendril", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" +dependencies = [ + "bitflags", + "cssparser", + "derive_more", + "fxhash", + "log", + "new_debug_unreachable", + "phf 0.10.1", + "phf_codegen 0.10.0", + "precomputed-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "servo_arc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/scraper/Cargo.toml b/scraper/Cargo.toml new file mode 100644 index 00000000..1711fd0c --- /dev/null +++ b/scraper/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "course-mate-scraper" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.89" +chrono = "0.4.38" +futures = "0.3.31" +lazy_static = "1.5.0" +reqwest = "0.12.8" +scraper = "0.20.0" +serde = { version = "1.0.210", features = ["serde_derive"] } +serde_json = "1.0.128" +sha2 = "0.10.8" +tokio = { version = "1.40.0", features = ["full"] } diff --git a/scraper/readme.md b/scraper/readme.md new file mode 100644 index 00000000..9b1b87c2 --- /dev/null +++ b/scraper/readme.md @@ -0,0 +1,25 @@ +# 後期課程の授業をスクレイピングするスクリプト + +https://catalog.he.u-tokyo.ac.jp/result にある授業情報を取得するスクリプトです。 + +## Quick Start + +以下のコマンドを実行すると、 `data.json` に授業情報がjson形式で保存されます。 + +```bash +cd /path/to/this/dir +cargo run --release +``` + +## Maintaining + +### Add faculty + +ページに移動したときに、左側に学科を選ぶとhttps://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=1のようになります。 +faculty_idが学科のIDです。 +urls.rsのUrlsに学部の名前とその url の tuple を追加してください。 +すでに全ての学科のIDが入っているので、特に追加するような状況にならない限りは変更する必要はありません。 + +### Extending code + +コード中に .unwrap() が多くあると思います。これは意図的です。 diff --git a/scraper/rust-toolchain.toml b/scraper/rust-toolchain.toml new file mode 100644 index 00000000..044314a7 --- /dev/null +++ b/scraper/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.82.0" +targets = ["aarch64-apple-darwin", "x86_64-unknown-linux-gnu"] +components = ["rustc", "cargo", "rustfmt", "clippy"] diff --git a/scraper/src/io.rs b/scraper/src/io.rs new file mode 100644 index 00000000..b6b3cba2 --- /dev/null +++ b/scraper/src/io.rs @@ -0,0 +1,36 @@ +use crate::types::*; +use anyhow::ensure; +use sha2::{Digest, Sha256}; +use tokio::fs; +use tokio::io::AsyncWriteExt; + +pub async fn write_to(file: &mut fs::File, content: Entry) -> anyhow::Result<()> { + let s = serde_json::to_string(&content)?; + file.write_all(s.as_bytes()).await?; + Ok(()) +} + +use crate::CACHE_DIR; + +pub async fn request(url: &str) -> anyhow::Result { + println!("[request] sending request to {}", url); + + let hash = Sha256::digest(url.as_bytes()); + let path = format!("{CACHE_DIR}/{:x}", hash); + if let Ok(bytes) = fs::read(&path).await { + if let Ok(text) = String::from_utf8(bytes) { + return Ok(text); + } + } + let res = reqwest::get(url).await?; + let status = res.status().as_u16(); + ensure!( + (200..=299).contains(&status), + "Status check failed: expected 200~299, got {}", + status + ); + let text = res.text().await?; + let mut f = tokio::fs::File::create(path).await?; + f.write_all(text.as_bytes()).await?; + Ok(text) +} diff --git a/scraper/src/logger.rs b/scraper/src/logger.rs new file mode 100644 index 00000000..c2311d87 --- /dev/null +++ b/scraper/src/logger.rs @@ -0,0 +1,43 @@ +pub struct Logger { + limit: usize, + current: usize, + start_ms: i64, + start_sec: i64, + start_min: i64, +} + +impl Logger { + pub fn new(limit: usize) -> Self { + let start_ms = chrono::Local::now().timestamp_millis(); + let start_sec = chrono::Local::now().timestamp(); + let start_min = chrono::Local::now().timestamp() / 60; + Logger { + current: 0, + limit, + start_ms, + start_sec, + start_min, + } + } + pub fn done(&mut self, name: &str) { + let now_ms = chrono::Local::now().timestamp_millis(); + let now_sec = chrono::Local::now().timestamp(); + let now_min = now_sec / 60; + self.current += 1; + let count = self.current; + println!( + "[log] faculty {name} done. ({count} / {}) timestamp: {}ms / {}sec / {}min", + self.limit, + now_ms - self.start_ms, + now_sec - self.start_sec, + now_min - self.start_min, + ); + } + pub fn close(self) -> Result<(), ()> { + if self.limit == self.current { + Ok(()) + } else { + Err(()) + } + } +} diff --git a/scraper/src/main.rs b/scraper/src/main.rs new file mode 100644 index 00000000..d3fd67b6 --- /dev/null +++ b/scraper/src/main.rs @@ -0,0 +1,99 @@ +mod io; +mod logger; +mod parser; +mod types; +mod urls; + +use lazy_static::lazy_static; +use std::time::Duration; +use tokio::{fs, io::AsyncWriteExt}; + +use anyhow::Context; +use tokio::time::sleep; +use types::*; + +use scraper::{Html, Selector}; +use urls::URLS; + +const RESULT_FILE: &str = "./data.json"; +const CACHE_DIR: &str = "./.cache"; + +#[tokio::main(flavor = "multi_thread")] +async fn main() { + println!("[log] starting..."); + + let _ = fs::DirBuilder::new().create(CACHE_DIR).await; + + let mut file = fs::File::create(RESULT_FILE) + .await + .expect("Failed to create file"); + file.write_all("[".as_bytes()).await.unwrap(); + + let total = URLS.len(); + let mut logger = logger::Logger::new(total); + for (faculty_name, base_url) in URLS { + let courses = get_courses_of(base_url).await; + logger.done(faculty_name); + let result = Entry { + name: faculty_name.to_owned(), + courses, + }; + io::write_to(&mut file, result).await.unwrap(); + file.write_all(",".as_bytes()).await.unwrap(); + } + + file.write_all("]".as_bytes()).await.unwrap(); + logger.close().unwrap(); +} + +async fn get_courses_of(base_url: &str) -> Vec { + let courses = page_index_pages(base_url) + .await + .into_iter() + .map(|content_page_url| async { + let html = scrape(&content_page_url).await; + parser::parse_course_info(html) + .context(content_page_url) + .unwrap() + }); + futures::future::join_all(courses) + .await + .into_iter() + .collect::>() +} + +lazy_static! { + static ref DETAIL_BUTTONS: Selector = + Selector::parse(".catalog-search-result-card-header-detail-link") + .expect("invalid selector"); +} +const BASE_URL: &str = "https://catalog.he.u-tokyo.ac.jp/"; + +async fn page_index_pages(base_url: &str) -> Vec { + let mut urls: Vec = Vec::new(); + for key in 0.. { + let html = scrape(&format!("{}{}", base_url, key)).await; + if html.select(&DETAIL_BUTTONS).next().is_none() { + break; + } + urls.extend( + html.select(&DETAIL_BUTTONS) + .map(|elem| BASE_URL.to_owned() + elem.attr("href").unwrap()), + ); + } + urls +} + +async fn scrape(url: &str) -> Html { + for tries in 0..10 { + let res = io::request(url).await; + match res { + Ok(val) => return Html::parse_document(&val), + Err(err) => { + eprintln!("request error: {err} for {} times", tries + 1); + sleep(Duration::from_millis(200)).await; + } + } + } + panic!("Request failed too many times"); +} diff --git a/scraper/src/parser.rs b/scraper/src/parser.rs new file mode 100644 index 00000000..f3214ed2 --- /dev/null +++ b/scraper/src/parser.rs @@ -0,0 +1,57 @@ +use anyhow::anyhow; +use lazy_static::lazy_static; +use scraper::{Html, Selector}; + +use crate::types::*; + +lazy_static! { + static ref NAME_SELECTOR: Selector = + Selector::parse(".catalog-page-detail-table-cell.name-cell").unwrap(); + static ref TEACHER_SELECTOR: Selector = + Selector::parse(".catalog-page-detail-table-cell.lecturer-cell").unwrap(); + static ref SEMESTER_SELECTOR: Selector = + Selector::parse(".catalog-page-detail-table-cell.semester-cell").unwrap(); + static ref PERIOD_SELECTOR: Selector = + Selector::parse(".catalog-page-detail-table-cell.period-cell").unwrap(); + static ref CODE_SELECTOR: Selector = + Selector::parse(".catalog-page-detail-table-cell.code-cell").unwrap(); +} + +pub fn parse_course_info(html: Html) -> anyhow::Result { + Ok(Course { + name: select(&html, &NAME_SELECTOR, 1)?, + teacher: select(&html, &TEACHER_SELECTOR, 1)?, + semester: select_all(&html, &SEMESTER_SELECTOR, 1)?.join(","), + period: select(&html, &PERIOD_SELECTOR, 1)?, + code: select_all(&html, &CODE_SELECTOR, 1)?.join(" "), + }) +} + +fn select(html: &Html, selector: &Selector, nth: usize) -> anyhow::Result { + html.select(selector) + .nth(nth) + .ok_or(anyhow!( + "Couldn't find matching element for selector {:?}", + selector, + )) + .map(|val| val.text().next().unwrap().trim().to_owned()) +} + +fn select_all<'a>( + html: &'a Html, + selector: &'static Selector, + nth: usize, +) -> anyhow::Result> { + html.select(selector) + .nth(nth) + .ok_or(anyhow!( + "Couldn't find matching element for selector {:?}", + selector, + )) + .map(|val| { + val.text() + .map(|text| text.trim()) + .filter(|&text| !text.is_empty()) + .collect::>() + }) +} diff --git a/scraper/src/types.rs b/scraper/src/types.rs new file mode 100644 index 00000000..664d3c41 --- /dev/null +++ b/scraper/src/types.rs @@ -0,0 +1,15 @@ +use serde::Serialize; + +#[derive(Serialize, Clone)] +pub struct Course { + pub name: String, + pub teacher: String, + pub semester: String, + pub period: String, + pub code: String, +} +#[derive(Serialize, Clone)] +pub struct Entry { + pub name: String, + pub courses: Vec, +} diff --git a/scraper/src/urls.rs b/scraper/src/urls.rs new file mode 100644 index 00000000..aaa7f78c --- /dev/null +++ b/scraper/src/urls.rs @@ -0,0 +1,42 @@ +pub static URLS: [(&str, &str); 10] = [ + ( + "law", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=1&page=", + ), + ( + "medicine", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=2&page=", + ), + ( + "engineering", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=3&page=", + ), + ( + "arts", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=4&page=", + ), + ( + "science", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=5&page=", + ), + ( + "agriculture", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=6&page=", + ), + ( + "economics", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=7&page=", + ), + ( + "liberal_arts", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=8&page=", + ), + ( + "education", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=9&page=", + ), + ( + "pharmacy", + "https://catalog.he.u-tokyo.ac.jp/result?type=ug&faculty_id=10&page=", + ), +]; diff --git a/web/bun.lockb b/web/bun.lockb index 870ae427d6ecd0b62c08df27cfd69eff83abf329..299f713b26f45b25dbfcf373fc6e6287a0cd91ca 100755 GIT binary patch delta 25 hcmbQYh<)B7_J%Et{08idai)5PdWPG@3>Y~U0sv@H2Lk{A delta 25 dcmbQYh<)B7_J%Et{08hy3}CQb%z%+&Apl!r1+xGE From a3a8e5cea36978a43e6e0611e3506f05cb1e0a63 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Sat, 2 Nov 2024 10:40:34 +0900 Subject: [PATCH 013/131] =?UTF-8?q?=E8=88=88=E5=91=B3=E3=81=AE=E3=81=82?= =?UTF-8?q?=E3=82=8B=E5=88=86=E9=87=8E=20(server=20=E3=81=AE=E3=81=BF)=20(?= =?UTF-8?q?#488)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [x] 改めてセルフレビューしたか - [x] 手動での動作検証を行ったか - [x] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [x] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [x] わかりやすいPRになっているか --- common/types.ts | 1 + common/zod/schemas.ts | 6 + common/zod/types.ts | 2 + server/package.json | 2 +- server/prisma/schema.prisma | 24 ++- server/src/database/interest.test.ts | 25 +++ server/src/database/interest.ts | 39 ++++ server/src/functions/user.test.ts | 3 +- server/src/seeds/data/subjects.ts | 10 ++ server/src/seeds/seed-test.ts | 95 ++++++++++ server/src/seeds/seed.ts | 257 ++------------------------- server/src/seeds/test-data/data.ts | 161 +++++++++++++++++ 12 files changed, 380 insertions(+), 245 deletions(-) create mode 100644 server/src/database/interest.test.ts create mode 100644 server/src/database/interest.ts create mode 100644 server/src/seeds/data/subjects.ts create mode 100644 server/src/seeds/seed-test.ts create mode 100644 server/src/seeds/test-data/data.ts diff --git a/common/types.ts b/common/types.ts index 0bd9ed92..c89a5b5e 100644 --- a/common/types.ts +++ b/common/types.ts @@ -6,6 +6,7 @@ export type { IDToken, Gender, RelationshipStatus, + InterestSubject, User, InitUser, UpdateUser, diff --git a/common/zod/schemas.ts b/common/zod/schemas.ts index 2e8d8c0b..2c1c9cad 100644 --- a/common/zod/schemas.ts +++ b/common/zod/schemas.ts @@ -32,6 +32,12 @@ export const IntroLongSchema = z // .min(2, { message: "自己紹介文は2文字以上です" }) .max(225, { message: "自己紹介文は225文字以下です" }); +export const InterestSubjectSchema = z.object({ + id: z.number(), + name: z.string(), + group: z.string(), +}); + export const UserSchema = z.object({ id: UserIDSchema, guid: GUIDSchema, diff --git a/common/zod/types.ts b/common/zod/types.ts index 6929b022..c0f1a44b 100644 --- a/common/zod/types.ts +++ b/common/zod/types.ts @@ -14,6 +14,7 @@ import type { InitRoomSchema, InitSharedRoomSchema, InitUserSchema, + InterestSubjectSchema, IntroLongSchema, IntroShortSchema, MessageIDSchema, @@ -45,6 +46,7 @@ export type Name = z.infer; export type PictureUrl = z.infer; export type Gender = z.infer; export type RelationshipStatus = z.infer; +export type InterestSubject = z.infer; export type User = z.infer; export type InitUser = z.infer; export type UpdateUser = z.infer; diff --git a/server/package.json b/server/package.json index a1974ba3..188e2cca 100644 --- a/server/package.json +++ b/server/package.json @@ -11,7 +11,7 @@ "prisma-generate-sql": "bunx dotenv -e .env.dev -- prisma generate --sql" }, "prisma": { - "seed": "bun src/seeds/seed.ts" + "seed": "bun src/seeds/seed-test.ts" }, "keywords": [], "author": "", diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index c4cdaefd..67986b44 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -1,7 +1,7 @@ //schema.prisma generator client { - provider = "prisma-client-js" - binaryTargets = ["native", "debian-openssl-3.0.x"] + provider = "prisma-client-js" + binaryTargets = ["native", "debian-openssl-3.0.x"] previewFeatures = ["typedSql"] } @@ -39,6 +39,7 @@ model User { enrollments Enrollment[] sendingUsers Relationship[] @relation("sending") // 自分がマッチリクエストを送ったユーザー receivingUsers Relationship[] @relation("receiving") // 自分にマッチリクエストを送ったユーザー + interests Interest[] } // プロフィールの画像。 @@ -47,6 +48,25 @@ model Avatar { data Bytes } +model InterestSubject { + id Int @id @default(autoincrement()) + name String + group String // such as Computer Science | name = ML + + @@unique([name, group]) + Interest Interest[] // ignore this +} + +// User->Interest->InterestSubject +model Interest { + userId Int + user User @relation(fields: [userId], references: [id]) + subjectId Int + subject InterestSubject @relation(fields: [subjectId], references: [id]) + + @@unique([userId, subjectId]) +} + // enum Gender { // MALE // FEMALE diff --git a/server/src/database/interest.test.ts b/server/src/database/interest.test.ts new file mode 100644 index 00000000..b9a22b25 --- /dev/null +++ b/server/src/database/interest.test.ts @@ -0,0 +1,25 @@ +import { beforeAll, expect, test } from "bun:test"; +import { assertLocalDB } from "../load-env"; +import * as interest from "./interest"; + +beforeAll(assertLocalDB); + +test("list", async () => { + const got = (await interest.all()).sort((a, b) => a.id - b.id); + expect(got).toEqual([ + { id: 1, group: "Computer Science", name: "型システム" }, + { id: 2, group: "Computer Science", name: "機械学習" }, + { id: 3, group: "Computer Science", name: "CPU アーキテクチャ" }, + { id: 4, group: "Computer Science", name: "分散処理" }, + { id: 5, group: "Math", name: "Lean4" }, + ]); +}); + +test("get by user id", async () => { + const got = (await interest.of(101)).sort((a, b) => a.id - b.id); + expect(got).toEqual([ + { id: 1, group: "Computer Science", name: "型システム" }, + { id: 2, group: "Computer Science", name: "機械学習" }, + { id: 3, group: "Computer Science", name: "CPU アーキテクチャ" }, + ]); +}); diff --git a/server/src/database/interest.ts b/server/src/database/interest.ts new file mode 100644 index 00000000..00f4b10c --- /dev/null +++ b/server/src/database/interest.ts @@ -0,0 +1,39 @@ +import type { InterestSubject, UserID } from "../common/types"; +import { prisma } from "./client"; + +export async function all(): Promise { + return await prisma.interestSubject.findMany(); +} + +export async function get(id: number): Promise { + return await prisma.interestSubject.findUnique({ where: { id } }); +} + +export async function of(userId: UserID): Promise { + return await prisma.interest + .findMany({ + where: { + userId, + }, + select: { + subject: true, + }, + }) + .then((res) => res.map((interest) => interest.subject)); +} + +export async function add(userId: UserID, subjectId: number) { + return await prisma.interest.create({ + data: { + userId, + subjectId, + }, + }); +} +export async function remove(userId: UserID, subjectId: number) { + return await prisma.interest.delete({ + where: { + userId_subjectId: { userId, subjectId }, + }, + }); +} diff --git a/server/src/functions/user.test.ts b/server/src/functions/user.test.ts index 36c316f4..ba04f302 100644 --- a/server/src/functions/user.test.ts +++ b/server/src/functions/user.test.ts @@ -16,7 +16,8 @@ test("get all users", async () => { expect(result.code).toBe(200); expect(result.body).toSatisfy((s) => s.length === 3); expect(result.body).toSatisfy( - (s) => typeof s !== "string" && s[0].name === "田中太郎", + (s) => + typeof s !== "string" && s.some((person) => person.name === "田中太郎"), ); }); diff --git a/server/src/seeds/data/subjects.ts b/server/src/seeds/data/subjects.ts new file mode 100644 index 00000000..2cfe78ab --- /dev/null +++ b/server/src/seeds/data/subjects.ts @@ -0,0 +1,10 @@ +export const subjects = [ + { + group: "Computer Science", + subjects: ["機械学習", "CPU アーキテクチャ", "型システム", "分散処理"], + }, + { + group: "Math", + subjects: ["Lean4"], + }, +]; diff --git a/server/src/seeds/seed-test.ts b/server/src/seeds/seed-test.ts new file mode 100644 index 00000000..23b54a51 --- /dev/null +++ b/server/src/seeds/seed-test.ts @@ -0,0 +1,95 @@ +import { prisma } from "../database/client"; +import { + courses, + enrollments, + interest, + slots, + subjects, + users, +} from "./test-data/data"; + +async function main() { + await Promise.all( + subjects.map(async ({ group, subjects }) => { + for (const [id, name] of subjects) { + await prisma.interestSubject.upsert({ + where: { id }, + update: { name, group }, + create: { id, name, group }, + }); + } + }), + ); + + await Promise.all( + users.map(async (user) => { + await prisma.user.upsert({ + where: { id: user.id }, + update: {}, + create: user, + }); + }), + ); + + await Promise.all( + interest.map(async (interest) => { + await prisma.interest.upsert({ + where: { + userId_subjectId: interest, + }, + update: interest, + create: interest, + }); + }), + ); + + await Promise.all( + courses.map(async (course) => { + await prisma.course.upsert({ + where: { id: course.id }, + update: course, + create: course, + }); + }), + ); + + await Promise.all( + slots.map(async (slot) => { + await prisma.slot.upsert({ + where: { + courseId_period_day: { + courseId: slot.courseId, + period: slot.period, + day: slot.day, + }, + }, + update: slot, + create: slot, + }); + }), + ); + + const promises = enrollments.map(async ([user, course]) => { + await prisma.enrollment.upsert({ + where: { + userId_courseId: { userId: user, courseId: course }, + }, + update: {}, + create: { + userId: user, + courseId: course, + }, + }); + }); + await Promise.all(promises); +} + +await main() + .then(async () => { + await prisma.$disconnect(); + }) + .catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); + }); diff --git a/server/src/seeds/seed.ts b/server/src/seeds/seed.ts index 2cb2fe30..bf98d1a6 100644 --- a/server/src/seeds/seed.ts +++ b/server/src/seeds/seed.ts @@ -1,243 +1,18 @@ import { prisma } from "../database/client"; - -async function main() { - // users - await prisma.user.upsert({ - where: { id: 101 }, - update: {}, - create: { - id: 101, - name: "田中太郎", - gender: "男", - grade: "D2", - faculty: "工学部", - department: "電気電子工学科", - intro: "田中太郎です。", - pictureUrl: - "https://firebasestorage.googleapis.com/v0/b/coursemate-tutorial.appspot.com/o/YdulS1s41LVh1nWgOBqzMiXN7803%2FtP5PrelZVe6v4UoF.jpg?alt=media&token=252da169-cccb-45b3-bec6-946ec3de3e27", - guid: "abc101", - }, - }); - - await prisma.user.upsert({ - where: { id: 102 }, - update: {}, - create: { - id: 102, - name: "山田花子", - gender: "女", - grade: "B2", - faculty: "経済学部", - department: "経営学科", - intro: "山田花子です。", - pictureUrl: - "https://firebasestorage.googleapis.com/v0/b/coursemate-tutorial.appspot.com/o/45QiYkH65OWHZYPruT9sHKAHa4I3%2FulavVaTxMNACkcn4.jpg?alt=media&token=6eea4c9f-c9ec-4c6e-943b-96b0afe013c3", - guid: "abc102", - }, - }); - await prisma.user.upsert({ - where: { id: 103 }, - update: {}, - create: { - id: 103, - name: "小五郎", - gender: "男", - grade: "B3", - faculty: "経済学部", - department: "経営学科", - intro: "小五郎です。", - pictureUrl: - "https://firebasestorage.googleapis.com/v0/b/coursemate-tutorial.appspot.com/o/45QiYkH65OWHZYPruT9sHKAHa4I3%2FulavVaTxMNACkcn4.jpg?alt=media&token=6eea4c9f-c9ec-4c6e-943b-96b0afe013c3", - guid: "abc103", - }, - }); // courses - - await prisma.course.upsert({ - where: { id: "10001" }, - update: {}, - create: { - id: "10001", - name: "国語八列", - teacher: "足助太郎", - }, - }); - await prisma.course.upsert({ - where: { id: "10002" }, - update: {}, - create: { - id: "10002", - name: "数学八列", - teacher: "足助太郎", - }, - }); - await prisma.course.upsert({ - where: { id: "10003" }, - update: {}, - create: { - id: "10003", - name: "英語八列", - teacher: "足助太郎", - }, - }); - await prisma.course.upsert({ - where: { id: "10004" }, - update: {}, - create: { - id: "10004", - name: "理科八列", - teacher: "足助太郎", - }, - }); - await prisma.course.upsert({ - where: { id: "10005" }, - update: {}, - create: { - id: "10005", - name: "社会八列", - teacher: "足助太郎", - }, - }); - - // slot - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10001", period: 4, day: "tue" }, - }, - update: {}, - create: { - courseId: "10001", - day: "tue", - period: 4, - }, - }); - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10001", period: 4, day: "thu" }, - }, - update: {}, - create: { - courseId: "10001", - day: "thu", - period: 4, - }, - }); - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10002", period: 3, day: "mon" }, - }, - update: {}, - create: { - courseId: "10002", - day: "mon", - period: 3, - }, - }); - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10003", period: 3, day: "mon" }, - }, - update: {}, - create: { - courseId: "10003", - day: "mon", - period: 3, - }, - }); - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10003", period: 3, day: "wed" }, - }, - update: {}, - create: { - courseId: "10003", - day: "wed", - period: 3, - }, - }); - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10004", period: 3, day: "wed" }, - }, - update: {}, - create: { - courseId: "10004", - day: "wed", - period: 3, - }, - }); - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10004", period: 3, day: "fri" }, - }, - update: {}, - create: { - courseId: "10004", - day: "fri", - period: 3, - }, - }); - - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10005", period: 2, day: "tue" }, - }, - update: {}, - create: { - courseId: "10005", - day: "tue", - period: 2, - }, - }); - - await prisma.slot.upsert({ - where: { - courseId_period_day: { courseId: "10005", period: 3, day: "tue" }, - }, - update: {}, - create: { - courseId: "10005", - day: "tue", - period: 3, - }, - }); - - // userId, courseId - const enrollments: Array<[number, string]> = [ - // assert: 101 and 102 has more overlaps in courses than 101 and 103, but less than 102 and 103 - // if you change the assertion above, fix test in engines/recommendation.test.ts too. - [101, "10001"], - [101, "10002"], - [101, "10003"], - [102, "10002"], - [102, "10003"], - [102, "10004"], - [102, "10005"], - [103, "10003"], - [103, "10004"], - [103, "10005"], - ]; - - const promises = enrollments.map(async ([user, course]) => { - await prisma.enrollment.upsert({ - where: { - userId_courseId: { userId: user, courseId: course }, - }, - update: {}, - create: { - userId: user, - courseId: course, - }, - }); - }); - await Promise.all(promises); +import { subjects } from "./data/subjects"; + +const promises: Array> = []; +for (const subjectGroup of subjects) { + const group = subjectGroup.group; + for (const name of subjectGroup.subjects) { + promises.push( + prisma.interestSubject.upsert({ + where: { + name_group: { name, group }, + }, + update: { name, group }, + create: { name, group }, + }), + ); + } } - -await main() - .then(async () => { - await prisma.$disconnect(); - }) - .catch(async (e) => { - console.error(e); - await prisma.$disconnect(); - process.exit(1); - }); diff --git a/server/src/seeds/test-data/data.ts b/server/src/seeds/test-data/data.ts new file mode 100644 index 00000000..c4a2714c --- /dev/null +++ b/server/src/seeds/test-data/data.ts @@ -0,0 +1,161 @@ +import type { Day } from "../../common/types"; + +export const subjects: Array<{ + group: string; + subjects: Array<[number, string]>; +}> = [ + { + group: "Computer Science", + subjects: [ + [1, "型システム"], + [2, "機械学習"], + [3, "CPU アーキテクチャ"], + [4, "分散処理"], + ] as const, + }, + { + group: "Math", + subjects: [[5, "Lean4"]], + }, +]; +export const interest = [ + { userId: 101, subjectId: 1 }, + { userId: 102, subjectId: 1 }, + { userId: 103, subjectId: 1 }, + { userId: 101, subjectId: 2 }, + { userId: 101, subjectId: 3 }, + { userId: 102, subjectId: 2 }, + { userId: 102, subjectId: 4 }, + { userId: 103, subjectId: 3 }, + { userId: 103, subjectId: 4 }, +]; + +export const users = [ + { + id: 101, + name: "田中太郎", + gender: "男", + grade: "D2", + faculty: "工学部", + department: "電気電子工学科", + intro: "田中太郎です。", + pictureUrl: + "https://firebasestorage.googleapis.com/v0/b/coursemate-tutorial.appspot.com/o/YdulS1s41LVh1nWgOBqzMiXN7803%2FtP5PrelZVe6v4UoF.jpg?alt=media&token=252da169-cccb-45b3-bec6-946ec3de3e27", + guid: "abc101", + }, + { + id: 102, + name: "山田花子", + gender: "女", + grade: "B2", + faculty: "経済学部", + department: "経営学科", + intro: "山田花子です。", + pictureUrl: + "https://firebasestorage.googleapis.com/v0/b/coursemate-tutorial.appspot.com/o/45QiYkH65OWHZYPruT9sHKAHa4I3%2FulavVaTxMNACkcn4.jpg?alt=media&token=6eea4c9f-c9ec-4c6e-943b-96b0afe013c3", + guid: "abc102", + }, + { + id: 103, + name: "小五郎", + gender: "男", + grade: "B3", + faculty: "経済学部", + department: "経営学科", + intro: "小五郎です。", + pictureUrl: + "https://firebasestorage.googleapis.com/v0/b/coursemate-tutorial.appspot.com/o/45QiYkH65OWHZYPruT9sHKAHa4I3%2FulavVaTxMNACkcn4.jpg?alt=media&token=6eea4c9f-c9ec-4c6e-943b-96b0afe013c3", + guid: "abc103", + }, +]; + +export const courses = [ + { + id: "10001", + name: "国語八列", + teacher: "足助太郎", + }, + { + id: "10002", + name: "数学八列", + teacher: "足助太郎", + }, + { + id: "10003", + name: "英語八列", + teacher: "足助太郎", + }, + { + id: "10004", + name: "理科八列", + teacher: "足助太郎", + }, + { + id: "10005", + name: "社会八列", + teacher: "足助太郎", + }, +]; + +export const slots: Array<{ courseId: string; day: Day; period: number }> = [ + { + courseId: "10001", + day: "tue", + period: 4, + }, + { + courseId: "10001", + day: "thu", + period: 4, + }, + { + courseId: "10002", + day: "mon", + period: 3, + }, + { + courseId: "10003", + day: "mon", + period: 3, + }, + { + courseId: "10003", + day: "wed", + period: 3, + }, + { + courseId: "10004", + day: "wed", + period: 3, + }, + { + courseId: "10004", + day: "fri", + period: 3, + }, + { + courseId: "10005", + day: "tue", + period: 2, + }, + { + courseId: "10005", + day: "tue", + period: 3, + }, +]; + +export const enrollments: Array<[number, string]> = [ + // assert: 101 and 102 has more overlaps in courses than 101 and 103, but less than 102 and 103 + // if you change the assertion above, fix test in engines/recommendation.test.ts too. + [101, "10001"], + [101, "10002"], + [101, "10003"], + [102, "10002"], + [102, "10003"], + [102, "10004"], + [102, "10005"], + [103, "10003"], + [103, "10004"], + [103, "10005"], +]; From 3c8d155da548b3d11b5e9e59beee12e822c51e75 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:23:27 +0900 Subject: [PATCH 014/131] =?UTF-8?q?=E4=BB=BB=E6=84=8F=E3=81=AE=E3=83=A1?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E3=82=A2=E3=83=89=E3=83=AC=E3=82=B9=E3=81=A7?= =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3=E3=82=92=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=20(#509)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [x] 改めてセルフレビューしたか - [x] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [x] わかりやすいPRになっているか --- README.md | 15 +++++++++++++++ web/.env.sample | 1 + web/src/routes/login.tsx | 14 ++++++++++---- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cfcff829..d34d4f9c 100644 --- a/README.md +++ b/README.md @@ -59,3 +59,18 @@ make docker # または make docker-watch ``` + +## Deploy + +web: +```sh +VITE_ALLOW_ANY_MAIL_ADDR=true # optional +make prepare-deploy-web` +# serve ./web/dist +``` + +server: +```sh +make prepare-deploy-server +make deploy-server +``` diff --git a/web/.env.sample b/web/.env.sample index 05df56ba..87ad24dd 100644 --- a/web/.env.sample +++ b/web/.env.sample @@ -6,6 +6,7 @@ VITE_FIREBASE_PROJECT_ID=example # VITE_FIREBASE_MESSAGING_SENDER_ID=example VITE_FIREBASE_APP_ID=example VITE_FIREBASE_MEASUREMENT_ID=example +VITE_ALLOW_ANY_MAIL_ADDR=true #supabase VITE_SUPABASE_URL= diff --git a/web/src/routes/login.tsx b/web/src/routes/login.tsx index 8a3eec2e..23843c2a 100644 --- a/web/src/routes/login.tsx +++ b/web/src/routes/login.tsx @@ -13,12 +13,15 @@ import { CourseMateIcon } from "../components/common/CourseMateIcon"; import FullScreenCircularProgress from "../components/common/FullScreenCircularProgress"; const provider = new GoogleAuthProvider(); +const ALLOW_ANY_MAIL_ADDR = import.meta.env.VITE_ALLOW_ANY_MAIL_ADDR === "true"; async function signInWithGoogle() { try { - provider.setCustomParameters({ - hd: "g.ecc.u-tokyo.ac.jp", - }); + if (!ALLOW_ANY_MAIL_ADDR) { + provider.setCustomParameters({ + hd: "g.ecc.u-tokyo.ac.jp", + }); + } const result = await signInWithPopup(auth, provider); const credential = GoogleAuthProvider.credentialFromResult(result); @@ -29,7 +32,10 @@ async function signInWithGoogle() { const user = result.user; const email = user.email; - if (!email || !email.endsWith("@g.ecc.u-tokyo.ac.jp")) { + if ( + !ALLOW_ANY_MAIL_ADDR && + (!email || !email.endsWith("@g.ecc.u-tokyo.ac.jp")) + ) { throw new Error( "Unauthorized domain. Access is restricted to g.ecc.u-tokyo.ac.jp domain.", ); From 5737ddd93e238eef81dedfaee3da248a872cdbb1 Mon Sep 17 00:00:00 2001 From: Shogo NAKAMURA <104970808+naka-12@users.noreply.github.com> Date: Tue, 5 Nov 2024 21:53:45 +0900 Subject: [PATCH 015/131] =?UTF-8?q?Vite=20+=20React=20Router=20=E2=86=92?= =?UTF-8?q?=20Next.js=20(#512)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vite, React Router から Next.js に移行 (Express はそのまま) --- .cspell.json | 3 +- .github/workflows/ci.yml | 8 + Makefile | 4 +- README.md | 2 +- biome.json | 11 +- bun.lockb | Bin 71909 -> 71964 bytes package-lock.json | 1671 ++++++++++++++++ package.json | 3 +- server/package-lock.json | 1111 +---------- web/.env.sample | 22 +- web/.gitignore | 6 + web/Dockerfile | 2 +- web/{src => }/api/chat/chat.ts | 2 +- web/{src => }/api/chat/hooks.ts | 16 +- web/api/course.hook.ts | 10 + web/{src => }/api/course.ts | 2 +- web/{src => }/api/image.ts | 0 web/{src => }/api/internal/endpoints.ts | 8 +- web/{src => }/api/internal/fetch-func.ts | 2 +- web/{src => }/api/match.ts | 0 web/{src => }/api/request.ts | 0 web/{src => }/api/share/types.ts | 0 web/{src => }/api/user.ts | 22 +- web/app/chat/layout.tsx | 31 + web/app/chat/page.tsx | 41 + web/app/edit/courses/page.tsx | 78 + web/app/edit/layout.tsx | 29 + web/app/edit/profile/page.tsx | 552 ++++++ web/app/error.tsx | 11 + web/{src/routes/faq.tsx => app/faq/page.tsx} | 10 +- web/app/favicon.ico | Bin 0 -> 15086 bytes web/app/friends/layout.tsx | 31 + .../tabs/friends.tsx => app/friends/page.tsx} | 9 +- web/app/home/layout.tsx | 31 + .../tabs/home.tsx => app/home/page.tsx} | 118 +- web/{src => app}/index.css | 0 web/app/layout.tsx | 63 + .../routes/login.tsx => app/login/page.tsx} | 38 +- .../styles/login.css => app/login/style.css} | 0 web/app/not-found.tsx | 10 + web/app/page.tsx | 5 + web/app/settings/aboutUs/page.tsx | 126 ++ web/app/settings/contact/page.tsx | 45 + .../settings/delete/page.tsx} | 14 +- web/app/settings/disclaimer/page.tsx | 59 + web/app/settings/layout.tsx | 31 + web/app/settings/page.tsx | 58 + web/app/settings/profile/page.tsx | 72 + .../registration => app/signup}/common.tsx | 0 .../registration => app/signup}/data.ts | 0 .../registration => app/signup}/functions.ts | 8 +- .../index.tsx => app/signup/page.tsx} | 11 +- .../signup}/steps/step1_profile.tsx | 8 +- .../signup}/steps/step2_img.tsx | 2 +- .../signup}/steps/step3_confirmation.tsx | 4 +- .../signup}/steps/step4_course.tsx | 5 +- .../tutorial.tsx => app/tutorial/page.tsx} | 26 +- web/bun.lockb | Bin 119070 -> 124783 bytes web/{src => }/components/BanLandscape.tsx | 2 + web/components/BottomBar.tsx | 84 + web/{src => }/components/Card.tsx | 0 web/{src => }/components/DraggableCard.tsx | 2 +- web/{src => }/components/Header.tsx | 11 +- web/{src => }/components/ImageCropper.tsx | 0 web/{src => }/components/ImageFallback.tsx | 4 +- web/{src => }/components/LogOutButton.tsx | 10 +- web/{src => }/components/about.tsx | 0 .../components/chat/MessageInput.tsx | 0 web/{src => }/components/chat/Room.tsx | 2 +- web/{src => }/components/chat/RoomHeader.tsx | 8 +- web/{src => }/components/chat/RoomList.tsx | 19 +- web/{src => }/components/chat/RoomWindow.tsx | 17 +- .../components/common/CourseMateIcon.tsx | 0 web/{src => }/components/common/Dots.tsx | 0 .../common/FullScreenCircularProgress.tsx | 0 .../components/common/NavigateByAuthState.tsx | 17 +- web/{src => }/components/common/Popup.tsx | 0 .../common}/TopNavigation.tsx | 9 +- .../components/common/alert/AlertProvider.tsx | 2 + .../components/common/alert/alertContext.ts | 0 .../components/common/modal/ModalProvider.tsx | 0 .../components/config/PhotoModal.tsx | 0 .../components/config/PhotoPreview.tsx | 0 .../course/EditableCoursesTable/index.tsx | 0 .../course/NonEditableCoursesTable/index.tsx | 0 .../components/CourseDeleteConfirmDialog.tsx | 0 .../CourseRegisterConfirmDialog.tsx | 0 .../components/CoursesTableCore/index.tsx | 0 .../course/components/CoursesTableCore/lib.ts | 0 .../CoursesTableCore/styles.module.css | 0 .../course/components/SelectCourseDialog.tsx | 0 .../components/data/photo-preview.tsx | 0 web/{src => }/components/data/socket.ts | 6 +- .../components/human/WithFallback.tsx | 0 web/{src => }/components/human/avatar.tsx | 0 .../components/human/humanListItem.tsx | 0 web/{src => }/components/match/matching.tsx | 0 web/{src => }/components/match/myRequests.tsx | 0 .../components/match/othersRequests.tsx | 0 web/{src => }/components/match/requests.tsx | 0 web/{src => }/firebase/auth/AuthProvider.tsx | 2 +- web/{src => }/firebase/auth/lib.ts | 2 +- web/firebase/config.ts | 17 + web/{src => }/hooks/useCurrentUser.ts | 0 .../useSWR.ts => hooks/useCustomizedSWR.ts} | 18 +- web/{src => }/hooks/useData.ts | 2 +- web/index.html | 13 - web/next.config.mjs | 6 + web/package-lock.json | 1723 +++++++++++------ web/package.json | 14 +- web/src/.gitignore | 1 - web/src/App.tsx | 196 -- web/src/api/course.hook.ts | 10 - web/src/firebase/config.ts | 17 - web/src/main.tsx | 29 - web/src/routes/editCourses.tsx | 81 - web/src/routes/editProfile.tsx | 547 ------ web/src/routes/root.tsx | 133 -- web/src/routes/tabs/chat.tsx | 18 - web/src/routes/tabs/settings/aboutUs.tsx | 123 -- web/src/routes/tabs/settings/contact.tsx | 42 - web/src/routes/tabs/settings/disclaimer.tsx | 56 - web/src/routes/tabs/settings/profile.tsx | 72 - web/src/routes/tabs/settings/settings.tsx | 58 - web/src/vite-env.d.ts | 1 - web/tsconfig.json | 29 +- web/tsconfig.node.json | 11 - web/vite.config.ts | 7 - 128 files changed, 4668 insertions(+), 3184 deletions(-) create mode 100644 package-lock.json rename web/{src => }/api/chat/chat.ts (99%) rename web/{src => }/api/chat/hooks.ts (62%) create mode 100644 web/api/course.hook.ts rename web/{src => }/api/course.ts (97%) rename web/{src => }/api/image.ts (100%) rename web/{src => }/api/internal/endpoints.ts (96%) rename web/{src => }/api/internal/fetch-func.ts (93%) rename web/{src => }/api/match.ts (100%) rename web/{src => }/api/request.ts (100%) rename web/{src => }/api/share/types.ts (100%) rename web/{src => }/api/user.ts (84%) create mode 100644 web/app/chat/layout.tsx create mode 100644 web/app/chat/page.tsx create mode 100644 web/app/edit/courses/page.tsx create mode 100644 web/app/edit/layout.tsx create mode 100644 web/app/edit/profile/page.tsx create mode 100644 web/app/error.tsx rename web/{src/routes/faq.tsx => app/faq/page.tsx} (93%) create mode 100644 web/app/favicon.ico create mode 100644 web/app/friends/layout.tsx rename web/{src/routes/tabs/friends.tsx => app/friends/page.tsx} (87%) create mode 100644 web/app/home/layout.tsx rename web/{src/routes/tabs/home.tsx => app/home/page.tsx} (61%) rename web/{src => app}/index.css (100%) create mode 100644 web/app/layout.tsx rename web/{src/routes/login.tsx => app/login/page.tsx} (88%) rename web/{src/styles/login.css => app/login/style.css} (100%) create mode 100644 web/app/not-found.tsx create mode 100644 web/app/page.tsx create mode 100644 web/app/settings/aboutUs/page.tsx create mode 100644 web/app/settings/contact/page.tsx rename web/{src/routes/tabs/settings/deleteAccount.tsx => app/settings/delete/page.tsx} (90%) create mode 100644 web/app/settings/disclaimer/page.tsx create mode 100644 web/app/settings/layout.tsx create mode 100644 web/app/settings/page.tsx create mode 100644 web/app/settings/profile/page.tsx rename web/{src/routes/registration => app/signup}/common.tsx (100%) rename web/{src/routes/registration => app/signup}/data.ts (100%) rename web/{src/routes/registration => app/signup}/functions.ts (85%) rename web/{src/routes/registration/index.tsx => app/signup/page.tsx} (90%) rename web/{src/routes/registration => app/signup}/steps/step1_profile.tsx (95%) rename web/{src/routes/registration => app/signup}/steps/step2_img.tsx (97%) rename web/{src/routes/registration => app/signup}/steps/step3_confirmation.tsx (95%) rename web/{src/routes/registration => app/signup}/steps/step4_course.tsx (89%) rename web/{src/routes/tutorial.tsx => app/tutorial/page.tsx} (83%) rename web/{src => }/components/BanLandscape.tsx (99%) create mode 100644 web/components/BottomBar.tsx rename web/{src => }/components/Card.tsx (100%) rename web/{src => }/components/DraggableCard.tsx (98%) rename web/{src => }/components/Header.tsx (78%) rename web/{src => }/components/ImageCropper.tsx (100%) rename web/{src => }/components/ImageFallback.tsx (92%) rename web/{src => }/components/LogOutButton.tsx (87%) rename web/{src => }/components/about.tsx (100%) rename web/{src => }/components/chat/MessageInput.tsx (100%) rename web/{src => }/components/chat/Room.tsx (93%) rename web/{src => }/components/chat/RoomHeader.tsx (85%) rename web/{src => }/components/chat/RoomList.tsx (75%) rename web/{src => }/components/chat/RoomWindow.tsx (94%) rename web/{src => }/components/common/CourseMateIcon.tsx (100%) rename web/{src => }/components/common/Dots.tsx (100%) rename web/{src => }/components/common/FullScreenCircularProgress.tsx (100%) rename web/{src => }/components/common/NavigateByAuthState.tsx (82%) rename web/{src => }/components/common/Popup.tsx (100%) rename web/{src/routes/tabs/settings/components => components/common}/TopNavigation.tsx (86%) rename web/{src => }/components/common/alert/AlertProvider.tsx (99%) rename web/{src => }/components/common/alert/alertContext.ts (100%) rename web/{src => }/components/common/modal/ModalProvider.tsx (100%) rename web/{src => }/components/config/PhotoModal.tsx (100%) rename web/{src => }/components/config/PhotoPreview.tsx (100%) rename web/{src => }/components/course/EditableCoursesTable/index.tsx (100%) rename web/{src => }/components/course/NonEditableCoursesTable/index.tsx (100%) rename web/{src => }/components/course/components/CourseDeleteConfirmDialog.tsx (100%) rename web/{src => }/components/course/components/CourseRegisterConfirmDialog.tsx (100%) rename web/{src => }/components/course/components/CoursesTableCore/index.tsx (100%) rename web/{src => }/components/course/components/CoursesTableCore/lib.ts (100%) rename web/{src => }/components/course/components/CoursesTableCore/styles.module.css (100%) rename web/{src => }/components/course/components/SelectCourseDialog.tsx (100%) rename web/{src => }/components/data/photo-preview.tsx (100%) rename web/{src => }/components/data/socket.ts (51%) rename web/{src => }/components/human/WithFallback.tsx (100%) rename web/{src => }/components/human/avatar.tsx (100%) rename web/{src => }/components/human/humanListItem.tsx (100%) rename web/{src => }/components/match/matching.tsx (100%) rename web/{src => }/components/match/myRequests.tsx (100%) rename web/{src => }/components/match/othersRequests.tsx (100%) rename web/{src => }/components/match/requests.tsx (100%) rename web/{src => }/firebase/auth/AuthProvider.tsx (94%) rename web/{src => }/firebase/auth/lib.ts (96%) create mode 100644 web/firebase/config.ts rename web/{src => }/hooks/useCurrentUser.ts (100%) rename web/{src/hooks/useSWR.ts => hooks/useCustomizedSWR.ts} (78%) rename web/{src => }/hooks/useData.ts (96%) delete mode 100644 web/index.html create mode 100644 web/next.config.mjs delete mode 100644 web/src/.gitignore delete mode 100644 web/src/App.tsx delete mode 100644 web/src/api/course.hook.ts delete mode 100644 web/src/firebase/config.ts delete mode 100644 web/src/main.tsx delete mode 100644 web/src/routes/editCourses.tsx delete mode 100644 web/src/routes/editProfile.tsx delete mode 100644 web/src/routes/root.tsx delete mode 100644 web/src/routes/tabs/chat.tsx delete mode 100644 web/src/routes/tabs/settings/aboutUs.tsx delete mode 100644 web/src/routes/tabs/settings/contact.tsx delete mode 100644 web/src/routes/tabs/settings/disclaimer.tsx delete mode 100644 web/src/routes/tabs/settings/profile.tsx delete mode 100644 web/src/routes/tabs/settings/settings.tsx delete mode 100644 web/src/vite-env.d.ts delete mode 100644 web/tsconfig.node.json delete mode 100644 web/vite.config.ts diff --git a/.cspell.json b/.cspell.json index 1f059cd5..22705a27 100644 --- a/.cspell.json +++ b/.cspell.json @@ -45,6 +45,7 @@ "**/data.json", "**/Cargo.*", "scraper/target", - "**/rust-toolchain.toml" + "**/rust-toolchain.toml", + "web/out" ] } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 102c2e26..f40718eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,14 @@ on: env: DATABASE_URL: ${{ secrets.DATABASE_URL_FOR_PRISMA_SQL_GENERATION }} + NEXT_PUBLIC_API_ENDPOINT: "sample" + NEXT_PUBLIC_FIREBASE_API_KEY: "sample" + NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: "sample" + NEXT_PUBLIC_FIREBASE_PROJECT_ID: "sample" + NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: "sample" + NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: "sample" + NEXT_PUBLIC_FIREBASE_APP_ID: "sample" + NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: "sample" jobs: build: name: Build diff --git a/Makefile b/Makefile index 72b7c243..d70acbf8 100644 --- a/Makefile +++ b/Makefile @@ -166,7 +166,7 @@ copy-common-to-server: @ if [ -d server/src/common ]; then rm -r server/src/common; fi @ cp -r common server/src/common copy-common-to-web: - @ if [ -d web/src/common ]; then rm -r web/src/common; fi - @ cp -r common web/src/common + @ if [ -d web/common ]; then rm -r web/common; fi + @ cp -r common web/common .PHONY: test diff --git a/README.md b/README.md index d34d4f9c..f9d8a2e7 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ make docker-watch web: ```sh -VITE_ALLOW_ANY_MAIL_ADDR=true # optional +NEXT_PUBLIC_ALLOW_ANY_MAIL_ADDR=true # optional make prepare-deploy-web` # serve ./web/dist ``` diff --git a/biome.json b/biome.json index b2ca8999..6381c36c 100644 --- a/biome.json +++ b/biome.json @@ -10,6 +10,15 @@ "useIgnoreFile": true }, "files": { - "ignore": ["bun.lockb", "server/target", "data.json", "scraper/target"] + "ignore": [ + "bun.lockb", + "server/target", + "data.json", + "scraper/target", + ".next", + "next-env.d.ts", + "out", + "package-lock.json" + ] } } diff --git a/bun.lockb b/bun.lockb index 6a861deffa7dc8296ef865b1e3189000a02a5421..176fd1dd3aad6e186658b20348807dba8f939cc3 100755 GIT binary patch delta 50 zcmaF5k!8*%mI;20J&pcb{TbOLgiC@-gG*dpQaU6U89<;%f^o9`0R@&tb8=Qpp4d47 E0ITT`$N&HU delta 21 dcmbQUiRI}=mI;20p^g4q{TbOLCeQ4g003XV2w4CC diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..f8dd50fc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1671 @@ +{ + "name": "course-mate", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "course-mate", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@types/bun": "^1.1.10", + "cspell": "^8.14.4", + "zod": "^3.23.8" + }, + "devDependencies": { + "@biomejs/biome": "^1.9.1", + "husky": "^9.1.4", + "lint-staged": "^15.2.10", + "typescript": "^5.6.2" + } + }, + "node_modules/@biomejs/biome": { + "version": "1.9.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.1", + "@biomejs/cli-darwin-x64": "1.9.1", + "@biomejs/cli-linux-arm64": "1.9.1", + "@biomejs/cli-linux-arm64-musl": "1.9.1", + "@biomejs/cli-linux-x64": "1.9.1", + "@biomejs/cli-linux-x64-musl": "1.9.1", + "@biomejs/cli-win32-arm64": "1.9.1", + "@biomejs/cli-win32-x64": "1.9.1" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.9.1", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@cspell/cspell-bundled-dicts": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/dict-ada": "^4.0.2", + "@cspell/dict-aws": "^4.0.4", + "@cspell/dict-bash": "^4.1.4", + "@cspell/dict-companies": "^3.1.4", + "@cspell/dict-cpp": "^5.1.16", + "@cspell/dict-cryptocurrencies": "^5.0.0", + "@cspell/dict-csharp": "^4.0.2", + "@cspell/dict-css": "^4.0.13", + "@cspell/dict-dart": "^2.2.1", + "@cspell/dict-django": "^4.1.0", + "@cspell/dict-docker": "^1.1.7", + "@cspell/dict-dotnet": "^5.0.5", + "@cspell/dict-elixir": "^4.0.3", + "@cspell/dict-en_us": "^4.3.23", + "@cspell/dict-en-common-misspellings": "^2.0.4", + "@cspell/dict-en-gb": "1.1.33", + "@cspell/dict-filetypes": "^3.0.4", + "@cspell/dict-flutter": "^1.0.0", + "@cspell/dict-fonts": "^4.0.0", + "@cspell/dict-fsharp": "^1.0.1", + "@cspell/dict-fullstack": "^3.2.0", + "@cspell/dict-gaming-terms": "^1.0.5", + "@cspell/dict-git": "^3.0.0", + "@cspell/dict-golang": "^6.0.12", + "@cspell/dict-google": "^1.0.1", + "@cspell/dict-haskell": "^4.0.1", + "@cspell/dict-html": "^4.0.5", + "@cspell/dict-html-symbol-entities": "^4.0.0", + "@cspell/dict-java": "^5.0.7", + "@cspell/dict-julia": "^1.0.1", + "@cspell/dict-k8s": "^1.0.6", + "@cspell/dict-latex": "^4.0.0", + "@cspell/dict-lorem-ipsum": "^4.0.0", + "@cspell/dict-lua": "^4.0.3", + "@cspell/dict-makefile": "^1.0.0", + "@cspell/dict-monkeyc": "^1.0.6", + "@cspell/dict-node": "^5.0.1", + "@cspell/dict-npm": "^5.1.4", + "@cspell/dict-php": "^4.0.10", + "@cspell/dict-powershell": "^5.0.8", + "@cspell/dict-public-licenses": "^2.0.8", + "@cspell/dict-python": "^4.2.6", + "@cspell/dict-r": "^2.0.1", + "@cspell/dict-ruby": "^5.0.3", + "@cspell/dict-rust": "^4.0.5", + "@cspell/dict-scala": "^5.0.3", + "@cspell/dict-software-terms": "^4.1.3", + "@cspell/dict-sql": "^2.1.5", + "@cspell/dict-svelte": "^1.0.2", + "@cspell/dict-swift": "^2.0.1", + "@cspell/dict-terraform": "^1.0.1", + "@cspell/dict-typescript": "^3.1.6", + "@cspell/dict-vue": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-json-reporter": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/cspell-types": "8.14.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-pipe": { + "version": "8.14.4", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-resolver": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-service-bus": { + "version": "8.14.4", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-types": { + "version": "8.14.4", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/dict-ada": { + "version": "4.0.2", + "license": "MIT" + }, + "node_modules/@cspell/dict-aws": { + "version": "4.0.4", + "license": "MIT" + }, + "node_modules/@cspell/dict-bash": { + "version": "4.1.4", + "license": "MIT" + }, + "node_modules/@cspell/dict-companies": { + "version": "3.1.4", + "license": "MIT" + }, + "node_modules/@cspell/dict-cpp": { + "version": "5.1.16", + "license": "MIT" + }, + "node_modules/@cspell/dict-cryptocurrencies": { + "version": "5.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-csharp": { + "version": "4.0.2", + "license": "MIT" + }, + "node_modules/@cspell/dict-css": { + "version": "4.0.13", + "license": "MIT" + }, + "node_modules/@cspell/dict-dart": { + "version": "2.2.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-data-science": { + "version": "2.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-django": { + "version": "4.1.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-docker": { + "version": "1.1.7", + "license": "MIT" + }, + "node_modules/@cspell/dict-dotnet": { + "version": "5.0.5", + "license": "MIT" + }, + "node_modules/@cspell/dict-elixir": { + "version": "4.0.3", + "license": "MIT" + }, + "node_modules/@cspell/dict-en_us": { + "version": "4.3.23", + "license": "MIT" + }, + "node_modules/@cspell/dict-en-common-misspellings": { + "version": "2.0.4", + "license": "CC BY-SA 4.0" + }, + "node_modules/@cspell/dict-en-gb": { + "version": "1.1.33", + "license": "MIT" + }, + "node_modules/@cspell/dict-filetypes": { + "version": "3.0.4", + "license": "MIT" + }, + "node_modules/@cspell/dict-flutter": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-fonts": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-fsharp": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-fullstack": { + "version": "3.2.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-gaming-terms": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/@cspell/dict-git": { + "version": "3.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-golang": { + "version": "6.0.12", + "license": "MIT" + }, + "node_modules/@cspell/dict-google": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-haskell": { + "version": "4.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-html": { + "version": "4.0.6", + "license": "MIT" + }, + "node_modules/@cspell/dict-html-symbol-entities": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-java": { + "version": "5.0.7", + "license": "MIT" + }, + "node_modules/@cspell/dict-julia": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-k8s": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/@cspell/dict-latex": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-lorem-ipsum": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-lua": { + "version": "4.0.3", + "license": "MIT" + }, + "node_modules/@cspell/dict-makefile": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dict-monkeyc": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/@cspell/dict-node": { + "version": "5.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-npm": { + "version": "5.1.5", + "license": "MIT" + }, + "node_modules/@cspell/dict-php": { + "version": "4.0.10", + "license": "MIT" + }, + "node_modules/@cspell/dict-powershell": { + "version": "5.0.9", + "license": "MIT" + }, + "node_modules/@cspell/dict-public-licenses": { + "version": "2.0.8", + "license": "MIT" + }, + "node_modules/@cspell/dict-python": { + "version": "4.2.6", + "license": "MIT", + "dependencies": { + "@cspell/dict-data-science": "^2.0.1" + } + }, + "node_modules/@cspell/dict-r": { + "version": "2.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-ruby": { + "version": "5.0.3", + "license": "MIT" + }, + "node_modules/@cspell/dict-rust": { + "version": "4.0.5", + "license": "MIT" + }, + "node_modules/@cspell/dict-scala": { + "version": "5.0.3", + "license": "MIT" + }, + "node_modules/@cspell/dict-software-terms": { + "version": "4.1.4", + "license": "MIT" + }, + "node_modules/@cspell/dict-sql": { + "version": "2.1.5", + "license": "MIT" + }, + "node_modules/@cspell/dict-svelte": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/@cspell/dict-swift": { + "version": "2.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-terraform": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/@cspell/dict-typescript": { + "version": "3.1.6", + "license": "MIT" + }, + "node_modules/@cspell/dict-vue": { + "version": "3.0.0", + "license": "MIT" + }, + "node_modules/@cspell/dynamic-import": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "import-meta-resolve": "^4.1.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@cspell/filetypes": { + "version": "8.14.4", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/strong-weak-map": { + "version": "8.14.4", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/url": { + "version": "8.14.4", + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/bun": { + "version": "1.1.10", + "license": "MIT", + "dependencies": { + "bun-types": "1.1.29" + } + }, + "node_modules/@types/node": { + "version": "20.12.14", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bun-types": { + "version": "1.1.29", + "license": "MIT", + "dependencies": { + "@types/node": "~20.12.8", + "@types/ws": "~8.5.10" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "chalk": "^5.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/clear-module": { + "version": "4.1.2", + "license": "MIT", + "dependencies": { + "parent-module": "^2.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/comment-json": { + "version": "4.2.5", + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1", + "has-own-prop": "^2.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cspell": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/cspell-json-reporter": "8.14.4", + "@cspell/cspell-pipe": "8.14.4", + "@cspell/cspell-types": "8.14.4", + "@cspell/dynamic-import": "8.14.4", + "@cspell/url": "8.14.4", + "chalk": "^5.3.0", + "chalk-template": "^1.1.0", + "commander": "^12.1.0", + "cspell-dictionary": "8.14.4", + "cspell-gitignore": "8.14.4", + "cspell-glob": "8.14.4", + "cspell-io": "8.14.4", + "cspell-lib": "8.14.4", + "fast-glob": "^3.3.2", + "fast-json-stable-stringify": "^2.1.0", + "file-entry-cache": "^9.1.0", + "get-stdin": "^9.0.0", + "semver": "^7.6.3", + "strip-ansi": "^7.1.0" + }, + "bin": { + "cspell": "bin.mjs", + "cspell-esm": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" + } + }, + "node_modules/cspell-config-lib": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/cspell-types": "8.14.4", + "comment-json": "^4.2.5", + "yaml": "^2.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-dictionary": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "8.14.4", + "@cspell/cspell-types": "8.14.4", + "cspell-trie-lib": "8.14.4", + "fast-equals": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-gitignore": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/url": "8.14.4", + "cspell-glob": "8.14.4", + "cspell-io": "8.14.4", + "find-up-simple": "^1.0.0" + }, + "bin": { + "cspell-gitignore": "bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-glob": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/url": "8.14.4", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-grammar": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "8.14.4", + "@cspell/cspell-types": "8.14.4" + }, + "bin": { + "cspell-grammar": "bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-io": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/cspell-service-bus": "8.14.4", + "@cspell/url": "8.14.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-lib": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/cspell-bundled-dicts": "8.14.4", + "@cspell/cspell-pipe": "8.14.4", + "@cspell/cspell-resolver": "8.14.4", + "@cspell/cspell-types": "8.14.4", + "@cspell/dynamic-import": "8.14.4", + "@cspell/filetypes": "8.14.4", + "@cspell/strong-weak-map": "8.14.4", + "@cspell/url": "8.14.4", + "clear-module": "^4.1.2", + "comment-json": "^4.2.5", + "cspell-config-lib": "8.14.4", + "cspell-dictionary": "8.14.4", + "cspell-glob": "8.14.4", + "cspell-grammar": "8.14.4", + "cspell-io": "8.14.4", + "cspell-trie-lib": "8.14.4", + "env-paths": "^3.0.0", + "fast-equals": "^5.0.1", + "gensequence": "^7.0.0", + "import-fresh": "^3.3.0", + "resolve-from": "^5.0.0", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-uri": "^3.0.8", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-trie-lib": { + "version": "8.14.4", + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "8.14.4", + "@cspell/cspell-types": "8.14.4", + "gensequence": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "9.1.0", + "license": "MIT", + "dependencies": { + "flat-cache": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "license": "ISC" + }, + "node_modules/gensequence": { + "version": "7.0.0", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stdin": { + "version": "9.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-own-prop": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/husky": { + "version": "9.1.4", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/parent-module": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ini": { + "version": "4.1.1", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lilconfig": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lint-staged": { + "version": "15.2.10", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.6", + "execa": "~8.0.1", + "lilconfig": "~3.1.2", + "listr2": "~8.2.4", + "micromatch": "~4.0.8", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.5.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "8.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "callsites": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.6.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "license": "MIT" + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yaml": { + "version": "2.5.1", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json index d12a6c1e..d8731d54 100644 --- a/package.json +++ b/package.json @@ -23,5 +23,6 @@ }, "lint-staged": { "*.{js,jsx,ts,tsx,json}": ["biome check --write"] - } + }, + "trustedDependencies": ["@biomejs/biome"] } diff --git a/server/package-lock.json b/server/package-lock.json index 704db8b5..049ba3dd 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -10,437 +10,23 @@ "license": "ISC", "dependencies": { "@prisma/client": "^5.20.0", - "@types/cors": "^2.8.17", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "dotenv": "^16.4.5", - "express": "^4.21.0", - "firebase-admin": "^12.5.0", + "dotenv-cli": "^7.4.2", + "express": "^4.18.2", + "firebase-admin": "^12.2.0", "sharp": "^0.33.5", - "socket.io": "^4.8.0", + "socket.io": "^4.7.5", "zod": "^3.23.8" }, "devDependencies": { "@types/cookie-parser": "^1.4.7", + "@types/cors": "^2.8.17", "@types/express": "^4.17.21", - "@types/react": "^18.3.10", - "@types/react-dom": "^18.3.0", - "@vitejs/plugin-react": "^4.3.1", - "globals": "^15.9.0", - "prisma": "^5.20.0", - "typescript": "^5.6.2", - "vite": "^5.4.8" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/picocolors": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/@babel/compat-data": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.8", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.8", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.8", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.5", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/core/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.8", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/picocolors": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/@babel/parser": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.8", - "@babel/types": "^7.24.8", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.5", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.24.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "globals": "^15.8.0", + "prisma": "^5.11.0", + "typescript": "^5.4.5" } }, "node_modules/@fastify/busboy": { @@ -652,49 +238,6 @@ "@img/sharp-libvips-linuxmusl-x64": "1.0.4" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@js-sdsl/ordered-map": { "version": "4.4.2", "license": "MIT", @@ -814,30 +357,6 @@ "license": "BSD-3-Clause", "optional": true }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.22.4", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.22.4", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "license": "MIT" @@ -850,43 +369,6 @@ "node": ">= 10" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, "node_modules/@types/body-parser": { "version": "1.19.5", "license": "MIT", @@ -926,11 +408,6 @@ "@types/node": "*" } }, - "node_modules/@types/estree": { - "version": "1.0.5", - "dev": true, - "license": "MIT" - }, "node_modules/@types/express": { "version": "4.17.21", "license": "MIT", @@ -978,11 +455,6 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "dev": true, - "license": "MIT" - }, "node_modules/@types/qs": { "version": "6.9.15", "license": "MIT" @@ -991,32 +463,6 @@ "version": "1.2.7", "license": "MIT" }, - "node_modules/@types/react": { - "version": "18.3.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-dom/node_modules/@types/react": { - "version": "18.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, "node_modules/@types/request": { "version": "2.48.12", "license": "MIT", @@ -1050,24 +496,6 @@ "license": "MIT", "optional": true }, - "node_modules/@vitejs/plugin-react": { - "version": "4.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.5", - "@babel/plugin-transform-react-jsx-self": "^7.24.5", - "@babel/plugin-transform-react-jsx-source": "^7.24.1", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0" - } - }, "node_modules/abort-controller": { "version": "3.0.0", "license": "MIT", @@ -1130,30 +558,6 @@ "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-styles/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/ansi-styles/node_modules/color-convert/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, "node_modules/array-flatten": { "version": "1.1.1", "license": "MIT" @@ -1235,37 +639,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/browserslist": { - "version": "4.23.2", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "license": "BSD-3-Clause" @@ -1282,48 +655,16 @@ "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001641", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/cliui": { @@ -1400,11 +741,6 @@ "node": ">= 0.6" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/cookie": { "version": "0.4.1", "license": "MIT", @@ -1438,10 +774,18 @@ "node": ">= 0.10" } }, - "node_modules/csstype": { - "version": "3.1.3", - "dev": true, - "license": "MIT" + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } }, "node_modules/debug": { "version": "2.6.9", @@ -1505,6 +849,28 @@ "url": "https://dotenvx.com" } }, + "node_modules/dotenv-cli": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.4.2.tgz", + "integrity": "sha512-SbUj8l61zIbzyhIbg0FwPJq6+wjbzdn9oEtozQpZ6kW2ihCcapKVZj49oCT3oPM+mgQm+itgvUQcG5szxVrZTA==", + "dependencies": { + "cross-spawn": "^7.0.3", + "dotenv": "^16.3.0", + "dotenv-expand": "^10.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "dotenv": "cli.js" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "engines": { + "node": ">=12" + } + }, "node_modules/duplexify": { "version": "4.1.3", "license": "MIT", @@ -1527,11 +893,6 @@ "version": "1.1.1", "license": "MIT" }, - "node_modules/electron-to-chromium": { - "version": "1.4.827", - "dev": true, - "license": "ISC" - }, "node_modules/emoji-regex": { "version": "8.0.0", "license": "MIT", @@ -1632,47 +993,10 @@ "node": ">= 0.4" } }, - "node_modules/esbuild": { - "version": "0.21.5", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "node_modules/escalade": { "version": "3.1.2", - "devOptional": true, "license": "MIT", + "optional": true, "engines": { "node": ">=6" } @@ -1681,14 +1005,6 @@ "version": "1.0.3", "license": "MIT" }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/etag": { "version": "1.8.1", "license": "MIT", @@ -1954,14 +1270,6 @@ "node": ">=14" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "license": "ISC", @@ -2120,14 +1428,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "license": "MIT", @@ -2323,6 +1623,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, "node_modules/jose": { "version": "4.15.9", "license": "MIT", @@ -2330,22 +1635,6 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/json-bigint": { "version": "1.0.0", "license": "MIT", @@ -2354,17 +1643,6 @@ "bignumber.js": "^9.0.0" } }, - "node_modules/json5": { - "version": "2.2.3", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/jsonwebtoken": { "version": "9.0.2", "license": "MIT", @@ -2495,14 +1773,6 @@ "license": "Apache-2.0", "optional": true }, - "node_modules/lru-cache": { - "version": "5.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/lru-memoizer": { "version": "2.3.0", "license": "MIT", @@ -2573,27 +1843,18 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { "version": "2.0.0", "license": "MIT" }, - "node_modules/nanoid": { - "version": "3.3.7", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/negotiator": { "version": "0.6.3", "license": "MIT", @@ -2627,11 +1888,6 @@ "node": ">= 6.13.0" } }, - "node_modules/node-releases": { - "version": "2.0.14", - "dev": true, - "license": "MIT" - }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -2696,42 +1952,18 @@ "node": ">= 0.8" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "0.1.10", "license": "MIT" }, - "node_modules/picocolors": { - "version": "1.1.0", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.4.47", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/prisma": { "version": "5.20.0", "devOptional": true, @@ -2828,14 +2060,6 @@ "node": ">= 0.8" } }, - "node_modules/react-refresh": { - "version": "0.14.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "license": "MIT", @@ -2878,40 +2102,6 @@ "node": ">=14" } }, - "node_modules/rollup": { - "version": "4.22.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.22.4", - "@rollup/rollup-android-arm64": "4.22.4", - "@rollup/rollup-darwin-arm64": "4.22.4", - "@rollup/rollup-darwin-x64": "4.22.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", - "@rollup/rollup-linux-arm-musleabihf": "4.22.4", - "@rollup/rollup-linux-arm64-gnu": "4.22.4", - "@rollup/rollup-linux-arm64-musl": "4.22.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", - "@rollup/rollup-linux-riscv64-gnu": "4.22.4", - "@rollup/rollup-linux-s390x-gnu": "4.22.4", - "@rollup/rollup-linux-x64-gnu": "4.22.4", - "@rollup/rollup-linux-x64-musl": "4.22.4", - "@rollup/rollup-win32-arm64-msvc": "4.22.4", - "@rollup/rollup-win32-ia32-msvc": "4.22.4", - "@rollup/rollup-win32-x64-msvc": "4.22.4", - "fsevents": "~2.3.2" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -3046,6 +2236,25 @@ "@img/sharp-win32-x64": "0.33.5" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.6", "license": "MIT", @@ -3161,14 +2370,6 @@ "version": "2.1.2", "license": "MIT" }, - "node_modules/source-map-js": { - "version": "1.2.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/statuses": { "version": "2.0.1", "license": "MIT", @@ -3231,17 +2432,6 @@ "license": "MIT", "optional": true }, - "node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/teeny-request": { "version": "9.0.0", "license": "Apache-2.0", @@ -3313,14 +2503,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "license": "MIT", @@ -3371,40 +2553,6 @@ "node": ">= 0.8" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/update-browserslist-db/node_modules/picocolors": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, "node_modules/util-deprecate": { "version": "1.0.2", "license": "MIT", @@ -3435,64 +2583,6 @@ "node": ">= 0.8" } }, - "node_modules/vite": { - "version": "5.4.8", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "license": "BSD-2-Clause", @@ -3526,6 +2616,20 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "license": "MIT", @@ -3604,11 +2708,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, "node_modules/yargs": { "version": "17.7.2", "license": "MIT", diff --git a/web/.env.sample b/web/.env.sample index 87ad24dd..a514b4d3 100644 --- a/web/.env.sample +++ b/web/.env.sample @@ -1,14 +1,14 @@ -VITE_API_ENDPOINT=http://localhost:3000 -VITE_FIREBASE_API_KEY=example -VITE_FIREBASE_AUTH_DOMAIN=example.com -VITE_FIREBASE_PROJECT_ID=example -# VITE_FIREBASE_STORAGE_BUCKET=example.com -# VITE_FIREBASE_MESSAGING_SENDER_ID=example -VITE_FIREBASE_APP_ID=example -VITE_FIREBASE_MEASUREMENT_ID=example -VITE_ALLOW_ANY_MAIL_ADDR=true +NEXT_PUBLIC_API_ENDPOINT=http://localhost:3000 +NEXT_PUBLIC_FIREBASE_API_KEY=example +NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=example.com +NEXT_PUBLIC_FIREBASE_PROJECT_ID=example +# NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=example.com +# NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=example +NEXT_PUBLIC_FIREBASE_APP_ID=example +NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=example +NEXT_PUBLIC_ALLOW_ANY_MAIL_ADDR=true #supabase -VITE_SUPABASE_URL= -VITE_SUPABASE_KEY= +NEXT_PUBLIC_SUPABASE_URL= +NEXT_PUBLIC_SUPABASE_KEY= diff --git a/web/.gitignore b/web/.gitignore index a547bf36..0876a4e4 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -22,3 +22,9 @@ dist-ssr *.njsproj *.sln *.sw? + +.next +next-env.d.ts +out + +/common diff --git a/web/Dockerfile b/web/Dockerfile index 458eef88..7a223471 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /coursemate/dev/web COPY package.json package-lock.json ./ RUN npm ci -ENV VITE_API_ENDPOINT=http://localhost:3000 +ENV NEXT_PUBLIC_API_ENDPOINT=http://localhost:3000 COPY ./ . CMD npm run dev -- --host diff --git a/web/src/api/chat/chat.ts b/web/api/chat/chat.ts similarity index 99% rename from web/src/api/chat/chat.ts rename to web/api/chat/chat.ts index c161e1ba..a574b973 100644 --- a/web/src/api/chat/chat.ts +++ b/web/api/chat/chat.ts @@ -9,7 +9,7 @@ import type { SharedRoom, UpdateRoom, UserID, -} from "../../common/types"; +} from "../../../common/types"; import { ErrUnauthorized, credFetch } from "../../firebase/auth/lib"; import endpoints from "../internal/endpoints"; diff --git a/web/src/api/chat/hooks.ts b/web/api/chat/hooks.ts similarity index 62% rename from web/src/api/chat/hooks.ts rename to web/api/chat/hooks.ts index d7737afb..d835cb9c 100644 --- a/web/src/api/chat/hooks.ts +++ b/web/api/chat/hooks.ts @@ -1,16 +1,22 @@ +"use client"; + import { useCallback } from "react"; import { z } from "zod"; // import { useCallback, useEffect, useState } from "react"; -import type { Message, RoomOverview } from "../../common/types"; -import { MessageSchema, RoomOverviewSchema } from "../../common/zod/schemas"; -import { type Hook, useSWR } from "../../hooks/useSWR"; +import type { Message, RoomOverview } from "../../../common/types"; +import { MessageSchema, RoomOverviewSchema } from "../../../common/zod/schemas"; +import { type Hook, useCustomizedSWR } from "../../hooks/useCustomizedSWR"; import type { UserID } from "../internal/endpoints"; // import type { Hook } from "../share/types"; import * as chat from "./chat"; const OverviewListSchema = z.array(RoomOverviewSchema); export function useRoomsOverview(): Hook { - return useSWR("useRoomsOverview", chat.overview, OverviewListSchema); + return useCustomizedSWR( + "useRoomsOverview", + chat.overview, + OverviewListSchema, + ); } const MessageListSchema = z.array(MessageSchema); @@ -21,5 +27,5 @@ export function useMessages(friendId: UserID): Hook { async () => (await chat.getDM(friendId)).messages, [friendId], ); - return useSWR(key, fetcher, MessageListSchema); + return useCustomizedSWR(key, fetcher, MessageListSchema); } diff --git a/web/api/course.hook.ts b/web/api/course.hook.ts new file mode 100644 index 00000000..509c2169 --- /dev/null +++ b/web/api/course.hook.ts @@ -0,0 +1,10 @@ +import { z } from "zod"; +import type { Course } from "../../common/types"; +import { CourseSchema } from "../../common/zod/schemas"; +import { type Hook, useCustomizedSWR } from "../hooks/useCustomizedSWR"; +import { getMyCourses } from "./course"; + +const CourseListSchema = z.array(CourseSchema); +export function useMyCourses(): Hook { + return useCustomizedSWR("useMyCourses", getMyCourses, CourseListSchema); +} diff --git a/web/src/api/course.ts b/web/api/course.ts similarity index 97% rename from web/src/api/course.ts rename to web/api/course.ts index bc4bd9d7..17bba9bf 100644 --- a/web/src/api/course.ts +++ b/web/api/course.ts @@ -1,4 +1,4 @@ -import type { Course, CourseID, Day } from "../common/types"; +import type { Course, CourseID, Day } from "../../common/types"; import { credFetch } from "../firebase/auth/lib"; import endpoints from "./internal/endpoints"; diff --git a/web/src/api/image.ts b/web/api/image.ts similarity index 100% rename from web/src/api/image.ts rename to web/api/image.ts diff --git a/web/src/api/internal/endpoints.ts b/web/api/internal/endpoints.ts similarity index 96% rename from web/src/api/internal/endpoints.ts rename to web/api/internal/endpoints.ts index aeb1c67e..53fc770a 100644 --- a/web/src/api/internal/endpoints.ts +++ b/web/api/internal/endpoints.ts @@ -1,8 +1,8 @@ -import type { CourseID, Day, GUID } from "../../common/types"; -import type { MessageID, ShareRoomID } from "../../common/types"; +import type { CourseID, Day, GUID } from "../../../common/types"; +import type { MessageID, ShareRoomID } from "../../../common/types"; -export const origin: string | null = import.meta.env.VITE_API_ENDPOINT; -if (!origin) throw new Error("import.meta.env.VITE_API_ENDPOINT not found!"); +export const origin: string | null = process.env.NEXT_PUBLIC_API_ENDPOINT ?? ""; +if (!origin) throw new Error("process.env.NEXT_PUBLIC_API_ENDPOINT not found!"); // TODO: de-export this and use one from /common export type UserID = number; diff --git a/web/src/api/internal/fetch-func.ts b/web/api/internal/fetch-func.ts similarity index 93% rename from web/src/api/internal/fetch-func.ts rename to web/api/internal/fetch-func.ts index b5e712f5..2ef45789 100644 --- a/web/src/api/internal/fetch-func.ts +++ b/web/api/internal/fetch-func.ts @@ -1,4 +1,4 @@ -import { Err, Ok, type Result } from "../../common/lib/result"; +import { Err, Ok, type Result } from "../../../common/lib/result"; export async function safeFetch( path: string, diff --git a/web/src/api/match.ts b/web/api/match.ts similarity index 100% rename from web/src/api/match.ts rename to web/api/match.ts diff --git a/web/src/api/request.ts b/web/api/request.ts similarity index 100% rename from web/src/api/request.ts rename to web/api/request.ts diff --git a/web/src/api/share/types.ts b/web/api/share/types.ts similarity index 100% rename from web/src/api/share/types.ts rename to web/api/share/types.ts diff --git a/web/src/api/user.ts b/web/api/user.ts similarity index 84% rename from web/src/api/user.ts rename to web/api/user.ts index 64315258..8f61ff2a 100644 --- a/web/src/api/user.ts +++ b/web/api/user.ts @@ -1,10 +1,10 @@ import { z } from "zod"; -import type { GUID, UpdateUser, User, UserID } from "../common/types"; -import { parseUser } from "../common/zod/methods.ts"; -import { UserIDSchema, UserSchema } from "../common/zod/schemas.ts"; +import type { GUID, UpdateUser, User, UserID } from "../../common/types.ts"; +import { parseUser } from "../../common/zod/methods.ts"; +import { UserIDSchema, UserSchema } from "../../common/zod/schemas.ts"; import { credFetch } from "../firebase/auth/lib.ts"; +import { type Hook, useCustomizedSWR } from "../hooks/useCustomizedSWR.ts"; import { useAuthorizedData } from "../hooks/useData.ts"; -import { type Hook, useSWR } from "../hooks/useSWR.ts"; import endpoints from "./internal/endpoints.ts"; import type { Hook as UseHook } from "./share/types.ts"; @@ -15,13 +15,17 @@ export function useRecommended(): UseHook { return useAuthorizedData(url); } export function useMatched(): Hook { - return useSWR("users::matched", matched, UserListSchema); + return useCustomizedSWR("users::matched", matched, UserListSchema); } export function usePendingToMe(): Hook { - return useSWR("users::pending::to-me", pendingToMe, UserListSchema); + return useCustomizedSWR("users::pending::to-me", pendingToMe, UserListSchema); } export function usePendingFromMe(): Hook { - return useSWR("users::pending::from-me", pendingFromMe, UserListSchema); + return useCustomizedSWR( + "users::pending::from-me", + pendingFromMe, + UserListSchema, + ); } async function matched(): Promise { @@ -39,7 +43,7 @@ async function pendingFromMe(): Promise { // 自身のユーザー情報を取得する export function useAboutMe(): Hook { - return useSWR("users::aboutMe", aboutMe, UserSchema); + return useCustomizedSWR("users::aboutMe", aboutMe, UserSchema); } async function aboutMe(): Promise { @@ -49,7 +53,7 @@ async function aboutMe(): Promise { // 自身のユーザーIDを取得する export function useMyID(): Hook { - return useSWR("users::myId", getMyId, UserIDSchema); + return useCustomizedSWR("users::myId", getMyId, UserIDSchema); } async function getMyId(): Promise { const me = await aboutMe(); diff --git a/web/app/chat/layout.tsx b/web/app/chat/layout.tsx new file mode 100644 index 00000000..6925d366 --- /dev/null +++ b/web/app/chat/layout.tsx @@ -0,0 +1,31 @@ +import { Box } from "@mui/material"; +import BottomBar from "../../components/BottomBar"; +import Header from "../../components/Header"; + +export default function ChatPageLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> +
+ + {children} + + + + ); +} diff --git a/web/app/chat/page.tsx b/web/app/chat/page.tsx new file mode 100644 index 00000000..e24b11ce --- /dev/null +++ b/web/app/chat/page.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { Typography } from "@mui/material"; +import { useSearchParams } from "next/navigation"; +import { Suspense } from "react"; +import { useRoomsOverview } from "../../api/chat/hooks"; +import RoomList from "../../components/chat/RoomList"; +import { RoomWindow } from "../../components/chat/RoomWindow"; +import FullScreenCircularProgress from "../../components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; + +function ChatListContent() { + const searchParams = useSearchParams(); + + const friendId = searchParams.get("friendId"); + + const { state } = useRoomsOverview(); + + return friendId ? ( + <> +

Chat - friend Id: {friendId}

+ + + ) : state.current === "loading" ? ( + + ) : state.current === "error" ? ( + Error: {state.error.message} + ) : ( + + ); +} + +export default function Chat() { + return ( + + }> + + + + ); +} diff --git a/web/app/edit/courses/page.tsx b/web/app/edit/courses/page.tsx new file mode 100644 index 00000000..8fc3ec09 --- /dev/null +++ b/web/app/edit/courses/page.tsx @@ -0,0 +1,78 @@ +"use client"; + +import { Box, Button, Typography } from "@mui/material"; +import Link from "next/link"; +import { useAboutMe } from "../../../api/user"; +import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; +import EditableCoursesTable from "../../../components/course/EditableCoursesTable"; + +export default function EditCourses() { + const { state } = useAboutMe(); + const data = state.data; + const loading = state.current === "loading"; + const error = state.current === "error" ? state.error : null; + + return ( + + + + 授業編集 + + {loading ? ( + + ) : error ? ( +

Error: {error.message}

+ ) : data ? ( + <> + + + ) : ( +

データがありません。

+ )} + + + + + +
+
+ ); +} diff --git a/web/app/edit/layout.tsx b/web/app/edit/layout.tsx new file mode 100644 index 00000000..adb4ac22 --- /dev/null +++ b/web/app/edit/layout.tsx @@ -0,0 +1,29 @@ +import { Box } from "@mui/material"; +import Header from "../../components/Header"; + +export default function EditPageLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> +
+ + {children} + + + ); +} diff --git a/web/app/edit/profile/page.tsx b/web/app/edit/profile/page.tsx new file mode 100644 index 00000000..1f259962 --- /dev/null +++ b/web/app/edit/profile/page.tsx @@ -0,0 +1,552 @@ +"use client"; + +import EditIcon from "@mui/icons-material/Edit"; +import { + Box, + Button, + FormControl, + IconButton, + InputLabel, + MenuItem, + Select, + TextField, + Typography, +} from "@mui/material"; +import type { SelectChangeEvent } from "@mui/material"; +import { useRouter } from "next/navigation"; +import { enqueueSnackbar } from "notistack"; +import { useEffect, useState } from "react"; +import { update, useAboutMe } from "../../../api/user"; +import type { UpdateUser } from "../../../common/types"; +import { UpdateUserSchema } from "../../../common/zod/schemas"; +import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; +import { useAlert } from "../../../components/common/alert/AlertProvider"; +import PhotoModal from "../../../components/config/PhotoModal"; +import { PhotoPreviewButton } from "../../../components/config/PhotoPreview"; +import UserAvatar from "../../../components/human/avatar"; +import { facultiesAndDepartments } from "../../signup/data"; + +export default function EditProfile() { + const router = useRouter(); + const { showAlert } = useAlert(); + const { state } = useAboutMe(); + const data = state.data; + const error = state.current === "error" ? state.error : null; + const loading = state.current === "loading"; + + const [name, setName] = useState(""); + const [gender, setGender] = useState(""); + const [grade, setGrade] = useState(""); + const [faculty, setFaculty] = useState(""); + const [department, setDepartment] = useState(""); + const [intro, setIntro] = useState(""); + const [pictureUrl, setPictureUrl] = useState(""); + const [tmpName, setTmpName] = useState(""); + const [tmpGender, setTmpGender] = useState(""); + const [tmpGrade, setTmpGrade] = useState(""); + const [tmpFaculty, setTmpFaculty] = useState(""); + const [tmpDepartment, setTmpDepartment] = useState(""); + const [tmpIntro, setTmpIntro] = useState(""); + + const [isEditingName, setIsEditingName] = useState(false); + const [isEditingGender, setIsEditingGender] = useState(false); + const [isEditingGrade, setIsEditingGrade] = useState(false); + const [isEditingFaculty, setIsEditingFaculty] = useState(false); + const [isEditingDepartment, setIsEditingDepartment] = useState(false); + const [isEditingIntro, setIsEditingIntro] = useState(false); + + const [errorMessage, setErrorMessage] = useState(""); + + const [nameError, setNameError] = useState(""); + const [genderError, setGenderError] = useState(""); + const [gradeError, setGradeError] = useState(""); + const [facultyError, setFacultyError] = useState(""); + const [departmentError, setDepartmentError] = useState(""); + const [introError, setIntroError] = useState(""); + + useEffect(() => { + if (data) { + setName(data.name); + setGender(data.gender); + setGrade(data.grade); + setFaculty(data.faculty); + setDepartment(data.department); + setIntro(data.intro); + setPictureUrl(data.pictureUrl); + setTmpName(data.name); + setTmpGender(data.gender); + setTmpGrade(data.grade); + setTmpFaculty(data.faculty); + setTmpDepartment(data.department); + setTmpIntro(data.intro); + } + }, [data]); + + function afterPhotoUpload(result: string) { + try { + setPictureUrl(result); + handleSave({ pictureUrl: result }); + } catch (err) { + console.error(err); + // probably a network error + onPhotoError(new Error("画像の更新に失敗しました")); + } + } + + function onPhotoError(err: Error) { + enqueueSnackbar({ + message: err?.message ?? "画像の更新に失敗しました", + }); + } + const [open, setOpen] = useState(false); + + function hasUnsavedChangesOrErrors() { + return ( + isEditingName || + isEditingGender || + isEditingGrade || + isEditingFaculty || + isEditingDepartment || + isEditingIntro || + errorMessage || + nameError || + genderError || + gradeError || + facultyError || + departmentError || + introError + ); + } + + function handleGoToCourses() { + if (hasUnsavedChangesOrErrors()) { + showAlert({ + AlertMessage: "まだ編集中のフィールド、もしくはエラーがあります", + subAlertMessage: "本当にページを移動しますか?変更は破棄されます", + yesMessage: "移動", + clickYes: () => { + router.push("/edit/courses"); + }, + }); + } else { + router.push("/edit/courses"); + } + } + + function handleBack() { + if (hasUnsavedChangesOrErrors()) { + showAlert({ + AlertMessage: "編集中のフィールド、もしくはエラーがあります。", + subAlertMessage: "本当にページを移動しますか?変更は破棄されます", + yesMessage: "移動", + clickYes: () => { + router.push("/settings/profile"); + }, + }); + } else { + router.push("/settings/profile"); + } + } + + async function handleSave(input: Partial) { + setErrorMessage(""); + setNameError(""); + setGenderError(""); + setGradeError(""); + setFacultyError(""); + setDepartmentError(""); + setIntroError(""); + const data: UpdateUser = { + name: (input.name ?? name).trim(), + gender: input.gender ?? gender, + grade: input.grade ?? grade, + faculty: input.faculty ?? faculty, + department: input.department ?? department, + intro: (input.intro ?? intro).trim(), + pictureUrl: input.pictureUrl ?? pictureUrl, + }; + const result = UpdateUserSchema.safeParse(data); + if (!result.success) { + result.error.errors.map((err) => { + switch (err.path[0]) { + case "name": + setNameError(err.message); + break; + case "gender": + setGenderError(err.message); + break; + case "grade": + setGradeError(err.message); + break; + case "faculty": + setFacultyError(err.message); + break; + case "department": + setDepartmentError(err.message); + break; + case "intro": + setIntroError(err.message); + break; + default: + setErrorMessage("入力に誤りがあります"); + } + }); + return; + } + await update(data); + } + + function handleEdit(setter: React.Dispatch>) { + setTmpName(name); + setTmpGender(gender); + setTmpGrade(grade); + setTmpFaculty(faculty); + setTmpDepartment(department); + setTmpIntro(intro); + setIsEditingName(false); + setIsEditingGender(false); + setIsEditingGrade(false); + setIsEditingFaculty(false); + setIsEditingDepartment(false); + setIsEditingIntro(false); + setter(true); + } + + const handleFacultyChange = (event: SelectChangeEvent) => { + setTmpFaculty(event.target.value); + }; + + const handleDepartmentChange = (event: SelectChangeEvent) => { + setTmpDepartment(event.target.value); + }; + + return ( + + + {loading ? ( + + ) : error ? ( +

Error: {error.message}

+ ) : data ? ( + + + プロフィール編集 + + + + setTmpName(e.target.value)} + label="名前" + disabled={!isEditingName} + fullWidth + error={!!nameError} + helperText={nameError} + autoComplete="off" + /> + { + if (isEditingName) { + setName(tmpName); + handleSave({ name: tmpName }); + setIsEditingName(false); + } else { + handleEdit(setIsEditingName); + } + }} + > + {isEditingName ? ( + 保存 + ) : ( + + )} + + + + + + + 性別 + + {genderError && ( + + {genderError} + + )} + { + if (isEditingGender) { + setGender(tmpGender); + handleSave({ gender: tmpGender }); + setIsEditingGender(false); + } else { + handleEdit(setIsEditingGender); + } + }} + > + {isEditingGender ? ( + 保存 + ) : ( + + )} + + + + + + + 学年 + + {gradeError && ( + + {gradeError} + + )} + { + if (isEditingGrade) { + setGrade(tmpGrade); + handleSave({ grade: tmpGrade }); + setIsEditingGrade(false); + } else { + handleEdit(setIsEditingGrade); + } + }} + > + {isEditingGrade ? ( + 保存 + ) : ( + + )} + + + + + + + 学部 + + {facultyError && ( + + {facultyError} + + )} + + { + if (isEditingFaculty) { + setDepartment(""); + setFaculty(tmpFaculty); + handleSave({ faculty: tmpFaculty, department: "" }); + setIsEditingFaculty(false); + } else { + handleEdit(setIsEditingFaculty); + } + }} + > + {isEditingFaculty ? ( + 保存 + ) : ( + + )} + + + + + + + 学科 + + {departmentError && ( + + {departmentError} + + )} + + { + if (isEditingDepartment) { + setDepartment(tmpDepartment); + handleSave({ department: tmpDepartment }); + setIsEditingDepartment(false); + } else { + handleEdit(setIsEditingDepartment); + } + }} + > + {isEditingDepartment ? ( + 保存 + ) : ( + + )} + + + + + + + setTmpIntro(e.target.value)} + label="自己紹介" + disabled={!isEditingIntro} + fullWidth + autoComplete="off" + error={!!introError} // エラースタイル適用 + helperText={introError} // エラーメッセージを表示 + /> + + { + if (isEditingIntro) { + setIntro(tmpIntro); + handleSave({ intro: tmpIntro }); + setIsEditingIntro(false); + } else { + handleEdit(setIsEditingIntro); + } + }} + > + {isEditingIntro ? ( + 保存 + ) : ( + + )} + + + + {errorMessage && ( + + {errorMessage} + + )} + +
+
+ + プロフィール画像 + +
+
+
+ +
+ setOpen(true)} + /> + setOpen(false)} + afterUpload={afterPhotoUpload} + onError={onPhotoError} + /> +
+
+ + + + + +
+ ) : ( +

データがありません。

+ )} +
+
+ ); +} diff --git a/web/app/error.tsx b/web/app/error.tsx new file mode 100644 index 00000000..846db7da --- /dev/null +++ b/web/app/error.tsx @@ -0,0 +1,11 @@ +"use client"; + +import Link from "next/link"; + +export default function ErrorPage() { + return ( +

+ Sorry, an unexpected error has occurred. Go Back +

+ ); +} diff --git a/web/src/routes/faq.tsx b/web/app/faq/page.tsx similarity index 93% rename from web/src/routes/faq.tsx rename to web/app/faq/page.tsx index c824af41..459fb382 100644 --- a/web/src/routes/faq.tsx +++ b/web/app/faq/page.tsx @@ -1,10 +1,12 @@ +"use client"; + import { ArrowBack } from "@mui/icons-material"; import { Box, IconButton, Typography } from "@mui/material"; -import { useNavigate } from "react-router-dom"; -import Header from "../components/Header"; +import { useRouter } from "next/navigation"; +import Header from "../../components/Header"; export default function FAQ() { - const navigate = useNavigate(); + const router = useRouter(); return ( navigate(-1)} + onClick={() => router.back()} > diff --git a/web/app/favicon.ico b/web/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9818886a772932e7d710a31fc5249c95f22980db GIT binary patch literal 15086 zcmeHO&5ImG6tCTB(+Xk-yh8d^qO#uhKQI$EYi>G=2S*&Ie6OlscYSyF%rv`^X{dem`QGn+)MqA)?XVu(+hag4 zv(KJm>_x_^@WAgJ5k=s1`RqlvAwMe=zGwBH3R$G zK0rNqqp=M;os1H0<7-kzpViI_zYkD{ydk{WZmZ%a9c$ys?}Lr$fa?okS)C?4XsECc z+N}ox0d3}PT)(IeXm4N21F#xbuNyz6P5~XlF(euT4RmaRU+rxQ8^$~B(SF}~bO^`L z+Vh|eKVl51E~`-&R}V{vIQ}UAm~3h@W5C?UYS)*7iP}f>wb;?Y7yne36P8tI41ZgG zs0aB2=s`I?HV4++55h6DdsNDAnIWE~|I9NT*_~*83`O>Bl4bj$F81qGB45aTdfhpo z{FjGh+SsWa>ffe*gZP7f3i+V)zYSQ~6YpD%(%ny9`5>@|VGIRvm13*;!^=7?gL92+ zZqmfY&!8V!gze(S4xu=UxZYqf9S- zKXG)B=gNjA>!h1+KhiMk_=`NpJAv+t;Q6!@z#gV;bSQ?~>(V3Q2d-;RcPr(K-J^5j zhp{JS)Ev;kYkA!lXRZU{^2Za`AbYdqk28-YLZ|dlXEv&9K+g^S23WUUwiei0pc@Ma z`7suu|G?=6+vk!lhKm7~XW24CI!#3?>5k|joiG8n7o-EOXn!)4<*cCTbRf$;MU$Or zAItrTq|^TFMh4~SV6msDxCr#G=|FIxf6fMCDk%&CdOEwL=v>j+{25uE-F#BfYmW~Q z_xxq1XrB#bIa73za!`%2MQ)M@bjisidO+ zucE*X$H^~D@poF6A?1+D-)X^bxVGQ_*aGTa#5b)Wo=wy_E2-yQoqy{4HUMhh7x27_ zZ)DM-)%_OFlXUWj`!#H@}bnmAtVX$TT%L(_YP}zX)kgYNJ<22xeA0lhX9CEhRmyRxr{4+UftnLKjZ;E4mR5d#{JG8-^imb#pFBw z(S2aWw@>NSd{%u-4RF32WDF-(8`#x!u3`&4iZyuz2iE0rGsw2 zoY>iO0pBAFKXdA{5nB0x_~*n|_ru*!UsdhYxG8G-l`wE(ufOMi`1xxSs^2Iz&>i}}&fG~_{Zj&91`M8cudu$kk!UFv%9?k#*C%BzsKtXm)l5mY8j!t4h zlTqN{det`nXj?$NBhcT6Lc9mh4ZZnr$>xJ4yV+l|YmhEwOZHT{O!w1eb`0t7^!IGh zJIYw^@B*ZLmLA_?>HW!2Od-v#3o+Y+hnx>T7xv zl4YFvEJ#&R=_UJ;9gE*2?tOdb>K~C2Zd1iW`skaFuO1*4w_~KH z;-Tr%l1GI7rkm88C%nrZF*bb&=~+lmGj{v~yxrbs>^7_!w}*`V0_i6KZ9q>4_&%!A Ee+BOV literal 0 HcmV?d00001 diff --git a/web/app/friends/layout.tsx b/web/app/friends/layout.tsx new file mode 100644 index 00000000..a0cdb541 --- /dev/null +++ b/web/app/friends/layout.tsx @@ -0,0 +1,31 @@ +import { Box } from "@mui/material"; +import BottomBar from "../../components/BottomBar"; +import Header from "../../components/Header"; + +export default function FriendsPageLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> +
+ + {children} + + + + ); +} diff --git a/web/src/routes/tabs/friends.tsx b/web/app/friends/page.tsx similarity index 87% rename from web/src/routes/tabs/friends.tsx rename to web/app/friends/page.tsx index 284ff2e4..3e254f0c 100644 --- a/web/src/routes/tabs/friends.tsx +++ b/web/app/friends/page.tsx @@ -1,9 +1,12 @@ +"use client"; + import { Box, Tab, Tabs } from "@mui/material"; import { useState } from "react"; +import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; import Matchings from "../../components/match/matching"; import Requests from "../../components/match/requests"; -export function Friends() { +export default function Friends() { const [open, setOpen] = useState(0); const handleChange = (_event: React.SyntheticEvent, newOpen: number) => { @@ -11,7 +14,7 @@ export function Friends() { }; return ( - <> + : open === 1 ? : null} - + ); } diff --git a/web/app/home/layout.tsx b/web/app/home/layout.tsx new file mode 100644 index 00000000..7b43b1af --- /dev/null +++ b/web/app/home/layout.tsx @@ -0,0 +1,31 @@ +import { Box } from "@mui/material"; +import BottomBar from "../../components/BottomBar"; +import Header from "../../components/Header"; + +export default function HomePageLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> +
+ + {children} + + + + ); +} diff --git a/web/src/routes/tabs/home.tsx b/web/app/home/page.tsx similarity index 61% rename from web/src/routes/tabs/home.tsx rename to web/app/home/page.tsx index 9d1b5edb..a868a7fd 100644 --- a/web/src/routes/tabs/home.tsx +++ b/web/app/home/page.tsx @@ -1,3 +1,5 @@ +"use client"; + import CloseIcon from "@mui/icons-material/Close"; import FavoriteIcon from "@mui/icons-material/Favorite"; import { Box, Button } from "@mui/material"; @@ -10,6 +12,7 @@ import { useMyID, useRecommended } from "../../api/user"; import { Card } from "../../components/Card"; import { DraggableCard } from "../../components/DraggableCard"; import FullScreenCircularProgress from "../../components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; export default function Home() { const { data: recommended, error } = useRecommended(); @@ -79,63 +82,68 @@ export default function Home() { } return ( -
- {displayedUser ? ( - - - {nextUser ? ( - - - - ) : null} - - +
+ {displayedUser ? ( + + + {nextUser ? ( + + + + ) : null} + + + + +
+ } /> + } /> - +
-
- } /> - } /> -
- - ) : ( - - )} -
+ ) : ( + + )} +
+ ); } diff --git a/web/src/index.css b/web/app/index.css similarity index 100% rename from web/src/index.css rename to web/app/index.css diff --git a/web/app/layout.tsx b/web/app/layout.tsx new file mode 100644 index 00000000..10bee45a --- /dev/null +++ b/web/app/layout.tsx @@ -0,0 +1,63 @@ +"use client"; + +import { CssBaseline, ThemeProvider, createTheme } from "@mui/material"; +import { SnackbarProvider } from "notistack"; +import React from "react"; +import "./index.css"; +import "@fontsource/roboto/300.css"; +import "@fontsource/roboto/400.css"; +import "@fontsource/roboto/500.css"; +import "@fontsource/roboto/700.css"; +import BanLandscape from "../components/BanLandscape"; +import { AlertProvider } from "../components/common/alert/AlertProvider"; +import { ModalProvider } from "../components/common/modal/ModalProvider"; +import AuthProvider from "../firebase/auth/AuthProvider"; + +const theme = createTheme({ + palette: { + primary: { + main: "#039BE5", + }, + secondary: { + main: "#E9F8FF", + }, + }, +}); + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + + + CourseMate + + + + + + + + + + + + {children} + + + + + + + + + ); +} diff --git a/web/src/routes/login.tsx b/web/app/login/page.tsx similarity index 88% rename from web/src/routes/login.tsx rename to web/app/login/page.tsx index 23843c2a..b60b854a 100644 --- a/web/src/routes/login.tsx +++ b/web/app/login/page.tsx @@ -1,19 +1,23 @@ +"use client"; + import { Box, Button, Link, Typography } from "@mui/material"; import { GoogleAuthProvider, signInWithPopup } from "firebase/auth"; +import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; -import { useNavigate } from "react-router-dom"; -import * as user from "../api/user"; -import { getByGUID } from "../api/user"; -import type { GUID } from "../common/types"; -import Header from "../components/Header"; -import { auth } from "../firebase/config"; -import "../styles/login.css"; +import * as user from "../../api/user"; +import { getByGUID } from "../../api/user"; +import type { GUID } from "../../common/types"; +import Header from "../../components/Header"; +import { auth } from "../../firebase/config"; +import "./style.css"; import { useState } from "react"; -import { CourseMateIcon } from "../components/common/CourseMateIcon"; -import FullScreenCircularProgress from "../components/common/FullScreenCircularProgress"; +import { CourseMateIcon } from "../../components/common/CourseMateIcon"; +import FullScreenCircularProgress from "../../components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; const provider = new GoogleAuthProvider(); -const ALLOW_ANY_MAIL_ADDR = import.meta.env.VITE_ALLOW_ANY_MAIL_ADDR === "true"; +const ALLOW_ANY_MAIL_ADDR = + process.env.NEXT_PUBLIC_ALLOW_ANY_MAIL_ADDR === "true"; async function signInWithGoogle() { try { @@ -48,7 +52,7 @@ async function signInWithGoogle() { } export default function Login() { - const navigate = useNavigate(); + const router = useRouter(); const { enqueueSnackbar } = useSnackbar(); const [loading, setLoading] = useState(false); @@ -66,7 +70,7 @@ export default function Login() { "この Google アカウントは登録されていません。登録画面にリダイレクトしました。", { variant: "info" }, ); - navigate("/signup"); + router.push("/signup"); } else if (response.status >= 500) { enqueueSnackbar( "サーバーエラーが発生しました。しばらくしてから再度お試しください。", @@ -76,7 +80,7 @@ export default function Login() { enqueueSnackbar(`こんにちは、${response.data.name} さん!`, { variant: "success", }); - navigate("/home"); + router.push("/home"); } } catch (error) { console.error(error); @@ -100,10 +104,10 @@ export default function Login() { enqueueSnackbar("この Google アカウントはすでに登録されています", { variant: "error", }); - navigate("/login"); + router.push("/login"); } else { enqueueSnackbar("新規登録を開始します", { variant: "info" }); - navigate("/signup"); + router.push("/signup"); } } catch (e) { console.error(e); @@ -119,7 +123,7 @@ export default function Login() { } return ( - <> +
- + ); } diff --git a/web/src/styles/login.css b/web/app/login/style.css similarity index 100% rename from web/src/styles/login.css rename to web/app/login/style.css diff --git a/web/app/not-found.tsx b/web/app/not-found.tsx new file mode 100644 index 00000000..0b2cbf35 --- /dev/null +++ b/web/app/not-found.tsx @@ -0,0 +1,10 @@ +import Link from "next/link"; + +export default function NotFound() { + return ( + <> +

お探しのページは見つかりませんでした。

+ 戻る + + ); +} diff --git a/web/app/page.tsx b/web/app/page.tsx new file mode 100644 index 00000000..a452ea14 --- /dev/null +++ b/web/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function Page() { + redirect("/login"); +} diff --git a/web/app/settings/aboutUs/page.tsx b/web/app/settings/aboutUs/page.tsx new file mode 100644 index 00000000..a38d99be --- /dev/null +++ b/web/app/settings/aboutUs/page.tsx @@ -0,0 +1,126 @@ +import { GitHub, Language } from "@mui/icons-material"; +import XIcon from "@mui/icons-material/X"; +import { Box, Link, Typography } from "@mui/material"; +import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; +import TopNavigation from "../../../components/common/TopNavigation"; + +export default function AboutUs() { + return ( + + + + + + CourseMateについて + + + 大学の授業を受けている際に、一緒に受ける友達がいなくて困ったことはありませんか? + 「友達がいないから授業をサボるようになってしまった」、「過去問を共有してくれる人がいない」 + などの東大生の悩みを解決したいと思い、CourseMateは開発されました。CourseMateを使うことで + みなさまの大学生活がより良くなれば幸いです。ぜひ知り合いの方々に広めていただければと思っています。 + + + + + CourseMate の GitHub + + + CourseMate の X (旧 Twitter) + + + + + ut.code();について + + + ut.code();は、2019年設立の東京大学のソフトウェアエンジニアリングコミュニティです。 + 「学習」、「交流」、「開発」の三つを活動の軸としており、さまざまなアプリを開発しています。 + + + + + ut.code(); の ウェブサイト + + + + ut.code(); の GitHub + + + + ut.code(); の X (旧 Twitter) + + + + + + ); +} diff --git a/web/app/settings/contact/page.tsx b/web/app/settings/contact/page.tsx new file mode 100644 index 00000000..d51ae0fd --- /dev/null +++ b/web/app/settings/contact/page.tsx @@ -0,0 +1,45 @@ +import { Box, Button, Typography } from "@mui/material"; +import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; +import TopNavigation from "../../../components/common/TopNavigation"; + +export default function Contact() { + return ( + + + + + + ご利用いただきありがとうございます。サービスに関するご意見やバグ報告がございましたら、以下のリンクからお問い合わせください。皆様のフィードバックは、サービスの改善に役立てさせていただきます。 + + + + + + + ); +} diff --git a/web/src/routes/tabs/settings/deleteAccount.tsx b/web/app/settings/delete/page.tsx similarity index 90% rename from web/src/routes/tabs/settings/deleteAccount.tsx rename to web/app/settings/delete/page.tsx index c63769f6..78684e1d 100644 --- a/web/src/routes/tabs/settings/deleteAccount.tsx +++ b/web/app/settings/delete/page.tsx @@ -1,13 +1,16 @@ +"use client"; + import { ArrowBack } from "@mui/icons-material"; import { Box, Button, IconButton, Typography } from "@mui/material"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback } from "react"; -import { useNavigate } from "react-router-dom"; import { deleteAccount } from "../../../api/user"; import { useAlert } from "../../../components/common/alert/AlertProvider"; export default function DeleteAccount() { - const navigate = useNavigate(); + const router = useRouter(); const { showAlert } = useAlert(); const { enqueueSnackbar } = useSnackbar(); @@ -26,11 +29,11 @@ export default function DeleteAccount() { variant: "error", }); } finally { - navigate("/login"); + router.push("/login"); } }, }); - }, [showAlert, enqueueSnackbar, navigate]); + }, [showAlert, enqueueSnackbar, router.push]); return ( navigate("/settings")} + href="/settings" > diff --git a/web/app/settings/disclaimer/page.tsx b/web/app/settings/disclaimer/page.tsx new file mode 100644 index 00000000..c65aef75 --- /dev/null +++ b/web/app/settings/disclaimer/page.tsx @@ -0,0 +1,59 @@ +import { Box, Typography } from "@mui/material"; +import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; +import TopNavigation from "../../../components/common/TopNavigation"; + +export default function Disclaimer() { + return ( + + + + + + 本サービスはut.code();によって運営されており、東京大学は運営に関与しておりません。本サービスは東大生のみを対象としており、ECCSアカウントによるログインが必須です。 + + + + 本サービスの機能の利用に伴ういかなるトラブルや損害について、ut.code();は一切の責任を負いかねます。利用者の自己責任においてご利用ください。 + + + + 本サービスで収集した個人情報は、サインインおよびサービス提供の目的にのみ使用され、他の目的には使用されません。 + + + + 本サービスを通じて他の利用者と接触した際のトラブルや、マッチングを通じて生じた問題等に関しても、ut.code();は責任を負いません。ご理解とご協力をお願いいたします。 + + + + + ); +} diff --git a/web/app/settings/layout.tsx b/web/app/settings/layout.tsx new file mode 100644 index 00000000..9f618772 --- /dev/null +++ b/web/app/settings/layout.tsx @@ -0,0 +1,31 @@ +import { Box } from "@mui/material"; +import BottomBar from "../../components/BottomBar"; +import Header from "../../components/Header"; + +export default function SettingsPageLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> +
+ + {children} + + + + ); +} diff --git a/web/app/settings/page.tsx b/web/app/settings/page.tsx new file mode 100644 index 00000000..0ac88214 --- /dev/null +++ b/web/app/settings/page.tsx @@ -0,0 +1,58 @@ +import { + Box, + Divider, + List, + ListItemButton, + ListItemText, +} from "@mui/material"; +import Link from "next/link"; +import LogOutButton from "../../components/LogOutButton"; +import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; + +export default function Settings() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/web/app/settings/profile/page.tsx b/web/app/settings/profile/page.tsx new file mode 100644 index 00000000..49be77a1 --- /dev/null +++ b/web/app/settings/profile/page.tsx @@ -0,0 +1,72 @@ +"use client"; + +import EditIcon from "@mui/icons-material/Edit"; +import { Box, Button, Typography } from "@mui/material"; +import Link from "next/link"; +import { useState } from "react"; +import { useAboutMe } from "../../../api/user"; +import { Card } from "../../../components/Card"; +import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; +import TopNavigation from "../../../components/common/TopNavigation"; + +export default function SettingsProfile() { + const { state } = useAboutMe(); + const data = state.data; + const error = state.current === "error" ? state.error : null; + const loading = state.current === "loading"; + const [back, setBack] = useState(false); + + return ( + + {loading ? ( + + ) : error ? ( + Error: {error.message} + ) : !data ? ( + データがありません。 + ) : ( + + + + + + + setBack(back)} /> + + + )} + + ); +} diff --git a/web/src/routes/registration/common.tsx b/web/app/signup/common.tsx similarity index 100% rename from web/src/routes/registration/common.tsx rename to web/app/signup/common.tsx diff --git a/web/src/routes/registration/data.ts b/web/app/signup/data.ts similarity index 100% rename from web/src/routes/registration/data.ts rename to web/app/signup/data.ts diff --git a/web/src/routes/registration/functions.ts b/web/app/signup/functions.ts similarity index 85% rename from web/src/routes/registration/functions.ts rename to web/app/signup/functions.ts index 2062bbd2..a6d2a989 100644 --- a/web/src/routes/registration/functions.ts +++ b/web/app/signup/functions.ts @@ -1,18 +1,18 @@ import { getAuth } from "firebase/auth"; +import type { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; import type { EnqueueSnackbar } from "notistack"; -import type { NavigateFunction } from "react-router-dom"; import * as userAPI from "../../api/user"; import type { GUID, User } from "../../common/types"; import type { UpdateUser } from "../../common/types"; type Dependencies = { - navigate: NavigateFunction; + router: AppRouterInstance; enqueueSnackbar: EnqueueSnackbar; }; export async function register( data: UpdateUser, - { navigate, enqueueSnackbar }: Dependencies, + { router, enqueueSnackbar }: Dependencies, ) { const user = getAuth().currentUser; @@ -33,7 +33,7 @@ export async function register( enqueueSnackbar("アカウント作成に失敗しました", { variant: "error", }); - navigate("/", { replace: true }); + router.replace("/"); } } diff --git a/web/src/routes/registration/index.tsx b/web/app/signup/page.tsx similarity index 90% rename from web/src/routes/registration/index.tsx rename to web/app/signup/page.tsx index 4f116eb0..5c5e65d7 100644 --- a/web/src/routes/registration/index.tsx +++ b/web/app/signup/page.tsx @@ -1,8 +1,11 @@ +"use client"; + import { Box } from "@mui/material"; import { useSnackbar } from "notistack"; import { useState } from "react"; -import { useNavigate } from "react-router-dom"; -import type { Step1User } from "../../common/zod/types"; + +import { useRouter } from "next/navigation"; +import type { Step1User } from "../../../common/zod/types"; import Header from "../../components/Header"; import { register } from "./functions"; import Step1 from "./steps/step1_profile"; @@ -12,7 +15,7 @@ import Step4 from "./steps/step4_course"; function Registration() { const { enqueueSnackbar } = useSnackbar(); - const navigate = useNavigate(); + const router = useRouter(); const [step, setStep] = useState<1 | 2 | 3 | 4>(1); const [step1Data, setStep1Data] = useState(); @@ -54,7 +57,7 @@ function Registration() { ...step2Data, }; try { - await register(concat, { enqueueSnackbar, navigate }); + await register(concat, { enqueueSnackbar, router }); setStep(4); } catch (error) { enqueueSnackbar("サインアップに失敗しました", { diff --git a/web/src/routes/registration/steps/step1_profile.tsx b/web/app/signup/steps/step1_profile.tsx similarity index 95% rename from web/src/routes/registration/steps/step1_profile.tsx rename to web/app/signup/steps/step1_profile.tsx index 6d318f26..72b15b4b 100644 --- a/web/src/routes/registration/steps/step1_profile.tsx +++ b/web/app/signup/steps/step1_profile.tsx @@ -11,10 +11,10 @@ import { Typography, } from "@mui/material"; import type { SelectChangeEvent } from "@mui/material"; -import { parseStep1UserSchema } from "../../../common/zod/methods"; -import type { Step1User } from "../../../common/zod/types"; -import type { StepProps } from "../common"; -import { facultiesAndDepartments } from "../data"; +import { parseStep1UserSchema } from "../../../../common/zod/methods"; +import type { Step1User } from "../../../../common/zod/types"; +import type { StepProps } from "../../../app/signup/common"; +import { facultiesAndDepartments } from "../../../app/signup/data"; export default function Step1({ onSave, prev, caller }: StepProps) { const [name, setName] = useState(prev?.name ?? ""); diff --git a/web/src/routes/registration/steps/step2_img.tsx b/web/app/signup/steps/step2_img.tsx similarity index 97% rename from web/src/routes/registration/steps/step2_img.tsx rename to web/app/signup/steps/step2_img.tsx index a7bdf329..799e25c6 100644 --- a/web/src/routes/registration/steps/step2_img.tsx +++ b/web/app/signup/steps/step2_img.tsx @@ -1,10 +1,10 @@ import { Box, Button, Typography } from "@mui/material"; import { enqueueSnackbar } from "notistack"; import { useCallback, useState } from "react"; +import type { BackProp, StepProps } from "../../../app/signup/common"; import PhotoModal from "../../../components/config/PhotoModal"; import { PhotoPreviewButton } from "../../../components/config/PhotoPreview"; import UserAvatar from "../../../components/human/avatar"; -import type { BackProp, StepProps } from "../common"; export type Step2Data = { pictureUrl: string; diff --git a/web/src/routes/registration/steps/step3_confirmation.tsx b/web/app/signup/steps/step3_confirmation.tsx similarity index 95% rename from web/src/routes/registration/steps/step3_confirmation.tsx rename to web/app/signup/steps/step3_confirmation.tsx index 69df91cc..c2cc703f 100644 --- a/web/src/routes/registration/steps/step3_confirmation.tsx +++ b/web/app/signup/steps/step3_confirmation.tsx @@ -1,7 +1,7 @@ import { Box, Button, Typography } from "@mui/material"; -import type { Step1User } from "../../../common/zod/types"; +import type { Step1User } from "../../../../common/zod/types"; +import type { BackProp, StepProps } from "../../../app/signup/common"; import UserAvatar from "../../../components/human/avatar"; -import type { BackProp, StepProps } from "../common"; import type { Step2Data } from "./step2_img"; interface UserInfoProp { diff --git a/web/src/routes/registration/steps/step4_course.tsx b/web/app/signup/steps/step4_course.tsx similarity index 89% rename from web/src/routes/registration/steps/step4_course.tsx rename to web/app/signup/steps/step4_course.tsx index a87b9ae9..2b041c64 100644 --- a/web/src/routes/registration/steps/step4_course.tsx +++ b/web/app/signup/steps/step4_course.tsx @@ -1,12 +1,11 @@ import { Button, Typography } from "@mui/material"; import { Box } from "@mui/system"; -import { useNavigate } from "react-router-dom"; +import Link from "next/link"; import { useMyID } from "../../../api/user"; import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; import EditableCoursesTable from "../../../components/course/EditableCoursesTable"; export default function Step4() { - const navigate = useNavigate(); const { state } = useMyID(); return ( <> @@ -37,7 +36,7 @@ export default function Step4() { }} > - diff --git a/web/src/routes/tutorial.tsx b/web/app/tutorial/page.tsx similarity index 83% rename from web/src/routes/tutorial.tsx rename to web/app/tutorial/page.tsx index f8833a7e..b860e6fa 100644 --- a/web/src/routes/tutorial.tsx +++ b/web/app/tutorial/page.tsx @@ -1,46 +1,47 @@ +"use client"; + import { Box, Button, Typography } from "@mui/material"; -import { useNavigate } from "react-router-dom"; import { Navigation, Pagination } from "swiper/modules"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; import "swiper/css/navigation"; import "swiper/css/pagination"; -import Header from "../components/Header"; +import Link from "next/link"; +import Header from "../../components/Header"; +// TODO: 画像表示の参照を直す const tutorialSteps = [ { label: "CourseMateの使い方", - imgPath: "/course-mate-icon.svg", + imgPath: "../../../public/course-mate-icon.svg", }, { label: "気になる人に「いいね」を送ろう!", - imgPath: "/tutorial-1.png", + imgPath: "../../public/tutorial-1.png", }, { label: "リクエストが来たら...", - imgPath: "/tutorial-2.png", + imgPath: "../../public/tutorial-2.png", }, { label: "承認して、マッチング!", - imgPath: "/tutorial-3.png", + imgPath: "../../public/tutorial-3.png", }, { label: "チャットを使って仲良くなろう!", - imgPath: "/tutorial-4.png", + imgPath: "../../public/tutorial-4.png", }, { label: "魅力的なカードを作ってね!", - imgPath: "/tutorial-5.png", + imgPath: "../../public/tutorial-5.png", }, { label: "実際にCourseMateを使ってみよう!下のボタンをクリック!", - imgPath: "/tutorial-6.png", + imgPath: "../../public/tutorial-6.png", }, ]; export default function Tutorial() { - const navigate = useNavigate(); - return ( navigate("/home")} + LinkComponent={Link} + href="/home" sx={{ width: "100%" }} > ホーム画面へ diff --git a/web/bun.lockb b/web/bun.lockb index 299f713b26f45b25dbfcf373fc6e6287a0cd91ca..9c4777ea547024f113a0ef8f51d1f333d14f6b89 100755 GIT binary patch delta 38563 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 delta 35831 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 { diff --git a/web/components/BottomBar.tsx b/web/components/BottomBar.tsx new file mode 100644 index 00000000..84153585 --- /dev/null +++ b/web/components/BottomBar.tsx @@ -0,0 +1,84 @@ +import ChatIcon from "@mui/icons-material/Chat"; +import HomeIcon from "@mui/icons-material/Home"; +import PeopleIcon from "@mui/icons-material/People"; +import SettingsIcon from "@mui/icons-material/Settings"; +import { BottomNavigation, BottomNavigationAction, Box } from "@mui/material"; +import Link from "next/link"; + +type Props = { + activeTab: "0_home" | "1_friends" | "2_chat" | "3_settings"; +}; + +export default function BottomBar(props: Props) { + const { activeTab } = props; + return ( + + {/* TODO: 単に Viewer として BottomNavigation を使用しているので Box 等で置き換える */} + + + } + /> + + } + /> + + } + /> + + } + /> + + + ); +} diff --git a/web/src/components/Card.tsx b/web/components/Card.tsx similarity index 100% rename from web/src/components/Card.tsx rename to web/components/Card.tsx diff --git a/web/src/components/DraggableCard.tsx b/web/components/DraggableCard.tsx similarity index 98% rename from web/src/components/DraggableCard.tsx rename to web/components/DraggableCard.tsx index 4a09ec29..8aaff469 100644 --- a/web/src/components/DraggableCard.tsx +++ b/web/components/DraggableCard.tsx @@ -3,7 +3,7 @@ import FavoriteIcon from "@mui/icons-material/Favorite"; import { Box, Typography } from "@mui/material"; import { motion, useMotionValue, useMotionValueEvent } from "framer-motion"; import { useCallback, useState } from "react"; -import type { User, UserID } from "../common/types"; +import type { User, UserID } from "../../common/types"; import { Card } from "./Card"; const SWIPE_THRESHOLD = 30; diff --git a/web/src/components/Header.tsx b/web/components/Header.tsx similarity index 78% rename from web/src/components/Header.tsx rename to web/components/Header.tsx index 2b05d6c5..4a2c3bf1 100644 --- a/web/src/components/Header.tsx +++ b/web/components/Header.tsx @@ -1,6 +1,6 @@ import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import { AppBar, Box, IconButton, Toolbar, Typography } from "@mui/material"; -import { useNavigate } from "react-router-dom"; +import Link from "next/link"; import { CourseMateIcon } from "./common/CourseMateIcon"; type Props = { @@ -9,7 +9,6 @@ type Props = { export default function Header(props: Props) { const { title } = props; - const navigate = useNavigate(); return ( <> - { - navigate("/home"); - }} - > + @@ -36,7 +31,7 @@ export default function Header(props: Props) { > {title} - navigate("/faq")} sx={{ zIndex: "100" }}> + diff --git a/web/src/components/ImageCropper.tsx b/web/components/ImageCropper.tsx similarity index 100% rename from web/src/components/ImageCropper.tsx rename to web/components/ImageCropper.tsx diff --git a/web/src/components/ImageFallback.tsx b/web/components/ImageFallback.tsx similarity index 92% rename from web/src/components/ImageFallback.tsx rename to web/components/ImageFallback.tsx index c0b3f717..f31e65f4 100644 --- a/web/src/components/ImageFallback.tsx +++ b/web/components/ImageFallback.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useEffect, useState } from "react"; type Props = { @@ -16,7 +18,7 @@ export function ImageFallback({ width, height, url, fallback, alt }: Props) { setOK(true); }, [url]); const URL = url?.startsWith("/") - ? `${import.meta.env.VITE_API_ENDPOINT}${url}` + ? `${process.env.NEXT_PUBLIC_API_ENDPOINT}${url}` : url; return ok ? ( diff --git a/web/src/components/LogOutButton.tsx b/web/components/LogOutButton.tsx similarity index 87% rename from web/src/components/LogOutButton.tsx rename to web/components/LogOutButton.tsx index aae5d611..dc12e80f 100644 --- a/web/src/components/LogOutButton.tsx +++ b/web/components/LogOutButton.tsx @@ -1,14 +1,16 @@ +"use client"; + import { ListItemButton, ListItemText } from "@mui/material"; import { signOut } from "firebase/auth"; +import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback } from "react"; -import { useNavigate } from "react-router-dom"; import { auth } from "../firebase/config"; import { useAlert } from "./common/alert/AlertProvider"; export default function LogOutButton() { const { showAlert } = useAlert(); - const navigate = useNavigate(); + const router = useRouter(); const { enqueueSnackbar } = useSnackbar(); const signOutUser = useCallback(async () => { @@ -19,9 +21,9 @@ export default function LogOutButton() { console.error(error); enqueueSnackbar("ログアウトに失敗しました", { variant: "error" }); } finally { - navigate("/login"); + router.push("/login"); } - }, [navigate, enqueueSnackbar]); + }, [router.push, enqueueSnackbar]); const onClick = useCallback(() => { showAlert({ diff --git a/web/src/components/about.tsx b/web/components/about.tsx similarity index 100% rename from web/src/components/about.tsx rename to web/components/about.tsx diff --git a/web/src/components/chat/MessageInput.tsx b/web/components/chat/MessageInput.tsx similarity index 100% rename from web/src/components/chat/MessageInput.tsx rename to web/components/chat/MessageInput.tsx diff --git a/web/src/components/chat/Room.tsx b/web/components/chat/Room.tsx similarity index 93% rename from web/src/components/chat/Room.tsx rename to web/components/chat/Room.tsx index 639bfea4..c3a11cd0 100644 --- a/web/src/components/chat/Room.tsx +++ b/web/components/chat/Room.tsx @@ -1,5 +1,5 @@ import { ListItem, Stack, Typography } from "@mui/material"; -import type { DMOverview } from "../../common/types"; +import type { DMOverview } from "../../../common/types"; import UserAvatar from "../human/avatar"; type Props = { diff --git a/web/src/components/chat/RoomHeader.tsx b/web/components/chat/RoomHeader.tsx similarity index 85% rename from web/src/components/chat/RoomHeader.tsx rename to web/components/chat/RoomHeader.tsx index 106d3054..9871df93 100644 --- a/web/src/components/chat/RoomHeader.tsx +++ b/web/components/chat/RoomHeader.tsx @@ -1,6 +1,6 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import { Box, Button, Typography } from "@mui/material"; -import { useNavigate } from "react-router-dom"; +import Link from "next/link"; import type { DMOverview } from "../../common/types"; import UserAvatar from "../human/avatar"; type Props = { @@ -9,7 +9,6 @@ type Props = { export function RoomHeader(props: Props) { const { room } = props; - const navigation = useNavigate(); return ( { - navigation("/chat"); - }} + LinkComponent={Link} + href="/chat" > diff --git a/web/src/components/chat/RoomList.tsx b/web/components/chat/RoomList.tsx similarity index 75% rename from web/src/components/chat/RoomList.tsx rename to web/components/chat/RoomList.tsx index b4eb112a..a11bfb65 100644 --- a/web/src/components/chat/RoomList.tsx +++ b/web/components/chat/RoomList.tsx @@ -1,5 +1,7 @@ +"use client"; + import { Box, List, Typography } from "@mui/material"; -import { useNavigate } from "react-router-dom"; +import { useRouter } from "next/navigation"; import type { RoomOverview } from "../../common/types"; import { HumanListItem } from "../human/humanListItem"; @@ -9,7 +11,17 @@ type RoomListProps = { export function RoomList(props: RoomListProps) { const { roomsData } = props; - const navigate = useNavigate(); + const router = useRouter(); + + /** + * FIXME: + * React Router が使えなくなったので、一時的に room の情報を URL に載せることで状態管理 + */ + const navigateToRoom = (room: Extract) => { + router.push( + `./?friendId=${room.friendId}&roomData=${encodeURIComponent(JSON.stringify(room))}`, + ); + }; return ( @@ -33,8 +45,7 @@ export function RoomList(props: RoomListProps) { { - // `state`を使って`room`データを渡す - navigate(`./${room.friendId}`, { state: { room } }); + navigateToRoom(room); }} > 部屋が見つかりません。; + } const { state: { data: myId }, @@ -60,6 +68,7 @@ export function RoomWindow() { useEffect(() => { async function registerSocket() { + if (!room) return; const idToken = await getIdToken(); socket.emit("register", idToken); socket.on("newMessage", async (msg: Message) => { @@ -93,7 +102,7 @@ export function RoomWindow() { socket.off("deleteMessage"); }; }, [ - room.friendId, + room, enqueueSnackbar, appendLocalMessage, updateLocalMessage, diff --git a/web/src/components/common/CourseMateIcon.tsx b/web/components/common/CourseMateIcon.tsx similarity index 100% rename from web/src/components/common/CourseMateIcon.tsx rename to web/components/common/CourseMateIcon.tsx diff --git a/web/src/components/common/Dots.tsx b/web/components/common/Dots.tsx similarity index 100% rename from web/src/components/common/Dots.tsx rename to web/components/common/Dots.tsx diff --git a/web/src/components/common/FullScreenCircularProgress.tsx b/web/components/common/FullScreenCircularProgress.tsx similarity index 100% rename from web/src/components/common/FullScreenCircularProgress.tsx rename to web/components/common/FullScreenCircularProgress.tsx diff --git a/web/src/components/common/NavigateByAuthState.tsx b/web/components/common/NavigateByAuthState.tsx similarity index 82% rename from web/src/components/common/NavigateByAuthState.tsx rename to web/components/common/NavigateByAuthState.tsx index 3372ee83..9e22d2ae 100644 --- a/web/src/components/common/NavigateByAuthState.tsx +++ b/web/components/common/NavigateByAuthState.tsx @@ -1,6 +1,8 @@ +"use client"; + import { getAuth, onAuthStateChanged } from "firebase/auth"; +import { useRouter } from "next/navigation"; import { type ReactNode, useEffect, useState } from "react"; -import { Navigate } from "react-router-dom"; import { userExists } from "../../api/internal/endpoints"; import FullScreenCircularProgress from "./FullScreenCircularProgress"; @@ -18,6 +20,7 @@ export function NavigateByAuthState({ children: ReactNode; }) { const [isAuthenticated, setIsAuthenticated] = useState(null); + const router = useRouter(); useEffect(() => { const unsubscribe = onAuthStateChanged(getAuth(), async (user) => { @@ -36,7 +39,15 @@ export function NavigateByAuthState({ } if (type === "toHomeForAuthenticated") { - return isAuthenticated ? : children; + if (isAuthenticated) { + router.push("/home"); + } else { + return children; + } + } else { + if (isAuthenticated) { + return children; + } + router.push("/login"); } - return isAuthenticated ? children : ; } diff --git a/web/src/components/common/Popup.tsx b/web/components/common/Popup.tsx similarity index 100% rename from web/src/components/common/Popup.tsx rename to web/components/common/Popup.tsx diff --git a/web/src/routes/tabs/settings/components/TopNavigation.tsx b/web/components/common/TopNavigation.tsx similarity index 86% rename from web/src/routes/tabs/settings/components/TopNavigation.tsx rename to web/components/common/TopNavigation.tsx index 2edfc8d0..a52a45bc 100644 --- a/web/src/routes/tabs/settings/components/TopNavigation.tsx +++ b/web/components/common/TopNavigation.tsx @@ -1,12 +1,11 @@ import { ArrowBack } from "@mui/icons-material"; import { Box, IconButton, Typography } from "@mui/material"; -import { useNavigate } from "react-router-dom"; +import Link from "next/link"; /** * Settings の子ページから Setting に戻るナビゲーションを提供 */ export default function TopNavigation({ title }: { title: string }) { - const navigate = useNavigate(); return ( - { - navigate("/settings"); - }} - > + (undefined); diff --git a/web/src/firebase/auth/lib.ts b/web/firebase/auth/lib.ts similarity index 96% rename from web/src/firebase/auth/lib.ts rename to web/firebase/auth/lib.ts index 064eceb8..15f33c3a 100644 --- a/web/src/firebase/auth/lib.ts +++ b/web/firebase/auth/lib.ts @@ -1,6 +1,6 @@ import { getAuth, onAuthStateChanged } from "firebase/auth"; import type { User } from "firebase/auth"; -import type { IDToken } from "../../common/types"; +import type { IDToken } from "../../../common/types"; import { app } from "../config"; export class ErrUnauthorized extends Error {} diff --git a/web/firebase/config.ts b/web/firebase/config.ts new file mode 100644 index 00000000..c41f97dc --- /dev/null +++ b/web/firebase/config.ts @@ -0,0 +1,17 @@ +import { initializeApp } from "firebase/app"; +import { getAuth } from "firebase/auth"; + +export const firebaseConfig = { + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, + projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + // storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, + // messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, + measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID, +}; + +const app = initializeApp(firebaseConfig); +const auth = getAuth(app); + +export { app, auth }; diff --git a/web/src/hooks/useCurrentUser.ts b/web/hooks/useCurrentUser.ts similarity index 100% rename from web/src/hooks/useCurrentUser.ts rename to web/hooks/useCurrentUser.ts diff --git a/web/src/hooks/useSWR.ts b/web/hooks/useCustomizedSWR.ts similarity index 78% rename from web/src/hooks/useSWR.ts rename to web/hooks/useCustomizedSWR.ts index 83d3906d..69378be1 100644 --- a/web/src/hooks/useSWR.ts +++ b/web/hooks/useCustomizedSWR.ts @@ -1,3 +1,5 @@ +"use client"; + import { parse, stringify } from "devalue"; import { useCallback, useEffect, useState } from "react"; import type { ZodSchema } from "zod"; @@ -42,7 +44,7 @@ const SWR_PREFIX = "CourseMate::useSWR::"; cacheKey **MUST** be unique in all the codebase, otherwise the cache will interfere each other. (I recommend using URL Path, friend's name + unique prefix, or randomly generate static string.) **/ -export function useSWR( +export function useCustomizedSWR( cacheKey: string, fetcher: () => Promise, schema: Zod.Schema, @@ -82,7 +84,10 @@ export function useSWR( current: "success", error: null, }); - localStorage.setItem(CACHE_KEY, stringify(data)); + // FIXME: クライアント側である (window オブジェクトが存在する) ことを保証しないと Next.js のエラーが出るため回避している + if (typeof window !== "undefined") { + localStorage.setItem(CACHE_KEY, stringify(data)); + } } catch (e) { setState({ data: null, @@ -94,7 +99,10 @@ export function useSWR( const write = useCallback( (data: T) => { - localStorage.setItem(CACHE_KEY, stringify(data)); + // FIXME: クライアント側である (window オブジェクトが存在する) ことを保証しないと Next.js のエラーが出るため回避している + if (typeof window !== "undefined") { + localStorage.setItem(CACHE_KEY, stringify(data)); + } }, [CACHE_KEY], ); @@ -114,7 +122,9 @@ function loadOldData( CACHE_KEY: string, schema: ZodSchema, ): Loading | Stale { - const oldData = localStorage.getItem(CACHE_KEY); + // FIXME: クライアント側である (window オブジェクトが存在する) ことを保証しないと Next.js のエラーが出るため回避している + const oldData = + typeof window !== "undefined" ? localStorage.getItem(CACHE_KEY) : null; if (oldData) { try { const data = parse(oldData); diff --git a/web/src/hooks/useData.ts b/web/hooks/useData.ts similarity index 96% rename from web/src/hooks/useData.ts rename to web/hooks/useData.ts index 98e77648..586ddab7 100644 --- a/web/src/hooks/useData.ts +++ b/web/hooks/useData.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from "react"; -import { Err, Ok, type Result } from "../common/lib/result"; +import { Err, Ok, type Result } from "../../common/lib/result"; import { credFetch } from "../firebase/auth/lib"; // TODO: separate this into concrete types and urls s.t. there is no unsafe any diff --git a/web/index.html b/web/index.html deleted file mode 100644 index 32883bb3..00000000 --- a/web/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - CourseMate - - -
- - - diff --git a/web/next.config.mjs b/web/next.config.mjs new file mode 100644 index 00000000..4350313c --- /dev/null +++ b/web/next.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: "export", +}; + +export default nextConfig; diff --git a/web/package-lock.json b/web/package-lock.json index d6607767..4cb4be05 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -13,36 +13,25 @@ "@fontsource/roboto": "^5.0.13", "@mui/icons-material": "^5.16.7", "@mui/material": "^5.15.20", + "devalue": "^5.1.1", "firebase": "^10.12.2", "framer-motion": "^11.3.23", + "next": "^14.2.16", "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-easy-crop": "^5.0.8", - "react-router-dom": "^6.26.2", "socket.io-client": "^4.7.5", "swiper": "^11.1.14", "zod": "^3.23.8" }, "devDependencies": { + "@types/css-modules": "^1.0.5", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", - "@vitejs/plugin-react": "^4.2.1", + "eslint": "^9.14.0", "globals": "^15.9.0", - "typescript": "^5.2.2", - "vite": "^5.2.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" + "typescript": "^5.2.2" } }, "node_modules/@babel/code-frame": { @@ -56,43 +45,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, "node_modules/@babel/generator": { "version": "7.24.7", "license": "MIT", @@ -106,21 +58,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.24.7", "license": "MIT", @@ -163,44 +100,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.24.7", "license": "MIT", @@ -225,26 +124,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/highlight": { "version": "7.24.7", "license": "MIT", @@ -268,34 +147,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/runtime": { "version": "7.24.7", "license": "MIT", @@ -480,19 +331,131 @@ "version": "0.3.1", "license": "MIT" }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", + "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", + "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@fastify/busboy": { @@ -1044,6 +1007,67 @@ "node": ">=6" } }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.0.tgz", + "integrity": "sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "license": "MIT", @@ -1318,27 +1342,167 @@ } } }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } + "node_modules/@next/env": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.16.tgz", + "integrity": "sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag==" }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "license": "BSD-3-Clause" + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.16.tgz", + "integrity": "sha512-uFT34QojYkf0+nn6MEZ4gIWQ5aqGF11uIZ1HSxG+cSbj+Mg3+tYm8qXYd3dKN5jqKUm5rBVvf1PBRO/MeQ6rxw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "license": "BSD-3-Clause" + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.16.tgz", + "integrity": "sha512-mCecsFkYezem0QiZlg2bau3Xul77VxUD38b/auAjohMA22G9KTJneUYMv78vWoCCFkleFAhY1NIvbyjj1ncG9g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "license": "BSD-3-Clause" + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.16.tgz", + "integrity": "sha512-yhkNA36+ECTC91KSyZcgWgKrYIyDnXZj8PqtJ+c2pMvj45xf7y/HrgI17hLdrcYamLfVt7pBaJUMxADtPaczHA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@protobufjs/eventemitter": { + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.16.tgz", + "integrity": "sha512-X2YSyu5RMys8R2lA0yLMCOCtqFOoLxrq2YbazFvcPOE4i/isubYjkh+JCpRmqYfEuCVltvlo+oGfj/b5T2pKUA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.16.tgz", + "integrity": "sha512-9AGcX7VAkGbc5zTSa+bjQ757tkjr6C/pKS7OK8cX7QEiK6MHIIezBLcQ7gQqbDW2k5yaqba2aDtaBeyyZh1i6Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.16.tgz", + "integrity": "sha512-Klgeagrdun4WWDaOizdbtIIm8khUDQJ/5cRzdpXHfkbY91LxBXeejL4kbZBrpR/nmgRrQvmz4l3OtttNVkz2Sg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.16.tgz", + "integrity": "sha512-PwW8A1UC1Y0xIm83G3yFGPiOBftJK4zukTmk7DI1CebyMOoaVpd8aSy7K6GhobzhkjYvqS/QmzcfsWG2Dwizdg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.16.tgz", + "integrity": "sha512-jhPl3nN0oKEshJBNDAo0etGMzv0j3q3VYorTSFqH1o3rwv1MQRdor27u1zhkgsHPNeY1jxcgyx1ZsCkDD1IHgg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.16.tgz", + "integrity": "sha512-OA7NtfxgirCjfqt+02BqxC3MIgM/JaGjw9tOe4fyZgPsqfseNiMPnCRP44Pfs+Gpo9zPN+SXaFsgP6vk8d571A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "license": "BSD-3-Clause" }, @@ -1370,70 +1534,41 @@ "version": "1.1.0", "license": "BSD-3-Clause" }, - "node_modules/@remix-run/router": { - "version": "1.19.2", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "license": "MIT" }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "dev": true, - "license": "MIT", + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" } }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } + "node_modules/@types/css-modules": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/css-modules/-/css-modules-1.0.5.tgz", + "integrity": "sha512-oeKafs/df9lwOvtfiXVliZsocFVOexK9PZtLQWuPeuVCFR7jwiqlg60lu80JTe5NFNtH3tnV6Fs/ySR8BUPHAw==", + "dev": true }, "node_modules/@types/estree": { - "version": "1.0.5", - "dev": true, - "license": "MIT" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true }, "node_modules/@types/node": { "version": "20.14.5", @@ -1473,22 +1608,41 @@ "@types/react": "*" } }, - "node_modules/@vitejs/plugin-react": { - "version": "4.3.1", + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.5", - "@babel/plugin-transform-react-jsx-self": "^7.24.5", - "@babel/plugin-transform-react-jsx-source": "^7.24.1", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0" + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ansi-regex": { @@ -1508,6 +1662,12 @@ "node": ">=4" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "license": "MIT", @@ -1521,35 +1681,31 @@ "npm": ">=6" } }, - "node_modules/browserslist": { - "version": "4.23.1", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" - }, - "bin": { - "browserslist": "cli.js" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=10.16.0" } }, "node_modules/callsites": { @@ -1561,7 +1717,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001636", - "dev": true, "funding": [ { "type": "opencollective", @@ -1597,6 +1752,11 @@ "node": ">=0.8.0" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, "node_modules/cliui": { "version": "8.0.1", "license": "ISC", @@ -1627,10 +1787,11 @@ "version": "1.1.3", "license": "MIT" }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "dev": true, - "license": "MIT" + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/cosmiconfig": { "version": "7.1.0", @@ -1646,6 +1807,20 @@ "node": ">=10" } }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/csstype": { "version": "3.1.3", "license": "MIT" @@ -1665,6 +1840,17 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/devalue": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", + "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==" + }, "node_modules/dom-helpers": { "version": "5.2.1", "license": "MIT", @@ -1673,11 +1859,6 @@ "csstype": "^3.0.2" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.805", - "dev": true, - "license": "ISC" - }, "node_modules/emoji-regex": { "version": "8.0.0", "license": "MIT" @@ -1726,43 +1907,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/esbuild": { - "version": "0.21.5", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "node_modules/escalade": { "version": "3.1.2", "license": "MIT", @@ -1780,6 +1924,241 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", + "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.14.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.0", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, "node_modules/faye-websocket": { "version": "0.11.4", "license": "Apache-2.0", @@ -1790,10 +2169,38 @@ "node": ">=0.8.0" } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/find-root": { "version": "1.1.0", "license": "MIT" }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/firebase": { "version": "10.12.2", "license": "Apache-2.0", @@ -1827,6 +2234,25 @@ "@firebase/vertexai-preview": "0.0.2" } }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, "node_modules/framer-motion": { "version": "11.3.23", "license": "MIT", @@ -1850,18 +2276,6 @@ } } }, - "node_modules/fsevents": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -1869,14 +2283,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "license": "ISC", @@ -1884,6 +2290,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/globals": { "version": "15.9.0", "dev": true, @@ -1902,6 +2320,11 @@ "csstype": "^3.0.10" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "node_modules/has-flag": { "version": "3.0.0", "license": "MIT", @@ -1938,6 +2361,15 @@ "version": "7.1.1", "license": "ISC" }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "license": "MIT", @@ -1952,6 +2384,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "license": "MIT" @@ -1966,6 +2407,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "license": "MIT", @@ -1973,10 +2423,40 @@ "node": ">=8" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/js-tokens": { "version": "4.0.0", "license": "MIT" }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsesc": { "version": "2.5.2", "license": "MIT", @@ -1987,29 +2467,79 @@ "node": ">=4" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=6" + "node": ">= 0.8.0" } }, "node_modules/lines-and-columns": { "version": "1.2.4", "license": "MIT" }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/long": { "version": "5.2.3", "license": "Apache-2.0" @@ -2024,40 +2554,120 @@ "loose-envify": "cli.js" } }, - "node_modules/lru-cache": { - "version": "5.1.1", + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { - "yallist": "^3.0.2" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/next": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.16.tgz", + "integrity": "sha512-LcO7WnFu6lYSvCzZoo1dB+IO0xXz5uEv52HF1IUN0IqVTUIZGHuuR10I5efiLadGt+4oZqTcNZyVVEem/TM5nA==", + "dependencies": { + "@next/env": "14.2.16", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.16", + "@next/swc-darwin-x64": "14.2.16", + "@next/swc-linux-arm64-gnu": "14.2.16", + "@next/swc-linux-arm64-musl": "14.2.16", + "@next/swc-linux-x64-gnu": "14.2.16", + "@next/swc-linux-x64-musl": "14.2.16", + "@next/swc-win32-arm64-msvc": "14.2.16", + "@next/swc-win32-ia32-msvc": "14.2.16", + "@next/swc-win32-x64-msvc": "14.2.16" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } } }, - "node_modules/ms": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "dev": true, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^10 || ^12 || >=14" } }, - "node_modules/node-releases": { - "version": "2.0.14", - "dev": true, - "license": "MIT" - }, "node_modules/normalize-wheel": { "version": "1.0.1", "license": "BSD-3-Clause" @@ -2096,6 +2706,53 @@ "node": ">=0.10.0" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parent-module": { "version": "1.0.1", "license": "MIT", @@ -2122,6 +2779,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "license": "MIT" @@ -2137,31 +2812,13 @@ "version": "1.0.1", "license": "ISC" }, - "node_modules/postcss": { - "version": "8.4.38", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">= 0.8.0" } }, "node_modules/prop-types": { @@ -2199,6 +2856,15 @@ "node": ">=12.0.0" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/react": { "version": "18.3.1", "license": "MIT", @@ -2236,42 +2902,6 @@ "version": "18.3.1", "license": "MIT" }, - "node_modules/react-refresh": { - "version": "0.14.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "6.26.2", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.19.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.26.2", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.19.2", - "react-router": "6.26.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, "node_modules/react-transition-group": { "version": "4.4.5", "license": "BSD-3-Clause", @@ -2319,40 +2949,6 @@ "node": ">=4" } }, - "node_modules/rollup": { - "version": "4.18.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", - "fsevents": "~2.3.2" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -2378,12 +2974,25 @@ "loose-envify": "^1.1.0" } }, - "node_modules/semver": { - "version": "6.3.1", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/socket.io-client": { @@ -2419,12 +3028,19 @@ }, "node_modules/source-map-js": { "version": "1.2.0", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "license": "MIT", @@ -2447,6 +3063,40 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/stylis": { "version": "4.2.0", "license": "MIT" @@ -2490,6 +3140,12 @@ "node": ">= 4.7.0" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/to-fast-properties": { "version": "2.0.0", "license": "MIT", @@ -2501,6 +3157,18 @@ "version": "2.6.3", "license": "0BSD" }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/typescript": { "version": "5.4.5", "dev": true, @@ -2527,87 +3195,13 @@ "version": "5.26.5", "license": "MIT" }, - "node_modules/update-browserslist-db": { - "version": "1.0.16", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/vite": { - "version": "5.3.1", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.38", - "rollup": "^4.13.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "punycode": "^2.1.0" } }, "node_modules/websocket-driver": { @@ -2629,6 +3223,30 @@ "node": ">=0.8.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "license": "MIT", @@ -2684,11 +3302,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, "node_modules/yaml": { "version": "1.10.2", "license": "ISC", @@ -2719,6 +3332,18 @@ "node": ">=12" } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "3.23.8", "license": "MIT", diff --git a/web/package.json b/web/package.json index 53c29a8d..7f6c22ea 100644 --- a/web/package.json +++ b/web/package.json @@ -4,9 +4,9 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" + "dev": "next dev -p 5173", + "build": "next build", + "preview": "next start" }, "dependencies": { "@emotion/react": "^11.11.4", @@ -17,21 +17,21 @@ "devalue": "^5.1.1", "firebase": "^10.12.2", "framer-motion": "^11.3.23", + "next": "^14.2.16", "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-easy-crop": "^5.0.8", - "react-router-dom": "^6.26.2", "socket.io-client": "^4.7.5", "swiper": "^11.1.14", "zod": "^3.23.8" }, "devDependencies": { + "@types/css-modules": "^1.0.5", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", - "@vitejs/plugin-react": "^4.2.1", + "eslint": "^9.14.0", "globals": "^15.9.0", - "typescript": "^5.2.2", - "vite": "^5.2.0" + "typescript": "^5.2.2" } } diff --git a/web/src/.gitignore b/web/src/.gitignore deleted file mode 100644 index 8c91e3f6..00000000 --- a/web/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/common diff --git a/web/src/App.tsx b/web/src/App.tsx deleted file mode 100644 index 82acfa6c..00000000 --- a/web/src/App.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import { CssBaseline, Link, createTheme } from "@mui/material"; -import { ThemeProvider } from "@mui/system"; -import { SnackbarProvider } from "notistack"; -import { - Navigate, - RouterProvider, - createBrowserRouter, -} from "react-router-dom"; -import { RoomWindow } from "./components/chat/RoomWindow"; -import { NavigateByAuthState } from "./components/common/NavigateByAuthState"; -import EditCourses from "./routes/editCourses"; -import EditProfile from "./routes/editProfile"; -import FAQ from "./routes/faq"; -import Login from "./routes/login"; -import RegistrationPage from "./routes/registration/index"; -import Root from "./routes/root"; -import Chat from "./routes/tabs/chat"; -import { Friends } from "./routes/tabs/friends"; -import Home from "./routes/tabs/home"; -import AboutUs from "./routes/tabs/settings/aboutUs"; -import Contact from "./routes/tabs/settings/contact"; -import DeleteAccount from "./routes/tabs/settings/deleteAccount"; -import Disclaimer from "./routes/tabs/settings/disclaimer"; -import Profile from "./routes/tabs/settings/profile"; -import Settings from "./routes/tabs/settings/settings"; -import Tutorial from "./routes/tutorial"; - -export default function App() { - const router = createBrowserRouter([ - { - path: "/", - element: , - errorElement: ( -

- Sorry, an unexpected error has occurred.{" "} - Go Back -

- ), - children: [ - { - index: true, - element: , - }, - { - path: "home", - element: ( - - - - ), - }, - { - path: "friends", - element: ( - - - - ), - }, - { - path: "settings", - element: ( - - - - ), - }, - { - path: "settings/profile", - element: ( - - - - ), - }, - { - path: "settings/contact", - element: ( - - - - ), - }, - { - path: "settings/aboutUs", - element: ( - - - - ), - }, - { - path: "settings/disclaimer", - element: ( - - - - ), - }, - { - path: "settings/delete", - element: ( - - - - ), - }, - { - path: "chat", - element: ( - - - - ), - }, - { - path: "chat/:friendId", - element: ( - - - - ), - }, - { - path: "edit/profile", - element: ( - - - - ), - }, - { - path: "edit/courses", - element: ( - - - - ), - }, - ], - }, - { - path: "/login", - element: ( - - - - ), - }, - { - path: "/signup", - element: , - }, - { - path: "/faq", - element: , - }, - { - path: "/tutorial", - element: , - }, - { - path: "*", - element: ( -

- お探しのリンクは見つかりませんでした。 戻る -

- ), - }, - ]); - - return ( - <> - - - - - - - - ); -} - -const theme = createTheme({ - palette: { - primary: { - main: "#039BE5", - }, - secondary: { - main: "#E9F8FF", - }, - }, -}); diff --git a/web/src/api/course.hook.ts b/web/src/api/course.hook.ts deleted file mode 100644 index f042b631..00000000 --- a/web/src/api/course.hook.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { z } from "zod"; -import type { Course } from "../common/types"; -import { CourseSchema } from "../common/zod/schemas"; -import { type Hook, useSWR } from "../hooks/useSWR"; -import { getMyCourses } from "./course"; - -const CourseListSchema = z.array(CourseSchema); -export function useMyCourses(): Hook { - return useSWR("useMyCourses", getMyCourses, CourseListSchema); -} diff --git a/web/src/firebase/config.ts b/web/src/firebase/config.ts deleted file mode 100644 index 3ba5507b..00000000 --- a/web/src/firebase/config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { initializeApp } from "firebase/app"; -import { getAuth } from "firebase/auth"; - -export const firebaseConfig = { - apiKey: import.meta.env.VITE_FIREBASE_API_KEY, - authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, - projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, - // storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, - // messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, - appId: import.meta.env.VITE_FIREBASE_APP_ID, - measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID, -}; - -const app = initializeApp(firebaseConfig); -const auth = getAuth(app); - -export { app, auth }; diff --git a/web/src/main.tsx b/web/src/main.tsx deleted file mode 100644 index 56bfdaf8..00000000 --- a/web/src/main.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.css"; -import "@fontsource/roboto/300.css"; -import "@fontsource/roboto/400.css"; -import "@fontsource/roboto/500.css"; -import "@fontsource/roboto/700.css"; -import { CssBaseline } from "@mui/material"; -import App from "./App"; -import BanLandscape from "./components/BanLandscape"; -import { AlertProvider } from "./components/common/alert/AlertProvider"; -import { ModalProvider } from "./components/common/modal/ModalProvider"; -import AuthProvider from "./firebase/auth/AuthProvider"; - -const root = document.getElementById("root"); -if (!root) throw "couldn't find root!"; -ReactDOM.createRoot(root).render( - - - - - - - - - - - , -); diff --git a/web/src/routes/editCourses.tsx b/web/src/routes/editCourses.tsx deleted file mode 100644 index 44e2eed9..00000000 --- a/web/src/routes/editCourses.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { Box, Button, Typography } from "@mui/material"; -import { useNavigate } from "react-router-dom"; -import { useAboutMe } from "../api/user"; -import FullScreenCircularProgress from "../components/common/FullScreenCircularProgress"; -import EditableCoursesTable from "../components/course/EditableCoursesTable"; - -export default function EditCourses() { - const navigate = useNavigate(); - - const { state } = useAboutMe(); - const data = state.data; - const loading = state.current === "loading"; - const error = state.current === "error" ? state.error : null; - - function handleBack() { - navigate("/settings/profile"); - } - - function handleGoToProfile() { - navigate("/edit/profile"); - } - - return ( - - - 授業編集 - - {loading ? ( - - ) : error ? ( -

Error: {error.message}

- ) : data ? ( - <> - - - ) : ( -

データがありません。

- )} - - - - - -
- ); -} diff --git a/web/src/routes/editProfile.tsx b/web/src/routes/editProfile.tsx deleted file mode 100644 index 0a8265bb..00000000 --- a/web/src/routes/editProfile.tsx +++ /dev/null @@ -1,547 +0,0 @@ -import EditIcon from "@mui/icons-material/Edit"; -import { - Box, - Button, - FormControl, - IconButton, - InputLabel, - MenuItem, - Select, - TextField, - Typography, -} from "@mui/material"; -import type { SelectChangeEvent } from "@mui/material"; -import { enqueueSnackbar } from "notistack"; -import { useEffect, useState } from "react"; -import { useNavigate } from "react-router-dom"; -import { update, useAboutMe } from "../api/user"; -import type { UpdateUser } from "../common/types"; -import { UpdateUserSchema } from "../common/zod/schemas"; -import FullScreenCircularProgress from "../components/common/FullScreenCircularProgress"; -import { useAlert } from "../components/common/alert/AlertProvider"; -import PhotoModal from "../components/config/PhotoModal"; -import { PhotoPreviewButton } from "../components/config/PhotoPreview"; -import UserAvatar from "../components/human/avatar"; -import { facultiesAndDepartments } from "./registration/data"; - -export default function EditProfile() { - const navigate = useNavigate(); - const { showAlert } = useAlert(); - const { state } = useAboutMe(); - const data = state.data; - const error = state.current === "error" ? state.error : null; - const loading = state.current === "loading"; - - const [name, setName] = useState(""); - const [gender, setGender] = useState(""); - const [grade, setGrade] = useState(""); - const [faculty, setFaculty] = useState(""); - const [department, setDepartment] = useState(""); - const [intro, setIntro] = useState(""); - const [pictureUrl, setPictureUrl] = useState(""); - const [tmpName, setTmpName] = useState(""); - const [tmpGender, setTmpGender] = useState(""); - const [tmpGrade, setTmpGrade] = useState(""); - const [tmpFaculty, setTmpFaculty] = useState(""); - const [tmpDepartment, setTmpDepartment] = useState(""); - const [tmpIntro, setTmpIntro] = useState(""); - - const [isEditingName, setIsEditingName] = useState(false); - const [isEditingGender, setIsEditingGender] = useState(false); - const [isEditingGrade, setIsEditingGrade] = useState(false); - const [isEditingFaculty, setIsEditingFaculty] = useState(false); - const [isEditingDepartment, setIsEditingDepartment] = useState(false); - const [isEditingIntro, setIsEditingIntro] = useState(false); - - const [errorMessage, setErrorMessage] = useState(""); - - const [nameError, setNameError] = useState(""); - const [genderError, setGenderError] = useState(""); - const [gradeError, setGradeError] = useState(""); - const [facultyError, setFacultyError] = useState(""); - const [departmentError, setDepartmentError] = useState(""); - const [introError, setIntroError] = useState(""); - - useEffect(() => { - if (data) { - setName(data.name); - setGender(data.gender); - setGrade(data.grade); - setFaculty(data.faculty); - setDepartment(data.department); - setIntro(data.intro); - setPictureUrl(data.pictureUrl); - setTmpName(data.name); - setTmpGender(data.gender); - setTmpGrade(data.grade); - setTmpFaculty(data.faculty); - setTmpDepartment(data.department); - setTmpIntro(data.intro); - } - }, [data]); - - function afterPhotoUpload(result: string) { - try { - setPictureUrl(result); - handleSave({ pictureUrl: result }); - } catch (err) { - console.error(err); - // probably a network error - onPhotoError(new Error("画像の更新に失敗しました")); - } - } - - function onPhotoError(err: Error) { - enqueueSnackbar({ - message: err?.message ?? "画像の更新に失敗しました", - }); - } - const [open, setOpen] = useState(false); - - function hasUnsavedChangesOrErrors() { - return ( - isEditingName || - isEditingGender || - isEditingGrade || - isEditingFaculty || - isEditingDepartment || - isEditingIntro || - errorMessage || - nameError || - genderError || - gradeError || - facultyError || - departmentError || - introError - ); - } - - function handleGoToCourses() { - if (hasUnsavedChangesOrErrors()) { - showAlert({ - AlertMessage: "まだ編集中のフィールド、もしくはエラーがあります", - subAlertMessage: "本当にページを移動しますか?変更は破棄されます", - yesMessage: "移動", - clickYes: () => { - navigate("/edit/courses"); - }, - }); - } else { - navigate("/edit/courses"); - } - } - - function handleBack() { - if (hasUnsavedChangesOrErrors()) { - showAlert({ - AlertMessage: "編集中のフィールド、もしくはエラーがあります。", - subAlertMessage: "本当にページを移動しますか?変更は破棄されます", - yesMessage: "移動", - clickYes: () => { - navigate("/settings/profile"); - }, - }); - } else { - navigate("/settings/profile"); - } - } - - async function handleSave(input: Partial) { - setErrorMessage(""); - setNameError(""); - setGenderError(""); - setGradeError(""); - setFacultyError(""); - setDepartmentError(""); - setIntroError(""); - const data: UpdateUser = { - name: (input.name ?? name).trim(), - gender: input.gender ?? gender, - grade: input.grade ?? grade, - faculty: input.faculty ?? faculty, - department: input.department ?? department, - intro: (input.intro ?? intro).trim(), - pictureUrl: input.pictureUrl ?? pictureUrl, - }; - const result = UpdateUserSchema.safeParse(data); - if (!result.success) { - result.error.errors.map((err) => { - switch (err.path[0]) { - case "name": - setNameError(err.message); - break; - case "gender": - setGenderError(err.message); - break; - case "grade": - setGradeError(err.message); - break; - case "faculty": - setFacultyError(err.message); - break; - case "department": - setDepartmentError(err.message); - break; - case "intro": - setIntroError(err.message); - break; - default: - setErrorMessage("入力に誤りがあります"); - } - }); - return; - } - await update(data); - } - - function handleEdit(setter: React.Dispatch>) { - setTmpName(name); - setTmpGender(gender); - setTmpGrade(grade); - setTmpFaculty(faculty); - setTmpDepartment(department); - setTmpIntro(intro); - setIsEditingName(false); - setIsEditingGender(false); - setIsEditingGrade(false); - setIsEditingFaculty(false); - setIsEditingDepartment(false); - setIsEditingIntro(false); - setter(true); - } - - const handleFacultyChange = (event: SelectChangeEvent) => { - setTmpFaculty(event.target.value); - }; - - const handleDepartmentChange = (event: SelectChangeEvent) => { - setTmpDepartment(event.target.value); - }; - - return ( - - {loading ? ( - - ) : error ? ( -

Error: {error.message}

- ) : data ? ( - - - プロフィール編集 - - - - setTmpName(e.target.value)} - label="名前" - disabled={!isEditingName} - fullWidth - error={!!nameError} - helperText={nameError} - autoComplete="off" - /> - { - if (isEditingName) { - setName(tmpName); - handleSave({ name: tmpName }); - setIsEditingName(false); - } else { - handleEdit(setIsEditingName); - } - }} - > - {isEditingName ? ( - 保存 - ) : ( - - )} - - - - - - - 性別 - - {genderError && ( - - {genderError} - - )} - { - if (isEditingGender) { - setGender(tmpGender); - handleSave({ gender: tmpGender }); - setIsEditingGender(false); - } else { - handleEdit(setIsEditingGender); - } - }} - > - {isEditingGender ? ( - 保存 - ) : ( - - )} - - - - - - - 学年 - - {gradeError && ( - - {gradeError} - - )} - { - if (isEditingGrade) { - setGrade(tmpGrade); - handleSave({ grade: tmpGrade }); - setIsEditingGrade(false); - } else { - handleEdit(setIsEditingGrade); - } - }} - > - {isEditingGrade ? ( - 保存 - ) : ( - - )} - - - - - - - 学部 - - {facultyError && ( - - {facultyError} - - )} - - { - if (isEditingFaculty) { - setDepartment(""); - setFaculty(tmpFaculty); - handleSave({ faculty: tmpFaculty, department: "" }); - setIsEditingFaculty(false); - } else { - handleEdit(setIsEditingFaculty); - } - }} - > - {isEditingFaculty ? ( - 保存 - ) : ( - - )} - - - - - - - 学科 - - {departmentError && ( - - {departmentError} - - )} - - { - if (isEditingDepartment) { - setDepartment(tmpDepartment); - handleSave({ department: tmpDepartment }); - setIsEditingDepartment(false); - } else { - handleEdit(setIsEditingDepartment); - } - }} - > - {isEditingDepartment ? ( - 保存 - ) : ( - - )} - - - - - - - setTmpIntro(e.target.value)} - label="自己紹介" - disabled={!isEditingIntro} - fullWidth - autoComplete="off" - error={!!introError} // エラースタイル適用 - helperText={introError} // エラーメッセージを表示 - /> - - { - if (isEditingIntro) { - setIntro(tmpIntro); - handleSave({ intro: tmpIntro }); - setIsEditingIntro(false); - } else { - handleEdit(setIsEditingIntro); - } - }} - > - {isEditingIntro ? ( - 保存 - ) : ( - - )} - - - - {errorMessage && ( - - {errorMessage} - - )} - -
-
- - プロフィール画像 - -
-
-
- -
- setOpen(true)} - /> - setOpen(false)} - afterUpload={afterPhotoUpload} - onError={onPhotoError} - /> -
-
- - - - - -
- ) : ( -

データがありません。

- )} -
- ); -} diff --git a/web/src/routes/root.tsx b/web/src/routes/root.tsx deleted file mode 100644 index 863c9e57..00000000 --- a/web/src/routes/root.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import ChatIcon from "@mui/icons-material/Chat"; -import HomeIcon from "@mui/icons-material/Home"; -import PeopleIcon from "@mui/icons-material/People"; -import SettingsIcon from "@mui/icons-material/Settings"; -import { BottomNavigation, BottomNavigationAction, Box } from "@mui/material"; -import { useEffect, useState } from "react"; -import { Link, Outlet, useLocation } from "react-router-dom"; -import Header from "../components/Header"; - -const labels = [ - "ホーム/Home", - "フレンド/Friends", - "チャット/Chat", - "設定/Settings", - "設定/Settings", - "設定/Settings", - "設定/Settings", - "設定/Settings", - "編集/Edit", - "編集/Edit", -]; -const paths = [ - "/home", - "/friends", - "/chat", - "/settings", - "/settings/profile", - "/settings/contact", - "/settings/aboutUs", - "/settings/disclaim", - "/settings/delete", - "/edit/profile", - "/edit/courses", -]; - -export default function Root() { - const location = useLocation(); - const [tabIndex, setTabIndex] = useState(0); - - //TODO: この処理の遅さが気になる場合は、Header は 各コンポーネントから呼ぶことにしてもよい - useEffect(() => { - const currentPath = `/${location.pathname.split("/")[1]}`; - const currentIndex = paths.indexOf(currentPath); - if (currentIndex !== -1) { - setTabIndex(currentIndex); - } - }, [location.pathname]); - - return ( - <> -
- - - - - { - setTabIndex(newValue); - }} - sx={{ - width: "100%", - bottom: 0, - borderTop: "1px solid", - borderColor: "primary.main", - }} - > - - } - /> - - } - /> - - } - /> - - } - /> - - - - ); -} diff --git a/web/src/routes/tabs/chat.tsx b/web/src/routes/tabs/chat.tsx deleted file mode 100644 index db65d266..00000000 --- a/web/src/routes/tabs/chat.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Typography } from "@mui/material"; -import { useRoomsOverview } from "../../api/chat/hooks"; -import RoomList from "../../components/chat/RoomList"; -import FullScreenCircularProgress from "../../components/common/FullScreenCircularProgress"; - -export default function Chat() { - const { state } = useRoomsOverview(); - - if (state.current === "loading") { - return ; - } - - if (state.current === "error") { - return Error: {state.error.message}; - } - - return ; -} diff --git a/web/src/routes/tabs/settings/aboutUs.tsx b/web/src/routes/tabs/settings/aboutUs.tsx deleted file mode 100644 index ba1ef143..00000000 --- a/web/src/routes/tabs/settings/aboutUs.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { GitHub, Language } from "@mui/icons-material"; -import XIcon from "@mui/icons-material/X"; -import { Box, Link, Typography } from "@mui/material"; -import TopNavigation from "./components/TopNavigation"; - -export default function AboutUs() { - return ( - - - - - CourseMateについて - - - 大学の授業を受けている際に、一緒に受ける友達がいなくて困ったことはありませんか? - 「友達がいないから授業をサボるようになってしまった」、「過去問を共有してくれる人がいない」 - などの東大生の悩みを解決したいと思い、CourseMateは開発されました。CourseMateを使うことで - みなさまの大学生活がより良くなれば幸いです。ぜひ知り合いの方々に広めていただければと思っています。 - - - - - CourseMate の GitHub - - - CourseMate の X (旧 Twitter) - - - - - ut.code();について - - - ut.code();は、2019年設立の東京大学のソフトウェアエンジニアリングコミュニティです。 - 「学習」、「交流」、「開発」の三つを活動の軸としており、さまざまなアプリを開発しています。 - - - - - ut.code(); の ウェブサイト - - - - ut.code(); の GitHub - - - - ut.code(); の X (旧 Twitter) - - - - - ); -} diff --git a/web/src/routes/tabs/settings/contact.tsx b/web/src/routes/tabs/settings/contact.tsx deleted file mode 100644 index 1a80c31c..00000000 --- a/web/src/routes/tabs/settings/contact.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Box, Button, Typography } from "@mui/material"; -import TopNavigation from "./components/TopNavigation"; - -export default function Contact() { - return ( - - - - - ご利用いただきありがとうございます。サービスに関するご意見やバグ報告がございましたら、以下のリンクからお問い合わせください。皆様のフィードバックは、サービスの改善に役立てさせていただきます。 - - - - - - ); -} diff --git a/web/src/routes/tabs/settings/disclaimer.tsx b/web/src/routes/tabs/settings/disclaimer.tsx deleted file mode 100644 index 0e2c2d59..00000000 --- a/web/src/routes/tabs/settings/disclaimer.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Box, Typography } from "@mui/material"; -import TopNavigation from "./components/TopNavigation"; - -export default function Disclaimer() { - return ( - - - - - 本サービスはut.code();によって運営されており、東京大学は運営に関与しておりません。本サービスは東大生のみを対象としており、ECCSアカウントによるログインが必須です。 - - - - 本サービスの機能の利用に伴ういかなるトラブルや損害について、ut.code();は一切の責任を負いかねます。利用者の自己責任においてご利用ください。 - - - - 本サービスで収集した個人情報は、サインインおよびサービス提供の目的にのみ使用され、他の目的には使用されません。 - - - - 本サービスを通じて他の利用者と接触した際のトラブルや、マッチングを通じて生じた問題等に関しても、ut.code();は責任を負いません。ご理解とご協力をお願いいたします。 - - - - ); -} diff --git a/web/src/routes/tabs/settings/profile.tsx b/web/src/routes/tabs/settings/profile.tsx deleted file mode 100644 index b0c78a5c..00000000 --- a/web/src/routes/tabs/settings/profile.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import EditIcon from "@mui/icons-material/Edit"; -import { Box, Button, Typography } from "@mui/material"; -import { useState } from "react"; -import { useNavigate } from "react-router-dom"; -import { useAboutMe } from "../../../api/user"; -("../../../api/user"); -import { Card } from "../../../components/Card"; -import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; -import TopNavigation from "./components/TopNavigation"; - -export default function Profile() { - const { state } = useAboutMe(); - const data = state.data; - const navigate = useNavigate(); - const error = state.current === "error" ? state.error : null; - const loading = state.current === "loading"; - const [back, setBack] = useState(false); - - if (loading) { - return ; - } - - if (error) { - return Error: {error.message}; - } - - if (!data) { - return データがありません。; - } - - return ( - - - - - - - setBack(back)} /> - - - ); -} diff --git a/web/src/routes/tabs/settings/settings.tsx b/web/src/routes/tabs/settings/settings.tsx deleted file mode 100644 index e214b73a..00000000 --- a/web/src/routes/tabs/settings/settings.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { - Box, - Divider, - List, - ListItemButton, - ListItemText, -} from "@mui/material"; -import { useNavigate } from "react-router-dom"; -import LogOutButton from "../../../components/LogOutButton"; - -export default function Settings() { - console.log("Settings: rendering..."); - const navigate = useNavigate(); - - return ( - - - navigate("/settings/profile")}> - - - - navigate("/tutorial")}> - - - - navigate("/settings/contact")}> - - - - navigate("/faq")}> - - - - navigate("/settings/aboutUs")}> - - - - navigate("/settings/disclaimer")}> - - - - navigate("/settings/delete")}> - - - - - - - - ); -} diff --git a/web/src/vite-env.d.ts b/web/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2..00000000 --- a/web/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/web/tsconfig.json b/web/tsconfig.json index 195e164a..a6c3386f 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -4,22 +4,35 @@ "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", + "esModuleInterop": true, "skipLibCheck": true, "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx", + "jsx": "preserve", "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "incremental": true, + "plugins": [ + { + "name": "next" + } + ] }, - "include": ["src"], - "references": [ - { - "path": "./tsconfig.node.json" - } - ] + "include": [ + "./src", + "./dist/types/**/*.ts", + "./next-env.d.ts", + ".next/types/**/*.ts", + "common", + "app", + "components" + ], + "exclude": ["./node_modules"] } diff --git a/web/tsconfig.node.json b/web/tsconfig.node.json deleted file mode 100644 index 97ede7ee..00000000 --- a/web/tsconfig.node.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true, - "strict": true - }, - "include": ["vite.config.ts"] -} diff --git a/web/vite.config.ts b/web/vite.config.ts deleted file mode 100644 index 1ff0da0a..00000000 --- a/web/vite.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import react from "@vitejs/plugin-react"; -import { defineConfig } from "vite"; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], -}); From 021ea3e101383ad459859a4f18c36dc301d88957 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:29:56 +0900 Subject: [PATCH 016/131] fix web build (#514) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 Next のビルドエラーの解消 と path alias の作成 (こっちがメイン) - [x] 改めてセルフレビューしたか - [x] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- .github/workflows/ci.yml | 2 +- Makefile | 2 -- web/api/chat/chat.ts | 8 +++---- web/api/chat/hooks.ts | 8 +++---- web/api/course.hook.ts | 6 +++--- web/api/course.ts | 4 ++-- web/api/internal/endpoints.ts | 4 ++-- web/api/internal/fetch-func.ts | 4 ++-- web/api/match.ts | 2 +- web/api/request.ts | 2 +- web/api/user.ts | 12 +++++------ web/app/chat/layout.tsx | 4 ++-- web/app/chat/page.tsx | 10 ++++----- web/app/edit/courses/page.tsx | 8 +++---- web/app/edit/layout.tsx | 2 +- web/app/edit/profile/page.tsx | 20 +++++++++--------- web/app/faq/page.tsx | 2 +- web/app/friends/layout.tsx | 4 ++-- web/app/friends/page.tsx | 6 +++--- web/app/home/layout.tsx | 4 ++-- web/app/home/page.tsx | 12 +++++------ web/app/layout.tsx | 8 +++---- web/app/login/page.tsx | 16 +++++++------- web/app/settings/aboutUs/page.tsx | 4 ++-- web/app/settings/contact/page.tsx | 4 ++-- web/app/settings/delete/page.tsx | 4 ++-- web/app/settings/disclaimer/page.tsx | 4 ++-- web/app/settings/layout.tsx | 4 ++-- web/app/settings/page.tsx | 4 ++-- web/app/settings/profile/page.tsx | 10 ++++----- web/app/signup/functions.ts | 6 +++--- web/app/signup/page.tsx | 4 ++-- web/app/signup/steps/step1_profile.tsx | 8 +++---- web/app/signup/steps/step2_img.tsx | 8 +++---- web/app/signup/steps/step3_confirmation.tsx | 6 +++--- web/app/signup/steps/step4_course.tsx | 6 +++--- web/app/tutorial/page.tsx | 17 +++++++-------- web/bun.lockb | Bin 124783 -> 124783 bytes web/components/Card.tsx | 2 +- web/components/DraggableCard.tsx | 2 +- web/components/LogOutButton.tsx | 2 +- web/components/chat/MessageInput.tsx | 4 ++-- web/components/chat/Room.tsx | 2 +- web/components/chat/RoomHeader.tsx | 2 +- web/components/chat/RoomList.tsx | 2 +- web/components/chat/RoomWindow.tsx | 14 ++++++------ web/components/common/NavigateByAuthState.tsx | 2 +- web/components/common/modal/ModalProvider.tsx | 4 ++-- web/components/config/PhotoModal.tsx | 2 +- web/components/config/PhotoPreview.tsx | 2 +- .../course/EditableCoursesTable/index.tsx | 6 +++--- .../course/NonEditableCoursesTable/index.tsx | 6 +++--- .../components/CourseDeleteConfirmDialog.tsx | 4 ++-- .../CourseRegisterConfirmDialog.tsx | 4 ++-- .../components/CoursesTableCore/index.tsx | 4 ++-- .../course/components/SelectCourseDialog.tsx | 6 +++--- web/components/match/matching.tsx | 4 ++-- web/components/match/myRequests.tsx | 4 ++-- web/components/match/othersRequests.tsx | 4 ++-- web/firebase/auth/AuthProvider.tsx | 4 ++-- web/firebase/auth/lib.ts | 2 +- web/hooks/useData.ts | 4 ++-- web/tsconfig.json | 6 +++++- 63 files changed, 169 insertions(+), 168 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f40718eb..106a74bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,7 @@ jobs: - uses: actions/setup-node@v3 - uses: oven-sh/setup-bun@v2 - run: make prepare-deploy-web - - run: test `ls web/dist | wc -l` != 0 + - run: test `ls web/out | wc -l` != 0 deploy-test-server: name: Deploy Test (server) diff --git a/Makefile b/Makefile index d70acbf8..bee23e3a 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,6 @@ setup: if [ ! `command -v bun` ]; then echo 'ERR: Bun is required!'; exit 1; fi make sync bunx husky - cd web; if [ ! -f .env ]; then cp ./.env.sample ./.env ; fi - cd server; if [ ! -f .env.dev ]; then cp ./.env.sample ./.env.dev ; fi @echo "auto setup is done. now do:" @echo "- edit server/.env.dev" @echo "- edit web/.env" diff --git a/web/api/chat/chat.ts b/web/api/chat/chat.ts index a574b973..c8d5dc4b 100644 --- a/web/api/chat/chat.ts +++ b/web/api/chat/chat.ts @@ -9,13 +9,13 @@ import type { SharedRoom, UpdateRoom, UserID, -} from "../../../common/types"; -import { ErrUnauthorized, credFetch } from "../../firebase/auth/lib"; +} from "~/common/types"; +import { ErrUnauthorized, credFetch } from "~/firebase/auth/lib"; import endpoints from "../internal/endpoints"; /* TODO -import { UserID } from "../common/types"; -import type { User } from "../common/types"; +import { UserID } from "~/common/types"; +import type { User } from "~/common/types"; */ //// DM グループチャット 共通//// diff --git a/web/api/chat/hooks.ts b/web/api/chat/hooks.ts index d835cb9c..009eebed 100644 --- a/web/api/chat/hooks.ts +++ b/web/api/chat/hooks.ts @@ -3,11 +3,11 @@ import { useCallback } from "react"; import { z } from "zod"; // import { useCallback, useEffect, useState } from "react"; -import type { Message, RoomOverview } from "../../../common/types"; -import { MessageSchema, RoomOverviewSchema } from "../../../common/zod/schemas"; -import { type Hook, useCustomizedSWR } from "../../hooks/useCustomizedSWR"; +import type { Message, RoomOverview } from "~/common/types"; +import { MessageSchema, RoomOverviewSchema } from "~/common/zod/schemas"; +import { type Hook, useCustomizedSWR } from "~/hooks/useCustomizedSWR"; import type { UserID } from "../internal/endpoints"; -// import type { Hook } from "../share/types"; +// import type { Hook } from "~/share/types"; import * as chat from "./chat"; const OverviewListSchema = z.array(RoomOverviewSchema); diff --git a/web/api/course.hook.ts b/web/api/course.hook.ts index 509c2169..086d8b1c 100644 --- a/web/api/course.hook.ts +++ b/web/api/course.hook.ts @@ -1,7 +1,7 @@ import { z } from "zod"; -import type { Course } from "../../common/types"; -import { CourseSchema } from "../../common/zod/schemas"; -import { type Hook, useCustomizedSWR } from "../hooks/useCustomizedSWR"; +import type { Course } from "~/common/types"; +import { CourseSchema } from "~/common/zod/schemas"; +import { type Hook, useCustomizedSWR } from "~/hooks/useCustomizedSWR"; import { getMyCourses } from "./course"; const CourseListSchema = z.array(CourseSchema); diff --git a/web/api/course.ts b/web/api/course.ts index 17bba9bf..8a09b707 100644 --- a/web/api/course.ts +++ b/web/api/course.ts @@ -1,5 +1,5 @@ -import type { Course, CourseID, Day } from "../../common/types"; -import { credFetch } from "../firebase/auth/lib"; +import type { Course, CourseID, Day } from "~/common/types"; +import { credFetch } from "~/firebase/auth/lib"; import endpoints from "./internal/endpoints"; // TODO: migrate to safe functions diff --git a/web/api/internal/endpoints.ts b/web/api/internal/endpoints.ts index 53fc770a..1aa12839 100644 --- a/web/api/internal/endpoints.ts +++ b/web/api/internal/endpoints.ts @@ -1,5 +1,5 @@ -import type { CourseID, Day, GUID } from "../../../common/types"; -import type { MessageID, ShareRoomID } from "../../../common/types"; +import type { CourseID, Day, GUID } from "~/common/types"; +import type { MessageID, ShareRoomID } from "~/common/types"; export const origin: string | null = process.env.NEXT_PUBLIC_API_ENDPOINT ?? ""; if (!origin) throw new Error("process.env.NEXT_PUBLIC_API_ENDPOINT not found!"); diff --git a/web/api/internal/fetch-func.ts b/web/api/internal/fetch-func.ts index 2ef45789..a2dc1aee 100644 --- a/web/api/internal/fetch-func.ts +++ b/web/api/internal/fetch-func.ts @@ -1,4 +1,4 @@ -import { Err, Ok, type Result } from "../../../common/lib/result"; +import { Err, Ok, type Result } from "~/common/lib/result"; export async function safeFetch( path: string, @@ -15,7 +15,7 @@ export async function safeFetch( } } -import { getIdToken } from "../../firebase/auth/lib"; +import { getIdToken } from "~/firebase/auth/lib"; import endpoints from "./endpoints"; type URL = string; diff --git a/web/api/match.ts b/web/api/match.ts index f9496afe..0751c11f 100644 --- a/web/api/match.ts +++ b/web/api/match.ts @@ -1,4 +1,4 @@ -import { credFetch } from "../firebase/auth/lib"; +import { credFetch } from "~/firebase/auth/lib"; import endpoints from "./internal/endpoints"; import type { UserID } from "./internal/endpoints"; diff --git a/web/api/request.ts b/web/api/request.ts index efb20dcb..aaecadad 100644 --- a/web/api/request.ts +++ b/web/api/request.ts @@ -1,4 +1,4 @@ -import { credFetch } from "../firebase/auth/lib"; +import { credFetch } from "~/firebase/auth/lib"; import endpoints, { type UserID } from "./internal/endpoints"; //指定したユーザにリクエストを送る diff --git a/web/api/user.ts b/web/api/user.ts index 8f61ff2a..7ad033b0 100644 --- a/web/api/user.ts +++ b/web/api/user.ts @@ -1,10 +1,10 @@ import { z } from "zod"; -import type { GUID, UpdateUser, User, UserID } from "../../common/types.ts"; -import { parseUser } from "../../common/zod/methods.ts"; -import { UserIDSchema, UserSchema } from "../../common/zod/schemas.ts"; -import { credFetch } from "../firebase/auth/lib.ts"; -import { type Hook, useCustomizedSWR } from "../hooks/useCustomizedSWR.ts"; -import { useAuthorizedData } from "../hooks/useData.ts"; +import type { GUID, UpdateUser, User, UserID } from "~/common/types.ts"; +import { parseUser } from "~/common/zod/methods.ts"; +import { UserIDSchema, UserSchema } from "~/common/zod/schemas.ts"; +import { credFetch } from "~/firebase/auth/lib.ts"; +import { type Hook, useCustomizedSWR } from "~/hooks/useCustomizedSWR.ts"; +import { useAuthorizedData } from "~/hooks/useData.ts"; import endpoints from "./internal/endpoints.ts"; import type { Hook as UseHook } from "./share/types.ts"; diff --git a/web/app/chat/layout.tsx b/web/app/chat/layout.tsx index 6925d366..49f79339 100644 --- a/web/app/chat/layout.tsx +++ b/web/app/chat/layout.tsx @@ -1,6 +1,6 @@ import { Box } from "@mui/material"; -import BottomBar from "../../components/BottomBar"; -import Header from "../../components/Header"; +import BottomBar from "~/components/BottomBar"; +import Header from "~/components/Header"; export default function ChatPageLayout({ children, diff --git a/web/app/chat/page.tsx b/web/app/chat/page.tsx index e24b11ce..09c0b7c7 100644 --- a/web/app/chat/page.tsx +++ b/web/app/chat/page.tsx @@ -3,11 +3,11 @@ import { Typography } from "@mui/material"; import { useSearchParams } from "next/navigation"; import { Suspense } from "react"; -import { useRoomsOverview } from "../../api/chat/hooks"; -import RoomList from "../../components/chat/RoomList"; -import { RoomWindow } from "../../components/chat/RoomWindow"; -import FullScreenCircularProgress from "../../components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; +import { useRoomsOverview } from "~/api/chat/hooks"; +import RoomList from "~/components/chat/RoomList"; +import { RoomWindow } from "~/components/chat/RoomWindow"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; function ChatListContent() { const searchParams = useSearchParams(); diff --git a/web/app/edit/courses/page.tsx b/web/app/edit/courses/page.tsx index 8fc3ec09..b4b9b9b2 100644 --- a/web/app/edit/courses/page.tsx +++ b/web/app/edit/courses/page.tsx @@ -2,10 +2,10 @@ import { Box, Button, Typography } from "@mui/material"; import Link from "next/link"; -import { useAboutMe } from "../../../api/user"; -import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; -import EditableCoursesTable from "../../../components/course/EditableCoursesTable"; +import { useAboutMe } from "~/api/user"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import EditableCoursesTable from "~/components/course/EditableCoursesTable"; export default function EditCourses() { const { state } = useAboutMe(); diff --git a/web/app/edit/layout.tsx b/web/app/edit/layout.tsx index adb4ac22..7a6df71e 100644 --- a/web/app/edit/layout.tsx +++ b/web/app/edit/layout.tsx @@ -1,5 +1,5 @@ import { Box } from "@mui/material"; -import Header from "../../components/Header"; +import Header from "~/components/Header"; export default function EditPageLayout({ children, diff --git a/web/app/edit/profile/page.tsx b/web/app/edit/profile/page.tsx index 1f259962..2e0f34e1 100644 --- a/web/app/edit/profile/page.tsx +++ b/web/app/edit/profile/page.tsx @@ -16,16 +16,16 @@ import type { SelectChangeEvent } from "@mui/material"; import { useRouter } from "next/navigation"; import { enqueueSnackbar } from "notistack"; import { useEffect, useState } from "react"; -import { update, useAboutMe } from "../../../api/user"; -import type { UpdateUser } from "../../../common/types"; -import { UpdateUserSchema } from "../../../common/zod/schemas"; -import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; -import { useAlert } from "../../../components/common/alert/AlertProvider"; -import PhotoModal from "../../../components/config/PhotoModal"; -import { PhotoPreviewButton } from "../../../components/config/PhotoPreview"; -import UserAvatar from "../../../components/human/avatar"; -import { facultiesAndDepartments } from "../../signup/data"; +import { update, useAboutMe } from "~/api/user"; +import { facultiesAndDepartments } from "~/app/signup/data"; +import type { UpdateUser } from "~/common/types"; +import { UpdateUserSchema } from "~/common/zod/schemas"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import { useAlert } from "~/components/common/alert/AlertProvider"; +import PhotoModal from "~/components/config/PhotoModal"; +import { PhotoPreviewButton } from "~/components/config/PhotoPreview"; +import UserAvatar from "~/components/human/avatar"; export default function EditProfile() { const router = useRouter(); diff --git a/web/app/faq/page.tsx b/web/app/faq/page.tsx index 459fb382..16184255 100644 --- a/web/app/faq/page.tsx +++ b/web/app/faq/page.tsx @@ -3,7 +3,7 @@ import { ArrowBack } from "@mui/icons-material"; import { Box, IconButton, Typography } from "@mui/material"; import { useRouter } from "next/navigation"; -import Header from "../../components/Header"; +import Header from "~/components/Header"; export default function FAQ() { const router = useRouter(); diff --git a/web/app/friends/layout.tsx b/web/app/friends/layout.tsx index a0cdb541..a471c311 100644 --- a/web/app/friends/layout.tsx +++ b/web/app/friends/layout.tsx @@ -1,6 +1,6 @@ import { Box } from "@mui/material"; -import BottomBar from "../../components/BottomBar"; -import Header from "../../components/Header"; +import BottomBar from "~/components/BottomBar"; +import Header from "~/components/Header"; export default function FriendsPageLayout({ children, diff --git a/web/app/friends/page.tsx b/web/app/friends/page.tsx index 3e254f0c..18817fcb 100644 --- a/web/app/friends/page.tsx +++ b/web/app/friends/page.tsx @@ -2,9 +2,9 @@ import { Box, Tab, Tabs } from "@mui/material"; import { useState } from "react"; -import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; -import Matchings from "../../components/match/matching"; -import Requests from "../../components/match/requests"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import Matchings from "~/components/match/matching"; +import Requests from "~/components/match/requests"; export default function Friends() { const [open, setOpen] = useState(0); diff --git a/web/app/home/layout.tsx b/web/app/home/layout.tsx index 7b43b1af..5a9eb8f3 100644 --- a/web/app/home/layout.tsx +++ b/web/app/home/layout.tsx @@ -1,6 +1,6 @@ import { Box } from "@mui/material"; -import BottomBar from "../../components/BottomBar"; -import Header from "../../components/Header"; +import BottomBar from "~/components/BottomBar"; +import Header from "~/components/Header"; export default function HomePageLayout({ children, diff --git a/web/app/home/page.tsx b/web/app/home/page.tsx index a868a7fd..be1bc198 100644 --- a/web/app/home/page.tsx +++ b/web/app/home/page.tsx @@ -4,15 +4,15 @@ import CloseIcon from "@mui/icons-material/Close"; import FavoriteIcon from "@mui/icons-material/Favorite"; import { Box, Button } from "@mui/material"; import { useCallback, useEffect, useState } from "react"; -import request from "../../api/request"; +import request from "~/api/request"; import shadows from "@mui/material/styles/shadows"; import { motion, useAnimation } from "framer-motion"; -import { useMyID, useRecommended } from "../../api/user"; -import { Card } from "../../components/Card"; -import { DraggableCard } from "../../components/DraggableCard"; -import FullScreenCircularProgress from "../../components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; +import { useMyID, useRecommended } from "~/api/user"; +import { Card } from "~/components/Card"; +import { DraggableCard } from "~/components/DraggableCard"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; export default function Home() { const { data: recommended, error } = useRecommended(); diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 10bee45a..cb24e25a 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -8,10 +8,10 @@ import "@fontsource/roboto/300.css"; import "@fontsource/roboto/400.css"; import "@fontsource/roboto/500.css"; import "@fontsource/roboto/700.css"; -import BanLandscape from "../components/BanLandscape"; -import { AlertProvider } from "../components/common/alert/AlertProvider"; -import { ModalProvider } from "../components/common/modal/ModalProvider"; -import AuthProvider from "../firebase/auth/AuthProvider"; +import BanLandscape from "~/components/BanLandscape"; +import { AlertProvider } from "~/components/common/alert/AlertProvider"; +import { ModalProvider } from "~/components/common/modal/ModalProvider"; +import AuthProvider from "~/firebase/auth/AuthProvider"; const theme = createTheme({ palette: { diff --git a/web/app/login/page.tsx b/web/app/login/page.tsx index b60b854a..f4e73469 100644 --- a/web/app/login/page.tsx +++ b/web/app/login/page.tsx @@ -4,16 +4,16 @@ import { Box, Button, Link, Typography } from "@mui/material"; import { GoogleAuthProvider, signInWithPopup } from "firebase/auth"; import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; -import * as user from "../../api/user"; -import { getByGUID } from "../../api/user"; -import type { GUID } from "../../common/types"; -import Header from "../../components/Header"; -import { auth } from "../../firebase/config"; +import * as user from "~/api/user"; +import { getByGUID } from "~/api/user"; +import type { GUID } from "~/common/types"; +import Header from "~/components/Header"; +import { auth } from "~/firebase/config"; import "./style.css"; import { useState } from "react"; -import { CourseMateIcon } from "../../components/common/CourseMateIcon"; -import FullScreenCircularProgress from "../../components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; +import { CourseMateIcon } from "~/components/common/CourseMateIcon"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; const provider = new GoogleAuthProvider(); const ALLOW_ANY_MAIL_ADDR = diff --git a/web/app/settings/aboutUs/page.tsx b/web/app/settings/aboutUs/page.tsx index a38d99be..e742e407 100644 --- a/web/app/settings/aboutUs/page.tsx +++ b/web/app/settings/aboutUs/page.tsx @@ -1,8 +1,8 @@ import { GitHub, Language } from "@mui/icons-material"; import XIcon from "@mui/icons-material/X"; import { Box, Link, Typography } from "@mui/material"; -import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; -import TopNavigation from "../../../components/common/TopNavigation"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import TopNavigation from "~/components/common/TopNavigation"; export default function AboutUs() { return ( diff --git a/web/app/settings/contact/page.tsx b/web/app/settings/contact/page.tsx index d51ae0fd..1481d5ac 100644 --- a/web/app/settings/contact/page.tsx +++ b/web/app/settings/contact/page.tsx @@ -1,6 +1,6 @@ import { Box, Button, Typography } from "@mui/material"; -import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; -import TopNavigation from "../../../components/common/TopNavigation"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import TopNavigation from "~/components/common/TopNavigation"; export default function Contact() { return ( diff --git a/web/app/settings/delete/page.tsx b/web/app/settings/delete/page.tsx index 78684e1d..233be5a4 100644 --- a/web/app/settings/delete/page.tsx +++ b/web/app/settings/delete/page.tsx @@ -6,8 +6,8 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback } from "react"; -import { deleteAccount } from "../../../api/user"; -import { useAlert } from "../../../components/common/alert/AlertProvider"; +import { deleteAccount } from "~/api/user"; +import { useAlert } from "~/components/common/alert/AlertProvider"; export default function DeleteAccount() { const router = useRouter(); diff --git a/web/app/settings/disclaimer/page.tsx b/web/app/settings/disclaimer/page.tsx index c65aef75..053d2372 100644 --- a/web/app/settings/disclaimer/page.tsx +++ b/web/app/settings/disclaimer/page.tsx @@ -1,6 +1,6 @@ import { Box, Typography } from "@mui/material"; -import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; -import TopNavigation from "../../../components/common/TopNavigation"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import TopNavigation from "~/components/common/TopNavigation"; export default function Disclaimer() { return ( diff --git a/web/app/settings/layout.tsx b/web/app/settings/layout.tsx index 9f618772..a32f0987 100644 --- a/web/app/settings/layout.tsx +++ b/web/app/settings/layout.tsx @@ -1,6 +1,6 @@ import { Box } from "@mui/material"; -import BottomBar from "../../components/BottomBar"; -import Header from "../../components/Header"; +import BottomBar from "~/components/BottomBar"; +import Header from "~/components/Header"; export default function SettingsPageLayout({ children, diff --git a/web/app/settings/page.tsx b/web/app/settings/page.tsx index 0ac88214..1fb8a79e 100644 --- a/web/app/settings/page.tsx +++ b/web/app/settings/page.tsx @@ -6,8 +6,8 @@ import { ListItemText, } from "@mui/material"; import Link from "next/link"; -import LogOutButton from "../../components/LogOutButton"; -import { NavigateByAuthState } from "../../components/common/NavigateByAuthState"; +import LogOutButton from "~/components/LogOutButton"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; export default function Settings() { return ( diff --git a/web/app/settings/profile/page.tsx b/web/app/settings/profile/page.tsx index 49be77a1..9475bc9e 100644 --- a/web/app/settings/profile/page.tsx +++ b/web/app/settings/profile/page.tsx @@ -4,11 +4,11 @@ import EditIcon from "@mui/icons-material/Edit"; import { Box, Button, Typography } from "@mui/material"; import Link from "next/link"; import { useState } from "react"; -import { useAboutMe } from "../../../api/user"; -import { Card } from "../../../components/Card"; -import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "../../../components/common/NavigateByAuthState"; -import TopNavigation from "../../../components/common/TopNavigation"; +import { useAboutMe } from "~/api/user"; +import { Card } from "~/components/Card"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; +import TopNavigation from "~/components/common/TopNavigation"; export default function SettingsProfile() { const { state } = useAboutMe(); diff --git a/web/app/signup/functions.ts b/web/app/signup/functions.ts index a6d2a989..d928ed40 100644 --- a/web/app/signup/functions.ts +++ b/web/app/signup/functions.ts @@ -1,9 +1,9 @@ import { getAuth } from "firebase/auth"; import type { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; import type { EnqueueSnackbar } from "notistack"; -import * as userAPI from "../../api/user"; -import type { GUID, User } from "../../common/types"; -import type { UpdateUser } from "../../common/types"; +import * as userAPI from "~/api/user"; +import type { GUID, User } from "~/common/types"; +import type { UpdateUser } from "~/common/types"; type Dependencies = { router: AppRouterInstance; diff --git a/web/app/signup/page.tsx b/web/app/signup/page.tsx index 5c5e65d7..09944965 100644 --- a/web/app/signup/page.tsx +++ b/web/app/signup/page.tsx @@ -5,8 +5,8 @@ import { useSnackbar } from "notistack"; import { useState } from "react"; import { useRouter } from "next/navigation"; -import type { Step1User } from "../../../common/zod/types"; -import Header from "../../components/Header"; +import type { Step1User } from "~/common/zod/types"; +import Header from "~/components/Header"; import { register } from "./functions"; import Step1 from "./steps/step1_profile"; import Step2, { type Step2Data } from "./steps/step2_img"; diff --git a/web/app/signup/steps/step1_profile.tsx b/web/app/signup/steps/step1_profile.tsx index 72b15b4b..8314232d 100644 --- a/web/app/signup/steps/step1_profile.tsx +++ b/web/app/signup/steps/step1_profile.tsx @@ -11,10 +11,10 @@ import { Typography, } from "@mui/material"; import type { SelectChangeEvent } from "@mui/material"; -import { parseStep1UserSchema } from "../../../../common/zod/methods"; -import type { Step1User } from "../../../../common/zod/types"; -import type { StepProps } from "../../../app/signup/common"; -import { facultiesAndDepartments } from "../../../app/signup/data"; +import type { StepProps } from "~/app/signup/common"; +import { facultiesAndDepartments } from "~/app/signup/data"; +import { parseStep1UserSchema } from "~/common/zod/methods"; +import type { Step1User } from "~/common/zod/types"; export default function Step1({ onSave, prev, caller }: StepProps) { const [name, setName] = useState(prev?.name ?? ""); diff --git a/web/app/signup/steps/step2_img.tsx b/web/app/signup/steps/step2_img.tsx index 799e25c6..621685ac 100644 --- a/web/app/signup/steps/step2_img.tsx +++ b/web/app/signup/steps/step2_img.tsx @@ -1,10 +1,10 @@ import { Box, Button, Typography } from "@mui/material"; import { enqueueSnackbar } from "notistack"; import { useCallback, useState } from "react"; -import type { BackProp, StepProps } from "../../../app/signup/common"; -import PhotoModal from "../../../components/config/PhotoModal"; -import { PhotoPreviewButton } from "../../../components/config/PhotoPreview"; -import UserAvatar from "../../../components/human/avatar"; +import type { BackProp, StepProps } from "~/app/signup/common"; +import PhotoModal from "~/components/config/PhotoModal"; +import { PhotoPreviewButton } from "~/components/config/PhotoPreview"; +import UserAvatar from "~/components/human/avatar"; export type Step2Data = { pictureUrl: string; diff --git a/web/app/signup/steps/step3_confirmation.tsx b/web/app/signup/steps/step3_confirmation.tsx index c2cc703f..a1ad70a7 100644 --- a/web/app/signup/steps/step3_confirmation.tsx +++ b/web/app/signup/steps/step3_confirmation.tsx @@ -1,7 +1,7 @@ import { Box, Button, Typography } from "@mui/material"; -import type { Step1User } from "../../../../common/zod/types"; -import type { BackProp, StepProps } from "../../../app/signup/common"; -import UserAvatar from "../../../components/human/avatar"; +import type { BackProp, StepProps } from "~/app/signup/common"; +import type { Step1User } from "~/common/zod/types"; +import UserAvatar from "~/components/human/avatar"; import type { Step2Data } from "./step2_img"; interface UserInfoProp { diff --git a/web/app/signup/steps/step4_course.tsx b/web/app/signup/steps/step4_course.tsx index 2b041c64..f9d738a4 100644 --- a/web/app/signup/steps/step4_course.tsx +++ b/web/app/signup/steps/step4_course.tsx @@ -1,9 +1,9 @@ import { Button, Typography } from "@mui/material"; import { Box } from "@mui/system"; import Link from "next/link"; -import { useMyID } from "../../../api/user"; -import FullScreenCircularProgress from "../../../components/common/FullScreenCircularProgress"; -import EditableCoursesTable from "../../../components/course/EditableCoursesTable"; +import { useMyID } from "~/api/user"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; +import EditableCoursesTable from "~/components/course/EditableCoursesTable"; export default function Step4() { const { state } = useMyID(); diff --git a/web/app/tutorial/page.tsx b/web/app/tutorial/page.tsx index b860e6fa..26c0eecb 100644 --- a/web/app/tutorial/page.tsx +++ b/web/app/tutorial/page.tsx @@ -7,37 +7,36 @@ import "swiper/css"; import "swiper/css/navigation"; import "swiper/css/pagination"; import Link from "next/link"; -import Header from "../../components/Header"; +import Header from "~/components/Header"; -// TODO: 画像表示の参照を直す const tutorialSteps = [ { label: "CourseMateの使い方", - imgPath: "../../../public/course-mate-icon.svg", + imgPath: "/course-mate-icon.svg", }, { label: "気になる人に「いいね」を送ろう!", - imgPath: "../../public/tutorial-1.png", + imgPath: "/tutorial-1.png", }, { label: "リクエストが来たら...", - imgPath: "../../public/tutorial-2.png", + imgPath: "/tutorial-2.png", }, { label: "承認して、マッチング!", - imgPath: "../../public/tutorial-3.png", + imgPath: "/tutorial-3.png", }, { label: "チャットを使って仲良くなろう!", - imgPath: "../../public/tutorial-4.png", + imgPath: "/tutorial-4.png", }, { label: "魅力的なカードを作ってね!", - imgPath: "../../public/tutorial-5.png", + imgPath: "/tutorial-5.png", }, { label: "実際にCourseMateを使ってみよう!下のボタンをクリック!", - imgPath: "../../public/tutorial-6.png", + imgPath: "/tutorial-6.png", }, ]; diff --git a/web/bun.lockb b/web/bun.lockb index 9c4777ea547024f113a0ef8f51d1f333d14f6b89..fd54ff76250e33b3ee2d01740be45360a2890fe2 100755 GIT binary patch delta 25 hcmaEVj{W^P_J%EtnJ(;%afW&ZdZyb8Tp0f^0sx7i3D5ul delta 25 dcmaEVj{W^P_J%EtnJ(;13}CRmz=iSuA^?2L2!Q|q diff --git a/web/components/Card.tsx b/web/components/Card.tsx index 9575ac94..e2eb3407 100644 --- a/web/components/Card.tsx +++ b/web/components/Card.tsx @@ -1,7 +1,7 @@ import ThreeSixtyIcon from "@mui/icons-material/ThreeSixty"; import { Chip } from "@mui/material"; import { useEffect, useState } from "react"; -import type { User, UserID } from "../common/types"; +import type { User, UserID } from "~/common/types"; import NonEditableCoursesTable from "./course/NonEditableCoursesTable"; import UserAvatar from "./human/avatar"; diff --git a/web/components/DraggableCard.tsx b/web/components/DraggableCard.tsx index 8aaff469..34e12d0f 100644 --- a/web/components/DraggableCard.tsx +++ b/web/components/DraggableCard.tsx @@ -3,7 +3,7 @@ import FavoriteIcon from "@mui/icons-material/Favorite"; import { Box, Typography } from "@mui/material"; import { motion, useMotionValue, useMotionValueEvent } from "framer-motion"; import { useCallback, useState } from "react"; -import type { User, UserID } from "../../common/types"; +import type { User, UserID } from "~/common/types"; import { Card } from "./Card"; const SWIPE_THRESHOLD = 30; diff --git a/web/components/LogOutButton.tsx b/web/components/LogOutButton.tsx index dc12e80f..0bff3fc9 100644 --- a/web/components/LogOutButton.tsx +++ b/web/components/LogOutButton.tsx @@ -5,7 +5,7 @@ import { signOut } from "firebase/auth"; import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback } from "react"; -import { auth } from "../firebase/config"; +import { auth } from "~/firebase/config"; import { useAlert } from "./common/alert/AlertProvider"; export default function LogOutButton() { diff --git a/web/components/chat/MessageInput.tsx b/web/components/chat/MessageInput.tsx index 7959383a..da8e7fe0 100644 --- a/web/components/chat/MessageInput.tsx +++ b/web/components/chat/MessageInput.tsx @@ -1,8 +1,8 @@ import SendIcon from "@mui/icons-material/Send"; import { Box, IconButton, Stack, TextField, Typography } from "@mui/material"; import { useEffect, useState } from "react"; -import type { DMOverview, SendMessage, UserID } from "../../common/types"; -import { parseContent } from "../../common/zod/methods"; +import type { DMOverview, SendMessage, UserID } from "~/common/types"; +import { parseContent } from "~/common/zod/methods"; type Props = { send: (to: UserID, m: SendMessage) => void; diff --git a/web/components/chat/Room.tsx b/web/components/chat/Room.tsx index c3a11cd0..302c54d6 100644 --- a/web/components/chat/Room.tsx +++ b/web/components/chat/Room.tsx @@ -1,5 +1,5 @@ import { ListItem, Stack, Typography } from "@mui/material"; -import type { DMOverview } from "../../../common/types"; +import type { DMOverview } from "~/common/types"; import UserAvatar from "../human/avatar"; type Props = { diff --git a/web/components/chat/RoomHeader.tsx b/web/components/chat/RoomHeader.tsx index 9871df93..e0baa125 100644 --- a/web/components/chat/RoomHeader.tsx +++ b/web/components/chat/RoomHeader.tsx @@ -1,7 +1,7 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import { Box, Button, Typography } from "@mui/material"; import Link from "next/link"; -import type { DMOverview } from "../../common/types"; +import type { DMOverview } from "~/common/types"; import UserAvatar from "../human/avatar"; type Props = { room: DMOverview; diff --git a/web/components/chat/RoomList.tsx b/web/components/chat/RoomList.tsx index a11bfb65..5d5513d7 100644 --- a/web/components/chat/RoomList.tsx +++ b/web/components/chat/RoomList.tsx @@ -2,7 +2,7 @@ import { Box, List, Typography } from "@mui/material"; import { useRouter } from "next/navigation"; -import type { RoomOverview } from "../../common/types"; +import type { RoomOverview } from "~/common/types"; import { HumanListItem } from "../human/humanListItem"; type RoomListProps = { diff --git a/web/components/chat/RoomWindow.tsx b/web/components/chat/RoomWindow.tsx index 7c1f6038..eb2777d8 100644 --- a/web/components/chat/RoomWindow.tsx +++ b/web/components/chat/RoomWindow.tsx @@ -2,19 +2,19 @@ import { Box, Button, Paper, TextField, Typography } from "@mui/material"; import { useSearchParams } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback, useEffect, useRef, useState } from "react"; -import * as chat from "../../api/chat/chat"; -import { useMessages } from "../../api/chat/hooks"; -import * as user from "../../api/user"; -import { useMyID } from "../../api/user"; +import * as chat from "~/api/chat/chat"; +import { useMessages } from "~/api/chat/hooks"; +import * as user from "~/api/user"; +import { useMyID } from "~/api/user"; import type { DMOverview, Message, MessageID, SendMessage, UserID, -} from "../../common/types"; -import type { Content } from "../../common/zod/types"; -import { getIdToken } from "../../firebase/auth/lib"; +} from "~/common/types"; +import type { Content } from "~/common/zod/types"; +import { getIdToken } from "~/firebase/auth/lib"; import Dots from "../common/Dots"; import { socket } from "../data/socket"; import { MessageInput } from "./MessageInput"; diff --git a/web/components/common/NavigateByAuthState.tsx b/web/components/common/NavigateByAuthState.tsx index 9e22d2ae..a88d4a32 100644 --- a/web/components/common/NavigateByAuthState.tsx +++ b/web/components/common/NavigateByAuthState.tsx @@ -3,7 +3,7 @@ import { getAuth, onAuthStateChanged } from "firebase/auth"; import { useRouter } from "next/navigation"; import { type ReactNode, useEffect, useState } from "react"; -import { userExists } from "../../api/internal/endpoints"; +import { userExists } from "~/api/internal/endpoints"; import FullScreenCircularProgress from "./FullScreenCircularProgress"; /** diff --git a/web/components/common/modal/ModalProvider.tsx b/web/components/common/modal/ModalProvider.tsx index 591057cc..3b59bbcb 100644 --- a/web/components/common/modal/ModalProvider.tsx +++ b/web/components/common/modal/ModalProvider.tsx @@ -1,8 +1,8 @@ import { Box } from "@mui/material"; import { styled } from "@mui/system"; import { type ReactNode, createContext, useContext, useState } from "react"; -import { useMyID } from "../../../api/user"; -import type { User } from "../../../common/types"; +import { useMyID } from "~/api/user"; +import type { User } from "~/common/types"; import { Card } from "../../Card"; const Overlay = styled(Box)({ diff --git a/web/components/config/PhotoModal.tsx b/web/components/config/PhotoModal.tsx index b68dae31..7c9c4a04 100644 --- a/web/components/config/PhotoModal.tsx +++ b/web/components/config/PhotoModal.tsx @@ -1,6 +1,6 @@ import { Box, Button, Modal } from "@mui/material"; import { useState } from "react"; -import { MAX_IMAGE_SIZE, uploadImage } from "../../api/internal/fetch-func"; +import { MAX_IMAGE_SIZE, uploadImage } from "~/api/internal/fetch-func"; import { PhotoPreview } from "./PhotoPreview"; async function upload( diff --git a/web/components/config/PhotoPreview.tsx b/web/components/config/PhotoPreview.tsx index 7469da6d..c829f056 100644 --- a/web/components/config/PhotoPreview.tsx +++ b/web/components/config/PhotoPreview.tsx @@ -6,7 +6,7 @@ import { useRef, useState, } from "react"; -import { uploadImage } from "../../api/image"; +import { uploadImage } from "~/api/image"; import { ImageCropper } from "../ImageCropper"; import { photo } from "../data/photo-preview"; diff --git a/web/components/course/EditableCoursesTable/index.tsx b/web/components/course/EditableCoursesTable/index.tsx index f04d27af..5dd32447 100644 --- a/web/components/course/EditableCoursesTable/index.tsx +++ b/web/components/course/EditableCoursesTable/index.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; -import courseApi from "../../../api/course"; -import type { Course, Day, UserID } from "../../../common/types"; -import FullScreenCircularProgress from "../../common/FullScreenCircularProgress"; +import courseApi from "~/api/course"; +import type { Course, Day, UserID } from "~/common/types"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; import CoursesTableCore from "../components/CoursesTableCore"; import SelectCourseDialog from "../components/SelectCourseDialog"; diff --git a/web/components/course/NonEditableCoursesTable/index.tsx b/web/components/course/NonEditableCoursesTable/index.tsx index 727543ab..7a9870e4 100644 --- a/web/components/course/NonEditableCoursesTable/index.tsx +++ b/web/components/course/NonEditableCoursesTable/index.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; -import courseApi from "../../../api/course"; -import type { Course, UserID } from "../../../common/types"; -import FullScreenCircularProgress from "../../common/FullScreenCircularProgress"; +import courseApi from "~/api/course"; +import type { Course, UserID } from "~/common/types"; +import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; import CoursesTableCore from "../components/CoursesTableCore"; type Props = { diff --git a/web/components/course/components/CourseDeleteConfirmDialog.tsx b/web/components/course/components/CourseDeleteConfirmDialog.tsx index 52905c58..2fdcbe69 100644 --- a/web/components/course/components/CourseDeleteConfirmDialog.tsx +++ b/web/components/course/components/CourseDeleteConfirmDialog.tsx @@ -9,8 +9,8 @@ import { DialogTitle, } from "@mui/material"; import { useEffect, useState } from "react"; -import { deleteMyCourse, getMyCoursesOverlapWith } from "../../../api/course"; -import type { Course } from "../../../common/types"; +import { deleteMyCourse, getMyCoursesOverlapWith } from "~/api/course"; +import type { Course } from "~/common/types"; export default function CourseDeleteRegisterConfirmDialog({ open, diff --git a/web/components/course/components/CourseRegisterConfirmDialog.tsx b/web/components/course/components/CourseRegisterConfirmDialog.tsx index 3709583f..faa344a3 100644 --- a/web/components/course/components/CourseRegisterConfirmDialog.tsx +++ b/web/components/course/components/CourseRegisterConfirmDialog.tsx @@ -9,8 +9,8 @@ import { DialogTitle, } from "@mui/material"; import { useEffect, useState } from "react"; -import { addMyCourse, getMyCoursesOverlapWith } from "../../../api/course"; -import type { Course } from "../../../common/types"; +import { addMyCourse, getMyCoursesOverlapWith } from "~/api/course"; +import type { Course } from "~/common/types"; export default function CourseRegisterConfirmDialog({ open, diff --git a/web/components/course/components/CoursesTableCore/index.tsx b/web/components/course/components/CoursesTableCore/index.tsx index 4cec8f70..a4dd7cba 100644 --- a/web/components/course/components/CoursesTableCore/index.tsx +++ b/web/components/course/components/CoursesTableCore/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from "react"; -import { ACTIVE_DAYS, DAY_TO_JAPANESE_MAP } from "../../../../common/consts"; -import type { Course, Day } from "../../../../common/types"; +import { ACTIVE_DAYS, DAY_TO_JAPANESE_MAP } from "~/common/consts"; +import type { Course, Day } from "~/common/types"; import { truncateStr } from "./lib"; import styles from "./styles.module.css"; diff --git a/web/components/course/components/SelectCourseDialog.tsx b/web/components/course/components/SelectCourseDialog.tsx index 046a3475..41bba1b7 100644 --- a/web/components/course/components/SelectCourseDialog.tsx +++ b/web/components/course/components/SelectCourseDialog.tsx @@ -15,9 +15,9 @@ import { } from "@mui/material"; import IconButton from "@mui/material/IconButton"; import { useEffect, useState } from "react"; -import courseApi from "../../../api/course"; -import { DAY_TO_JAPANESE_MAP } from "../../../common/consts"; -import type { Course, Day } from "../../../common/types"; +import courseApi from "~/api/course"; +import { DAY_TO_JAPANESE_MAP } from "~/common/consts"; +import type { Course, Day } from "~/common/types"; import CourseDeleteConfirmDialog from "./CourseDeleteConfirmDialog"; import CourseRegisterConfirmDialog from "./CourseRegisterConfirmDialog"; diff --git a/web/components/match/matching.tsx b/web/components/match/matching.tsx index fc5f5a41..140515a0 100644 --- a/web/components/match/matching.tsx +++ b/web/components/match/matching.tsx @@ -1,6 +1,6 @@ import { Box, List } from "@mui/material"; -import { deleteMatch } from "../../api/match"; -import { useMatched } from "../../api/user"; +import { deleteMatch } from "~/api/match"; +import { useMatched } from "~/api/user"; import FullScreenCircularProgress from "../common/FullScreenCircularProgress"; import { useModal } from "../common/modal/ModalProvider"; import { HumanListItem } from "../human/humanListItem"; diff --git a/web/components/match/myRequests.tsx b/web/components/match/myRequests.tsx index 3061044d..1e247d16 100644 --- a/web/components/match/myRequests.tsx +++ b/web/components/match/myRequests.tsx @@ -1,7 +1,7 @@ import { Box } from "@mui/material"; import { List } from "@mui/material"; -import * as request from "../../api/request"; -import { usePendingFromMe } from "../../api/user"; +import * as request from "~/api/request"; +import { usePendingFromMe } from "~/api/user"; import FullScreenCircularProgress from "../common/FullScreenCircularProgress"; import { useModal } from "../common/modal/ModalProvider"; import { HumanListItem } from "../human/humanListItem"; diff --git a/web/components/match/othersRequests.tsx b/web/components/match/othersRequests.tsx index a51e34b2..1e828f14 100644 --- a/web/components/match/othersRequests.tsx +++ b/web/components/match/othersRequests.tsx @@ -1,6 +1,6 @@ import { Box, List } from "@mui/material"; -import request from "../../api/request"; -import { usePendingToMe } from "../../api/user"; +import request from "~/api/request"; +import { usePendingToMe } from "~/api/user"; import FullScreenCircularProgress from "../common/FullScreenCircularProgress"; import { useModal } from "../common/modal/ModalProvider"; import { HumanListItem } from "../human/humanListItem"; diff --git a/web/firebase/auth/AuthProvider.tsx b/web/firebase/auth/AuthProvider.tsx index 49412bc3..9a009760 100644 --- a/web/firebase/auth/AuthProvider.tsx +++ b/web/firebase/auth/AuthProvider.tsx @@ -1,8 +1,8 @@ import { getAuth, onAuthStateChanged } from "firebase/auth"; import type React from "react"; import { createContext, useContext, useEffect, useState } from "react"; -import type { GUID, User } from "../../../common/types"; -import * as userAPI from "../../api/user"; +import * as userAPI from "~/api/user"; +import type { GUID, User } from "~/common/types"; const AuthContext = createContext(undefined); diff --git a/web/firebase/auth/lib.ts b/web/firebase/auth/lib.ts index 15f33c3a..0d0e22b3 100644 --- a/web/firebase/auth/lib.ts +++ b/web/firebase/auth/lib.ts @@ -1,6 +1,6 @@ import { getAuth, onAuthStateChanged } from "firebase/auth"; import type { User } from "firebase/auth"; -import type { IDToken } from "../../../common/types"; +import type { IDToken } from "~/common/types"; import { app } from "../config"; export class ErrUnauthorized extends Error {} diff --git a/web/hooks/useData.ts b/web/hooks/useData.ts index 586ddab7..303699e8 100644 --- a/web/hooks/useData.ts +++ b/web/hooks/useData.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from "react"; -import { Err, Ok, type Result } from "../../common/lib/result"; -import { credFetch } from "../firebase/auth/lib"; +import { Err, Ok, type Result } from "~/common/lib/result"; +import { credFetch } from "~/firebase/auth/lib"; // TODO: separate this into concrete types and urls s.t. there is no unsafe any // also use sth like something like Zod diff --git a/web/tsconfig.json b/web/tsconfig.json index a6c3386f..cf84dc49 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -23,7 +23,11 @@ { "name": "next" } - ] + ], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, "include": [ "./src", From a7a61daf9830acebe60209bada4df1f1945793ff Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Sun, 10 Nov 2024 11:12:00 +0900 Subject: [PATCH 017/131] delete package-lock.json (#513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- package-lock.json | 1671 --------------------------------------------- 1 file changed, 1671 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f8dd50fc..00000000 --- a/package-lock.json +++ /dev/null @@ -1,1671 +0,0 @@ -{ - "name": "course-mate", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "course-mate", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@types/bun": "^1.1.10", - "cspell": "^8.14.4", - "zod": "^3.23.8" - }, - "devDependencies": { - "@biomejs/biome": "^1.9.1", - "husky": "^9.1.4", - "lint-staged": "^15.2.10", - "typescript": "^5.6.2" - } - }, - "node_modules/@biomejs/biome": { - "version": "1.9.1", - "dev": true, - "hasInstallScript": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.1", - "@biomejs/cli-darwin-x64": "1.9.1", - "@biomejs/cli-linux-arm64": "1.9.1", - "@biomejs/cli-linux-arm64-musl": "1.9.1", - "@biomejs/cli-linux-x64": "1.9.1", - "@biomejs/cli-linux-x64-musl": "1.9.1", - "@biomejs/cli-win32-arm64": "1.9.1", - "@biomejs/cli-win32-x64": "1.9.1" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.1", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@cspell/cspell-bundled-dicts": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/dict-ada": "^4.0.2", - "@cspell/dict-aws": "^4.0.4", - "@cspell/dict-bash": "^4.1.4", - "@cspell/dict-companies": "^3.1.4", - "@cspell/dict-cpp": "^5.1.16", - "@cspell/dict-cryptocurrencies": "^5.0.0", - "@cspell/dict-csharp": "^4.0.2", - "@cspell/dict-css": "^4.0.13", - "@cspell/dict-dart": "^2.2.1", - "@cspell/dict-django": "^4.1.0", - "@cspell/dict-docker": "^1.1.7", - "@cspell/dict-dotnet": "^5.0.5", - "@cspell/dict-elixir": "^4.0.3", - "@cspell/dict-en_us": "^4.3.23", - "@cspell/dict-en-common-misspellings": "^2.0.4", - "@cspell/dict-en-gb": "1.1.33", - "@cspell/dict-filetypes": "^3.0.4", - "@cspell/dict-flutter": "^1.0.0", - "@cspell/dict-fonts": "^4.0.0", - "@cspell/dict-fsharp": "^1.0.1", - "@cspell/dict-fullstack": "^3.2.0", - "@cspell/dict-gaming-terms": "^1.0.5", - "@cspell/dict-git": "^3.0.0", - "@cspell/dict-golang": "^6.0.12", - "@cspell/dict-google": "^1.0.1", - "@cspell/dict-haskell": "^4.0.1", - "@cspell/dict-html": "^4.0.5", - "@cspell/dict-html-symbol-entities": "^4.0.0", - "@cspell/dict-java": "^5.0.7", - "@cspell/dict-julia": "^1.0.1", - "@cspell/dict-k8s": "^1.0.6", - "@cspell/dict-latex": "^4.0.0", - "@cspell/dict-lorem-ipsum": "^4.0.0", - "@cspell/dict-lua": "^4.0.3", - "@cspell/dict-makefile": "^1.0.0", - "@cspell/dict-monkeyc": "^1.0.6", - "@cspell/dict-node": "^5.0.1", - "@cspell/dict-npm": "^5.1.4", - "@cspell/dict-php": "^4.0.10", - "@cspell/dict-powershell": "^5.0.8", - "@cspell/dict-public-licenses": "^2.0.8", - "@cspell/dict-python": "^4.2.6", - "@cspell/dict-r": "^2.0.1", - "@cspell/dict-ruby": "^5.0.3", - "@cspell/dict-rust": "^4.0.5", - "@cspell/dict-scala": "^5.0.3", - "@cspell/dict-software-terms": "^4.1.3", - "@cspell/dict-sql": "^2.1.5", - "@cspell/dict-svelte": "^1.0.2", - "@cspell/dict-swift": "^2.0.1", - "@cspell/dict-terraform": "^1.0.1", - "@cspell/dict-typescript": "^3.1.6", - "@cspell/dict-vue": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@cspell/cspell-json-reporter": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/cspell-types": "8.14.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@cspell/cspell-pipe": { - "version": "8.14.4", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@cspell/cspell-resolver": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "global-directory": "^4.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@cspell/cspell-service-bus": { - "version": "8.14.4", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@cspell/cspell-types": { - "version": "8.14.4", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@cspell/dict-ada": { - "version": "4.0.2", - "license": "MIT" - }, - "node_modules/@cspell/dict-aws": { - "version": "4.0.4", - "license": "MIT" - }, - "node_modules/@cspell/dict-bash": { - "version": "4.1.4", - "license": "MIT" - }, - "node_modules/@cspell/dict-companies": { - "version": "3.1.4", - "license": "MIT" - }, - "node_modules/@cspell/dict-cpp": { - "version": "5.1.16", - "license": "MIT" - }, - "node_modules/@cspell/dict-cryptocurrencies": { - "version": "5.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-csharp": { - "version": "4.0.2", - "license": "MIT" - }, - "node_modules/@cspell/dict-css": { - "version": "4.0.13", - "license": "MIT" - }, - "node_modules/@cspell/dict-dart": { - "version": "2.2.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-data-science": { - "version": "2.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-django": { - "version": "4.1.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-docker": { - "version": "1.1.7", - "license": "MIT" - }, - "node_modules/@cspell/dict-dotnet": { - "version": "5.0.5", - "license": "MIT" - }, - "node_modules/@cspell/dict-elixir": { - "version": "4.0.3", - "license": "MIT" - }, - "node_modules/@cspell/dict-en_us": { - "version": "4.3.23", - "license": "MIT" - }, - "node_modules/@cspell/dict-en-common-misspellings": { - "version": "2.0.4", - "license": "CC BY-SA 4.0" - }, - "node_modules/@cspell/dict-en-gb": { - "version": "1.1.33", - "license": "MIT" - }, - "node_modules/@cspell/dict-filetypes": { - "version": "3.0.4", - "license": "MIT" - }, - "node_modules/@cspell/dict-flutter": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-fonts": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-fsharp": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-fullstack": { - "version": "3.2.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-gaming-terms": { - "version": "1.0.5", - "license": "MIT" - }, - "node_modules/@cspell/dict-git": { - "version": "3.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-golang": { - "version": "6.0.12", - "license": "MIT" - }, - "node_modules/@cspell/dict-google": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-haskell": { - "version": "4.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-html": { - "version": "4.0.6", - "license": "MIT" - }, - "node_modules/@cspell/dict-html-symbol-entities": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-java": { - "version": "5.0.7", - "license": "MIT" - }, - "node_modules/@cspell/dict-julia": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-k8s": { - "version": "1.0.6", - "license": "MIT" - }, - "node_modules/@cspell/dict-latex": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-lorem-ipsum": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-lua": { - "version": "4.0.3", - "license": "MIT" - }, - "node_modules/@cspell/dict-makefile": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dict-monkeyc": { - "version": "1.0.6", - "license": "MIT" - }, - "node_modules/@cspell/dict-node": { - "version": "5.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-npm": { - "version": "5.1.5", - "license": "MIT" - }, - "node_modules/@cspell/dict-php": { - "version": "4.0.10", - "license": "MIT" - }, - "node_modules/@cspell/dict-powershell": { - "version": "5.0.9", - "license": "MIT" - }, - "node_modules/@cspell/dict-public-licenses": { - "version": "2.0.8", - "license": "MIT" - }, - "node_modules/@cspell/dict-python": { - "version": "4.2.6", - "license": "MIT", - "dependencies": { - "@cspell/dict-data-science": "^2.0.1" - } - }, - "node_modules/@cspell/dict-r": { - "version": "2.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-ruby": { - "version": "5.0.3", - "license": "MIT" - }, - "node_modules/@cspell/dict-rust": { - "version": "4.0.5", - "license": "MIT" - }, - "node_modules/@cspell/dict-scala": { - "version": "5.0.3", - "license": "MIT" - }, - "node_modules/@cspell/dict-software-terms": { - "version": "4.1.4", - "license": "MIT" - }, - "node_modules/@cspell/dict-sql": { - "version": "2.1.5", - "license": "MIT" - }, - "node_modules/@cspell/dict-svelte": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/@cspell/dict-swift": { - "version": "2.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-terraform": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/@cspell/dict-typescript": { - "version": "3.1.6", - "license": "MIT" - }, - "node_modules/@cspell/dict-vue": { - "version": "3.0.0", - "license": "MIT" - }, - "node_modules/@cspell/dynamic-import": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "import-meta-resolve": "^4.1.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@cspell/filetypes": { - "version": "8.14.4", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@cspell/strong-weak-map": { - "version": "8.14.4", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@cspell/url": { - "version": "8.14.4", - "license": "MIT", - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@types/bun": { - "version": "1.1.10", - "license": "MIT", - "dependencies": { - "bun-types": "1.1.29" - } - }, - "node_modules/@types/node": { - "version": "20.12.14", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/ws": { - "version": "8.5.12", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/ansi-escapes": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/array-timsort": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/braces": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bun-types": { - "version": "1.1.29", - "license": "MIT", - "dependencies": { - "@types/node": "~20.12.8", - "@types/ws": "~8.5.10" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "5.3.0", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk-template": { - "version": "1.1.0", - "license": "MIT", - "dependencies": { - "chalk": "^5.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" - } - }, - "node_modules/clear-module": { - "version": "4.1.2", - "license": "MIT", - "dependencies": { - "parent-module": "^2.0.0", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "12.1.0", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/comment-json": { - "version": "4.2.5", - "license": "MIT", - "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cspell": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/cspell-json-reporter": "8.14.4", - "@cspell/cspell-pipe": "8.14.4", - "@cspell/cspell-types": "8.14.4", - "@cspell/dynamic-import": "8.14.4", - "@cspell/url": "8.14.4", - "chalk": "^5.3.0", - "chalk-template": "^1.1.0", - "commander": "^12.1.0", - "cspell-dictionary": "8.14.4", - "cspell-gitignore": "8.14.4", - "cspell-glob": "8.14.4", - "cspell-io": "8.14.4", - "cspell-lib": "8.14.4", - "fast-glob": "^3.3.2", - "fast-json-stable-stringify": "^2.1.0", - "file-entry-cache": "^9.1.0", - "get-stdin": "^9.0.0", - "semver": "^7.6.3", - "strip-ansi": "^7.1.0" - }, - "bin": { - "cspell": "bin.mjs", - "cspell-esm": "bin.mjs" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" - } - }, - "node_modules/cspell-config-lib": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/cspell-types": "8.14.4", - "comment-json": "^4.2.5", - "yaml": "^2.5.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cspell-dictionary": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/cspell-pipe": "8.14.4", - "@cspell/cspell-types": "8.14.4", - "cspell-trie-lib": "8.14.4", - "fast-equals": "^5.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cspell-gitignore": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/url": "8.14.4", - "cspell-glob": "8.14.4", - "cspell-io": "8.14.4", - "find-up-simple": "^1.0.0" - }, - "bin": { - "cspell-gitignore": "bin.mjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cspell-glob": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/url": "8.14.4", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cspell-grammar": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/cspell-pipe": "8.14.4", - "@cspell/cspell-types": "8.14.4" - }, - "bin": { - "cspell-grammar": "bin.mjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cspell-io": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/cspell-service-bus": "8.14.4", - "@cspell/url": "8.14.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cspell-lib": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/cspell-bundled-dicts": "8.14.4", - "@cspell/cspell-pipe": "8.14.4", - "@cspell/cspell-resolver": "8.14.4", - "@cspell/cspell-types": "8.14.4", - "@cspell/dynamic-import": "8.14.4", - "@cspell/filetypes": "8.14.4", - "@cspell/strong-weak-map": "8.14.4", - "@cspell/url": "8.14.4", - "clear-module": "^4.1.2", - "comment-json": "^4.2.5", - "cspell-config-lib": "8.14.4", - "cspell-dictionary": "8.14.4", - "cspell-glob": "8.14.4", - "cspell-grammar": "8.14.4", - "cspell-io": "8.14.4", - "cspell-trie-lib": "8.14.4", - "env-paths": "^3.0.0", - "fast-equals": "^5.0.1", - "gensequence": "^7.0.0", - "import-fresh": "^3.3.0", - "resolve-from": "^5.0.0", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-uri": "^3.0.8", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cspell-trie-lib": { - "version": "8.14.4", - "license": "MIT", - "dependencies": { - "@cspell/cspell-pipe": "8.14.4", - "@cspell/cspell-types": "8.14.4", - "gensequence": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/debug": { - "version": "4.3.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/emoji-regex": { - "version": "10.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/env-paths": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/environment": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/execa": { - "version": "8.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/fast-equals": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.17.1", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "9.1.0", - "license": "MIT", - "dependencies": { - "flat-cache": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up-simple": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "license": "ISC" - }, - "node_modules/gensequence": { - "version": "7.0.0", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stdin": { - "version": "9.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stream": { - "version": "8.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-directory": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "ini": "4.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has-own-prop": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/husky": { - "version": "9.1.4", - "dev": true, - "license": "MIT", - "bin": { - "husky": "bin.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/parent-module": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.1.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ini": { - "version": "4.1.1", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/lilconfig": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lint-staged": { - "version": "15.2.10", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "~5.3.0", - "commander": "~12.1.0", - "debug": "~4.3.6", - "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", - "micromatch": "~4.0.8", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.5.0" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/listr2": { - "version": "8.2.4", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/log-update": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "callsites": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.6.0", - "dev": true, - "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.6.3", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/string-argv": { - "version": "0.3.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-width": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/typescript": { - "version": "5.6.2", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "license": "MIT" - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "license": "MIT" - }, - "node_modules/vscode-uri": { - "version": "3.0.8", - "license": "MIT" - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yaml": { - "version": "2.5.1", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/zod": { - "version": "3.23.8", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} From a9e3b789027201975d79635d892117513fb95ea7 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Sun, 10 Nov 2024 11:25:02 +0900 Subject: [PATCH 018/131] edit pull_request_template.md (#504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- .github/pull_request_template.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 28926e86..90a1a3ff 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,21 +1,12 @@ - # PRの概要 - - - - ## 具体的な変更内容 - ## 影響範囲 - ## 動作要件 - ## 補足 - ## レビューリクエストを出す前にチェック! From a91c531e67c4af632861693215f4adc91f5fa2d9 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Sun, 10 Nov 2024 11:31:06 +0900 Subject: [PATCH 019/131] =?UTF-8?q?SERVER=5FORIGIN,=20WEB=5FORIGIN,=20WEB?= =?UTF-8?q?=5FORIGIN=5FBUILD=20etc=20=E3=81=AE=E4=BB=A3=E3=82=8F=E3=82=8A?= =?UTF-8?q?=E3=81=AB=20CORS=5FALLOW=5FORIGINS=20=E3=82=92=E4=BD=BF?= =?UTF-8?q?=E3=81=86=20(#516)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 影響範囲 環境変数に影響がある。 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [x] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [x] わかりやすいPRになっているか --- Makefile | 1 + server/.env.sample | 6 ++---- server/src/index.ts | 16 +++++++++------- server/src/lib/utils.ts | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 server/src/lib/utils.ts diff --git a/Makefile b/Makefile index bee23e3a..3892e689 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ test: export DATABASE_URL=$(LOCAL_DB) test: export NEVER_LOAD_DOTENV=1 test: export UNSAFE_SKIP_AUTH=1 test: export FIREBASE_PROJECT_ID=mock-proj +test: export CORS_ALLOW_ORIGINS=http://localhost:3000,https://localhost:5173 test: dev-db cd server/src; ENV_FILE=../.env.dev bun test cd ./test; ENV_FILE=../server/.env.dev bun test diff --git a/server/.env.sample b/server/.env.sample index bb0e5bca..55763721 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -3,10 +3,8 @@ # below can be used for docker db created via `make dev-db` DATABASE_URL=postgres://user:password@localhost:5432/database -# Application origins -SERVER_ORIGIN=http://localhost:3000 -WEB_ORIGIN=http://localhost:5173 -MOBILE_ORIGIN=http://localhost:8081 +# CORS allow origins, separated by "," | no space is allowed before/after "," +CORS_ALLOW_ORIGINS=http://localhost:3000,http://localhost:5173 # Firebase FIREBASE_PROJECT_ID=project-id diff --git a/server/src/index.ts b/server/src/index.ts index de19b304..a32aa9e2 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -3,6 +3,7 @@ import express from "express"; import csrf from "./lib/cross-origin/block-unknown-origin"; import cors from "./lib/cross-origin/multi-origin-cors"; import { initializeSocket } from "./lib/socket/socket"; +import { allUrlMustBeValid, panic } from "./lib/utils"; import chatRoutes from "./router/chat"; import coursesRoutes from "./router/courses"; import matchesRoutes from "./router/matches"; @@ -17,14 +18,15 @@ const app = express(); app.set("query parser", "simple"); const port = 3000; -const allowedOrigins = [ - process.env.SERVER_ORIGIN ?? "http://localhost:3000", // delete this fallback when you think everyone has updated their .env - process.env.WEB_ORIGIN, - process.env.MOBILE_ORIGIN, - process.env.WEB_ORIGIN_BUILD, -]; +const allowedOrigins = ( + process.env.CORS_ALLOW_ORIGINS || panic("env CORS_ALLOW_ORIGINS is missing") +) + .split(",") + .filter((s) => s); // ignore empty string (trailing comma?) +allUrlMustBeValid(allowedOrigins); + export const corsOptions = { - origins: allowedOrigins.filter((s) => s != null).filter((s) => s), // ignore empty string too + origins: allowedOrigins, methods: ["GET", "HEAD", "POST", "PUT", "DELETE"], credentials: true, }; diff --git a/server/src/lib/utils.ts b/server/src/lib/utils.ts new file mode 100644 index 00000000..8eddd99d --- /dev/null +++ b/server/src/lib/utils.ts @@ -0,0 +1,14 @@ +export function panic(reason: string): never { + throw new Error(`function panic() called for reason: "${reason}"`); +} + +export function allUrlMustBeValid(urls: string[]) { + for (const url of urls) { + try { + new URL(url); + } catch (err) { + console.error(err); + throw err; + } + } +} From 175724d6fafddf8c9fb5682caaa4a11ed719d43f Mon Sep 17 00:00:00 2001 From: Shogo NAKAMURA <104970808+naka-12@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:03:56 +0900 Subject: [PATCH 020/131] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E7=9A=84=E3=81=AA?= =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=20daisyUI=20=E3=81=AB=E7=A7=BB=E8=A1=8C=20(#515)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- .cspell.json | 8 +- biome.json | 7 + web/app/chat/layout.tsx | 22 +- web/app/chat/page.tsx | 9 +- web/app/edit/courses/page.tsx | 84 +-- web/app/edit/layout.tsx | 22 +- web/app/edit/profile/page.tsx | 604 +++++++++--------- web/app/faq/page.tsx | 125 ++-- web/app/friends/layout.tsx | 17 +- web/app/friends/page.tsx | 29 +- web/app/globals.css | 13 + web/app/index.css | 5 - web/app/layout.tsx | 2 +- web/app/settings/aboutUs/page.tsx | 137 ++-- web/app/settings/contact/page.tsx | 40 +- web/app/settings/delete/page.tsx | 73 +-- web/app/settings/disclaimer/page.tsx | 54 +- web/app/settings/layout.tsx | 17 +- web/app/settings/page.tsx | 98 +-- web/app/settings/profile/page.tsx | 49 +- web/app/signup/common.tsx | 14 +- web/app/signup/steps/step1_profile.tsx | 5 +- web/app/signup/steps/step2_img.tsx | 10 +- web/app/signup/steps/step3_confirmation.tsx | 14 +- web/app/signup/steps/step4_course.tsx | 6 +- web/app/tutorial/page.tsx | 12 +- web/bun.lockb | Bin 124783 -> 158550 bytes web/components/BottomBar.tsx | 2 +- web/components/LogOutButton.tsx | 11 +- web/components/about.tsx | 41 -- .../common/FullScreenCircularProgress.tsx | 15 +- web/components/common/alert/AlertProvider.tsx | 53 +- web/components/human/humanListItem.tsx | 140 ++-- web/package.json | 5 + web/postcss.config.js | 6 + web/tailwind.config.js | 27 + 36 files changed, 723 insertions(+), 1053 deletions(-) create mode 100644 web/app/globals.css delete mode 100644 web/app/index.css delete mode 100644 web/components/about.tsx create mode 100644 web/postcss.config.js create mode 100644 web/tailwind.config.js diff --git a/.cspell.json b/.cspell.json index 22705a27..0b130d14 100644 --- a/.cspell.json +++ b/.cspell.json @@ -4,20 +4,22 @@ "words": [ "autoincrement", "bunx", + "chrono", "coursemate", + "daisyui", "datasource", "direnv", "ECCS", "isready", "lockb", + "mytheme", "notistack", "psql", "qiita", + "reqwest", "safify", "supabase", - "swiper", - "reqwest", - "chrono" + "swiper" ], "dictionaries": [ "softwareTerms", diff --git a/biome.json b/biome.json index 6381c36c..ff3fe524 100644 --- a/biome.json +++ b/biome.json @@ -4,6 +4,13 @@ "indentStyle": "space", "indentWidth": 2 }, + "linter": { + "rules": { + "nursery": { + "useSortedClasses": "warn" + } + } + }, "vcs": { "enabled": true, "clientKind": "git", diff --git a/web/app/chat/layout.tsx b/web/app/chat/layout.tsx index 49f79339..c40e4153 100644 --- a/web/app/chat/layout.tsx +++ b/web/app/chat/layout.tsx @@ -1,6 +1,6 @@ -import { Box } from "@mui/material"; import BottomBar from "~/components/BottomBar"; import Header from "~/components/Header"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; export default function ChatPageLayout({ children, @@ -8,24 +8,12 @@ export default function ChatPageLayout({ children: React.ReactNode; }) { return ( - <> +
- +
{children} - +
- + ); } diff --git a/web/app/chat/page.tsx b/web/app/chat/page.tsx index 09c0b7c7..8546f988 100644 --- a/web/app/chat/page.tsx +++ b/web/app/chat/page.tsx @@ -7,7 +7,6 @@ import { useRoomsOverview } from "~/api/chat/hooks"; import RoomList from "~/components/chat/RoomList"; import { RoomWindow } from "~/components/chat/RoomWindow"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; function ChatListContent() { const searchParams = useSearchParams(); @@ -32,10 +31,8 @@ function ChatListContent() { export default function Chat() { return ( - - }> - - - + }> + + ); } diff --git a/web/app/edit/courses/page.tsx b/web/app/edit/courses/page.tsx index b4b9b9b2..e55e6ec7 100644 --- a/web/app/edit/courses/page.tsx +++ b/web/app/edit/courses/page.tsx @@ -1,10 +1,8 @@ "use client"; -import { Box, Button, Typography } from "@mui/material"; import Link from "next/link"; import { useAboutMe } from "~/api/user"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; import EditableCoursesTable from "~/components/course/EditableCoursesTable"; export default function EditCourses() { @@ -14,65 +12,27 @@ export default function EditCourses() { const error = state.current === "error" ? state.error : null; return ( - - - - 授業編集 - - {loading ? ( - - ) : error ? ( -

Error: {error.message}

- ) : data ? ( - <> - - - ) : ( -

データがありません。

- )} - - - - - -
-
+
+

授業編集

+ {loading ? ( + + ) : error ? ( +

Error: {error.message}

+ ) : data ? ( + <> + + + ) : ( +

データがありません。

+ )} +
+ + 設定画面に戻る + + + プロフィール編集へ + +
+
); } diff --git a/web/app/edit/layout.tsx b/web/app/edit/layout.tsx index 7a6df71e..bc1ad10f 100644 --- a/web/app/edit/layout.tsx +++ b/web/app/edit/layout.tsx @@ -1,5 +1,5 @@ -import { Box } from "@mui/material"; import Header from "~/components/Header"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; export default function EditPageLayout({ children, @@ -7,23 +7,11 @@ export default function EditPageLayout({ children: React.ReactNode; }) { return ( - <> +
- +
{children} - - +
+ ); } diff --git a/web/app/edit/profile/page.tsx b/web/app/edit/profile/page.tsx index 2e0f34e1..55e20763 100644 --- a/web/app/edit/profile/page.tsx +++ b/web/app/edit/profile/page.tsx @@ -3,7 +3,6 @@ import EditIcon from "@mui/icons-material/Edit"; import { Box, - Button, FormControl, IconButton, InputLabel, @@ -21,7 +20,6 @@ import { facultiesAndDepartments } from "~/app/signup/data"; import type { UpdateUser } from "~/common/types"; import { UpdateUserSchema } from "~/common/zod/schemas"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; -import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; import { useAlert } from "~/components/common/alert/AlertProvider"; import PhotoModal from "~/components/config/PhotoModal"; import { PhotoPreviewButton } from "~/components/config/PhotoPreview"; @@ -222,331 +220,321 @@ export default function EditProfile() { }; return ( - - - {loading ? ( - - ) : error ? ( -

Error: {error.message}

- ) : data ? ( - - - プロフィール編集 - - - - setTmpName(e.target.value)} - label="名前" - disabled={!isEditingName} - fullWidth - error={!!nameError} - helperText={nameError} - autoComplete="off" - /> - { - if (isEditingName) { - setName(tmpName); - handleSave({ name: tmpName }); - setIsEditingName(false); - } else { - handleEdit(setIsEditingName); - } - }} - > - {isEditingName ? ( - 保存 - ) : ( - - )} - - - - - - - 性別 - - {genderError && ( - - {genderError} - + + {loading ? ( + + ) : error ? ( +

Error: {error.message}

+ ) : data ? ( + + + プロフィール編集 + + + + setTmpName(e.target.value)} + label="名前" + disabled={!isEditingName} + fullWidth + error={!!nameError} + helperText={nameError} + autoComplete="off" + /> + { + if (isEditingName) { + setName(tmpName); + handleSave({ name: tmpName }); + setIsEditingName(false); + } else { + handleEdit(setIsEditingName); + } + }} + > + {isEditingName ? ( + 保存 + ) : ( + )} - { - if (isEditingGender) { - setGender(tmpGender); - handleSave({ gender: tmpGender }); - setIsEditingGender(false); - } else { - handleEdit(setIsEditingGender); - } - }} - > - {isEditingGender ? ( - 保存 - ) : ( - - )} - - - + + +
- - - 学年 - - {gradeError && ( - - {gradeError} - + + + 性別 + + {genderError && ( + + {genderError} + + )} + { + if (isEditingGender) { + setGender(tmpGender); + handleSave({ gender: tmpGender }); + setIsEditingGender(false); + } else { + handleEdit(setIsEditingGender); + } + }} + > + {isEditingGender ? ( + 保存 + ) : ( + )} - { - if (isEditingGrade) { - setGrade(tmpGrade); - handleSave({ grade: tmpGrade }); - setIsEditingGrade(false); - } else { - handleEdit(setIsEditingGrade); - } - }} - > - {isEditingGrade ? ( - 保存 - ) : ( - - )} - - - + + + - - - 学部 - - {facultyError && ( - - {facultyError} - + + + 学年 + + {gradeError && ( + + {gradeError} + + )} + { + if (isEditingGrade) { + setGrade(tmpGrade); + handleSave({ grade: tmpGrade }); + setIsEditingGrade(false); + } else { + handleEdit(setIsEditingGrade); + } + }} + > + {isEditingGrade ? ( + 保存 + ) : ( + )} + + + - { - if (isEditingFaculty) { - setDepartment(""); - setFaculty(tmpFaculty); - handleSave({ faculty: tmpFaculty, department: "" }); - setIsEditingFaculty(false); - } else { - handleEdit(setIsEditingFaculty); - } - }} - > - {isEditingFaculty ? ( - 保存 - ) : ( - - )} - - - + + + 学部 + + {facultyError && ( + + {facultyError} + + )} - - - 学科 - - {departmentError && ( - - {departmentError} - + { + if (isEditingFaculty) { + setDepartment(""); + setFaculty(tmpFaculty); + handleSave({ faculty: tmpFaculty, department: "" }); + setIsEditingFaculty(false); + } else { + handleEdit(setIsEditingFaculty); + } + }} + > + {isEditingFaculty ? ( + 保存 + ) : ( + )} + + + - { - if (isEditingDepartment) { - setDepartment(tmpDepartment); - handleSave({ department: tmpDepartment }); - setIsEditingDepartment(false); - } else { - handleEdit(setIsEditingDepartment); - } - }} - > - {isEditingDepartment ? ( - 保存 - ) : ( - - )} - - - + + + 学科 + + {departmentError && ( + + {departmentError} + + )} - - - setTmpIntro(e.target.value)} - label="自己紹介" - disabled={!isEditingIntro} - fullWidth - autoComplete="off" - error={!!introError} // エラースタイル適用 - helperText={introError} // エラーメッセージを表示 - /> + { + if (isEditingDepartment) { + setDepartment(tmpDepartment); + handleSave({ department: tmpDepartment }); + setIsEditingDepartment(false); + } else { + handleEdit(setIsEditingDepartment); + } + }} + > + {isEditingDepartment ? ( + 保存 + ) : ( + + )} + + + - { - if (isEditingIntro) { - setIntro(tmpIntro); - handleSave({ intro: tmpIntro }); - setIsEditingIntro(false); - } else { - handleEdit(setIsEditingIntro); - } - }} - > - {isEditingIntro ? ( - 保存 - ) : ( - - )} - - - - {errorMessage && ( - - {errorMessage} - - )} + + + setTmpIntro(e.target.value)} + label="自己紹介" + disabled={!isEditingIntro} + fullWidth + autoComplete="off" + error={!!introError} // エラースタイル適用 + helperText={introError} // エラーメッセージを表示 + /> -
-
- - プロフィール画像 - -
-
{ + if (isEditingIntro) { + setIntro(tmpIntro); + handleSave({ intro: tmpIntro }); + setIsEditingIntro(false); + } else { + handleEdit(setIsEditingIntro); + } }} > -
- -
- setOpen(true)} - /> - setOpen(false)} - afterUpload={afterPhotoUpload} - onError={onPhotoError} + {isEditingIntro ? ( + 保存 + ) : ( + + )} + + + + {errorMessage && ( + + {errorMessage} + + )} + +
+
+ + プロフィール画像 + +
+
+
+
+ setOpen(true)} + /> + setOpen(false)} + afterUpload={afterPhotoUpload} + onError={onPhotoError} + />
+
- + - - + 設定画面に戻る + + - ) : ( -

データがありません。

- )} - - + + ) : ( +

データがありません。

+ )} + ); } diff --git a/web/app/faq/page.tsx b/web/app/faq/page.tsx index 16184255..a6d422fb 100644 --- a/web/app/faq/page.tsx +++ b/web/app/faq/page.tsx @@ -1,96 +1,47 @@ "use client"; -import { ArrowBack } from "@mui/icons-material"; -import { Box, IconButton, Typography } from "@mui/material"; -import { useRouter } from "next/navigation"; import Header from "~/components/Header"; +import TopNavigation from "~/components/common/TopNavigation"; export default function FAQ() { - const router = useRouter(); - return ( - + <>
- router.back()} - > - - - - - - よくある質問 - - - - Q: 東大生以外も利用できますか? -
- A: - 本サービスは東大生のみを対象としています。それゆえ、ECCSアカウントによるログインが必須です。他のGoogleアカウントではログインできません。 -
- - - Q: 授業登録機能はすべての学部に対応していますか? -
- A: - 本サービスの授業登録機能は前期教養学部のみに対応しており、今のところ後期学部には対応しておりません。 -
- - - Q: 収集された個人情報はどのように利用されますか? -
- A: - 収集した個人情報は、サインインおよびサービス提供の目的にのみ使用され、他の目的には使用されません。 -
- - - Q: 東大公式のアプリですか? -
- A: - 本サービスはut.code();によって運営されており、東京大学は運営に関与しておりません -
-
- +
+
+ +
+

+ {/* TODO: この辺の構造を直す */} + Q: 東大生以外も利用できますか? +
+ A: + 本サービスは東大生のみを対象としています。それゆえ、ECCSアカウントによるログインが必須です。他のGoogleアカウントではログインできません。 +

+ +

+ Q: 授業登録機能はすべての学部に対応していますか? +
+ A: + 本サービスの授業登録機能は前期教養学部のみに対応しており、今のところ後期学部には対応しておりません。 +

+ +

+ Q: 収集された個人情報はどのように利用されますか? +
+ A: + 収集した個人情報は、サインインおよびサービス提供の目的にのみ使用され、他の目的には使用されません。 +

+ +

+ Q: 東大公式のアプリですか? +
+ A: + 本サービスはut.code();によって運営されており、東京大学は運営に関与しておりません +

+
+
+
+ ); } diff --git a/web/app/friends/layout.tsx b/web/app/friends/layout.tsx index a471c311..735f5e62 100644 --- a/web/app/friends/layout.tsx +++ b/web/app/friends/layout.tsx @@ -1,4 +1,3 @@ -import { Box } from "@mui/material"; import BottomBar from "~/components/BottomBar"; import Header from "~/components/Header"; @@ -10,21 +9,9 @@ export default function FriendsPageLayout({ return ( <>
- +
{children} - +
); diff --git a/web/app/friends/page.tsx b/web/app/friends/page.tsx index 18817fcb..c9461704 100644 --- a/web/app/friends/page.tsx +++ b/web/app/friends/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { Box, Tab, Tabs } from "@mui/material"; +import { Tab, Tabs } from "@mui/material"; import { useState } from "react"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; import Matchings from "~/components/match/matching"; @@ -15,34 +15,17 @@ export default function Friends() { return ( - +
- - +
+
{open === 0 ? : open === 1 ? : null} - +
); } @@ -60,7 +43,7 @@ function TabPanel({ id={`tabpanel-${open}`} aria-labelledby={`tab-${open}`} > - {children} +
{children}
); } diff --git a/web/app/globals.css b/web/app/globals.css new file mode 100644 index 00000000..c9334c97 --- /dev/null +++ b/web/app/globals.css @@ -0,0 +1,13 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +.icon-image-preview { + /* TODO: make it flexible */ + max-width: 450px; + max-height: 450px; +} + +.cm-li-btn { + @apply no-animation h-auto w-full justify-start rounded-none border-none bg-white px-4 py-4 text-left font-normal text-base shadow-none hover:bg-zinc-100 focus:bg-zinc-300; +} diff --git a/web/app/index.css b/web/app/index.css deleted file mode 100644 index c29a91ee..00000000 --- a/web/app/index.css +++ /dev/null @@ -1,5 +0,0 @@ -.icon-image-preview { - /* TODO: make it flexible */ - max-width: 450px; - max-height: 450px; -} diff --git a/web/app/layout.tsx b/web/app/layout.tsx index cb24e25a..8c7ac0a6 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -3,7 +3,7 @@ import { CssBaseline, ThemeProvider, createTheme } from "@mui/material"; import { SnackbarProvider } from "notistack"; import React from "react"; -import "./index.css"; +import "./globals.css"; import "@fontsource/roboto/300.css"; import "@fontsource/roboto/400.css"; import "@fontsource/roboto/500.css"; diff --git a/web/app/settings/aboutUs/page.tsx b/web/app/settings/aboutUs/page.tsx index e742e407..97293496 100644 --- a/web/app/settings/aboutUs/page.tsx +++ b/web/app/settings/aboutUs/page.tsx @@ -1,126 +1,81 @@ -import { GitHub, Language } from "@mui/icons-material"; -import XIcon from "@mui/icons-material/X"; -import { Box, Link, Typography } from "@mui/material"; +import { FaGithub, FaXTwitter } from "react-icons/fa6"; +import { MdLanguage } from "react-icons/md"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; import TopNavigation from "~/components/common/TopNavigation"; export default function AboutUs() { return ( - +
- - +
+

CourseMateについて - - +

+
大学の授業を受けている際に、一緒に受ける友達がいなくて困ったことはありませんか? 「友達がいないから授業をサボるようになってしまった」、「過去問を共有してくれる人がいない」 などの東大生の悩みを解決したいと思い、CourseMateは開発されました。CourseMateを使うことで みなさまの大学生活がより良くなれば幸いです。ぜひ知り合いの方々に広めていただければと思っています。 - - - - + - +

ut.code();について - - +

+
ut.code();は、2019年設立の東京大学のソフトウェアエンジニアリングコミュニティです。 「学習」、「交流」、「開発」の三つを活動の軸としており、さまざまなアプリを開発しています。 - - - - + +
+
); } diff --git a/web/app/settings/contact/page.tsx b/web/app/settings/contact/page.tsx index 1481d5ac..1b9b1b71 100644 --- a/web/app/settings/contact/page.tsx +++ b/web/app/settings/contact/page.tsx @@ -1,45 +1,25 @@ -import { Box, Button, Typography } from "@mui/material"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; import TopNavigation from "~/components/common/TopNavigation"; export default function Contact() { return ( - +
- - +
+

ご利用いただきありがとうございます。サービスに関するご意見やバグ報告がございましたら、以下のリンクからお問い合わせください。皆様のフィードバックは、サービスの改善に役立てさせていただきます。 - - - - - + +

+
); } diff --git a/web/app/settings/delete/page.tsx b/web/app/settings/delete/page.tsx index 233be5a4..c32b7ea4 100644 --- a/web/app/settings/delete/page.tsx +++ b/web/app/settings/delete/page.tsx @@ -1,12 +1,10 @@ "use client"; -import { ArrowBack } from "@mui/icons-material"; -import { Box, Button, IconButton, Typography } from "@mui/material"; -import Link from "next/link"; import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback } from "react"; import { deleteAccount } from "~/api/user"; +import TopNavigation from "~/components/common/TopNavigation"; import { useAlert } from "~/components/common/alert/AlertProvider"; export default function DeleteAccount() { @@ -36,59 +34,22 @@ export default function DeleteAccount() { }, [showAlert, enqueueSnackbar, router.push]); return ( - - - - - - - - アカウント削除 - - - +
+ +
+

アカウントを削除した場合、マッチングやチャットに関する情報の一切が削除されます。 - - - - - +

+
+ +
+
+
); } diff --git a/web/app/settings/disclaimer/page.tsx b/web/app/settings/disclaimer/page.tsx index 053d2372..1c18f180 100644 --- a/web/app/settings/disclaimer/page.tsx +++ b/web/app/settings/disclaimer/page.tsx @@ -1,59 +1,29 @@ -import { Box, Typography } from "@mui/material"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; import TopNavigation from "~/components/common/TopNavigation"; export default function Disclaimer() { return ( - +
- - +
+

本サービスはut.code();によって運営されており、東京大学は運営に関与しておりません。本サービスは東大生のみを対象としており、ECCSアカウントによるログインが必須です。 - +

- +

本サービスの機能の利用に伴ういかなるトラブルや損害について、ut.code();は一切の責任を負いかねます。利用者の自己責任においてご利用ください。 - +

- +

本サービスで収集した個人情報は、サインインおよびサービス提供の目的にのみ使用され、他の目的には使用されません。 - +

- +

本サービスを通じて他の利用者と接触した際のトラブルや、マッチングを通じて生じた問題等に関しても、ut.code();は責任を負いません。ご理解とご協力をお願いいたします。 - - - +

+
+
); } diff --git a/web/app/settings/layout.tsx b/web/app/settings/layout.tsx index a32f0987..1fc99689 100644 --- a/web/app/settings/layout.tsx +++ b/web/app/settings/layout.tsx @@ -1,4 +1,3 @@ -import { Box } from "@mui/material"; import BottomBar from "~/components/BottomBar"; import Header from "~/components/Header"; @@ -10,21 +9,9 @@ export default function SettingsPageLayout({ return ( <>
- +
{children} - +
); diff --git a/web/app/settings/page.tsx b/web/app/settings/page.tsx index 1fb8a79e..abf97e34 100644 --- a/web/app/settings/page.tsx +++ b/web/app/settings/page.tsx @@ -1,10 +1,3 @@ -import { - Box, - Divider, - List, - ListItemButton, - ListItemText, -} from "@mui/material"; import Link from "next/link"; import LogOutButton from "~/components/LogOutButton"; import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; @@ -12,47 +5,56 @@ import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; export default function Settings() { return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+
    +
  • + + あなたのカード + +
  • +
    +
  • + + CourseMateの使い方 + +
  • +
    +
  • + + お問い合わせ + +
  • +
    +
  • + + よくある質問 + +
  • +
    +
  • + + About Us + +
  • +
    +
  • + + 免責事項 + +
  • +
    +
  • + + アカウント削除 + +
  • +
    +
  • + +
  • +
    +
+
); } diff --git a/web/app/settings/profile/page.tsx b/web/app/settings/profile/page.tsx index 9475bc9e..f86d993a 100644 --- a/web/app/settings/profile/page.tsx +++ b/web/app/settings/profile/page.tsx @@ -1,9 +1,8 @@ "use client"; -import EditIcon from "@mui/icons-material/Edit"; -import { Box, Button, Typography } from "@mui/material"; import Link from "next/link"; import { useState } from "react"; +import { MdEdit } from "react-icons/md"; import { useAboutMe } from "~/api/user"; import { Card } from "~/components/Card"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; @@ -22,50 +21,30 @@ export default function SettingsProfile() { {loading ? ( ) : error ? ( - Error: {error.message} +

エラーが発生しました。{error.message}

) : !data ? ( - データがありません。 +

データがありません。

) : ( - +
- - - - + +
setBack(back)} /> -
-
+
+
)} ); diff --git a/web/app/signup/common.tsx b/web/app/signup/common.tsx index 84a07e82..c4cdbe32 100644 --- a/web/app/signup/common.tsx +++ b/web/app/signup/common.tsx @@ -1,4 +1,3 @@ -import { Button } from "@mui/material"; export type Caller = "registration" | "configMenu"; export type StepProps = { onSave: (t: T) => void; @@ -20,17 +19,12 @@ export function NextButton({ children: string; }) { return ( - + ); } diff --git a/web/app/signup/steps/step1_profile.tsx b/web/app/signup/steps/step1_profile.tsx index 8314232d..f275c829 100644 --- a/web/app/signup/steps/step1_profile.tsx +++ b/web/app/signup/steps/step1_profile.tsx @@ -2,7 +2,6 @@ import { useState } from "react"; import { Box, - Button, FormControl, InputLabel, MenuItem, @@ -164,9 +163,9 @@ export default function Step1({ onSave, prev, caller }: StepProps) { }} > - + ); diff --git a/web/app/signup/steps/step2_img.tsx b/web/app/signup/steps/step2_img.tsx index 621685ac..d9be0c99 100644 --- a/web/app/signup/steps/step2_img.tsx +++ b/web/app/signup/steps/step2_img.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Typography } from "@mui/material"; +import { Box, Typography } from "@mui/material"; import { enqueueSnackbar } from "notistack"; import { useCallback, useState } from "react"; import type { BackProp, StepProps } from "~/app/signup/common"; @@ -82,12 +82,12 @@ export default function Step2({ width: "100%", }} > - - + + ); diff --git a/web/app/signup/steps/step3_confirmation.tsx b/web/app/signup/steps/step3_confirmation.tsx index a1ad70a7..7a4032e6 100644 --- a/web/app/signup/steps/step3_confirmation.tsx +++ b/web/app/signup/steps/step3_confirmation.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Typography } from "@mui/material"; +import { Box, Typography } from "@mui/material"; import type { BackProp, StepProps } from "~/app/signup/common"; import type { Step1User } from "~/common/zod/types"; import UserAvatar from "~/components/human/avatar"; @@ -69,12 +69,16 @@ export default function Confirmation({ width: "100%", }} > - - + + ); diff --git a/web/app/signup/steps/step4_course.tsx b/web/app/signup/steps/step4_course.tsx index f9d738a4..a8c24d91 100644 --- a/web/app/signup/steps/step4_course.tsx +++ b/web/app/signup/steps/step4_course.tsx @@ -1,4 +1,4 @@ -import { Button, Typography } from "@mui/material"; +import { Typography } from "@mui/material"; import { Box } from "@mui/system"; import Link from "next/link"; import { useMyID } from "~/api/user"; @@ -36,9 +36,9 @@ export default function Step4() { }} > - + ); diff --git a/web/app/tutorial/page.tsx b/web/app/tutorial/page.tsx index 26c0eecb..bf0c6491 100644 --- a/web/app/tutorial/page.tsx +++ b/web/app/tutorial/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { Box, Button, Typography } from "@mui/material"; +import { Box, Typography } from "@mui/material"; import { Navigation, Pagination } from "swiper/modules"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; @@ -99,15 +99,9 @@ export default function Tutorial() { ))} - + diff --git a/web/bun.lockb b/web/bun.lockb index fd54ff76250e33b3ee2d01740be45360a2890fe2..120c913dc4d3cf5bc1451b16e7e8c6d1bbc666cc 100755 GIT binary patch delta 43184 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 delta 23024 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�>`M*35sXGPN>is*;LwR%{`Ww$f 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 } diff --git a/web/components/LogOutButton.tsx b/web/components/LogOutButton.tsx index 0bff3fc9..01805940 100644 --- a/web/components/LogOutButton.tsx +++ b/web/components/LogOutButton.tsx @@ -1,6 +1,5 @@ "use client"; -import { ListItemButton, ListItemText } from "@mui/material"; import { signOut } from "firebase/auth"; import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; @@ -36,8 +35,12 @@ export default function LogOutButton() { }, [showAlert, signOutUser]); return ( - - - + ); } diff --git a/web/components/about.tsx b/web/components/about.tsx deleted file mode 100644 index d6fdc8fc..00000000 --- a/web/components/about.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { GitHub, Language } from "@mui/icons-material"; -import XIcon from "@mui/icons-material/X"; -import { Button, Link } from "@mui/material"; - -export function About() { - return ( - <> - {/* FAQ comes here*/} -

Contact

- -

About Us

-

- ut.code(); - は、2019年設立の東京大学のソフトウェアエンジニアリングコミュニティです。 -

-

-

- - ウェブサイト - -

-

- - ut.code(); の GitHub - -

-

- - X (旧 Twitter) - -

-

- - ); -} diff --git a/web/components/common/FullScreenCircularProgress.tsx b/web/components/common/FullScreenCircularProgress.tsx index 4a6738ea..dd2241a0 100644 --- a/web/components/common/FullScreenCircularProgress.tsx +++ b/web/components/common/FullScreenCircularProgress.tsx @@ -1,16 +1,7 @@ -import { Box, CircularProgress } from "@mui/material"; - export default function FullScreenCircularProgress() { return ( - - - +
+ +
); } diff --git a/web/components/common/alert/AlertProvider.tsx b/web/components/common/alert/AlertProvider.tsx index 63c944f8..cf7a0201 100644 --- a/web/components/common/alert/AlertProvider.tsx +++ b/web/components/common/alert/AlertProvider.tsx @@ -1,13 +1,5 @@ "use client"; -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, -} from "@mui/material"; import { type ReactNode, createContext, useContext, useState } from "react"; export type Alert = { @@ -49,27 +41,34 @@ export const AlertProvider = ({ children }: AlertProviderProps) => { {children} {alertProps && ( - - - {alertProps.AlertMessage} - - - - {alertProps.subAlertMessage} - - - - - - - +
+

{alertProps.AlertMessage}

+

{alertProps.subAlertMessage}

+
+
+
+ + +
+
+
+
+ )}
); diff --git a/web/components/human/humanListItem.tsx b/web/components/human/humanListItem.tsx index 2bcb0e84..bf2a6dae 100644 --- a/web/components/human/humanListItem.tsx +++ b/web/components/human/humanListItem.tsx @@ -1,11 +1,3 @@ -import { - Box, - Button, - ListItem, - ListItemAvatar, - Stack, - Typography, -} from "@mui/material"; import UserAvatar from "./avatar"; import Dots from "../common/Dots"; @@ -49,70 +41,30 @@ export function HumanListItem(props: HumanListItemProps) { }; return ( - - {onAccept && } - {onReject && } - {onCancel && } - - } - sx={{ - pr: 2, - }} - > - +
+

{name}

+ {rollUpName && ( +

+ {lastMessage} +

+ )} +
+
+ + + + {/* TODO: button の中に移す */} {hasDots && ( - +
- +
+ )} + + ); +} + +function ActionMenu({ + onAccept, + onReject, + onCancel, + id, +}: { + onAccept?: (id: number) => void; + onReject?: (id: number) => void; + onCancel?: (id: number) => void; + id: number; +}) { + return ( +
+ {onAccept && ( + + )} + {onReject && ( + + )} + {onCancel && ( + )} - +
); } diff --git a/web/package.json b/web/package.json index 7f6c22ea..2b2576cd 100644 --- a/web/package.json +++ b/web/package.json @@ -22,6 +22,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-easy-crop": "^5.0.8", + "react-icons": "^5.3.0", "socket.io-client": "^4.7.5", "swiper": "^11.1.14", "zod": "^3.23.8" @@ -30,8 +31,12 @@ "@types/css-modules": "^1.0.5", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", + "autoprefixer": "^10.4.20", + "daisyui": "^4.12.14", "eslint": "^9.14.0", "globals": "^15.9.0", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.14", "typescript": "^5.2.2" } } diff --git a/web/postcss.config.js b/web/postcss.config.js new file mode 100644 index 00000000..2aa7205d --- /dev/null +++ b/web/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/web/tailwind.config.js b/web/tailwind.config.js new file mode 100644 index 00000000..df376d37 --- /dev/null +++ b/web/tailwind.config.js @@ -0,0 +1,27 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./app/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + daisyui: { + themes: [ + { + mytheme: { + // available options: https://daisyui.com/docs/themes/#-4 + // optional colors: https://daisyui.com/docs/colors/#-2 + primary: "#039BE5", + "primary-content": "#FFFFFF", + secondary: "#E9F8FF", + accent: "#FFC700", + neutral: "#000000", + "base-100": "#ffffff", + + "--rounded-btn": "0.25rem", + "--rounded-box": "0.25rem", + }, + }, + ], + }, + plugins: [require("daisyui")], +}; From 7b253fb9177bbc85781679fc48aee7f141ca1d59 Mon Sep 17 00:00:00 2001 From: Shogo NAKAMURA <104970808+naka-12@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:04:45 +0900 Subject: [PATCH 021/131] =?UTF-8?q?signup=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=81=AE=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=82=92=E5=BF=98?= =?UTF-8?q?=E3=82=8C=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=81=A7=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=20(#517)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/app/signup/page.tsx | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/web/app/signup/page.tsx b/web/app/signup/page.tsx index 09944965..4bb9bce5 100644 --- a/web/app/signup/page.tsx +++ b/web/app/signup/page.tsx @@ -7,6 +7,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import type { Step1User } from "~/common/zod/types"; import Header from "~/components/Header"; +import { NavigateByAuthState } from "~/components/common/NavigateByAuthState"; import { register } from "./functions"; import Step1 from "./steps/step1_profile"; import Step2, { type Step2Data } from "./steps/step2_img"; @@ -76,18 +77,20 @@ function Registration() { } export default function RegistrationPage() { return ( - -
- - + + +
+ + + ); } From d58d8fcd42dbf85cae652eff31a747f767339bc6 Mon Sep 17 00:00:00 2001 From: KaichiManabe <154493010+KaichiManabe@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:46:48 +0900 Subject: [PATCH 022/131] =?UTF-8?q?Next=E7=A7=BB=E8=A1=8C=E3=81=AB?= =?UTF-8?q?=E4=BC=B4=E3=81=86=E3=80=81=E3=83=AA=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E8=A7=A3=E6=B1=BA=20(#518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- web/app/chat/page.tsx | 16 ++++++++-------- web/firebase/auth/lib.ts | 32 +++++++++++++------------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/web/app/chat/page.tsx b/web/app/chat/page.tsx index 8546f988..b6ced666 100644 --- a/web/app/chat/page.tsx +++ b/web/app/chat/page.tsx @@ -8,6 +8,14 @@ import RoomList from "~/components/chat/RoomList"; import { RoomWindow } from "~/components/chat/RoomWindow"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; +export default function Chat() { + return ( + }> + + + ); +} + function ChatListContent() { const searchParams = useSearchParams(); @@ -28,11 +36,3 @@ function ChatListContent() { ); } - -export default function Chat() { - return ( - }> - - - ); -} diff --git a/web/firebase/auth/lib.ts b/web/firebase/auth/lib.ts index 0d0e22b3..4d6e852d 100644 --- a/web/firebase/auth/lib.ts +++ b/web/firebase/auth/lib.ts @@ -5,25 +5,19 @@ import { app } from "../config"; export class ErrUnauthorized extends Error {} -let user: User; -let token: string; - const auth = getAuth(app); -onAuthStateChanged(auth, async (u: User | null) => { - if (u != null) { - user = u; - token = await user.getIdToken(); - } -}); -async function refreshToken() { - token = await user.getIdToken(true); -} +// 認証状態の完了を待機するためのPromiseを作成 +const token = new Promise((resolve) => { + onAuthStateChanged(auth, async (u: User | null) => { + if (u != null) { + resolve(await u.getIdToken()); + } + }); +}); export async function getIdToken(): Promise { - if (token) return token; - await refreshToken(); - return token; + return await token; } type RequestMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; @@ -39,7 +33,7 @@ export async function credFetch( path: string, body?: unknown, ): Promise { - const idToken = await getIdToken(); + let idToken = await getIdToken(); const init: RequestInit = { method }; if (body) { init.body = JSON.stringify(body); @@ -47,11 +41,11 @@ export async function credFetch( "Content-Type": "application/json", }; } - let res = await fetch(`${path}?token=${idToken}`, init); + if (res.status === 401) { - await refreshToken(); - res = await fetch(`${path}?token=${idToken}`); + idToken = await getIdToken(); + res = await fetch(`${path}?token=${idToken}`, init); } return res; From bd7142bf4b509efc727219d6b448839b7074f860 Mon Sep 17 00:00:00 2001 From: Shogo NAKAMURA <104970808+naka-12@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:39:04 +0900 Subject: [PATCH 023/131] =?UTF-8?q?BottomBar=20=E3=82=92=20daisyUI=20?= =?UTF-8?q?=E3=81=AB=E7=BD=AE=E3=81=8D=E6=8F=9B=E3=81=88=20(#520)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/components/BottomBar.tsx | 129 +++++++++++++++-------------------- 1 file changed, 56 insertions(+), 73 deletions(-) diff --git a/web/components/BottomBar.tsx b/web/components/BottomBar.tsx index c9aa6c6e..8520ec7a 100644 --- a/web/components/BottomBar.tsx +++ b/web/components/BottomBar.tsx @@ -1,84 +1,67 @@ -import ChatIcon from "@mui/icons-material/Chat"; -import HomeIcon from "@mui/icons-material/Home"; -import PeopleIcon from "@mui/icons-material/People"; -import SettingsIcon from "@mui/icons-material/Settings"; -import { BottomNavigation, BottomNavigationAction, Box } from "@mui/material"; import Link from "next/link"; +import { MdHome } from "react-icons/md"; +import { MdPeople } from "react-icons/md"; +import { MdChat } from "react-icons/md"; +import { MdSettings } from "react-icons/md"; type Props = { activeTab: "0_home" | "1_friends" | "2_chat" | "3_settings"; }; +function BottomBarCell({ + label, + href, + iconComponent, + isActive, +}: { + label: string; + href: string; + iconComponent: React.ReactNode; + isActive: boolean; +}) { + return ( + + {iconComponent} + + {label} + + + ); +} + export default function BottomBar(props: Props) { const { activeTab } = props; return ( - - {/* TODO: 単に Viewer として BottomNavigation を使用しているので Box 等で置き換える */} - - - } - /> - - } - /> - - } - /> - - } - /> - - +
+ } + /> + } + /> + } + /> + } + /> +
); } From 67993bdd921ddc08938a53d19d5bd22c5fd53465 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi <137767097+aster-void@users.noreply.github.com> Date: Mon, 11 Nov 2024 23:29:31 +0900 Subject: [PATCH 024/131] install lefthook (#522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- .cspell.json | 3 ++- .github/workflows/ci.yml | 2 +- .gitignore | 1 + .husky/pre-commit | 1 - Makefile | 12 +----------- README.md | 2 +- bun.lockb | Bin 71964 -> 55420 bytes flake.nix | 2 ++ lefthook.yml | 16 ++++++++++++++++ package.json | 8 +------- 10 files changed, 25 insertions(+), 22 deletions(-) delete mode 100755 .husky/pre-commit create mode 100644 lefthook.yml diff --git a/.cspell.json b/.cspell.json index 0b130d14..7a3271c7 100644 --- a/.cspell.json +++ b/.cspell.json @@ -19,7 +19,8 @@ "reqwest", "safify", "supabase", - "swiper" + "swiper", + "lefthook" ], "dictionaries": [ "softwareTerms", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 106a74bc..969ca1dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: bun-version: latest - run: make setup-ci - - run: make spell-check + - run: bunx cspell --quiet . test: name: Bun Test diff --git a/.gitignore b/.gitignore index 7894de25..9754c10c 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ dist-ssr .env /.direnv +.husky diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index c82ad188..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -make precommit diff --git a/Makefile b/Makefile index 3892e689..52564e1b 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,6 @@ LOCAL_DB := postgres://user:password@localhost:5432/database setup: if [ ! `command -v bun` ]; then echo 'ERR: Bun is required!'; exit 1; fi make sync - bunx husky @echo "auto setup is done. now do:" @echo "- edit server/.env.dev" @echo "- edit web/.env" @@ -17,6 +16,7 @@ setup-ci: make generate-sql sync: sync-server sync-web sync-root copy-common + lefthook install || true @echo '----------------------------------------------------------------------------------------------------------' @echo '| Most work is done. now running prisma-generate-sql (which might fail if .env.dev is not set configured)|' @echo '----------------------------------------------------------------------------------------------------------' @@ -80,16 +80,6 @@ dev-db: @make seed; @echo "Seeding completed." - -precommit: check-branch lint-staged spell-check - -lint-staged: - bunx lint-staged -check-branch: - @ if [ "$(git branch --show-current)" == "main" ]; then echo "Cannot make commit on main! aborting..."; exit 1; fi -spell-check: - bunx cspell --quiet . - # Sync (install/update packages, generate prisma, etc) sync-web: diff --git a/README.md b/README.md index f9d8a2e7..b8081128 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ GNU Make が導入されています。以下は、ユーザーが使うこと - make setup (セットアップします。) - make start (build -> serve します。) - make watch (ホットリロードします。) -- make precommit (type-check, format-check, lint を実行します。husky で自動実行されます。) ### 環境構築 @@ -27,6 +26,7 @@ GNU Make が導入されています。以下は、ユーザーが使うこと - Bun (js) - GNU Make - nvm (optional) + - lefthook - `make setup` を実行します。 diff --git a/bun.lockb b/bun.lockb index 176fd1dd3aad6e186658b20348807dba8f939cc3..b7524f525f8a6b898aedece1f4c713722a21484d 100755 GIT binary patch delta 7808 zcmeHMdvsOB)j#Lv-oS~FCkZ)R$diXRkV}%Aa1%pvFeo8ntVkn|3X;SK7ZMO-1s1jm zN-2?7!ls}F1R@qLAT*{GSQ;UecX)({U?HNI)@9`3ic+Wwq`y7q925KT*>81yZU5<6 z+2=QV_MSa^_UxHEnX~#W>#g7VR5~4#$GV56f3+<6jRkGK8IbdsbHNPBjcq%Oe8zyRP> zv|=|$pnD!p23~B%1!O)5uNa^W$nCyHmy9OA9m-w7XQGn5xrP_FZ#M8e5PxOm4R~{h zM-YQI-T3d@ZKrsqEc-yIE#4Z{~#Qf5G z^-y;OkLfIL!@P3n3n%9nPepkZh(iwN#A=9+)AgnSIVo#_BH4_3L_z35ChSZapgZ<( zA__d`$v~ch2q1fC19IrD^*53S$nrh?Yp3`Qvr>4MN;#gqT{QV=YU!dWtKjHh$U9MM zLTYD&@ukGBZaI#;T{UF_wwwE)pipa=TX_ZCaKS-C`5YWKwy{j^OJ09X&Zic<*ODVZ zQ{Knc^aSJqB+$9ixN zJvhk|q{#*34bqf#*o_83Vx`2W)Xreo5ewA>y5*ka3D)FkX@n@fXO?7zys9Rr zQ48K%$k9Vnt|E7NR2DIUaY%7Jv^;`Ja4eH)Mxa}11gDRVxnQhDmRYGL$gMmEj%C7% zZ8^9fiJqHL8Y)VCu^4l?q|8U@5z!Knww(bdbQKr&1x_%;5`E7B=hW>7x@}%?V}+d% zr1%iJ8kR1PB1Z&f8T`Z0lSQkm;5dcghre4+q?QQHHWNPqLnuBZUEV>SNKNUA#iOSg zk7Fb__8h&2xD_%un;7x?;Pl*LYeD3V(v)Xk3y#BRc@s2vEXb~;D%61 zOuBN?s25|GL&?!wQ$|NgQo(mEcY))6tu!OXt$YivJ;FnyC25%6KjK{hE*qRLwRUsc z8o`MuD}gckc+m&OQ2>sk?L#xbZ3EYC;W{{utL}3u_BrlLA06*{db`$Ow|ta5{WOke zKTUbOwYRsr^hhW<v^g!)Hg5nodNXaX6@@$$(rrocyi&L{7o`!RbQGnLqSOnEC0CTD zpd|DxyPUK+{k}46q!JT9j978}!ysYH=N}C5+znZdwIf+T$aA5OJQmO0kZmIegg)j` zJir*yun$-^h-ndsh>J%c6%CFo<8JViC8QX%co?}JLlX4^MXe-tL>@}EQU628c5)0o zM)n5lPdq?O7@iIl-|_e9e#osW4hp{tg8#8$RS>(2y%1rz)cGyVD z{vA7s7+4>4%me)XM$$=={=1VPDo=v=VdVVC2XVAI?jT?V&-MS^NjMrEw>D!X|Hrm6 zqyPWdPBQilJtF`AJ4x-X>?S95oj*dZpt18u%P&(6-U~_jS4WcHzB*f)ri?V55#Uffw-GVsN3p~?HihwWR4582^ zI!#e4t+hR0S#0+Utkj2;T1k(-J9waUr%ivjb_s|VLKZW4m}hg7jW(N>k#kQB4X6vZ z@m9K#Jau-*dX#tr^aWw(5gV)m^aJ64K>k0TFOVdEdb6%yPBlueg9<=-pvOV4f$(pH zI9KptCJ~ej;-7IhC>4|j!oMGq2D%@VMfvZAIQZm+{|=-`P!xzyZhQpdUxFA=EQsUq zG}XO#qKwz~lOR5c`~>s_Xgp{FXe4dM~J6D`5>Gz#lZ|GUU3fW z19XCT2hIi!2Ys*%FFcjM($R15PhClwQIx(XRF-P<_N=nRaX^=XIKqoTi$DuOFM}#T zdyJ;V8|j-r43SluzE727sbXKQofjicpye|^82;(~A1wUZBBweNlbs0I4r+zg5&H0` zYQG4rFld#1GWX`EH~d4}wXW02!zzv0-^1Pp9e?Q5znwMftLmV!7I{LVGtrrV`J%=9 zRl9jL*ef8a;Bw=e&Fwb&QT={Zc2oQr)oxxiO3kxA`|YL&KWo<-K>-KU6!QW$QQOex zg_nYQpcj|Z<-`%wyrP|_ov}aEcqki^B)$JxH2#1pFQAzRTyhm{IiT9j>)>(n>I=he zu6+i&gPaLYq{_$i(Scm~af&~v+RajyjD`po6gk5K+c z^H)r34y$(ac6IOg9YeAc!aKD`UCgh%oGwGlyu`h=<@7fr+b;bSy(c@9oLE>}De}k< z&9InTTnv>!>wll&23mhamCw^VcsEnfF;%`sUmQ_W%-igXZ*K`GeC&;N=$GdlnPuK@ zU-@gnhfjudPKJb|3P})UG^lp-emt~o_pqADAG|GECOEMLWYCNTRo+eup>-E4$-0$H z+6}D^D?#_jZeD+1`O}w&w-w$V{P*$d*g}sWXGxe99d{Jz1J;KsCJGmM4<2*b%}em| z#=UJ{CI`%f+gM0Wmn6+6g5114w^j}HE*w$%7UW5~!>`dLgl#*0d+e^mGJ=yy1COhA z^Xh!dS2wE@-v8ndtR(9TCYB11yX@vQ?6-Fgl%6}BA1ve|{e!9IxGKj`1FV?0xN|3@ zdH#|!w;EbVd)`2DsO`9#V&3|GGXB9a8FO}yfZXuHycaG?qnG>rINQ^1zk+g4V9A8) z_risJ$H#tDzcU{4B%~Xb#Acd%LPa{Qf|hv;y}L2&#U(H8oM_lbQa00x6KaZiYn@gb zv-SMt1Wpl-75p>rwcR(?%v)u_KO0oPWQmJysovdg#r&|aYu%Kux8BzjV@PmGIfd$A+58aUczxQo>z}7) zwOd|Jt>}LP-E&H{o8K|c?u^*G>Dr3Gb}Pq7JEaaXzk9^~N^aWUk~JOj6g@x8FC)XI z#nwk0d})ngKQTeN)18)_3b&h|QksLaQfrT=Uxort9Trg{)t^%32j~JEFh9undVl}v z>V0v)5FLocQb+-()fDps&L5xneEK~%?W@}5<|m%W&MS_76Llg&IDibm|AI9Bv}zaM zgb>=yse3Ie4-G4UoTm=^|30cZt;%O<3$)BHN;md2Jo=0^C&P?%TlYZJ3gCyIQ zzTev&sikq#e{}W1p>dsN=_zhyZ<=tTM`j!vkVt)j_f_)T+F<2dRg zo7e$-Dz^l#0lWj{gyRDu!qG&Q1DX{31EU~C(t(LqYocSqA_8Q|Jgt=_P8BMw6dH({ z&jei;4JHVJA3|sy&cs$4KmK)OT3$dGp@OTxXd++^*cug|L0xKiO~A(myiLGsfJwoH zz|`?1p*#rK0`vf(+#47o6P3VJ-=a3`PYtRH6&&GAL@#ThGc_%5OlP7AFqu9uG$4Eo z<~SQbCbtEq)zG??u3kJaIpscJa<*upyopc`*5LWKZFD$V8x|BBqgKnp!!)rmSgggZ zb%HFEIsvi3Wa2Pj5^N^mnBbs5$dVmFeG`;lfhL44>~ubGTA+`DCdKR7>)J;}s$;_< z)UqX@sa$&i6-m>U4!R0EA+RdwJVB96VB-H{2c;MRSw=oZ3eR#D*%d*9{Tq-oR+DP3(XSb?6REBTxd3s z^+ka}>QUoDz>m=2f)qMVGbSDwGWZb|y6b!(3z$~dJYaJ63BYwQiHZKmke7A_hGQq% z0oMaoxaaE`b~4buOKBQkyy0zuf0cc4r^_C+E$T35__1H6Cftwi8D7o1^QwUto-~;l zW$O1eoZA#_<$HBN-pX^WO>aJU5;SyRnOC82FS~=Q6-9??6^?wk{ARPt-xm1Cta#r! zz@z;LRcC{o<s{PBBLxcFnrAYj9$(ef`1?>3zGPO&Z)LXT-c3 z+>&W?_cV;!e}7J!;Po2T+<)}DrOVcbX0(^Do%K}RETBba`S;|NBV%uIPhwk4^qAAM z=RbB3*C6cdt(pF>e)sdU82N@Zh#puxFH_g+%!bQ<<=k1RT*ZxL3KJEV&HPMM z@+Vl4UYI2#7GUDWxv)}{h2cnTgEBeGsO%=s1l5J7sK#5CT3N+8uu@#ZnWBn{ThIKe zsN^?s><&XMJzkC%D+MQqDXOaEFK_~qG;pf8ajjTtRTUS?O2OHJ6V?|TLuOgUP5vDe z)vH9kavm(Tnu?2HrPWmO9GtZ9)MAatmExMq6s9Wf zHuJ;PiKXH?nU&&toGEIk3@c%ov}G11p7Q=!AKrYw7?C^~6v@+PmXLRvDa=%cZQ%9o zSbAkoZX!!HQ^}7JtneKei2{Fgj zVx@IdoG(+tK_*T>wz+z`9FXnC(>tVyvIU0>+7%0*nJ=buPuMj9qQFV7?5IWx?VGUTRg-ZTw1DUL!*n#{ss3AHE z-qMt%HdJvMR@zV{--;8GR5fG)rf%{#pr{jm<~diE+DIi&Xeg6)Lm3PVFFFoNXHrti zxRFfO2R!&Vctb#u!aQY|1`6KT$Wwk@DCdXHHD-R6D*33!GTA7xuKX}4>K82=xyg+z zrAF=rsuOrR!z~2W8I%#TG;@<*14Y{KhL)RK>BfwjFi`|3GB|h`*)C8OQoe)wp{d*l zcNsLPx)II+rE6zt>dsP|sK}yCRPul(GFdQcVag!y7$}mbTisQg>L}Q)h8x$Pl{Qt$ zQ-w0#g}8F&*G$D(veaf6do!`EWdk?Eb)bgQs@2nL&W1Mkmisgp703X40jLfvy@99v z9CA9-lL9qb{Gd>)FDQO7%GV)BL(>iB2`EocRrqDzyrsyaML!0VBY1`^!_3Vv9Te}q z+&iXdrNXA_hwD_9ifa)o#kDe1*r>Sf%+E$uBh^ONI~J`>4`9NQ<;_`AUAZwBw3Eo# zttxI+JzDFQDr(4#P{oFwl^a)$La8(A7*k{B5W$zqOHfMtjBfvKsnm6V9m&+qa3-ie zEZx*oUV@wowM@{?j-<)!Vg*uD=3-)vJ$0Qyrs_8mK%f74h(*^=-D~)`i#MZ3=#|F2SDkzUfRDtgQEE| z=J%c{PP!dR=lbVBk*-GkLaYSq;yy+vTCM^`9r5!l*Z#!YA@FoN>w8cYTfGDBx!`$~ zJ>?qY$dtOC^FYyPVHa!xwa_hTg@dw%yYUZ#_JcT3v_^EoPl6(|=oGH%rc+U$&chlV zL6Hyx7zjtQtdh=1rt&Iaz%O{~!K2M|u97KKhpWK%Z-DY-Wj0<07_%QsxA8P|^`J9{ za&wUD&F8Kn*PYKb#TfhYx$(&LM!7d zz%=200Gjk+07`_(Dgi_w{X3ZC1_DUCAOIy`6tf8Dx>_m@1`rMbP$CRZmZcGa^k0Fg zzjXZ0;X6R)e+cXPCr$om6cGCVf2sg^PKGcbDJGMz7Rm_|t<4lNYk4A6dQrw2_ToP>5c?S9PApj|G1V9}g74R`&Dn9|BM40$R zL?96+`XmuZe}$?2X#nA303{uU{V5{{lu#)WVG>wM1QKDQuM&au?_jb(8Gzc|08k=K zbU6`7Jf``l3^8s3DBa|*I!reL4+Q%E6jSp`0*HBW&!(rT{~eq5|L)lVWO}532a}%$ z0%&snch80qkSexlS~EWt{5R7={}PSi!d?rkcM+3Z|v4tHkq$<5=v5BPc> z=$2MseK0cM*!H<~ySxs@#=*(HRsRM&OXr;Y)nN3hcRoGiZmrzkDPvd3l^JH8hVHVj zHG~yta#_N;SuLJkn71*apI#&F(peLedPGgX-ml8H@?j09?DuHon3Voy_|cCN!Ly}; z<08`iT%zw4JW@1xnl{!!<1}{1qE4H7%|GBa*?dA--|yXwj%qw(mOlMcSYwy^1>>`)1y$aha4O88Q8l@PHo1pQ*ryTC-;F=wI&z_^kDuc;H}m0f zB_|aL2`l0|vetDp_GP&-D;~_pL@G0=dh!Vrg4*6HAZACpEIjo#I>WzBv&iU-F&IwtMT4Xt!hX4 zn{G5bVsWD2_Sp~NYn;wC=)Lps7rkoTydLdozU1&yW9Pox%6ipx#~UHwN+tt6)leXcB!2u_j;4!`mB6*r>2Hg8VVbI)5H z^{c-&w`^Uf_D%Y^-@C~|ZuT}hR_D&vh$E4kjVKnJvy^gC5+G{>vSBJY@u`hc+_GQA3 z=&p;WCa*4eeXwrC@%bz6ZV#F8Zu_Rj61gc-x$l}6SY0^xsH8;u=b!Q!2lof>`R;o( ze!#rI0=!;A?xn_Eqo(>!9+y$@$bUA5i5X#IVOU?vqD z(IRDqk#=6llK0W0;}XrS!dZBij5%)ht`4CcJJkEyHrlUpqb(7O4h>r6m7C@Nrlz+4 z&crSuooBR4UcAOhtD3Q0B6y)xu*aYsdrF%a7I;P-d2x3{?+>aAZ?&m+ZjaeDt>@^4 zciK)L(%<9ky&0F|{HiV3&}R7bIJJ_L>*Ds-RqK~7DQ@q7&u-7fS>itSWRJSM=i1(|TI7KK>CON;h{K@zQ-$)R+jbGw+rU7~AN^ zi>mLVf7Rx7D_QEveNH%aH+ayv;?G0h$Xa;!oOXM4wFVNwOQeFMyizYc_d0Z8+syO7 zw9;hlo%(pli+gu6w}nUOm&G*S|2!h;)63j)>%P9X>-4#sGHUVU-+Mf0eq4X}`JJQZ zk2$|CPa=1z@T-&;Z!j<>P;=%;*8{)3`g3LX81+JTJH4p1uPsLu_sUwm^W9D(x7)i0 z?D2{C^xb~LgPEyCH}%|)H%uSnzwpq){OO0H1|5(HPL(P=;M>>XJ}(E4Ts>*Z`+4EA zJ_FY537);K#^+u3_19TdiykSUlR8!&4TtUZA6|Gnvs$h2b+?pP zcYo=gGO$r6`x`|Aet-PVz^m-p`{}3Nx|H?5(W30W`P&0WlC>kBPkGdC<~51NE2Nf4 zdq1@HMdukWr_MGiHW~KzVq3efDZYmkD+VU7aGY0q&!+b_-@E!(T^4#L9h{jns>zho zJM34guP=~KUYl3v;?$izByv|u<<_#xn%w$Q+Yj0m=et_|;hT2Ae4FRTjt?%ZY8KWZ zF>k+eu8-WqYg0&8K*ph|gZ16+uDx#at!%)un+;ZE-mX%)O^-xW(G7Kv$?f&KY+I=HSu|(Womv}g zox;AIdf)IxsML!yq=F~TPAIguTQg@s(P_h>?T@#-x%b3&n;yXz?+o;?*}r`IWxt6t zqtfS}dV4uO$zof*ORHRLn|m9*o_A(X(=#1%&N1PV|F&(3?>=+SHm{iWE}`3}kH4|*HBaUr z{@uS-wnT7dg@BnPt~T%2Ee>qi$)ljG{G6?e>#hN5F?;(MEKdoUv1NMZzJYhN zWA9#`(Yu3>&k0R%--c^GaaL6a-k*z`un9IJB!cOgiI@y7`t|)}e8pzlH@(72TjqUq z{B7Rd_0tO%j{5%lh#k4HP9x%`-#GtiTjJgu#SKrkJz_QYRL}ZbzJ?v2bhYX1eTLi9 zIEmaW@fQXOH`ups@`Y6!r`EbSF8N!>S-))>dh)`JJpK8-Sc;ivj=`6rxAW>8ZIqb1 zqF#aaHD1ImV{dCOC3waS7+)hW`Hy$c9<(f8c+EQQMnT!#H|rv2eEG8S?TfU19{1BF zg4c>tfeogu@hw`m=ctdT*^Zmmjh%~c2HxamFFc&Sh;yC)D)f>0;_7`ZlCo=mh`Mll z;?|Mv$L}+^@T%L~TI*eQ#(%xv?L$`%`>(hG(c>mD8HDZmzV5zl+mCL2YnE5{NbTRR zXxS65WYyNRNzYqtSyiXNDq)s({Is;#YKrwol3w_Xsr84&w(*GmO+6BJQ7ug`35*QeLEhMKr#DUT++H?!97 zGylS%&Yd@UM{YUaYV-BT#vbEmR=qa7x>4|qN(CjG+I)7`u1$@sWvKRVD-oO{6+AFY zz9nfyg7&1n)xcFJ&vT`@(P=4{ax(8N&svc%AY(?o`@atu8WKIN_L}HXYZbjGEKfc^ z?0a|BnA|o6IYt*-6iVdgO6ATnEpC!keSp@-=jpj(&!W5G4~y3}el@A^$hOdj@qrzt z*ws;uzM`pp@}*~!j(3|rcyyp7@>`!}S0|qyeI+?^+?k_~C4x6d1siN}>ovUpFPi5| z_BJhVwIgt&)yLeH2PWp$t5YYX+uZHruT0L@|J>s9#@bs`Zs{M>It~pOn^_|=bkh26 z&Ie4KkKYcL2;L|a?E6~}+m-6HjH4Ecn{T-QUu*M`m1<`v?A;PHJ!V6Bd2ZmOfrEP0 z`>=lXwH~`hKee%b(P*V*Ro|VKM%H27@|!fYmdMTfAs6e zSCI(b^g}S@K7XEG$9&ka-#^~CIW(v6Q0FO;hwE;rWWPtPpONZpoHo|t;np1O@RF$~ z?eolgmEM_FFLrB&Uymg_n5wA3mx-OtFG>V&mI_|&vBIre`e&tezJoGjzE_^ljhrRX z_Fat^T5o(HAP3S1Uf% zCHV$sd)I1Se(scCH>gGjvzEJiw4BR&roMW0u(ZzYiy7QxlhAW-b}#YWwqpDr2S-_z zH{JgsaMtMbZX2uFpN;c6pSWInDCJ8Pe&DCswe@6B#EH_p(*qhejq4J4W@G44?p0`0 zZQB#mo=5Mo%NR7HS>BWTwU0l3dqtI_dD>{K>~ZIG>;2n)?N}Dnf1yO zn;gGSK6p!sU+v8yU&cK2?A5Ehfy2k7@(ZRTSCoI9PkmFaa>cLFZR`*AV0>)B1rO$SFh5*(vdy^eV)Br|+-??t>mIfX*8)}} zbTGG)3Yet4hPfG@CV zt@|(wZ8qsq8`Cd%31b2&MtDD14Vbl$h-25ojrB%rST9W-<4btGhbPxmUTDaF-<9tW zn`WNhCpO*KWM+Y2lD+K80x#{i@47^VI{>7@MIw+ynjX1q<4bhU@u?0y$@Ue>b^%iz zdRSE`t`XoS;1=LE;11v} z;2z*lztFD5po+^jMsp*~2>g2W?f^k)-DIeJFjg#y^~)r}}3B z=K$vc7XUQgOMuIOD**Cx@X4f1?UZM05}0KH8MAV zhfFq_-VoCpZF&bRYY*@N_yRftIsrNZx&Rsh8Uri=RseHAeE_{$xCXcmpw|#50jB`_ z0Q&(u0nGr}Y;1C|mhSqO1Jr;3z%amYfInaaU?c!eMsJDv7o`n=EdVzFWq>n)Jph`n zBEV5Vb3hs(7!U}s11JHl01W{x0QP_r^Z{xqGKGMyfNp@pfMb9@g}5NSIK+-#u`(pn zZq2`xvRqG_h-?CYEWa9%0hj}Tcgto2W&vCPD*)*LQY8hD444Z@0?^YIeZT_1d?8P! zSOj1KE!JHKn(8bDET!j4OOQzgklj`SNEiuS1;_>D0M-Nk0MNZhHh|oK+GPT=0BZr{ zjCp_!fQ^7n0CJ_h0J109bvIxafM#t6U^{*A*_e!kwCNgKwy2$p_*JI(sh=_D?_uj; z>#TF+%2?duXO`eNxY#<{I@mX1*A^-8*~Z&NJoT>dm zma$C1U1GP=72HFXyja0~XWA?UJG{8Qo(acpF0SVye#Lvann$Er?e1Om&;>fMwU=3P z!iT`s8;^Q?cEg zW7d6XPZu#5Afjc%QR^3u>aE8)+1lIES`@#_pA)ojbHUXUT7Lu_7hN~+SUTF@V&_s7 zTrhi_+SBEq`=xHlhsDT&8I@YD=$W}?Eu=VLMA#@aEMl3$T>Kt>?b}UFe#ts=rNYL# zsT;}m{5*j!;urVcQ|=TgxmDvTT8Q7|9~{1;U1$4xl`1&=q??Q1@Mr4Jn%ZPs^GN98 z3|(NI95xm+xr5DFt}y@mS?A~5TnsVLn>l2pNtf%66}{*@;WDe5rZ5*G7!sHDIrnPl znZ+jPizW#({fxCwQ*a;IFQ_HPMcmk)Q$4U>)+)Xh>cLCcq?HO6F^*zUtHiUxL2t`= z>tU)LWn!d7?eF_LZ4NEjF35Ms*2>BtS&UQ2u^h7c)3onRgqFPI8dwZnnGLSe<)PC^ z45WB+_tlAwfnU8UM#tOmrxrS^ilHZuBMvVga=-6M^yG|@LKCTU-YT4K?7HQc!#1yQ zG8f}363-s|{>r)f1VK+6D#`3S_+ns&L9)*ubGmD`fKRT4as0u0uKde!3Vhyz80T`- z5~DJ5UcX;$f9d&gUPpf2*|Or5PUhkl{-3`c)m%9dU?z+Ko?*c(Rw>NIkbq{@^IJ7v z+Dap|#|Gog`mR!N1K1eU5(5fu5AQgn?XNq#qZX|g=sJn5T%~Xk0}#p-ulp;%KKn)J z#lcCornYXU32Zjpy8OUSB{+_>%i%<3Pa#S3Fy(sW$6}jP{WR@8U*wr&b zVb0aj1w?q{TjgGTZ12oVv8QdY35&{5xQGE1*Mo=MHEHz*`x#~myOWbl46$G%H@BI7 z{h<#yfBz+Y&)b>cyMhmI7Gp$~44kwzZS?$jp(V!uh0Q}tF{VVN+;<=) zXKJv}KO~D$Cz|us51iThc8|b;%{#Db&`FF^Sv^IsmG#5b`zs`?S@leXix|}sGU3m% z@0b7lR?%LJduf>&x$(^-_YM`Dm27yX!dwiLarO1Puw=&&CP+rlhgou_f-7M;s3nHk zSTEwPA9>h;X3hmO2g`nB<>+4w%W3tzWrMT&p&2}%Up->r&K`ljdkV4 zlxq~`V!+SleMW09%Oh#z#D~p>vw$@UE|UF99vpmEW5$7v%HnD{qa$MGP+* z;a_WFvcz%F8ijg$(D6gZB`YwYF5^#6m3iFDwHpzBF=QZ2b)R{gluq%TN%lSJ3@hQ=@O$n*-w7lLcLHJ6XdxCz3@||(d#x?j4}Lk|{+4c%#26G}>Fzqj za5v{qRrU^EwAmjVN7xT-GF$5+Z^UpA%nNk{ju_h`28;+Z=4dYyqkqJZ51`aF~9{JxIejRF;t+NB{4(>91ooeVjPng9K%cFw?i@VNf%C|^}Py~ zo37E{-#dxnQDV#usXzv@mx%#XVpI-s{xfM}OcVlbw0xvZg$iO^l^Co8j&J~o0a9Yr z4c~^h&)=*4pW8?K7Y4#_mAWvh7&la*!rzZo34cjp(Lee9PgdZy69e5ORuH^f z3?LJul7taqKNLgE#JDAJ+~EswR51umjBNTbAgxRYR7(_tpTMGn6IKvI*~IWDaD*EZ zG3ZTLvbd3V@u~`rzdL)9 zU9W*I%1@Sf@1xV0yVm-86&2>Wv&L)dd5d9ze5~2R(T6H3h=GG*G+G6Re{O-^3$L^B zYdh%Sr``6oR$K@xU2A9QUPJf$@A&L3Uk>%@o81Mp2?lg0c5;|$P}ghySj%-Pa~1Y; z{76om>h5xii>vnS#dSKj@FjXZj!jwTT{%1~GFGXH4G2*O=AR|`3=D`>2S$ZQ#i$Y3t{$(B zj8#X3Asgcq9u=Y-7aautgqQ%)e-fnDOm)fz$T2{<%E zX;8k>f{Ib`e5D(7vL^xRh^WzF${2NsIxa*Vt5gSQVwC}!uz*PA4?%W+#el%SDpumR zB^jZiL@ugIC?(DzXR2Z$(H&JJl+qPK+UHtUrh^nLLMeGoy(+AO!U_V_X-gHYq1FTj zMB@Tu2ab!;M8#;r!vfXJd%dL_Wkh50r91&+2<`D70fNVchwDrf7!{;eMn}Q%$eK=n z!-FP&!%@1hN#~l_{!jxoD|AxwTRrI->T9;aw2W@((}(%!+7 zlFa^m)0*O83!a#zb{5hRkWBiI9KwK Date: Mon, 11 Nov 2024 23:31:03 +0900 Subject: [PATCH 025/131] use object (#474) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- web/bun.lockb | Bin 158550 -> 158550 bytes web/components/ImageFallback.tsx | 37 +++++++++++--------------- web/components/human/WithFallback.tsx | 1 - web/public/avatar.svg | 6 +++++ 4 files changed, 22 insertions(+), 22 deletions(-) create mode 100755 web/public/avatar.svg diff --git a/web/bun.lockb b/web/bun.lockb index 120c913dc4d3cf5bc1451b16e7e8c6d1bbc666cc..d5338f3e146ed596e48fa53dfe1b0874ba9c6f2a 100755 GIT binary patch delta 25 hcmcb1jq}5JJJ<4pC8^$fP>EoOXi8~}~Q3VZ+n delta 25 dcmcb1jq}5JK!7{FkA-eSfV#{q!M2`c~q diff --git a/web/components/ImageFallback.tsx b/web/components/ImageFallback.tsx index f31e65f4..9e449b9e 100644 --- a/web/components/ImageFallback.tsx +++ b/web/components/ImageFallback.tsx @@ -1,29 +1,23 @@ "use client"; - -import { useEffect, useState } from "react"; - type Props = { url?: string; fallback: React.ReactElement; width: string; height: string; - alt: string; }; // https://medium.com/@abhishekmicosoft/handling-img-fallback-307653b2f30 -export function ImageFallback({ width, height, url, fallback, alt }: Props) { - const [ok, setOK] = useState(true); - useEffect(() => { - url; - setOK(true); - }, [url]); +export function ImageFallback({ width, height, url }: Props) { const URL = url?.startsWith("/") ? `${process.env.NEXT_PUBLIC_API_ENDPOINT}${url}` : url; - return ok ? ( - { - console.log("failed to fetch image data of:", URL); - setOK(false); - }} - alt={alt} - /> - ) : ( - <>{fallback} + > + + ); } diff --git a/web/components/human/WithFallback.tsx b/web/components/human/WithFallback.tsx index 2e2f92a9..f9d3981e 100644 --- a/web/components/human/WithFallback.tsx +++ b/web/components/human/WithFallback.tsx @@ -18,7 +18,6 @@ export function AvatarWithFallback({ width, height, url }: Props) { sx={{ width: `${width}`, height: `${height}`, objectFit: "cover" }} /> } - alt="" /> ); } diff --git a/web/public/avatar.svg b/web/public/avatar.svg new file mode 100755 index 00000000..ac3aa6fe --- /dev/null +++ b/web/public/avatar.svg @@ -0,0 +1,6 @@ + + From 2f44a82f8d87f532cf7476ad192c76773b991506 Mon Sep 17 00:00:00 2001 From: KaichiManabe <154493010+KaichiManabe@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:26:58 +0900 Subject: [PATCH 026/131] =?UTF-8?q?step4=E3=82=92=E4=BF=AE=E6=AD=A3=20(#52?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- web/app/chat/page.tsx | 3 +-- web/app/signup/steps/step4_course.tsx | 31 ++++++++------------------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/web/app/chat/page.tsx b/web/app/chat/page.tsx index b6ced666..66a344ed 100644 --- a/web/app/chat/page.tsx +++ b/web/app/chat/page.tsx @@ -1,6 +1,5 @@ "use client"; -import { Typography } from "@mui/material"; import { useSearchParams } from "next/navigation"; import { Suspense } from "react"; import { useRoomsOverview } from "~/api/chat/hooks"; @@ -31,7 +30,7 @@ function ChatListContent() { ) : state.current === "loading" ? ( ) : state.current === "error" ? ( - Error: {state.error.message} +

Error: {state.error.message}

) : ( ); diff --git a/web/app/signup/steps/step4_course.tsx b/web/app/signup/steps/step4_course.tsx index a8c24d91..a9321dd7 100644 --- a/web/app/signup/steps/step4_course.tsx +++ b/web/app/signup/steps/step4_course.tsx @@ -1,5 +1,3 @@ -import { Typography } from "@mui/material"; -import { Box } from "@mui/system"; import Link from "next/link"; import { useMyID } from "~/api/user"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; @@ -9,37 +7,26 @@ export default function Step4() { const { state } = useMyID(); return ( <> - +
{state.current === "loading" ? ( ) : state.current === "error" ? (

Error: {state.error.message}

) : ( - - - 授業情報の登録 (スキップ可) - - +
+

授業情報の登録 (スキップ可)

+
- - +
+
)} -
- +
+
次へ - +
); } From f159108a1270779c04492bb97092c6e44604808a Mon Sep 17 00:00:00 2001 From: KaichiManabe <154493010+KaichiManabe@users.noreply.github.com> Date: Sun, 17 Nov 2024 17:22:03 +0900 Subject: [PATCH 027/131] =?UTF-8?q?=E7=B4=B0=E3=81=8B=E3=81=84DaisyUI?= =?UTF-8?q?=E7=A7=BB=E8=A1=8C=E3=82=92=E5=AE=9F=E8=A1=8C=20(#526)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # PRの概要 ## 具体的な変更内容 ## 影響範囲 ## 動作要件 ## 補足 ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか --- web/app/home/layout.tsx | 17 +- web/app/login/page.tsx | 43 ++--- web/app/signup/page.tsx | 14 +- web/app/tutorial/page.tsx | 55 ++---- web/components/Header.tsx | 45 ++--- web/components/ImageCropper.tsx | 28 +-- web/components/chat/MessageInput.tsx | 39 ++-- web/components/chat/Room.tsx | 33 +--- web/components/chat/RoomHeader.tsx | 39 ++-- web/components/chat/RoomWindow.tsx | 136 ++++---------- web/components/common/TopNavigation.tsx | 49 +++-- web/components/common/modal/ModalProvider.tsx | 33 ++-- .../components/CourseDeleteConfirmDialog.tsx | 72 ++++---- .../CourseRegisterConfirmDialog.tsx | 76 ++++---- .../course/components/SelectCourseDialog.tsx | 167 ++++++++---------- web/components/human/WithFallback.tsx | 10 +- web/components/human/humanListItem.tsx | 118 +++++-------- web/components/match/matching.tsx | 18 +- web/components/match/myRequests.tsx | 18 +- web/components/match/othersRequests.tsx | 17 +- 20 files changed, 394 insertions(+), 633 deletions(-) diff --git a/web/app/home/layout.tsx b/web/app/home/layout.tsx index 5a9eb8f3..9dca6bd3 100644 --- a/web/app/home/layout.tsx +++ b/web/app/home/layout.tsx @@ -1,4 +1,3 @@ -import { Box } from "@mui/material"; import BottomBar from "~/components/BottomBar"; import Header from "~/components/Header"; @@ -10,21 +9,9 @@ export default function HomePageLayout({ return ( <>
- +
{children} - +
); diff --git a/web/app/login/page.tsx b/web/app/login/page.tsx index f4e73469..327ebae2 100644 --- a/web/app/login/page.tsx +++ b/web/app/login/page.tsx @@ -1,6 +1,5 @@ "use client"; -import { Box, Button, Link, Typography } from "@mui/material"; import { GoogleAuthProvider, signInWithPopup } from "firebase/auth"; import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; @@ -125,34 +124,21 @@ export default function Login() { return (
- - +
+
- - - +
+
+

CourseMateを使って
同じ授業の人と
友達になろう - - +

+
- +

- 初めての方はこちら - - - + +
+
); } diff --git a/web/app/signup/page.tsx b/web/app/signup/page.tsx index 4bb9bce5..bc96ff21 100644 --- a/web/app/signup/page.tsx +++ b/web/app/signup/page.tsx @@ -1,6 +1,5 @@ "use client"; -import { Box } from "@mui/material"; import { useSnackbar } from "notistack"; import { useState } from "react"; @@ -78,19 +77,10 @@ function Registration() { export default function RegistrationPage() { return ( - +
- +
); } diff --git a/web/app/tutorial/page.tsx b/web/app/tutorial/page.tsx index bf0c6491..7982323d 100644 --- a/web/app/tutorial/page.tsx +++ b/web/app/tutorial/page.tsx @@ -1,6 +1,5 @@ "use client"; -import { Box, Typography } from "@mui/material"; import { Navigation, Pagination } from "swiper/modules"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; @@ -42,68 +41,36 @@ const tutorialSteps = [ export default function Tutorial() { return ( - +
- +
{tutorialSteps.map((step) => ( - - - {step.label} - +
+

{step.label}

{step.label} - +
))}
- +
ホーム画面へ - - - +
+
+
); } diff --git a/web/components/Header.tsx b/web/components/Header.tsx index 4a2c3bf1..f8c39270 100644 --- a/web/components/Header.tsx +++ b/web/components/Header.tsx @@ -1,6 +1,5 @@ -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { AppBar, Box, IconButton, Toolbar, Typography } from "@mui/material"; import Link from "next/link"; +import { MdInfoOutline } from "react-icons/md"; import { CourseMateIcon } from "./common/CourseMateIcon"; type Props = { @@ -10,32 +9,20 @@ type Props = { export default function Header(props: Props) { const { title } = props; return ( - <> - - - - - - - - - {title} - - - - - - - +
+
+ + + + +

+ {title} +

+ + + + +
+
); } diff --git a/web/components/ImageCropper.tsx b/web/components/ImageCropper.tsx index c3b6f0da..24be6a06 100644 --- a/web/components/ImageCropper.tsx +++ b/web/components/ImageCropper.tsx @@ -1,4 +1,3 @@ -import { Slider } from "@mui/material"; import { useState } from "react"; import Cropper from "react-easy-crop"; @@ -11,6 +10,10 @@ export function ImageCropper({ sameOriginURL: url, onImageChange }: Props) { const [crop, setCrop] = useState({ x: 0, y: 0 }); const [zoom, setZoom] = useState(1); + const handleSliderChange = (event: React.ChangeEvent) => { + setZoom(Number(event.target.value)); + }; + return ( <>
- { - setZoom(Number(newVal) || 1); - }} - /> +
+ + {zoom.toFixed(2)} +
); } diff --git a/web/components/chat/MessageInput.tsx b/web/components/chat/MessageInput.tsx index da8e7fe0..9e359ccf 100644 --- a/web/components/chat/MessageInput.tsx +++ b/web/components/chat/MessageInput.tsx @@ -1,6 +1,5 @@ -import SendIcon from "@mui/icons-material/Send"; -import { Box, IconButton, Stack, TextField, Typography } from "@mui/material"; import { useEffect, useState } from "react"; +import { MdSend } from "react-icons/md"; import type { DMOverview, SendMessage, UserID } from "~/common/types"; import { parseContent } from "~/common/zod/methods"; @@ -58,34 +57,30 @@ export function MessageInput({ send, room }: Props) { } return ( - +
- - +