feat: edit habit working version

This commit is contained in:
Théo LUDWIG 2024-04-11 13:07:17 +02:00
parent 3fa3681c9b
commit 246cbe918a
Signed by: theoludwig
GPG Key ID: ADFE5A563D718F3B
9 changed files with 59 additions and 72 deletions

View File

@ -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 <Redirect href="/application/habits/" />
}
return (
<SafeAreaView
style={[
{
flex: 1,
alignItems: "center",
},
]}
>
<Text>
Habit Page {habitId} {habitHistory.habit.name}
</Text>
<HabitEditForm user={user} habit={habitHistory.habit} />
</SafeAreaView>
<HabitEditForm habit={habitHistory.habit} key={habitHistory.habit.id} />
)
}

View File

@ -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
}

View File

@ -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<typeof GoalCreateSchema>
export type GoalEditData = z.infer<typeof GoalEditSchema>
interface GoalBase {
frequency: GoalFrequency
}

View File

@ -15,9 +15,7 @@ export const HabitCreateSchema = HabitSchema.extend({
}).omit({ id: true })
export type HabitCreateData = z.infer<typeof HabitCreateSchema>
export const HabitEditSchema = HabitSchema.extend({
goal: GoalCreateSchema,
})
export const HabitEditSchema = HabitSchema.extend({})
export type HabitEditData = z.infer<typeof HabitEditSchema>
type HabitBase = z.infer<typeof HabitSchema>

View File

@ -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,

View File

@ -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,
})

View File

@ -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),
})

View File

@ -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<keyof HabitCreateData>
fields: Array<keyof HabitEditData>
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<FetchState> {
@ -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<HabitCreateData>(error)
getErrorsFieldsFromZodError<HabitEditData>(error)
} else {
state.habitEdit.errors.global = "unknown"
}

View File

@ -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<HabitEditFormProps> = ({ user }) => {
export const HabitEditForm: React.FC<HabitEditFormProps> = ({ habit }) => {
const { habitEdit, habitsTrackerPresenter } = useHabitsTracker()
const {
control,
handleSubmit,
reset,
formState: { errors },
} = useForm<HabitEditData>({
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<HabitEditFormProps> = ({ user }) => {
const onSubmit = async (data: HabitEditData): Promise<void> => {
await habitsTrackerPresenter.habitEdit(data)
setIsVisibleSnackbar(true)
reset()
}
return (
@ -141,7 +132,7 @@ export const HabitEditForm: React.FC<HabitEditFormProps> = ({ user }) => {
disabled={habitEdit.state === "loading"}
style={[styles.spacing, { width: "90%" }]}
>
Create your habit! 🚀
Save
</Button>
</ScrollView>
@ -150,7 +141,7 @@ export const HabitEditForm: React.FC<HabitEditFormProps> = ({ user }) => {
onDismiss={onDismissSnackbar}
duration={2_000}
>
Habit created successfully!
Habit Saved successfully!
</Snackbar>
</SafeAreaView>
)