VibeCoderzVibeCoderz
All Prompts
Interactive Investment Timeline Dashboard preview
dashboardtimelinetailwindmodalfiltersortresponsiveinvestment

Interactive Investment Timeline Dashboard

Адаптивная дашборд-карточка с таймлайном инвестиций. Фильтры, сортировка, выбор периода, модальное окно добавления. Идеально для финансов.

Prompt

<html lang="en"><head><meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Portfolio Manager — Timeline</title>
<!-- Inter font -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&amp;display=swap" rel="stylesheet">
<!-- Tailwind CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script></head>
<body class="bg-[#0b0f14] text-neutral-200 antialiased min-h-screen flex items-center justify-center p-4 sm:p-6" style="font-family:'Inter', ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Arial">

  <!-- Single Card Container -->
  <div class="w-full max-w-3xl min-h-[400px] rounded-2xl sm:rounded-3xl border border-white/10 bg-white/[0.03] backdrop-blur-sm shadow-[0_20px_50px_-10px_rgba(0,0,0,0.6)]">

    
    <!-- Card Header -->
    <div class="flex flex-col sm:flex-row items-start justify-between gap-4 p-4 sm:p-6 lg:p-8 pb-4 sm:pb-6">
      <div>
        <h1 class="text-xl sm:text-2xl lg:text-3xl font-semibold tracking-tight">Investment Timeline</h1>
        <p class="mt-1 text-sm text-neutral-400" id="period-subtitle">Q4 portfolio overview</p>
      </div>
      <div class="flex items-center gap-2 sm:gap-3 w-full sm:w-auto">
        <!-- View range button -->
        <div class="relative flex-1 sm:flex-none">
          <button id="period-btn" class="w-full sm:w-auto inline-flex items-center gap-2 rounded-xl border border-white/10 bg-white/5 px-3 sm:px-3.5 py-2.5 text-sm text-neutral-200 hover:bg-white/[0.08] transition">
            <i data-lucide="calendar" class="h-4 w-4 text-neutral-300"></i>
            <span class="font-medium" id="period-text">Quarter</span>
            <i data-lucide="chevron-down" class="h-4 w-4 text-neutral-400"></i>
          </button>
          <!-- Dropdown -->
          <div id="period-dropdown" class="absolute top-full mt-2 right-0 w-48 rounded-xl border border-white/10 bg-[#0b0f14] backdrop-blur-sm shadow-[0_20px_50px_-10px_rgba(0,0,0,0.6)] z-20 hidden">
            <div class="p-2">
              <button class="period-option w-full px-3 py-2 text-left text-sm rounded-lg hover:bg-white/[0.06] transition" data-period="week">Week</button>
              <button class="period-option w-full px-3 py-2 text-left text-sm rounded-lg hover:bg-white/[0.06] transition" data-period="month">Month</button>
              <button class="period-option w-full px-3 py-2 text-left text-sm rounded-lg hover:bg-white/[0.06] transition bg-white/[0.06]" data-period="quarter">Quarter</button>
              <button class="period-option w-full px-3 py-2 text-left text-sm rounded-lg hover:bg-white/[0.06] transition" data-period="year">Year</button>
            </div>
          </div>
        </div>

        <!-- New Investment -->
        <button id="new-investment-btn" class="inline-flex items-center gap-2 rounded-xl px-3 sm:px-4 py-2.5 text-sm font-medium text-white shadow-[0_6px_20px_-6px_rgba(59,130,246,0.5)] bg-gradient-to-b from-[#1f78ff] to-[#1766e8] hover:from-[#2a82ff] hover:to-[#1a6ef1] transition whitespace-nowrap">
          <i data-lucide="plus" class="h-4 w-4"></i>
          <span class="hidden xs:inline">New Investment</span>
          <span class="xs:hidden">New</span>
        </button>
      </div>
    </div>

    <!-- Divider -->
    <div class="mx-4 sm:mx-6 lg:mx-8 h-px bg-gradient-to-r from-transparent via-white/10 to-transparent"></div>

    <!-- Navigation Tabs -->
    <div class="px-4 sm:px-6 lg:px-8 pt-4 sm:pt-6">
      <div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
        <nav class="flex gap-1 w-full sm:w-auto overflow-x-auto">
          <button class="nav-tab relative px-3.5 py-2 text-sm text-neutral-300 hover:text-white rounded-lg hover:bg-white/[0.06] transition whitespace-nowrap" data-tab="portfolio">Portfolio</button>
          <button class="nav-tab relative px-3.5 py-2 text-sm rounded-lg text-white whitespace-nowrap" data-tab="timeline">
            <span class="relative z-10 font-medium">Timeline</span>
            <span class="absolute inset-0 rounded-lg bg-white/[0.06]"></span>
            <span class="absolute left-1/2 top-full h-[2px] w-10 -translate-x-1/2 bg-gradient-to-r from-transparent via-[#2a7fff] to-transparent"></span>
          </button>
          <button class="nav-tab relative px-3.5 py-2 text-sm text-neutral-300 hover:text-white rounded-lg hover:bg-white/[0.06] transition whitespace-nowrap" data-tab="analytics">Analytics</button>
          <button class="nav-tab relative px-3.5 py-2 text-sm text-neutral-300 hover:text-white rounded-lg hover:bg-white/[0.06] transition whitespace-nowrap" data-tab="holdings">Holdings</button>
        </nav>

        <!-- Right toolbar -->
        <div class="flex items-center gap-2">
          <div class="relative">
            <button id="filter-btn" class="inline-flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-neutral-300 hover:text-white hover:bg-white/[0.06] transition">
              <i data-lucide="filter" class="h-4 w-4"></i>
              <span class="hidden sm:inline">Filter</span>
              <span id="filter-count" class="hidden ml-1 px-1.5 py-0.5 text-xs bg-[#2a7fff] text-white rounded-md">2</span>
            </button>
            <!-- Filter dropdown -->
            <div id="filter-dropdown" class="absolute top-full mt-2 right-0 w-56 rounded-xl border border-white/10 bg-[#0b0f14] backdrop-blur-sm shadow-[0_20px_50px_-10px_rgba(0,0,0,0.6)] z-20 hidden">
              <div class="p-4">
                <div class="mb-3">
                  <label class="block text-xs font-medium text-neutral-300 mb-2">Investment Type</label>
                  <div class="space-y-2">
                    <label class="flex items-center gap-2 text-sm">
                      <input type="checkbox" class="filter-checkbox rounded border-white/20 bg-white/5 text-[#2a7fff]" data-filter="analysis" checked="">
                      <span>Market Analysis</span>
                    </label>
                    <label class="flex items-center gap-2 text-sm">
                      <input type="checkbox" class="filter-checkbox rounded border-white/20 bg-white/5 text-[#2a7fff]" data-filter="esg" checked="">
                      <span>ESG Screening</span>
                    </label>
                    <label class="flex items-center gap-2 text-sm">
                      <input type="checkbox" class="filter-checkbox rounded border-white/20 bg-white/5 text-[#2a7fff]" data-filter="derivatives">
                      <span>Derivatives</span>
                    </label>
                  </div>
                </div>
                <div class="pt-3 border-t border-white/10">
                  <button id="clear-filters" class="text-xs text-[#2a7fff] hover:text-blue-300 transition">Clear all</button>
                </div>
              </div>
            </div>
          </div>
          
          <div class="relative">
            <button id="sort-btn" class="inline-flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-neutral-300 hover:text-white hover:bg-white/[0.06] transition">
              <i data-lucide="sort-desc" class="h-4 w-4"></i>
              <span class="hidden sm:inline">Sort</span>
            </button>
            <!-- Sort dropdown -->
            <div id="sort-dropdown" class="absolute top-full mt-2 right-0 w-48 rounded-xl border border-white/10 bg-[#0b0f14] backdrop-blur-sm shadow-[0_20px_50px_-10px_rgba(0,0,0,0.6)] z-20 hidden">
              <div class="p-2">
                <button class="sort-option w-full px-3 py-2 text-left text-sm rounded-lg hover:bg-white/[0.06] transition bg-white/[0.06]" data-sort="date">Date</button>
                <button class="sort-option w-full px-3 py-2 text-left text-sm rounded-lg hover:bg-white/[0.06] transition" data-sort="amount">Amount</button>
                <button class="sort-option w-full px-3 py-2 text-left text-sm rounded-lg hover:bg-white/[0.06] transition" data-sort="type">Type</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Tab Content Container - Responsive height -->
    <div class="mt-4 sm:mt-6 min-h-[350px] sm:min-h-[400px]">
      <!-- Portfolio Tab Content -->
      <div class="tab-content hidden" data-content="portfolio">
        <div class="px-4 sm:px-6 lg:px-8">
          <div class="rounded-xl sm:rounded-2xl border border-white/10 bg-white/[0.02] p-6 sm:p-8">
            <div class="text-center py-8 sm:py-12">
              <i data-lucide="briefcase" class="h-10 sm:h-12 w-10 sm:w-12 text-neutral-400 mx-auto mb-4"></i>
              <h3 class="text-lg font-semibold text-neutral-200 mb-2">Portfolio Overview</h3>
              <p class="text-sm text-neutral-400 mb-6 max-w-sm mx-auto">View your complete investment portfolio performance and allocation.</p>
              <button class="inline-flex items-center gap-2 rounded-xl px-4 py-2.5 text-sm font-medium text-white bg-gradient-to-b from-[#1f78ff] to-[#1766e8] hover:from-[#2a82ff] hover:to-[#1a6ef1] transition">
                <i data-lucide="pie-chart" class="h-4 w-4"></i>
                View Portfolio
              </button>
            </div>
          </div>
        </div>
      </div>

      <!-- Timeline Tab Content (Active) -->
      <div class="tab-content" data-content="timeline">
        <div class="px-4 sm:px-6 lg:px-8">
          <!-- Navigation arrows -->
          <div class="flex items-center justify-between mb-4">
            <button id="prev-period" class="inline-flex items-center gap-1 sm:gap-2 rounded-lg px-2 sm:px-3 py-2 text-sm text-neutral-300 hover:text-white hover:bg-white/[0.06] transition">
              <i data-lucide="chevron-left" class="h-4 w-4"></i>
              <span class="hidden sm:inline">Previous</span>
            </button>
            <div class="text-xs sm:text-sm text-neutral-400 text-center" id="current-period">October - November 2023</div>
            <button id="next-period" class="inline-flex items-center gap-1 sm:gap-2 rounded-lg px-2 sm:px-3 py-2 text-sm text-neutral-300 hover:text-white hover:bg-white/[0.06] transition">
              <span class="hidden sm:inline">Next</span>
              <i data-lucide="chevron-right" class="h-4 w-4"></i>
            </button>
          </div>

          <!-- Scroll container -->
          <div class="relative overflow-x-auto rounded-xl sm:rounded-2xl border border-white/10 bg-white/[0.02]">
            <!-- This inner wrapper controls column width -->
            <div class="min-w-[600px] sm:min-w-[900px]" id="timeline-container">
              <!-- Header rows -->
              <div id="timeline-header" class="border-b border-white/10">
                <!-- Dynamic header content will be inserted here -->
              </div>

              <!-- Grid + Tasks -->
              <div class="relative">
                <!-- Column grid background -->
                <div id="timeline-grid">
                  <!-- Dynamic grid columns will be inserted here -->
                </div>

                <!-- Current day marker -->
                <div id="current-day-marker" class="pointer-events-none absolute top-0 bottom-0 w-px bg-gradient-to-b from-transparent via-[#2a7fff] to-transparent hidden">
                </div>

                <!-- Tasks layer -->
                <div class="absolute inset-0" id="tasks-container">
                  <!-- Dynamic tasks will be inserted here -->
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Analytics Tab Content -->
      <div class="tab-content hidden" data-content="analytics">
        <div class="px-4 sm:px-6 lg:px-8">
          <div class="rounded-xl sm:rounded-2xl border border-white/10 bg-white/[0.02] p-6 sm:p-8">
            <div class="text-center py-8 sm:py-12">
              <i data-lucide="bar-chart-3" class="h-10 sm:h-12 w-10 sm:w-12 text-neutral-400 mx-auto mb-4"></i>
              <h3 class="text-lg font-semibold text-neutral-200 mb-2">Analytics Dashboard</h3>
              <p class="text-sm text-neutral-400 mb-6 max-w-sm mx-auto">Deep dive into performance metrics, risk analysis, and market insights.</p>
              <button class="inline-flex items-center gap-2 rounded-xl px-4 py-2.5 text-sm font-medium text-white bg-gradient-to-b from-[#1f78ff] to-[#1766e8] hover:from-[#2a82ff] hover:to-[#1a6ef1] transition">
                <i data-lucide="trending-up" class="h-4 w-4"></i>
                View Analytics
              </button>
            </div>
          </div>
        </div>
      </div>

      <!-- Holdings Tab Content -->
      <div class="tab-content hidden" data-content="holdings">
        <div class="px-4 sm:px-6 lg:px-8">
          <div class="rounded-xl sm:rounded-2xl border border-white/10 bg-white/[0.02] p-6 sm:p-8">
            <div class="text-center py-8 sm:py-12">
              <i data-lucide="layers" class="h-10 sm:h-12 w-10 sm:w-12 text-neutral-400 mx-auto mb-4"></i>
              <h3 class="text-lg font-semibold text-neutral-200 mb-2">Current Holdings</h3>
              <p class="text-sm text-neutral-400 mb-6 max-w-sm mx-auto">Detailed view of all your current positions and asset allocation.</p>
              <button class="inline-flex items-center gap-2 rounded-xl px-4 py-2.5 text-sm font-medium text-white bg-gradient-to-b from-[#1f78ff] to-[#1766e8] hover:from-[#2a82ff] hover:to-[#1a6ef1] transition">
                <i data-lucide="list" class="h-4 w-4"></i>
                View Holdings
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>

  </div>

  <!-- New Investment Modal -->
  <div id="investment-modal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4 sm:p-6 hidden">
    <div class="w-full max-w-md rounded-xl sm:rounded-2xl border border-white/10 bg-[#0b0f14] shadow-[0_20px_50px_-10px_rgba(0,0,0,0.8)] max-h-[90vh] overflow-y-auto">
      <div class="p-4 sm:p-6">
        <div class="flex items-center justify-between mb-4 sm:mb-6">
          <h2 class="text-lg sm:text-xl font-semibold tracking-tight">New Investment</h2>
          <button id="close-modal" class="text-neutral-400 hover:text-white">
            <i data-lucide="x" class="h-5 w-5"></i>
          </button>
        </div>
        
        <form id="investment-form" class="space-y-4">
          <div>
            <label class="block text-sm font-medium text-neutral-300 mb-2">Title</label>
            <input type="text" id="investment-title" class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white placeholder-neutral-400 focus:outline-none focus:border-[#2a7fff] text-sm" placeholder="Investment title">
          </div>
          
          <div>
            <label class="block text-sm font-medium text-neutral-300 mb-2">Type</label>
            <select id="investment-type" class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white focus:outline-none focus:border-[#2a7fff] text-sm">
              <option value="analysis">Market Analysis</option>
              <option value="esg">ESG Screening</option>
              <option value="derivatives">Derivatives</option>
            </select>
          </div>
          
          <div class="grid grid-cols-2 gap-4">
            <div>
              <label class="block text-sm font-medium text-neutral-300 mb-2">Start Date</label>
              <input type="date" id="investment-start" class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white focus:outline-none focus:border-[#2a7fff] text-sm">
            </div>
            <div>
              <label class="block text-sm font-medium text-neutral-300 mb-2">End Date</label>
              <input type="date" id="investment-end" class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white focus:outline-none focus:border-[#2a7fff] text-sm">
            </div>
          </div>
          
          <div>
            <label class="block text-sm font-medium text-neutral-300 mb-2">Amount</label>
            <input type="text" id="investment-amount" class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white placeholder-neutral-400 focus:outline-none focus:border-[#2a7fff] text-sm" placeholder="$0.00">
          </div>
          
          <div class="flex gap-3 pt-4">
            <button type="button" id="cancel-investment" class="flex-1 px-4 py-2.5 text-sm font-medium text-neutral-300 border border-white/10 rounded-xl hover:bg-white/[0.06] transition">
              Cancel
            </button>
            <button type="submit" class="flex-1 px-4 py-2.5 text-sm font-medium text-white bg-gradient-to-b from-[#1f78ff] to-[#1766e8] hover:from-[#2a82ff] hover:to-[#1a6ef1] rounded-xl transition shadow-[0_6px_20px_-6px_rgba(59,130,246,0.5)]">
              Add Investment
            </button>
          </div>
        </form>
      </div>
    </div>
  </div>

  <script>
    // Add responsive breakpoint configuration to Tailwind
    tailwind.config = {
      theme: {
        extend: {
          screens: {
            'xs': '475px',
          }
        }
      }
    }

    // Current date for timeline calculations
    const currentDate = new Date();
    
    // Investment data with different time ranges
    const investmentData = {
      week: [
        {
          id: 1,
          title: 'Daily Market Analysis',
          type: 'analysis',
          startDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() - 2),
          endDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1),
          amount: 850000,
          color: 'from-[#2a7fff] to-[#0ea5e9]',
          icon: 'trending-up',
          teamSize: 2
        },
        {
          id: 2,
          title: 'ESG Quick Screen',
          type: 'esg',
          startDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()),
          endDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 2),
          amount: 425000,
          color: 'from-[#22d3ee] to-[#14b8a6]',
          icon: 'leaf',
          teamSize: 1
        }
      ],
      month: [
        {
          id: 1,
          title: 'Monthly Portfolio Review',
          type: 'analysis',
          startDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), 5),
          endDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), 12),
          amount: 1850000,
          color: 'from-[#2a7fff] to-[#0ea5e9]',
          icon: 'trending-up',
          teamSize: 4
        },
        {
          id: 2,
          title: 'ESG Compliance Audit',
          type: 'esg',
          startDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), 8),
          endDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), 18),
          amount: 1200000,
          color: 'from-[#22d3ee] to-[#14b8a6]',
          icon: 'leaf',
          teamSize: 3
        },
        {
          id: 3,
          title: 'Options Strategy',
          type: 'derivatives',
          startDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), 15),
          endDate: new Date(currentDate.getFullYear(), currentDate.getMonth(), 28),
          amount: 3200000,
          color: 'from-[#a78bfa] to-[#7c3aed]',
          icon: 'shield',
          teamSize: 5
        }
      ],
      quarter: [
        {
          id: 1,
          title: 'Market Analysis',
          type: 'analysis',
          startDate: new Date(2023, 9, 28),
          endDate: new Date(2023, 10, 2),
          amount: 2500000,
          color: 'from-[#2a7fff] to-[#0ea5e9]',
          icon: 'trending-up',
          teamSize: 3
        },
        {
          id: 2,
          title: 'ESG Screening',
          type: 'esg',
          startDate: new Date(2023, 9, 29),
          endDate: new Date(2023, 10, 3),
          amount: 1800000,
          color: 'from-[#22d3ee] to-[#14b8a6]',
          icon: 'leaf',
          teamSize: 2
        },
        {
          id: 3,
          title: 'Derivatives Hedge',
          type: 'derivatives',
          startDate: new Date(2023, 9, 31),
          endDate: new Date(2023, 10, 6),
          amount: 4200000,
          color: 'from-[#a78bfa] to-[#7c3aed]',
          icon: 'shield',
          teamSize: 4
        }
      ],
      year: [
        {
          id: 1,
          title: 'Annual Strategy Review',
          type: 'analysis',
          startDate: new Date(2023, 1, 15),
          endDate: new Date(2023, 4, 30),
          amount: 8500000,
          color: 'from-[#2a7fff] to-[#0ea5e9]',
          icon: 'trending-up',
          teamSize: 8
        },
        {
          id: 2,
          title: 'ESG Transformation',
          type: 'esg',
          startDate: new Date(2023, 3, 1),
          endDate: new Date(2023, 8, 15),
          amount: 12000000,
          color: 'from-[#22d3ee] to-[#14b8a6]',
          icon: 'leaf',
          teamSize: 12
        },
        {
          id: 3,
          title: 'Risk Management Overhaul',
          type: 'derivatives',
          startDate: new Date(2023, 6, 1),
          endDate: new Date(2023, 11, 31),
          amount: 15500000,
          color: 'from-[#a78bfa] to-[#7c3aed]',
          icon: 'shield',
          teamSize: 15
        }
      ]
    };

    // Current view state
    let currentPeriod = 'quarter';
    let currentSort = 'date';
    let activeFilters = ['analysis', 'esg'];
    let activeTab = 'timeline';
    let currentTimelineData = {};

    // Initialize app
    document.addEventListener('DOMContentLoaded', () => {
      if (window.lucide) lucide.createIcons();
      
      initializeEventListeners();
      updateTimelineData();
      renderTimeline();
      updateStats();
    });

    function initializeEventListeners() {
      // Period dropdown
      document.getElementById('period-btn').addEventListener('click', (e) => {
        e.stopPropagation();
        toggleDropdown('period-dropdown');
      });

      document.querySelectorAll('.period-option').forEach(btn => {
        btn.addEventListener('click', (e) => {
          // Update active state
          document.querySelectorAll('.period-option').forEach(b => b.classList.remove('bg-white/[0.06]'));
          e.target.classList.add('bg-white/[0.06]');
          
          currentPeriod = e.target.dataset.period;
          document.getElementById('period-text').textContent = currentPeriod.charAt(0).toUpperCase() + currentPeriod.slice(1);
          
          updatePeriodSubtitle();
          updateTimelineData();
          renderTimeline();
          updateStats();
          hideDropdown('period-dropdown');
        });
      });

      // Filter dropdown
      document.getElementById('filter-btn').addEventListener('click', (e) => {
        e.stopPropagation();
        toggleDropdown('filter-dropdown');
      });

      document.querySelectorAll('.filter-checkbox').forEach(checkbox => {
        checkbox.addEventListener('change', (e) => {
          const filter = e.target.dataset.filter;
          if (e.target.checked) {
            if (!activeFilters.includes(filter)) {
              activeFilters.push(filter);
            }
          } else {
            activeFilters = activeFilters.filter(f => f !== filter);
          }
          updateFilterCount();
          renderTimeline();
        });
      });

      document.getElementById('clear-filters').addEventListener('click', () => {
        activeFilters = [];
        document.querySelectorAll('.filter-checkbox').forEach(cb => cb.checked = false);
        updateFilterCount();
        renderTimeline();
      });

      // Sort dropdown
      document.getElementById('sort-btn').addEventListener('click', (e) => {
        e.stopPropagation();
        toggleDropdown('sort-dropdown');
      });

      document.querySelectorAll('.sort-option').forEach(btn => {
        btn.addEventListener('click', (e) => {
          currentSort = e.target.dataset.sort;
          document.querySelectorAll('.sort-option').forEach(b => b.classList.remove('bg-white/[0.06]'));
          e.target.classList.add('bg-white/[0.06]');
          renderTimeline();
          hideDropdown('sort-dropdown');
        });
      });

      // Navigation tabs
      document.querySelectorAll('.nav-tab').forEach(tab => {
        tab.addEventListener('click', (e) => {
          const tabName = e.currentTarget.dataset.tab;
          switchTab(tabName);
        });
      });

      // Timeline navigation
      document.getElementById('prev-period').addEventListener('click', () => {
        console.log('Previous period clicked');
      });

      document.getElementById('next-period').addEventListener('click', () => {
        console.log('Next period clicked');
      });

      // New investment modal
      document.getElementById('new-investment-btn').addEventListener('click', () => {
        document.getElementById('investment-modal').classList.remove('hidden');
        const today = new Date();
        document.getElementById('investment-start').valueAsDate = today;
        document.getElementById('investment-end').valueAsDate = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
      });

      document.getElementById('close-modal').addEventListener('click', closeModal);
      document.getElementById('cancel-investment').addEventListener('click', closeModal);

      document.getElementById('investment-form').addEventListener('submit', (e) => {
        e.preventDefault();
        addNewInvestment();
      });

      // Close dropdowns when clicking outside
      document.addEventListener('click', () => {
        hideDropdown('period-dropdown');
        hideDropdown('filter-dropdown');
        hideDropdown('sort-dropdown');
      });

      // Close modal when clicking backdrop
      document.getElementById('investment-modal').addEventListener('click', (e) => {
        if (e.target.id === 'investment-modal') {
          closeModal();
        }
      });
    }

    function updateTimelineData() {
      currentTimelineData = getTimelineConfig(currentPeriod);
    }

    function getTimelineConfig(period) {
      const now = new Date();
      
      switch (period) {
        case 'week':
          const weekStart = new Date(now);
          weekStart.setDate(now.getDate() - now.getDay()); // Start of week (Sunday)
          return {
            investments: investmentData.week,
            columns: 7,
            startDate: weekStart,
            getColumnLabel: (index) => {
              const date = new Date(weekStart);
              date.setDate(weekStart.getDate() + index);
              const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
              return `${date.getDate()} ${dayNames[date.getDay()]}`;
            },
            getPeriodLabel: () => {
              const weekEnd = new Date(weekStart);
              weekEnd.setDate(weekStart.getDate() + 6);
              return `${weekStart.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} - ${weekEnd.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}`;
            },
            getCurrentMarkerPosition: () => {
              const dayOfWeek = now.getDay();
              return `calc((100% / 7) * ${dayOfWeek} + (100% / 14))`;
            },
            showCurrentMarker: true
          };

        case 'month':
          const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
          const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
          const columns = Math.min(daysInMonth, 31);
          return {
            investments: investmentData.month,
            columns: columns,
            startDate: monthStart,
            getColumnLabel: (index) => {
              return `${index + 1}`;
            },
            getPeriodLabel: () => {
              return now.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
            },
            getCurrentMarkerPosition: () => {
              const currentDay = now.getDate();
              return `calc((100% / ${columns}) * ${currentDay - 1} + (100% / ${columns * 2}))`;
            },
            showCurrentMarker: true
          };

        case 'quarter':
          return {
            investments: investmentData.quarter,
            columns: 14,
            startDate: new Date(2023, 9, 27),
            getColumnLabel: (index) => {
              const dates = [
                '27 F', '28 S', '29 S', '30 M', '31 T', '1 W', '2 T',
                '3 F', '4 S', '5 S', '6 M', '7 T', '8 W', '9 T'
              ];
              return dates[index] || '';
            },
            getPeriodLabel: () => 'October - November 2023',
            getCurrentMarkerPosition: () => 'calc((100% / 14) * 4)',
            showCurrentMarker: false
          };

        case 'year':
          return {
            investments: investmentData.year,
            columns: 12,
            startDate: new Date(2023, 0, 1),
            getColumnLabel: (index) => {
              const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
              return monthNames[index] || '';
            },
            getPeriodLabel: () => '2023',
            getCurrentMarkerPosition: () => {
              const currentMonth = now.getMonth();
              return `calc((100% / 12) * ${currentMonth} + (100% / 24))`;
            },
            showCurrentMarker: true
          };

        default:
          return getTimelineConfig('quarter');
      }
    }

    function switchTab(tabName) {
      activeTab = tabName;
      
      document.querySelectorAll('.nav-tab').forEach(tab => {
        const isActive = tab.dataset.tab === tabName;
        const tabText = tab.dataset.tab.charAt(0).toUpperCase() + tab.dataset.tab.slice(1);
        
        if (isActive) {
          tab.className = 'nav-tab relative px-3.5 py-2 text-sm rounded-lg text-white whitespace-nowrap';
          tab.innerHTML = `
            <span class="relative z-10 font-medium">${tabText}</span>
            <span class="absolute inset-0 rounded-lg bg-white/[0.06]"></span>
            <span class="absolute left-1/2 top-full h-[2px] w-10 -translate-x-1/2 bg-gradient-to-r from-transparent via-[#2a7fff] to-transparent"></span>
          `;
        } else {
          tab.className = 'nav-tab relative px-3.5 py-2 text-sm text-neutral-300 hover:text-white rounded-lg hover:bg-white/[0.06] transition whitespace-nowrap';
          tab.innerHTML = tabText;
        }
      });
      
      document.querySelectorAll('.tab-content').forEach(content => {
        if (content.dataset.content === tabName) {
          content.classList.remove('hidden');
        } else {
          content.classList.add('hidden');
        }
      });

      if (tabName === 'timeline') {
        renderTimeline();
      }

      if (window.lucide) lucide.createIcons();
    }

    function toggleDropdown(id) {
      const dropdown = document.getElementById(id);
      dropdown.classList.toggle('hidden');
    }

    function hideDropdown(id) {
      document.getElementById(id).classList.add('hidden');
    }

    function updatePeriodSubtitle() {
      const subtitles = {
        week: 'Weekly portfolio overview',
        month: 'Monthly portfolio overview', 
        quarter: 'Q4 portfolio overview',
        year: 'Annual portfolio overview'
      };
      document.getElementById('period-subtitle').textContent = subtitles[currentPeriod];
    }

    function updateFilterCount() {
      const count = activeFilters.length;
      const countElement = document.getElementById('filter-count');
      if (count > 0) {
        countElement.textContent = count;
        countElement.classList.remove('hidden');
      } else {
        countElement.classList.add('hidden');
      }
    }

    function renderTimeline() {
      renderTimelineHeader();
      renderTimelineGrid();
      renderTasks();
      updateCurrentMarker();
    }

    function renderTimelineHeader() {
      const header = document.getElementById('timeline-header');
      const { columns, getColumnLabel, getPeriodLabel } = currentTimelineData;
      
      // Update period label
      document.getElementById('current-period').textContent = getPeriodLabel();
      
      let headerHTML = '';
      
      if (currentPeriod === 'quarter') {
        // Special layout for quarter view with month headers
        headerHTML = `
          <div class="grid [grid-template-columns:repeat(${columns},minmax(60px,1fr))] sm:[grid-template-columns:repeat(${columns},minmax(80px,1fr))]">
            <div class="col-[1/8] px-2 sm:px-4 py-3 text-sm text-neutral-400 font-medium">October</div>
            <div class="col-[8/15] px-2 sm:px-4 py-3 text-sm text-neutral-400 font-medium">November</div>
          </div>
          <div class="grid [grid-template-columns:repeat(${columns},minmax(60px,1fr))] sm:[grid-template-columns:repeat(${columns},minmax(80px,1fr))] text-[10px] sm:text-[11px] uppercase tracking-wide text-neutral-400/90">
            ${Array.from({ length: columns }, (_, i) => 
              `<div class="px-2 sm:px-4 py-2 border-l border-white/5">${getColumnLabel(i)}</div>`
            ).join('')}
          </div>
        `;
      } else {
        // Single row header for other views
        headerHTML = `
          <div class="grid [grid-template-columns:repeat(${columns},minmax(60px,1fr))] sm:[grid-template-columns:repeat(${columns},minmax(80px,1fr))] text-[10px] sm:text-[11px] uppercase tracking-wide text-neutral-400/90">
            ${Array.from({ length: columns }, (_, i) => 
              `<div class="px-2 sm:px-4 py-2 border-l border-white/5">${getColumnLabel(i)}</div>`
            ).join('')}
          </div>
        `;
      }
      
      header.innerHTML = headerHTML;
    }

    function renderTimelineGrid() {
      const grid = document.getElementById('timeline-grid');
      const { columns } = currentTimelineData;
      
      let gridHTML = `<div class="grid [grid-template-columns:repeat(${columns},minmax(60px,1fr))] sm:[grid-template-columns:repeat(${columns},minmax(80px,1fr))]">`;
      for (let i = 0; i < columns; i++) {
        const bgClass = i % 2 === 1 ? 'bg-white/[0.01]' : '';
        const borderClass = i === columns - 1 ? 'border-r' : '';
        gridHTML += `<div class="h-[220px] sm:h-[250px] border-l border-white/[0.05] ${bgClass} ${borderClass}"></div>`;
      }
      gridHTML += '</div>';
      
      grid.innerHTML = gridHTML;
    }

    function renderTasks() {
      const container = document.getElementById('tasks-container');
      const filteredInvestments = getFilteredInvestments();
      const { columns } = currentTimelineData;
      
      let tasksHTML = `<div class="grid [grid-template-columns:repeat(${columns},minmax(60px,1fr))] sm:[grid-template-columns:repeat(${columns},minmax(80px,1fr))] gap-y-3 sm:gap-y-4 p-2 sm:p-4">`;
      
      filteredInvestments.forEach((investment, index) => {
        const startCol = getColumnForDate(investment.startDate);
        const endCol = getColumnForDate(investment.endDate);
        
        tasksHTML += `
          <div class="col-[1/${columns + 1}] h-1"></div>
          <div class="col-[${startCol}/${endCol + 1}]">
            <div class="relative flex items-center justify-between rounded-lg sm:rounded-xl border border-white/10 bg-white/[0.06] px-2 sm:px-3 py-2 sm:py-2.5 shadow-[0_6px_24px_-10px_rgba(0,0,0,0.6)] hover:bg-white/[0.08] transition cursor-pointer" onclick="editInvestment(${investment.id})">
              <span class="absolute inset-y-0 left-0 w-1 rounded-l-lg sm:rounded-l-xl bg-gradient-to-b ${investment.color}"></span>
              <div class="pl-2 sm:pl-3 flex-1 min-w-0">
                <h3 class="text-xs sm:text-sm font-medium tracking-tight truncate">${investment.title}</h3>
                <div class="mt-0.5 flex items-center gap-1.5 text-[10px] sm:text-xs text-neutral-400">
                  <i data-lucide="${investment.icon}" class="h-3 w-3 text-[#2a7fff] flex-shrink-0"></i>
                  <span class="truncate">${formatAmount(investment.amount)}</span>
                </div>
              </div>
              <div class="flex items-center gap-1 sm:gap-1.5 flex-shrink-0 ml-2">
                <img alt="Assignee" class="h-5 w-5 sm:h-6 sm:w-6 rounded-full ring-1 ring-white/10 object-cover" src="https://images.unsplash.com/photo-1621619856624-42fd193a0661?w=1080&q=80" />
                <div class="h-5 w-5 sm:h-6 sm:w-6 rounded-full bg-white/10 text-[10px] sm:text-[11px] text-neutral-200 grid place-items-center">+${investment.teamSize}</div>
              </div>
            </div>
          </div>
        `;
      });
      
      tasksHTML += '</div>';
      container.innerHTML = tasksHTML;
      
      if (window.lucide) lucide.createIcons();
    }

    function updateCurrentMarker() {
      const marker = document.getElementById('current-day-marker');
      const { getCurrentMarkerPosition, showCurrentMarker } = currentTimelineData;
      
      if (showCurrentMarker) {
        marker.style.left = getCurrentMarkerPosition();
        marker.classList.remove('hidden');
      } else {
        marker.classList.add('hidden');
      }
    }

    function getFilteredInvestments() {
      const { investments } = currentTimelineData;
      let filteredInvestments = investments.filter(investment => {
        return activeFilters.length === 0 || activeFilters.includes(investment.type);
      });

      if (currentSort === 'date') {
        filteredInvestments.sort((a, b) => a.startDate - b.startDate);
      } else if (currentSort === 'amount') {
        filteredInvestments.sort((a, b) => b.amount - a.amount);
      } else if (currentSort === 'type') {
        filteredInvestments.sort((a, b) => a.type.localeCompare(b.type));
      }

      return filteredInvestments;
    }

    function getColumnForDate(date) {
      const { startDate, columns } = currentTimelineData;
      
      if (currentPeriod === 'week') {
        const dayOfWeek = date.getDay();
        return Math.max(1, Math.min(columns, dayOfWeek + 1));
      } else if (currentPeriod === 'month') {
        const dayOfMonth = date.getDate();
        return Math.max(1, Math.min(columns, dayOfMonth));
      } else if (currentPeriod === 'quarter') {
        const baseDate = new Date(2023, 9, 27);
        const dayDiff = Math.floor((date - baseDate) / (1000 * 60 * 60 * 24));
        return Math.max(1, Math.min(columns, dayDiff + 1));
      } else if (currentPeriod === 'year') {
        const month = date.getMonth();
        return Math.max(1, Math.min(columns, month + 1));
      }
      
      return 1;
    }

    function formatAmount(amount) {
      if (amount >= 1000000) {
        return `$${(amount / 1000000).toFixed(1)}M`;
      } else if (amount >= 1000) {
        return `$${(amount / 1000).toFixed(0)}K`;
      } else {
        return `$${amount.toLocaleString()}`;
      }
    }

    function updateStats() {
      const { investments } = currentTimelineData;
      const totalAmount = investments.reduce((sum, inv) => sum + inv.amount, 0);
      document.getElementById('total-managed').textContent = `${formatAmount(totalAmount)} managed`;
      document.getElementById('active-positions').textContent = `${investments.length} active positions`;
    }

    function closeModal() {
      document.getElementById('investment-modal').classList.add('hidden');
      document.getElementById('investment-form').reset();
    }

    function addNewInvestment() {
      const title = document.getElementById('investment-title').value;
      const type = document.getElementById('investment-type').value;
      const startDate = new Date(document.getElementById('investment-start').value);
      const endDate = new Date(document.getElementById('investment-end').value);
      const amountText = document.getElementById('investment-amount').value;
      
      if (!title || !amountText) {
        alert('Please fill in all required fields');
        return;
      }

      const amount = parseFloat(amountText.replace(/[$,]/g, ''));
      if (isNaN(amount) || amount <= 0) {
        alert('Please enter a valid amount');
        return;
      }

      const newInvestment = {
        id: currentTimelineData.investments.length + 1,
        title,
        type,
        startDate,
        endDate,
        amount,
        color: getColorForType(type),
        icon: getIconForType(type),
        teamSize: Math.floor(Math.random() * 4) + 1
      };

      currentTimelineData.investments.push(newInvestment);
      investmentData[currentPeriod].push(newInvestment);
      
      renderTimeline();
      updateStats();
      closeModal();

      if (!activeFilters.includes(type)) {
        activeFilters.push(type);
        document.querySelector(`[data-filter="${type}"]`).checked = true;
        updateFilterCount();
      }
    }

    function getColorForType(type) {
      const colors = {
        analysis: 'from-[#2a7fff] to-[#0ea5e9]',
        esg: 'from-[#22d3ee] to-[#14b8a6]',
        derivatives: 'from-[#a78bfa] to-[#7c3aed]'
      };
      return colors[type] || 'from-[#6b7280] to-[#4b5563]';
    }

    function getIconForType(type) {
      const icons = {
        analysis: 'trending-up',
        esg: 'leaf',
        derivatives: 'shield'
      };
      return icons[type] || 'circle';
    }

    function editInvestment(id) {
      const investment = currentTimelineData.investments.find(inv => inv.id === id);
      if (investment) {
        console.log('Editing investment:', investment.title);
      }
    }

    window.addEventListener('resize', () => {
      if (activeTab === 'timeline') {
        renderTimeline();
      }
    });

    updateFilterCount();
  </script>

</body></html>
All Prompts