Skip to content

Commit 34db429

Browse files
authored
イベントのルーティングを修正 (#58)
* イベントを e/:eventId に * Nano ID にハイフン・アンダースコアを含めない
1 parent 269bf09 commit 34db429

File tree

5 files changed

+59
-14
lines changed

5 files changed

+59
-14
lines changed

client/src/App.tsx

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,58 @@
1-
import { BrowserRouter, Outlet, Route, Routes } from "react-router";
1+
import { BrowserRouter, Navigate, Outlet, Route, Routes, useParams } from "react-router";
22
import SubmissionPage from "./pages/eventId/Submission.tsx";
33
import HomePage from "./pages/Home.tsx";
44
import LandingPage from "./pages/Landing.tsx";
55
import NotFoundPage from "./pages/NotFound.tsx";
66
import ProjectPage from "./pages/Project.tsx";
77

8+
/**
9+
* Nano ID 形式の正規表現。
10+
* 現在はハイフン・アンダースコアを含まないが、初期は含んでいたため URL の検証時は両方許容する。
11+
*/
12+
const NANOID_REGEX = /^[A-Za-z0-9_-]{21}$/;
13+
14+
/**
15+
* 旧パス /:eventId から新パス /e/:eventId へのリダイレクト。eventId は Nano ID 形式。
16+
*/
17+
function LegacyEventRedirect() {
18+
const { eventId } = useParams();
19+
if (!eventId || !NANOID_REGEX.test(eventId)) {
20+
return <NotFoundPage />;
21+
}
22+
return <Navigate to={`/e/${eventId}`} replace />;
23+
}
24+
25+
/**
26+
* 旧パス /:eventId/edit から新パス /e/:eventId/edit へのリダイレクト。eventId は Nano ID 形式。
27+
*/
28+
function LegacyEventEditRedirect() {
29+
const { eventId } = useParams();
30+
if (!eventId || !NANOID_REGEX.test(eventId)) {
31+
return <NotFoundPage />;
32+
}
33+
return <Navigate to={`/e/${eventId}/edit`} replace />;
34+
}
35+
836
export default function App() {
937
return (
1038
<BrowserRouter>
1139
<Routes>
1240
<Route index element={<LandingPage />} />
1341
<Route path="home" element={<HomePage />} />
1442
<Route path="new" element={<ProjectPage />} />
15-
<Route path=":eventId" element={<Outlet />}>
16-
<Route index element={<SubmissionPage />} />
17-
<Route path="edit" element={<ProjectPage />} />
18-
<Route path="*" element={<NotFoundPage />} />
43+
44+
<Route path="e">
45+
<Route path=":eventId" element={<Outlet />}>
46+
<Route index element={<SubmissionPage />} />
47+
<Route path="edit" element={<ProjectPage />} />
48+
<Route path="*" element={<NotFoundPage />} />
49+
</Route>
1950
</Route>
51+
52+
{/* /:eventId (旧形式) を /e/:eventId (新形式) にリダイレクト */}
53+
<Route path=":eventId" element={<LegacyEventRedirect />} />
54+
<Route path=":eventId/edit" element={<LegacyEventEditRedirect />} />
55+
2056
<Route path="*" element={<NotFoundPage />} />
2157
</Routes>
2258
</BrowserRouter>

client/src/pages/Home.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ function ProjectDashboard({ involvedProjects }: { involvedProjects: BriefProject
107107
function ProjectCard({ project }: { project: BriefProject }) {
108108
return (
109109
<NavLink
110-
to={`/${project.id}`}
110+
to={`/e/${project.id}`}
111111
className={`group hover:-translate-y-1 relative block transform overflow-hidden rounded-xl border-l-4 bg-white shadow-lg transition-all duration-300 hover:shadow-xl ${project.isHost ? "border-primary" : "border-secondary"} focus:outline-none focus:ring-4 focus:ring-primary/20`}
112112
aria-label={`「${project.name}」の詳細を見る`}
113113
>

client/src/pages/Project.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export default function ProjectPage() {
242242
useEffect(() => {
243243
if (!loading && project && !isHost) {
244244
if (eventId) {
245-
navigate(`/${eventId}`);
245+
navigate(`/e/${eventId}`);
246246
} else {
247247
navigate("/");
248248
}
@@ -613,7 +613,7 @@ export default function ProjectPage() {
613613
)}
614614
<div className="fixed bottom-0 left-0 flex max-h-18 w-full justify-between bg-white p-4 shadow-[0_-2px_8px_rgba(0,0,0,0.1)]">
615615
{eventId ? (
616-
<NavLink to={`/${eventId}`} className="btn btn-outline btn-primary">
616+
<NavLink to={`/e/${eventId}`} className="btn btn-outline btn-primary">
617617
日程調整に戻る
618618
</NavLink>
619619
) : (
@@ -639,12 +639,12 @@ export default function ProjectPage() {
639639
type="text"
640640
disabled
641641
className="input input-info w-full"
642-
value={`${FRONTEND_ORIGIN}/${dialogStatus.projectId}`}
642+
value={`${FRONTEND_ORIGIN}/e/${dialogStatus.projectId}`}
643643
/>
644644
<button
645645
type="button"
646646
onClick={async () => {
647-
await navigator.clipboard.writeText(`${FRONTEND_ORIGIN}/${dialogStatus.projectId}`);
647+
await navigator.clipboard.writeText(`${FRONTEND_ORIGIN}/e/${dialogStatus.projectId}`);
648648
setCopied(true);
649649
setTimeout(() => {
650650
setCopied(false);
@@ -657,7 +657,7 @@ export default function ProjectPage() {
657657
</button>
658658
</div>
659659
<div className="modal-action">
660-
<NavLink className="btn btn-primary" to={`/${dialogStatus.projectId}`}>
660+
<NavLink className="btn btn-primary" to={`/e/${dialogStatus.projectId}`}>
661661
イベントへ
662662
</NavLink>
663663
</div>

client/src/pages/eventId/Submission.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export default function SubmissionPage() {
210210
<>
211211
<div className="flex h-[100dvh] flex-col">
212212
<Header />
213-
{loading || !selectedParticipationOptionId ? (
213+
{loading ? (
214214
<div className="flex w-full flex-1 items-center justify-center">
215215
<span className="loading loading-dots loading-md text-gray-400" />
216216
</div>
@@ -221,12 +221,16 @@ export default function SubmissionPage() {
221221
ホームに戻る
222222
</NavLink>
223223
</div>
224+
) : !selectedParticipationOptionId ? (
225+
<div className="flex w-full flex-1 items-center justify-center">
226+
<span className="loading loading-dots loading-md text-gray-400" />
227+
</div>
224228
) : (
225229
<div className="flex h-full flex-1 flex-col overflow-y-auto p-4">
226230
<div className="flex items-center justify-between">
227231
<h1 className="mb-2 font-bold text-2xl text-gray-800">{project.name} の日程調整</h1>
228232
{isHost && (
229-
<NavLink to={`/${projectId}/edit`} className="btn btn-sm font-normal text-gray-600">
233+
<NavLink to={`/e/${projectId}/edit`} className="btn btn-sm font-normal text-gray-600">
230234
<HiOutlineCog />
231235
イベント設定
232236
</NavLink>

server/src/routes/projects.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { zValidator } from "@hono/zod-validator";
22
import dotenv from "dotenv";
33
import { Hono } from "hono";
4-
import { nanoid } from "nanoid";
4+
import { customAlphabet } from "nanoid";
55
import { z } from "zod";
66
import { editReqSchema, projectReqSchema, submitReqSchema } from "../../../common/validators.js";
77
import { prisma } from "../main.js";
88

99
dotenv.config();
1010

11+
/**
12+
* ハイフン・アンダースコアを含まない Nano ID 形式。
13+
*/
14+
const nanoid = customAlphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 21);
15+
1116
const projectIdParamsSchema = z.object({ projectId: z.string().length(21) });
1217

1318
type AppVariables = {

0 commit comments

Comments
 (0)