Files
NGRX-Playground/src/app/features/tasks/feature/tasks-page.component.html
T

188 lines
6.6 KiB
HTML

<section class="tasks-page">
<header class="hero">
<div class="hero-copy">
<div class="hero-badges">
<p class="eyebrow">Signal Store Feature</p>
<span class="hero-chip">Devtools Connected</span>
</div>
<h1>Delivery Board</h1>
<p class="lead">
A realistic feature slice with entity state, asynchronous updates,
persistence, and a clean operator flow for scalable Angular
teams.
</p>
</div>
<dl class="summary-grid">
<div>
<dt>Total</dt>
<dd>{{ store.totalCount() }}</dd>
</div>
<div>
<dt>Active</dt>
<dd>{{ store.activeCount() }}</dd>
</div>
<div>
<dt>Completed</dt>
<dd>{{ store.completedCount() }}</dd>
</div>
<div>
<dt>Synced</dt>
<dd>{{ store.lastSyncedLabel() }}</dd>
</div>
</dl>
</header>
<section class="workspace">
<form
class="composer"
(submit)="createTask(); $event.preventDefault()">
<div class="section-heading">
<div>
<p class="section-kicker">Compose</p>
<h2>Capture the next delivery item</h2>
</div>
<p class="section-note">
Fast input for planning work without leaving the board.
</p>
</div>
<div class="composer-grid">
<label>
<span>Task title</span>
<input
type="text"
placeholder="Add a task with product value"
[value]="draftTitle()"
(input)="updateDraftTitle($event)" />
</label>
<label>
<span>Priority</span>
<select
[value]="store.draftPriority()"
(change)="updateDraftPriority($event)">
@for (priority of priorities; track priority) {
<option [value]="priority">{{ priority }}</option>
}
</select>
</label>
<button
type="submit"
class="primary-action"
[disabled]="!canCreateTask()">
Create task
</button>
</div>
</form>
<div class="toolbar">
<div class="section-heading">
<div>
<p class="section-kicker">Refine</p>
<h2>Focus the board</h2>
</div>
<p class="section-note">
Search, segment, refresh, or archive completed work.
</p>
</div>
<div class="toolbar-grid">
<label class="search">
<span>Search</span>
<input
type="search"
placeholder="Filter tasks"
[value]="store.searchTerm()"
(input)="updateSearchTerm($event)" />
</label>
<div
class="filters"
role="group"
aria-label="Task filters">
@for (filter of filters; track filter) {
<button
type="button"
[class.active]="store.filter() === filter"
(click)="store.setFilter(filter)">
{{ filter }}
</button>
}
</div>
<div
class="toolbar-divider"
aria-hidden="true"></div>
<div
class="toolbar-actions"
role="group"
aria-label="Task actions">
<button
type="button"
class="ghost-action"
(click)="store.loadTasks()">
Refresh
</button>
<button
type="button"
class="primary-action"
[disabled]="!store.hasCompletedTasks() || store.loading()"
(click)="store.archiveCompleted()">
Archive completed
</button>
</div>
</div>
</div>
@if (store.error(); as errorMessage) {
<p
class="feedback error"
role="alert">
{{ errorMessage }}
</p>
}
@if (store.loading()) {
<p class="feedback">Synchronizing tasks...</p>
}
<ul class="task-list">
@for (task of store.filteredTasks(); track task.id) {
<li
class="task-card"
[class.done]="task.completed">
<div class="task-main">
<div class="task-status">
<span
class="priority-mark"
[class.high]="task.priority === 'high'"
[class.medium]="task.priority === 'medium'"
[class.low]="task.priority === 'low'"></span>
<button
type="button"
class="toggle"
[attr.aria-pressed]="task.completed"
(click)="store.toggleTask(task.id)">
{{ task.completed ? 'Completed' : 'Open' }}
</button>
</div>
<div class="task-copy">
<h2>{{ task.title }}</h2>
<div class="task-meta">
<span class="meta-pill">{{ task.priority }}</span>
<span>Updated {{ task.updatedAt }}</span>
</div>
</div>
</div>
</li>
} @empty {
<li class="empty-state">{{ store.emptyStateMessage() }}</li>
}
</ul>
</section>
</section>