initial commit

This commit is contained in:
2026-08-26 15:01:15 +05:30
parent aa42d9ee81
commit cf4f4ecd9c
87 changed files with 12814 additions and 2328 deletions

View File

@@ -1,22 +1,20 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import '../DoormileAI.css';
import PropTypes from 'prop-types';
import { useLocation } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import dayjs from 'dayjs';
import { X as CloseOutlined, MoreHorizontal as MoreOutlined } from 'lucide-react';
import { ChevronDown as LuChevronDown, PanelRightOpen as LuPanelRightOpen, PanelRightClose as LuPanelRightClose, History as LuHistory, Trash2 as LuTrash2, MessageSquare as LuMessageSquare } from 'lucide-react';
import { AiOutlineClose as CloseOutlined, AiOutlineMore as MoreOutlined } from 'react-icons/ai';
import { LuChevronDown, LuPanelRightOpen, LuPanelRightClose, LuHistory, LuTrash2, LuMessageSquare } from 'react-icons/lu';
import { HStack } from './shim';
import { VStack } from './shim';
import { Text } from './shim';
import { IconButton } from './shim';
import { DropdownMenu } from './shim';
import { HStack } from '@astryxdesign/core/HStack';
import { VStack } from '@astryxdesign/core/VStack';
import { Text } from '@astryxdesign/core/Text';
import { IconButton } from '@astryxdesign/core/IconButton';
import { DropdownMenu } from '@astryxdesign/core/DropdownMenu';
import { toast } from '@/components/ui/use-toast';
const OpenToast = ({message, tone}) => toast({description: message, variant: tone === 'error' ? 'destructive' : 'default'});
const STATUS = { error: 'error', success: 'success', warning: 'warning', info: 'info', muted: 'muted' };
import { OpenToast } from 'components/third-party/OpenToast';
import { STATUS } from 'themes/dt/tokens';
import { answerQuestion, FOLLOW_UP_SUGGESTIONS } from '@/lib/assistant/intents';
import { executeCreateCustomer, buildCustomerPayload } from '@/lib/assistant/actions';
import { executeCreateOrder, buildOrderPayload, validateOrderDraft } from '@/lib/assistant/orderActions';
@@ -228,17 +226,8 @@ const AIPanel = ({ isOpen, onClose }) => {
useEffect(() => {
if (isOpen) {
setIsMounted(true);
// One frame between mounting and showing, so the slide-in has a
// from-state to animate out of. rAF is the right clock for that, but a
// hidden or heavily throttled document never runs the callback at all —
// and a panel that refuses to open is worse than one that opens without
// its animation, so a timer backs it up.
const raf = requestAnimationFrame(() => setIsShown(true));
const fallback = setTimeout(() => setIsShown(true), 50);
return () => {
cancelAnimationFrame(raf);
clearTimeout(fallback);
};
return () => cancelAnimationFrame(raf);
}
setIsShown(false);
const timer = setTimeout(() => setIsMounted(false), ANIMATION_MS);
@@ -275,14 +264,15 @@ const AIPanel = ({ isOpen, onClose }) => {
}, [isWide]);
// ---- anchor the dock to the real header height ----
// The dock starts where the top nav ends. Measured rather than assumed, and
// re-measured on resize, so the seam stays closed if the header ever changes
// height — a hard-coded number leaves a sliver of page visible above the
// panel the moment the bar grows.
// The dock starts where the top nav ends. `--appshell-header-height` looks
// like the right token for that and Dispatch.css already reads it, but
// Astryx does not actually publish it — verified in the browser, where it
// resolves to nothing and the fallback wins. The header is 57px, not the 64
// a fallback would guess, so trusting it left a 7px sliver of page visible
// above the panel.
//
// AdminLayout marks its bar `data-app-header`; that attribute exists purely
// as this hook. The selector used to be `.astryx-layout-header`, a class this
// app does not render at all, so the measurement silently never ran.
// Measured instead, and re-measured on resize, so the seam stays closed if
// the header ever changes height.
useEffect(() => {
if (!isMounted) return undefined;
const header = document.querySelector('[data-app-header]');
@@ -1394,25 +1384,22 @@ const AIPanel = ({ isOpen, onClose }) => {
// shrink and scroll. See the [data-compact] rule in DoormileAI.css.
const isCompactStrip = hasThread;
if (!isMounted || !isShown) return null;
return createPortal(
<>
<div
className="dai-scrim"
data-open={isShown}
onClick={onClose}
aria-hidden="true"
/>
<HStack className="dai-root dai-scrim" data-open={isShown} padding={0} gap={0} onClick={onClose} aria-hidden="true" />
<VStack
as="aside"
ref={panelRef}
className="dai-root dai-panel"
data-open={isShown}
data-state={isShown ? 'open' : 'closed'}
gap={0}
padding={0}
/* `complementary`, not `dialog`. A dialog promises modality — focus is
trapped, the rest of the page is inert, and screen readers announce
it as blocking. None of that is true any more: the page beside it
stays live and operable, so announcing it as a dialog would be a
lie to exactly the users who cannot see that it is docked. */
role="complementary"
aria-label="Doormile AI — Operations Copilot"
tabIndex={-1}