yadflow 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +4 -7
  2. package/README.md +23 -2
  3. package/bin/yad.mjs +22 -1
  4. package/cli/docs.mjs +298 -0
  5. package/cli/manifest.mjs +6 -1
  6. package/docs/index.html +4 -4
  7. package/package.json +2 -2
  8. package/skills/sdlc/config.yaml +19 -0
  9. package/skills/sdlc/install.sh +1 -1
  10. package/skills/sdlc/module-help.csv +4 -0
  11. package/skills/yad-connect-docs/SKILL.md +132 -0
  12. package/skills/yad-connect-docs/references/docs-registry.md +74 -0
  13. package/skills/yad-docs/SKILL.md +159 -0
  14. package/skills/yad-docs/references/data-mapping.md +75 -0
  15. package/skills/yad-docs/references/theme-map.md +69 -0
  16. package/skills/yad-docs/templates/app/README.md +31 -0
  17. package/skills/yad-docs/templates/app/eslint.config.js +23 -0
  18. package/skills/yad-docs/templates/app/index.html +17 -0
  19. package/skills/yad-docs/templates/app/package-lock.json +4030 -0
  20. package/skills/yad-docs/templates/app/package.json +35 -0
  21. package/skills/yad-docs/templates/app/public/favicon.svg +28 -0
  22. package/skills/yad-docs/templates/app/public/logo.svg +39 -0
  23. package/skills/yad-docs/templates/app/public/vite.svg +1 -0
  24. package/skills/yad-docs/templates/app/src/App.tsx +98 -0
  25. package/skills/yad-docs/templates/app/src/components/Auth/LoginPage.tsx +101 -0
  26. package/skills/yad-docs/templates/app/src/components/Canvas/AnimatedMessage.tsx +101 -0
  27. package/skills/yad-docs/templates/app/src/components/Canvas/ConnectionLine.tsx +90 -0
  28. package/skills/yad-docs/templates/app/src/components/Canvas/FlowCanvas.tsx +216 -0
  29. package/skills/yad-docs/templates/app/src/components/Canvas/SystemComponent.tsx +153 -0
  30. package/skills/yad-docs/templates/app/src/components/Controls/PlaybackBar.tsx +284 -0
  31. package/skills/yad-docs/templates/app/src/components/Controls/StepDetail.tsx +167 -0
  32. package/skills/yad-docs/templates/app/src/components/DetailPanel/HandlerLogicSnippet.tsx +41 -0
  33. package/skills/yad-docs/templates/app/src/components/DetailPanel/RequestPayloadPreview.tsx +46 -0
  34. package/skills/yad-docs/templates/app/src/components/DetailPanel/RightPanel.tsx +88 -0
  35. package/skills/yad-docs/templates/app/src/components/DetailPanel/StatusCard.tsx +76 -0
  36. package/skills/yad-docs/templates/app/src/components/DetailPanel/TriggerEventCard.tsx +45 -0
  37. package/skills/yad-docs/templates/app/src/components/DocLayout/DocPageShell.tsx +80 -0
  38. package/skills/yad-docs/templates/app/src/components/DocLayout/DocSectionCard.tsx +55 -0
  39. package/skills/yad-docs/templates/app/src/components/DocLayout/DocTableOfContents.tsx +79 -0
  40. package/skills/yad-docs/templates/app/src/components/DocLayout/RoleCard.tsx +67 -0
  41. package/skills/yad-docs/templates/app/src/components/DocSections/ApiReferenceSection.tsx +108 -0
  42. package/skills/yad-docs/templates/app/src/components/DocSections/CancelabilitySection.tsx +73 -0
  43. package/skills/yad-docs/templates/app/src/components/DocSections/CriticalRunbookSection.tsx +177 -0
  44. package/skills/yad-docs/templates/app/src/components/DocSections/DataMigrationSection.tsx +102 -0
  45. package/skills/yad-docs/templates/app/src/components/DocSections/DbSchemaSection.tsx +98 -0
  46. package/skills/yad-docs/templates/app/src/components/DocSections/DeploymentGuideSection.tsx +104 -0
  47. package/skills/yad-docs/templates/app/src/components/DocSections/DriverIntegrationSection.tsx +127 -0
  48. package/skills/yad-docs/templates/app/src/components/DocSections/ExecutiveSummarySection.tsx +69 -0
  49. package/skills/yad-docs/templates/app/src/components/DocSections/FlowOverviewSection.tsx +73 -0
  50. package/skills/yad-docs/templates/app/src/components/DocSections/FlowPathsChecklistSection.tsx +96 -0
  51. package/skills/yad-docs/templates/app/src/components/DocSections/MiddlewareChainSection.tsx +107 -0
  52. package/skills/yad-docs/templates/app/src/components/DocSections/MonitoringAlertingSection.tsx +106 -0
  53. package/skills/yad-docs/templates/app/src/components/DocSections/NotificationLocalizationSection.tsx +102 -0
  54. package/skills/yad-docs/templates/app/src/components/DocSections/PMRoadmapSection.tsx +133 -0
  55. package/skills/yad-docs/templates/app/src/components/DocSections/PerformanceTestingSection.tsx +91 -0
  56. package/skills/yad-docs/templates/app/src/components/DocSections/RiderIntegrationSection.tsx +99 -0
  57. package/skills/yad-docs/templates/app/src/components/DocSections/SecuritySection.tsx +74 -0
  58. package/skills/yad-docs/templates/app/src/components/DocSections/StatusMachineSection.tsx +90 -0
  59. package/skills/yad-docs/templates/app/src/components/DocSections/TestPlanSection.tsx +163 -0
  60. package/skills/yad-docs/templates/app/src/components/Logs/SystemLogsTerminal.tsx +126 -0
  61. package/skills/yad-docs/templates/app/src/components/Navigation/TopNavBar.tsx +90 -0
  62. package/skills/yad-docs/templates/app/src/components/Reference/BullMQJobsList.tsx +60 -0
  63. package/skills/yad-docs/templates/app/src/components/Reference/DecisionTreeView.tsx +49 -0
  64. package/skills/yad-docs/templates/app/src/components/Reference/DeeplinkActionsChips.tsx +69 -0
  65. package/skills/yad-docs/templates/app/src/components/Reference/DriverUIStatesTable.tsx +61 -0
  66. package/skills/yad-docs/templates/app/src/components/Reference/FeatureFlagMatrix.tsx +73 -0
  67. package/skills/yad-docs/templates/app/src/components/Reference/RiderUIStatesTable.tsx +61 -0
  68. package/skills/yad-docs/templates/app/src/components/Reference/RulesLegendPanel.tsx +217 -0
  69. package/skills/yad-docs/templates/app/src/components/Reference/StakeholderToggle.tsx +41 -0
  70. package/skills/yad-docs/templates/app/src/components/Reference/TroubleshootingSection.tsx +93 -0
  71. package/skills/yad-docs/templates/app/src/components/Sidebar/PathSelector.tsx +148 -0
  72. package/skills/yad-docs/templates/app/src/components/Sidebar/SidebarFooter.tsx +40 -0
  73. package/skills/yad-docs/templates/app/src/components/Sidebar/StepList.tsx +234 -0
  74. package/skills/yad-docs/templates/app/src/components/shared/Badge.tsx +28 -0
  75. package/skills/yad-docs/templates/app/src/components/shared/CommandPalette.tsx +213 -0
  76. package/skills/yad-docs/templates/app/src/components/shared/Icon.tsx +21 -0
  77. package/skills/yad-docs/templates/app/src/components/shared/Tooltip.tsx +42 -0
  78. package/skills/yad-docs/templates/app/src/data/components.ts +74 -0
  79. package/skills/yad-docs/templates/app/src/data/docSections.ts +231 -0
  80. package/skills/yad-docs/templates/app/src/data/paths.ts +2319 -0
  81. package/skills/yad-docs/templates/app/src/data/referenceData.ts +392 -0
  82. package/skills/yad-docs/templates/app/src/data/roles.ts +145 -0
  83. package/skills/yad-docs/templates/app/src/data/types.ts +79 -0
  84. package/skills/yad-docs/templates/app/src/hooks/useAnimationQueue.ts +41 -0
  85. package/skills/yad-docs/templates/app/src/hooks/usePlayback.ts +100 -0
  86. package/skills/yad-docs/templates/app/src/hooks/useStakeholderFilter.ts +10 -0
  87. package/skills/yad-docs/templates/app/src/index.css +121 -0
  88. package/skills/yad-docs/templates/app/src/main.tsx +13 -0
  89. package/skills/yad-docs/templates/app/src/pages/RoleSelectPage.tsx +34 -0
  90. package/skills/yad-docs/templates/app/src/pages/StakeholderDocPage.tsx +98 -0
  91. package/skills/yad-docs/templates/app/src/pages/SubPathDetailPage.tsx +282 -0
  92. package/skills/yad-docs/templates/app/src/store/useAuthStore.ts +42 -0
  93. package/skills/yad-docs/templates/app/src/store/useFlowStore.ts +197 -0
  94. package/skills/yad-docs/templates/app/src/utils/iconMap.ts +46 -0
  95. package/skills/yad-docs/templates/app/tsconfig.app.json +28 -0
  96. package/skills/yad-docs/templates/app/tsconfig.json +7 -0
  97. package/skills/yad-docs/templates/app/tsconfig.node.json +26 -0
  98. package/skills/yad-docs/templates/app/vite.config.ts +10 -0
  99. package/skills/yad-docs-overview/SKILL.md +129 -0
  100. package/skills/yad-docs-overview/references/pipeline-model.md +102 -0
  101. package/skills/yad-docs-sync/SKILL.md +99 -0
  102. package/skills/yad-docs-sync/references/staleness.md +81 -0
