110 lines
3.2 KiB
React
110 lines
3.2 KiB
React
import React, { useEffect, useState, useCallback } from "react";
|
|
import { api } from "./api";
|
|
import ProjectsTab from "./components/ProjectsTab.jsx";
|
|
import PeopleTab from "./components/PeopleTab.jsx";
|
|
import TimelineTab from "./components/TimelineTab.jsx";
|
|
import CapacityTab from "./components/CapacityTab.jsx";
|
|
|
|
const NAV = [
|
|
{ key: "timeline", index: "01", label: "Zeitachse" },
|
|
{ key: "capacity", index: "02", label: "Kapazitaet" },
|
|
{ key: "projects", index: "03", label: "Projekte & Aufgaben" },
|
|
{ key: "people", index: "04", label: "Team" }
|
|
];
|
|
|
|
export default function App() {
|
|
const [view, setView] = useState("timeline");
|
|
const [people, setPeople] = useState([]);
|
|
const [projects, setProjects] = useState([]);
|
|
const [tasks, setTasks] = useState([]);
|
|
const [absences, setAbsences] = useState([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [loadError, setLoadError] = useState(null);
|
|
|
|
const reloadAll = useCallback(async () => {
|
|
try {
|
|
const [p, pr, t, a] = await Promise.all([
|
|
api.people.list(),
|
|
api.projects.list(),
|
|
api.tasks.list(),
|
|
api.absences.list()
|
|
]);
|
|
setPeople(p);
|
|
setProjects(pr);
|
|
setTasks(t);
|
|
setAbsences(a);
|
|
setLoadError(null);
|
|
} catch (err) {
|
|
setLoadError(err.message);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
reloadAll();
|
|
}, [reloadAll]);
|
|
|
|
const currentYear = new Date().getFullYear();
|
|
|
|
return (
|
|
<div className="app-shell">
|
|
<aside className="sidebar">
|
|
<div className="brand">
|
|
<span className="brand-mark">Jahresplan</span>
|
|
<span className="brand-sub">Planung & Kapazitaet {currentYear}</span>
|
|
</div>
|
|
<ul className="nav-list">
|
|
{NAV.map((item) => (
|
|
<li key={item.key}>
|
|
<button
|
|
className={`nav-item ${view === item.key ? "active" : ""}`}
|
|
onClick={() => setView(item.key)}
|
|
>
|
|
<span className="nav-index">{item.index}</span>
|
|
{item.label}
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<div className="sidebar-foot">
|
|
{people.length} Personen · {projects.length} Projekte
|
|
<br />
|
|
{tasks.length} Aufgaben insgesamt
|
|
</div>
|
|
</aside>
|
|
|
|
<main className="main">
|
|
{loadError && (
|
|
<div className="panel" style={{ borderColor: "var(--danger)" }}>
|
|
<p className="error-text" style={{ margin: 0 }}>
|
|
Daten konnten nicht geladen werden: {loadError}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{!loading && view === "timeline" && (
|
|
<TimelineTab projects={projects} tasks={tasks} people={people} />
|
|
)}
|
|
|
|
{!loading && view === "capacity" && (
|
|
<CapacityTab people={people} />
|
|
)}
|
|
|
|
{!loading && view === "projects" && (
|
|
<ProjectsTab
|
|
projects={projects}
|
|
tasks={tasks}
|
|
people={people}
|
|
onChange={reloadAll}
|
|
/>
|
|
)}
|
|
|
|
{!loading && view === "people" && (
|
|
<PeopleTab people={people} absences={absences} onChange={reloadAll} />
|
|
)}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|