From f500bd60958f1ba9ec15d5874caba79ceabed1df Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Mon, 10 Aug 2026 22:36:14 +0300 Subject: [PATCH] snapshot schedule source data --- .../Http/Controllers/ScheduleController.php | 35 ++++--------------- .../Schedule/CreateScheduleAssignmentDto.php | 8 +++-- backend/app/Schedule/CreateScheduleDto.php | 3 +- .../Schedule/EloquentScheduleRepository.php | 32 +++++------------ backend/app/Schedule/Schedule.php | 7 ++-- backend/app/Schedule/ScheduleAssignment.php | 25 ++++++++++--- .../app/Schedule/ScheduleAssignmentModel.php | 10 ++++-- backend/app/Schedule/ScheduleModel.php | 5 ++- .../CreateSchedule/CreateSchedule.php | 22 ++++++++++-- ...26_08_10_000000_create_schedules_table.php | 4 +-- ...0001_create_schedule_assignments_table.php | 7 ++-- .../website/cypress/e2e/set-scheduling.cy.ts | 8 ++--- frontend/website/src/stores/schedules.ts | 3 +- .../website/src/views/ScheduleDetailView.vue | 2 +- 14 files changed, 84 insertions(+), 87 deletions(-) diff --git a/backend/app/Http/Controllers/ScheduleController.php b/backend/app/Http/Controllers/ScheduleController.php index 3ff4a39..fb0bb40 100644 --- a/backend/app/Http/Controllers/ScheduleController.php +++ b/backend/app/Http/Controllers/ScheduleController.php @@ -2,7 +2,6 @@ namespace App\Http\Controllers; -use App\Element\Element; use App\Exceptions\BadRequestException; use App\Exceptions\NotFoundException; use App\Schedule\Schedule; @@ -96,13 +95,10 @@ class ScheduleController extends Controller */ private function summaryPayload(Schedule $schedule): array { - $set = $schedule->getSet(); - return [ 'id' => $schedule->getId(), 'set' => [ - 'id' => $set->getId(), - 'name' => $set->getName(), + 'name' => $schedule->getSetName(), ], 'elementKind' => $schedule->getElementKind(), 'startDate' => $schedule->getStartDate()->format('Y-m-d'), @@ -150,8 +146,7 @@ class ScheduleController extends Controller } /** - * @return array{element: array{ - * id: int, + * @return array{id: int, element: array{ * name: string, * kind: string, * path: list @@ -160,34 +155,16 @@ class ScheduleController extends Controller private function assignmentPayload( ScheduleAssignment $assignment, ): array { - $element = $assignment->getElement(); - return [ + 'id' => $assignment->getId(), 'element' => [ - 'id' => $element->getId(), - 'name' => $element->getName(), - 'kind' => $element->getKind(), - 'path' => $this->elementPath($element), + 'name' => $assignment->getName(), + 'kind' => $assignment->getKind(), + 'path' => $assignment->getPath(), ], ]; } - /** - * @return list - */ - private function elementPath(Element $element): array - { - $path = []; - $currentElement = $element; - - while ($currentElement !== null) { - array_unshift($path, $currentElement->getName()); - $currentElement = $currentElement->getParentElement(); - } - - return $path; - } - private function user(Request $request): User { /** @var User $user */ diff --git a/backend/app/Schedule/CreateScheduleAssignmentDto.php b/backend/app/Schedule/CreateScheduleAssignmentDto.php index 682438c..6acf329 100644 --- a/backend/app/Schedule/CreateScheduleAssignmentDto.php +++ b/backend/app/Schedule/CreateScheduleAssignmentDto.php @@ -2,13 +2,17 @@ namespace App\Schedule; -use App\Element\Element; use DateTimeImmutable; final readonly class CreateScheduleAssignmentDto { + /** + * @param list $path + */ public function __construct( - public Element $element, + public string $name, + public string $kind, + public array $path, public DateTimeImmutable $scheduledDate, public int $position, ) {} diff --git a/backend/app/Schedule/CreateScheduleDto.php b/backend/app/Schedule/CreateScheduleDto.php index 4683bc6..5c5ffdc 100644 --- a/backend/app/Schedule/CreateScheduleDto.php +++ b/backend/app/Schedule/CreateScheduleDto.php @@ -2,7 +2,6 @@ namespace App\Schedule; -use App\Set\Set; use App\User\User; use DateTimeImmutable; @@ -13,7 +12,7 @@ final readonly class CreateScheduleDto */ public function __construct( public User $user, - public Set $set, + public string $setName, public string $elementKind, public DateTimeImmutable $startDate, public DateTimeImmutable $targetDate, diff --git a/backend/app/Schedule/EloquentScheduleRepository.php b/backend/app/Schedule/EloquentScheduleRepository.php index f23cb30..31bb859 100644 --- a/backend/app/Schedule/EloquentScheduleRepository.php +++ b/backend/app/Schedule/EloquentScheduleRepository.php @@ -2,27 +2,19 @@ namespace App\Schedule; -use App\Element\ElementRepository; -use App\Set\SetRepository; use App\User\User; use DateTimeImmutable; use DateTimeZone; use Illuminate\Support\Facades\DB; -use RuntimeException; class EloquentScheduleRepository implements ScheduleRepository { - public function __construct( - private SetRepository $setRepository, - private ElementRepository $elementRepository, - ) {} - public function create(CreateScheduleDto $dto): Schedule { return DB::transaction(function () use ($dto): Schedule { $model = ScheduleModel::create([ 'user_id' => $dto->user->getId(), - 'set_id' => $dto->set->getId(), + 'set_name' => $dto->setName, 'element_kind' => $dto->elementKind, 'start_date' => $dto->startDate->format('Y-m-d'), 'target_date' => $dto->targetDate->format('Y-m-d'), @@ -31,7 +23,9 @@ class EloquentScheduleRepository implements ScheduleRepository foreach ($dto->assignments as $assignmentDto) { ScheduleAssignmentModel::create([ 'schedule_id' => $model->id, - 'element_id' => $assignmentDto->element->getId(), + 'element_name' => $assignmentDto->name, + 'element_kind' => $assignmentDto->kind, + 'element_path' => $assignmentDto->path, 'scheduled_date' => $assignmentDto->scheduledDate ->format('Y-m-d'), 'position' => $assignmentDto->position, @@ -69,11 +63,6 @@ class EloquentScheduleRepository implements ScheduleRepository private function toDomain(ScheduleModel $model, User $user): Schedule { - $set = $this->setRepository->find($model->set_id); - if ($set === null) { - throw new RuntimeException('schedule set not found'); - } - $assignmentModels = ScheduleAssignmentModel::query() ->where('schedule_id', $model->id) ->orderBy('position') @@ -82,16 +71,11 @@ class EloquentScheduleRepository implements ScheduleRepository $assignments = []; foreach ($assignmentModels as $assignmentModel) { - $element = $this->elementRepository->find( - $assignmentModel->element_id, - ); - if ($element === null) { - throw new RuntimeException('schedule element not found'); - } - $assignments[] = new ScheduleAssignment( id: $assignmentModel->id, - element: $element, + name: $assignmentModel->element_name, + kind: $assignmentModel->element_kind, + path: $assignmentModel->element_path, scheduledDate: $this->date($assignmentModel->scheduled_date), position: $assignmentModel->position, ); @@ -100,7 +84,7 @@ class EloquentScheduleRepository implements ScheduleRepository return new Schedule( id: $model->id, user: $user, - set: $set, + setName: $model->set_name, elementKind: $model->element_kind, startDate: $this->date($model->start_date), targetDate: $this->date($model->target_date), diff --git a/backend/app/Schedule/Schedule.php b/backend/app/Schedule/Schedule.php index ecb3cfa..0a14ce8 100644 --- a/backend/app/Schedule/Schedule.php +++ b/backend/app/Schedule/Schedule.php @@ -2,7 +2,6 @@ namespace App\Schedule; -use App\Set\Set; use App\User\User; use DateTimeImmutable; @@ -14,7 +13,7 @@ final readonly class Schedule public function __construct( private int $id, private User $user, - private Set $set, + private string $setName, private string $elementKind, private DateTimeImmutable $startDate, private DateTimeImmutable $targetDate, @@ -31,9 +30,9 @@ final readonly class Schedule return $this->user; } - public function getSet(): Set + public function getSetName(): string { - return $this->set; + return $this->setName; } public function getElementKind(): string diff --git a/backend/app/Schedule/ScheduleAssignment.php b/backend/app/Schedule/ScheduleAssignment.php index 97c67a3..482b45d 100644 --- a/backend/app/Schedule/ScheduleAssignment.php +++ b/backend/app/Schedule/ScheduleAssignment.php @@ -2,14 +2,18 @@ namespace App\Schedule; -use App\Element\Element; use DateTimeImmutable; final readonly class ScheduleAssignment { + /** + * @param list $path + */ public function __construct( private int $id, - private Element $element, + private string $name, + private string $kind, + private array $path, private DateTimeImmutable $scheduledDate, private int $position, ) {} @@ -19,9 +23,22 @@ final readonly class ScheduleAssignment return $this->id; } - public function getElement(): Element + public function getName(): string { - return $this->element; + return $this->name; + } + + public function getKind(): string + { + return $this->kind; + } + + /** + * @return list + */ + public function getPath(): array + { + return $this->path; } public function getScheduledDate(): DateTimeImmutable diff --git a/backend/app/Schedule/ScheduleAssignmentModel.php b/backend/app/Schedule/ScheduleAssignmentModel.php index 782b906..f0f33df 100644 --- a/backend/app/Schedule/ScheduleAssignmentModel.php +++ b/backend/app/Schedule/ScheduleAssignmentModel.php @@ -9,7 +9,9 @@ use Illuminate\Database\Eloquent\Model; /** * @property int $id * @property int $schedule_id - * @property int $element_id + * @property string $element_name + * @property string $element_kind + * @property list $element_path * @property string $scheduled_date * @property int $position * @@ -21,7 +23,9 @@ use Illuminate\Database\Eloquent\Model; */ #[Fillable([ 'schedule_id', - 'element_id', + 'element_name', + 'element_kind', + 'element_path', 'scheduled_date', 'position', ])] @@ -38,7 +42,7 @@ class ScheduleAssignmentModel extends Model { return [ 'schedule_id' => 'integer', - 'element_id' => 'integer', + 'element_path' => 'array', 'position' => 'integer', ]; } diff --git a/backend/app/Schedule/ScheduleModel.php b/backend/app/Schedule/ScheduleModel.php index 6d0132d..f19fb97 100644 --- a/backend/app/Schedule/ScheduleModel.php +++ b/backend/app/Schedule/ScheduleModel.php @@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Model; /** * @property int $id * @property int $user_id - * @property int $set_id + * @property string $set_name * @property string $element_kind * @property string $start_date * @property string $target_date @@ -22,7 +22,7 @@ use Illuminate\Database\Eloquent\Model; */ #[Fillable([ 'user_id', - 'set_id', + 'set_name', 'element_kind', 'start_date', 'target_date', @@ -40,7 +40,6 @@ class ScheduleModel extends Model { return [ 'user_id' => 'integer', - 'set_id' => 'integer', ]; } } diff --git a/backend/app/Schedule/UseCases/CreateSchedule/CreateSchedule.php b/backend/app/Schedule/UseCases/CreateSchedule/CreateSchedule.php index c41b19c..1759d73 100644 --- a/backend/app/Schedule/UseCases/CreateSchedule/CreateSchedule.php +++ b/backend/app/Schedule/UseCases/CreateSchedule/CreateSchedule.php @@ -70,7 +70,7 @@ class CreateSchedule return $this->scheduleRepository->create(new CreateScheduleDto( user: $request->user, - set: $set, + setName: $set->getName(), elementKind: $request->elementKind, startDate: $startDate, targetDate: $targetDate, @@ -153,7 +153,9 @@ class CreateSchedule dayCount: $dayCount, ); $assignments[] = new CreateScheduleAssignmentDto( - element: $element, + name: $element->getName(), + kind: $element->getKind(), + path: $this->elementPath($element), scheduledDate: $startDate->modify("+{$dayIndex} days"), position: $index + 1, ); @@ -162,6 +164,22 @@ class CreateSchedule return $assignments; } + /** + * @return list + */ + private function elementPath(Element $element): array + { + $path = []; + $currentElement = $element; + + while ($currentElement !== null) { + array_unshift($path, $currentElement->getName()); + $currentElement = $currentElement->getParentElement(); + } + + return $path; + } + private function dayIndex( int $elementIndex, int $elementCount, diff --git a/backend/database/migrations/2026_08_10_000000_create_schedules_table.php b/backend/database/migrations/2026_08_10_000000_create_schedules_table.php index cfe3028..390b246 100644 --- a/backend/database/migrations/2026_08_10_000000_create_schedules_table.php +++ b/backend/database/migrations/2026_08_10_000000_create_schedules_table.php @@ -13,9 +13,7 @@ return new class extends Migration $table->foreignId('user_id') ->constrained('users') ->restrictOnDelete(); - $table->foreignId('set_id') - ->constrained('sets') - ->restrictOnDelete(); + $table->string('set_name'); $table->string('element_kind'); $table->date('start_date'); $table->date('target_date'); diff --git a/backend/database/migrations/2026_08_10_000001_create_schedule_assignments_table.php b/backend/database/migrations/2026_08_10_000001_create_schedule_assignments_table.php index 88c05b3..b52c60a 100644 --- a/backend/database/migrations/2026_08_10_000001_create_schedule_assignments_table.php +++ b/backend/database/migrations/2026_08_10_000001_create_schedule_assignments_table.php @@ -15,12 +15,11 @@ return new class extends Migration $table->foreignId('schedule_id') ->constrained('schedules') ->cascadeOnDelete(); - $table->foreignId('element_id') - ->constrained('elements') - ->restrictOnDelete(); + $table->string('element_name'); + $table->string('element_kind'); + $table->json('element_path'); $table->date('scheduled_date'); $table->unsignedInteger('position'); - $table->unique(['schedule_id', 'element_id']); $table->unique(['schedule_id', 'position']); $table->index([ 'schedule_id', diff --git a/frontend/website/cypress/e2e/set-scheduling.cy.ts b/frontend/website/cypress/e2e/set-scheduling.cy.ts index 824024d..6ebe605 100644 --- a/frontend/website/cypress/e2e/set-scheduling.cy.ts +++ b/frontend/website/cypress/e2e/set-scheduling.cy.ts @@ -45,7 +45,7 @@ const bibleLayout = { const scheduleDetail = { schedule: { id: 73, - set: { id: 41, name: 'Bible' }, + set: { name: 'Bible' }, elementKind: 'chapter', startDate: '2026-08-10', targetDate: '2026-08-12', @@ -55,8 +55,8 @@ const scheduleDetail = { date: '2026-08-10', assignments: [ { + id: 1, element: { - id: 4, name: 'Chapter 1', kind: 'chapter', path: ['Genesis', 'Creation', 'Chapter 1'], @@ -69,8 +69,8 @@ const scheduleDetail = { date: '2026-08-12', assignments: [ { + id: 2, element: { - id: 5, name: 'Chapter 1', kind: 'chapter', path: ['Exodus', 'Chapter 1'], @@ -213,7 +213,7 @@ describe('set scheduling', () => { schedules: [ { id: 73, - set: { id: 41, name: 'Bible' }, + set: { name: 'Bible' }, elementKind: 'chapter', startDate: '2026-08-10', targetDate: '2026-08-12', diff --git a/frontend/website/src/stores/schedules.ts b/frontend/website/src/stores/schedules.ts index 79f27a1..d5b46a2 100644 --- a/frontend/website/src/stores/schedules.ts +++ b/frontend/website/src/stores/schedules.ts @@ -9,7 +9,6 @@ const isoDateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/) export const scheduleSummarySchema = z.object({ id: z.number().int().positive(), set: z.object({ - id: z.number().int().positive(), name: z.string().min(1), }), elementKind: z.string().min(1), @@ -19,8 +18,8 @@ export const scheduleSummarySchema = z.object({ }) const scheduleAssignmentSchema = z.object({ + id: z.number().int().positive(), element: z.object({ - id: z.number().int().positive(), name: z.string().min(1), kind: z.string().min(1), path: z.array(z.string().min(1)).min(1), diff --git a/frontend/website/src/views/ScheduleDetailView.vue b/frontend/website/src/views/ScheduleDetailView.vue index 0471be6..fe430b4 100644 --- a/frontend/website/src/views/ScheduleDetailView.vue +++ b/frontend/website/src/views/ScheduleDetailView.vue @@ -117,7 +117,7 @@ function pluralKind(kind: string, count: number): string {

Rest day

    -
  • +
  • {{ assignment.element.path.join(' / ') }} {{ humanizeKind(assignment.element.kind) }}