@@ -0,0 +1,100 @@
1
+ import { useEffect, useRef } from "react";
2
+ import { useFlowStore } from "../store/useFlowStore";
3
+
4
+ export function usePlayback() {
5
+ const {
6
+ playbackState,
7
+ speed,
8
+ activeStepIndex,
9
+ animatingMessages,
10
+ nextStep,
11
+ getCurrentSteps,
12
+ setAnimatingMessages,
13
+ addLog,
14
+ tickElapsed,
15
+ resetElapsed,
16
+ } = useFlowStore();
17
+
18
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
19
+ const elapsedRef = useRef<ReturnType<typeof setInterval> | null>(null);
20
+ const prevStepRef = useRef<number>(-1);
21
+
22
+ // Elapsed time ticker
23
+ useEffect(() => {
24
+ if (playbackState === 'playing') {
25
+ elapsedRef.current = setInterval(tickElapsed, 100);
26
+ } else {
27
+ if (elapsedRef.current) clearInterval(elapsedRef.current);
28
+ }
29
+ if (playbackState === 'idle') resetElapsed();
30
+ return () => {
31
+ if (elapsedRef.current) clearInterval(elapsedRef.current);
32
+ };
33
+ }, [playbackState]);
34
+
35
+ // Generate logs on step change
36
+ useEffect(() => {
37
+ if (prevStepRef.current === activeStepIndex) return;
38
+ prevStepRef.current = activeStepIndex;
39
+
40
+ const steps = getCurrentSteps();
41
+ const currentStep = steps[activeStepIndex];
42
+ if (!currentStep) return;
43
+
44
+ addLog({
45
+ level: 'info',
46
+ source: currentStep.actor.toUpperCase(),
47
+ message: `Step ${activeStepIndex + 1}: ${currentStep.title}`,
48
+ });
49
+
50
+ for (const msg of currentStep.messages) {
51
+ setTimeout(() => {
52
+ addLog({
53
+ level: 'info',
54
+ source: msg.from,
55
+ message: `${msg.type}: ${msg.label} → ${msg.to}`,
56
+ });
57
+ }, msg.delay / speed);
58
+ }
59
+ }, [activeStepIndex, speed]);
60
+
61
+ useEffect(() => {
62
+ if (playbackState !== "playing") {
63
+ if (timerRef.current) clearTimeout(timerRef.current);
64
+ return;
65
+ }
66
+
67
+ const steps = getCurrentSteps();
68
+ const currentStep = steps[activeStepIndex];
69
+ if (!currentStep) return;
70
+
71
+ // Calculate total duration for current step messages
72
+ const messages = currentStep.messages;
73
+ const lastMessage = messages[messages.length - 1];
74
+ const totalAnimDuration = lastMessage
75
+ ? (lastMessage.delay + lastMessage.duration) / speed
76
+ : 1000 / speed;
77
+
78
+ // Trigger message animations
79
+ if (!animatingMessages) {
80
+ setAnimatingMessages(true);
81
+ }
82
+
83
+ // Wait for animations + pause, then advance
84
+ const baseDelay = 3000 / speed;
85
+ const stepDuration = totalAnimDuration + baseDelay;
86
+
87
+ timerRef.current = setTimeout(() => {
88
+ const isLast = activeStepIndex >= steps.length - 1;
89
+ if (!isLast) {
90
+ nextStep();
91
+ } else {
92
+ useFlowStore.getState().pause();
93
+ }
94
+ }, stepDuration);
95
+
96
+ return () => {
97
+ if (timerRef.current) clearTimeout(timerRef.current);
98
+ };
99
+ }, [playbackState, activeStepIndex, speed, animatingMessages]);
100
+ }
@@ -0,0 +1,10 @@
1
+ import { useMemo } from 'react';
2
+ import { useFlowStore } from '../store/useFlowStore';
3
+ import type { StakeholderView } from '../data/types';
4
+
5
+ export function useStakeholderFilter<T extends { visibleTo: StakeholderView[] }>(
6
+ items: T[]
7
+ ): T[] {
8
+ const view = useFlowStore((s) => s.stakeholderView);
9
+ return useMemo(() => items.filter((item) => item.visibleTo.includes(view)), [items, view]);
10
+ }
@@ -0,0 +1,121 @@
1
+ @import "tailwindcss";
2
+
3
+ @layer base {
4
+ :root {
5
+ --color-bg-primary: #141118;
6
+ --color-bg-secondary: #1e1a25;
7
+ --color-bg-tertiary: #2f2938;
8
+ --color-bg-brand-soft: #1a0244;
9
+ --color-bg-accent-soft: #25060e;
10
+
11
+ --color-surface-dark: #1e1a25;
12
+ --color-surface-highlight: #2f2938;
13
+ --color-surface-darker: #0f0e13;
14
+
15
+ --color-text-primary: #ffffff;
16
+ --color-text-secondary: #a8a4b2;
17
+ --color-text-muted: #767284;
18
+
19
+ --color-primary: #6116da;
20
+ --color-primary-hover: #7a2ce0;
21
+ --color-primary-soft: #35087c;
22
+
23
+ --color-accent: #ff6490;
24
+ --color-accent-hover: #fb2576;
25
+ --color-accent-soft: #4f0520;
26
+
27
+ --color-border-light: #453c53;
28
+ --color-border-default: #342e40;
29
+ --color-border-strong: #141118;
30
+ }
31
+
32
+ * {
33
+ margin: 0;
34
+ padding: 0;
35
+ box-sizing: border-box;
36
+ }
37
+
38
+ html, body, #root {
39
+ height: 100%;
40
+ width: 100%;
41
+ overflow: hidden;
42
+ }
43
+
44
+ body {
45
+ font-family: 'Noto Sans', system-ui, -apple-system, sans-serif;
46
+ background: var(--color-bg-primary);
47
+ color: var(--color-text-primary);
48
+ -webkit-font-smoothing: antialiased;
49
+ -moz-osx-font-smoothing: grayscale;
50
+ }
51
+
52
+ h1, h2, h3, h4, h5, h6 {
53
+ font-family: 'Space Grotesk', sans-serif;
54
+ }
55
+
56
+ ::-webkit-scrollbar {
57
+ width: 8px;
58
+ height: 8px;
59
+ }
60
+
61
+ ::-webkit-scrollbar-track {
62
+ background: var(--color-bg-primary);
63
+ }
64
+
65
+ ::-webkit-scrollbar-thumb {
66
+ background: var(--color-surface-highlight);
67
+ border-radius: 4px;
68
+ }
69
+
70
+ ::-webkit-scrollbar-thumb:hover {
71
+ background: var(--color-border-light);
72
+ }
73
+ }
74
+
75
+ /* Utility classes */
76
+ .glass-panel {
77
+ background: rgba(47, 41, 56, 0.4);
78
+ backdrop-filter: blur(12px);
79
+ border: 1px solid rgba(255, 255, 255, 0.05);
80
+ }
81
+
82
+ .flow-grid {
83
+ background-size: 40px 40px;
84
+ background-image:
85
+ linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),
86
+ linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
87
+ }
88
+
89
+ .code-block {
90
+ font-family: 'Courier New', Courier, monospace;
91
+ background: var(--color-surface-darker);
92
+ border: 1px solid var(--color-surface-highlight);
93
+ border-radius: 0.5rem;
94
+ padding: 0.75rem;
95
+ }
96
+
97
+ .font-display {
98
+ font-family: 'Space Grotesk', sans-serif;
99
+ }
100
+
101
+ .font-body {
102
+ font-family: 'Noto Sans', sans-serif;
103
+ }
104
+
105
+ /* Logs terminal scrollbar */
106
+ .logs-scrollbar::-webkit-scrollbar {
107
+ width: 6px;
108
+ }
109
+
110
+ .logs-scrollbar::-webkit-scrollbar-track {
111
+ background: var(--color-surface-darker);
112
+ }
113
+
114
+ .logs-scrollbar::-webkit-scrollbar-thumb {
115
+ background: #4a4059;
116
+ border-radius: 10px;
117
+ }
118
+
119
+ .logs-scrollbar::-webkit-scrollbar-thumb:hover {
120
+ background: #5d5270;
121
+ }
@@ -0,0 +1,13 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import { BrowserRouter } from 'react-router-dom'
4
+ import './index.css'
5
+ import App from './App'
6
+
7
+ createRoot(document.getElementById('root')!).render(
8
+ <StrictMode>
9
+ <BrowserRouter basename={import.meta.env.BASE_URL}>
10
+ <App />
11
+ </BrowserRouter>
12
+ </StrictMode>,
13
+ )
@@ -0,0 +1,34 @@
1
+ import { DocPageShell } from '../components/DocLayout/DocPageShell';
2
+ import { RoleCard } from '../components/DocLayout/RoleCard';
3
+ import { ROLES } from '../data/roles';
4
+
5
+ export function RoleSelectPage() {
6
+ return (
7
+ <DocPageShell
8
+ title="Documentation Hub"
9
+ subtitle="Select your role to see tailored documentation for the Booking Status Sync feature"
10
+ icon="menu_book"
11
+ backTo="/"
12
+ backLabel="Dashboard"
13
+ >
14
+ <div className="p-8 max-w-5xl mx-auto">
15
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
16
+ {ROLES.map((role) => (
17
+ <RoleCard key={role.slug} role={role} />
18
+ ))}
19
+ </div>
20
+
21
+ <div
22
+ className="mt-8 rounded-xl border p-5 text-center"
23
+ style={{ background: 'rgba(20,17,24,0.3)', borderColor: 'var(--color-border-default)' }}
24
+ >
25
+ <p className="text-sm text-slate-400">
26
+ Each role page includes tailored documentation sections and relevant flow paths.
27
+ <br />
28
+ <span className="text-slate-500">Use the Reference panel (top nav) for quick-access to the legend and rules.</span>
29
+ </p>
30
+ </div>
31
+ </div>
32
+ </DocPageShell>
33
+ );
34
+ }
@@ -0,0 +1,98 @@
1
+ import { useParams, useNavigate } from 'react-router-dom';
2
+ import { useEffect } from 'react';
3
+ import { DocPageShell } from '../components/DocLayout/DocPageShell';
4
+ import { DocTableOfContents } from '../components/DocLayout/DocTableOfContents';
5
+ import { DocSectionCard } from '../components/DocLayout/DocSectionCard';
6
+ import { getRoleBySlug } from '../data/roles';
7
+ import { DOC_SECTIONS } from '../data/docSections';
8
+ import { useFlowStore } from '../store/useFlowStore';
9
+ import { Icon } from '../components/shared/Icon';
10
+ import type { StakeholderView } from '../data/types';
11
+
12
+ export function StakeholderDocPage() {
13
+ const { roleSlug } = useParams<{ roleSlug: string }>();
14
+ const navigate = useNavigate();
15
+ const setStakeholderView = useFlowStore((s) => s.setStakeholderView);
16
+
17
+ const role = getRoleBySlug(roleSlug ?? '');
18
+
19
+ useEffect(() => {
20
+ if (role) {
21
+ setStakeholderView(role.slug as StakeholderView);
22
+ }
23
+ }, [role, setStakeholderView]);
24
+
25
+ if (!role) {
26
+ return (
27
+ <DocPageShell title="Role Not Found" backTo="/docs" backLabel="Back to Docs">
28
+ <div className="flex items-center justify-center h-full">
29
+ <div className="text-center">
30
+ <Icon name="error_outline" size={48} className="text-slate-600 mb-3" />
31
+ <p className="text-slate-400">Unknown role: {roleSlug}</p>
32
+ <button
33
+ onClick={() => navigate('/docs')}
34
+ className="mt-4 px-4 py-2 rounded-lg text-sm font-medium text-white"
35
+ style={{ background: 'var(--color-primary)' }}
36
+ >
37
+ Back to Documentation Hub
38
+ </button>
39
+ </div>
40
+ </div>
41
+ </DocPageShell>
42
+ );
43
+ }
44
+
45
+ const sections = role.sectionIds
46
+ .map((id) => DOC_SECTIONS[id])
47
+ .filter(Boolean);
48
+
49
+ const tocItems = sections.map((s) => ({ id: s.id, title: s.title, icon: s.icon }));
50
+
51
+ return (
52
+ <DocPageShell
53
+ title={role.label}
54
+ subtitle={role.description}
55
+ icon={role.icon}
56
+ iconColor={role.color}
57
+ backTo="/docs"
58
+ backLabel="All Roles"
59
+ headerRight={
60
+ <button
61
+ onClick={() => navigate('/')}
62
+ className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors"
63
+ style={{ background: `${role.color}20`, color: role.color }}
64
+ onMouseEnter={(e) => (e.currentTarget.style.background = `${role.color}30`)}
65
+ onMouseLeave={(e) => (e.currentTarget.style.background = `${role.color}20`)}
66
+ >
67
+ <Icon name="play_circle" size={18} />
68
+ Open Visualizer
69
+ </button>
70
+ }
71
+ sidebar={
72
+ <DocTableOfContents
73
+ items={tocItems}
74
+ roleLabel={role.shortLabel}
75
+ roleIcon={role.icon}
76
+ roleColor={role.color}
77
+ />
78
+ }
79
+ >
80
+ <div className="p-8 max-w-4xl space-y-2">
81
+ {sections.map((section) => {
82
+ const Component = section.component;
83
+ return (
84
+ <DocSectionCard
85
+ key={section.id}
86
+ id={section.id}
87
+ title={section.title}
88
+ icon={section.icon}
89
+ iconColor={section.iconColor}
90
+ >
91
+ <Component />
92
+ </DocSectionCard>
93
+ );
94
+ })}
95
+ </div>
96
+ </DocPageShell>
97
+ );
98
+ }
@@ -0,0 +1,282 @@
1
+ import { useParams, useNavigate } from 'react-router-dom';
2
+ import { useMemo, useState } from 'react';
3
+ import { PATHS } from '../data/paths';
4
+ import { useFlowStore } from '../store/useFlowStore';
5
+ import { Icon } from '../components/shared/Icon';
6
+
7
+ export function SubPathDetailPage() {
8
+ const { pathId } = useParams<{ pathId: string }>();
9
+ const navigate = useNavigate();
10
+ const selectPath = useFlowStore((s) => s.selectPath);
11
+ const [activeTab, setActiveTab] = useState(0);
12
+
13
+ const path = useMemo(() => {
14
+ const id = parseInt(pathId || '0');
15
+ return PATHS.find((p) => p.id === id);
16
+ }, [pathId]);
17
+
18
+ if (!path) {
19
+ return (
20
+ <div className="flex-1 flex items-center justify-center">
21
+ <div className="text-center">
22
+ <Icon name="error" size={48} className="text-slate-500 mb-4" />
23
+ <h2 className="text-xl font-bold text-white mb-2">Path Not Found</h2>
24
+ <button
25
+ onClick={() => navigate('/')}
26
+ className="text-sm px-4 py-2 rounded-lg"
27
+ style={{ background: 'var(--color-primary)', color: 'white' }}
28
+ >
29
+ Go Back
30
+ </button>
31
+ </div>
32
+ </div>
33
+ );
34
+ }
35
+
36
+ const subPaths = path.subPaths || [];
37
+ const currentSubPath = subPaths[activeTab];
38
+ const steps = currentSubPath?.steps || path.steps;
39
+
40
+ return (
41
+ <main className="flex-1 flex flex-col overflow-y-auto px-6 py-8 max-w-7xl mx-auto w-full">
42
+ {/* Header */}
43
+ <header className="mb-8">
44
+ <div className="flex flex-col md:flex-row md:items-end justify-between gap-6">
45
+ <div>
46
+ <div className="flex items-center gap-2 mb-2">
47
+ <span className="px-2 py-0.5 rounded text-xs font-bold uppercase tracking-wider"
48
+ style={{ background: `${path.color}25`, color: path.color, border: `1px solid ${path.color}40` }}
49
+ >
50
+ Path {path.id}
51
+ </span>
52
+ <button
53
+ onClick={() => navigate('/')}
54
+ className="text-slate-400 text-sm hover:text-white transition-colors"
55
+ >
56
+ / Dashboard
57
+ </button>
58
+ </div>
59
+ <h2 className="text-3xl md:text-4xl font-bold text-white font-display mb-2">
60
+ {path.label}
61
+ </h2>
62
+ <p className="text-slate-400 max-w-2xl text-lg">{path.description}</p>
63
+ </div>
64
+ <div className="flex gap-3">
65
+ <button className="px-4 py-2 rounded-lg border text-white text-sm font-medium flex items-center gap-2 transition-colors"
66
+ style={{
67
+ background: 'var(--color-surface-dark)',
68
+ borderColor: 'var(--color-border-default)',
69
+ }}
70
+ >
71
+ <Icon name="download" size={18} />
72
+ Export PDF
73
+ </button>
74
+ <button
75
+ onClick={() => { selectPath(path.id); navigate('/'); }}
76
+ className="px-4 py-2 rounded-lg text-white text-sm font-medium flex items-center gap-2 transition-colors"
77
+ style={{
78
+ background: 'var(--color-primary)',
79
+ boxShadow: '0 4px 15px rgba(97,22,218,0.2)',
80
+ }}
81
+ >
82
+ <Icon name="play_arrow" size={18} />
83
+ Simulate Path
84
+ </button>
85
+ </div>
86
+ </div>
87
+ </header>
88
+
89
+ {/* Sub-path tabs */}
90
+ {subPaths.length > 0 && (
91
+ <div className="border-b mb-8" style={{ borderColor: 'var(--color-border-default)' }}>
92
+ <div className="flex gap-8 overflow-x-auto pb-px">
93
+ {subPaths.map((sub, idx) => (
94
+ <button
95
+ key={sub.id}
96
+ onClick={() => setActiveTab(idx)}
97
+ className="group flex flex-col items-center gap-3 min-w-[140px] cursor-pointer"
98
+ >
99
+ <span className={`font-medium text-sm ${idx === activeTab ? 'text-[var(--color-primary)] font-bold' : 'text-slate-400 hover:text-white'} transition-colors`}>
100
+ {sub.id}. {sub.label}
101
+ </span>
102
+ <div className={`h-0.5 w-full rounded-t-full ${idx === activeTab ? 'bg-[var(--color-primary)]' : 'bg-transparent hover:bg-[var(--color-border-default)]'} transition-colors`} />
103
+ </button>
104
+ ))}
105
+ </div>
106
+ </div>
107
+ )}
108
+
109
+ {/* Content Grid */}
110
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-12">
111
+ {/* Left: System State & Side Effects Table */}
112
+ <div className="lg:col-span-2 flex flex-col gap-6">
113
+ {/* System State */}
114
+ <div className="rounded-xl p-6 border shadow-xl relative overflow-hidden"
115
+ style={{
116
+ background: 'var(--color-surface-dark)',
117
+ borderColor: 'var(--color-border-default)',
118
+ }}
119
+ >
120
+ <h3 className="text-xl font-bold text-white mb-6 flex items-center gap-2 font-display">
121
+ <Icon name="schema" size={20} className="text-[var(--color-primary)]" />
122
+ System State Visualization
123
+ </h3>
124
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
125
+ {steps[0]?.activeComponents.slice(0, 3).map((compId, idx) => (
126
+ <div key={compId} className="flex items-center gap-4">
127
+ <div className="rounded-lg p-4 flex flex-col gap-3 flex-1 border"
128
+ style={{
129
+ background: 'var(--color-bg-primary)',
130
+ borderColor: idx === 0 ? 'rgba(97,22,218,0.5)' : 'var(--color-border-default)',
131
+ }}
132
+ >
133
+ <div className="text-slate-300 text-sm font-semibold uppercase tracking-wider">
134
+ {compId.replace(/-/g, ' ')}
135
+ </div>
136
+ <div className="text-xs text-slate-500">
137
+ Status: <span className="text-slate-300 font-mono">{steps[0]?.status}</span>
138
+ </div>
139
+ </div>
140
+ {idx < 2 && (
141
+ <Icon name="arrow_forward" size={20} className="text-slate-600 hidden md:block shrink-0" />
142
+ )}
143
+ </div>
144
+ ))}
145
+ </div>
146
+ </div>
147
+
148
+ {/* Side Effects Table */}
149
+ <div className="rounded-xl border overflow-hidden"
150
+ style={{
151
+ background: 'var(--color-surface-dark)',
152
+ borderColor: 'var(--color-border-default)',
153
+ }}
154
+ >
155
+ <div className="px-6 py-4 border-b flex justify-between items-center"
156
+ style={{
157
+ borderColor: 'var(--color-border-default)',
158
+ background: 'var(--color-bg-primary)',
159
+ }}
160
+ >
161
+ <h3 className="text-white font-bold text-lg flex items-center gap-2 font-display">
162
+ <Icon name="table_chart" size={20} className="text-slate-400" />
163
+ Scenario Side Effects
164
+ </h3>
165
+ </div>
166
+ <div className="overflow-x-auto">
167
+ <table className="w-full text-left text-sm">
168
+ <thead>
169
+ <tr className="border-b" style={{ borderColor: 'var(--color-border-default)', color: 'var(--color-text-muted)' }}>
170
+ <th className="px-6 py-3 font-medium">Step</th>
171
+ <th className="px-6 py-3 font-medium">Status</th>
172
+ <th className="px-6 py-3 font-medium">Side Effects</th>
173
+ </tr>
174
+ </thead>
175
+ <tbody>
176
+ {steps.map((step) => (
177
+ <tr key={step.id} className="border-t hover:bg-white/5 transition-colors"
178
+ style={{ borderColor: 'var(--color-border-default)' }}
179
+ >
180
+ <td className="px-6 py-4 font-medium text-white">{step.title}</td>
181
+ <td className="px-6 py-4">
182
+ <span className="inline-flex items-center px-2 py-1 rounded text-xs font-medium"
183
+ style={{
184
+ background: 'rgba(97,22,218,0.1)',
185
+ color: 'var(--color-primary)',
186
+ }}
187
+ >
188
+ {step.bookingStatus}
189
+ </span>
190
+ </td>
191
+ <td className="px-6 py-4 text-slate-400">
192
+ {Object.entries(step.sideEffects)
193
+ .filter(([, v]) => v)
194
+ .map(([k, v]) => `${k}: ${v}`)
195
+ .join(', ') || 'None'}
196
+ </td>
197
+ </tr>
198
+ ))}
199
+ </tbody>
200
+ </table>
201
+ </div>
202
+ </div>
203
+ </div>
204
+
205
+ {/* Right: Step Detail Panel */}
206
+ <div className="flex flex-col gap-6">
207
+ <div className="rounded-xl p-6 border h-full"
208
+ style={{
209
+ background: 'var(--color-surface-dark)',
210
+ borderColor: 'var(--color-border-default)',
211
+ }}
212
+ >
213
+ <div className="flex items-center justify-between mb-6">
214
+ <h2 className="text-white text-xl font-bold font-display">Step Detail Panel</h2>
215
+ <div className="p-2 rounded-lg" style={{ background: 'rgba(97,22,218,0.2)' }}>
216
+ <Icon name="info" size={20} className="text-[var(--color-primary)]" />
217
+ </div>
218
+ </div>
219
+
220
+ <div className="space-y-6">
221
+ {/* Pre-conditions */}
222
+ <div>
223
+ <h4 className="text-slate-400 text-xs font-bold uppercase tracking-wider mb-2">Pre-conditions</h4>
224
+ <ul className="space-y-2">
225
+ {steps[0] && (
226
+ <>
227
+ <li className="flex items-start gap-2 text-sm text-slate-300">
228
+ <Icon name="check_circle" size={18} className="text-emerald-400 shrink-0" />
229
+ <span>Status is <code className="px-1 rounded text-xs" style={{ background: 'var(--color-bg-primary)', color: 'var(--color-primary)' }}>{steps[0].status}</code></span>
230
+ </li>
231
+ <li className="flex items-start gap-2 text-sm text-slate-300">
232
+ <Icon name="check_circle" size={18} className="text-emerald-400 shrink-0" />
233
+ <span>Trigger: {steps[0].trigger}</span>
234
+ </li>
235
+ </>
236
+ )}
237
+ </ul>
238
+ </div>
239
+
240
+ <div className="h-px w-full" style={{ background: 'var(--color-border-default)' }} />
241
+
242
+ {/* Critical Logic */}
243
+ <div>
244
+ <h4 className="text-slate-400 text-xs font-bold uppercase tracking-wider mb-2">Critical Logic</h4>
245
+ <div className="p-4 rounded-r-lg border-l-2"
246
+ style={{
247
+ background: 'var(--color-bg-primary)',
248
+ borderLeftColor: 'var(--color-primary)',
249
+ }}
250
+ >
251
+ <p className="text-sm text-slate-300 leading-relaxed">
252
+ {steps[0]?.description || 'No description available.'}
253
+ </p>
254
+ </div>
255
+ </div>
256
+
257
+ {/* Component Flags */}
258
+ {steps[0] && (
259
+ <div>
260
+ <h4 className="text-slate-400 text-xs font-bold uppercase tracking-wider mb-2">Component Flags</h4>
261
+ <div className="grid grid-cols-2 gap-3">
262
+ {steps[0].activeComponents.map((comp) => (
263
+ <div key={comp} className="p-3 rounded border"
264
+ style={{
265
+ background: 'var(--color-bg-primary)',
266
+ borderColor: 'var(--color-border-default)',
267
+ }}
268
+ >
269
+ <div className="text-xs text-slate-500 mb-1">{comp}</div>
270
+ <div className="text-emerald-400 font-mono text-sm">ACTIVE</div>
271
+ </div>
272
+ ))}
273
+ </div>
274
+ </div>
275
+ )}
276
+ </div>
277
+ </div>
278
+ </div>
279
+ </div>
280
+ </main>
281
+ );
282
+ }