feat(expo): add TaskRow component
This commit is contained in:
parent
c7b2b8c2d0
commit
f30df0901d
1 changed files with 120 additions and 0 deletions
120
xtablo-expo/components/tasks/TaskRow.tsx
Normal file
120
xtablo-expo/components/tasks/TaskRow.tsx
Normal 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",
|
||||
},
|
||||
});
|
||||
Loading…
Reference in a new issue