-
{ticket.ticket_number}
+
{ticket.title || ticket.ticket_number}
+ {ticket.title ?
{ticket.ticket_number}
: null}
{ticket.customer_name ?? "Keine Organisation"} · {workTypeLabel(ticket.work_type)} · {ticket.session_count} Session(s)
diff --git a/frontend/src/views/ProjectsPage.tsx b/frontend/src/views/ProjectsPage.tsx
index 737f198..95e87b9 100644
--- a/frontend/src/views/ProjectsPage.tsx
+++ b/frontend/src/views/ProjectsPage.tsx
@@ -1,10 +1,11 @@
-import { CalendarDays, FolderKanban, Play, Save, Search, Target } from "lucide-react";
+import { CalendarDays, FolderKanban, Play, Plus, Save, Search, Target } from "lucide-react";
import { FormEvent, useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { HelpLink } from "@/components/HelpLink";
@@ -22,6 +23,7 @@ type ProjectsPageProps = {
type ProjectForm = {
ticketNumber: string;
+ title: string;
organizationId: string;
organizationName: string | null;
teamspaceProject: string;
@@ -35,6 +37,7 @@ type ProjectForm = {
function emptyProjectForm(): ProjectForm {
return {
ticketNumber: "",
+ title: "",
organizationId: "",
organizationName: null,
teamspaceProject: "",
@@ -73,6 +76,7 @@ function parseHoursInput(value: string) {
function formFromProject(project: ProjectSummary): ProjectForm {
return {
ticketNumber: project.ticket_number,
+ title: project.title ?? "",
organizationId: project.organization_id ?? "",
organizationName: project.organization_name ?? project.customer_name ?? null,
teamspaceProject: project.teamspace_project ?? "",
@@ -84,6 +88,120 @@ function formFromProject(project: ProjectSummary): ProjectForm {
};
}
+type ProjectFormFieldsProps = {
+ form: ProjectForm;
+ setForm: (form: ProjectForm) => void;
+ organizationLocked: boolean;
+ lookupStatus: "idle" | "loading" | "found" | "not-found";
+ saving: boolean;
+ submitLabel: string;
+ onSubmit: (event: FormEvent) => void;
+};
+
+function ProjectFormFields({ form, setForm, organizationLocked, lookupStatus, saving, submitLabel, onSubmit }: ProjectFormFieldsProps) {
+ return (
+
+ );
+}
+
export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
const [projects, setProjects] = useState
([]);
const [loading, setLoading] = useState(true);
@@ -91,6 +209,7 @@ export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
const [lookupStatus, setLookupStatus] = useState<"idle" | "loading" | "found" | "not-found">("idle");
const [lookupResult, setLookupResult] = useState(null);
const [saving, setSaving] = useState(false);
+ const [createOpen, setCreateOpen] = useState(false);
const [editingProjectId, setEditingProjectId] = useState(null);
const [editForm, setEditForm] = useState(() => emptyProjectForm());
const [savingProjectId, setSavingProjectId] = useState(null);
@@ -141,6 +260,7 @@ export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
current.ticketNumber.trim() === normalizedTicketNumber
? {
...current,
+ title: current.title || result.ticket?.title || "",
organizationId: result.ticket?.organization_id ?? "",
organizationName: result.ticket?.organization_name ?? result.ticket?.customer_name ?? null
}
@@ -197,6 +317,7 @@ export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
try {
const result = await createProject({
ticketNumber,
+ title: form.title || null,
organizationId: form.organizationId,
teamspaceProject: form.teamspaceProject,
description: form.description,
@@ -209,6 +330,7 @@ export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
description: `${result.project.ticket_number} ist jetzt als Consulting-Projekt markiert.`
});
setForm(emptyProjectForm());
+ setCreateOpen(false);
setLookupResult(null);
setLookupStatus("idle");
await load();
@@ -238,6 +360,7 @@ export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
try {
await updateProject(project.id, {
ticketNumber: editForm.ticketNumber,
+ title: editForm.title || null,
organizationId: editForm.organizationId,
teamspaceProject: editForm.teamspaceProject,
description: editForm.description,
@@ -265,126 +388,48 @@ export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
Projekte
Consulting-Projekte als Erweiterung bestehender oder neuer Tickets.
-