Wathuta commited on
Commit
cdc7470
·
verified ·
1 Parent(s): 20ece93

### UX design

Browse files

#### Calendar View Core Layout Architecture

The calendar view is built around a **column-per-team-member layout** that provides immediate visual ownership and workload distribution. This design is critical for project management efficiency as it enables:

- **Individual Accountability**: Each team member's work is contained in their dedicated column, making responsibilities clear
- **Workload Balancing**: Project managers can instantly see uneven task distribution across team members
- **Dependency Tracking**: Cross-column relationships become visually apparent when tasks depend on multiple people
- **Resource Planning**: PTO, on-call, and availability patterns are immediately visible per person

#### Grid Structure Specification

```
┌─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬──────────────┐
│ Date │ Team Member │ Team Member │ Team Member │ Team Member │ Milestones │
│ │ A │ B │ C │ D │ │
├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼──────────────┤
│ Mon 1/15 │ [Cell A1] │ [Cell B1] │ [Cell C1] │ [Cell D1] │ [Mile 1] │
│ Tue 1/16 │ [Cell A2] │ [Cell B2] │ [Cell C2] │ [Cell D2] │ [Mile 2] │
│ Wed 1/17 │ [Cell A3] │ [Cell B3] │ [Cell C3] │ [Cell D3] │ [Mile 3] │
└─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴──────────────┘
```

#### Column Specifications

**Date Column (Fixed Left)**

