NeonClary
Complete Cybersecurity -> MuscleGrowthAI theme swap and fix Profile
7a97213
Raw
History Blame Contribute Delete
3.76 kB
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;