1
1
mirror of https://github.com/theoludwig/p61-project.git synced 2024-07-17 07:00:12 +02:00
p61-project/domain/entities/Goal.ts

211 lines
4.9 KiB
TypeScript
Raw Normal View History

2024-04-04 12:31:56 +02:00
import { z } from "zod"
export const GOAL_FREQUENCIES_ZOD = [
z.literal("daily"),
z.literal("weekly"),
z.literal("monthly"),
] as const
export const goalFrequencyZod = z.union(GOAL_FREQUENCIES_ZOD)
export const GOAL_FREQUENCIES = GOAL_FREQUENCIES_ZOD.map((frequency) => {
return frequency.value
})
2024-03-15 22:48:28 +01:00
export type GoalFrequency = (typeof GOAL_FREQUENCIES)[number]
2024-05-22 13:44:54 +02:00
export const GOAL_FREQUENCIES_TYPES = {
daily: "day",
weekly: "week",
monthly: "month",
} as const
2024-04-04 12:31:56 +02:00
export const GOAL_TYPES_ZOD = [
z.literal("boolean"),
2024-04-05 00:08:40 +02:00
z.literal("numeric"),
2024-04-04 12:31:56 +02:00
] as const
export const goalTypeZod = z.union(GOAL_TYPES_ZOD)
export const GOAL_TYPES = GOAL_TYPES_ZOD.map((type) => {
return type.value
})
2024-03-15 22:48:28 +01:00
export type GoalType = (typeof GOAL_TYPES)[number]
2024-04-04 12:31:56 +02:00
export const GoalCreateSchema = 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(1),
2024-04-04 12:31:56 +02:00
unit: z.string().min(1),
}),
]),
})
export type GoalCreateData = z.infer<typeof GoalCreateSchema>
2024-03-15 22:48:28 +01:00
interface GoalBase {
frequency: GoalFrequency
}
2024-03-22 10:23:28 +01:00
export interface GoalBaseJSON extends GoalBase {
type: GoalType
}
2024-03-15 22:48:28 +01:00
export abstract class Goal implements GoalBase {
public frequency: GoalBase["frequency"]
public abstract readonly type: GoalType
public constructor(options: GoalBase) {
const { frequency } = options
this.frequency = frequency
}
2024-04-05 00:08:40 +02:00
public static create(options: GoalCreateData): Goal {
if (options.target.type === "boolean") {
return new GoalBoolean(options)
}
return new GoalNumeric({
frequency: options.frequency,
target: options.target,
})
}
2024-03-15 22:48:28 +01:00
public static isNumeric(goal: Goal): goal is GoalNumeric {
return goal.type === "numeric"
}
public isNumeric(): this is GoalNumeric {
return Goal.isNumeric(this)
}
public static isBoolean(goal: Goal): goal is GoalBoolean {
return goal.type === "boolean"
}
public isBoolean(): this is GoalBoolean {
return Goal.isBoolean(this)
}
2024-03-22 10:23:28 +01:00
public abstract toJSON(): GoalBaseJSON
2024-03-15 22:48:28 +01:00
}
2024-03-22 10:23:28 +01:00
export interface GoalProgressBase {
2024-03-15 22:48:28 +01:00
goal: Goal
}
export abstract class GoalProgress implements GoalProgressBase {
public abstract readonly goal: Goal
public abstract isCompleted(): boolean
2024-03-22 10:23:28 +01:00
public abstract toJSON(): GoalProgressBase
public static isNumeric(
goalProgress: GoalProgress,
): goalProgress is GoalNumericProgress {
return goalProgress.goal.isNumeric()
}
public isNumeric(): this is GoalNumericProgress {
return GoalProgress.isNumeric(this)
}
public static isBoolean(
goalProgress: GoalProgress,
): goalProgress is GoalBooleanProgress {
return goalProgress.goal.isBoolean()
}
public isBoolean(): this is GoalBooleanProgress {
return GoalProgress.isBoolean(this)
}
2024-03-15 22:48:28 +01:00
}
interface GoalNumericOptions extends GoalBase {
target: {
value: number
unit: string
}
}
export class GoalNumeric extends Goal {
public readonly type = "numeric"
public target: {
value: number
unit: string
}
public constructor(options: GoalNumericOptions) {
super(options)
const { target } = options
this.target = target
}
2024-03-22 10:23:28 +01:00
public override toJSON(): GoalNumericOptions & GoalBaseJSON {
return {
frequency: this.frequency,
target: this.target,
type: this.type,
}
}
2024-03-15 22:48:28 +01:00
}
interface GoalNumericProgressOptions extends GoalProgressBase {
goal: GoalNumeric
progress: number
}
export class GoalNumericProgress extends GoalProgress {
public readonly goal: GoalNumeric
public readonly progress: number
public constructor(options: GoalNumericProgressOptions) {
const { goal, progress } = options
super()
this.goal = goal
this.progress = progress
}
public override isCompleted(): boolean {
return this.progress >= this.goal.target.value
}
public progressRatio(): number {
return this.goal.target.value <= 0
? 0
: this.progress / this.goal.target.value
}
2024-03-22 10:23:28 +01:00
public override toJSON(): GoalNumericProgressOptions {
return {
goal: this.goal,
progress: this.progress,
}
}
2024-03-15 22:48:28 +01:00
}
export class GoalBoolean extends Goal {
public readonly type = "boolean"
2024-03-22 10:23:28 +01:00
public override toJSON(): GoalBaseJSON {
return {
frequency: this.frequency,
type: this.type,
}
}
2024-03-15 22:48:28 +01:00
}
interface GoalBooleanProgressOptions extends GoalProgressBase {
goal: GoalBoolean
progress: boolean
}
export class GoalBooleanProgress extends GoalProgress {
public readonly goal: GoalBoolean
public progress: boolean
public constructor(options: GoalBooleanProgressOptions) {
const { goal, progress } = options
super()
this.goal = goal
this.progress = progress
}
public override isCompleted(): boolean {
return this.progress
}
2024-03-22 10:23:28 +01:00
public override toJSON(): GoalBooleanProgressOptions {
return {
goal: this.goal,
progress: this.progress,
}
}
2024-03-15 22:48:28 +01:00
}