diff --git a/app/Plan/JsonPlanRepository.php b/app/Plan/JsonPlanRepository.php deleted file mode 100644 index 8a88838..0000000 --- a/app/Plan/JsonPlanRepository.php +++ /dev/null @@ -1,93 +0,0 @@ -filePath = __DIR__ . '/../../data/plans.json'; - } - - public function create(CreatePlanDto $dto): Plan - { - $plans = $this->readPlans(); - $id = $this->getNextId($plans); - - $plans[] = [ - 'id' => $id, - 'name' => $dto->name, - 'userId' => $dto->user->getId(), - ]; - $this->writePlans($plans); - - return new Plan( - id: $id, - name: $dto->name, - user: $dto->user, - ); - } - - public function find(int $id): ?Plan - { - $plans = $this->readPlans(); - - foreach ($plans as $data) { - if ($data['id'] === $id) { - $user = $this->userRepository->find($data['userId']); - if ($user === null) { - return null; - } - return new Plan( - id: $data['id'], - name: $data['name'], - user: $user, - ); - } - } - - return null; - } - - private function readPlans(): array - { - if (!file_exists($this->filePath)) { - return []; - } - - $content = file_get_contents($this->filePath); - - return json_decode($content, true) ?? []; - } - - private function writePlans(array $plans): void - { - file_put_contents( - $this->filePath, - json_encode($plans, JSON_PRETTY_PRINT) - ); - } - - private function getNextId(array $plans): int - { - if (empty($plans)) { - return 0; - } - - $maxId = -1; - foreach ($plans as $plan) { - if ($plan['id'] > $maxId) { - $maxId = $plan['id']; - } - } - - return $maxId + 1; - } -} diff --git a/app/Plan/PlanController.php b/app/Plan/PlanController.php deleted file mode 100644 index cb88de8..0000000 --- a/app/Plan/PlanController.php +++ /dev/null @@ -1,56 +0,0 @@ -getBody(), true) ?? []; - - $userId = isset($data['userId']) ? (int) $data['userId'] : null; - $textId = isset($data['textId']) ? (int) $data['textId'] : null; - $name = $data['name'] ?? null; - $dateStart = $data['dateStart'] ?? null; - $dateEnd = $data['dateEnd'] ?? null; - - try { - $plan = $createPlanUseCase->execute(new CreatePlanRequest( - userId: $userId, - textId: $textId, - name: $name, - dateStart: $dateStart, - dateEnd: $dateEnd, - )); - } catch (BadRequestException $exception) { - $response->getBody()->write( - json_encode(['error' => $exception->getMessage()]) - ); - return $response->withStatus(400) - ->withHeader('Content-Type', 'application/json'); - } catch (DomainException $exception) { - $response->getBody()->write( - json_encode(['error' => $exception->getMessage()]) - ); - return $response->withStatus(404) - ->withHeader('Content-Type', 'application/json'); - } - - $response->getBody()->write(json_encode([ - 'id' => $plan->getId(), - 'name' => $plan->getName(), - ])); - return $response->withStatus(201) - ->withHeader('Content-Type', 'application/json'); - } -} diff --git a/app/ScheduledNode/JsonScheduledNodeRepository.php b/app/ScheduledNode/JsonScheduledNodeRepository.php deleted file mode 100644 index bee31ea..0000000 --- a/app/ScheduledNode/JsonScheduledNodeRepository.php +++ /dev/null @@ -1,67 +0,0 @@ -filePath = __DIR__ . '/../../data/scheduledNodes.json'; - } - - public function create(CreateScheduledNodeDto $dto): ScheduledNode - { - $scheduledNodes = $this->readScheduledNodes(); - $id = $this->getNextId($scheduledNodes); - - $scheduledNodes[] = [ - 'id' => $id, - 'date' => $dto->date->format('Y-m-d'), - 'planId' => $dto->plan->getId(), - ]; - $this->writeScheduledNodes($scheduledNodes); - - return new ScheduledNode( - id: $id, - date: $dto->date, - plan: $dto->plan, - ); - } - - private function readScheduledNodes(): array - { - if (!file_exists($this->filePath)) { - return []; - } - - $content = file_get_contents($this->filePath); - - return json_decode($content, true) ?? []; - } - - private function writeScheduledNodes(array $scheduledNodes): void - { - file_put_contents( - $this->filePath, - json_encode($scheduledNodes, JSON_PRETTY_PRINT) - ); - } - - private function getNextId(array $scheduledNodes): int - { - if (empty($scheduledNodes)) { - return 0; - } - - $maxId = -1; - foreach ($scheduledNodes as $scheduledNode) { - if ($scheduledNode['id'] > $maxId) { - $maxId = $scheduledNode['id']; - } - } - - return $maxId + 1; - } -} diff --git a/app/User/JsonUserRepository.php b/app/User/JsonUserRepository.php deleted file mode 100644 index bde8c62..0000000 --- a/app/User/JsonUserRepository.php +++ /dev/null @@ -1,84 +0,0 @@ -filePath = __DIR__ . '/../../data/users.json'; - } - - public function create(CreateUserDto $dto): User - { - $users = $this->readUsers(); - $id = $this->getNextId($users); - - $users[] = [ - 'id' => $id, - 'email' => (string) $dto->email, - ]; - $this->writeUsers($users); - - return new User( - id: $id, - email: $dto->email, - ); - } - - public function find(int $id): ?User - { - $users = $this->readUsers(); - - foreach ($users as $data) { - if ($data['id'] === $id) { - return new User( - id: $data['id'], - email: new EmailAddress($data['email']), - ); - } - } - - return null; - } - - private function readUsers(): array - { - if (!file_exists($this->filePath)) { - return []; - } - - $content = file_get_contents($this->filePath); - - return json_decode($content, true) ?? []; - } - - private function writeUsers(array $users): void - { - file_put_contents( - $this->filePath, - json_encode($users, JSON_PRETTY_PRINT) - ); - } - - private function getNextId(array $users): int - { - if (empty($users)) { - return 0; - } - - $maxId = -1; - foreach ($users as $user) { - if ($user['id'] > $maxId) { - $maxId = $user['id']; - } - } - - return $maxId + 1; - } -} diff --git a/bootstrap/app.php b/bootstrap/app.php index 1ceeb8e..db0dd63 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -6,7 +6,6 @@ use DI\Bridge\Slim\Bridge; use App\View\ViewController; use App\Text\TextController; use App\Node\NodeController; -use App\Plan\PlanController; $container = require __DIR__ . '/container.php'; $app = Bridge::create($container); @@ -27,6 +26,4 @@ $app->get('/api/nodes/{textId}', [NodeController::class, 'getNodesOfText']); $app->post('/api/nodes/bulk', [NodeController::class, 'bulkCreateNodes']); $app->post('/api/nodes', [NodeController::class, 'createNode']); -$app->post('/api/plans', [PlanController::class, 'createPlan']); - return $app; diff --git a/bootstrap/container.php b/bootstrap/container.php index fb80996..efcfde5 100644 --- a/bootstrap/container.php +++ b/bootstrap/container.php @@ -6,20 +6,10 @@ use App\Text\TextRepository; use App\Text\JsonTextRepository; use App\Node\NodeRepository; use App\Node\JsonNodeRepository; -use App\Plan\PlanRepository; -use App\Plan\JsonPlanRepository; -use App\User\UserRepository; -use App\User\JsonUserRepository; -use App\ScheduledNode\ScheduledNodeRepository; -use App\ScheduledNode\JsonScheduledNodeRepository; $container = new Container([ TextRepository::class => DI\autowire(JsonTextRepository::class), NodeRepository::class => DI\autowire(JsonNodeRepository::class), - PlanRepository::class => DI\autowire(JsonPlanRepository::class), - UserRepository::class => DI\autowire(JsonUserRepository::class), - ScheduledNodeRepository::class => - DI\autowire(JsonScheduledNodeRepository::class), ]); return $container; diff --git a/cypress/e2e/homeCreatePlan.cy.js b/cypress/e2e/homeCreatePlan.cy.js deleted file mode 100644 index 3c9ee87..0000000 --- a/cypress/e2e/homeCreatePlan.cy.js +++ /dev/null @@ -1,98 +0,0 @@ -describe('Create plan modal on the home page', () => { - beforeEach(() => { - cy.exec('npm run db:seed') - cy.intercept('GET', '/api/texts').as('getTexts') - cy.visit('/home') - cy.wait('@getTexts') - }) - - afterEach(() => { - cy.exec('npm run db:wipe') - }) - - it('shows a "Create plan" button on each text', () => { - cy.get('#texts-list li').each((textItem) => { - cy.wrap(textItem).find('button.create-plan').should('exist') - }) - }) - - it('hides the create plan modal by default', () => { - cy.get('#create-plan-modal').should('not.be.visible') - }) - - it('shows the modal when clicking "Create plan"', () => { - cy.get('#texts-list li').first() - .find('button.create-plan').click() - cy.get('#create-plan-modal').should('be.visible') - }) - - it('modal contains name, date start, date end, save, cancel', () => { - cy.get('#texts-list li').first() - .find('button.create-plan').click() - cy.get('#create-plan-modal input.plan-name').should('be.visible') - cy.get('#create-plan-modal input.plan-date-start') - .should('be.visible') - cy.get('#create-plan-modal input.plan-date-end') - .should('be.visible') - cy.get('#create-plan-modal button.save-plan').should('be.visible') - cy.get('#create-plan-modal button.cancel-plan').should('be.visible') - }) - - it('hides the modal when clicking "Cancel"', () => { - cy.get('#texts-list li').first() - .find('button.create-plan').click() - cy.get('#create-plan-modal').should('be.visible') - cy.get('#create-plan-modal button.cancel-plan').click() - cy.get('#create-plan-modal').should('not.be.visible') - }) - - it('submits plan details to /api/plans', () => { - cy.intercept('POST', '/api/plans').as('createPlan') - cy.get('#texts-list li').first() - .find('button.create-plan').click() - cy.get('#create-plan-modal input.plan-name') - .type('My reading plan') - cy.get('#create-plan-modal input.plan-date-start') - .type('2025-01-01') - cy.get('#create-plan-modal input.plan-date-end') - .type('2025-01-31') - cy.get('#create-plan-modal button.save-plan').click() - cy.wait('@createPlan').then((createPlanRequest) => { - expect(createPlanRequest.response.statusCode).to.eq(201) - expect(createPlanRequest.request.body).to.deep.equal({ - userId: 0, - textId: 0, - name: 'My reading plan', - dateStart: '2025-01-01', - dateEnd: '2025-01-31', - }) - }) - }) - - it('closes the modal after successful submit', () => { - cy.intercept('POST', '/api/plans').as('createPlan') - cy.get('#texts-list li').first() - .find('button.create-plan').click() - cy.get('#create-plan-modal input.plan-name') - .type('Another plan') - cy.get('#create-plan-modal input.plan-date-start') - .type('2025-02-01') - cy.get('#create-plan-modal input.plan-date-end') - .type('2025-02-28') - cy.get('#create-plan-modal button.save-plan').click() - cy.wait('@createPlan') - cy.get('#create-plan-modal').should('not.be.visible') - }) - - it('does not submit if name is empty', () => { - cy.intercept('POST', '/api/plans').as('createPlan') - cy.get('#texts-list li').first() - .find('button.create-plan').click() - cy.get('#create-plan-modal input.plan-date-start') - .type('2025-01-01') - cy.get('#create-plan-modal input.plan-date-end') - .type('2025-01-31') - cy.get('#create-plan-modal button.save-plan').click() - cy.get('@createPlan.all').should('have.length', 0) - }) -}) diff --git a/data/seedDb.php b/data/seedDb.php index 922ca90..3206372 100644 --- a/data/seedDb.php +++ b/data/seedDb.php @@ -28,22 +28,9 @@ $nodes = [ ], ]; -$users = [ - [ - 'id' => 0, - 'email' => 'user@example.com', - ], -]; - -$plans = []; -$scheduledNodes = []; - $fileDataMap = [ 'texts.json' => $texts, 'nodes.json' => $nodes, - 'users.json' => $users, - 'plans.json' => $plans, - 'scheduledNodes.json' => $scheduledNodes, ]; foreach ($fileDataMap as $file => $data) { diff --git a/data/wipeDb.php b/data/wipeDb.php index 658bb2f..4183d5b 100644 --- a/data/wipeDb.php +++ b/data/wipeDb.php @@ -3,9 +3,6 @@ $files = [ 'texts.json', 'nodes.json', - 'users.json', - 'plans.json', - 'scheduledNodes.json', ]; foreach ($files as $file) { diff --git a/public/js/home.js b/public/js/home.js index b46a051..3e7cde8 100644 --- a/public/js/home.js +++ b/public/js/home.js @@ -1,85 +1,13 @@ document.addEventListener('DOMContentLoaded', () => { const textsList = document.getElementById('texts-list'); - const createPlanModal = document.getElementById('create-plan-modal'); async function loadTexts() { const response = await fetch('/api/texts'); const texts = await response.json(); textsList.innerHTML = texts - .map(text => - '
  • ' + text.name + - '
  • ' - ) + .map(text => '
  • ' + text.name + '
  • ') .join(''); } - const cancelPlanButton = createPlanModal.querySelector( - 'button.cancel-plan' - ); - const savePlanButton = createPlanModal.querySelector( - 'button.save-plan' - ); - const planNameInput = createPlanModal.querySelector('input.plan-name'); - const planDateStartInput = createPlanModal.querySelector( - 'input.plan-date-start' - ); - const planDateEndInput = createPlanModal.querySelector( - 'input.plan-date-end' - ); - - function openCreatePlanModal(textId) { - createPlanModal.dataset.textId = textId; - createPlanModal.hidden = false; - } - - function closeCreatePlanModal() { - createPlanModal.hidden = true; - planNameInput.value = ''; - planDateStartInput.value = ''; - planDateEndInput.value = ''; - } - - textsList.addEventListener('click', (clickEvent) => { - const createPlanButton = clickEvent.target.closest( - 'button.create-plan' - ); - if (createPlanButton === null) { - return; - } - openCreatePlanModal(createPlanButton.dataset.textId); - }); - - cancelPlanButton.addEventListener('click', () => { - closeCreatePlanModal(); - }); - - savePlanButton.addEventListener('click', async () => { - const planName = planNameInput.value; - const dateStart = planDateStartInput.value; - const dateEnd = planDateEndInput.value; - const textId = Number(createPlanModal.dataset.textId); - - if (planName === '' || dateStart === '' || dateEnd === '') { - return; - } - - const response = await fetch('/api/plans', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - userId: 0, - textId: textId, - name: planName, - dateStart: dateStart, - dateEnd: dateEnd, - }), - }); - - if (response.ok) { - closeCreatePlanModal(); - } - }); - loadTexts(); }); diff --git a/tests/e2e/Controllers/PlanControllerTest.php b/tests/e2e/Controllers/PlanControllerTest.php deleted file mode 100644 index ee51012..0000000 --- a/tests/e2e/Controllers/PlanControllerTest.php +++ /dev/null @@ -1,280 +0,0 @@ -planRepo = new FakePlanRepository(); - $this->userRepo = new FakeUserRepository(); - $this->textRepo = new FakeTextRepository(); - $this->nodeRepo = new FakeNodeRepository(); - $this->scheduledNodeRepo = new FakeScheduledNodeRepository(); - - $this->userRepo->create(new CreateUserDto( - email: new EmailAddress('test@test.com'), - )); - $text = $this->textRepo->create(new CreateTextDto('testname')); - $this->nodeRepo->create(new CreateNodeDto( - text: $text, - title: 'Root Node', - parentNode: null, - )); - - $createScheduledNode = new CreateScheduledNode( - scheduledNodeRepo: $this->scheduledNodeRepo, - planRepo: $this->planRepo, - ); - $this->createPlan = new CreatePlan( - $this->planRepo, - $this->userRepo, - $this->textRepo, - $this->nodeRepo, - $createScheduledNode, - ); - $this->controller = new PlanController(); - } - - private function makeRequest(array $data): ServerRequestInterface - { - $body = new StreamFactory()->createStream(json_encode($data)); - return new ServerRequestFactory() - ->createServerRequest('POST', 'http://localhost/api/plans') - ->withHeader('Content-Type', 'application/json') - ->withBody($body); - } - - public function test_create_plan_returns_201_with_id_and_name(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'textId' => 0, - 'name' => 'My Plan', - 'dateStart' => '2025-01-01', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(201, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('id', $body); - $this->assertEquals('My Plan', $body['name']); - } - - public function test_create_plan_returns_400_when_user_id_missing(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'textId' => 0, - 'name' => 'My Plan', - 'dateStart' => '2025-01-01', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(400, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('error', $body); - } - - public function test_create_plan_returns_400_when_text_id_missing(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'name' => 'My Plan', - 'dateStart' => '2025-01-01', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(400, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('error', $body); - } - - public function test_create_plan_returns_400_when_name_missing(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'textId' => 0, - 'dateStart' => '2025-01-01', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(400, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('error', $body); - } - - public function test_create_plan_returns_400_when_date_start_missing(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'textId' => 0, - 'name' => 'My Plan', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(400, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('error', $body); - } - - public function test_create_plan_returns_400_when_date_end_missing(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'textId' => 0, - 'name' => 'My Plan', - 'dateStart' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(400, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('error', $body); - } - - public function test_create_plan_returns_400_when_date_end_before_start(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'textId' => 0, - 'name' => 'My Plan', - 'dateStart' => '2025-01-02', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(400, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('error', $body); - } - - public function test_create_plan_returns_404_when_user_not_found(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 99, - 'textId' => 0, - 'name' => 'My Plan', - 'dateStart' => '2025-01-01', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(404, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('error', $body); - } - - public function test_create_plan_returns_404_when_text_not_found(): void - { - $response = $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'textId' => 99, - 'name' => 'My Plan', - 'dateStart' => '2025-01-01', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals(404, $response->getStatusCode()); - $body = json_decode($response->getBody(), true); - $this->assertArrayHasKey('error', $body); - } - - public function test_create_plan_persists_plan_in_repository(): void - { - $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'textId' => 0, - 'name' => 'Persistent Plan', - 'dateStart' => '2025-01-01', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $storedPlan = $this->planRepo->find(0); - $this->assertNotNull($storedPlan); - $this->assertEquals('Persistent Plan', $storedPlan->getName()); - } - - public function test_create_plan_schedules_nodes(): void - { - $this->controller->createPlan( - $this->makeRequest([ - 'userId' => 0, - 'textId' => 0, - 'name' => 'Scheduling Plan', - 'dateStart' => '2025-01-01', - 'dateEnd' => '2025-01-01', - ]), - new Response(), - $this->createPlan, - ); - - $this->assertEquals( - 1, - $this->scheduledNodeRepo->getNumberOfTimesCreateCalled() - ); - $this->assertNotNull($this->scheduledNodeRepo->find(0)); - } -} diff --git a/views/templates/home.php b/views/templates/home.php index ddbc361..a386efe 100644 --- a/views/templates/home.php +++ b/views/templates/home.php @@ -7,20 +7,6 @@

    Home

    -