diff --git a/backend/app/Http/Controllers/ScheduleController.php b/backend/app/Http/Controllers/ScheduleController.php
index 33a6316..f80461a 100644
--- a/backend/app/Http/Controllers/ScheduleController.php
+++ b/backend/app/Http/Controllers/ScheduleController.php
@@ -249,8 +249,6 @@ class ScheduleController extends Controller
return [
'id' => $assignment->getId(),
- 'scheduledDate' => $assignment->getScheduledDate()
- ->format('Y-m-d'),
'schedule' => [
'id' => $assignmentForDate->getScheduleId(),
'set' => [
diff --git a/backend/app/Schedule/EloquentScheduleRepository.php b/backend/app/Schedule/EloquentScheduleRepository.php
index 8576591..214e8a5 100644
--- a/backend/app/Schedule/EloquentScheduleRepository.php
+++ b/backend/app/Schedule/EloquentScheduleRepository.php
@@ -63,7 +63,7 @@ class EloquentScheduleRepository implements ScheduleRepository
return $schedules;
}
- public function findIncompleteAssignmentsForUserDueOnOrBefore(
+ public function findAssignmentsForUserOnDate(
User $user,
DateTimeImmutable $date,
): array {
@@ -76,14 +76,11 @@ class EloquentScheduleRepository implements ScheduleRepository
'schedule_assignments.schedule_id',
)
->where('schedules.user_id', $user->getId())
- ->where(
- 'schedule_assignments.scheduled_date',
- '<=',
- $date->format('Y-m-d'),
- )
+ ->where('schedule_assignments.scheduled_date', $date->format(
+ 'Y-m-d',
+ ))
->whereNull('schedule_assignments.completed_at')
->with('schedule')
- ->orderBy('schedule_assignments.scheduled_date')
->orderByDesc('schedules.id')
->orderBy('schedule_assignments.position')
->orderBy('schedule_assignments.id')
diff --git a/backend/app/Schedule/ScheduleRepository.php b/backend/app/Schedule/ScheduleRepository.php
index 20b5f9f..3222311 100644
--- a/backend/app/Schedule/ScheduleRepository.php
+++ b/backend/app/Schedule/ScheduleRepository.php
@@ -19,7 +19,7 @@ interface ScheduleRepository
/**
* @return list
*/
- public function findIncompleteAssignmentsForUserDueOnOrBefore(
+ public function findAssignmentsForUserOnDate(
User $user,
DateTimeImmutable $date,
): array;
diff --git a/backend/app/Schedule/UseCases/ListAssignmentsForDate/ListAssignmentsForDate.php b/backend/app/Schedule/UseCases/ListAssignmentsForDate/ListAssignmentsForDate.php
index 23f388d..9e3017d 100644
--- a/backend/app/Schedule/UseCases/ListAssignmentsForDate/ListAssignmentsForDate.php
+++ b/backend/app/Schedule/UseCases/ListAssignmentsForDate/ListAssignmentsForDate.php
@@ -22,11 +22,10 @@ class ListAssignmentsForDate
{
$date = $this->parseDate($request->date);
- return $this->scheduleRepository
- ->findIncompleteAssignmentsForUserDueOnOrBefore(
- $request->user,
- $date,
- );
+ return $this->scheduleRepository->findAssignmentsForUserOnDate(
+ $request->user,
+ $date,
+ );
}
/**
diff --git a/backend/tests/Fakes/FakeScheduleRepository.php b/backend/tests/Fakes/FakeScheduleRepository.php
index e12d126..e2f35bc 100644
--- a/backend/tests/Fakes/FakeScheduleRepository.php
+++ b/backend/tests/Fakes/FakeScheduleRepository.php
@@ -78,7 +78,7 @@ class FakeScheduleRepository implements ScheduleRepository
}, array_values($schedules));
}
- public function findIncompleteAssignmentsForUserDueOnOrBefore(
+ public function findAssignmentsForUserOnDate(
User $user,
DateTimeImmutable $date,
): array {
@@ -86,7 +86,8 @@ class FakeScheduleRepository implements ScheduleRepository
foreach ($this->findAllForUser($user) as $schedule) {
foreach ($schedule->getAssignments() as $assignment) {
- if ($assignment->getScheduledDate() > $date
+ if ($assignment->getScheduledDate()->format('Y-m-d')
+ !== $date->format('Y-m-d')
|| $assignment->getCompletedAt() !== null
) {
continue;
@@ -100,37 +101,6 @@ class FakeScheduleRepository implements ScheduleRepository
}
}
- usort(
- $assignments,
- function (
- AssignmentForDate $first,
- AssignmentForDate $second,
- ): int {
- $firstAssignment = $first->getAssignment();
- $secondAssignment = $second->getAssignment();
- $dateComparison = $firstAssignment->getScheduledDate()
- <=> $secondAssignment->getScheduledDate();
- if ($dateComparison !== 0) {
- return $dateComparison;
- }
-
- $scheduleComparison = $second->getScheduleId()
- <=> $first->getScheduleId();
- if ($scheduleComparison !== 0) {
- return $scheduleComparison;
- }
-
- $positionComparison = $firstAssignment->getPosition()
- <=> $secondAssignment->getPosition();
- if ($positionComparison !== 0) {
- return $positionComparison;
- }
-
- return $firstAssignment->getId()
- <=> $secondAssignment->getId();
- },
- );
-
return $assignments;
}
diff --git a/backend/tests/Feature/Schedule/ScheduleEndpointTest.php b/backend/tests/Feature/Schedule/ScheduleEndpointTest.php
index 295a245..b68e2ad 100644
--- a/backend/tests/Feature/Schedule/ScheduleEndpointTest.php
+++ b/backend/tests/Feature/Schedule/ScheduleEndpointTest.php
@@ -225,7 +225,7 @@ class ScheduleEndpointTest extends TestCase
]);
}
- public function test_it_lists_the_users_assignments_due_through_a_date(): void
+ public function test_it_lists_the_users_assignments_for_a_date(): void
{
$user = $this->createUser('reader@example.com');
$otherUser = $this->createUser('other@example.com');
@@ -255,8 +255,8 @@ class ScheduleEndpointTest extends TestCase
$this->credentialedPost('/api/schedules', [
'setId' => $olderSet->getId(),
'levelId' => $olderLevel->getId(),
- 'startDate' => '2026-08-14',
- 'targetDate' => '2026-08-14',
+ 'startDate' => '2026-08-15',
+ 'targetDate' => '2026-08-15',
])->assertCreated();
$this->credentialedPost('/api/schedules', [
'setId' => $newerSet->getId(),
@@ -286,9 +286,22 @@ class ScheduleEndpointTest extends TestCase
->assertExactJson([
'date' => '2026-08-15',
'assignments' => [
+ [
+ 'id' => 3,
+ 'schedule' => [
+ 'id' => 2,
+ 'set' => [
+ 'name' => 'Newer course',
+ ],
+ ],
+ 'element' => [
+ 'name' => 'Only chapter',
+ 'kind' => 'chapter',
+ 'path' => ['Only chapter'],
+ ],
+ ],
[
'id' => 1,
- 'scheduledDate' => '2026-08-14',
'schedule' => [
'id' => 1,
'set' => [
@@ -303,7 +316,6 @@ class ScheduleEndpointTest extends TestCase
],
[
'id' => 2,
- 'scheduledDate' => '2026-08-14',
'schedule' => [
'id' => 1,
'set' => [
@@ -316,21 +328,6 @@ class ScheduleEndpointTest extends TestCase
'path' => ['Second lesson'],
],
],
- [
- 'id' => 3,
- 'scheduledDate' => '2026-08-15',
- 'schedule' => [
- 'id' => 2,
- 'set' => [
- 'name' => 'Newer course',
- ],
- ],
- 'element' => [
- 'name' => 'Only chapter',
- 'kind' => 'chapter',
- 'path' => ['Only chapter'],
- ],
- ],
],
]);
}
diff --git a/backend/tests/Unit/Schedule/UseCases/ListAssignmentsForDateTest.php b/backend/tests/Unit/Schedule/UseCases/ListAssignmentsForDateTest.php
index 4fca1fc..c6bc95f 100644
--- a/backend/tests/Unit/Schedule/UseCases/ListAssignmentsForDateTest.php
+++ b/backend/tests/Unit/Schedule/UseCases/ListAssignmentsForDateTest.php
@@ -17,7 +17,7 @@ use Tests\Fakes\FakeScheduleRepository;
class ListAssignmentsForDateTest extends TestCase
{
- public function test_it_lists_the_users_assignments_due_through_the_date(): void
+ public function test_it_lists_the_users_assignments_for_the_date(): void
{
$user = $this->user(1, 'reader@example.com');
$otherUser = $this->user(2, 'other@example.com');
@@ -25,7 +25,7 @@ class ListAssignmentsForDateTest extends TestCase
$olderSchedule = $repository->create($this->schedule(
user: $user,
setName: 'Older plan',
- date: '2026-08-14',
+ date: '2026-08-15',
assignmentNames: ['First', 'Second'],
));
$repository->create($this->schedule(
@@ -37,7 +37,7 @@ class ListAssignmentsForDateTest extends TestCase
$repository->create($this->schedule(
user: $otherUser,
setName: 'Private plan',
- date: '2026-08-13',
+ date: '2026-08-15',
assignmentNames: ['Hidden'],
));
$repository->create($this->schedule(
@@ -61,18 +61,18 @@ class ListAssignmentsForDateTest extends TestCase
);
$this->assertSame(
- ['Older plan', 'Newer plan'],
+ ['Newer plan', 'Older plan'],
array_map(function ($assignment): string {
return $assignment->getSetName();
}, $assignments),
);
$this->assertSame(
- ['Second', 'Third'],
+ ['Third', 'Second'],
array_map(function ($assignment): string {
return $assignment->getAssignment()->getName();
}, $assignments),
);
- $this->assertSame(1, $assignments[0]->getScheduleId());
+ $this->assertSame(2, $assignments[0]->getScheduleId());
}
public function test_it_rejects_a_missing_date(): void
diff --git a/frontend/website/cypress/e2e/set-scheduling.cy.ts b/frontend/website/cypress/e2e/set-scheduling.cy.ts
index 0511405..9f83629 100644
--- a/frontend/website/cypress/e2e/set-scheduling.cy.ts
+++ b/frontend/website/cypress/e2e/set-scheduling.cy.ts
@@ -121,55 +121,6 @@ const completedScheduleDetail = {
},
}
-const splitScheduleDetail = {
- schedule: {
- ...scheduleDetail.schedule,
- targetDate: '2026-08-13',
- assignmentCount: 4,
- days: [
- scheduleDetail.schedule.days[0],
- scheduleDetail.schedule.days[1],
- {
- date: '2026-08-12',
- assignments: [
- {
- id: 2,
- completedAt: '2026-08-11T08:30:00+00:00',
- element: {
- name: 'Chapter 1',
- kind: 'Chapter_sections-v2',
- path: ['Exodus', 'Shemot', 'Chapter 1'],
- },
- },
- {
- id: 3,
- completedAt: null,
- element: {
- name: 'Chapter 1',
- kind: 'Chapter_sections-v2',
- path: ['Leviticus', 'Vayikra', 'Chapter 1'],
- },
- },
- ],
- },
- {
- date: '2026-08-13',
- assignments: [
- {
- id: 4,
- completedAt: '2026-08-11T09:45:00+00:00',
- element: {
- name: 'Chapter 1',
- kind: 'Chapter_sections-v2',
- path: ['Numbers', 'Bamidbar', 'Chapter 1'],
- },
- },
- ],
- },
- ],
- },
-}
-
function interceptAuthenticatedUser(): void {
cy.intercept('GET', '**/api/me', {
statusCode: 200,
@@ -208,12 +159,9 @@ describe('set scheduling', () => {
cy.location('pathname').should('equal', '/sets/41/schedules/new')
cy.get('h1').should('have.text', 'Schedule Bible')
cy.get('#schedule-level option').then(($options) => {
- expect([...$options].map((option) => option.textContent?.trim())).to.deep.equal([
- 'Choose a level',
- 'book (2)',
- 'portion (2)',
- 'Chapter_sections-v2 (2)',
- ])
+ expect([...$options].map((option) => option.textContent?.trim())).to.deep.equal(
+ ['Choose a level', 'book (2)', 'portion (2)', 'Chapter_sections-v2 (2)'],
+ )
})
cy.get('#schedule-level').select('3')
cy.get('#schedule-start-date').type('2026-08-10')
@@ -236,12 +184,6 @@ describe('set scheduling', () => {
'contain.text',
'Exodus / Shemot / Chapter 1',
)
- cy.get('[data-assignment-section="completed"]').should('not.have.attr', 'open')
- cy.get('[data-assignment-section="completed"] summary').click()
- cy.get('[data-assignment-section="completed"]').should(
- 'contain.text',
- 'No assignments completed yet.',
- )
})
it('validates the schedule form before submitting', () => {
@@ -306,72 +248,6 @@ describe('set scheduling', () => {
cy.get('h1').should('have.text', 'Schedule not found')
})
- it('splits assignments by progress while preserving the schedule timeline', () => {
- cy.clock(new Date(2026, 7, 11, 12).getTime(), ['Date'])
- cy.intercept('GET', '**/api/schedules/73', {
- statusCode: 200,
- body: splitScheduleDetail,
- }).as('schedule')
-
- cy.visit('/schedules/73')
- cy.wait('@me')
- cy.wait('@schedule')
-
- cy.get('[data-assignment-section="remaining"]')
- .should('have.attr', 'data-assignment-count', '2')
- .within(() => {
- cy.get('h2').should('have.text', 'Remaining')
- cy.get('[data-schedule-day]').then(($days) => {
- expect([...$days].map((day) => day.getAttribute('data-schedule-date'))).to.deep.equal([
- '2026-08-10',
- '2026-08-11',
- '2026-08-12',
- ])
- })
- cy.get('[data-schedule-date="2026-08-10"]')
- .should('contain.text', 'Overdue')
- .and('contain.text', 'Genesis / Creation / Chapter 1')
- cy.get('[data-schedule-date="2026-08-11"]')
- .should('contain.text', 'Rest day')
- .and('not.contain.text', 'Overdue')
- cy.get('[data-schedule-date="2026-08-12"]')
- .should('contain.text', 'Leviticus / Vayikra / Chapter 1')
- .and('not.contain.text', 'Exodus / Shemot / Chapter 1')
- cy.get('[data-schedule-date="2026-08-13"]').should('not.exist')
- })
-
- cy.get('[data-assignment-section="completed"]').should(
- 'have.attr',
- 'data-assignment-count',
- '2',
- )
- cy.get('[data-assignment-section="completed"]').should('not.have.attr', 'open')
- cy.get('[data-assignment-section="completed"]').within(() => {
- cy.get('summary').should('contain.text', 'Completed').click()
- cy.get('[data-schedule-day]').then(($days) => {
- expect([...$days].map((day) => day.getAttribute('data-schedule-date'))).to.deep.equal([
- '2026-08-12',
- '2026-08-13',
- ])
- })
- cy.get('[data-schedule-date="2026-08-12"]')
- .should('contain.text', 'Exodus / Shemot / Chapter 1')
- .and('not.contain.text', 'Leviticus / Vayikra / Chapter 1')
- cy.get('[data-schedule-date="2026-08-13"]').should(
- 'contain.text',
- 'Numbers / Bamidbar / Chapter 1',
- )
- cy.contains('Rest day').should('not.exist')
- })
-
- cy.viewport(375, 667)
- cy.document().then((document) => {
- expect(document.documentElement.scrollWidth).to.be.at.most(
- document.documentElement.clientWidth,
- )
- })
- })
-
it('completes and reopens assignments from the schedule', () => {
const completedAt = '2026-08-15T12:30:00+00:00'
cy.intercept('GET', '**/api/schedules/73', {
@@ -402,79 +278,33 @@ describe('set scheduling', () => {
cy.wait('@me')
cy.wait('@schedule')
- cy.get('[data-assignment-section="completed"] summary').click()
- cy.get('[data-assignment-section="completed"] [data-schedule-assignment="2"]').within(() => {
+ cy.get('[data-schedule-assignment="2"]').within(() => {
cy.contains('Completed').should('be.visible')
cy.get('time')
.should('have.attr', 'datetime', '2026-08-15T09:30:00+00:00')
.and('have.text', formatCompletionTime('2026-08-15T09:30:00+00:00'))
- })
-
- cy.get('[data-assignment-section="remaining"] [data-schedule-assignment="1"]').within(() => {
- cy.contains('button', 'Mark complete').click()
- cy.contains('button', 'Saving...').should('be.disabled')
- })
- cy.wait('@completeAssignment')
- cy.get('[data-assignment-section="remaining"]')
- .should('have.attr', 'data-assignment-count', '0')
- .and('contain.text', 'All assignments are complete.')
- .and('contain.text', 'Rest day')
- cy.get('[data-assignment-section="completed"]')
- .should('have.attr', 'data-assignment-count', '2')
- .within(() => {
- cy.get('[data-schedule-assignment="1"]').within(() => {
- cy.contains('Completed').should('be.visible')
- cy.get('time')
- .should('have.attr', 'datetime', completedAt)
- .and('have.text', formatCompletionTime(completedAt))
- cy.contains('button', 'Reopen').should('be.enabled')
- })
- })
- cy.get('[data-completion-announcement]').should('have.text', 'Assignment moved to Completed.')
-
- cy.get('[data-assignment-section="completed"] [data-schedule-assignment="2"]').within(() => {
cy.contains('button', 'Reopen').click()
})
cy.wait('@reopenAssignment')
- cy.get('[data-assignment-section="remaining"]')
- .should('have.attr', 'data-assignment-count', '1')
- .find('[data-schedule-assignment="2"]')
+ cy.get('[data-schedule-assignment="2"]')
.should('contain.text', 'Not completed')
.and('not.contain.text', 'Completed')
.within(() => {
cy.contains('button', 'Mark complete').should('be.enabled')
})
- cy.get('[data-assignment-section="completed"]')
- .should('have.attr', 'data-assignment-count', '1')
- .find('[data-schedule-assignment="2"]')
- .should('not.exist')
- cy.get('[data-completion-announcement]').should('have.text', 'Assignment moved to Remaining.')
- })
- it('keeps an assignment in place when a completion request fails', () => {
- cy.intercept('GET', '**/api/schedules/73', {
- statusCode: 200,
- body: scheduleDetail,
- }).as('schedule')
- cy.intercept('PATCH', '**/api/assignments/1', {
- statusCode: 500,
- }).as('completeAssignment')
-
- cy.visit('/schedules/73')
- cy.wait('@me')
- cy.wait('@schedule')
-
- cy.get('[data-assignment-section="remaining"] [data-schedule-assignment="1"]')
- .contains('button', 'Mark complete')
- .click()
+ cy.get('[data-schedule-assignment="1"]').within(() => {
+ cy.contains('button', 'Mark complete').click()
+ cy.contains('button', 'Saving...').should('be.disabled')
+ })
cy.wait('@completeAssignment')
-
- cy.get('[data-assignment-section="remaining"] [data-schedule-assignment="1"]')
- .should('be.visible')
- .and('contain.text', "We couldn't update this assignment.")
- cy.get('[data-assignment-section="completed"]')
- .find('[data-schedule-assignment="1"]')
- .should('not.exist')
+ cy.get('[data-schedule-assignment="1"]').within(() => {
+ cy.contains('Completed').should('be.visible')
+ cy.get('time')
+ .should('have.attr', 'datetime', completedAt)
+ .and('have.text', formatCompletionTime(completedAt))
+ cy.contains('button', 'Reopen').should('be.enabled')
+ })
})
it('lists the users schedules on the dashboard', () => {
diff --git a/frontend/website/cypress/e2e/today-assignments.cy.ts b/frontend/website/cypress/e2e/today-assignments.cy.ts
index 553c57c..77d2ba5 100644
--- a/frontend/website/cypress/e2e/today-assignments.cy.ts
+++ b/frontend/website/cypress/e2e/today-assignments.cy.ts
@@ -36,7 +36,6 @@ describe("today's assignments", () => {
assignments: [
{
id: 12,
- scheduledDate: '2026-08-13',
schedule: {
id: 73,
set: { name: 'Bible' },
@@ -49,7 +48,6 @@ describe("today's assignments", () => {
},
{
id: 13,
- scheduledDate: browserToday,
schedule: {
id: 81,
set: { name: 'Course' },
@@ -79,16 +77,6 @@ describe("today's assignments", () => {
'have.length',
2,
)
- cy.get('[data-today-assignment]')
- .first()
- .should('have.attr', 'data-today-assignment', '12')
- cy.get('[data-today-assignment="12"] .today-assignment__overdue').should(
- 'have.text',
- 'Overdue · Due Aug 13, 2026',
- )
- cy.get('[data-today-assignment="13"] .today-assignment__overdue').should(
- 'not.exist',
- )
cy.contains('a', 'Genesis / Creation / Chapter 1')
.should('contain.text', 'Bible')
.and('have.attr', 'href', '/schedules/73')
@@ -121,7 +109,7 @@ describe("today's assignments", () => {
cy.get('.today-assignments [role="status"]').should(
'contain.text',
- 'Nothing is due today.',
+ 'Nothing is assigned for today.',
)
})
@@ -133,7 +121,6 @@ describe("today's assignments", () => {
assignments: [
{
id: 12,
- scheduledDate: browserToday,
schedule: {
id: 73,
set: { name: 'Bible' },
@@ -190,7 +177,7 @@ describe("today's assignments", () => {
cy.get('[data-today-assignment="12"]').should('not.exist')
cy.get('.today-assignments [role="status"]').should(
'contain.text',
- 'Nothing is due today.',
+ 'Nothing is assigned for today.',
)
})
@@ -232,7 +219,7 @@ describe("today's assignments", () => {
cy.get('.today-assignments [role="status"]').should(
'contain.text',
- 'Nothing is due today.',
+ 'Nothing is assigned for today.',
)
})
})
diff --git a/frontend/website/src/components/ScheduleAssignmentTimeline.vue b/frontend/website/src/components/ScheduleAssignmentTimeline.vue
deleted file mode 100644
index 779d712..0000000
--- a/frontend/website/src/components/ScheduleAssignmentTimeline.vue
+++ /dev/null
@@ -1,335 +0,0 @@
-
-
-
-
- -
-
-
-
Rest day
-
-
-
-
-
-
-
diff --git a/frontend/website/src/stores/schedules.ts b/frontend/website/src/stores/schedules.ts
index 825bbfb..2471d66 100644
--- a/frontend/website/src/stores/schedules.ts
+++ b/frontend/website/src/stores/schedules.ts
@@ -31,7 +31,6 @@ const scheduleAssignmentSchema = assignmentIdentitySchema.extend({
})
export const assignmentForDateSchema = assignmentIdentitySchema.extend({
- scheduledDate: isoDateSchema,
schedule: z.object({
id: z.number().int().positive(),
set: z.object({
diff --git a/frontend/website/src/views/DashboardView.vue b/frontend/website/src/views/DashboardView.vue
index 7d1eeaf..822da86 100644
--- a/frontend/website/src/views/DashboardView.vue
+++ b/frontend/website/src/views/DashboardView.vue
@@ -36,14 +36,6 @@ function formatDate(value: string): string {
}).format(new Date(`${value}T00:00:00Z`))
}
-function isOverdue(scheduledDate: string): boolean {
- return scheduledDate < todayDate.value
-}
-
-function overdueLabel(scheduledDate: string): string {
- return `Overdue · Due ${formatDate(scheduledDate)}`
-}
-
async function completeAssignment(assignmentId: number): Promise {
await schedulesStore.setAssignmentCompleted(assignmentId, true)
}
@@ -84,7 +76,7 @@ async function completeAssignment(assignmentId: number): Promise {
- Nothing is due today.
+ Nothing is assigned for today.
-
- {{ completionAnnouncement }}
-
+
+ -
+
-
-
+
Rest day
-
- All assignments are complete.
-
-
-
-
-
-
-
-
- Completed
- Finished work, grouped by its original schedule date.
-
-
-
- {{ completedAssignmentCount }}
- {{ completedAssignmentCount === 1 ? 'assignment' : 'assignments' }}
-
- ⌄
-
-
-
-
-
- No assignments completed yet.
-
-
-
-
+
+
+
@@ -294,144 +233,155 @@ h1 {
color: #68776f;
}
-.completion-announcement {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- white-space: nowrap;
- border: 0;
-}
-
-.assignment-section {
- margin-top: 3rem;
-}
-
-.assignment-section__header {
- display: flex;
- align-items: end;
- justify-content: space-between;
- gap: 2rem;
-}
-
-.assignment-section__eyebrow {
- margin: 0 0 0.45rem;
- color: #926044;
- font-size: 0.68rem;
- font-weight: 800;
- letter-spacing: 0.12em;
- text-transform: uppercase;
-}
-
-.assignment-section__header h2,
-.completed-section__heading {
- font-family: Georgia, 'Times New Roman', serif;
- font-weight: 500;
- letter-spacing: -0.035em;
-}
-
-.assignment-section__header h2 {
- margin: 0;
- font-size: clamp(2rem, 5vw, 3rem);
- line-height: 1;
-}
-
-.assignment-section__header p:last-child {
- margin: 0.65rem 0 0;
- color: #68776f;
- line-height: 1.55;
-}
-
-.assignment-section__count {
- flex: 0 0 auto;
- padding: 0.45rem 0.7rem;
- border-radius: 999px;
- color: #4f665d;
- background: rgb(224 233 223 / 72%);
- font-size: 0.72rem;
- font-weight: 800;
-}
-
-.section-state {
+.schedule-days {
display: grid;
- min-height: 6rem;
- place-items: center;
- margin: 1.4rem 0 0;
- padding: 1.25rem;
- border: 1px dashed rgb(24 48 41 / 18%);
- border-radius: 1rem;
- color: #5e7067;
- background: rgb(255 253 247 / 52%);
- text-align: center;
-}
-
-.completed-section {
- overflow: hidden;
- border: 1px solid rgb(24 48 41 / 12%);
- border-radius: 1rem;
- background: rgb(246 245 237 / 78%);
-}
-
-.completed-section__summary {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 1.5rem;
- padding: 1.15rem 1.25rem;
- cursor: pointer;
+ gap: 1rem;
+ margin: 3rem 0 0;
+ padding: 0;
list-style: none;
}
-.completed-section__summary::-webkit-details-marker {
- display: none;
+.schedule-day {
+ overflow: hidden;
+ border: 1px solid rgb(24 48 41 / 12%);
+ border-radius: 1rem;
+ background: rgb(255 253 247 / 82%);
+ box-shadow: 0 0.75rem 2rem rgb(40 62 52 / 6%);
}
-.completed-section__summary:focus-visible {
- outline: 3px solid rgb(86 127 112 / 34%);
- outline-offset: -0.25rem;
-}
-
-.completed-section__introduction {
- display: grid;
- gap: 0.25rem;
-}
-
-.completed-section__heading {
- color: #344e45;
- font-size: 1.35rem;
-}
-
-.completed-section__introduction > span:last-child {
- color: #79877f;
- font-size: 0.78rem;
- line-height: 1.45;
-}
-
-.completed-section__controls {
- display: inline-flex;
+.schedule-day__header {
+ display: flex;
align-items: center;
- gap: 0.75rem;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 0.85rem 1rem;
+ border-bottom: 1px solid rgb(24 48 41 / 10%);
+ color: #5e7067;
+ background: rgb(239 228 212 / 42%);
+ font-size: 0.75rem;
+ font-weight: 800;
}
-.completed-section__chevron {
- color: #68776f;
- font-size: 1.35rem;
- line-height: 1;
- transition: transform 160ms ease;
+.rest-day {
+ margin: 0;
+ padding: 1.2rem 1rem;
+ color: #79877f;
+ font-style: italic;
}
-.completed-section[open] .completed-section__chevron {
- transform: rotate(180deg);
+.assignment-list {
+ display: grid;
+ gap: 0;
+ margin: 0;
+ padding: 0;
+ list-style: none;
}
-.completed-section__content {
- padding: 0 1rem 1rem;
+.assignment-list li {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ align-items: start;
+ gap: 1rem;
+ padding: 1rem;
+}
+
+.assignment-list li.is-completed {
+ background: rgb(220 235 224 / 34%);
+}
+
+.assignment-list li + li {
border-top: 1px solid rgb(24 48 41 / 10%);
}
+.assignment-path {
+ min-width: 0;
+ font-family: Georgia, 'Times New Roman', serif;
+ font-size: 1.05rem;
+ overflow-wrap: anywhere;
+}
+
+.assignment-description {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ min-width: 0;
+}
+
+.assignment-kind {
+ flex: 0 0 auto;
+ padding: 0.35rem 0.55rem;
+ border-radius: 999px;
+ color: #81533a;
+ background: #efe4d4;
+ font-size: 0.62rem;
+ font-weight: 800;
+ letter-spacing: 0.1em;
+ text-transform: none;
+}
+
+.assignment-completion {
+ display: grid;
+ justify-items: end;
+ gap: 0.55rem;
+ min-width: 12rem;
+}
+
+.assignment-completion__status,
+.assignment-completion__error {
+ margin: 0;
+ font-size: 0.74rem;
+ font-weight: 700;
+ text-align: right;
+}
+
+.assignment-completion__status {
+ color: #5e7067;
+}
+
+.assignment-completion__status time {
+ display: block;
+ margin-top: 0.2rem;
+ color: #344e45;
+}
+
+.assignment-completion__error {
+ max-width: 14rem;
+ color: #9b3f32;
+}
+
+.assignment-completion-button {
+ min-height: 2.4rem;
+ padding: 0.55rem 0.8rem;
+ border: 1px solid rgb(24 58 49 / 28%);
+ border-radius: 0.65rem;
+ color: #183a31;
+ background: #fffdf7;
+ font-size: 0.75rem;
+ font-weight: 800;
+ cursor: pointer;
+}
+
+.is-completed .assignment-completion-button {
+ color: #5e7067;
+ background: transparent;
+}
+
+.assignment-completion-button:hover:not(:disabled) {
+ border-color: #285c4e;
+ background: #f9f5e9;
+}
+
+.assignment-completion-button:focus-visible {
+ outline: 3px solid rgb(86 127 112 / 34%);
+ outline-offset: 0.25rem;
+}
+
+.assignment-completion-button:disabled {
+ cursor: wait;
+ opacity: 0.65;
+}
+
.page-state {
display: grid;
min-height: 10rem;
@@ -480,26 +430,22 @@ h1 {
margin-top: 3rem;
}
- .assignment-section__header {
+ .assignment-list li {
+ grid-template-columns: 1fr;
+ }
+
+ .assignment-description {
align-items: flex-start;
- flex-direction: column;
- gap: 1rem;
}
- .completed-section__summary {
- align-items: flex-start;
- gap: 1rem;
- padding: 1rem;
+ .assignment-completion {
+ justify-items: start;
+ min-width: 0;
}
- .completed-section__controls {
- flex-direction: column;
- align-items: flex-end;
- gap: 0.35rem;
- }
-
- .completed-section__content {
- padding: 0 0.75rem 0.75rem;
+ .assignment-completion__status,
+ .assignment-completion__error {
+ text-align: left;
}
}