# ActionFocusSession

> The focus timebox for one task in the actions cockpit: a live billable clock with approve, start, pause, run and done, and a burst of sparkles on completion.

## Facts

- **Product**: Max
- **Family**: Motion
- **Global**: `window.DigitalCrew.ActionFocusSession`
- **Source**: `max-agent: src/features/actions/ui/cockpit/action-focus-session.tsx`
- **Import in the app**: `import { ActionFocusSession } from "@/features/actions/ui/cockpit/action-focus-session";`
- **Live preview**: /design/components/action-focus-session/preview.html
- **Page**: /design/components/action-focus-session

## Guidelines

**Use it for** working one action at a time, such as an email, a LinkedIn message or a call log.

**What you provide**
- `task`: a `TaskDto`. It reads `status`, `estimatedMinutes` (default 15), `executionStartedAt`, `executionElapsedSeconds` and `dueAt`.
- `reasoning`: a `SuggestionReasoning`; its `rationale` shows for suggestions.
- `automatable`, `running`, `saving` and `runLabel`, plus optional `runDisabledReason` and `runSummary` (what Run sends, from which account).
- `onApprove`, `onReject`, `onStart`, `onPause`, `onRun`, and `onComplete()` returning `Promise<boolean>`.

**Anatomy**
- A `rounded-xl border px-4 py-3 shadow-sm backdrop-blur-xl` box, tinted `primary` while working, `amber-500` in overtime and `emerald-500` when done.
- A `text-[10px] uppercase tracking-[0.16em]` eyebrow over a `text-sm font-semibold` label, with `text-[11px]` pills for the billable minutes and the due date.
- The clock uses [`AnimateDigits`](/design/components/animate-digits.md) (`text-lg`, `sm:text-xl`). It counts down, then up as "+mm:ss" in overtime.
- A `h-1` bar up to 200px wide springs with stiffness 100 and damping 20.
- Buttons change with status: Approve and Dismiss; Start or Resume; then amber Pause, Run and Done.

**Behaviour**
- The clock runs only while the task is `in_progress`. Approval and deadlines never start it, and a pause keeps the seconds worked.
- Done shows "Beautiful. That's done." as five sparkles rise 80px over 0.65s. It reverts if `onComplete` resolves false.

**Don't** drive the clock from `dueAt`, or show Run without saying what it sends.

## API

```ts
export interface ActionFocusSessionProps {
    task: TaskDto;
    reasoning: SuggestionReasoning;
    automatable: boolean;
    running: boolean;
    saving: boolean;
    runLabel: string;
    runDisabledReason?: string | null;
    /**
     * What pressing Run will actually do, from the action catalogue — e.g.
     * "Sends the email now from your connected mailbox." The catalogue has
     * carried this copy for every automatable kind since it was written, and
     * nothing rendered it: the least reversible button in the product said only
     * "Run".
     */
    runSummary?: string | null;
    onApprove(): Promise<void>;
    onReject(): Promise<void>;
    onStart(): Promise<void>;
    onPause(): Promise<void>;
    onRun(): Promise<void>;
    onComplete(): Promise<boolean>;
}
export declare function ActionFocusSession({ task, reasoning, automatable, running, saving, runLabel, runDisabledReason, runSummary, onApprove, onReject, onStart, onPause, onRun, onComplete, }: ActionFocusSessionProps): JSX.Element;
```

## Example

```html
<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
  var MIN = 60 * 1000;
  function task(over) {
    var now = Date.now(), due = new Date(now + 24 * 60 * MIN);
    due.setHours(17, 0, 0, 0);
    return Object.assign({
      id: "8b1d4c2a-0000-4000-8000-000000000001",
      title: "Send Claire Dubois the Thursday call agenda",
      description: "Why: she confirmed Thursday 10:00 CET and asked for the invite to go to her assistant.",
      status: "in_progress", priority: "high", assigneeType: "user", assigneeUserId: "u-mathieu", assigneeAgent: null,
      executionMode: "manual", dueAt: due.toISOString(), sourceType: "inbox",
      sessionId: null, prospectId: "p-claire", sourceSummaryId: null, sourceTranscriptVersionId: null,
      approvedAt: new Date(now - 40 * MIN).toISOString(), completedAt: null,
      createdAt: new Date(now - 2 * 60 * MIN).toISOString(), updatedAt: new Date(now - 4 * MIN).toISOString(),
      actionType: "send_email", channel: "email", automationStatus: "idle", estimatedMinutes: 15,
      executionStartedAt: new Date(now - (4 * MIN + 12 * 1000)).toISOString(), executionElapsedSeconds: 0
    }, over || {});
  }
  var REASONING = { notes: null, rationale: "She confirmed Thursday and asked for the invite to go to her assistant.", mentionedDate: null };
  function wait(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
  function App() {
    var t = React.useState(function () { return task(); }), current = t[0], setTask = t[1];
    var k = React.useState(0), key = k[0], setKey = k[1];
    var r = React.useState(false), running = r[0], setRunning = r[1];
    return h("div", { className: "mx-auto max-w-[560px]" },
      h(DC.ActionFocusSession, {
        key: key, task: current, reasoning: REASONING, automatable: true, running: running, saving: false,
        runLabel: "Run task", runSummary: "Sends the email now from your connected mailbox. Sending as mathieu@digitalcrew.ai.",
        onApprove: function () { setTask(task({ status: "approved", executionStartedAt: null })); return wait(0); },
        onReject: function () { return wait(0); },
        onStart: function () { setTask(Object.assign({}, current, { status: "in_progress", executionStartedAt: new Date().toISOString() })); return wait(0); },
        onPause: function () {
          var worked = (current.executionElapsedSeconds || 0) + Math.floor((Date.now() - Date.parse(current.executionStartedAt)) / 1000);
          setTask(Object.assign({}, current, { status: "approved", executionStartedAt: null, executionElapsedSeconds: worked }));
          return wait(0);
        },
        onRun: function () { setRunning(true); return wait(1400).then(function () { setRunning(false); }); },
        onComplete: function () {
          return wait(900).then(function () {
            setTimeout(function () { setTask(task()); setKey(key + 1); }, 2600);
            return true;
          });
        }
      }));
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>
```

