4 Commits

Author SHA1 Message Date
3afdc6609f Add src/app/projects/page.tsx 2026-03-03 12:00:41 +00:00
c0e1747800 Update src/app/page.tsx 2026-03-03 12:00:41 +00:00
7a1652a28c Merge version_2 into main
Merge version_2 into main
2026-03-03 11:51:15 +00:00
95efb1ecea Merge version_2 into main
Merge version_2 into main
2026-03-03 11:46:58 +00:00
2 changed files with 335 additions and 1 deletions

View File

@@ -32,7 +32,7 @@ export default function LandingPage() {
navItems={[
{ name: "Features", id: "features" },
{ name: "How It Works", id: "workflow" },
{ name: "Pricing", id: "pricing" },
{ name: "Projects", id: "/projects" },
{ name: "Resources", id: "resources" },
{ name: "Contact", id: "contact" },
]}

334
src/app/projects/page.tsx Normal file
View File

@@ -0,0 +1,334 @@
"use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen';
import HeroLogoBillboard from '@/components/sections/hero/HeroLogoBillboard';
import FooterBase from '@/components/sections/footer/FooterBase';
import { Send } from 'lucide-react';
import { useState } from 'react';
interface Node {
id: string;
type: string;
label: string;
x: number;
y: number;
}
interface Connection {
id: string;
source: string;
target: string;
}
interface Project {
id: string;
name: string;
description: string;
nodes: Node[];
connections: Connection[];
createdAt: string;
}
export default function ProjectsPage() {
const [projects, setProjects] = useState<Project[]>([
{
id: "proj-1", name: "Email Marketing Automation", description: "Automated email sequences triggered by user behavior", nodes: [
{ id: "node-1", type: "trigger", label: "User Signup", x: 100, y: 100 },
{ id: "node-2", type: "action", label: "Send Welcome Email", x: 300, y: 100 },
{ id: "node-3", type: "action", label: "Add to List", x: 500, y: 100 },
],
connections: [
{ id: "conn-1", source: "node-1", target: "node-2" },
{ id: "conn-2", source: "node-2", target: "node-3" },
],
createdAt: "2025-01-15"},
{
id: "proj-2", name: "Data Pipeline Processing", description: "Transform and validate data from multiple sources", nodes: [
{ id: "node-1", type: "trigger", label: "Data Received", x: 100, y: 150 },
{ id: "node-2", type: "transform", label: "Parse JSON", x: 300, y: 150 },
{ id: "node-3", type: "validate", label: "Validate", x: 500, y: 150 },
{ id: "node-4", type: "action", label: "Save to DB", x: 700, y: 150 },
],
connections: [
{ id: "conn-1", source: "node-1", target: "node-2" },
{ id: "conn-2", source: "node-2", target: "node-3" },
{ id: "conn-3", source: "node-3", target: "node-4" },
],
createdAt: "2025-01-14"},
{
id: "proj-3", name: "Customer Onboarding", description: "Complete onboarding workflow with document generation", nodes: [
{ id: "node-1", type: "trigger", label: "New Customer", x: 100, y: 200 },
{ id: "node-2", type: "action", label: "Generate Documents", x: 300, y: 200 },
{ id: "node-3", type: "action", label: "Send Package", x: 500, y: 200 },
{ id: "node-4", type: "notify", label: "Notify Team", x: 700, y: 200 },
],
connections: [
{ id: "conn-1", source: "node-1", target: "node-2" },
{ id: "conn-2", source: "node-2", target: "node-3" },
{ id: "conn-3", source: "node-3", target: "node-4" },
],
createdAt: "2025-01-13"},
]);
const [selectedProject, setSelectedProject] = useState<Project | null>(projects[0]);
const [canvasZoom, setCanvasZoom] = useState(1);
const [panX, setPanX] = useState(0);
const [panY, setPanY] = useState(0);
const handleZoom = (direction: 'in' | 'out') => {
setCanvasZoom((prev) => direction === 'in' ? Math.min(prev + 0.2, 3) : Math.max(prev - 0.2, 0.5));
};
const handlePan = (dx: number, dy: number) => {
setPanX((prev) => prev + dx);
setPanY((prev) => prev + dy);
};
const nodeTypeColors: { [key: string]: string } = {
trigger: 'bg-blue-500',
action: 'bg-green-500',
transform: 'bg-purple-500',
validate: 'bg-orange-500',
notify: 'bg-red-500',
};
return (
<ThemeProvider
defaultButtonVariant="elastic-effect"
defaultTextAnimation="background-highlight"
borderRadius="pill"
contentWidth="mediumLarge"
sizing="medium"
background="noise"
cardStyle="glass-depth"
primaryButtonStyle="diagonal-gradient"
secondaryButtonStyle="layered"
headingFontWeight="normal"
>
<div id="nav" data-section="nav">
<NavbarStyleFullscreen
navItems={[
{ name: "Features", id: "features" },
{ name: "How It Works", id: "workflow" },
{ name: "Projects", id: "/projects" },
{ name: "Resources", id: "resources" },
{ name: "Contact", id: "contact" },
]}
brandName="Ranavo"
bottomLeftText="Automation for Everyone"
bottomRightText="hello@ranavo.app"
/>
</div>
<div id="projects-hero" data-section="projects-hero" className="py-20">
<HeroLogoBillboard
logoText="Your Projects"
description="Manage and edit your automation workflows. Select a project to view the canvas, manage nodes, and collaborate with your team."
buttons={[
{ text: "Create New Project", href: "#" },
{ text: "Back to Home", href: "/" },
]}
background={{ variant: "plain" }}
mediaAnimation="none"
frameStyle="card"
buttonAnimation="slide-up"
/>
</div>
<div id="projects-workspace" data-section="projects-workspace" className="py-12">
<div className="w-full max-w-full px-4 md:px-8">
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6 h-screen">
{/* Projects List Sidebar */}
<div className="lg:col-span-1 bg-card rounded-lg p-4 border border-foreground/10 overflow-y-auto">
<h3 className="text-lg font-semibold mb-4 text-foreground">Projects</h3>
<div className="space-y-2">
{projects.map((project) => (
<button
key={project.id}
onClick={() => setSelectedProject(project)}
className={`w-full text-left p-3 rounded-md transition-colors ${
selectedProject?.id === project.id
? 'bg-primary-cta text-primary-cta-text'
: 'bg-background/50 text-foreground hover:bg-background/70'
}`}
>
<div className="font-medium text-sm">{project.name}</div>
<div className="text-xs mt-1 opacity-75">{project.description}</div>
<div className="text-xs mt-2 opacity-60">{project.createdAt}</div>
</button>
))}
</div>
</div>
{/* Canvas Area */}
<div className="lg:col-span-3 bg-background rounded-lg border border-foreground/10 relative overflow-hidden">
{selectedProject ? (
<>
{/* Canvas Header */}
<div className="absolute top-0 left-0 right-0 bg-card border-b border-foreground/10 p-4 z-10">
<div className="flex justify-between items-center">
<div>
<h2 className="text-2xl font-bold text-foreground">{selectedProject.name}</h2>
<p className="text-sm text-foreground/60 mt-1">{selectedProject.description}</p>
</div>
<div className="flex gap-2">
<button
onClick={() => handleZoom('in')}
className="px-3 py-1 bg-primary-cta text-primary-cta-text rounded text-sm hover:opacity-90"
>
+
</button>
<span className="px-3 py-1 text-foreground text-sm">{Math.round(canvasZoom * 100)}%</span>
<button
onClick={() => handleZoom('out')}
className="px-3 py-1 bg-primary-cta text-primary-cta-text rounded text-sm hover:opacity-90"
>
</button>
<button
onClick={() => {
setCanvasZoom(1);
setPanX(0);
setPanY(0);
}}
className="px-3 py-1 bg-secondary-cta text-secondary-cta-text rounded text-sm hover:opacity-90 ml-2"
>
Reset
</button>
</div>
</div>
</div>
{/* Canvas SVG */}
<svg
className="w-full h-full mt-24"
onMouseMove={(e) => {
if (e.buttons === 4) {
handlePan(e.movementX, e.movementY);
}
}}
>
{/* Grid Background */}
<defs>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="currentColor" strokeWidth="0.5" opacity="0.1" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
{/* Connections (Lines) */}
<g transform={`translate(${panX}, ${panY}) scale(${canvasZoom})`}>
{selectedProject.connections.map((conn) => {
const sourceNode = selectedProject.nodes.find((n) => n.id === conn.source);
const targetNode = selectedProject.nodes.find((n) => n.id === conn.target);
if (!sourceNode || !targetNode) return null;
return (
<line
key={conn.id}
x1={sourceNode.x + 50}
y1={sourceNode.y + 25}
x2={targetNode.x + 50}
y2={targetNode.y + 25}
stroke="currentColor"
strokeWidth="2"
opacity="0.5"
/>
);
})}
{/* Nodes */}
{selectedProject.nodes.map((node) => (
<g key={node.id}>
<rect
x={node.x}
y={node.y}
width="100"
height="50"
className={`${nodeTypeColors[node.type] || 'bg-gray-500'} cursor-move`}
rx="4"
fill={nodeTypeColors[node.type] || '#6b7280'}
opacity="0.8"
/>
<text
x={node.x + 50}
y={node.y + 30}
textAnchor="middle"
fill="white"
fontSize="12"
fontWeight="500"
>
{node.label}
</text>
</g>
))}
</g>
</svg>
{/* Canvas Footer */}
<div className="absolute bottom-0 left-0 right-0 bg-card border-t border-foreground/10 p-4 text-xs text-foreground/60">
<div className="flex justify-between items-center">
<div>Nodes: {selectedProject.nodes.length} | Connections: {selectedProject.connections.length}</div>
<div className="flex gap-4">
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-blue-500 rounded"></div>
<span>Trigger</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-green-500 rounded"></div>
<span>Action</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-purple-500 rounded"></div>
<span>Transform</span>
</div>
</div>
</div>
</div>
</>
) : (
<div className="flex items-center justify-center h-full text-foreground/50">
Select a project to view
</div>
)}
</div>
</div>
</div>
</div>
<div id="footer" data-section="footer">
<FooterBase
columns={[
{
title: "Product", items: [
{ label: "Canvas Workspace", href: "#features" },
{ label: "Node Library", href: "#workflow" },
{ label: "Pricing", href: "#" },
{ label: "Security", href: "#" },
],
},
{
title: "Resources", items: [
{ label: "Documentation", href: "#" },
{ label: "Tutorials", href: "#" },
{ label: "Community", href: "#" },
{ label: "Blog", href: "#" },
],
},
{
title: "Company", items: [
{ label: "About Us", href: "#" },
{ label: "Contact", href: "#" },
{ label: "Careers", href: "#" },
{ label: "Status", href: "#" },
],
},
]}
logoText="Ranavo"
copyrightText="© 2025 Ranavo Canvas. All rights reserved."
/>
</div>
</ThemeProvider>
);
}