pixelpulse-wizardry / flow.html
dl3239491's picture
create a landing page for my event organization company that holds concerts
d6e5bdf verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Flow Interface | PixelPulse</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/reactflow@11.7.0/dist/umd/react-flow.production.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
overflow: hidden;
}
.react-flow__node {
border-radius: 8px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
border: 1px solid #e5e7eb;
background: white;
}
.react-flow__node.selected {
box-shadow: 0 0 0 2px #8b5cf6;
}
.sidebar {
transition: all 0.3s ease;
}
.gradient-text {
background: linear-gradient(90deg, #8b5cf6 0%, #ec4899 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
</style>
</head>
<body class="bg-gray-50">
<div class="flex h-full">
<!-- Sidebar -->
<div class="sidebar bg-white border-r border-gray-200 w-64 flex-shrink-0 p-4 flex flex-col">
<div class="flex items-center mb-6">
<a href="/" class="text-xl font-bold gradient-text">Flow Builder</a>
</div>
<div class="mb-6">
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-3">Nodes</h3>
<div class="space-y-2">
<div class="node-item bg-white p-3 rounded border border-gray-200 cursor-move hover:bg-gray-50"
draggable="true" data-type="input">
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-green-500 mr-2"></div>
<span class="font-medium">Input Node</span>
</div>
</div>
<div class="node-item bg-white p-3 rounded border border-gray-200 cursor-move hover:bg-gray-50"
draggable="true" data-type="default">
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-blue-500 mr-2"></div>
<span class="font-medium">Default Node</span>
</div>
</div>
<div class="node-item bg-white p-3 rounded border border-gray-200 cursor-move hover:bg-gray-50"
draggable="true" data-type="output">
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-red-500 mr-2"></div>
<span class="font-medium">Output Node</span>
</div>
</div>
</div>
</div>
<div class="space-y-4 mt-auto">
<a href="/events.html" class="block text-center bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-md transition">
Back to Events
</a>
<button class="w-full bg-purple-600 hover:bg-purple-700 text-white py-2 px-4 rounded-md transition flex items-center justify-center">
<i data-feather="save" class="w-4 h-4 mr-2"></i>
Save Flow
</button>
</div>
</div>
<!-- Flow Area -->
<div id="reactflow-container" class="flex-1 relative">
<!-- This will be replaced by React Flow -->
</div>
<!-- Controls Panel -->
<div class="sidebar bg-white border-l border-gray-200 w-64 flex-shrink-0 p-4 overflow-y-auto">
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Node Settings</h3>
<div id="settings-panel" class="space-y-4">
<div class="text-center text-gray-400 py-8">
<i data-feather="box" class="w-8 h-8 mx-auto mb-2"></i>
<p>Select a node to edit its properties</p>
</div>
</div>
</div>
</div>
<script type="text/babel">
document.addEventListener('DOMContentLoaded', function() {
const { useState, useCallback, useRef } = React;
const ReactFlow = window.ReactFlow;
const { useNodesState, useEdgesState, addEdge, MiniMap, Controls, Background, applyNodeChanges, applyEdgeChanges } = ReactFlow;
const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'Start' },
position: { x: 250, y: 100 },
},
{
id: '2',
data: { label: 'Process' },
position: { x: 250, y: 200 },
},
{
id: '3',
type: 'output',
data: { label: 'End' },
position: { x: 250, y: 300 },
}
];
const initialEdges = [
{ id: 'e1-2', source: '1', target: '2' },
{ id: 'e2-3', source: '2', target: '3' }
];
const FlowBuilder = () => {
const reactFlowWrapper = useRef(null);
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const [reactFlowInstance, setReactFlowInstance] = useState(null);
const [selectedNode, setSelectedNode] = useState(null);
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[]
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[]
);
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[]
);
const onDragOver = useCallback((event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);
const onDrop = useCallback(
(event) => {
event.preventDefault();
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
const type = event.dataTransfer.getData('application/reactflow');
if (typeof type === 'undefined' || !type) {
return;
}
const position = reactFlowInstance.project({
x: event.clientX - reactFlowBounds.left,
y: event.clientY - reactFlowBounds.top,
});
const newNode = {
id: `${Date.now()}`,
type,
position,
data: { label: `${type.charAt(0).toUpperCase() + type.slice(1)} Node` },
};
setNodes((nds) => nds.concat(newNode));
},
[reactFlowInstance]
);
const onNodeClick = useCallback((event, node) => {
setSelectedNode(node);
// Update settings panel
const panel = document.getElementById('settings-panel');
panel.innerHTML = `
<div>
<h4 class="font-medium text-gray-900 mb-2">${node.data.label}</h4>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
<input type="text" value="${node.data.label}"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Node Type</label>
<select class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500">
<option ${node.type === 'input' ? 'selected' : ''}>Input</option>
<option ${!node.type || node.type === 'default' ? 'selected' : ''}>Default</option>
<option ${node.type === 'output' ? 'selected' : ''}>Output</option>
</select>
</div>
<button class="w-full bg-red-100 text-red-600 py-2 px-4 rounded-md hover:bg-red-200 transition">
Delete Node
</button>
</div>
</div>
`;
}, []);
return (
<div className="reactflow-wrapper h-full w-full" ref={reactFlowWrapper}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
onInit={setReactFlowInstance}
onDrop={onDrop}
onDragOver={onDragOver}
onNodeClick={onNodeClick}
fitView
>
<Controls />
<MiniMap />
<Background variant="dots" gap={12} size={1} />
</ReactFlow>
</div>
);
};
// Check if the container exists before rendering
const container = document.getElementById('reactflow-container');
if (container) {
const root = ReactDOM.createRoot(container);
root.render(<FlowBuilder />);
}
});
</script>
<script>
// Initialize Feather Icons when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
feather.replace();
// Make nodes draggable from sidebar
const nodeItems = document.querySelectorAll('.node-item');
nodeItems.forEach(item => {
item.addEventListener('dragstart', (event) => {
event.dataTransfer.setData('application/reactflow', event.target.getAttribute('data-type'));
event.dataTransfer.effectAllowed = 'move';
});
});
});
</script>
</body>
</html>