feature: add projects page (2 parts works)

This commit is contained in:
Daniil
2026-01-29 00:57:22 +03:00
parent 3dfb9453ec
commit 2e4820ac91
65 changed files with 2223 additions and 45 deletions
+3 -1
View File
@@ -6,6 +6,7 @@ import { FunctionComponent } from "react"
import { useForm } from "react-hook-form"
import Link from "next/link"
import { useRouter } from "next/navigation"
import api from "@shared/api"
import { useCookie } from "@shared/hooks/useCookie"
@@ -26,6 +27,7 @@ interface ILoginFormData {
export const LoginPage: FunctionComponent<
ILoginPageProps
> = (): JSX.Element => {
const router = useRouter()
const [, setAccessTokenCookie] = useCookie(ACCESS_TOKEN_COOKIE)
const [, setRefreshTokenCookie] = useCookie(REFRESH_TOKEN_COOKIE)
@@ -33,7 +35,7 @@ export const LoginPage: FunctionComponent<
onSuccess: ({ access, refresh, user }) => {
setAccessTokenCookie(access)
setRefreshTokenCookie(refresh)
console.log("Login successful:", user)
router.push("/")
},
onError: (error) => {
console.error("Login failed:", error)
+1
View File
@@ -0,0 +1 @@
export * from "./ui/ProjectsPage"
+3
View File
@@ -0,0 +1,3 @@
export interface IProjectsPageProps {
message?: string
}
@@ -0,0 +1,38 @@
.root {
padding: 0 24px;
display: flex;
flex-direction: column;
gap: 32px;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
}
.titles {
display: flex;
flex-direction: column;
gap: 4px;
}
.title {
@include typography.font-display;
color: variables.$text-primary;
margin: 0;
}
.subtitle {
@include typography.font-body-16(600);
color: variables.$text-primary;
}
.projectList {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
margin-top: 24px;
padding-bottom: 40px;
}
@@ -0,0 +1,75 @@
"use client"
import type { JSX } from "react"
import { PlusIcon } from "lucide-react"
import { FunctionComponent, useState } from "react"
import { ProjectCard } from "@entities/ProjectCard"
import { CreateProjectModal } from "@features/CreateProjectModal"
import api from "@shared/api"
import { Button } from "@shared/ui"
import { StaticLoader } from "@shared/ui/Loader"
import { ProjectsHeader } from "@widgets/Projects/ProjectsHeader/ui/ProjectsHeader"
import { IProjectsPageProps } from "../model/ProjectsPage.d"
import styles from "./ProjectsPage.module.scss"
export const ProjectsPage: FunctionComponent<
IProjectsPageProps
> = (): JSX.Element => {
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false)
const {
data: projects,
isLoading: projectsLoading,
refetch: refetchProjects,
} = api.useQuery("get", "/api/projects/")
return (
<div className={styles.root} data-testid="ProjectsPage">
{projectsLoading && <StaticLoader fullscreen />}
<div className={styles.header}>
<div className={styles.titles}>
<h1 className={styles.title}>Мои проекты</h1>
<h4 className={styles.subtitle}>
Управляйте своими последними проектами
</h4>
</div>
<div>
<Button
variant="primary"
size="lg"
onClick={() => setIsCreateModalOpen(true)}
>
<PlusIcon /> Создать проект
</Button>
</div>
</div>
<CreateProjectModal
open={isCreateModalOpen}
onOpenChange={setIsCreateModalOpen}
onCreated={async () => {
await refetchProjects()
}}
/>
<ProjectsHeader />
<div className={styles.projectList}>
{projects?.map((project) => (
<ProjectCard
key={project.id}
project={project}
// Mock random progress for demo since API doesn't provide it yet
progress={project.status === "PROCESSING" ? 45 : 0}
currentAction={
project.status === "PROCESSING" ? "Rendering" : undefined
}
/>
))}
</div>
</div>
)
}