Docfile commited on
Commit
8caf859
·
verified ·
1 Parent(s): 481589f

Upload 15 files

Browse files
templates/admin_articles.html ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Gérer les Articles{% endblock %}
4
+
5
+ {% block content %}
6
+ <h2 class="mb-4">Gérer les Articles</h2>
7
+ <div class="mb-3">
8
+ <a href="{{ url_for('admin_home') }}" class="btn btn-secondary me-2">Retour à l'admin</a>
9
+ <a href="{{ url_for('admin_new_article') }}" class="btn btn-primary">Nouvel Article</a>
10
+ </div>
11
+ <div class="table-responsive">
12
+ <table class="table table-striped table-hover">
13
+ <thead class="table-dark">
14
+ <tr>
15
+ <th>ID</th>
16
+ <th>Titre</th>
17
+ <th>Catégorie</th>
18
+ <th>Actions</th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ {% for article in articles %}
23
+ <tr>
24
+ <td>{{ article.id }}</td>
25
+ <td>{{ article.title }}</td>
26
+ <td>{{ article.category.name }}</td>
27
+ <td>
28
+ <a href="{{ url_for('admin_edit_article', article_id=article.id) }}" class="btn btn-sm btn-outline-primary me-2">Éditer</a>
29
+ <form method="post" action="{{ url_for('admin_delete_article', article_id=article.id) }}" style="display:inline;">
30
+ <button type="submit" class="btn btn-sm btn-outline-danger" onclick="return confirm('Êtes-vous sûr de vouloir supprimer cet article ?')">Supprimer</button>
31
+ </form>
32
+ </td>
33
+ </tr>
34
+ {% endfor %}
35
+ </tbody>
36
+ </table>
37
+ </div>
38
+ {% endblock %}
templates/admin_categories.html ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Gérer les Catégories</title>
6
+ <style>
7
+ body { font-family: Arial, sans-serif; margin: 20px; }
8
+ table { width: 100%; border-collapse: collapse; }
9
+ th, td { border: 1px solid #ccc; padding: 10px; text-align: left; }
10
+ .actions a { margin-right: 10px; }
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <h1>Gérer les Catégories</h1>
15
+ <a href="{{ url_for('admin_home') }}">Retour à l'admin</a> | <a href="{{ url_for('admin_new_category') }}">Nouvelle Catégorie</a>
16
+ <table>
17
+ <thead>
18
+ <tr>
19
+ <th>ID</th>
20
+ <th>Nom</th>
21
+ <th>Sujet</th>
22
+ <th>Actions</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ {% for category in categories %}
27
+ <tr>
28
+ <td>{{ category.id }}</td>
29
+ <td>{{ category.name }}</td>
30
+ <td>{{ category.subject.name }}</td>
31
+ <td class="actions">
32
+ <a href="{{ url_for('admin_edit_category', category_id=category.id) }}">Éditer</a>
33
+ <form method="post" action="{{ url_for('admin_delete_category', category_id=category.id) }}" style="display:inline;">
34
+ <button type="submit" onclick="return confirm('Êtes-vous sûr de vouloir supprimer cette catégorie ?')">Supprimer</button>
35
+ </form>
36
+ </td>
37
+ </tr>
38
+ {% endfor %}
39
+ </tbody>
40
+ </table>
41
+ </body>
42
+ </html>
templates/admin_edit_article.html ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Éditer l'Article</title>
6
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.8/dist/trix.css">
7
+ <script type="text/javascript" src="https://unpkg.com/trix@2.0.8/dist/trix.umd.min.js"></script>
8
+ <style>
9
+ body { font-family: Arial, sans-serif; margin: 20px; }
10
+ form { max-width: 800px; }
11
+ label { display: block; margin-top: 10px; }
12
+ input, button { width: 100%; padding: 10px; margin-top: 5px; }
13
+ trix-editor { border: 1px solid #ccc; min-height: 300px; }
14
+ </style>
15
+ <script>
16
+ document.addEventListener("trix-before-initialize", () => {
17
+ // Allow more tags in sanitization
18
+ Trix.config.dompurify.ADD_TAGS = ["iframe", "video", "source", "table", "tr", "td", "th"];
19
+ Trix.config.dompurify.ADD_ATTR = ["src", "controls", "width", "height"];
20
+
21
+ // Custom toolbar with all options
22
+ Trix.config.toolbar.getDefaultHTML = () => `
23
+ <div class="trix-button-row">
24
+ <span class="trix-button-group trix-button-group--text-tools" data-trix-button-group="text-tools">
25
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-bold" data-trix-attribute="bold" data-trix-key="b" tabindex="-1" title="Bold">Bold</button>
26
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-italic" data-trix-attribute="italic" data-trix-key="i" tabindex="-1" title="Italic">Italic</button>
27
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-strike" data-trix-attribute="strike" tabindex="-1" title="Strikethrough">Strikethrough</button>
28
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-link" data-trix-attribute="href" data-trix-action="link" data-trix-key="k" tabindex="-1" title="Link">Link</button>
29
+ </span>
30
+ <span class="trix-button-group trix-button-group--block-tools" data-trix-button-group="block-tools">
31
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-heading-1" data-trix-attribute="heading1" tabindex="-1" title="Heading">Heading</button>
32
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-quote" data-trix-attribute="quote" tabindex="-1" title="Quote">Quote</button>
33
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-code" data-trix-attribute="code" tabindex="-1" title="Code">Code</button>
34
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-bullet-list" data-trix-attribute="bullet" tabindex="-1" title="Bullets">Bullets</button>
35
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-number-list" data-trix-attribute="number" tabindex="-1" title="Numbers">Numbers</button>
36
+ </span>
37
+ <span class="trix-button-group trix-button-group--file-tools" data-trix-button-group="file-tools">
38
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-attach" data-trix-action="attachFiles" tabindex="-1" title="Attach Files">Attach Files</button>
39
+ </span>
40
+ <span class="trix-button-group trix-button-group--image-tools" data-trix-button-group="image-tools">
41
+ <button type="button" class="trix-button" data-trix-action="x-align-left" tabindex="-1" title="Align Left">Align Left</button>
42
+ <button type="button" class="trix-button" data-trix-action="x-align-right" tabindex="-1" title="Align Right">Align Right</button>
43
+ </span>
44
+ <span class="trix-button-group trix-button-group--history-tools" data-trix-button-group="history-tools">
45
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-undo" data-trix-action="undo" data-trix-key="z" tabindex="-1" title="Undo">Undo</button>
46
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-redo" data-trix-action="redo" data-trix-key="shift+z" tabindex="-1" title="Redo">Redo</button>
47
+ </span>
48
+ </div>
49
+ <div class="trix-dialogs" data-trix-dialogs>
50
+ <div class="trix-dialog trix-dialog--link" data-trix-dialog="href" data-trix-dialog-attribute="href">
51
+ <div class="trix-dialog__link-fields">
52
+ <input type="url" name="href" class="trix-input trix-input--dialog" placeholder="Enter a URL..." aria-label="URL" required data-trix-input>
53
+ <div class="trix-button-group">
54
+ <input type="button" class="trix-button trix-button--dialog" value="Link" data-trix-method="setAttribute">
55
+ <input type="button" class="trix-button trix-button--dialog" value="Unlink" data-trix-method="removeAttribute">
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ `;
61
+ });
62
+ </script>
63
+ </head>
64
+ <body>
65
+ <h1>Éditer l'Article</h1>
66
+ <a href="{{ url_for('admin_articles') }}">Retour aux articles</a>
67
+ <form method="post" enctype="multipart/form-data">
68
+ <label for="title">Titre:</label>
69
+ <input type="text" id="title" name="title" value="{{ article.title }}" required>
70
+
71
+ {% if article.icon_data %}
72
+ <p>Image actuelle: <img src="{{ url_for('get_article_image', article_id=article.id) }}" style="max-width: 100px; max-height: 100px;"></p>
73
+ {% endif %}
74
+
75
+ <label for="icon">Nouvelle image (fichier image):</label>
76
+ <input type="file" id="icon" name="icon" accept="image/*">
77
+
78
+ <label for="content">Contenu:</label>
79
+ <input id="content" type="hidden" name="content" value="{{ article.content }}">
80
+ <trix-editor input="content"></trix-editor>
81
+
82
+ <button type="submit">Sauvegarder</button>
83
+ </form>
84
+ <script>
85
+ document.addEventListener("trix-attachment-add", function(event) {
86
+ var file = event.attachment.file;
87
+ if (file) {
88
+ var formData = new FormData();
89
+ formData.append("file", file);
90
+ fetch("/upload", {
91
+ method: "POST",
92
+ body: formData
93
+ }).then(response => response.json()).then(data => {
94
+ event.attachment.setAttributes({url: data.url});
95
+ }).catch(error => {
96
+ console.error("Upload failed:", error);
97
+ });
98
+ }
99
+ });
100
+
101
+ document.addEventListener("trix-action-invoke", function(event) {
102
+ const { actionName, target } = event;
103
+ if (actionName === "x-align-left" || actionName === "x-align-right") {
104
+ const editor = target.editor;
105
+ const selectedRange = editor.getSelectedRange();
106
+ const document = editor.getDocument();
107
+ const attachments = document.getAttachments();
108
+ attachments.forEach(attachment => {
109
+ const range = attachment.getRange();
110
+ if (selectedRange[0] <= range[0] && range[1] <= selectedRange[1]) {
111
+ attachment.setAttribute("class", actionName === "x-align-left" ? "float-left" : "float-right");
112
+ }
113
+ });
114
+ }
115
+ });
116
+ </script>
117
+ </body>
118
+ </html>
templates/admin_edit_category.html ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Éditer la Catégorie</title>
6
+ <style>
7
+ body { font-family: Arial, sans-serif; margin: 20px; }
8
+ form { max-width: 800px; }
9
+ label { display: block; margin-top: 10px; }
10
+ input, select, button { width: 100%; padding: 10px; margin-top: 5px; }
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <h1>Éditer la Catégorie</h1>
15
+ <a href="{{ url_for('admin_categories') }}">Retour aux catégories</a>
16
+ <form method="post">
17
+ <label for="name">Nom:</label>
18
+ <input type="text" id="name" name="name" value="{{ category.name }}" required>
19
+
20
+ <label for="subject_id">Sujet:</label>
21
+ <select id="subject_id" name="subject_id" required>
22
+ {% for subject in subjects %}
23
+ <option value="{{ subject.id }}" {% if subject.id == category.subject_id %}selected{% endif %}>{{ subject.name }}</option>
24
+ {% endfor %}
25
+ </select>
26
+
27
+ <button type="submit">Sauvegarder</button>
28
+ </form>
29
+ </body>
30
+ </html>
templates/admin_edit_subject.html ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Éditer le Sujet</title>
6
+ <style>
7
+ body { font-family: Arial, sans-serif; margin: 20px; }
8
+ form { max-width: 800px; }
9
+ label { display: block; margin-top: 10px; }
10
+ input, button { width: 100%; padding: 10px; margin-top: 5px; }
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <h1>Éditer le Sujet</h1>
15
+ <a href="{{ url_for('admin_subjects') }}">Retour aux sujets</a>
16
+ <form method="post" enctype="multipart/form-data">
17
+ <label for="name">Nom:</label>
18
+ <input type="text" id="name" name="name" value="{{ subject.name }}" required>
19
+
20
+ {% if subject.icon_data %}
21
+ <p>Icône actuelle: <img src="{{ url_for('get_subject_icon', subject_id=subject.id) }}" style="max-width: 50px; max-height: 50px;"></p>
22
+ {% endif %}
23
+
24
+ <label for="icon">Nouvelle icône (fichier image):</label>
25
+ <input type="file" id="icon" name="icon" accept="image/*">
26
+
27
+ <button type="submit">Sauvegarder</button>
28
+ </form>
29
+ </body>
30
+ </html>
templates/admin_home.html ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Administration{% endblock %}
4
+
5
+ {% block content %}
6
+ <h2 class="mb-4">Zone d'Administration</h2>
7
+ <div class="admin-nav">
8
+ <a href="{{ url_for('admin_subjects') }}">Gérer les Sujets</a>
9
+ <a href="{{ url_for('admin_categories') }}">Gérer les Catégories</a>
10
+ <a href="{{ url_for('admin_articles') }}">Gérer les Articles</a>
11
+ </div>
12
+ <p class="text-muted">Bienvenue dans la zone d'administration. Utilisez les liens ci-dessus pour gérer le contenu.</p>
13
+ {% endblock %}
templates/admin_new_article.html ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Nouvel Article</title>
6
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.8/dist/trix.css">
7
+ <script type="text/javascript" src="https://unpkg.com/trix@2.0.8/dist/trix.umd.min.js"></script>
8
+ <style>
9
+ body { font-family: Arial, sans-serif; margin: 20px; }
10
+ form { max-width: 800px; }
11
+ label { display: block; margin-top: 10px; }
12
+ input, select, button { width: 100%; padding: 10px; margin-top: 5px; }
13
+ trix-editor { border: 1px solid #ccc; min-height: 300px; }
14
+ </style>
15
+ <script>
16
+ document.addEventListener("trix-before-initialize", () => {
17
+ // Allow more tags in sanitization
18
+ Trix.config.dompurify.ADD_TAGS = ["iframe", "video", "source", "table", "tr", "td", "th"];
19
+ Trix.config.dompurify.ADD_ATTR = ["src", "controls", "width", "height"];
20
+
21
+ // Custom toolbar with all options
22
+ Trix.config.toolbar.getDefaultHTML = () => `
23
+ <div class="trix-button-row">
24
+ <span class="trix-button-group trix-button-group--text-tools" data-trix-button-group="text-tools">
25
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-bold" data-trix-attribute="bold" data-trix-key="b" tabindex="-1" title="Bold">Bold</button>
26
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-italic" data-trix-attribute="italic" data-trix-key="i" tabindex="-1" title="Italic">Italic</button>
27
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-strike" data-trix-attribute="strike" tabindex="-1" title="Strikethrough">Strikethrough</button>
28
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-link" data-trix-attribute="href" data-trix-action="link" data-trix-key="k" tabindex="-1" title="Link">Link</button>
29
+ </span>
30
+ <span class="trix-button-group trix-button-group--block-tools" data-trix-button-group="block-tools">
31
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-heading-1" data-trix-attribute="heading1" tabindex="-1" title="Heading">Heading</button>
32
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-quote" data-trix-attribute="quote" tabindex="-1" title="Quote">Quote</button>
33
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-code" data-trix-attribute="code" tabindex="-1" title="Code">Code</button>
34
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-bullet-list" data-trix-attribute="bullet" tabindex="-1" title="Bullets">Bullets</button>
35
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-number-list" data-trix-attribute="number" tabindex="-1" title="Numbers">Numbers</button>
36
+ </span>
37
+ <span class="trix-button-group trix-button-group--file-tools" data-trix-button-group="file-tools">
38
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-attach" data-trix-action="attachFiles" tabindex="-1" title="Attach Files">Attach Files</button>
39
+ </span>
40
+ <span class="trix-button-group trix-button-group--image-tools" data-trix-button-group="image-tools">
41
+ <button type="button" class="trix-button" data-trix-action="x-align-left" tabindex="-1" title="Align Left">Align Left</button>
42
+ <button type="button" class="trix-button" data-trix-action="x-align-right" tabindex="-1" title="Align Right">Align Right</button>
43
+ </span>
44
+ <span class="trix-button-group trix-button-group--history-tools" data-trix-button-group="history-tools">
45
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-undo" data-trix-action="undo" data-trix-key="z" tabindex="-1" title="Undo">Undo</button>
46
+ <button type="button" class="trix-button trix-button--icon trix-button--icon-redo" data-trix-action="redo" data-trix-key="shift+z" tabindex="-1" title="Redo">Redo</button>
47
+ </span>
48
+ </div>
49
+ <div class="trix-dialogs" data-trix-dialogs>
50
+ <div class="trix-dialog trix-dialog--link" data-trix-dialog="href" data-trix-dialog-attribute="href">
51
+ <div class="trix-dialog__link-fields">
52
+ <input type="url" name="href" class="trix-input trix-input--dialog" placeholder="Enter a URL..." aria-label="URL" required data-trix-input>
53
+ <div class="trix-button_group">
54
+ <input type="button" class="trix-button trix-button--dialog" value="Link" data-trix-method="setAttribute">
55
+ <input type="button" class="trix-button trix-button--dialog" value="Unlink" data-trix-method="removeAttribute">
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ `;
61
+ });
62
+ </script>
63
+ </head>
64
+ <body>
65
+ <h1>Nouvel Article</h1>
66
+ <a href="{{ url_for('admin_articles') }}">Retour aux articles</a>
67
+ <form method="post" enctype="multipart/form-data">
68
+ <label for="title">Titre:</label>
69
+ <input type="text" id="title" name="title" required>
70
+
71
+ <label for="category_id">Catégorie:</label>
72
+ <select id="category_id" name="category_id" required>
73
+ {% for category in categories %}
74
+ <option value="{{ category.id }}">{{ category.name }}</option>
75
+ {% endfor %}
76
+ </select>
77
+
78
+ <label for="icon">Image (fichier image):</label>
79
+ <input type="file" id="icon" name="icon" accept="image/*">
80
+
81
+ <label for="content">Contenu:</label>
82
+ <input id="content" type="hidden" name="content">
83
+ <trix-editor input="content"></trix-editor>
84
+
85
+ <button type="submit">Créer</button>
86
+ </form>
87
+ <script>
88
+ document.addEventListener("trix-attachment-add", function(event) {
89
+ var file = event.attachment.file;
90
+ if (file) {
91
+ var formData = new FormData();
92
+ formData.append("file", file);
93
+ fetch("/upload", {
94
+ method: "POST",
95
+ body: formData
96
+ }).then(response => response.json()).then(data => {
97
+ event.attachment.setAttributes({url: data.url});
98
+ }).catch(error => {
99
+ console.error("Upload failed:", error);
100
+ });
101
+ }
102
+ });
103
+
104
+ document.addEventListener("trix-action-invoke", function(event) {
105
+ const { actionName, target } = event;
106
+ if (actionName === "x-align-left" || actionName === "x-align-right") {
107
+ const editor = target.editor;
108
+ const selectedRange = editor.getSelectedRange();
109
+ const document = editor.getDocument();
110
+ const attachments = document.getAttachments();
111
+ attachments.forEach(attachment => {
112
+ const range = attachment.getRange();
113
+ if (selectedRange[0] <= range[0] && range[1] <= selectedRange[1]) {
114
+ attachment.setAttribute("class", actionName === "x-align-left" ? "float-left" : "float-right");
115
+ }
116
+ });
117
+ }
118
+ });
119
+ </script>
120
+ </body>
121
+ </html>
templates/admin_new_category.html ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Nouvelle Catégorie</title>
6
+ <style>
7
+ body { font-family: Arial, sans-serif; margin: 20px; }
8
+ form { max-width: 800px; }
9
+ label { display: block; margin-top: 10px; }
10
+ input, select, button { width: 100%; padding: 10px; margin-top: 5px; }
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <h1>Nouvelle Catégorie</h1>
15
+ <a href="{{ url_for('admin_categories') }}">Retour aux catégories</a>
16
+ <form method="post">
17
+ <label for="name">Nom:</label>
18
+ <input type="text" id="name" name="name" required>
19
+
20
+ <label for="subject_id">Sujet:</label>
21
+ <select id="subject_id" name="subject_id" required>
22
+ {% for subject in subjects %}
23
+ <option value="{{ subject.id }}">{{ subject.name }}</option>
24
+ {% endfor %}
25
+ </select>
26
+
27
+ <button type="submit">Créer</button>
28
+ </form>
29
+ </body>
30
+ </html>
templates/admin_new_subject.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Nouveau Sujet</title>
6
+ <style>
7
+ body { font-family: Arial, sans-serif; margin: 20px; }
8
+ form { max-width: 800px; }
9
+ label { display: block; margin-top: 10px; }
10
+ input, button { width: 100%; padding: 10px; margin-top: 5px; }
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <h1>Nouveau Sujet</h1>
15
+ <a href="{{ url_for('admin_subjects') }}">Retour aux sujets</a>
16
+ <form method="post" enctype="multipart/form-data">
17
+ <label for="name">Nom:</label>
18
+ <input type="text" id="name" name="name" required>
19
+
20
+ <label for="icon">Icône (fichier image):</label>
21
+ <input type="file" id="icon" name="icon" accept="image/*">
22
+
23
+ <button type="submit">Créer</button>
24
+ </form>
25
+ </body>
26
+ </html>
templates/admin_subjects.html ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Gérer les Sujets</title>
6
+ <style>
7
+ body { font-family: Arial, sans-serif; margin: 20px; }
8
+ table { width: 100%; border-collapse: collapse; }
9
+ th, td { border: 1px solid #ccc; padding: 10px; text-align: left; }
10
+ .actions a { margin-right: 10px; }
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <h1>Gérer les Sujets</h1>
15
+ <a href="{{ url_for('admin_home') }}">Retour à l'admin</a> | <a href="{{ url_for('admin_new_subject') }}">Nouveau Sujet</a>
16
+ <table>
17
+ <thead>
18
+ <tr>
19
+ <th>ID</th>
20
+ <th>Nom</th>
21
+ <th>Icône</th>
22
+ <th>Actions</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ {% for subject in subjects %}
27
+ <tr>
28
+ <td>{{ subject.id }}</td>
29
+ <td>{{ subject.name }}</td>
30
+ <td>
31
+ {% if subject.icon_data %}
32
+ <img src="{{ url_for('get_subject_icon', subject_id=subject.id) }}" style="max-width: 50px; max-height: 50px;">
33
+ {% else %}
34
+ Aucune icône
35
+ {% endif %}
36
+ </td>
37
+ <td class="actions">
38
+ <a href="{{ url_for('admin_edit_subject', subject_id=subject.id) }}">Éditer</a>
39
+ <form method="post" action="{{ url_for('admin_delete_subject', subject_id=subject.id) }}" style="display:inline;">
40
+ <button type="submit" onclick="return confirm('Êtes-vous sûr de vouloir supprimer ce sujet ?')">Supprimer</button>
41
+ </form>
42
+ </td>
43
+ </tr>
44
+ {% endfor %}
45
+ </tbody>
46
+ </table>
47
+ </body>
48
+ </html>
templates/article.html ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ article.title }}{% endblock %}
4
+
5
+ {% block extra_head %}
6
+ <link rel="stylesheet" href="https://unpkg.com/trix@2.0.8/dist/trix.css">
7
+ {% endblock %}
8
+
9
+ {% block content %}
10
+ <nav aria-label="breadcrumb">
11
+ <ol class="breadcrumb">
12
+ <li class="breadcrumb-item"><a href="{{ url_for('home') }}">Matières</a></li>
13
+ <li class="breadcrumb-item"><a href="{{ url_for('categories', subject_id=article.category.subject_id) }}">{{ article.category.subject.name }}</a></li>
14
+ <li class="breadcrumb-item"><a href="{{ url_for('articles', category_id=article.category_id) }}">{{ article.category.name }}</a></li>
15
+ <li class="breadcrumb-item active" aria-current="page">{{ article.title }}</li>
16
+ </ol>
17
+ </nav>
18
+
19
+ <div class="article-content">
20
+ <h1>{{ article.title }}</h1>
21
+ <div class="trix-content">
22
+ {{ article.content | safe }}
23
+ </div>
24
+ </div>
25
+ {% endblock %}
templates/articles.html ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Articles - {{ category.name }}{% endblock %}
4
+
5
+ {% block content %}
6
+ <nav aria-label="breadcrumb">
7
+ <ol class="breadcrumb">
8
+ <li class="breadcrumb-item"><a href="{{ url_for('home') }}">Matières</a></li>
9
+ <li class="breadcrumb-item"><a href="{{ url_for('categories', subject_id=category.subject_id) }}">{{ category.subject.name }}</a></li>
10
+ <li class="breadcrumb-item active" aria-current="page">{{ category.name }}</li>
11
+ </ol>
12
+ </nav>
13
+
14
+ <h2 class="mb-4">Articles pour {{ category.name }}</h2>
15
+ <div class="row">
16
+ {% for article in articles %}
17
+ <div class="col-12 mb-3">
18
+ <div class="card" onclick="window.location.href='/articles/{{ article.id }}'" style="cursor: pointer;">
19
+ <div class="card-body">
20
+ <h5 class="card-title">{{ article.title }}</h5>
21
+ <p class="card-text text-muted">Cliquez pour lire l'article</p>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ {% endfor %}
26
+ </div>
27
+ {% endblock %}
templates/base.html ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}Mariam AI{% endblock %}</title>
7
+ <!-- Bootstrap CSS -->
8
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
9
+ <!-- Custom CSS -->
10
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
11
+ {% block extra_head %}{% endblock %}
12
+ </head>
13
+ <body>
14
+ <header class="header">
15
+ <div class="container">
16
+ <h1>Mariam AI</h1>
17
+ </div>
18
+ </header>
19
+
20
+ <nav class="navbar">
21
+ <div class="container">
22
+ <ul class="nav">
23
+ <li class="nav-item">
24
+ <a class="nav-link" href="{{ url_for('home') }}">Accueil</a>
25
+ </li>
26
+ {% if request.endpoint and 'admin' in request.endpoint %}
27
+ <li class="nav-item">
28
+ <a class="nav-link" href="{{ url_for('admin_home') }}">Administration</a>
29
+ </li>
30
+ {% endif %}
31
+ </ul>
32
+ </div>
33
+ </nav>
34
+
35
+ <main class="container my-4">
36
+ {% block content %}{% endblock %}
37
+ </main>
38
+
39
+
40
+
41
+ <!-- Bootstrap JS -->
42
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
43
+ {% block extra_scripts %}{% endblock %}
44
+ </body>
45
+ </html>
templates/categories.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Catégories - {{ subject.name }}{% endblock %}
4
+
5
+ {% block content %}
6
+ <nav aria-label="breadcrumb">
7
+ <ol class="breadcrumb">
8
+ <li class="breadcrumb-item"><a href="{{ url_for('home') }}">Matières</a></li>
9
+ <li class="breadcrumb-item active" aria-current="page">{{ subject.name }}</li>
10
+ </ol>
11
+ </nav>
12
+
13
+ <h2 class="mb-4">Catégories pour {{ subject.name }}</h2>
14
+ <div class="row">
15
+ {% for category in categories %}
16
+ <div class="col-12 col-md-6 mb-3">
17
+ <div class="card" onclick="window.location.href='/categories/{{ category.id }}/articles'" style="cursor: pointer;">
18
+ <div class="card-body">
19
+ <h5 class="card-title">{{ category.name }}</h5>
20
+ <p class="card-text text-muted">Cliquez pour voir les articles</p>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ {% endfor %}
25
+ </div>
26
+ {% endblock %}
templates/home.html ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Matières - {{ super() }}{% endblock %}
4
+
5
+ {% block content %}
6
+ <h2 class="mb-4">Choisissez une matière</h2>
7
+ <div class="row">
8
+ {% for subject in subjects %}
9
+ <div class="col-12 col-sm-6 col-md-4 col-lg-3 mb-3">
10
+ <div class="card h-100" onclick="window.location.href='/subjects/{{ subject.id }}/categories'" style="cursor: pointer;">
11
+ <div class="card-body text-center">
12
+ {% if subject.icon_url %}
13
+ <img src="{{ subject.icon_url }}" alt="{{ subject.name }}" class="mb-3" style="width: 60px; height: 60px; object-fit: cover; border-radius: 50%;">
14
+ {% endif %}
15
+ <h5 class="card-title">{{ subject.name }}</h5>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ {% endfor %}
20
+ </div>
21
+ {% endblock %}