Files
tdb-rdb/public/index.html
T
2026-07-23 15:52:54 +02:00

409 lines
21 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tableau de bord — RdB</title>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Lora:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.0/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
<script type="module">
import { Editor, Mark } from 'https://esm.sh/@tiptap/core@2.11.7';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.11.7';
const Comment = Mark.create({
name: 'comment',
addAttributes() {
return {
text: { default: '', parseHTML: el => el.getAttribute('data-comment') || '' },
author: { default: 'E', parseHTML: el => el.getAttribute('data-author') || 'E' },
date: { default: '', parseHTML: el => el.getAttribute('data-date') || '' },
};
},
parseHTML() {
return [{ tag: 'span[data-comment]' }];
},
renderHTML({ HTMLAttributes }) {
return ['span', {
'data-comment': HTMLAttributes.text,
'data-author': HTMLAttributes.author,
'data-date': HTMLAttributes.date,
class: `comment-mark comment-${(HTMLAttributes.author || 'E').toLowerCase()}`,
}, 0];
},
});
window.TiptapEditor = Editor;
window.TiptapStarterKit = StarterKit;
window.TiptapComment = Comment;
window.dispatchEvent(new Event('tiptap-loaded'));
</script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="app">
<div class="sidebar">
<div class="sidebar-logo">Tableau de bord</div>
<div class="sidebar-project">Projet RdB</div>
<div class="nav-item" data-view="vision" onclick="switchView('vision')"><span class="nav-icon"><svg viewBox="0 0 24 24"><polygon points="12,2 15.1,8.3 22,9.2 17,14.1 18.2,21 12,17.8 5.8,21 7,14.1 2,9.2 8.9,8.3" stroke-width="1.6" fill="none"/></svg></span> Vision</div>
<div class="nav-item" data-view="frise" onclick="switchView('frise')"><span class="nav-icon"><svg viewBox="0 0 24 24"><line x1="3" y1="12" x2="21" y2="12"/><circle cx="3" cy="12" r="3"/><circle cx="12" cy="12" r="3"/><circle cx="21" cy="12" r="3"/></svg></span> Frise</div>
<div class="nav-item" data-view="redaction" onclick="switchView('redaction')"><span class="nav-icon"><svg viewBox="0 0 24 24"><path d="M17 3l4 4L7 21H3v-4L17 3z"/><line x1="14" y1="6" x2="18" y2="10"/></svg></span> Rédaction</div>
<div class="nav-item" data-view="personnages" onclick="switchView('personnages')"><span class="nav-icon"><svg viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8"/></svg></span> Personnages</div>
<div class="nav-item" data-view="ressources" onclick="switchView('ressources')"><span class="nav-icon"><svg viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg></span> Ressources</div>
<div class="sidebar-footer">Etienne & Myriam</div>
</div>
<div class="main">
<div class="topbar">
<div class="topbar-title">Vision</div>
<div class="topbar-right">
<button class="btn-new" onclick="openNew()" style="display:none;">+ Nouvelle idee</button>
<button class="dark-toggle" onclick="toggleDark()" title="Mode sombre"></button>
<div class="avatar e">E</div>
<div class="avatar m">M</div>
</div>
</div>
<!-- ══ VUE FRISE ══ -->
<div class="view" id="view-frise">
<div class="filter-bar" id="filter-bar">
<div class="filter-group">
<label>Personnage</label>
<select id="filter-perso" onchange="applyFilters()"><option value="">Tous</option></select>
</div>
<div class="filter-group">
<label>Tag</label>
<select id="filter-tag" onchange="applyFilters()"><option value="">Tous</option></select>
</div>
<div class="filter-group">
<label>Recherche</label>
<input type="text" id="filter-search" placeholder="Mot-cle..." oninput="applyFilters()">
</div>
<div class="filter-group">
<label>Tri</label>
<select id="filter-sort" onchange="applyFilters()">
<option value="ordre">Ordre (défaut)</option>
<option value="date-desc">Plus récentes</option>
<option value="date-asc">Plus anciennes</option>
<option value="alpha">Alphabétique</option>
</select>
</div>
<button class="btn-clear-filters" onclick="clearFilters()">Effacer</button>
<button class="btn-history" onclick="openArchives()" style="margin-left:auto;">Archives</button>
</div>
<div class="frise-wrapper">
<div class="timeline-area" id="timeline-area">
<div class="loading" id="loading">Chargement...</div>
</div>
<div class="waiting-zone">
<div class="waiting-section" id="section-a-revoir">
<div class="waiting-header" id="toggle-a-revoir">
<span>À revoir</span>
<span class="waiting-count" id="count-a-revoir"></span>
<span class="waiting-arrow"></span>
</div>
<div class="waiting-cards" id="cards-a-revoir" data-acte="En attente"></div>
</div>
<div class="waiting-section" id="section-a-garder">
<div class="waiting-header" id="toggle-a-garder">
<span>À garder</span>
<span class="waiting-count" id="count-a-garder"></span>
<span class="waiting-arrow"></span>
</div>
<div class="waiting-cards" id="cards-a-garder" data-acte="En attente"></div>
</div>
<div class="waiting-section" id="section-a-etudier">
<div class="waiting-header" id="toggle-a-etudier">
<span>À étudier</span>
<span class="waiting-count" id="count-a-etudier"></span>
<span class="waiting-arrow"></span>
</div>
<div class="waiting-cards" id="cards-a-etudier" data-acte="En attente"></div>
</div>
</div>
</div>
</div>
<!-- ══ VUE VISION ══ -->
<div class="view active" id="view-vision">
<div class="vision-content">
<div class="vision-grid">
<div class="hero-banner" id="hero-banner">
<div class="hero-controls">
<button class="hero-btn" onclick="document.getElementById('hero-file-input').click()">Changer l'image</button>
</div>
<div class="hero-overlay">
<div class="three-words" id="v-trois-mots" contenteditable="true" onclick="event.stopPropagation()"></div>
<div class="hero-sub" id="v-hero-sub" contenteditable="true" onclick="event.stopPropagation()"></div>
</div>
</div>
<input type="file" id="hero-file-input" accept="image/*" style="display:none;" onchange="uploadHeroImage(this)">
<div class="v-card">
<h3>L'histoire en une phrase</h3>
<div class="vision-phrase" id="v-phrase" contenteditable="true"></div>
<div class="vision-detail" id="v-phrase-detail" contenteditable="true"></div>
</div>
<div class="v-card">
<h3>Ambiance, époque, rythme</h3>
<div class="tag-group" id="v-tags"></div>
</div>
<div class="v-card full">
<h3>Moodboard</h3>
<div class="mood-grid" id="v-moodboard"></div>
<input type="file" id="mood-file-input" accept="image/*" style="display:none;">
</div>
<div class="v-card full">
<h3>Synopsis</h3>
<div class="vision-detail" id="v-synopsis" contenteditable="true" style="min-height:80px;"></div>
</div>
<div class="v-card">
<h3 class="yes">Ce qu'on veut</h3>
<div id="v-on-veut"></div>
</div>
<div class="v-card">
<h3 class="no">Ce qu'on ne veut pas</h3>
<div id="v-on-ne-veut-pas"></div>
</div>
</div>
</div>
</div>
<!-- ══ VUE RÉDACTION ══ -->
<div class="view" id="view-redaction">
<div class="redaction-filters">
<div class="filter-group">
<label>Personnage</label>
<select id="red-filter-perso" onchange="buildBinder()"><option value="">Tous</option></select>
</div>
<div class="filter-group">
<label>Tag</label>
<select id="red-filter-tag" onchange="buildBinder()"><option value="">Tous</option></select>
</div>
<div class="filter-group">
<label>Recherche</label>
<input type="text" id="red-filter-search" placeholder="Mot-clé..." oninput="buildBinder()">
</div>
</div>
<div class="redaction-layout">
<div class="binder" id="binder"></div>
<div class="editor-pane" id="editor-pane">
<div class="editor-empty" id="editor-empty">Choisir une scène dans le Binder</div>
<div id="editor-active" style="display:none; flex:1; display:none; flex-direction:column;">
<div class="editor-header">
<div style="flex:1;">
<div class="editor-scene-title" id="editor-title"></div>
<div class="editor-scene-meta" id="editor-meta"></div>
</div>
<button class="btn-history" id="btn-history-redaction" onclick="openVersionPanel('redaction', currentSceneId)" style="display:none;">Historique</button>
</div>
<div class="editor-fiche" id="editor-fiche">
<div class="editor-fiche-toggle" onclick="toggleFiche()">
<span class="editor-fiche-toggle-icon" id="fiche-toggle-icon">&#9656;</span>
<span class="editor-fiche-toggle-label">Fiche</span>
</div>
<div class="editor-fiche-content" id="editor-fiche-content"></div>
</div>
<div class="editor-toolbar" id="editor-toolbar"></div>
<div class="editor-body"><div id="editor-content"></div></div>
<div class="word-count" id="word-count"></div>
</div>
</div>
</div>
</div>
<!-- ══ VUE PERSONNAGES ══ -->
<div class="view" id="view-personnages">
<div class="page-content">
<div class="page-header">
<h2>Personnages</h2>
<button class="btn-new" onclick="openPersoModal()">+ Nouveau personnage</button>
</div>
<div class="perso-grid" id="perso-grid"></div>
</div>
</div>
<!-- ══ VUE RESSOURCES ══ -->
<div class="view" id="view-ressources">
<div class="page-content">
<div class="page-header">
<h2>Ressources</h2>
<button class="btn-new" onclick="openResModal()">+ Nouvelle ressource</button>
</div>
<div class="res-filters">
<div class="filter-group">
<label>Catégorie</label>
<select id="res-filter-cat" onchange="renderRessources()"><option value="">Toutes</option></select>
</div>
<div class="filter-group">
<label>Recherche</label>
<input type="text" id="res-filter-search" placeholder="Mot-clé..." oninput="renderRessources()">
</div>
</div>
<div class="res-grid" id="res-grid"></div>
</div>
</div>
<div class="editor-saving" id="editor-saving">Enregistré</div>
<div class="comment-popup" id="comment-popup" style="display:none;">
<div class="comment-popup-author" id="comment-popup-author"></div>
<div class="comment-popup-date" id="comment-popup-date"></div>
<div class="comment-popup-text" id="comment-popup-text"></div>
<div class="comment-popup-actions">
<button onclick="closeCommentPopup()">Fermer</button>
<button class="delete" onclick="deleteComment()">Supprimer</button>
</div>
</div>
<div class="vision-saving" id="vision-saving">Enregistré</div>
</div>
</div>
<!-- Modal -->
<div class="modal-overlay" id="modal">
<div class="modal">
<div class="modal-header">
<span style="font-family:'Playfair Display',serif;font-size:12px;text-transform:uppercase;letter-spacing:1px;color:var(--text-hint);" id="modal-label">Nouvelle idee</span>
<button class="modal-close" onclick="closeModal()">&times;</button>
</div>
<div class="modal-body">
<div class="field field-title">
<input type="text" id="f-title" placeholder="Titre de l'idee...">
</div>
<div class="field">
<label>Tags</label>
<div class="tags-input-container" id="f-tags">
<input type="text" class="tags-input" id="f-tags-input" placeholder="Ajouter un tag..." onkeydown="if(event.key==='Enter'){event.preventDefault();addTag()}" oninput="updateTagSuggestions()" onfocus="updateTagSuggestions()">
<select class="tags-existing-select" id="f-tags-select" onchange="addExistingTag()">
<option value="">Tags existants...</option>
</select>
</div>
</div>
<div class="field-row">
<div class="field">
<label>Position</label>
<select id="f-acte"></select>
</div>
<div class="field">
<label>Personnages</label>
<div class="multi-select-container" id="f-perso"></div>
</div>
</div>
<div class="field">
<label>Resume</label>
<textarea id="f-resume" placeholder="Description, resume de la scene ou de l'idee..."></textarea>
</div>
<div class="field">
<label>Interet narratif</label>
<textarea id="f-interet" placeholder="Pourquoi cette idee est importante pour l'histoire..."></textarea>
</div>
<div class="field">
<label>Implications</label>
<textarea id="f-implications" placeholder="Consequences dans l'histoire..."></textarea>
</div>
<div class="field">
<label>Origine</label>
<textarea id="f-origine" placeholder="D'ou vient cette idee..."></textarea>
</div>
<div class="field">
<label>Notes</label>
<textarea id="f-lien" placeholder="Notes, réflexions..."></textarea>
</div>
<div class="modal-comments" id="card-comments-section" style="display:none;">
<h4>Commentaires</h4>
<div class="comment-list" id="card-comment-list"></div>
<div class="comment-add">
<textarea id="card-comment-input" placeholder="Ajouter un commentaire..." rows="1" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();addCardComment()}"></textarea>
<div class="comment-add-btns">
<button class="comment-author-toggle" id="comment-author-btn" onclick="toggleCommentAuthor()">Etienne</button>
<button class="comment-send" onclick="addCardComment()">Envoyer</button>
</div>
</div>
</div>
<div class="modal-actions">
<button class="btn-delete" id="btn-delete" onclick="deleteCard()" style="display:none;">Supprimer</button>
<div class="modal-actions-spacer"></div>
<button class="btn-history" id="btn-history-frise" onclick="openVersionPanel('frise', editingId)" style="display:none;">Historique</button>
<button class="btn-history" id="btn-rediger" onclick="goToRedaction(editingId)" style="display:none;">Rédiger</button>
<button class="btn-save" onclick="saveCard()">Enregistrer</button>
</div>
</div>
</div>
</div>
<!-- Modale Personnage -->
<div class="modal-overlay" id="perso-modal">
<div class="modal">
<div class="modal-header">
<span style="font-family:'Playfair Display',serif;font-size:12px;text-transform:uppercase;letter-spacing:1px;color:var(--text-hint);" id="perso-modal-label">Nouveau personnage</span>
<button class="modal-close" onclick="closePersoModal()">&times;</button>
</div>
<div class="modal-body">
<div class="field field-title"><input type="text" id="fp-nom" placeholder="Nom du personnage..."></div>
<div class="field"><label>Rôle</label><input type="text" id="fp-role" placeholder="Héros, antagoniste, allié..." style="width:100%;padding:8px 12px;border:1px solid var(--border);border-radius:8px;font-size:14px;font-family:'Lora',serif;color:var(--text);background:var(--bg-input);"></div>
<div class="field"><label>Portrait</label><input type="file" id="fp-image-input" accept="image/*" onchange="previewPersoImage(this)" style="font-family:'Lora',serif;font-size:13px;"><div id="fp-image-preview" style="margin-top:8px;"></div></div>
<div class="field"><label>Description</label><textarea id="fp-desc" placeholder="Personnalité, arc, motivations..." rows="4"></textarea></div>
<div class="field"><label>Origines et influences</label><textarea id="fp-origines" placeholder="D'où vient le personnage, quels personnages de fiction l'ont inspiré..." rows="3"></textarea></div>
<div class="field"><label>Backstory</label><textarea id="fp-backstory" placeholder="Son passé, ce qui l'a façonné avant le début de l'histoire..." rows="4"></textarea></div>
<div class="field"><label>Tags</label><input type="text" id="fp-tags" placeholder="Tags séparés par des virgules..." style="width:100%;padding:8px 12px;border:1px solid var(--border);border-radius:8px;font-size:14px;font-family:'Lora',serif;color:var(--text);background:var(--bg-input);"></div>
<div id="fp-scenes-container"></div>
<div class="modal-actions">
<button class="btn-delete" id="btn-delete-perso" onclick="deletePerso()" style="display:none;">Supprimer</button>
<div class="modal-actions-spacer"></div>
<button class="btn-save" onclick="savePerso()">Enregistrer</button>
</div>
</div>
</div>
</div>
<!-- Modale Ressource -->
<div class="modal-overlay" id="res-modal">
<div class="modal">
<div class="modal-header">
<span style="font-family:'Playfair Display',serif;font-size:12px;text-transform:uppercase;letter-spacing:1px;color:var(--text-hint);" id="res-modal-label">Nouvelle ressource</span>
<button class="modal-close" onclick="closeResModal()">&times;</button>
</div>
<div class="modal-body">
<div class="field field-title"><input type="text" id="fr-titre" placeholder="Titre de la ressource..."></div>
<div class="field-row">
<div class="field"><label>Catégorie</label><select id="fr-cat"><option value=""></option><option>Image</option><option>Lien</option><option>Carte</option><option>Note</option><option>Référence</option></select></div>
<div class="field"><label>Tags</label><input type="text" id="fr-tags" placeholder="Tags séparés par des virgules..." style="width:100%;padding:8px 12px;border:1px solid var(--border);border-radius:8px;font-size:14px;font-family:'Lora',serif;color:var(--text);background:var(--bg-input);"></div>
</div>
<div class="field"><label>URL</label><input type="text" id="fr-url" placeholder="https://..." style="width:100%;padding:8px 12px;border:1px solid var(--border);border-radius:8px;font-size:14px;font-family:'Lora',serif;color:var(--text);background:var(--bg-input);"><div id="fr-url-link" class="res-modal-link" style="margin-top:4px;"></div></div>
<div class="field"><label>Image</label><input type="file" id="fr-image-input" accept="image/*" onchange="previewResImage(this)" style="font-family:'Lora',serif;font-size:13px;"><div id="fr-image-preview" style="margin-top:8px;"></div></div>
<div class="field"><label>Description</label><textarea id="fr-desc" placeholder="Notes, contexte..." rows="4"></textarea></div>
<div class="modal-actions">
<button class="btn-delete" id="btn-delete-res" onclick="deleteRes()" style="display:none;">Supprimer</button>
<div class="modal-actions-spacer"></div>
<button class="btn-save" onclick="saveRes()">Enregistrer</button>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
<!-- Panneau Historique des versions -->
<div class="version-panel" id="version-panel">
<div class="version-panel-header">
<h3 id="version-panel-title">Historique</h3>
<button class="modal-close" onclick="closeVersionPanel()">&times;</button>
</div>
<div class="version-panel-list" id="version-list"></div>
<div class="version-preview-pane" id="version-preview" style="display:none;">
<h4>Aperçu</h4>
<div class="version-preview-content" id="version-preview-content"></div>
</div>
<div class="version-actions" id="version-actions" style="display:none;">
<button class="btn-save" onclick="restoreVersion()">Restaurer cette version</button>
</div>
</div>
</body>
</html>