diff --git a/src/assets/securelink1.png b/src/assets/securelink1.png new file mode 100644 index 0000000..8b45204 Binary files /dev/null and b/src/assets/securelink1.png differ diff --git a/src/assets/securelink2.png b/src/assets/securelink2.png new file mode 100644 index 0000000..cca9029 Binary files /dev/null and b/src/assets/securelink2.png differ diff --git a/src/assets/securelink3.png b/src/assets/securelink3.png new file mode 100644 index 0000000..d09d7d3 Binary files /dev/null and b/src/assets/securelink3.png differ diff --git a/src/assets/virtualguardednetwork1.png b/src/assets/virtualguardednetwork1.png new file mode 100644 index 0000000..a968d7f Binary files /dev/null and b/src/assets/virtualguardednetwork1.png differ diff --git a/src/assets/virtualguardednetwork2.png b/src/assets/virtualguardednetwork2.png new file mode 100644 index 0000000..fbd07aa Binary files /dev/null and b/src/assets/virtualguardednetwork2.png differ diff --git a/src/components/AttackSurfaceTour.css b/src/components/AttackSurfaceTour.css index efd0c8c..6ba66ea 100644 --- a/src/components/AttackSurfaceTour.css +++ b/src/components/AttackSurfaceTour.css @@ -66,7 +66,7 @@ height: 100%; background-color: #0b0d10; display: flex; - align-items: center; + align-items: flex-start; justify-content: center; overflow: hidden; padding: 20px; @@ -80,17 +80,20 @@ .attack-surface-modal-image-wrapper { position: relative; max-width: 95vw; - max-height: 95vh; - width: fit-content; - height: fit-content; + max-height: calc(100vh - 100px); + width: auto; + height: auto; margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; } .attack-surface-modal-image { - width: 100%; - height: 100%; - max-width: 100%; - max-height: 100%; + width: auto; + height: auto; + max-width: 95vw; + max-height: calc(100vh - 100px); object-fit: contain; display: block; } @@ -166,7 +169,7 @@ background-color: rgba(0, 0, 0, 0.3); display: flex; gap: 2px; - z-index: 1001; + z-index: 1002; backdrop-filter: blur(10px); animation: attackSurfaceProgressBarFadeIn 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; } @@ -230,7 +233,7 @@ font-size: 14px; font-weight: 600; transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1); - z-index: 10; + z-index: 1003; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } diff --git a/src/components/AttackSurfaceTour.tsx b/src/components/AttackSurfaceTour.tsx index 43412aa..31950a5 100644 --- a/src/components/AttackSurfaceTour.tsx +++ b/src/components/AttackSurfaceTour.tsx @@ -118,10 +118,13 @@ export default function AttackSurfaceTour({ autoStart = false, onClose }: Attack if (currentStep < attackSurfaceSteps.length - 1) { setTransitionDirection('next') setIsTransitioning(true) + // Allow slide-out, then change step, keep transitioning for a tick to slide-in setTimeout(() => { setCurrentStep(currentStep + 1) - setIsTransitioning(false) - }, 500) + setTimeout(() => { + setIsTransitioning(false) + }, 50) + }, 300) } } @@ -131,8 +134,10 @@ export default function AttackSurfaceTour({ autoStart = false, onClose }: Attack setIsTransitioning(true) setTimeout(() => { setCurrentStep(currentStep - 1) - setIsTransitioning(false) - }, 500) + setTimeout(() => { + setIsTransitioning(false) + }, 50) + }, 300) } } @@ -142,8 +147,10 @@ export default function AttackSurfaceTour({ autoStart = false, onClose }: Attack setIsTransitioning(true) setTimeout(() => { setCurrentStep(stepIndex) - setIsTransitioning(false) - }, 500) + setTimeout(() => { + setIsTransitioning(false) + }, 50) + }, 300) } } diff --git a/src/components/DashboardTour.css b/src/components/DashboardTour.css index c23fd3b..c83f896 100644 --- a/src/components/DashboardTour.css +++ b/src/components/DashboardTour.css @@ -273,7 +273,7 @@ height: 100%; background-color: #0b0d10; display: flex; - align-items: center; + align-items: flex-start; justify-content: center; overflow: hidden; animation: modalContentSlideIn 300ms cubic-bezier(0.4, 0, 0.2, 1) forwards; @@ -290,17 +290,20 @@ .modal-image-wrapper { position: relative; max-width: 95vw; - max-height: 95vh; - width: fit-content; - height: fit-content; + max-height: calc(100vh - 100px); + width: auto; + height: auto; margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; } .modal-image { - width: 100%; - height: 100%; - max-width: 100%; - max-height: 100%; + width: auto; + height: auto; + max-width: 95vw; + max-height: calc(100vh - 100px); object-fit: contain; display: block; } @@ -529,7 +532,7 @@ background-color: rgba(0, 0, 0, 0.3); display: flex; gap: 2px; - z-index: 1001; + z-index: 1002; backdrop-filter: blur(10px); animation: progressBarFadeIn 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; } @@ -594,7 +597,7 @@ font-size: 14px; font-weight: 600; transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1); - z-index: 10; + z-index: 1003; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } diff --git a/src/components/DashboardTour.tsx b/src/components/DashboardTour.tsx index 596b068..7ef8222 100644 --- a/src/components/DashboardTour.tsx +++ b/src/components/DashboardTour.tsx @@ -2,8 +2,12 @@ import { useState } from 'react' import dashboardImg from '../assets/dashboard.jpeg' import guardpotImg from '../assets/guardpot.jpeg' import attackSurfaceImg from '../assets/attacksurface1.png' +import secureLinkImg from '../assets/securelink1.png' +import vgnImg from '../assets/virtualguardednetwork1.png' import GuardpotTour from './GuardpotTour' import AttackSurfaceTour from './AttackSurfaceTour' +import SecureLinkTour from './SecureLinkTour' +import VirtualGuardedNetworkTour from './VirtualGuardedNetworkTour' import './DashboardTour.css' type TourStep = { @@ -38,12 +42,13 @@ const dashboardSteps: TourStep[] = [ }, ] -const tourOptions = [ +type TourOption = { id: string; name: string; description: string; disabled?: boolean } +const tourOptions: TourOption[] = [ { id: 'dashboard', name: 'Dashboard', description: 'Dashboard overview and features' }, { id: 'guardpot', name: 'Guardpot', description: 'Guardpot management system' }, { id: 'attack-surface', name: 'Attack Surface', description: 'Attack surface analysis' }, - { id: 'secure-link', name: 'Secure Link', description: 'Secure link management', disabled: true }, - { id: 'virtual-guarded-network', name: 'Virtual Guarded Network', description: 'Virtual network configuration', disabled: true } + { id: 'secure-link', name: 'Secure Link', description: 'Secure link management' }, + { id: 'virtual-guarded-network', name: 'Virtual Guarded Network', description: 'Virtual network configuration' } ] export default function DashboardTour() { @@ -145,12 +150,16 @@ export default function DashboardTour() { selectedTour === 'dashboard' ? dashboardImg : selectedTour === 'guardpot' ? guardpotImg : selectedTour === 'attack-surface' ? attackSurfaceImg : + selectedTour === 'secure-link' ? secureLinkImg : + selectedTour === 'virtual-guarded-network' ? vgnImg : dashboardImg } alt={ selectedTour === 'dashboard' ? 'Guardpot Dashboard' : selectedTour === 'guardpot' ? 'Guardpot Management' : selectedTour === 'attack-surface' ? 'Attack Surface' : + selectedTour === 'secure-link' ? 'Secure Link' : + selectedTour === 'virtual-guarded-network' ? 'Virtual Guarded Network' : 'Tour Preview' } /> @@ -160,6 +169,8 @@ export default function DashboardTour() { {selectedTour === 'dashboard' && 'Start Dashboard Tour'} {selectedTour === 'guardpot' && 'Start Guardpot Tour'} {selectedTour === 'attack-surface' && 'Start Attack Surface Tour'} + {selectedTour === 'secure-link' && 'Start Secure Link Tour'} + {selectedTour === 'virtual-guarded-network' && 'Start VGN Tour'} {!selectedTour && 'Select a Tour'} @@ -181,6 +192,18 @@ export default function DashboardTour() { )} + {(isModalOpen || isClosing) && selectedTour === 'secure-link' && ( +
+ +
+ )} + + {(isModalOpen || isClosing) && selectedTour === 'virtual-guarded-network' && ( +
+ +
+ )} + {/* Modal - Dashboard */} {isModalOpen && selectedTour === 'dashboard' && ( <> diff --git a/src/components/GuardpotTour.css b/src/components/GuardpotTour.css index 395db29..40adada 100644 --- a/src/components/GuardpotTour.css +++ b/src/components/GuardpotTour.css @@ -66,7 +66,7 @@ height: 100%; background-color: #0b0d10; display: flex; - align-items: center; + align-items: flex-start; justify-content: center; overflow: hidden; padding: 20px; @@ -80,17 +80,20 @@ .guardpot-modal-image-wrapper { position: relative; max-width: 95vw; - max-height: 95vh; - width: fit-content; - height: fit-content; + max-height: calc(100vh - 100px); + width: auto; + height: auto; margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; } .guardpot-modal-image { - width: 100%; - height: 100%; - max-width: 100%; - max-height: 100%; + width: auto; + height: auto; + max-width: 95vw; + max-height: calc(100vh - 100px); object-fit: contain; display: block; } @@ -334,7 +337,7 @@ background-color: rgba(0, 0, 0, 0.3); display: flex; gap: 2px; - z-index: 1001; + z-index: 1002; backdrop-filter: blur(10px); animation: guardpotProgressBarFadeIn 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; } @@ -398,7 +401,7 @@ font-size: 14px; font-weight: 600; transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1); - z-index: 10; + z-index: 1003; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } diff --git a/src/components/SecureLinkTour.css b/src/components/SecureLinkTour.css new file mode 100644 index 0000000..ae966e1 --- /dev/null +++ b/src/components/SecureLinkTour.css @@ -0,0 +1,480 @@ +/* SecureLinkTour Component Styles */ + +/* Main Container */ +.secure-link-tour-container { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +/* Preview Section */ +.secure-link-preview-section { + width: 100%; + max-width: 800px; + margin: 0 auto; +} + +.secure-link-preview-image-wrapper { + position: relative; + width: 100%; + aspect-ratio: 16 / 9; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); +} + +.secure-link-preview-image { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.secure-link-preview-overlay { + position: absolute; + inset: 0; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.7), rgba(11, 13, 16, 0.85)); + display: flex; + align-items: center; + justify-content: center; + transition: background 300ms ease; +} + +.secure-link-preview-overlay:hover { + background: linear-gradient(135deg, rgba(0, 0, 0, 0.6), rgba(11, 13, 16, 0.75)); +} + +/* Modal */ +.secure-link-modal-backdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #0f0f12; + z-index: 1000; + animation: secureLinkModalBackdropFadeIn 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; + overflow: hidden; +} + +.secure-link-modal-backdrop.closing { + animation: secureLinkModalBackdropFadeOut 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; +} + +.secure-link-modal-content { + position: relative; + width: 100%; + height: 100%; + background-color: #0b0d10; + display: flex; + align-items: flex-start; + justify-content: center; + overflow: hidden; + padding: 10px 20px 20px 20px; + animation: secureLinkModalContentSlideIn 300ms cubic-bezier(0.4, 0, 0.2, 1) forwards; +} + +.secure-link-modal-content.closing { + animation: secureLinkModalContentSlideOut 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; +} + +.secure-link-modal-image-wrapper { + position: relative; + max-width: 95vw; + max-height: calc(100vh - 80px); + width: auto; + height: auto; + margin: 0 auto; + margin-top: 10px; + display: flex; + align-items: center; + justify-content: center; +} + +.secure-link-modal-image { + width: auto; + height: auto; + max-width: 95vw; + max-height: calc(100vh - 80px); + object-fit: contain; + display: block; +} + +/* Overlay System */ +.secure-link-overlay-top { + position: absolute; + top: 0; + left: 0; + right: 0; + background: rgba(0, 0, 0, 0.85); + pointer-events: none; + transition: all 500ms ease-in-out; + opacity: 1; +} + +.secure-link-overlay-left { + position: absolute; + left: 0; + background: rgba(0, 0, 0, 0.85); + pointer-events: none; + transition: all 500ms ease-in-out; + opacity: 1; +} + +.secure-link-overlay-right { + position: absolute; + right: 0; + background: rgba(0, 0, 0, 0.85); + pointer-events: none; + transition: all 500ms ease-in-out; + opacity: 1; +} + +.secure-link-overlay-bottom { + position: absolute; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.85); + pointer-events: none; + transition: all 500ms ease-in-out; + opacity: 1; +} + +.secure-link-overlay-transitioning { + opacity: 0.3; +} + +.secure-link-highlight-border { + position: absolute; + border: 2px solid #b02f34; + border-radius: 10px; + pointer-events: none; + box-shadow: 0 0 0 2px rgba(176, 47, 52, 0.3); + transition: all 500ms ease-in-out; + opacity: 1; + transform: scale(1); +} + +.secure-link-highlight-border.transitioning { + opacity: 0.5; + transform: scale(0.98); +} + +/* Progress Bar */ +.secure-link-tour-progress-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 8px; + background-color: rgba(0, 0, 0, 0.3); + display: flex; + gap: 2px; + z-index: 1002; + backdrop-filter: blur(10px); + animation: secureLinkProgressBarFadeIn 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; +} + +.secure-link-tour-progress-bar.closing { + animation: secureLinkProgressBarFadeOut 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; +} + +.secure-link-progress-segment { + flex: 1; + position: relative; + cursor: pointer; + overflow: visible; + transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1); + background-color: rgba(255, 255, 255, 0.1); +} + +.secure-link-progress-segment:hover { + background-color: rgba(255, 255, 255, 0.15); +} + +.secure-link-progress-segment-fill { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(90deg, #b02f34 0%, #d24449 100%); + transform: scaleX(0); + transform-origin: left; + transition: transform 500ms cubic-bezier(0.4, 0, 0.2, 1); +} + +.secure-link-progress-segment.completed .secure-link-progress-segment-fill { + transform: scaleX(1); +} + +.secure-link-progress-segment.active .secure-link-progress-segment-fill { + transform: scaleX(1); + animation: secureLinkProgressFillPulse 2s ease-in-out infinite; +} + +/* Progress Step Numbers */ +.secure-link-progress-step-number { + position: absolute; + bottom: 12px; + left: 50%; + transform: translateX(-50%); + width: 32px; + height: 32px; + border-radius: 50%; + background-color: #1a1d25; + border: 2px solid #262a33; + color: #6b7280; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: 600; + transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1); + z-index: 1003; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.secure-link-progress-segment:hover .secure-link-progress-step-number { + transform: translateX(-50%) scale(1.1); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); +} + +.secure-link-progress-step-number.completed { + background-color: #8e262a; + border-color: #b02f34; + color: #f1f2f5; + box-shadow: 0 2px 8px rgba(176, 47, 52, 0.3); +} + +.secure-link-progress-step-number.active { + background: linear-gradient(135deg, #b02f34 0%, #d24449 100%); + border-color: #d24449; + color: white; + box-shadow: 0 4px 16px rgba(176, 47, 52, 0.6); + animation: stepNumberPulse 2s infinite; +} + +/* Secure Link Step Popup - Animated */ +.secure-link-step-popup { + position: absolute; + background-color: #111319; + border: 1px solid #262a33; + border-radius: 12px; + padding: 20px; + color: #f1f2f5; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(176, 47, 52, 0.3); + min-width: 300px; + max-width: 400px; + animation: secureLinkPopupSlideIn 600ms ease-out forwards; + transition: all 500ms ease-in-out; +} + +.secure-link-step-popup.transitioning { + opacity: 0.3; + transition: all 500ms cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + +.secure-link-step-popup.transitioning.transition-next { + transform: translateX(80px) scale(0.95); +} + +.secure-link-step-popup.transitioning.transition-prev { + transform: translateX(-80px) scale(0.95); +} + +.secure-link-step-popup.closing { + opacity: 0; + transform: translateY(20px) scale(0.8); + transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Popup Animated Border */ +.secure-link-popup-animated-border { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 12px; + border: 2px solid transparent; + background: linear-gradient(45deg, rgba(176, 47, 52, 0.3), rgba(176, 47, 52, 0.1)); + background-clip: border-box; + animation: pulseGlow 2s infinite; + transition: all 400ms ease-in-out; + pointer-events: none; +} + +/* Quit Button */ +.secure-link-quit-button { + position: absolute; + top: 12px; + right: 12px; + width: 28px; + height: 28px; + background: rgba(0, 0, 0, 0.5); + border: 1px solid #262a33; + border-radius: 50%; + color: #b9bfca; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + font-weight: normal; + line-height: 1; + z-index: 20; + transition: all 200ms ease; +} + +.secure-link-quit-button:hover { + background: rgba(176, 47, 52, 0.8); + color: white; + transform: scale(1.1); +} + +/* Popup Content */ +.secure-link-popup-content { + position: relative; + z-index: 10; + transition: all 400ms ease-in-out; +} + +/* Animations */ +@keyframes secureLinkPopupSlideIn { + from { + opacity: 0; + transform: translateY(20px) scale(0.9); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +@keyframes pulseGlow { + 0% { box-shadow: 0 0 0 0 rgba(176, 47, 52, 0.4); } + 50% { box-shadow: 0 0 8px 4px rgba(176, 47, 52, 0.6); } + 100% { box-shadow: 0 0 0 0 rgba(176, 47, 52, 0.4); } +} + +@keyframes secureLinkModalBackdropFadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes secureLinkModalBackdropFadeOut { + from { opacity: 1; } + to { opacity: 0; } +} + +@keyframes secureLinkModalContentSlideIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes secureLinkModalContentSlideOut { + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.95); + } +} + +@keyframes secureLinkProgressBarFadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes secureLinkProgressBarFadeOut { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(10px); + } +} + +@keyframes secureLinkProgressFillPulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } +} + +@keyframes stepNumberPulse { + 0%, 100% { + transform: translateX(-50%) scale(1); + box-shadow: 0 4px 16px rgba(176, 47, 52, 0.6); + } + 50% { + transform: translateX(-50%) scale(1.05); + box-shadow: 0 6px 20px rgba(176, 47, 52, 0.8); + } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .secure-link-step-popup { + min-width: 250px; + max-width: 90vw; + padding: 16px; + } + + .secure-link-modal-content { + padding: 5px 10px 10px 10px; + } + + .secure-link-modal-image-wrapper { + margin-top: 5px; + max-height: calc(100vh - 60px); + } + + .secure-link-modal-image { + max-width: 98vw; + max-height: calc(100vh - 60px); + } +} + +@media (min-width: 769px) and (max-width: 1440px) { + .secure-link-modal-image-wrapper { + max-height: calc(100vh - 70px); + } + + .secure-link-modal-image { + max-height: calc(100vh - 70px); + } +} + +@media (min-width: 1441px) { + .secure-link-modal-image-wrapper { + max-height: calc(100vh - 90px); + } + + .secure-link-modal-image { + max-height: calc(100vh - 90px); + } +} + diff --git a/src/components/SecureLinkTour.tsx b/src/components/SecureLinkTour.tsx new file mode 100644 index 0000000..a0ec80a --- /dev/null +++ b/src/components/SecureLinkTour.tsx @@ -0,0 +1,342 @@ +import { useState } from 'react' +import secureLink1 from '../assets/securelink1.png' +import secureLink2 from '../assets/securelink2.png' +import secureLink3 from '../assets/securelink3.png' +import './SecureLinkTour.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 secureLinkSteps: TourStep[] = [ + { + id: 'overview', + title: 'Secure Link Overview', + description: 'Secure Link sayfasına hoş geldiniz! Bu sayfada Guardpot\'lar arasındaki ağ performansını ve bağlantı doğrulamasını görüntüleyebilirsiniz.', + image: secureLink1, + region: { leftPct: 0, topPct: 0, widthPct: 100, heightPct: 100 }, + popupPosition: { rightPct: 3, topPct: 5 }, + }, + { + id: 'network-topology', + title: 'Network Topology', + description: 'Bu interaktif ağ topolojisi haritası, tüm Guardpot bağlantılarınızı görsel olarak gösterir. Her düğüm bir Guardpot\'u temsil eder.', + image: secureLink1, + region: { leftPct: 15, topPct: 1, widthPct: 83, heightPct: 10 }, + popupPosition: { rightPct: 3, topPct: 13 }, + }, + { + id: 'network-stats', + title: 'Network Statistics', + description: 'Üst kısımda ağınızın sağlık durumunu gösteren istatistikler bulunur. Healthy, Errors, Running ve Total bağlantı sayılarını buradan takip edebilirsiniz.', + image: secureLink1, + region: { leftPct: 15, topPct: 14, widthPct: 84, heightPct: 75 }, + popupPosition: { rightPct: 70, topPct: 23 }, + }, + { + id: 'connection-details', + title: 'Connection Details', + description: 'Bağlantılar arasında tıklayarak detaylı ping, jitter ve paket kaybı istatistiklerini görüntüleyebilirsiniz. Grafikler gerçek zamanlı performansı gösterir.', + image: secureLink2, + region: { leftPct: 19, topPct: 1, widthPct: 74, heightPct: 93 }, + popupPosition: { leftPct: 2, topPct: 14 }, + }, + { + id: 'navigation-controls', + title: 'Navigation Controls', + description: 'Sol alt köşedeki kontroller ile ağ haritasını döndürebilir, yakınlaştırabilir ve istediğiniz açıdan görüntüleyebilirsiniz.', + image: secureLink1, + region: { leftPct: 92, topPct: 42, widthPct: 8, heightPct: 15 }, + popupPosition: { leftPct: 62, bottomPct: 45 }, + }, + { + id: 'graph-view', + title: 'Graph View Options', + description: 'Sağ üst köşede Graph, Link Checks ve List seçenekleri ile görünümü özelleştirebilirsiniz. Her görünüm farklı perspektifler sunar.', + image: secureLink3, + region: { leftPct: 28, topPct: 5, widthPct: 44, heightPct: 90 }, + popupPosition: { leftPct: 72, topPct: 14 }, + }, +] + +type SecureLinkTourProps = { + autoStart?: boolean + onClose?: () => void +} + +export default function SecureLinkTour({ autoStart = false, onClose }: SecureLinkTourProps) { + 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) + // Parent'ı hemen bilgilendir + if (onClose) { + onClose() + } + setTimeout(() => { + setIsModalOpen(false) + setCurrentStep(0) + setIsTransitioning(false) + setTimeout(() => { + setIsModalAnimating(false) + }, 50) + }, 250) + } + + const handleNext = () => { + if (currentStep < secureLinkSteps.length - 1) { + setTransitionDirection('next') + setIsTransitioning(true) + setTimeout(() => { + setCurrentStep(currentStep + 1) + setIsTransitioning(false) + }, 500) + } + } + + const handlePrevious = () => { + if (currentStep > 0) { + setTransitionDirection('prev') + setIsTransitioning(true) + setTimeout(() => { + setCurrentStep(currentStep - 1) + setIsTransitioning(false) + }, 500) + } + } + + const handleStepClick = (stepIndex: number) => { + if (stepIndex !== currentStep) { + setTransitionDirection(stepIndex > currentStep ? 'next' : 'prev') + setIsTransitioning(true) + setTimeout(() => { + setCurrentStep(stepIndex) + setIsTransitioning(false) + }, 500) + } + } + + const activeStep = secureLinkSteps[currentStep] + + if (!autoStart && !isModalOpen) { + return ( +
+
+
+ Secure Link Preview +
+ +
+
+
+
+ ) + } + + return ( + <> +
+
e.stopPropagation()}> +
+ Secure Link + + {/* Dark overlay with highlight window - only show if not first step */} + {currentStep > 0 && ( + <> + {/* Top overlay */} + {activeStep.region.topPct > 0 && ( +
+ )} + + {/* Left overlay */} + {activeStep.region.leftPct > 0 && ( +
+ )} + + {/* Right overlay */} + {activeStep.region.leftPct + activeStep.region.widthPct < 100 && ( +
+ )} + + {/* Bottom overlay */} + {activeStep.region.topPct + activeStep.region.heightPct < 100 && ( +
+ )} + + {/* Highlight border */} +
+ + )} + + {/* Step info popup */} +
+ {/* Subtle animated border */} +
+ + {/* Quit button */} + + + {/* Content with higher z-index */} +
+
+
+ {currentStep + 1}/{secureLinkSteps.length} +
+

