Skip to content
Merged
50 changes: 0 additions & 50 deletions .agents/specs/823-pr-826-review-follow-up.md

This file was deleted.

68 changes: 0 additions & 68 deletions .agents/specs/835-collection-ui-removal.md

This file was deleted.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## AI
docs/superpowers
.agents/specs/*
!.agents/specs/README.md
.agents/plans/

# MACOS
.DS_Store
Expand Down
69 changes: 69 additions & 0 deletions Application/Domain/Sources/DomainAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public final class DomainAssembler: Assembler {
registerAuthUseCases(container)
registerConnectivityUseCases(container)
registerAuthProviderUseCases(container)
registerDevelopmentGoalUseCases(container)
registerDevelopmentRecordUseCases(container)
registerTodoUseCases(container)
registerTodoCategoryUseCases(container)
registerUserDataUseCases(container)
Expand All @@ -26,6 +28,66 @@ public final class DomainAssembler: Assembler {
}

private extension DomainAssembler {
func registerDevelopmentGoalUseCases(_ container: any DIContainer) {
container.register(CreateDevelopmentGoalUseCase.self) {
CreateDevelopmentGoalUseCaseImpl(container.resolve(DevelopmentGoalRepository.self))
}

container.register(FetchDevelopmentGoalUseCase.self) {
FetchDevelopmentGoalUseCaseImpl(container.resolve(DevelopmentGoalRepository.self))
}

container.register(FetchDevelopmentGoalsUseCase.self) {
FetchDevelopmentGoalsUseCaseImpl(container.resolve(DevelopmentGoalRepository.self))
}

container.register(UpdateDevelopmentGoalStatusUseCase.self) {
UpdateDevelopmentGoalStatusUseCaseImpl(
container.resolve(DevelopmentGoalRepository.self)
)
}
}

func registerDevelopmentRecordUseCases(_ container: any DIContainer) {
container.register(CreateDevelopmentRecordUseCase.self) {
CreateDevelopmentRecordUseCaseImpl(
container.resolve(DevelopmentRecordRepository.self),
container.resolve(DevelopmentGoalRepository.self)
Comment thread
opficdev marked this conversation as resolved.
)
}

container.register(FetchDevelopmentRecordsUseCase.self) {
FetchDevelopmentRecordsUseCaseImpl(container.resolve(DevelopmentRecordRepository.self))
}

container.register(FetchDevelopmentRecordHistoryUseCase.self) {
FetchDevelopmentRecordHistoryUseCaseImpl(
container.resolve(DevelopmentRecordRepository.self)
)
}

container.register(SaveDevelopmentRecordDraftUseCase.self) {
SaveDevelopmentRecordDraftUseCaseImpl(
container.resolve(DevelopmentRecordRepository.self),
container.resolve(DevelopmentGoalRepository.self)
)
}

container.register(ConfirmDevelopmentRecordUseCase.self) {
ConfirmDevelopmentRecordUseCaseImpl(
container.resolve(DevelopmentRecordRepository.self),
container.resolve(DevelopmentGoalRepository.self)
)
}

container.register(RestoreDevelopmentRecordUseCase.self) {
RestoreDevelopmentRecordUseCaseImpl(
container.resolve(DevelopmentRecordRepository.self),
container.resolve(DevelopmentGoalRepository.self)
)
}
}

func registerAppUpdateUseCases(_ container: any DIContainer) {
container.register(CheckAppUpdateUseCase.self) {
CheckAppUpdateUseCaseImpl(container.resolve(AppVersionRepository.self))
Expand Down Expand Up @@ -102,6 +164,13 @@ private extension DomainAssembler {
container.register(UndoDeleteTodoUseCase.self) {
UndoDeleteTodoUseCaseImpl(container.resolve(TodoRepository.self))
}

container.register(UpdateTodoGoalUseCase.self) {
UpdateTodoGoalUseCaseImpl(
container.resolve(TodoRepository.self),
container.resolve(DevelopmentGoalRepository.self)
)
}
}

func registerTodoCategoryUseCases(_ container: any DIContainer) {
Expand Down
87 changes: 87 additions & 0 deletions Application/Domain/Sources/Entity/DevelopmentGoal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// DevelopmentGoal.swift
// Domain
//
// Created by opfic on 8/27/26.
//

import Foundation

public struct DevelopmentGoal: Hashable {
public enum Status: Hashable {
case inProgress
case completed
case archived
}

public struct Query: Hashable {
public let status: Status?

public init(status: Status? = nil) {
self.status = status
}
}

public struct CompletionSnapshot: Hashable {
public let goal: DevelopmentGoal
public let records: [DevelopmentRecord]

public init(goal: DevelopmentGoal, records: [DevelopmentRecord]) {
self.goal = goal
self.records = records
}
}

public let id: String
public let title: String
public let description: String
public let status: Status
public let createdAt: Date
public let updatedAt: Date
public let completedAt: Date?

public init(
id: String,
title: String,
description: String,
status: Status,
createdAt: Date,
updatedAt: Date,
completedAt: Date?
) throws {
guard title.containsMeaningfulCharacter else {
throw DomainLayerError.invalidDevelopmentGoalTitle
}

self.id = id
self.title = title
self.description = description
self.status = status
self.createdAt = createdAt
self.updatedAt = updatedAt
self.completedAt = completedAt
}

public func validateTransition(to status: Status) throws {
let isAllowed: Bool
switch (self.status, status) {
case (.inProgress, .completed),
(.inProgress, .archived),
(.completed, .inProgress),
(.archived, .inProgress):
isAllowed = true
default:
isAllowed = false
}

guard isAllowed else {
throw DomainLayerError.invalidDevelopmentGoalTransition
}
}
}

private extension String {
var containsMeaningfulCharacter: Bool {
!trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
}
Loading
Loading