first commit

This commit is contained in:
Roger Oriol
2026-02-03 23:50:19 +01:00
commit 87fb32b559
80 changed files with 8884 additions and 0 deletions

0
src/web/__init__.py Normal file
View File

View File

View File

View File

@@ -0,0 +1,299 @@
/* MyOrg Assistant Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #2563eb;
--secondary: #64748b;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--bg: #f8fafc;
--surface: #ffffff;
--text: #1e293b;
--text-muted: #64748b;
--border: #e2e8f0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
}
/* Navigation */
.navbar {
background: var(--surface);
border-bottom: 1px solid var(--border);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-brand h1 {
font-size: 1.5rem;
color: var(--primary);
}
.nav-links {
display: flex;
gap: 1.5rem;
}
.nav-links a {
text-decoration: none;
color: var(--text-muted);
font-weight: 500;
transition: color 0.2s;
}
.nav-links a:hover,
.nav-links a.active {
color: var(--primary);
}
/* Container */
.container {
max-width: 1200px;
margin: 2rem auto;
padding: 0 2rem;
}
/* Dashboard */
.dashboard-header {
margin-bottom: 2rem;
}
.date {
color: var(--text-muted);
font-size: 0.95rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.stat-card {
background: var(--surface);
padding: 1.5rem;
border-radius: 8px;
border: 1px solid var(--border);
}
.stat-value {
font-size: 2rem;
font-weight: bold;
color: var(--primary);
}
.stat-label {
color: var(--text-muted);
font-size: 0.9rem;
margin-top: 0.5rem;
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.dashboard-section {
background: var(--surface);
padding: 1.5rem;
border-radius: 8px;
border: 1px solid var(--border);
}
.dashboard-section h3 {
margin-bottom: 1rem;
color: var(--text);
}
.empty-state {
color: var(--text-muted);
font-style: italic;
}
/* Task Items */
.task-list, .event-list, .project-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.task-item, .event-item, .project-item {
padding: 0.75rem;
background: var(--bg);
border-radius: 4px;
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.priority-badge {
background: var(--warning);
color: white;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.85rem;
font-weight: bold;
}
.priority-A { background: var(--danger); }
.priority-B { background: var(--warning); }
.priority-C { background: var(--secondary); }
.tag {
background: var(--primary);
color: white;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.85rem;
}
.tag.context {
background: var(--success);
}
.tag.project {
background: var(--primary);
}
.due-date {
color: var(--warning);
font-size: 0.9rem;
margin-left: auto;
}
.event-time {
font-weight: 600;
color: var(--primary);
min-width: 60px;
}
.project-tag {
font-weight: 600;
color: var(--primary);
}
/* Chat */
.chat-container {
max-width: 800px;
margin: 0 auto;
}
.chat-messages {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 1rem;
height: 500px;
overflow-y: auto;
margin-bottom: 1rem;
}
.message {
margin-bottom: 1rem;
padding: 0.75rem;
border-radius: 4px;
}
.message.assistant {
background: var(--bg);
}
.message.user {
background: var(--primary);
color: white;
margin-left: 20%;
}
.chat-form {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
.chat-form input {
flex: 1;
padding: 0.75rem;
border: 1px solid var(--border);
border-radius: 4px;
font-size: 1rem;
}
.chat-form button {
padding: 0.75rem 1.5rem;
background: var(--primary);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
}
.chat-form button:hover {
opacity: 0.9;
}
/* Buttons */
.btn-link {
color: var(--primary);
text-decoration: none;
font-size: 0.9rem;
margin-top: 0.5rem;
display: inline-block;
}
.btn-secondary {
background: var(--secondary);
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-secondary:hover {
opacity: 0.9;
}
/* Footer */
footer {
text-align: center;
padding: 2rem;
color: var(--text-muted);
font-size: 0.9rem;
}
/* Responsive */
@media (max-width: 768px) {
.navbar {
flex-direction: column;
gap: 1rem;
}
.nav-links {
flex-wrap: wrap;
justify-content: center;
}
.dashboard-grid {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}

View File

View File

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}MyOrg Assistant{% endblock %}</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
{% block extra_head %}{% endblock %}
</head>
<body>
<nav class="navbar">
<div class="nav-brand">
<h1>🤖 MyOrg Assistant</h1>
</div>
<div class="nav-links">
<a href="/dashboard" class="{% if page == 'dashboard' %}active{% endif %}">📊 Dashboard</a>
<a href="/chat" class="{% if page == 'chat' %}active{% endif %}">💬 Chat</a>
<a href="/tasks" class="{% if page == 'tasks' %}active{% endif %}">✅ Tasks</a>
<a href="/calendar" class="{% if page == 'calendar' %}active{% endif %}">📅 Calendar</a>
<a href="/projects" class="{% if page == 'projects' %}active{% endif %}">📂 Projects</a>
</div>
</nav>
<main class="container">
{% block content %}{% endblock %}
</main>
<footer>
<p>MyOrg Assistant - Powered by Claude Sonnet 4.5</p>
</footer>
{% block extra_scripts %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,23 @@
{% extends "base.html" %}
{% block title %}Calendar - MyOrg Assistant{% endblock %}
{% block content %}
<h2>📅 Calendar</h2>
<h3>Today - {{ today }}</h3>
<div class="event-list">
{% for event in today_events %}
<div class="event-item">
<span class="event-time">{% if event.time %}{{ event.time.strftime('%H:%M') }}{% else %}All day{% endif %}</span>
{{ event.description }}
</div>
{% endfor %}
</div>
<h3>Upcoming (Next 7 Days)</h3>
<div class="event-list">
{% for event in upcoming_events %}
<div class="event-item">
<span>{{ event.date.strftime('%Y-%m-%d') }} {% if event.time %}{{ event.time.strftime('%H:%M') }}{% endif %}</span>
{{ event.description }}
</div>
{% endfor %}
</div>
{% endblock %}

View File

@@ -0,0 +1,32 @@
{% extends "base.html" %}
{% block title %}Chat - MyOrg Assistant{% endblock %}
{% block content %}
<div class="chat-container">
<h2>💬 Chat with Assistant</h2>
<div class="chat-messages" id="messages">
<div class="message assistant">
<strong>Assistant:</strong> Hi! I'm your MyOrg assistant. Ask me anything about your tasks, calendar, or projects!
</div>
</div>
<form class="chat-form" hx-post="/api/chat" hx-target="#messages" hx-swap="beforeend" hx-on::after-request="this.reset()">
<input type="text" name="message" placeholder="Type your message..." required autofocus>
<button type="submit">Send</button>
</form>
<button hx-post="/api/chat/reset" hx-swap="none" class="btn-secondary">Clear History</button>
</div>
{% endblock %}
{% block extra_scripts %}
<script>
// Auto-scroll to bottom when new messages arrive
htmx.on('htmx:afterSwap', function(evt) {
var messages = document.getElementById('messages');
messages.scrollTop = messages.scrollHeight;
});
</script>
{% endblock %}

View File

@@ -0,0 +1,111 @@
{% extends "base.html" %}
{% block title %}Dashboard - MyOrg Assistant{% endblock %}
{% block content %}
<div class="dashboard">
<div class="dashboard-header">
<h2>📊 Dashboard</h2>
<p class="date">{{ today }}</p>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value">{{ stats.events_today }}</div>
<div class="stat-label">Events Today</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ stats.priority_tasks }}</div>
<div class="stat-label">Priority Tasks</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ stats.due_soon }}</div>
<div class="stat-label">Due Soon</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ stats.active_projects }}</div>
<div class="stat-label">Active Projects</div>
</div>
</div>
<div class="dashboard-grid">
<!-- Today's Events -->
<div class="dashboard-section">
<h3>📅 Today's Schedule</h3>
{% if events %}
<div class="event-list">
{% for event in events %}
<div class="event-item">
<span class="event-time">
{% if event.time %}{{ event.time.strftime('%H:%M') }}{% else %}All day{% endif %}
</span>
<span class="event-desc">{{ event.description }}</span>
{% for context in event.contexts %}
<span class="tag context">@{{ context }}</span>
{% endfor %}
</div>
{% endfor %}
</div>
{% else %}
<p class="empty-state">No events scheduled today</p>
{% endif %}
</div>
<!-- Priority Tasks -->
<div class="dashboard-section">
<h3>✅ Priority Tasks</h3>
{% if priority_tasks %}
<div class="task-list">
{% for task in priority_tasks %}
<div class="task-item">
<span class="priority-badge priority-{{ task.priority }}">{{ task.priority }}</span>
<span class="task-desc">{{ task.description }}</span>
{% for project in task.projects %}
<span class="tag project">+{{ project }}</span>
{% endfor %}
</div>
{% endfor %}
</div>
<a href="/tasks?priority=A" class="btn-link">View all →</a>
{% else %}
<p class="empty-state">No priority tasks</p>
{% endif %}
</div>
<!-- Due Soon -->
<div class="dashboard-section">
<h3>⏰ Due Soon</h3>
{% if due_soon %}
<div class="task-list">
{% for task in due_soon %}
<div class="task-item">
<span class="task-desc">{{ task.description }}</span>
<span class="due-date">📅 {{ task.due_date.strftime('%Y-%m-%d') }}</span>
</div>
{% endfor %}
</div>
{% else %}
<p class="empty-state">Nothing due soon</p>
{% endif %}
</div>
<!-- Active Projects -->
<div class="dashboard-section">
<h3>📂 Active Projects</h3>
{% if active_projects %}
<div class="project-list">
{% for project in active_projects %}
<div class="project-item">
<span class="project-tag">+{{ project.tag }}</span>
<span class="project-desc">{{ project.description }}</span>
</div>
{% endfor %}
</div>
<a href="/projects" class="btn-link">View all →</a>
{% else %}
<p class="empty-state">No active projects</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block title %}Projects - MyOrg Assistant{% endblock %}
{% block content %}
<h2>📂 Projects</h2>
<p>Active: {{ stats.active }} | Waiting: {{ stats.waiting }} | Someday: {{ stats.someday }}</p>
<div class="project-list">
{% for project in projects %}
<div class="project-item">
<strong>+{{ project.tag }}</strong> - {{ project.description }} [{{ project.status }}]
{% if project_task_counts.get(project.tag) %}
<span class="badge">{{ project_task_counts[project.tag] }} tasks</span>
{% endif %}
</div>
{% endfor %}
</div>
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block title %}Tasks - MyOrg Assistant{% endblock %}
{% block content %}
<h2>✅ Tasks</h2>
<p>Total: {{ total_tasks }} active, {{ completed_tasks }} completed</p>
<div class="task-list">
{% for task in tasks %}
<div class="task-item">
<span class="{% if task.priority %}priority-{{ task.priority }}{% endif %}">
{% if task.priority %}({{ task.priority }}){% endif %} {{ task.description }}
</span>
{% for p in task.projects %}<span class="tag">+{{ p }}</span>{% endfor %}
{% for c in task.contexts %}<span class="tag">@{{ c }}</span>{% endfor %}
</div>
{% endfor %}
</div>
{% endblock %}