+ {activeStep.title} +

+
+

+ {activeStep.description} +

+
+ + +
+
+
+
+
+
+ + {/* Progress Bar */} +
+ {secureLinkSteps.map((step, index) => ( +
handleStepClick(index)} + title={step.title} + > +
+
+ {index + 1} +
+
+ ))} +
+ + ) +} + diff --git a/src/components/VirtualGuardedNetworkTour.css b/src/components/VirtualGuardedNetworkTour.css new file mode 100644 index 0000000..bd4253c --- /dev/null +++ b/src/components/VirtualGuardedNetworkTour.css @@ -0,0 +1,88 @@ +/* VGN Tour Styles - mirrors other tours */ + +.vgn-modal-backdrop { + position: fixed; + inset: 0; + background: #0f0f12; + z-index: 1000; + animation: vgnBackdropIn 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; +} +.vgn-modal-backdrop.closing { animation: vgnBackdropOut 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; } + +.vgn-modal-content { + position: relative; + width: 100%; + height: 100%; + background: #0b0d10; + display: flex; + align-items: flex-start; + justify-content: center; + overflow: hidden; + padding: 20px; + animation: vgnContentIn 300ms cubic-bezier(0.4, 0, 0.2, 1) forwards; +} +.vgn-modal-content.closing { animation: vgnContentOut 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards; } + +.vgn-modal-image-wrapper { + position: relative; + max-width: 95vw; + max-height: calc(100vh - 100px); + width: auto; + height: auto; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; +} +.vgn-modal-image { width: auto; height: auto; max-width: 95vw; max-height: calc(100vh - 100px); object-fit: contain; display: block; } + +/* Overlays */ +.vgn-overlay-top, .vgn-overlay-left, .vgn-overlay-right, .vgn-overlay-bottom { + position: absolute; + background: rgba(0,0,0,0.85); + pointer-events: none; + transition: all 500ms ease-in-out; + opacity: 1; +} +.vgn-overlay-top { top: 0; left: 0; right: 0; } +.vgn-overlay-left { left: 0; } +.vgn-overlay-right { right: 0; } +.vgn-overlay-bottom { left: 0; right: 0; bottom: 0; } +.vgn-overlay-transitioning { opacity: 0.3; } + +.vgn-highlight-border { position: absolute; border: 2px solid #b02f34; border-radius: 10px; pointer-events: none; transition: all 500ms ease-in-out; box-shadow: 0 0 0 2px rgba(176,47,52,0.3); opacity: 1; transform: scale(1); } +.vgn-highlight-border.transitioning { opacity: .5; transform: scale(.98); } + +/* Step Popup */ +.vgn-step-popup { position: absolute; background:#111319; border:1px solid #262a33; border-radius:12px; padding:20px; color:#f1f2f5; min-width:300px; max-width:400px; box-shadow:0 10px 40px rgba(0,0,0,.5),0 0 0 1px rgba(176,47,52,.3); animation: vgnPopupIn 600ms ease-out forwards; transition: all 500ms ease-in-out; } +.vgn-step-popup.transitioning { opacity:.3; transition: all 500ms cubic-bezier(0.25,0.46,0.45,0.94); } +.vgn-step-popup.transitioning.transition-next { transform: translateX(80px) scale(.95); } +.vgn-step-popup.transitioning.transition-prev { transform: translateX(-80px) scale(.95); } +.vgn-step-popup.closing { opacity:0; transform: translateY(20px) scale(.8); transition: all 250ms cubic-bezier(0.4,0,0.2,1); } +.vgn-popup-animated-border { position:absolute; inset:0; border-radius:12px; border:2px solid transparent; background: linear-gradient(45deg, rgba(176,47,52,.3), rgba(176,47,52,.1)); background-clip:border-box; animation:pulseGlow 2s infinite; pointer-events:none; } +.vgn-popup-content { position:relative; z-index:10; } +.vgn-quit-button { position:absolute; top:12px; right:12px; width:28px; height:28px; background:rgba(0,0,0,.5); border:1px solid #262a33; border-radius:50%; color:#b9bfca; cursor:pointer; display:flex; align-items:center; justify-content:center; font-size:16px; line-height:1; z-index:20; transition:all 200ms ease; } +.vgn-quit-button:hover { background: rgba(176,47,52,.8); color:#fff; transform: scale(1.1); } + +/* Progress Bar */ +.vgn-tour-progress-bar { position: fixed; bottom: 0; left: 0; right: 0; height: 8px; background: rgba(0,0,0,.3); display:flex; gap:2px; z-index:1002; backdrop-filter: blur(10px); animation: vgnBarIn 250ms cubic-bezier(0.4,0,0.2,1) forwards; } +.vgn-tour-progress-bar.closing { animation: vgnBarOut 250ms cubic-bezier(0.4,0,0.2,1) forwards; } +.vgn-progress-segment { flex:1; position:relative; cursor:pointer; overflow:visible; transition: all 300ms cubic-bezier(0.4,0,0.2,1); background: rgba(255,255,255,.1); } +.vgn-progress-segment:hover { background: rgba(255,255,255,.15); } +.vgn-progress-segment-fill { position:absolute; inset:0; background: linear-gradient(90deg,#b02f34 0%,#d24449 100%); transform: scaleX(0); transform-origin:left; transition: transform 500ms cubic-bezier(0.4,0,0.2,1); } +.vgn-progress-segment.completed .vgn-progress-segment-fill, .vgn-progress-segment.active .vgn-progress-segment-fill { transform: scaleX(1); } +.vgn-progress-step-number { position:absolute; bottom:12px; left:50%; transform: translateX(-50%); width:32px; height:32px; border-radius:50%; background:#1a1d25; border:2px solid #262a33; color:#6b7280; display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:600; transition: all 250ms cubic-bezier(0.4,0,0.2,1); z-index:1003; box-shadow:0 2px 8px rgba(0,0,0,.3); } +.vgn-progress-segment:hover .vgn-progress-step-number { transform: translateX(-50%) scale(1.1); box-shadow: 0 4px 12px rgba(0,0,0,.4); } +.vgn-progress-step-number.completed { background:#8e262a; border-color:#b02f34; color:#f1f2f5; box-shadow: 0 2px 8px rgba(176,47,52,.3); } +.vgn-progress-step-number.active { background: linear-gradient(135deg,#b02f34 0%, #d24449 100%); border-color:#d24449; color:#fff; box-shadow: 0 4px 16px rgba(176,47,52,.6); animation: stepNumberPulse 2s infinite; } + +@keyframes pulseGlow { 0%{ box-shadow:0 0 0 0 rgba(176,47,52,.4);} 50%{ box-shadow:0 0 8px 4px rgba(176,47,52,.6);} 100%{ box-shadow:0 0 0 0 rgba(176,47,52,.4);} } +@keyframes vgnBackdropIn { from{opacity:0} to{opacity:1}} +@keyframes vgnBackdropOut { from{opacity:1} to{opacity:0}} +@keyframes vgnContentIn { from{opacity:0; transform: scale(.95)} to{opacity:1; transform: scale(1)}} +@keyframes vgnContentOut { from{opacity:1; transform: scale(1)} to{opacity:0; transform: scale(.95)}} +@keyframes vgnBarIn { from{opacity:0; transform: translateY(10px)} to{opacity:1; transform: translateY(0)}} +@keyframes vgnBarOut { from{opacity:1; transform: translateY(0)} to{opacity:0; transform: translateY(10px)}} +@keyframes vgnPopupIn { from{opacity:0; transform: translateY(20px) scale(.9)} to{opacity:1; transform: translateY(0) scale(1)}} + + diff --git a/src/components/VirtualGuardedNetworkTour.tsx b/src/components/VirtualGuardedNetworkTour.tsx new file mode 100644 index 0000000..1460025 --- /dev/null +++ b/src/components/VirtualGuardedNetworkTour.tsx @@ -0,0 +1,198 @@ +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 ( +
+
+
+ VGN Preview +
+ +
+
+
+
+ ) + } + + return ( + <> +
+
e.stopPropagation()}> +
+ VGN + + {currentStep > 0 && ( + <> + {activeStep.region.topPct > 0 && ( +
+ )} + {activeStep.region.leftPct > 0 && ( +
+ )} + {activeStep.region.leftPct + activeStep.region.widthPct < 100 && ( +
+ )} + {activeStep.region.topPct + activeStep.region.heightPct < 100 && ( +
+ )} +
+ + )} + +
+
+ + +
+
+
+ {currentStep + 1}/{vgnSteps.length} +
+

{activeStep.title}

+
+

{activeStep.description}

+
+ + +
+
+
+
+
+
+ +
+ {vgnSteps.map((step, index) => ( +
handleStepClick(index)} title={step.title}> +
+
{index + 1}
+
+ ))} +
+ + ) +} + +