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 (
+
+
+
+

+
+
+
+
+
+
+ )
+ }
+
+ return (
+ <>
+
+
e.stopPropagation()}>
+
+

+
+ {/* 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 (
+
+
+
+

+
+
+
+
+
+
+ )
+ }
+
+ return (
+ <>
+
+
e.stopPropagation()}>
+
+

+
+ {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}
+
+ ))}
+
+ >
+ )
+}
+
+