style: 优化样式

This commit is contained in:
sealday 2024-04-06 01:08:52 +08:00
parent b35c692998
commit 910bd2d4dc
10 changed files with 603 additions and 268 deletions

View File

@ -1,11 +1,24 @@
'use client'; "use client";
import { signout } from '@/app/lib/actions'; import { signout } from "@/app/lib/actions/auth";
import { usePathname } from "next/navigation";
export default function SignoutButton() {
return <button onClick={async () => {
export default function SignoutButton({
className,
}: {
className: string | undefined;
}) {
const pathname = usePathname();
if (pathname.match("/signin") || pathname.match("/signup")) {
return null;
}
return (
<button
className={className}
onClick={async () => {
await signout(); await signout();
}}> }}
>
</button> </button>
}; );
}

View File

@ -1,6 +1,7 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import "./globals.css"; import "./globals.css";
import SignoutButton from "./components/signout-button";
const inter = Inter({ subsets: ["latin"] }); const inter = Inter({ subsets: ["latin"] });
@ -16,7 +17,262 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="zh_CN" data-theme="light"> <html lang="zh_CN" data-theme="light">
<body className={inter.className}>{children}</body> <body className={inter.className}>
<header className="text-gray-600 body-font">
<div className="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
<a
href="#"
className="flex title-font font-medium items-center text-gray-900 mb-4 md:mb-0"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
className="w-10 h-10 text-white p-2 bg-indigo-500 rounded-full"
viewBox="0 0 24 24"
>
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"></path>
</svg>
<span className="ml-3 text-xl"></span>
</a>
<nav className="md:ml-auto md:mr-auto flex flex-wrap items-center text-base justify-center">
<a href="#" className="mr-5 hover:text-gray-900">
First Link
</a>
<a href="#" className="mr-5 hover:text-gray-900">
Second Link
</a>
<a href="#" className="mr-5 hover:text-gray-900">
Third Link
</a>
<a href="#" className="mr-5 hover:text-gray-900">
Fourth Link
</a>
</nav>
<SignoutButton className="inline-flex items-center bg-gray-100 border-0 py-1 px-3 focus:outline-none hover:bg-gray-200 rounded text-base mt-4 md:mt-0" />
</div>
</header>
<main className="min-h-[50vh]">{children}</main>
<footer className="text-gray-600 body-font">
<div className="container px-5 py-24 mx-auto flex md:items-center lg:items-start md:flex-row md:flex-nowrap flex-wrap flex-col">
<div className="w-64 flex-shrink-0 md:mx-0 mx-auto text-center md:text-left">
<a
href="#"
className="flex title-font font-medium items-center md:justify-start justify-center text-gray-900"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
className="w-10 h-10 text-white p-2 bg-indigo-500 rounded-full"
viewBox="0 0 24 24"
>
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"></path>
</svg>
<span className="ml-3 text-xl">Tailblocks</span>
</a>
<p className="mt-2 text-sm text-gray-500">
Air plant banjo lyft occupy retro adaptogen indego
</p>
</div>
<div className="flex-grow flex flex-wrap md:pl-20 -mb-10 md:mt-0 mt-10 md:text-left text-center">
<div className="lg:w-1/4 md:w-1/2 w-full px-4">
<h2 className="title-font font-medium text-gray-900 tracking-widest text-sm mb-3">
CATEGORIES
</h2>
<nav className="list-none mb-10">
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
First Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Second Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Third Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Fourth Link
</a>
</li>
</nav>
</div>
<div className="lg:w-1/4 md:w-1/2 w-full px-4">
<h2 className="title-font font-medium text-gray-900 tracking-widest text-sm mb-3">
CATEGORIES
</h2>
<nav className="list-none mb-10">
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
First Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Second Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Third Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Fourth Link
</a>
</li>
</nav>
</div>
<div className="lg:w-1/4 md:w-1/2 w-full px-4">
<h2 className="title-font font-medium text-gray-900 tracking-widest text-sm mb-3">
CATEGORIES
</h2>
<nav className="list-none mb-10">
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
First Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Second Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Third Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Fourth Link
</a>
</li>
</nav>
</div>
<div className="lg:w-1/4 md:w-1/2 w-full px-4">
<h2 className="title-font font-medium text-gray-900 tracking-widest text-sm mb-3">
CATEGORIES
</h2>
<nav className="list-none mb-10">
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
First Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Second Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Third Link
</a>
</li>
<li>
<a href="#" className="text-gray-600 hover:text-gray-800">
Fourth Link
</a>
</li>
</nav>
</div>
</div>
</div>
<div className="bg-gray-100">
<div className="container mx-auto py-4 px-5 flex flex-wrap flex-col sm:flex-row">
<p className="text-gray-500 text-sm text-center sm:text-left">
© 2024
<a
href="https://github.com/sealday"
rel="noopener noreferrer"
className="text-gray-600 ml-1"
target="_blank"
>
@sealday
</a>
</p>
<span className="inline-flex sm:ml-auto sm:mt-0 mt-2 justify-center sm:justify-start">
<a href="#" className="text-gray-500">
<svg
fill="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
className="w-5 h-5"
viewBox="0 0 24 24"
>
<path d="M18 2h-3a5 5 0 00-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 011-1h3z"></path>
</svg>
</a>
<a href="#" className="ml-3 text-gray-500">
<svg
fill="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
className="w-5 h-5"
viewBox="0 0 24 24"
>
<path d="M23 3a10.9 10.9 0 01-3.14 1.53 4.48 4.48 0 00-7.86 3v1A10.66 10.66 0 013 4s-4 9 5 13a11.64 11.64 0 01-7 2c9 5 20 0 20-11.5a4.5 4.5 0 00-.08-.83A7.72 7.72 0 0023 3z"></path>
</svg>
</a>
<a href="#" className="ml-3 text-gray-500">
<svg
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
className="w-5 h-5"
viewBox="0 0 24 24"
>
<rect
width="20"
height="20"
x="2"
y="2"
rx="5"
ry="5"
></rect>
<path d="M16 11.37A4 4 0 1112.63 8 4 4 0 0116 11.37zm1.5-4.87h.01"></path>
</svg>
</a>
<a href="#" className="ml-3 text-gray-500">
<svg
fill="currentColor"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="0"
className="w-5 h-5"
viewBox="0 0 24 24"
>
<path
stroke="none"
d="M16 8a6 6 0 016 6v7h-4v-7a2 2 0 00-2-2 2 2 0 00-2 2v7h-4v-7a6 6 0 016-6zM2 9h4v12H2z"
></path>
<circle cx="4" cy="4" r="2" stroke="none"></circle>
</svg>
</a>
</span>
</div>
</div>
</footer>
</body>
</html> </html>
); );
} }

