This commit is contained in:
120
prisma/migrations/20251210105812_init/migration.sql
Normal file
120
prisma/migrations/20251210105812_init/migration.sql
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "pairs" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"locale1" VARCHAR(10) NOT NULL,
|
||||||
|
"locale2" VARCHAR(10) NOT NULL,
|
||||||
|
"text1" TEXT NOT NULL,
|
||||||
|
"text2" TEXT NOT NULL,
|
||||||
|
"ipa1" TEXT,
|
||||||
|
"ipa2" TEXT,
|
||||||
|
"folder_id" INTEGER NOT NULL,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "pairs_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "folders" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"user_id" TEXT NOT NULL,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "folders_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "user" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"emailVerified" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"image" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "session" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"token" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"ipAddress" TEXT,
|
||||||
|
"userAgent" TEXT,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "session_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "account" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"accountId" TEXT NOT NULL,
|
||||||
|
"providerId" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"accessToken" TEXT,
|
||||||
|
"refreshToken" TEXT,
|
||||||
|
"idToken" TEXT,
|
||||||
|
"accessTokenExpiresAt" TIMESTAMP(3),
|
||||||
|
"refreshTokenExpiresAt" TIMESTAMP(3),
|
||||||
|
"scope" TEXT,
|
||||||
|
"password" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "account_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "verification" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"identifier" TEXT NOT NULL,
|
||||||
|
"value" TEXT NOT NULL,
|
||||||
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "verification_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "pairs_folder_id_idx" ON "pairs"("folder_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "pairs_folder_id_locale1_locale2_text1_key" ON "pairs"("folder_id", "locale1", "locale2", "text1");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "folders_user_id_idx" ON "folders"("user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "session_userId_idx" ON "session"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "session_token_key" ON "session"("token");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "account_userId_idx" ON "account"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "verification_identifier_idx" ON "verification"("identifier");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "pairs" ADD CONSTRAINT "pairs_folder_id_fkey" FOREIGN KEY ("folder_id") REFERENCES "folders"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "folders" ADD CONSTRAINT "folders_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -4,6 +4,7 @@ import IMAGES from "@/config/images";
|
|||||||
import IconClick from "./IconClick";
|
import IconClick from "./IconClick";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import LightButton from "./buttons/LightButton";
|
import LightButton from "./buttons/LightButton";
|
||||||
|
import GhostButton from "./buttons/GhostButton";
|
||||||
|
|
||||||
export default function LanguageSettings() {
|
export default function LanguageSettings() {
|
||||||
const [showLanguageMenu, setShowLanguageMenu] = useState(false);
|
const [showLanguageMenu, setShowLanguageMenu] = useState(false);
|
||||||
@@ -26,18 +27,18 @@ export default function LanguageSettings() {
|
|||||||
{showLanguageMenu && (
|
{showLanguageMenu && (
|
||||||
<div>
|
<div>
|
||||||
<div className="absolute top-10 right-0 rounded-md shadow-md flex flex-col gap-2">
|
<div className="absolute top-10 right-0 rounded-md shadow-md flex flex-col gap-2">
|
||||||
<LightButton
|
<GhostButton
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => setLocale("en-US")}
|
onClick={() => setLocale("en-US")}
|
||||||
>
|
>
|
||||||
English
|
English
|
||||||
</LightButton>
|
</GhostButton>
|
||||||
<LightButton
|
<GhostButton
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => setLocale("zh-CN")}
|
onClick={() => setLocale("zh-CN")}
|
||||||
>
|
>
|
||||||
中文
|
中文
|
||||||
</LightButton>
|
</GhostButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import LanguageSettings from "./LanguageSettings";
|
|||||||
import { auth } from "@/auth";
|
import { auth } from "@/auth";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import GhostButton from "./buttons/GhostButton";
|
||||||
|
|
||||||
export async function Navbar() {
|
export async function Navbar() {
|
||||||
const t = await getTranslations("navbar");
|
const t = await getTranslations("navbar");
|
||||||
@@ -15,14 +16,14 @@ export async function Navbar() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between items-center w-full h-16 px-8 bg-[#35786f] text-white">
|
<div className="flex justify-between items-center w-full h-16 px-8 bg-[#35786f] text-white">
|
||||||
<Link href={"/"} className="text-xl border-b hidden md:block">
|
<GhostButton href="/" className="text-xl border-b hidden md:block">
|
||||||
{t("title")}
|
{t("title")}
|
||||||
</Link>
|
</GhostButton>
|
||||||
<Link className="block md:hidden" href={"/"}>
|
<GhostButton className="block md:hidden" href={"/"}>
|
||||||
<Home />
|
<Home />
|
||||||
</Link>
|
</GhostButton>
|
||||||
<div className="flex gap-4 text-xl justify-center items-center flex-wrap">
|
<div className="flex text-xl gap-0.5 justify-center items-center flex-wrap">
|
||||||
<Link
|
<GhostButton
|
||||||
className="md:hidden block"
|
className="md:hidden block"
|
||||||
href="https://github.com/GoddoNebianU/learn-languages"
|
href="https://github.com/GoddoNebianU/learn-languages"
|
||||||
>
|
>
|
||||||
@@ -32,29 +33,29 @@ export async function Navbar() {
|
|||||||
width={24}
|
width={24}
|
||||||
height={24}
|
height={24}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</GhostButton>
|
||||||
<LanguageSettings />
|
<LanguageSettings />
|
||||||
<Link href="/folders" className="md:block hidden">
|
<GhostButton href="/folders" className="md:block hidden">
|
||||||
{t("folders")}
|
{t("folders")}
|
||||||
</Link>
|
</GhostButton>
|
||||||
<Link href="/folders" className="md:hidden block">
|
<GhostButton href="/folders" className="md:hidden block">
|
||||||
<Folder />
|
<Folder />
|
||||||
</Link>
|
</GhostButton>
|
||||||
{
|
{
|
||||||
(() => {
|
(() => {
|
||||||
return session &&
|
return session &&
|
||||||
<Link href="/profile">{t("profile")}</Link>
|
<GhostButton href="/profile">{t("profile")}</GhostButton>
|
||||||
|| <Link href="/signin">{t("sign_in")}</Link>;
|
|| <GhostButton href="/signin">{t("sign_in")}</GhostButton>;
|
||||||
|
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
<Link href="/changelog.txt">{t("about")}</Link>
|
<GhostButton href="/changelog.txt">{t("about")}</GhostButton>
|
||||||
<Link
|
<GhostButton
|
||||||
className="hidden md:block"
|
className="hidden md:block"
|
||||||
href="https://github.com/GoddoNebianU/learn-languages"
|
href="https://github.com/GoddoNebianU/learn-languages"
|
||||||
>
|
>
|
||||||
{t("sourceCode")}
|
{t("sourceCode")}
|
||||||
</Link>
|
</GhostButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
27
src/components/buttons/GhostButton.tsx
Normal file
27
src/components/buttons/GhostButton.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export type ButtonType = "button" | "submit" | "reset" | undefined;
|
||||||
|
|
||||||
|
export default function GhostButton({
|
||||||
|
onClick,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
type = "button",
|
||||||
|
href
|
||||||
|
}: {
|
||||||
|
onClick?: () => void;
|
||||||
|
className?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
type?: ButtonType;
|
||||||
|
href?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
className={`rounded hover:bg-black/30 p-2 ${className}`}
|
||||||
|
type={type}
|
||||||
|
>
|
||||||
|
{href ? <Link href={href}>{children}</Link> : children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user