-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
1,222 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
import { useMemo, useState } from "react"; | ||
import { CustomDate } from "../../services/progress/date.services"; | ||
|
||
const statuses = { | ||
idle: "idle", | ||
editing: "editing", | ||
}; | ||
|
||
const AddMealForm = ({ onSave }) => { | ||
const [meal, setMeal] = useState({}); | ||
|
||
const handleChange = (evt) => { | ||
setMeal((m) => ({ | ||
...m, | ||
[evt.target.name]: evt.target.value, | ||
})); | ||
}; | ||
|
||
return ( | ||
<div className="mt-2"> | ||
<input | ||
onChange={handleChange} | ||
className="border p-1 mr-2 mb-1" | ||
placeholder="food" | ||
name="food" | ||
value={meal["food"]} | ||
/> | ||
<input | ||
onChange={handleChange} | ||
className="border p-1 mr-2 mb-1 w-20" | ||
placeholder="calories" | ||
name="calories" | ||
value={meal["calories"]} | ||
/> | ||
<input | ||
onChange={handleChange} | ||
className="border p-1 mr-2 mb-1" | ||
placeholder="date" | ||
name="date" | ||
value={meal["date"]} | ||
/> | ||
<span | ||
onClick={() => onSave(meal)} | ||
className="cursor-pointer hover:underline" | ||
> | ||
Save | ||
</span> | ||
</div> | ||
); | ||
}; | ||
|
||
export default function Card(props) { | ||
const { onSave, ...defaultEvent } = props; | ||
const [status, setStatus] = useState(statuses.idle); | ||
const [event, setEvent] = useState(defaultEvent); | ||
const toggleStatus = () => | ||
setStatus(status === statuses.editing ? statuses.idle : statuses.editing); | ||
|
||
const handleChange = (e) => { | ||
const { name, value, type, checked } = e.target; | ||
|
||
if (type === "checkbox") { | ||
setEvent((e) => ({ | ||
...e, | ||
[name]: checked, | ||
})); | ||
return; | ||
} | ||
|
||
setEvent((e) => ({ | ||
...e, | ||
[name]: value, | ||
})); | ||
}; | ||
|
||
const handleSave = () => { | ||
onSave(event); | ||
toggleStatus(); | ||
}; | ||
|
||
const handleSaveMeal = (meal) => { | ||
setEvent((e) => ({ | ||
...e, | ||
meals: e.meals.concat(meal), | ||
})); | ||
onSave(event); | ||
}; | ||
|
||
const handleDeleteMeal = (idx) => () => { | ||
setEvent((e) => ({ | ||
...e, | ||
meals: e.meals.filter((_, iIdx) => iIdx !== idx), | ||
})); | ||
onSave(event); | ||
}; | ||
|
||
const isEditing = status === statuses.editing; | ||
|
||
const totalCalories = useMemo(() => { | ||
return event.meals.reduce((acc, meal) => acc + Number(meal.calories), 0); | ||
}, []); | ||
|
||
return ( | ||
<div className="bg-white p-6 rounded-lg shadow-lg"> | ||
<div className="flex"> | ||
{isEditing ? ( | ||
<input | ||
placeholder="Date" | ||
className="border p-1" | ||
value={event.date} | ||
onChange={handleChange} | ||
name="date" | ||
/> | ||
) : ( | ||
<span className="font-semibold" name="date"> | ||
{CustomDate.getDate(event.date)} | ||
</span> | ||
)} | ||
{isEditing ? ( | ||
<div className="ml-auto"> | ||
<span | ||
onClick={handleSave} | ||
className="hover:underline cursor-pointer" | ||
> | ||
Save | ||
</span> | ||
</div> | ||
) : ( | ||
<span onClick={toggleStatus} className="ml-auto cursor-pointer"> | ||
✏️ | ||
</span> | ||
)} | ||
</div> | ||
|
||
<ul className="mt-1 p-l-4"> | ||
{event.meals.map(({ date, calories, food }, idx) => ( | ||
<li key={date}> | ||
<span>{food} — </span> | ||
<span name="calories" className="text-gray-600 text-sm"> | ||
{calories} kcal | ||
</span>{" "} | ||
/ <span className="text-gray-500 text-sm">{date}</span>{" "} | ||
{isEditing && ( | ||
<span onClick={handleDeleteMeal(idx)} className="cursor-pointer"> | ||
🪓 | ||
</span> | ||
)} | ||
</li> | ||
))} | ||
{isEditing && <AddMealForm onSave={handleSaveMeal} />} | ||
</ul> | ||
|
||
<div className="flex flex-wrap items-center mt-4"> | ||
{isEditing ? ( | ||
<input | ||
placeholder="Weight" | ||
className="border p-1" | ||
name="weight" | ||
onChange={handleChange} | ||
value={event.weight} | ||
/> | ||
) : ( | ||
<span className="bg-blue-200 text-blue-800 text-xs px-2 inline-block rounded-full mr-1 uppercase font-semibold tracking-wide"> | ||
{event.weight} kg | ||
</span> | ||
)} | ||
{!isEditing && ( | ||
<span className="bg-blue-100 text-blue-600 text-xs px-2 inline-block rounded-full mr-1 uppercase font-semibold tracking-wide"> | ||
{event.meals.length} meals / {totalCalories} kcal | ||
</span> | ||
)} | ||
{!isEditing ? ( | ||
event.workout && ( | ||
<span className="bg-red-200 text-red-800 text-xs px-2 inline-block rounded-full mr-1 uppercase font-semibold tracking-wide"> | ||
Workout | ||
</span> | ||
) | ||
) : ( | ||
<label className="ml-auto"> | ||
<input | ||
className="border p-1" | ||
onChange={handleChange} | ||
value={!event.workout} | ||
type="checkbox" | ||
name="workout" | ||
defaultChecked={event.workout} | ||
/> | ||
<span className="pl-2">Workout</span> | ||
</label> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { useEffect, useState } from "react"; | ||
|
||
import { addEvent, getEvents } from "../../services/progress/events.services"; | ||
|
||
import { useAuthentication } from "../../hooks/useAuthentication.hooks"; | ||
import Card from "./Card"; | ||
|
||
export default function Dashboard() { | ||
const { user } = useAuthentication(); | ||
const [events, setEvents] = useState({ | ||
data: [], | ||
loading: true, | ||
error: "", | ||
}); | ||
|
||
useEffect(() => { | ||
getEvents() | ||
.then((e) => { | ||
setEvents((events) => ({ | ||
...events, | ||
loading: false, | ||
data: e, | ||
})); | ||
}) | ||
.catch((e) => { | ||
setEvents((events) => ({ | ||
...events, | ||
loading: false, | ||
e: e.message, | ||
})); | ||
}); | ||
}, []); | ||
|
||
const saveEvent = (event) => { | ||
addEvent(event); | ||
}; | ||
|
||
if (events.loading) { | ||
return <div>Loading...</div>; | ||
} | ||
|
||
return ( | ||
<div className="m-4"> | ||
<header className="my-4 flex justify-between items-center"> | ||
<h1 className="text-xl font-bold">Dashboard</h1> | ||
<p>Ahoy, {user.email}</p> | ||
</header> | ||
|
||
<div className="mb-4"> | ||
<h2>Today</h2> | ||
<Card | ||
onSave={saveEvent} | ||
{...{ | ||
meals: [], | ||
date: new Date().toString(), | ||
workout: false, | ||
weight: "", | ||
}} | ||
/> | ||
</div> | ||
|
||
<hr className="mb-8" /> | ||
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> | ||
{events.data.map((event) => { | ||
return ( | ||
<div className="" key={event.date}> | ||
<Card | ||
onSave={saveEvent} | ||
meals={event.meals} | ||
weight={event.weight} | ||
date={event.date} | ||
workout={event.workout} | ||
/> | ||
</div> | ||
); | ||
})} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import React, { useState } from "react"; | ||
import { useAuthentication } from "../../hooks/useAuthentication.hooks"; | ||
|
||
export default function Login() { | ||
const [email, setEmail] = useState(""); | ||
const [pass, setPass] = useState(""); | ||
const [loading, setLoading] = useState(false); | ||
const { signin } = useAuthentication(); | ||
|
||
const handleSubmit = async () => { | ||
setLoading(true); | ||
|
||
await signin(email, pass) | ||
.then(() => { | ||
setPass(""); | ||
setEmail(""); | ||
}) | ||
.catch((e) => { | ||
alert(e.message); | ||
}) | ||
.finally(() => { | ||
setLoading(false); | ||
}); | ||
}; | ||
|
||
if (loading) { | ||
return <div>Loading...</div>; | ||
} | ||
|
||
return ( | ||
<div className="w-full h-screen mx-auto flex flex-col justify-center items-center"> | ||
<h2 className="text-2xl font-medium mb-2">Login:</h2> | ||
|
||
<div className="flex flex-col w-full p-4 sm:w-3/4 md:w-1/3 md:p-0"> | ||
<input | ||
label="Email" | ||
type="email" | ||
value={email} | ||
onChange={(e) => setEmail(e.target.value)} | ||
className="border p-1 w-full" | ||
/> | ||
<input | ||
label="Password" | ||
type="password" | ||
value={pass} | ||
style={{ margin: "10px 0" }} | ||
onChange={(e) => setPass(e.target.value)} | ||
className="border p-1 w-full" | ||
/> | ||
<div | ||
style={{ | ||
width: "100%", | ||
display: "flex", | ||
alignItems: "center", | ||
justifyContent: "center", | ||
}} | ||
> | ||
<button | ||
className="flex border items-center text-sm my-4 mx-auto px-4 py-2 rounded-md font-medium text-gray-900 dark:text-gray-100 hover:bg-gray-200" | ||
type="submit" | ||
onClick={handleSubmit} | ||
> | ||
Log In | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const Documents = { | ||
events: "events", | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const Operators = { | ||
less: "<", | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const Routes = { | ||
login: () => "/login", | ||
dashboard: () => "/", | ||
}; |
Oops, something went wrong.