Include locale on all links and routers

This commit is contained in:
Takeshi Kimata
2024-05-04 12:19:53 +09:00
parent ec9e2da7bc
commit 747d191412
20 changed files with 104 additions and 61 deletions

View File

@@ -21,7 +21,7 @@ export default function LangSwitch(params: { locale: string }) {
async function changeLocale(nextLocale: string) { async function changeLocale(nextLocale: string) {
let newPathname; let newPathname;
if (params.locale.length < 4) { if (pathname.length < 4) {
// when root path // when root path
router.push("/", { locale: nextLocale }); router.push("/", { locale: nextLocale });
} else { } else {

View File

@@ -43,17 +43,6 @@ export default function RootLayout({
<div className="relative flex flex-col min-h-screen light:bg-neutral-50 dark:bg-neutral-800"> <div className="relative flex flex-col min-h-screen light:bg-neutral-50 dark:bg-neutral-800">
<Header locale={locale} /> <Header locale={locale} />
<main>{children}</main> <main>{children}</main>
{/* <footer className="w-full flex items-center justify-center py-3">
<Link
isExternal
className="flex items-center gap-1 text-current"
href="https://nextui-docs-v2.vercel.app?utm_source=next-app-template"
title="nextui.org homepage"
>
<span className="text-default-600">Powered by</span>
<p className="text-primary">NextUI</p>
</Link>
</footer> */}
</div> </div>
</Providers> </Providers>
</body> </body>

View File

@@ -14,9 +14,10 @@ import {
export type Props = { export type Props = {
messages: ProjectsMessages; messages: ProjectsMessages;
locale: string;
}; };
export default function ProjectsPage({ messages }: Props) { export default function ProjectsPage({ messages, locale }: Props) {
const [projects, setProjects] = useState([]); const [projects, setProjects] = useState([]);
useEffect(() => { useEffect(() => {
@@ -101,6 +102,7 @@ export default function ProjectsPage({ messages }: Props) {
onEditProject={onEditClick} onEditProject={onEditClick}
onDeleteProject={onDeleteClick} onDeleteProject={onDeleteClick}
messages={messages} messages={messages}
locale={locale}
/> />
<ProjectDialog <ProjectDialog

View File

@@ -12,8 +12,8 @@ import {
DropdownMenu, DropdownMenu,
DropdownItem, DropdownItem,
SortDescriptor, SortDescriptor,
Link,
} from "@nextui-org/react"; } from "@nextui-org/react";
import { Link, NextUiLinkClasses } from "@/src/navigation";
import { MoreVertical } from "lucide-react"; import { MoreVertical } from "lucide-react";
import { ProjectType, ProjectsMessages } from "@/types/project"; import { ProjectType, ProjectsMessages } from "@/types/project";
import dayjs from "dayjs"; import dayjs from "dayjs";
@@ -23,6 +23,7 @@ type Props = {
onEditProject: (project: ProjectType) => void; onEditProject: (project: ProjectType) => void;
onDeleteProject: (projectId: number) => void; onDeleteProject: (projectId: number) => void;
messages: ProjectsMessages; messages: ProjectsMessages;
locale: string;
}; };
export default function ProjectsTable({ export default function ProjectsTable({
@@ -30,6 +31,7 @@ export default function ProjectsTable({
onEditProject, onEditProject,
onDeleteProject, onDeleteProject,
messages, messages,
locale,
}: Props) { }: Props) {
const headerColumns = [ const headerColumns = [
{ name: messages.id, uid: "id", sortable: true }, { name: messages.id, uid: "id", sortable: true },
@@ -67,9 +69,9 @@ export default function ProjectsTable({
case "name": case "name":
return ( return (
<Link <Link
underline="hover"
href={`/projects/${project.id}/home`} href={`/projects/${project.id}/home`}
className="text-blue-500" locale={locale}
className={NextUiLinkClasses}
> >
{cellValue} {cellValue}
</Link> </Link>

View File

@@ -2,10 +2,14 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Listbox, ListboxItem } from "@nextui-org/react"; import { Listbox, ListboxItem } from "@nextui-org/react";
import { Home, Files, FlaskConical } from "lucide-react"; import { Home, Files, FlaskConical } from "lucide-react";
import { useRouter, usePathname } from "next/navigation"; import { usePathname, useRouter } from "@/src/navigation";
import useGetCurrentIds from "@/utils/useGetCurrentIds"; import useGetCurrentIds from "@/utils/useGetCurrentIds";
export default function Sidebar() { export type Props = {
locale: string;
};
export default function Sidebar({ locale }: Props) {
const { projectId } = useGetCurrentIds(); const { projectId } = useGetCurrentIds();
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
@@ -16,11 +20,11 @@ export default function Sidebar() {
const handleTabClick = (key: string) => { const handleTabClick = (key: string) => {
if (key === "home") { if (key === "home") {
router.push(`/projects/${projectId}/home`); router.push(`/projects/${projectId}/home`, { locale: locale });
} else if (key === "cases") { } else if (key === "cases") {
router.push(`/projects/${projectId}/folders`); router.push(`/projects/${projectId}/folders`, { locale: locale });
} else if (key === "runs") { } else if (key === "runs") {
router.push(`/projects/${projectId}/runs`); router.push(`/projects/${projectId}/runs`, { locale: locale });
} }
}; };
@@ -58,13 +62,8 @@ export default function Sidebar() {
return ( return (
<div className="w-64 border-r-1 dark:border-neutral-700"> <div className="w-64 border-r-1 dark:border-neutral-700">
<Listbox <Listbox aria-label="Listbox Variants" variant="light" className="p-0">
aria-label="Listbox Variants" {tabItems.map((itr) => (
variant="light"
className="p-0"
onClick={() => router.push(`/projects/${projectId}/home`)}
>
{tabItems.map((itr, index) => (
<ListboxItem <ListboxItem
key={itr.key} key={itr.key}
startContent={itr.startContent} startContent={itr.startContent}

View File

@@ -4,7 +4,7 @@ import { FolderType } from "@/types/folder";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Button, Listbox, ListboxItem } from "@nextui-org/react"; import { Button, Listbox, ListboxItem } from "@nextui-org/react";
import { Folder, Plus } from "lucide-react"; import { Folder, Plus } from "lucide-react";
import { useRouter, usePathname } from "next/navigation"; import { usePathname, useRouter } from "@/src/navigation";
import useGetCurrentIds from "@/utils/useGetCurrentIds"; import useGetCurrentIds from "@/utils/useGetCurrentIds";
import FolderDialog from "./FolderDialog"; import FolderDialog from "./FolderDialog";
import FolderEditMenu from "./FolderEditMenu"; import FolderEditMenu from "./FolderEditMenu";
@@ -18,9 +18,10 @@ import {
type Props = { type Props = {
projectId: string; projectId: string;
locale: string;
}; };
export default function FoldersPane({ projectId }: Props) { export default function FoldersPane({ projectId, locale }: Props) {
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
const [folders, setFolders] = useState([]); const [folders, setFolders] = useState([]);
@@ -68,7 +69,7 @@ export default function FoldersPane({ projectId }: Props) {
const onDeleteClick = async (folderId: number) => { const onDeleteClick = async (folderId: number) => {
await deleteFolder(folderId); await deleteFolder(folderId);
router.push(`/projects/${projectId}/folders`); router.push(`/projects/${projectId}/folders`, { locale: locale });
}; };
useEffect(() => { useEffect(() => {
@@ -86,7 +87,8 @@ export default function FoldersPane({ projectId }: Props) {
if (pathname === `/projects/${projectId}/folders`) { if (pathname === `/projects/${projectId}/folders`) {
const smallestFolderId = Math.min(...data.map((folder) => folder.id)); const smallestFolderId = Math.min(...data.map((folder) => folder.id));
router.push( router.push(
`/projects/${projectId}/folders/${smallestFolderId}/cases` `/projects/${projectId}/folders/${smallestFolderId}/cases`,
{ locale: locale }
); );
} }
} catch (error: any) { } catch (error: any) {
@@ -117,7 +119,10 @@ export default function FoldersPane({ projectId }: Props) {
<ListboxItem <ListboxItem
key={index} key={index}
onClick={() => onClick={() =>
router.push(`/projects/${projectId}/folders/${folder.id}/cases`) router.push(
`/projects/${projectId}/folders/${folder.id}/cases`,
{ locale: locale }
)
} }
startContent={<Folder size={20} color="#F7C24E" fill="#F7C24E" />} startContent={<Folder size={20} color="#F7C24E" fill="#F7C24E" />}
className={ className={

View File

@@ -6,9 +6,10 @@ import { fetchCases, createCase, deleteCase, deleteCases } from "./caseControl";
type Props = { type Props = {
projectId: string; projectId: string;
folderId: string; folderId: string;
locale: string;
}; };
export default function CasesPane({ projectId, folderId }: Props) { export default function CasesPane({ projectId, folderId, locale }: Props) {
const [cases, setCases] = useState([]); const [cases, setCases] = useState([]);
useEffect(() => { useEffect(() => {
async function fetchDataEffect() { async function fetchDataEffect() {
@@ -50,6 +51,7 @@ export default function CasesPane({ projectId, folderId }: Props) {
onCreateCase={() => handleCreateCase(folderId)} onCreateCase={() => handleCreateCase(folderId)}
onDeleteCase={handleDeleteCase} onDeleteCase={handleDeleteCase}
onDeleteCases={handleDeleteCases} onDeleteCases={handleDeleteCases}
locale={locale}
/> />
</> </>
); );

View File

@@ -13,8 +13,8 @@ import {
DropdownItem, DropdownItem,
Selection, Selection,
SortDescriptor, SortDescriptor,
Link,
} from "@nextui-org/react"; } from "@nextui-org/react";
import { Link, NextUiLinkClasses } from "@/src/navigation";
import { Plus, MoreVertical, Trash, Circle } from "lucide-react"; import { Plus, MoreVertical, Trash, Circle } from "lucide-react";
const headerColumns = [ const headerColumns = [
@@ -48,6 +48,7 @@ type Props = {
onCreateCase: () => void; onCreateCase: () => void;
onDeleteCase: (caseId: number) => void; onDeleteCase: (caseId: number) => void;
onDeleteCases: (selectedCases: string[]) => void; onDeleteCases: (selectedCases: string[]) => void;
locale: string;
}; };
export default function TestCaseTable({ export default function TestCaseTable({
@@ -56,6 +57,7 @@ export default function TestCaseTable({
onCreateCase, onCreateCase,
onDeleteCase, onDeleteCase,
onDeleteCases, onDeleteCases,
locale,
}: Props) { }: Props) {
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([])); const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({ const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
@@ -81,9 +83,9 @@ export default function TestCaseTable({
case "title": case "title":
return ( return (
<Link <Link
underline="hover"
href={`/projects/${projectId}/folders/${testCase.folderId}/cases/${testCase.id}`} href={`/projects/${projectId}/folders/${testCase.folderId}/cases/${testCase.id}`}
className="dark:text-white" locale={locale}
className={NextUiLinkClasses}
> >
{cellValue} {cellValue}
</Link> </Link>

View File

@@ -9,7 +9,7 @@ import {
Divider, Divider,
Tooltip, Tooltip,
} from "@nextui-org/react"; } from "@nextui-org/react";
import { useRouter } from "next/navigation"; import { useRouter } from "@/src/navigation";
import { Save, Plus, ArrowLeft, ArrowUpFromLine, Circle } from "lucide-react"; import { Save, Plus, ArrowLeft, ArrowUpFromLine, Circle } from "lucide-react";
import { priorities, testTypes, templates } from "@/config/selection"; import { priorities, testTypes, templates } from "@/config/selection";
import CaseStepsEditor from "./CaseStepsEditor"; import CaseStepsEditor from "./CaseStepsEditor";
@@ -40,7 +40,12 @@ const defaultTestCase = {
export default function CaseEditor({ export default function CaseEditor({
params, params,
}: { }: {
params: { projectId: string; folderId: string; caseId: string }; params: {
projectId: string;
folderId: string;
caseId: string;
locale: string;
};
}) { }) {
const [testCase, setTestCase] = useState<CaseType>(defaultTestCase); const [testCase, setTestCase] = useState<CaseType>(defaultTestCase);
const [isTitleInvalid, setIsTitleInvalid] = useState<boolean>(false); const [isTitleInvalid, setIsTitleInvalid] = useState<boolean>(false);
@@ -171,7 +176,8 @@ export default function CaseEditor({
className="rounded-full bg-neutral-50 dark:bg-neutral-600" className="rounded-full bg-neutral-50 dark:bg-neutral-600"
onPress={() => onPress={() =>
router.push( router.push(
`/projects/${params.projectId}/folders/${params.folderId}/cases` `/projects/${params.projectId}/folders/${params.folderId}/cases`,
{ locale: params.locale }
) )
} }
> >

View File

@@ -3,7 +3,12 @@ import CaseEditor from "./CaseEditor";
export default function Page({ export default function Page({
params, params,
}: { }: {
params: { projectId: string; folderId: string; caseId: string }; params: {
projectId: string;
folderId: string;
caseId: string;
locale: string;
};
}) { }) {
return ( return (
<CaseEditor <CaseEditor
@@ -11,6 +16,7 @@ export default function Page({
projectId: params.projectId, projectId: params.projectId,
folderId: params.folderId, folderId: params.folderId,
caseId: params.caseId, caseId: params.caseId,
locale: params.locale,
}} }}
/> />
); );

View File

@@ -3,11 +3,15 @@ import CasesPane from "./CasesPane";
export default function Page({ export default function Page({
params, params,
}: { }: {
params: { projectId: string; folderId: string }; params: { projectId: string; folderId: string; locale: string };
}) { }) {
return ( return (
<> <>
<CasesPane projectId={params.projectId} folderId={params.folderId} /> <CasesPane
projectId={params.projectId}
folderId={params.folderId}
locale={params.locale}
/>
</> </>
); );
} }

View File

@@ -5,11 +5,11 @@ export default function FoldersLayout({
params, params,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
params: { projectId: string }; params: { projectId: string; locale: string };
}) { }) {
return ( return (
<div className="flex w-full"> <div className="flex w-full">
<FoldersPane projectId={params.projectId} /> <FoldersPane projectId={params.projectId} locale={params.locale} />
<div className="flex-grow w-full">{children}</div> <div className="flex-grow w-full">{children}</div>
</div> </div>
); );

View File

@@ -1,14 +1,16 @@
import Sidebar from "./sidebar"; import Sidebar from "./Sidebar";
export default function SidebarLayout({ export default function SidebarLayout({
children, children,
params: { locale },
}: { }: {
children: React.ReactNode; children: React.ReactNode;
params: { locale: string };
}) { }) {
return ( return (
<> <>
<div className="flex border-t-1 dark:border-neutral-700 min-h-[calc(100vh-64px)]"> <div className="flex border-t-1 dark:border-neutral-700 min-h-[calc(100vh-64px)]">
<Sidebar /> <Sidebar locale={locale} />
<div className="flex w-full"> <div className="flex w-full">
<div className="flex-grow">{children}</div> <div className="flex-grow">{children}</div>
</div> </div>

View File

@@ -7,9 +7,10 @@ import { fetchRuns, createRun, deleteRun } from "./runsControl";
type Props = { type Props = {
projectId: string; projectId: string;
locale: string;
}; };
export default function RunsPage({ projectId }: Props) { export default function RunsPage({ projectId, locale }: Props) {
const [runs, setRuns] = useState([]); const [runs, setRuns] = useState([]);
useEffect(() => { useEffect(() => {
@@ -66,6 +67,7 @@ export default function RunsPage({ projectId }: Props) {
projectId={projectId} projectId={projectId}
runs={runs} runs={runs}
onDeleteRun={onDeleteClick} onDeleteRun={onDeleteClick}
locale={locale}
/> />
</div> </div>
); );

View File

@@ -12,8 +12,8 @@ import {
DropdownMenu, DropdownMenu,
DropdownItem, DropdownItem,
SortDescriptor, SortDescriptor,
Link,
} from "@nextui-org/react"; } from "@nextui-org/react";
import { Link, NextUiLinkClasses } from "@/src/navigation";
import { MoreVertical } from "lucide-react"; import { MoreVertical } from "lucide-react";
import { RunType } from "@/types/run"; import { RunType } from "@/types/run";
import dayjs from "dayjs"; import dayjs from "dayjs";
@@ -30,9 +30,15 @@ type Props = {
projectId: string; projectId: string;
runs: RunType[]; runs: RunType[];
onDeleteRun: (runId: number) => void; onDeleteRun: (runId: number) => void;
locale: string;
}; };
export default function RunsTable({ projectId, runs, onDeleteRun }: Props) { export default function RunsTable({
projectId,
runs,
onDeleteRun,
locale,
}: Props) {
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({ const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
column: "id", column: "id",
direction: "ascending", direction: "ascending",
@@ -61,9 +67,9 @@ export default function RunsTable({ projectId, runs, onDeleteRun }: Props) {
case "name": case "name":
return ( return (
<Link <Link
underline="hover"
href={`/projects/${projectId}/runs/${run.id}`} href={`/projects/${projectId}/runs/${run.id}`}
className="text-blue-500" locale={locale}
className={NextUiLinkClasses}
> >
{cellValue} {cellValue}
</Link> </Link>

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "@/src/navigation";
import { import {
Button, Button,
Input, Input,
@@ -63,9 +63,10 @@ const defaultTestRun = {
type Props = { type Props = {
projectId: string; projectId: string;
runId: string; runId: string;
locale: string;
}; };
export default function RunEditor({ projectId, runId }: Props) { export default function RunEditor({ projectId, runId, locale }: Props) {
const [testRun, setTestRun] = useState<RunType>(defaultTestRun); const [testRun, setTestRun] = useState<RunType>(defaultTestRun);
const [folders, setFolders] = useState([]); const [folders, setFolders] = useState([]);
const [runCases, setRunCases] = useState<RunCaseType[]>([]); const [runCases, setRunCases] = useState<RunCaseType[]>([]);
@@ -220,7 +221,9 @@ export default function RunEditor({ projectId, runId }: Props) {
isIconOnly isIconOnly
size="sm" size="sm"
className="rounded-full bg-neutral-50 dark:bg-neutral-600" className="rounded-full bg-neutral-50 dark:bg-neutral-600"
onPress={() => router.push(`/projects/${projectId}/runs`)} onPress={() =>
router.push(`/projects/${projectId}/runs`, { locale: locale })
}
> >
<ArrowLeft size={16} /> <ArrowLeft size={16} />
</Button> </Button>

View File

@@ -3,7 +3,13 @@ import RunEditor from "./RunEditor";
export default function Page({ export default function Page({
params, params,
}: { }: {
params: { projectId: string; runId: string }; params: { projectId: string; runId: string; locale: string };
}) { }) {
return <RunEditor projectId={params.projectId} runId={params.runId} />; return (
<RunEditor
projectId={params.projectId}
runId={params.runId}
locale={params.locale}
/>
);
} }

View File

@@ -1,9 +1,13 @@
import RunsPage from "./RunsPage"; import RunsPage from "./RunsPage";
export default function Page({ params }: { params: { projectId: string } }) { export default function Page({
params,
}: {
params: { projectId: string; locale: string };
}) {
return ( return (
<> <>
<RunsPage projectId={params.projectId} /> <RunsPage projectId={params.projectId} locale={params.locale} />
</> </>
); );
} }

View File

@@ -1,7 +1,7 @@
import ProjectsPage from "./ProjectsPage"; import ProjectsPage from "./ProjectsPage";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
export default function Page() { export default function Page(params: { locale }) {
const t = useTranslations("Projects"); const t = useTranslations("Projects");
const messages = { const messages = {
projects: t("projects"), projects: t("projects"),
@@ -23,7 +23,7 @@ export default function Page() {
}; };
return ( return (
<> <>
<ProjectsPage messages={messages} /> <ProjectsPage messages={messages} locale={params.locale} />
</> </>
); );
} }

View File

@@ -5,3 +5,6 @@ export const localePrefix = "always";
export const { Link, redirect, usePathname, useRouter } = export const { Link, redirect, usePathname, useRouter } =
createSharedPathnamesNavigation({ locales, localePrefix }); createSharedPathnamesNavigation({ locales, localePrefix });
export const NextUiLinkClasses =
"data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 text-medium text-primary hover:underline hover:opacity-80 active:opacity-disabled transition-opacity underline-offset-4 dark:text-white";