added fixes for bug plan 1.0.6RC01
Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 5m43s
Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 5m43s
This commit is contained in:
125
.github/specs/archive/feat-landing-page.md
vendored
Normal file
125
.github/specs/archive/feat-landing-page.md
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
# Feature: Landing page with details
|
||||
|
||||
## Overview
|
||||
|
||||
When an unauthenticated user visits the web site, they will be sent to this landing page. The page will contain a hero with various images. A sign up/sign in component. On scrolling, various components will show describing the application features and functionality.
|
||||
|
||||
**Goal:** New users are brought to the landing page to learn more about the app.
|
||||
|
||||
**User Story:**
|
||||
As a new user, I should be presented with the landing page that tells me about the app.
|
||||
|
||||
**Rules:**
|
||||
.github/copilot-instructions.md
|
||||
You know how my app works at a high level
|
||||
|
||||
**Discussions:**
|
||||
|
||||
- I want each part of the landing page as different components (rows)
|
||||
- Should these components go into their own directory (components/landing)
|
||||
|
||||
1. Row 1
|
||||
- My logo is resources\logo\full_logo.png
|
||||
- I plan to have my logo in the center near the top of the landing page
|
||||
- I'd like to have my hero as resources\logo\hero.png
|
||||
- I'd like to have my current sign in / sign up logic copied to a new component, but with this hero, it should be modern looking - semi-transparent glassmorphism box
|
||||
- Should I have sign in or sign up? or compact both together
|
||||
2. Row 2
|
||||
- This should describe the problem - getting kids to do chores consistently - Describe how a system benefits:
|
||||
1. Develops Executive Function: Managing a "to-do list" teaches kids how to prioritize tasks, manage their time, and follow multi-step directions—skills that translate directly to schoolwork and future careers.
|
||||
2. Fosters a "Can-Do" Attitude: Completing a task provides a tangible sense of mastery. This builds genuine self-esteem because it’s based on actual competence rather than just empty praise.
|
||||
3. Encourages Accountability: A system moves chores from "Mom/Dad is nagging me" to "This is my responsibility." It teaches that their actions (or lack thereof) have a direct impact on the people they live with.
|
||||
4. Normalizes Life Skills: By the time they head out on their own, "adulting" won't feel like a mountain to climb. Cooking, cleaning, and organizing will be second nature rather than a stressful learning curve.
|
||||
5. Promotes Family Belonging: Contributing to the home makes a child feel like a teammate rather than a guest. It reinforces the idea that a family is a unit where everyone supports one another.
|
||||
3. Row 3
|
||||
- This should describe how my app helps to solve the problem by creating the system
|
||||
1. Chores can be created with customizable points that children have fun collecting.
|
||||
2. Customized reward offer insentives for a child to set a goal. (saving points, etc)
|
||||
3. Easy interface for children to see which chores need to be done and what rewards they can afford.
|
||||
4. Parental controls (hidden) behind a pin that offer powerful tools for managing the system
|
||||
- In this component we'll show some screen shots of the interface.
|
||||
4. Row 4
|
||||
- I'm not sure what else I should add for now
|
||||
|
||||
- How should I handle colors - should I follow my current theme in colors.css or should I adopt my logo / hero colors?
|
||||
- Should my landing page be more modern and sleek with animations? What kind?
|
||||
- Considerations for mobile should also be handled (whether in portrait or landscape mode)
|
||||
|
||||
- ***
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Backend Model
|
||||
|
||||
### Frontend Model
|
||||
|
||||
---
|
||||
|
||||
## Backend Implementation
|
||||
|
||||
## Backend Tests
|
||||
|
||||
- [ ]
|
||||
|
||||
---
|
||||
|
||||
## Frontend Implementation
|
||||
|
||||
- [x] Copy `resources/logo/full_logo.png` and `hero.png` to `frontend/vue-app/public/images/`
|
||||
- [x] Add landing CSS variables to `colors.css`
|
||||
- [x] Create `src/components/landing/LandingPage.vue` — orchestrator with sticky nav
|
||||
- [x] Create `src/components/landing/LandingHero.vue` — hero section with logo, tagline, glassmorphism CTA card
|
||||
- [x] Create `src/components/landing/LandingProblem.vue` — 5-benefit problem section
|
||||
- [x] Create `src/components/landing/LandingFeatures.vue` — 4-feature alternating layout with screenshot placeholders
|
||||
- [x] Create `src/components/landing/LandingFooter.vue` — dark footer with links
|
||||
- [x] Update router: add `/` → LandingPage route (meta: isPublic), update auth guard
|
||||
|
||||
## Frontend Tests
|
||||
|
||||
- [x] Update `authGuard.spec.ts`: fix redirect assertions (`/auth` → `/`) and add 3 landing-route guard tests
|
||||
- [x] Create `src/components/landing/__tests__/LandingHero.spec.ts`: renders logo, tagline, buttons; CTA clicks push correct routes
|
||||
|
||||
---
|
||||
|
||||
## Future Considerations
|
||||
|
||||
- Eventually add a pricing section (component)
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria (Definition of Done)
|
||||
|
||||
### Backend
|
||||
|
||||
- [ ]
|
||||
|
||||
### Frontend
|
||||
|
||||
- [x] Unauthenticated user visiting `/` sees the full landing page
|
||||
- [x] Sign In CTA routes to `/auth/login`
|
||||
- [x] Get Started CTA routes to `/auth/signup`
|
||||
- [x] Logged-in user visiting `/` is redirected to `/child` or `/parent`
|
||||
- [x] Unauthenticated user visiting any protected route is redirected to `/` (landing)
|
||||
- [x] All 4 rows render: Hero, Problem, Features, Footer
|
||||
- [x] Sticky nav appears on scroll
|
||||
- [x] Mobile: hero buttons stack vertically, grid goes single-column
|
||||
- [x] All colors use `colors.css` variables only
|
||||
|
||||
---
|
||||
|
||||
## Bugs
|
||||
|
||||
### BUG-001 — Landing page instantly redirects to `/auth` on first visit
|
||||
|
||||
**Status:** Fixed
|
||||
|
||||
**Description:**
|
||||
Visiting `/` as an unauthenticated user caused a visible flash of the landing page followed by an immediate hard redirect to `/auth`, making the landing page effectively unreachable.
|
||||
|
||||
**Root Cause:**
|
||||
`App.vue` calls `checkAuth()` on mount, which hits `/api/auth/me`. For an unauthenticated user this returns a `401`. The fetch interceptor in `api.ts` (`handleUnauthorizedResponse`) then attempted a token refresh, which also failed, and finally called `window.location.assign('/auth')`. The interceptor only exempted paths already starting with `/auth` — not the landing page at `/`.
|
||||
|
||||
**Solution:**
|
||||
|
||||
- [`frontend/vue-app/src/common/api.ts`](../frontend/vue-app/src/common/api.ts) — Added `if (window.location.pathname === '/') return` inside `handleUnauthorizedResponse()` so the unauthorized interceptor does not forcibly redirect away from the public landing page.
|
||||
- [`frontend/vue-app/src/stores/auth.ts`](../frontend/vue-app/src/stores/auth.ts) — Updated the cross-tab logout storage event handler to redirect to `/` instead of `/auth/login`, and skip the redirect entirely if already on `/`.
|
||||
242
.github/specs/archive/feat-schedule-enable-disable.md
vendored
Normal file
242
.github/specs/archive/feat-schedule-enable-disable.md
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
# Feature: Chore Schedule Enable/Disable Toggle
|
||||
|
||||
## Overview
|
||||
|
||||
**Goal:** Allow parents to pause and resume a chore schedule without deleting it. A paused schedule retains all its configuration (days, intervals, deadlines) but acts as if the chore had no schedule — it shows every day with no deadline or expiry — until re-enabled.
|
||||
|
||||
**User Story:**
|
||||
As a parent, I want to temporarily pause a chore's schedule so my child still sees it every day but it loses its deadline constraints, without losing the schedule configuration I've set up.
|
||||
|
||||
**Rules:**
|
||||
follow .github/copilot-instructions.md
|
||||
|
||||
---
|
||||
|
||||
## UI Design
|
||||
|
||||
### Toggle Placement
|
||||
|
||||
A dedicated row between the `ModalDialog` heading and the mode toggle ("Specific Days" / "Every X Days"):
|
||||
|
||||
```
|
||||
┌────────────────────────────────────┐
|
||||
│ 🛏️ Schedule Chore │ heading (unchanged)
|
||||
│ Make your bed │
|
||||
│ │
|
||||
│ Schedule ━━━━━━━━━━━━━━━● ON │ ← new toggle row
|
||||
│ │
|
||||
│ [Specific Days] [Every X Days] │ mode toggle (unchanged)
|
||||
│ ... │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- Adds ~44px of height; does not modify `ModalDialog.vue`.
|
||||
- Sits above all config, reflecting its role as the highest-priority decision ("Is this schedule even active?").
|
||||
|
||||
### Toggle Widget
|
||||
|
||||
Clean slider (no text inside the track) + external label:
|
||||
|
||||
- **ON:** Track = `var(--btn-primary)` (#667eea), label = "Enabled"
|
||||
- **OFF:** Track = `var(--form-input-border)` (#cbd5e1), label = "Paused"
|
||||
- **Thumb:** `#fff` with subtle box-shadow
|
||||
- **Dimensions:** 48×24px track, 20px circular thumb, 44px min-height row (mobile touch target)
|
||||
- Uses `role="switch"` and `aria-checked` for accessibility.
|
||||
|
||||
### Dim-on-Disable
|
||||
|
||||
When the toggle is OFF, the form body (mode toggle + all form fields) receives `opacity: 0.45; pointer-events: none`. The action buttons (Cancel / Save) remain fully interactive so the user can save the "paused" state.
|
||||
|
||||
### Color Rationale
|
||||
|
||||
Green (`--btn-green`) is reserved for "reward" semantics in this app. Brand blue (`--btn-primary`) is the correct choice for a generic on/off state, matching the mode toggle, day chips, and primary CTA button.
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Backend Model — `backend/models/chore_schedule.py`
|
||||
|
||||
- [x] Add field `enabled: bool = True` to `ChoreSchedule` dataclass
|
||||
- [x] Add `enabled=d.get('enabled', True)` to `from_dict()`
|
||||
- [x] Add `'enabled': self.enabled` to `to_dict()`
|
||||
|
||||
### Frontend Model — `frontend/vue-app/src/common/models.ts`
|
||||
|
||||
- [x] Add `enabled?: boolean` to `ChoreSchedule` interface
|
||||
|
||||
---
|
||||
|
||||
## Backend Implementation
|
||||
|
||||
### API — `backend/api/chore_schedule_api.py`
|
||||
|
||||
- [x] Accept `enabled` field in the schedule create/update endpoint payload
|
||||
- [x] Persist `enabled` value through to the database
|
||||
|
||||
### Scheduler Logic
|
||||
|
||||
- [x] In `scheduleUtils.ts` (`isScheduledToday`), return `true` when `schedule.enabled === false` — a paused chore shows every day, like an unscheduled chore:
|
||||
```typescript
|
||||
if (schedule.enabled === false) return true; // paused = show always, like an unscheduled chore
|
||||
```
|
||||
- [x] In `scheduleUtils.ts` (`getDueTimeToday`), return `null` when `schedule.enabled === false` — a paused chore has no deadline or expiry:
|
||||
```typescript
|
||||
if (schedule.enabled === false) return null; // paused = no deadline, no expiry
|
||||
```
|
||||
> Note: Scheduling is evaluated client-side. The original spec referenced Python; this was updated to reflect the actual architecture.
|
||||
> Note: Behavior was revised from "hide chore" to "show always, no deadline" — paused acts like the chore has no schedule at all.
|
||||
|
||||
### SSE Events
|
||||
|
||||
- [x] Existing `chore_schedule_updated` event is sufficient (the payload includes the full schedule object, which will now contain `enabled`)
|
||||
|
||||
---
|
||||
|
||||
## Backend Tests
|
||||
|
||||
### Unit Tests — `backend/tests/test_chore_schedule_api.py`
|
||||
|
||||
- [x] **test_create_schedule_enabled_by_default**: Create a schedule without specifying `enabled`; verify the persisted schedule has `enabled=True`
|
||||
- [x] **test_create_schedule_with_enabled_false**: Create a schedule with `enabled=False`; verify it persists correctly
|
||||
- [x] **test_update_schedule_toggle_enabled**: Create a schedule (enabled), update it with `enabled=False`, verify the change persists; toggle back to `True`, verify again
|
||||
- [x] **test_enabled_field_in_get_response**: Fetch a schedule via GET; verify the `enabled` field is present in the JSON response
|
||||
- [x] **test_enabled_invalid_value_returns_400**: Pass a non-boolean `enabled` value; verify 400 response
|
||||
|
||||
### Model Tests
|
||||
|
||||
- [x] **test_chore_schedule_from_dict_defaults_enabled**: Call `ChoreSchedule.from_dict({...})` without `enabled`; assert `enabled is True`
|
||||
- [x] **test_chore_schedule_from_dict_enabled_false**: Call `ChoreSchedule.from_dict({..., 'enabled': False})`; assert `enabled is False`
|
||||
- [x] **test_chore_schedule_to_dict_includes_enabled**: Create a `ChoreSchedule` instance; call `to_dict()`; assert `'enabled'` key is present with correct value
|
||||
|
||||
---
|
||||
|
||||
## Frontend Implementation
|
||||
|
||||
### Component — `frontend/vue-app/src/components/shared/ScheduleModal.vue`
|
||||
|
||||
#### Template
|
||||
|
||||
- [x] Add toggle row between `ModalDialog` open and mode toggle
|
||||
- [x] Wrap mode toggle + form content in `<div class="schedule-body" :class="{ disabled: !scheduleEnabled }">`
|
||||
- [x] Keep action buttons (Cancel / Save) outside the dim wrapper
|
||||
|
||||
#### Script
|
||||
|
||||
- [x] Add `const scheduleEnabled = ref<boolean>(props.schedule?.enabled ?? true)`
|
||||
- [x] Add `const origEnabled = props.schedule?.enabled ?? true` for dirty tracking
|
||||
- [x] Add `if (scheduleEnabled.value !== origEnabled) return true` to `isDirty` computed
|
||||
- [x] Include `enabled: scheduleEnabled.value` in both `setChoreSchedule` payloads (days + interval modes)
|
||||
|
||||
#### CSS
|
||||
|
||||
- [x] Add toggle row styles (`.schedule-toggle-row`, `.toggle-label`)
|
||||
- [x] Add toggle track/thumb styles (`.toggle-track`, `.toggle-track.on`, `.toggle-thumb`)
|
||||
- [x] Add dim wrapper styles (`.schedule-body`, `.schedule-body.disabled`)
|
||||
|
||||
### Design Tokens — `frontend/vue-app/src/assets/colors.css` (optional)
|
||||
|
||||
- [ ] Optionally add reusable toggle tokens (not needed — existing tokens used directly)
|
||||
|
||||
---
|
||||
|
||||
## Frontend Tests
|
||||
|
||||
### scheduleUtils Tests — `frontend/vue-app/src/__tests__/scheduleUtils.spec.ts`
|
||||
|
||||
- [x] **returns true when enabled is false on a scheduled day (days mode)**: A paused schedule always returns `true` regardless of day
|
||||
- [x] **returns true when enabled is false on an unscheduled day (days mode)**: A paused schedule returns `true` even on days that would normally be skipped
|
||||
- [x] **returns true when enabled is false on a hit day (interval mode)**: A paused interval schedule always returns `true`
|
||||
- [x] **returns true when enabled is false on a non-hit day (interval mode)**: A paused interval schedule returns `true` even on non-hit days
|
||||
- [x] **getDueTimeToday returns null when paused**: A paused schedule's `getDueTimeToday` returns `null` — no deadline, no expiry
|
||||
- [x] **treats enabled=undefined as enabled (backward compat)**: Schedules without `enabled` field behave as enabled
|
||||
|
||||
---
|
||||
|
||||
## CSS Spec
|
||||
|
||||
```css
|
||||
/* ── Enable/Disable Toggle ────────────────────── */
|
||||
|
||||
.schedule-toggle-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 1rem;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--form-label, #444);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.toggle-track {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 24px;
|
||||
border-radius: 999px;
|
||||
border: none;
|
||||
background: var(--form-input-border, #cbd5e1);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
padding: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toggle-track.on {
|
||||
background: var(--btn-primary, #667eea);
|
||||
}
|
||||
|
||||
.toggle-thumb {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.toggle-track.on .toggle-thumb {
|
||||
transform: translateX(24px);
|
||||
}
|
||||
|
||||
/* ── Dim wrapper when paused ──────────────────── */
|
||||
|
||||
.schedule-body {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.schedule-body.disabled {
|
||||
opacity: 0.45;
|
||||
pointer-events: none;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria (Definition of Done)
|
||||
|
||||
### Backend
|
||||
|
||||
- [x] `ChoreSchedule` model has `enabled: bool` field, defaulting to `True`
|
||||
- [x] API accepts and persists `enabled` in create/update
|
||||
- [x] Client-side scheduler: `isScheduledToday` returns `true` for paused — chore shows every day
|
||||
- [x] Client-side scheduler: `getDueTimeToday` returns `null` for paused — no deadline or expiry
|
||||
- [x] All backend unit tests pass (28/28)
|
||||
|
||||
### Frontend
|
||||
|
||||
- [x] Toggle row renders in `ScheduleModal` between heading and mode toggle
|
||||
- [x] Toggle uses `--btn-primary` for ON and `--form-input-border` for OFF
|
||||
- [x] Form body dims with `opacity: 0.45` and `pointer-events: none` when paused
|
||||
- [x] `enabled` field is included in save payloads for both modes
|
||||
- [x] Dirty detection tracks `enabled` changes
|
||||
- [x] Toggle meets 44px minimum touch target
|
||||
- [x] All frontend scheduleUtils tests pass (38/38)
|
||||
Reference in New Issue
Block a user