feat(mobile): simplify triage quick dump capture and convert priority to 4 buttons across tasks
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m4s

This commit is contained in:
sandrodach committed 2026-10-26 12:56:11 +01:00
1 parent 832f2f9c2b
commit baac20ea10
4 files changed
+269 -199

No files matched your search

+6 -2
View File
@@ -1034,9 +1034,11 @@ export const App: React.FC = () => {
<div className="flex items-center justify-between pb-3 border-b border-border mb-3"> <div className="flex items-center justify-between pb-3 border-b border-border mb-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-6 h-6 rounded-btn bg-accent/15 text-accent flex items-center justify-center"> <div className="w-6 h-6 rounded-btn bg-accent/15 text-accent flex items-center justify-center">
<Plus className="w-4 h-4" /> {activeTab === 'triage' ? <Layers className="w-4 h-4" /> : <Plus className="w-4 h-4" />}
</div> </div>
<h3 className="text-sm font-semibold text-main">Schnell erfassen</h3> <h3 className="text-sm font-semibold text-main">
{activeTab === 'triage' ? 'Quick Dump erfassen' : 'Schnell erfassen'}
</h3>
</div> </div>
<button <button
type="button" type="button"
@@ -1052,6 +1054,8 @@ export const App: React.FC = () => {
<InstantCaptureBar <InstantCaptureBar
inlineOptions={true} inlineOptions={true}
defaultShowOptions={true} defaultShowOptions={true}
hideTargetPicker={activeTab === 'triage'}
fixedTarget={activeTab === 'triage' ? 'inbox' : undefined}
onCaptured={(thought, target) => { onCaptured={(thought, target) => {
handleCaptured(thought, target); handleCaptured(thought, target);
setShowMobileCapture(false); setShowMobileCapture(false);
@@ -11,6 +11,8 @@ import {
FileText, FileText,
Layers, Layers,
ChevronDown, ChevronDown,
AlertTriangle,
Flame,
} from 'lucide-react'; } from 'lucide-react';
import { PriorityLevel, SharedList, Thought } from '../../lib/types'; import { PriorityLevel, SharedList, Thought } from '../../lib/types';
import { api, triggerHaptic } from '../../lib/api'; import { api, triggerHaptic } from '../../lib/api';
@@ -23,6 +25,8 @@ interface InstantCaptureBarProps {
autoFocusTrigger?: number; autoFocusTrigger?: number;
inlineOptions?: boolean; inlineOptions?: boolean;
defaultShowOptions?: boolean; defaultShowOptions?: boolean;
hideTargetPicker?: boolean;
fixedTarget?: CaptureTarget;
} }
export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
@@ -31,13 +35,15 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
autoFocusTrigger = 0, autoFocusTrigger = 0,
inlineOptions = false, inlineOptions = false,
defaultShowOptions = false, defaultShowOptions = false,
hideTargetPicker = false,
fixedTarget,
}) => { }) => {
const [content, setContent] = useState(''); const [content, setContent] = useState('');
const [priority, setPriority] = useState<PriorityLevel>('normal'); const [priority, setPriority] = useState<PriorityLevel>('normal');
const [dueDate, setDueDate] = useState(''); const [dueDate, setDueDate] = useState('');
const [target, setTarget] = useState<CaptureTarget>('inbox'); const [target, setTarget] = useState<CaptureTarget>(fixedTarget || 'inbox');
const [targetListId, setTargetListId] = useState<string>(''); const [targetListId, setTargetListId] = useState<string>('');
const [showOptions, setShowOptions] = useState(defaultShowOptions); const [showOptions, setShowOptions] = useState(defaultShowOptions || hideTargetPicker);
const [isListening, setIsListening] = useState(false); const [isListening, setIsListening] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [justSavedLabel, setJustSavedLabel] = useState<string | null>(null); const [justSavedLabel, setJustSavedLabel] = useState<string | null>(null);
@@ -169,7 +175,7 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
const trimmed = content.trim(); const trimmed = content.trim();
if (!trimmed || isSubmitting) return; if (!trimmed || isSubmitting) return;
const effectiveTarget = overrideTarget || target; const effectiveTarget = fixedTarget || overrideTarget || target;
const effectiveListId = const effectiveListId =
overrideListId || targetListId || (lists.length > 0 ? lists[0].id : ''); overrideListId || targetListId || (lists.length > 0 ? lists[0].id : '');
const capturedPriority = priority; const capturedPriority = priority;
@@ -181,8 +187,8 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
setIsMultiline(false); setIsMultiline(false);
setPriority('normal'); setPriority('normal');
setDueDate(''); setDueDate('');
setTarget('inbox'); setTarget(fixedTarget || 'inbox');
setShowOptions(false); setShowOptions(defaultShowOptions || hideTargetPicker);
setIsSubmitting(true); setIsSubmitting(true);
triggerHaptic(15); triggerHaptic(15);
if (inputRef.current) { if (inputRef.current) {
@@ -196,7 +202,7 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
? 'Auf Liste!' ? 'Auf Liste!'
: effectiveTarget === 'document' : effectiveTarget === 'document'
? 'Als Notiz!' ? 'Als Notiz!'
: 'Im Dump!'; : 'In Triage!';
setJustSavedLabel(savedMsg); setJustSavedLabel(savedMsg);
setTimeout(() => setJustSavedLabel(null), 1600); setTimeout(() => setJustSavedLabel(null), 1600);
inputRef.current?.focus(); inputRef.current?.focus();
@@ -275,6 +281,8 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
placeholder={ placeholder={
isListening isListening
? 'Ich höre zu…' ? 'Ich höre zu…'
: hideTargetPicker || fixedTarget === 'inbox'
? 'Quick Dump erfassen… (Enter zum Speichern)'
: target === 'task' : target === 'task'
? 'Neue Aufgabe direkt anlegen… (Enter / Shift+Enter für Mehrzeiler)' ? 'Neue Aufgabe direkt anlegen… (Enter / Shift+Enter für Mehrzeiler)'
: target === 'list_item' : target === 'list_item'
@@ -293,32 +301,34 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
isMultiline ? 'self-end mb-0.5' : '' isMultiline ? 'self-end mb-0.5' : ''
}`} }`}
> >
{/* Target & Options Toggle Pill */} {/* Target & Options Toggle Pill (Hidden when target picker is disabled) */}
<button {!hideTargetPicker && (
type="button" <button
onClick={() => setShowOptions((prev) => !prev)} type="button"
title="Ziel wählen (Quick Dump, direkt als Aufgabe, auf Liste oder als Notiz) & Optionen" onClick={() => setShowOptions((prev) => !prev)}
className={`h-6 px-2.5 capture-pill rounded-full text-[11px] flex items-center gap-1 transition-colors cursor-pointer ${ title="Ziel wählen (Quick Dump, direkt als Aufgabe, auf Liste oder als Notiz) & Optionen"
showOptions || target !== 'inbox' || priority !== 'normal' || dueDate className={`h-6 px-2.5 capture-pill rounded-full text-[11px] flex items-center gap-1 transition-colors cursor-pointer ${
? 'bg-accent-dim text-accent border border-accent-border font-medium' showOptions || target !== 'inbox' || priority !== 'normal' || dueDate
: 'text-muted hover:text-main hover:bg-panel-elevated border border-transparent' ? 'bg-accent-dim text-accent border border-accent-border font-medium'
}`} : 'text-muted hover:text-main hover:bg-panel-elevated border border-transparent'
> }`}
{target === 'task' ? ( >
<CheckSquare className="w-3 h-3" /> {target === 'task' ? (
) : target === 'list_item' ? ( <CheckSquare className="w-3 h-3" />
<ShoppingCart className="w-3 h-3" /> ) : target === 'list_item' ? (
) : target === 'document' ? ( <ShoppingCart className="w-3 h-3" />
<FileText className="w-3 h-3" /> ) : target === 'document' ? (
) : ( <FileText className="w-3 h-3" />
<Layers className="w-3 h-3" /> ) : (
)} <Layers className="w-3 h-3" />
<span className={`${inlineOptions ? 'inline' : 'hidden md:inline'} max-w-[110px] truncate`}> )}
{getTargetLabel()} <span className={`${inlineOptions ? 'inline' : 'hidden md:inline'} max-w-[110px] truncate`}>
</span> {getTargetLabel()}
{dueDate && <span className="hidden lg:inline text-[10px] opacity-80">• {dueDate.slice(5)}</span>} </span>
<ChevronDown className={`w-3 h-3 opacity-70 transition-transform ${showOptions ? 'rotate-180' : ''}`} /> {dueDate && <span className="hidden lg:inline text-[10px] opacity-80">• {dueDate.slice(5)}</span>}
</button> <ChevronDown className={`w-3 h-3 opacity-70 transition-transform ${showOptions ? 'rotate-180' : ''}`} />
</button>
)}
{/* Voice Dictation Button */} {/* Voice Dictation Button */}
<button <button
@@ -347,13 +357,15 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
{justSavedLabel ? ( {justSavedLabel ? (
<> <>
<Check className="w-3 h-3" /> <Check className="w-3 h-3" />
<span className="hidden sm:inline">{justSavedLabel}</span> <span className="inline">{justSavedLabel}</span>
</> </>
) : ( ) : (
<> <>
<ArrowUp className="w-3 h-3" /> <ArrowUp className="w-3 h-3" />
<span className="hidden sm:inline"> <span className="inline">
{target === 'task' {hideTargetPicker || (fixedTarget || target) === 'inbox'
? 'Quick Dump'
: target === 'task'
? 'Als Aufgabe' ? 'Als Aufgabe'
: target === 'list_item' : target === 'list_item'
? 'Auf Liste' ? 'Auf Liste'
@@ -376,162 +388,171 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
: 'absolute top-full left-0 right-0 mt-1.5 z-50 p-3 panel-surface shadow-panel space-y-3 text-xs' : 'absolute top-full left-0 right-0 mt-1.5 z-50 p-3 panel-surface shadow-panel space-y-3 text-xs'
} }
> >
{/* Row 1: Where should this entry go? */} {/* Row 1: Where should this entry go? (Omitted when hideTargetPicker is true) */}
<div> {!hideTargetPicker && (
<div className="flex items-center justify-between mb-1.5"> <>
<span className="text-[11px] font-semibold uppercase tracking-wider text-muted"> <div>
Wohin damit? {content.trim() ? '(Klick speichert sofort oder wählt Ziel)' : '(Ziel für Eingabe wählen)'} <div className="flex items-center justify-between mb-1.5">
</span> <span className="text-[11px] font-semibold uppercase tracking-wider text-muted">
<span className="text-[11px] text-faint"> Wohin damit? {content.trim() ? '(Klick speichert sofort oder wählt Ziel)' : '(Ziel für Eingabe wählen)'}
Standard: Quick Dump für späteres Swipen </span>
</span> <span className="text-[11px] text-faint">
</div> Standard: Quick Dump für späteres Swipen
</span>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-1.5"> <div className="grid grid-cols-2 sm:grid-cols-4 gap-1.5">
{/* Option 1: Quick Dump (Triage) */} {/* Option 1: Quick Dump (Triage) */}
<button
type="button"
onClick={() => {
setTarget('inbox');
if (content.trim()) {
executeCapture('inbox');
}
}}
className={`min-h-[36px] px-2.5 border rounded-btn flex items-center gap-1.5 text-left transition-colors cursor-pointer ${
target === 'inbox'
? 'bg-accent-dim border-accent text-main font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<Layers className="w-3.5 h-3.5 text-accent flex-shrink-0" />
<span className="truncate">Quick Dump (Triage)</span>
</button>
{/* Option 2: Direct Task */}
<button
type="button"
onClick={() => {
setTarget('task');
if (content.trim()) {
executeCapture('task');
}
}}
className={`min-h-[36px] px-2.5 border rounded-btn flex items-center gap-1.5 text-left transition-colors cursor-pointer ${
target === 'task'
? 'bg-accent-dim border-accent text-main font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<CheckSquare className="w-3.5 h-3.5 text-accent flex-shrink-0" />
<span className="truncate">Direkt als Aufgabe</span>
</button>
{/* Option 3: Direct List Item */}
<button
type="button"
onClick={() => {
setTarget('list_item');
if (!targetListId && lists.length > 0) {
setTargetListId(lists[0].id);
}
}}
className={`min-h-[36px] px-2.5 border rounded-btn flex items-center gap-1.5 text-left transition-colors cursor-pointer ${
target === 'list_item'
? 'bg-accent-dim border-accent text-main font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<ShoppingCart className="w-3.5 h-3.5 text-accent flex-shrink-0" />
<span className="truncate">Direkt auf Liste…</span>
</button>
{/* Option 4: Direct Workspace Note */}
<button
type="button"
onClick={() => {
setTarget('document');
if (content.trim()) {
executeCapture('document');
}
}}
className={`min-h-[36px] px-2.5 border rounded-btn flex items-center gap-1.5 text-left transition-colors cursor-pointer ${
target === 'document'
? 'bg-accent-dim border-accent text-main font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<FileText className="w-3.5 h-3.5 text-accent flex-shrink-0" />
<span className="truncate">Direkt als Notiz</span>
</button>
</div>
</div>
{/* Optional Sub-Row: Shared List Picker */}
{lists.length > 0 && (
<div className="pt-2 border-t border-border flex flex-wrap items-center gap-1.5">
<span className="text-muted mr-1 flex items-center gap-1">
<ShoppingCart className="w-3 h-3 text-accent" />
Liste:
</span>
{lists.map((lst) => {
const isSelected = target === 'list_item' && targetListId === lst.id;
return (
<button <button
key={lst.id}
type="button" type="button"
onClick={() => { onClick={() => {
setTarget('list_item'); setTarget('inbox');
setTargetListId(lst.id);
if (content.trim()) { if (content.trim()) {
executeCapture('list_item', lst.id); executeCapture('inbox');
} }
}} }}
className={`min-h-[30px] px-2.5 border rounded-btn text-xs flex items-center gap-1.5 transition-colors cursor-pointer ${ className={`min-h-[36px] px-2.5 border rounded-btn flex items-center gap-1.5 text-left transition-colors cursor-pointer ${
isSelected target === 'inbox'
? 'bg-accent text-on-accent border-accent font-medium' ? 'bg-accent-dim border-accent text-main font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover' : 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`} }`}
> >
<span>{lst.icon}</span> <Layers className="w-3.5 h-3.5 text-accent flex-shrink-0" />
<span>{lst.title}</span> <span className="truncate">Quick Dump (Triage)</span>
</button> </button>
);
})} {/* Option 2: Direct Task */}
</div> <button
type="button"
onClick={() => {
setTarget('task');
if (content.trim()) {
executeCapture('task');
}
}}
className={`min-h-[36px] px-2.5 border rounded-btn flex items-center gap-1.5 text-left transition-colors cursor-pointer ${
target === 'task'
? 'bg-accent-dim border-accent text-main font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<CheckSquare className="w-3.5 h-3.5 text-accent flex-shrink-0" />
<span className="truncate">Direkt als Aufgabe</span>
</button>
{/* Option 3: Direct List Item */}
<button
type="button"
onClick={() => {
setTarget('list_item');
if (!targetListId && lists.length > 0) {
setTargetListId(lists[0].id);
}
}}
className={`min-h-[36px] px-2.5 border rounded-btn flex items-center gap-1.5 text-left transition-colors cursor-pointer ${
target === 'list_item'
? 'bg-accent-dim border-accent text-main font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<ShoppingCart className="w-3.5 h-3.5 text-accent flex-shrink-0" />
<span className="truncate">Direkt auf Liste…</span>
</button>
{/* Option 4: Direct Workspace Note */}
<button
type="button"
onClick={() => {
setTarget('document');
if (content.trim()) {
executeCapture('document');
}
}}
className={`min-h-[36px] px-2.5 border rounded-btn flex items-center gap-1.5 text-left transition-colors cursor-pointer ${
target === 'document'
? 'bg-accent-dim border-accent text-main font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<FileText className="w-3.5 h-3.5 text-accent flex-shrink-0" />
<span className="truncate">Direkt als Notiz</span>
</button>
</div>
</div>
{/* Optional Sub-Row: Shared List Picker */}
{lists.length > 0 && (
<div className="pt-2 border-t border-border flex flex-wrap items-center gap-1.5">
<span className="text-muted mr-1 flex items-center gap-1">
<ShoppingCart className="w-3 h-3 text-accent" />
Liste:
</span>
{lists.map((lst) => {
const isSelected = target === 'list_item' && targetListId === lst.id;
return (
<button
key={lst.id}
type="button"
onClick={() => {
setTarget('list_item');
setTargetListId(lst.id);
if (content.trim()) {
executeCapture('list_item', lst.id);
}
}}
className={`min-h-[30px] px-2.5 border rounded-btn text-xs flex items-center gap-1.5 transition-colors cursor-pointer ${
isSelected
? 'bg-accent text-on-accent border-accent font-medium'
: 'bg-canvas border-border text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<span>{lst.icon}</span>
<span>{lst.title}</span>
</button>
);
})}
</div>
)}
</>
)} )}
{/* Row 2: Priority & Due Date */} {/* Row 2: Priority & Due Date */}
<div className="pt-2 border-t border-border flex flex-wrap items-center justify-between gap-2"> <div className={`${!hideTargetPicker ? 'pt-2 border-t border-border' : ''} flex flex-wrap items-center justify-between gap-2.5`}>
<div className="flex items-center gap-1.5"> <div className="flex flex-wrap items-center gap-1.5">
<span className="text-muted mr-1">Priorität:</span> <span className="text-muted text-[11px] font-semibold mr-1">Priorität:</span>
{(['low', 'normal', 'high', 'urgent'] as PriorityLevel[]).map((p) => { {(['low', 'normal', 'high', 'urgent'] as PriorityLevel[]).map((p) => {
const labels: Record<PriorityLevel, string> = { const config: Record<PriorityLevel, { label: string; icon?: React.ReactNode; activeClass: string }> = {
low: 'Niedrig', low: { label: 'Niedrig', activeClass: 'bg-canvas border-strong text-main font-semibold shadow-xs' },
normal: 'Normal', normal: { label: 'Normal', activeClass: 'bg-accent/15 border-accent text-accent font-semibold shadow-xs' },
high: 'Hoch', high: { label: 'Wichtig', icon: <AlertTriangle className="w-3 h-3 text-status-warn" />, activeClass: 'bg-status-warn/15 border-status-warn text-status-warn font-semibold shadow-xs' },
urgent: 'Dringend', urgent: { label: 'Dringend', icon: <Flame className="w-3 h-3 text-status-err" />, activeClass: 'bg-status-err/15 border-status-err text-status-err font-semibold shadow-xs' },
}; };
const active = priority === p; const active = priority === p;
const item = config[p];
return ( return (
<button <button
key={p} key={p}
type="button" type="button"
onClick={() => setPriority(p)} onClick={() => {
className={`min-h-[30px] px-2.5 py-1 border rounded-btn transition-colors text-xs cursor-pointer ${ triggerHaptic(10);
setPriority(p);
}}
className={`min-h-[30px] px-2.5 py-1 border rounded-btn text-xs flex items-center gap-1 transition-colors cursor-pointer ${
active active
? 'bg-accent text-on-accent border-accent font-medium' ? item.activeClass
: 'bg-canvas text-muted hover:text-main border-border' : 'bg-canvas text-muted hover:text-main border-border'
}`} }`}
> >
{labels[p]} {item.icon}
<span>{item.label}</span>
</button> </button>
); );
})} })}
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-1.5">
<span className="text-muted flex items-center gap-1"> <span className="text-muted text-[11px] font-semibold flex items-center gap-1">
<Calendar className="w-3 h-3 text-accent" /> <Calendar className="w-3.5 h-3.5 text-accent" />
Fällig am: Fällig am:
</span> </span>
<input <input
@@ -335,29 +335,49 @@ export const TaskDetailModal: React.FC<TaskDetailModalProps> = ({
</div> </div>
</div> </div>
{/* Priority Buttons */} {/* Priority Buttons (4 Buttons) */}
<div className="space-y-1.5 pt-1 border-t border-border"> <div className="space-y-1.5 pt-1 border-t border-border">
<label className="block text-xs font-semibold text-main"> <label className="block text-xs font-semibold text-main">
Priorität: Priorität:
</label> </label>
<div className="flex flex-wrap items-center gap-1.5"> <div className="grid grid-cols-2 sm:grid-cols-4 gap-1.5">
<button <button
type="button" type="button"
onClick={() => onUpdatePriority(task, 'normal')} onClick={() => {
className={`h-7 px-3 text-xs rounded-btn font-medium transition-colors border ${ triggerHaptic(10);
!task.priority || task.priority === 'normal' || task.priority === 'low' onUpdatePriority(task, 'low');
? 'bg-panel border-accent text-main font-semibold' }}
className={`h-8 px-2 text-xs rounded-btn font-medium transition-colors border flex items-center justify-center gap-1 cursor-pointer ${
task.priority === 'low'
? 'bg-canvas border-strong text-main font-semibold shadow-xs'
: 'bg-canvas border-border text-muted hover:text-main' : 'bg-canvas border-border text-muted hover:text-main'
}`} }`}
> >
Normal / Keine <span>Niedrig</span>
</button> </button>
<button <button
type="button" type="button"
onClick={() => onUpdatePriority(task, 'high')} onClick={() => {
className={`h-7 px-3 text-xs rounded-btn font-medium transition-colors border flex items-center gap-1 ${ triggerHaptic(10);
onUpdatePriority(task, 'normal');
}}
className={`h-8 px-2 text-xs rounded-btn font-medium transition-colors border flex items-center justify-center gap-1 cursor-pointer ${
!task.priority || task.priority === 'normal'
? 'bg-accent/15 border-accent text-accent font-semibold shadow-xs'
: 'bg-canvas border-border text-muted hover:text-main'
}`}
>
<span>Normal</span>
</button>
<button
type="button"
onClick={() => {
triggerHaptic(10);
onUpdatePriority(task, 'high');
}}
className={`h-8 px-2 text-xs rounded-btn font-medium transition-colors border flex items-center justify-center gap-1 cursor-pointer ${
task.priority === 'high' task.priority === 'high'
? 'bg-status-warn/15 border-status-warn text-status-warn font-semibold' ? 'bg-status-warn/15 border-status-warn text-status-warn font-semibold shadow-xs'
: 'bg-canvas border-border text-muted hover:text-main' : 'bg-canvas border-border text-muted hover:text-main'
}`} }`}
> >
@@ -366,10 +386,13 @@ export const TaskDetailModal: React.FC<TaskDetailModalProps> = ({
</button> </button>
<button <button
type="button" type="button"
onClick={() => onUpdatePriority(task, 'urgent')} onClick={() => {
className={`h-7 px-3 text-xs rounded-btn font-medium transition-colors border flex items-center gap-1 ${ triggerHaptic(10);
onUpdatePriority(task, 'urgent');
}}
className={`h-8 px-2 text-xs rounded-btn font-medium transition-colors border flex items-center justify-center gap-1 cursor-pointer ${
task.priority === 'urgent' task.priority === 'urgent'
? 'bg-status-err/15 border-status-err text-status-err font-semibold' ? 'bg-status-err/15 border-status-err text-status-err font-semibold shadow-xs'
: 'bg-canvas border-border text-muted hover:text-main' : 'bg-canvas border-border text-muted hover:text-main'
}`} }`}
> >
+45 -23
View File
@@ -1660,31 +1660,53 @@ export const TasksView: React.FC<TasksViewProps> = ({
</div> </div>
)} )}
{/* Priority & Due Date */} {/* Priority - 4 Buttons */}
<div className="grid grid-cols-2 gap-2 pt-1"> <div className="space-y-1.5 pt-1">
<div> <label className="text-[11px] font-semibold text-muted block">Priorität</label>
<label className="text-[11px] font-semibold text-muted block mb-1">Priorität</label> <div className="grid grid-cols-4 gap-1.5">
<select {(['low', 'normal', 'high', 'urgent'] as PriorityLevel[]).map((p) => {
value={mobileCreatePriority} const config: Record<PriorityLevel, { label: string; icon?: React.ReactNode; activeClass: string }> = {
onChange={(e) => setMobileCreatePriority(e.target.value as PriorityLevel)} low: { label: 'Niedrig', activeClass: 'bg-canvas border-strong text-main font-semibold shadow-xs' },
className="w-full input-field px-2 py-2 text-xs bg-panel" normal: { label: 'Normal', activeClass: 'bg-accent/15 border-accent text-accent font-semibold shadow-xs' },
> high: { label: 'Wichtig', icon: <AlertTriangle className="w-3 h-3 text-status-warn" />, activeClass: 'bg-status-warn/15 border-status-warn text-status-warn font-semibold shadow-xs' },
<option value="normal">Normal</option> urgent: { label: 'Dringend', icon: <Flame className="w-3 h-3 text-status-err" />, activeClass: 'bg-status-err/15 border-status-err text-status-err font-semibold shadow-xs' },
<option value="high">⚠️ Wichtig</option> };
<option value="urgent">🔥 Dringend</option> const active = mobileCreatePriority === p;
<option value="low">Niedrig</option> const item = config[p];
</select> return (
<button
key={p}
type="button"
onClick={() => {
triggerHaptic(10);
setMobileCreatePriority(p);
}}
className={`h-9 px-1 text-xs rounded-btn font-medium transition-colors border flex items-center justify-center gap-1 cursor-pointer ${
active
? item.activeClass
: 'bg-canvas border-border text-muted hover:text-main'
}`}
>
{item.icon}
<span className="truncate">{item.label}</span>
</button>
);
})}
</div> </div>
</div>
<div> {/* Due Date */}
<label className="text-[11px] font-semibold text-muted block mb-1">Fällig am</label> <div className="space-y-1 pt-1">
<input <label className="text-[11px] font-semibold text-muted flex items-center gap-1">
type="date" <Calendar className="w-3.5 h-3.5 text-accent" />
value={mobileCreateDueDate} <span>Fällig am</span>
onChange={(e) => setMobileCreateDueDate(e.target.value)} </label>
className="w-full input-field px-2 py-2 text-xs" <input
/> type="date"
</div> value={mobileCreateDueDate}
onChange={(e) => setMobileCreateDueDate(e.target.value)}
className="w-full input-field px-3 py-2 text-xs"
/>
</div> </div>
<div className="pt-2 flex justify-end gap-2"> <div className="pt-2 flex justify-end gap-2">