|
|
|
|
@@ -184,11 +184,10 @@ function TaskCard({ task, provided, snapshot, columnAccent }: TaskCardProps): Re
|
|
|
|
|
interface KanbanColumnProps {
|
|
|
|
|
config: ColumnConfig;
|
|
|
|
|
tasks: Task[];
|
|
|
|
|
onAddTask: (status: TaskStatus, title: string, projectId?: string) => Promise<void>;
|
|
|
|
|
projectId?: string;
|
|
|
|
|
onAddTask: (status: TaskStatus, title: string) => Promise<void>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function KanbanColumn({ config, tasks, onAddTask, projectId }: KanbanColumnProps): ReactElement {
|
|
|
|
|
function KanbanColumn({ config, tasks, onAddTask }: KanbanColumnProps): ReactElement {
|
|
|
|
|
const [showAddForm, setShowAddForm] = useState(false);
|
|
|
|
|
const [inputValue, setInputValue] = useState("");
|
|
|
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
|
@@ -209,7 +208,7 @@ function KanbanColumn({ config, tasks, onAddTask, projectId }: KanbanColumnProps
|
|
|
|
|
|
|
|
|
|
setIsSubmitting(true);
|
|
|
|
|
try {
|
|
|
|
|
await onAddTask(config.status, inputValue.trim(), projectId);
|
|
|
|
|
await onAddTask(config.status, inputValue.trim());
|
|
|
|
|
setInputValue("");
|
|
|
|
|
setShowAddForm(false);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
@@ -363,45 +362,6 @@ function KanbanColumn({ config, tasks, onAddTask, projectId }: KanbanColumnProps
|
|
|
|
|
}}
|
|
|
|
|
autoFocus
|
|
|
|
|
/>
|
|
|
|
|
<div style={{ display: "flex", gap: 6, marginTop: 6 }}>
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
disabled={isSubmitting || !inputValue.trim()}
|
|
|
|
|
style={{
|
|
|
|
|
padding: "6px 12px",
|
|
|
|
|
borderRadius: "var(--r)",
|
|
|
|
|
border: "1px solid var(--primary)",
|
|
|
|
|
background: "var(--primary)",
|
|
|
|
|
color: "#fff",
|
|
|
|
|
fontSize: "0.8rem",
|
|
|
|
|
fontWeight: 500,
|
|
|
|
|
cursor: isSubmitting || !inputValue.trim() ? "not-allowed" : "pointer",
|
|
|
|
|
opacity: isSubmitting || !inputValue.trim() ? 0.5 : 1,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
✓ Add
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setShowAddForm(false);
|
|
|
|
|
setInputValue("");
|
|
|
|
|
}}
|
|
|
|
|
disabled={isSubmitting}
|
|
|
|
|
style={{
|
|
|
|
|
padding: "6px 12px",
|
|
|
|
|
borderRadius: "var(--r)",
|
|
|
|
|
border: "1px solid var(--border)",
|
|
|
|
|
background: "transparent",
|
|
|
|
|
color: "var(--muted)",
|
|
|
|
|
fontSize: "0.8rem",
|
|
|
|
|
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
|
|
|
opacity: isSubmitting ? 0.5 : 1,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div style={{ marginTop: 6, fontSize: "0.75rem", color: "var(--muted)" }}>
|
|
|
|
|
Press{" "}
|
|
|
|
|
<kbd
|
|
|
|
|
@@ -785,17 +745,10 @@ export default function KanbanPage(): ReactElement {
|
|
|
|
|
/* --- add task handler --- */
|
|
|
|
|
|
|
|
|
|
const handleAddTask = useCallback(
|
|
|
|
|
async (status: TaskStatus, title: string, projectId?: string) => {
|
|
|
|
|
async (status: TaskStatus, title: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const wsId = workspaceId ?? undefined;
|
|
|
|
|
const taskData: { title: string; status: TaskStatus; projectId?: string } = {
|
|
|
|
|
title,
|
|
|
|
|
status,
|
|
|
|
|
};
|
|
|
|
|
if (projectId) {
|
|
|
|
|
taskData.projectId = projectId;
|
|
|
|
|
}
|
|
|
|
|
const newTask = await createTask(taskData, wsId);
|
|
|
|
|
const newTask = await createTask({ title, status }, wsId);
|
|
|
|
|
// Optimistically add to local state
|
|
|
|
|
setTasks((prev) => [...prev, newTask]);
|
|
|
|
|
} catch (err: unknown) {
|
|
|
|
|
@@ -913,8 +866,23 @@ export default function KanbanPage(): ReactElement {
|
|
|
|
|
Clear filters
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
) : tasks.length === 0 ? (
|
|
|
|
|
/* Empty state */
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
background: "var(--surface)",
|
|
|
|
|
border: "1px solid var(--border)",
|
|
|
|
|
borderRadius: "var(--r-lg)",
|
|
|
|
|
padding: 48,
|
|
|
|
|
textAlign: "center",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<p style={{ color: "var(--muted)", margin: 0, fontSize: "0.9rem" }}>
|
|
|
|
|
No tasks yet. Create some tasks to see them here.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
/* Board (always render columns to allow adding first task) */
|
|
|
|
|
/* Board */
|
|
|
|
|
<DragDropContext onDragEnd={handleDragEnd}>
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
@@ -931,7 +899,6 @@ export default function KanbanPage(): ReactElement {
|
|
|
|
|
config={col}
|
|
|
|
|
tasks={grouped[col.status]}
|
|
|
|
|
onAddTask={handleAddTask}
|
|
|
|
|
projectId={filterProject}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|