## More in Motion

- [CardResize](/design/components/card-resize.md): A container that tweens its width and height when its content changes (`.t-resize`).
- [ChapterScrubber](/design/components/chapter-scrubber.md): A rail of ticks, one per chapter, that swell like a dock under the pointer, with a card previewing the hovered chapter.
- [ContainerScroll](/design/components/container-scroll.md): A hero block where a device-like frame starts tilted back in 3D and straightens as the page scrolls, with the title drifting up.
- [FeatureSlideshow](/design/components/feature-slideshow.md): A step-by-step feature explainer: an accordion of steps on the left that advances on a timer, a progress line on the open step, and a large media slot on the right.
- [FirstBentoAnimation](/design/components/first-bento-animation.md): A self-playing chat vignette for the bento grid: a user asks for a meeting slot, the Digital Crew avatar shows typing dots, then the reply streams in as a `Reasoning` block.
- [FourthBentoAnimation](/design/components/fourth-bento-animation.md): A scheduling vignette for the bento grid: a week ruler, three task bars that spring into place, and a time cursor that follows the pointer.
- [HyperText](/design/components/hyper-text.md): A monospaced, uppercase text effect: letters scramble through random characters, then lock in from left to right.
- [InfiniteSlider](/design/components/infinite-slider.md): A JavaScript-driven endless strip (Framer Motion) that renders its children twice and slides them in a loop, optionally slowing on hover.
- [JoyfulTaskCheckbox](/design/components/joyful-task-checkbox.md): A task checkbox with a small, restrained completion moment: one soft bloom, two sparks, then stillness.
- [Lottie](/design/components/lottie.md): The lottie-react player, exported as-is, for the Lottie animations that ship with Crew OS (`animations/*.json`).
- [Marquee](/design/components/marquee.md): An endless CSS scroll of repeated content, horizontal or vertical, speed set with two CSS variables.
- [MotionScale](/design/components/motion-scale.md): The shared motion scale made visible: the same distance travelled on each duration token and each easing, with a Replay button.
- [NumberFlowCounter](/design/components/number-flow-counter.md): A small dark pill that shows one number at a time and rolls its digits to the next value every two seconds (NumberFlow).
- [OrbitingCircles](/design/components/orbiting-circles.md): Places its children on a circular orbit around the centre of the parent and spins them, with an optional faint ring for the path.
- [ShimmerText](/design/components/shimmer-text.md): A status line with a light band sweeping across its glyphs — Max's "Thinking…" and live tool lines (`.t-shimmer`).
- [SignatureGradient](/design/components/signature-gradient.md): The signature Digital Crew gradient — indigo → pink → amber (`dc-andre`, `dc-camille`, `dc-amber`) — in its four forms.
- [SkeletonReveal](/design/components/skeleton-reveal.md): A skeleton that cross-blurs into the loaded content, so the swap reads as one motion (`.t-skel`).
- [StreamingText](/design/components/streaming-text.md): Streamed words resolving through a soft cross-blur, one after another (`.t-stream-w` → `.is-in`).
- [ThirdBentoAnimation](/design/components/third-bento-animation.md): An insight vignette for the bento grid: a smooth area line draws itself, a dot pulses at its midpoint and a dark pill above it ticks through values.
- [UniboxMotion](/design/components/unibox-motion.md): The Unibox's shared motion vocabulary: every surface moves on the same few springs, so the whole inbox feels like one physical material.
