99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useState } from "react";
|
||
import { useParams } from "next/navigation";
|
||
import { getProjects, Project } from "@/lib/api";
|
||
import Sidebar from "@/components/Sidebar";
|
||
import KanbanBoard from "@/components/KanbanBoard";
|
||
import ChatPanel from "@/components/ChatPanel";
|
||
import ProjectFiles from "@/components/ProjectFiles";
|
||
import ProjectSettings from "@/components/ProjectSettings";
|
||
|
||
const TABS = [
|
||
{ key: "board", label: "📋 Доска" },
|
||
{ key: "chat", label: "💬 Чат" },
|
||
{ key: "files", label: "📁 Файлы" },
|
||
{ key: "settings", label: "⚙️ Настройки" },
|
||
];
|
||
|
||
export default function ProjectPage() {
|
||
const { slug } = useParams<{ slug: string }>();
|
||
const [projects, setProjects] = useState<Project[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [activeTab, setActiveTab] = useState("board");
|
||
|
||
useEffect(() => {
|
||
getProjects()
|
||
.then(setProjects)
|
||
.catch(() => {})
|
||
.finally(() => setLoading(false));
|
||
}, []);
|
||
|
||
const project = projects.find((p) => p.slug === slug);
|
||
|
||
const handleProjectUpdated = (updated: Project) => {
|
||
setProjects((prev) => prev.map((p) => (p.id === updated.id ? updated : p)));
|
||
};
|
||
|
||
if (loading) {
|
||
return <div className="flex h-screen items-center justify-center text-[var(--muted)]">Загрузка...</div>;
|
||
}
|
||
|
||
if (!project) {
|
||
return <div className="flex h-screen items-center justify-center text-[var(--muted)]">Проект не найден</div>;
|
||
}
|
||
|
||
return (
|
||
<div className="flex h-screen">
|
||
<Sidebar projects={projects} activeSlug={slug} />
|
||
<main className="flex-1 flex flex-col overflow-hidden">
|
||
<header className="border-b border-[var(--border)] px-6 py-3 pl-14 md:pl-6">
|
||
<div className="flex items-center justify-between mb-2">
|
||
<div>
|
||
<h1 className="text-xl font-bold">{project.name}</h1>
|
||
{project.description && (
|
||
<p className="text-sm text-[var(--muted)]">{project.description}</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="flex gap-1">
|
||
{TABS.map((tab) => (
|
||
<button
|
||
key={tab.key}
|
||
onClick={() => setActiveTab(tab.key)}
|
||
className={`px-3 py-1.5 rounded text-sm transition-colors ${
|
||
activeTab === tab.key
|
||
? "bg-[var(--accent)]/10 text-[var(--accent)]"
|
||
: "text-[var(--muted)] hover:bg-white/5 hover:text-[var(--fg)]"
|
||
}`}
|
||
>
|
||
{tab.label}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</header>
|
||
|
||
<div className="flex-1 overflow-hidden">
|
||
{activeTab === "board" && (
|
||
<KanbanBoard projectId={project.id} projectSlug={project.slug} />
|
||
)}
|
||
{activeTab === "chat" && project.chat_id && (
|
||
<ChatPanel chatId={project.chat_id} fullscreen />
|
||
)}
|
||
{activeTab === "chat" && !project.chat_id && (
|
||
<div className="flex items-center justify-center h-full text-[var(--muted)] text-sm">
|
||
Чат недоступен
|
||
</div>
|
||
)}
|
||
{activeTab === "files" && (
|
||
<ProjectFiles project={project} />
|
||
)}
|
||
{activeTab === "settings" && (
|
||
<ProjectSettings project={project} onUpdated={handleProjectUpdated} />
|
||
)}
|
||
</div>
|
||
</main>
|
||
</div>
|
||
);
|
||
}
|