fix(expo): address review findings - cache invalidation, date handling, cleanup
- Add tasks cache invalidation to etape create/update hooks - Fix timezone-unsafe overdue date comparison in TaskRow - Add due date picker to EtapeSheet (was missing from spec) - Use initialized ref to prevent form state overwrite on refetch - Remove unused deleteTask import from TaskList Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0ffcb871d8
commit
66d9ab9ad8
5 changed files with 69 additions and 9 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
|
|
@ -61,9 +61,11 @@ export default function TaskDetailScreen() {
|
|||
const { mutate: deleteTask, isPending: isDeleting } = useDeleteTask();
|
||||
const isPending = isCreating || isUpdating || isDeleting;
|
||||
|
||||
// Populate form for edit mode
|
||||
// Populate form for edit mode (only once when data first arrives)
|
||||
const initialized = useRef(false);
|
||||
useEffect(() => {
|
||||
if (existingTask) {
|
||||
if (existingTask && !initialized.current) {
|
||||
initialized.current = true;
|
||||
setTitle(existingTask.title);
|
||||
setDescription(existingTask.description ?? "");
|
||||
setStatus(existingTask.status);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import {
|
|||
Platform,
|
||||
StyleSheet,
|
||||
} from "react-native";
|
||||
import { X } from "lucide-react-native";
|
||||
import { X, Calendar } from "lucide-react-native";
|
||||
import DateTimePicker from "@react-native-community/datetimepicker";
|
||||
import { useThemeColor } from "@/hooks/useThemeColor";
|
||||
import { useColorScheme } from "@/hooks/useColorScheme";
|
||||
import { Etape } from "@/types/tasks.types";
|
||||
|
|
@ -29,9 +30,12 @@ export default function EtapeSheet({ visible, onClose, tabloId, etape }: EtapeSh
|
|||
const textColor = useThemeColor({ light: "#1f2937", dark: "#f9fafb" }, "text");
|
||||
const inputBg = useThemeColor({ light: "#f1f5f9", dark: "#374151" }, "background");
|
||||
const borderColor = isDark ? "#374151" : "#e5e7eb";
|
||||
const subtextColor = useThemeColor({ light: "#6b7280", dark: "#9ca3af" }, "text");
|
||||
|
||||
const [title, setTitle] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [dueDate, setDueDate] = useState<Date | null>(null);
|
||||
const [showDatePicker, setShowDatePicker] = useState(false);
|
||||
|
||||
const { mutate: createEtape, isPending: isCreating } = useCreateEtape();
|
||||
const { mutate: updateEtape, isPending: isUpdating } = useUpdateEtape();
|
||||
|
|
@ -43,15 +47,24 @@ export default function EtapeSheet({ visible, onClose, tabloId, etape }: EtapeSh
|
|||
if (etape) {
|
||||
setTitle(etape.title);
|
||||
setDescription(etape.description ?? "");
|
||||
setDueDate(etape.due_date ? new Date(etape.due_date) : null);
|
||||
} else {
|
||||
setTitle("");
|
||||
setDescription("");
|
||||
setDueDate(null);
|
||||
}
|
||||
setShowDatePicker(false);
|
||||
}, [etape, visible]);
|
||||
|
||||
const formatDueDate = (date: Date) => {
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
if (!title.trim()) return;
|
||||
|
||||
const dueDateStr = dueDate ? formatDueDate(dueDate) : null;
|
||||
|
||||
if (isEditMode && etape) {
|
||||
updateEtape(
|
||||
{
|
||||
|
|
@ -59,6 +72,7 @@ export default function EtapeSheet({ visible, onClose, tabloId, etape }: EtapeSh
|
|||
tabloId,
|
||||
title: title.trim(),
|
||||
description: description.trim() || null,
|
||||
due_date: dueDateStr,
|
||||
},
|
||||
{ onSuccess: onClose }
|
||||
);
|
||||
|
|
@ -68,12 +82,17 @@ export default function EtapeSheet({ visible, onClose, tabloId, etape }: EtapeSh
|
|||
tabloId,
|
||||
title: title.trim(),
|
||||
description: description.trim() || null,
|
||||
due_date: dueDateStr,
|
||||
},
|
||||
{ onSuccess: onClose }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const dueDateDisplay = dueDate
|
||||
? dueDate.toLocaleDateString("fr-FR", { day: "numeric", month: "long", year: "numeric" })
|
||||
: "Aucune";
|
||||
|
||||
return (
|
||||
<Modal visible={visible} animationType="slide" transparent onRequestClose={onClose}>
|
||||
<KeyboardAvoidingView
|
||||
|
|
@ -111,6 +130,34 @@ export default function EtapeSheet({ visible, onClose, tabloId, etape }: EtapeSh
|
|||
multiline
|
||||
numberOfLines={3}
|
||||
/>
|
||||
|
||||
<Text style={[styles.label, { color: textColor }]}>Date limite</Text>
|
||||
<TouchableOpacity
|
||||
style={[styles.pickerButton, { backgroundColor: inputBg }]}
|
||||
onPress={() => setShowDatePicker(true)}
|
||||
>
|
||||
<Calendar size={16} color={subtextColor} />
|
||||
<Text style={[styles.pickerText, { color: dueDate ? textColor : subtextColor }]}>
|
||||
{dueDateDisplay}
|
||||
</Text>
|
||||
{dueDate && (
|
||||
<TouchableOpacity onPress={() => setDueDate(null)}>
|
||||
<Text style={{ color: "#ef4444", fontSize: 13 }}>Retirer</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
{showDatePicker && (
|
||||
<DateTimePicker
|
||||
value={dueDate ?? new Date()}
|
||||
mode="date"
|
||||
display={Platform.OS === "ios" ? "spinner" : "default"}
|
||||
onChange={(event, date) => {
|
||||
setShowDatePicker(Platform.OS !== "ios");
|
||||
if (date) setDueDate(date);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
|
|
@ -168,6 +215,18 @@ const styles = StyleSheet.create({
|
|||
minHeight: 80,
|
||||
textAlignVertical: "top",
|
||||
},
|
||||
pickerButton: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 12,
|
||||
gap: 10,
|
||||
},
|
||||
pickerText: {
|
||||
flex: 1,
|
||||
fontSize: 15,
|
||||
},
|
||||
saveButton: {
|
||||
marginHorizontal: 16,
|
||||
backgroundColor: "#3b82f6",
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ import { Plus } from "lucide-react-native";
|
|||
import { useThemeColor } from "@/hooks/useThemeColor";
|
||||
import { useColorScheme } from "@/hooks/useColorScheme";
|
||||
import { useTasksByTablo } from "@/hooks/tasks";
|
||||
import { useTabloEtapes } from "@/hooks/etapes";
|
||||
import { useDeleteTask } from "@/hooks/tasks";
|
||||
import { useDeleteEtape } from "@/hooks/etapes";
|
||||
import { useTabloEtapes, useDeleteEtape } from "@/hooks/etapes";
|
||||
import { Task, Etape, TASK_STATUSES, TaskStatus } from "@/types/tasks.types";
|
||||
import EtapeSection from "./EtapeSection";
|
||||
import TaskRow from "./TaskRow";
|
||||
|
|
@ -58,7 +56,6 @@ export default function TaskList({ tabloId, onEditEtape, onCreateEtape }: TaskLi
|
|||
|
||||
const { data: tasks, isLoading: tasksLoading, refetch: refetchTasks } = useTasksByTablo(tabloId);
|
||||
const { data: etapes, isLoading: etapesLoading, refetch: refetchEtapes } = useTabloEtapes(tabloId);
|
||||
const { mutate: deleteTask } = useDeleteTask();
|
||||
const { mutate: deleteEtape } = useDeleteEtape();
|
||||
|
||||
const [collapsedSections, setCollapsedSections] = useState<Set<string>>(new Set());
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default function TaskRow({ task, onPress }: TaskRowProps) {
|
|||
const borderColor = isDark ? "#374151" : "#e5e7eb";
|
||||
|
||||
const statusColor = TASK_STATUSES.find((s) => s.value === task.status)?.color ?? "#9ca3af";
|
||||
const isOverdue = task.due_date && new Date(task.due_date) < new Date();
|
||||
const isOverdue = task.due_date && task.due_date < new Date().toISOString().slice(0, 10);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ export const useCreateEtape = () => {
|
|||
},
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tablo-etapes", variables.tabloId] });
|
||||
queryClient.invalidateQueries({ queryKey: ["tasks", "tablo", variables.tabloId] });
|
||||
},
|
||||
onError: () => {
|
||||
Alert.alert("Erreur", "Impossible de créer l'étape.");
|
||||
|
|
@ -83,6 +84,7 @@ export const useUpdateEtape = () => {
|
|||
},
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tablo-etapes", variables.tabloId] });
|
||||
queryClient.invalidateQueries({ queryKey: ["tasks", "tablo", variables.tabloId] });
|
||||
},
|
||||
onError: () => {
|
||||
Alert.alert("Erreur", "Impossible de modifier l'étape.");
|
||||
|
|
|
|||
Loading…
Reference in a new issue