web-client-vite/src/components/CreateTaskModal.tsx
Markov 3a2a32a50e fix: restore all original components (KanbanBoard, ChatPanel, TaskModal, etc.)
Previous migration stubbed most components. Now properly ported from
Next.js with type-only imports and react-router-dom Link/navigation.
2026-02-24 09:33:04 +01:00

113 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from "react";
import type { Task } from "@/lib/api";
import { createTask } from "@/lib/api";
const PRIORITIES = [
{ key: "low", label: "Low", color: "#737373" },
{ key: "medium", label: "Medium", color: "#3b82f6" },
{ key: "high", label: "High", color: "#f59e0b" },
{ key: "critical", label: "Critical", color: "#ef4444" },
];
interface Props {
projectSlug: string;
initialStatus: string;
onClose: () => void;
onCreated: (task: Task) => void;
}
export default function CreateTaskModal({ projectSlug, initialStatus, onClose, onCreated }: Props) {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [priority, setPriority] = useState("medium");
const [saving, setSaving] = useState(false);
const [error, setError] = useState("");
const handleSubmit = async () => {
if (!title.trim()) return;
setSaving(true);
setError("");
try {
const task = await createTask(projectSlug, {
title: title.trim(),
description: description.trim() || undefined,
status: initialStatus,
priority,
});
onCreated(task);
onClose();
} catch (e: any) {
setError(e.message || "Ошибка");
} finally {
setSaving(false);
}
};
return (
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 px-4" onClick={onClose}>
<div
className="bg-[var(--card)] border border-[var(--border)] rounded-xl w-full max-w-md p-6"
onClick={(e) => e.stopPropagation()}
>
<h3 className="text-lg font-bold mb-4">Новая задача</h3>
<div className="space-y-4">
<div>
<label className="text-xs text-[var(--muted)] mb-1 block">Название</label>
<input
autoFocus
value={title}
onChange={(e) => setTitle(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSubmit()}
placeholder="Название задачи..."
className="w-full bg-[var(--bg)] border border-[var(--border)] rounded-lg px-3 py-2 text-sm outline-none focus:border-[var(--accent)]"
/>
</div>
<div>
<label className="text-xs text-[var(--muted)] mb-1 block">Описание</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Описание (опционально)..."
rows={3}
className="w-full bg-[var(--bg)] border border-[var(--border)] rounded-lg px-3 py-2 text-sm outline-none focus:border-[var(--accent)] resize-y"
/>
</div>
<div>
<label className="text-xs text-[var(--muted)] mb-1 block">Приоритет</label>
<div className="flex gap-1">
{PRIORITIES.map((p) => (
<button
key={p.key}
onClick={() => setPriority(p.key)}
className={`flex items-center gap-1 px-2 py-1 rounded text-xs transition-colors
${priority === p.key ? "bg-white/10 text-[var(--fg)]" : "text-[var(--muted)] hover:bg-white/5"}`}
>
<span className="w-2 h-2 rounded-full" style={{ background: p.color }} />
{p.label}
</button>
))}
</div>
</div>
{error && <div className="text-xs text-red-400">{error}</div>}
<div className="flex gap-2 justify-end pt-2">
<button onClick={onClose} className="px-4 py-2 text-sm text-[var(--muted)]">Отмена</button>
<button
onClick={handleSubmit}
disabled={!title.trim() || saving}
className="px-4 py-2 bg-[var(--accent)] text-white rounded-lg text-sm disabled:opacity-50"
>
{saving ? "..." : "Создать"}
</button>
</div>
</div>
</div>
</div>
);
}