diff --git a/frontend/src/lib/mobile/MobileItemDetail.svelte b/frontend/src/lib/mobile/MobileItemDetail.svelte index 59e80f23..3384bd0f 100644 --- a/frontend/src/lib/mobile/MobileItemDetail.svelte +++ b/frontend/src/lib/mobile/MobileItemDetail.svelte @@ -24,7 +24,8 @@ import BasePicker from '../pickers/BasePicker.svelte'; import UserPicker from '../pickers/UserPicker.svelte'; import Avatar from '../components/Avatar.svelte'; - import SafeMarkdown from '../components/SafeMarkdown.svelte'; + import Input from '../components/Input.svelte'; + import ItemDetailDescription from '../features/items/ItemDetailDescription.svelte'; let { itemId } = $props(); @@ -35,6 +36,11 @@ let transitioning = $state(false); let isWatching = $state(false); let watchBusy = $state(false); + let saving = $state(false); + let editingTitle = $state(false); + let editTitle = $state(''); + let editingDescription = $state(false); + let editDescription = $state(''); // Workflow-less personal tasks use the permitted transition endpoint to toggle // globally stable Open/Done IDs, matching desktop and PWA personal views. @@ -91,6 +97,8 @@ const summary = await loadMobileItemDetailSummary(id); if (token !== loadToken) return; item = summary?.item ?? null; + editTitle = item?.title || ''; + editDescription = item?.description || ''; transitions = summary?.transitions?.available_transitions ?? []; isWatching = summary?.watching || false; personalTaskCount = summary?.personal_task_count ?? 0; @@ -167,6 +175,59 @@ } } + async function saveTitle() { + const title = editTitle.trim(); + if (!title || title === item.title) { + editTitle = item.title || ''; + editingTitle = false; + return; + } + saving = true; + try { + const updated = await api.items.update(itemId, { title }); + item = { ...item, ...updated, title }; + editingTitle = false; + } catch (err) { + console.error('Failed to save title:', err); + } finally { + saving = false; + } + } + + function handleTitleKeydown(event) { + if (event.key === 'Enter') { + event.preventDefault(); + saveTitle(); + } else if (event.key === 'Escape') { + editTitle = item.title || ''; + editingTitle = false; + } + } + + function handleSaveField({ field, value }) { + if (field !== 'description') return; + editDescription = value; + saveDescription(); + } + + async function saveDescription() { + saving = true; + try { + const updated = await api.items.update(itemId, { description: editDescription }); + item = { ...item, ...updated, description: editDescription }; + editingDescription = false; + } catch (err) { + console.error('Failed to save description:', err); + } finally { + saving = false; + } + } + + function cancelDescription() { + editDescription = item.description || ''; + editingDescription = false; + } + async function updateAssignee(user) { const assigneeId = user?.id ?? null; if (assigneeId === (item.assignee_id ?? null)) return; @@ -411,7 +472,26 @@