App Dépenses Trello v1.0
This commit is contained in:
233
templates/index.html
Normal file
233
templates/index.html
Normal file
@@ -0,0 +1,233 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>💸 Dépenses Trello</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root { --bg-dark: #0d0d1a; --bg-card: #16162a; --bg-input: #1a1a35; --primary: #e94560; --secondary: #7b2cbf; --accent: #00d9ff; --text: #fff; --text-dim: #888; --success: #00ff88; --danger: #ff4757; }
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Outfit', sans-serif; background: var(--bg-dark); color: var(--text); padding: 15px; min-height: 100vh; }
|
||||
h1 { text-align: center; font-size: 1.6em; background: linear-gradient(135deg, var(--primary), var(--secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
||||
.nav { display: flex; gap: 10px; margin: 15px 0; }
|
||||
.nav a { flex: 1; padding: 12px; background: var(--bg-card); border-radius: 10px; text-align: center; color: var(--text-dim); text-decoration: none; }
|
||||
.nav a.active { background: linear-gradient(135deg, var(--primary), var(--secondary)); color: #fff; }
|
||||
.card { background: var(--bg-card); border-radius: 15px; padding: 15px; margin-bottom: 15px; }
|
||||
h2 { font-size: 1em; margin-bottom: 12px; }
|
||||
.form-group { margin-bottom: 10px; }
|
||||
.form-group label { display: block; color: var(--text-dim); font-size: 0.85em; margin-bottom: 4px; }
|
||||
.form-group input, .form-group select { width: 100%; padding: 12px; background: var(--bg-input); border: 2px solid transparent; border-radius: 10px; color: var(--text); font-size: 1em; }
|
||||
.form-group input:focus { border-color: var(--accent); outline: none; }
|
||||
.btn { width: 100%; padding: 14px; border: none; border-radius: 10px; font-weight: 600; cursor: pointer; margin-top: 10px; }
|
||||
.btn-primary { background: linear-gradient(135deg, var(--accent), #0099cc); color: var(--bg-dark); }
|
||||
.btn-success { background: linear-gradient(135deg, var(--success), #00cc66); color: var(--bg-dark); }
|
||||
.btn-danger { background: var(--danger); color: #fff; }
|
||||
.prenom-tags { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
|
||||
.prenom-tag { display: flex; align-items: center; gap: 5px; background: linear-gradient(135deg, var(--primary), var(--secondary)); padding: 8px 12px; border-radius: 20px; font-size: 0.85em; }
|
||||
.expense-item { display: flex; justify-content: space-between; align-items: center; padding: 12px; background: var(--bg-input); border-radius: 10px; margin-bottom: 8px; flex-wrap: wrap; gap: 5px; }
|
||||
.expense-prenom { background: linear-gradient(135deg, var(--primary), var(--secondary)); padding: 4px 10px; border-radius: 15px; font-size: 0.75em; white-space: nowrap; }
|
||||
.expense-montant { font-weight: 700; color: var(--accent); }
|
||||
.total { text-align: right; font-size: 1.2em; margin: 10px 0; padding: 12px; background: rgba(0,217,255,0.1); border-radius: 10px; }
|
||||
.total span { font-weight: 700; color: var(--accent); }
|
||||
.preview { background: var(--bg-input); padding: 12px; border-radius: 10px; white-space: pre-wrap; color: var(--success); font-family: monospace; margin-top: 10px; }
|
||||
.actions { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>💸 Dépenses Trello</h1>
|
||||
|
||||
<div class="nav">
|
||||
<a href="?page=saisie" id="nav-saisie">✏️ Saisie</a>
|
||||
<a href="?page=config" id="nav-config">⚙️ Config</a>
|
||||
</div>
|
||||
|
||||
<div id="saisie" style="display:none">
|
||||
<div class="card">
|
||||
<h2>➕ Nouvelle dépense</h2>
|
||||
<form method="POST" action="/api/add" id="form-add">
|
||||
<div class="form-group"><label>👤 Prénom</label><select name="prenom" id="prenom-select" required></select></div>
|
||||
<div class="form-group"><label>📅 Date</label><input type="date" name="date" id="depense-date" required></div>
|
||||
<div class="form-group"><label>📝 Libellé</label><input type="text" name="libelle" placeholder="Courses, Essence..." required></div>
|
||||
<div class="form-group"><label>💰 Montant (€)</label><input type="number" name="montant" placeholder="0.00" step="0.01" required></div>
|
||||
<button type="submit" class="btn btn-primary">➕ Ajouter</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>📋 Dépenses (<span id="count">0</span>)</h2>
|
||||
<div id="expenses"></div>
|
||||
<div class="total">Total: <span id="total">0.00€</span></div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" onclick="generate()">👁️ Aperçu</button>
|
||||
<button class="btn btn-success" onclick="sendTrello()">🟦 Envoyer Trello</button>
|
||||
<button class="btn btn-danger" onclick="clearAll()">🗑️ Tout effacer</button>
|
||||
</div>
|
||||
<div class="preview" id="preview" style="display:none"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="config" style="display:none">
|
||||
<div class="card">
|
||||
<h2>👥 Prénoms</h2>
|
||||
<div id="prenom-tags"></div>
|
||||
<form method="POST" action="/api/prenom/add" id="form-prenom">
|
||||
<div style="display:flex;gap:10px;margin-top:10px">
|
||||
<input type="text" name="prenom" placeholder="Nouveau prénom..." style="flex:1">
|
||||
<button type="submit" class="btn btn-primary" style="width:auto">➕</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>📄 Format du texte</h2>
|
||||
<form method="POST" action="/api/format" id="form-format">
|
||||
<div class="form-group"><input type="text" name="format" id="format-input"></div>
|
||||
<button type="submit" class="btn btn-primary">💾 Sauvegarder</button>
|
||||
</form>
|
||||
<div class="preview" id="format-preview"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>🔗 Trello</h2>
|
||||
<form method="POST" action="/api/trello/save" id="form-trello">
|
||||
<div class="form-group"><label>🔑 API Key</label><input type="text" name="api_key" id="trello-api_key"></div>
|
||||
<div class="form-group"><label>🔐 Token</label><input type="password" name="token" id="trello-token"></div>
|
||||
<div class="form-group"><label>📁 List ID</label><input type="text" name="list_id" id="trello-list_id"></div>
|
||||
<button type="submit" class="btn btn-primary">💾 Sauvegarder</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var config = { prenoms: [], format: '', trello: {} };
|
||||
var depenses = [];
|
||||
|
||||
function formatDate(d) { if(!d) return ""; return d.split("-").reverse().join("/"); }
|
||||
|
||||
async function load() {
|
||||
var r = await fetch('/api/config');
|
||||
config = await r.json();
|
||||
r = await fetch('/api/depenses');
|
||||
depenses = await r.json();
|
||||
render();
|
||||
checkPage();
|
||||
}
|
||||
|
||||
function checkPage() {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var page = params.get('page') || 'saisie';
|
||||
showPage(page);
|
||||
}
|
||||
|
||||
function showPage(page) {
|
||||
document.getElementById('saisie').style.display = page === 'saisie' ? 'block' : 'none';
|
||||
document.getElementById('config').style.display = page === 'config' ? 'block' : 'none';
|
||||
document.getElementById('nav-saisie').className = page === 'saisie' ? 'nav a active' : 'nav a';
|
||||
document.getElementById('nav-config').className = page === 'config' ? 'nav a active' : 'nav a';
|
||||
}
|
||||
|
||||
function render() {
|
||||
// Prenoms
|
||||
document.getElementById('prenom-select').innerHTML = '<option value="">Choisir...</option>' + config.prenoms.map(function(p) { return '<option value="' + p + '">' + p + '</option>'; }).join('');
|
||||
document.getElementById('prenom-tags').innerHTML = config.prenoms.map(function(p, i) { return '<span class="prenom-tag">' + p + '<a href="/api/prenom/del/' + i + '" style="color:#fff;text-decoration:none;margin-left:5px">×</a></span>'; }).join('');
|
||||
|
||||
// Format
|
||||
document.getElementById('format-input').value = config.format || '{prenom} - {date} - {libelle} - {montant}€';
|
||||
var fmt = config.format || '{prenom} - {date} - {libelle} - {montant}€';
|
||||
fmt = fmt.replace('{prenom}', 'Papa').replace('{date}', '27/02/2026').replace('{libelle}', 'Courses').replace('{montant}', '45.50');
|
||||
document.getElementById('format-preview').textContent = fmt;
|
||||
|
||||
// Trello
|
||||
if (config.trello) {
|
||||
document.getElementById('trello-api_key').value = config.trello.api_key || '';
|
||||
document.getElementById('trello-token').value = config.trello.token || '';
|
||||
document.getElementById('trello-list_id').value = config.trello.list_id || '';
|
||||
}
|
||||
|
||||
// Expenses
|
||||
document.getElementById('count').textContent = depenses.length;
|
||||
var total = depenses.reduce(function(s, d) { return s + (parseFloat(d.montant) || 0); }, 0);
|
||||
document.getElementById('total').textContent = total.toFixed(2) + '€';
|
||||
|
||||
var html = '';
|
||||
for (var i = 0; i < depenses.length; i++) {
|
||||
var d = depenses[i];
|
||||
html += '<div class="expense-item">';
|
||||
html += '<div style="display:flex;align-items:center;gap:10px">';
|
||||
html += '<span class="expense-prenom">' + d.prenom + '</span>';
|
||||
html += '<span>' + formatDate(d.date) + ' - ' + d.libelle + '</span>';
|
||||
html += '</div>';
|
||||
html += '<div style="display:flex;align-items:center;gap:10px">';
|
||||
html += '<span class="expense-montant">' + parseFloat(d.montant).toFixed(2) + '€</span>';
|
||||
html += '<a href="/api/del/' + d.id + '" style="color:var(--danger);text-decoration:none;font-size:1.2em">🗑️</a>';
|
||||
html += '</div></div>';
|
||||
}
|
||||
document.getElementById('expenses').innerHTML = html;
|
||||
|
||||
document.getElementById('depense-date').value = new Date().toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
document.getElementById('form-add').onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
var fd = new FormData(e.target);
|
||||
fetch('/api/add', { method: 'POST', body: fd }).then(function() {
|
||||
e.target.reset();
|
||||
load();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('form-prenom').onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
var fd = new FormData(e.target);
|
||||
fetch('/api/prenom/add', { method: 'POST', body: fd }).then(function() {
|
||||
e.target.reset();
|
||||
load();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('form-format').onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
var fd = new FormData(e.target);
|
||||
fetch('/api/format', { method: 'POST', body: fd }).then(function() {
|
||||
alert('Format sauvegardé!');
|
||||
load();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('form-trello').onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
var fd = new FormData(e.target);
|
||||
fetch('/api/trello/save', { method: 'POST', body: fd }).then(function() {
|
||||
alert('Config Trello sauvegardée!');
|
||||
load();
|
||||
});
|
||||
};
|
||||
|
||||
async function generate() {
|
||||
var r = await fetch('/api/generate', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({depenses: depenses}) });
|
||||
var d = await r.json();
|
||||
document.getElementById('preview').textContent = d.text;
|
||||
document.getElementById('preview').style.display = 'block';
|
||||
}
|
||||
|
||||
async function sendTrello() {
|
||||
if (!config.trello || !config.trello.api_key) { alert('Configure Trello!'); showPage('config'); return; }
|
||||
await generate();
|
||||
var text = document.getElementById('preview').textContent;
|
||||
var r = await fetch('/api/trello/send', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({title: 'Dépenses ' + new Date().toLocaleDateString('fr'), text: text}) });
|
||||
if (r.ok) { alert('Envoyé sur Trello!'); clearAll(); }
|
||||
else { var e = await r.json(); alert('Erreur: ' + e.error); }
|
||||
}
|
||||
|
||||
async function clearAll() {
|
||||
if (confirm('Tout effacer?')) {
|
||||
await fetch('/api/clear', { method: 'POST' });
|
||||
load();
|
||||
}
|
||||
}
|
||||
|
||||
load();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user