Feat: started admin panel
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -41,4 +41,8 @@ yarn-error.log*
|
||||
next-env.d.ts
|
||||
|
||||
# repomix
|
||||
repomix-output.txt
|
||||
repomix-output.txt
|
||||
|
||||
# vscode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"scss.lint.unknownAtRules": "ignore"
|
||||
}
|
||||
@@ -10,12 +10,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@mantine/core": "^7.16.1",
|
||||
"@mantine/form": "^7.16.1",
|
||||
"@mantine/hooks": "^7.16.1",
|
||||
"@mantine/modals": "^7.16.1",
|
||||
"next": "15.1.5",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-icons": "^5.4.0"
|
||||
"react-icons": "^5.4.0",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
|
||||
9
src/app/admin/actions.ts
Normal file
9
src/app/admin/actions.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
"use server";
|
||||
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export async function login(formData: { name: string; password: string }) {
|
||||
console.log("data");
|
||||
console.log(formData);
|
||||
redirect("/admin/panel");
|
||||
}
|
||||
0
src/app/admin/admin.module.scss
Normal file
0
src/app/admin/admin.module.scss
Normal file
60
src/app/admin/page.tsx
Normal file
60
src/app/admin/page.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import { Button, Card, Flex, PasswordInput, Text, TextInput } from "@mantine/core";
|
||||
import { hasLength, useForm } from "@mantine/form";
|
||||
import { login } from "./actions";
|
||||
|
||||
interface FormValues {
|
||||
name: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const AdminPage = () => {
|
||||
const form = useForm<FormValues>({
|
||||
mode: "uncontrolled",
|
||||
initialValues: {
|
||||
name: "",
|
||||
password: "",
|
||||
},
|
||||
validate: {
|
||||
name: hasLength({ min: 5 }, "Too short"),
|
||||
password: hasLength({ min: 5 }, "Too short"),
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex w="100vw" h="100vh" justify="center" align="center">
|
||||
<Card shadow="md" w="400" radius="md">
|
||||
<form onSubmit={form.onSubmit(login)}>
|
||||
<Text c="dimmed" size="xl" ta="center">
|
||||
Admin
|
||||
</Text>
|
||||
<TextInput
|
||||
label="Name"
|
||||
placeholder="Username"
|
||||
key={form.key("name")}
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
name="name"
|
||||
autoComplete="username"
|
||||
/>
|
||||
<PasswordInput
|
||||
label="Password"
|
||||
mt="md"
|
||||
withAsterisk
|
||||
placeholder="password"
|
||||
key={form.key("password")}
|
||||
name="password"
|
||||
autoComplete="current-password"
|
||||
{...form.getInputProps("password")}
|
||||
/>
|
||||
<Button radius="md" type="submit" mt="md" w="100%">
|
||||
Login
|
||||
</Button>
|
||||
</form>
|
||||
</Card>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminPage;
|
||||
35
src/app/admin/panel/page.tsx
Normal file
35
src/app/admin/panel/page.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
import { ActionIcon, AppShell, Burger, Flex, Group, Skeleton, useMantineColorScheme } from "@mantine/core";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { LuMoon, LuSun } from "react-icons/lu";
|
||||
|
||||
const PanelPage = () => {
|
||||
const [opened, { toggle }] = useDisclosure();
|
||||
const { setColorScheme, colorScheme } = useMantineColorScheme();
|
||||
const changeColorScheme = () => {
|
||||
setColorScheme(colorScheme === "light" ? "dark" : "light");
|
||||
};
|
||||
return (
|
||||
<AppShell header={{ height: 60 }} navbar={{ width: 300, breakpoint: "sm", collapsed: { mobile: !opened } }}>
|
||||
<AppShell.Header>
|
||||
<Flex h="100%" px="md" justify="space-between" align="center">
|
||||
<Group h="100%">
|
||||
<Burger opened={opened} onClick={toggle} size="sm" hiddenFrom="sm" />
|
||||
<div>Logo</div>
|
||||
</Group>
|
||||
<ActionIcon onClick={changeColorScheme} variant="default" size="md" aria-label="Toggle color scheme">
|
||||
{colorScheme === "light" ? <LuMoon /> : <LuSun />}
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
</AppShell.Header>
|
||||
<AppShell.Navbar p="md">
|
||||
{Array(15)
|
||||
.fill(0)
|
||||
.map((_, index) => (
|
||||
<Skeleton key={index} h={28} mt="sm" animate={false} />
|
||||
))}
|
||||
</AppShell.Navbar>
|
||||
</AppShell>
|
||||
);
|
||||
};
|
||||
export default PanelPage;
|
||||
3
src/app/api/route.ts
Normal file
3
src/app/api/route.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export async function GET() {
|
||||
return Response.json({ test: 1 });
|
||||
}
|
||||
3
src/lib/auth.ts
Normal file
3
src/lib/auth.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function isAuthenticated() {
|
||||
return true;
|
||||
}
|
||||
14
src/middleware.ts
Normal file
14
src/middleware.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { isAuthenticated } from "@/lib/auth";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export const config = {
|
||||
matcher: "/admin/:path*",
|
||||
};
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
if (pathname === "/admin" || isAuthenticated()) {
|
||||
return NextResponse.next();
|
||||
}
|
||||
return NextResponse.redirect(new URL("/admin", request.url));
|
||||
}
|
||||
Reference in New Issue
Block a user