Skip to content

Commit ccc33fe

Browse files
committed
docs: finalize portfolio README and product polish
1 parent aede7f1 commit ccc33fe

25 files changed

Lines changed: 638 additions & 134 deletions

README.md

Lines changed: 66 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
````md
12
<div align="center">
23

34
# DevBreak
@@ -20,13 +21,7 @@ DevBreak is a portfolio-grade productivity workspace built with Angular. It comb
2021

2122
The product is intentionally calm: it helps users move work forward without turning productivity into a noisy scoring system.
2223

23-
## Screenshots
24-
25-
> Add final screenshots before publishing.
26-
27-
| Workspace | Focus Mode | Settings |
28-
| --- | --- | --- |
29-
| `docs/screenshots/workspace.png` | `docs/screenshots/focus-mode.png` | `docs/screenshots/settings.png` |
24+
---
3025

3126
## Highlights
3227

@@ -35,12 +30,15 @@ The product is intentionally calm: it helps users move work forward without turn
3530
- Advanced filters, sorting, density modes, and search
3631
- Focus ownership mode with active-task workflow
3732
- Timer and Pomodoro sessions with resilient timestamp-based persistence
38-
- Adaptive wellness reminders for hydration, posture, and movement
33+
- Adaptive wellness reminders for hydration, posture, and movement recovery
3934
- Contextual recovery suggestions and lightweight wellness consistency tracking
4035
- Keyboard shortcut personalization with conflict prevention
36+
- Workspace and daily productivity reset actions
4137
- Dark/light themes, reduced-motion support, and responsive layouts
4238
- Cypress E2E coverage for core workflows, persistence, keyboard, and accessibility behavior
4339

40+
---
41+
4442
## Product Philosophy
4543

4644
DevBreak is not a Pomodoro clone and not a fitness tracker. It is a focused workspace that treats productivity and recovery as part of the same workflow.
@@ -53,6 +51,8 @@ Wellness guidance is intentionally subtle:
5351
- no invasive notifications
5452
- supportive summaries only after meaningful activity exists
5553

54+
---
55+
5656
## Architecture
5757

5858
The app uses a modular feature structure with service-owned state and presentational component boundaries.
@@ -70,29 +70,33 @@ src/app/
7070
services/
7171
models/
7272
services/
73-
```
73+
````
7474
7575
Key architectural choices:
7676
77-
- RxJS streams for timer, workspace mode, focus, wellness, and derived UI state
78-
- LocalStorage persistence with validation and safe fallbacks
79-
- Angular CDK DragDrop for board interactions
80-
- Focused presentational components for settings, timer actions, wellness cards, and insights
81-
- CSS variables for density, theme, and responsive ergonomics
82-
- Minimal global state, no backend assumptions, no heavyweight store layer
77+
* RxJS streams for timer, workspace mode, focus, wellness, and derived UI state
78+
* LocalStorage persistence with validation and safe fallbacks
79+
* Angular CDK DragDrop for board interactions
80+
* Focused presentational component boundaries for settings, timer actions, wellness cards, and insights
81+
* CSS variables for density, theme, and responsive ergonomics
82+
* Minimal global state, no backend assumptions, no heavyweight store layer
83+
84+
---
8385
8486
## Accessibility
8587
8688
DevBreak includes an accessibility pass across semantics, keyboard behavior, screen-reader feedback, and motion preferences.
8789
88-
- Landmark and heading structure
89-
- Accessible form labels and icon/control labels
90-
- Visible focus states
91-
- ESC handling and focus restoration
92-
- Keyboard activation parity for primary workflows
93-
- ARIA live regions for meaningful state changes
94-
- `prefers-reduced-motion` support
95-
- Cypress coverage for keyboard and accessibility-critical flows
90+
* Landmark and heading structure
91+
* Accessible form labels and icon/control labels
92+
* Visible focus states
93+
* ESC handling and focus restoration
94+
* Keyboard activation parity for primary workflows
95+
* ARIA live regions for meaningful state changes
96+
* `prefers-reduced-motion` support
97+
* Cypress coverage for keyboard and accessibility-critical flows
98+
99+
---
96100
97101
## Testing & Reliability
98102
@@ -102,11 +106,17 @@ npm run build
102106
npm run e2e
103107
```
104108

109+
Current validation status:
110+
111+
* 56 automated tests passing
112+
* Production build passing
113+
* Cypress workflow coverage stable
114+
105115
Coverage includes:
106116

107-
- Unit tests for timer persistence, wellness heuristics, shortcuts, filters, density, and component behavior
108-
- Cypress E2E tests for Kanban, focus sessions, persistence restore, keyboard workflows, and reduced-motion smoke coverage
109-
- Production build validation through Angular budgets
117+
* Unit tests for timer persistence, wellness heuristics, shortcuts, filters, density, and component behavior
118+
* Cypress E2E tests for Kanban, focus sessions, persistence restore, keyboard workflows, and reduced-motion smoke coverage
119+
* Production build validation through Angular budgets
110120

