@emberai-engg/task-board
v0.6.0
Published
Reusable Kanban task board component
Readme
@emberai-engg/task-board
Reusable Kanban task board component with built-in create/detail UI, threaded discussions with highlight-to-comment, structured outstanding questions, file attachments backed by Google Cloud Storage, and a WYSIWYG markdown editor.
Installation
npm install @emberai-engg/task-boardThen import the bundled stylesheet once at app start (e.g. in app/layout.tsx):
import '@emberai-engg/task-board/styles.css';That's it. The stylesheet is self-contained — Tailwind utilities used by the
package are pre-compiled into it, so you don't need to add anything to your own
Tailwind config or @source paths. The bundled CSS deliberately omits the
Tailwind preflight so it won't trample your app's own resets.
Quick Start — list page only (slide-over detail)
The default <TaskBoard> ships with a built-in <TaskDetailPanel> slide-over
that opens when a task card is clicked.
import { TaskBoardProvider, TaskBoard } from '@emberai-engg/task-board';
import '@emberai-engg/task-board/styles.css';
import { apiClient } from './lib/api';
function App() {
const user = useAuth();
return (
<TaskBoardProvider
apiClient={apiClient}
user={user}
projects={[{ slug: 'my-project', name: 'My Project' }]}
>
<TaskBoard onShareFeedback={() => router.push('/feedback')} />
</TaskBoardProvider>
);
}Quick Start — list + dedicated detail route
For larger workflows (description sections, outstanding questions, attachments,
threads with highlight-to-comment), pair <TaskBoard> on the list route with
<TaskDetailView> on a separate detail route.
// app/task-board/page.tsx
function TaskBoardPage() {
const router = useRouter();
return (
<TaskBoardProvider apiClient={apiClient} user={user} projects={projects}>
<TaskBoard
onTaskOpen={(task) => router.push(`/task-board/${task.id}?project=${task.project_slug}`)}
/>
</TaskBoardProvider>
);
}
// app/task-board/[taskId]/page.tsx
function TaskDetailPage({ params }: { params: { taskId: string } }) {
const router = useRouter();
return (
<TaskBoardProvider apiClient={apiClient} user={user} projects={projects}>
<TaskDetailView
taskId={params.taskId}
onBack={() => router.push('/task-board')}
onNavigateToTask={(id, slug) => router.push(`/task-board/${id}?project=${slug}`)}
onDeleted={() => router.push('/task-board')}
/>
</TaskBoardProvider>
);
}When onTaskOpen is set, <TaskBoard> skips its built-in slide-over panel —
the consumer owns navigation.
What the detail page gives you
- Inline-editable title, click-to-edit
- Status / priority / share / delete in the header
- Four description sections (Problem, User Story, Proposed Behavior, Acceptance Criteria), each with a Draft/Approved toggle
- WYSIWYG markdown editor — bold/italic, H2, bullet/numbered lists with
multi-line support and auto-incrementing numbers, blockquote, inline code,
@mentions. Stores markdown on disk via
mdToHtml/htmlToMdround-trip. - Outstanding Questions — structured list with awaiting/answered status, per-question replies, mark-answered/reopen, author-only delete
- Attachments — three groups (Images / Files / Links & recordings), GCS upload, signed read URLs, hover-to-delete
- Threads panel — Threads + Activity tabs, collapse toggle (persisted), active/completed filter, per-thread title and complete/reopen, thread composer with attachments and public/internal toggle
- Highlight-to-comment — select text in a description section to attach a
thread anchor; the source text stays wrapped in a yellow
<mark>so you can see where threads attach. Clicking the mark (or the anchor pill on a thread card) scrolls back to the section and pulses the highlight. - Edit + delete on messages and threads — hover a message you wrote in a thread for an Edit / Delete kebab. The thread-detail header has its own kebab (Edit title + Delete thread). Deleting a thread cascades to its replies on the backend.
Props / Config Reference
TaskBoardProvider
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| apiClient | ApiClient | Yes | Axios-like HTTP client with auth headers |
| user | TaskBoardUser | Yes | Current logged-in user |
| projects | Project[] | No | Available projects |
| columns | ColumnConfig[] | No | Column definitions (defaults to 8-column kanban) |
| priorities | PriorityConfig[] | No | Priority levels |
| tags | TagConfig[] | No | Predefined tags |
| apiBasePath | string | No | API prefix (defaults to /api/v1/taskboard) |
| internalLabel | string | No | Label on internal-only comment chips. Defaults to "Internal". |
| features | object | No | Feature flags for enabling/disabling features |
| onTaskCreate | (task) => void | No | Callback on task creation |
| onTaskUpdate | (task) => void | No | Callback on task update |
| onTaskDelete | (id) => void | No | Callback on task deletion |
| onError | (error) => void | No | Error handler callback |
TaskBoard
| Prop | Type | Description |
|------|------|-------------|
| className | string | CSS class for the outer container |
| headerActions | ReactNode | Additional buttons in the header |
| onShareFeedback | () => void | Callback for Share Feedback button. Hidden if omitted. |
| onTaskOpen | (task) => void | When provided, the built-in slide-over panel is suppressed and the consumer owns navigation. |
| renderTaskDetail | function | Override for task detail panel (built-in slide-over used if omitted) |
| renderCreateTask | function | Override for create task modal (built-in used if omitted) |
TaskDetailView
| Prop | Type | Description |
|------|------|-------------|
| taskId | string | Required. The task to show. |
| onBack | () => void | Callback for the back link. Use this for SPA routing. |
| backHref | string | href for the back link if onBack is omitted. |
| breadcrumb | ReactNode | Slot rendered above the main content (e.g. consumer breadcrumb bar). |
| onDeleted | (task) => void | Called after successful delete; consumer should navigate away. |
| onNavigateToTask | (id, projectSlug) => void | Provide to enable prev/next buttons. |
| buildShareUrl | (task) => string | Override the share URL. Defaults to ${origin}/task-board/${id}?project=${slug}. |
Hooks API
| Hook | Purpose |
|------|---------|
| useTaskBoard() | Board state: projects, tasks, loading, pagination |
| useTaskActions(...) | CRUD: create, update, delete, move tasks |
| useTaskDetail(taskId) | Single task: comments, activity, field updates |
| useTaskQuestions(taskId, initial?) | Outstanding Questions for a task: list + create / update / delete / reply |
| useTaskAttachments(taskId, initial?) | Attachments: list + uploadFile / addLink / remove |
| useHighlightAnchor() | Selection-driven anchor flow: bubble + pendingAnchor + focusAnchor |
| useShareLink() | Copy shareable task URLs |
Feature Flags
<TaskBoardProvider
features={{
dragAndDrop: true,
comments: true,
mentions: true,
notifications: true,
internalComments: true,
tags: true,
sharing: true,
filters: true,
unreadIndicators: true,
}}
/>Backwards-compatibility notes (v0.3 → v0.4)
- The
StructuredDescription.open_questionsfield still exists and is preserved on existing tasks, but is no longer rendered in the description editor — the Outstanding Questions feature replaces it. Tasks created before v0.4 keep their content; new tasks have an emptyopen_questionsfield. Commentgained optionalparent_id,title,thread_status,anchor,attachment_ids. Existing comments without these fields render the same as before.
Backend
The backend-reference/ folder contains a Python FastAPI reference implementation:
models/taskboard.py— Pydantic models (Tasks / Comments / Threads / Questions / Attachments + theThreadAnchorschema)api/taskboard.py— full FastAPI router covering all v0.4 endpoints, including the threads, questions, and attachments routesservices/gcs_storage.py— Google Cloud Storage helper for file uploads. Supports inline service-account env vars,GOOGLE_APPLICATION_CREDENTIALS, and Application Default Credentialsservices/config_snippet.py— settings fields and sample.envvalues
Add google-cloud-storage>=2.18.0 to requirements.txt. Without
GCP_STORAGE_BUCKET set, link attachments still work but image/file uploads
return a clean 503.
See backend-reference/README.md for the
adaptation guide.
Development
npm install
npm run dev # Watch mode
npm run build # Production build
npm test # Run testsLicense
Private — internal use only.
