import * as React from "react" import { cn } from "../../utils/cn" import { SquareAvatar as Avatar } from "../ui/square-avatar" import { Button } from "../ui/button" import { PlusCircleIcon } from "../plus-circle-icon" import { XmarkIcon } from "../icons-v2-generated/signs-and-symbols/xmark-icon" import { Chevron02LeftIcon } from "../icons-v2-generated" import { TicketStatusTag, resolveStatusTagProps } from "../ui/ticket-status-tag" import { Skeleton } from "../ui/skeleton" import { MspOrganizationCard } from "./msp-organization-card" import { MspOrganizationCardSkeleton } from "./msp-organization-card-skeleton" import type { ConnectionIndicatorProps, ChatContainerProps, ChatHeaderProps } from "./types" const ConnectionIndicator: React.FC = ({ status }) => { const getStatusStyles = () => { switch (status) { // ODS semantic status tokens — preset-defined utilities aliasing the same // green/yellow/red as the raw ods-attention palette (which is CSS-vars only). case 'connected': return 'bg-ods-success' case 'connecting': return 'bg-ods-warning animate-pulse' case 'disconnected': return 'bg-ods-error' default: return 'bg-ods-text-tertiary' } } return (
) } const ChatContainer = React.forwardRef( ({ className, children, ...props }, ref) => { return (
{children}
) } ) ChatContainer.displayName = "ChatContainer" const ChatHeader = React.forwardRef( ({ className, userName = 'Grace "Fae" Meadows', userTitle = "Your Personal Assistant", userAvatar, userIcon, onSettingsClick, onNewChat, onClose, onBack, showNewChat = false, connectionStatus = 'disconnected', serverUrl = null, headerActions, ticketInfo, mspOrganization, isMspLoading = false, fullWidth = false, bare = false, isLoading = false, ...props }, ref) => { const cardClasses = bare ? "" : "rounded-md bg-ods-card border border-ods-border" return (
{onBack && ( )} {onClose && ( )} {headerActions}
{/* MSP branding slot — home-screen only; `ticketInfo` (open chat) claims this space, so it wins when both are provided. The card already draws the chrome: strip the section's own ring and keep only the bottom rounding so its bg fits the card corners. */} {!ticketInfo && (isMspLoading || mspOrganization) && ( <>
{isMspLoading || !mspOrganization ? ( ) : ( )} )} {ticketInfo && ( <>
{ticketInfo.title} {ticketInfo.meta && (
{ticketInfo.meta}
)}
{(ticketInfo.status || ticketInfo.statusName) && ( )}
)}
) } ) ChatHeader.displayName = "ChatHeader" const ChatContent = React.forwardRef>( ({ className, children, ...props }, ref) => { return (
{children}
) } ) ChatContent.displayName = "ChatContent" /** * `ChatFooter` props. * * Layout API: * - `fullWidth` (preferred) — drop the inner-wrapper * `max-w-ods-content-narrow` so the footer fills the parent. * - `contentClassName` (legacy escape hatch) — explicit class names * applied to the inner wrapper. Use only when `fullWidth` is too * coarse (e.g. custom max-w value). */ export interface ChatFooterProps extends React.HTMLAttributes { /** Same `fullWidth` semantics as `ChatHeaderProps.fullWidth` — drops * the inner wrapper's `max-w-ods-content-narrow` so the footer * spans the full parent width. */ fullWidth?: boolean /** @deprecated Prefer `fullWidth` for the full-panel-width use case. * This prop remains supported for callers that need a NON-binary * override (custom max-w value, custom padding, etc.). */ contentClassName?: string } const ChatFooter = React.forwardRef( ({ className, contentClassName, fullWidth = false, children, ...props }, ref) => { return (
{children}
) } ) ChatFooter.displayName = "ChatFooter" export { ChatContainer, ChatHeader, ChatContent, ChatFooter }