project.html
html
<html data-theme="dark">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{.Name}} - AI Project Manager</title>
  <!-- HTMX -->
  <script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"
    integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz"
    crossorigin="anonymous"></script>
  <!-- Tailwind CSS & Daisy UI -->
  <link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
  <link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
  <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
  <!-- Sortable.js for drag-and-drop -->
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
</head>
{{with projects.CurrentProject}}
<body class="bg-base-200">
  <div class="min-h-screen">
    <!-- Navbar -->
    <div class="navbar bg-base-100 shadow-lg">
      <div class="flex-1">
        <a href="{{hostPrefix}}/" class="btn btn-ghost text-xl">🤖 AI Project Manager</a>
        <div class="divider divider-horizontal mx-2"></div>
        <span class="text-lg font-semibold">{{.Name}}</span>
      </div>
      <div class="flex-none">
        <div class="flex gap-2">
          {{if not projects.HasAPIKey}}
          <button class="btn btn-warning btn-sm" onclick="api_key_modal.showModal()">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24"
              stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
            </svg>
            Configure API Key
          </button>
          {{end}}
          <button class="btn btn-ghost btn-sm" onclick="ai_summary_modal.showModal()">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24"
              stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
            </svg>
            AI Summary
          </button>
          <button class="btn btn-secondary btn-sm" onclick="ai_generate_modal.showModal()">
            ✨ Generate Tasks
          </button>
          <button class="btn btn-primary btn-sm" onclick="create_task_modal.showModal()">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24"
              stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
            </svg>
            New Task
          </button>
        </div>
      </div>
    </div>
    <!-- Main Content -->
    <div class="container mx-auto p-6 max-w-7xl">
      <!-- Project Header -->
      <div class="card bg-base-100 shadow-xl mb-6">
        <div class="card-body">
          <h2 class="card-title text-2xl">{{.Name}}</h2>
          <p class="text-base-content/70">{{.Description}}</p>
          <!-- Stats -->
          <div class="stats shadow mt-4">
            <div class="stat place-items-center">
              <div class="stat-title">To Do</div>
              <div class="stat-value text-info">{{index projects.TaskStats "todo"}}</div>
            </div>
            <div class="stat place-items-center">
              <div class="stat-title">In Progress</div>
              <div class="stat-value text-warning">{{index projects.TaskStats "in_progress"}}</div>
            </div>
            <div class="stat place-items-center">
              <div class="stat-title">Done</div>
              <div class="stat-value text-success">{{index projects.TaskStats "done"}}</div>
            </div>
          </div>
        </div>
      </div>
      <!-- Kanban Board -->
      <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
        <!-- To Do Column -->
        <div class="card bg-base-100 shadow-xl">
          <div class="card-body">
            <h3 class="card-title">
              <div class="badge badge-info badge-lg">{{index projects.TaskStats "todo"}}</div>
              To Do
            </h3>
            <form class="sortable space-y-3 mt-4" hx-post="{{hostPrefix}}/tasks/reorder" hx-trigger="end">
              {{range projects.TodoTasks}}
              <div class="card bg-base-200 shadow-sm cursor-move hover:shadow-md transition-shadow">
                <input type="hidden" name="task" value="{{.ID}}" />
                <div class="card-body p-4">
                  <h4 class="font-semibold">{{.Title}}</h4>
                  {{if .Description}}
                  <p class="text-sm opacity-70 mt-1">{{.Description}}</p>
                  {{end}}
                  <div class="card-actions justify-between items-center mt-3">
                    <div class="badge badge-sm {{if eq .Priority " high"}}badge-error{{else if eq .Priority "medium"
                      }}badge-warning{{else}}badge-info{{end}}">
                      {{.Priority}}
                    </div>
                    <button type="button" onclick="event.stopPropagation();"
                      hx-post="{{hostPrefix}}/task/{{.ID}}/move/in_progress" hx-swap="none"
                      class="btn btn-xs btn-ghost">
                    </button>
                  </div>
                </div>
              </div>
              {{else}}
              <div class="text-center text-sm opacity-50 py-8">
                No tasks yet
              </div>
              {{end}}
            </form>
          </div>
        </div>
        <!-- In Progress Column -->
        <div class="card bg-base-100 shadow-xl">
          <div class="card-body">
            <h3 class="card-title">
              <div class="badge badge-warning badge-lg">{{index projects.TaskStats "in_progress"}}</div>
              In Progress
            </h3>
            <form class="sortable space-y-3 mt-4" hx-post="{{hostPrefix}}/tasks/reorder" hx-trigger="end">
              {{range projects.InProgressTasks}}
              <div class="card bg-base-200 shadow-sm cursor-move hover:shadow-md transition-shadow">
                <input type="hidden" name="task" value="{{.ID}}" />
                <div class="card-body p-4">
                  <h4 class="font-semibold">{{.Title}}</h4>
                  {{if .Description}}
                  <p class="text-sm opacity-70 mt-1">{{.Description}}</p>
                  {{end}}
                  <div class="card-actions justify-between items-center mt-3">
                    <div class="badge badge-sm {{if eq .Priority " high"}}badge-error{{else if eq .Priority "medium"
                      }}badge-warning{{else}}badge-info{{end}}">
                      {{.Priority}}
                    </div>
                    <div class="btn-group btn-group-horizontal">
                      <button type="button" onclick="event.stopPropagation();"
                        hx-post="{{hostPrefix}}/task/{{.ID}}/move/todo" hx-swap="none" class="btn btn-xs btn-ghost">
                      </button>
                      <button type="button" onclick="event.stopPropagation();"
                        hx-post="{{hostPrefix}}/task/{{.ID}}/move/done" hx-swap="none" class="btn btn-xs btn-ghost">
                      </button>
                    </div>
                  </div>
                </div>
              </div>
              {{else}}
              <div class="text-center text-sm opacity-50 py-8">
                No tasks yet
              </div>
              {{end}}
            </form>
          </div>
        </div>
        <!-- Done Column -->
        <div class="card bg-base-100 shadow-xl">
          <div class="card-body">
            <h3 class="card-title">
              <div class="badge badge-success badge-lg">{{index projects.TaskStats "done"}}</div>
              Done
            </h3>
            <form class="sortable space-y-3 mt-4" hx-post="{{hostPrefix}}/tasks/reorder" hx-trigger="end">
              {{range projects.DoneTasks}}
              <div class="card bg-base-200 shadow-sm opacity-75 cursor-move hover:opacity-100 transition-opacity">
                <input type="hidden" name="task" value="{{.ID}}" />
                <div class="card-body p-4">
                  <h4 class="font-semibold line-through">{{.Title}}</h4>
                  {{if .Description}}
                  <p class="text-sm opacity-70 mt-1 line-through">{{.Description}}</p>
                  {{end}}
                  <div class="card-actions justify-between items-center mt-3">
                    <div class="badge badge-sm {{if eq .Priority " high"}}badge-error{{else if eq .Priority "medium"
                      }}badge-warning{{else}}badge-info{{end}}">
                      {{.Priority}}
                    </div>
                    <button type="button" onclick="event.stopPropagation();"
                      hx-post="{{hostPrefix}}/task/{{.ID}}/move/in_progress" hx-swap="none"
                      class="btn btn-xs btn-ghost">
                    </button>
                  </div>
                </div>
              </div>
              {{else}}
              <div class="text-center text-sm opacity-50 py-8">
                No completed tasks
              </div>
              {{end}}
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
  <!-- Create Task Modal -->
  <dialog id="create_task_modal" class="modal">
    <div class="modal-box">
      <h3 class="font-bold text-lg mb-4">Create New Task</h3>
      <form hx-post="{{hostPrefix}}/task/create" hx-swap="none">
        <input type="hidden" name="project_id" value="{{.ID}}" />
        <div class="form-control mb-4">
          <label class="label">
            <span class="label-text">Task Title</span>
          </label>
          <input type="text" name="title" placeholder="Fix the login bug" class="input input-bordered" required />
        </div>
        <div class="form-control mb-4">
          <label class="label">
            <span class="label-text">Description</span>
          </label>
          <textarea name="description" placeholder="What needs to be done?" class="textarea textarea-bordered h-24"
            rows="3"></textarea>
        </div>
        <div class="form-control mb-4">
          <label class="label">
            <span class="label-text">Priority</span>
          </label>
          <select name="priority" class="select select-bordered">
            <option value="low">Low</option>
            <option value="medium" selected>Medium</option>
            <option value="high">High</option>
          </select>
        </div>
        <div class="modal-action">
          <button type="button" class="btn" onclick="create_task_modal.close()">Cancel</button>
          <button type="submit" class="btn btn-primary">Create Task</button>
        </div>
      </form>
    </div>
    <form method="dialog" class="modal-backdrop">
      <button>close</button>
    </form>
  </dialog>
  <!-- AI Generate Tasks Modal -->
  <dialog id="ai_generate_modal" class="modal">
    <div class="modal-box max-w-4xl">
      <h3 class="font-bold text-lg mb-2">✨ AI Task Generator</h3>
      <p class="text-sm opacity-70 mb-4">Let AI analyze your project and generate suggested tasks</p>
      <div id="ai-generate-output" class="bg-base-200 rounded-box p-4 min-h-[200px] max-h-[500px] overflow-y-auto">
        <div class="flex flex-col items-center justify-center h-full text-base-content/50 py-12">
          <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mb-4 opacity-30" fill="none" viewBox="0 0 24 24"
            stroke="currentColor">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
              d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
          </svg>
          <p>Click "Generate" to start AI task generation</p>
        </div>
      </div>
      <div class="modal-action">
        <button type="button" class="btn" onclick="ai_generate_modal.close()">Close</button>
        <button type="button" class="btn btn-primary" hx-post="{{hostPrefix}}/ai/generate-tasks"
          hx-vals='{"project_id": "{{.ID}}"}' hx-target="#ai-generate-output" hx-swap="innerHTML"
          hx-indicator="#ai-generate-spinner">
          <span id="ai-generate-spinner" class="loading loading-spinner loading-sm htmx-indicator"></span>
          Generate Tasks
        </button>
      </div>
    </div>
    <form method="dialog" class="modal-backdrop">
      <button>close</button>
    </form>
  </dialog>
  <!-- API Key Configuration Modal -->
  <dialog id="api_key_modal" class="modal">
    <div class="modal-box">
      <h3 class="font-bold text-lg mb-2">🔑 Configure Friendli API Key</h3>
      <p class="text-sm opacity-70 mb-4">Enter your Friendli.ai API key to enable AI features</p>
      <form hx-post="{{hostPrefix}}/api/settings/api-key" hx-swap="none">
        <div class="form-control mb-4">
          <label class="label">
            <span class="label-text">API Key</span>
          </label>
          <input type="password" name="api_key" placeholder="flp_..." class="input input-bordered" required />
          <label class="label">
            <span class="label-text-alt">Get your API key from <a href="https://friendli.ai" target="_blank"
                class="link">friendli.ai</a></span>
          </label>
        </div>
        <div class="modal-action">
          <button type="button" class="btn" onclick="api_key_modal.close()">Cancel</button>
          <button type="submit" class="btn btn-primary">Save API Key</button>
        </div>
      </form>
    </div>
    <form method="dialog" class="modal-backdrop">
      <button>close</button>
    </form>
  </dialog>
  <!-- AI Project Summary Modal -->
  <dialog id="ai_summary_modal" class="modal">
    <div class="modal-box max-w-4xl">
      <h3 class="font-bold text-lg mb-2">🤖 AI Project Summary</h3>
      <p class="text-sm opacity-70 mb-4">AI analysis of your project's current status</p>
      <div id="ai-summary-output"
        class="prose max-w-none bg-base-200 rounded-box p-4 min-h-[300px] max-h-[600px] overflow-y-auto">
        <div class="flex flex-col items-center justify-center h-full text-base-content/50 py-12 not-prose">
          <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mb-4 opacity-30" fill="none" viewBox="0 0 24 24"
            stroke="currentColor">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
              d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />
          </svg>
          <p>Click "Analyze" to get AI insights</p>
        </div>
      </div>
      <div class="modal-action">
        <button type="button" class="btn" onclick="ai_summary_modal.close()">Close</button>
        <button type="button" class="btn btn-primary" hx-post="{{hostPrefix}}/ai/project-summary"
          hx-vals='{"project_id": "{{.ID}}"}' hx-target="#ai-summary-output" hx-swap="innerHTML"
          hx-indicator="#ai-summary-spinner">
          <span id="ai-summary-spinner" class="loading loading-spinner loading-sm htmx-indicator"></span>
          Analyze Project
        </button>
      </div>
    </div>
    <form method="dialog" class="modal-backdrop">
      <button>close</button>
    </form>
  </dialog>
  <!-- Initialize Sortable.js -->
  <script>
    htmx.onLoad(function (content) {
      var sortables = content.querySelectorAll(".sortable");
      for (var i = 0; i < sortables.length; i++) {
        var sortable = sortables[i];
        new Sortable(sortable, {
          animation: 150,
          ghostClass: 'opacity-50',
          dragClass: 'rotate-1 scale-105',
          handle: '.card',
          onEnd: function (evt) {
            htmx.trigger(evt.from, 'end');
          }
        });
      }
    });
  </script>
</body>
{{else}}
<body class="bg-base-200">
  <div class="hero min-h-screen">
    <div class="hero-content text-center">
      <div class="max-w-md">
        <h1 class="text-5xl font-bold mb-4">404</h1>
        <p class="text-xl mb-6">Project not found</p>
        <a href="{{hostPrefix}}/" class="btn btn-primary">Go Home</a>
      </div>
    </div>
  </div>
</body>
{{end}}
</html>
No comments yet.