View File

@ -1,71 +0,0 @@
'use server';
import { redirect } from 'next/navigation'
import { cookies } from 'next/headers'
const SIGNUP_URL = 'https://tachy.daoyoucloud.com/api/auth:signUp';
const SIGNIN_URL = 'https://tachy.daoyoucloud.com/api/auth:signIn';
const SIGNOUT_URL = 'https://tachy.daoyoucloud.com/api/auth:signOut'
export async function signout() {
const token = cookies().get('token');
const result = await fetch(SIGNOUT_URL, {
"headers": {
"authorization": "Bearer " + token?.value,
"cache-control": "no-cache",
"pragma": "no-cache",
"x-app": "danmu-sim",
"x-authenticator": "basic",
"x-locale": "zh-CN",
"x-timezone": "+08:00",
"x-with-acl-meta": "true",
},
"body": '',
"method": "POST"
});
cookies().delete('token');
redirect('/signin');
}
export async function signin(_currentState: unknown, formData: FormData) {
const result = await fetch(SIGNIN_URL, {
"headers": {
"cache-control": "no-cache",
"content-type": "application/json",
"pragma": "no-cache",
"x-app": "danmu-sim",
"x-authenticator": "basic",
"x-hostname": "tachy.daoyoucloud.com",
"x-locale": "zh-CN",
"x-timezone": "+08:00",
"x-with-acl-meta": "true",
},
"body": JSON.stringify(Object.fromEntries(formData)),
"method": "POST"
});
const res = await result.json();
if (res.errors) {
return res.errors[0].message;
}
cookies().set('token', res.data.token);
redirect('/');
}
export async function signup(_currentState: unknown, formData: FormData) {
const result = await fetch(SIGNUP_URL, {
method: 'POST',
headers: {
'X-App': 'danmu-sim',
"content-type": "application/json",
"x-authenticator": "basic",
"x-locale": "zh-CN",
"x-timezone": "+08:00",
},
body: JSON.stringify(Object.fromEntries(formData))
})
const response = await result.json();
if (response.errors) {
return response.errors[0].message;
}
}

70
app/lib/actions/auth.ts Normal file
View File

