feat: add list selector to task detail screen
Allow viewing and changing a task's list from the task detail page. Uses the same chip-style selector with list icons and colors as the new task screen. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d31b6f45fc
commit
c58a4dce2d
1 changed files with 59 additions and 1 deletions
|
|
@ -10,7 +10,13 @@ import {
|
|||
Platform,
|
||||
} from 'react-native';
|
||||
import { useRouter, useLocalSearchParams } from 'expo-router';
|
||||
import { ArrowLeft, Plus, Trash2, Calendar, X, Repeat, Download } from 'lucide-react-native';
|
||||
import {
|
||||
ArrowLeft, Plus, Trash2, Calendar, X, Repeat, Download,
|
||||
List, ShoppingCart, Briefcase, Home, Heart, Star, BookOpen,
|
||||
GraduationCap, Dumbbell, Utensils, Plane, Music, Code, Wrench,
|
||||
Gift, Camera, Palette, Dog, Leaf, Zap,
|
||||
} from 'lucide-react-native';
|
||||
import type { LucideIcon } from 'lucide-react-native';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import DateTimePicker, { DateTimePickerEvent } from '@react-native-community/datetimepicker';
|
||||
|
|
@ -29,6 +35,13 @@ import {
|
|||
toggleComplete,
|
||||
} from '@/src/db/repository/tasks';
|
||||
import { getAllTags, getTagsForTask, setTagsForTask } from '@/src/db/repository/tags';
|
||||
import { getAllLists } from '@/src/db/repository/lists';
|
||||
|
||||
const ICON_MAP: Record<string, LucideIcon> = {
|
||||
List, ShoppingCart, Briefcase, Home, Heart, Star, BookOpen,
|
||||
GraduationCap, Dumbbell, Utensils, Plane, Music, Code, Wrench,
|
||||
Gift, Camera, Palette, Dog, Leaf, Zap,
|
||||
};
|
||||
import TagChip from '@/src/components/task/TagChip';
|
||||
import { exportAndShareICS } from '@/src/services/icsExport';
|
||||
|
||||
|
|
@ -64,6 +77,8 @@ export default function TaskDetailScreen() {
|
|||
const [newSubtask, setNewSubtask] = useState('');
|
||||
const [availableTags, setAvailableTags] = useState<{ id: string; name: string; color: string }[]>([]);
|
||||
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
|
||||
const [lists, setLists] = useState<{ id: string; name: string; color: string | null; icon: string | null; isInbox: boolean }[]>([]);
|
||||
const [selectedListId, setSelectedListId] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!isValidUUID(id)) {
|
||||
|
|
@ -73,6 +88,7 @@ export default function TaskDetailScreen() {
|
|||
loadTask();
|
||||
loadSubtasks();
|
||||
getAllTags().then(setAvailableTags);
|
||||
getAllLists().then(setLists);
|
||||
}, [id]);
|
||||
|
||||
const loadTask = async () => {
|
||||
|
|
@ -84,6 +100,7 @@ export default function TaskDetailScreen() {
|
|||
setPriority(result.priority);
|
||||
setDueDate(result.dueDate ? new Date(result.dueDate) : null);
|
||||
setRecurrence(result.recurrence ?? null);
|
||||
setSelectedListId(result.listId);
|
||||
|
||||
const taskTags = await getTagsForTask(id!);
|
||||
setSelectedTagIds(taskTags.map((t) => t.id));
|
||||
|
|
@ -102,6 +119,7 @@ export default function TaskDetailScreen() {
|
|||
priority,
|
||||
dueDate,
|
||||
recurrence,
|
||||
listId: selectedListId,
|
||||
});
|
||||
await setTagsForTask(task.id, selectedTagIds);
|
||||
router.back();
|
||||
|
|
@ -298,6 +316,46 @@ export default function TaskDetailScreen() {
|
|||
</>
|
||||
)}
|
||||
|
||||
{/* List selector */}
|
||||
{lists.length > 1 && (
|
||||
<>
|
||||
<Text className={`mb-2 mt-6 text-xs uppercase tracking-wide ${isDark ? 'text-[#A0A0A0]' : 'text-[#6B6B6B]'}`} style={{ fontFamily: 'Inter_600SemiBold' }}>
|
||||
{t('nav.lists')}
|
||||
</Text>
|
||||
<View className="flex-row flex-wrap">
|
||||
{lists.map((list) => {
|
||||
const isSelected = selectedListId === list.id;
|
||||
const chipColor = isSelected ? colors.bleu.DEFAULT : isDark ? '#A0A0A0' : '#6B6B6B';
|
||||
const IconComp = list.icon && ICON_MAP[list.icon] ? ICON_MAP[list.icon] : null;
|
||||
return (
|
||||
<Pressable
|
||||
key={list.id}
|
||||
onPress={() => setSelectedListId(list.id)}
|
||||
className={`mb-2 mr-2 flex-row items-center rounded-full border px-3 py-1.5 ${
|
||||
isSelected ? 'border-bleu bg-bleu/10' : isDark ? 'border-[#3A3A3A]' : 'border-[#E5E7EB]'
|
||||
}`}
|
||||
>
|
||||
{IconComp ? (
|
||||
<IconComp size={14} color={isSelected ? colors.bleu.DEFAULT : list.color || chipColor} />
|
||||
) : list.color ? (
|
||||
<View className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: isSelected ? colors.bleu.DEFAULT : list.color }} />
|
||||
) : null}
|
||||
<Text
|
||||
className={`text-sm ${IconComp || list.color ? 'ml-1.5' : ''}`}
|
||||
style={{
|
||||
fontFamily: isSelected ? 'Inter_600SemiBold' : 'Inter_400Regular',
|
||||
color: chipColor,
|
||||
}}
|
||||
>
|
||||
{list.isInbox ? t('list.inbox') : list.name}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Subtasks */}
|
||||
<Text className={`mb-2 mt-6 text-xs uppercase tracking-wide ${isDark ? 'text-[#A0A0A0]' : 'text-[#6B6B6B]'}`} style={{ fontFamily: 'Inter_600SemiBold' }}>
|
||||
{t('task.subtasks')}
|
||||
|
|
|
|||
Loading…
Reference in a new issue