removed todo_app

This commit is contained in:
Juthatip McDevitt 2024-07-20 14:20:31 -05:00
parent 1eee5aa334
commit 9e8c79c046
25 changed files with 0 additions and 21516 deletions

29
todo_app/.gitignore vendored
View file

@ -1,29 +0,0 @@
node_modules/
.expo/
.idea/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
# macOS
.DS_Store
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
# The following patterns were generated by expo-cli
expo-env.d.ts
api/index_.js
.env
README.md
scripts/*
hooks/*
constants/*
components/*

View file

@ -1,173 +0,0 @@
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const crypto = require("crypto");
const User = require("./models/user");
const Todo = require("./models/todo");
const app = express();
const port = 3030;
const cors = require("cors");
const jwt = require("jsonwebtoken");
const moment = require("moment")
app.use(cors());
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
//connect with database
mongoose.connect(process.env.MONGO_URL).then(() => {
console.log("Connectd to mongodb");
}).catch((error) => {
console.log("Error to connect with mongodb", error);
});
app.listen(port, () => {
console.log("Server is running on port 3030")
});
//login & register
const generateSecretKey = () => {
const secretKey = crypto.randomBytes(32).toString("hex")
return secretKey;
};
const secretKey = generateSecretKey();
app.post("/register", async(req, res) => {
try {
const {name, email, password} = req.body;
//check existing user
const existingUser = await User.findOne({email});
if(existingUser){
console.log("Email already registered");
}
const newUser = new User({name, email, password});
await newUser.save();
res.status(202).json({message: "Registration succeeded!"});
} catch (error) {
console.log("Fail to register an account!", error);
res.status(500).json({message: "Registration failed!"});
}
});
app.post("/login", async(req, res) => {
try {
const {email, password} = req.body;
const user = await User.findOne({email});
if(!user){
return res.status(401).json({message: "Wrong credentials: invalid username or passowrd"});
}
if(user.password !== password){
return res.status(401).json({message: "Wrong credentials: invalid username or passowrd"});
}
const token = jwt.sign({userId:user._id,}, secretKey);
res.status(200).json({token});
} catch (error) {
console.log("Fail to login!", error);
res.status(500).json({message: "Login failed!"});
}
});
//todo list
app.post("/todos/:userId", async(req, res) => {
try {
const userId = req.params.userId;
const {title, category} = req.body;
const newTodo = new Todo({
title,
category,
dueDate: moment().format("MMM Do YY")
});
await newTodo.save();
//add todo id
const user = await User.findById(userId);
if(!user){
res.status(404).json({message: "User not found!"});
}
user?.todos.push(newTodo._id);
await user.save();
res.status(200).json({message: "A list is added successfully!", todo:newTodo});
} catch (error) {
res.status(500).json({message: "Fail to add a list!"});
console.log(error)
}
});
app.get("/users/:userId/todos", async(req, res) => {
try {
const userId = req.params.userId;
const user = await User.findById(userId).populate("todos");
if(!user){
return res.status(404).json({message: "user not found"});
}
res.status(200).json({todos:user.todos});
} catch (error) {
res.status(500).json({message: "Error, something went wrong!"});
}
});
app.patch("/todos/:todoId/complete", async(req, res) => {
try {
const todoId = req.params.todoId;
const updatedTodo = await Todo.findByIdAndUpdate(todoId, {
status:"completed"
},
{new:true}
);
if(!updatedTodo){
return res.status(404).json({error:"Todo not found"});
}
res.status(200).json({message: "Mark as completed!", todo:updatedTodo});
} catch (error) {
res.status(500).json({message: "Error, something went wrong!"});
}
});
//calendar
app.get("/users/:userId/todos/completed/:date", async(req, res) => {
try {
const date = req.params.date;
const userId = req.params.userId;
const user = await User.findById(userId);
const userToDos = await user.populate('todos');
const toDos = userToDos.todos;
const completedTodos = toDos.filter(item => {
return item.status == "completed" && // status filter
item.createdAt.getTime() >= new Date(`${date}T00:00:00.000Z`) && // gte filter
item.createdAt.getTime() <= new Date(`${date}T23:59:59.999Z`) // lt filter
});
res.status(200).json({completedTodos})
} catch (error) {
res.status(500).json({message: "Error, something went wrong!"});
console.log(error)
}
});
app.get("/users/:userId/todos/count", async(req, res) => {
try {
const userId = req.params.userId;
const user = await User.findById(userId);
const userToDos = await user.populate('todos');
const toDos = userToDos.todos;
const completedTodos = toDos.filter(item => {
return item.status == "completed"
});
const pendingTodos = toDos.filter(item => {
return item.status == "pending"
});
const totalCompletedTodos = completedTodos.length;
const totalPendingTodos = pendingTodos.length;
res.status(200).json({totalCompletedTodos, totalPendingTodos});
} catch (error) {
res.status(500).json({message: "Error, something went wrong!"});
console.log(error)
}
});

View file

@ -1,28 +0,0 @@
const mongoose = require("mongoose");
const todoSchema = new mongoose.Schema({
title:{
type: String,
required: true,
},
status:{
type: String,
enum: ["pending", "completed"],
default: "pending"
},
category:{
type: String,
required: true
},
dueDate:{
type: String,
required: true,
},
createdAt:{
type: Date,
default: Date.now
}
});
const Todo= mongoose.model("Todo", todoSchema);
module.exports = Todo

View file

@ -1,30 +0,0 @@
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name:{
type: String,
required: true,
},
email:{
type: String,
required: true,
unique: true,
},
password:{
type: String,
required: true,
},
todos:[
{
type: mongoose.Schema.Types.ObjectId,
ref: "Todo",
}
],
createdAt:{
type: Date,
default: Date.now
}
});
const User = mongoose.model("User", userSchema);
module.exports = User

File diff suppressed because it is too large Load diff

View file

@ -1,21 +0,0 @@
{
"name": "api",
"version": "1.0.0",
"description": "server_side",
"main": "index.js",
"scripts": {
"start": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongodb": "^6.8.0",
"mongoose": "^8.5.1",
"nodemon": "^3.1.4"
}
}

View file

@ -1,36 +0,0 @@
{
"expo": {
"name": "todo_app",
"slug": "todo_app",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "todo_app",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./assets/images/logo.png"
},
"plugins": [
"expo-router"
],
"experiments": {
"typedRoutes": true
}
}
}

View file

@ -1,10 +0,0 @@
import {Stack} from "expo-router";
export default function Layout(){
return(
<Stack>
<Stack.Screen name="login" options={{headerShown: false}}/>
<Stack.Screen name="register" options={{headerShown: false}}/>
</Stack>
)
}

View file

@ -1,83 +0,0 @@
import { StyleSheet, Text, View, SafeAreaView, KeyboardAvoidingView, Image, TextInput, Pressable } from 'react-native'
import React, { useEffect, useState } from 'react'
import { Fontisto } from '@expo/vector-icons';
import { Entypo } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import axios from "axios";
import AsyncStorage from "@react-native-async-storage/async-storage";
const login = () => {
const router = useRouter();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
useEffect(() => {
const checkLoginStatus = async() => {
try {
const token = await AsyncStorage.getItem("authToken");
if(token){
router.replace("/(tabs)/home")
}
} catch (error) {
console.log(error)
}
};
checkLoginStatus();
}, [])
const handleLogin = () => {
const user = {
email: email,
password: password,
};
axios.post("http://localhost:3030/login", user).then((res) => {
const token = res.data.token;
AsyncStorage.setItem("authToken", JSON.stringify(token));
router.replace("/(tabs)/home")
})
};
return (
<SafeAreaView style={{flex:1, backgroundColor: "white", alignItems: "center", justifyContent:"center"}}>
<View>
<View style={{alignItems: "center"}}>
<Image
style={{width: 150, height: 150, resizeMode: "containe"}}
source={{
uri:"./assets/images/logo.png",
}}
/>
</View>
<Text style={{fontSize: 24, fontWeight: 600, color: "#222831", marginTop: 20}}>Keep Tracking</Text>
</View>
<KeyboardAvoidingView>
<View style={{alignItems: "center"}}>
<Text style={{fontSize: 16, fontWeight: 600, color: "black", marginTop: 20}}>Welcome back!</Text>
</View>
<View style={{marginTop: 50}}>
<View style={{flexDirection: "row", justifyContent: "center", alignItems: "center", gap:5, backgroundColor: "#EEEEEE", paddingHorizontal:10, borderRadius:5}}>
<Fontisto name="email" size={16} color="#31363F" />
<TextInput value={email} onChangeText={(text) => setEmail(text)} style={{color: "#31363F", width:250, outlineStyle: 'none', marginVertical: 10, fontSize:email ? 14 : 14}} placeholder='Enter your email'/>
</View>
<View style={{flexDirection: "row", justifyContent: "center", alignItems: "center", gap:5, backgroundColor: "#EEEEEE", paddingHorizontal:10, borderRadius:5, marginTop: 10}}>
<Entypo name="key" size={16} color="#31363F" />
<TextInput value={password} onChangeText={(text) => setPassword(text)} secureTextEntry={true} style={{color: "#31363F", width:250, outlineStyle: 'none', marginVertical: 10, fontSize:email ? 14 : 14}} placeholder='Enter your password'/>
</View>
<View style={{marginTop:10}}>
<Text style={{color: "#61677A", fontSize: 12 }}>Forgot password?</Text>
</View>
<View style={{marginTop: 50}}/>
<Pressable onPress={handleLogin} style={{backgroundColor: "#61677A", padding:10, borderRadius:5, alignItems: "center"}}>
<Text style={{color: "#D8D9DA", fontSize: 16, fontWeight: 600}}>Login</Text>
</Pressable>
<Pressable onPress={() => router.replace("/register")} style={{marginTop: 10}}>
<Text style={{fontSize: 12, textAlign:"center"}}>Don't have an account? <Text style={{fontWeight: 700}}>Register</Text></Text>
</Pressable>
</View>
</KeyboardAvoidingView>
</SafeAreaView>
)
}
export default login
const styles = StyleSheet.create({})

View file

@ -1,83 +0,0 @@
import { StyleSheet, Text, View, SafeAreaView, KeyboardAvoidingView, Image, TextInput, Pressable, Alert } from 'react-native'
import React, { useState } from 'react'
import { Fontisto } from '@expo/vector-icons';
import { Entypo } from '@expo/vector-icons';
import { AntDesign } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import axios from "axios";
const register = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const router = useRouter();
const handleRegister = () => {
const user = {
name: name,
email: email,
password: password,
}
axios.post("http://localhost:3030/register", user).then((res) => {
console.log(res);
Alert.alert("Registration succeeded!");
setEmail('');
setPassword('');
setName('');
}).catch((error) => {
Alert.alert("Registration failed!");
console.log("error", error);
})
}
return (
<SafeAreaView style={{flex:1, backgroundColor: "white", alignItems: "center", justifyContent:"center"}}>
<View>
<View style={{alignItems: "center"}}>
<Image
style={{width: 150, height: 150, resizeMode: "containe"}}
source={{
uri:"./assets/images/logo.png",
}}
/>
</View>
<Text style={{fontSize: 24, fontWeight: 600, color: "#222831", marginTop: 20}}>Keep Tracking</Text>
</View>
<KeyboardAvoidingView>
<View style={{alignItems: "center"}}>
<Text style={{fontSize: 16, fontWeight: 600, color: "black", marginTop: 20}}>Register</Text>
</View>
<View style={{marginTop: 50}}>
<View style={{flexDirection: "row", justifyContent: "center", alignItems: "center", gap:5, backgroundColor: "#EEEEEE", paddingHorizontal:10, borderRadius:5}}>
<AntDesign name="user" size={16} color="#31363F" />
<TextInput value={name} onChangeText={(text) => setName(text)} style={{color: "#31363F", width:250, outlineStyle: 'none', marginVertical: 10, fontSize:email ? 14 : 14}} placeholder='Username'/>
</View>
<View style={{flexDirection: "row", justifyContent: "center", alignItems: "center", gap:5, backgroundColor: "#EEEEEE", paddingHorizontal:10, borderRadius:5, marginTop: 10}}>
<Fontisto name="email" size={16} color="#31363F" />
<TextInput value={email} onChangeText={(text) => setEmail(text)} style={{color: "#31363F", width:250, outlineStyle: 'none', marginVertical: 10, fontSize:email ? 14 : 14}} placeholder='Enter your email'/>
</View>
<View style={{flexDirection: "row", justifyContent: "center", alignItems: "center", gap:5, backgroundColor: "#EEEEEE", paddingHorizontal:10, borderRadius:5, marginTop: 10}}>
<Entypo name="key" size={16} color="#31363F" />
<TextInput value={password} onChangeText={(text) => setPassword(text)} secureTextEntry={true} style={{color: "#31363F", width:250, outlineStyle: 'none', marginVertical: 10, fontSize:email ? 14 : 14}} placeholder='Enter your password'/>
</View>
<View style={{marginTop: 50}}/>
<Pressable onPress={handleRegister} style={{backgroundColor: "#61677A", padding:10, borderRadius:5, alignItems: "center"}}>
<Text style={{color: "#D8D9DA", fontSize: 16, fontWeight: 600}}>Register</Text>
</Pressable>
<Pressable onPress={() => router.replace("/login")} style={{marginTop: 10}}>
<Text style={{fontSize: 12, textAlign:"center"}}>Already have an account? <Text style={{fontWeight: 700}}>Login</Text></Text>
</Pressable>
</View>
</KeyboardAvoidingView>
</SafeAreaView>
)
}
export default register
const styles = StyleSheet.create({})

View file

@ -1,53 +0,0 @@
import {Tabs} from "expo-router";
import { FontAwesome } from '@expo/vector-icons';
import { AntDesign } from '@expo/vector-icons';
import { MaterialCommunityIcons } from '@expo/vector-icons';
export default function Layout(){
return(
<Tabs>
<Tabs.Screen
name="home"
options={{
tabBarLabel:"Home",
tabBarLabelStyle:{color:"black"},
headerShown: false,
tabBarIcon:({focused}) =>
focused? (
<FontAwesome name="tasks" size={24} color="black" />
) : (
<FontAwesome name="tasks" size={24} color="#45474B" />
)
}}
/>
<Tabs.Screen
name="calendar"
options={{
tabBarLabel:"calendar",
tabBarLabelStyle:{color:"black"},
headerShown: false,
tabBarIcon:({focused}) =>
focused? (
<AntDesign name="calendar" size={24} color="black" />
) : (
<AntDesign name="calendar" size={24} color="#45474B" />
)
}}
/>
<Tabs.Screen
name="profile"
options={{
tabBarLabel:"profile",
tabBarLabelStyle:{color:"black"},
headerShown: false,
tabBarIcon:({focused}) =>
focused? (
<MaterialCommunityIcons name="account-details" size={24} color="black" />
) : (
<MaterialCommunityIcons name="account-details" size={24} color="#45474B" />
)
}}
/>
</Tabs>
)
}

View file

@ -1,9 +0,0 @@
import {Stack} from "expo-router";
export default function Layout(){
return(
<Stack screenOptions={{headerShown:false}}>
<Stack.Screen name="index"/>
</Stack>
)
}

View file

@ -1,80 +0,0 @@
import { Pressable, StyleSheet, Text, View } from 'react-native';
import React, { useEffect, useState } from 'react';
import moment from 'moment';
import {Calendar} from "react-native-calendars";
import axios from "axios"
import { Feather } from '@expo/vector-icons';
import { Buffer } from 'buffer';
import AsyncStorage from "@react-native-async-storage/async-storage";
const getToken = async () => {
try {
let token = await AsyncStorage.getItem('authToken'); // get the authToken stored in AsyncStorage from login.js
let decodeToken = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
let userId = decodeToken.userId;
return userId;
} catch (e) {
// error reading value
console.log(e);
}
};
const index = () => {
const today = moment().format("YYYY-MM-DD");
const [selectedDate, setSelectedDate] = useState(today);
const [todos, setTodos] = useState([]);
const fetchCompletedTodos = () => {
try {
getToken().then( async (resp) => {
let userId = resp;
const res = await axios.get(`http://localhost:3030/users/${userId}/todos/completed/${selectedDate}`);
const completedTodos = res.data.completedTodos || [];
setTodos(completedTodos);
});
} catch (error) {
console.log("Error", error)
}
}
useEffect(() => {
fetchCompletedTodos();
}, [selectedDate]);
//Daypress
const handleDayPress = (day) => {
setSelectedDate(day.dateString)
}
return (
<View style={{flex:1, backgroundColor: "white"}}>
<Calendar onDayPress={handleDayPress}
theme = {{calendarBackground: "white", todayTextColor: 'red', arrowColor: "black",}}
markedDates={{[selectedDate]: {selected: true, selectedColor: "red"}}}
/>
{todos?.length > 0 && (
<View style={{marginTop: 20, marginHorizontal: 10}}>
<View style={{flexDirection:"row", alignItems: "center", gap: 10}}>
<Text style={{marginBottom: 20, fontSize: 16}}>You completed <Text style={{fontWeight: 600, color: "#61677A"}}>{todos.length}</Text> tasks</Text>
</View>
{todos.map((item, index) => (
<Pressable key={index} style={{backgroundColor: "#EEEEEE", marginBottom: 10, padding: 5, borderRadius: 5}}>
<View style={{flexDirection:"row", alignItems: "center", gap: 10}}>
<Feather name="check-circle" size={15} color="#1A5319" />
<Text style={{flex: 1, textDecorationLine: "line-through", color: "#1A5319"}}>{item.title}</Text>
</View>
</Pressable>
))}
</View>
)}
</View>
)
}
export default index
const styles = StyleSheet.create({})

View file

@ -1,13 +0,0 @@
import {Stack} from "expo-router";
import {ModalPortal} from "react-native-modals"
export default function Layout(){
return(
<>
<Stack screenOptions={{headerShown:false}}>
<Stack.Screen name="index"/>
</Stack>
<ModalPortal />
</>
)
}

View file

@ -1,240 +0,0 @@
import { Image, Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
import React, { useEffect, useState } from 'react';
import { AntDesign } from '@expo/vector-icons';
import { BottomModal, ModalContent, ModalTitle, SlideAnimation } from 'react-native-modals';
import { Entypo } from '@expo/vector-icons';
import { Feather } from '@expo/vector-icons';
import axios from "axios"
import moment from 'moment';
import { Buffer } from 'buffer';
import AsyncStorage from "@react-native-async-storage/async-storage";
const index = () => {
const [todos, setTodos] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [todo, setTodo] = useState('');
const [category, setCategory] = useState('all');
const [pendingTodos, setPendingTodos] = useState([]);
const [compledTodos, setCompletedTodos] = useState([]);
const [markCompleted, setMarkCompleted] = useState(false);
const todayDate = moment().format("MMM Do YYYY");
const getToken = async () => {
try {
let token = await AsyncStorage.getItem('authToken'); // get the authToken stored in AsyncStorage from login.js
let decodeToken = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
let userId = decodeToken.userId;
return userId;
} catch (e) {
// error reading value
console.log(e);
}
};
const addTodo = async() => {
try {
const todoData = {
title:todo,
category:category,
}
getToken().then((resp) => {
let userId = resp;
axios.post(`http://localhost:3030/todos/${userId}`, todoData).then((res) => {
})
}).catch((error) => {
console.log("Error", error);
})
await getUserTodos();
setModalVisible(false);
setTodo('');
} catch (error) {
console.log("Error", error)
}
}
useEffect(() => {
getUserTodos();
}, [markCompleted, modalVisible])
const getUserTodos = () => {
try {
getToken().then( async(resp) => {
let userId = resp;
const res = await axios.get(`http://localhost:3030/users/${userId}/todos`);
setTodos(res.data.todos);
const fetchTodos = res.data.todos || [];
const pending = fetchTodos.filter((todo) => todo.status !== "completed");
const completed = fetchTodos.filter((todo) => todo.status === "completed");
setPendingTodos(pending);
setCompletedTodos(completed);
})
} catch (error) {
console.log("Error", error)
}
};
const markTodocompleted = async (todoId) => {
try {
setMarkCompleted(true);
const res = await axios.patch(`http://localhost:3030/todos/${todoId}/complete`);
} catch (error) {
console.log("Error", error)
}
}
//data
const suggestion = [
{
id: 1,
todo: "Walk the dog"
},
{
id: 2,
todo: "Go to the gym"
},
{
id: 3,
todo: "Call mom"
},
{
id: 4,
todo: "Get goceries"
},
{
id: 5,
todo: "5 minute meditation"
},
]
return (
<>
<View style={{marginHorizontal:20, marginVertical:20, alignItems: "center", flexDirection: "row", gap: 10}}>
<Pressable style={{backgroundColor: "#000000", paddingHorizontal:10, paddingVertical: 5, borderRadius:25, alignItems: "center", justifyContent: "center"}}>
<Text style={{color: "white", textAlign: "center"}}>All</Text>
</Pressable>
<Pressable style={{backgroundColor: "#000000", paddingHorizontal:10, paddingVertical: 5, borderRadius:25, alignItems: "center", justifyContent: "center"}}>
<Text style={{color: "white", textAlign: "center"}}>Work</Text>
</Pressable>
<Pressable style={{backgroundColor: "#000000", paddingHorizontal:10, paddingVertical: 5, borderRadius:25, alignItems: "center", justifyContent: "center", marginRight: "auto"}}>
<Text style={{color: "white", textAlign: "center"}}>Personal</Text>
</Pressable>
<Pressable onPress={() => setModalVisible(!modalVisible)}>
<AntDesign name="plus" size={24} color="black" />
</Pressable>
</View>
<ScrollView style={{flex:1, backgroundColor: "white"}}>
<View style={{padding: 10}}>
{todos?.length > 0 ? (
<View>
{pendingTodos?.length > 0 &&
<>
<Text style={{fontSize: 20, fontWeight: 600, marginBottom: 10}}>{todayDate}</Text>
<Text style={{marginBottom: 20, fontSize: 16}}>You have <Text style={{fontWeight: 600, color: "#61677A"}}>{pendingTodos.length}</Text> tasks to do!</Text>
</>
}
{pendingTodos.map((item, index) => (
<Pressable key={index} style={{backgroundColor: "#EEEEEE", marginBottom: 10, padding: 5, borderRadius: 5}}>
<View style={{flexDirection:"row", alignItems: "center", gap: 10}}>
<Entypo onPress={() => markTodocompleted(item?._id)} name="circle" size={15} color="black" />
<Text style={{flex: 1}}>{item.title}</Text>
</View>
</Pressable>
))}
{compledTodos?.length > 0 && (
<View style={{marginTop: 20}}>
<View style={{flexDirection:"row", alignItems: "center", gap: 10}}>
<Text style={{marginBottom: 20, fontSize: 16}}>You have completed <Text style={{fontWeight: 600, color: "#61677A"}}>{compledTodos.length}</Text> tasks</Text>
</View>
{compledTodos.map((item, index) => (
<Pressable key={index} style={{backgroundColor: "#EEEEEE", marginBottom: 10, padding: 5, borderRadius: 5}}>
<View style={{flexDirection:"row", alignItems: "center", gap: 10}}>
<Feather name="check-circle" size={15} color="#1A5319" />
<Text style={{flex: 1, textDecorationLine: "line-through", color: "#1A5319"}}>{item.title}</Text>
</View>
</Pressable>
))}
</View>
)}
</View>
) : (
<View style={{flex:1, justifyContent:"center", alignItems:"center", marginTop:150, marginLeft:"auto", marginRight:"auto"}}>
<Image
style={{width: 100, height: 100, resizeMode: "containe"}}
source={{
uri:"./assets/images/to-do.png",
}}
/>
<Text style={{fontSize:16, fontWeight:600, marginTop: 20, textAlign:"center"}}>Hooray! There is no tasks</Text>
<Pressable onPress={() => setModalVisible(!modalVisible)} style={{backgroundColor: "black", marginTop: 20, padding:5, borderRadius: 5}}>
<Text style={{fontSize:14, fontWeight:600, textAlign:"center", color: "white"}}>Add task <AntDesign name="plus" size={16} color="white" /></Text>
</Pressable>
</View>
)}
</View>
</ScrollView>
<BottomModal
onBackdropPress={() => setModalVisible(!modalVisible)}
onHardwareBackPress={() => setModalVisible(!modalVisible)}
swipeDirection={['up', 'down']}
swipeThreshold={200}
modalTitle={<ModalTitle title="Add Task"/>}
modalAnimation={
new SlideAnimation({
slideFrom: 'bottom',
})
}
visible={modalVisible}
onTouchOutside={() => setModalVisible(!modalVisible)}
>
<ModalContent style={{width:"100%", height:280}}>
<View style={{marginTop: 10, flexDirection: "row", alignItems: "center", gap: 10}}>
<TextInput value={todo} onChangeText={(text) => setTodo(text)} style={{padding:10, borderColor: "#61677A", borderRadius:5, borderWidth:1, flex:1, outlineStyle: 'none', color: "#31363F"}} placeholder='Add your new task'/>
<Entypo onPress={addTodo} name="add-to-list" size={24} color="black" />
</View>
<Text style={{marginTop: 10, fontWeight: 600}}>Category:</Text>
<View style={{flexDirection: "row", alignItems: "center", gap: 10, marginVertical: 10}}>
<Pressable onPress={() => setCategory('personal')} style={{backgroundColor: "#D8D9DA", padding: 5, borderRadius: 5,}}>
<Text>Personal</Text>
</Pressable>
<Pressable onPress={() => setCategory('work')} style={{backgroundColor: "#D8D9DA", padding: 5, borderRadius: 5}}>
<Text>Work</Text>
</Pressable>
<Pressable onPress={() => setCategory('house')} style={{backgroundColor: "#D8D9DA", padding: 5, borderRadius: 5}}>
<Text>House</Text>
</Pressable>
<Pressable onPress={() => setCategory('social')} style={{backgroundColor: "#D8D9DA", padding: 5, borderRadius: 5}}>
<Text>Social</Text>
</Pressable>
</View>
<Text style={{marginTop: 10, fontWeight: 600}}>Suggestions:</Text>
<View style={{flexDirection: "row", alignItems: "center", gap: 10, marginVertical: 10, flexWrap: "wrap"}}>
{suggestion?.map((item, index) =>
<Pressable onPress={() => setTodo(item?.todo)} key={index} style={{backgroundColor: "#EEEEEE", padding: 5, borderRadius: 5}}>
<Text>{item.todo}</Text>
</Pressable>
)}
</View>
</ModalContent>
</BottomModal>
</>
)
}
export default index
const styles = StyleSheet.create({})

View file

@ -1,9 +0,0 @@
import {Stack} from "expo-router";
export default function Layout(){
return(
<Stack screenOptions={{headerShown:false}}>
<Stack.Screen name="index"/>
</Stack>
)
}

View file

@ -1,106 +0,0 @@
import { Dimensions, Image, Pressable, StyleSheet, Text, View } from 'react-native';
import React, { useEffect, useState } from 'react';
import axios from "axios";
import {BarChart} from "react-native-chart-kit";
import { Buffer } from 'buffer';
import AsyncStorage from "@react-native-async-storage/async-storage";
const getToken = async () => {
try {
let token = await AsyncStorage.getItem('authToken'); // get the authToken stored in AsyncStorage from login.js
let decodeToken = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
let userId = decodeToken.userId;
return userId;
} catch (e) {
// error reading value
console.log(e);
}
};
const index = () => {
const [completedTasks, setCompletedTasks] = useState(0);
const [pendingTasks, setPendingTasks] = useState(0);
const fetchTaskData = () => {
try {
getToken().then( async (resp) => {
let userId = resp;
const res = await axios.get(`http://localhost:3030/users/${userId}/todos/count`);
const {totalCompletedTodos, totalPendingTodos} = res.data;
setCompletedTasks(totalCompletedTodos);
setPendingTasks(totalPendingTodos);
});
} catch (error) {
console.log("Error", error)
}
}
useEffect(() => {
fetchTaskData();
}, []);
return (
<>
<View style={{marginHorizontal:20, marginVertical:20, alignItems: "center", flexDirection: "row", gap: 10}}>
<Pressable style={{marginRight: "auto"}}>
<Text style={{color: "black", textAlign: "center", fontSize: 20, fontWeight: 600}}>Keep Tracking</Text>
</Pressable>
</View>
<View style={{padding: 10, flex: 1, backgroundColor: "white"}}>
<View style={{marginTop: 10, marginHorizontal:10}}>
<Text style={{fontSize: 16, fontWeight: 600}}>Overview</Text>
<View style={{flexDirection: "row", alignItems: "center", gap: 10, marginVertical: 10}}>
<View style={{padding:10, backgroundColor: "#E8C4C4", borderRadius: 5, flex:1, justifyContent: "center", alignItems: "center"}}>
<Text style={{color: "#CE7777", fontWeight: 600}}>{pendingTasks}</Text>
<Text style={{color: "#CE7777", fontWeight: 600}}>Pending Tasks</Text>
</View>
<View style={{padding:10, backgroundColor: "#C8DBBE", borderRadius: 5, flex:1, justifyContent: "center", alignItems: "center"}}>
<Text style={{color: "#829460", fontWeight: 600}}>{completedTasks}</Text>
<Text style={{color: "#829460", fontWeight: 600}}>Completed Tasks</Text>
</View>
</View>
</View>
{/*========== chart =========*/}
<BarChart
data={{
labels: ["Pending", "Completed"],
datasets: [
{
data: [pendingTasks, completedTasks],
},
],
}}
width={Dimensions.get("window").width - 20}
height={200}
yAxisInterval={2}
chartConfig={{
backgroundColor: "#BFACE2",
backgroundGradientFrom: "#BCCEF8",
backgroundGradientTo: "#F3E8FF",
decimalPlaces: 2,
color: (opacity = 1) => `rgba(106, 90, 205, ${opacity})`,
labelColor: (opacity = 1) => `rgba(108, 56, 172, ${opacity})`,
style: {
borderRadius: 5
},
}}
style={{
borderRadius: 10,
marginTop: 10
}}
/>
</View>
</>
)
}
export default index
const styles = StyleSheet.create({})

