infinite-agents-public/themed_hybrids/ui_hybrid_15.html

1236 lines
43 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Japanese Origami Project Manager</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;500;700&display=swap');
:root {
--paper-white: #FEFEF8;
--paper-cream: #FAF8F3;
--fold-shadow: rgba(0, 0, 0, 0.08);
--fold-line: rgba(0, 0, 0, 0.12);
--sakura-pink: #FFB5C5;
--bamboo-green: #8FBC8F;
--indigo-blue: #4B6584;
--gold-stamp: #D4AF37;
--ink-black: #2C3E50;
--fold-depth: 0 2px 8px rgba(0, 0, 0, 0.1);
--paper-texture: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4KPGZpbHRlciBpZD0icGFwZXIiPgo8ZmVUdXJidWxlbmNlIHR5cGU9ImZyYWN0YWxOb2lzZSIgYmFzZUZyZXF1ZW5jeT0iMC4wNCIgbnVtT2N0YXZlcz0iNSIgcmVzdWx0PSJub2lzZSIgc2VlZD0iMSIvPgo8ZmVEaWZmdXNlTGlnaHRpbmcgaW49Im5vaXNlIiBsaWdodGluZy1jb2xvcj0id2hpdGUiIHN1cmZhY2VTY2FsZT0iMSI+CjxmZURpc3RhbnRMaWdodCBhenFtdXRoPSI0NSIgZWxldmF0aW9uPSI2MCIvPgo8L2ZlRGlmZnVzZUxpZ2h0aW5nPgo8L2ZpbHRlcj4KPHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIGZpbHRlcj0idXJsKCNwYXBlcikiIG9wYWNpdHk9IjAuNCIvPgo8L3N2Zz4=');
}
body {
font-family: 'Noto Sans JP', sans-serif;
background: linear-gradient(135deg, #F5F5F0 0%, #E8E8E0 100%);
color: var(--ink-black);
min-height: 100vh;
position: relative;
overflow-x: auto;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--paper-texture);
opacity: 0.3;
pointer-events: none;
}
main {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
position: relative;
z-index: 1;
}
h1 {
font-size: 2rem;
font-weight: 700;
margin-bottom: 2rem;
color: var(--indigo-blue);
display: flex;
align-items: center;
gap: 1rem;
}
h1::before {
content: '鶴';
font-size: 2.5rem;
color: var(--sakura-pink);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.project-board {
background: var(--paper-white);
border-radius: 4px;
box-shadow: var(--fold-depth);
padding: 2rem;
position: relative;
overflow: hidden;
}
.project-board::before {
content: '';
position: absolute;
top: 0;
left: 50%;
height: 100%;
width: 1px;
background: var(--fold-line);
transform: translateX(-50%);
opacity: 0.3;
}
.board-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px dashed var(--fold-line);
}
.project-title {
font-size: 1.5rem;
font-weight: 500;
color: var(--indigo-blue);
}
.add-task-btn {
background: var(--sakura-pink);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
}
.add-task-btn::before {
content: '折';
position: absolute;
right: -20px;
top: 50%;
transform: translateY(-50%);
font-size: 3rem;
opacity: 0.1;
transition: right 0.3s ease;
}
.add-task-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.add-task-btn:hover::before {
right: 10px;
}
.kanban-container {
display: flex;
gap: 1.5rem;
overflow-x: auto;
padding-bottom: 1rem;
min-height: 500px;
}
.kanban-column {
flex: 0 0 320px;
background: var(--paper-cream);
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
position: relative;
display: flex;
flex-direction: column;
transition: transform 0.3s ease;
}
.kanban-column::before,
.kanban-column::after {
content: '';
position: absolute;
top: 0;
width: 20px;
height: 100%;
background: linear-gradient(to right, transparent, var(--fold-shadow));
pointer-events: none;
}
.kanban-column::before {
left: -20px;
transform: scaleX(-1);
}
.kanban-column::after {
right: -20px;
}
.column-header {
padding: 1rem 1.5rem;
background: rgba(255, 255, 255, 0.7);
border-bottom: 1px solid var(--fold-line);
position: relative;
}
.column-title {
font-size: 1.1rem;
font-weight: 500;
color: var(--indigo-blue);
display: flex;
align-items: center;
gap: 0.5rem;
}
.task-count {
font-size: 0.85rem;
color: #666;
font-weight: 300;
margin-left: auto;
}
.tasks-container {
flex: 1;
padding: 1rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.task-card {
background: var(--paper-white);
border-radius: 4px;
padding: 1rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
cursor: move;
transition: all 0.3s ease;
position: relative;
transform-style: preserve-3d;
}
.task-card::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 30px;
height: 30px;
background: linear-gradient(135deg, transparent 50%, var(--paper-cream) 50%);
border-radius: 0 4px 0 0;
box-shadow: -2px 2px 2px rgba(0, 0, 0, 0.05);
}
.task-card:hover {
transform: translateY(-2px) rotateX(2deg);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
}
.task-card.dragging {
opacity: 0.5;
transform: rotate(-2deg);
}
.task-priority {
position: absolute;
top: 0.5rem;
left: 0.5rem;
width: 12px;
height: 12px;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.priority-high {
background: #FF6B6B;
}
.priority-medium {
background: var(--gold-stamp);
}
.priority-low {
background: var(--bamboo-green);
}
.task-title {
font-weight: 500;
margin-bottom: 0.5rem;
color: var(--ink-black);
padding-left: 1.5rem;
}
.task-description {
font-size: 0.85rem;
color: #666;
margin-bottom: 0.75rem;
line-height: 1.4;
}
.task-meta {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 0.5rem;
}
.task-assignee {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.8rem;
}
.assignee-stamp {
width: 24px;
height: 24px;
border-radius: 4px;
background: var(--gold-stamp);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.7rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
transform: rotate(-3deg);
}
.task-actions {
display: flex;
gap: 0.5rem;
}
.task-action {
width: 24px;
height: 24px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: #999;
transition: all 0.2s ease;
border-radius: 4px;
}
.task-action:hover {
background: rgba(0, 0, 0, 0.05);
color: var(--indigo-blue);
}
.task-progress {
width: 100%;
height: 4px;
background: rgba(0, 0, 0, 0.1);
border-radius: 2px;
margin-top: 0.75rem;
overflow: hidden;
position: relative;
}
.task-progress::before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
background: var(--bamboo-green);
transition: width 0.3s ease;
width: var(--progress, 0%);
}
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease;
}
.modal-overlay.active {
display: flex;
align-items: center;
justify-content: center;
opacity: 1;
}
.modal {
background: var(--paper-white);
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
width: 90%;
max-width: 600px;
max-height: 90vh;
overflow-y: auto;
position: relative;
transform: scale(0.9) rotateX(10deg);
transition: transform 0.3s ease;
}
.modal-overlay.active .modal {
transform: scale(1) rotateX(0deg);
}
.modal-header {
padding: 1.5rem;
border-bottom: 1px dashed var(--fold-line);
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-title {
font-size: 1.25rem;
font-weight: 500;
color: var(--indigo-blue);
}
.close-modal {
width: 32px;
height: 32px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 1.5rem;
transition: all 0.2s ease;
border-radius: 50%;
}
.close-modal:hover {
background: rgba(0, 0, 0, 0.05);
color: var(--ink-black);
transform: rotate(90deg);
}
.modal-body {
padding: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
font-size: 0.9rem;
font-weight: 500;
margin-bottom: 0.5rem;
color: var(--ink-black);
}
.form-input,
.form-textarea,
.form-select {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--fold-line);
border-radius: 4px;
font-size: 0.9rem;
font-family: inherit;
background: rgba(255, 255, 255, 0.5);
transition: all 0.2s ease;
}
.form-input:focus,
.form-textarea:focus,
.form-select:focus {
outline: none;
border-color: var(--sakura-pink);
background: white;
box-shadow: 0 0 0 3px rgba(255, 181, 197, 0.2);
}
.form-textarea {
min-height: 100px;
resize: vertical;
}
.priority-selector {
display: flex;
gap: 1rem;
}
.priority-option {
flex: 1;
padding: 0.75rem;
border: 2px solid transparent;
border-radius: 4px;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.5);
}
.priority-option:hover {
transform: translateY(-2px);
}
.priority-option.selected {
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.priority-option.high {
border-color: #FF6B6B;
color: #FF6B6B;
}
.priority-option.medium {
border-color: var(--gold-stamp);
color: var(--gold-stamp);
}
.priority-option.low {
border-color: var(--bamboo-green);
color: var(--bamboo-green);
}
.attachment-area {
border: 2px dashed var(--fold-line);
border-radius: 4px;
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.3);
position: relative;
overflow: hidden;
}
.attachment-area::before {
content: '📎';
position: absolute;
font-size: 4rem;
opacity: 0.1;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.attachment-area:hover {
border-color: var(--sakura-pink);
background: rgba(255, 181, 197, 0.05);
}
.attachment-area.dragover {
border-color: var(--bamboo-green);
background: rgba(143, 188, 143, 0.1);
}
.modal-actions {
display: flex;
gap: 1rem;
justify-content: flex-end;
padding: 1.5rem;
border-top: 1px dashed var(--fold-line);
}
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
font-family: inherit;
}
.btn-primary {
background: var(--sakura-pink);
color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.btn-secondary {
background: transparent;
color: var(--ink-black);
border: 1px solid var(--fold-line);
}
.btn-secondary:hover {
background: rgba(0, 0, 0, 0.05);
}
.comments-section {
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 1px dashed var(--fold-line);
}
.comments-header {
font-weight: 500;
margin-bottom: 1rem;
color: var(--indigo-blue);
}
.comment {
background: var(--paper-cream);
padding: 1rem;
border-radius: 4px;
margin-bottom: 0.75rem;
position: relative;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.comment::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
background: linear-gradient(135deg, transparent 50%, var(--paper-white) 50%);
box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.05);
}
.comment-author {
font-weight: 500;
font-size: 0.85rem;
color: var(--indigo-blue);
margin-bottom: 0.25rem;
}
.comment-text {
font-size: 0.9rem;
color: var(--ink-black);
line-height: 1.4;
}
.comment-time {
font-size: 0.75rem;
color: #999;
margin-top: 0.5rem;
}
.add-comment {
display: flex;
gap: 0.75rem;
margin-top: 1rem;
}
.comment-input {
flex: 1;
padding: 0.5rem 0.75rem;
border: 1px solid var(--fold-line);
border-radius: 20px;
font-size: 0.85rem;
font-family: inherit;
background: rgba(255, 255, 255, 0.5);
transition: all 0.2s ease;
}
.comment-input:focus {
outline: none;
border-color: var(--sakura-pink);
background: white;
}
.comment-submit {
padding: 0.5rem 1rem;
background: var(--bamboo-green);
color: white;
border: none;
border-radius: 20px;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.2s ease;
}
.comment-submit:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.empty-state {
text-align: center;
padding: 3rem;
color: #999;
font-size: 0.9rem;
}
.empty-state::before {
content: '🏗️';
display: block;
font-size: 3rem;
margin-bottom: 1rem;
opacity: 0.5;
}
@keyframes unfold {
from {
transform: rotateX(90deg) scale(0.8);
opacity: 0;
}
to {
transform: rotateX(0deg) scale(1);
opacity: 1;
}
}
.task-card {
animation: unfold 0.4s ease-out;
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
.floating {
animation: float 3s ease-in-out infinite;
}
</style>
</head>
<body>
<main>
<h1>Project Manager - Japanese Origami Theme</h1>
<div class="project-board">
<div class="board-header">
<h2 class="project-title">Digital Product Launch 2024</h2>
<button class="add-task-btn" onclick="openTaskModal()">
<span>+ Add Task</span>
</button>
</div>
<div class="kanban-container">
<div class="kanban-column" data-status="todo">
<div class="column-header">
<div class="column-title">
<span>📋 To Do</span>
<span class="task-count">3 tasks</span>
</div>
</div>
<div class="tasks-container" ondrop="drop(event)" ondragover="allowDrop(event)">
<div class="task-card" draggable="true" ondragstart="drag(event)" data-task-id="1">
<div class="task-priority priority-high"></div>
<div class="task-title">Design System Documentation</div>
<div class="task-description">Create comprehensive documentation for the new design system components</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">JD</div>
<span>John Doe</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(1)">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 0%"></div>
</div>
<div class="task-card" draggable="true" ondragstart="drag(event)" data-task-id="2">
<div class="task-priority priority-medium"></div>
<div class="task-title">API Integration Testing</div>
<div class="task-description">Complete integration tests for payment gateway API</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">SK</div>
<span>Sarah Kim</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(2)">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 0%"></div>
</div>
<div class="task-card" draggable="true" ondragstart="drag(event)" data-task-id="3">
<div class="task-priority priority-low"></div>
<div class="task-title">Update Team Wiki</div>
<div class="task-description">Add new onboarding materials and process documentation</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">AL</div>
<span>Alex Lee</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(3)">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 0%"></div>
</div>
</div>
</div>
<div class="kanban-column" data-status="inprogress">
<div class="column-header">
<div class="column-title">
<span>🔄 In Progress</span>
<span class="task-count">2 tasks</span>
</div>
</div>
<div class="tasks-container" ondrop="drop(event)" ondragover="allowDrop(event)">
<div class="task-card" draggable="true" ondragstart="drag(event)" data-task-id="4">
<div class="task-priority priority-high"></div>
<div class="task-title">User Authentication Flow</div>
<div class="task-description">Implement OAuth2 authentication with social login options</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">MT</div>
<span>Mike Taylor</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(4)">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 65%"></div>
</div>
<div class="task-card" draggable="true" ondragstart="drag(event)" data-task-id="5">
<div class="task-priority priority-medium"></div>
<div class="task-title">Mobile Responsive Updates</div>
<div class="task-description">Optimize dashboard layouts for mobile devices</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">EC</div>
<span>Emma Chen</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(5)">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 40%"></div>
</div>
</div>
</div>
<div class="kanban-column" data-status="review">
<div class="column-header">
<div class="column-title">
<span>👁️ Review</span>
<span class="task-count">1 task</span>
</div>
</div>
<div class="tasks-container" ondrop="drop(event)" ondragover="allowDrop(event)">
<div class="task-card" draggable="true" ondragstart="drag(event)" data-task-id="6">
<div class="task-priority priority-high"></div>
<div class="task-title">Performance Optimization</div>
<div class="task-description">Code review for database query optimization changes</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">RJ</div>
<span>Ryan Johnson</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(6)">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 90%"></div>
</div>
</div>
</div>
<div class="kanban-column" data-status="done">
<div class="column-header">
<div class="column-title">
<span>✅ Done</span>
<span class="task-count">2 tasks</span>
</div>
</div>
<div class="tasks-container" ondrop="drop(event)" ondragover="allowDrop(event)">
<div class="task-card" draggable="true" ondragstart="drag(event)" data-task-id="7">
<div class="task-priority priority-medium"></div>
<div class="task-title">CI/CD Pipeline Setup</div>
<div class="task-description">Automated deployment pipeline with GitHub Actions</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">LW</div>
<span>Lisa Wang</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(7)">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 100%"></div>
</div>
<div class="task-card" draggable="true" ondragstart="drag(event)" data-task-id="8">
<div class="task-priority priority-low"></div>
<div class="task-title">Security Audit Preparation</div>
<div class="task-description">Prepare documentation for Q4 security audit</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">DM</div>
<span>David Miller</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(8)">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 100%"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Task Modal -->
<div class="modal-overlay" id="taskModal">
<div class="modal">
<div class="modal-header">
<h3 class="modal-title">Create New Task</h3>
<button class="close-modal" onclick="closeTaskModal()">×</button>
</div>
<div class="modal-body">
<form id="taskForm">
<div class="form-group">
<label class="form-label">Task Title</label>
<input type="text" class="form-input" id="taskTitle" placeholder="Enter task title...">
</div>
<div class="form-group">
<label class="form-label">Description</label>
<textarea class="form-textarea" id="taskDescription" placeholder="Describe the task..."></textarea>
</div>
<div class="form-group">
<label class="form-label">Priority</label>
<div class="priority-selector">
<div class="priority-option high" onclick="selectPriority('high')">
High
</div>
<div class="priority-option medium selected" onclick="selectPriority('medium')">
Medium
</div>
<div class="priority-option low" onclick="selectPriority('low')">
Low
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">Assignee</label>
<select class="form-select" id="taskAssignee">
<option value="">Select assignee...</option>
<option value="JD">John Doe</option>
<option value="SK">Sarah Kim</option>
<option value="AL">Alex Lee</option>
<option value="MT">Mike Taylor</option>
<option value="EC">Emma Chen</option>
<option value="RJ">Ryan Johnson</option>
<option value="LW">Lisa Wang</option>
<option value="DM">David Miller</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Attachments</label>
<div class="attachment-area" ondrop="dropFile(event)" ondragover="dragOverFile(event)" ondragleave="dragLeaveFile(event)">
<p>Drag and drop files here or click to browse</p>
<input type="file" id="fileInput" style="display: none;" multiple>
</div>
</div>
</form>
</div>
<div class="modal-actions">
<button class="btn btn-secondary" onclick="closeTaskModal()">Cancel</button>
<button class="btn btn-primary" onclick="createTask()">Create Task</button>
</div>
</div>
</div>
<!-- Task Details Modal -->
<div class="modal-overlay" id="detailsModal">
<div class="modal">
<div class="modal-header">
<h3 class="modal-title" id="detailsTitle">Task Details</h3>
<button class="close-modal" onclick="closeDetailsModal()">×</button>
</div>
<div class="modal-body">
<div id="taskDetailsContent">
<!-- Task details will be populated here -->
</div>
<div class="comments-section">
<h4 class="comments-header">Comments</h4>
<div id="commentsContainer">
<div class="comment">
<div class="comment-author">Sarah Kim</div>
<div class="comment-text">Great progress on this! The API endpoints look solid.</div>
<div class="comment-time">2 hours ago</div>
</div>
<div class="comment">
<div class="comment-author">Mike Taylor</div>
<div class="comment-text">I've reviewed the code. Just a minor suggestion on error handling.</div>
<div class="comment-time">1 hour ago</div>
</div>
</div>
<div class="add-comment">
<input type="text" class="comment-input" placeholder="Add a comment..." id="newComment">
<button class="comment-submit" onclick="addComment()">Send</button>
</div>
</div>
</div>
</div>
</div>
</main>
<script>
// Drag and Drop functionality
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("taskId", ev.target.dataset.taskId);
ev.target.classList.add('dragging');
}
function drop(ev) {
ev.preventDefault();
const taskId = ev.dataTransfer.getData("taskId");
const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
const targetContainer = ev.target.closest('.tasks-container');
if (targetContainer && taskElement) {
targetContainer.appendChild(taskElement);
taskElement.classList.remove('dragging');
updateTaskCounts();
updateProgress(taskElement, targetContainer.parentElement.dataset.status);
}
}
function updateTaskCounts() {
document.querySelectorAll('.kanban-column').forEach(column => {
const count = column.querySelector('.tasks-container').children.length;
column.querySelector('.task-count').textContent = `${count} task${count !== 1 ? 's' : ''}`;
});
}
function updateProgress(taskElement, status) {
const progressBar = taskElement.querySelector('.task-progress');
let progress = 0;
switch(status) {
case 'todo':
progress = 0;
break;
case 'inprogress':
progress = 50;
break;
case 'review':
progress = 90;
break;
case 'done':
progress = 100;
break;
}
progressBar.style.setProperty('--progress', `${progress}%`);
}
// Modal functionality
function openTaskModal() {
document.getElementById('taskModal').classList.add('active');
}
function closeTaskModal() {
document.getElementById('taskModal').classList.remove('active');
document.getElementById('taskForm').reset();
}
function selectPriority(priority) {
document.querySelectorAll('.priority-option').forEach(opt => {
opt.classList.remove('selected');
});
document.querySelector(`.priority-option.${priority}`).classList.add('selected');
}
function createTask() {
const title = document.getElementById('taskTitle').value;
const description = document.getElementById('taskDescription').value;
const assignee = document.getElementById('taskAssignee').value;
const priority = document.querySelector('.priority-option.selected').classList[1];
if (!title || !assignee) {
alert('Please fill in all required fields');
return;
}
// Create new task card
const taskId = Date.now();
const taskCard = document.createElement('div');
taskCard.className = 'task-card';
taskCard.draggable = true;
taskCard.dataset.taskId = taskId;
taskCard.ondragstart = drag;
const assigneeNames = {
'JD': 'John Doe',
'SK': 'Sarah Kim',
'AL': 'Alex Lee',
'MT': 'Mike Taylor',
'EC': 'Emma Chen',
'RJ': 'Ryan Johnson',
'LW': 'Lisa Wang',
'DM': 'David Miller'
};
taskCard.innerHTML = `
<div class="task-priority priority-${priority}"></div>
<div class="task-title">${title}</div>
<div class="task-description">${description}</div>
<div class="task-meta">
<div class="task-assignee">
<div class="assignee-stamp">${assignee}</div>
<span>${assigneeNames[assignee]}</span>
</div>
<div class="task-actions">
<button class="task-action" onclick="viewTaskDetails(${taskId})">💬</button>
<button class="task-action">📎</button>
</div>
</div>
<div class="task-progress" style="--progress: 0%"></div>
`;
// Add to todo column
document.querySelector('[data-status="todo"] .tasks-container').appendChild(taskCard);
updateTaskCounts();
closeTaskModal();
}
function viewTaskDetails(taskId) {
const task = document.querySelector(`[data-task-id="${taskId}"]`);
const title = task.querySelector('.task-title').textContent;
const description = task.querySelector('.task-description').textContent;
document.getElementById('detailsTitle').textContent = title;
document.getElementById('taskDetailsContent').innerHTML = `
<p>${description}</p>
<div style="margin-top: 1rem;">
<strong>Status:</strong> ${task.closest('.kanban-column').querySelector('.column-title span').textContent}
</div>
`;
document.getElementById('detailsModal').classList.add('active');
}
function closeDetailsModal() {
document.getElementById('detailsModal').classList.remove('active');
}
function addComment() {
const commentInput = document.getElementById('newComment');
const text = commentInput.value.trim();
if (!text) return;
const comment = document.createElement('div');
comment.className = 'comment';
comment.innerHTML = `
<div class="comment-author">You</div>
<div class="comment-text">${text}</div>
<div class="comment-time">Just now</div>
`;
document.getElementById('commentsContainer').appendChild(comment);
commentInput.value = '';
}
// File upload functionality
const attachmentArea = document.querySelector('.attachment-area');
const fileInput = document.getElementById('fileInput');
attachmentArea.addEventListener('click', () => {
fileInput.click();
});
function dragOverFile(ev) {
ev.preventDefault();
ev.target.closest('.attachment-area').classList.add('dragover');
}
function dragLeaveFile(ev) {
ev.target.closest('.attachment-area').classList.remove('dragover');
}
function dropFile(ev) {
ev.preventDefault();
ev.target.closest('.attachment-area').classList.remove('dragover');
const files = ev.dataTransfer.files;
handleFiles(files);
}
fileInput.addEventListener('change', (e) => {
handleFiles(e.target.files);
});
function handleFiles(files) {
console.log('Files uploaded:', files);
// Handle file upload logic here
}
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeTaskModal();
closeDetailsModal();
}
if (e.key === 'n' && e.ctrlKey) {
e.preventDefault();
openTaskModal();
}
});
// Add floating animation to certain elements
document.querySelectorAll('.assignee-stamp').forEach((stamp, index) => {
stamp.style.animationDelay = `${index * 0.5}s`;
stamp.classList.add('floating');
});
// Initialize
updateTaskCounts();
</script>
</body>
</html>