111121
Note for local Windows shells: if Cypress inherits `ELECTRON_RUN_AS_NODE`, clear it before running E2E.
112122

@@ -116,16 +126,20 @@ $env:CYPRESS_SKIP_VERIFY='true'
116126
npm run e2e
117127
```
118128

129+
---
130+
119131
## Tech Stack
120132

121-
- Angular
122-
- TypeScript
123-
- RxJS
124-
- SCSS
125-
- Angular CDK
126-
- Vitest / Angular unit testing
127-
- Cypress
128-
- GitHub Actions
133+
* Angular
134+
* TypeScript
135+
* RxJS
136+
* SCSS
137+
* Angular CDK
138+
* Vitest / Angular unit testing
139+
* Cypress
140+
* GitHub Actions
141+
142+
---
129143

130144
## Getting Started
131145

@@ -148,6 +162,8 @@ Production build:
148162
npm run build
149163
```
150164

165+
---
166+
151167
## Deployment
152168

153169
The app is static-build ready.
@@ -175,23 +191,30 @@ Install Command: npm ci
175191

176192
For SPA fallback routing, add a Vercel rewrite if routes are introduced later.
177193

194+
---
195+
178196
## GitHub Actions
179197

180198
Workflows are intentionally lightweight:
181199

182-
- CI: install, unit tests, production build
183-
- Pages deploy: install, unit tests, production build, upload static artifact
200+
* CI: install, unit tests, production build
201+
* Pages deploy: install, unit tests, production build, upload static artifact
184202

185203
Cypress remains available for local and future CI expansion without making the default pipeline heavy.
186204

205+
---
206+
187207
## Portfolio Notes
188208

189209
DevBreak demonstrates production-minded frontend work:
190210

191-
- product-oriented UX decisions
192-
- accessible keyboard-first interaction design
193-
- resilient local persistence
194-
- maintainable Angular boundaries
195-
- responsive operational UI
196-
- calm wellness-aware behavior
197-
- meaningful automated reliability coverage
211+
* product-oriented UX decisions
212+
* accessible keyboard-first interaction design
213+
* resilient local persistence
214+
* maintainable Angular boundaries
215+
* responsive operational UI
216+
* calm wellness-aware behavior
217+
* meaningful automated reliability coverage
218+
219+
```
220+
```

src/app/features/kanban/components/kanban-board/kanban-board-archive.component.scss

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,26 @@
4444
overscroll-behavior: contain;
4545
padding: 0 0.85rem 0.85rem;
4646
scrollbar-width: thin;
47-
scrollbar-color: color-mix(in srgb, var(--accent) 42%, transparent) transparent;
47+
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
4848
}
4949

5050
.kanban-archive__list::-webkit-scrollbar {
51-
width: 8px;
51+
width: 9px;
52+
}
53+
54+
.kanban-archive__list::-webkit-scrollbar-track {
55+
border-radius: 999px;
56+
background: var(--scrollbar-track);
5257
}
5358

5459
.kanban-archive__list::-webkit-scrollbar-thumb {
60+
border: 2px solid var(--scrollbar-track);
5561
border-radius: 999px;
56-
background: color-mix(in srgb, var(--accent) 34%, transparent);
62+
background: var(--scrollbar-thumb);
63+
}
64+
65+
.kanban-archive__list::-webkit-scrollbar-thumb:hover {
66+
background: var(--scrollbar-thumb-hover);
5767
}
5868