@ -0,0 +1,70 @@
"use server";
import { redirect } from "next/navigation";
import { cookies } from "next/headers";
const SIGNUP_URL = "https://tachy.daoyoucloud.com/api/auth:signUp";
const SIGNIN_URL = "https://tachy.daoyoucloud.com/api/auth:signIn";
const SIGNOUT_URL = "https://tachy.daoyoucloud.com/api/auth:signOut";
export async function signout() {
const token = cookies().get("token");
const result = await fetch(SIGNOUT_URL, {
headers: {
authorization: "Bearer " + token?.value,
"cache-control": "no-cache",
pragma: "no-cache",
"x-app": "danmu-sim",
"x-authenticator": "basic",
"x-locale": "zh-CN",
"x-timezone": "+08:00",
"x-with-acl-meta": "true",
},
body: "",
method: "POST",
});
cookies().delete("token");
redirect("/signin");
}
export async function signin(_currentState: unknown, formData: FormData) {
const result = await fetch(SIGNIN_URL, {
headers: {
"cache-control": "no-cache",
"content-type": "application/json",
pragma: "no-cache",
"x-app": "danmu-sim",
"x-authenticator": "basic",
"x-hostname": "tachy.daoyoucloud.com",
"x-locale": "zh-CN",
"x-timezone": "+08:00",
"x-with-acl-meta": "true",
},
body: JSON.stringify(Object.fromEntries(formData)),
method: "POST",
});
const res = await result.json();
if (res.errors) {
return res.errors[0].message;
}
cookies().set("token", res.data.token);
redirect("/");
}
export async function signup(_currentState: unknown, formData: FormData) {
const result = await fetch(SIGNUP_URL, {
method: "POST",
headers: {
"X-App": "danmu-sim",
"content-type": "application/json",
"x-authenticator": "basic",
"x-locale": "zh-CN",
"x-timezone": "+08:00",
},
body: JSON.stringify(Object.fromEntries(formData)),
});
const response = await result.json();
if (response.errors) {
return response.errors[0].message;
}
redirect("/signin");
}

1
app/music.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 204 KiB

View File

@ -1,124 +1,44 @@
import Image from "next/image"; import Image from "next/image";
import SignoutButton from "@/app/components/signout-button";
import Link from "next/link"; import Link from "next/link";
import musicSvg from "@/app/music.svg";
export default function Home() { export default function Home() {
return <main> return (
<SignoutButton /> <section className="text-gray-600 body-font">
<Link className="btn" href="/music"></Link> <div className="container mx-auto flex px-5 py-24 md:flex-row flex-col items-center">
</main> <div className="lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center">
<h1 className="title-font sm:text-4xl text-3xl mb-4 font-medium text-gray-900">
Before they sold out
<br className="hidden lg:inline-block" />
readymade gluten
</h1>
<p className="mb-8 leading-relaxed">
Copper mug try-hard pitchfork pour-over freegan heirloom neutra air
plant cold-pressed tacos poke beard tote bag. Heirloom echo park
mlkshk tote bag selvage hot chicken authentic tumeric truffaut
hexagon try-hard chambray.
</p>
<div className="flex justify-center">
<Link
href="/music"
className="inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"
>
</Link>
<button className="ml-4 inline-flex text-gray-700 bg-gray-100 border-0 py-2 px-6 focus:outline-none hover:bg-gray-200 rounded text-lg">
Button
</button>
</div>
</div>
<div className="lg:max-w-lg lg:w-full md:w-1/2 w-5/6">
<Image
className="object-cover object-center rounded"
alt="hero"
src={musicSvg}
/>
</div>
</div>
</section>
);
} }
// export default function Home() {
// return (
// <main className="flex min-h-screen flex-col items-center justify-between p-24">
// <SignoutButton />
// <Link className="btn" href="/music">弹幕测试</Link>
// <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
// <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
// Get started by editing&nbsp;
// <code className="font-mono font-bold">app/page.tsx</code>
// </p>
// <div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
// <a
// className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
// href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
// target="_blank"
// rel="noopener noreferrer"
// >
// By{" "}
// <Image
// src="/vercel.svg"
// alt="Vercel Logo"
// className="dark:invert"
// width={100}
// height={24}
// priority
// />
// </a>
// </div>
// </div>
// <div className="relative flex place-items-center before:absolute before:h-[300px] before:w-full sm:before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-full sm:after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
// <Image
// className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
// src="/next.svg"
// alt="Next.js Logo"
// width={180}
// height={37}
// priority
// />
// </div>
// <div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
// <a
// href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
// target="_blank"
// rel="noopener noreferrer"
// >
// <h2 className={`mb-3 text-2xl font-semibold`}>
// Docs{" "}
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
// -&gt;
// </span>
// </h2>
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
// Find in-depth information about Next.js features and API.
// </p>
// </a>
// <a
// href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
// target="_blank"
// rel="noopener noreferrer"
// >
// <h2 className={`mb-3 text-2xl font-semibold`}>
// Learn{" "}
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
// -&gt;
// </span>
// </h2>
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
// Learn about Next.js in an interactive course with&nbsp;quizzes!
// </p>
// </a>
// <a
// href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
// target="_blank"
// rel="noopener noreferrer"
// >
// <h2 className={`mb-3 text-2xl font-semibold`}>
// Templates{" "}
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
// -&gt;
// </span>
// </h2>
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
// Explore starter templates for Next.js.
// </p>
// </a>
// <a
// href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
// target="_blank"
// rel="noopener noreferrer"
// >
// <h2 className={`mb-3 text-2xl font-semibold`}>
// Deploy{" "}
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
// -&gt;
// </span>
// </h2>
// <p className={`m-0 max-w-[30ch] text-sm opacity-50 text-balance`}>
// Instantly deploy your Next.js site to a shareable URL with Vercel.
// </p>
// </a>
// </div>
// </main>
// );
// }