View file

@ -1,13 +0,0 @@
import { StyleSheet, Text, View } from "react-native";
import React from 'react';
import { Redirect } from "expo-router";
const index = () => {
return (
<Redirect href="/(authenticate)/login"/>
)
}
export default index
const styles = StyleSheet.create({})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View file

@ -1,6 +0,0 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};

19034
todo_app/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,55 +0,0 @@
{
"name": "todo_app",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"test": "jest --watchAll",
"lint": "expo lint"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "1.23.1",
"@react-navigation/native": "^6.0.2",
"axios": "^1.7.2",
"expo": "~51.0.20",
"expo-constants": "~16.0.2",
"expo-font": "~12.0.8",
"expo-linking": "~6.3.1",
"expo-router": "~3.5.18",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
"expo-web-browser": "~13.0.3",
"moment": "^2.30.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.74.3",
"react-native-calendars": "^1.1305.0",
"react-native-chart-kit": "^6.12.0",
"react-native-gesture-handler": "~2.16.1",
"react-native-modals": "^0.22.3",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.1",
"react-native-screens": "3.31.1",
"react-native-web": "~0.19.10"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"@types/react-test-renderer": "^18.0.7",
"jest": "^29.2.1",
"jest-expo": "~51.0.3",
"react-test-renderer": "18.2.0",
"typescript": "~5.3.3"
},
"private": true
}

View file

@ -1,17 +0,0 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": [
"./*"
]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
]
}