Spaces:
Sleeping
Sleeping
File size: 3,763 Bytes
c6537a8 7a97213 c6537a8 7a97213 c6537a8 7a97213 c6537a8 7a97213 c6537a8 7a97213 c6537a8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | import React, { useState, useEffect } from 'react';
import Icon from './CanvasIcon';
import { MOD } from './platform';
const TOUR_KEY = 'canvas-tour-seen-musclegrowth-v1';
const STEPS = [
{
title: 'Welcome to your Fitness Canvas',
icon: 'shield',
body: 'This is your training workspace. Two views — Insights (highlights from your chats) and Workspace (a customizable dashboard of widgets). It starts empty so you can build it the way you want.',
},
{
title: 'Add widgets from the palette',
icon: 'plus',
body: `Click "Add widget" on the Workspace view, or hit ${MOD}+K and search. There are 30+ widgets — workouts, deadlines, tracking, notes, plus challenge widgets that push back on weak plans.`,
},
{
title: 'Make it yours',
icon: 'layout',
body: 'Drag widget headers to reorder. Click the size pill (S/M/L) to resize. Hover and click trash to remove. Layout and content auto-save to your browser.',
},
{
title: 'Try the challenge widgets',
icon: 'gavel',
body: 'Reviewer 2, Devil\'s Advocate, and Scope Realism are tuned to push back, not validate. They\'re where your program gets sharpened. Add them last — when you\'re ready for honest feedback.',
},
];
const CanvasWelcomeTour = ({ forceShow = false, onClose }) => {
const [step, setStep] = useState(0);
const [visible, setVisible] = useState(false);
useEffect(() => {
const seen = localStorage.getItem(TOUR_KEY);
if (forceShow || !seen) setVisible(true);
}, [forceShow]);
const dismiss = () => {
localStorage.setItem(TOUR_KEY, '1');
setVisible(false);
if (onClose) onClose();
};
if (!visible) return null;
const s = STEPS[step];
const isLast = step === STEPS.length - 1;
return (
<div className="canvas-modal-backdrop" onClick={(e) => { if (e.target === e.currentTarget) dismiss(); }}>
<div className="canvas-modal canvas-tour" onClick={(e) => e.stopPropagation()}>
<div className="modal-head">
<div className="modal-icon"><Icon name={s.icon} size={18}/></div>
<div style={{ flex: 1 }}>
<div className="modal-title">{s.title}</div>
<div className="modal-sub">Step {step + 1} of {STEPS.length}</div>
</div>
<button className="icon-btn" onClick={dismiss} title="Skip"><Icon name="x" size={16}/></button>
</div>
<div className="modal-body" style={{ minHeight: 80 }}>
<p style={{ margin: 0, fontSize: 13.5, lineHeight: 1.6, color: 'var(--canvas-text-2)' }}>{s.body}</p>
</div>
<div className="modal-foot" style={{ justifyContent: 'space-between' }}>
<div style={{ display: 'flex', gap: 6 }}>
{STEPS.map((_, i) => (
<span key={i}
onClick={() => setStep(i)}
style={{
width: 8, height: 8, borderRadius: '50%', cursor: 'pointer',
background: i === step ? 'var(--canvas-accent)' : 'var(--canvas-surface-3)',
transition: 'background .15s',
}}/>
))}
</div>
<div style={{ display: 'flex', gap: 8 }}>
{step > 0 && (
<button className="btn btn-ghost" onClick={() => setStep(s => s - 1)}>Back</button>
)}
{!isLast && (
<button className="btn btn-ghost" onClick={dismiss}>Skip</button>
)}
<button className="btn btn-primary" onClick={() => isLast ? dismiss() : setStep(s => s + 1)}>
{isLast ? <><Icon name="check" size={13}/>Get started</> : <>Next<Icon name="arrow" size={13}/></>}
</button>
</div>
</div>
</div>
</div>
);
};
export default CanvasWelcomeTour;
|