# Log Activity Form URL: /docs/web/components/log-activity-form A presentational multi-tab "log activity" composer — comment, email, call, meeting, task, and status — with every tab rendered inline, wired to Docyrus via the useDocyrusLogActivity hook. **Demo:** ```tsx 'use client'; import { useState } from 'react'; import { type CommentUser } from '@docyrus/ui/components/comments-panel'; import { type EnumOption, type IField } from '@docyrus/ui/components/form-fields'; import { LogActivityForm, type LoggableActivityType } from '@docyrus/ui/components/log-activity-form'; const STATUS_FIELD: IField = { id: 'status', name: 'Status', slug: 'status', type: 'field-status' }; const STATUS_OPTIONS: Array = [ { id: 'open', name: 'Open', color: 'emerald-500' }, { id: 'in_progress', name: 'In Progress', color: 'amber-500' }, { id: 'won', name: 'Won', color: 'green-600' }, { id: 'lost', name: 'Lost', color: 'red-500' } ]; const TASK_STATUS_OPTIONS: Array = [{ id: 'backlog', name: 'Backlog', color: 'slate-400' }, { id: 'todo', name: 'To Do', color: 'blue-500' }, { id: 'doing', name: 'In Progress', color: 'amber-500' }]; const TASK_PRIORITY_OPTIONS: Array = [{ id: 'low', name: 'Low', color: 'slate-400' }, { id: 'medium', name: 'Medium', color: 'amber-500' }, { id: 'high', name: 'High', color: 'red-500' }]; const USER_OPTIONS: Array = [{ id: 'u1', name: 'Ada Lovelace' }, { id: 'u2', name: 'Alan Turing' }]; const COMMENT_USERS: Array = [{ id: 'u1', firstname: 'Ada', lastname: 'Lovelace' }, { id: 'u2', firstname: 'Alan', lastname: 'Turing' }]; const EVENT_TYPES: Array = [{ id: 'call', name: 'Call' }, { id: 'meeting', name: 'Meeting' }]; /** * Presentational demo — every tab renders its form inline (no dialog / dropdown). * In a real app, spread `useDocyrusLogActivity(...).logActivityProps` instead of * the static props below. */ export function LogActivityFormDemo() { const [tab, setTab] = useState('comment'); return (
console.info('comment', payload)} statusField={STATUS_FIELD} statusEnumOptions={STATUS_OPTIONS} statusInitialValues={{ status: '' }} onSubmitStatus={async input => console.info('status', input)} taskStatusOptions={TASK_STATUS_OPTIONS} taskPriorityOptions={TASK_PRIORITY_OPTIONS} taskUsers={USER_OPTIONS} onSubmitTask={async input => console.info('task', input)} eventTypes={EVENT_TYPES} eventUsers={USER_OPTIONS} callEventTypeId="call" meetingEventTypeId="meeting" onSubmitEvent={async (input, kind) => console.info('event', kind, input)} />
); } ``` ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/ui-log-activity-form ``` **Dependencies:** - [@tanstack/react-form](https://www.npmjs.com/package/@tanstack/react-form) - [@tanstack/react-query](https://www.npmjs.com/package/@tanstack/react-query) - [motion](https://www.npmjs.com/package/motion) - [lucide-react](https://www.npmjs.com/package/lucide-react) - [sonner](https://www.npmjs.com/package/sonner) ## Overview `LogActivityForm` is a **presentational** component (it owns no network I/O). A tab bar switches between six surfaces and each one renders its form **inline** — no dialog, no dropdown-triggered form. Every tab reuses the real Docyrus components / form-fields: | Tab | Inline surface | |-----|----------------| | Comment | [`CommentsPanel`](/docs/web/components/comments-panel) (threaded) | | Email | [`EmailComposer`](/docs/web/components/email-composer) (send-only) | | Status | inline `StatusFormField` (status list + sub-status / notes / follow-up date, no popover) | | Task | `subject` + `status` + `priority` + `assignee` + `due date` + `description` form-fields | | Call / Meeting | `subject` + `event type` + `start` / `end` + `owner` + `description` form-fields | Wire it to a Docyrus record with [`useDocyrusLogActivity`](/docs/web/hooks/use-docyrus-log-activity) — the hook owns ALL I/O (option fetches, the per-tab create mutations, the two-call status update, and an embedded email composer) and returns a spreadable `logActivityProps` bag. This mirrors the `EmailComposer` / `useDocyrusEmailComposer` split. ## Usage ```tsx import { LogActivityForm } from '@docyrus/ui/components/log-activity-form'; import { useDocyrusLogActivity } from '@docyrus/ui/library/hooks/use-docyrus-log-activity'; import { ContactActivityPanel, useDocyrusContactActivity } from '@docyrus/ui/components/contact-activity-panel'; function RecordActivity({ client, recordId }) { // One hook feeds the log form (all I/O). const { logActivityProps } = useDocyrusLogActivity({ client, appSlug: 'base', dataSourceSlug: 'organization', recordId, taskRelationSlug: 'organization', // per-tenant back-relation for task/event linking statusFieldSlug: 'status', callEventTypeName: 'Call', // resolve the call/meeting event type by name meetingEventTypeName: 'Meeting' }); // A sibling hook feeds the timeline. Both share the record-scoped query prefix, // so logging on the form auto-refreshes the panel — no manual refetch wiring. const { panelProps } = useDocyrusContactActivity({ client, appSlug: 'base', dataSourceSlug: 'organization', recordId, taskRelationSlug: 'organization', sources: { audit: true, comments: true, tasks: true, statusUpdates: true } }); return ( <> ); } ``` ## API Reference Every prop is optional — a bare `` renders the tab shell with empty surfaces. In practice you spread `logActivityProps` (see the hook), which fills all of these. ### Shell | Prop | Type | Default | Description | |------|------|---------|-------------| | `activityTypes` | `LoggableActivityType[]` | all six | Which tabs to show (order preserved). | | `activeType` | `LoggableActivityType` | — | Controlled active tab. | | `defaultActivityType` | `LoggableActivityType` | first tab | Uncontrolled initial tab. | | `onTypeChange` | `(type: LoggableActivityType) => void` | — | Fired on tab change. | | `disabled` | `boolean` | `false` | Disable every surface. | | `className` | `string` | — | Root class. | ### Comment tab | Prop | Type | Description | |------|------|-------------| | `comments` | `DocyrusComment[]` | Thread source. | | `users` | `CommentUser[]` | Users for `@` mentions / avatars. | | `currentUser` | `CommentUser` | The authoring user. | | `onCreateComment` | `(payload: LogActivityCommentPayload) => void \| Promise` | Create / reply. | | `onDeleteComment` | `(commentId: string) => void \| Promise` | Delete. | | `isCommentsLoading` / `isCreatingComment` | `boolean` | Pending flags. | ### Email tab | Prop | Type | Description | |------|------|-------------| | `emailComposerProps` | `Partial` | Spread onto the inline `EmailComposer` (from `useDocyrusEmailComposer`). | | `onSendEmail` | `() => void \| Promise` | Send handler (the tab wraps it with a toast). | | `isSendingEmail` | `boolean` | Pending flag. | ### Status tab | Prop | Type | Description | |------|------|-------------| | `statusField` | `IField \| null` | The record's status field (`field-status` / `field-select`). | | `statusEnumOptions` | `EnumOption[]` | Status options. | | `statusInitialValues` | `Record` | Current status + companion values, keyed by field slug. | | `onSubmitStatus` | `(input: LogStatusInput) => void \| Promise` | Submit (two-call in the hook). | | `isSubmittingStatus` / `isStatusLoading` | `boolean` | Pending / loading flags. | ### Task tab | Prop | Type | Description | |------|------|-------------| | `taskStatusOptions` / `taskPriorityOptions` | `EnumOption[]` | Task enum options. | | `taskUsers` | `EnumOption[]` | Assignee options (`{ id, name, icon }`). | | `onSubmitTask` | `(input: LogTaskInput) => void \| Promise` | Submit. | | `isSubmittingTask` | `boolean` | Pending flag. | ### Call / Meeting tabs | Prop | Type | Description | |------|------|-------------| | `eventTypes` | `EnumOption[]` | Event type options. | | `eventUsers` | `EnumOption[]` | Owner options. | | `callEventTypeId` / `meetingEventTypeId` | `string` | Preset event-type id per tab. | | `onSubmitEvent` | `(input: LogEventInput, kind: 'call' \| 'meeting') => void \| Promise` | Submit. | | `isSubmittingEvent` | `boolean` | Pending flag. | ## Type Exports | Type | Description | |------|-------------| | `LogActivityFormProps` | Props for the presentational form. | | `LoggableActivityType` | `"comment" \| "email" \| "call" \| "meeting" \| "task" \| "status_update"` | | `LogActivityCommentPayload` | Comment submit payload (`{ content, parent_id?, mentions? }`). | | `LogTaskInput` | Task submit payload. | | `LogEventInput` | Call / meeting submit payload. | | `LogStatusInput` | Status submit payload. |