LukasBe's picture
Add 3 files
affaaa6 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vibe Coding | HTML5 User Environment</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.kanban-column {
min-height: 500px;
background-color: rgba(243, 244, 246, 0.5);
}
.card {
transition: all 0.2s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.annotation-marker {
position: absolute;
width: 16px;
height: 16px;
background-color: #3B82F6;
border-radius: 50%;
border: 2px solid white;
cursor: pointer;
transform: translate(-50%, -50%);
}
.annotation-marker:hover {
background-color: #2563EB;
}
.annotation-tooltip {
position: absolute;
background-color: white;
padding: 8px;
border-radius: 4px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
z-index: 10;
max-width: 200px;
}
.feedback-canvas {
position: relative;
border: 1px dashed #ccc;
margin: 0 auto;
}
.priority-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 4px;
}
.priority-high {
background-color: #EF4444;
}
.priority-medium {
background-color: #F59E0B;
}
.priority-low {
background-color: #10B981;
}
.skeleton-loader {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 0.6;
}
50% {
opacity: 0.3;
}
}
</style>
</head>
<body class="bg-gray-50 font-sans">
<!-- Header -->
<header class="bg-indigo-700 text-white shadow-md">
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fas fa-code text-2xl"></i>
<h1 class="text-xl font-bold">Vibe Coding</h1>
<span class="text-sm bg-indigo-600 px-2 py-1 rounded ml-2">v1.0</span>
</div>
<div class="flex items-center space-x-4">
<div class="relative">
<button id="user-menu-btn" class="flex items-center space-x-2 focus:outline-none">
<span class="font-medium">John Doe</span>
<i class="fas fa-chevron-down text-xs"></i>
</button>
<div id="user-menu" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-10">
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Sign out</a>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 py-6">
<!-- App Title and Controls -->
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Product Backlog</h2>
<p class="text-gray-600">Last updated: <span id="last-updated">Just now</span></p>
</div>
<div class="flex space-x-3">
<button id="refresh-btn" class="flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50">
<i class="fas fa-sync-alt mr-2"></i> Refresh
</button>
<button id="new-feedback-btn" class="flex items-center px-4 py-2 bg-indigo-600 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-indigo-700">
<i class="fas fa-plus mr-2"></i> New Feedback
</button>
</div>
</div>
<!-- Filter/Search Bar -->
<div class="bg-white p-4 rounded-lg shadow-sm mb-6">
<div class="flex flex-col md:flex-row md:items-center md:space-x-4 space-y-3 md:space-y-0">
<div class="flex-1">
<label for="search" class="sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-search text-gray-400"></i>
</div>
<input id="search" name="search" type="text" class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="Search backlog items...">
</div>
</div>
<div class="flex space-x-3">
<div>
<label for="status-filter" class="sr-only">Status</label>
<select id="status-filter" name="status-filter" class="block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<option value="all">All Statuses</option>
<option value="todo">To Do</option>
<option value="in-progress">In Progress</option>
<option value="done">Done</option>
</select>
</div>
<div>
<label for="priority-filter" class="sr-only">Priority</label>
<select id="priority-filter" name="priority-filter" class="block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<option value="all">All Priorities</option>
<option value="high">High</option>
<option value="medium">Medium</option>
<option value="low">Low</option>
</select>
</div>
</div>
</div>
</div>
<!-- Kanban Board -->
<div id="kanban-board" class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<!-- To Do Column -->
<div class="kanban-column rounded-lg p-4 shadow-sm">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-lg text-gray-800">To Do</h3>
<span class="bg-gray-200 text-gray-700 text-xs font-semibold px-2 py-1 rounded-full">24 items</span>
</div>
<div id="todo-cards" class="space-y-3">
<!-- Card 1 -->
<div class="card bg-white p-4 rounded-lg shadow cursor-pointer hover:shadow-md transition" onclick="showCardDetails('card-1')">
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-gray-800">Implement user authentication</h4>
<span class="priority-indicator priority-high"></span>
</div>
<p class="text-sm text-gray-600 mb-3">Add secure login and registration functionality with JWT tokens</p>
<div class="flex justify-between items-center text-xs">
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">US-101</span>
<div class="flex items-center space-x-2">
<span class="text-gray-500"><i class="fas fa-comment mr-1"></i> 5</span>
<span class="text-gray-500"><i class="fas fa-code-branch mr-1"></i> 3</span>
</div>
</div>
</div>
<!-- Card 2 -->
<div class="card bg-white p-4 rounded-lg shadow cursor-pointer hover:shadow-md transition" onclick="showCardDetails('card-2')">
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-gray-800">Create dashboard layout</h4>
<span class="priority-indicator priority-medium"></span>
</div>
<p class="text-sm text-gray-600 mb-3">Design and implement the main dashboard UI components</p>
<div class="flex justify-between items-center text-xs">
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">US-102</span>
<div class="flex items-center space-x-2">
<span class="text-gray-500"><i class="fas fa-comment mr-1"></i> 2</span>
<span class="text-gray-500"><i class="fas fa-code-branch mr-1"></i> 1</span>
</div>
</div>
</div>
<!-- Card 3 (Skeleton Loader) -->
<div class="card bg-gray-100 p-4 rounded-lg shadow">
<div class="animate-pulse">
<div class="h-5 bg-gray-200 rounded w-3/4 mb-3"></div>
<div class="h-3 bg-gray-200 rounded w-full mb-4"></div>
<div class="flex justify-between">
<div class="h-6 bg-gray-200 rounded w-16"></div>
<div class="flex space-x-2">
<div class="h-6 bg-gray-200 rounded w-6"></div>
<div class="h-6 bg-gray-200 rounded w-6"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- In Progress Column -->
<div class="kanban-column rounded-lg p-4 shadow-sm">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-lg text-gray-800">In Progress</h3>
<span class="bg-blue-100 text-blue-800 text-xs font-semibold px-2 py-1 rounded-full">8 items</span>
</div>
<div id="in-progress-cards" class="space-y-3">
<!-- Card 4 -->
<div class="card bg-white p-4 rounded-lg shadow cursor-pointer hover:shadow-md transition" onclick="showCardDetails('card-4')">
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-gray-800">API endpoint for user data</h4>
<span class="priority-indicator priority-high"></span>
</div>
<p class="text-sm text-gray-600 mb-3">Create RESTful endpoints for user profile management</p>
<div class="flex justify-between items-center text-xs">
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">US-103</span>
<div class="flex items-center space-x-2">
<span class="text-gray-500"><i class="fas fa-comment mr-1"></i> 3</span>
<span class="text-gray-500"><i class="fas fa-code-branch mr-1"></i> 2</span>
</div>
</div>
</div>
<!-- Card 5 -->
<div class="card bg-white p-4 rounded-lg shadow cursor-pointer hover:shadow-md transition" onclick="showCardDetails('card-5')">
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-gray-800">Mobile responsive design</h4>
<span class="priority-indicator priority-medium"></span>
</div>
<p class="text-sm text-gray-600 mb-3">Ensure all components adapt to different screen sizes</p>
<div class="flex justify-between items-center text-xs">
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">US-104</span>
<div class="flex items-center space-x-2">
<span class="text-gray-500"><i class="fas fa-comment mr-1"></i> 7</span>
<span class="text-gray-500"><i class="fas fa-code-branch mr-1"></i> 4</span>
</div>
</div>
</div>
</div>
</div>
<!-- Done Column -->
<div class="kanban-column rounded-lg p-4 shadow-sm">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-lg text-gray-800">Done</h3>
<span class="bg-green-100 text-green-800 text-xs font-semibold px-2 py-1 rounded-full">12 items</span>
</div>
<div id="done-cards" class="space-y-3">
<!-- Card 6 -->
<div class="card bg-white p-4 rounded-lg shadow cursor-pointer hover:shadow-md transition" onclick="showCardDetails('card-6')">
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-gray-800">Project setup and config</h4>
<span class="priority-indicator priority-low"></span>
</div>
<p class="text-sm text-gray-600 mb-3">Initialize project with required dependencies and tooling</p>
<div class="flex justify-between items-center text-xs">
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">US-105</span>
<div class="flex items-center space-x-2">
<span class="text-gray-500"><i class="fas fa-comment mr-1"></i> 0</span>
<span class="text-gray-500"><i class="fas fa-code-branch mr-1"></i> 1</span>
</div>
</div>
</div>
<!-- Card 7 -->
<div class="card bg-white p-4 rounded-lg shadow cursor-pointer hover:shadow-md transition" onclick="showCardDetails('card-7')">
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-gray-800">Database schema design</h4>
<span class="priority-indicator priority-medium"></span>
</div>
<p class="text-sm text-gray-600 mb-3">Create initial database models and relationships</p>
<div class="flex justify-between items-center text-xs">
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">US-106</span>
<div class="flex items-center space-x-2">
<span class="text-gray-500"><i class="fas fa-comment mr-1"></i> 4</span>
<span class="text-gray-500"><i class="fas fa-code-branch mr-1"></i> 2</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Card Details Modal -->
<div id="card-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-11/12 md:w-3/4 lg:w-2/3 shadow-lg rounded-md bg-white">
<div class="flex justify-between items-start mb-4">
<div>
<h3 id="card-title" class="text-xl font-bold text-gray-800">Card Title</h3>
<div class="flex items-center mt-1 space-x-3">
<span id="card-id" class="bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs">US-000</span>
<span id="card-priority" class="text-xs font-medium flex items-center">
<span class="priority-indicator priority-high mr-1"></span>
<span>High Priority</span>
</span>
<span id="card-status" class="bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs">To Do</span>
</div>
</div>
<button onclick="closeCardDetails()" class="text-gray-400 hover:text-gray-500">
<i class="fas fa-times"></i>
</button>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2">
<div class="mb-6">
<h4 class="font-semibold text-gray-700 mb-2">Description</h4>
<p id="card-description" class="text-gray-600">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
<div class="mb-6">
<h4 class="font-semibold text-gray-700 mb-2">Acceptance Criteria</h4>
<ul id="card-criteria" class="list-disc pl-5 space-y-1 text-gray-600">
<li>First acceptance criterion</li>
<li>Second acceptance criterion</li>
<li>Third acceptance criterion</li>
</ul>
</div>
<div class="mb-6">
<h4 class="font-semibold text-gray-700 mb-2">Dependencies</h4>
<div id="card-dependencies" class="flex flex-wrap gap-2">
<span class="bg-gray-100 text-gray-800 px-2 py-1 rounded text-xs">US-105</span>
<span class="bg-gray-100 text-gray-800 px-2 py-1 rounded text-xs">US-107</span>
</div>
</div>
</div>
<div>
<div class="mb-6">
<div class="flex justify-between items-center mb-2">
<h4 class="font-semibold text-gray-700">Feedback (3)</h4>
<button onclick="startFeedbackSession()" class="text-sm text-indigo-600 hover:text-indigo-800 flex items-center">
<i class="fas fa-plus mr-1"></i> Add Feedback
</button>
</div>
<div id="card-feedback" class="space-y-3">
<div class="bg-gray-50 p-3 rounded">
<div class="flex justify-between items-start mb-1">
<span class="text-sm font-medium">Jane Smith</span>
<span class="text-xs text-gray-500">2 days ago</span>
</div>
<p class="text-sm text-gray-600">The login button color doesn't match our brand guidelines. Please use #3B82F6 instead.</p>
</div>
<div class="bg-gray-50 p-3 rounded">
<div class="flex justify-between items-start mb-1">
<span class="text-sm font-medium">Mike Johnson</span>
<span class="text-xs text-gray-500">1 week ago</span>
</div>
<p class="text-sm text-gray-600">We should add a "Forgot Password" link below the login form.</p>
</div>
</div>
</div>
<div>
<h4 class="font-semibold text-gray-700 mb-2">Development Links</h4>
<div id="card-dev-links" class="space-y-2">
<a href="#" class="flex items-center text-sm text-indigo-600 hover:text-indigo-800">
<i class="fas fa-file-code mr-2"></i> user-auth.controller.js
</a>
<a href="#" class="flex items-center text-sm text-indigo-600 hover:text-indigo-800">
<i class="fas fa-folder mr-2"></i> /src/routes/auth
</a>
<a href="#" class="flex items-center text-sm text-indigo-600 hover:text-indigo-800">
<i class="fas fa-database mr-2"></i> users table schema
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Feedback Modal -->
<div id="feedback-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-10 mx-auto p-5 border w-11/12 md:w-4/5 lg:w-3/4 shadow-lg rounded-md bg-white">
<div class="flex justify-between items-start mb-4">
<div>
<h3 class="text-xl font-bold text-gray-800">Add Feedback</h3>
<p class="text-sm text-gray-600">Attach visual feedback to: <span id="feedback-card-title" class="font-medium">Implement user authentication</span></p>
</div>
<button onclick="closeFeedbackModal()" class="text-gray-400 hover:text-gray-500">
<i class="fas fa-times"></i>
</button>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Feedback Context</label>
<div class="flex space-x-3">
<button id="upload-image-btn" class="flex-1 flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50">
<i class="fas fa-image mr-2"></i> Upload Image
</button>
<button id="paste-url-btn" class="flex-1 flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50">
<i class="fas fa-link mr-2"></i> Paste URL
</button>
</div>
</div>
<div id="feedback-upload-area" class="hidden">
<div class="border-2 border-dashed border-gray-300 rounded-md p-4 text-center">
<i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-2"></i>
<p class="text-sm text-gray-600 mb-2">Drag and drop an image file here, or click to select</p>
<input type="file" id="image-upload" class="hidden" accept="image/*">
<button onclick="document.getElementById('image-upload').click()" class="px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-md hover:bg-indigo-700">
Select Image
</button>
</div>
</div>
<div id="feedback-url-area" class="hidden">
<div class="mb-4">
<label for="url-input" class="block text-sm font-medium text-gray-700 mb-1">URL to Prototype/Screenshot</label>
<input type="url" id="url-input" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="https://example.com/prototype">
</div>
<button id="load-url-btn" class="px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-md hover:bg-indigo-700">
Load URL
</button>
</div>
<div id="feedback-canvas-container" class="hidden mt-4">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium text-gray-700">Click on the image to add annotations</h4>
<button id="clear-annotations-btn" class="text-sm text-red-600 hover:text-red-800">
<i class="fas fa-trash-alt mr-1"></i> Clear All
</button>
</div>
<div id="feedback-canvas" class="feedback-canvas bg-gray-100 relative">
<!-- Image will be loaded here -->
<img id="feedback-image" src="" alt="Feedback context" class="max-w-full h-auto">
<!-- Annotation markers will be added here -->
</div>
</div>
</div>
<div>
<div id="annotation-form" class="hidden">
<div class="mb-4">
<label for="annotation-comment" class="block text-sm font-medium text-gray-700 mb-1">Your Comment</label>
<textarea id="annotation-comment" rows="4" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="Add details about this specific area..."></textarea>
</div>
<div class="flex justify-end space-x-3">
<button id="cancel-annotation-btn" class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50">
Cancel
</button>
<button id="submit-annotation-btn" class="px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-md hover:bg-indigo-700">
Submit Annotation
</button>
</div>
</div>
<div id="existing-annotations" class="hidden">
<h4 class="font-medium text-gray-700 mb-2">Existing Annotations</h4>
<div id="annotations-list" class="space-y-3">
<!-- Annotations will be listed here -->
</div>
</div>
<div id="feedback-instructions" class="bg-blue-50 p-4 rounded-md">
<h4 class="font-medium text-blue-800 mb-2">How to provide feedback</h4>
<ol class="list-decimal pl-5 space-y-1 text-sm text-blue-700">
<li>Upload an image or paste a URL to load the visual context</li>
<li>Click on specific areas to add annotation markers</li>
<li>Add detailed comments for each annotation</li>
<li>Submit your feedback to attach it to this backlog item</li>
</ol>
</div>
</div>
</div>
</div>
</div>
</main>
<script>
// Sample data for the demo
const cards = {
'card-1': {
id: 'US-101',
title: 'Implement user authentication',
priority: 'high',
status: 'todo',
description: 'Add secure login and registration functionality with JWT tokens. This includes creating the necessary API endpoints, frontend components, and validation logic.',
criteria: [
'User can register with email and password',
'User can login with valid credentials',
'Invalid login attempts are handled securely',
'JWT tokens are issued and validated properly'
],
dependencies: ['US-105', 'US-107'],
feedback: [
{ author: 'Jane Smith', date: '2 days ago', comment: 'The login button color doesn\'t match our brand guidelines. Please use #3B82F6 instead.' },
{ author: 'Mike Johnson', date: '1 week ago', comment: 'We should add a "Forgot Password" link below the login form.' }
],
devLinks: [
{ type: 'file', name: 'user-auth.controller.js' },
{ type: 'folder', name: '/src/routes/auth' },
{ type: 'db', name: 'users table schema' }
]
},
'card-2': {
id: 'US-102',
title: 'Create dashboard layout',
priority: 'medium',
status: 'todo',
description: 'Design and implement the main dashboard UI components including navigation, summary cards, and the main content area.',
criteria: [
'Dashboard adapts to different screen sizes',
'Navigation is accessible and intuitive',
'Summary cards display key metrics',
'Main content area is flexible for different widgets'
],
dependencies: ['US-105'],
feedback: [
{ author: 'Sarah Williams', date: '3 days ago', comment: 'The navigation should be collapsible on smaller screens.' }
],
devLinks: [
{ type: 'file', name: 'Dashboard.jsx' },
{ type: 'folder', name: '/src/components/layout' }
]
},
'card-4': {
id: 'US-103',
title: 'API endpoint for user data',
priority: 'high',
status: 'in-progress',
description: 'Create RESTful endpoints for user profile management including CRUD operations and data validation.',
criteria: [
'GET /api/users returns user list',
'GET /api/users/:id returns specific user',
'PUT /api/users/:id updates user data',
'DELETE /api/users/:id deactivates user'
],
dependencies: ['US-101', 'US-106'],
feedback: [],
devLinks: [
{ type: 'file', name: 'users.routes.js' },
{ type: 'folder', name: '/src/controllers' }
]
},
'card-5': {
id: 'US-104',
title: 'Mobile responsive design',
priority: 'medium',
status: 'in-progress',
description: 'Ensure all components adapt to different screen sizes with appropriate breakpoints and responsive behavior.',
criteria: [
'Layout adjusts at 768px and 480px breakpoints',
'Navigation converts to hamburger menu on mobile',
'Forms remain usable on small screens',
'Images scale appropriately'
],
dependencies: ['US-102'],
feedback: [
{ author: 'Alex Chen', date: '5 days ago', comment: 'The form fields need more padding on mobile devices.' },
{ author: 'Taylor Swift', date: '1 week ago', comment: 'Consider adding swipe gestures for the image carousel on mobile.' }
],
devLinks: [
{ type: 'file', name: 'responsive.scss' },
{ type: 'folder', name: '/src/styles' }
]
},
'card-6': {
id: 'US-105',
title: 'Project setup and config',
priority: 'low',
status: 'done',
description: 'Initialize project with required dependencies and tooling including linters, testing frameworks, and CI/CD pipeline.',
criteria: [
'Project can be installed with npm install',
'ESLint and Prettier configured',
'Jest testing framework installed',
'GitHub Actions workflow for CI'
],
dependencies: [],
feedback: [],
devLinks: [
{ type: 'file', name: 'package.json' },
{ type: 'folder', name: '/.github/workflows' }
]
},
'card-7': {
id: 'US-106',
title: 'Database schema design',
priority: 'medium',
status: 'done',
description: 'Create initial database models and relationships including users, products, and orders tables with appropriate indexes.',
criteria: [
'Users table with required fields',
'Products table with categories',
'Orders table with relationships',
'Appropriate indexes for common queries'
],
dependencies: ['US-105'],
feedback: [
{ author: 'David Miller', date: '2 weeks ago', comment: 'Consider adding a created_at timestamp to all tables.' }
],
devLinks: [
{ type: 'file', name: 'schema.sql' },
{ type: 'folder', name: '/database/migrations' }
]
}
};
// DOM elements
const userMenuBtn = document.getElementById('user-menu-btn');
const userMenu = document.getElementById('user-menu');
const refreshBtn = document.getElementById('refresh-btn');
const newFeedbackBtn = document.getElementById('new-feedback-btn');
const searchInput = document.getElementById('search');
const statusFilter = document.getElementById('status-filter');
const priorityFilter = document.getElementById('priority-filter');
const cardModal = document.getElementById('card-modal');
const feedbackModal = document.getElementById('feedback-modal');
const uploadImageBtn = document.getElementById('upload-image-btn');
const pasteUrlBtn = document.getElementById('paste-url-btn');
const feedbackUploadArea = document.getElementById('feedback-upload-area');
const feedbackUrlArea = document.getElementById('feedback-url-area');
const feedbackCanvasContainer = document.getElementById('feedback-canvas-container');
const annotationForm = document.getElementById('annotation-form');
const existingAnnotations = document.getElementById('existing-annotations');
const feedbackInstructions = document.getElementById('feedback-instructions');
const feedbackImage = document.getElementById('feedback-image');
const feedbackCanvas = document.getElementById('feedback-canvas');
const clearAnnotationsBtn = document.getElementById('clear-annotations-btn');
const cancelAnnotationBtn = document.getElementById('cancel-annotation-btn');
const submitAnnotationBtn = document.getElementById('submit-annotation-btn');
const annotationComment = document.getElementById('annotation-comment');
const annotationsList = document.getElementById('annotations-list');
// State variables
let currentCardId = null;
let annotations = [];
let currentAnnotation = null;
// Event listeners
userMenuBtn.addEventListener('click', toggleUserMenu);
refreshBtn.addEventListener('click', refreshBacklog);
newFeedbackBtn.addEventListener('click', startFeedbackSession);
searchInput.addEventListener('input', filterCards);
statusFilter.addEventListener('change', filterCards);
priorityFilter.addEventListener('change', filterCards);
uploadImageBtn.addEventListener('click', showUploadArea);
pasteUrlBtn.addEventListener('click', showUrlArea);
clearAnnotationsBtn.addEventListener('click', clearAnnotations);
cancelAnnotationBtn.addEventListener('click', cancelAnnotation);
submitAnnotationBtn.addEventListener('click', submitAnnotation);
document.getElementById('image-upload').addEventListener('change', handleImageUpload);
document.getElementById('load-url-btn').addEventListener('click', loadUrl);
// Initialize the app
function init() {
// Simulate loading data
setTimeout(() => {
document.querySelectorAll('.skeleton-loader').forEach(el => {
el.classList.remove('animate-pulse');
el.innerHTML = `
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-gray-800">Data validation middleware</h4>
<span class="priority-indicator priority-medium"></span>
</div>
<p class="text-sm text-gray-600 mb-3">Create reusable validation middleware for API requests</p>
<div class="flex justify-between items-center text-xs">
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">US-108</span>
<div class="flex items-center space-x-2">
<span class="text-gray-500"><i class="fas fa-comment mr-1"></i> 0</span>
<span class="text-gray-500"><i class="fas fa-code-branch mr-1"></i> 0</span>
</div>
</div>
`;
});
}, 1500);
}
// Toggle user menu
function toggleUserMenu() {
userMenu.classList.toggle('hidden');
}
// Close user menu when clicking outside
document.addEventListener('click', (e) => {
if (!userMenu.contains(e.target) && e.target !== userMenuBtn) {
userMenu.classList.add('hidden');
}
});
// Refresh backlog data
function refreshBacklog() {
refreshBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Refreshing';
setTimeout(() => {
refreshBtn.innerHTML = '<i class="fas fa-sync-alt mr-2"></i> Refresh';
document.getElementById('last-updated').textContent = new Date().toLocaleString();
// In a real app, this would fetch new data from the API
}, 1000);
}
// Show card details modal
function showCardDetails(cardId) {
currentCardId = cardId;
const card = cards[cardId];
// Update modal content
document.getElementById('card-title').textContent = card.title;
document.getElementById('card-id').textContent = card.id;
document.getElementById('card-description').textContent = card.description;
document.getElementById('card-status').textContent = card.status === 'todo' ? 'To Do' :
card.status === 'in-progress' ? 'In Progress' : 'Done';
// Update priority
const priorityIndicator = document.getElementById('card-priority');
priorityIndicator.querySelector('.priority-indicator').className = `priority-indicator priority-${card.priority}`;
priorityIndicator.querySelector('span:last-child').textContent = `${card.priority.charAt(0).toUpperCase() + card.priority.slice(1)} Priority`;
// Update criteria
const criteriaList = document.getElementById('card-criteria');
criteriaList.innerHTML = '';
card.criteria.forEach(criterion => {
const li = document.createElement('li');
li.textContent = criterion;
criteriaList.appendChild(li);
});
// Update dependencies
const dependenciesContainer = document.getElementById('card-dependencies');
dependenciesContainer.innerHTML = '';
card.dependencies.forEach(dep => {
const span = document.createElement('span');
span.className = 'bg-gray-100 text-gray-800 px-2 py-1 rounded text-xs';
span.textContent = dep;
dependenciesContainer.appendChild(span);
});
// Update feedback
const feedbackContainer = document.getElementById('card-feedback');
feedbackContainer.innerHTML = '';
card.feedback.forEach(fb => {
const div = document.createElement('div');
div.className = 'bg-gray-50 p-3 rounded';
div.innerHTML = `
<div class="flex justify-between items-start mb-1">
<span class="text-sm font-medium">${fb.author}</span>
<span class="text-xs text-gray-500">${fb.date}</span>
</div>
<p class="text-sm text-gray-600">${fb.comment}</p>
`;
feedbackContainer.appendChild(div);
});
// Update dev links
const devLinksContainer = document.getElementById('card-dev-links');
devLinksContainer.innerHTML = '';
card.devLinks.forEach(link => {
const a = document.createElement('a');
a.href = '#';
a.className = 'flex items-center text-sm text-indigo-600 hover:text-indigo-800';
a.innerHTML = `
<i class="fas ${link.type === 'file' ? 'fa-file-code' : link.type === 'folder' ? 'fa-folder' : 'fa-database'} mr-2"></i>
${link.name}
`;
devLinksContainer.appendChild(a);
});
// Show modal
document.getElementById('feedback-card-title').textContent = card.title;
cardModal.classList.remove('hidden');
}
// Close card details modal
function closeCardDetails() {
cardModal.classList.add('hidden');
}
// Start feedback session
function startFeedbackSession() {
if (!currentCardId) return;
// Reset feedback modal
feedbackUploadArea.classList.add('hidden');
feedbackUrlArea.classList.add('hidden');
feedbackCanvasContainer.classList.add('hidden');
annotationForm.classList.add('hidden');
existingAnnotations.classList.add('hidden');
feedbackInstructions.classList.remove('hidden');
// Show modal
feedbackModal.classList.remove('hidden');
}
// Close feedback modal
function closeFeedbackModal() {
feedbackModal.classList.add('hidden');
}
// Show upload area
function showUploadArea() {
feedbackUploadArea.classList.remove('hidden');
feedbackUrlArea.classList.add('hidden');
feedbackInstructions.classList.add('hidden');
}
// Show URL area
function showUrlArea() {
feedbackUrlArea.classList.remove('hidden');
feedbackUploadArea.classList.add('hidden');
feedbackInstructions.classList.add('hidden');
}
// Handle image upload
function handleImageUpload(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
feedbackImage.src = event.target.result;
feedbackCanvasContainer.classList.remove('hidden');
feedbackUploadArea.classList.add('hidden');
// Set up canvas for annotations
setupAnnotationCanvas();
};
reader.readAsDataURL(file);
}
// Load URL
function loadUrl() {
const url = document.getElementById('url-input').value;
if (!url) return;
// In a real app, we would load the URL content here
// For demo purposes, we'll just show a placeholder
feedbackImage.src = 'https://via.placeholder.com/800x500?text=Prototype+Preview';
feedbackCanvasContainer.classList.remove('hidden');
feedbackUrlArea.classList.add('hidden');
// Set up canvas for annotations
setupAnnotationCanvas();
}
// Set up annotation canvas
function setupAnnotationCanvas() {
// Clear any existing annotations
clearAnnotations();
// Add click handler for the canvas
feedbackCanvas.addEventListener('click', handleCanvasClick);
}
// Handle canvas click to add annotation
function handleCanvasClick(e) {
// Get position relative to the image
const rect = feedbackCanvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Create a new annotation marker
currentAnnotation = { x, y, comment: '' };
// Show the annotation form
annotationForm.classList.remove('hidden');
existingAnnotations.classList.add('hidden');
// Position the form near the click
annotationForm.style.top = `${y + 20}px`;
annotationForm.style.left = `${x + 20}px`;
}
// Clear all annotations
function clearAnnotations() {
annotations = [];
currentAnnotation = null;
renderAnnotations();
}
// Cancel current annotation
function cancelAnnotation() {
currentAnnotation = null;
annotationForm.classList.add('hidden');
}
// Submit annotation
function submitAnnotation() {
if (!currentAnnotation) return;
const comment = annotationComment.value.trim();
if (!comment) return;
currentAnnotation.comment = comment;
currentAnnotation.date = new Date().toLocaleDateString();
currentAnnotation.author = 'You';
annotations.push(currentAnnotation);
currentAnnotation = null;
annotationComment.value = '';
annotationForm.classList.add('hidden');
renderAnnotations();
}
// Render annotations
function renderAnnotations() {
// Clear existing markers
document.querySelectorAll('.annotation-marker').forEach(marker => marker.remove());
document.querySelectorAll('.annotation-tooltip').forEach(tooltip => tooltip.remove());
// Add markers for all annotations
annotations.forEach((annotation, index) => {
// Create marker
const marker = document.createElement('div');
marker.className = 'annotation-marker';
marker.style.top = `${annotation.y}px`;
marker.style.left = `${annotation.x}px`;
marker.dataset.index = index;
// Add click handler to show tooltip
marker.addEventListener('click', (e) => {
e.stopPropagation();
showAnnotationTooltip(annotation, marker);
});
feedbackCanvas.appendChild(marker);
});
// Update annotations list
annotationsList.innerHTML = '';
annotations.forEach((annotation, index) => {
const div = document.createElement('div');
div.className = 'bg-gray-50 p-3 rounded cursor-pointer hover:bg-gray-100';
div.innerHTML = `
<div class="flex justify-between items-start mb-1">
<span class="text-sm font-medium">${annotation.author}</span>
<span class="text-xs text-gray-500">${annotation.date}</span>
</div>
<p class="text-sm text-gray-600">${annotation.comment}</p>
`;
div.addEventListener('click', () => {
const marker = feedbackCanvas.querySelector(`.annotation-marker[data-index="${index}"]`);
if (marker) {
showAnnotationTooltip(annotation, marker);
}
});
annotationsList.appendChild(div);
});
// Show annotations list if there are any
if (annotations.length > 0) {
existingAnnotations.classList.remove('hidden');
} else {
existingAnnotations.classList.add('hidden');
}
}
// Show annotation tooltip
function showAnnotationTooltip(annotation, marker) {
// Remove any existing tooltip
document.querySelectorAll('.annotation-tooltip').forEach(tooltip => tooltip.remove());
// Create new tooltip
const tooltip = document.createElement('div');
tooltip.className = 'annotation-tooltip';
tooltip.style.top = `${parseInt(marker.style.top) - 10}px`;
tooltip.style.left = `${parseInt(marker.style.left) + 20}px`;
tooltip.innerHTML = `
<div class="text-sm font-medium">${annotation.author}</div>
<div class="text-xs text-gray-500 mb-1">${annotation.date}</div>
<div class="text-sm">${annotation.comment}</div>
`;
feedbackCanvas.appendChild(tooltip);
// Close tooltip when clicking outside
setTimeout(() => {
const closeTooltip = (e) => {
if (!tooltip.contains(e.target) && e.target !== marker) {
tooltip.remove();
document.removeEventListener('click', closeTooltip);
}
};
document.addEventListener('click', closeTooltip);
}, 10);
}
// Filter cards based on search and filters
function filterCards() {
const searchTerm = searchInput.value.toLowerCase();
const statusValue = statusFilter.value;
const priorityValue = priorityFilter.value;
document.querySelectorAll('.card').forEach(card => {
const title = card.querySelector('h4').textContent.toLowerCase();
const status = card.closest('.kanban-column').querySelector('h3').textContent.toLowerCase();
const priority = card.querySelector('.priority-indicator').className.includes('high') ? 'high' :
card.querySelector('.priority-indicator').className.includes('medium') ? 'medium' : 'low';
const matchesSearch = title.includes(searchTerm);
const matchesStatus = statusValue === 'all' ||
(statusValue === 'todo' && status.includes('to do')) ||
(statusValue === 'in-progress' && status.includes('in progress')) ||
(statusValue === 'done' && status.includes('done'));
const matchesPriority = priorityValue === 'all' || priority === priorityValue;
if (matchesSearch && matchesStatus && matchesPriority) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
}
// Initialize the app
init();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LukasBe/vibe-coding-backlog-driven" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>