- Width: Fixed 120px minimum, non-resizable
- Content: `Day MM/DD/YYYY` format (e.g., "Mon 1/15")
- Styling: Sticky positioning, always visible during horizontal scroll
- Background: Light gray (#F8F9FA) to distinguish from content columns

**Team Member Columns (Dynamic)**

- Width: 200px default, resizable between 150px-400px
- Header: Team member company alias (e.g. johndoe)
- Order: Alphabetical by default, drag-to-reorder enabled
- Add/Remove: "+" button after last column, "×" button on column header hover

**Milestone Column (Fixed Right)**

- Width: Fixed 180px, non-resizable
- Content: Project milestones, deadlines, company holidays
- Styling: Sticky positioning on right edge
- Background: Distinct accent color (#E3F2FD)

#### Cell-Level Functionality and Content Hierarchy

Each intersection of team member column and date row creates a **multi-functional cell** that serves as the primary interaction unit. Cells must accommodate overlapping information types with clear visual priority:

**Primary Content Layer (Always Visible)**

1. Task Title: Maximum 2 lines, 14px font, truncate with ellipsis
2. Status Indicator: 8px colored circle in top-left corner

- Green: Complete
- Blue: In Progress
- Orange: Blocked
- Gray: Not Started

**Secondary Content Layer (Contextual)**

3. Task Type Icon: 16px icon in top-right corner
4. Link Count Badge: "🔗 3" format in bottom-right if attachments exist
5. Time Estimate: "4h" format in bottom-left if specified

**Background State Layer (Full Cell)**

- Normal: White background with subtle border (#E0E0E0)
- PTO: Diagonal stripe pattern (#F5F5F5 / #E0E0E0)
- On-call: Solid yellow background (#FFF9C4)
- Holiday: Light blue background (#E3F2FD)
- Conference: Purple background
- Multiple States: Layered approach (PTO stripes over on-call yellow)

**Cell Content Overflow Handling**

- Vertical Overflow (Multiple Tasks): Stack max 3 tasks, show "+2 more" indicator
- Horizontal Overflow (Long Names): Ellipsis truncation, full text on hover tooltip
- Expansion: Click "+X more" to expand cell or show popup list

#### Interactive Features and Behaviors

**Cell Selection System**

- Single Select: Click cell → blue border (2px #1976D2)
- Multi Select: Ctrl+Click → multiple blue borders maintained
- Range Select: Shift+Click → select continuous range between first and last clicked
- Clear Selection: Click empty area or press Escape

**Drag and Drop Operations**

- Draggable Elements: Individual task items within cells, not entire cells
- Drop Zones: Any valid cell in any team member column
- Visual Feedback During Drag:
- Dragged Item: Semi-transparent copy (50% opacity)
- Valid Drop Zone: Green border highlight (2px #4CAF50)
- Invalid Drop Zone: Red border highlight (2px #F44336)
- Drop Constraints: Cannot drop on PTO days, weekends (if hidden), or dates with scheduling conflicts

**Dependency Visualization System**

- Trigger: Hover or select any cell containing tasks with dependencies
- Visual Representation: Curved connecting lines (2px #757575) between related cells
- Direction Indicators: Arrow heads showing dependency flow direction
- Multi-level Dependencies: Different colors for direct (dark) vs indirect (light) dependencies
- Cross-Sprint Dependencies: Dotted lines when dependencies span sprint boundaries

#### Navigation and Controls System

**Top Navigation Bar**

- Date Range Selector: Calendar widget with preset options (This Week, Next 2 Weeks, This Quarter, Custom Range)
- Sprint Boundary Controls: Toggle buttons for "Show Sprint Lines" and "Edit Sprint Boundaries"
- View Options: Checkboxes for "Show Weekends", "Show Holidays", "Compact View"
- Team Member Management: Dropdown to filter visible columns, "Add Team Member" button
- Export Actions: "Export to Excel/CSV/Markdown", "Share Link", "Print View" options

**Quick Action Interface**

- Floating Action Button: Fixed position "+" button (bottom-right) for rapid task creation
- Context Menu System: Right-click on any cell reveals contextual actions (Edit, Copy, Move, Delete, Add Dependency)
- Keyboard Navigation: Arrow keys for cell navigation, Tab for column switching, Enter to edit, Delete to remove

#### Advanced Cell Features

**Sprint Boundary Visualization**

- Visual Treatment: Horizontal lines (3px #2196F3) spanning full calendar width
- Positioning: Lines appear between date rows at sprint start/end boundaries
- Labels: Sprint names displayed in left margin aligned with sprint start date
- Interaction: Click line to edit sprint dates, hover shows sprint details tooltip

**Multi-Task Cell Management**

- Task Stacking: Up to 3 tasks visible per cell, additional tasks collapsed with "+X more" indicator
- Task Prioritization: Drag tasks within cell to reorder by priority
- Quick Task Creation: Double-click empty cell area to create new task
- Task Splitting: Drag task to multiple cells to create multi-day assignments

**Conflict Detection and Indicators**

- PTO vs Assignment Conflicts: Red corner triangle with "!" when tasks are assigned during PTO
- On-call vs PTO Conflicts: Orange border when on-call duty overlaps with requested PTO
- Overallocation Warnings: Yellow background fade when team member has >8 hours of tasks in one day
- Dependency Conflicts: Purple dotted border when task dependencies create scheduling impossibilities

#### Desktop-Optimized Design Specifications

**Primary Target: Desktop/Laptop (1200px+ width)**

- **Full Calendar Grid**: Complete calendar grid visible without horizontal scrolling on standard laptop screens (1366px+)
- **Multi-Column Layout**: All team member columns displayed simultaneously for comprehensive team overview
- **Rich Interactions**: Complete feature set enabled including drag/drop, multi-select, keyboard shortcuts, and hover states
- **Detailed Information**: Full tooltips, status indicators, and contextual information always visible
- **Power User Features**: Advanced keyboard navigation, bulk operations, and command shortcuts optimized for productivity
- **High Information Density**: Maximize visible information while maintaining readability and usability
- **Professional Interface**: Clean, business-focused design suitable for engineering team environments
- **Multi-Monitor Support**: Layout scales effectively on large displays and multi-monitor setups

#### Visual Design System

**Color Palette and Usage**

- **Primary Colors**: Blue theme (#1976D2 for selections, #2196F3 for sprint boundaries)
- **Status Colors**: Green (#4CAF50) complete, Blue (#2196F3) in-progress, Orange (#FF9800) blocked, Gray (#757575) not-started
- **Background Colors**: White (#FFFFFF) normal cells, Light gray (#F8F9FA) date column, Yellow (#FFF9C4) on-call, Blue (#E3F2FD) holidays/milestones
- **Conflict Indicators**: Red (#F44336) for conflicts, Orange (#FF9800) for warnings, Purple (#9C27B0) for dependency issues
- **Interactive States**: Green borders (#4CAF50) valid drop zones, Red borders (#F44336) invalid drop zones

**Typography Specifications**

- **Primary Font**: `Atkinson Hyperlegible Next` for all text content
- **Task Titles**: 14px weight-500, maximum 2 lines with ellipsis overflow
- **Column Headers**: 16px weight-600, team member names with 32px circular avatars
- **Date Labels**: 12px weight-400, format "Day MM/DD" (e.g., "Mon 1/15")
- **Status Indicators**: 10px weight-400 for badges and time estimates
- **Milestone Text**: 14px weight-600 with distinct styling for importance

**Visual Hierarchy Rules**

- **Information Priority**: Task titles > Status indicators > Type icons > Time estimates > Link counts
- **Interactive Priority**: Selection borders > Conflict indicators > Status colors > Background states
- **Spatial Priority**: Primary content layer > Secondary content layer > Background state layer
- **Attention Management**: Animations only for state changes, subtle hover effects, clear focus indicators for accessibility

#### Command Panel Layout & Behavior

**Side Panel Architecture (Right Side)**
The command panel is implemented as a collapsible side panel that provides structured access to all project management commands. This design prioritizes discoverability, efficiency, and learning.

**Collapsed State (Right edge

README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Taskflow Calendar Commander
3
- emoji: 🏆
4
- colorFrom: red
5
  colorTo: red
 
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: TaskFlow Calendar Commander 🗓️
3
+ colorFrom: pink
 
4
  colorTo: red
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/calendar-header.js ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomCalendarHeader extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ :host {
7
+ display: block;
8
+ background: white;
9
+ border-bottom: 1px solid #e5e7eb;
10
+ }
11
+
12
+ .header-content {
13
+ padding: 1rem 1.5rem;
14
+ }
15
+
16
+ .action-buttons {
17
+ display: flex;
18
+ gap: 0.5rem;
19
+ align-items: center;
20
+ }
21
+
22
+ .btn {
23
+ padding: 0.5rem 1rem;
24
+ border-radius: 0.375rem;
25
+ font-size: 0.875rem;
26
+ font-weight: 500;
27
+ transition: all 0.2s;
28
+ cursor: pointer;
29
+ }
30
+
31
+ .btn-primary {
32
+ background: #0ea5e9;
33
+ color: white;
34
+ border: none;
35
+ }
36
+
37
+ .btn-primary:hover {
38
+ background: #0284c7;
39
+ }
40
+
41
+ .btn-outline {
42
+ background: white;
43
+ color: #374151;
44
+ border: 1px solid #d1d5db;
45
+ }
46
+
47
+ .btn-outline:hover {
48
+ background: #f9fafb;
49
+ border-color: #9ca3af;
50
+ }
51
+ </style>
52
+ <div class="header-content">
53
+ <div class="flex justify-between items-center">
54
+ <div class="flex items-center space-x-4">
55
+ <h2 class="text-xl font-semibold text-gray-800" id="calendar-title">This Week</h2>
56
+ <div class="text-sm text-gray-500">Showing 5 days, 4 team members</div>
57
+ </div>
58
+
59
+ <div class="action-buttons">
60
+ <button class="btn btn-outline flex items-center space-x-2">
61
+ <i data-feather="users"></i>
62
+ <span>Team</span>
63
+ </button>
64
+
65
+ <button class="btn btn-outline flex items-center space-x-2">
66
+ <i data-feather="download"></i>
67
+ <span>Export</span>
68
+ </button>
69
+
70
+ <button class="btn btn-primary flex items-center space-x-2">
71
+ <i data-feather="plus"></i>
72
+ <span>Add Member</span>
73
+ </button>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ `;
78
+
79
+ // Initialize feather icons
80
+ if (typeof feather !== 'undefined') {
81
+ feather.replace();
82
+ }
83
+ }
84
+ }
85
+
86
+ customElements.define('custom-calendar-header', CustomCalendarHeader);
components/command-panel.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomCommandPanel extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.isOpen = false;
5
+ }
6
+
7
+ connectedCallback() {
8
+ this.attachShadow({ mode: 'open' });
9
+ this.render();
10
+ this.setupEventListeners();
11
+ }
12
+
13
+ render() {
14
+ this.shadowRoot.innerHTML = `
15
+ <style>
16
+ :host {
17
+ display: block;
18
+ position: relative;
19
+ }
20
+
21
+ .panel-toggle {
22
+ position: fixed;
23
+ right: 0;
24
+ top: 50%;
25
+ transform: translateY(-50%);
26
+ background: #0ea5e9;
27
+ color: white;
28
+ padding: 1rem 0.5rem;
29
+ border-radius: 0.5rem 0 0 0.5rem;
30
+ writing-mode: vertical-rl;
31
+ cursor: pointer;
32
+ z-index: 40;
33
+ transition: all 0.3s ease;
34
+ box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
35
+ }
36
+
37
+ .panel-toggle:hover {
38
+ background: #0284c7;
39
+ }
40
+
41
+ .command-panel {
42
+ position: fixed;
43
+ right: 0;
44
+ top: 0;
45
+ height: 100vh;
46
+ width: 400px;
47
+ background: white;
48
+ border-left: 1px solid #e5e7eb;
49
+ box-shadow: -4px 0 16px rgba(0, 0, 0, 0.1);
50
+ transform: translateX(100%);
51
+ transition: transform 0.3s ease-in-out;
52
+ z-index: 50;
53
+ }
54
+
55
+ .panel-open {
56
+ transform: translateX(0);
57
+ }
58
+
59
+ .search-input {
60
+ width: 100%;
61
+ padding: 0.75rem 1rem;
62
+ border: 1px solid #
components/navbar.js ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ :host {
7
+ display: block;
8
+ position: fixed;
9
+ top: 0;
10
+ left: 0;
11
+ right: 0;
12
+ z-index: 1000;
13
+ }
14
+
15
+ .navbar {
16
+ background: white;
17
+ border-bottom: 1px solid #e5e7eb;
18
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
19
+ }
20
+
21
+ .logo {
22
+ font-weight: 700;
23
+ font-size: 1.25rem;
24
+ background: linear-gradient(135deg, #0ea5e9, #3b82f6);
25
+ -webkit-background-clip: text;
26
+ -webkit-text-fill-color: transparent;
27
+ }
28
+ </style>
29
+ <nav class="navbar">
30
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
31
+ <div class="flex justify-between items-center h-16">
32
+ <!-- Logo and Brand -->
33
+ <div class="flex items-center space-x-4">
34
+ <div class="logo">TaskFlow</div>
35
+ <span class="text-gray-500">Calendar Commander</span>
36
+ </div>
37
+
38
+ <!-- Navigation Items -->
39
+ <div class="flex items-center space-x-6">
40
+ <!-- Date Range Selector -->
41
+ <div class="relative">
42
+ <select id="date-range-select" class="appearance-none bg-white border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
43
+ <option value="this-week">This Week</option>
44
+ <option value="next-2-weeks">Next 2 Weeks</option>
45
+ <option value="this-quarter">This Quarter</option>
46
+ <option value="custom">Custom Range</option>
47
+ </select>
48
+ <i data-feather="chevron-down" class="absolute right-2 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400"></i>
49
+ </div>
50
+
51
+ <!-- View Options -->
52
+ <div class="flex items-center space-x-2">
53
+ <label class="inline-flex items-center">
54
+ <input type="checkbox" class="rounded border-gray-300 text-primary-600 focus:ring-primary-500">
55
+ <span class="ml-2 text-sm text-gray-700">Weekends</span>
56
+ </label>
57
+ <label class="inline-flex items-center">
58
+ <input type="checkbox" class="rounded border-gray-300 text-primary-600 focus:ring-primary-500">
59
+ <span class="ml-2 text-sm text-gray-700">Holidays</span>
60
+ </label>
61
+ </div>
62
+
63
+ <!-- User Menu -->
64
+ <div class="relative">
65
+ <button id="user-menu-button" class="flex items-center space-x-2 text-sm focus:outline-none">
66
+ <img src="http://static.photos/people/200x200/5" alt="User" class="w-8 h-8 rounded-full">
67
+ <span class="text-gray-700">Admin</span>
68
+ <i data-feather="chevron-down" class="w-4 h-4 text-gray-400"></i>
69
+ </button>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </nav>
74
+ `;
75
+
76
+ // Add event listeners
77
+ this.shadowRoot.getElementById('date-range-select')?.addEventListener('change', (e) => {
78
+ currentDateRange = e.target.value;
79
+ initializeCalendar();
80
+ });
81
+
82
+ // Initialize feather icons
83
+ if (typeof feather !== 'undefined') {
84
+ feather.replace();
85
+ }
86
+ }
87
+ }
88
+
89
+ customElements.define('custom-navbar', CustomNavbar);
index.html CHANGED
@@ -1,19 +1,83 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>TaskFlow Calendar - Project Management Dashboard</title>
7
+ <link rel="icon" type="image/x-icon" href="/static/favicon.ico">
8
+ <link rel="stylesheet" href="style.css">
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
11
+ <script src="https://unpkg.com/feather-icons"></script>
12
+ <script>
13
+ tailwind.config = {
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ primary: {
18
+ 50: '#f0f9ff',
19
+ 100: '#e0f2fe',
20
+ 200: '#bae6fd',
21
+ 300: '#7dd3fc',
22
+ 400: '#38bdf8',
23
+ 500: '#0ea5e9',
24
+ 600: '#0284c7',
25
+ 700: '#0369a1',
26
+ 800: '#075985',
27
+ 900: '#0c4a6e',
28
+ },
29
+ secondary: {
30
+ 50: '#f8fafc',
31
+ 100: '#f1f5f9',
32
+ 200: '#e2e8f0',
33
+ 300: '#cbd5e1',
34
+ 400: '#94a3b8',
35
+ 500: '#64748b',
36
+ 600: '#475569',
37
+ 700: '#334155',
38
+ 800: '#1e293b',
39
+ 900: '#0f172a',
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ </script>
46
+ </head>
47
+ <body class="bg-gray-50 font-sans">
48
+ <custom-navbar></custom-navbar>
49
+ <main class="flex h-screen pt-16">
50
+ <!-- Calendar Container -->
51
+ <div class="flex-1 overflow-auto">
52
+ <custom-calendar-header></custom-calendar-header>
53
+ <div id="calendar-grid" class="bg-white border border-gray-200 rounded-lg shadow-sm m-4">
54
+ <!-- Calendar grid will be populated by JavaScript -->
55
+ </div>
56
+ </div>
57
+
58
+ <!-- Command Panel -->
59
+ <custom-command-panel></custom-command-panel>
60
+ </main>
61
+
62
+ <!-- Floating Action Button -->
63
+ <div class="fixed bottom-6 right-6 z-50">
64
+ <button id="quick-add-btn" class="bg-primary-600 hover:bg-primary-700 text-white p-4 rounded-full shadow-lg transition-all duration-200 transform hover:scale-110">
65
+ <i data-feather="plus"></i>
66
+ </button>
67
+ </div>
68
+
69
+ <script src="components/navbar.js"></script>
70
+ <script src="components/calendar-header.js"></script>
71
+ <script src="components/command-panel.js"></script>
72
+ <script src="script.js"></script>
73
+ <script>
74
+ feather.replace();
75
+
76
+ // Initialize calendar
77
+ document.addEventListener('DOMContentLoaded', function() {
78
+ initializeCalendar();
79
+ });
80
+ </script>
81
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
82
+ </body>
83
+ </html>
script.js ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Global variables
2
+ let currentDateRange = 'this-week';
3
+ let selectedCells = new Set();
4
+ let isCommandPanelOpen = false;
5
+ let teamMembers = [
6
+ { id: 'alice', name: 'Alice Smith', avatar: 'http://static.photos/people/200x200/1', capacity: 8 },
7
+ { id: 'bob', name: 'Bob Wilson', avatar: 'http://static.photos/people/200x200/2', capacity: 6 },
8
+ { id: 'charlie', name: 'Charlie Brown', avatar: 'http://static.photos/people/200x200/3', capacity: 8 },
9
+ { id: 'diana', name: 'Diana Prince', avatar: 'http://static.photos/people/200x200/4', capacity: 7 }
10
+ ];
11
+
12
+ let tasks = [
13
+ { id: 1, title: 'API Integration', assignee: 'alice', date: getDateString(new Date()), status: 'in-progress', priority: 'high', timeEstimate: 4, links: 2 },
14
+ { id: 2, title: 'Database Migration', assignee: 'bob', date: getDateString(new Date()), status: 'not-started', priority: 'medium', timeEstimate: 6, links: 0 },
15
+ { id: 3, title: 'Frontend Refactor', assignee: 'charlie', date: getDateString(new Date()), status: 'complete', priority: 'low', timeEstimate: 8, links: 1 },
16
+ { id: 4, title: 'Security Audit', assignee: 'diana', date: getDateString(new Date()), status: 'blocked', priority: 'high', timeEstimate: 3, links: 3 }
17
+ ];
18
+
19
+ // Utility functions
20
+ function getDateString(date) {
21
+ return date.toISOString().split('T')[0];
22
+ }
23
+
24
+ function formatDateDisplay(date) {
25
+ const options = { weekday: 'short', month: 'numeric', day: 'numeric' };
26
+ return date.toLocaleDateString('en-US', options);
27
+ }
28
+
29
+ function getDateRange(rangeType) {
30
+ const today = new Date();
31
+ const start = new Date(today);
32
+ const end = new Date(today);
33
+
34
+ switch(rangeType) {
35
+ case 'this-week':
36
+ start.setDate(today.getDate() - today.getDay() + 1);
37
+ end.setDate(start.getDate() + 4);
38
+ break;
39
+ case 'next-2-weeks':
40
+ start.setDate(today.getDate() - today.getDay() + 1);
41
+ end.setDate(start.getDate() + 9);
42
+ break;
43
+ case 'this-quarter':
44
+ const quarter = Math.floor(today.getMonth() / 3);
45
+ start.setMonth(quarter * 3, 1);
46
+ end.setMonth((quarter + 1) * 3, 0);
47
+ break;
48
+ default:
49
+ start.setDate(today.getDate() - today.getDay() + 1);
50
+ end.setDate(start.getDate() + 4);
51
+ }
52
+
53
+ return { start, end };
54
+ }
55
+
56
+ function generateDates(startDate, endDate) {
57
+ const dates = [];
58
+ const current = new Date(startDate);
59
+
60
+ while (current <= endDate) {
61
+ dates.push(new Date(current));
62
+ current.setDate(current.getDate() + 1);
63
+ }
64
+
65
+ return dates;
66
+ }
67
+
68
+ // Calendar initialization
69
+ function initializeCalendar() {
70
+ const calendarGrid = document.getElementById('calendar-grid');
71
+ const { start, end } = getDateRange(currentDateRange);
72
+ const dates = generateDates(start, end);
73
+
74
+ // Create calendar structure
75
+ let calendarHTML = `
76
+ <div class="calendar-grid grid" style="
77
+ grid-template-columns: 120px repeat(${teamMembers.length}, 200px) 180px;
78
+ min-width: ${120 + (teamMembers.length * 200) + 180}px;
79
+ ">
80
+ <!-- Date Column Header -->
81
+ <div class="sticky left-0 z-20 bg-gray-50 border-r border-gray-200 p-3 font-semibold text-gray-700">
82
+ Date
83
+ </div>
84
+ `;
85
+
86
+ // Team Member Column Headers
87
+ teamMembers.forEach(member => {
88
+ calendarHTML += `
89
+ <div class="team-member-header bg-white border-r border-gray-200 p-3 flex items-center justify-between sticky top-0 z-10">
90
+ <div class="flex items-center space-x-3">
91
+ <img src="${member.avatar}" alt="${member.name}" class="w-8 h-8 rounded-full">
92
+ <span class="font-semibold text-gray-800">${member.id}</span>
93
+ </div>
94
+ <button class="text-gray-400 hover:text-gray-600 transition-colors">
95
+ <i data-feather="x" class="w-4 h-4"></i>
96
+ </button>
97
+ </div>
98
+ `;
99
+ });
100
+
101
+ // Milestone Column Header
102
+ calendarHTML += `
103
+ <div class="sticky right-0 z-20 bg-blue-50 border-l border-blue-200 p-3 font-semibold text-blue-800">
104
+ Milestones
105
+ </div>
106
+ </div>
107
+ `;
108
+
109
+ // Date Rows
110
+ dates.forEach(date => {
111
+ const dateStr = getDateString(date);
112
+ const isWeekend = date.getDay() === 0 || date.getDay() === 6;
113
+
114
+ calendarHTML += `
115
+ <!-- Date Cell -->
116
+ <div class="sticky left-0 z-10 bg-gray-50 border-r border-gray-200 p-2 text-sm text-gray-600 ${isWeekend ? 'bg-gray-100' : ''}">
117
+ ${formatDateDisplay(date)}
118
+ </div>
119
+ `;
120
+
121
+ // Team Member Cells
122
+ teamMembers.forEach(member => {
123
+ const cellTasks = tasks.filter(task =>
124
+ task.assignee === member.id && task.date === dateStr
125
+ );
126
+
127
+ calendarHTML += `
128
+ <div class="cell bg-white border-r border-gray-200 p-1 min-h-16 relative ${isWeekend ? 'bg-gray-50' : ''}"
129
+ data-date="${dateStr}"
130
+ data-assignee="${member.id}"
131
+ onclick="handleCellClick(event, '${dateStr}', '${member.id}')"
132
+ ondragover="handleDragOver(event)"
133
+ ondrop="handleDrop(event, '${dateStr}', '${member.id}')"
134
+ >
135
+ ${renderCellContent(cellTasks)}
136
+ </div>
137
+ `;
138
+ });
139
+
140
+ // Milestone Cell
141
+ calendarHTML += `
142
+ <div class="sticky right-0 z-10 bg-blue-50 border-l border-blue-200 p-2 ${isWeekend ? 'bg-blue-100' : ''}">
143
+ <!-- Milestone content -->
144
+ </div>
145
+ `;
146
+ });
147
+
148
+ calendarGrid.innerHTML = calendarHTML;
149
+ feather.replace();
150
+ }
151
+
152
+ function renderCellContent(cellTasks) {
153
+ if (cellTasks.length === 0) return '';
154
+
155
+ let content = '';
156
+ const visibleTasks = cellTasks.slice(0, 3);
157
+ const hiddenCount = cellTasks.length - 3;
158
+
159
+ visibleTasks.forEach(task => {
160
+ content += `
161
+ <div class="task-item bg-white border border-gray-200 rounded p-1 mb-1 text-xs cursor-pointer draggable="true"
162
+ ondragstart="handleDragStart(event, ${task.id})"
163
+ >
164
+ <div class="flex items-center justify-between">
165
+ <div class="flex items-center space-x-1">
166
+ <div class="status-indicator w-2 h-2 rounded-full status-${task.status}"></div>
167
+ <span class="task-title flex-1 truncate">${task.title}</span>
168
+ </div>
169
+ <div class="flex items-center justify-between mt-1">
170
+ <span class="time-estimate text-gray-500">${task.timeEstimate}h</span>
171
+ ${task.links > 0 ? `<span class="link-count text-blue-500">🔗 ${task.links}</span> : ''}
172
+ </div>
173
+ </div>
174
+ </div>
175
+ `;
176
+ });
177
+
178
+ if (hiddenCount > 0) {
179
+ content += `
180
+ <div class="more-indicator text-center text-gray-400 text-xs py-1">
181
+ +${hiddenCount} more
182
+ </div>
183
+ `;
184
+ }
185
+
186
+ return content;
187
+ }
188
+
189
+ // Event Handlers
190
+ function handleCellClick(event, date, assignee) {
191
+ event.stopPropagation();
192
+
193
+ const cell = event.currentTarget;
194
+ const cellKey = `${date}-${assignee}`;
195
+
196
+ if (event.ctrlKey || event.metaKey) {
197
+ // Multi-select
198
+ if (selectedCells.has(cellKey)) {
199
+ selectedCells.delete(cellKey);
200
+ cell.classList.remove('cell-selected');
201
+ } else {
202
+ selectedCells.add(cellKey);
203
+ cell.classList.add('cell-selected');
204
+ }
205
+ } else if (event.shiftKey && selectedCells.size > 0) {
206
+ // Range select
207
+ // Implementation for range selection
208
+ } else {
209
+ // Single select
210
+ clearCellSelection();
211
+ selectedCells.add(cellKey);
212
+ cell.classList.add('cell-selected');
213
+ }
214
+ }
215
+
216
+ function clearCellSelection() {
217
+ document.querySelectorAll('.cell').forEach(cell => {
218
+ cell.classList.remove('cell-selected');
219
+ });
220
+ selectedCells.clear();
221
+ }
222
+
223
+ function handleDragStart(event, taskId) {
224
+ event.dataTransfer.setData('text/plain', taskId.toString());
225
+ event.currentTarget.classList.add('dragging');
226
+ }
227
+
228
+ function handleDragOver(event) {
229
+ event.preventDefault();
230
+ event.currentTarget.classList.add('drag-over-valid');
231
+ }
232
+
233
+ function handleDrop(event, date, assignee) {
234
+ event.preventDefault();
235
+ event.currentTarget.classList.remove('drag-over-valid', 'dragging');
236
+
237
+ const taskId = parseInt(event.dataTransfer.getData('text/plain'));
238
+ const task = tasks.find(t => t.id === taskId);
239
+
240
+ if (task) {
241
+ // Update task assignment and date
242
+ task.assignee = assignee;
243
+ task.date = date;
244
+ initializeCalendar(); // Refresh calendar
245
+ }
246
+ }
247
+
248
+ // Quick Add Button
249
+ document.getElementById('quick-add-btn')?.addEventListener('click', function() {
250
+ // Open command panel with task creation template
251
+ const commandPanel = document.querySelector('custom-command-panel');
252
+ if (commandPanel && commandPanel.openPanel) {
253
+ commandPanel.openPanel('task-create');
254
+ }
255
+ });
256
+
257
+ // Keyboard shortcuts
258
+ document.addEventListener('keydown', function(event) {
259
+ // Ctrl+K or Cmd+K to open command panel
260
+ if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
261
+ event.preventDefault();
262
+ const commandPanel = document.querySelector('custom-command-panel');
263
+ if (commandPanel && commandPanel.togglePanel) {
264
+ commandPanel.togglePanel();
265
+ }
266
+ }
267
+
268
+ // Escape to clear selection
269
+ if (event.key === 'Escape') {
270
+ clearCellSelection();
271
+ }
272
+ });
273
+
274
+ // Export functions for web components
275
+ window.CalendarUtils = {
276
+ getDateString,
277
+ formatDateDisplay,
278
+ getDateRange,
279
+ generateDates,
280
+ teamMembers,
281
+ tasks,
282
+ selectedCells,
283
+ clearCellSelection
284
+ };
style.css CHANGED
@@ -1,28 +1,178 @@
 
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:wght@400;500;600;700&display=swap');
2
+
3
+ * {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
+ }
8
+
9
  body {
10
+ font-family: 'Atkinson Hyperlegible Next', -apple-system, BlinkMacSystemFont, sans-serif;
11
+ }
12
+
13
+ /* Custom scrollbar */
14
+ ::-webkit-scrollbar {
15
+ width: 8px;
16
+ height: 8px;
17
+ }
18
+
19
+ ::-webkit-scrollbar-track {
20
+ background: #f1f5f9;
21
+ }
22
+
23
+ ::-webkit-scrollbar-thumb {
24
+ background: #cbd5e1;
25
+ border-radius: 4px;
26
+ }
27
+
28
+ ::-webkit-scrollbar-thumb:hover {
29
+ background: #94a3b8;
30
+ }
31
+
32
+ /* PTO Background Pattern */
33
+ .pto-pattern {
34
+ background-image: linear-gradient(45deg, #f5f5f5 25%, transparent 25%),
35
+ linear-gradient(-45deg, #f5f5f5 25%, transparent 25%),
36
+ linear-gradient(45deg, transparent 75%, #f5f5f5 75%),
37
+ linear-gradient(-45deg, transparent 75%, #f5f5f5 75%);
38
+ background-size: 20px 20px;
39
+ background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
40
+ }
41
+
42
+ /* On-call Background */
43
+ .on-call-bg {
44
+ background-color: #fef9c3;
45
+ }
46
+
47
+ /* Holiday Background */
48
+ .holiday-bg {
49
+ background-color: #dbeafe;
50
+ }
51
+
52
+ /* Conference Background */
53
+ .conference-bg {
54
+ background-color: #f3e8ff;
55
+ }
56
+
57
+ /* Status Indicator Colors */
58
+ .status-complete {
59
+ background-color: #10b981;
60
+ }
61
+
62
+ .status-in-progress {
63
+ background-color: #3b82f6;
64
+ }
65
+
66
+ .status-blocked {
67
+ background-color: #f59e0b;
68
  }
69
 
70
+ .status-not-started {
71
+ background-color: #6b7280;
 
72
  }
73
 
74
+ /* Cell Selection */
75
+ .cell-selected {
76
+ border: 2px solid #0ea5e9 !important;
77
+ box-shadow: 0 0 0 1px #0ea5e9;
 
78
  }
79
 
80
+ /* Drag and Drop Styles */
81
+ .drag-over-valid {
82
+ border: 2px solid #10b981 !important;
 
 
 
83
  }
84
 
85
+ .drag-over-invalid {
86
+ border: 2px solid #ef4444 !important;
87
  }
88
+
89
+ .dragging {
90
+ opacity: 0.5;
91
+ }
92
+
93
+ /* Dependency Lines */
94
+ .dependency-line {
95
+ stroke: #6b7280;
96
+ stroke-width: 2;
97
+ marker-end: url(#arrowhead);
98
+ }
99
+
100
+ /* Sprint Boundary Lines */
101
+ .sprint-boundary {
102
+ border-top: 3px solid #3b82f6;
103
+ }
104
+
105
+ /* Conflict Indicators */
106
+ .conflict-indicator {
107
+ position: absolute;
108
+ top: 2px;
109
+ right: 2px;
110
+ width: 12px;
111
+ height: 12px;
112
+ background-color: #ef4444;
113
+ border-radius: 50%;
114
+ }
115
+
116
+ /* Command Panel Animation */
117
+ .command-panel-enter {
118
+ transform: translateX(100%);
119
+ }
120
+
121
+ .command-panel-enter-active {
122
+ transform: translateX(0);
123
+ transition: transform 300ms ease-in-out;
124
+ }
125
+
126
+ .command-panel-exit {
127
+ transform: translateX(0);
128
+ }
129
+
130
+ .command-panel-exit-active {
131
+ transform: translateX(100%);
132
+ transition: transform 300ms ease-in-out;
133
+ }
134
+
135
+ /* Task Stack Animation */
136
+ .task-stack-enter {
137
+ opacity: 0;
138
+ transform: translateY(-10px);
139
+ }
140
+
141
+ .task-stack-enter-active {
142
+ opacity: 1;
143
+ transform: translateY(0);
144
+ transition: opacity 200ms, transform 200ms;
145
+ }
146
+
147
+ /* Responsive Adjustments */
148
+ @media (max-width: 1024px) {
149
+ .calendar-grid {
150
+ font-size: 12px;
151
+ }
152
+
153
+ .team-member-column {
154
+ min-width: 150px;
155
+ }
156
+ }
157
+
158
+ @media (max-width: 768px) {
159
+ .calendar-grid {
160
+ font-size: 11px;
161
+ }
162
+
163
+ .team-member-column {
164
+ min-width: 120px;
165
+ }
166
+ }
167
+
168
+ /* Print Styles */
169
+ @media print {
170
+ .command-panel,
171
+ .floating-action-btn {
172
+ display: none !important;
173
+ }
174
+
175
+ .calendar-grid {
176
+ break-inside: avoid;
177
+ }
178
+ }