demeter
Autonomous Hydroponic Intelligencegit clone https://github.com/maydayv7/demeter
frontend/src/components/Onboarding.jsx (11408B)
1import React, { useState } from "react";
2import { useNavigate } from "react-router-dom";
3import {
4 Leaf,
5 LayoutGrid,
6 PlusCircle,
7 Bell,
8 CheckCircle2,
9 ChevronRight,
10 X,
11 Sparkles,
12 HelpCircle,
13} from "lucide-react";
14import { useSettings } from "../hooks/useSettings";
15import { useT } from "../hooks/useTranslation";
16
17const STEP_ICONS = [Leaf, LayoutGrid, PlusCircle, Bell, CheckCircle2, Sparkles];
18const STEP_COLORS = [
19 "var(--green)",
20 "var(--blue)",
21 "var(--green)",
22 "var(--red)",
23 "#f59e0b",
24 "var(--blue)",
25];
26const STEP_KEYS = [
27 { title: "onboarding_s1_title", desc: "onboarding_s1_desc" },
28 { title: "onboarding_s2_title", desc: "onboarding_s2_desc" },
29 { title: "onboarding_s3_title", desc: "onboarding_s3_desc" },
30 { title: "onboarding_s4_title", desc: "onboarding_s4_desc" },
31 { title: "onboarding_s5_title", desc: "onboarding_s5_desc" },
32 { title: "onboarding_s6_title", desc: "onboarding_s6_desc" },
33];
34
35export default function Onboarding({ onDone }) {
36 const [step, setStep] = useState(0);
37 const { update } = useSettings();
38 const { t, lang } = useT();
39 const navigate = useNavigate();
40
41 const total = STEP_KEYS.length;
42 const isLast = step === total - 1;
43 const Icon = STEP_ICONS[step];
44 const color = STEP_COLORS[step];
45 const { title: titleKey, desc: descKey } = STEP_KEYS[step];
46
47 const finish = () => {
48 update("onboardingDone", true);
49 onDone?.();
50 };
51
52 const goToAI = () => {
53 finish();
54 navigate("/intelligence");
55 };
56
57 const goToHelp = () => {
58 finish();
59 navigate("/help");
60 };
61
62 // AI Labels
63 const askAILabel = lang === "hi" ? "AI से पूछें →" : "Ask AI →";
64 const askAISubLabel =
65 lang === "hi"
66 ? "सवाल हैं? फार्म बुद्धिमत्ता AI से पूछें"
67 : "Have questions? Ask Farm Intelligence";
68
69 // Help Labels
70 const helpLabel = lang === "hi" ? "मदद और शब्दकोश →" : "Help & Glossary →";
71 const helpSubLabel =
72 lang === "hi"
73 ? "खेती के शब्द और AI के काम करने का तरीका समझें"
74 : "Understand farming terms and how AI works";
75
76 return (
77 /* Backdrop */
78 <div
79 style={{
80 position: "fixed",
81 inset: 0,
82 zIndex: 9999,
83 background: "rgba(0,0,0,0.65)",
84 display: "flex",
85 alignItems: "center",
86 justifyContent: "center",
87 padding: 24,
88 }}
89 >
90 {/* Modal */}
91 <div
92 className="animate-fade-up"
93 style={{
94 width: "100%",
95 maxWidth: 480,
96 borderRadius: 20,
97 background: "var(--bg-2)",
98 border: "1px solid var(--border)",
99 overflow: "hidden",
100 boxShadow: "0 24px 80px rgba(0,0,0,0.5)",
101 }}
102 >
103 {/* Header strip */}
104 <div
105 style={{
106 height: 4,
107 background: "var(--border)",
108 position: "relative",
109 }}
110 >
111 <div
112 style={{
113 position: "absolute",
114 left: 0,
115 top: 0,
116 height: "100%",
117 width: `${((step + 1) / total) * 100}%`,
118 background: color,
119 borderRadius: 4,
120 transition: "width 0.4s ease",
121 }}
122 />
123 </div>
124
125 {/* Top row */}
126 <div
127 style={{
128 display: "flex",
129 alignItems: "center",
130 justifyContent: "space-between",
131 padding: "16px 20px 0",
132 }}
133 >
134 <span
135 style={{
136 fontSize: 11,
137 fontFamily: "DM Mono, monospace",
138 color: "var(--text-3)",
139 }}
140 >
141 {t("onboarding_step", { n: step + 1, total })}
142 </span>
143 <button
144 onClick={finish}
145 style={{
146 background: "none",
147 border: "none",
148 cursor: "pointer",
149 color: "var(--text-3)",
150 display: "flex",
151 padding: 4,
152 }}
153 >
154 <X size={16} />
155 </button>
156 </div>
157
158 {/* Body */}
159 <div
160 style={{
161 padding: "24px 28px 28px",
162 display: "flex",
163 flexDirection: "column",
164 alignItems: "flex-start",
165 gap: 16,
166 }}
167 >
168 {/* Icon */}
169 <div
170 style={{
171 width: 56,
172 height: 56,
173 borderRadius: 16,
174 background: `${color}18`,
175 border: `1px solid ${color}35`,
176 display: "flex",
177 alignItems: "center",
178 justifyContent: "center",
179 }}
180 >
181 <Icon size={26} style={{ color }} />
182 </div>
183
184 {/* Title */}
185 <div
186 style={{
187 fontSize: 22,
188 fontWeight: 700,
189 color: "var(--text)",
190 lineHeight: 1.2,
191 }}
192 >
193 {t(titleKey)}
194 </div>
195
196 {/* Description */}
197 <div
198 style={{
199 fontSize: 14,
200 color: "var(--text-2)",
201 lineHeight: 1.75,
202 whiteSpace: "pre-line",
203 width: "100%",
204 }}
205 >
206 {t(descKey)}
207 </div>
208
209 {/* Help & AI Nudges */}
210 {isLast && (
211 <div
212 style={{
213 display: "flex",
214 flexDirection: "column",
215 gap: 10,
216 width: "100%",
217 }}
218 >
219 {/* Help Page Link */}
220 <div
221 style={{
222 width: "100%",
223 padding: "12px 14px",
224 borderRadius: 10,
225 background: "rgba(59,130,246,0.08)",
226 border: "1px solid rgba(59,130,246,0.25)",
227 display: "flex",
228 alignItems: "center",
229 gap: 10,
230 cursor: "pointer",
231 boxSizing: "border-box",
232 }}
233 onClick={goToHelp}
234 >
235 <div
236 style={{
237 width: 32,
238 height: 32,
239 borderRadius: 8,
240 background: "rgba(59,130,246,0.15)",
241 display: "flex",
242 alignItems: "center",
243 justifyContent: "center",
244 flexShrink: 0,
245 }}
246 >
247 <HelpCircle size={15} style={{ color: "var(--blue)" }} />
248 </div>
249 <div>
250 <div
251 style={{
252 fontSize: 12,
253 fontWeight: 700,
254 color: "var(--blue)",
255 marginBottom: 2,
256 }}
257 >
258 {helpLabel}
259 </div>
260 <div style={{ fontSize: 11, color: "var(--text-3)" }}>
261 {helpSubLabel}
262 </div>
263 </div>
264 <ChevronRight
265 size={14}
266 style={{ color: "var(--blue)", marginLeft: "auto" }}
267 />
268 </div>
269
270 {/* Ask AI Nudge */}
271 <div
272 style={{
273 width: "100%",
274 padding: "12px 14px",
275 borderRadius: 10,
276 background: "rgba(74,222,128,0.08)",
277 border: "1px solid rgba(74,222,128,0.25)",
278 display: "flex",
279 alignItems: "center",
280 gap: 10,
281 cursor: "pointer",
282 boxSizing: "border-box",
283 }}
284 onClick={goToAI}
285 >
286 <div
287 style={{
288 width: 32,
289 height: 32,
290 borderRadius: 8,
291 background: "rgba(74,222,128,0.15)",
292 display: "flex",
293 alignItems: "center",
294 justifyContent: "center",
295 flexShrink: 0,
296 }}
297 >
298 <Sparkles size={15} style={{ color: "var(--green)" }} />
299 </div>
300 <div>
301 <div
302 style={{
303 fontSize: 12,
304 fontWeight: 700,
305 color: "var(--green)",
306 marginBottom: 2,
307 }}
308 >
309 {askAILabel}
310 </div>
311 <div style={{ fontSize: 11, color: "var(--text-3)" }}>
312 {askAISubLabel}
313 </div>
314 </div>
315 <ChevronRight
316 size={14}
317 style={{ color: "var(--green)", marginLeft: "auto" }}
318 />
319 </div>
320 </div>
321 )}
322
323 {/* Step dots */}
324 <div
325 style={{
326 display: "flex",
327 gap: 6,
328 marginTop: 4,
329 alignSelf: "center",
330 }}
331 >
332 {STEP_KEYS.map((_, i) => (
333 <div
334 key={i}
335 onClick={() => setStep(i)}
336 style={{
337 width: i === step ? 18 : 7,
338 height: 7,
339 borderRadius: 4,
340 background: i === step ? color : "var(--border)",
341 cursor: "pointer",
342 transition: "all 0.3s",
343 }}
344 />
345 ))}
346 </div>
347
348 {/* Buttons */}
349 <div
350 style={{
351 display: "flex",
352 gap: 10,
353 width: "100%",
354 marginTop: 4,
355 }}
356 >
357 {!isLast && (
358 <button
359 onClick={finish}
360 style={{
361 flex: 1,
362 padding: "10px 0",
363 borderRadius: 10,
364 fontSize: 13,
365 fontWeight: 500,
366 background: "var(--surface)",
367 border: "1px solid var(--border)",
368 color: "var(--text-3)",
369 cursor: "pointer",
370 }}
371 >
372 {t("onboarding_skip")}
373 </button>
374 )}
375
376 <button
377 onClick={() => (isLast ? finish() : setStep((s) => s + 1))}
378 style={{
379 flex: isLast ? 1 : 2,
380 padding: "10px 0",
381 borderRadius: 10,
382 fontSize: 14,
383 fontWeight: 700,
384 background: color,
385 border: "none",
386 color: "#0c1a0e",
387 cursor: "pointer",
388 display: "flex",
389 alignItems: "center",
390 justifyContent: "center",
391 gap: 6,
392 }}
393 >
394 {isLast ? t("onboarding_finish") : t("onboarding_next")}
395 {!isLast && <ChevronRight size={14} />}
396 </button>
397 </div>
398 </div>
399 </div>
400 </div>
401 );
402}