From 246cbe918a2a0b344e2602f67142a6d0ae2d5ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Thu, 11 Apr 2024 13:07:17 +0200 Subject: [PATCH] feat: edit habit working version --- app/application/habits/[habitId]/index.tsx | 25 +++---------------- app/application/habits/new.tsx | 2 +- domain/entities/Goal.ts | 14 ----------- domain/entities/Habit.ts | 4 +-- domain/entities/HabitsTracker.ts | 8 ++++++ infrastructure/instances.ts | 9 +++++++ .../supabase/repositories/HabitEdit.ts | 24 +++++++++++------- presentation/presenters/HabitsTracker.ts | 20 ++++++++++----- .../HabitEditForm/HabitEditForm.tsx | 25 ++++++------------- 9 files changed, 59 insertions(+), 72 deletions(-) diff --git a/app/application/habits/[habitId]/index.tsx b/app/application/habits/[habitId]/index.tsx index 5a03693..02c162c 100644 --- a/app/application/habits/[habitId]/index.tsx +++ b/app/application/habits/[habitId]/index.tsx @@ -1,39 +1,20 @@ import { Redirect, useLocalSearchParams } from "expo-router" -import { Text } from "react-native-paper" -import { SafeAreaView } from "react-native-safe-area-context" -import { useHabitsTracker } from "@/presentation/react/contexts/HabitsTracker" import { HabitEditForm } from "@/presentation/react/components/HabitEditForm/HabitEditForm" -import { useAuthentication } from "@/presentation/react/contexts/Authentication" +import { useHabitsTracker } from "@/presentation/react/contexts/HabitsTracker" const HabitPage: React.FC = () => { const { habitId } = useLocalSearchParams() const { habitsTracker } = useHabitsTracker() - const { user } = useAuthentication() - - if (user === null) { - return null - } const habitHistory = habitsTracker.getHabitHistoryById(habitId as string) + if (habitHistory == null) { return } return ( - - - Habit Page {habitId} {habitHistory.habit.name} - - - + ) } diff --git a/app/application/habits/new.tsx b/app/application/habits/new.tsx index f2c5361..f410ab0 100644 --- a/app/application/habits/new.tsx +++ b/app/application/habits/new.tsx @@ -4,7 +4,7 @@ import { useAuthentication } from "@/presentation/react/contexts/Authentication" const NewHabitPage: React.FC = () => { const { user } = useAuthentication() - if (user === null) { + if (user == null) { return null } diff --git a/domain/entities/Goal.ts b/domain/entities/Goal.ts index d55fcbf..ded0b30 100644 --- a/domain/entities/Goal.ts +++ b/domain/entities/Goal.ts @@ -33,22 +33,8 @@ export const GoalCreateSchema = z.object({ ]), }) -export const GoalEditSchema = z.object({ - frequency: goalFrequencyZod, - target: z.discriminatedUnion("type", [ - z.object({ type: z.literal("boolean") }), - z.object({ - type: z.literal("numeric"), - value: z.number().int().min(0), - unit: z.string().min(1), - }), - ]), -}) - export type GoalCreateData = z.infer -export type GoalEditData = z.infer - interface GoalBase { frequency: GoalFrequency } diff --git a/domain/entities/Habit.ts b/domain/entities/Habit.ts index e9de6c3..d80ba59 100644 --- a/domain/entities/Habit.ts +++ b/domain/entities/Habit.ts @@ -15,9 +15,7 @@ export const HabitCreateSchema = HabitSchema.extend({ }).omit({ id: true }) export type HabitCreateData = z.infer -export const HabitEditSchema = HabitSchema.extend({ - goal: GoalCreateSchema, -}) +export const HabitEditSchema = HabitSchema.extend({}) export type HabitEditData = z.infer type HabitBase = z.infer diff --git a/domain/entities/HabitsTracker.ts b/domain/entities/HabitsTracker.ts index 7a99215..19f2122 100644 --- a/domain/entities/HabitsTracker.ts +++ b/domain/entities/HabitsTracker.ts @@ -35,6 +35,14 @@ export class HabitsTracker implements HabitsTrackerData { ) } + public editHabit(habit: Habit): void { + const habitHistory = this.getHabitHistoryById(habit.id) + if (habitHistory == null) { + return + } + habitHistory.habit = habit + } + public getAllHabitsHistory(): HabitHistory[] { return [ ...this.habitsHistory.daily, diff --git a/infrastructure/instances.ts b/infrastructure/instances.ts index 89b1690..8e92055 100644 --- a/infrastructure/instances.ts +++ b/infrastructure/instances.ts @@ -8,6 +8,8 @@ import { supabaseClient } from "./supabase/supabase" import { AuthenticationPresenter } from "@/presentation/presenters/Authentication" import { HabitCreateSupabaseRepository } from "./supabase/repositories/HabitCreate" import { HabitCreateUseCase } from "@/domain/use-cases/HabitCreate" +import { HabitEditSupabaseRepository } from "./supabase/repositories/HabitEdit" +import { HabitEditUseCase } from "@/domain/use-cases/HabitEdit" /** * Repositories @@ -25,6 +27,9 @@ const getHabitsByUserIdRepository = new GetHabitsByUserIdSupabaseRepository({ const habitCreateRepository = new HabitCreateSupabaseRepository({ supabaseClient, }) +const habitEditRepository = new HabitEditSupabaseRepository({ + supabaseClient, +}) /** * Use Cases @@ -39,6 +44,9 @@ const retrieveHabitsTrackerUseCase = new RetrieveHabitsTrackerUseCase({ getHabitProgressHistoryRepository: getHabitProgressesRepository, getHabitsByUserIdRepository, }) +const habitEditUseCase = new HabitEditUseCase({ + habitEditRepository, +}) /** * Presenters @@ -49,4 +57,5 @@ export const authenticationPresenter = new AuthenticationPresenter({ export const habitsTrackerPresenter = new HabitsTrackerPresenter({ retrieveHabitsTrackerUseCase, habitCreateUseCase, + habitEditUseCase, }) diff --git a/infrastructure/supabase/repositories/HabitEdit.ts b/infrastructure/supabase/repositories/HabitEdit.ts index 9020098..f482cc3 100644 --- a/infrastructure/supabase/repositories/HabitEdit.ts +++ b/infrastructure/supabase/repositories/HabitEdit.ts @@ -12,18 +12,11 @@ export class HabitEditSupabaseRepository const { data, error } = await this.supabaseClient .from("habits") .update({ - id: Number(habitEditData.id), name: habitEditData.name, color: habitEditData.color, icon: habitEditData.icon, - goal_frequency: habitEditData.goal.frequency, - ...(habitEditData.goal.target.type === "numeric" - ? { - goal_target: habitEditData.goal.target.value, - goal_target_unit: habitEditData.goal.target.unit, - } - : {}), }) + .eq("id", habitEditData.id) .select("*") const updatedHabit = data?.[0] if (error != null || updatedHabit == null) { @@ -34,7 +27,20 @@ export class HabitEditSupabaseRepository userId: updatedHabit.user_id.toString(), name: updatedHabit.name, icon: updatedHabit.icon, - goal: Goal.create(habitEditData.goal), + goal: Goal.create({ + frequency: updatedHabit.goal_frequency, + target: + updatedHabit.goal_target != null && + updatedHabit.goal_target_unit != null + ? { + type: "numeric", + value: updatedHabit.goal_target, + unit: updatedHabit.goal_target_unit, + } + : { + type: "boolean", + }, + }), color: updatedHabit.color, startDate: new Date(updatedHabit.start_date), }) diff --git a/presentation/presenters/HabitsTracker.ts b/presentation/presenters/HabitsTracker.ts index 7e0d923..9688adb 100644 --- a/presentation/presenters/HabitsTracker.ts +++ b/presentation/presenters/HabitsTracker.ts @@ -7,9 +7,10 @@ import type { RetrieveHabitsTrackerUseCase, RetrieveHabitsTrackerUseCaseOptions, } from "@/domain/use-cases/RetrieveHabitsTracker" -import type { HabitCreateData } from "@/domain/entities/Habit" +import type { HabitCreateData, HabitEditData } from "@/domain/entities/Habit" import { getErrorsFieldsFromZodError } from "../../utils/zod" import type { HabitCreateUseCase } from "@/domain/use-cases/HabitCreate" +import type { HabitEditUseCase } from "@/domain/use-cases/HabitEdit" export interface HabitsTrackerPresenterState { habitsTracker: HabitsTracker @@ -29,7 +30,7 @@ export interface HabitsTrackerPresenterState { habitEdit: { state: FetchState errors: { - fields: Array + fields: Array global: ErrorGlobal } } @@ -38,6 +39,7 @@ export interface HabitsTrackerPresenterState { export interface HabitsTrackerPresenterOptions { retrieveHabitsTrackerUseCase: RetrieveHabitsTrackerUseCase habitCreateUseCase: HabitCreateUseCase + habitEditUseCase: HabitEditUseCase } export class HabitsTrackerPresenter @@ -46,9 +48,14 @@ export class HabitsTrackerPresenter { public retrieveHabitsTrackerUseCase: RetrieveHabitsTrackerUseCase public habitCreateUseCase: HabitCreateUseCase + public habitEditUseCase: HabitEditUseCase public constructor(options: HabitsTrackerPresenterOptions) { - const { retrieveHabitsTrackerUseCase, habitCreateUseCase } = options + const { + retrieveHabitsTrackerUseCase, + habitCreateUseCase, + habitEditUseCase, + } = options const habitsTracker = HabitsTracker.default() super({ habitsTracker, @@ -70,6 +77,7 @@ export class HabitsTrackerPresenter }) this.retrieveHabitsTrackerUseCase = retrieveHabitsTrackerUseCase this.habitCreateUseCase = habitCreateUseCase + this.habitEditUseCase = habitEditUseCase } public async habitCreate(data: unknown): Promise { @@ -110,10 +118,10 @@ export class HabitsTrackerPresenter global: null, } }) - const habit = await this.habitCreateUseCase.execute(data) + const habit = await this.habitEditUseCase.execute(data) this.setState((state) => { state.habitEdit.state = "success" - state.habitsTracker.addHabit(habit) + state.habitsTracker.editHabit(habit) }) return "success" } catch (error) { @@ -121,7 +129,7 @@ export class HabitsTrackerPresenter state.habitEdit.state = "error" if (error instanceof ZodError) { state.habitEdit.errors.fields = - getErrorsFieldsFromZodError(error) + getErrorsFieldsFromZodError(error) } else { state.habitEdit.errors.global = "unknown" } diff --git a/presentation/react/components/HabitEditForm/HabitEditForm.tsx b/presentation/react/components/HabitEditForm/HabitEditForm.tsx index b26d6e2..be69d7f 100644 --- a/presentation/react/components/HabitEditForm/HabitEditForm.tsx +++ b/presentation/react/components/HabitEditForm/HabitEditForm.tsx @@ -12,36 +12,28 @@ import ColorPicker, { import type { Habit, HabitEditData } from "@/domain/entities/Habit" import { HabitEditSchema } from "@/domain/entities/Habit" -import type { User } from "@/domain/entities/User" import { useHabitsTracker } from "../../contexts/HabitsTracker" export interface HabitEditFormProps { - user: User habit: Habit } -export const HabitEditForm: React.FC = ({ user }) => { +export const HabitEditForm: React.FC = ({ habit }) => { const { habitEdit, habitsTrackerPresenter } = useHabitsTracker() const { control, handleSubmit, - reset, formState: { errors }, } = useForm({ mode: "onChange", resolver: zodResolver(HabitEditSchema), defaultValues: { - userId: user.id, - name: "", - color: "#006CFF", - icon: "lightbulb", - goal: { - frequency: "daily", - target: { - type: "boolean", - }, - }, + id: habit.id, + userId: habit.userId, + name: habit.name, + color: habit.color, + icon: habit.icon, }, }) @@ -54,7 +46,6 @@ export const HabitEditForm: React.FC = ({ user }) => { const onSubmit = async (data: HabitEditData): Promise => { await habitsTrackerPresenter.habitEdit(data) setIsVisibleSnackbar(true) - reset() } return ( @@ -141,7 +132,7 @@ export const HabitEditForm: React.FC = ({ user }) => { disabled={habitEdit.state === "loading"} style={[styles.spacing, { width: "90%" }]} > - Create your habit! 🚀 + Save @@ -150,7 +141,7 @@ export const HabitEditForm: React.FC = ({ user }) => { onDismiss={onDismissSnackbar} duration={2_000} > - ✅ Habit created successfully! + ✅ Habit Saved successfully! )