5969
@media (max-width: 980px) {

src/app/features/kanban/components/kanban-board/kanban-board-toolbar.component.scss

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.kanban-board__toolbar {
2-
display: grid;
3-
grid-template-columns: minmax(14rem, 22rem) minmax(0, 1fr) auto auto;
2+
display: flex;
3+
flex-wrap: wrap;
44
align-items: end;
55
gap: var(--kb-toolbar-gap);
66
flex: 0 0 auto;
@@ -9,8 +9,9 @@
99

1010
.kanban-search {
1111
display: grid;
12+
flex: 1 1 16rem;
1213
gap: 0.3rem;
13-
width: min(100%, 22rem);
14+
max-width: 22rem;
1415
min-width: 0;
1516
}
1617

@@ -50,16 +51,11 @@
5051

5152
.kanban-filters {
5253
display: flex;
54+
flex: 999 1 22rem;
55+
flex-wrap: wrap;
5356
align-items: center;
5457
gap: 0.4rem;
5558
min-width: 0;
56-
overflow-x: auto;
57-
padding-bottom: 0.05rem;
58-
scrollbar-width: none;
59-
}
60-
61-
.kanban-filters::-webkit-scrollbar {
62-
display: none;
6359
}
6460

6561
.kanban-filter-chip {
@@ -108,6 +104,7 @@
108104

109105
.kanban-sort {
110106
display: grid;
107+
flex: 0 1 12rem;
111108
gap: 0.3rem;
112109
min-width: 9.8rem;
113110
}
@@ -127,6 +124,8 @@
127124

128125
.kanban-density {
129126
display: inline-flex;
127+
flex: 0 0 auto;
128+
flex-wrap: wrap;
130129
align-items: center;
131130
gap: 0.2rem;
132131
min-width: 0;
@@ -165,13 +164,44 @@
165164
color: var(--accent);
166165
}
167166

167+
.kanban-toolbar-action {
168+
flex: 0 0 auto;
169+
min-width: 0;
170+
padding: var(--kb-chip-padding-y) var(--kb-chip-padding-x);
171+
border: 1px solid var(--border-subtle);
172+
border-radius: 999px;
173+
background: transparent;
174+
color: var(--text-muted);
175+
font: inherit;
176+
font-size: calc(var(--kb-control-font-size) - 0.06rem);
177+
font-weight: 800;
178+
cursor: pointer;
179+
}
180+
181+
.kanban-toolbar-action:hover,
182+
.kanban-toolbar-action:focus-visible {
183+
border-color: color-mix(in srgb, var(--accent) 42%, transparent);
184+
background: color-mix(in srgb, var(--accent) 8%, transparent);
185+
color: var(--text-secondary);
186+
}
187+
168188
@media (max-width: 980px) {
169189
.kanban-board__toolbar {
170-
grid-template-columns: minmax(0, 1fr);
171190
align-items: stretch;
172191
}
173192

193+
.kanban-search,
174194
.kanban-sort {
195+
flex-basis: 100%;
196+
max-width: none;
175197
min-width: 0;
176198
}
199+
200+
.kanban-filters {
201+
flex-basis: 100%;
202+
}
203+
204+
.kanban-density {
205+
justify-content: flex-start;
206+
}
177207
}

src/app/features/kanban/components/kanban-board/kanban-board.component.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ <h2 id="kanban-title">Kanban</h2>
6363
{{ density.label }}
6464
</button>
6565
</div>
66+
67+
<button
68+
type="button"
69+
class="kanban-toolbar-action"
70+
(click)="resetWorkspace()">
71+
Reset workspace
72+
</button>
6673
</div>
6774

6875
<app-task-create (createTask)="createTask($event)"></app-task-create>

src/app/features/kanban/components/kanban-board/kanban-board.component.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ describe('KanbanBoardComponent', () => {
2929
| 'createTask'
3030
| 'updateTask'
3131
| 'handleDrop'
32+
| 'resetWorkspace'
3233
>;
3334
let shortcutService: Pick<ShortcutService, 'formatCombo' | 'getCombo' | 'matches'>;
3435
let workspaceModeService: Pick<WorkspaceModeService, 'getSelectedMode'>;
@@ -62,6 +63,7 @@ describe('KanbanBoardComponent', () => {
6263
createTask: vi.fn(() => tasks),
6364
updateTask: vi.fn(() => tasks),
6465
handleDrop: vi.fn(() => tasks),
66+
resetWorkspace: vi.fn(() => []),
6567
};
6668
shortcutService = {
6769
formatCombo: vi.fn((combo: string) => combo),
@@ -110,6 +112,15 @@ describe('KanbanBoardComponent', () => {
110112
expect(component.tasksFor('todo').map((item: Task) => item.id)).toEqual(['2']);
111113
});
112114

115+
it('keeps completed tasks visible in the default active board', () => {
116+
const component = createComponent();
117+
118+
expect(component.tasksFor('done').map((item: Task) => item.title)).toEqual([
119+
'Done alpha',
120+
'Done beta',
121+
]);
122+
});
123+
113124
it('blocks focus task actions outside Focus mode', () => {
114125
const component = createComponent();
115126

@@ -144,6 +155,18 @@ describe('KanbanBoardComponent', () => {
144155
expect(wellnessReminderEngine.recordTaskCreated).toHaveBeenCalled();
145156
});
146157

158+
it('resets the workspace through the kanban service', () => {
159+
const component = createComponent();
160+
161+
component.activeQuickAddStatus = 'todo';
162+
component.resetWorkspace();
163+
164+
expect(kanbanService.resetWorkspace).toHaveBeenCalled();
165+
expect(component.tasks).toEqual([]);
166+
expect(component.activeQuickAddStatus).toBeNull();
167+
expect(component.boardAnnouncement).toBe('Workspace reset');
168+
});
169+
147170
function createComponent(): any {
148171
return new KanbanBoardComponent(
149172
kanbanService as KanbanService,

0 commit comments

Comments
 (0)