nf
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
export interface IHomePageProps {
|
||||
className?: string
|
||||
}
|
||||
@@ -1,36 +1,48 @@
|
||||
.homepage {
|
||||
.root {
|
||||
padding: 28px 24px 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
|
||||
height: 100vh;
|
||||
|
||||
color: #c7d0cc;
|
||||
|
||||
background: #000;
|
||||
|
||||
font-family: Roboto, sans-serif;
|
||||
font-size: 2vw;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.path {
|
||||
font-style: italic;
|
||||
.welcome {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
min-height: 160px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 4vw;
|
||||
.greeting {
|
||||
font-weight: 700;
|
||||
font-size: 52px;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -1px;
|
||||
color: variables.$text-primary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hint {
|
||||
padding: 0.5rem;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
border: rgb(199 208 204 / 5%) 1px solid;
|
||||
border-radius: 15px;
|
||||
|
||||
font-size: 1vw;
|
||||
.subtitle {
|
||||
@include typography.font-body-mr;
|
||||
font-size: 18px;
|
||||
color: variables.$text-secondary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.actionsSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.actionsTitle {
|
||||
@include typography.font-header-l;
|
||||
color: variables.$text-primary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,103 @@
|
||||
"use client"
|
||||
|
||||
import { useBreadcrumbs } from "@shared/context/BreadcrumbsContext"
|
||||
import { Button } from "@shared/ui"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { FunctionComponent, useMemo, useState } from "react"
|
||||
|
||||
import { FolderKanban, PlusIcon } from "lucide-react"
|
||||
import { useRouter } from "next/navigation"
|
||||
|
||||
import { ActionCard } from "@entities/dashboard"
|
||||
import { CreateProjectModal } from "@features/project"
|
||||
import api from "@shared/api"
|
||||
import { useBreadcrumbs } from "@shared/context/BreadcrumbsContext"
|
||||
import { useAppSelector } from "@shared/hooks/useAppSelector"
|
||||
import { StaticLoader } from "@shared/ui/Loader"
|
||||
import { RecentProjects } from "@widgets/Dashboard/RecentProjects"
|
||||
import { StatsGrid } from "@widgets/Dashboard/StatsGrid"
|
||||
|
||||
import { IHomePageProps } from "./HomePage.d"
|
||||
import cls from "./HomePage.module.scss"
|
||||
|
||||
const HomePage = () => {
|
||||
const RECENT_PROJECTS_LIMIT = 3
|
||||
|
||||
const WELCOME_SUBTITLES = [
|
||||
"Управляйте своими проектами и отслеживайте их статус",
|
||||
"Что будем делать сегодня?",
|
||||
"Ваши проекты ждут вас",
|
||||
"Создайте новый проект или продолжите работу над существующим",
|
||||
"Всё под контролем — просматривайте и управляйте проектами",
|
||||
]
|
||||
|
||||
export const HomePage: FunctionComponent<IHomePageProps> = (): JSX.Element => {
|
||||
useBreadcrumbs([{ label: "Главная" }])
|
||||
const router = useRouter()
|
||||
const user = useAppSelector((state) => state.user.user)
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false)
|
||||
|
||||
const { data: projects, isLoading, refetch } = api.useQuery("get", "/api/projects/")
|
||||
|
||||
const subtitle = useMemo(
|
||||
() => WELCOME_SUBTITLES[Math.floor(Math.random() * WELCOME_SUBTITLES.length)],
|
||||
[],
|
||||
)
|
||||
|
||||
const stats = {
|
||||
total: projects?.length ?? 0,
|
||||
processing: projects?.filter((p) => p.status === "PROCESSING").length ?? 0,
|
||||
done: projects?.filter((p) => p.status === "DONE").length ?? 0,
|
||||
failed: projects?.filter((p) => p.status === "FAILED").length ?? 0,
|
||||
}
|
||||
|
||||
const recentProjects = [...(projects ?? [])]
|
||||
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
|
||||
.slice(0, RECENT_PROJECTS_LIMIT)
|
||||
|
||||
const userName = user?.first_name || user?.username || "пользователь"
|
||||
|
||||
return (
|
||||
<div className={cls.homepage}>
|
||||
<p className={cls.title}>Coffee Project Starter</p>
|
||||
<pre className={cls.hint}>
|
||||
Редактируйте <span className={cls.path}>src/pages/HomePage</span>{" "}
|
||||
чтобы начать разработку.
|
||||
</pre>
|
||||
<Button variant="primary">Начать</Button>
|
||||
<div className={cls.root}>
|
||||
{isLoading && <StaticLoader fullscreen />}
|
||||
|
||||
<div className={cls.welcome}>
|
||||
<h1 className={cls.greeting}>Добро пожаловать, {userName}</h1>
|
||||
<p className={cls.subtitle}>{subtitle}</p>
|
||||
</div>
|
||||
|
||||
<StatsGrid {...stats} />
|
||||
|
||||
<RecentProjects
|
||||
projects={recentProjects}
|
||||
isLoading={isLoading}
|
||||
onProjectClick={(id) => router.push(`/projects/${id}`)}
|
||||
onCreateClick={() => setIsCreateModalOpen(true)}
|
||||
onViewAllClick={() => router.push("/projects")}
|
||||
/>
|
||||
|
||||
<div className={cls.actionsSection}>
|
||||
<h2 className={cls.actionsTitle}>Быстрые действия</h2>
|
||||
<div className={cls.actions}>
|
||||
<ActionCard
|
||||
icon={PlusIcon}
|
||||
label="Создать проект"
|
||||
accent
|
||||
onClick={() => setIsCreateModalOpen(true)}
|
||||
/>
|
||||
<ActionCard
|
||||
icon={FolderKanban}
|
||||
label="Все проекты"
|
||||
onClick={() => router.push("/projects")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CreateProjectModal
|
||||
open={isCreateModalOpen}
|
||||
onOpenChange={setIsCreateModalOpen}
|
||||
onCreated={async () => {
|
||||
await refetch()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user