View File

@ -1,33 +1,89 @@
'use client' "use client";
import { signin } from '@/app/lib/actions' import { signin } from "@/app/lib/actions/auth";
import { useFormState, useFormStatus } from 'react-dom' import Link from "next/link";
import { useFormState, useFormStatus } from "react-dom";
export default function Page() { export default function Page() {
const [errorMessage, dispatch] = useFormState(signin, undefined) const [errorMessage, dispatch] = useFormState(signin, undefined);
return ( return (
<form action={dispatch}> <section className="text-gray-600 body-font">
<input name="account" placeholder="Account" required /> <div className="container px-5 py-24 mx-auto flex flex-wrap items-center">
<input type="password" name="password" placeholder="Password" required /> <div className="lg:w-3/5 md:w-1/2 md:pr-16 lg:pr-0 pr-0">
<div>{errorMessage && <p>{errorMessage}</p>}</div> <h1 className="title-font font-medium text-3xl text-gray-900">
<LoginButton /> Slow-carb next level shoindcgoitch ethical authentic, poko scenester
</h1>
<p className="leading-relaxed mt-4">
Poke slow-carb mixtape knausgaard, typewriter street art gentrify
hammock starladder roathse. Craies vegan tousled etsy austin.
</p>
</div>
<form
action={dispatch}
className="lg:w-2/6 md:w-1/2 bg-gray-100 rounded-lg p-8 flex flex-col md:ml-auto w-full mt-10 md:mt-0"
>
<h2 className="text-gray-900 text-lg font-medium title-font mb-5">
</h2>
<div className="relative mb-4">
<label
htmlFor="account"
className="leading-7 text-sm text-gray-600"
>
</label>
<input
id="account"
className="w-full bg-white rounded border border-gray-300 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
type="text"
required
name="account"
/>
</div>
<div className="relative mb-4">
<label
htmlFor="password"
className="leading-7 text-sm text-gray-600"
>
</label>
<input
id="password"
className="w-full bg-white rounded border border-gray-300 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
required
type="password"
name="password"
/>
</div>
<LoginButton className="text-white bg-indigo-500 border-0 py-2 px-8 focus:outline-none hover:bg-indigo-600 rounded text-lg" />
<Link href="/signup" className="btn btn-link"></Link>
{errorMessage ? (
<p className="text-xs text-red-400 mt-3">{errorMessage}</p>
) : null}
</form> </form>
) </div>
</section>
);
} }
function LoginButton() { function LoginButton({ className }: { className: string | undefined }) {
const { pending } = useFormStatus() const { pending } = useFormStatus();
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
if (pending) { if (pending) {
event.preventDefault() event.preventDefault();
}
} }
};
return ( return (
<button aria-disabled={pending} type="submit" onClick={handleClick}> <button
Login className={className}
aria-disabled={pending}
type="submit"
onClick={handleClick}
>
</button> </button>
) );
} }

View File

