Skip to content
ilinxa/pro-ui

Team Quest Log

alphav0.2.1

Team quest overlay — an editable skippable quest name plus a milestone chapter timeline with done, current, and upcoming beats.

Category: GamificationUpdated: 2026-08-11Created: 2026-07-01Author: ilinxa

Context

The Team Narrative Framing surface of the gamification pack (E5, Autonomy + Relatedness). Two mountable surfaces over one team + its milestones: (a) a quest-name editor — display the resolved title (questName?.trim() || name) with an inline display↔edit toggle; clearing/saving blank reverts to the literal team name (the skip path, never forced); a quiet 'name your quest' invitation on the default; and (b) a milestone-chapter timeline — one vertical-rail beat per NarrativeChapter, ordered, each framing a milestone, with done / current ('you are here') / upcoming states derived from Milestone.done (current = the first not-done beat; unresolved milestoneId renders gracefully + dev-warns, never crashes). Deliberately lighter than gantt-timeline / event-calendar — chapters, not a time axis; it consumes no TaskItem[] and imports no other registry component. Emits narrative.chapter-viewed once per chapter per mount when a beat first scrolls into view (one IntersectionObserver, fire-once Set, SSR-guarded, click fallback). Ships as a light shadcn-style compound (no React.lazy) — headless TeamQuestLogRoot (title resolution + draft/edit state + beat derivation + telemetry + a forwardRef handle: focusNameEditor / scrollToChapter) + flat parts (TeamQuestNameEditor, TeamQuestChapters) + context-free primitives (QuestTitle, QuestNameField, ChapterRail, ChapterBeat, EmptyNarrative) + the TeamQuestLog assembly — so a timeline-only or name-only subset falls out. Controlled (D-06); portable — zero next/*, own types.ts slice; SSR-safe. Sixth and final component of the gamification-system.

Installation

Initialize shadcn (once per project)Seeds lib/utils.ts and components.json. Skip if you've already used any shadcn component.
pnpm dlx shadcn@latest init
Register the @ilinxa namespace (once per project)Add to your components.json. Merge with existing config.
"registries": {
  "@ilinxa": "https://ui.ilinxa.com/r/{name}.json"
}
Install the component
pnpm dlx shadcn@latest add @ilinxa/team-quest-log

Add -fixtures for dummy data:

pnpm dlx shadcn@latest add @ilinxa/team-quest-log-fixtures

Preview

Full overlay — live (name your quest)

Starts on the default team name. Name it, edit it, or clear it to revert — never forced. Beats emit narrative.chapter-viewed once when scrolled into view.

Team Aurora

Custom quest name + in-progress narrative

done · done · current (you are here) · upcoming — each visually distinct (icon + color).

The Road to Launch

Where it beganProject kickoffCompleted 3 Feb 2026
It moves!First playable buildCompleted 18 Mar 2026
Into the wild (beta)Closed beta
Doors openPublic launch
Looking back1.0 retrospective

All done vs none done

All done → every beat completed, no 'current'. None done → the first beat is 'current'.

The Road to Launch

Where it beganProject kickoffCompleted 3 Feb 2026
It moves!First playable buildCompleted 18 Mar 2026
Into the wild (beta)Closed betaCompleted 1 May 2026
Doors openPublic launchCompleted 1 May 2026
Looking back1.0 retrospectiveCompleted 1 May 2026

Fresh Start

Where it beganProject kickoff
It moves!First playable build
Into the wild (beta)Closed beta
Doors openPublic launch
Looking back1.0 retrospective

Edge cases

Empty narrative (quiet placeholder) · an unresolved-milestone beat (renders gracefully).

The Road to Launch

Your team's story will appear here as milestones are reached.

The Road to Launch

Where it beganProject kickoffCompleted 3 Feb 2026
It moves!First playable buildCompleted 18 Mar 2026
Into the wild (beta)Closed beta
Doors openPublic launch
Looking back1.0 retrospective
The lost chapter

Composed / lighter — timeline-only + name-only subsets

Hand-assembled from Root + one part each — the compound tree-shakes to a subset.

Where it beganProject kickoffCompleted 3 Feb 2026
It moves!First playable buildCompleted 18 Mar 2026
Into the wild (beta)Closed beta
Doors openPublic launch
Looking back1.0 retrospective

Team Aurora

Demo source

demo.tsxtsx

Usage

When to use

Reach for TeamQuestLog to give a team a light sense of story over its journey — a name for the quest and milestone beats framed as chapters. It is the E5 (Autonomy + Relatedness) surface of the gamification pack. The narrative is optional and skippable: a blank quest name falls back to the team's literal name, and the chapter timeline degrades quietly when no chapters are authored. It is deliberately lighter than the Gantt / calendar — chapters, not a time axis.

Basic example

import { TeamQuestLog } from "@/components/team-quest-log"

<TeamQuestLog
  team={team}               // { id, name, questName? }
  milestones={milestones}   // host-owned shared spine
  chapters={chapters}       // { id, title, milestoneId, order }
  onQuestNameChange={(name) => saveQuestName(team.id, name)} // "" reverts to team name
  onEvent={(e) => analytics.track(e.type, e)}                // narrative.chapter-viewed
/>

Never forced

The displayed title is questName?.trim() || name. Clearing the field (saving blank) reverts to the literal team name — a first-class skip path, no required-field validation, no nag. On the default name a quiet Name your quest invitation shows (never a blocking prompt).

Beat states

  • done — the milestone is complete (check marker).
  • current — the first not-done beat by order (the you are here pin).
  • upcoming — later not-done beats (hollow marker).

State is derived from milestones and conveyed by icon + color (never color alone). An unresolved milestoneId renders gracefully (as upcoming) and dev-warns — it never crashes.

Compose a lighter version

import { TeamQuestLogRoot, TeamQuestChapters } from "@/components/team-quest-log"

// Timeline-only — the name-editor code never enters this bundle:
<TeamQuestLogRoot team={team} milestones={milestones} chapters={chapters} onEvent={track}>
  <TeamQuestChapters />
</TeamQuestLogRoot>

Notes

  • Telemetry. onEvent emits narrative.chapter-viewed once per chapter per mount, when a beat first scrolls into view (IntersectionObserver). Omit onEvent and no observer attaches.
  • Imperative handle. A ref exposes focusNameEditor() and scrollToChapter(id) for onboarding / deep-link hosts.
  • Team-scoped.Only this team's narrative — no comparison, no other team's data.
  • Portable. No next/*, own types.ts slice, no other registry import; only the shadcn input / button primitives + lucide.

Features

  • Skippable quest name — displayed title = questName?.trim() || name; clearing reverts to the team name (never forced, no validation)
  • Milestone-chapter timeline — one vertical-rail beat per chapter, done / current / upcoming derived from milestones
  • Deterministic beat states — current = first not-done by order; all-done has no current; unresolved milestone renders gracefully + dev-warns
  • narrative.chapter-viewed telemetry — one IntersectionObserver, fire-once per chapter per mount, SSR-guarded with a click fallback
  • Non-color state cues (check / you-are-here pin / hollow) + accessible beat names — state reads without color
  • Light compound — headless Root + flat TeamQuestNameEditor / TeamQuestChapters + context-free primitives; timeline-only + name-only subsets fall out
  • Imperative handle — focusNameEditor() + scrollToChapter(id) for onboarding / deep-link hosts

Tags

team-quest-loggamificationnarrativemilestonestimelineteamcooperativetelemetry

Dependencies

shadcn primitives: button, input
npm peer deps: lucide-react@^1.11.0