From a411f91c8e72057ac20152c160a63d82d5cf53a9 Mon Sep 17 00:00:00 2001 From: Maxime RICHARD Date: Thu, 11 Apr 2024 12:32:09 +0200 Subject: [PATCH] feat: add habit progress update --- domain/entities/HabitHistory.ts | 2 +- domain/entities/HabitsTracker.ts | 19 +++ domain/repositories/HabitProgressCreate.ts | 12 ++ domain/repositories/HabitProgressUpdate.ts | 12 ++ domain/use-cases/HabitGoalProgressUpdate.ts | 28 ++++ .../components/HabitHistory/HabitHistory.tsx | 143 ++++++++++++++++++ .../components/HabitsMainPage/HabitCard.tsx | 12 +- 7 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 domain/repositories/HabitProgressCreate.ts create mode 100644 domain/repositories/HabitProgressUpdate.ts create mode 100644 domain/use-cases/HabitGoalProgressUpdate.ts create mode 100644 presentation/react/components/HabitHistory/HabitHistory.tsx diff --git a/domain/entities/HabitHistory.ts b/domain/entities/HabitHistory.ts index b7f7a08..5f34f3e 100644 --- a/domain/entities/HabitHistory.ts +++ b/domain/entities/HabitHistory.ts @@ -34,7 +34,7 @@ export class HabitHistory implements HabitHistoryJSON { }) } - private getProgressesByDate(date: Date): HabitProgress[] { + public getProgressesByDate(date: Date): HabitProgress[] { return this._progressHistory.filter((progress) => { if (this.habit.goal.frequency === "monthly") { return ( diff --git a/domain/entities/HabitsTracker.ts b/domain/entities/HabitsTracker.ts index 7a99215..834fb82 100644 --- a/domain/entities/HabitsTracker.ts +++ b/domain/entities/HabitsTracker.ts @@ -1,6 +1,7 @@ import type { GoalFrequency } from "./Goal" import type { Habit } from "./Habit" import { HabitHistory } from "./HabitHistory" +import { HabitProgress } from "./HabitProgress" export interface HabitsTrackerData { habitsHistory: { @@ -35,6 +36,24 @@ export class HabitsTracker implements HabitsTrackerData { ) } + public setHabitProgress(options: SetHabitProgressOptions): void { + const { date, goalProgress, habitHistory } = options + if (goalProgress.isBoolean()) { + const currentHabitProgress = habitHistory.getProgressesByDate(date)[0] + if (currentHabitProgress == null) { + habitHistory.progressHistory = [ + ...habitHistory.progressHistory, + new HabitProgress({ + date, + goalProgress, + habitId, + id, + }), + ] + } + } + } + public getAllHabitsHistory(): HabitHistory[] { return [ ...this.habitsHistory.daily, diff --git a/domain/repositories/HabitProgressCreate.ts b/domain/repositories/HabitProgressCreate.ts new file mode 100644 index 0000000..1f78d9a --- /dev/null +++ b/domain/repositories/HabitProgressCreate.ts @@ -0,0 +1,12 @@ +import type { + HabitProgress, + HabitProgressData, +} from "../entities/HabitProgress" + +export interface HabitProgressCreateOptions { + habitProgressData: Omit +} + +export interface HabitProgressCreateRepository { + execute: (options: HabitProgressCreateOptions) => Promise +} diff --git a/domain/repositories/HabitProgressUpdate.ts b/domain/repositories/HabitProgressUpdate.ts new file mode 100644 index 0000000..c3fa305 --- /dev/null +++ b/domain/repositories/HabitProgressUpdate.ts @@ -0,0 +1,12 @@ +import type { + HabitProgress, + HabitProgressData, +} from "../entities/HabitProgress" + +export interface HabitProgressUpdateOptions { + habitProgressData: HabitProgressData +} + +export interface HabitProgressUpdateRepository { + execute: (options: HabitProgressUpdateOptions) => Promise +} diff --git a/domain/use-cases/HabitGoalProgressUpdate.ts b/domain/use-cases/HabitGoalProgressUpdate.ts new file mode 100644 index 0000000..965ddb0 --- /dev/null +++ b/domain/use-cases/HabitGoalProgressUpdate.ts @@ -0,0 +1,28 @@ +import type { GoalProgress } from "../entities/Goal" +import type { HabitHistory } from "../entities/HabitHistory" +import type { HabitProgressCreateRepository } from "../repositories/HabitProgressCreate" +import type { HabitProgressUpdateRepository } from "../repositories/HabitProgressUpdate" + +export interface HabitGoalProgressUpdateOptions { + date: Date + goalProgress: GoalProgress + habitHistory: HabitHistory +} + +export class HabitGoalProgressUpdateUseCase + implements HabitGoalProgressUpdateOptions +{ + public date: Date + public goalProgress: GoalProgress + public habitHistory: HabitHistory + + public constructor(option: HabitGoalProgressUpdateOptions) { + this.date = option.date + this.goalProgress = option.goalProgress + this.habitHistory = option.habitHistory + } + + public async execute(data: unknown): Promise { + // + } +} diff --git a/presentation/react/components/HabitHistory/HabitHistory.tsx b/presentation/react/components/HabitHistory/HabitHistory.tsx new file mode 100644 index 0000000..4a1c572 --- /dev/null +++ b/presentation/react/components/HabitHistory/HabitHistory.tsx @@ -0,0 +1,143 @@ +import { useState } from "react" +import { Calendar } from "react-native-calendars" +import { Text } from "react-native-paper" +import { SafeAreaView } from "react-native-safe-area-context" + +export const HabitHistory: React.FC = () => { + const [selected, setSelected] = useState("") + + return ( + + { + setSelected(day.dateString) + }} + markedDates={{ + "2023-03-01": { selected: true, marked: true, selectedColor: "blue" }, + "2023-03-02": { marked: true }, + "2023-03-03": { selected: true, marked: true, selectedColor: "blue" }, + [selected]: { + selected: true, + disableTouchEvent: true, + selectedColor: "orange", + }, + }} + theme={{ + backgroundColor: "#000000", + calendarBackground: "#000000", + textSectionTitleColor: "#b6c1cd", + selectedDayBackgroundColor: "#00adf5", + selectedDayTextColor: "#ffffff", + todayTextColor: "#00adf5", + dayTextColor: "#2d4150", + textDisabledColor: "#d9efff", + }} + /> + {selected} + + ) +} + +/* + { + console.log('trigger items loading'); + }} + // Callback that fires when the calendar is opened or closed + onCalendarToggled={calendarOpened => { + console.log(calendarOpened); + }} + // Callback that gets called on day press + onDayPress={day => { + console.log('day pressed'); + }} + // Callback that gets called when day changes while scrolling agenda list + onDayChange={day => { + console.log('day changed'); + }} + // Initially selected day + selected={'2012-05-16'} + // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined + minDate={'2012-05-10'} + // Maximum date that can be selected, dates after maxDate will be grayed out. Default = undefined + maxDate={'2012-05-30'} + // Max amount of months allowed to scroll to the past. Default = 50 + pastScrollRange={50} + // Max amount of months allowed to scroll to the future. Default = 50 + futureScrollRange={50} + // Specify how each item should be rendered in agenda + renderItem={(item, firstItemInDay) => { + return ; + }} + // Specify how each date should be rendered. day can be undefined if the item is not first in that day + renderDay={(day, item) => { + return ; + }} + // Specify how empty date content with no items should be rendered + renderEmptyDate={() => { + return ; + }} + // Specify how agenda knob should look like + renderKnob={() => { + return ; + }} + // Override inner list with a custom implemented component + renderList={listProps => { + return ; + }} + // Specify what should be rendered instead of ActivityIndicator + renderEmptyData={() => { + return ; + }} + // Specify your item comparison function for increased performance + rowHasChanged={(r1, r2) => { + return r1.text !== r2.text; + }} + // Hide knob button. Default = false + hideKnob={true} + // When `true` and `hideKnob` prop is `false`, the knob will always be visible and the user will be able to drag the knob up and close the calendar. Default = false + showClosingKnob={false} + // By default, agenda dates are marked if they have at least one item, but you can override this if needed + markedDates={{ + '2012-05-16': {selected: true, marked: true}, + '2012-05-17': {marked: true}, + '2012-05-18': {disabled: true} + }} + // If disabledByDefault={true} dates flagged as not disabled will be enabled. Default = false + disabledByDefault={true} + // If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly + onRefresh={() => console.log('refreshing...')} + // Set this true while waiting for new data from a refresh + refreshing={false} + // Add a custom RefreshControl component, used to provide pull-to-refresh functionality for the ScrollView + refreshControl={null} + // Agenda theme + theme={{ + ...calendarTheme, + agendaDayTextColor: 'yellow', + agendaDayNumColor: 'green', + agendaTodayColor: 'red', + agendaKnobColor: 'blue' + }} + // Agenda container style + style={{}} +/> +*/ diff --git a/presentation/react/components/HabitsMainPage/HabitCard.tsx b/presentation/react/components/HabitsMainPage/HabitCard.tsx index 3cf9979..a772666 100644 --- a/presentation/react/components/HabitsMainPage/HabitCard.tsx +++ b/presentation/react/components/HabitsMainPage/HabitCard.tsx @@ -1,7 +1,8 @@ import FontAwesome6 from "@expo/vector-icons/FontAwesome6" import { useRouter } from "expo-router" import { View } from "react-native" -import { List, Text } from "react-native-paper" +import { List, Text, Checkbox } from "react-native-paper" +import { useState } from "react" import type { GoalProgress } from "@/domain/entities/Goal" import type { Habit } from "@/domain/entities/Habit" @@ -22,6 +23,8 @@ export const HabitCard: React.FC = (props) => { opacity: 0.4, }) + const [checked, setChecked] = useState(goalProgress.isCompleted()) + return ( { @@ -80,7 +83,12 @@ export const HabitCard: React.FC = (props) => { return ( - {goalProgress.isCompleted() ? "true" : "false"} + { + setChecked(!checked) + }} + /> ) }}