@ -1,35 +1,125 @@
'use client' "use client";
import { signup } from '@/app/lib/actions' import { signup } from "@/app/lib/actions/auth";
import { useFormState, useFormStatus } from 'react-dom' import Link from "next/link";
import { useFormState, useFormStatus } from "react-dom";
export default function Page() { export default function Page() {
const [errorMessage, dispatch] = useFormState(signup, undefined) const [errorMessage, dispatch] = useFormState(signup, undefined);
console.log(errorMessage, '=== in client');
return ( return (
<form action={dispatch}> <section className="text-gray-600 body-font">
<input name="username" required /> <div className="container px-5 py-24 mx-auto flex flex-wrap items-center">
<input type="password" name="password" placeholder='密码' required /> <div className="lg:w-3/5 md:w-1/2 md:pr-16 lg:pr-0 pr-0">
<input type="password" name="confirm_password" placeholder='确认密码' required /> <h1 className="title-font font-medium text-3xl text-gray-900">
<div>{errorMessage && <p>{errorMessage}</p>}</div> Slow-carb next level shoindcgoitch ethical authentic, poko scenester
<LoginButton /> </h1>
<p className="leading-relaxed mt-4">
Poke slow-carb mixtape knausgaard, typewriter street art gentrify
hammock starladder roathse. Craies vegan tousled etsy austin.
</p>
</div>
<form
action={dispatch}
className="lg:w-2/6 md:w-1/2 bg-gray-100 rounded-lg p-8 flex flex-col md:ml-auto w-full mt-10 md:mt-0"
>
<h2 className="text-gray-900 text-lg font-medium title-font mb-5">
</h2>
<div className="relative mb-4">
<label
htmlFor="username"
className="leading-7 text-sm text-gray-600"
>
</label>
<input
id="username"
className="w-full bg-white rounded border border-gray-300 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
type="text"
required
name="username"
/>
</div>
<div className="relative mb-4">
<label
htmlFor="password"
className="leading-7 text-sm text-gray-600"
>
</label>
<input
id="password"
className="w-full bg-white rounded border border-gray-300 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
required
type="password"
name="password"
/>
</div>
<div className="relative mb-4">
<label
htmlFor="confirm_password"
className="leading-7 text-sm text-gray-600"
>
</label>
<input
id="confirm_password"
className="w-full bg-white rounded border border-gray-300 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
required
type="password"
name="confirm_password"
/>
</div>
<SignupButton className="text-white bg-indigo-500 border-0 py-2 px-8 focus:outline-none hover:bg-indigo-600 rounded text-lg" />
<Link href="/signin" className="btn btn-link"></Link>
{errorMessage ? (
<p className="text-xs text-red-400 mt-3">{errorMessage}</p>
) : null}
</form> </form>
) </div>
</section>
);
} }
function LoginButton() {
const { pending } = useFormStatus() // export default function Page() {
// const [errorMessage, dispatch] = useFormState(signup, undefined);
// return (
// <form action={dispatch}>
// <input name="username" required />
// <input type="password" name="password" placeholder="密码" required />
// <input
// type="password"
// name="confirm_password"
// placeholder="确认密码"
// required
// />
// <div>{errorMessage && <p>{errorMessage}</p>}</div>
// <LoginButton />
// </form>
// );
// }
function SignupButton({ className }: { className: string | undefined }) {
const { pending } = useFormStatus();
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
if (pending) { if (pending) {
event.preventDefault() event.preventDefault();
}
} }
};
return ( return (
<button aria-disabled={pending} type="submit" onClick={handleClick}> <button
Login {pending} className={className}
aria-disabled={pending}
type="submit"
onClick={handleClick}
>
</button> </button>
) );
} }

View File

@ -11,21 +11,21 @@
"dependencies": { "dependencies": {
"axios": "^1.6.8", "axios": "^1.6.8",
"next": "14.1.4", "next": "14.1.4",
"react": "^18", "react": "^18.2.0",
"react-dom": "^18", "react-dom": "^18.2.0",
"react-use-websocket": "^4.8.1" "react-use-websocket": "^4.8.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20", "@types/node": "^20.12.4",
"@types/react": "^18", "@types/react": "^18.2.74",
"@types/react-dom": "^18", "@types/react-dom": "^18.2.24",
"autoprefixer": "^10.0.1", "autoprefixer": "^10.4.19",
"daisyui": "^4.10.1", "daisyui": "^4.10.1",
"eslint": "^8", "eslint": "^8.57.0",
"eslint-config-next": "14.1.4", "eslint-config-next": "14.1.4",
"postcss": "^8", "postcss": "^8.4.38",
"tailwindcss": "^3.3.0", "tailwindcss": "^3.4.3",
"typescript": "^5" "typescript": "^5.4.4"
}, },
"packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589" "packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589"
} }

