feat: Kanban touch drag, admins delete backups, bigger share message
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m5s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m5s
- Kanban on phones/tablets: hold a card, drag it onto a card or column, release; the page scrolls at the edges
- Admin settings: database backups can be deleted (DELETE /api/v1/admin/backups/{name})
- Share message: large Liste/Aufgabe buttons, close button, tap outside closes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
da258fdce2
commit
f76b0f34ff
14 files changed
+371
-57
No files matched your search
@@ -31,6 +31,7 @@ import {
|
||||
import { TaskList, Thought, SubTask, Team, User, TaskListMember, KanbanColumn, PriorityLevel, Recurrence, RECURRENCE_LABELS } from '../../lib/types';
|
||||
import { formatDue, isDueToday, isOverdue, localDay, splitDue } from '../../lib/due';
|
||||
import { readViewSession, writeViewSession } from '../../lib/viewSession';
|
||||
import { useKanbanTouchDrag } from './useKanbanTouchDrag';
|
||||
import { api, triggerHaptic } from '../../lib/api';
|
||||
import { UnifiedShareModal } from '../../components/UnifiedShareModal';
|
||||
import { TaskDetailModal } from './TaskDetailModal';
|
||||
@@ -807,7 +808,11 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
setDraggingTaskId(null);
|
||||
setDragOverTaskTarget(null);
|
||||
setDragOverKanbanCol(null);
|
||||
await moveTaskNextTo(sourceId, targetTask, position, newKanbanCol);
|
||||
};
|
||||
|
||||
// Shared by mouse drag & drop and the touch drag on the Kanban board
|
||||
const moveTaskNextTo = async (sourceId: string | null, targetTask: Thought, position: 'before' | 'after', newKanbanCol?: string) => {
|
||||
if (!sourceId || sourceId === targetTask.id) return;
|
||||
triggerHaptic(15);
|
||||
|
||||
@@ -864,7 +869,10 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
setDraggingTaskId(null);
|
||||
setDragOverTaskTarget(null);
|
||||
setDragOverKanbanCol(null);
|
||||
await moveTaskToColumn(sourceId, colId);
|
||||
};
|
||||
|
||||
const moveTaskToColumn = async (sourceId: string | null, colId: string) => {
|
||||
if (!sourceId) return;
|
||||
triggerHaptic(15);
|
||||
|
||||
@@ -905,6 +913,28 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
// Kanban with a finger: hold a card, drag it, let go (mouse and pen use HTML5 drag & drop)
|
||||
const clearDragState = () => {
|
||||
setDraggingTaskId(null);
|
||||
setDragOverTaskTarget(null);
|
||||
setDragOverKanbanCol(null);
|
||||
};
|
||||
const kanbanTouch = useKanbanTouchDrag({
|
||||
enabled: !canDragTasks && viewMode === 'kanban',
|
||||
onStart: (taskId) => setDraggingTaskId(taskId),
|
||||
onHover: (colId, target) => {
|
||||
setDragOverKanbanCol(colId);
|
||||
setDragOverTaskTarget(target);
|
||||
},
|
||||
onDrop: (taskId, colId, target) => {
|
||||
clearDragState();
|
||||
const targetTask = target ? tasks.find((t) => t.id === target.taskId) : undefined;
|
||||
if (targetTask && target) void moveTaskNextTo(taskId, targetTask, target.position, colId ?? undefined);
|
||||
else if (colId) void moveTaskToColumn(taskId, colId);
|
||||
},
|
||||
onCancel: clearDragState,
|
||||
});
|
||||
|
||||
const handleCreateTaskList = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const title = newListTitle.trim();
|
||||
@@ -2224,6 +2254,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
{viewMode === 'kanban' ? (
|
||||
/* DYNAMIC KANBAN BOARD */
|
||||
<div className="space-y-2">
|
||||
{kanbanTouch.ghost}
|
||||
<div
|
||||
className={`grid grid-cols-1 md:grid-cols-2 gap-3 pt-1 lg:w-[var(--kanban-w)] lg:ml-[var(--kanban-ml)] transition-[width,margin] duration-300 ease-out ${
|
||||
isKanbanWide ? 'lg:grid-cols-[repeat(var(--kanban-cols),minmax(0,1fr))]' : 'lg:grid-cols-4'
|
||||
@@ -2251,6 +2282,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
if (dragOverKanbanCol === col.id) setDragOverKanbanCol(null);
|
||||
}}
|
||||
onDrop={(e) => handleDropOnKanbanCol(e, col.id)}
|
||||
data-kanban-col={col.id}
|
||||
className={`bg-canvas border border-border flex flex-col min-h-[420px] overflow-hidden transition-all ${
|
||||
dragOverKanbanCol === col.id ? 'ring-2 ring-accent/60 bg-accent/5' : ''
|
||||
}`}
|
||||
@@ -2295,11 +2327,14 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
onDragOver={(e) => handleTaskDragOver(e, task.id)}
|
||||
onDragLeave={(e) => handleTaskDragLeave(e, task.id)}
|
||||
onDrop={(e) => handleDropOnTask(e, task, col.id)}
|
||||
data-kanban-task={task.id}
|
||||
{...kanbanTouch.cardProps(task.id, task.content, !readOnly)}
|
||||
onClick={() => {
|
||||
if (kanbanTouch.consumeClick()) return;
|
||||
setDetailTaskId(task.id);
|
||||
setShowDetailModal(true);
|
||||
}}
|
||||
className={`panel-surface p-2.5 space-y-2 hover:border-strong transition-all shadow-sm group relative ${
|
||||
className={`panel-surface p-2.5 space-y-2 hover:border-strong transition-all shadow-sm group relative select-none ${
|
||||
readOnly ? 'cursor-pointer' : 'cursor-grab active:cursor-grabbing'
|
||||
} ${
|
||||
isDragging ? 'opacity-30 border border-dashed border-accent/40' : ''
|
||||
|
||||
Reference in new issue
Block a user