This commit is contained in:
@@ -5,24 +5,26 @@ import { folder } from "../../../../generated/prisma/client";
|
||||
import { Folder } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Center } from "@/components/Center";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface FolderSelectorProps {
|
||||
folders: (folder & { total_pairs: number })[];
|
||||
}
|
||||
|
||||
const FolderSelector: React.FC<FolderSelectorProps> = ({ folders }) => {
|
||||
const t = useTranslations("memorize/folder-selector");
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Center>
|
||||
<Container className="p-6 gap-4 flex flex-col">
|
||||
{(folders.length === 0 && (
|
||||
<h1 className="text-2xl text-gray-900 font-light">
|
||||
No folders found.
|
||||
{t("noFolders")}
|
||||
</h1>
|
||||
)) || (
|
||||
<>
|
||||
<h1 className="text-2xl text-gray-900 font-light">
|
||||
Select a folder:
|
||||
{t("selectFolder")}
|
||||
</h1>
|
||||
<div className="text-gray-900 border border-gray-200 rounded-2xl max-h-96 overflow-y-auto">
|
||||
{folders.map((folder) => (
|
||||
@@ -36,9 +38,12 @@ const FolderSelector: React.FC<FolderSelectorProps> = ({ folders }) => {
|
||||
<Folder />
|
||||
<div className="flex-1 flex gap-2">
|
||||
<span className="group-hover:text-blue-500">
|
||||
{folder.id}. {folder.name}
|
||||
{t("folderInfo", {
|
||||
id: folder.id,
|
||||
name: folder.name,
|
||||
count: folder.total_pairs,
|
||||
})}
|
||||
</span>
|
||||
<span>({folder.total_pairs})</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -8,12 +8,14 @@ import LightButton from "@/components/buttons/LightButton";
|
||||
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
||||
import { getTTSAudioUrl } from "@/lib/tts";
|
||||
import { VOICES } from "@/config/locales";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface MemorizeProps {
|
||||
textPairs: text_pair[];
|
||||
}
|
||||
|
||||
const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
|
||||
const t = useTranslations("memorize.memorize");
|
||||
const [reverse, setReverse] = useState(false);
|
||||
const [dictation, setDictation] = useState(false);
|
||||
const [index, setIndex] = useState(0);
|
||||
@@ -27,7 +29,7 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
|
||||
<>
|
||||
<div className="h-36 flex flex-col gap-2 justify-start items-center font-serif text-3xl">
|
||||
<div className="text-sm text-gray-500">
|
||||
{index + 1}/{textPairs.length}
|
||||
{t("progress", { current: index + 1, total: textPairs.length })}
|
||||
</div>
|
||||
{dictation ? (
|
||||
show === "question" ? (
|
||||
@@ -86,7 +88,7 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
|
||||
setShow(show === "question" ? "answer" : "question");
|
||||
}}
|
||||
>
|
||||
{show === "question" ? "Show Answer" : "Next"}
|
||||
{show === "question" ? t("showAnswer") : t("next")}
|
||||
</LightButton>
|
||||
<LightButton
|
||||
onClick={() => {
|
||||
@@ -94,7 +96,7 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
|
||||
}}
|
||||
selected={reverse}
|
||||
>
|
||||
Reverse
|
||||
{t("reverse")}
|
||||
</LightButton>
|
||||
<LightButton
|
||||
onClick={() => {
|
||||
@@ -102,11 +104,11 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
|
||||
}}
|
||||
selected={dictation}
|
||||
>
|
||||
Dictation
|
||||
{t("dictation")}
|
||||
</LightButton>
|
||||
</div>
|
||||
</>
|
||||
)) || <p>No text pairs available</p>}
|
||||
)) || <p>{t("noTextPairs")}</p>}
|
||||
</Container>
|
||||
</Center>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { redirect } from "next/navigation";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import {
|
||||
getFoldersWithTotalPairsByOwner,
|
||||
getOwnerByFolderId,
|
||||
@@ -18,9 +19,14 @@ export default async function MemorizePage({
|
||||
}) {
|
||||
const session = await getServerSession();
|
||||
const username = session?.user?.name;
|
||||
const t = await getTranslations("memorize.page");
|
||||
|
||||
const t = (await searchParams).folder_id;
|
||||
const folder_id = t ? (isNonNegativeInteger(t) ? parseInt(t) : null) : null;
|
||||
const tParam = (await searchParams).folder_id;
|
||||
const folder_id = tParam
|
||||
? isNonNegativeInteger(tParam)
|
||||
? parseInt(tParam)
|
||||
: null
|
||||
: null;
|
||||
|
||||
if (!username)
|
||||
redirect(
|
||||
@@ -37,7 +43,7 @@ export default async function MemorizePage({
|
||||
|
||||
const owner = await getOwnerByFolderId(folder_id);
|
||||
if (owner !== username) {
|
||||
return <p>无权访问该文件夹</p>;
|
||||
return <p>{t("unauthorized")}</p>;
|
||||
}
|
||||
|
||||
return <Memorize textPairs={await getTextPairsByFolderId(folder_id)} />;
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getFoldersByOwner } from "@/lib/services/folderService";
|
||||
import { Folder } from "lucide-react";
|
||||
import { createTextPair } from "@/lib/services/textPairService";
|
||||
import { toast } from "sonner";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface AddToFolderProps {
|
||||
item: z.infer<typeof TranslationHistorySchema>;
|
||||
@@ -18,6 +19,7 @@ interface AddToFolderProps {
|
||||
const AddToFolder: React.FC<AddToFolderProps> = ({ item, setShow }) => {
|
||||
const session = useSession();
|
||||
const [folders, setFolders] = useState<folder[]>([]);
|
||||
const t = useTranslations("translator.add-to-folder");
|
||||
|
||||
useEffect(() => {
|
||||
const username = session.data!.user!.name as string;
|
||||
@@ -28,7 +30,7 @@ const AddToFolder: React.FC<AddToFolderProps> = ({ item, setShow }) => {
|
||||
return (
|
||||
<div className="fixed left-0 top-0 z-50 w-screen h-screen bg-black/50 flex justify-center items-center">
|
||||
<Container className="p-6">
|
||||
<div>You are not authenticated</div>;
|
||||
<div>{t("notAuthenticated")}</div>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
@@ -36,7 +38,7 @@ const AddToFolder: React.FC<AddToFolderProps> = ({ item, setShow }) => {
|
||||
return (
|
||||
<div className="fixed left-0 top-0 z-50 w-screen h-screen bg-black/50 flex justify-center items-center">
|
||||
<Container className="p-6">
|
||||
<h1>Choose a Folder to Add to</h1>
|
||||
<h1>{t("chooseFolder")}</h1>
|
||||
<div className="border border-gray-200 rounded-2xl">
|
||||
{(folders.length > 0 &&
|
||||
folders.map((folder) => (
|
||||
@@ -56,20 +58,20 @@ const AddToFolder: React.FC<AddToFolderProps> = ({ item, setShow }) => {
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Text pair added to folder");
|
||||
toast.success(t("success"));
|
||||
setShow(false);
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Failed to add text pair to folder");
|
||||
toast.error(t("error"));
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Folder />
|
||||
{folder.id}. {folder.name}
|
||||
{t("folderInfo", { id: folder.id, name: folder.name })}
|
||||
</button>
|
||||
))) || <div>No folders found</div>}
|
||||
))) || <div>{t("noFolders")}</div>}
|
||||
</div>
|
||||
<LightButton onClick={() => setShow(false)}>Close</LightButton>
|
||||
<LightButton onClick={() => setShow(false)}>{t("close")}</LightButton>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -238,7 +238,7 @@ export default function TranslatorPage() {
|
||||
<LightButton
|
||||
selected={!["chinese", "english", "italian"].includes(lang)}
|
||||
onClick={() => {
|
||||
const newLang = prompt("Enter language");
|
||||
const newLang = prompt(t("enterLanguage"));
|
||||
if (newLang) {
|
||||
setLang(newLang);
|
||||
}
|
||||
@@ -261,7 +261,7 @@ export default function TranslatorPage() {
|
||||
</div>
|
||||
{history.length > 0 && (
|
||||
<div className="m-6 flex flex-col items-center">
|
||||
<h1 className="text-2xl font-light">History</h1>
|
||||
<h1 className="text-2xl font-light">{t("history")}</h1>
|
||||
<div className="border border-gray-200 rounded-2xl m-4">
|
||||
{history.map((item, index) => (
|
||||
<div key={index}>
|
||||
|
||||
Reference in New Issue
Block a user