2025-07-22 14:04:38 +02:00

207 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { toast } from '@backpackapp-io/react-native-toast';
import Ionicons from '@expo/vector-icons/Ionicons';
import { Stack, useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router';
import React, { useState } from 'react';
import { Pressable, ScrollView, StyleSheet, TextInput, View } from 'react-native';
import Dropdown from 'react-native-input-select';
import { ThemedText } from '../../components/ThemedText';
import { ThemedView } from '../../components/ThemedView';
import { Category, CategoryQuery } from '../../models/category';
import { Task, TaskQuery } from '../../models/task';
import { CategoryRepository } from '../../repositories/CategoryRepository';
import { TaskRepository } from '../../repositories/TaskRepository';
import { SQLiteDataService } from '../../services/data/sqliteDataService';
const taskRepository = new TaskRepository(new SQLiteDataService<Task>(TaskQuery));
const categoryRepository = new CategoryRepository(new SQLiteDataService<Category>(CategoryQuery));
export default function TasksForm() {
const { task: taskJson } = useLocalSearchParams();
const task: Task = taskJson ? JSON.parse(taskJson as string) : null;
const [categories, setCategories] = useState<Category[]>([]);
const router = useRouter();
const [newTask, setNewTask] = useState({
title: task?.title || '',
daysToRedo: task?.daysToRedo || NaN,
category: task?.category || -1,
});
useFocusEffect(React.useCallback(() => {
categoryRepository.findAll().then((categories) => {
categories.push(new Category("_create new_", "", 0));
setCategories(categories);
if (newTask.category === 0) {
setNewTask({ ...newTask, category: -1 });
}
}).catch((error) => {
console.error("Error fetching categories:", error);
});
}, []));
return (
<ThemedView style={styles.mainContainer} >
<Stack.Screen options={{ header: () => null }} />
<ThemedView style={styles.headerContainer}>
<ThemedText type="title" style={{
color: '#fff',
}}> Taskeep! </ThemedText>
< Pressable onPress={() => {
router.back();
}
}>
<Ionicons name='close-circle-outline' size={32} color="#fff" />
</Pressable>
</ThemedView>
< ScrollView style={styles.scrollView} >
<ThemedText type="subtitle">Add a new task</ThemedText>
<ThemedView style={styles.stepContainer}>
<TextInput
placeholder="Task title *"
style={styles.inputText}
placeholderTextColor="#333"
value={newTask.title}
onChange={(e) => setNewTask({ ...newTask, title: e.nativeEvent.text })}
/>
<TextInput
placeholder="Days after which task is due *"
style={styles.inputText}
placeholderTextColor="#333"
keyboardType='numeric'
value={isNaN(newTask.daysToRedo ?? NaN) ? undefined : newTask.daysToRedo?.toString()}
onChange={(e) => setNewTask({ ...newTask, daysToRedo: e.nativeEvent.text.length > 0 ? parseInt(e.nativeEvent.text) : NaN })}
/>
<Dropdown
placeholder="Select category *"
options={
categories.map(category => ({
label: `${category.icon} ${category.title}`,
value: category.id
}))
}
onValueChange={(value) => {
if (value === 0) {
router.push({ pathname: "./categoryForm" });
}
setNewTask({ ...newTask, category: (value ?? -1) as number }
)
}}
selectedValue={newTask.category > -1 ? newTask.category : undefined}
maxSelectableItems={1}
checkboxControls={{
checkboxLabelStyle: { fontSize: 20, paddingVertical: 10 },
checkboxStyle: {
backgroundColor: '#e744a9ff',
borderColor: 'transparent',
}
}}
dropdownIconStyle={{
alignSelf: 'flex-start',
margin: 0,
padding: 0,
marginTop: -30
}}
/>
</ThemedView>
<Pressable style={styles.addButton} onPress={() => {
if (newTask.title.length > 0 && newTask.daysToRedo > 0 && newTask.category > -1) {
if (task && task.id) {
// Update existing task
taskRepository.update(task.id, {
...task,
...newTask
}).then(() => {
toast.success("Task updated successfully!");
router.back();
});
} else {
// Create new task
taskRepository.create(new Task(newTask.title, newTask.daysToRedo, newTask.category)).then(() => {
toast.success("Task added successfully!");
router.back();
});
}
} else {
toast.error("Please fill in all fields correctly.");
}
}}>
<Ionicons name='checkmark-circle-outline' size={24} color="#fff" />
<ThemedText style={styles.addButtonText}>{task?.id ? "Update" : "Add"} Task</ThemedText>
</Pressable>
< View style={{ height: 50 }} />
</ScrollView >
</ThemedView >
);
}
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
},
headerContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: 8,
paddingHorizontal: 24,
paddingTop: 50,
paddingBottom: 18,
backgroundColor: '#6a254fff',
},
scrollView: {
padding: 24,
flex: 1,
},
stepContainer: {
gap: 24,
marginBottom: 8,
marginTop: 16,
},
reactLogo: {
height: 178,
width: 290,
bottom: 0,
left: 0,
position: 'absolute',
},
inputText: {
backgroundColor: '#fff',
height: 50,
color: '#333333',
borderRadius: 8,
padding: 10,
lineHeight: 30,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 32,
borderWidth: 2,
borderColor: '#ccc',
},
textArea: {
backgroundColor: '#fff',
color: '#333333',
borderRadius: 8,
padding: 10,
paddingHorizontal: 32,
minHeight: 100,
textAlignVertical: 'top'
},
addButton: {
flexDirection: 'row',
gap: 8,
backgroundColor: '#b91f7eff',
color: '#fff',
borderRadius: 8,
padding: 20,
alignItems: 'center',
justifyContent: 'center',
},
addButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',
},
});