207 lines
7.8 KiB
TypeScript
207 lines
7.8 KiB
TypeScript
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',
|
||
},
|
||
});
|