thors1 commited on
Commit
49bb569
Β·
verified Β·
1 Parent(s): ee6c7ff

Initial DeepSite commit

Browse files
Files changed (2) hide show
  1. README.md +9 -6
  2. index.html +1222 -19
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Deepsite Project Etach
3
- emoji: πŸ’»
4
- colorFrom: purple
5
- colorTo: red
6
  sdk: static
7
- pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: DeepSite Project
3
+ colorFrom: gray
4
+ colorTo: green
 
5
  sdk: static
6
+ emoji: 🚧
7
+ tags:
8
+ - deepsite-v4
9
  ---
10
 
11
+ # DeepSite Project
12
+
13
+ This project has been created with [DeepSite](https://deepsite.hf.co) AI Vibe Coding.
index.html CHANGED
@@ -1,19 +1,1222 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Verify MC β€” AI Patient Intake</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
9
+ <script src="https://unpkg.com/lucide@latest"></script>
10
+ <script>
11
+ tailwind.config = {
12
+ theme: {
13
+ extend: {
14
+ colors: {
15
+ 'vm-bg': '#F8FAFC',
16
+ 'vm-card': '#FFFFFF',
17
+ 'vm-border': '#E5E7EB',
18
+ 'vm-text': '#0F172A',
19
+ 'vm-muted': '#64748B',
20
+ 'vm-teal': '#0C8F8B',
21
+ 'vm-teal-hover': '#0A7F7B',
22
+ 'vm-success': '#10B981',
23
+ 'vm-warning': '#F59E0B',
24
+ 'vm-risk': '#EF4444',
25
+ 'vm-success-bg': '#F0FDF4',
26
+ 'vm-warning-bg': '#FFFBEB',
27
+ 'vm-risk-bg': '#FEF2F2',
28
+ },
29
+ fontFamily: {
30
+ 'inter': ['Inter', 'sans-serif'],
31
+ },
32
+ maxWidth: {
33
+ 'vm': '1360px',
34
+ }
35
+ }
36
+ }
37
+ }
38
+ </script>
39
+ <style>
40
+ * { font-family: 'Inter', sans-serif; }
41
+ body { background: #F8FAFC; color: #0F172A; }
42
+
43
+ /* Scrollbar */
44
+ .custom-scroll::-webkit-scrollbar { width: 4px; }
45
+ .custom-scroll::-webkit-scrollbar-track { background: transparent; }
46
+ .custom-scroll::-webkit-scrollbar-thumb { background: #E5E7EB; border-radius: 4px; }
47
+ .custom-scroll::-webkit-scrollbar-thumb:hover { background: #CBD5E1; }
48
+
49
+ /* Field highlight animation */
50
+ @keyframes fieldHighlight {
51
+ 0% { background-color: rgba(12, 143, 139, 0.08); }
52
+ 100% { background-color: transparent; }
53
+ }
54
+ .field-highlight {
55
+ animation: fieldHighlight 2s ease-out forwards;
56
+ }
57
+
58
+ /* Typing indicator */
59
+ @keyframes typingDot {
60
+ 0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
61
+ 30% { opacity: 1; transform: translateY(-3px); }
62
+ }
63
+ .typing-dot:nth-child(1) { animation: typingDot 1.4s infinite 0s; }
64
+ .typing-dot:nth-child(2) { animation: typingDot 1.4s infinite 0.2s; }
65
+ .typing-dot:nth-child(3) { animation: typingDot 1.4s infinite 0.4s; }
66
+
67
+ /* Fade in */
68
+ @keyframes fadeInUp {
69
+ from { opacity: 0; transform: translateY(8px); }
70
+ to { opacity: 1; transform: translateY(0); }
71
+ }
72
+ .fade-in-up { animation: fadeInUp 0.3s ease-out forwards; }
73
+
74
+ /* Stream text */
75
+ @keyframes pulseSoft {
76
+ 0%, 100% { opacity: 1; }
77
+ 50% { opacity: 0.6; }
78
+ }
79
+ .pulse-soft { animation: pulseSoft 1.5s ease-in-out infinite; }
80
+
81
+ /* Toast */
82
+ @keyframes toastIn {
83
+ from { opacity: 0; transform: translateY(-12px); }
84
+ to { opacity: 1; transform: translateY(0); }
85
+ }
86
+ @keyframes toastOut {
87
+ from { opacity: 1; transform: translateY(0); }
88
+ to { opacity: 0; transform: translateY(-12px); }
89
+ }
90
+ .toast-in { animation: toastIn 0.3s ease-out forwards; }
91
+ .toast-out { animation: toastOut 0.3s ease-in forwards; }
92
+
93
+ /* Inline edit transition */
94
+ .field-row { transition: background-color 0.2s ease; }
95
+ .field-row:hover { background-color: #F8FAFC; }
96
+
97
+ /* Mode toggle */
98
+ .mode-btn { transition: all 0.2s ease; }
99
+ .mode-btn.active {
100
+ background: #0C8F8B;
101
+ color: white;
102
+ }
103
+
104
+ /* Mobile tab */
105
+ .mobile-tab.active {
106
+ color: #0C8F8B;
107
+ border-bottom: 2px solid #0C8F8B;
108
+ }
109
+
110
+ /* Card */
111
+ .vm-card {
112
+ background: #FFFFFF;
113
+ border: 1px solid #E5E7EB;
114
+ border-radius: 8px;
115
+ }
116
+
117
+ textarea:focus, input:focus { outline: none; }
118
+ </style>
119
+ </head>
120
+ <body class="min-h-screen">
121
+
122
+ <!-- Toast Container -->
123
+ <div id="toast-container" class="fixed top-16 right-4 z-50 flex flex-col gap-2"></div>
124
+
125
+ <!-- Header -->
126
+ <header class="sticky top-0 z-40 bg-white border-b border-vm-border" style="height: 52px;">
127
+ <div class="max-w-vm mx-auto h-full px-6 flex items-center justify-between">
128
+ <!-- Left: Logo -->
129
+ <div class="flex items-center gap-2">
130
+ <div class="w-7 h-7 rounded-md bg-vm-teal flex items-center justify-center">
131
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
132
+ <path d="M8 1L14 4.5V11.5L8 15L2 11.5V4.5L8 1Z" fill="white" stroke="white" stroke-width="0.5"/>
133
+ </svg>
134
+ </div>
135
+ <span class="text-[15px] font-semibold text-vm-text tracking-tight">Verify MC</span>
136
+ </div>
137
+
138
+ <!-- Right: Actions -->
139
+ <div class="flex items-center gap-5">
140
+ <button onclick="saveDraft()" class="text-[13px] font-medium text-vm-muted hover:text-vm-text transition-colors">Save Draft</button>
141
+ <button onclick="switchToManual()" class="text-[13px] font-medium text-vm-muted hover:text-vm-text transition-colors">Switch to Manual</button>
142
+ </div>
143
+ </div>
144
+ </header>
145
+
146
+ <!-- Mobile Tab Bar -->
147
+ <div class="lg:hidden flex border-b border-vm-border bg-white">
148
+ <button onclick="showMobileTab('ai')" id="mobile-tab-ai" class="mobile-tab active flex-1 py-3 text-[13px] font-medium text-center">AI Intake</button>
149
+ <button onclick="showMobileTab('record')" id="mobile-tab-record" class="mobile-tab flex-1 py-3 text-[13px] font-medium text-vm-muted text-center">Patient Record</button>
150
+ </div>
151
+
152
+ <!-- Main Content -->
153
+ <main class="max-w-vm mx-auto px-6 py-6">
154
+ <!-- Page Title -->
155
+ <div class="mb-6">
156
+ <h1 class="text-[22px] font-semibold text-vm-text leading-tight">Add Patient</h1>
157
+ <p class="text-[14px] text-vm-muted mt-1">Guided by intelligent assistant</p>
158
+ </div>
159
+
160
+ <!-- Two Column Layout -->
161
+ <div class="flex gap-8" style="min-height: calc(100vh - 180px);">
162
+
163
+ <!-- LEFT COLUMN (65%) -->
164
+ <div id="left-column" class="w-full lg:w-[65%] flex flex-col min-h-0">
165
+
166
+ <!-- Mode Toggle -->
167
+ <div class="flex items-center gap-1 p-1 bg-vm-bg rounded-lg mb-6 self-start">
168
+ <button onclick="setMode('oneshot')" id="mode-oneshot" class="mode-btn active px-4 py-1.5 rounded-md text-[13px] font-medium">One-shot</button>
169
+ <button onclick="setMode('guided')" id="mode-guided" class="mode-btn px-4 py-1.5 rounded-md text-[13px] font-medium text-vm-muted">Guided AI Intake</button>
170
+ </div>
171
+
172
+ <!-- One-shot Input Card -->
173
+ <div id="oneshot-card" class="vm-card p-6 mb-6">
174
+ <label class="text-[14px] font-medium text-vm-text block mb-2">Add instantly with one sentence</label>
175
+ <textarea id="oneshot-input" rows="3"
176
+ class="w-full border border-vm-border rounded-lg px-4 py-3 text-[14px] text-vm-text placeholder-vm-muted/60 resize-none focus:border-vm-teal focus:ring-1 focus:ring-vm-teal/20 transition-all"
177
+ placeholder="e.g. Sarah Johnson, DOB 03/15/1985, 555-123-4567"></textarea>
178
+
179
+ <div class="flex items-center justify-between mt-3">
180
+ <button onclick="handleOneShot()" id="generate-btn"
181
+ class="bg-vm-teal hover:bg-vm-teal-hover text-white text-[13px] font-medium px-5 py-2 rounded-lg transition-colors flex items-center gap-2">
182
+ <i data-lucide="sparkles" class="w-3.5 h-3.5"></i>
183
+ Generate Patient Record
184
+ </button>
185
+ <span class="text-[12px] text-vm-muted">Type one answer or everything β€” I'll handle it.</span>
186
+ </div>
187
+
188
+ <!-- Smart Suggestion -->
189
+ <div class="mt-4 pt-4 border-t border-vm-border/60">
190
+ <span class="text-[12px] text-vm-muted font-medium">Try this</span>
191
+ <button onclick="fillSuggestion()" id="suggestion-btn"
192
+ class="block mt-1.5 text-[13px] text-vm-teal hover:text-vm-teal-hover text-left transition-colors leading-relaxed">
193
+ Sarah Johnson, DOB 03/15/1985, 555-123-4567
194
+ </button>
195
+ </div>
196
+ </div>
197
+
198
+ <!-- One-shot Processed Summary (hidden by default) -->
199
+ <div id="oneshot-processed" class="vm-card p-4 mb-6 hidden">
200
+ <div class="flex items-center justify-between">
201
+ <div class="flex items-center gap-2">
202
+ <div class="w-5 h-5 rounded-full bg-vm-success/10 flex items-center justify-center">
203
+ <i data-lucide="check" class="w-3 h-3 text-vm-success"></i>
204
+ </div>
205
+ <span id="processed-text" class="text-[13px] text-vm-text font-medium"></span>
206
+ </div>
207
+ <button onclick="resetOneShot()" class="text-[12px] text-vm-muted hover:text-vm-teal transition-colors">Edit</button>
208
+ </div>
209
+ </div>
210
+
211
+ <!-- AI Canvas Card -->
212
+ <div id="ai-canvas" class="vm-card flex-1 min-h-0 flex flex-col mb-0 overflow-hidden">
213
+ <div class="px-6 py-4 border-b border-vm-border/60 flex items-center gap-2">
214
+ <div class="w-5 h-5 rounded-full bg-vm-teal/10 flex items-center justify-center">
215
+ <i data-lucide="bot" class="w-3 h-3 text-vm-teal"></i>
216
+ </div>
217
+ <span class="text-[13px] font-medium text-vm-text">AI Assistant</span>
218
+ <span id="ai-status" class="text-[12px] text-vm-muted ml-auto hidden">Thinking...</span>
219
+ </div>
220
+
221
+ <div id="conversation-area" class="flex-1 overflow-y-auto custom-scroll px-6 py-4 space-y-4" style="max-height: calc(100vh - 460px); min-height: 200px;">
222
+ <!-- Welcome message -->
223
+ <div class="fade-in-up">
224
+ <div class="flex items-start gap-3">
225
+ <div class="w-6 h-6 rounded-full bg-vm-teal/10 flex items-center justify-center shrink-0 mt-0.5">
226
+ <i data-lucide="bot" class="w-3 h-3 text-vm-teal"></i>
227
+ </div>
228
+ <div class="bg-vm-bg rounded-lg rounded-tl-none px-4 py-3 max-w-[85%]">
229
+ <p class="text-[14px] text-vm-text leading-relaxed">Welcome! I'll help you register a new patient. Enter the details above or switch to guided mode, and I'll walk you through step by step.</p>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </div>
234
+ </div>
235
+
236
+ <!-- Left Column Sticky Footer - Chat Input -->
237
+ <div class="shrink-0 pt-4">
238
+ <div class="vm-card p-4">
239
+ <div class="flex items-center gap-3">
240
+ <input type="text" id="chat-input"
241
+ class="flex-1 border border-vm-border rounded-lg px-4 py-2.5 text-[14px] text-vm-text placeholder-vm-muted/60 focus:border-vm-teal focus:ring-1 focus:ring-vm-teal/20 transition-all"
242
+ placeholder="Type your response..."
243
+ onkeydown="if(event.key==='Enter')handleChatSend()">
244
+ <button onclick="handleChatSend()"
245
+ class="bg-vm-teal hover:bg-vm-teal-hover text-white p-2.5 rounded-lg transition-colors shrink-0">
246
+ <i data-lucide="send" class="w-4 h-4"></i>
247
+ </button>
248
+ </div>
249
+ </div>
250
+ </div>
251
+ </div>
252
+
253
+ <!-- RIGHT COLUMN (35%) -->
254
+ <div id="right-column" class="hidden lg:flex w-[35%] flex-col min-h-0">
255
+
256
+ <!-- Performance Indicator -->
257
+ <div id="perf-indicator" class="vm-card p-4 mb-6 hidden">
258
+ <div class="flex items-center gap-2">
259
+ <div class="w-5 h-5 rounded-full bg-vm-success/10 flex items-center justify-center">
260
+ <i data-lucide="zap" class="w-3 h-3 text-vm-success"></i>
261
+ </div>
262
+ <span id="perf-text" class="text-[13px] font-medium text-vm-text"></span>
263
+ </div>
264
+ </div>
265
+
266
+ <!-- Patient Record Card -->
267
+ <div class="vm-card flex-1 min-h-0 flex flex-col overflow-hidden">
268
+ <div class="px-6 py-4 border-b border-vm-border/60 flex items-center justify-between">
269
+ <div class="flex items-center gap-2">
270
+ <i data-lucide="user-circle" class="w-4 h-4 text-vm-muted"></i>
271
+ <span class="text-[14px] font-medium text-vm-text">Patient Record</span>
272
+ </div>
273
+ <span id="completeness-badge" class="text-[11px] font-medium text-vm-muted bg-vm-bg px-2 py-0.5 rounded-full">0% complete</span>
274
+ </div>
275
+
276
+ <div id="record-area" class="flex-1 overflow-y-auto custom-scroll px-6 py-4 space-y-6">
277
+
278
+ <!-- Personal Information -->
279
+ <div>
280
+ <h3 class="text-[12px] font-medium text-vm-muted uppercase tracking-wider mb-3">Personal Information</h3>
281
+ <div class="space-y-0">
282
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('fullName')" data-field="fullName">
283
+ <span class="text-[13px] text-vm-muted">Full Name</span>
284
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-fullName">β€”</span>
285
+ </div>
286
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('dob')" data-field="dob">
287
+ <span class="text-[13px] text-vm-muted">Date of Birth</span>
288
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-dob">β€”</span>
289
+ </div>
290
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('phone')" data-field="phone">
291
+ <span class="text-[13px] text-vm-muted">Phone</span>
292
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-phone">β€”</span>
293
+ </div>
294
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('email')" data-field="email">
295
+ <span class="text-[13px] text-vm-muted">Email</span>
296
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-email">β€”</span>
297
+ </div>
298
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('address')" data-field="address">
299
+ <span class="text-[13px] text-vm-muted">Address</span>
300
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-address">β€”</span>
301
+ </div>
302
+ </div>
303
+ </div>
304
+
305
+ <!-- Emergency Contact -->
306
+ <div>
307
+ <h3 class="text-[12px] font-medium text-vm-muted uppercase tracking-wider mb-3">Emergency Contact</h3>
308
+ <div class="space-y-0">
309
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('emergencyName')" data-field="emergencyName">
310
+ <span class="text-[13px] text-vm-muted">Contact Name</span>
311
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-emergencyName">β€”</span>
312
+ </div>
313
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('emergencyPhone')" data-field="emergencyPhone">
314
+ <span class="text-[13px] text-vm-muted">Contact Phone</span>
315
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-emergencyPhone">β€”</span>
316
+ </div>
317
+ </div>
318
+ </div>
319
+
320
+ <!-- Insurance -->
321
+ <div>
322
+ <h3 class="text-[12px] font-medium text-vm-muted uppercase tracking-wider mb-3">Insurance</h3>
323
+ <div class="space-y-0">
324
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('insuranceProvider')" data-field="insuranceProvider">
325
+ <span class="text-[13px] text-vm-muted">Provider</span>
326
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-insuranceProvider">β€”</span>
327
+ </div>
328
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('policyNumber')" data-field="policyNumber">
329
+ <span class="text-[13px] text-vm-muted">Policy Number</span>
330
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-policyNumber">β€”</span>
331
+ </div>
332
+ </div>
333
+ </div>
334
+
335
+ <!-- Medical Information -->
336
+ <div>
337
+ <h3 class="text-[12px] font-medium text-vm-muted uppercase tracking-wider mb-3">Medical Information</h3>
338
+ <div class="space-y-0">
339
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('allergies')" data-field="allergies">
340
+ <span class="text-[13px] text-vm-muted">Allergies</span>
341
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-allergies">β€”</span>
342
+ </div>
343
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('medications')" data-field="medications">
344
+ <span class="text-[13px] text-vm-muted">Current Medications</span>
345
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-medications">β€”</span>
346
+ </div>
347
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('conditions')" data-field="conditions">
348
+ <span class="text-[13px] text-vm-muted">Medical Conditions</span>
349
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-conditions">β€”</span>
350
+ </div>
351
+ <div class="field-row flex items-center justify-between py-2.5 px-2 rounded-md cursor-pointer" onclick="editField('reasonForVisit')" data-field="reasonForVisit">
352
+ <span class="text-[13px] text-vm-muted">Reason for Visit</span>
353
+ <span class="field-value text-[14px] text-vm-text font-medium" id="display-reasonForVisit">β€”</span>
354
+ </div>
355
+ </div>
356
+ </div>
357
+ </div>
358
+ </div>
359
+
360
+ <!-- Right Column Sticky Footer -->
361
+ <div class="shrink-0 pt-4">
362
+ <button onclick="reviewAndCreate()" id="create-btn"
363
+ class="w-full bg-vm-teal hover:bg-vm-teal-hover text-white text-[14px] font-medium py-3 rounded-lg transition-colors flex items-center justify-center gap-2">
364
+ <i data-lucide="check-circle" class="w-4 h-4"></i>
365
+ Review & Create Patient
366
+ </button>
367
+ <p class="text-[11px] text-vm-muted text-center mt-2">Verify all information before creating the record</p>
368
+ </div>
369
+ </div>
370
+ </div>
371
+ </main>
372
+
373
+ <!-- Edit Field Modal -->
374
+ <div id="edit-modal" class="fixed inset-0 z-50 hidden">
375
+ <div class="absolute inset-0 bg-black/20" onclick="closeEditModal()"></div>
376
+ <div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-lg border border-vm-border p-6 w-[90%] max-w-md fade-in-up">
377
+ <h3 id="edit-modal-title" class="text-[16px] font-medium text-vm-text mb-4">Edit Field</h3>
378
+ <input type="text" id="edit-modal-input"
379
+ class="w-full border border-vm-border rounded-lg px-4 py-3 text-[14px] text-vm-text focus:border-vm-teal focus:ring-1 focus:ring-vm-teal/20 transition-all"
380
+ onkeydown="if(event.key==='Enter')saveEditField()">
381
+ <div class="flex items-center justify-end gap-3 mt-5">
382
+ <button onclick="closeEditModal()" class="text-[13px] font-medium text-vm-muted hover:text-vm-text transition-colors px-4 py-2">Cancel</button>
383
+ <button onclick="saveEditField()" class="bg-vm-teal hover:bg-vm-teal-hover text-white text-[13px] font-medium px-5 py-2 rounded-lg transition-colors">Save</button>
384
+ </div>
385
+ </div>
386
+ </div>
387
+
388
+ <!-- Success Modal -->
389
+ <div id="success-modal" class="fixed inset-0 z-50 hidden">
390
+ <div class="absolute inset-0 bg-black/20"></div>
391
+ <div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-lg border border-vm-border p-8 w-[90%] max-w-sm text-center fade-in-up">
392
+ <div class="w-12 h-12 rounded-full bg-vm-success/10 flex items-center justify-center mx-auto mb-4">
393
+ <i data-lucide="check" class="w-6 h-6 text-vm-success"></i>
394
+ </div>
395
+ <h3 class="text-[18px] font-semibold text-vm-text mb-2">Patient Created</h3>
396
+ <p class="text-[14px] text-vm-muted mb-6">The patient record has been successfully created and saved.</p>
397
+ <button onclick="closeSuccessModal()" class="bg-vm-teal hover:bg-vm-teal-hover text-white text-[14px] font-medium px-6 py-2.5 rounded-lg transition-colors">Done</button>
398
+ </div>
399
+ </div>
400
+
401
+ <script>
402
+ // ─── STATE ───────────────────────────────────────────────────
403
+ const state = {
404
+ mode: 'oneshot',
405
+ patientRecord: {
406
+ fullName: '',
407
+ dob: '',
408
+ phone: '',
409
+ email: '',
410
+ address: '',
411
+ emergencyName: '',
412
+ emergencyPhone: '',
413
+ insuranceProvider: '',
414
+ policyNumber: '',
415
+ allergies: '',
416
+ medications: '',
417
+ conditions: '',
418
+ reasonForVisit: ''
419
+ },
420
+ conversations: [],
421
+ questionsQueue: [],
422
+ currentQuestionField: null,
423
+ isProcessing: false,
424
+ isConversationActive: false,
425
+ autoFilledCount: 0,
426
+ autoFillStartTime: 0,
427
+ editingField: null
428
+ };
429
+
430
+ const fieldLabels = {
431
+ fullName: 'Full Name',
432
+ dob: 'Date of Birth',
433
+ phone: 'Phone Number',
434
+ email: 'Email Address',
435
+ address: 'Address',
436
+ emergencyName: 'Emergency Contact Name',
437
+ emergencyPhone: 'Emergency Contact Phone',
438
+ insuranceProvider: 'Insurance Provider',
439
+ policyNumber: 'Policy Number',
440
+ allergies: 'Known Allergies',
441
+ medications: 'Current Medications',
442
+ conditions: 'Medical Conditions',
443
+ reasonForVisit: 'Reason for Visit'
444
+ };
445
+
446
+ const questionMap = {
447
+ fullName: "What is the patient's full name?",
448
+ dob: "What is the patient's date of birth?",
449
+ phone: "What is the patient's phone number?",
450
+ email: "Do you have an email address for this patient?",
451
+ address: "What is the patient's home address?",
452
+ emergencyName: "Who should we contact in case of emergency?",
453
+ emergencyPhone: "What is the emergency contact's phone number?",
454
+ insuranceProvider: "What is the patient's insurance provider?",
455
+ policyNumber: "What is the insurance policy number?",
456
+ allergies: "Does the patient have any known allergies?",
457
+ medications: "Is the patient currently taking any medications?",
458
+ conditions: "Does the patient have any existing medical conditions?",
459
+ reasonForVisit: "What is the reason for today's visit?"
460
+ };
461
+
462
+ // Priority order for questions
463
+ const fieldOrder = ['fullName', 'dob', 'phone', 'email', 'address', 'emergencyName', 'emergencyPhone', 'insuranceProvider', 'policyNumber', 'allergies', 'medications', 'conditions', 'reasonForVisit'];
464
+ const requiredFields = ['fullName', 'dob', 'phone'];
465
+
466
+ // ─── SUGGESTIONS ─────────────────────────────────────────────
467
+ const suggestions = [
468
+ "Sarah Johnson, DOB 03/15/1985, 555-123-4567",
469
+ "Michael Chen, born 07/22/1990, mchen@email.com, BlueCross Insurance",
470
+ "Emily Davis, DOB 11/05/1978, 555-987-6543, allergic to penicillin"
471
+ ];
472
+ let currentSuggestion = 0;
473
+
474
+ // ─── PARSE ONE-SHOT INPUT ────────────────────────────────────
475
+ function parseOneShotInput(input) {
476
+ const record = {};
477
+ let filledCount = 0;
478
+ const original = input;
479
+
480
+ // Extract email
481
+ const emailMatch = input.match(/[\w.-]+@[\w.-]+\.\w+/);
482
+ if (emailMatch) {
483
+ record.email = emailMatch[0];
484
+ filledCount++;
485
+ input = input.replace(emailMatch[0], ' ');
486
+ }
487
+
488
+ // Extract phone
489
+ const phonePatterns = [
490
+ /(?:phone|tel|cell|mobile)?[:\s]*(\(\d{3}\)\s*\d{3}[-.]?\d{4})/i,
491
+ /(?:phone|tel|cell|mobile)?[:\s]*(\d{3}[-.]\d{3}[-.]\d{4})/i,
492
+ /(\d{3}[-.]\d{3}[-.]\d{4})/,
493
+ /(\(\d{3}\)\s*\d{3}[-.]?\d{4})/
494
+ ];
495
+ for (const pat of phonePatterns) {
496
+ const m = input.match(pat);
497
+ if (m) {
498
+ record.phone = m[1].trim();
499
+ filledCount++;
500
+ input = input.replace(m[0], ' ');
501
+ break;
502
+ }
503
+ }
504
+
505
+ // Extract DOB
506
+ const dobPatterns = [
507
+ /(?:DOB|born|date of birth|d\.o\.b\.?)[:\s]*(\d{1,2}[\/-]\d{1,2}[\/-]\d{2,4})/i,
508
+ /(\d{1,2}[\/-]\d{1,2}[\/-]\d{4})/
509
+ ];
510
+ for (const pat of dobPatterns) {
511
+ const m = input.match(pat);
512
+ if (m) {
513
+ let dob = m[1];
514
+ // Normalize to MM/DD/YYYY
515
+ const parts = dob.split(/[\/\-]/);
516
+ if (parts.length === 3) {
517
+ if (parts[2].length === 2) parts[2] = '19' + parts[2];
518
+ if (parseInt(parts[0]) > 12) {
519
+ // Might be DD/MM/YYYY, swap
520
+ [parts[0], parts[1]] = [parts[1], parts[0]];
521
+ }
522
+ dob = parts[0].padStart(2, '0') + '/' + parts[1].padStart(2, '0') + '/' + parts[2];
523
+ }
524
+ record.dob = dob;
525
+ filledCount++;
526
+ input = input.replace(m[0], ' ');
527
+ break;
528
+ }
529
+ }
530
+
531
+ // Extract insurance
532
+ const insuranceMatch = input.match(/(?:insurance|ins|provider|coverage)[:\s]*(.+?)(?:,|\.|$)/i);
533
+ if (insuranceMatch) {
534
+ record.insuranceProvider = insuranceMatch[1].trim();
535
+ filledCount++;
536
+ input = input.replace(insuranceMatch[0], ' ');
537
+ } else {
538
+ // Check for known insurance names
539
+ const knownInsurers = ['Aetna', 'BlueCross', 'Blue Shield', 'Cigna', 'UnitedHealth', 'Humana', 'Kaiser', 'Medicare', 'Medicaid', 'Anthem'];
540
+ for (const ins of knownInsurers) {
541
+ if (input.toLowerCase().includes(ins.toLowerCase())) {
542
+ record.insuranceProvider = ins;
543
+ filledCount++;
544
+ input = input.replace(new RegExp(ins, 'i'), ' ');
545
+ break;
546
+ }
547
+ }
548
+ }
549
+
550
+ // Extract allergies
551
+ const allergyMatch = input.match(/(?:allergic to|allergies|allergy)[:\s]*(.+?)(?:,|\.|$)/i);
552
+ if (allergyMatch) {
553
+ record.allergies = allergyMatch[1].trim();
554
+ filledCount++;
555
+ input = input.replace(allergyMatch[0], ' ');
556
+ }
557
+
558
+ // Extract medications
559
+ const medMatch = input.match(/(?:medications?|meds?|taking)[:\s]*(.+?)(?:,|\.|$)/i);
560
+ if (medMatch) {
561
+ record.medications = medMatch[1].trim();
562
+ filledCount++;
563
+ input = input.replace(medMatch[0], ' ');
564
+ }
565
+
566
+ // Extract conditions
567
+ const condMatch = input.match(/(?:conditions?|diagnosed with|history of)[:\s]*(.+?)(?:,|\.|$)/i);
568
+ if (condMatch) {
569
+ record.conditions = condMatch[1].trim();
570
+ filledCount++;
571
+ input = input.replace(condMatch[0], ' ');
572
+ }
573
+
574
+ // Extract address
575
+ const addrMatch = input.match(/(?:address|addr|lives at|located at)[:\s]*(.+?)(?:,|\.|$)/i);
576
+ if (addrMatch) {
577
+ record.address = addrMatch[1].trim();
578
+ filledCount++;
579
+ input = input.replace(addrMatch[0], ' ');
580
+ }
581
+
582
+ // Extract reason for visit
583
+ const reasonMatch = input.match(/(?:reason|visit|appointment|seeing|complaint)[:\s]*(.+?)(?:,|\.|$)/i);
584
+ if (reasonMatch) {
585
+ record.reasonForVisit = reasonMatch[1].trim();
586
+ filledCount++;
587
+ input = input.replace(reasonMatch[0], ' ');
588
+ }
589
+
590
+ // Extract name (remaining text, look for capitalized words pattern)
591
+ let remaining = input.replace(/\s+/g, ' ').trim();
592
+ // Remove common noise words
593
+ remaining = remaining.replace(/\b(DOB|born|phone|tel|email|insurance|allergies?|medications?|patient)\b/gi, '');
594
+ remaining = remaining.replace(/[,\d\(\)\-\.\/]/g, ' ').replace(/\s+/g, ' ').trim();
595
+
596
+ // Find name pattern: 2-3 capitalized words
597
+ const nameMatch = remaining.match(/([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)/);
598
+ if (nameMatch) {
599
+ record.fullName = nameMatch[1].trim();
600
+ filledCount++;
601
+ } else {
602
+ // Try to get any remaining meaningful text as name
603
+ const words = remaining.split(/\s+/).filter(w => w.length > 1 && /^[A-Za-z]/.test(w));
604
+ if (words.length >= 2) {
605
+ const nameWords = [];
606
+ for (const w of words) {
607
+ if (/^[A-Z]/.test(w)) nameWords.push(w);
608
+ else if (nameWords.length >= 2) break;
609
+ }
610
+ if (nameWords.length >= 2) {
611
+ record.fullName = nameWords.join(' ');
612
+ filledCount++;
613
+ }
614
+ }
615
+ }
616
+
617
+ return { record, filledCount };
618
+ }
619
+
620
+ // ─── MODE TOGGLE ─────────────────────────────────────────────
621
+ function setMode(mode) {
622
+ state.mode = mode;
623
+ const oneshotBtn = document.getElementById('mode-oneshot');
624
+ const guidedBtn = document.getElementById('mode-guided');
625
+ const oneshotCard = document.getElementById('oneshot-card');
626
+
627
+ if (mode === 'oneshot') {
628
+ oneshotBtn.classList.add('active');
629
+ oneshotBtn.classList.remove('text-vm-muted');
630
+ guidedBtn.classList.remove('active');
631
+ guidedBtn.classList.add('text-vm-muted');
632
+ oneshotCard.classList.remove('hidden');
633
+ } else {
634
+ guidedBtn.classList.add('active');
635
+ guidedBtn.classList.remove('text-vm-muted');
636
+ oneshotBtn.classList.remove('active');
637
+ oneshotBtn.classList.add('text-vm-muted');
638
+ oneshotCard.classList.add('hidden');
639
+
640
+ // Start guided intake
641
+ if (!state.isConversationActive) {
642
+ startGuidedIntake();
643
+ }
644
+ }
645
+ }
646
+
647
+ // ─── SUGGESTION FILL ─────────────────────────────────────────
648
+ function fillSuggestion() {
649
+ const input = document.getElementById('oneshot-input');
650
+ input.value = suggestions[currentSuggestion];
651
+ input.focus();
652
+ currentSuggestion = (currentSuggestion + 1) % suggestions.length;
653
+ }
654
+
655
+ // ─── ONE-SHOT HANDLER ────────────────────────────────────────
656
+ async function handleOneShot() {
657
+ const input = document.getElementById('oneshot-input').value.trim();
658
+ if (!input || state.isProcessing) return;
659
+
660
+ state.isProcessing = true;
661
+ state.autoFillStartTime = Date.now();
662
+
663
+ // Show processed summary
664
+ document.getElementById('oneshot-card').classList.add('hidden');
665
+ document.getElementById('oneshot-processed').classList.remove('hidden');
666
+ document.getElementById('processed-text').textContent = `"${input.substring(0, 50)}${input.length > 50 ? '...' : ''}" processed`;
667
+
668
+ // Parse the input
669
+ const { record, filledCount } = parseOneShotInput(input);
670
+
671
+ // Show typing indicator
672
+ showAITyping();
673
+
674
+ // Simulate processing delay
675
+ await delay(1200);
676
+
677
+ // Apply parsed data to patient record
678
+ let actualFilled = 0;
679
+ for (const [key, value] of Object.entries(record)) {
680
+ if (value && !state.patientRecord[key]) {
681
+ state.patientRecord[key] = value;
682
+ actualFilled++;
683
+ }
684
+ }
685
+
686
+ state.autoFilledCount = actualFilled;
687
+ const elapsed = ((Date.now() - state.autoFillStartTime) / 1000).toFixed(1);
688
+
689
+ // Update display
690
+ updateRecordDisplay();
691
+ showPerformanceIndicator(actualFilled, elapsed);
692
+ hideAITyping();
693
+
694
+ // Build conversation
695
+ state.isConversationActive = true;
696
+
697
+ // AI greeting
698
+ const firstName = state.patientRecord.fullName ? state.patientRecord.fullName.split(' ')[0] : 'the patient';
699
+ await addAIMessage(`I've captured the details for ${firstName}. Let me fill in the record for you.`);
700
+
701
+ // Highlight filled fields
702
+ for (const key of Object.keys(record)) {
703
+ if (record[key]) {
704
+ highlightField(key);
705
+ }
706
+ }
707
+
708
+ await delay(800);
709
+
710
+ // Check what's missing
711
+ const missingFields = fieldOrder.filter(f => !state.patientRecord[f]);
712
+
713
+ if (missingFields.length > 0) {
714
+ await addAIMessage(`I've added most of the patient details. I still need a few things to complete the record.`);
715
+
716
+ // Queue up questions for missing fields
717
+ state.questionsQueue = missingFields;
718
+ await delay(600);
719
+ askNextQuestion();
720
+ } else {
721
+ await addAIMessage(`All patient details have been captured. You can review the record on the right and make any adjustments needed.`);
722
+ }
723
+
724
+ state.isProcessing = false;
725
+ }
726
+
727
+ // ─── GUIDED INTAKE ───────────────────────────────────────────
728
+ async function startGuidedIntake() {
729
+ if (state.isConversationActive) return;
730
+
731
+ state.isConversationActive = true;
732
+ state.isProcessing = true;
733
+
734
+ showAITyping();
735
+ await delay(800);
736
+ hideAITyping();
737
+
738
+ await addAIMessage("Welcome! I'll help you register a new patient. Let's go through this together β€” I'll ask one question at a time.");
739
+
740
+ await delay(600);
741
+
742
+ // Queue all fields
743
+ state.questionsQueue = [...fieldOrder];
744
+ askNextQuestion();
745
+
746
+ state.isProcessing = false;
747
+ }
748
+
749
+ // ─── QUESTION FLOW ───────────────────────────────────────────
750
+ async function askNextQuestion() {
751
+ if (state.questionsQueue.length === 0) {
752
+ await addAIMessage("That's everything I need! The patient record is now complete. Review the details on the right, and click **Review & Create Patient** when you're ready.");
753
+ state.isConversationActive = false;
754
+ return;
755
+ }
756
+
757
+ const nextField = state.questionsQueue.shift();
758
+ state.currentQuestionField = nextField;
759
+
760
+ const question = questionMap[nextField];
761
+ const isRequired = requiredFields.includes(nextField);
762
+
763
+ await addAIMessageWithSkip(question, nextField, isRequired);
764
+ }
765
+
766
+ // ─── CHAT SEND ───────────────────────────────────────────────
767
+ async function handleChatSend() {
768
+ const input = document.getElementById('chat-input');
769
+ const value = input.value.trim();
770
+ if (!value || state.isProcessing) return;
771
+
772
+ input.value = '';
773
+
774
+ // Add user message
775
+ addUserMessage(value);
776
+
777
+ // Process response
778
+ state.isProcessing = true;
779
+
780
+ if (state.currentQuestionField) {
781
+ // Update the patient record with the answer
782
+ state.patientRecord[state.currentQuestionField] = value;
783
+ updateRecordDisplay();
784
+ highlightField(state.currentQuestionField);
785
+
786
+ // Update completeness
787
+ updateCompleteness();
788
+
789
+ showAITyping();
790
+ await delay(800);
791
+ hideAITyping();
792
+
793
+ // Brief acknowledgment
794
+ const acknowledgments = [
795
+ "Got it.",
796
+ "Noted.",
797
+ "Added.",
798
+ "Thanks, I've added that.",
799
+ "Recorded.",
800
+ "Updated."
801
+ ];
802
+ await addAIMessage(acknowledgments[Math.floor(Math.random() * acknowledgments.length)]);
803
+
804
+ await delay(400);
805
+ state.currentQuestionField = null;
806
+
807
+ // Ask next question
808
+ askNextQuestion();
809
+ } else {
810
+ // General response - try to extract info
811
+ showAITyping();
812
+ await delay(600);
813
+ hideAITyping();
814
+ await addAIMessage("I've noted that. Let me know if there's anything else you'd like to update in the patient record.");
815
+ }
816
+
817
+ state.isProcessing = false;
818
+ }
819
+
820
+ // ─── SKIP HANDLER ────────────────────────────────────────────
821
+ async function handleSkip(field) {
822
+ // Remove skip button
823
+ const skipContainer = document.getElementById(`skip-${field}`);
824
+ if (skipContainer) skipContainer.remove();
825
+
826
+ addUserMessage("Skip");
827
+
828
+ showAITyping();
829
+ await delay(500);
830
+ hideAITyping();
831
+
832
+ await addAIMessage("No problem, we can fill that in later.");
833
+
834
+ state.currentQuestionField = null;
835
+ await delay(300);
836
+ askNextQuestion();
837
+ }
838
+
839
+ // ─── CONVERSATION UI ─────────────────────────────────────────
840
+ async function addAIMessage(text) {
841
+ const convArea = document.getElementById('conversation-area');
842
+ const msgDiv = document.createElement('div');
843
+ msgDiv.className = 'fade-in-up';
844
+
845
+ // Simple markdown bold handling
846
+ const formattedText = text.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
847
+
848
+ msgDiv.innerHTML = `
849
+ <div class="flex items-start gap-3">
850
+ <div class="w-6 h-6 rounded-full bg-vm-teal/10 flex items-center justify-center shrink-0 mt-0.5">
851
+ <i data-lucide="bot" class="w-3 h-3 text-vm-teal"></i>
852
+ </div>
853
+ <div class="bg-vm-bg rounded-lg rounded-tl-none px-4 py-3 max-w-[85%]">
854
+ <p class="text-[14px] text-vm-text leading-relaxed">${formattedText}</p>
855
+ </div>
856
+ </div>
857
+ `;
858
+
859
+ convArea.appendChild(msgDiv);
860
+ lucide.createIcons();
861
+ scrollToBottom();
862
+ }
863
+
864
+ async function addAIMessageWithSkip(text, field, isRequired) {
865
+ const convArea = document.getElementById('conversation-area');
866
+ const msgDiv = document.createElement('div');
867
+ msgDiv.className = 'fade-in-up';
868
+ msgDiv.id = `skip-${field}`;
869
+
870
+ const skipBtn = !isRequired
871
+ ? `<button onclick="handleSkip('${field}')" class="text-[12px] text-vm-muted hover:text-vm-teal transition-colors font-medium ml-3">Skip</button>`
872
+ : `<span class="text-[11px] text-vm-muted/60 ml-3">Required</span>`;
873
+
874
+ msgDiv.innerHTML = `
875
+ <div class="flex items-start gap-3">
876
+ <div class="w-6 h-6 rounded-full bg-vm-teal/10 flex items-center justify-center shrink-0 mt-0.5">
877
+ <i data-lucide="bot" class="w-3 h-3 text-vm-teal"></i>
878
+ </div>
879
+ <div>
880
+ <div class="bg-vm-bg rounded-lg rounded-tl-none px-4 py-3 max-w-[85%]">
881
+ <p class="text-[14px] text-vm-text leading-relaxed">${text}</p>
882
+ </div>
883
+ <div class="flex items-center mt-2 ml-1">
884
+ ${skipBtn}
885
+ </div>
886
+ </div>
887
+ </div>
888
+ `;
889
+
890
+ convArea.appendChild(msgDiv);
891
+ lucide.createIcons();
892
+ scrollToBottom();
893
+
894
+ // Focus the chat input
895
+ document.getElementById('chat-input').focus();
896
+ }
897
+
898
+ function addUserMessage(text) {
899
+ const convArea = document.getElementById('conversation-area');
900
+ const msgDiv = document.createElement('div');
901
+ msgDiv.className = 'fade-in-up flex justify-end';
902
+
903
+ msgDiv.innerHTML = `
904
+ <div class="bg-vm-teal text-white rounded-lg rounded-tr-none px-4 py-3 max-w-[75%]">
905
+ <p class="text-[14px] leading-relaxed">${text}</p>
906
+ </div>
907
+ `;
908
+
909
+ convArea.appendChild(msgDiv);
910
+ scrollToBottom();
911
+ }
912
+
913
+ function showAITyping() {
914
+ const convArea = document.getElementById('conversation-area');
915
+ const typingDiv = document.createElement('div');
916
+ typingDiv.id = 'typing-indicator';
917
+ typingDiv.className = 'fade-in-up';
918
+
919
+ typingDiv.innerHTML = `
920
+ <div class="flex items-start gap-3">
921
+ <div class="w-6 h-6 rounded-full bg-vm-teal/10 flex items-center justify-center shrink-0 mt-0.5">
922
+ <i data-lucide="bot" class="w-3 h-3 text-vm-teal"></i>
923
+ </div>
924
+ <div class="bg-vm-bg rounded-lg rounded-tl-none px-4 py-3">
925
+ <div class="flex items-center gap-1">
926
+ <div class="typing-dot w-1.5 h-1.5 bg-vm-muted rounded-full"></div>
927
+ <div class="typing-dot w-1.5 h-1.5 bg-vm-muted rounded-full"></div>
928
+ <div class="typing-dot w-1.5 h-1.5 bg-vm-muted rounded-full"></div>
929
+ </div>
930
+ </div>
931
+ </div>
932
+ `;
933
+
934
+ convArea.appendChild(typingDiv);
935
+ lucide.createIcons();
936
+ scrollToBottom();
937
+
938
+ document.getElementById('ai-status').classList.remove('hidden');
939
+ }
940
+
941
+ function hideAITyping() {
942
+ const typing = document.getElementById('typing-indicator');
943
+ if (typing) typing.remove();
944
+ document.getElementById('ai-status').classList.add('hidden');
945
+ }
946
+
947
+ function scrollToBottom() {
948
+ const convArea = document.getElementById('conversation-area');
949
+ requestAnimationFrame(() => {
950
+ convArea.scrollTop = convArea.scrollHeight;
951
+ });
952
+ }
953
+
954
+ // ─── RECORD DISPLAY ──────────────────────────────────────────
955
+ function updateRecordDisplay() {
956
+ for (const [key, value] of Object.entries(state.patientRecord)) {
957
+ const el = document.getElementById(`display-${key}`);
958
+ if (el) {
959
+ if (value) {
960
+ el.textContent = value;
961
+ el.classList.remove('text-vm-muted/50');
962
+ el.classList.add('text-vm-text', 'font-medium');
963
+ } else {
964
+ el.textContent = 'β€”';
965
+ el.classList.add('text-vm-muted/50');
966
+ el.classList.remove('font-medium');
967
+ }
968
+ }
969
+ }
970
+ updateCompleteness();
971
+ }
972
+
973
+ function highlightField(field) {
974
+ const row = document.querySelector(`[data-field="${field}"]`);
975
+ if (row) {
976
+ row.classList.remove('field-highlight');
977
+ void row.offsetWidth; // Trigger reflow
978
+ row.classList.add('field-highlight');
979
+ }
980
+ }
981
+
982
+ function updateCompleteness() {
983
+ const totalFields = fieldOrder.length;
984
+ const filledFields = fieldOrder.filter(f => state.patientRecord[f]).length;
985
+ const percentage = Math.round((filledFields / totalFields) * 100);
986
+
987
+ const badge = document.getElementById('completeness-badge');
988
+ badge.textContent = `${percentage}% complete`;
989
+
990
+ if (percentage === 100) {
991
+ badge.classList.remove('text-vm-muted', 'bg-vm-bg');
992
+ badge.classList.add('text-vm-success', 'bg-vm-success-bg');
993
+ } else if (percentage >= 50) {
994
+ badge.classList.remove('text-vm-muted', 'bg-vm-bg');
995
+ badge.classList.add('text-vm-warning', 'bg-vm-warning-bg');
996
+ }
997
+ }
998
+
999
+ // ─── PERFORMANCE INDICATOR ───────────────────────────────────
1000
+ function showPerformanceIndicator(count, time) {
1001
+ const indicator = document.getElementById('perf-indicator');
1002
+ const text = document.getElementById('perf-text');
1003
+ indicator.classList.remove('hidden');
1004
+ text.textContent = `${count} field${count !== 1 ? 's' : ''} auto-filled in ${time}s`;
1005
+ }
1006
+
1007
+ // ─── INLINE EDITING ──────────────────────────────────────────
1008
+ function editField(field) {
1009
+ state.editingField = field;
1010
+ const modal = document.getElementById('edit-modal');
1011
+ const title = document.getElementById('edit-modal-title');
1012
+ const input = document.getElementById('edit-modal-input');
1013
+
1014
+ title.textContent = `Edit ${fieldLabels[field]}`;
1015
+ input.value = state.patientRecord[field] || '';
1016
+ modal.classList.remove('hidden');
1017
+
1018
+ requestAnimationFrame(() => input.focus());
1019
+ }
1020
+
1021
+ function saveEditField() {
1022
+ const input = document.getElementById('edit-modal-input');
1023
+ const value = input.value.trim();
1024
+
1025
+ if (state.editingField) {
1026
+ state.patientRecord[state.editingField] = value;
1027
+ updateRecordDisplay();
1028
+ highlightField(state.editingField);
1029
+ }
1030
+
1031
+ closeEditModal();
1032
+ }
1033
+
1034
+ function closeEditModal() {
1035
+ document.getElementById('edit-modal').classList.add('hidden');
1036
+ state.editingField = null;
1037
+ }
1038
+
1039
+ // ─── REVIEW & CREATE ─────────────────────���───────────────────
1040
+ function reviewAndCreate() {
1041
+ // Check required fields
1042
+ const missing = requiredFields.filter(f => !state.patientRecord[f]);
1043
+
1044
+ if (missing.length > 0) {
1045
+ const missingLabels = missing.map(f => fieldLabels[f]);
1046
+ showToast(`Missing required fields: ${missingLabels.join(', ')}`, 'warning');
1047
+ return;
1048
+ }
1049
+
1050
+ // Show success modal
1051
+ document.getElementById('success-modal').classList.remove('hidden');
1052
+ lucide.createIcons();
1053
+ }
1054
+
1055
+ function closeSuccessModal() {
1056
+ document.getElementById('success-modal').classList.add('hidden');
1057
+
1058
+ // Reset everything
1059
+ state.patientRecord = {
1060
+ fullName: '', dob: '', phone: '', email: '', address: '',
1061
+ emergencyName: '', emergencyPhone: '', insuranceProvider: '',
1062
+ policyNumber: '', allergies: '', medications: '', conditions: '',
1063
+ reasonForVisit: ''
1064
+ };
1065
+ state.isConversationActive = false;
1066
+ state.isProcessing = false;
1067
+ state.questionsQueue = [];
1068
+ state.currentQuestionField = null;
1069
+ state.autoFilledCount = 0;
1070
+
1071
+ updateRecordDisplay();
1072
+
1073
+ // Reset conversation
1074
+ const convArea = document.getElementById('conversation-area');
1075
+ convArea.innerHTML = `
1076
+ <div class="fade-in-up">
1077
+ <div class="flex items-start gap-3">
1078
+ <div class="w-6 h-6 rounded-full bg-vm-teal/10 flex items-center justify-center shrink-0 mt-0.5">
1079
+ <i data-lucide="bot" class="w-3 h-3 text-vm-teal"></i>
1080
+ </div>
1081
+ <div class="bg-vm-bg rounded-lg rounded-tl-none px-4 py-3 max-w-[85%]">
1082
+ <p class="text-[14px] text-vm-text leading-relaxed">Welcome! I'll help you register a new patient. Enter the details above or switch to guided mode, and I'll walk you through step by step.</p>
1083
+ </div>
1084
+ </div>
1085
+ </div>
1086
+ `;
1087
+ lucide.createIcons();
1088
+
1089
+ // Reset oneshot card
1090
+ document.getElementById('oneshot-card').classList.remove('hidden');
1091
+ document.getElementById('oneshot-processed').classList.add('hidden');
1092
+ document.getElementById('oneshot-input').value = '';
1093
+ document.getElementById('perf-indicator').classList.add('hidden');
1094
+
1095
+ // Reset completeness badge
1096
+ const badge = document.getElementById('completeness-badge');
1097
+ badge.textContent = '0% complete';
1098
+ badge.className = 'text-[11px] font-medium text-vm-muted bg-vm-bg px-2 py-0.5 rounded-full';
1099
+
1100
+ setMode('oneshot');
1101
+ }
1102
+
1103
+ // ─── RESET ONE-SHOT ──────────────────────────────────────────
1104
+ function resetOneShot() {
1105
+ document.getElementById('oneshot-card').classList.remove('hidden');
1106
+ document.getElementById('oneshot-processed').classList.add('hidden');
1107
+ document.getElementById('oneshot-input').focus();
1108
+ }
1109
+
1110
+ // ─── HEADER ACTIONS ──────────────────────────────────────────
1111
+ function saveDraft() {
1112
+ // Save to localStorage
1113
+ localStorage.setItem('verifyMC_draft', JSON.stringify(state.patientRecord));
1114
+ showToast('Draft saved successfully', 'success');
1115
+ }
1116
+
1117
+ function switchToManual() {
1118
+ showToast('Manual mode is not available in this demo', 'info');
1119
+ }
1120
+
1121
+ // ─── TOAST ───────────────────────────────────────────────────
1122
+ function showToast(message, type = 'info') {
1123
+ const container = document.getElementById('toast-container');
1124
+ const toast = document.createElement('div');
1125
+
1126
+ const colors = {
1127
+ success: 'bg-vm-success-bg border-vm-success/20 text-vm-success',
1128
+ warning: 'bg-vm-warning-bg border-vm-warning/20 text-vm-warning',
1129
+ info: 'bg-vm-bg border-vm-border text-vm-text',
1130
+ error: 'bg-vm-risk-bg border-vm-risk/20 text-vm-risk'
1131
+ };
1132
+
1133
+ const icons = {
1134
+ success: 'check-circle',
1135
+ warning: 'alert-triangle',
1136
+ info: 'info',
1137
+ error: 'alert-circle'
1138
+ };
1139
+
1140
+ toast.className = `toast-in flex items-center gap-2 px-4 py-3 rounded-lg border ${colors[type]} shadow-sm min-w-[260px]`;
1141
+ toast.innerHTML = `
1142
+ <i data-lucide="${icons[type]}" class="w-4 h-4 shrink-0"></i>
1143
+ <span class="text-[13px] font-medium">${message}</span>
1144
+ `;
1145
+
1146
+ container.appendChild(toast);
1147
+ lucide.createIcons();
1148
+
1149
+ setTimeout(() => {
1150
+ toast.classList.remove('toast-in');
1151
+ toast.classList.add('toast-out');
1152
+ setTimeout(() => toast.remove(), 300);
1153
+ }, 3000);
1154
+ }
1155
+
1156
+ // ─── MOBILE TABS ─────────────────────────────────────────────
1157
+ function showMobileTab(tab) {
1158
+ const leftCol = document.getElementById('left-column');
1159
+ const rightCol = document.getElementById('right-column');
1160
+ const tabAI = document.getElementById('mobile-tab-ai');
1161
+ const tabRecord = document.getElementById('mobile-tab-record');
1162
+
1163
+ if (tab === 'ai') {
1164
+ leftCol.classList.remove('hidden');
1165
+ rightCol.classList.add('hidden');
1166
+ rightCol.classList.remove('flex');
1167
+ tabAI.classList.add('active');
1168
+ tabRecord.classList.remove('active');
1169
+ tabRecord.classList.add('text-vm-muted');
1170
+ } else {
1171
+ leftCol.classList.add('hidden');
1172
+ rightCol.classList.remove('hidden');
1173
+ rightCol.classList.add('flex');
1174
+ tabRecord.classList.add('active');
1175
+ tabRecord.classList.remove('text-vm-muted');
1176
+ tabAI.classList.remove('active');
1177
+ }
1178
+ }
1179
+
1180
+ // ─── UTILITY ─────────────────────────────────────────────────
1181
+ function delay(ms) {
1182
+ return new Promise(resolve => setTimeout(resolve, ms));
1183
+ }
1184
+
1185
+ // ─── LOAD DRAFT ──────────────────────────────────────────────
1186
+ function loadDraft() {
1187
+ const draft = localStorage.getItem('verifyMC_draft');
1188
+ if (draft) {
1189
+ try {
1190
+ const parsed = JSON.parse(draft);
1191
+ const hasData = Object.values(parsed).some(v => v);
1192
+ if (hasData) {
1193
+ Object.assign(state.patientRecord, parsed);
1194
+ updateRecordDisplay();
1195
+ showToast('Draft recovered from previous session', 'info');
1196
+ }
1197
+ } catch (e) {}
1198
+ }
1199
+ }
1200
+
1201
+ // ─── INIT ────────────────────────────────────────────────────
1202
+ document.addEventListener('DOMContentLoaded', () => {
1203
+ lucide.createIcons();
1204
+ loadDraft();
1205
+
1206
+ // Handle responsive on resize
1207
+ function handleResize() {
1208
+ const rightCol = document.getElementById('right-column');
1209
+ const leftCol = document.getElementById('left-column');
1210
+ if (window.innerWidth >= 1024) {
1211
+ rightCol.classList.remove('hidden');
1212
+ rightCol.classList.add('flex');
1213
+ leftCol.classList.remove('hidden');
1214
+ }
1215
+ }
1216
+ window.addEventListener('resize', handleResize);
1217
+ handleResize();
1218
+ });
1219
+ </script>
1220
+ <script src="https://deepsite.hf.co/deepsite-badge.js"></script>
1221
+ </body>
1222
+ </html>