The committed .ts/.tsx files were written through a shell heredoc that stripped every quote character, so types.ts, App.tsx, api.ts, TaskList.tsx, TaskRow.tsx, TaskCard.tsx and AppShell.tsx were not valid TypeScript. Nothing surfaced this because index.js imported ./App, which resolved to the Create React App template App.js ahead of App.tsx, and typescript was never added as a dependency, so the broken files were never compiled. The preview was showing the CRA starter page. Rewritten and transferred as a binary archive rather than through a shell, with tsc and the test suite as the check that the files actually parse. Priority 1 (blocking) - TaskForm.tsx implemented: title, description, assignee, status, due date and priority, validated with react-hook-form and actionable messages. - App.tsx is now a real container wired to the service layer, replacing the hardcoded mock markup. Filtering by My Tasks / All Tasks / Blocked is driven by the signed-in user instead of a hardcoded id. - Edit, delete and inline status changes work from both the table and the cards. Priority 2 - Accessibility: skip link, ARIA tablist with arrow keys, table roles, dialog with focus trap and restore, aria-invalid and aria-describedby on fields, polite live region, 44px targets. Contrast corrected to meet AA: buttons use #2563eb and accent text #1d4ed8, since the mockup #3b82f6 reaches only 3.7:1 behind white text. - Error handling: ErrorBoundary, load failure with retry, per-filter empty states, blocked guidance, and save failures that keep the dialog and the user's input on screen. - 17 integration tests replace the CRA placeholder test. Removed the dead CRA template files (App.js, index.js, logo.svg, reportWebVitals.js, App_temp.css) and added tsconfig.json plus typescript. Verified in devbox 306: tsc --noEmit clean, 17/17 tests pass, CI build compiles with no lint warnings. Screenshots of all six states are in frontend/preview/.
152 lines
2.3 KiB
CSS
152 lines
2.3 KiB
CSS
/* AppShell layout: header, team sidebar, main region */
|
|
.app-shell {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: #f8fafc;
|
|
}
|
|
|
|
/* Header */
|
|
.app-header {
|
|
background-color: white;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
box-shadow: 0 1px 3px 0 rgba(15, 23, 42, 0.06);
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 10;
|
|
}
|
|
|
|
.app-header-content {
|
|
max-width: 1440px;
|
|
margin: 0 auto;
|
|
padding: 0.875rem 2rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.app-logo h1 {
|
|
font-size: 1.375rem;
|
|
font-weight: 700;
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
.app-header-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.user-profile {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.625rem;
|
|
}
|
|
|
|
.user-name {
|
|
font-weight: 500;
|
|
color: #334155;
|
|
font-size: 0.9375rem;
|
|
}
|
|
|
|
/* Body */
|
|
.app-body {
|
|
flex: 1;
|
|
display: flex;
|
|
max-width: 1440px;
|
|
margin: 0 auto;
|
|
width: 100%;
|
|
}
|
|
|
|
/* Sidebar */
|
|
.app-sidebar {
|
|
width: 264px;
|
|
flex: none;
|
|
background-color: white;
|
|
border-right: 1px solid #e2e8f0;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.sidebar-section {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.sidebar-heading {
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
color: #6b7280;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
margin-bottom: 0.875rem;
|
|
}
|
|
|
|
.team-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
list-style: none;
|
|
}
|
|
|
|
.team-member {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.625rem;
|
|
padding: 0.5rem;
|
|
border-radius: 0.375rem;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.team-member:hover {
|
|
background-color: #f1f5f9;
|
|
}
|
|
|
|
.member-name {
|
|
flex: 1;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
color: #334155;
|
|
}
|
|
|
|
.online-indicator {
|
|
width: 8px;
|
|
height: 8px;
|
|
background-color: #059669;
|
|
border-radius: 50%;
|
|
flex: none;
|
|
}
|
|
|
|
/* Main */
|
|
.app-main {
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding: 1.75rem 2rem;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.app-header-content {
|
|
padding: 0.75rem 1rem;
|
|
}
|
|
|
|
.app-logo h1 {
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
.user-name {
|
|
display: none;
|
|
}
|
|
|
|
.app-body {
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Team list is secondary on small screens; the task list gets the room. */
|
|
.app-sidebar {
|
|
display: none;
|
|
}
|
|
|
|
.app-main {
|
|
padding: 1rem;
|
|
padding-bottom: 5.5rem; /* clear of the floating Create Task button */
|
|
}
|
|
}
|