constants.ts3.3 KBView on GitHub
import { ListTodo, Plug } from 'lucide-react';
import { primaryNavButtons } from '@/modules/conversations/components/nav-buttons';
import type { ConnectStepKind, SetupSection, SetupStep } from './types';

const navIcon = (id: string) => {
  const button = primaryNavButtons.find((b) => b.id === id);
  if (!button) throw new Error(`onboarding/setup: unknown nav button id "${id}"`);
  return button;
};

/**
 * Rail sections, in order. Icons come from `primaryNavButtons` on purpose — the
 * rail is a facsimile of the real one, so a user who learns this rail has
 * learned the app's. Tasks has no `primaryNavButtons` entry (the real rail
 * builds it from TaskGroupsNav), so it carries the same icon that component
 * uses; Connect is not a destination at all, so it gets a plug.
 */
export const SETUP_SECTIONS: SetupSection[] = [
  { id: 'connect', label: 'Connect', icon: Plug },
  { id: 'mail', label: 'Inbox', icon: navIcon('mail').icon },
  { id: 'tasks', label: 'Tasks', icon: ListTodo },
  {
    id: 'pipeline',
    label: 'Pipeline',
    icon: navIcon('pipeline').icon,
    isCustomIcon: true,
  },
];

/** Copy for each integration step. Which ones appear is decided at runtime. */
export const CONNECT_STEPS: Record<ConnectStepKind, Omit<SetupStep, 'section'>> = {
  meeting: {
    id: 'connect-meeting',
    connect: 'meeting',
    railLabel: 'Meeting recorder',
    headline: 'Connect your recorder',
  },
  crm: {
    id: 'connect-crm',
    connect: 'crm',
    railLabel: 'CRM',
    headline: 'Connect your CRM',
  },
  slack: {
    id: 'connect-slack',
    connect: 'slack',
    railLabel: 'Slack',
    headline: 'Connect Slack',
  },
  mcp: {
    id: 'connect-mcp',
    connect: 'mcp',
    railLabel: 'Extra context',
    headline: 'Anything else Cedar should read',
  },
  linkedin: {
    id: 'connect-linkedin',
    connect: 'linkedin',
    railLabel: 'LinkedIn',
    headline: 'Connect LinkedIn',
  },
  whatsapp: {
    id: 'connect-whatsapp',
    connect: 'whatsapp',
    railLabel: 'WhatsApp',
    headline: 'Connect WhatsApp',
  },
  drive: {
    id: 'connect-drive',
    connect: 'drive',
    railLabel: 'Google Drive',
    headline: 'Connect Google Drive',
  },
};

/** The product-configuration half — always present, in this order. */
export const PRODUCT_STEPS: SetupStep[] = [
  {
    id: 'inbox-display',
    section: 'mail',
    railLabel: 'Display',
    headline: 'Make it yours',
  },
  {
    id: 'inbox-splits',
    section: 'mail',
    railLabel: 'Sub-inboxes',
    headline: 'Split your inbox',
  },
  {
    id: 'inbox-live',
    section: 'mail',
    railLabel: 'Your mail',
    headline: 'Your actual inbox',
  },
  {
    id: 'tasks',
    section: 'tasks',
    headline: 'Tasks do the work',
  },
  {
    id: 'pipeline',
    section: 'pipeline',
    headline: 'Watch your pipeline',
  },
];

/**
 * The full ordered step list.
 *
 * Connect steps are spliced in rather than hard-coded because which
 * integrations exist is an org setting — a team with no CRM installed should
 * never see a CRM step, and the rail must not count one toward its progress.
 */
export function buildSetupSteps(availableConnects: ConnectStepKind[]): SetupStep[] {
  const connectSteps = availableConnects.map<SetupStep>((kind) => ({
    ...CONNECT_STEPS[kind],
    section: 'connect',
  }));
  return [...connectSteps, ...PRODUCT_STEPS];
}