diff options
| author | soryu <soryu@soryu.co> | 2026-01-18 02:58:27 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-18 02:58:27 +0000 |
| commit | fcf9e70d54bd737d2dea848d25314120f37db503 (patch) | |
| tree | bc304a9e153485f7686830614b2ddae4d4ff182e /apps/mobile/app/task | |
| parent | f84a7f2d820f6f432be2b1d78d6bf833b5b19380 (diff) | |
| download | soryu-fcf9e70d54bd737d2dea848d25314120f37db503.tar.gz soryu-fcf9e70d54bd737d2dea848d25314120f37db503.zip | |
[WIP] Heartbeat checkpoint - 2026-01-18 02:58:27 UTC
Diffstat (limited to 'apps/mobile/app/task')
| -rw-r--r-- | apps/mobile/app/task/[id].tsx | 259 | ||||
| -rw-r--r-- | apps/mobile/app/task/_layout.tsx | 5 |
2 files changed, 264 insertions, 0 deletions
diff --git a/apps/mobile/app/task/[id].tsx b/apps/mobile/app/task/[id].tsx new file mode 100644 index 0000000..121063a --- /dev/null +++ b/apps/mobile/app/task/[id].tsx @@ -0,0 +1,259 @@ +import React from 'react'; +import { + View, + Text, + StyleSheet, + ScrollView, + useColorScheme, + ActivityIndicator, +} from 'react-native'; +import { useLocalSearchParams, Stack } from 'expo-router'; +import { Colors } from '../../constants/Colors'; +import { useTask } from '../../hooks/useTasks'; +import { TaskStatusBadge } from '../../components/TaskStatusBadge'; +import { EmptyState } from '../../components/EmptyState'; + +export default function TaskDetailScreen() { + const colorScheme = useColorScheme() ?? 'light'; + const colors = Colors[colorScheme]; + const { id } = useLocalSearchParams<{ id: string }>(); + + const { data: task, isLoading, isError } = useTask(id); + + if (isLoading) { + return ( + <> + <Stack.Screen options={{ title: 'Loading...' }} /> + <View style={[styles.container, styles.centered, { backgroundColor: colors.background }]}> + <ActivityIndicator size="large" color={colors.tint} /> + </View> + </> + ); + } + + if (isError || !task) { + return ( + <> + <Stack.Screen options={{ title: 'Error' }} /> + <View style={[styles.container, { backgroundColor: colors.background }]}> + <EmptyState + icon="alert-circle-outline" + title="Failed to load task" + message="The task could not be found or an error occurred" + /> + </View> + </> + ); + } + + return ( + <> + <Stack.Screen options={{ title: task.name }} /> + <ScrollView + style={[styles.container, { backgroundColor: colors.background }]} + contentContainerStyle={styles.content} + > + {/* Header */} + <View style={[styles.header, { backgroundColor: colors.card }]}> + <View style={styles.headerTop}> + <TaskStatusBadge status={task.status} showLabel size="large" /> + </View> + <Text style={[styles.taskName, { color: colors.text }]}> + {task.name} + </Text> + {task.description && ( + <Text style={[styles.description, { color: colors.secondaryText }]}> + {task.description} + </Text> + )} + </View> + + {/* Progress Summary */} + {task.progressSummary && ( + <View style={[styles.section, { backgroundColor: colors.card }]}> + <Text style={[styles.sectionTitle, { color: colors.text }]}> + Progress + </Text> + <Text style={[styles.progressText, { color: colors.secondaryText }]}> + {task.progressSummary} + </Text> + </View> + )} + + {/* Task Info */} + <View style={[styles.section, { backgroundColor: colors.card }]}> + <Text style={[styles.sectionTitle, { color: colors.text }]}> + Details + </Text> + + <View style={styles.infoRow}> + <Text style={[styles.infoLabel, { color: colors.secondaryText }]}> + Created + </Text> + <Text style={[styles.infoValue, { color: colors.text }]}> + {new Date(task.createdAt).toLocaleString()} + </Text> + </View> + + {task.startedAt && ( + <View style={styles.infoRow}> + <Text style={[styles.infoLabel, { color: colors.secondaryText }]}> + Started + </Text> + <Text style={[styles.infoValue, { color: colors.text }]}> + {new Date(task.startedAt).toLocaleString()} + </Text> + </View> + )} + + {task.completedAt && ( + <View style={styles.infoRow}> + <Text style={[styles.infoLabel, { color: colors.secondaryText }]}> + Completed + </Text> + <Text style={[styles.infoValue, { color: colors.text }]}> + {new Date(task.completedAt).toLocaleString()} + </Text> + </View> + )} + + {task.repositoryUrl && ( + <View style={styles.infoRow}> + <Text style={[styles.infoLabel, { color: colors.secondaryText }]}> + Repository + </Text> + <Text + style={[styles.infoValue, { color: colors.tint }]} + numberOfLines={1} + > + {task.repositoryUrl} + </Text> + </View> + )} + </View> + + {/* Subtasks */} + {task.subtasks && task.subtasks.length > 0 && ( + <View style={[styles.section, { backgroundColor: colors.card }]}> + <Text style={[styles.sectionTitle, { color: colors.text }]}> + Subtasks ({task.subtasks.length}) + </Text> + {task.subtasks.map((subtask) => ( + <View key={subtask.id} style={styles.subtaskRow}> + <TaskStatusBadge status={subtask.status} size="small" /> + <Text + style={[styles.subtaskName, { color: colors.text }]} + numberOfLines={1} + > + {subtask.name} + </Text> + </View> + ))} + </View> + )} + + {/* Error message */} + {task.errorMessage && ( + <View style={[styles.section, styles.errorSection]}> + <Text style={[styles.sectionTitle, { color: '#991b1b' }]}> + Error + </Text> + <Text style={styles.errorText}>{task.errorMessage}</Text> + </View> + )} + + {/* Placeholder for future features */} + <View style={styles.placeholder}> + <Text style={[styles.placeholderText, { color: colors.secondaryText }]}> + Task output and controls will be added here + </Text> + </View> + </ScrollView> + </> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + centered: { + justifyContent: 'center', + alignItems: 'center', + }, + content: { + padding: 16, + gap: 16, + }, + header: { + padding: 16, + borderRadius: 12, + gap: 8, + }, + headerTop: { + flexDirection: 'row', + justifyContent: 'flex-start', + }, + taskName: { + fontSize: 20, + fontWeight: '700', + }, + description: { + fontSize: 14, + lineHeight: 20, + }, + section: { + padding: 16, + borderRadius: 12, + gap: 12, + }, + sectionTitle: { + fontSize: 16, + fontWeight: '600', + }, + progressText: { + fontSize: 14, + lineHeight: 20, + }, + infoRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + infoLabel: { + fontSize: 14, + }, + infoValue: { + fontSize: 14, + fontWeight: '500', + flex: 1, + textAlign: 'right', + marginLeft: 16, + }, + subtaskRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + paddingVertical: 4, + }, + subtaskName: { + fontSize: 14, + flex: 1, + }, + errorSection: { + backgroundColor: '#fee2e2', + }, + errorText: { + fontSize: 14, + color: '#991b1b', + lineHeight: 20, + }, + placeholder: { + padding: 32, + alignItems: 'center', + }, + placeholderText: { + fontSize: 14, + textAlign: 'center', + }, +}); diff --git a/apps/mobile/app/task/_layout.tsx b/apps/mobile/app/task/_layout.tsx new file mode 100644 index 0000000..7e40c63 --- /dev/null +++ b/apps/mobile/app/task/_layout.tsx @@ -0,0 +1,5 @@ +import { Stack } from 'expo-router'; + +export default function TaskLayout() { + return <Stack />; +} |
