feat(expo): add TaskRow component

This commit is contained in:
Arthur Belleville 2026-04-15 09:46:08 +02:00
parent c7b2b8c2d0
commit f30df0901d
No known key found for this signature in database

View file

@ -0,0 +1,120 @@
import React from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import { Task, TASK_STATUSES } from "@/types/tasks.types";
import { useThemeColor } from "@/hooks/useThemeColor";
import { useColorScheme } from "@/hooks/useColorScheme";
import { Calendar } from "lucide-react-native";
type TaskRowProps = {
task: Task;
onPress: (task: Task) => void;
};
export default function TaskRow({ task, onPress }: TaskRowProps) {
const colorScheme = useColorScheme();
const isDark = colorScheme === "dark";
const textColor = useThemeColor({ light: "#1f2937", dark: "#f9fafb" }, "text");
const subtextColor = useThemeColor({ light: "#6b7280", dark: "#9ca3af" }, "text");
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();
return (
<TouchableOpacity
style={[styles.container, { borderBottomColor: borderColor }]}
onPress={() => onPress(task)}
activeOpacity={0.7}
>
<View style={[styles.statusDot, { backgroundColor: statusColor }]} />
<View style={styles.content}>
<Text style={[styles.title, { color: textColor }]} numberOfLines={1}>
{task.title}
</Text>
<View style={styles.meta}>
{task.due_date && (
<View style={styles.dueDateBadge}>
<Calendar size={12} color={isOverdue ? "#ef4444" : subtextColor} />
<Text
style={[
styles.dueDate,
{ color: isOverdue ? "#ef4444" : subtextColor },
]}
>
{new Date(task.due_date).toLocaleDateString("fr-FR", {
day: "numeric",
month: "short",
})}
</Text>
</View>
)}
</View>
</View>
{task.assignee_name ? (
<View style={styles.avatar}>
<Text style={styles.avatarText}>
{task.assignee_name.charAt(0).toUpperCase()}
</Text>
</View>
) : (
<View style={[styles.avatar, styles.avatarEmpty]} />
)}
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 16,
borderBottomWidth: StyleSheet.hairlineWidth,
gap: 12,
},
statusDot: {
width: 10,
height: 10,
borderRadius: 5,
},
content: {
flex: 1,
gap: 2,
},
title: {
fontSize: 15,
fontWeight: "500",
},
meta: {
flexDirection: "row",
alignItems: "center",
gap: 8,
},
dueDateBadge: {
flexDirection: "row",
alignItems: "center",
gap: 4,
},
dueDate: {
fontSize: 12,
},
avatar: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: "#3b82f6",
alignItems: "center",
justifyContent: "center",
},
avatarEmpty: {
backgroundColor: "transparent",
borderWidth: 1.5,
borderColor: "#d1d5db",
borderStyle: "dashed",
},
avatarText: {
color: "#ffffff",
fontSize: 13,
fontWeight: "600",
},
});