diff --git a/src/assets/index.css b/src/assets/index.css index f8eb2dd864..c3788c8480 100644 --- a/src/assets/index.css +++ b/src/assets/index.css @@ -2,7 +2,6 @@ @import url("https://fonts.googleapis.com/css?family=Fira+Code:wght@300,400,500,600,700"); @import url("mermaid.css"); @import url("loader.css"); -@import url("newChatDesign.css"); @import url("remark-github-blockquote-alert/alert.css"); @import "github-markdown-css/github-markdown-light.css"; @@ -144,6 +143,7 @@ a { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); @@ -379,8 +379,8 @@ ul li::before { } /* Override dark Tremor background and border for tooltips/popovers only */ -[data-radix-popper-content-wrapper] > div, -.recharts-tooltip-wrapper > div { +[data-radix-popper-content-wrapper]>div, +.recharts-tooltip-wrapper>div { background-color: #1c1c1c !important; border-color: #454343 !important; color: white !important; @@ -412,11 +412,12 @@ ul li::before { .tremor-DonutChart-label { @apply fill-white !important; } + .text-right.whitespace-nowrap.text-tremor-content.dark\:text-dark-tremor-content { @apply text-white !important; } -.dark\:bg-dark-tremor-background{ +.dark\:bg-dark-tremor-background { @apply bg-gray-1400 !important; @apply border-gray-700 !important; } @@ -425,6 +426,6 @@ ul li::before { @apply text-gray-300 !important; } -.dark\:data-[selected]:bg-dark-tremor-background-muted{ +.dark\:data-[selected]:bg-dark-tremor-background-muted { @apply bg-gray-1250 !important; } \ No newline at end of file diff --git a/src/assets/newChatDesign.css b/src/assets/newChatDesign.css deleted file mode 100644 index 8c4b52aff2..0000000000 --- a/src/assets/newChatDesign.css +++ /dev/null @@ -1,1764 +0,0 @@ -nav { - display: flex; - justify-content: space-between; - align-items: center; -} - -.logo-section { - display: flex; - align-items: center; - gap: 15px; -} - -.logo { - font-size: 24px; - font-weight: 700; - color: #ffffff; - text-decoration: none; -} - -.tagline { - color: #888; - font-size: 14px; - font-weight: 500; -} - -.nav-links { - display: flex; - gap: 30px; - list-style: none; - align-items: center; -} - -.nav-links a { - color: #ffffff; - text-decoration: none; - font-weight: 500; - transition: color 0.3s ease; -} - -.nav-links a:hover { - color: #7ed321; -} - -.btn { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 12px 24px; - border-radius: 8px; - text-decoration: none; - font-weight: 600; - font-size: 14px; - transition: all 0.3s ease; - border: none; - cursor: pointer; -} - -.btn-primary { - background: #7ed321; - color: white; -} - -.btn-primary:hover { - background: #6bb31a; - transform: translateY(-1px); -} - -.btn-secondary { - background: transparent; - color: #ffffff; - border: 2px solid #7ed321; -} - -.btn-secondary:hover { - background: #7ed321; - color: #000000; - transform: translateY(-1px); -} - -/* Hero Section */ -.hero { - background: linear-gradient(135deg, #1a1a1a 0%, #0f0f0f 100%); - /* padding: 80px 0 0; */ - text-align: center; - position: relative; - overflow: hidden; -} - -.hero::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: radial-gradient(circle at 20% 80%, rgba(126, 211, 33, 0.1) 0%, transparent 50%), - radial-gradient(circle at 80% 20%, rgba(126, 211, 33, 0.05) 0%, transparent 50%); - pointer-events: none; -} - -.hero-content { - position: relative; - z-index: 1; -} - -.hero h1#production-grade-vibe-automation { - font-size: 2.8rem; - font-weight: 900; - color: #ffffff; - line-height: 1.4; -} - -.hero h1 .highlight { - background: linear-gradient(135deg, #7ed321, #9aff3d); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.hero-subtitle { - font-size: 1.1rem; - color: #cccccc; - margin-bottom: 40px; - max-width: 650px; - margin-left: auto; - margin-right: auto; -} - -.hero-cta { - display: flex; - gap: 16px; - justify-content: center; - margin-bottom: 60px; - flex-wrap: wrap; -} - -/* Demo Section */ -.demo-section { - background: rgba(26, 26, 26, 0.8); - border: 2px solid rgba(126, 211, 33, 0.3); - border-radius: 24px; - padding: 40px 20px; - /* max-width: 900px; */ - backdrop-filter: blur(10px); - box-shadow: 0 20px 60px rgba(126, 211, 33, 0.1); - text-align: center; -} - -.demo-main-title { - font-size: 2rem; - font-weight: 700; - color: #ffffff; - margin-bottom: 32px; - line-height: 1.2; -} - -.demo-input-container { - position: relative; - max-width: 700px; - margin: 0 auto 24px; -} - -.demo-input-modern { - width: 100%; - padding: 20px 60px 20px 20px; - border: 2px solid rgba(126, 211, 33, 0.3); - border-radius: 16px; - font-size: 1rem; - font-family: inherit; - resize: none; - min-height: 60px; - background: rgba(15, 15, 15, 0.9); - color: #888; - transition: all 0.3s ease; - overflow: hidden; -} - -.demo-input-modern:focus { - outline: none; - border-color: #7ed321; - box-shadow: 0 0 20px rgba(126, 211, 33, 0.2); - color: #ffffff; -} - -.demo-input-modern:focus::selection { - background: rgba(126, 211, 33, 0.3); -} - -.demo-input-modern::placeholder { - color: #888; -} - -.demo-send-btn { - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - width: 36px; - height: 36px; - background: #7ed321; - border: none; - border-radius: 8px; - color: #000; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s ease; -} - -.demo-send-btn:hover { - background: #6bb31a; - transform: translateY(-50%) scale(1.05); -} - -.demo-suggestions { - display: flex; - flex-wrap: wrap; - gap: 12px; - justify-content: center; - max-width: 700px; - margin: 0 auto; -} - -.suggestion-chip { - background: rgba(40, 40, 40, 0.8); - border: 1px solid rgba(126, 211, 33, 0.2); - border-radius: 24px; - padding: 10px 18px; - color: #cccccc; - font-size: 0.9rem; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - backdrop-filter: blur(10px); -} - -.suggestion-chip:hover { - background: rgba(126, 211, 33, 0.1); - border-color: #7ed321; - color: #ffffff; - transform: translateY(-2px); -} - -.demo-output { - background: linear-gradient(135deg, #0a0a0a, #1a1a1a); - color: #7ed321; - padding: 24px; - border-radius: 16px; - font-family: 'Monaco', 'Menlo', monospace; - font-size: 0.95rem; - line-height: 1.6; - border: 1px solid rgba(126, 211, 33, 0.2); - box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.3); -} - -/* Workflow Section */ -.workflow-section { - padding: 100px 0; - background: linear-gradient(135deg, #1a1a1a 0%, #0f0f0f 100%); - position: relative; - overflow: hidden; -} - -.workflow-section::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: radial-gradient(circle at 50% 50%, rgba(126, 211, 33, 0.05) 0%, transparent 70%); - pointer-events: none; -} - -.workflow-steps { - display: flex; - align-items: flex-start; - justify-content: space-between; - margin: 60px 0 40px; - position: relative; - z-index: 2; -} - -.workflow-step { - flex: 1; - text-align: center; - max-width: 200px; - position: relative; -} - -.step-number { - position: absolute; - top: -10px; - right: -10px; - width: 32px; - height: 32px; - background: linear-gradient(135deg, #7ed321, #9aff3d); - color: #000; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-weight: 700; - font-size: 0.9rem; - box-shadow: 0 4px 12px rgba(126, 211, 33, 0.4); - z-index: 3; -} - -.step-icon { - width: 80px; - height: 80px; - background: linear-gradient(135deg, rgba(126, 211, 33, 0.2), rgba(126, 211, 33, 0.1)); - border: 2px solid rgba(126, 211, 33, 0.3); - border-radius: 20px; - display: flex; - align-items: center; - justify-content: center; - font-size: 2rem; - margin: 0 auto 20px; - transition: all 0.4s ease; - backdrop-filter: blur(10px); - position: relative; -} - -.workflow-step:hover .step-icon { - background: linear-gradient(135deg, rgba(126, 211, 33, 0.3), rgba(126, 211, 33, 0.2)); - border-color: #7ed321; - transform: translateY(-8px); - box-shadow: 0 12px 30px rgba(126, 211, 33, 0.3); -} - -.step-title { - font-size: 1.1rem; - font-weight: 700; - color: #ffffff; - margin-bottom: 12px; - line-height: 1.3; -} - -.step-description { - color: #cccccc; - font-size: 0.9rem; - line-height: 1.5; -} - -.workflow-connector { - display: flex; - align-items: center; - justify-content: center; - margin: 0 20px; - position: relative; - top: 40px; -} - -.connector-line { - width: 60px; - height: 2px; - background: linear-gradient(90deg, rgba(126, 211, 33, 0.3), rgba(126, 211, 33, 0.6), rgba(126, 211, 33, 0.3)); - position: relative; -} - -.connector-line::before { - content: ''; - position: absolute; - top: 50%; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, transparent, #7ed321, transparent); - transform: translateY(-50%); - animation: flowPulse 2s ease-in-out infinite; -} - -.connector-arrow { - color: #7ed321; - font-size: 1.2rem; - font-weight: bold; - margin-left: -8px; - animation: arrowBounce 2s ease-in-out infinite; -} - -.workflow-cta { - text-align: center; - margin-top: 60px; -} - -@keyframes flowPulse { - 0%, 100% { opacity: 0.3; } - 50% { opacity: 1; } -} - -@keyframes arrowBounce { - 0%, 100% { transform: translateX(0); } - 50% { transform: translateX(4px); } -} - -/* Step Examples */ -.step-example { - margin-top: 20px; - background: rgba(15, 15, 15, 0.8); - border: 1px solid rgba(126, 211, 33, 0.2); - border-radius: 12px; - padding: 16px; - backdrop-filter: blur(10px); -} - -/* Chat Interface */ -.example-chat { - display: flex; - flex-direction: column; - gap: 12px; -} - -.chat-message { - display: flex; - align-items: flex-start; - gap: 10px; -} - -.chat-message.user { - flex-direction: row-reverse; -} - -.chat-message.user .message-content { - background: rgba(126, 211, 33, 0.2); - border: 1px solid rgba(126, 211, 33, 0.3); -} - -.chat-message.assistant .message-content { - background: rgba(40, 40, 40, 0.8); - border: 1px solid rgba(255, 255, 255, 0.1); -} - -.message-content { - max-width: 85%; - padding: 10px 14px; - border-radius: 16px; - font-size: 0.85rem; - line-height: 1.4; - color: #ffffff; -} - -/* View Toggle */ -.view-toggle { - display: flex; - background: rgba(40, 40, 40, 0.6); - border-radius: 8px; - padding: 4px; - margin-bottom: 12px; - border: 1px solid rgba(126, 211, 33, 0.2); -} - -.toggle-option { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - gap: 6px; - padding: 8px 12px; - border-radius: 6px; - font-size: 0.8rem; - font-weight: 600; - color: #cccccc; - cursor: pointer; - transition: all 0.3s ease; -} - -.toggle-option.active { - background: rgba(126, 211, 33, 0.2); - color: #7ed321; - border: 1px solid rgba(126, 211, 33, 0.3); -} - -.toggle-icon { - font-size: 0.9rem; -} - -/* View Content */ -.view-content { - display: none; - min-height: 100px; -} - -.view-content.active { - display: block; -} - -/* Code Preview */ -.code-preview { - background: rgba(10, 10, 10, 0.8); - border: 1px solid rgba(126, 211, 33, 0.3); - border-radius: 8px; - padding: 12px; - font-family: 'Monaco', 'Menlo', monospace; - font-size: 0.75rem; - line-height: 1.4; -} - -.code-line { - color: #7ed321; - margin-bottom: 2px; -} - -/* Visual Workflow */ -.visual-workflow { - display: flex; - align-items: center; - justify-content: center; - flex-wrap: wrap; - gap: 8px; - padding: 16px 8px; -} - -.workflow-node { - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; - padding: 8px; - background: rgba(126, 211, 33, 0.1); - border: 1px solid rgba(126, 211, 33, 0.3); - border-radius: 8px; - min-width: 60px; - transition: all 0.3s ease; -} - -.workflow-node:hover { - background: rgba(126, 211, 33, 0.2); - transform: scale(1.05); -} - -.workflow-node.trigger { - border-color: #4CAF50; - background: rgba(76, 175, 80, 0.1); -} - -.workflow-node.action { - border-color: #2196F3; - background: rgba(33, 150, 243, 0.1); -} - -.workflow-node.condition { - border-color: #FF9800; - background: rgba(255, 152, 0, 0.1); -} - -.node-icon { - font-size: 1.2rem; -} - -.node-label { - font-size: 0.7rem; - font-weight: 600; - color: #ffffff; - text-align: center; - line-height: 1.2; -} - -.workflow-arrow { - color: #7ed321; - font-weight: bold; - font-size: 1rem; - margin: 0 4px; -} - -/* Deployment Status */ -.deployment-status { - display: flex; - flex-direction: column; - gap: 8px; -} - -.status-item { - display: flex; - align-items: center; - gap: 8px; - font-size: 0.8rem; - color: #ffffff; -} - -.status-indicator { - width: 8px; - height: 8px; - border-radius: 50%; - flex-shrink: 0; -} - -.status-indicator.success { - background: #4CAF50; - box-shadow: 0 0 8px rgba(76, 175, 80, 0.4); -} - -.status-indicator.monitoring { - background: #7ed321; - animation: pulse 2s ease-in-out infinite; -} - -@keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.6; } -} - -/* Workflow Benefits */ -.workflow-benefits { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 30px; - margin: 60px 0 40px; - padding: 40px 0; - border-top: 1px solid rgba(126, 211, 33, 0.2); -} - -.benefit-highlight { - display: flex; - align-items: flex-start; - gap: 16px; - padding: 24px; - background: rgba(26, 26, 26, 0.6); - border: 1px solid rgba(126, 211, 33, 0.2); - border-radius: 16px; - transition: all 0.3s ease; - backdrop-filter: blur(10px); -} - -.benefit-highlight:hover { - border-color: #7ed321; - background: rgba(126, 211, 33, 0.05); - transform: translateY(-4px); -} - -.highlight-icon { - width: 48px; - height: 48px; - background: linear-gradient(135deg, #7ed321, #9aff3d); - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - flex-shrink: 0; - box-shadow: 0 4px 16px rgba(126, 211, 33, 0.3); -} - -.highlight-content h4 { - font-size: 1.1rem; - font-weight: 700; - color: #ffffff; - margin-bottom: 8px; - line-height: 1.3; -} - -.highlight-content p { - color: #cccccc; - font-size: 0.95rem; - line-height: 1.5; - margin: 0; -} - -/* Feature Grid */ -.features-section { - padding: 100px 0; - background: linear-gradient(135deg, #1a1a1a 0%, #0f0f0f 100%); -} - -/* Platform Benefits Section */ -.platform-benefits-section { - padding: 100px 0; - background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 100%); -} - -/* Benefits Grid - Simplified */ -.benefits-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 30px; - margin-top: 50px; -} - -.benefit-card { - background: linear-gradient(135deg, rgba(26, 26, 26, 0.8), rgba(15, 15, 15, 0.9)); - border: 2px solid rgba(126, 211, 33, 0.2); - border-radius: 20px; - padding: 32px; - text-align: center; - transition: all 0.4s ease; - backdrop-filter: blur(10px); -} - -.benefit-card:hover { - border-color: #7ed321; - transform: translateY(-8px); - box-shadow: 0 20px 60px rgba(126, 211, 33, 0.2); - background: linear-gradient(135deg, rgba(126, 211, 33, 0.1), rgba(26, 26, 26, 0.9)); -} - -.benefit-icon { - width: 64px; - height: 64px; - background: linear-gradient(135deg, #7ed321, #9aff3d); - border-radius: 16px; - display: flex; - align-items: center; - justify-content: center; - font-size: 2rem; - margin: 0 auto 20px; - box-shadow: 0 8px 24px rgba(126, 211, 33, 0.3); -} - -.benefit-card h3 { - font-size: 1.4rem; - font-weight: 700; - color: #ffffff; - margin-bottom: 16px; - line-height: 1.3; -} - -.benefit-card p { - color: #cccccc; - line-height: 1.6; - font-size: 1rem; - margin: 0; -} - -/* Bottom CTA - Simplified */ -.bottom-cta { - text-align: center; - margin-top: 80px; - padding: 40px; - background: linear-gradient(135deg, rgba(26, 26, 26, 0.6), rgba(15, 15, 15, 0.8)); - border: 2px solid rgba(126, 211, 33, 0.3); - border-radius: 24px; - backdrop-filter: blur(10px); -} - -.bottom-cta h3 { - font-size: 2rem; - font-weight: 800; - color: #ffffff; - margin-bottom: 12px; -} - -.bottom-cta p { - font-size: 1.1rem; - color: #cccccc; - margin-bottom: 32px; -} - -.cta-buttons { - display: flex; - gap: 16px; - justify-content: center; - flex-wrap: wrap; -} - -/* Comparison Section */ -.comparison-section { - margin-top: 60px; -} - -.comparison-block { - margin-bottom: 80px; -} - -.comparison-header { - text-align: center; - margin-bottom: 50px; -} - -.comparison-icon { - width: 80px; - height: 80px; - margin: 0 auto 20px; - background: linear-gradient(135deg, #7ed321, #9aff3d); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 2.5rem; - box-shadow: 0 10px 30px rgba(126, 211, 33, 0.3); -} - -.comparison-icon.vs-nocode { - background: linear-gradient(135deg, #FF6B6B, #FF8E8E); - box-shadow: 0 10px 30px rgba(255, 107, 107, 0.3); -} - -.comparison-icon.vs-code { - background: linear-gradient(135deg, #4ECDC4, #6ED5CD); - box-shadow: 0 10px 30px rgba(78, 205, 196, 0.3); -} - -.comparison-header h3 { - font-size: 2.2rem; - font-weight: 800; - color: #ffffff; - margin-bottom: 8px; -} - -.comparison-subtitle { - font-size: 1.1rem; - color: #cccccc; - font-style: italic; -} - -.comparison-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); - gap: 30px; -} - -.comparison-item { - background: linear-gradient(135deg, rgba(26, 26, 26, 0.8), rgba(15, 15, 15, 0.9)); - border: 2px solid rgba(126, 211, 33, 0.2); - border-radius: 20px; - padding: 32px; - transition: all 0.4s ease; - backdrop-filter: blur(10px); -} - -.comparison-item:hover { - border-color: #7ed321; - transform: translateY(-8px); - box-shadow: 0 20px 60px rgba(126, 211, 33, 0.2); - background: linear-gradient(135deg, rgba(126, 211, 33, 0.1), rgba(26, 26, 26, 0.9)); -} - -.comparison-item-header { - display: flex; - align-items: center; - gap: 16px; - margin-bottom: 20px; -} - -.item-icon { - width: 48px; - height: 48px; - background: linear-gradient(135deg, #7ed321, #9aff3d); - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - box-shadow: 0 4px 16px rgba(126, 211, 33, 0.3); -} - -.comparison-item-header h4 { - font-size: 1.3rem; - font-weight: 700; - color: #ffffff; - margin: 0; -} - -.comparison-item p { - color: #cccccc; - line-height: 1.7; - font-size: 1rem; - margin-bottom: 20px; -} - -/* Code Examples */ -.code-example { - background: rgba(10, 10, 10, 0.9); - border: 1px solid rgba(126, 211, 33, 0.3); - border-radius: 12px; - overflow: hidden; - font-family: 'Monaco', 'Menlo', monospace; - font-size: 0.85rem; -} - -.code-header { - background: rgba(126, 211, 33, 0.1); - border-bottom: 1px solid rgba(126, 211, 33, 0.2); - padding: 8px 16px; - display: flex; - justify-content: space-between; - align-items: center; -} - -.code-lang { - color: #7ed321; - font-weight: 600; - font-size: 0.75rem; -} - -.code-label { - color: #cccccc; - font-size: 0.75rem; -} - -.code-content { - padding: 16px; - color: #ffffff; - line-height: 1.5; -} - -.code-comment { - color: #888; -} - -.code-keyword { - color: #7ed321; - font-weight: 600; -} - -.code-string { - color: #FFD93D; -} - -/* Durability Stats */ -.durability-stats { - display: flex; - gap: 20px; - justify-content: space-around; -} - -.stat-item { - text-align: center; -} - -.stat-number { - font-size: 1.8rem; - font-weight: 800; - color: #7ed321; - line-height: 1; -} - -.stat-label { - font-size: 0.8rem; - color: #cccccc; - margin-top: 4px; -} - -/* AI Example */ -.ai-example { - background: rgba(15, 15, 15, 0.8); - border-radius: 12px; - padding: 16px; - border-left: 4px solid #7ed321; -} - -.ai-prompt { - color: #ffffff; - margin-bottom: 8px; - font-size: 0.9rem; -} - -.ai-response { - color: #7ed321; - font-size: 0.9rem; - font-style: italic; -} - -/* Dev Tools List */ -.dev-tools-list { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 12px; -} - -.tool-item { - display: flex; - align-items: center; - gap: 8px; - color: #ffffff; - font-size: 0.9rem; -} - -.tool-icon { - font-size: 1rem; -} - -/* Infrastructure Comparison */ -.infrastructure-comparison { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; -} - -.infra-option { - background: rgba(15, 15, 15, 0.6); - border-radius: 8px; - padding: 16px; - border: 1px solid rgba(255, 255, 255, 0.1); -} - -.infra-option.custom { - border-color: rgba(255, 107, 107, 0.3); -} - -.infra-option.autokitteh { - border-color: rgba(126, 211, 33, 0.3); -} - -.infra-header { - font-weight: 700; - color: #ffffff; - margin-bottom: 12px; - text-align: center; - font-size: 0.9rem; -} - -.infra-items { - display: flex; - flex-direction: column; - gap: 6px; -} - -.infra-item { - font-size: 0.8rem; - line-height: 1.3; -} - -.infra-item.negative { - color: #ff6b6b; -} - -.infra-item.positive { - color: #7ed321; -} - -/* Integrations Grid */ -.integrations-grid { - display: flex; - flex-direction: column; - gap: 16px; -} - -.integration-category { - background: rgba(15, 15, 15, 0.6); - border-radius: 8px; - padding: 12px; - border: 1px solid rgba(126, 211, 33, 0.2); -} - -.category-title { - font-size: 0.8rem; - font-weight: 700; - color: #7ed321; - margin-bottom: 8px; -} - -.integration-items { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.integration-badge { - background: rgba(126, 211, 33, 0.1); - border: 1px solid rgba(126, 211, 33, 0.3); - border-radius: 12px; - padding: 4px 8px; - font-size: 0.7rem; - color: #ffffff; - font-weight: 500; -} - -/* Security Features */ -.security-features { - display: flex; - flex-direction: column; - gap: 12px; -} - -.security-row { - display: flex; - gap: 16px; -} - -.security-item { - flex: 1; - display: flex; - align-items: center; - gap: 8px; - color: #ffffff; - font-size: 0.85rem; -} - -.security-icon { - font-size: 1rem; - color: #7ed321; -} - -/* Observability Dashboard */ -.observability-dashboard { - background: rgba(15, 15, 15, 0.8); - border-radius: 12px; - padding: 16px; - border: 1px solid rgba(126, 211, 33, 0.2); -} - -.dashboard-metrics { - display: flex; - gap: 12px; - margin-bottom: 12px; -} - -.metric-card { - flex: 1; - background: rgba(126, 211, 33, 0.1); - border-radius: 8px; - padding: 12px; - text-align: center; -} - -.metric-value { - font-size: 1.4rem; - font-weight: 700; - color: #7ed321; - line-height: 1; -} - -.metric-label { - font-size: 0.7rem; - color: #cccccc; - margin-top: 4px; -} - -.dashboard-features { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.feature-chip { - background: rgba(40, 40, 40, 0.8); - border: 1px solid rgba(126, 211, 33, 0.2); - border-radius: 16px; - padding: 4px 12px; - font-size: 0.75rem; - color: #cccccc; -} - -/* Comparison Divider */ -.comparison-divider { - height: 2px; - background: linear-gradient(90deg, transparent, rgba(126, 211, 33, 0.3), transparent); - margin: 60px 0; - position: relative; -} - -.comparison-divider::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 8px; - height: 8px; - background: #7ed321; - border-radius: 50%; - box-shadow: 0 0 20px rgba(126, 211, 33, 0.6); -} - -/* Comparison CTA */ -.comparison-cta { - background: linear-gradient(135deg, rgba(26, 26, 26, 0.8), rgba(15, 15, 15, 0.9)); - border: 2px solid rgba(126, 211, 33, 0.3); - border-radius: 24px; - padding: 40px; - margin-top: 60px; - text-align: center; - backdrop-filter: blur(10px); - box-shadow: 0 20px 60px rgba(126, 211, 33, 0.1); -} - -.cta-content { - margin-bottom: 32px; -} - -.cta-content h3 { - font-size: 2rem; - font-weight: 800; - color: #ffffff; - margin-bottom: 12px; -} - -.cta-content p { - font-size: 1.1rem; - color: #cccccc; - max-width: 600px; - margin: 0 auto; -} - -.cta-actions { - display: flex; - gap: 16px; - justify-content: center; - flex-wrap: wrap; -} - -.section-title { - font-size: 2.8rem; - font-weight: 900; - text-align: center; - margin-bottom: 20px; - color: #ffffff; -} - -.section-subtitle { - text-align: center; - font-size: 1.2rem; - color: #cccccc; - margin-bottom: 60px; - max-width: 600px; - margin-left: auto; - margin-right: auto; -} - -.features-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 40px; - margin-bottom: 60px; -} - -.feature-card { - background: linear-gradient(135deg, rgba(26, 26, 26, 0.8), rgba(15, 15, 15, 0.9)); - border: 2px solid rgba(126, 211, 33, 0.2); - border-radius: 20px; - padding: 40px; - transition: all 0.4s ease; - backdrop-filter: blur(10px); -} - -.feature-card:hover { - border-color: #7ed321; - transform: translateY(-8px); - box-shadow: 0 20px 60px rgba(126, 211, 33, 0.2); - background: linear-gradient(135deg, rgba(126, 211, 33, 0.1), rgba(26, 26, 26, 0.9)); -} - -.feature-header { - display: flex; - align-items: center; - gap: 16px; - margin-bottom: 20px; -} - -.feature-icon { - width: 56px; - height: 56px; - background: linear-gradient(135deg, #7ed321, #9aff3d); - border-radius: 16px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.8rem; - box-shadow: 0 8px 24px rgba(126, 211, 33, 0.3); -} - -.feature-title { - font-size: 1.3rem; - font-weight: 700; - color: #ffffff; -} - -.feature-card p { - color: #cccccc; - line-height: 1.7; - font-size: 1rem; -} - -/* Benefits List */ -.benefits-list { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 30px; - margin-bottom: 60px; -} - -.benefit-item { - display: flex; - align-items: flex-start; - gap: 12px; -} - -.check-icon { - width: 24px; - height: 24px; - background: #7ed321; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: bold; - flex-shrink: 0; - margin-top: 2px; -} - -.benefit-text { - color: #ffffff; - font-weight: 600; -} - -.benefit-description { - color: #cccccc; - font-size: 0.95rem; - margin-top: 4px; -} - -/* Use Cases */ -.use-cases-section { - padding: 100px 0; - background: linear-gradient(135deg, #1a1a1a 0%, #0f0f0f 100%); -} - -.use-cases-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); - gap: 30px; -} - -.use-case-card { - background: linear-gradient(135deg, rgba(26, 26, 26, 0.8), rgba(15, 15, 15, 0.9)); - border: 2px solid rgba(126, 211, 33, 0.2); - border-radius: 20px; - padding: 32px; - transition: all 0.4s ease; - backdrop-filter: blur(10px); -} - -.use-case-card:hover { - border-color: #7ed321; - transform: translateY(-8px); - box-shadow: 0 20px 60px rgba(126, 211, 33, 0.2); - background: linear-gradient(135deg, rgba(126, 211, 33, 0.1), rgba(26, 26, 26, 0.9)); -} - -.use-case-title { - font-size: 1.2rem; - font-weight: 700; - color: #7ed321; - margin-bottom: 8px; -} - -.use-case-subtitle { - color: #cccccc; - font-style: italic; - margin-bottom: 16px; -} - -.use-case-example { - background: rgba(15, 15, 15, 0.8); - border-left: 4px solid #7ed321; - padding: 20px; - border-radius: 0 12px 12px 0; - font-family: 'Monaco', 'Menlo', monospace; - font-size: 0.9rem; - color: #cccccc; - line-height: 1.6; - box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.2); -} - -/* Deployment Options */ -.deployment-section { - padding: 100px 0; - background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 100%); -} - -.deployment-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 40px; - margin-top: 40px; -} - -.deployment-card { - background: linear-gradient(135deg, rgba(26, 26, 26, 0.8), rgba(15, 15, 15, 0.9)); - border: 2px solid rgba(126, 211, 33, 0.2); - border-radius: 20px; - padding: 40px; - text-align: center; - transition: all 0.4s ease; - backdrop-filter: blur(10px); -} - -.deployment-card:hover { - border-color: #7ed321; - transform: translateY(-8px); - box-shadow: 0 20px 60px rgba(126, 211, 33, 0.2); - background: linear-gradient(135deg, rgba(126, 211, 33, 0.1), rgba(26, 26, 26, 0.9)); -} - -.deployment-icon { - width: 72px; - height: 72px; - background: linear-gradient(135deg, #7ed321, #9aff3d); - border-radius: 20px; - display: flex; - align-items: center; - justify-content: center; - font-size: 2.2rem; - margin: 0 auto 20px; - box-shadow: 0 10px 30px rgba(126, 211, 33, 0.3); -} - -.deployment-card h3 { - font-size: 1.6rem; - font-weight: 700; - margin-bottom: 12px; - color: #ffffff; -} - -.deployment-card p { - color: #cccccc; - margin-bottom: 24px; - line-height: 1.7; -} - -.deployment-features { - list-style: none; - text-align: left; - margin-bottom: 32px; -} - -.deployment-features li { - display: flex; - align-items: center; - gap: 12px; - margin-bottom: 12px; - color: #ffffff; - font-size: 0.95rem; -} - -.deployment-features .check { - color: #7ed321; - font-weight: bold; - font-size: 1.1rem; -} - -/* Screenshots Section */ -.screenshots-section { - padding: 100px 0; - background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 100%); -} - -.screenshot-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); - gap: 40px; - margin-top: 40px; -} - -.screenshot-card { - background: linear-gradient(135deg, rgba(26, 26, 26, 0.8), rgba(15, 15, 15, 0.9)); - border: 2px solid rgba(126, 211, 33, 0.2); - border-radius: 20px; - overflow: hidden; - transition: all 0.4s ease; - backdrop-filter: blur(10px); -} - -.screenshot-card:hover { - border-color: #7ed321; - transform: translateY(-8px); - box-shadow: 0 20px 60px rgba(126, 211, 33, 0.2); -} - -.screenshot-image { - width: 100%; - height: 220px; - background: linear-gradient(135deg, #1a1a1a, #0f0f0f); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - border-bottom: 1px solid rgba(126, 211, 33, 0.2); -} - -.screenshot-content { - padding: 24px; -} - -.screenshot-title { - font-size: 1.2rem; - font-weight: 700; - color: #ffffff; - margin-bottom: 8px; -} - -.screenshot-description { - color: #cccccc; - font-size: 0.95rem; - line-height: 1.6; -} - -/* CTA Section */ -.cta-section { - padding: 100px 0; - background: #1a1a1a; - color: white; - text-align: center; -} - -.cta-section h2 { - font-size: 2.5rem; - font-weight: 800; - margin-bottom: 20px; -} - -.cta-section p { - font-size: 1.1rem; - color: #ccc; - margin-bottom: 40px; - max-width: 500px; - margin-left: auto; - margin-right: auto; -} - -.cta-buttons { - display: flex; - gap: 16px; - justify-content: center; - flex-wrap: wrap; -} - -/* Footer */ -footer { - background: #f8f9fa; - padding: 40px 0; - text-align: center; - border-top: 1px solid #e9ecef; -} - -.footer-content { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 20px; -} - -.footer-links { - display: flex; - gap: 30px; - list-style: none; -} - -.footer-links a { - color: #666; - text-decoration: none; - font-size: 0.9rem; -} - -.footer-links a:hover { - color: #7ed321; -} - -/* Responsive */ -@media (max-width: 768px) { - .hero h1 { - font-size: 2rem; - } - - .demo-main-title { - font-size: 1.5rem; - } - - .demo-section { - padding: 0 20px; - } - - .demo-input-modern { - font-size: 0.9rem; - padding: 18px 50px 18px 18px; - } - - .demo-send-btn { - width: 32px; - height: 32px; - right: 10px; - } - - .suggestion-chip { - font-size: 0.85rem; - padding: 8px 14px; - } - - .workflow-steps { - flex-direction: column; - align-items: center; - gap: 40px; - } - - .workflow-connector { - transform: rotate(90deg); - margin: 0; - top: 0; - } - - .connector-line { - width: 40px; - } - - .connector-arrow { - transform: rotate(90deg); - margin-left: -6px; - } - - .workflow-step { - max-width: 280px; - } - - .workflow-steps { - flex-direction: column; - align-items: center; - gap: 40px; - } - - .workflow-connector { - transform: rotate(90deg); - margin: 0; - top: 0; - } - - .connector-line { - width: 40px; - } - - .connector-arrow { - transform: rotate(90deg); - margin-left: -6px; - } - - .visual-workflow { - flex-direction: column; - gap: 12px; - } - - .workflow-arrow { - transform: rotate(90deg); - margin: 4px 0; - } - - .workflow-benefits { - grid-template-columns: 1fr; - gap: 20px; - margin: 40px 0 30px; - } - - .benefit-highlight { - padding: 20px; - } - - .highlight-icon { - width: 40px; - height: 40px; - font-size: 1.3rem; - } - - .step-example { - padding: 12px; - } - - .message-content { - max-width: 90%; - padding: 8px 12px; - font-size: 0.8rem; - } - - .toggle-option { - padding: 6px 8px; - font-size: 0.75rem; - } - - .code-preview { - padding: 10px; - font-size: 0.7rem; - } - - .node-label { - font-size: 0.65rem; - } - - /* Responsive Benefits Grid */ - .benefits-grid { - grid-template-columns: 1fr; - gap: 20px; - margin-top: 40px; - } - - .benefit-card { - padding: 24px; - } - - .benefit-icon { - width: 56px; - height: 56px; - font-size: 1.8rem; - } - - .benefit-card h3 { - font-size: 1.2rem; - } - - .benefit-card p { - font-size: 0.95rem; - } - - .bottom-cta { - margin-top: 60px; - padding: 30px 20px; - } - - .bottom-cta h3 { - font-size: 1.6rem; - } - - .bottom-cta p { - font-size: 1rem; - } - - .cta-buttons { - flex-direction: column; - align-items: center; - } - - - - .features-grid, - .deployment-grid, - .screenshot-grid { - grid-template-columns: 1fr; - } - - .hero-cta, - .cta-buttons { - flex-direction: column; - align-items: center; - } - - .btn { - width: 100%; - max-width: 300px; - justify-content: center; - } - - .nav-links { - display: none; - } - - .footer-content { - flex-direction: column; - text-align: center; - } -} - -/* Animations */ -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.fade-in { - animation: fadeInUp 0.8s ease forwards; -} \ No newline at end of file diff --git a/src/assets/templates/kittehub.zip b/src/assets/templates/kittehub.zip index 443427ae80..ac1bb75494 100644 Binary files a/src/assets/templates/kittehub.zip and b/src/assets/templates/kittehub.zip differ diff --git a/src/components/molecules/index.ts b/src/components/molecules/index.ts index 1a92e36c99..75a55dd127 100644 --- a/src/components/molecules/index.ts +++ b/src/components/molecules/index.ts @@ -12,7 +12,6 @@ export { Drawer } from "@components/molecules/drawer"; export { EmptyTableAddButton } from "@components/molecules/emptyTableAddButton"; export { IdCopyButton } from "@components/molecules/idCopyButton"; export { ImageMotion } from "@components/molecules/imageMotion"; -export { ProjectsMenu } from "@components/molecules/projectsMenu"; export { Modal } from "@components/molecules/modal"; export { NavigationButton } from "@components/molecules/navigationButton"; export { PopoverTrigger } from "@components/molecules/popover"; diff --git a/src/components/molecules/projectsMenu/index.ts b/src/components/molecules/projectsMenu/index.ts deleted file mode 100644 index 85149a4eb4..0000000000 --- a/src/components/molecules/projectsMenu/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ProjectsMenu } from "@components/molecules/projectsMenu/projectsMenu"; diff --git a/src/components/molecules/projectsMenu/projectsMenu.tsx b/src/components/molecules/projectsMenu/projectsMenu.tsx deleted file mode 100644 index 31b31f9f3c..0000000000 --- a/src/components/molecules/projectsMenu/projectsMenu.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import React, { useEffect, useState } from "react"; - -import { AnimatePresence, motion } from "motion/react"; -import { useTranslation } from "react-i18next"; -import { useNavigate, useParams } from "react-router-dom"; - -import { SidebarHrefMenu } from "@enums/components"; -import { MenuProps } from "@interfaces/components"; -import { LoggerService } from "@services/logger.service"; -import { descopeProjectId, namespaces } from "@src/constants"; -import { Project } from "@type/models"; -import { cn } from "@utilities"; - -import { useOrganizationStore, useProjectStore, useSharedBetweenProjectsStore, useToastStore } from "@store"; - -import { Button, IconSvg, Tooltip } from "@components/atoms"; -import { PopoverListWrapper, PopoverListContent, PopoverListTrigger } from "@components/molecules/popover"; - -import { NewProject, ProjectsIcon } from "@assets/image"; - -export const ProjectsMenu = ({ className, isOpen = false }: MenuProps) => { - const { t } = useTranslation(["menu", "errors"]); - const { getProjectsList, projectsList } = useProjectStore(); - const navigate = useNavigate(); - const { projectId } = useParams(); - const addToast = useToastStore((state) => state.addToast); - const [sortedProjectsList, setSortedProjectsList] = useState([]); - const { user } = useOrganizationStore(); - const { drawers, settingsPath } = useSharedBetweenProjectsStore(); - - useEffect(() => { - const sortedProjects = projectsList.slice().sort((a, b) => a.name.localeCompare(b.name)); - setSortedProjectsList(sortedProjects); - }, [projectsList]); - - const animateVariant = { - hidden: { opacity: 0, width: 0 }, - visible: { opacity: 1, transition: { duration: 0.35, ease: "easeOut" as const }, width: "auto" }, - }; - - const fetchProjects = async () => { - const { error } = await getProjectsList(); - if (error) { - addToast({ - message: t("projectsListFetchFailed"), - type: "error", - }); - - LoggerService.error( - namespaces.ui.menu, - t("projectsListFetchFailedExtended", { - error, - }) - ); - } - }; - - useEffect(() => { - if (!descopeProjectId) { - fetchProjects(); - return; - } - if (user) { - fetchProjects(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - ); -}; diff --git a/src/components/organisms/sidebar/components/desktopMenuSidebar.tsx b/src/components/organisms/sidebar/components/desktopMenuSidebar.tsx new file mode 100644 index 0000000000..3385e6d328 --- /dev/null +++ b/src/components/organisms/sidebar/components/desktopMenuSidebar.tsx @@ -0,0 +1,65 @@ +import { Suspense, useMemo } from "react"; + +import { SidebarLogo } from "./logo"; +import { ConnectionsMenuItem } from "./menuItems/connectionsMenuItem"; +import { EventsMenuItem } from "./menuItems/eventsMenuItem"; +import { IntroMenuItem } from "./menuItems/introMenuItem"; +import { SystemLogMenuItem } from "./menuItems/systemLogMenuItem"; +import { SidebarMenuToggle } from "./menuToggle"; +import { ProjectsMenu } from "./projectsMenu"; +import { SidebarMenuItemProps } from "./sidebar.types"; +import { SidebarUserSection } from "./userSection"; +import { cn } from "@src/utilities"; + +import { Loader } from "@components/atoms"; +import { UserFeedbackForm } from "@components/organisms"; + +interface DesktopSidebarProps { + isFeedbackOpen: boolean; + isOpen: boolean; + menuItemProps: SidebarMenuItemProps; + onCloseFeedbackForm: () => void; + onOpenFeedbackForm: () => void; + onToggle: () => void; +} + +export const DesktopSidebar = ({ + isFeedbackOpen, + isOpen, + menuItemProps, + onCloseFeedbackForm, + onOpenFeedbackForm, + onToggle, +}: DesktopSidebarProps) => { + const rootClassName = useMemo( + () => cn("relative z-30 flex h-full items-start", { "z-50": isFeedbackOpen }), + [isFeedbackOpen] + ); + + return ( + }> +
+
+
+ + + + +
+ +
+ + + + +
+ +
+
+
+ ); +}; diff --git a/src/components/organisms/sidebar/components/index.ts b/src/components/organisms/sidebar/components/index.ts new file mode 100644 index 0000000000..cd7fcaa7a1 --- /dev/null +++ b/src/components/organisms/sidebar/components/index.ts @@ -0,0 +1,13 @@ +export { ConnectionsMenuItem } from "./menuItems/connectionsMenuItem"; +export { EventsMenuItem } from "./menuItems/eventsMenuItem"; +export { IntroMenuItem } from "./menuItems/introMenuItem"; +export { SystemLogMenuItem } from "./menuItems/systemLogMenuItem"; + +export { DesktopSidebar } from "./desktopMenuSidebar"; +export { MobileSidebar } from "./mobileTopMenuBar"; +export { ProjectsMenu } from "./projectsMenu/projectsMenu"; +export { SidebarLogo } from "./logo"; +export { SidebarMenuItem } from "./menuItem"; +export { SidebarMenuToggle } from "./menuToggle"; +export { SidebarUserSection } from "./userSection"; +export { sidebarAnimateVariant, type SidebarMenuItemProps } from "./sidebar.types"; diff --git a/src/components/organisms/sidebar/components/logo.tsx b/src/components/organisms/sidebar/components/logo.tsx new file mode 100644 index 0000000000..0c4d946ed4 --- /dev/null +++ b/src/components/organisms/sidebar/components/logo.tsx @@ -0,0 +1,58 @@ +import { AnimatePresence, motion } from "motion/react"; +import { useNavigate } from "react-router-dom"; + +import { sidebarAnimateVariant } from "./sidebar.types"; + +import { Button } from "@components/atoms"; + +import { IconLogo, IconLogoName } from "@assets/image"; + +interface SidebarLogoProps { + isMobile: boolean; + isOpen: boolean; + showName?: boolean; +} + +export const SidebarLogo = ({ isMobile, isOpen, showName = true }: SidebarLogoProps) => { + const navigate = useNavigate(); + + const handleLogoClick = () => { + navigate("/"); + }; + + if (isMobile && !isOpen) { + return ( + + ); + } + + if (isMobile && isOpen) { + return ( + + ); + } + + return ( + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItem.tsx b/src/components/organisms/sidebar/components/menuItem.tsx new file mode 100644 index 0000000000..e11c25b367 --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItem.tsx @@ -0,0 +1,69 @@ +import { ReactNode } from "react"; + +import { AnimatePresence, motion } from "motion/react"; + +import { sidebarAnimateVariant, SidebarMenuItemProps } from "./sidebar.types"; +import { SvgIconType } from "@interfaces/components/icon.interface"; +import { cn } from "@src/utilities"; + +import { Button, IconSvg, Tooltip } from "@components/atoms"; + +interface MenuItemProps extends SidebarMenuItemProps { + ariaLabel: string; + children?: ReactNode; + href?: string; + icon: SvgIconType; + iconClassName?: string; + label: string; + onClick?: () => void; + className?: string; +} + +export const SidebarMenuItem = ({ + ariaLabel, + children, + href, + icon, + iconClassName = "fill-gray-1100", + className, + isMobile, + isOpen, + label, + mobileMenuItemClass, + onClick, +}: MenuItemProps) => { + if (isMobile) { + return ( + + ); + } + + const itemClassName = cn("w-full gap-1.5 p-0.5 hover:bg-green-200", className); + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItems/connectionsMenuItem.tsx b/src/components/organisms/sidebar/components/menuItems/connectionsMenuItem.tsx new file mode 100644 index 0000000000..3870e70d19 --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/connectionsMenuItem.tsx @@ -0,0 +1,49 @@ +import { AnimatePresence, motion } from "motion/react"; +import { useTranslation } from "react-i18next"; +import { LuUnplug } from "react-icons/lu"; + +import { sidebarAnimateVariant, SidebarMenuItemProps } from "../sidebar.types"; +import { featureFlags } from "@constants"; + +import { Button, Tooltip } from "@components/atoms"; + +export const ConnectionsMenuItem = ({ isMobile, isOpen, mobileMenuItemClass }: SidebarMenuItemProps) => { + const { t } = useTranslation("sidebar"); + + if (featureFlags.hideOrgConnections) { + return null; + } + + if (isMobile) { + return ( + + ); + } + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItems/eventsMenuItem.tsx b/src/components/organisms/sidebar/components/menuItems/eventsMenuItem.tsx new file mode 100644 index 0000000000..c4bba8f073 --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/eventsMenuItem.tsx @@ -0,0 +1,25 @@ +import { useTranslation } from "react-i18next"; + +import { SidebarMenuItem } from "../menuItem"; +import { SidebarMenuItemProps } from "../sidebar.types"; + +import { IconSvg } from "@components/atoms"; + +import { EventsFlag } from "@assets/image/icons"; + +export const EventsMenuItem = (props: SidebarMenuItemProps) => { + const { t } = useTranslation("sidebar"); + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItems/index.ts b/src/components/organisms/sidebar/components/menuItems/index.ts new file mode 100644 index 0000000000..cebd8a88de --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/index.ts @@ -0,0 +1,4 @@ +export { ConnectionsMenuItem } from "./connectionsMenuItem"; +export { EventsMenuItem } from "./eventsMenuItem"; +export { IntroMenuItem } from "./introMenuItem"; +export { SystemLogMenuItem } from "./systemLogMenuItem"; diff --git a/src/components/organisms/sidebar/components/menuItems/introMenuItem.tsx b/src/components/organisms/sidebar/components/menuItems/introMenuItem.tsx new file mode 100644 index 0000000000..a877d3c5d8 --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/introMenuItem.tsx @@ -0,0 +1,25 @@ +import { useTranslation } from "react-i18next"; + +import { SidebarMenuItem } from "../menuItem"; +import { SidebarMenuItemProps } from "../sidebar.types"; + +import { IconSvg } from "@components/atoms"; + +import { CircleQuestionIcon } from "@assets/image/icons/sidebar"; + +export const IntroMenuItem = (props: SidebarMenuItemProps) => { + const { t } = useTranslation("sidebar"); + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItems/systemLogMenuItem.tsx b/src/components/organisms/sidebar/components/menuItems/systemLogMenuItem.tsx new file mode 100644 index 0000000000..8077b6f63d --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/systemLogMenuItem.tsx @@ -0,0 +1,83 @@ +import { AnimatePresence, motion } from "motion/react"; +import { useTranslation } from "react-i18next"; + +import { sidebarAnimateVariant, SidebarMenuItemProps } from "../sidebar.types"; + +import { useLoggerStore } from "@store"; + +import { Badge, Button, IconSvg, Tooltip } from "@components/atoms"; + +import { FileIcon } from "@assets/image/icons/sidebar"; + +interface SystemLogMenuItemProps extends SidebarMenuItemProps { + onClose?: () => void; +} + +export const SystemLogMenuItem = ({ isMobile, isOpen, mobileMenuItemClass, onClose }: SystemLogMenuItemProps) => { + const { t } = useTranslation("sidebar"); + const { isNewLogs, setSystemLogHeight, setNewLogs, lastLogType, systemLogHeight } = useLoggerStore(); + + const toggleSystemLogHeight = () => { + setNewLogs(false); + setSystemLogHeight(systemLogHeight < 1 ? 20 : 0); + }; + + const handleClick = () => { + toggleSystemLogHeight(); + onClose?.(); + }; + + if (isMobile) { + return ( + + ); + } + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuToggle.tsx b/src/components/organisms/sidebar/components/menuToggle.tsx new file mode 100644 index 0000000000..e390664bef --- /dev/null +++ b/src/components/organisms/sidebar/components/menuToggle.tsx @@ -0,0 +1,76 @@ +import { useMemo } from "react"; + +import { AnimatePresence, motion } from "motion/react"; +import { useTranslation } from "react-i18next"; +import { LuX } from "react-icons/lu"; + +import { sidebarAnimateVariant } from "./sidebar.types"; +import { cn } from "@src/utilities"; + +import { Button } from "@components/atoms"; +import { MenuToggle } from "@components/atoms/menuToggle"; + +interface SidebarMenuToggleProps { + isMobile: boolean; + isOpen: boolean; + onToggle: () => void; +} + +export const SidebarMenuToggle = ({ isMobile, isOpen, onToggle }: SidebarMenuToggleProps) => { + const { t } = useTranslation("sidebar"); + const btnClassName = useMemo(() => cn("mt-7 w-full p-0 hover:bg-green-200", { "pr-2": isOpen }), [isOpen]); + + if (isMobile && !isOpen) { + return ( + + ); + } + + if (isMobile && isOpen) { + return ( + + + + ); + } + + return ( + + ); +}; diff --git a/src/components/organisms/sidebar/components/mobileTopMenuBar.tsx b/src/components/organisms/sidebar/components/mobileTopMenuBar.tsx new file mode 100644 index 0000000000..fef8c00abc --- /dev/null +++ b/src/components/organisms/sidebar/components/mobileTopMenuBar.tsx @@ -0,0 +1,64 @@ +import { Suspense } from "react"; + +import { SidebarLogo } from "./logo"; +import { ConnectionsMenuItem } from "./menuItems/connectionsMenuItem"; +import { EventsMenuItem } from "./menuItems/eventsMenuItem"; +import { IntroMenuItem } from "./menuItems/introMenuItem"; +import { SystemLogMenuItem } from "./menuItems/systemLogMenuItem"; +import { SidebarMenuToggle } from "./menuToggle"; +import { ProjectsMenu } from "./projectsMenu"; +import { SidebarMenuItemProps } from "./sidebar.types"; +import { SidebarUserSection } from "./userSection"; + +import { Loader } from "@components/atoms"; + +interface MobileSidebarProps { + isOpen: boolean; + menuItemProps: SidebarMenuItemProps; + onClose: () => void; + onOpenFeedbackForm: () => void; + onToggle: () => void; +} + +export const MobileSidebar = ({ isOpen, menuItemProps, onClose, onOpenFeedbackForm, onToggle }: MobileSidebarProps) => { + return ( + }> +
+ + +
+ + {isOpen ? ( + <> +
e.key === "Escape" && onClose()} + role="button" + tabIndex={0} + /> +
+
+
+ + +
+ +
+ + + + +
+
+
+ + ) : null} + + ); +}; diff --git a/src/components/organisms/sidebar/components/projectsMenu/index.ts b/src/components/organisms/sidebar/components/projectsMenu/index.ts new file mode 100644 index 0000000000..722b8649da --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/index.ts @@ -0,0 +1,4 @@ +export { MyProjectsPopover } from "./myProjectsPopover"; +export { NewProjectButton } from "./newProjectButton"; +export { ProjectsMenu } from "./projectsMenu"; +export { useProjectsMenu } from "./useProjectsMenu"; diff --git a/src/components/organisms/sidebar/components/projectsMenu/myProjectsPopover.tsx b/src/components/organisms/sidebar/components/projectsMenu/myProjectsPopover.tsx new file mode 100644 index 0000000000..6644e309a7 --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/myProjectsPopover.tsx @@ -0,0 +1,73 @@ +import { AnimatePresence, motion } from "motion/react"; + +import { sidebarAnimateVariant } from "../sidebar.types"; +import { Project } from "@type/models"; +import { cn } from "@utilities"; + +import { IconSvg, Tooltip } from "@components/atoms"; +import { PopoverListContent, PopoverListTrigger, PopoverListWrapper } from "@components/molecules/popover"; + +import { ProjectsIcon } from "@assets/image"; + +interface MyProjectsPopoverProps { + activeProjectId?: string; + emptyListMessage: string; + isOpen: boolean; + label: string; + onProjectSelect: (project: { id: string }) => void; + projects: Project[]; +} + +export const MyProjectsPopover = ({ + activeProjectId, + emptyListMessage, + isOpen, + label, + onProjectSelect, + projects, +}: MyProjectsPopoverProps) => { + return ( + + +
  • + +
    +
    + +
    + + + {isOpen ? ( + + {label} + + ) : null} + +
    +
    +
  • +
    + ({ id, label: name, value: id }))} + onItemSelect={onProjectSelect} + /> +
    + ); +}; diff --git a/src/components/organisms/sidebar/components/projectsMenu/newProjectButton.tsx b/src/components/organisms/sidebar/components/projectsMenu/newProjectButton.tsx new file mode 100644 index 0000000000..2ccd6058f9 --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/newProjectButton.tsx @@ -0,0 +1,45 @@ +import { AnimatePresence, motion } from "motion/react"; + +import { sidebarAnimateVariant } from "../sidebar.types"; + +import { Button, IconSvg, Tooltip } from "@components/atoms"; + +import { NewProject } from "@assets/image"; + +interface NewProjectButtonProps { + isOpen: boolean; + label: string; + onClick: () => void; +} + +export const NewProjectButton = ({ isOpen, label, onClick }: NewProjectButtonProps) => { + return ( +
  • + + + +
  • + ); +}; diff --git a/src/components/organisms/sidebar/components/projectsMenu/projectsMenu.tsx b/src/components/organisms/sidebar/components/projectsMenu/projectsMenu.tsx new file mode 100644 index 0000000000..3d31a4a88c --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/projectsMenu.tsx @@ -0,0 +1,29 @@ +import { MyProjectsPopover } from "./myProjectsPopover"; +import { NewProjectButton } from "./newProjectButton"; +import { useProjectsMenu } from "./useProjectsMenu"; +import { cn } from "@utilities"; + +interface ProjectsMenuProps { + className?: string; + isOpen?: boolean; +} + +export const ProjectsMenu = ({ className, isOpen = false }: ProjectsMenuProps) => { + const { handleNewProject, handleProjectSelect, projectId, sortedProjectsList, t } = useProjectsMenu(); + + return ( + + ); +}; diff --git a/src/components/organisms/sidebar/components/projectsMenu/useProjectsMenu.ts b/src/components/organisms/sidebar/components/projectsMenu/useProjectsMenu.ts new file mode 100644 index 0000000000..bf292e4b76 --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/useProjectsMenu.ts @@ -0,0 +1,73 @@ +import { useEffect, useState } from "react"; + +import { useTranslation } from "react-i18next"; +import { useNavigate, useParams } from "react-router-dom"; + +import { SidebarHrefMenu } from "@enums/components"; +import { LoggerService } from "@services/logger.service"; +import { descopeProjectId, namespaces } from "@src/constants"; +import { Project } from "@type/models"; + +import { useOrganizationStore, useProjectStore, useSharedBetweenProjectsStore, useToastStore } from "@store"; + +export const useProjectsMenu = () => { + const { t } = useTranslation(["menu", "errors"]); + const { getProjectsList, projectsList } = useProjectStore(); + const navigate = useNavigate(); + const { projectId } = useParams(); + const addToast = useToastStore((state) => state.addToast); + const [sortedProjectsList, setSortedProjectsList] = useState([]); + const { user } = useOrganizationStore(); + const { drawers, settingsPath } = useSharedBetweenProjectsStore(); + + useEffect(() => { + const sortedProjects = projectsList.slice().sort((a, b) => a.name.localeCompare(b.name)); + setSortedProjectsList(sortedProjects); + }, [projectsList]); + + const fetchProjects = async () => { + const { error } = await getProjectsList(); + if (error) { + addToast({ + message: t("projectsListFetchFailed"), + type: "error", + }); + + LoggerService.error( + namespaces.ui.menu, + t("projectsListFetchFailedExtended", { + error, + }) + ); + } + }; + + useEffect(() => { + if (!descopeProjectId) { + fetchProjects(); + return; + } + if (user) { + fetchProjects(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleProjectSelect = ({ id: selectedProjectId }: { id: string }) => { + const isSettingsDrawerOpen = drawers[selectedProjectId]?.settings === true; + const storedSettingsPath = settingsPath[selectedProjectId]; + const basePath = `/${SidebarHrefMenu.projects}/${selectedProjectId}/explorer`; + const fullPath = isSettingsDrawerOpen && storedSettingsPath ? `${basePath}/${storedSettingsPath}` : basePath; + navigate(fullPath); + }; + + const handleNewProject = () => navigate("/ai"); + + return { + handleNewProject, + handleProjectSelect, + projectId, + sortedProjectsList, + t, + }; +}; diff --git a/src/components/organisms/sidebar/components/sidebar.types.ts b/src/components/organisms/sidebar/components/sidebar.types.ts new file mode 100644 index 0000000000..b6605f3827 --- /dev/null +++ b/src/components/organisms/sidebar/components/sidebar.types.ts @@ -0,0 +1,10 @@ +export interface SidebarMenuItemProps { + isMobile: boolean; + isOpen: boolean; + mobileMenuItemClass: string; +} + +export const sidebarAnimateVariant = { + hidden: { opacity: 0, width: 0 }, + visible: { opacity: 1, transition: { duration: 0.35, ease: "easeOut" as const }, width: "auto" }, +}; diff --git a/src/components/organisms/sidebar/components/userSection.tsx b/src/components/organisms/sidebar/components/userSection.tsx new file mode 100644 index 0000000000..049c709870 --- /dev/null +++ b/src/components/organisms/sidebar/components/userSection.tsx @@ -0,0 +1,59 @@ +import { AnimatePresence, motion } from "motion/react"; +import Avatar from "react-avatar"; + +import { sidebarAnimateVariant, SidebarMenuItemProps } from "./sidebar.types"; +import { descopeProjectId } from "@constants"; + +import { useOrganizationStore } from "@store"; + +import { PopoverWrapper, PopoverContent, PopoverTrigger } from "@components/molecules/popover"; +import { UserMenu } from "@components/organisms/sidebar"; + +interface SidebarUserSectionProps extends Omit { + onOpenFeedbackForm: () => void; +} + +export const SidebarUserSection = ({ isMobile, isOpen, onOpenFeedbackForm }: SidebarUserSectionProps) => { + const { user } = useOrganizationStore(); + + if (!descopeProjectId) { + return null; + } + + if (isMobile) { + return ( +
    + + {user?.name} +
    + ); + } + + return ( + + +
    +
    + +
    + + {isOpen ? ( + + {user?.name} + + ) : null} + +
    +
    + + + +
    + ); +}; diff --git a/src/components/organisms/sidebar/sidebar.tsx b/src/components/organisms/sidebar/sidebar.tsx index 347a532347..e2b83e39a2 100644 --- a/src/components/organisms/sidebar/sidebar.tsx +++ b/src/components/organisms/sidebar/sidebar.tsx @@ -1,38 +1,22 @@ -import React, { Suspense, useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; -import { AnimatePresence, motion } from "motion/react"; -import Avatar from "react-avatar"; -import { useTranslation } from "react-i18next"; -import { LuUnplug } from "react-icons/lu"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useLocation } from "react-router-dom"; -import { descopeProjectId, featureFlags } from "@constants"; -import { cn } from "@src/utilities"; +import { DesktopSidebar, MobileSidebar } from "./components"; +import { descopeProjectId } from "@constants"; import { useWindowDimensions } from "@hooks"; -import { useLoggerStore, useOrganizationStore, useToastStore } from "@store"; +import { useOrganizationStore, useToastStore } from "@store"; -import { Badge, Button, IconSvg, Loader, Tooltip } from "@components/atoms"; -import { MenuToggle } from "@components/atoms/menuToggle"; -import { PopoverWrapper, PopoverContent, PopoverTrigger } from "@components/molecules/popover"; -import { ProjectsMenu } from "@components/molecules/projectsMenu"; -import { UserFeedbackForm } from "@components/organisms"; -import { UserMenu } from "@components/organisms/sidebar"; - -import { IconLogo, IconLogoName } from "@assets/image"; -import { EventsFlag } from "@assets/image/icons"; -import { CircleQuestionIcon, FileIcon, StatsBlackIcon } from "@assets/image/icons/sidebar"; +const mobileMenuItemClass = "w-full justify-start gap-3 p-2 hover:bg-green-200"; export const Sidebar = () => { const [isOpen, setIsOpen] = useState(false); const [isFeedbackOpen, setIsFeedbackOpen] = useState(false); const { isMobile } = useWindowDimensions(); const { user, getEnrichedOrganizations, currentOrganization } = useOrganizationStore(); - const { isNewLogs, setSystemLogHeight, setNewLogs, lastLogType, systemLogHeight } = useLoggerStore(); const location = useLocation(); - const { t } = useTranslation("sidebar"); const addToast = useToastStore((state) => state.addToast); - const navigate = useNavigate(); useEffect(() => { setIsOpen(false); @@ -42,7 +26,7 @@ export const Sidebar = () => { const { data, error } = await getEnrichedOrganizations(); if (error || !data) { addToast({ - message: t("organizationFetchingFailed"), + message: "Failed to fetch organizations", type: "error", }); return; @@ -57,344 +41,37 @@ export const Sidebar = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [user, currentOrganization]); - const handleLogoClick = () => { - navigate("/"); - }; + const handleToggle = () => setIsOpen(!isOpen); + const handleClose = () => setIsOpen(false); + const handleOpenFeedbackForm = () => setIsFeedbackOpen(true); + const handleCloseFeedbackForm = () => setIsFeedbackOpen(false); - const animateVariant = { - hidden: { opacity: 0, width: 0 }, - visible: { opacity: 1, transition: { duration: 0.35, ease: "easeOut" as const }, width: "auto" }, + const menuItemProps = { + isMobile, + isOpen, + mobileMenuItemClass, }; - const toggleSystemLogHeight = () => { - setNewLogs(false); - setSystemLogHeight(systemLogHeight < 1 ? 20 : 0); - }; - - const rootClassName = useMemo( - () => cn("relative z-30 flex h-full items-start", { "z-50": isFeedbackOpen }), - [isFeedbackOpen] - ); - if (isMobile) { return ( - }> -
    - - -
    - - - {isOpen ? ( - <> - setIsOpen(false)} - /> - -
    -
    - - - {featureFlags.hideOrgConnections ? null : ( - - )} -
    - -
    - - - - - {descopeProjectId ? ( -
    - - {user?.name} -
    - ) : null} -
    -
    -
    - - ) : null} -
    -
    + ); } return ( - }> -
    -
    -
    - - - - - - {featureFlags.hideOrgConnections ? null : ( - - - - )} -
    - -
    - - - - - - - - - - - - - {descopeProjectId ? ( - - -
    - - - {isOpen ? ( - - {user?.name} - - ) : null} - -
    -
    - - setIsFeedbackOpen(true)} /> - -
    - ) : null} -
    - setIsFeedbackOpen(false)} - /> -
    -
    -
    + ); }; diff --git a/src/components/pages/aiLandingPage.tsx b/src/components/pages/aiLandingPage.tsx index 166ecdc6dc..37ecaf2309 100644 --- a/src/components/pages/aiLandingPage.tsx +++ b/src/components/pages/aiLandingPage.tsx @@ -124,7 +124,7 @@ export const AiLandingPage = () => { const showQuickstart = !projectsList.some((project) => project.name.toLowerCase() === "quickstart"); return ( -
    +
    diff --git a/src/components/pages/dashboard.tsx b/src/components/pages/dashboard.tsx index 007bba07b2..09c04a7e36 100644 --- a/src/components/pages/dashboard.tsx +++ b/src/components/pages/dashboard.tsx @@ -28,7 +28,7 @@ export const Dashboard = () => { } return ( -
    +
    { <> -
    +
    { return (
    -
    +
    diff --git a/src/components/templates/systemLogLayout.tsx b/src/components/templates/systemLogLayout.tsx index 575582c5d9..125e73973b 100644 --- a/src/components/templates/systemLogLayout.tsx +++ b/src/components/templates/systemLogLayout.tsx @@ -28,7 +28,7 @@ export const SystemLogLayout = ({ sidebar?: ReactNode; topbar?: ReactNode; }) => { - const layoutClasses = cn("flex h-screen flex-1 overflow-hidden", className); + const layoutClasses = cn("mr-1 mt-1 flex h-screen flex-1 overflow-hidden", className); const location = useLocation(); const { pathname } = location; const { projectId } = useParams(); @@ -82,7 +82,7 @@ export const SystemLogLayout = ({ }); const buttonResizeClasses = cn("my-0.5", { "my-0": systemLogHeight === 100 }); - const innerLayoutClasses = cn("mr-2 flex min-w-0 flex-1 flex-col md:mb-2", { + const innerLayoutClasses = cn("mr-1 flex min-w-0 flex-1 flex-col md:mb-2", { "md:mb-0.5": systemLogHeight === 0, "w-0": ["/", "/intro"].includes(pathname), "mr-0": isMobile,