summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/static/css/styles.css70
-rw-r--r--web/static/js/app.js77
-rw-r--r--web/templates/index.html185
3 files changed, 332 insertions, 0 deletions
diff --git a/web/static/css/styles.css b/web/static/css/styles.css
new file mode 100644
index 0000000..aee6ee3
--- /dev/null
+++ b/web/static/css/styles.css
@@ -0,0 +1,70 @@
+/* Custom styles for Personal Dashboard */
+
+/* Line clamp utility for truncating text */
+.line-clamp-3 {
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+/* Loading spinner */
+.spinner {
+ border: 3px solid #f3f3f3;
+ border-top: 3px solid #3b82f6;
+ border-radius: 50%;
+ width: 20px;
+ height: 20px;
+ animation: spin 1s linear infinite;
+ display: inline-block;
+ margin-left: 8px;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* Smooth transitions */
+* {
+ transition-property: background-color, border-color, color, fill, stroke;
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ transition-duration: 150ms;
+}
+
+/* Custom scrollbar */
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: #f1f1f1;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #888;
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #555;
+}
+
+/* Print styles */
+@media print {
+ .no-print {
+ display: none;
+ }
+}
+
+/* Dark mode support (optional) */
+@media (prefers-color-scheme: dark) {
+ /* Uncomment to enable dark mode */
+ /*
+ body {
+ background-color: #1a202c;
+ color: #e2e8f0;
+ }
+ */
+}
diff --git a/web/static/js/app.js b/web/static/js/app.js
new file mode 100644
index 0000000..a96c05d
--- /dev/null
+++ b/web/static/js/app.js
@@ -0,0 +1,77 @@
+// Personal Dashboard JavaScript
+
+// Auto-refresh every 5 minutes
+const AUTO_REFRESH_INTERVAL = 5 * 60 * 1000; // 5 minutes in milliseconds
+
+// Initialize auto-refresh on page load
+document.addEventListener('DOMContentLoaded', function() {
+ // Set up auto-refresh
+ setInterval(autoRefresh, AUTO_REFRESH_INTERVAL);
+});
+
+// Auto-refresh function
+async function autoRefresh() {
+ console.log('Auto-refreshing data...');
+ try {
+ const response = await fetch('/api/refresh', {
+ method: 'POST'
+ });
+
+ if (response.ok) {
+ // Reload the page to show updated data
+ window.location.reload();
+ }
+ } catch (error) {
+ console.error('Auto-refresh failed:', error);
+ }
+}
+
+// Manual refresh function
+async function refreshData() {
+ const button = event.target;
+ const originalText = button.textContent;
+
+ // Show loading state
+ button.disabled = true;
+ button.innerHTML = 'Refreshing...<span class="spinner"></span>';
+
+ try {
+ const response = await fetch('/api/refresh', {
+ method: 'POST'
+ });
+
+ if (response.ok) {
+ // Update last updated time
+ const now = new Date();
+ const timeString = now.toLocaleTimeString('en-US', {
+ hour: 'numeric',
+ minute: '2-digit'
+ });
+ document.getElementById('last-updated').textContent = timeString;
+
+ // Reload the page to show updated data
+ setTimeout(() => {
+ window.location.reload();
+ }, 500);
+ } else {
+ throw new Error('Refresh failed');
+ }
+ } catch (error) {
+ console.error('Refresh failed:', error);
+ alert('Failed to refresh data. Please try again.');
+ button.disabled = false;
+ button.textContent = originalText;
+ }
+}
+
+// Filter tasks by status
+function filterTasks(status) {
+ // This will be implemented in Phase 2
+ console.log('Filter tasks:', status);
+}
+
+// Toggle task completion
+function toggleTask(taskId) {
+ // This will be implemented in Phase 2
+ console.log('Toggle task:', taskId);
+}
diff --git a/web/templates/index.html b/web/templates/index.html
new file mode 100644
index 0000000..7668a94
--- /dev/null
+++ b/web/templates/index.html
@@ -0,0 +1,185 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Personal Dashboard</title>
+ <link rel="stylesheet" href="/static/css/styles.css">
+ <script src="https://cdn.tailwindcss.com"></script>
+</head>
+<body class="bg-gray-100 min-h-screen">
+ <div class="container mx-auto px-4 py-8 max-w-7xl">
+ <!-- Header -->
+ <header class="mb-8 flex justify-between items-center">
+ <h1 class="text-3xl font-bold text-gray-800">Personal Dashboard</h1>
+ <div class="flex items-center gap-4">
+ <span class="text-sm text-gray-600">
+ Last updated: <span id="last-updated">{{.LastUpdated.Format "3:04 PM"}}</span>
+ </span>
+ <button onclick="refreshData()" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition">
+ Refresh
+ </button>
+ </div>
+ </header>
+
+ <!-- Error Messages -->
+ {{if .Errors}}
+ <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
+ <p class="font-bold">Errors:</p>
+ <ul class="list-disc list-inside">
+ {{range .Errors}}
+ <li>{{.}}</li>
+ {{end}}
+ </ul>
+ </div>
+ {{end}}
+
+ <!-- Main Grid -->
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
+ <!-- Trello Boards Section (PRIORITY) -->
+ <div class="lg:col-span-3">
+ {{if .Boards}}
+ <div class="bg-white rounded-lg shadow-md p-6 mb-6">
+ <h2 class="text-xl font-semibold mb-4 text-gray-800">📋 Trello Boards</h2>
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+ {{range .Boards}}
+ <div class="border border-gray-300 rounded-lg p-4 bg-gradient-to-br from-blue-50 to-white">
+ <h3 class="font-bold text-lg text-gray-800 mb-3">{{.Name}}</h3>
+ {{if .Cards}}
+ <div class="space-y-2 max-h-96 overflow-y-auto">
+ {{range .Cards}}
+ <div class="bg-white border border-gray-200 rounded p-3 hover:shadow-md transition">
+ <p class="font-medium text-gray-800 text-sm">{{.Name}}</p>
+ {{if .ListName}}
+ <span class="inline-block mt-1 text-xs bg-gray-200 text-gray-700 px-2 py-1 rounded">
+ {{.ListName}}
+ </span>
+ {{end}}
+ {{if .DueDate}}
+ <span class="inline-block mt-1 text-xs bg-red-100 text-red-800 px-2 py-1 rounded">
+ Due: {{.DueDate.Format "Jan 2"}}
+ </span>
+ {{end}}
+ {{if .URL}}
+ <a href="{{.URL}}" target="_blank" class="text-blue-600 hover:text-blue-800 text-xs mt-1 inline-block">
+ View →
+ </a>
+ {{end}}
+ </div>
+ {{end}}
+ </div>
+ {{else}}
+ <p class="text-gray-500 text-sm text-center py-4">No cards</p>
+ {{end}}
+ </div>
+ {{end}}
+ </div>
+ </div>
+ {{end}}
+ </div>
+
+ <!-- Tasks Section -->
+ <div class="lg:col-span-2">
+ <div class="bg-white rounded-lg shadow-md p-6">
+ <h2 class="text-xl font-semibold mb-4 text-gray-800">✓ Todoist Tasks</h2>
+
+ {{if .Tasks}}
+ <div class="space-y-3">
+ {{range .Tasks}}
+ <div class="flex items-start gap-3 p-3 hover:bg-gray-50 rounded-lg transition">
+ <input type="checkbox" {{if .Completed}}checked{{end}}
+ class="mt-1 h-5 w-5 text-blue-600 rounded" disabled>
+ <div class="flex-1">
+ <p class="font-medium text-gray-800 {{if .Completed}}line-through text-gray-500{{end}}">
+ {{.Content}}
+ </p>
+ {{if .Description}}
+ <p class="text-sm text-gray-600 mt-1">{{.Description}}</p>
+ {{end}}
+ <div class="flex gap-2 mt-2 text-xs text-gray-500">
+ {{if .ProjectName}}
+ <span class="bg-gray-200 px-2 py-1 rounded">{{.ProjectName}}</span>
+ {{end}}
+ {{if .DueDate}}
+ <span class="bg-yellow-100 text-yellow-800 px-2 py-1 rounded">
+ Due: {{.DueDate.Format "Jan 2"}}
+ </span>
+ {{end}}
+ {{range .Labels}}
+ <span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">{{.}}</span>
+ {{end}}
+ </div>
+ </div>
+ {{if .URL}}
+ <a href="{{.URL}}" target="_blank" class="text-blue-600 hover:text-blue-800">
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
+ </svg>
+ </a>
+ {{end}}
+ </div>
+ {{end}}
+ </div>
+ {{else}}
+ <p class="text-gray-500 text-center py-8">No tasks found</p>
+ {{end}}
+ </div>
+ </div>
+
+ <!-- Meals Section -->
+ <div>
+ <div class="bg-white rounded-lg shadow-md p-6">
+ <h2 class="text-xl font-semibold mb-4 text-gray-800">Upcoming Meals</h2>
+
+ {{if .Meals}}
+ <div class="space-y-3">
+ {{range .Meals}}
+ <div class="border-l-4 border-green-500 pl-3 py-2">
+ <p class="font-medium text-gray-800">{{.RecipeName}}</p>
+ <div class="flex justify-between items-center mt-1">
+ <span class="text-sm text-gray-600">{{.Date.Format "Mon, Jan 2"}}</span>
+ <span class="text-xs bg-green-100 text-green-800 px-2 py-1 rounded capitalize">
+ {{.MealType}}
+ </span>
+ </div>
+ </div>
+ {{end}}
+ </div>
+ {{else}}
+ <p class="text-gray-500 text-center py-8">No meals planned</p>
+ {{end}}
+ </div>
+ </div>
+ </div>
+
+ <!-- Notes Section -->
+ {{if .Notes}}
+ <div class="mt-6">
+ <div class="bg-white rounded-lg shadow-md p-6">
+ <h2 class="text-xl font-semibold mb-4 text-gray-800">Recent Notes</h2>
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+ {{range .Notes}}
+ <div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition">
+ <h3 class="font-semibold text-gray-800 mb-2">{{.Title}}</h3>
+ <p class="text-sm text-gray-600 mb-2 line-clamp-3">{{.Content}}</p>
+ <div class="flex justify-between items-center text-xs text-gray-500">
+ <span>{{.Modified.Format "Jan 2, 3:04 PM"}}</span>
+ {{if .Tags}}
+ <div class="flex gap-1">
+ {{range .Tags}}
+ <span class="bg-purple-100 text-purple-800 px-2 py-1 rounded">#{{.}}</span>
+ {{end}}
+ </div>
+ {{end}}
+ </div>
+ </div>
+ {{end}}
+ </div>
+ </div>
+ </div>
+ {{end}}
+ </div>
+
+ <script src="/static/js/app.js"></script>
+</body>
+</html>