View File

@ -12,10 +12,10 @@ dependencies:
specifier: 14.1.4 specifier: 14.1.4
version: 14.1.4(react-dom@18.2.0)(react@18.2.0) version: 14.1.4(react-dom@18.2.0)(react@18.2.0)
react: react:
specifier: ^18 specifier: ^18.2.0
version: 18.2.0 version: 18.2.0
react-dom: react-dom:
specifier: ^18 specifier: ^18.2.0
version: 18.2.0(react@18.2.0) version: 18.2.0(react@18.2.0)
react-use-websocket: react-use-websocket:
specifier: ^4.8.1 specifier: ^4.8.1
@ -23,34 +23,34 @@ dependencies:
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: ^20 specifier: ^20.12.4
version: 20.12.4 version: 20.12.4
'@types/react': '@types/react':
specifier: ^18 specifier: ^18.2.74
version: 18.2.74 version: 18.2.74
'@types/react-dom': '@types/react-dom':
specifier: ^18 specifier: ^18.2.24
version: 18.2.24 version: 18.2.24
autoprefixer: autoprefixer:
specifier: ^10.0.1 specifier: ^10.4.19
version: 10.4.19(postcss@8.4.38) version: 10.4.19(postcss@8.4.38)
daisyui: daisyui:
specifier: ^4.10.1 specifier: ^4.10.1
version: 4.10.1(postcss@8.4.38) version: 4.10.1(postcss@8.4.38)
eslint: eslint:
specifier: ^8 specifier: ^8.57.0
version: 8.57.0 version: 8.57.0
eslint-config-next: eslint-config-next:
specifier: 14.1.4 specifier: 14.1.4
version: 14.1.4(eslint@8.57.0)(typescript@5.4.4) version: 14.1.4(eslint@8.57.0)(typescript@5.4.4)
postcss: postcss:
specifier: ^8 specifier: ^8.4.38
version: 8.4.38 version: 8.4.38
tailwindcss: tailwindcss:
specifier: ^3.3.0 specifier: ^3.4.3
version: 3.4.3 version: 3.4.3
typescript: typescript:
specifier: ^5 specifier: ^5.4.4
version: 5.4.4 version: 5.4.4
packages: packages:
@ -588,7 +588,7 @@ packages:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
browserslist: 4.23.0 browserslist: 4.23.0
caniuse-lite: 1.0.30001605 caniuse-lite: 1.0.30001606
fraction.js: 4.3.7 fraction.js: 4.3.7
normalize-range: 0.1.2 normalize-range: 0.1.2
picocolors: 1.0.0 picocolors: 1.0.0
@ -658,7 +658,7 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true hasBin: true
dependencies: dependencies:
caniuse-lite: 1.0.30001605 caniuse-lite: 1.0.30001606
electron-to-chromium: 1.4.728 electron-to-chromium: 1.4.728
node-releases: 2.0.14 node-releases: 2.0.14
update-browserslist-db: 1.0.13(browserslist@4.23.0) update-browserslist-db: 1.0.13(browserslist@4.23.0)
@ -692,8 +692,8 @@ packages:
engines: {node: '>= 6'} engines: {node: '>= 6'}
dev: true dev: true
/caniuse-lite@1.0.30001605: /caniuse-lite@1.0.30001606:
resolution: {integrity: sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==} resolution: {integrity: sha512-LPbwnW4vfpJId225pwjZJOgX1m9sGfbw/RKJvw/t0QhYOOaTXHvkjVGFGPpvwEzufrjvTlsULnVTxdy4/6cqkg==}
/chalk@4.1.2: /chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
@ -2073,7 +2073,7 @@ packages:
'@next/env': 14.1.4 '@next/env': 14.1.4
'@swc/helpers': 0.5.2 '@swc/helpers': 0.5.2
busboy: 1.6.0 busboy: 1.6.0
caniuse-lite: 1.0.30001605 caniuse-lite: 1.0.30001606
graceful-fs: 4.2.11 graceful-fs: 4.2.11
postcss: 8.4.31 postcss: 8.4.31
react: 18.2.0 react: 18.2.0