Spaces:
Sleeping
Sleeping
Synced repo using 'sync_with_huggingface' Github Action
Browse filesThis view is limited to 50 files because it contains too many changes. Β
See raw diff
- .gitattributes +13 -0
- Business Strategy Presentation/app.js +534 -0
- Business Strategy Presentation/index.html +638 -0
- Business Strategy Presentation/readme.txt.txt +1 -0
- Business Strategy Presentation/style.css +1575 -0
- __pycache__/agent.cpython-312.pyc +0 -0
- __pycache__/sticky.cpython-312.pyc +0 -0
- __pycache__/sticky.cpython-313.pyc +0 -0
- __pycache__/tools.cpython-312.pyc +0 -0
- __pycache__/tools.cpython-313.pyc +0 -0
- __pycache__/ui_utils.cpython-312.pyc +0 -0
- __pycache__/var.cpython-313.pyc +0 -0
- app.py +500 -0
- assets/all_resto.png +3 -0
- assets/booking_successful.png +3 -0
- assets/general_conv_info_through_chat.png +3 -0
- assets/greet_general_convo.png +3 -0
- assets/landing.png +3 -0
- assets/mermaid-1.png +3 -0
- assets/mermaid.png +3 -0
- assets/name_entering.png +3 -0
- assets/ready_to_book.png +3 -0
- assets/rubbish.PNG +3 -0
- assets/some_results.PNG +3 -0
- db/chroma/0f1c557e-a6e2-45cb-8079-0720b4f2093f/data_level0.bin +3 -0
- db/chroma/0f1c557e-a6e2-45cb-8079-0720b4f2093f/header.bin +3 -0
- db/chroma/0f1c557e-a6e2-45cb-8079-0720b4f2093f/length.bin +3 -0
- db/chroma/0f1c557e-a6e2-45cb-8079-0720b4f2093f/link_lists.bin +0 -0
- db/chroma/45948c9b-58d5-4762-9fd1-cf9b5925ba84/data_level0.bin +3 -0
- db/chroma/45948c9b-58d5-4762-9fd1-cf9b5925ba84/header.bin +3 -0
- db/chroma/45948c9b-58d5-4762-9fd1-cf9b5925ba84/length.bin +3 -0
- db/chroma/45948c9b-58d5-4762-9fd1-cf9b5925ba84/link_lists.bin +0 -0
- db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/data_level0.bin +3 -0
- db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/header.bin +3 -0
- db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/index_metadata.pickle +3 -0
- db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/length.bin +3 -0
- db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/link_lists.bin +3 -0
- db/chroma/chroma.sqlite3 +3 -0
- db/create_base.py +64 -0
- db/create_slots.py +33 -0
- db/dbmodify.py +54 -0
- db/fill_details.py +141 -0
- db/print_db.py +24 -0
- db/resetdb.py +27 -0
- db/restaurant_reservation.db +3 -0
- efficiency_log.txt +141 -0
- prompts/determine_intent.txt +21 -0
- prompts/generate_reservation_conversation.txt +30 -0
- prompts/interpret_sql_result.txt +28 -0
- prompts/schema_prompt.txt +50 -0
.gitattributes
CHANGED
@@ -33,3 +33,16 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
assets/all_resto.png filter=lfs diff=lfs merge=lfs -text
|
37 |
+
assets/general_conv_info_through_chat.png filter=lfs diff=lfs merge=lfs -text
|
38 |
+
assets/some_results.PNG filter=lfs diff=lfs merge=lfs -text
|
39 |
+
assets/greet_general_convo.png filter=lfs diff=lfs merge=lfs -text
|
40 |
+
assets/name_entering.png filter=lfs diff=lfs merge=lfs -text
|
41 |
+
assets/rubbish.PNG filter=lfs diff=lfs merge=lfs -text
|
42 |
+
assets/landing.png filter=lfs diff=lfs merge=lfs -text
|
43 |
+
assets/mermaid.png filter=lfs diff=lfs merge=lfs -text
|
44 |
+
assets/ready_to_book.png filter=lfs diff=lfs merge=lfs -text
|
45 |
+
assets/mermaid-1.png filter=lfs diff=lfs merge=lfs -text
|
46 |
+
assets/booking_successful.png filter=lfs diff=lfs merge=lfs -text
|
47 |
+
db/restaurant_reservation.db filter=lfs diff=lfs merge=lfs -text
|
48 |
+
db/chroma/chroma.sqlite3 filter=lfs diff=lfs merge=lfs -text
|
Business Strategy Presentation/app.js
ADDED
@@ -0,0 +1,534 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Presentation JavaScript functionality
|
2 |
+
|
3 |
+
class Presentation {
|
4 |
+
constructor() {
|
5 |
+
this.currentSlide = 0;
|
6 |
+
this.totalSlides = 15;
|
7 |
+
this.slides = [];
|
8 |
+
this.isFullscreen = false;
|
9 |
+
|
10 |
+
this.init();
|
11 |
+
}
|
12 |
+
|
13 |
+
init() {
|
14 |
+
// Wait for DOM to be fully loaded
|
15 |
+
if (document.readyState === 'loading') {
|
16 |
+
document.addEventListener('DOMContentLoaded', () => this.setupPresentation());
|
17 |
+
} else {
|
18 |
+
this.setupPresentation();
|
19 |
+
}
|
20 |
+
}
|
21 |
+
|
22 |
+
setupPresentation() {
|
23 |
+
this.slides = document.querySelectorAll('.slide');
|
24 |
+
console.log('Found slides:', this.slides.length);
|
25 |
+
|
26 |
+
if (this.slides.length === 0) {
|
27 |
+
console.error('No slides found!');
|
28 |
+
return;
|
29 |
+
}
|
30 |
+
|
31 |
+
this.totalSlides = this.slides.length;
|
32 |
+
this.bindEvents();
|
33 |
+
this.showSlide(0);
|
34 |
+
this.updateProgress();
|
35 |
+
this.updateCounter();
|
36 |
+
this.updateNavigationButtons();
|
37 |
+
}
|
38 |
+
|
39 |
+
bindEvents() {
|
40 |
+
// Button controls
|
41 |
+
const prevBtn = document.getElementById('prev-slide');
|
42 |
+
const nextBtn = document.getElementById('next-slide');
|
43 |
+
const fullscreenBtn = document.getElementById('fullscreen-toggle');
|
44 |
+
|
45 |
+
if (prevBtn) prevBtn.addEventListener('click', () => this.previousSlide());
|
46 |
+
if (nextBtn) nextBtn.addEventListener('click', () => this.nextSlide());
|
47 |
+
if (fullscreenBtn) fullscreenBtn.addEventListener('click', () => this.toggleFullscreen());
|
48 |
+
|
49 |
+
// Keyboard controls
|
50 |
+
document.addEventListener('keydown', (e) => this.handleKeydown(e));
|
51 |
+
|
52 |
+
// Fullscreen change events
|
53 |
+
document.addEventListener('fullscreenchange', () => this.handleFullscreenChange());
|
54 |
+
document.addEventListener('webkitfullscreenchange', () => this.handleFullscreenChange());
|
55 |
+
document.addEventListener('mozfullscreenchange', () => this.handleFullscreenChange());
|
56 |
+
document.addEventListener('msfullscreenchange', () => this.handleFullscreenChange());
|
57 |
+
|
58 |
+
// Touch/swipe events for mobile
|
59 |
+
this.addTouchEvents();
|
60 |
+
|
61 |
+
console.log('Event listeners bound successfully');
|
62 |
+
}
|
63 |
+
|
64 |
+
handleKeydown(e) {
|
65 |
+
switch(e.key) {
|
66 |
+
case 'ArrowRight':
|
67 |
+
case ' ':
|
68 |
+
case 'Enter':
|
69 |
+
e.preventDefault();
|
70 |
+
this.nextSlide();
|
71 |
+
break;
|
72 |
+
case 'ArrowLeft':
|
73 |
+
e.preventDefault();
|
74 |
+
this.previousSlide();
|
75 |
+
break;
|
76 |
+
case 'Home':
|
77 |
+
e.preventDefault();
|
78 |
+
this.goToSlide(0);
|
79 |
+
break;
|
80 |
+
case 'End':
|
81 |
+
e.preventDefault();
|
82 |
+
this.goToSlide(this.totalSlides - 1);
|
83 |
+
break;
|
84 |
+
case 'f':
|
85 |
+
case 'F':
|
86 |
+
if (!e.ctrlKey && !e.metaKey) {
|
87 |
+
e.preventDefault();
|
88 |
+
this.toggleFullscreen();
|
89 |
+
}
|
90 |
+
break;
|
91 |
+
case 'Escape':
|
92 |
+
if (this.isFullscreen) {
|
93 |
+
this.exitFullscreen();
|
94 |
+
}
|
95 |
+
break;
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
addTouchEvents() {
|
100 |
+
let startX = 0;
|
101 |
+
let startY = 0;
|
102 |
+
const container = document.querySelector('.presentation-container');
|
103 |
+
|
104 |
+
if (!container) return;
|
105 |
+
|
106 |
+
container.addEventListener('touchstart', (e) => {
|
107 |
+
startX = e.touches[0].clientX;
|
108 |
+
startY = e.touches[0].clientY;
|
109 |
+
}, { passive: true });
|
110 |
+
|
111 |
+
container.addEventListener('touchend', (e) => {
|
112 |
+
if (!startX || !startY) return;
|
113 |
+
|
114 |
+
const endX = e.changedTouches[0].clientX;
|
115 |
+
const endY = e.changedTouches[0].clientY;
|
116 |
+
|
117 |
+
const deltaX = startX - endX;
|
118 |
+
const deltaY = startY - endY;
|
119 |
+
|
120 |
+
// Only trigger if horizontal swipe is dominant and significant
|
121 |
+
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) {
|
122 |
+
if (deltaX > 0) {
|
123 |
+
this.nextSlide();
|
124 |
+
} else {
|
125 |
+
this.previousSlide();
|
126 |
+
}
|
127 |
+
}
|
128 |
+
|
129 |
+
startX = 0;
|
130 |
+
startY = 0;
|
131 |
+
}, { passive: true });
|
132 |
+
}
|
133 |
+
|
134 |
+
nextSlide() {
|
135 |
+
if (this.currentSlide < this.totalSlides - 1) {
|
136 |
+
this.goToSlide(this.currentSlide + 1);
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
previousSlide() {
|
141 |
+
if (this.currentSlide > 0) {
|
142 |
+
this.goToSlide(this.currentSlide - 1);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
goToSlide(slideIndex) {
|
147 |
+
if (slideIndex >= 0 && slideIndex < this.totalSlides) {
|
148 |
+
console.log(`Going to slide ${slideIndex + 1}`);
|
149 |
+
|
150 |
+
// Hide current slide
|
151 |
+
this.hideAllSlides();
|
152 |
+
|
153 |
+
// Update current slide index
|
154 |
+
this.currentSlide = slideIndex;
|
155 |
+
|
156 |
+
// Show new slide
|
157 |
+
this.showSlide(slideIndex);
|
158 |
+
|
159 |
+
// Update UI elements
|
160 |
+
this.updateProgress();
|
161 |
+
this.updateCounter();
|
162 |
+
this.updateNavigationButtons();
|
163 |
+
|
164 |
+
// Handle slide-specific logic
|
165 |
+
this.handleSlideSpecifics(slideIndex);
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
hideAllSlides() {
|
170 |
+
this.slides.forEach(slide => {
|
171 |
+
slide.classList.remove('active', 'animate-in');
|
172 |
+
});
|
173 |
+
}
|
174 |
+
|
175 |
+
showSlide(index) {
|
176 |
+
const slide = this.slides[index];
|
177 |
+
if (slide) {
|
178 |
+
// Remove active class from all slides first
|
179 |
+
this.hideAllSlides();
|
180 |
+
|
181 |
+
// Add active class to current slide
|
182 |
+
slide.classList.add('active');
|
183 |
+
|
184 |
+
// Add animation after a brief delay
|
185 |
+
setTimeout(() => {
|
186 |
+
slide.classList.add('animate-in');
|
187 |
+
this.animateSlideContent(slide);
|
188 |
+
}, 50);
|
189 |
+
}
|
190 |
+
}
|
191 |
+
|
192 |
+
animateSlideContent(slide) {
|
193 |
+
const elements = slide.querySelectorAll('.bullet-points li, .value-prop, .flow-step, .metric-category, .tech-component, .framework-item, .phase-card, .roi-metric, .challenge-category, .advantage, .timeline-item');
|
194 |
+
|
195 |
+
elements.forEach((element, index) => {
|
196 |
+
element.style.opacity = '0';
|
197 |
+
element.style.transform = 'translateY(20px)';
|
198 |
+
element.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
|
199 |
+
|
200 |
+
setTimeout(() => {
|
201 |
+
element.style.opacity = '1';
|
202 |
+
element.style.transform = 'translateY(0)';
|
203 |
+
}, 100 + (index * 100));
|
204 |
+
});
|
205 |
+
}
|
206 |
+
|
207 |
+
updateProgress() {
|
208 |
+
const progressBar = document.querySelector('.progress-indicator');
|
209 |
+
if (progressBar) {
|
210 |
+
const progress = ((this.currentSlide + 1) / this.totalSlides) * 100;
|
211 |
+
progressBar.style.width = `${progress}%`;
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
updateCounter() {
|
216 |
+
const currentSlideEl = document.getElementById('current-slide');
|
217 |
+
const totalSlidesEl = document.getElementById('total-slides');
|
218 |
+
|
219 |
+
if (currentSlideEl) currentSlideEl.textContent = this.currentSlide + 1;
|
220 |
+
if (totalSlidesEl) totalSlidesEl.textContent = this.totalSlides;
|
221 |
+
}
|
222 |
+
|
223 |
+
updateNavigationButtons() {
|
224 |
+
const prevBtn = document.getElementById('prev-slide');
|
225 |
+
const nextBtn = document.getElementById('next-slide');
|
226 |
+
|
227 |
+
if (prevBtn) {
|
228 |
+
prevBtn.disabled = this.currentSlide === 0;
|
229 |
+
}
|
230 |
+
|
231 |
+
if (nextBtn) {
|
232 |
+
nextBtn.disabled = this.currentSlide === this.totalSlides - 1;
|
233 |
+
|
234 |
+
// Update button text for last slide
|
235 |
+
if (this.currentSlide === this.totalSlides - 1) {
|
236 |
+
nextBtn.innerHTML = `
|
237 |
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
238 |
+
<path d="M5 12h14M12 5l7 7-7 7"/>
|
239 |
+
</svg>
|
240 |
+
`;
|
241 |
+
nextBtn.setAttribute('aria-label', 'Finish presentation');
|
242 |
+
} else {
|
243 |
+
nextBtn.innerHTML = `
|
244 |
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
245 |
+
<path d="M9 18l6-6-6-6"/>
|
246 |
+
</svg>
|
247 |
+
`;
|
248 |
+
nextBtn.setAttribute('aria-label', 'Next slide');
|
249 |
+
}
|
250 |
+
}
|
251 |
+
}
|
252 |
+
|
253 |
+
toggleFullscreen() {
|
254 |
+
if (!this.isFullscreen) {
|
255 |
+
this.enterFullscreen();
|
256 |
+
} else {
|
257 |
+
this.exitFullscreen();
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
enterFullscreen() {
|
262 |
+
const container = document.querySelector('.presentation-container');
|
263 |
+
|
264 |
+
if (container.requestFullscreen) {
|
265 |
+
container.requestFullscreen();
|
266 |
+
} else if (container.webkitRequestFullscreen) {
|
267 |
+
container.webkitRequestFullscreen();
|
268 |
+
} else if (container.mozRequestFullScreen) {
|
269 |
+
container.mozRequestFullScreen();
|
270 |
+
} else if (container.msRequestFullscreen) {
|
271 |
+
container.msRequestFullscreen();
|
272 |
+
}
|
273 |
+
}
|
274 |
+
|
275 |
+
exitFullscreen() {
|
276 |
+
if (document.exitFullscreen) {
|
277 |
+
document.exitFullscreen();
|
278 |
+
} else if (document.webkitExitFullscreen) {
|
279 |
+
document.webkitExitFullscreen();
|
280 |
+
} else if (document.mozCancelFullScreen) {
|
281 |
+
document.mozCancelFullScreen();
|
282 |
+
} else if (document.msExitFullscreen) {
|
283 |
+
document.msExitFullscreen();
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
handleFullscreenChange() {
|
288 |
+
const isFullscreen = !!(document.fullscreenElement ||
|
289 |
+
document.webkitFullscreenElement ||
|
290 |
+
document.mozFullScreenElement ||
|
291 |
+
document.msFullscreenElement);
|
292 |
+
|
293 |
+
this.isFullscreen = isFullscreen;
|
294 |
+
this.updateFullscreenIcon();
|
295 |
+
|
296 |
+
const container = document.querySelector('.presentation-container');
|
297 |
+
if (container) {
|
298 |
+
if (isFullscreen) {
|
299 |
+
container.classList.add('fullscreen');
|
300 |
+
} else {
|
301 |
+
container.classList.remove('fullscreen');
|
302 |
+
}
|
303 |
+
}
|
304 |
+
}
|
305 |
+
|
306 |
+
updateFullscreenIcon() {
|
307 |
+
const icon = document.getElementById('fullscreen-icon');
|
308 |
+
|
309 |
+
if (icon) {
|
310 |
+
if (this.isFullscreen) {
|
311 |
+
icon.innerHTML = `
|
312 |
+
<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
|
313 |
+
`;
|
314 |
+
} else {
|
315 |
+
icon.innerHTML = `
|
316 |
+
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
|
317 |
+
`;
|
318 |
+
}
|
319 |
+
}
|
320 |
+
}
|
321 |
+
|
322 |
+
// Method to handle special slide interactions
|
323 |
+
initSlideInteractions() {
|
324 |
+
// Add click handlers for call-to-action buttons
|
325 |
+
const ctaButtons = document.querySelectorAll('.call-to-action .btn');
|
326 |
+
ctaButtons.forEach(button => {
|
327 |
+
button.addEventListener('click', (e) => {
|
328 |
+
e.preventDefault();
|
329 |
+
this.handleCTAClick(button.textContent.trim());
|
330 |
+
});
|
331 |
+
});
|
332 |
+
|
333 |
+
// Add hover effects for interactive elements
|
334 |
+
this.addHoverEffects();
|
335 |
+
}
|
336 |
+
|
337 |
+
handleCTAClick(buttonText) {
|
338 |
+
if (buttonText.includes('Schedule')) {
|
339 |
+
this.showNotification('Meeting request sent! Check your calendar for confirmation.');
|
340 |
+
} else if (buttonText.includes('Download')) {
|
341 |
+
this.showNotification('Strategy document download initiated.');
|
342 |
+
}
|
343 |
+
}
|
344 |
+
|
345 |
+
showNotification(message) {
|
346 |
+
// Create notification element
|
347 |
+
const notification = document.createElement('div');
|
348 |
+
notification.className = 'notification';
|
349 |
+
notification.style.cssText = `
|
350 |
+
position: fixed;
|
351 |
+
top: 20px;
|
352 |
+
right: 20px;
|
353 |
+
background-color: var(--color-success);
|
354 |
+
color: var(--color-btn-primary-text);
|
355 |
+
padding: 16px 24px;
|
356 |
+
border-radius: 8px;
|
357 |
+
font-size: 14px;
|
358 |
+
font-weight: 500;
|
359 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
360 |
+
z-index: 1001;
|
361 |
+
opacity: 0;
|
362 |
+
transform: translateX(100%);
|
363 |
+
transition: all 0.3s ease;
|
364 |
+
`;
|
365 |
+
notification.textContent = message;
|
366 |
+
|
367 |
+
document.body.appendChild(notification);
|
368 |
+
|
369 |
+
// Animate in
|
370 |
+
setTimeout(() => {
|
371 |
+
notification.style.opacity = '1';
|
372 |
+
notification.style.transform = 'translateX(0)';
|
373 |
+
}, 100);
|
374 |
+
|
375 |
+
// Remove after 3 seconds
|
376 |
+
setTimeout(() => {
|
377 |
+
notification.style.opacity = '0';
|
378 |
+
notification.style.transform = 'translateX(100%)';
|
379 |
+
setTimeout(() => {
|
380 |
+
if (notification.parentNode) {
|
381 |
+
notification.parentNode.removeChild(notification);
|
382 |
+
}
|
383 |
+
}, 300);
|
384 |
+
}, 3000);
|
385 |
+
}
|
386 |
+
|
387 |
+
addHoverEffects() {
|
388 |
+
// Add hover effects to interactive elements
|
389 |
+
const interactiveElements = document.querySelectorAll('.value-prop, .tech-component, .framework-item, .advantage, .phase-card');
|
390 |
+
|
391 |
+
interactiveElements.forEach(element => {
|
392 |
+
element.addEventListener('mouseenter', () => {
|
393 |
+
element.style.transform = 'translateY(-5px)';
|
394 |
+
element.style.transition = 'transform 0.3s ease, box-shadow 0.3s ease';
|
395 |
+
element.style.boxShadow = 'var(--shadow-lg)';
|
396 |
+
});
|
397 |
+
|
398 |
+
element.addEventListener('mouseleave', () => {
|
399 |
+
element.style.transform = 'translateY(0)';
|
400 |
+
element.style.boxShadow = '';
|
401 |
+
});
|
402 |
+
});
|
403 |
+
}
|
404 |
+
|
405 |
+
// Method to handle slide-specific logic
|
406 |
+
handleSlideSpecifics(slideIndex) {
|
407 |
+
switch(slideIndex) {
|
408 |
+
case 0: // Title slide
|
409 |
+
setTimeout(() => this.animateTitleSlide(), 500);
|
410 |
+
break;
|
411 |
+
case 5: // Customer journey flow
|
412 |
+
setTimeout(() => this.animateFlowDiagram(), 500);
|
413 |
+
break;
|
414 |
+
case 11: // Business impact
|
415 |
+
setTimeout(() => this.animateROIMetrics(), 500);
|
416 |
+
break;
|
417 |
+
case 14: // Conclusion
|
418 |
+
setTimeout(() => this.initSlideInteractions(), 500);
|
419 |
+
break;
|
420 |
+
}
|
421 |
+
}
|
422 |
+
|
423 |
+
animateTitleSlide() {
|
424 |
+
const highlights = document.querySelectorAll('.highlight');
|
425 |
+
highlights.forEach((highlight, index) => {
|
426 |
+
highlight.style.opacity = '0';
|
427 |
+
highlight.style.transform = 'scale(0.8)';
|
428 |
+
highlight.style.transition = 'all 0.5s ease';
|
429 |
+
|
430 |
+
setTimeout(() => {
|
431 |
+
highlight.style.opacity = '1';
|
432 |
+
highlight.style.transform = 'scale(1)';
|
433 |
+
}, index * 200);
|
434 |
+
});
|
435 |
+
}
|
436 |
+
|
437 |
+
animateFlowDiagram() {
|
438 |
+
const steps = document.querySelectorAll('.flow-step');
|
439 |
+
steps.forEach((step, index) => {
|
440 |
+
step.style.opacity = '0';
|
441 |
+
step.style.transform = 'translateX(-50px)';
|
442 |
+
step.style.transition = 'all 0.5s ease';
|
443 |
+
|
444 |
+
setTimeout(() => {
|
445 |
+
step.style.opacity = '1';
|
446 |
+
step.style.transform = 'translateX(0)';
|
447 |
+
}, index * 300);
|
448 |
+
});
|
449 |
+
}
|
450 |
+
|
451 |
+
animateROIMetrics() {
|
452 |
+
const metrics = document.querySelectorAll('.roi-metric h3');
|
453 |
+
metrics.forEach((metric, index) => {
|
454 |
+
const finalValue = metric.textContent;
|
455 |
+
const isPercentage = finalValue.includes('%');
|
456 |
+
const numericValue = parseInt(finalValue.replace(/[^\d]/g, ''));
|
457 |
+
|
458 |
+
if (numericValue > 0) {
|
459 |
+
metric.textContent = isPercentage ? '0%' : '0';
|
460 |
+
|
461 |
+
setTimeout(() => {
|
462 |
+
this.animateNumber(metric, finalValue, numericValue, isPercentage);
|
463 |
+
}, index * 200);
|
464 |
+
}
|
465 |
+
});
|
466 |
+
}
|
467 |
+
|
468 |
+
animateNumber(element, finalValue, numericValue, isPercentage) {
|
469 |
+
let currentValue = 0;
|
470 |
+
const increment = Math.ceil(numericValue / 30);
|
471 |
+
|
472 |
+
const timer = setInterval(() => {
|
473 |
+
currentValue += increment;
|
474 |
+
if (currentValue >= numericValue) {
|
475 |
+
element.textContent = finalValue;
|
476 |
+
clearInterval(timer);
|
477 |
+
} else {
|
478 |
+
element.textContent = isPercentage ? `${currentValue}%` : `${currentValue}`;
|
479 |
+
}
|
480 |
+
}, 50);
|
481 |
+
}
|
482 |
+
}
|
483 |
+
|
484 |
+
// Initialize presentation when DOM is loaded
|
485 |
+
document.addEventListener('DOMContentLoaded', () => {
|
486 |
+
console.log('DOM loaded, initializing presentation...');
|
487 |
+
window.presentation = new Presentation();
|
488 |
+
});
|
489 |
+
|
490 |
+
// Fallback initialization for cases where DOMContentLoaded already fired
|
491 |
+
if (document.readyState !== 'loading') {
|
492 |
+
console.log('DOM already loaded, initializing presentation...');
|
493 |
+
window.presentation = new Presentation();
|
494 |
+
}
|
495 |
+
|
496 |
+
// Add global error handling
|
497 |
+
window.addEventListener('error', (e) => {
|
498 |
+
console.error('Presentation error:', e.error);
|
499 |
+
});
|
500 |
+
|
501 |
+
// Add performance optimization
|
502 |
+
window.addEventListener('load', () => {
|
503 |
+
console.log('Window loaded, optimizing...');
|
504 |
+
// Preload images
|
505 |
+
const images = document.querySelectorAll('img[src]');
|
506 |
+
images.forEach(img => {
|
507 |
+
const link = document.createElement('link');
|
508 |
+
link.rel = 'prefetch';
|
509 |
+
link.href = img.src;
|
510 |
+
document.head.appendChild(link);
|
511 |
+
});
|
512 |
+
});
|
513 |
+
|
514 |
+
// Add visibility change handling for presentations
|
515 |
+
document.addEventListener('visibilitychange', () => {
|
516 |
+
if (document.hidden) {
|
517 |
+
// Pause any ongoing animations when tab is not visible
|
518 |
+
document.querySelectorAll('.animate-in').forEach(el => {
|
519 |
+
if (el.style.animationPlayState !== undefined) {
|
520 |
+
el.style.animationPlayState = 'paused';
|
521 |
+
}
|
522 |
+
});
|
523 |
+
} else {
|
524 |
+
// Resume animations when tab becomes visible
|
525 |
+
document.querySelectorAll('.animate-in').forEach(el => {
|
526 |
+
if (el.style.animationPlayState !== undefined) {
|
527 |
+
el.style.animationPlayState = 'running';
|
528 |
+
}
|
529 |
+
});
|
530 |
+
}
|
531 |
+
});
|
532 |
+
|
533 |
+
// Export for potential external use
|
534 |
+
window.PresentationApp = Presentation;
|
Business Strategy Presentation/index.html
ADDED
@@ -0,0 +1,638 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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>FoodieSpot AI Strategy 2025</title>
|
7 |
+
<link rel="stylesheet" href="style.css">
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<div class="presentation-container">
|
11 |
+
<!-- Presentation Controls -->
|
12 |
+
<div class="presentation-controls">
|
13 |
+
<button id="prev-slide" class="btn btn--secondary" aria-label="Previous slide">
|
14 |
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
15 |
+
<path d="M15 18l-6-6 6-6"/>
|
16 |
+
</svg>
|
17 |
+
</button>
|
18 |
+
<div class="slide-counter">
|
19 |
+
<span id="current-slide">1</span> / <span id="total-slides">15</span>
|
20 |
+
</div>
|
21 |
+
<button id="next-slide" class="btn btn--primary" aria-label="Next slide">
|
22 |
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
23 |
+
<path d="M9 18l6-6-6-6"/>
|
24 |
+
</svg>
|
25 |
+
</button>
|
26 |
+
<button id="fullscreen-toggle" class="btn btn--secondary" aria-label="Toggle fullscreen">
|
27 |
+
<svg id="fullscreen-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
28 |
+
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
|
29 |
+
</svg>
|
30 |
+
</button>
|
31 |
+
</div>
|
32 |
+
|
33 |
+
<!-- Progress Bar -->
|
34 |
+
<div class="progress-bar">
|
35 |
+
<div class="progress-indicator"></div>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<!-- Slides Container -->
|
39 |
+
<div class="slides-container">
|
40 |
+
<!-- Slide 1 - Title Slide -->
|
41 |
+
<div class="slide slide-title" id="slide-1">
|
42 |
+
<div class="slide-content">
|
43 |
+
<h1 class="main-title">FoodieSpot AI Strategy 2025</h1>
|
44 |
+
<h2 class="subtitle">LLM-Based Conversational Agent Business Strategy</h2>
|
45 |
+
<div class="key-highlights">
|
46 |
+
<div class="highlight-container">
|
47 |
+
<div class="highlight">Transform Customer Experience</div>
|
48 |
+
<div class="highlight">30% Cost Reduction</div>
|
49 |
+
<div class="highlight">90% Automation</div>
|
50 |
+
</div>
|
51 |
+
</div>
|
52 |
+
<div class="company-info">
|
53 |
+
<p>FoodieSpot Restaurant Chain</p>
|
54 |
+
<p>Presented by: Sri Vallabh Tammireddy</p>
|
55 |
+
</div>
|
56 |
+
<img src="https://pplx-res.cloudinary.com/image/upload/v1748841437/pplx_project_search_images/a5fcd65452d252b1218791421c3e65897d481ee6.jpg" alt="Restaurant integrating AI for customer service and order management" class="background-image">
|
57 |
+
</div>
|
58 |
+
</div>
|
59 |
+
|
60 |
+
<!-- Slide 2 - Executive Summary -->
|
61 |
+
<div class="slide" id="slide-2">
|
62 |
+
<div class="slide-content">
|
63 |
+
<h2 class="slide-title">Executive Summary</h2>
|
64 |
+
<ul class="bullet-points">
|
65 |
+
<li>Industry-leading AI-powered restaurant customer experience by 2027</li>
|
66 |
+
<li>90% automation of queries, 30% cost reduction, 25% satisfaction boost</li>
|
67 |
+
<li>3-phase rollout across 20+ restaurant locations</li>
|
68 |
+
<li>ROI: 12% table turnover increase, 20% customer retention</li>
|
69 |
+
</ul>
|
70 |
+
<div class="slide-image-container">
|
71 |
+
<img src="https://pplx-res.cloudinary.com/image/upload/v1748619282/pplx_project_search_images/aa71ff0f31e06a994ec03f9f8492b61c1876f366.jpg" alt="Chatbots can assist restaurants in multiple ways" class="slide-image">
|
72 |
+
</div>
|
73 |
+
</div>
|
74 |
+
</div>
|
75 |
+
|
76 |
+
<!-- Slide 3 - Strategic Vision & Goals -->
|
77 |
+
<div class="slide" id="slide-3">
|
78 |
+
<div class="slide-content">
|
79 |
+
<h2 class="slide-title">Strategic Vision & Goals</h2>
|
80 |
+
<div class="two-column-layout">
|
81 |
+
<div class="column">
|
82 |
+
<h3 class="column-title">Long Term Goals</h3>
|
83 |
+
<ul class="bullet-points">
|
84 |
+
<li>Become restaurant industry leader in AI-powered customer experience by 2027</li>
|
85 |
+
<li>Establish new standard for restaurant automation and efficiency</li>
|
86 |
+
<li>Scale solution across 100+ locations nationwide</li>
|
87 |
+
<li>Develop proprietary conversation flow technology</li>
|
88 |
+
</ul>
|
89 |
+
</div>
|
90 |
+
<div class="column">
|
91 |
+
<h3 class="column-title">Key Metrics & Targets</h3>
|
92 |
+
<ul class="bullet-points">
|
93 |
+
<li>90% query automation within 12 months</li>
|
94 |
+
<li>25% improvement in customer satisfaction scores</li>
|
95 |
+
<li>30% reduction in operational costs</li>
|
96 |
+
<li>20% boost in return customer frequency</li>
|
97 |
+
</ul>
|
98 |
+
</div>
|
99 |
+
</div>
|
100 |
+
</div>
|
101 |
+
</div>
|
102 |
+
|
103 |
+
<!-- Slide 4 - Success Metrics -->
|
104 |
+
<div class="slide" id="slide-4">
|
105 |
+
<div class="slide-content">
|
106 |
+
<h2 class="slide-title">Success Metrics</h2>
|
107 |
+
<div class="metrics-container">
|
108 |
+
<div class="metric-category">
|
109 |
+
<h3 class="metric-title operational">Operational</h3>
|
110 |
+
<ul class="bullet-points">
|
111 |
+
<li>Handle 80%+ of customer queries without human intervention</li>
|
112 |
+
<li>Achieve <1 minute average response time for complex queries</li>
|
113 |
+
<li>Process 95%+ reservation requests successfully</li>
|
114 |
+
<li>Reduce no-show rates by 15%</li>
|
115 |
+
</ul>
|
116 |
+
</div>
|
117 |
+
<div class="metric-category">
|
118 |
+
<h3 class="metric-title business">Business Impact</h3>
|
119 |
+
<ul class="bullet-points">
|
120 |
+
<li>Increase table turnover rate by 12%</li>
|
121 |
+
<li>Boost customer retention by 20%</li>
|
122 |
+
<li>Generate 15% cost savings within 12 months</li>
|
123 |
+
<li>Achieve 4.5+ star customer satisfaction rating</li>
|
124 |
+
</ul>
|
125 |
+
</div>
|
126 |
+
<div class="metric-category">
|
127 |
+
<h3 class="metric-title technical">Technical Performance</h3>
|
128 |
+
<ul class="bullet-points">
|
129 |
+
<li>Maintain 99.5% uptime</li>
|
130 |
+
<li>Support 500+ concurrent users during peak</li>
|
131 |
+
<li>Seamless POS integration</li>
|
132 |
+
<li>Multi-language natural language processing</li>
|
133 |
+
</ul>
|
134 |
+
</div>
|
135 |
+
</div>
|
136 |
+
</div>
|
137 |
+
</div>
|
138 |
+
|
139 |
+
<!-- Slide 5 - The FoodieSpot Solution -->
|
140 |
+
<div class="slide" id="slide-5">
|
141 |
+
<div class="slide-content">
|
142 |
+
<h2 class="slide-title">The FoodieSpot Solution</h2>
|
143 |
+
<div class="solution-description">
|
144 |
+
<p>FoodieSpot's AI-powered conversational agent revolutionizes the dining experience by providing customers with instant, intelligent assistance for restaurant discovery, menu inquiries, and seamless reservation booking. The system combines advanced natural language processing with real-time database integration to handle everything from simple questions about restaurant hours to complex multi-party reservations across 20+ restaurant locations.</p>
|
145 |
+
</div>
|
146 |
+
<div class="value-props">
|
147 |
+
<h3>Key Value Propositions</h3>
|
148 |
+
<div class="value-prop-container">
|
149 |
+
<div class="value-prop">
|
150 |
+
<div class="value-icon">π¬</div>
|
151 |
+
<h4>Intelligent Conversations</h4>
|
152 |
+
<p>Natural language understanding with contextual awareness</p>
|
153 |
+
</div>
|
154 |
+
<div class="value-prop">
|
155 |
+
<div class="value-icon">β‘</div>
|
156 |
+
<h4>Instant Response</h4>
|
157 |
+
<p>24/7 availability with sub-second response times</p>
|
158 |
+
</div>
|
159 |
+
<div class="value-prop">
|
160 |
+
<div class="value-icon">π</div>
|
161 |
+
<h4>Data-Driven Insights</h4>
|
162 |
+
<p>Continuous learning from customer interactions</p>
|
163 |
+
</div>
|
164 |
+
</div>
|
165 |
+
</div>
|
166 |
+
</div>
|
167 |
+
</div>
|
168 |
+
|
169 |
+
<!-- Slide 6 - End-User Experience Flow -->
|
170 |
+
<div class="slide" id="slide-6">
|
171 |
+
<div class="slide-content">
|
172 |
+
<h2 class="slide-title">End-User Experience Flow</h2>
|
173 |
+
<div class="flow-diagram">
|
174 |
+
<div class="flow-step">
|
175 |
+
<div class="step-number">1</div>
|
176 |
+
<div class="step-content">
|
177 |
+
<h4>Discovery & Engagement</h4>
|
178 |
+
<p>Customer initiates conversation and system identifies dining intent</p>
|
179 |
+
</div>
|
180 |
+
</div>
|
181 |
+
<div class="flow-step">
|
182 |
+
<div class="step-number">2</div>
|
183 |
+
<div class="step-content">
|
184 |
+
<h4>Intelligent Recommendation</h4>
|
185 |
+
<p>AI analyzes preferences and provides personalized restaurant suggestions</p>
|
186 |
+
</div>
|
187 |
+
</div>
|
188 |
+
<div class="flow-step">
|
189 |
+
<div class="step-number">3</div>
|
190 |
+
<div class="step-content">
|
191 |
+
<h4>Seamless Reservation</h4>
|
192 |
+
<p>Collects booking information and confirms real-time availability</p>
|
193 |
+
</div>
|
194 |
+
</div>
|
195 |
+
<div class="flow-step">
|
196 |
+
<div class="step-number">4</div>
|
197 |
+
<div class="step-content">
|
198 |
+
<h4>Pre-Arrival Enhancement</h4>
|
199 |
+
<p>Automated reminders and menu pre-ordering options</p>
|
200 |
+
</div>
|
201 |
+
</div>
|
202 |
+
<div class="flow-step">
|
203 |
+
<div class="step-number">5</div>
|
204 |
+
<div class="step-content">
|
205 |
+
<h4>Post-Experience Follow-up</h4>
|
206 |
+
<p>Feedback collection and loyalty program enrollment</p>
|
207 |
+
</div>
|
208 |
+
</div>
|
209 |
+
</div>
|
210 |
+
</div>
|
211 |
+
</div>
|
212 |
+
|
213 |
+
<!-- Slide 7 - Conversation Flow Architecture -->
|
214 |
+
<div class="slide" id="slide-7">
|
215 |
+
<div class="slide-content">
|
216 |
+
<h2 class="slide-title">Conversation Flow Architecture</h2>
|
217 |
+
<div class="architecture-diagram">
|
218 |
+
<img src="https://pplx-res.cloudinary.com/image/upload/v1748552102/pplx_project_search_images/c4f7d6e3c3b2738159eacb794d10350d9760befc.jpg" alt="Diagram illustrating how chatbots function, including NLP, knowledge base, and data storage" class="architecture-image">
|
219 |
+
</div>
|
220 |
+
<div class="architecture-details">
|
221 |
+
<h3>Key Conversation States</h3>
|
222 |
+
<div class="states-container">
|
223 |
+
<div class="state">Initial Greeting</div>
|
224 |
+
<div class="state-arrow">β</div>
|
225 |
+
<div class="state">Intent Recognition</div>
|
226 |
+
<div class="state-arrow">β</div>
|
227 |
+
<div class="state">Answer user queries</div>
|
228 |
+
<div class="state-arrow">β</div>
|
229 |
+
<div class="state">Information Collection</div>
|
230 |
+
<div class="state-arrow">β</div>
|
231 |
+
<div class="state">Confirmation</div>
|
232 |
+
<div class="state-arrow">β</div>
|
233 |
+
<div class="state">Completion</div>
|
234 |
+
</div>
|
235 |
+
</div>
|
236 |
+
</div>
|
237 |
+
</div>
|
238 |
+
|
239 |
+
<!-- Slide 8 - Technical Capabilities -->
|
240 |
+
<div class="slide" id="slide-8">
|
241 |
+
<div class="slide-content">
|
242 |
+
<h2 class="slide-title">Technical Capabilities</h2>
|
243 |
+
<div class="capability-container">
|
244 |
+
<div class="capability-section">
|
245 |
+
<h3>Knowledge Bases</h3>
|
246 |
+
<ul class="bullet-points">
|
247 |
+
<li>Restaurant locations, hours & policies</li>
|
248 |
+
<li>Complete menu database with allergen info</li>
|
249 |
+
<li>Historical customer preferences</li>
|
250 |
+
</ul>
|
251 |
+
</div>
|
252 |
+
<div class="capability-section">
|
253 |
+
<h3>Integration Requirements</h3>
|
254 |
+
<ul class="bullet-points">
|
255 |
+
<li>Reservation management system</li>
|
256 |
+
<li>Customer relationship management (CRM)</li>
|
257 |
+
<li>Point of sale (POS) system</li>
|
258 |
+
</ul>
|
259 |
+
</div>
|
260 |
+
</div>
|
261 |
+
<div class="feature-matrix">
|
262 |
+
<h3>Feature Implementation Complexity</h3>
|
263 |
+
<div class="feature-difficulty">
|
264 |
+
<div class="difficulty-container">
|
265 |
+
<h4 class="difficulty-level green">Green (Easy)</h4>
|
266 |
+
<ul class="bullet-points">
|
267 |
+
<li>SMS/WhatsApp notification integration</li>
|
268 |
+
<li>Basic customer feedback collection</li>
|
269 |
+
<li>Email reminder system</li>
|
270 |
+
<li>Simple loyalty point tracking</li>
|
271 |
+
</ul>
|
272 |
+
</div>
|
273 |
+
<div class="difficulty-container">
|
274 |
+
<h4 class="difficulty-level yellow">Yellow (Medium)</h4>
|
275 |
+
<ul class="bullet-points">
|
276 |
+
<li>Voice interface integration</li>
|
277 |
+
<li>Advanced dietary preference matching</li>
|
278 |
+
<li>Dynamic pricing integration</li>
|
279 |
+
<li>Multi-language support expansion</li>
|
280 |
+
</ul>
|
281 |
+
</div>
|
282 |
+
<div class="difficulty-container">
|
283 |
+
<h4 class="difficulty-level red">Red (Complex)</h4>
|
284 |
+
<ul class="bullet-points">
|
285 |
+
<li>Predictive table optimization ML</li>
|
286 |
+
<li>External calendar integration</li>
|
287 |
+
<li>Advanced sentiment analysis</li>
|
288 |
+
<li>IoT integration for smart management</li>
|
289 |
+
</ul>
|
290 |
+
</div>
|
291 |
+
</div>
|
292 |
+
</div>
|
293 |
+
</div>
|
294 |
+
</div>
|
295 |
+
|
296 |
+
<!-- Slide 9 - Technology Architecture -->
|
297 |
+
<div class="slide" id="slide-9">
|
298 |
+
<div class="slide-content">
|
299 |
+
<h2 class="slide-title">Technology Architecture</h2>
|
300 |
+
<div class="tech-stack-container">
|
301 |
+
<div class="tech-component">
|
302 |
+
<div class="tech-icon">π§ </div>
|
303 |
+
<h3>Language Model</h3>
|
304 |
+
<p>LLaMA 3 8B via Groq API</p>
|
305 |
+
</div>
|
306 |
+
<div class="tech-component">
|
307 |
+
<div class="tech-icon">π</div>
|
308 |
+
<h3>Vector Database</h3>
|
309 |
+
<p>ChromaDB for semantic search</p>
|
310 |
+
</div>
|
311 |
+
<div class="tech-component">
|
312 |
+
<div class="tech-icon">πΎ</div>
|
313 |
+
<h3>SQL Database</h3>
|
314 |
+
<p>SQLite for structured data</p>
|
315 |
+
</div>
|
316 |
+
<div class="tech-component">
|
317 |
+
<div class="tech-icon">π</div>
|
318 |
+
<h3>Embedding Model</h3>
|
319 |
+
<p>Sentence Transformers</p>
|
320 |
+
</div>
|
321 |
+
<div class="tech-component">
|
322 |
+
<div class="tech-icon">π₯οΈ</div>
|
323 |
+
<h3>UI Framework</h3>
|
324 |
+
<p>Streamlit for web interface</p>
|
325 |
+
</div>
|
326 |
+
</div>
|
327 |
+
</div>
|
328 |
+
</div>
|
329 |
+
|
330 |
+
<!-- Slide 10 - Phase 1: Pilot Program -->
|
331 |
+
<div class="slide" id="slide-10">
|
332 |
+
<div class="slide-content">
|
333 |
+
<h2 class="slide-title">Phase 1: Pilot Program (Months 1-3)</h2>
|
334 |
+
<div class="phase-details">
|
335 |
+
<div class="phase-content">
|
336 |
+
<div class="phase-stat">
|
337 |
+
<h3>Scope</h3>
|
338 |
+
<p>3 flagship locations</p>
|
339 |
+
</div>
|
340 |
+
<div class="phase-stat">
|
341 |
+
<h3>Capacity</h3>
|
342 |
+
<p>100 daily interactions per location</p>
|
343 |
+
</div>
|
344 |
+
<div class="phase-stat">
|
345 |
+
<h3>Focus</h3>
|
346 |
+
<p>Testing and validation</p>
|
347 |
+
</div>
|
348 |
+
</div>
|
349 |
+
<div class="phase-framework">
|
350 |
+
<h3>Testing Framework</h3>
|
351 |
+
<div class="framework-container">
|
352 |
+
<div class="framework-item">
|
353 |
+
<h4>A/B Testing</h4>
|
354 |
+
<p>Comparing AI vs traditional customer service</p>
|
355 |
+
</div>
|
356 |
+
<div class="framework-item">
|
357 |
+
<h4>User Satisfaction</h4>
|
358 |
+
<p>Post-interaction surveys and feedback collection</p>
|
359 |
+
</div>
|
360 |
+
<div class="framework-item">
|
361 |
+
<h4>Performance Metrics</h4>
|
362 |
+
<p>Response time, accuracy, completion rate</p>
|
363 |
+
</div>
|
364 |
+
<div class="framework-item">
|
365 |
+
<h4>Staff Feedback</h4>
|
366 |
+
<p>Regular listening sessions with restaurant teams</p>
|
367 |
+
</div>
|
368 |
+
</div>
|
369 |
+
</div>
|
370 |
+
</div>
|
371 |
+
</div>
|
372 |
+
</div>
|
373 |
+
|
374 |
+
<!-- Slide 11 - Scale-Up Plan -->
|
375 |
+
<div class="slide" id="slide-11">
|
376 |
+
<div class="slide-content">
|
377 |
+
<h2 class="slide-title">Scale-Up Plan (Months 4-12)</h2>
|
378 |
+
<div class="phases-container">
|
379 |
+
<div class="phase-card">
|
380 |
+
<h3 class="phase-title phase-2">Phase 2: Regional Expansion</h3>
|
381 |
+
<div class="phase-timeline">Months 4-8</div>
|
382 |
+
<ul class="bullet-points">
|
383 |
+
<li><strong>Scope:</strong> 10 additional locations across 3-4 regions</li>
|
384 |
+
<li><strong>Capacity:</strong> 5,000+ daily interactions</li>
|
385 |
+
<li><strong>Focus:</strong> Multi-language support and advanced personalization</li>
|
386 |
+
</ul>
|
387 |
+
</div>
|
388 |
+
<div class="phase-card">
|
389 |
+
<h3 class="phase-title phase-3">Phase 3: National Deployment</h3>
|
390 |
+
<div class="phase-timeline">Months 9-12</div>
|
391 |
+
<ul class="bullet-points">
|
392 |
+
<li><strong>Scope:</strong> All 20+ FoodieSpot locations nationwide</li>
|
393 |
+
<li><strong>Capacity:</strong> 50,000+ daily interactions</li>
|
394 |
+
<li><strong>Focus:</strong> Predictive analytics and cross-location recommendations</li>
|
395 |
+
</ul>
|
396 |
+
</div>
|
397 |
+
</div>
|
398 |
+
<div class="feature-progression">
|
399 |
+
<h3>Feature Progression</h3>
|
400 |
+
<div class="progression-timeline">
|
401 |
+
<div class="timeline-marker">
|
402 |
+
<div class="marker"></div>
|
403 |
+
<p>Basic reservation</p>
|
404 |
+
</div>
|
405 |
+
<div class="timeline-marker">
|
406 |
+
<div class="marker"></div>
|
407 |
+
<p>Menu recommendations</p>
|
408 |
+
</div>
|
409 |
+
<div class="timeline-marker">
|
410 |
+
<div class="marker"></div>
|
411 |
+
<p>Multi-language</p>
|
412 |
+
</div>
|
413 |
+
<div class="timeline-marker">
|
414 |
+
<div class="marker"></div>
|
415 |
+
<p>Personalization</p>
|
416 |
+
</div>
|
417 |
+
<div class="timeline-marker">
|
418 |
+
<div class="marker"></div>
|
419 |
+
<p>Predictive analytics</p>
|
420 |
+
</div>
|
421 |
+
</div>
|
422 |
+
</div>
|
423 |
+
</div>
|
424 |
+
</div>
|
425 |
+
|
426 |
+
<!-- Slide 12 - Business Impact & ROI -->
|
427 |
+
<div class="slide" id="slide-12">
|
428 |
+
<div class="slide-content">
|
429 |
+
<h2 class="slide-title">Business Impact & ROI</h2>
|
430 |
+
<div class="roi-metrics">
|
431 |
+
<div class="roi-metric">
|
432 |
+
<h3>30%</h3>
|
433 |
+
<p>Reduction in customer service costs</p>
|
434 |
+
</div>
|
435 |
+
<div class="roi-metric">
|
436 |
+
<h3>12%</h3>
|
437 |
+
<p>Table turnover improvement</p>
|
438 |
+
</div>
|
439 |
+
<div class="roi-metric">
|
440 |
+
<h3>20%</h3>
|
441 |
+
<p>Customer retention increase</p>
|
442 |
+
</div>
|
443 |
+
</div>
|
444 |
+
<div class="market-metrics">
|
445 |
+
<h3>Market Opportunity</h3>
|
446 |
+
<div class="market-stats">
|
447 |
+
<div class="market-stat">
|
448 |
+
<p class="stat-value">$20.81B</p>
|
449 |
+
<p class="stat-label">Chatbot market by 2029</p>
|
450 |
+
</div>
|
451 |
+
<div class="market-stat">
|
452 |
+
<p class="stat-value">24.53%</p>
|
453 |
+
<p class="stat-label">Annual growth rate</p>
|
454 |
+
</div>
|
455 |
+
<div class="market-stat">
|
456 |
+
<p class="stat-value">$6.2B</p>
|
457 |
+
<p class="stat-label">Reservation system market by 2033</p>
|
458 |
+
</div>
|
459 |
+
</div>
|
460 |
+
</div>
|
461 |
+
<div class="sources">
|
462 |
+
<h3>Sources</h3>
|
463 |
+
<ul>
|
464 |
+
<li><strong>Chatbot Market Size & Growth:</strong> <a href="https://www.researchandmarkets.com/reports/ai-chatbot-market" target="_blank">ResearchAndMarkets</a> (Note: $20.81B is a conservative estimate; latest reports project $46.64B by 2029)</li>
|
465 |
+
<li><strong>Reservation System Market:</strong> <a href="https://www.businessresearchinsights.com/market-reports/online-restaurant-reservation-system-market-112156" target="_blank">Business Research Insights</a>, <a href="https://www.verifiedmarketreports.com/product/global-reservation-and-online-booking-software-market-size-and-forecast/" target="_blank">Verified Market Reports</a></li>
|
466 |
+
<li><strong>ROI Metrics:</strong> Industry case studies from McKinsey, Gartner, and IBM</li>
|
467 |
+
</ul>
|
468 |
+
<p><strong>Indian Conversion Note (at 1 USD = βΉ85.48):</strong><br>
|
469 |
+
$20.81B β βΉ1,78,000 crores<br>
|
470 |
+
$6.2B β βΉ53,000 crores</p>
|
471 |
+
</div>
|
472 |
+
</div>
|
473 |
+
</div>
|
474 |
+
|
475 |
+
|
476 |
+
<!-- Slide 13 - Implementation Challenges & Mitigation -->
|
477 |
+
<div class="slide" id="slide-13">
|
478 |
+
<div class="slide-content">
|
479 |
+
<h2 class="slide-title">Implementation Challenges & Mitigation</h2>
|
480 |
+
<div class="challenges-container">
|
481 |
+
<div class="challenge-category">
|
482 |
+
<h3 class="category-title technical">Technical Challenges</h3>
|
483 |
+
<div class="challenge-items">
|
484 |
+
<div class="challenge-item">
|
485 |
+
<h4>LLM reliability</h4>
|
486 |
+
<p>Prompt engineering & fallback mechanisms</p>
|
487 |
+
</div>
|
488 |
+
<div class="challenge-item">
|
489 |
+
<h4>Real-time data sync</h4>
|
490 |
+
<p>API integration protocols</p>
|
491 |
+
</div>
|
492 |
+
<div class="challenge-item">
|
493 |
+
<h4>Peak load performance</h4>
|
494 |
+
<p>Auto-scaling infrastructure</p>
|
495 |
+
</div>
|
496 |
+
</div>
|
497 |
+
</div>
|
498 |
+
<div class="challenge-category">
|
499 |
+
<h3 class="category-title business">Business Challenges</h3>
|
500 |
+
<div class="challenge-items">
|
501 |
+
<div class="challenge-item">
|
502 |
+
<h4>Customer adoption</h4>
|
503 |
+
<p>Gradual introduction with clear benefits</p>
|
504 |
+
</div>
|
505 |
+
<div class="challenge-item">
|
506 |
+
<h4>Integration complexity</h4>
|
507 |
+
<p>Phased integration approach</p>
|
508 |
+
</div>
|
509 |
+
<div class="challenge-item">
|
510 |
+
<h4>Data privacy</h4>
|
511 |
+
<p>Enterprise security protocols</p>
|
512 |
+
</div>
|
513 |
+
</div>
|
514 |
+
</div>
|
515 |
+
<div class="challenge-category">
|
516 |
+
<h3 class="category-title operational">Operational Challenges</h3>
|
517 |
+
<div class="challenge-items">
|
518 |
+
<div class="challenge-item">
|
519 |
+
<h4>Staff training</h4>
|
520 |
+
<p>Comprehensive training programs</p>
|
521 |
+
</div>
|
522 |
+
<div class="challenge-item">
|
523 |
+
<h4>Quality assurance</h4>
|
524 |
+
<p>Continuous monitoring systems</p>
|
525 |
+
</div>
|
526 |
+
<div class="challenge-item">
|
527 |
+
<h4>Competitive response</h4>
|
528 |
+
<p>Rapid innovation cycles</p>
|
529 |
+
</div>
|
530 |
+
</div>
|
531 |
+
</div>
|
532 |
+
</div>
|
533 |
+
</div>
|
534 |
+
</div>
|
535 |
+
|
536 |
+
<!-- Slide 14 - Market Position & Differentiation -->
|
537 |
+
<div class="slide" id="slide-14">
|
538 |
+
<div class="slide-content">
|
539 |
+
<h2 class="slide-title">Market Position & Differentiation</h2>
|
540 |
+
<div class="market-content">
|
541 |
+
<div class="market-section">
|
542 |
+
<h3>Market Trends</h3>
|
543 |
+
<ul class="bullet-points">
|
544 |
+
<li>Rapid growth in restaurant automation solutions</li>
|
545 |
+
<li>Rising customer expectations for personalization</li>
|
546 |
+
<li>Increasing adoption of voice and chat interfaces</li>
|
547 |
+
<li>Growing focus on data-driven customer experiences</li>
|
548 |
+
</ul>
|
549 |
+
</div>
|
550 |
+
<div class="market-section">
|
551 |
+
<h3>Competitive Landscape</h3>
|
552 |
+
<ul class="bullet-points">
|
553 |
+
<li>Most competitors offer single-channel solutions</li>
|
554 |
+
<li>Limited personalization capabilities</li>
|
555 |
+
<li>Few solutions integrate with existing POS systems</li>
|
556 |
+
<li>Minimal predictive analytics capabilities</li>
|
557 |
+
</ul>
|
558 |
+
</div>
|
559 |
+
<div class="advantages-section">
|
560 |
+
<h3>Our Sustainable Advantages</h3>
|
561 |
+
<div class="advantages-container">
|
562 |
+
<div class="advantage">
|
563 |
+
<h4>Omni-channel Integration</h4>
|
564 |
+
<p>Seamless experience across all touchpoints</p>
|
565 |
+
</div>
|
566 |
+
<div class="advantage">
|
567 |
+
<h4>Proprietary ML Models</h4>
|
568 |
+
<p>Custom-trained on restaurant-specific data</p>
|
569 |
+
</div>
|
570 |
+
<div class="advantage">
|
571 |
+
<h4>First-mover Advantage</h4>
|
572 |
+
<p>Rapid deployment ahead of competitors</p>
|
573 |
+
</div>
|
574 |
+
</div>
|
575 |
+
</div>
|
576 |
+
</div>
|
577 |
+
</div>
|
578 |
+
</div>
|
579 |
+
|
580 |
+
<div class="slide" id="slide-15">
|
581 |
+
<div class="slide-content">
|
582 |
+
<h2 class="slide-title">Key Elements of Our Brand</h2>
|
583 |
+
<div class="slide-image-container">
|
584 |
+
<img src="https://pplx-res.cloudinary.com/image/upload/v1748590630/pplx_project_search_images/1a55b83fd6249e2e068c025904b6f0a8ff840661.jpg" alt="Infographic detailing key elements of a company brand-building brainstorm" class="market-image">
|
585 |
+
</div>
|
586 |
+
</div>
|
587 |
+
</div>
|
588 |
+
|
589 |
+
|
590 |
+
<!-- Slide 15 - Conclusion & Next Steps -->
|
591 |
+
<div class="slide" id="slide-16">
|
592 |
+
<div class="slide-content">
|
593 |
+
<h2 class="slide-title">Conclusion & Recommended Actions</h2>
|
594 |
+
<div class="conclusion-content">
|
595 |
+
<div class="recommendations">
|
596 |
+
<h3>Strategic Recommendations</h3>
|
597 |
+
<ul class="bullet-points highlighted">
|
598 |
+
<li>Immediately approve Phase 1 pilot for Q3 2025 launch</li>
|
599 |
+
<li>Allocate $1.2M initial budget for technology development</li>
|
600 |
+
<li>Form cross-functional team from IT, Operations and Marketing</li>
|
601 |
+
<li>Begin customer research at 3 pilot locations next month</li>
|
602 |
+
<li>Approve vendor selection process for technology partners</li>
|
603 |
+
</ul>
|
604 |
+
</div>
|
605 |
+
<div class="timeline-section">
|
606 |
+
<h3>Implementation Timeline</h3>
|
607 |
+
<div class="timeline">
|
608 |
+
<div class="timeline-item">
|
609 |
+
<span class="timeline-date">July 2025</span>
|
610 |
+
<p>Project kickoff & vendor selection</p>
|
611 |
+
</div>
|
612 |
+
<div class="timeline-item">
|
613 |
+
<span class="timeline-date">August 2025</span>
|
614 |
+
<p>Pilot development begins</p>
|
615 |
+
</div>
|
616 |
+
<div class="timeline-item">
|
617 |
+
<span class="timeline-date">October 2025</span>
|
618 |
+
<p>Phase 1 launch at pilot locations</p>
|
619 |
+
</div>
|
620 |
+
<div class="timeline-item">
|
621 |
+
<span class="timeline-date">January 2026</span>
|
622 |
+
<p>Phase 2 regional expansion</p>
|
623 |
+
</div>
|
624 |
+
<div class="timeline-item">
|
625 |
+
<span class="timeline-date">June 2026</span>
|
626 |
+
<p>Phase 3 national deployment</p>
|
627 |
+
</div>
|
628 |
+
</div>
|
629 |
+
</div>
|
630 |
+
</div>
|
631 |
+
</div>
|
632 |
+
</div>
|
633 |
+
</div>
|
634 |
+
</div>
|
635 |
+
|
636 |
+
<script src="app.js"></script>
|
637 |
+
</body>
|
638 |
+
</html>
|
Business Strategy Presentation/readme.txt.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
Open index.html for presentation
|
Business Strategy Presentation/style.css
ADDED
@@ -0,0 +1,1575 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
:root {
|
3 |
+
/* Colors */
|
4 |
+
--color-background: rgba(252, 252, 249, 1);
|
5 |
+
--color-surface: rgba(255, 255, 253, 1);
|
6 |
+
--color-text: rgba(19, 52, 59, 1);
|
7 |
+
--color-text-secondary: rgba(98, 108, 113, 1);
|
8 |
+
--color-primary: rgba(33, 128, 141, 1);
|
9 |
+
--color-primary-hover: rgba(29, 116, 128, 1);
|
10 |
+
--color-primary-active: rgba(26, 104, 115, 1);
|
11 |
+
--color-secondary: rgba(94, 82, 64, 0.12);
|
12 |
+
--color-secondary-hover: rgba(94, 82, 64, 0.2);
|
13 |
+
--color-secondary-active: rgba(94, 82, 64, 0.25);
|
14 |
+
--color-border: rgba(94, 82, 64, 0.2);
|
15 |
+
--color-btn-primary-text: rgba(252, 252, 249, 1);
|
16 |
+
--color-card-border: rgba(94, 82, 64, 0.12);
|
17 |
+
--color-card-border-inner: rgba(94, 82, 64, 0.12);
|
18 |
+
--color-error: rgba(192, 21, 47, 1);
|
19 |
+
--color-success: rgba(33, 128, 141, 1);
|
20 |
+
--color-warning: rgba(168, 75, 47, 1);
|
21 |
+
--color-info: rgba(98, 108, 113, 1);
|
22 |
+
--color-focus-ring: rgba(33, 128, 141, 0.4);
|
23 |
+
--color-select-caret: rgba(19, 52, 59, 0.8);
|
24 |
+
|
25 |
+
/* Common style patterns */
|
26 |
+
--focus-ring: 0 0 0 3px var(--color-focus-ring);
|
27 |
+
--focus-outline: 2px solid var(--color-primary);
|
28 |
+
--status-bg-opacity: 0.15;
|
29 |
+
--status-border-opacity: 0.25;
|
30 |
+
--select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
31 |
+
--select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
32 |
+
|
33 |
+
/* RGB versions for opacity control */
|
34 |
+
--color-success-rgb: 33, 128, 141;
|
35 |
+
--color-error-rgb: 192, 21, 47;
|
36 |
+
--color-warning-rgb: 168, 75, 47;
|
37 |
+
--color-info-rgb: 98, 108, 113;
|
38 |
+
|
39 |
+
/* Typography */
|
40 |
+
--font-family-base: "FKGroteskNeue", "Geist", "Inter", -apple-system,
|
41 |
+
BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
42 |
+
--font-family-mono: "Berkeley Mono", ui-monospace, SFMono-Regular, Menlo,
|
43 |
+
Monaco, Consolas, monospace;
|
44 |
+
--font-size-xs: 11px;
|
45 |
+
--font-size-sm: 12px;
|
46 |
+
--font-size-base: 14px;
|
47 |
+
--font-size-md: 14px;
|
48 |
+
--font-size-lg: 16px;
|
49 |
+
--font-size-xl: 18px;
|
50 |
+
--font-size-2xl: 20px;
|
51 |
+
--font-size-3xl: 24px;
|
52 |
+
--font-size-4xl: 30px;
|
53 |
+
--font-weight-normal: 400;
|
54 |
+
--font-weight-medium: 500;
|
55 |
+
--font-weight-semibold: 550;
|
56 |
+
--font-weight-bold: 600;
|
57 |
+
--line-height-tight: 1.2;
|
58 |
+
--line-height-normal: 1.5;
|
59 |
+
--letter-spacing-tight: -0.01em;
|
60 |
+
|
61 |
+
/* Spacing */
|
62 |
+
--space-0: 0;
|
63 |
+
--space-1: 1px;
|
64 |
+
--space-2: 2px;
|
65 |
+
--space-4: 4px;
|
66 |
+
--space-6: 6px;
|
67 |
+
--space-8: 8px;
|
68 |
+
--space-10: 10px;
|
69 |
+
--space-12: 12px;
|
70 |
+
--space-16: 16px;
|
71 |
+
--space-20: 20px;
|
72 |
+
--space-24: 24px;
|
73 |
+
--space-32: 32px;
|
74 |
+
|
75 |
+
/* Border Radius */
|
76 |
+
--radius-sm: 6px;
|
77 |
+
--radius-base: 8px;
|
78 |
+
--radius-md: 10px;
|
79 |
+
--radius-lg: 12px;
|
80 |
+
--radius-full: 9999px;
|
81 |
+
|
82 |
+
/* Shadows */
|
83 |
+
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.02);
|
84 |
+
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
|
85 |
+
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.04),
|
86 |
+
0 2px 4px -1px rgba(0, 0, 0, 0.02);
|
87 |
+
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.04),
|
88 |
+
0 4px 6px -2px rgba(0, 0, 0, 0.02);
|
89 |
+
--shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.15),
|
90 |
+
inset 0 -1px 0 rgba(0, 0, 0, 0.03);
|
91 |
+
|
92 |
+
/* Animation */
|
93 |
+
--duration-fast: 150ms;
|
94 |
+
--duration-normal: 250ms;
|
95 |
+
--ease-standard: cubic-bezier(0.16, 1, 0.3, 1);
|
96 |
+
|
97 |
+
/* Layout */
|
98 |
+
--container-sm: 640px;
|
99 |
+
--container-md: 768px;
|
100 |
+
--container-lg: 1024px;
|
101 |
+
--container-xl: 1280px;
|
102 |
+
}
|
103 |
+
|
104 |
+
/* Dark mode colors */
|
105 |
+
@media (prefers-color-scheme: dark) {
|
106 |
+
:root {
|
107 |
+
--color-background: rgba(31, 33, 33, 1);
|
108 |
+
--color-surface: rgba(38, 40, 40, 1);
|
109 |
+
--color-text: rgba(245, 245, 245, 1);
|
110 |
+
--color-text-secondary: rgba(167, 169, 169, 0.7);
|
111 |
+
--color-primary: rgba(50, 184, 198, 1);
|
112 |
+
--color-primary-hover: rgba(45, 166, 178, 1);
|
113 |
+
--color-primary-active: rgba(41, 150, 161, 1);
|
114 |
+
--color-secondary: rgba(119, 124, 124, 0.15);
|
115 |
+
--color-secondary-hover: rgba(119, 124, 124, 0.25);
|
116 |
+
--color-secondary-active: rgba(119, 124, 124, 0.3);
|
117 |
+
--color-border: rgba(119, 124, 124, 0.3);
|
118 |
+
--color-error: rgba(255, 84, 89, 1);
|
119 |
+
--color-success: rgba(50, 184, 198, 1);
|
120 |
+
--color-warning: rgba(230, 129, 97, 1);
|
121 |
+
--color-info: rgba(167, 169, 169, 1);
|
122 |
+
--color-focus-ring: rgba(50, 184, 198, 0.4);
|
123 |
+
--color-btn-primary-text: rgba(19, 52, 59, 1);
|
124 |
+
--color-card-border: rgba(119, 124, 124, 0.2);
|
125 |
+
--color-card-border-inner: rgba(119, 124, 124, 0.15);
|
126 |
+
--shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1),
|
127 |
+
inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
128 |
+
--button-border-secondary: rgba(119, 124, 124, 0.2);
|
129 |
+
--color-border-secondary: rgba(119, 124, 124, 0.2);
|
130 |
+
--color-select-caret: rgba(245, 245, 245, 0.8);
|
131 |
+
|
132 |
+
/* Common style patterns - updated for dark mode */
|
133 |
+
--focus-ring: 0 0 0 3px var(--color-focus-ring);
|
134 |
+
--focus-outline: 2px solid var(--color-primary);
|
135 |
+
--status-bg-opacity: 0.15;
|
136 |
+
--status-border-opacity: 0.25;
|
137 |
+
--select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
138 |
+
--select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
139 |
+
|
140 |
+
/* RGB versions for dark mode */
|
141 |
+
--color-success-rgb: 50, 184, 198;
|
142 |
+
--color-error-rgb: 255, 84, 89;
|
143 |
+
--color-warning-rgb: 230, 129, 97;
|
144 |
+
--color-info-rgb: 167, 169, 169;
|
145 |
+
}
|
146 |
+
}
|
147 |
+
|
148 |
+
/* Data attribute for manual theme switching */
|
149 |
+
[data-color-scheme="dark"] {
|
150 |
+
--color-background: rgba(31, 33, 33, 1);
|
151 |
+
--color-surface: rgba(38, 40, 40, 1);
|
152 |
+
--color-text: rgba(245, 245, 245, 1);
|
153 |
+
--color-text-secondary: rgba(167, 169, 169, 0.7);
|
154 |
+
--color-primary: rgba(50, 184, 198, 1);
|
155 |
+
--color-primary-hover: rgba(45, 166, 178, 1);
|
156 |
+
--color-primary-active: rgba(41, 150, 161, 1);
|
157 |
+
--color-secondary: rgba(119, 124, 124, 0.15);
|
158 |
+
--color-secondary-hover: rgba(119, 124, 124, 0.25);
|
159 |
+
--color-secondary-active: rgba(119, 124, 124, 0.3);
|
160 |
+
--color-border: rgba(119, 124, 124, 0.3);
|
161 |
+
--color-error: rgba(255, 84, 89, 1);
|
162 |
+
--color-success: rgba(50, 184, 198, 1);
|
163 |
+
--color-warning: rgba(230, 129, 97, 1);
|
164 |
+
--color-info: rgba(167, 169, 169, 1);
|
165 |
+
--color-focus-ring: rgba(50, 184, 198, 0.4);
|
166 |
+
--color-btn-primary-text: rgba(19, 52, 59, 1);
|
167 |
+
--color-card-border: rgba(119, 124, 124, 0.15);
|
168 |
+
--color-card-border-inner: rgba(119, 124, 124, 0.15);
|
169 |
+
--shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1),
|
170 |
+
inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
171 |
+
--color-border-secondary: rgba(119, 124, 124, 0.2);
|
172 |
+
--color-select-caret: rgba(245, 245, 245, 0.8);
|
173 |
+
|
174 |
+
/* Common style patterns - updated for dark mode */
|
175 |
+
--focus-ring: 0 0 0 3px var(--color-focus-ring);
|
176 |
+
--focus-outline: 2px solid var(--color-primary);
|
177 |
+
--status-bg-opacity: 0.15;
|
178 |
+
--status-border-opacity: 0.25;
|
179 |
+
--select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
180 |
+
--select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
181 |
+
|
182 |
+
/* RGB versions for dark mode */
|
183 |
+
--color-success-rgb: 50, 184, 198;
|
184 |
+
--color-error-rgb: 255, 84, 89;
|
185 |
+
--color-warning-rgb: 230, 129, 97;
|
186 |
+
--color-info-rgb: 167, 169, 169;
|
187 |
+
}
|
188 |
+
|
189 |
+
[data-color-scheme="light"] {
|
190 |
+
--color-background: rgba(252, 252, 249, 1);
|
191 |
+
--color-surface: rgba(255, 255, 253, 1);
|
192 |
+
--color-text: rgba(19, 52, 59, 1);
|
193 |
+
--color-text-secondary: rgba(98, 108, 113, 1);
|
194 |
+
--color-primary: rgba(33, 128, 141, 1);
|
195 |
+
--color-primary-hover: rgba(29, 116, 128, 1);
|
196 |
+
--color-primary-active: rgba(26, 104, 115, 1);
|
197 |
+
--color-secondary: rgba(94, 82, 64, 0.12);
|
198 |
+
--color-secondary-hover: rgba(94, 82, 64, 0.2);
|
199 |
+
--color-secondary-active: rgba(94, 82, 64, 0.25);
|
200 |
+
--color-border: rgba(94, 82, 64, 0.2);
|
201 |
+
--color-btn-primary-text: rgba(252, 252, 249, 1);
|
202 |
+
--color-card-border: rgba(94, 82, 64, 0.12);
|
203 |
+
--color-card-border-inner: rgba(94, 82, 64, 0.12);
|
204 |
+
--color-error: rgba(192, 21, 47, 1);
|
205 |
+
--color-success: rgba(33, 128, 141, 1);
|
206 |
+
--color-warning: rgba(168, 75, 47, 1);
|
207 |
+
--color-info: rgba(98, 108, 113, 1);
|
208 |
+
--color-focus-ring: rgba(33, 128, 141, 0.4);
|
209 |
+
|
210 |
+
/* RGB versions for light mode */
|
211 |
+
--color-success-rgb: 33, 128, 141;
|
212 |
+
--color-error-rgb: 192, 21, 47;
|
213 |
+
--color-warning-rgb: 168, 75, 47;
|
214 |
+
--color-info-rgb: 98, 108, 113;
|
215 |
+
}
|
216 |
+
|
217 |
+
/* Base styles */
|
218 |
+
html {
|
219 |
+
font-size: var(--font-size-base);
|
220 |
+
font-family: var(--font-family-base);
|
221 |
+
line-height: var(--line-height-normal);
|
222 |
+
color: var(--color-text);
|
223 |
+
background-color: var(--color-background);
|
224 |
+
-webkit-font-smoothing: antialiased;
|
225 |
+
box-sizing: border-box;
|
226 |
+
}
|
227 |
+
|
228 |
+
body {
|
229 |
+
margin: 0;
|
230 |
+
padding: 0;
|
231 |
+
}
|
232 |
+
|
233 |
+
*,
|
234 |
+
*::before,
|
235 |
+
*::after {
|
236 |
+
box-sizing: inherit;
|
237 |
+
}
|
238 |
+
|
239 |
+
/* Typography */
|
240 |
+
h1,
|
241 |
+
h2,
|
242 |
+
h3,
|
243 |
+
h4,
|
244 |
+
h5,
|
245 |
+
h6 {
|
246 |
+
margin: 0;
|
247 |
+
font-weight: var(--font-weight-semibold);
|
248 |
+
line-height: var(--line-height-tight);
|
249 |
+
color: var(--color-text);
|
250 |
+
letter-spacing: var(--letter-spacing-tight);
|
251 |
+
}
|
252 |
+
|
253 |
+
h1 {
|
254 |
+
font-size: var(--font-size-4xl);
|
255 |
+
}
|
256 |
+
h2 {
|
257 |
+
font-size: var(--font-size-3xl);
|
258 |
+
}
|
259 |
+
h3 {
|
260 |
+
font-size: var(--font-size-2xl);
|
261 |
+
}
|
262 |
+
h4 {
|
263 |
+
font-size: var(--font-size-xl);
|
264 |
+
}
|
265 |
+
h5 {
|
266 |
+
font-size: var(--font-size-lg);
|
267 |
+
}
|
268 |
+
h6 {
|
269 |
+
font-size: var(--font-size-md);
|
270 |
+
}
|
271 |
+
|
272 |
+
p {
|
273 |
+
margin: 0 0 var(--space-16) 0;
|
274 |
+
}
|
275 |
+
|
276 |
+
a {
|
277 |
+
color: var(--color-primary);
|
278 |
+
text-decoration: none;
|
279 |
+
transition: color var(--duration-fast) var(--ease-standard);
|
280 |
+
}
|
281 |
+
|
282 |
+
a:hover {
|
283 |
+
color: var(--color-primary-hover);
|
284 |
+
}
|
285 |
+
|
286 |
+
code,
|
287 |
+
pre {
|
288 |
+
font-family: var(--font-family-mono);
|
289 |
+
font-size: calc(var(--font-size-base) * 0.95);
|
290 |
+
background-color: var(--color-secondary);
|
291 |
+
border-radius: var(--radius-sm);
|
292 |
+
}
|
293 |
+
|
294 |
+
code {
|
295 |
+
padding: var(--space-1) var(--space-4);
|
296 |
+
}
|
297 |
+
|
298 |
+
pre {
|
299 |
+
padding: var(--space-16);
|
300 |
+
margin: var(--space-16) 0;
|
301 |
+
overflow: auto;
|
302 |
+
border: 1px solid var(--color-border);
|
303 |
+
}
|
304 |
+
|
305 |
+
pre code {
|
306 |
+
background: none;
|
307 |
+
padding: 0;
|
308 |
+
}
|
309 |
+
|
310 |
+
/* Buttons */
|
311 |
+
.btn {
|
312 |
+
display: inline-flex;
|
313 |
+
align-items: center;
|
314 |
+
justify-content: center;
|
315 |
+
padding: var(--space-8) var(--space-16);
|
316 |
+
border-radius: var(--radius-base);
|
317 |
+
font-size: var(--font-size-base);
|
318 |
+
font-weight: 500;
|
319 |
+
line-height: 1.5;
|
320 |
+
cursor: pointer;
|
321 |
+
transition: all var(--duration-normal) var(--ease-standard);
|
322 |
+
border: none;
|
323 |
+
text-decoration: none;
|
324 |
+
position: relative;
|
325 |
+
}
|
326 |
+
|
327 |
+
.btn:focus-visible {
|
328 |
+
outline: none;
|
329 |
+
box-shadow: var(--focus-ring);
|
330 |
+
}
|
331 |
+
|
332 |
+
.btn--primary {
|
333 |
+
background: var(--color-primary);
|
334 |
+
color: var(--color-btn-primary-text);
|
335 |
+
}
|
336 |
+
|
337 |
+
.btn--primary:hover {
|
338 |
+
background: var(--color-primary-hover);
|
339 |
+
}
|
340 |
+
|
341 |
+
.btn--primary:active {
|
342 |
+
background: var(--color-primary-active);
|
343 |
+
}
|
344 |
+
|
345 |
+
.btn--secondary {
|
346 |
+
background: var(--color-secondary);
|
347 |
+
color: var(--color-text);
|
348 |
+
}
|
349 |
+
|
350 |
+
.btn--secondary:hover {
|
351 |
+
background: var(--color-secondary-hover);
|
352 |
+
}
|
353 |
+
|
354 |
+
.btn--secondary:active {
|
355 |
+
background: var(--color-secondary-active);
|
356 |
+
}
|
357 |
+
|
358 |
+
.btn--outline {
|
359 |
+
background: transparent;
|
360 |
+
border: 1px solid var(--color-border);
|
361 |
+
color: var(--color-text);
|
362 |
+
}
|
363 |
+
|
364 |
+
.btn--outline:hover {
|
365 |
+
background: var(--color-secondary);
|
366 |
+
}
|
367 |
+
|
368 |
+
.btn--sm {
|
369 |
+
padding: var(--space-4) var(--space-12);
|
370 |
+
font-size: var(--font-size-sm);
|
371 |
+
border-radius: var(--radius-sm);
|
372 |
+
}
|
373 |
+
|
374 |
+
.btn--lg {
|
375 |
+
padding: var(--space-10) var(--space-20);
|
376 |
+
font-size: var(--font-size-lg);
|
377 |
+
border-radius: var(--radius-md);
|
378 |
+
}
|
379 |
+
|
380 |
+
.btn--full-width {
|
381 |
+
width: 100%;
|
382 |
+
}
|
383 |
+
|
384 |
+
.btn:disabled {
|
385 |
+
opacity: 0.5;
|
386 |
+
cursor: not-allowed;
|
387 |
+
}
|
388 |
+
|
389 |
+
/* Form elements */
|
390 |
+
.form-control {
|
391 |
+
display: block;
|
392 |
+
width: 100%;
|
393 |
+
padding: var(--space-8) var(--space-12);
|
394 |
+
font-size: var(--font-size-md);
|
395 |
+
line-height: 1.5;
|
396 |
+
color: var(--color-text);
|
397 |
+
background-color: var(--color-surface);
|
398 |
+
border: 1px solid var(--color-border);
|
399 |
+
border-radius: var(--radius-base);
|
400 |
+
transition: border-color var(--duration-fast) var(--ease-standard),
|
401 |
+
box-shadow var(--duration-fast) var(--ease-standard);
|
402 |
+
}
|
403 |
+
|
404 |
+
textarea.form-control {
|
405 |
+
font-family: var(--font-family-base);
|
406 |
+
font-size: var(--font-size-base);
|
407 |
+
}
|
408 |
+
|
409 |
+
select.form-control {
|
410 |
+
padding: var(--space-8) var(--space-12);
|
411 |
+
-webkit-appearance: none;
|
412 |
+
-moz-appearance: none;
|
413 |
+
appearance: none;
|
414 |
+
background-image: var(--select-caret-light);
|
415 |
+
background-repeat: no-repeat;
|
416 |
+
background-position: right var(--space-12) center;
|
417 |
+
background-size: 16px;
|
418 |
+
padding-right: var(--space-32);
|
419 |
+
}
|
420 |
+
|
421 |
+
/* Add a dark mode specific caret */
|
422 |
+
@media (prefers-color-scheme: dark) {
|
423 |
+
select.form-control {
|
424 |
+
background-image: var(--select-caret-dark);
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
/* Also handle data-color-scheme */
|
429 |
+
[data-color-scheme="dark"] select.form-control {
|
430 |
+
background-image: var(--select-caret-dark);
|
431 |
+
}
|
432 |
+
|
433 |
+
[data-color-scheme="light"] select.form-control {
|
434 |
+
background-image: var(--select-caret-light);
|
435 |
+
}
|
436 |
+
|
437 |
+
.form-control:focus {
|
438 |
+
border-color: var(--color-primary);
|
439 |
+
outline: var(--focus-outline);
|
440 |
+
}
|
441 |
+
|
442 |
+
.form-label {
|
443 |
+
display: block;
|
444 |
+
margin-bottom: var(--space-8);
|
445 |
+
font-weight: var(--font-weight-medium);
|
446 |
+
font-size: var(--font-size-sm);
|
447 |
+
}
|
448 |
+
|
449 |
+
.form-group {
|
450 |
+
margin-bottom: var(--space-16);
|
451 |
+
}
|
452 |
+
|
453 |
+
/* Card component */
|
454 |
+
.card {
|
455 |
+
background-color: var(--color-surface);
|
456 |
+
border-radius: var(--radius-lg);
|
457 |
+
border: 1px solid var(--color-card-border);
|
458 |
+
box-shadow: var(--shadow-sm);
|
459 |
+
overflow: hidden;
|
460 |
+
transition: box-shadow var(--duration-normal) var(--ease-standard);
|
461 |
+
}
|
462 |
+
|
463 |
+
.card:hover {
|
464 |
+
box-shadow: var(--shadow-md);
|
465 |
+
}
|
466 |
+
|
467 |
+
.card__body {
|
468 |
+
padding: var(--space-16);
|
469 |
+
}
|
470 |
+
|
471 |
+
.card__header,
|
472 |
+
.card__footer {
|
473 |
+
padding: var(--space-16);
|
474 |
+
border-bottom: 1px solid var(--color-card-border-inner);
|
475 |
+
}
|
476 |
+
|
477 |
+
/* Status indicators - simplified with CSS variables */
|
478 |
+
.status {
|
479 |
+
display: inline-flex;
|
480 |
+
align-items: center;
|
481 |
+
padding: var(--space-6) var(--space-12);
|
482 |
+
border-radius: var(--radius-full);
|
483 |
+
font-weight: var(--font-weight-medium);
|
484 |
+
font-size: var(--font-size-sm);
|
485 |
+
}
|
486 |
+
|
487 |
+
.status--success {
|
488 |
+
background-color: rgba(
|
489 |
+
var(--color-success-rgb, 33, 128, 141),
|
490 |
+
var(--status-bg-opacity)
|
491 |
+
);
|
492 |
+
color: var(--color-success);
|
493 |
+
border: 1px solid
|
494 |
+
rgba(var(--color-success-rgb, 33, 128, 141), var(--status-border-opacity));
|
495 |
+
}
|
496 |
+
|
497 |
+
.status--error {
|
498 |
+
background-color: rgba(
|
499 |
+
var(--color-error-rgb, 192, 21, 47),
|
500 |
+
var(--status-bg-opacity)
|
501 |
+
);
|
502 |
+
color: var(--color-error);
|
503 |
+
border: 1px solid
|
504 |
+
rgba(var(--color-error-rgb, 192, 21, 47), var(--status-border-opacity));
|
505 |
+
}
|
506 |
+
|
507 |
+
.status--warning {
|
508 |
+
background-color: rgba(
|
509 |
+
var(--color-warning-rgb, 168, 75, 47),
|
510 |
+
var(--status-bg-opacity)
|
511 |
+
);
|
512 |
+
color: var(--color-warning);
|
513 |
+
border: 1px solid
|
514 |
+
rgba(var(--color-warning-rgb, 168, 75, 47), var(--status-border-opacity));
|
515 |
+
}
|
516 |
+
|
517 |
+
.status--info {
|
518 |
+
background-color: rgba(
|
519 |
+
var(--color-info-rgb, 98, 108, 113),
|
520 |
+
var(--status-bg-opacity)
|
521 |
+
);
|
522 |
+
color: var(--color-info);
|
523 |
+
border: 1px solid
|
524 |
+
rgba(var(--color-info-rgb, 98, 108, 113), var(--status-border-opacity));
|
525 |
+
}
|
526 |
+
|
527 |
+
/* Container layout */
|
528 |
+
.container {
|
529 |
+
width: 100%;
|
530 |
+
margin-right: auto;
|
531 |
+
margin-left: auto;
|
532 |
+
padding-right: var(--space-16);
|
533 |
+
padding-left: var(--space-16);
|
534 |
+
}
|
535 |
+
|
536 |
+
@media (min-width: 640px) {
|
537 |
+
.container {
|
538 |
+
max-width: var(--container-sm);
|
539 |
+
}
|
540 |
+
}
|
541 |
+
@media (min-width: 768px) {
|
542 |
+
.container {
|
543 |
+
max-width: var(--container-md);
|
544 |
+
}
|
545 |
+
}
|
546 |
+
@media (min-width: 1024px) {
|
547 |
+
.container {
|
548 |
+
max-width: var(--container-lg);
|
549 |
+
}
|
550 |
+
}
|
551 |
+
@media (min-width: 1280px) {
|
552 |
+
.container {
|
553 |
+
max-width: var(--container-xl);
|
554 |
+
}
|
555 |
+
}
|
556 |
+
|
557 |
+
/* Utility classes */
|
558 |
+
.flex {
|
559 |
+
display: flex;
|
560 |
+
}
|
561 |
+
.flex-col {
|
562 |
+
flex-direction: column;
|
563 |
+
}
|
564 |
+
.items-center {
|
565 |
+
align-items: center;
|
566 |
+
}
|
567 |
+
.justify-center {
|
568 |
+
justify-content: center;
|
569 |
+
}
|
570 |
+
.justify-between {
|
571 |
+
justify-content: space-between;
|
572 |
+
}
|
573 |
+
.gap-4 {
|
574 |
+
gap: var(--space-4);
|
575 |
+
}
|
576 |
+
.gap-8 {
|
577 |
+
gap: var(--space-8);
|
578 |
+
}
|
579 |
+
.gap-16 {
|
580 |
+
gap: var(--space-16);
|
581 |
+
}
|
582 |
+
|
583 |
+
.m-0 {
|
584 |
+
margin: 0;
|
585 |
+
}
|
586 |
+
.mt-8 {
|
587 |
+
margin-top: var(--space-8);
|
588 |
+
}
|
589 |
+
.mb-8 {
|
590 |
+
margin-bottom: var(--space-8);
|
591 |
+
}
|
592 |
+
.mx-8 {
|
593 |
+
margin-left: var(--space-8);
|
594 |
+
margin-right: var(--space-8);
|
595 |
+
}
|
596 |
+
.my-8 {
|
597 |
+
margin-top: var(--space-8);
|
598 |
+
margin-bottom: var(--space-8);
|
599 |
+
}
|
600 |
+
|
601 |
+
.p-0 {
|
602 |
+
padding: 0;
|
603 |
+
}
|
604 |
+
.py-8 {
|
605 |
+
padding-top: var(--space-8);
|
606 |
+
padding-bottom: var(--space-8);
|
607 |
+
}
|
608 |
+
.px-8 {
|
609 |
+
padding-left: var(--space-8);
|
610 |
+
padding-right: var(--space-8);
|
611 |
+
}
|
612 |
+
.py-16 {
|
613 |
+
padding-top: var(--space-16);
|
614 |
+
padding-bottom: var(--space-16);
|
615 |
+
}
|
616 |
+
.px-16 {
|
617 |
+
padding-left: var(--space-16);
|
618 |
+
padding-right: var(--space-16);
|
619 |
+
}
|
620 |
+
|
621 |
+
.block {
|
622 |
+
display: block;
|
623 |
+
}
|
624 |
+
.hidden {
|
625 |
+
display: none;
|
626 |
+
}
|
627 |
+
|
628 |
+
/* Accessibility */
|
629 |
+
.sr-only {
|
630 |
+
position: absolute;
|
631 |
+
width: 1px;
|
632 |
+
height: 1px;
|
633 |
+
padding: 0;
|
634 |
+
margin: -1px;
|
635 |
+
overflow: hidden;
|
636 |
+
clip: rect(0, 0, 0, 0);
|
637 |
+
white-space: nowrap;
|
638 |
+
border-width: 0;
|
639 |
+
}
|
640 |
+
|
641 |
+
:focus-visible {
|
642 |
+
outline: var(--focus-outline);
|
643 |
+
outline-offset: 2px;
|
644 |
+
}
|
645 |
+
|
646 |
+
/* Dark mode specifics */
|
647 |
+
[data-color-scheme="dark"] .btn--outline {
|
648 |
+
border: 1px solid var(--color-border-secondary);
|
649 |
+
}
|
650 |
+
|
651 |
+
@font-face {
|
652 |
+
font-family: 'FKGroteskNeue';
|
653 |
+
src: url('https://www.perplexity.ai/fonts/FKGroteskNeue.woff2')
|
654 |
+
format('woff2');
|
655 |
+
}
|
656 |
+
|
657 |
+
/* Custom styles for the presentation */
|
658 |
+
|
659 |
+
/* Presentation container */
|
660 |
+
.presentation-container {
|
661 |
+
position: relative;
|
662 |
+
width: 100vw;
|
663 |
+
height: 100vh;
|
664 |
+
overflow: hidden;
|
665 |
+
background-color: var(--color-background);
|
666 |
+
font-family: var(--font-family-base);
|
667 |
+
}
|
668 |
+
|
669 |
+
/* Controls */
|
670 |
+
.presentation-controls {
|
671 |
+
position: fixed;
|
672 |
+
bottom: 20px;
|
673 |
+
left: 50%;
|
674 |
+
transform: translateX(-50%);
|
675 |
+
display: flex;
|
676 |
+
align-items: center;
|
677 |
+
gap: var(--space-16);
|
678 |
+
background-color: var(--color-surface);
|
679 |
+
padding: var(--space-8) var(--space-16);
|
680 |
+
border-radius: var(--radius-full);
|
681 |
+
box-shadow: var(--shadow-md);
|
682 |
+
z-index: 100;
|
683 |
+
}
|
684 |
+
|
685 |
+
.slide-counter {
|
686 |
+
font-size: var(--font-size-md);
|
687 |
+
color: var(--color-text);
|
688 |
+
font-weight: var(--font-weight-medium);
|
689 |
+
}
|
690 |
+
|
691 |
+
/* Progress bar */
|
692 |
+
.progress-bar {
|
693 |
+
position: fixed;
|
694 |
+
top: 0;
|
695 |
+
left: 0;
|
696 |
+
width: 100%;
|
697 |
+
height: 4px;
|
698 |
+
background-color: var(--color-border);
|
699 |
+
z-index: 100;
|
700 |
+
}
|
701 |
+
|
702 |
+
.progress-indicator {
|
703 |
+
height: 100%;
|
704 |
+
background-color: var(--color-primary);
|
705 |
+
width: 6.67%; /* Start at slide 1 of 15 */
|
706 |
+
transition: width 0.3s ease;
|
707 |
+
}
|
708 |
+
|
709 |
+
/* Slides container */
|
710 |
+
.slides-container {
|
711 |
+
width: 100%;
|
712 |
+
height: 100%;
|
713 |
+
position: relative;
|
714 |
+
}
|
715 |
+
|
716 |
+
/* Individual slides */
|
717 |
+
.slide {
|
718 |
+
position: absolute;
|
719 |
+
top: 0;
|
720 |
+
left: 0;
|
721 |
+
width: 100%;
|
722 |
+
height: 100%;
|
723 |
+
display: none;
|
724 |
+
flex-direction: column;
|
725 |
+
justify-content: center;
|
726 |
+
align-items: center;
|
727 |
+
padding: var(--space-32);
|
728 |
+
opacity: 0;
|
729 |
+
transition: opacity 0.5s ease;
|
730 |
+
overflow-y: auto;
|
731 |
+
box-sizing: border-box;
|
732 |
+
}
|
733 |
+
|
734 |
+
.slide.active {
|
735 |
+
display: flex;
|
736 |
+
opacity: 1;
|
737 |
+
}
|
738 |
+
|
739 |
+
.slide-content {
|
740 |
+
max-width: 1200px;
|
741 |
+
width: 100%;
|
742 |
+
margin: 0 auto;
|
743 |
+
padding: var(--space-32);
|
744 |
+
background-color: var(--color-surface);
|
745 |
+
border-radius: var(--radius-lg);
|
746 |
+
box-shadow: var(--shadow-md);
|
747 |
+
position: relative;
|
748 |
+
min-height: 60vh;
|
749 |
+
box-sizing: border-box;
|
750 |
+
}
|
751 |
+
|
752 |
+
/* Title slide specific styles */
|
753 |
+
.slide-title {
|
754 |
+
background: linear-gradient(135deg, var(--color-primary), var(--color-success));
|
755 |
+
color: var(--color-btn-primary-text);
|
756 |
+
position: relative;
|
757 |
+
overflow: hidden;
|
758 |
+
}
|
759 |
+
|
760 |
+
.slide-title .slide-content {
|
761 |
+
background-color: transparent;
|
762 |
+
box-shadow: none;
|
763 |
+
color: var(--color-btn-primary-text);
|
764 |
+
text-align: center;
|
765 |
+
}
|
766 |
+
|
767 |
+
.slide h2.slide-title {
|
768 |
+
font-size: var(--font-size-3xl);
|
769 |
+
color: var(--color-text);
|
770 |
+
margin-bottom: var(--space-24);
|
771 |
+
text-align: center;
|
772 |
+
font-weight: var(--font-weight-semibold);
|
773 |
+
line-height: 1.2;
|
774 |
+
}
|
775 |
+
|
776 |
+
/* Main title slide */
|
777 |
+
.main-title {
|
778 |
+
font-size: 48px;
|
779 |
+
margin-bottom: var(--space-16);
|
780 |
+
color: var(--color-btn-primary-text);
|
781 |
+
text-align: center;
|
782 |
+
font-weight: var(--font-weight-bold);
|
783 |
+
}
|
784 |
+
|
785 |
+
.subtitle {
|
786 |
+
font-size: var(--font-size-xl);
|
787 |
+
color: rgba(255, 255, 255, 0.9);
|
788 |
+
margin-bottom: var(--space-24);
|
789 |
+
text-align: center;
|
790 |
+
font-weight: var(--font-weight-medium);
|
791 |
+
}
|
792 |
+
|
793 |
+
.key-highlights {
|
794 |
+
display: flex;
|
795 |
+
justify-content: center;
|
796 |
+
margin-bottom: var(--space-32);
|
797 |
+
width: 100%;
|
798 |
+
}
|
799 |
+
|
800 |
+
.highlight-container {
|
801 |
+
display: flex;
|
802 |
+
gap: var(--space-16);
|
803 |
+
justify-content: center;
|
804 |
+
width: 100%;
|
805 |
+
flex-wrap: wrap;
|
806 |
+
}
|
807 |
+
|
808 |
+
.highlight {
|
809 |
+
background-color: rgba(255, 255, 255, 0.2);
|
810 |
+
color: var(--color-btn-primary-text);
|
811 |
+
padding: var(--space-12) var(--space-20);
|
812 |
+
border-radius: var(--radius-base);
|
813 |
+
font-weight: var(--font-weight-semibold);
|
814 |
+
font-size: var(--font-size-lg);
|
815 |
+
text-align: center;
|
816 |
+
backdrop-filter: blur(10px);
|
817 |
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
818 |
+
}
|
819 |
+
|
820 |
+
.company-info {
|
821 |
+
margin-top: var(--space-32);
|
822 |
+
text-align: center;
|
823 |
+
font-size: var(--font-size-lg);
|
824 |
+
color: rgba(255, 255, 255, 0.8);
|
825 |
+
}
|
826 |
+
|
827 |
+
.background-image {
|
828 |
+
position: absolute;
|
829 |
+
top: 0;
|
830 |
+
left: 0;
|
831 |
+
width: 100%;
|
832 |
+
height: 100%;
|
833 |
+
object-fit: cover;
|
834 |
+
opacity: 0.15;
|
835 |
+
z-index: -1;
|
836 |
+
border-radius: var(--radius-lg);
|
837 |
+
}
|
838 |
+
|
839 |
+
/* Bullet points */
|
840 |
+
.bullet-points {
|
841 |
+
font-size: var(--font-size-lg);
|
842 |
+
line-height: 1.8;
|
843 |
+
padding-left: var(--space-24);
|
844 |
+
margin-bottom: var(--space-16);
|
845 |
+
list-style-type: disc;
|
846 |
+
}
|
847 |
+
|
848 |
+
.bullet-points li {
|
849 |
+
margin-bottom: var(--space-12);
|
850 |
+
color: var(--color-text);
|
851 |
+
}
|
852 |
+
|
853 |
+
.bullet-points.highlighted li {
|
854 |
+
background-color: rgba(var(--color-success-rgb), 0.1);
|
855 |
+
padding: var(--space-8) var(--space-16);
|
856 |
+
border-left: 4px solid var(--color-success);
|
857 |
+
list-style-type: none;
|
858 |
+
margin-left: calc(-1 * var(--space-24));
|
859 |
+
border-radius: var(--radius-sm);
|
860 |
+
}
|
861 |
+
|
862 |
+
/* Two-column layout */
|
863 |
+
.two-column-layout {
|
864 |
+
display: flex;
|
865 |
+
gap: var(--space-32);
|
866 |
+
margin-top: var(--space-24);
|
867 |
+
}
|
868 |
+
|
869 |
+
.column {
|
870 |
+
flex: 1;
|
871 |
+
}
|
872 |
+
|
873 |
+
.column-title {
|
874 |
+
font-size: var(--font-size-xl);
|
875 |
+
margin-bottom: var(--space-16);
|
876 |
+
color: var(--color-text);
|
877 |
+
font-weight: var(--font-weight-semibold);
|
878 |
+
}
|
879 |
+
|
880 |
+
/* Metrics container */
|
881 |
+
.metrics-container {
|
882 |
+
display: grid;
|
883 |
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
884 |
+
gap: var(--space-24);
|
885 |
+
margin-top: var(--space-24);
|
886 |
+
}
|
887 |
+
|
888 |
+
.metric-category {
|
889 |
+
background-color: var(--color-background);
|
890 |
+
padding: var(--space-20);
|
891 |
+
border-radius: var(--radius-base);
|
892 |
+
border: 1px solid var(--color-card-border);
|
893 |
+
box-shadow: var(--shadow-sm);
|
894 |
+
}
|
895 |
+
|
896 |
+
.metric-title {
|
897 |
+
font-size: var(--font-size-xl);
|
898 |
+
margin-bottom: var(--space-16);
|
899 |
+
padding-bottom: var(--space-8);
|
900 |
+
border-bottom: 3px solid var(--color-primary);
|
901 |
+
color: var(--color-text);
|
902 |
+
font-weight: var(--font-weight-semibold);
|
903 |
+
}
|
904 |
+
|
905 |
+
.metric-title.operational {
|
906 |
+
border-color: #1FB8CD;
|
907 |
+
}
|
908 |
+
|
909 |
+
.metric-title.business {
|
910 |
+
border-color: #FFC185;
|
911 |
+
}
|
912 |
+
|
913 |
+
.metric-title.technical {
|
914 |
+
border-color: #B4413C;
|
915 |
+
}
|
916 |
+
|
917 |
+
/* Solution description */
|
918 |
+
.solution-description p {
|
919 |
+
font-size: var(--font-size-lg);
|
920 |
+
line-height: 1.8;
|
921 |
+
margin-bottom: var(--space-24);
|
922 |
+
text-align: justify;
|
923 |
+
color: var(--color-text);
|
924 |
+
}
|
925 |
+
|
926 |
+
/* Value propositions */
|
927 |
+
.value-props h3 {
|
928 |
+
text-align: center;
|
929 |
+
margin-bottom: var(--space-20);
|
930 |
+
font-size: var(--font-size-xl);
|
931 |
+
font-weight: var(--font-weight-semibold);
|
932 |
+
}
|
933 |
+
|
934 |
+
.value-prop-container {
|
935 |
+
display: grid;
|
936 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
937 |
+
gap: var(--space-20);
|
938 |
+
}
|
939 |
+
|
940 |
+
.value-prop {
|
941 |
+
text-align: center;
|
942 |
+
padding: var(--space-20);
|
943 |
+
background-color: rgba(var(--color-success-rgb), 0.05);
|
944 |
+
border-radius: var(--radius-base);
|
945 |
+
border: 1px solid rgba(var(--color-success-rgb), 0.2);
|
946 |
+
}
|
947 |
+
|
948 |
+
.value-icon {
|
949 |
+
font-size: 36px;
|
950 |
+
margin-bottom: var(--space-12);
|
951 |
+
}
|
952 |
+
|
953 |
+
.value-prop h4 {
|
954 |
+
font-size: var(--font-size-lg);
|
955 |
+
margin-bottom: var(--space-8);
|
956 |
+
font-weight: var(--font-weight-semibold);
|
957 |
+
}
|
958 |
+
|
959 |
+
.value-prop p {
|
960 |
+
font-size: var(--font-size-md);
|
961 |
+
color: var(--color-text-secondary);
|
962 |
+
line-height: 1.5;
|
963 |
+
}
|
964 |
+
|
965 |
+
/* Flow diagram */
|
966 |
+
.flow-diagram {
|
967 |
+
display: flex;
|
968 |
+
flex-direction: column;
|
969 |
+
gap: var(--space-16);
|
970 |
+
margin-bottom: var(--space-24);
|
971 |
+
}
|
972 |
+
|
973 |
+
.flow-step {
|
974 |
+
display: flex;
|
975 |
+
align-items: center;
|
976 |
+
gap: var(--space-20);
|
977 |
+
padding: var(--space-16);
|
978 |
+
background-color: var(--color-background);
|
979 |
+
border-radius: var(--radius-base);
|
980 |
+
border-left: 4px solid var(--color-primary);
|
981 |
+
box-shadow: var(--shadow-sm);
|
982 |
+
}
|
983 |
+
|
984 |
+
.step-number {
|
985 |
+
width: 50px;
|
986 |
+
height: 50px;
|
987 |
+
border-radius: 50%;
|
988 |
+
background-color: var(--color-primary);
|
989 |
+
color: var(--color-btn-primary-text);
|
990 |
+
display: flex;
|
991 |
+
justify-content: center;
|
992 |
+
align-items: center;
|
993 |
+
font-weight: var(--font-weight-bold);
|
994 |
+
font-size: var(--font-size-xl);
|
995 |
+
flex-shrink: 0;
|
996 |
+
}
|
997 |
+
|
998 |
+
.step-content h4 {
|
999 |
+
margin-bottom: var(--space-8);
|
1000 |
+
font-size: var(--font-size-lg);
|
1001 |
+
font-weight: var(--font-weight-semibold);
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
.step-content p {
|
1005 |
+
color: var(--color-text-secondary);
|
1006 |
+
font-size: var(--font-size-md);
|
1007 |
+
line-height: 1.5;
|
1008 |
+
}
|
1009 |
+
|
1010 |
+
/* Images */
|
1011 |
+
.slide-image-container {
|
1012 |
+
margin-top: var(--space-24);
|
1013 |
+
text-align: center;
|
1014 |
+
}
|
1015 |
+
|
1016 |
+
.slide-image, .flow-image, .architecture-image, .tech-diagram, .market-image {
|
1017 |
+
max-width: 100%;
|
1018 |
+
height: auto;
|
1019 |
+
border-radius: var(--radius-base);
|
1020 |
+
max-height: 300px;
|
1021 |
+
object-fit: cover;
|
1022 |
+
box-shadow: var(--shadow-sm);
|
1023 |
+
}
|
1024 |
+
|
1025 |
+
/* Architecture diagram */
|
1026 |
+
.architecture-diagram {
|
1027 |
+
text-align: center;
|
1028 |
+
margin-bottom: var(--space-24);
|
1029 |
+
}
|
1030 |
+
|
1031 |
+
.architecture-details h3 {
|
1032 |
+
text-align: center;
|
1033 |
+
margin-bottom: var(--space-16);
|
1034 |
+
font-size: var(--font-size-xl);
|
1035 |
+
font-weight: var(--font-weight-semibold);
|
1036 |
+
}
|
1037 |
+
|
1038 |
+
.states-container {
|
1039 |
+
display: flex;
|
1040 |
+
justify-content: center;
|
1041 |
+
align-items: center;
|
1042 |
+
flex-wrap: wrap;
|
1043 |
+
gap: var(--space-8);
|
1044 |
+
margin-top: var(--space-16);
|
1045 |
+
}
|
1046 |
+
|
1047 |
+
.state {
|
1048 |
+
padding: var(--space-8) var(--space-16);
|
1049 |
+
background-color: var(--color-primary);
|
1050 |
+
color: var(--color-btn-primary-text);
|
1051 |
+
border-radius: var(--radius-base);
|
1052 |
+
font-size: var(--font-size-md);
|
1053 |
+
font-weight: var(--font-weight-medium);
|
1054 |
+
}
|
1055 |
+
|
1056 |
+
.state-arrow {
|
1057 |
+
font-size: var(--font-size-xl);
|
1058 |
+
color: var(--color-text-secondary);
|
1059 |
+
margin: 0 var(--space-4);
|
1060 |
+
}
|
1061 |
+
|
1062 |
+
/* Feature matrix */
|
1063 |
+
.capability-container {
|
1064 |
+
display: grid;
|
1065 |
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
1066 |
+
gap: var(--space-24);
|
1067 |
+
margin-bottom: var(--space-24);
|
1068 |
+
}
|
1069 |
+
|
1070 |
+
.capability-section h3 {
|
1071 |
+
font-size: var(--font-size-xl);
|
1072 |
+
margin-bottom: var(--space-16);
|
1073 |
+
color: var(--color-text);
|
1074 |
+
border-bottom: 2px solid var(--color-border);
|
1075 |
+
padding-bottom: var(--space-8);
|
1076 |
+
font-weight: var(--font-weight-semibold);
|
1077 |
+
}
|
1078 |
+
|
1079 |
+
.feature-matrix h3 {
|
1080 |
+
font-size: var(--font-size-xl);
|
1081 |
+
margin-bottom: var(--space-16);
|
1082 |
+
text-align: center;
|
1083 |
+
font-weight: var(--font-weight-semibold);
|
1084 |
+
}
|
1085 |
+
|
1086 |
+
.feature-difficulty {
|
1087 |
+
display: grid;
|
1088 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
1089 |
+
gap: var(--space-16);
|
1090 |
+
}
|
1091 |
+
|
1092 |
+
.difficulty-container {
|
1093 |
+
padding: var(--space-16);
|
1094 |
+
border-radius: var(--radius-base);
|
1095 |
+
border: 1px solid var(--color-border);
|
1096 |
+
background-color: var(--color-surface);
|
1097 |
+
}
|
1098 |
+
|
1099 |
+
.difficulty-level {
|
1100 |
+
margin-bottom: var(--space-16);
|
1101 |
+
font-size: var(--font-size-lg);
|
1102 |
+
text-align: center;
|
1103 |
+
padding: var(--space-8) var(--space-12);
|
1104 |
+
border-radius: var(--radius-base);
|
1105 |
+
font-weight: var(--font-weight-semibold);
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
.difficulty-level.green {
|
1109 |
+
background-color: rgba(33, 128, 141, 0.1);
|
1110 |
+
color: #1FB8CD;
|
1111 |
+
border: 1px solid rgba(33, 128, 141, 0.3);
|
1112 |
+
}
|
1113 |
+
|
1114 |
+
.difficulty-level.yellow {
|
1115 |
+
background-color: rgba(210, 186, 76, 0.1);
|
1116 |
+
color: #D2BA4C;
|
1117 |
+
border: 1px solid rgba(210, 186, 76, 0.3);
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
.difficulty-level.red {
|
1121 |
+
background-color: rgba(180, 65, 60, 0.1);
|
1122 |
+
color: #B4413C;
|
1123 |
+
border: 1px solid rgba(180, 65, 60, 0.3);
|
1124 |
+
}
|
1125 |
+
|
1126 |
+
/* Tech stack */
|
1127 |
+
.tech-stack-container {
|
1128 |
+
display: grid;
|
1129 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
1130 |
+
gap: var(--space-20);
|
1131 |
+
margin-bottom: var(--space-24);
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
.tech-component {
|
1135 |
+
text-align: center;
|
1136 |
+
padding: var(--space-20);
|
1137 |
+
border: 1px solid var(--color-border);
|
1138 |
+
border-radius: var(--radius-base);
|
1139 |
+
background-color: var(--color-background);
|
1140 |
+
box-shadow: var(--shadow-sm);
|
1141 |
+
}
|
1142 |
+
|
1143 |
+
.tech-icon {
|
1144 |
+
font-size: 36px;
|
1145 |
+
margin-bottom: var(--space-12);
|
1146 |
+
}
|
1147 |
+
|
1148 |
+
.tech-component h3 {
|
1149 |
+
font-size: var(--font-size-lg);
|
1150 |
+
margin-bottom: var(--space-8);
|
1151 |
+
font-weight: var(--font-weight-semibold);
|
1152 |
+
}
|
1153 |
+
|
1154 |
+
.tech-component p {
|
1155 |
+
font-size: var(--font-size-md);
|
1156 |
+
color: var(--color-text-secondary);
|
1157 |
+
line-height: 1.4;
|
1158 |
+
}
|
1159 |
+
|
1160 |
+
/* Phase details */
|
1161 |
+
.phase-details {
|
1162 |
+
display: flex;
|
1163 |
+
flex-direction: column;
|
1164 |
+
gap: var(--space-24);
|
1165 |
+
}
|
1166 |
+
|
1167 |
+
.phase-content {
|
1168 |
+
display: grid;
|
1169 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
1170 |
+
gap: var(--space-16);
|
1171 |
+
margin-bottom: var(--space-16);
|
1172 |
+
}
|
1173 |
+
|
1174 |
+
.phase-stat {
|
1175 |
+
text-align: center;
|
1176 |
+
padding: var(--space-16);
|
1177 |
+
background-color: var(--color-background);
|
1178 |
+
border-radius: var(--radius-base);
|
1179 |
+
border: 1px solid var(--color-border);
|
1180 |
+
}
|
1181 |
+
|
1182 |
+
.phase-stat h3 {
|
1183 |
+
font-size: var(--font-size-xl);
|
1184 |
+
margin-bottom: var(--space-8);
|
1185 |
+
color: var(--color-primary);
|
1186 |
+
font-weight: var(--font-weight-semibold);
|
1187 |
+
}
|
1188 |
+
|
1189 |
+
.phase-stat p {
|
1190 |
+
font-size: var(--font-size-lg);
|
1191 |
+
color: var(--color-text);
|
1192 |
+
}
|
1193 |
+
|
1194 |
+
/* Continue with remaining styles... */
|
1195 |
+
.phase-framework h3 {
|
1196 |
+
text-align: center;
|
1197 |
+
margin-bottom: var(--space-16);
|
1198 |
+
font-size: var(--font-size-xl);
|
1199 |
+
font-weight: var(--font-weight-semibold);
|
1200 |
+
}
|
1201 |
+
|
1202 |
+
.framework-container {
|
1203 |
+
display: grid;
|
1204 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
1205 |
+
gap: var(--space-16);
|
1206 |
+
}
|
1207 |
+
|
1208 |
+
.framework-item {
|
1209 |
+
padding: var(--space-16);
|
1210 |
+
border-radius: var(--radius-base);
|
1211 |
+
background-color: var(--color-secondary);
|
1212 |
+
border-left: 4px solid var(--color-primary);
|
1213 |
+
}
|
1214 |
+
|
1215 |
+
.framework-item h4 {
|
1216 |
+
margin-bottom: var(--space-8);
|
1217 |
+
font-size: var(--font-size-lg);
|
1218 |
+
font-weight: var(--font-weight-semibold);
|
1219 |
+
}
|
1220 |
+
|
1221 |
+
.framework-item p {
|
1222 |
+
font-size: var(--font-size-md);
|
1223 |
+
color: var(--color-text-secondary);
|
1224 |
+
line-height: 1.5;
|
1225 |
+
}
|
1226 |
+
|
1227 |
+
/* Phases container */
|
1228 |
+
.phases-container {
|
1229 |
+
display: grid;
|
1230 |
+
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
1231 |
+
gap: var(--space-24);
|
1232 |
+
margin-bottom: var(--space-24);
|
1233 |
+
}
|
1234 |
+
|
1235 |
+
.phase-card {
|
1236 |
+
padding: var(--space-20);
|
1237 |
+
border-radius: var(--radius-base);
|
1238 |
+
background-color: var(--color-background);
|
1239 |
+
border: 1px solid var(--color-card-border);
|
1240 |
+
box-shadow: var(--shadow-sm);
|
1241 |
+
}
|
1242 |
+
|
1243 |
+
.phase-title {
|
1244 |
+
font-size: var(--font-size-xl);
|
1245 |
+
margin-bottom: var(--space-8);
|
1246 |
+
color: var(--color-text);
|
1247 |
+
font-weight: var(--font-weight-semibold);
|
1248 |
+
}
|
1249 |
+
|
1250 |
+
.phase-title.phase-2 {
|
1251 |
+
color: #1FB8CD;
|
1252 |
+
}
|
1253 |
+
|
1254 |
+
.phase-title.phase-3 {
|
1255 |
+
color: #FFC185;
|
1256 |
+
}
|
1257 |
+
|
1258 |
+
.phase-timeline {
|
1259 |
+
font-size: var(--font-size-md);
|
1260 |
+
color: var(--color-text-secondary);
|
1261 |
+
margin-bottom: var(--space-16);
|
1262 |
+
font-weight: var(--font-weight-medium);
|
1263 |
+
}
|
1264 |
+
|
1265 |
+
/* ROI metrics */
|
1266 |
+
.roi-metrics {
|
1267 |
+
display: grid;
|
1268 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
1269 |
+
gap: var(--space-16);
|
1270 |
+
margin-bottom: var(--space-32);
|
1271 |
+
}
|
1272 |
+
|
1273 |
+
.roi-metric {
|
1274 |
+
text-align: center;
|
1275 |
+
padding: var(--space-20);
|
1276 |
+
background-color: var(--color-background);
|
1277 |
+
border-radius: var(--radius-base);
|
1278 |
+
border: 1px solid var(--color-border);
|
1279 |
+
}
|
1280 |
+
|
1281 |
+
.roi-metric h3 {
|
1282 |
+
font-size: 48px;
|
1283 |
+
color: var(--color-primary);
|
1284 |
+
margin-bottom: var(--space-8);
|
1285 |
+
font-weight: var(--font-weight-bold);
|
1286 |
+
}
|
1287 |
+
|
1288 |
+
.roi-metric p {
|
1289 |
+
font-size: var(--font-size-lg);
|
1290 |
+
color: var(--color-text);
|
1291 |
+
line-height: 1.4;
|
1292 |
+
}
|
1293 |
+
|
1294 |
+
.market-metrics h3 {
|
1295 |
+
text-align: center;
|
1296 |
+
margin-bottom: var(--space-16);
|
1297 |
+
font-size: var(--font-size-xl);
|
1298 |
+
font-weight: var(--font-weight-semibold);
|
1299 |
+
}
|
1300 |
+
|
1301 |
+
.market-stats {
|
1302 |
+
display: grid;
|
1303 |
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
1304 |
+
gap: var(--space-16);
|
1305 |
+
}
|
1306 |
+
|
1307 |
+
.market-stat {
|
1308 |
+
text-align: center;
|
1309 |
+
padding: var(--space-16);
|
1310 |
+
background-color: var(--color-background);
|
1311 |
+
border-radius: var(--radius-base);
|
1312 |
+
border: 1px solid var(--color-border);
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
.stat-value {
|
1316 |
+
font-size: var(--font-size-2xl);
|
1317 |
+
font-weight: var(--font-weight-bold);
|
1318 |
+
color: var(--color-text);
|
1319 |
+
margin-bottom: var(--space-4);
|
1320 |
+
}
|
1321 |
+
|
1322 |
+
.stat-label {
|
1323 |
+
font-size: var(--font-size-md);
|
1324 |
+
color: var(--color-text-secondary);
|
1325 |
+
line-height: 1.4;
|
1326 |
+
}
|
1327 |
+
|
1328 |
+
/* Challenges */
|
1329 |
+
.challenges-container {
|
1330 |
+
display: flex;
|
1331 |
+
flex-direction: column;
|
1332 |
+
gap: var(--space-24);
|
1333 |
+
}
|
1334 |
+
|
1335 |
+
.challenge-category {
|
1336 |
+
padding: var(--space-20);
|
1337 |
+
border-radius: var(--radius-base);
|
1338 |
+
background-color: var(--color-background);
|
1339 |
+
border: 1px solid var(--color-card-border);
|
1340 |
+
}
|
1341 |
+
|
1342 |
+
.category-title {
|
1343 |
+
font-size: var(--font-size-xl);
|
1344 |
+
margin-bottom: var(--space-16);
|
1345 |
+
padding-bottom: var(--space-8);
|
1346 |
+
border-bottom: 3px solid;
|
1347 |
+
font-weight: var(--font-weight-semibold);
|
1348 |
+
}
|
1349 |
+
|
1350 |
+
.category-title.technical {
|
1351 |
+
border-color: #1FB8CD;
|
1352 |
+
color: #1FB8CD;
|
1353 |
+
}
|
1354 |
+
|
1355 |
+
.category-title.business {
|
1356 |
+
border-color: #FFC185;
|
1357 |
+
color: #FFC185;
|
1358 |
+
}
|
1359 |
+
|
1360 |
+
.category-title.operational {
|
1361 |
+
border-color: #B4413C;
|
1362 |
+
color: #B4413C;
|
1363 |
+
}
|
1364 |
+
|
1365 |
+
.challenge-items {
|
1366 |
+
display: grid;
|
1367 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
1368 |
+
gap: var(--space-16);
|
1369 |
+
}
|
1370 |
+
|
1371 |
+
.challenge-item {
|
1372 |
+
padding: var(--space-16);
|
1373 |
+
background-color: var(--color-secondary);
|
1374 |
+
border-radius: var(--radius-base);
|
1375 |
+
border: 1px solid var(--color-border);
|
1376 |
+
}
|
1377 |
+
|
1378 |
+
.challenge-item h4 {
|
1379 |
+
margin-bottom: var(--space-8);
|
1380 |
+
font-size: var(--font-size-md);
|
1381 |
+
font-weight: var(--font-weight-semibold);
|
1382 |
+
}
|
1383 |
+
|
1384 |
+
.challenge-item p {
|
1385 |
+
font-size: var(--font-size-sm);
|
1386 |
+
color: var(--color-text-secondary);
|
1387 |
+
line-height: 1.4;
|
1388 |
+
}
|
1389 |
+
|
1390 |
+
/* Market position */
|
1391 |
+
.market-content {
|
1392 |
+
display: grid;
|
1393 |
+
grid-template-columns: repeat(2, 1fr);
|
1394 |
+
gap: var(--space-24);
|
1395 |
+
margin-bottom: var(--space-24);
|
1396 |
+
}
|
1397 |
+
|
1398 |
+
.advantages-section {
|
1399 |
+
grid-column: span 2;
|
1400 |
+
}
|
1401 |
+
|
1402 |
+
.market-section h3, .advantages-section h3 {
|
1403 |
+
font-size: var(--font-size-xl);
|
1404 |
+
margin-bottom: var(--space-16);
|
1405 |
+
padding-bottom: var(--space-8);
|
1406 |
+
border-bottom: 2px solid var(--color-border);
|
1407 |
+
font-weight: var(--font-weight-semibold);
|
1408 |
+
}
|
1409 |
+
|
1410 |
+
.advantages-container {
|
1411 |
+
display: grid;
|
1412 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
1413 |
+
gap: var(--space-16);
|
1414 |
+
}
|
1415 |
+
|
1416 |
+
.advantage {
|
1417 |
+
padding: var(--space-16);
|
1418 |
+
background-color: rgba(31, 184, 205, 0.05);
|
1419 |
+
border-radius: var(--radius-base);
|
1420 |
+
border-left: 4px solid #1FB8CD;
|
1421 |
+
border: 1px solid rgba(31, 184, 205, 0.2);
|
1422 |
+
}
|
1423 |
+
|
1424 |
+
.advantage h4 {
|
1425 |
+
margin-bottom: var(--space-8);
|
1426 |
+
font-size: var(--font-size-lg);
|
1427 |
+
font-weight: var(--font-weight-semibold);
|
1428 |
+
}
|
1429 |
+
|
1430 |
+
.advantage p {
|
1431 |
+
font-size: var(--font-size-md);
|
1432 |
+
color: var(--color-text-secondary);
|
1433 |
+
line-height: 1.4;
|
1434 |
+
}
|
1435 |
+
|
1436 |
+
/* Conclusion */
|
1437 |
+
.conclusion-content {
|
1438 |
+
display: flex;
|
1439 |
+
flex-direction: column;
|
1440 |
+
gap: var(--space-24);
|
1441 |
+
}
|
1442 |
+
|
1443 |
+
.recommendations h3, .timeline-section h3 {
|
1444 |
+
margin-bottom: var(--space-16);
|
1445 |
+
font-size: var(--font-size-xl);
|
1446 |
+
font-weight: var(--font-weight-semibold);
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
.timeline {
|
1450 |
+
display: flex;
|
1451 |
+
flex-direction: column;
|
1452 |
+
gap: var(--space-12);
|
1453 |
+
}
|
1454 |
+
|
1455 |
+
.timeline-item {
|
1456 |
+
display: flex;
|
1457 |
+
gap: var(--space-16);
|
1458 |
+
padding: var(--space-12) 0;
|
1459 |
+
border-left: 3px solid var(--color-primary);
|
1460 |
+
padding-left: var(--space-16);
|
1461 |
+
}
|
1462 |
+
|
1463 |
+
.timeline-date {
|
1464 |
+
font-weight: var(--font-weight-bold);
|
1465 |
+
min-width: 120px;
|
1466 |
+
color: var(--color-primary);
|
1467 |
+
}
|
1468 |
+
|
1469 |
+
.call-to-action {
|
1470 |
+
display: flex;
|
1471 |
+
flex-direction: column;
|
1472 |
+
align-items: center;
|
1473 |
+
gap: var(--space-16);
|
1474 |
+
margin-top: var(--space-24);
|
1475 |
+
padding: var(--space-24);
|
1476 |
+
background-color: var(--color-background);
|
1477 |
+
border-radius: var(--radius-base);
|
1478 |
+
border: 1px solid var(--color-border);
|
1479 |
+
}
|
1480 |
+
|
1481 |
+
.call-to-action h3 {
|
1482 |
+
margin-bottom: var(--space-8);
|
1483 |
+
font-size: var(--font-size-xl);
|
1484 |
+
text-align: center;
|
1485 |
+
font-weight: var(--font-weight-semibold);
|
1486 |
+
}
|
1487 |
+
|
1488 |
+
.call-to-action .btn {
|
1489 |
+
min-width: 300px;
|
1490 |
+
margin-bottom: var(--space-8);
|
1491 |
+
}
|
1492 |
+
|
1493 |
+
/* Media queries for responsiveness */
|
1494 |
+
@media (max-width: 768px) {
|
1495 |
+
.slide {
|
1496 |
+
padding: var(--space-16);
|
1497 |
+
}
|
1498 |
+
|
1499 |
+
.slide-content {
|
1500 |
+
padding: var(--space-20);
|
1501 |
+
min-height: auto;
|
1502 |
+
}
|
1503 |
+
|
1504 |
+
.main-title {
|
1505 |
+
font-size: 36px;
|
1506 |
+
}
|
1507 |
+
|
1508 |
+
.bullet-points {
|
1509 |
+
font-size: var(--font-size-md);
|
1510 |
+
padding-left: var(--space-16);
|
1511 |
+
}
|
1512 |
+
|
1513 |
+
.two-column-layout, .market-content {
|
1514 |
+
grid-template-columns: 1fr;
|
1515 |
+
}
|
1516 |
+
|
1517 |
+
.advantages-section {
|
1518 |
+
grid-column: span 1;
|
1519 |
+
}
|
1520 |
+
|
1521 |
+
.highlight-container {
|
1522 |
+
flex-direction: column;
|
1523 |
+
align-items: center;
|
1524 |
+
}
|
1525 |
+
|
1526 |
+
.presentation-controls {
|
1527 |
+
bottom: 10px;
|
1528 |
+
gap: var(--space-8);
|
1529 |
+
padding: var(--space-6) var(--space-12);
|
1530 |
+
}
|
1531 |
+
|
1532 |
+
.states-container {
|
1533 |
+
flex-direction: column;
|
1534 |
+
align-items: center;
|
1535 |
+
}
|
1536 |
+
|
1537 |
+
.state-arrow {
|
1538 |
+
transform: rotate(90deg);
|
1539 |
+
}
|
1540 |
+
}
|
1541 |
+
|
1542 |
+
/* Fullscreen mode */
|
1543 |
+
.fullscreen {
|
1544 |
+
position: fixed;
|
1545 |
+
top: 0;
|
1546 |
+
left: 0;
|
1547 |
+
width: 100vw;
|
1548 |
+
height: 100vh;
|
1549 |
+
background-color: var(--color-background);
|
1550 |
+
z-index: 1000;
|
1551 |
+
overflow: hidden;
|
1552 |
+
}
|
1553 |
+
|
1554 |
+
/* Animation classes */
|
1555 |
+
.animate-in {
|
1556 |
+
animation: slideInFromRight 0.5s ease forwards;
|
1557 |
+
}
|
1558 |
+
|
1559 |
+
@keyframes slideInFromRight {
|
1560 |
+
from {
|
1561 |
+
opacity: 0;
|
1562 |
+
transform: translateX(30px);
|
1563 |
+
}
|
1564 |
+
to {
|
1565 |
+
opacity: 1;
|
1566 |
+
transform: translateX(0);
|
1567 |
+
}
|
1568 |
+
}
|
1569 |
+
|
1570 |
+
/* Button disabled state */
|
1571 |
+
.btn:disabled {
|
1572 |
+
opacity: 0.5;
|
1573 |
+
cursor: not-allowed;
|
1574 |
+
pointer-events: none;
|
1575 |
+
}
|
__pycache__/agent.cpython-312.pyc
ADDED
Binary file (3.58 kB). View file
|
|
__pycache__/sticky.cpython-312.pyc
ADDED
Binary file (1.36 kB). View file
|
|
__pycache__/sticky.cpython-313.pyc
ADDED
Binary file (1.36 kB). View file
|
|
__pycache__/tools.cpython-312.pyc
ADDED
Binary file (7.4 kB). View file
|
|
__pycache__/tools.cpython-313.pyc
ADDED
Binary file (15.2 kB). View file
|
|
__pycache__/ui_utils.cpython-312.pyc
ADDED
Binary file (6 kB). View file
|
|
__pycache__/var.cpython-313.pyc
ADDED
Binary file (7.81 kB). View file
|
|
app.py
ADDED
@@ -0,0 +1,500 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from openai import OpenAI
|
3 |
+
import sqlite3
|
4 |
+
import pandas as pd
|
5 |
+
import re
|
6 |
+
import json
|
7 |
+
from sticky import sticky_container
|
8 |
+
import chromadb
|
9 |
+
from sentence_transformers import SentenceTransformer
|
10 |
+
from transformers import pipeline
|
11 |
+
import hashlib
|
12 |
+
import inspect
|
13 |
+
from tools import *
|
14 |
+
from var import SCHEMA_DESCRIPTIONS, SchemaVectorDB, FullVectorDB
|
15 |
+
import os
|
16 |
+
from dotenv import load_dotenv
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
# Set your Groq API key
|
20 |
+
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
|
21 |
+
|
22 |
+
# Initialize Groq's OpenAI-compatible client
|
23 |
+
client = OpenAI(
|
24 |
+
api_key=GROQ_API_KEY,
|
25 |
+
base_url="https://api.groq.com/openai/v1"
|
26 |
+
)
|
27 |
+
|
28 |
+
# --- Load prompt templates from prompts folder ---
|
29 |
+
with open("prompts/determine_intent.txt", "r", encoding="utf-8") as f:
|
30 |
+
determine_intent_prompt = f.read()
|
31 |
+
|
32 |
+
with open("prompts/generate_reservation_conversation.txt", "r", encoding="utf-8") as f:
|
33 |
+
generate_reservation_conversation_prompt = f.read()
|
34 |
+
|
35 |
+
with open("prompts/interpret_sql_result.txt", "r", encoding="utf-8") as f:
|
36 |
+
interpret_sql_result_prompt = f.read()
|
37 |
+
|
38 |
+
with open("prompts/schema_prompt.txt", "r", encoding="utf-8") as f:
|
39 |
+
schema_prompt = f.read()
|
40 |
+
|
41 |
+
with open("prompts/store_user_info.txt", "r", encoding="utf-8") as f:
|
42 |
+
store_user_info_prompt = f.read()
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
st.set_page_config(page_title="FoodieSpot Assistant", layout="wide")
|
47 |
+
|
48 |
+
|
49 |
+
# --- Initialize State ---
|
50 |
+
if 'chat_history' not in st.session_state:
|
51 |
+
st.session_state.chat_history = []
|
52 |
+
|
53 |
+
if 'user_data' not in st.session_state:
|
54 |
+
st.session_state.user_data = {
|
55 |
+
"restaurant_name": None,
|
56 |
+
"user_name": None,
|
57 |
+
"contact": None,
|
58 |
+
"party_size": None,
|
59 |
+
"time": None
|
60 |
+
}
|
61 |
+
if 'vector_db' not in st.session_state:
|
62 |
+
st.session_state.vector_db = SchemaVectorDB()
|
63 |
+
vector_db = st.session_state.vector_db
|
64 |
+
if 'full_vector_db' not in st.session_state:
|
65 |
+
st.session_state.full_vector_db = FullVectorDB()
|
66 |
+
# Track last assistant reply for context
|
67 |
+
if 'last_assistant_reply' not in st.session_state:
|
68 |
+
st.session_state.last_assistant_reply = ""
|
69 |
+
# Fixed container at top for title + reservation
|
70 |
+
reservation_box = sticky_container(mode="top", border=False,z=999)
|
71 |
+
|
72 |
+
with reservation_box:
|
73 |
+
st.text("")
|
74 |
+
st.text("")
|
75 |
+
st.title("π½οΈ FoodieSpot Assistant")
|
76 |
+
cols = st.columns([3, 3, 3, 2, 2, 1])
|
77 |
+
|
78 |
+
with cols[0]:
|
79 |
+
restaurant_name = st.text_input(
|
80 |
+
"Restaurant Name",
|
81 |
+
value=st.session_state.user_data.get("restaurant_name") or "",
|
82 |
+
key="restaurant_name_input"
|
83 |
+
)
|
84 |
+
if restaurant_name!="":
|
85 |
+
st.session_state.user_data["restaurant_name"] = restaurant_name
|
86 |
+
|
87 |
+
with cols[1]:
|
88 |
+
user_name = st.text_input(
|
89 |
+
"Your Name",
|
90 |
+
value=st.session_state.user_data.get("user_name") or "",
|
91 |
+
key="user_name_input"
|
92 |
+
)
|
93 |
+
if user_name!="":
|
94 |
+
st.session_state.user_data["user_name"] = user_name
|
95 |
+
|
96 |
+
with cols[2]:
|
97 |
+
contact = st.text_input(
|
98 |
+
"Contact",
|
99 |
+
value=st.session_state.user_data.get("contact") or "",
|
100 |
+
key="contact_input"
|
101 |
+
)
|
102 |
+
if contact!="":
|
103 |
+
st.session_state.user_data["contact"] = contact
|
104 |
+
|
105 |
+
with cols[3]:
|
106 |
+
party_size = st.number_input(
|
107 |
+
"Party Size",
|
108 |
+
value=st.session_state.user_data.get("party_size") or 0,
|
109 |
+
key="party_size_input"
|
110 |
+
)
|
111 |
+
if party_size!=0:
|
112 |
+
st.session_state.user_data["party_size"] = party_size
|
113 |
+
|
114 |
+
with cols[4]:
|
115 |
+
time = st.number_input(
|
116 |
+
"Time(24hr form, 9-20, 8 ~ null)",
|
117 |
+
min_value=8,
|
118 |
+
max_value=20,
|
119 |
+
value=st.session_state.user_data.get("time") or 8,
|
120 |
+
key="time_input"
|
121 |
+
)
|
122 |
+
if time!=8:
|
123 |
+
st.session_state.user_data["time"] = time
|
124 |
+
# Place the BOOK button in the last column
|
125 |
+
with cols[5]:
|
126 |
+
st.text("")
|
127 |
+
st.text("")
|
128 |
+
book_clicked = st.button("BOOK", type="primary")
|
129 |
+
# Add a green BOOK button (primary style)
|
130 |
+
# book_clicked = st.button("BOOK", type="primary")
|
131 |
+
|
132 |
+
if book_clicked:
|
133 |
+
# Check if all required fields are filled
|
134 |
+
required_keys = ["restaurant_name", "user_name", "contact", "party_size", "time"]
|
135 |
+
if all(st.session_state.user_data.get(k) not in [None, "", 0, 8] for k in required_keys):
|
136 |
+
booking_conn = None
|
137 |
+
try:
|
138 |
+
user_data = st.session_state.user_data
|
139 |
+
party_size = int(user_data["party_size"])
|
140 |
+
tables_needed = -(-party_size // 4)
|
141 |
+
|
142 |
+
booking_conn = sqlite3.connect("db/restaurant_reservation.db")
|
143 |
+
booking_cursor = booking_conn.cursor()
|
144 |
+
|
145 |
+
booking_cursor.execute("SELECT id FROM restaurants WHERE LOWER(name) = LOWER(?)", (user_data["restaurant_name"],))
|
146 |
+
restaurant_row = booking_cursor.fetchone()
|
147 |
+
if not restaurant_row:
|
148 |
+
raise Exception("Restaurant not found.")
|
149 |
+
restaurant_id = restaurant_row[0]
|
150 |
+
|
151 |
+
booking_cursor.execute("""
|
152 |
+
SELECT t.id AS table_id, s.id AS slot_id
|
153 |
+
FROM tables t
|
154 |
+
JOIN slots s ON t.id = s.table_id
|
155 |
+
WHERE t.restaurant_id = ?
|
156 |
+
AND s.hour = ?
|
157 |
+
AND s.date = '2025-05-12'
|
158 |
+
AND s.is_reserved = 0
|
159 |
+
LIMIT ?
|
160 |
+
""", (restaurant_id, user_data["time"], tables_needed))
|
161 |
+
available = booking_cursor.fetchall()
|
162 |
+
|
163 |
+
if len(available) < tables_needed:
|
164 |
+
raise Exception("Not enough available tables.")
|
165 |
+
|
166 |
+
booking_cursor.execute("""
|
167 |
+
INSERT INTO reservations (restaurant_id, user_name, contact, date, time, party_size)
|
168 |
+
VALUES (?, ?, ?, '2025-05-12', ?, ?)
|
169 |
+
""", (restaurant_id, user_data["user_name"], user_data["contact"], user_data["time"], party_size))
|
170 |
+
reservation_id = booking_cursor.lastrowid
|
171 |
+
|
172 |
+
for table_id, _ in available:
|
173 |
+
booking_cursor.execute("INSERT INTO reservation_tables (reservation_id, table_id) VALUES (?, ?)", (reservation_id, table_id))
|
174 |
+
|
175 |
+
slot_ids = [slot_id for _, slot_id in available]
|
176 |
+
booking_cursor.executemany("UPDATE slots SET is_reserved = 1 WHERE id = ?", [(sid,) for sid in slot_ids])
|
177 |
+
|
178 |
+
booking_conn.commit()
|
179 |
+
|
180 |
+
booking_cursor.execute("SELECT name FROM restaurants WHERE id = ?", (restaurant_id,))
|
181 |
+
restaurant_name = booking_cursor.fetchone()[0]
|
182 |
+
|
183 |
+
confirmation_msg = (
|
184 |
+
f"β
Booking processed successfully!\n\n"
|
185 |
+
f"π Restaurant: **{restaurant_name}**\n"
|
186 |
+
f"β° Time: **{user_data['time']} on 2025-05-12**\n"
|
187 |
+
f"π½οΈ Tables Booked: **{tables_needed}**\n"
|
188 |
+
f"π Reservation ID: **{reservation_id}**\n\n"
|
189 |
+
f"π Please mention this Reservation ID at the restaurant reception when you arrive."
|
190 |
+
)
|
191 |
+
|
192 |
+
st.success(confirmation_msg)
|
193 |
+
st.session_state.chat_history.append({'role': 'assistant', 'message': confirmation_msg})
|
194 |
+
st.session_state.user_data["restaurant_name"] = None
|
195 |
+
st.session_state.user_data["party_size"] = None
|
196 |
+
st.session_state.user_data["time"] = None
|
197 |
+
st.session_state.last_assistant_reply = ""
|
198 |
+
except Exception as e:
|
199 |
+
if booking_conn:
|
200 |
+
booking_conn.rollback()
|
201 |
+
st.error(f"β Booking failed: {e}")
|
202 |
+
finally:
|
203 |
+
if booking_conn:
|
204 |
+
booking_cursor = None
|
205 |
+
booking_conn.close()
|
206 |
+
else:
|
207 |
+
st.warning("β οΈ Missing user information. Please provide all booking details first.")
|
208 |
+
st.text("")
|
209 |
+
# Inject custom CSS for smaller font and tighter layout
|
210 |
+
st.markdown("""
|
211 |
+
<style>
|
212 |
+
.element-container:has(.streamlit-expander) {
|
213 |
+
margin-bottom: 0.5rem;
|
214 |
+
}
|
215 |
+
.streamlit-expanderHeader {
|
216 |
+
font-size: 0.9rem;
|
217 |
+
}
|
218 |
+
.streamlit-expanderContent {
|
219 |
+
font-size: 0.85rem;
|
220 |
+
padding: 0.5rem 1rem;
|
221 |
+
}
|
222 |
+
</style>
|
223 |
+
""", unsafe_allow_html=True)
|
224 |
+
|
225 |
+
with st.container():
|
226 |
+
col1, col2, col3 = st.columns(3)
|
227 |
+
|
228 |
+
with col1:
|
229 |
+
with st.expander("π½οΈ Restaurants"):
|
230 |
+
st.markdown("""
|
231 |
+
- Bella Italia
|
232 |
+
- Spice Symphony
|
233 |
+
- Tokyo Ramen House
|
234 |
+
- Saffron Grill
|
235 |
+
- El Toro Loco
|
236 |
+
- Noodle Bar
|
237 |
+
- Le Petit Bistro
|
238 |
+
- Tandoori Nights
|
239 |
+
- Green Leaf Cafe
|
240 |
+
- Ocean Pearl
|
241 |
+
- Mama Mia Pizza
|
242 |
+
- The Dumpling Den
|
243 |
+
- Bangkok Express
|
244 |
+
- Curry Kingdom
|
245 |
+
- The Garden Table
|
246 |
+
- Skyline Dine
|
247 |
+
- Pasta Republic
|
248 |
+
- Street Tacos Co
|
249 |
+
- Miso Hungry
|
250 |
+
- Chez Marie
|
251 |
+
""")
|
252 |
+
|
253 |
+
with col2:
|
254 |
+
with st.expander("π Cuisines"):
|
255 |
+
st.markdown("""
|
256 |
+
- Italian
|
257 |
+
- French
|
258 |
+
- Chinese
|
259 |
+
- Japanese
|
260 |
+
- Indian
|
261 |
+
- Mexican
|
262 |
+
- Thai
|
263 |
+
- Healthy
|
264 |
+
- Fusion
|
265 |
+
""")
|
266 |
+
|
267 |
+
with col3:
|
268 |
+
with st.expander("β¨ Special Features"):
|
269 |
+
st.markdown("""
|
270 |
+
- Pet-Friendly
|
271 |
+
- Live Music
|
272 |
+
- Rooftop View
|
273 |
+
- Outdoor Seating
|
274 |
+
- Private Dining
|
275 |
+
""")
|
276 |
+
|
277 |
+
|
278 |
+
|
279 |
+
|
280 |
+
# --- Display previous chat history (before new input) ---
|
281 |
+
|
282 |
+
for msg in st.session_state.chat_history:
|
283 |
+
# Check if both 'role' and 'message' are not None
|
284 |
+
if msg['role'] is not None and msg['message'] is not None:
|
285 |
+
with st.chat_message(msg['role']):
|
286 |
+
st.markdown(msg['message'])
|
287 |
+
|
288 |
+
user_input = st.chat_input("Ask something about restaurants or reservations(eg. Tell me some best rated Italian cuisine restaurants)...")
|
289 |
+
if user_input:
|
290 |
+
# Show user message instantly
|
291 |
+
with st.chat_message("user"):
|
292 |
+
st.markdown(user_input)
|
293 |
+
st.session_state.chat_history.append({'role': 'user', 'message': user_input})
|
294 |
+
|
295 |
+
# Prepare conversation context
|
296 |
+
history_prompt = st.session_state.last_assistant_reply
|
297 |
+
|
298 |
+
# Store possible user info
|
299 |
+
user_info = store_user_info(user_input,history_prompt,store_user_info_prompt,client)
|
300 |
+
if user_info:
|
301 |
+
st.session_state.user_data.update(user_info)
|
302 |
+
# st.rerun()
|
303 |
+
|
304 |
+
# Detect intent
|
305 |
+
intent = determine_intent(user_input,determine_intent_prompt,client)
|
306 |
+
# st.write(intent)
|
307 |
+
if intent == "RUBBISH":
|
308 |
+
# Display user data for confirmation instead of invoking LLM
|
309 |
+
with st.chat_message("assistant"):
|
310 |
+
st.markdown("β Sorry, I didn't understand that. Could you rephrase your request?")
|
311 |
+
st.session_state.chat_history.append({
|
312 |
+
'role': 'assistant',
|
313 |
+
'message': "β Sorry, I didn't understand that. Could you rephrase your request?"
|
314 |
+
})
|
315 |
+
|
316 |
+
st.stop()
|
317 |
+
|
318 |
+
# Generate assistant reply
|
319 |
+
required_keys = ["restaurant_name", "user_name", "contact", "party_size", "time"]
|
320 |
+
user_data_complete = all(
|
321 |
+
k in st.session_state.user_data and st.session_state.user_data[k] not in [None, "", "NULL"]
|
322 |
+
for k in required_keys
|
323 |
+
)
|
324 |
+
|
325 |
+
|
326 |
+
if user_data_complete and intent != "BOOK":
|
327 |
+
|
328 |
+
# Format user data as a Markdown bullet list
|
329 |
+
user_details = "\n".join([f"- **{key.capitalize()}**: {value}" for key, value in st.session_state.user_data.items()])
|
330 |
+
|
331 |
+
with st.chat_message("assistant"):
|
332 |
+
st.markdown("β
I have all the details needed for your reservation:")
|
333 |
+
st.markdown(user_details)
|
334 |
+
st.markdown("If everything looks good, please type **`book`** to confirm the reservation.")
|
335 |
+
|
336 |
+
st.session_state.chat_history.append({
|
337 |
+
'role': 'assistant',
|
338 |
+
'message': f"β
I have all the details needed for your reservation:\n{user_details}\nPlease type **`book`** to confirm."
|
339 |
+
})
|
340 |
+
st.session_state.last_assistant_reply = "I have all the reservation details. Waiting for confirmation..."
|
341 |
+
st.rerun()
|
342 |
+
st.stop()
|
343 |
+
|
344 |
+
|
345 |
+
|
346 |
+
|
347 |
+
response_summary = None
|
348 |
+
|
349 |
+
if intent == "SELECT":
|
350 |
+
response_summary=handle_query(user_input, st.session_state.full_vector_db, client)
|
351 |
+
|
352 |
+
# First try semantic search
|
353 |
+
semantic_results = {}
|
354 |
+
|
355 |
+
# Search across all collections
|
356 |
+
restaurant_results = st.session_state.full_vector_db.semantic_search(user_input, "restaurants")
|
357 |
+
table_results = st.session_state.full_vector_db.semantic_search(user_input, "tables")
|
358 |
+
slot_results = st.session_state.full_vector_db.semantic_search(user_input, "slots")
|
359 |
+
|
360 |
+
if not is_large_output_request(user_input) and any([restaurant_results, table_results, slot_results]):
|
361 |
+
semantic_results = {
|
362 |
+
"restaurants": restaurant_results,
|
363 |
+
"tables": table_results,
|
364 |
+
"slots": slot_results
|
365 |
+
}
|
366 |
+
# Format semantic results
|
367 |
+
summary = []
|
368 |
+
for category, items in semantic_results.items():
|
369 |
+
if items:
|
370 |
+
summary.append(f"Found {len(items)} relevant {category}:")
|
371 |
+
summary.extend([f"- {item['name']}" if 'name' in item else f"- {item}"
|
372 |
+
for item in items[:3]])
|
373 |
+
st.write("### Semantic Search used")
|
374 |
+
response_summary = "\n".join(summary)
|
375 |
+
else:
|
376 |
+
# Fall back to SQL generation for large or exact output requests
|
377 |
+
sql = generate_sql_query_v2(user_input,SCHEMA_DESCRIPTIONS, history_prompt, vector_db, client)
|
378 |
+
result = execute_query(sql)
|
379 |
+
response_summary = interpret_result_v2(result, user_input, sql)
|
380 |
+
|
381 |
+
|
382 |
+
|
383 |
+
# sql = generate_sql_query_v2(user_input,history_prompt, vector_db, client)
|
384 |
+
# result = execute_query(sql)
|
385 |
+
# response_summary=interpret_result_v2(result, user_input, sql)
|
386 |
+
# if isinstance(result, pd.DataFrame):
|
387 |
+
# response_summary = interpret_sql_result(user_input, sql_query, result)
|
388 |
+
|
389 |
+
|
390 |
+
elif intent == "BOOK":
|
391 |
+
required_keys = ["restaurant_name", "user_name", "contact", "party_size", "time"]
|
392 |
+
if all(st.session_state.user_data.get(k) is not None for k in required_keys):
|
393 |
+
booking_conn = None
|
394 |
+
try:
|
395 |
+
user_data = st.session_state.user_data
|
396 |
+
party_size = int(user_data["party_size"])
|
397 |
+
tables_needed = -(-party_size // 4)
|
398 |
+
|
399 |
+
booking_conn = sqlite3.connect("db/restaurant_reservation.db")
|
400 |
+
booking_cursor = booking_conn.cursor()
|
401 |
+
|
402 |
+
booking_cursor.execute("SELECT id FROM restaurants WHERE LOWER(name) = LOWER(?)", (user_data["restaurant_name"],))
|
403 |
+
restaurant_row = booking_cursor.fetchone()
|
404 |
+
if not restaurant_row:
|
405 |
+
raise Exception("Restaurant not found.")
|
406 |
+
restaurant_id = restaurant_row[0]
|
407 |
+
|
408 |
+
booking_cursor.execute("""
|
409 |
+
SELECT t.id AS table_id, s.id AS slot_id
|
410 |
+
FROM tables t
|
411 |
+
JOIN slots s ON t.id = s.table_id
|
412 |
+
WHERE t.restaurant_id = ?
|
413 |
+
AND s.hour = ?
|
414 |
+
AND s.date = '2025-05-12'
|
415 |
+
AND s.is_reserved = 0
|
416 |
+
LIMIT ?
|
417 |
+
""", (restaurant_id, user_data["time"], tables_needed))
|
418 |
+
available = booking_cursor.fetchall()
|
419 |
+
# Debugging output
|
420 |
+
|
421 |
+
if len(available) < tables_needed:
|
422 |
+
raise Exception("Not enough available tables.")
|
423 |
+
|
424 |
+
booking_cursor.execute("""
|
425 |
+
INSERT INTO reservations (restaurant_id, user_name, contact, date, time, party_size)
|
426 |
+
VALUES (?, ?, ?, '2025-05-12', ?, ?)
|
427 |
+
""", (restaurant_id, user_data["user_name"], user_data["contact"], user_data["time"], party_size))
|
428 |
+
reservation_id = booking_cursor.lastrowid
|
429 |
+
|
430 |
+
for table_id, _ in available:
|
431 |
+
booking_cursor.execute("INSERT INTO reservation_tables (reservation_id, table_id) VALUES (?, ?)", (reservation_id, table_id))
|
432 |
+
|
433 |
+
slot_ids = [slot_id for _, slot_id in available]
|
434 |
+
booking_cursor.executemany("UPDATE slots SET is_reserved = 1 WHERE id = ?", [(sid,) for sid in slot_ids])
|
435 |
+
|
436 |
+
booking_conn.commit()
|
437 |
+
# Fetch the restaurant name to confirm
|
438 |
+
booking_cursor.execute("SELECT name FROM restaurants WHERE id = ?", (restaurant_id,))
|
439 |
+
restaurant_name = booking_cursor.fetchone()[0]
|
440 |
+
|
441 |
+
# Prepare confirmation details
|
442 |
+
confirmation_msg = (
|
443 |
+
f"β
Booking processed successfully!\n\n"
|
444 |
+
f"π Restaurant: **{restaurant_name}**\n"
|
445 |
+
f"β° Time: **{user_data['time']} on 2025-05-12**\n"
|
446 |
+
f"π½οΈ Tables Booked: **{tables_needed}**\n"
|
447 |
+
f"π Reservation ID: **{reservation_id}**\n\n"
|
448 |
+
f"π Please mention this Reservation ID at the restaurant reception when you arrive."
|
449 |
+
)
|
450 |
+
|
451 |
+
response_summary = confirmation_msg
|
452 |
+
st.success(response_summary)
|
453 |
+
st.session_state.chat_history.append({'role': 'assistant', 'message': response_summary})
|
454 |
+
response_summary="β
Booking processed successfully."
|
455 |
+
st.session_state.user_data["restaurant_name"]=None
|
456 |
+
st.session_state.user_data["party_size"]=None
|
457 |
+
st.session_state.user_data["time"]=None
|
458 |
+
st.session_state.last_assistant_reply=""
|
459 |
+
except Exception as e:
|
460 |
+
if booking_conn:
|
461 |
+
booking_conn.rollback()
|
462 |
+
response_summary = f"β Booking failed: {e}"
|
463 |
+
st.error(response_summary)
|
464 |
+
finally:
|
465 |
+
if booking_conn:
|
466 |
+
booking_cursor=None
|
467 |
+
booking_conn.close()
|
468 |
+
else:
|
469 |
+
st.markdown("β οΈ Missing user information. Please provide all booking details first.")
|
470 |
+
response_summary = "β οΈ Missing user information. Please provide all booking details first."
|
471 |
+
|
472 |
+
|
473 |
+
elif intent == "GREET":
|
474 |
+
response_summary = "π Hello! How can I help you with your restaurant reservation today?"
|
475 |
+
|
476 |
+
elif intent == "RUBBISH":
|
477 |
+
response_summary = "β Sorry, I didn't understand that. Could you rephrase your request?"
|
478 |
+
|
479 |
+
# Generate assistant reply
|
480 |
+
if response_summary!="β
Booking processed successfully.":
|
481 |
+
follow_up = generate_reservation_conversation(
|
482 |
+
user_input,
|
483 |
+
history_prompt,
|
484 |
+
response_summary or "Info stored.",
|
485 |
+
json.dumps(st.session_state.user_data),generate_reservation_conversation_prompt,client
|
486 |
+
)
|
487 |
+
else:
|
488 |
+
follow_up="Thanks for booking with FoodieSpot restaurant chain, I could assist you in new booking, also I could tell about restaurant features, pricing, etc... "
|
489 |
+
|
490 |
+
# Show assistant reply instantly
|
491 |
+
with st.chat_message("assistant"):
|
492 |
+
st.markdown(follow_up)
|
493 |
+
|
494 |
+
st.session_state.chat_history.append({'role': 'assistant', 'message': follow_up})
|
495 |
+
# Update it after assistant speaks
|
496 |
+
st.session_state.last_assistant_reply = follow_up
|
497 |
+
st.rerun()
|
498 |
+
# Reset if booking done
|
499 |
+
|
500 |
+
|
assets/all_resto.png
ADDED
![]() |
Git LFS Details
|
assets/booking_successful.png
ADDED
![]() |
Git LFS Details
|
assets/general_conv_info_through_chat.png
ADDED
![]() |
Git LFS Details
|
assets/greet_general_convo.png
ADDED
![]() |
Git LFS Details
|
assets/landing.png
ADDED
![]() |
Git LFS Details
|
assets/mermaid-1.png
ADDED
![]() |
Git LFS Details
|
assets/mermaid.png
ADDED
![]() |
Git LFS Details
|
assets/name_entering.png
ADDED
![]() |
Git LFS Details
|
assets/ready_to_book.png
ADDED
![]() |
Git LFS Details
|
assets/rubbish.PNG
ADDED
|
Git LFS Details
|
assets/some_results.PNG
ADDED
|
Git LFS Details
|
db/chroma/0f1c557e-a6e2-45cb-8079-0720b4f2093f/data_level0.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b8146ecc3e4c3a36ea9b3edc3778630c452f483990ec942d38e8006f4661e430
|
3 |
+
size 16760000
|
db/chroma/0f1c557e-a6e2-45cb-8079-0720b4f2093f/header.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:18f1e924efbb5e1af5201e3fbab86a97f5c195c311abe651eeec525884e5e449
|
3 |
+
size 100
|
db/chroma/0f1c557e-a6e2-45cb-8079-0720b4f2093f/length.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:861b50c40305a93f26d251878405410518604fff022602201f32d4a4f5139253
|
3 |
+
size 40000
|
db/chroma/0f1c557e-a6e2-45cb-8079-0720b4f2093f/link_lists.bin
ADDED
File without changes
|
db/chroma/45948c9b-58d5-4762-9fd1-cf9b5925ba84/data_level0.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b8146ecc3e4c3a36ea9b3edc3778630c452f483990ec942d38e8006f4661e430
|
3 |
+
size 16760000
|
db/chroma/45948c9b-58d5-4762-9fd1-cf9b5925ba84/header.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:18f1e924efbb5e1af5201e3fbab86a97f5c195c311abe651eeec525884e5e449
|
3 |
+
size 100
|
db/chroma/45948c9b-58d5-4762-9fd1-cf9b5925ba84/length.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d5b4759a12671b6d014df975fda1dd7ac0f7c71b4919c679a36bc9f76bc8aaf3
|
3 |
+
size 40000
|
db/chroma/45948c9b-58d5-4762-9fd1-cf9b5925ba84/link_lists.bin
ADDED
File without changes
|
db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/data_level0.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:3cdd813c7b938a646786a77ee5e41520302ea0d68cda1b40a41e3e633c7165a8
|
3 |
+
size 16760000
|
db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/header.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:834eaecd974231ba7c9bae46579a2e84cae7acd944d00827cddb0c3cbd1915e4
|
3 |
+
size 100
|
db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/index_metadata.pickle
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:18ec9cba57199ceca85f8017e5eec2047d0538fbeb5024abc3c260ba5ecbedb8
|
3 |
+
size 312140
|
db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/length.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:231f5aecbcb787adc68fa2e2f8f9876e22cdca514de5ffc2a9091a4ce5d072ae
|
3 |
+
size 40000
|
db/chroma/b97e1f95-8691-4375-b2f8-2f2e96d82a8c/link_lists.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:34aa97ec388caf3881bf351c822de9a429f610fb3528d766660d616c1e14e88a
|
3 |
+
size 25396
|
db/chroma/chroma.sqlite3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:94eef2082e840a627f17ecdbc0477cc7d9754ebf2663a2f1a039d09b777b8382
|
3 |
+
size 5046272
|
db/create_base.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sqlite3
|
2 |
+
|
3 |
+
# Connect to the SQLite database
|
4 |
+
conn = sqlite3.connect("restaurant_reservation.db")
|
5 |
+
cursor = conn.cursor()
|
6 |
+
|
7 |
+
# Create tables if they do not exist
|
8 |
+
cursor.executescript("""
|
9 |
+
CREATE TABLE IF NOT EXISTS restaurants (
|
10 |
+
id TEXT PRIMARY KEY,
|
11 |
+
name TEXT NOT NULL,
|
12 |
+
cuisine TEXT,
|
13 |
+
location TEXT,
|
14 |
+
seating_capacity INTEGER,
|
15 |
+
rating REAL,
|
16 |
+
address TEXT,
|
17 |
+
contact TEXT,
|
18 |
+
price_range TEXT,
|
19 |
+
special_features TEXT
|
20 |
+
);
|
21 |
+
|
22 |
+
CREATE TABLE IF NOT EXISTS tables (
|
23 |
+
id TEXT PRIMARY KEY,
|
24 |
+
restaurant_id TEXT,
|
25 |
+
capacity INTEGER DEFAULT 4,
|
26 |
+
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)
|
27 |
+
);
|
28 |
+
|
29 |
+
CREATE TABLE IF NOT EXISTS slots (
|
30 |
+
id TEXT PRIMARY KEY,
|
31 |
+
table_id TEXT,
|
32 |
+
date TEXT,
|
33 |
+
hour INTEGER,
|
34 |
+
is_reserved INTEGER DEFAULT 0,
|
35 |
+
FOREIGN KEY (table_id) REFERENCES tables(id)
|
36 |
+
);
|
37 |
+
|
38 |
+
CREATE TABLE IF NOT EXISTS reservations (
|
39 |
+
id TEXT PRIMARY KEY,
|
40 |
+
restaurant_id TEXT,
|
41 |
+
user_name TEXT,
|
42 |
+
contact TEXT,
|
43 |
+
date TEXT,
|
44 |
+
time INTEGER,
|
45 |
+
party_size INTEGER,
|
46 |
+
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)
|
47 |
+
);
|
48 |
+
|
49 |
+
CREATE TABLE IF NOT EXISTS reservation_tables (
|
50 |
+
id TEXT PRIMARY KEY,
|
51 |
+
reservation_id TEXT,
|
52 |
+
table_id TEXT,
|
53 |
+
FOREIGN KEY (reservation_id) REFERENCES reservations(id),
|
54 |
+
FOREIGN KEY (table_id) REFERENCES tables(id)
|
55 |
+
);
|
56 |
+
""")
|
57 |
+
|
58 |
+
# Commit the changes
|
59 |
+
conn.commit()
|
60 |
+
|
61 |
+
# Close the connection
|
62 |
+
conn.close()
|
63 |
+
|
64 |
+
print("Tables have been created successfully!")
|
db/create_slots.py
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sqlite3
|
2 |
+
import uuid
|
3 |
+
from datetime import datetime
|
4 |
+
|
5 |
+
# Connect to your SQLite DB
|
6 |
+
conn = sqlite3.connect("restaurant_reservation.db")
|
7 |
+
cursor = conn.cursor()
|
8 |
+
|
9 |
+
# Get all table IDs
|
10 |
+
cursor.execute("SELECT id FROM tables")
|
11 |
+
table_ids = [row[0] for row in cursor.fetchall()]
|
12 |
+
|
13 |
+
# Define the time range and current date
|
14 |
+
start_hour = 9 # 9AM
|
15 |
+
end_hour = 21 # 9PM
|
16 |
+
|
17 |
+
# Prepare slot entries
|
18 |
+
slot_entries = []
|
19 |
+
for table_id in table_ids:
|
20 |
+
for hour in range(start_hour, end_hour):
|
21 |
+
slot_id = str(uuid.uuid4())
|
22 |
+
slot_entries.append((slot_id, table_id, "2025-05-12", hour, 0)) # is_reserved = 0
|
23 |
+
|
24 |
+
# Insert into slots table
|
25 |
+
cursor.executemany("""
|
26 |
+
INSERT INTO slots (id, table_id, date, hour, is_reserved)
|
27 |
+
VALUES (?, ?, ?, ?, ?)
|
28 |
+
""", slot_entries)
|
29 |
+
|
30 |
+
conn.commit()
|
31 |
+
conn.close()
|
32 |
+
|
33 |
+
print("β
Slots successfully added for all tables for today.")
|
db/dbmodify.py
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import sqlite3
|
2 |
+
|
3 |
+
# conn = sqlite3.connect("restaurant_reservation.db")
|
4 |
+
# cursor = conn.cursor()
|
5 |
+
|
6 |
+
# # Drop the existing empty tables
|
7 |
+
# cursor.execute("DROP TABLE IF EXISTS reservations;")
|
8 |
+
# cursor.execute("DROP TABLE IF EXISTS reservation_tables;")
|
9 |
+
|
10 |
+
# # Recreate the tables with AUTOINCREMENT for `id`
|
11 |
+
# cursor.execute("""
|
12 |
+
# CREATE TABLE reservations (
|
13 |
+
# id INTEGER PRIMARY KEY AUTOINCREMENT,
|
14 |
+
# restaurant_id TEXT,
|
15 |
+
# user_name TEXT,
|
16 |
+
# contact TEXT,
|
17 |
+
# date TEXT, -- Hard coded to 2025-05-12
|
18 |
+
# time TEXT,
|
19 |
+
# party_size INTEGER
|
20 |
+
# );
|
21 |
+
# """)
|
22 |
+
|
23 |
+
# cursor.execute("""
|
24 |
+
# CREATE TABLE reservation_tables (
|
25 |
+
# id INTEGER PRIMARY KEY AUTOINCREMENT,
|
26 |
+
# reservation_id TEXT,
|
27 |
+
# table_id TEXT
|
28 |
+
# );
|
29 |
+
# """)
|
30 |
+
|
31 |
+
# conn.commit()
|
32 |
+
# conn.close()
|
33 |
+
|
34 |
+
# print("Tables recreated successfully with AUTOINCREMENT ids.")
|
35 |
+
|
36 |
+
import sqlite3
|
37 |
+
|
38 |
+
conn = sqlite3.connect("restaurant_reservation.db")
|
39 |
+
cursor = conn.cursor()
|
40 |
+
|
41 |
+
try:
|
42 |
+
cursor.execute("""
|
43 |
+
UPDATE restaurants
|
44 |
+
SET name = 'Street Tacos Co'
|
45 |
+
WHERE name = 'Street Tacos Co.';
|
46 |
+
""")
|
47 |
+
conn.commit()
|
48 |
+
print("β
Restaurant name updated successfully.")
|
49 |
+
except Exception as e:
|
50 |
+
conn.rollback()
|
51 |
+
print(f"β Update failed: {e}")
|
52 |
+
finally:
|
53 |
+
conn.close()
|
54 |
+
|
db/fill_details.py
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import uuid
|
2 |
+
import random
|
3 |
+
import sqlite3
|
4 |
+
|
5 |
+
# ---------------------------
|
6 |
+
# Data Classes
|
7 |
+
# ---------------------------
|
8 |
+
class Restaurant:
|
9 |
+
def __init__(self, restaurant_id, name, cuisine, location, seating_capacity, rating, address, contact, price_range, special_features):
|
10 |
+
self.restaurant_id = restaurant_id
|
11 |
+
self.name = name
|
12 |
+
self.cuisine = cuisine
|
13 |
+
self.location = location
|
14 |
+
self.seating_capacity = seating_capacity
|
15 |
+
self.rating = rating
|
16 |
+
self.address = address
|
17 |
+
self.contact = contact
|
18 |
+
self.price_range = price_range
|
19 |
+
self.special_features = special_features
|
20 |
+
self.tables = []
|
21 |
+
|
22 |
+
class Table:
|
23 |
+
def __init__(self, table_id, restaurant_id, capacity=4):
|
24 |
+
self.table_id = table_id
|
25 |
+
self.restaurant_id = restaurant_id
|
26 |
+
self.capacity = capacity
|
27 |
+
|
28 |
+
# ---------------------------
|
29 |
+
# Sample Data
|
30 |
+
# ---------------------------
|
31 |
+
restaurant_names = [
|
32 |
+
"Bella Italia", "Spice Symphony", "Tokyo Ramen House", "Saffron Grill", "El Toro Loco",
|
33 |
+
"Noodle Bar", "Le Petit Bistro", "Tandoori Nights", "Green Leaf Cafe", "Ocean Pearl",
|
34 |
+
"Mama Mia Pizza", "The Dumpling Den", "Bangkok Express", "Curry Kingdom", "The Garden Table",
|
35 |
+
"Skyline Dine", "Pasta Republic", "Street Tacos Co", "Miso Hungry", "Chez Marie"
|
36 |
+
]
|
37 |
+
|
38 |
+
locations = ['Downtown', 'Uptown', 'Midtown', 'Suburbs']
|
39 |
+
special_features_list = ['Outdoor Seating', 'Pet-Friendly', 'Live Music', 'Rooftop View', 'Private Dining']
|
40 |
+
|
41 |
+
def infer_cuisine(name):
|
42 |
+
name = name.lower()
|
43 |
+
if "italia" in name or "pasta" in name or "mama mia" in name:
|
44 |
+
return "Italian"
|
45 |
+
elif "tokyo" in name or "ramen" in name or "miso" in name:
|
46 |
+
return "Japanese"
|
47 |
+
elif "saffron" in name or "tandoori" in name or "curry" in name:
|
48 |
+
return "Indian"
|
49 |
+
elif "dumpling" in name or "noodle" in name:
|
50 |
+
return "Chinese"
|
51 |
+
elif "bistro" in name or "chez" in name or "marie" in name:
|
52 |
+
return "French"
|
53 |
+
elif "bangkok" in name:
|
54 |
+
return "Thai"
|
55 |
+
elif "el toro" in name or "tacos" in name:
|
56 |
+
return "Mexican"
|
57 |
+
elif "green" in name or "garden" in name:
|
58 |
+
return random.choice(["Multi-Cuisine", "Healthy", "Fusion"])
|
59 |
+
elif "skyline" in name or "ocean" in name:
|
60 |
+
return random.choice(["Multi-Cuisine", "Seafood", "Fusion"])
|
61 |
+
else:
|
62 |
+
return random.choice(["Italian", "Mexican", "Indian", "Japanese", "Chinese", "Thai", "French", "Multi-Cuisine"])
|
63 |
+
|
64 |
+
# Create restaurant objects
|
65 |
+
restaurants = []
|
66 |
+
|
67 |
+
for i in range(20):
|
68 |
+
rest_id = str(uuid.uuid4())
|
69 |
+
name = restaurant_names[i]
|
70 |
+
cuisine = infer_cuisine(name)
|
71 |
+
if cuisine == "Multi-Cuisine":
|
72 |
+
cuisine = random.sample(["Italian", "Chinese", "Indian", "Mexican", "French"], k=2)
|
73 |
+
|
74 |
+
location = random.choice(locations)
|
75 |
+
num_tables = random.randint(10, 20)
|
76 |
+
seating_capacity = num_tables * 4
|
77 |
+
rating = round(random.uniform(3.5, 5.0), 1)
|
78 |
+
address = f"{100 + i} Main Street, {location}"
|
79 |
+
contact = f"555-{1000 + i}"
|
80 |
+
price_range = random.choice(['$', '$$', '$$$'])
|
81 |
+
features = random.sample(special_features_list, k=2)
|
82 |
+
|
83 |
+
restaurant = Restaurant(
|
84 |
+
restaurant_id=rest_id,
|
85 |
+
name=name,
|
86 |
+
cuisine=cuisine,
|
87 |
+
location=location,
|
88 |
+
seating_capacity=seating_capacity,
|
89 |
+
rating=rating,
|
90 |
+
address=address,
|
91 |
+
contact=contact,
|
92 |
+
price_range=price_range,
|
93 |
+
special_features=features
|
94 |
+
)
|
95 |
+
|
96 |
+
for _ in range(num_tables):
|
97 |
+
table_id = str(uuid.uuid4())
|
98 |
+
table = Table(table_id=table_id, restaurant_id=rest_id)
|
99 |
+
restaurant.tables.append(table)
|
100 |
+
|
101 |
+
restaurants.append(restaurant)
|
102 |
+
|
103 |
+
# ---------------------------
|
104 |
+
# Insert into SQLite Database
|
105 |
+
# ---------------------------
|
106 |
+
conn = sqlite3.connect("restaurant_reservation.db")
|
107 |
+
cursor = conn.cursor()
|
108 |
+
|
109 |
+
for r in restaurants:
|
110 |
+
cuisine_str = ", ".join(r.cuisine) if isinstance(r.cuisine, list) else r.cuisine
|
111 |
+
features_str = ", ".join(r.special_features)
|
112 |
+
|
113 |
+
cursor.execute("""
|
114 |
+
INSERT INTO restaurants (id, name, cuisine, location, seating_capacity, rating, address, contact, price_range, special_features)
|
115 |
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
116 |
+
""", (
|
117 |
+
r.restaurant_id,
|
118 |
+
r.name,
|
119 |
+
cuisine_str,
|
120 |
+
r.location,
|
121 |
+
r.seating_capacity,
|
122 |
+
r.rating,
|
123 |
+
r.address,
|
124 |
+
r.contact,
|
125 |
+
r.price_range,
|
126 |
+
features_str
|
127 |
+
))
|
128 |
+
|
129 |
+
for t in r.tables:
|
130 |
+
cursor.execute("""
|
131 |
+
INSERT INTO tables (id, restaurant_id, capacity)
|
132 |
+
VALUES (?, ?, ?)
|
133 |
+
""", (
|
134 |
+
t.table_id,
|
135 |
+
t.restaurant_id,
|
136 |
+
t.capacity
|
137 |
+
))
|
138 |
+
|
139 |
+
conn.commit()
|
140 |
+
conn.close()
|
141 |
+
print("β
Restaurants and tables successfully added to the database.")
|
db/print_db.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sqlite3
|
2 |
+
|
3 |
+
# Connect to the SQLite database
|
4 |
+
conn = sqlite3.connect("restaurant_reservation.db")
|
5 |
+
cursor = conn.cursor()
|
6 |
+
|
7 |
+
# Function to print table contents
|
8 |
+
def print_table_contents(table_name):
|
9 |
+
print(f"Contents of the {table_name} table:")
|
10 |
+
cursor.execute(f"SELECT * FROM {table_name}")
|
11 |
+
rows = cursor.fetchall()
|
12 |
+
for row in rows:
|
13 |
+
print(row)
|
14 |
+
print("\n")
|
15 |
+
|
16 |
+
# Print contents of all the tables
|
17 |
+
print_table_contents("restaurants")
|
18 |
+
print_table_contents("tables")
|
19 |
+
print_table_contents("slots")
|
20 |
+
print_table_contents("reservations")
|
21 |
+
print_table_contents("reservation_tables")
|
22 |
+
|
23 |
+
# Close the database connection
|
24 |
+
conn.close()
|
db/resetdb.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sqlite3
|
2 |
+
|
3 |
+
def reset_reservations():
|
4 |
+
sql_statements = [
|
5 |
+
"UPDATE slots SET is_reserved = 0;",
|
6 |
+
"DELETE FROM reservation_tables;",
|
7 |
+
"DELETE FROM reservations;"
|
8 |
+
]
|
9 |
+
|
10 |
+
try:
|
11 |
+
conn = sqlite3.connect("restaurant_reservation.db")
|
12 |
+
cursor = conn.cursor()
|
13 |
+
|
14 |
+
cursor.execute("BEGIN TRANSACTION;")
|
15 |
+
for stmt in sql_statements:
|
16 |
+
cursor.execute(stmt)
|
17 |
+
conn.commit()
|
18 |
+
conn.close()
|
19 |
+
return "β
All slots marked as reserved and reservations cleared."
|
20 |
+
except Exception as e:
|
21 |
+
conn.rollback()
|
22 |
+
conn.close()
|
23 |
+
return f"β Error during reset: {e}"
|
24 |
+
|
25 |
+
# Call this function
|
26 |
+
result = reset_reservations()
|
27 |
+
print(result)
|
db/restaurant_reservation.db
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:7663a4d218aae1e7d53ef8594b6d366506fe95a96f00f7a10e9e95df0416d883
|
3 |
+
size 1273856
|
efficiency_log.txt
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Function: store_user_info
|
2 |
+
Prompt tokens: 521
|
3 |
+
Completion tokens: 26
|
4 |
+
Total tokens: 547
|
5 |
+
Prompt: You are a helpful assistant. Extract relevant user information from this user statement:
|
6 |
+
"Hi"
|
7 |
+
|
8 |
+
Previously collected data in json: {"restaurant_name": null, "user_name": null, "contact": null, "party_size": null, "time": null}
|
9 |
+
Always remember this json data, you need to update this based on user statement.
|
10 |
+
if user statement is book, dont change any value in this data
|
11 |
+
Return a JSON object with the following possible keys:
|
12 |
+
- restaurant_name - text
|
13 |
+
- user_name - text
|
14 |
+
- contact - text
|
15 |
+
- party_size - integer
|
16 |
+
- time (between 9 to 20, 9 represents 9AM, 20 represents 8PM) - integer
|
17 |
+
Donot consider time which is before 9 or after 20.
|
18 |
+
Never modify any entry to null if previous data is not null for that field.
|
19 |
+
Update the previous data with any new fields found. Do not make previously known fields unless you are sure the user wants to change them.
|
20 |
+
Respond ONLY with a single valid JSON object.
|
21 |
+
important rules:
|
22 |
+
- "restaurant_name": Must always match from this list:
|
23 |
+
Bella Italia, Spice Symphony, Tokyo Ramen House, Saffron Grill, El Toro Loco, Noodle Bar, Le Petit Bistro, Tandoori Nights, Green Leaf Cafe, Ocean Pearl, Mama Mia Pizza, The Dumpling Den, Bangkok Express, Curry Kingdom, The Garden Table, Skyline Dine, Pasta Republic, Street Tacos Co, Miso Hungry, Chez Marie
|
24 |
+
|
25 |
+
|
26 |
+
If in previously collected data, the restaurant_name is there but not in this list as the exact spelling or not with correct casing, replace it with the correct one.
|
27 |
+
|
28 |
+
If user statement is a restaurant_name, dont modify user_name thinking that it is restaurant name, only modify user_name.
|
29 |
+
- "user_name":
|
30 |
+
- Only extract if the input clearly states a name like βMy name is ...β or βThis is ...β
|
31 |
+
- Do not extract from greetings like βHiβ, βHelloβ, βHeyβ, βYoβ, βGood eveningβ
|
32 |
+
- Do not invent names based on formatting or assumptions
|
33 |
+
|
34 |
+
Output format rules:
|
35 |
+
-Make sure restaurant_name matches from the list given
|
36 |
+
- Return only valid JSON β starting with { and ending with }
|
37 |
+
- All keys and values must be in double quotes
|
38 |
+
- Include all 5 keys in the output
|
39 |
+
- No markdown, comments, or explanation in output, just give a json
|
40 |
+
---
|
41 |
+
Function: store_user_info
|
42 |
+
Prompt tokens: 521
|
43 |
+
Completion tokens: 63
|
44 |
+
Total tokens: 584
|
45 |
+
Prompt: You are a helpful assistant. Extract relevant user information from this user statement:
|
46 |
+
"Hi"
|
47 |
+
|
48 |
+
Previously collected data in json: {"restaurant_name": null, "user_name": null, "contact": null, "party_size": null, "time": null}
|
49 |
+
Always remember this json data, you need to update this based on user statement.
|
50 |
+
if user statement is book, dont change any value in this data
|
51 |
+
Return a JSON object with the following possible keys:
|
52 |
+
- restaurant_name - text
|
53 |
+
- user_name - text
|
54 |
+
- contact - text
|
55 |
+
- party_size - integer
|
56 |
+
- time (between 9 to 20, 9 represents 9AM, 20 represents 8PM) - integer
|
57 |
+
Donot consider time which is before 9 or after 20.
|
58 |
+
Never modify any entry to null if previous data is not null for that field.
|
59 |
+
Update the previous data with any new fields found. Do not make previously known fields unless you are sure the user wants to change them.
|
60 |
+
Respond ONLY with a single valid JSON object.
|
61 |
+
important rules:
|
62 |
+
- "restaurant_name": Must always match from this list:
|
63 |
+
Bella Italia, Spice Symphony, Tokyo Ramen House, Saffron Grill, El Toro Loco, Noodle Bar, Le Petit Bistro, Tandoori Nights, Green Leaf Cafe, Ocean Pearl, Mama Mia Pizza, The Dumpling Den, Bangkok Express, Curry Kingdom, The Garden Table, Skyline Dine, Pasta Republic, Street Tacos Co, Miso Hungry, Chez Marie
|
64 |
+
|
65 |
+
|
66 |
+
If in previously collected data, the restaurant_name is there but not in this list as the exact spelling or not with correct casing, replace it with the correct one.
|
67 |
+
|
68 |
+
If user statement is a restaurant_name, dont modify user_name thinking that it is restaurant name, only modify user_name.
|
69 |
+
- "user_name":
|
70 |
+
- Only extract if the input clearly states a name like βMy name is ...β or βThis is ...β
|
71 |
+
- Do not extract from greetings like βHiβ, βHelloβ, βHeyβ, βYoβ, βGood eveningβ
|
72 |
+
- Do not invent names based on formatting or assumptions
|
73 |
+
|
74 |
+
Output format rules:
|
75 |
+
-Make sure restaurant_name matches from the list given
|
76 |
+
- Return only valid JSON β starting with { and ending with }
|
77 |
+
- All keys and values must be in double quotes
|
78 |
+
- Include all 5 keys in the output
|
79 |
+
- No markdown, comments, or explanation in output, just give a json
|
80 |
+
---
|
81 |
+
Function: determine_intent
|
82 |
+
Prompt tokens: 257
|
83 |
+
Completion tokens: 3
|
84 |
+
Total tokens: 260
|
85 |
+
Prompt: You are an intent classification assistant for a restaurant reservation system.
|
86 |
+
|
87 |
+
User input: "Hi"
|
88 |
+
|
89 |
+
Classify the intent as one of:
|
90 |
+
- STORE: User shares name, contact, or reservation details (like party size or time) without asking anything.
|
91 |
+
- SELECT: User asks about availability, restaurants, time slots, or capacity.
|
92 |
+
- BOOK: User says only "book" (case-insensitive). Even "I want to book..." is SELECT, not BOOK.
|
93 |
+
- GREET: User greets or starts a conversation without giving info or asking.
|
94 |
+
- RUBBISH: Input is gibberish, irrelevant, or unrecognizable.
|
95 |
+
|
96 |
+
Examples:
|
97 |
+
- "My name is Raj" β STORE
|
98 |
+
- "book" β BOOK
|
99 |
+
- "15 people" β SELECT
|
100 |
+
- "Tell me best restaurants" β SELECT
|
101 |
+
- "7801061333" β STORE
|
102 |
+
- "asdfgh" β RUBBISH
|
103 |
+
- "Hi there" β GREET
|
104 |
+
|
105 |
+
Respond with ONE word only: SELECT, STORE, BOOK, GREET, or RUBBISH. No explanation
|
106 |
+
---
|
107 |
+
Function: generate_reservation_conversation
|
108 |
+
Prompt tokens: 427
|
109 |
+
Completion tokens: 50
|
110 |
+
Total tokens: 477
|
111 |
+
Prompt: You are a professional restaurant reservation assistant helping a customer make a booking. Speak concisely and professionally. Unless the booking is complete, end with a helpful question.
|
112 |
+
|
113 |
+
User said: "Hi"
|
114 |
+
Always try to answer this user query.
|
115 |
+
Current known user data (JSON): "{\"restaurant_name\": null, \"user_name\": null, \"contact\": null, \"party_size\": null, \"time\": null}"
|
116 |
+
Only ask about missing fields (those with null/None values). Do not repeat questions for data already present.
|
117 |
+
Never ask about the fields that are already present in the user data json.
|
118 |
+
- user_name: user's name
|
119 |
+
- contact: userβs phone (not for queries)
|
120 |
+
- restaurant_name: name of restaurant
|
121 |
+
- party_size: number of people
|
122 |
+
- time: hour of reservation (9β20)
|
123 |
+
|
124 |
+
If restaurant_name is missing, offer to suggest restaurants or cuisines. Never mention "null"βbe conversational. Show known info naturally if helpful.
|
125 |
+
|
126 |
+
Database info:
|
127 |
+
"π Hello! How can I help you with your restaurant reservation today?"
|
128 |
+
Explain this clearly based on what user said. If it says:
|
129 |
+
- "Info Stored": thank the user and ask next missing info.
|
130 |
+
- "β
Booking processed successfully.": Tell thanks for booking, I could assist you in new booking, also I could tell about restaurant features, pricing, etc, dont ask anything else.
|
131 |
+
- "β Booking failed: ...": explain the error simply and suggest trying again.
|
132 |
+
- A greeting: respond politely and ask if they need help with restaurant info or making a booking.
|
133 |
+
|
134 |
+
Personalize your response using available user data. Each table seats 4 people; use ceil(party_size / 4) to estimate how many are needed.
|
135 |
+
Try to explain as much information as possible from database info in a concise, professional way.
|
136 |
+
|
137 |
+
History snippet: ""
|
138 |
+
If earlier prompts asked for something now present in user data, don't ask again.
|
139 |
+
|
140 |
+
Be helpful, efficient, and professional in tone.
|
141 |
+
---
|
prompts/determine_intent.txt
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
You are an intent classification assistant for a restaurant reservation system.
|
2 |
+
|
3 |
+
User input: "{user_input}"
|
4 |
+
|
5 |
+
Classify the intent as one of:
|
6 |
+
- STORE: User shares name, contact, or reservation details (like party size or time) without asking anything.
|
7 |
+
- SELECT: User asks about availability, restaurants, time slots, or capacity.
|
8 |
+
- BOOK: User says only "book" (case-insensitive). Even "I want to book..." is SELECT, not BOOK.
|
9 |
+
- GREET: User greets or starts a conversation without giving info or asking.
|
10 |
+
- RUBBISH: Input is gibberish, irrelevant, or unrecognizable.
|
11 |
+
|
12 |
+
Examples:
|
13 |
+
- "My name is Raj" β STORE
|
14 |
+
- "book" β BOOK
|
15 |
+
- "15 people" β SELECT
|
16 |
+
- "Tell me best restaurants" β SELECT
|
17 |
+
- "7801061333" β STORE
|
18 |
+
- "asdfgh" β RUBBISH
|
19 |
+
- "Hi there" β GREET
|
20 |
+
|
21 |
+
Respond with ONE word only: SELECT, STORE, BOOK, GREET, or RUBBISH. No explanation
|
prompts/generate_reservation_conversation.txt
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
You are a professional restaurant reservation assistant helping a customer make a booking. Speak concisely and professionally. Unless the booking is complete, end with a helpful question.
|
2 |
+
|
3 |
+
User said: "{user_query}"
|
4 |
+
Always try to answer this user query.
|
5 |
+
Current known user data (JSON): {user_data}
|
6 |
+
Only ask about missing fields (those with null/None values). Do not repeat questions for data already present.
|
7 |
+
Never ask about the fields that are already present in the user data json.
|
8 |
+
- user_name: user's name
|
9 |
+
- contact: userβs phone (not for queries)
|
10 |
+
- restaurant_name: name of restaurant
|
11 |
+
- party_size: number of people
|
12 |
+
- time: hour of reservation (9β20)
|
13 |
+
|
14 |
+
If restaurant_name is missing, offer to suggest restaurants or cuisines. Never mention "null"βbe conversational. Show known info naturally if helpful.
|
15 |
+
|
16 |
+
Database info:
|
17 |
+
"{sql_summary}"
|
18 |
+
Explain this clearly based on what user said. If it says:
|
19 |
+
- "Info Stored": thank the user and ask next missing info.
|
20 |
+
- "β
Booking processed successfully.": Tell thanks for booking, I could assist you in new booking, also I could tell about restaurant features, pricing, etc, dont ask anything else.
|
21 |
+
- "β Booking failed: ...": explain the error simply and suggest trying again.
|
22 |
+
- A greeting: respond politely and ask if they need help with restaurant info or making a booking.
|
23 |
+
|
24 |
+
Personalize your response using available user data. Each table seats 4 people; use ceil(party_size / 4) to estimate how many are needed.
|
25 |
+
Try to explain as much information as possible from database info in a concise, professional way.
|
26 |
+
|
27 |
+
History snippet: "{history_prompt_snippet}"
|
28 |
+
If earlier prompts asked for something now present in user data, don't ask again.
|
29 |
+
|
30 |
+
Be helpful, efficient, and professional in tone.
|
prompts/interpret_sql_result.txt
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
You are an expert assistant interpreting SQL query results for a restaurant reservation system.
|
2 |
+
Try to explain as much information as possible from database info in a concise, professional way.
|
3 |
+
Database schema overview:
|
4 |
+
- restaurants(id, name, cuisine, location, seating_capacity, rating, address, contact, price_range [$, $$, $$$], special_features)
|
5 |
+
- tables(id, restaurant_id, capacity=4)
|
6 |
+
- slots(id, table_id, date, hour [9-21], is_reserved [0=free,1=reserved])
|
7 |
+
|
8 |
+
Notes:
|
9 |
+
- Each table seats 4 guests.
|
10 |
+
- To accommodate a party, number_of_tables_needed = ceil(party_size / 4).
|
11 |
+
- Slots represent table availability by hour.
|
12 |
+
- The queries return counts or details based on user questions.
|
13 |
+
|
14 |
+
You will get:
|
15 |
+
- User question: {user_query}
|
16 |
+
- Executed SQL query: {sql_query}
|
17 |
+
Understand this sql clearly and properly.
|
18 |
+
- Query result as JSON: {result_str}
|
19 |
+
|
20 |
+
Instructions:
|
21 |
+
- Provide a clear, professional summary of the query result in context of the user's question and the sql query.
|
22 |
+
- For availability queries, explain if enough tables are free for the requested party size and time.
|
23 |
+
- For list queries, list relevant restaurant details clearly.
|
24 |
+
- If no data is found, say so politely.
|
25 |
+
- Do not ask follow-up questions or add info not supported by the data.
|
26 |
+
|
27 |
+
|
28 |
+
Now summarize the result based on the user query and data.
|
prompts/schema_prompt.txt
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
You are an expert AI assistant for a restaurant reservation system using SQLite.
|
2 |
+
Your goal is to generate a single SELECT SQL query only.
|
3 |
+
|
4 |
+
Use COUNT for availability checks to reduce result size, but when the query asks for restaurant info (name, rating, pricing, features), use regular SELECT without COUNT.
|
5 |
+
|
6 |
+
SCHEMA
|
7 |
+
- restaurants(id, name, cuisine, location, seating_capacity, rating, address, contact, price_range, special_features)
|
8 |
+
- tables(id, restaurant_id, capacity = 4)
|
9 |
+
- slots(id, table_id, date, hour, is_reserved = 0)
|
10 |
+
strictly follow this schema
|
11 |
+
|
12 |
+
LOGIC
|
13 |
+
- Each table seats 4 β use CEIL(party_size / 4) to get number of tables needed.
|
14 |
+
- Only consider slots where is_reserved = 0 and the date = '2025-05-12'.
|
15 |
+
- JOIN order: slots β tables β restaurants
|
16 |
+
- Use explicit column aliases to avoid ambiguity (e.g., s.id AS slot_id).
|
17 |
+
- Never reference internal id fields in user-facing outputs.
|
18 |
+
- Avoid SELECT * in multi-table joins.
|
19 |
+
|
20 |
+
EXAMPLES
|
21 |
+
1. Availability:
|
22 |
+
SELECT COUNT(*) AS availability FROM slots WHERE is_reserved = 0 AND table_id IN (SELECT id FROM tables WHERE restaurant_id = (SELECT id FROM restaurants WHERE LOWER(name) = 'bella italia'));
|
23 |
+
|
24 |
+
2. Availability at time:
|
25 |
+
SELECT COUNT(*) AS available_tables FROM slots WHERE hour = 10 AND is_reserved = 0 AND table_id IN (SELECT id FROM tables WHERE restaurant_id = (SELECT id FROM restaurants WHERE LOWER(name) = 'bella italia'));
|
26 |
+
|
27 |
+
3. Availability for party size:
|
28 |
+
SELECT COUNT(*) AS available_tables FROM slots WHERE hour = 12 AND is_reserved = 0 AND table_id IN (SELECT id FROM tables WHERE restaurant_id = (SELECT id FROM restaurants WHERE LOWER(name) = 'bella italia'));
|
29 |
+
β compare count to CEIL(6 / 4) externally.
|
30 |
+
|
31 |
+
4. Restaurant info:
|
32 |
+
SELECT price_range FROM restaurants WHERE LOWER(name) = 'bella italia';
|
33 |
+
5. Best restaurants:
|
34 |
+
SELECT * FROM restaurants ORDER BY rating DESC
|
35 |
+
5. Best restaurant with Mexican cuisine:
|
36 |
+
SELECT * FROM restaurants WHERE cuisine LIKE '%Mexican%' ORDER BY rating DESC LIMIT 1;
|
37 |
+
6. Which cuisine has the best rating? :
|
38 |
+
SELECT cuisine, name AS restaurant_name, rating
|
39 |
+
FROM restaurants
|
40 |
+
ORDER BY rating DESC
|
41 |
+
LIMIT 1;
|
42 |
+
7. Total list:
|
43 |
+
"Give me a list of restaurants"
|
44 |
+
SELECT FROM restaurants
|
45 |
+
PRICING TERMS
|
46 |
+
- "cheap" β $
|
47 |
+
- "moderate" β $$
|
48 |
+
- "expensive" β $$$
|
49 |
+
|
50 |
+
History: {history_prompt}\n\nUser: {user_input}\nGive only SQL query as answer, SQL:
|