EduDemo/src/components/VirtualGuardedNetworkTour.tsx

199 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react'
import vgn1 from '../assets/virtualguardednetwork1.png'
import vgn2 from '../assets/virtualguardednetwork2.png'
import './VirtualGuardedNetworkTour.css'
type TourStep = {
id: string
title: string
description: string
image: string
region: { leftPct: number; topPct: number; widthPct: number; heightPct: number }
popupPosition?: { leftPct?: number; topPct?: number; rightPct?: number; bottomPct?: number }
}
const vgnSteps: TourStep[] = [
{
id: 'overview',
title: 'Virtual Guarded Network (VGN) Overview',
description: 'VGN bölümüne hoş geldiniz! Zincirler, erişim, MFA politikaları ve logları buradan yönetebilirsiniz.',
image: vgn1,
region: { leftPct: 0, topPct: 0, widthPct: 100, heightPct: 100 },
popupPosition: { rightPct: 35, topPct: 60 },
},
{
id: 'chain-header',
title: 'Chain Header & Controls',
description: 'Zincirin adı, mod, MTU, DHCP ve routing gibi meta veriler bu başlıkta yer alır. Arama ve Add New ile yeni zincirler ekleyebilirsiniz.',
image: vgn1,
region: { leftPct: 15, topPct: 2, widthPct: 84, heightPct: 10 },
popupPosition: { rightPct: 2, topPct: 14 },
},
{
id: 'chain-flow',
title: 'Chain Flow',
description: 'Kullanıcı girişinden başlayıp, iç düğümler ve Internet Exit\'e giden görsel akış burada gösterilir.',
image: vgn1,
region: { leftPct: 15, topPct: 16, widthPct: 84, heightPct: 43 },
popupPosition: { leftPct: 15, bottomPct: 20 },
},
{
id: 'actions-and-policies',
title: 'Actions & Policies',
description: 'Sağ üstte yer alan VGN Chains, Access, MFA Policies ve Logs butonları ile politikaları ve günlükleri yönetebilirsiniz.',
image: vgn2,
region: { leftPct: 71, topPct: 7, widthPct: 24, heightPct: 7 },
popupPosition: { rightPct: 8, topPct: 16 },
},
]
type VirtualGuardedNetworkTourProps = {
autoStart?: boolean
onClose?: () => void
}
export default function VirtualGuardedNetworkTour({ autoStart = false, onClose }: VirtualGuardedNetworkTourProps) {
const [isModalOpen, setIsModalOpen] = useState(autoStart)
const [currentStep, setCurrentStep] = useState(0)
const [isTransitioning, setIsTransitioning] = useState(false)
const [isModalAnimating, setIsModalAnimating] = useState(false)
const [transitionDirection, setTransitionDirection] = useState<'next' | 'prev'>('next')
const handleStartTour = () => {
setIsModalOpen(true)
setCurrentStep(0)
}
const handleClose = () => {
setIsModalAnimating(true)
if (onClose) onClose()
setTimeout(() => {
setIsModalOpen(false)
setCurrentStep(0)
setIsTransitioning(false)
setTimeout(() => setIsModalAnimating(false), 50)
}, 250)
}
const handleNext = () => {
if (currentStep < vgnSteps.length - 1) {
setTransitionDirection('next')
setIsTransitioning(true)
setTimeout(() => {
setCurrentStep(currentStep + 1)
setTimeout(() => setIsTransitioning(false), 50)
}, 300)
}
}
const handlePrevious = () => {
if (currentStep > 0) {
setTransitionDirection('prev')
setIsTransitioning(true)
setTimeout(() => {
setCurrentStep(currentStep - 1)
setTimeout(() => setIsTransitioning(false), 50)
}, 300)
}
}
const handleStepClick = (stepIndex: number) => {
if (stepIndex !== currentStep) {
setTransitionDirection(stepIndex > currentStep ? 'next' : 'prev')
setIsTransitioning(true)
setTimeout(() => {
setCurrentStep(stepIndex)
setTimeout(() => setIsTransitioning(false), 50)
}, 300)
}
}
const activeStep = vgnSteps[currentStep]
if (!autoStart && !isModalOpen) {
return (
<div className="vgn-tour-container">
<div className="vgn-preview-section">
<div className="vgn-preview-image-wrapper">
<img src={vgn1} alt="VGN Preview" className="vgn-preview-image" />
<div className="vgn-preview-overlay">
<button className="start-tour-button" onClick={handleStartTour}>
<span className="button-icon"></span>
Start VGN Tour
</button>
</div>
</div>
</div>
</div>
)
}
return (
<>
<div className={`vgn-modal-backdrop ${isModalAnimating ? 'closing' : ''}`} onClick={handleClose}>
<div className={`vgn-modal-content ${isModalAnimating ? 'closing' : ''}`} onClick={(e) => e.stopPropagation()}>
<div className="vgn-modal-image-wrapper">
<img className="vgn-modal-image" src={activeStep.image} alt="VGN" />
{currentStep > 0 && (
<>
{activeStep.region.topPct > 0 && (
<div className={`vgn-overlay-top ${isTransitioning ? 'vgn-overlay-transitioning' : ''}`} style={{ height: `${activeStep.region.topPct}%` }} />
)}
{activeStep.region.leftPct > 0 && (
<div className={`vgn-overlay-left ${isTransitioning ? 'vgn-overlay-transitioning' : ''}`} style={{ top: `${activeStep.region.topPct}%`, width: `${activeStep.region.leftPct}%`, height: `${activeStep.region.heightPct}%` }} />
)}
{activeStep.region.leftPct + activeStep.region.widthPct < 100 && (
<div className={`vgn-overlay-right ${isTransitioning ? 'vgn-overlay-transitioning' : ''}`} style={{ top: `${activeStep.region.topPct}%`, left: `${activeStep.region.leftPct + activeStep.region.widthPct}%`, height: `${activeStep.region.heightPct}%` }} />
)}
{activeStep.region.topPct + activeStep.region.heightPct < 100 && (
<div className={`vgn-overlay-bottom ${isTransitioning ? 'vgn-overlay-transitioning' : ''}`} style={{ top: `${activeStep.region.topPct + activeStep.region.heightPct}%` }} />
)}
<div className={`vgn-highlight-border ${isTransitioning ? 'transitioning' : ''}`} style={{ left: `${activeStep.region.leftPct}%`, top: `${activeStep.region.topPct}%`, width: `${activeStep.region.widthPct}%`, height: `${activeStep.region.heightPct}%` }} />
</>
)}
<div
className={`vgn-step-popup ${isTransitioning ? `transitioning transition-${transitionDirection}` : ''} ${isModalAnimating ? 'closing' : ''}`}
style={{
...(activeStep.popupPosition?.leftPct && { left: `${activeStep.popupPosition.leftPct}%` }),
...(activeStep.popupPosition?.topPct && { top: `${activeStep.popupPosition.topPct}%` }),
...(activeStep.popupPosition?.rightPct && { right: `${activeStep.popupPosition.rightPct}%` }),
...(activeStep.popupPosition?.bottomPct && { bottom: `${activeStep.popupPosition.bottomPct}%` }),
}}
>
<div className="vgn-popup-animated-border" />
<button className="vgn-quit-button" onClick={handleClose}>×</button>
<div className="vgn-popup-content">
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '12px' }}>
<div style={{ background: '#b02f34', color: 'white', fontSize: '12px', padding: '4px 8px', borderRadius: '999px', fontWeight: 600 }}>
{currentStep + 1}/{vgnSteps.length}
</div>
<h3 style={{ margin: 0, fontSize: '18px', fontWeight: 600, color: '#f1f2f5' }}>{activeStep.title}</h3>
</div>
<p style={{ margin: 0, fontSize: '14px', lineHeight: 1.6, color: '#b9bfca' }}>{activeStep.description}</p>
<div style={{ display: 'flex', gap: '10px', marginTop: '20px', justifyContent: 'space-between', alignItems: 'center' }}>
<button onClick={handlePrevious} disabled={currentStep === 0} style={{ background: currentStep === 0 ? 'rgba(0,0,0,0.3)' : '#1a1d26', border: '1px solid #262a33', color: currentStep === 0 ? '#555' : '#b9bfca', padding: '8px 16px', borderRadius: '6px', cursor: currentStep === 0 ? 'not-allowed' : 'pointer', fontSize: '14px', fontWeight: 500, transition: 'all 200ms ease' }}> Previous</button>
<button onClick={currentStep === vgnSteps.length - 1 ? handleClose : handleNext} style={{ background: 'linear-gradient(135deg, #b02f34 0%, #8a252a 100%)', border: '1px solid #b02f34', color: 'white', padding: '8px 20px', borderRadius: '6px', cursor: 'pointer', fontSize: '14px', fontWeight: 600, transition: 'all 200ms ease' }}>{currentStep === vgnSteps.length - 1 ? 'Finish' : 'Next →'}</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div className={`vgn-tour-progress-bar ${isModalAnimating ? 'closing' : ''}`}>
{vgnSteps.map((step, index) => (
<div key={step.id} className={`vgn-progress-segment ${index < currentStep ? 'completed' : ''} ${index === currentStep ? 'active' : ''}`} onClick={() => handleStepClick(index)} title={step.title}>
<div className="vgn-progress-segment-fill" />
<div className={`vgn-progress-step-number ${index < currentStep ? 'completed' : ''} ${index === currentStep ? 'active' : ''}`}>{index + 1}</div>
</div>
))}
</div>
</>
)
}