First commit
This commit is contained in:
27
app/Views/admin/dashboard.html
Normal file
27
app/Views/admin/dashboard.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<section class="stack-lg" aria-labelledby="dashboard-title">
|
||||
<header class="page-header">
|
||||
<h1 class="page-title" id="dashboard-title">Tableau de bord</h1>
|
||||
|
||||
<div class="page-actions">
|
||||
<a class="button" href="{{ @BASE }}/dashboard/posts/create">Nouvel article</a>
|
||||
<a class="button button--ghost" href="{{ @BASE }}/dashboard/media">Médiathèque</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<check if="{{ @posts }}">
|
||||
<true>
|
||||
<div class="card-grid">
|
||||
<repeat group="{{ @posts }}" value="{{ @post }}">
|
||||
<include href="partials/post_card_admin.html" />
|
||||
</repeat>
|
||||
</div>
|
||||
<include href="partials/pagination.html" />
|
||||
</true>
|
||||
<false>
|
||||
<section class="empty-state" aria-labelledby="dashboard-empty-title">
|
||||
<h2 class="card-title" id="dashboard-empty-title">Aucun article</h2>
|
||||
<p>Commence par créer un premier article.</p>
|
||||
</section>
|
||||
</false>
|
||||
</check>
|
||||
</section>
|
||||
35
app/Views/admin/media.html
Normal file
35
app/Views/admin/media.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<section class="stack-lg" aria-labelledby="media-title">
|
||||
<header class="page-header">
|
||||
<h1 class="page-title" id="media-title">Médiathèque</h1>
|
||||
|
||||
<div class="page-actions">
|
||||
<a class="button button--ghost" href="{{ @BASE }}/dashboard">Retour</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<form class="panel stack" method="post" action="{{ @BASE }}/dashboard/media" enctype="multipart/form-data">
|
||||
<input type="hidden" name="csrf_token" value="{{ @csrfToken }}">
|
||||
<label class="field">
|
||||
<span class="field-label">Nouvelle image</span>
|
||||
<input class="control" type="file" name="image" accept="image/jpeg,image/png,image/webp" required>
|
||||
<span class="field-help">Formats acceptés : JPG, PNG, WebP.</span>
|
||||
</label>
|
||||
<button class="button" type="submit">Envoyer</button>
|
||||
</form>
|
||||
|
||||
<check if="{{ @items }}">
|
||||
<true>
|
||||
<div class="card-grid">
|
||||
<repeat group="{{ @items }}" value="{{ @item }}">
|
||||
<include href="partials/media_card.html" />
|
||||
</repeat>
|
||||
</div>
|
||||
</true>
|
||||
<false>
|
||||
<section class="empty-state" aria-labelledby="media-empty-title">
|
||||
<h2 class="card-title" id="media-empty-title">Aucune image</h2>
|
||||
<p>Ajoute ta première image.</p>
|
||||
</section>
|
||||
</false>
|
||||
</check>
|
||||
</section>
|
||||
107
app/Views/admin/post_form.html
Normal file
107
app/Views/admin/post_form.html
Normal file
@@ -0,0 +1,107 @@
|
||||
<section class="stack-lg" aria-labelledby="post-form-title">
|
||||
<header class="page-header">
|
||||
<h1 class="page-title" id="post-form-title">{{ @pageTitle }}</h1>
|
||||
|
||||
<div class="page-actions">
|
||||
<a class="button button--ghost" href="{{ @BASE }}/dashboard">Retour</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="editor-layout" data-editor-layout>
|
||||
<form class="panel stack editor-form" method="post" action="{{ @formAction }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ @csrfToken }}">
|
||||
<input type="hidden" name="cover_media_id" value="{{ @post.cover_media_id }}" data-cover-input>
|
||||
|
||||
<label class="field">
|
||||
<span class="field-label">Titre</span>
|
||||
<input class="control" type="text" name="title" value="{{ @post.title }}" maxlength="{{ @titleMax }}" required data-char-count>
|
||||
<span class="char-counter"><span data-char-count-value>0</span> / {{ @titleMax }}</span>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span class="field-label">Extrait</span>
|
||||
<textarea class="control" name="excerpt" rows="3" maxlength="{{ @excerptMax }}" required data-char-count>{{ @post.excerpt }}</textarea>
|
||||
<span class="char-counter"><span data-char-count-value>0</span> / {{ @excerptMax }}</span>
|
||||
</label>
|
||||
|
||||
<section class="field cover-field">
|
||||
<div class="field-head">
|
||||
<div>
|
||||
<h2 class="field-label">Image de couverture</h2>
|
||||
<p class="field-help">Choisis une image si tu veux une couverture.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cover-picker">
|
||||
<check if="{{ @coverPreview }}">
|
||||
<true>
|
||||
<img class="media-frame media-frame--large cover-preview" data-cover-preview src="{{ @coverPreview.url }}" alt="">
|
||||
<div class="media-frame media-frame--large media-frame--placeholder is-hidden" data-cover-placeholder>Aucune image</div>
|
||||
</true>
|
||||
<false>
|
||||
<div class="media-frame media-frame--large media-frame--placeholder" data-cover-placeholder>Aucune image</div>
|
||||
<img class="media-frame media-frame--large cover-preview is-hidden" data-cover-preview alt="Aperçu couverture">
|
||||
</false>
|
||||
</check>
|
||||
|
||||
<div class="button-row">
|
||||
<button class="button button--ghost" type="button" data-media-picker-open="cover">Choisir une image</button>
|
||||
<button class="button button--ghost" type="button" data-cover-clear {{ @post.cover_media_id ? '' : 'disabled' }}>Retirer</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="field">
|
||||
<div class="field-head">
|
||||
<div>
|
||||
<h2 class="field-label">Contenu</h2>
|
||||
<p class="field-help">Markdown simple, avec insertion d’image au curseur.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar" role="toolbar" aria-label="Outils Markdown">
|
||||
<button class="tool-button" type="button" data-md-action="bold"><strong>Gras</strong></button>
|
||||
<button class="tool-button" type="button" data-md-action="italic"><em>Italique</em></button>
|
||||
<button class="tool-button" type="button" data-md-action="heading">Titre</button>
|
||||
<button class="tool-button" type="button" data-md-action="list">Liste</button>
|
||||
<button class="tool-button" type="button" data-md-action="quote">Citation</button>
|
||||
<button class="tool-button" type="button" data-md-action="link">Lien</button>
|
||||
<button class="tool-button" type="button" data-md-action="code">Code</button>
|
||||
<button class="tool-button" type="button" data-media-picker-open="markdown">Image</button>
|
||||
</div>
|
||||
|
||||
<textarea class="control editor-textarea" name="body_markdown" rows="18" required data-markdown-editor>{{ @post.body_markdown }}</textarea>
|
||||
</section>
|
||||
|
||||
<button class="button" type="submit">Enregistrer</button>
|
||||
</form>
|
||||
|
||||
<aside class="media-picker is-hidden" data-media-picker>
|
||||
<div class="media-picker__head">
|
||||
<div>
|
||||
<strong data-media-picker-title>Choisir une image</strong>
|
||||
<p class="field-help" data-media-picker-help>Choisis une image de la médiathèque.</p>
|
||||
</div>
|
||||
<button class="button button--ghost button--small" type="button" data-media-picker-close>Fermer</button>
|
||||
</div>
|
||||
|
||||
<check if="{{ @mediaItems }}">
|
||||
<true>
|
||||
<div class="media-picker__grid">
|
||||
<repeat group="{{ @mediaItems }}" value="{{ @item }}">
|
||||
<button class="media-picker__item" type="button" data-media-picker-select data-media-id="{{ @item.id }}" data-media-url="{{ @item.url }}" data-media-markdown="{{ @item.markdown }}">
|
||||
<img class="media-frame media-frame--square" src="{{ @item.url }}" alt="">
|
||||
</button>
|
||||
</repeat>
|
||||
</div>
|
||||
</true>
|
||||
<false>
|
||||
<section class="empty-state" aria-labelledby="media-picker-empty-title">
|
||||
<h2 class="card-title" id="media-picker-empty-title">Aucune image disponible</h2>
|
||||
<p>Ajoute une image depuis la médiathèque.</p>
|
||||
</section>
|
||||
</false>
|
||||
</check>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
21
app/Views/auth/login.html
Normal file
21
app/Views/auth/login.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<section class="auth-shell panel stack" aria-labelledby="login-title">
|
||||
<header class="page-header page-header--compact">
|
||||
<h1 class="page-title" id="login-title">Connexion</h1>
|
||||
</header>
|
||||
|
||||
<form class="stack" method="post" action="{{ @BASE }}/login">
|
||||
<input type="hidden" name="csrf_token" value="{{ @csrfToken }}">
|
||||
|
||||
<label class="field">
|
||||
<span class="field-label">Nom d’utilisateur</span>
|
||||
<input class="control" type="text" name="username" autocomplete="username" required>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span class="field-label">Mot de passe</span>
|
||||
<input class="control" type="password" name="password" autocomplete="current-password" required>
|
||||
</label>
|
||||
|
||||
<button class="button" type="submit">Se connecter</button>
|
||||
</form>
|
||||
</section>
|
||||
23
app/Views/errors/error.html
Normal file
23
app/Views/errors/error.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ @errorTitle ?: 'Erreur' }}</title>
|
||||
<link rel="icon" href="{{ @BASE }}/assets/favicon.svg" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="{{ @BASE }}/min/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="page error-page">
|
||||
<div class="container">
|
||||
<section class="error-card">
|
||||
<p class="error-page__code">Erreur {{ @errorCode ?: 500 }}</p>
|
||||
<h1 class="error-page__title">{{ @errorTitle ?: 'Erreur' }}</h1>
|
||||
<p class="error-page__message">{{ @errorMessage ?: 'Une erreur est survenue.' }}</p>
|
||||
<p class="error-page__hint">Vérifie l’adresse ou reviens à l’accueil.</p>
|
||||
<p class="error-page__actions"><a class="button" href="{{ @BASE }}/">Retour à l’accueil</a></p>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
24
app/Views/layout.html
Normal file
24
app/Views/layout.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ @pageTitle ? @pageTitle . ' · ' . @app.name : @app.name }}</title>
|
||||
<meta name="description" content="{{ @app.tagline }}">
|
||||
<link rel="icon" href="{{ @BASE }}/assets/favicon.svg" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="{{ @BASE }}/min/app.css">
|
||||
<script defer src="{{ @BASE }}/min/app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<include href="partials/site_navigation.html" />
|
||||
|
||||
<main class="page" id="main-content">
|
||||
<div class="container">
|
||||
<check if="{{ @flash }}">
|
||||
<div class="flash flash--{{ @flash.type }}" role="status">{{ @flash.message }}</div>
|
||||
</check>
|
||||
<include href="{{ @view }}" />
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
23
app/Views/partials/media_card.html
Normal file
23
app/Views/partials/media_card.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<article class="card article-card">
|
||||
<img class="media-frame" src="{{ @item.url }}" alt="{{ @item.alt }}">
|
||||
<div class="card-body article-card__body">
|
||||
<p class="meta-text">{{ @item.width }} × {{ @item.height }}<br>{{ @item.created_at_label }}</p>
|
||||
|
||||
<form class="stack" method="post" action="{{ @BASE }}/dashboard/media/{{ @item.id }}/alt">
|
||||
<input type="hidden" name="csrf_token" value="{{ @csrfToken }}">
|
||||
<label class="field">
|
||||
<span class="field-label">Texte alternatif</span>
|
||||
<input class="control" type="text" name="alt" value="{{ @item.alt }}" placeholder="Description de l'image" data-alt-input>
|
||||
</label>
|
||||
<button class="button button--ghost button--small" type="submit">Enregistrer</button>
|
||||
</form>
|
||||
|
||||
<div class="card-actions">
|
||||
<button class="button button--ghost" type="button" data-copy-text="{{ @item.markdown }}" data-markdown-template="">Copier le Markdown</button>
|
||||
<form method="post" action="{{ @BASE }}/dashboard/media/{{ @item.id }}/delete" data-confirm="Supprimer cette image ?">
|
||||
<input type="hidden" name="csrf_token" value="{{ @csrfToken }}">
|
||||
<button class="button button--danger" type="submit">Supprimer</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
20
app/Views/partials/nav_items.html
Normal file
20
app/Views/partials/nav_items.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<ul class="nav-items">
|
||||
<check if="{{ @currentUser }}">
|
||||
<true>
|
||||
<li class="nav-items__item">
|
||||
<a class="nav-items__link" href="{{ @BASE }}/dashboard">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-items__item">
|
||||
<form class="nav-items__form" method="post" action="{{ @BASE }}/logout">
|
||||
<input type="hidden" name="csrf_token" value="{{ @csrfToken }}">
|
||||
<button class="nav-items__button" type="submit">Déconnexion</button>
|
||||
</form>
|
||||
</li>
|
||||
</true>
|
||||
<false>
|
||||
<li class="nav-items__item">
|
||||
<a class="nav-items__link" href="{{ @BASE }}/login">Connexion</a>
|
||||
</li>
|
||||
</false>
|
||||
</check>
|
||||
</ul>
|
||||
25
app/Views/partials/pagination.html
Normal file
25
app/Views/partials/pagination.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<check if="{{ @pagination.pages > 1 }}">
|
||||
<true>
|
||||
<nav class="pagination" aria-label="Pagination">
|
||||
<check if="{{ @pagination.page > 1 }}">
|
||||
<true>
|
||||
<a class="button button--ghost" href="{{ @paginationBase }}?page={{ @pagination.page - 1 }}">Précédent</a>
|
||||
</true>
|
||||
<false>
|
||||
<span class="button button--ghost pagination__disabled">Précédent</span>
|
||||
</false>
|
||||
</check>
|
||||
|
||||
<span class="pagination__info">Page {{ @pagination.page }} sur {{ @pagination.pages }}</span>
|
||||
|
||||
<check if="{{ @pagination.page < @pagination.pages }}">
|
||||
<true>
|
||||
<a class="button button--ghost" href="{{ @paginationBase }}?page={{ @pagination.page + 1 }}">Suivant</a>
|
||||
</true>
|
||||
<false>
|
||||
<span class="button button--ghost pagination__disabled">Suivant</span>
|
||||
</false>
|
||||
</check>
|
||||
</nav>
|
||||
</true>
|
||||
</check>
|
||||
20
app/Views/partials/post_card.html
Normal file
20
app/Views/partials/post_card.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<article class="card article-card">
|
||||
<a class="card-media-link" href="{{ @BASE }}/posts/{{ @post.slug }}">
|
||||
<check if="{{ @post.cover_url }}">
|
||||
<true><img class="media-frame" src="{{ @post.cover_url }}" alt="{{ @post.title }}"></true>
|
||||
<false>
|
||||
<div class="media-frame media-frame--placeholder">Aucune image</div>
|
||||
</false>
|
||||
</check>
|
||||
</a>
|
||||
<div class="card-body article-card__body">
|
||||
<h2 class="card-title">{{ @post.title }}</h2>
|
||||
<p class="meta-text">
|
||||
Publié le <time datetime="{{ @post.created_at }}">{{ @post.created_at_label }}</time>
|
||||
<check if="{{ @post.has_updated_at }}">
|
||||
<true><br>Mis à jour le <time datetime="{{ @post.updated_at }}">{{ @post.updated_at_label }}</time></true>
|
||||
</check>
|
||||
</p>
|
||||
<p class="card-summary">{{ @post.excerpt }}</p>
|
||||
</div>
|
||||
</article>
|
||||
29
app/Views/partials/post_card_admin.html
Normal file
29
app/Views/partials/post_card_admin.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<article class="card article-card">
|
||||
<a class="card-media-link" href="{{ @BASE }}/dashboard/posts/{{ @post.id }}/edit">
|
||||
<check if="{{ @post.cover_url }}">
|
||||
<true><img class="media-frame" src="{{ @post.cover_url }}" alt="{{ @post.title }}"></true>
|
||||
<false>
|
||||
<div class="media-frame media-frame--placeholder">Aucune image</div>
|
||||
</false>
|
||||
</check>
|
||||
</a>
|
||||
<div class="card-body article-card__body">
|
||||
<h2 class="card-title">{{ @post.title }}</h2>
|
||||
<p class="meta-text">
|
||||
Publié le <time datetime="{{ @post.created_at }}">{{ @post.created_at_label }}</time>
|
||||
<check if="{{ @post.has_updated_at }}">
|
||||
<true><br>Mis à jour le <time datetime="{{ @post.updated_at }}">{{ @post.updated_at_label }}</time></true>
|
||||
</check>
|
||||
</p>
|
||||
<p class="card-summary">{{ @post.excerpt }}</p>
|
||||
<div class="card-actions">
|
||||
<a class="button button--ghost" href="{{ @BASE }}/posts/{{ @post.slug }}">Voir</a>
|
||||
<a class="button button--ghost" href="{{ @BASE }}/dashboard/posts/{{ @post.id }}/edit">Modifier</a>
|
||||
<form method="post" action="{{ @BASE }}/dashboard/posts/{{ @post.id }}/delete"
|
||||
data-confirm="Supprimer cet article ?">
|
||||
<input type="hidden" name="csrf_token" value="{{ @csrfToken }}">
|
||||
<button class="button button--danger" type="submit">Supprimer</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
1
app/Views/partials/site_brand.html
Normal file
1
app/Views/partials/site_brand.html
Normal file
@@ -0,0 +1 @@
|
||||
<a class="site-brand__title" href="{{ @BASE }}/">{{ @app.name }}</a>
|
||||
44
app/Views/partials/site_navigation.html
Normal file
44
app/Views/partials/site_navigation.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<input class="nav-toggle" type="checkbox" id="nav-toggle" aria-hidden="true">
|
||||
|
||||
<header class="site-header">
|
||||
<div class="container site-header__inner">
|
||||
<label class="nav-toggle-button" for="nav-toggle">
|
||||
<span class="sr-only">Ouvrir le menu</span>
|
||||
<span class="nav-toggle-button__line"></span>
|
||||
<span class="nav-toggle-button__line"></span>
|
||||
<span class="nav-toggle-button__line"></span>
|
||||
</label>
|
||||
|
||||
<div class="site-brand site-brand--header">
|
||||
<include href="partials/site_brand.html" />
|
||||
</div>
|
||||
|
||||
<nav class="nav nav--desktop" aria-label="Navigation principale">
|
||||
<include href="partials/nav_items.html" />
|
||||
</nav>
|
||||
|
||||
<span class="site-header__spacer" aria-hidden="true"></span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="mobile-menu">
|
||||
<label class="mobile-menu__backdrop" for="nav-toggle" aria-hidden="true"></label>
|
||||
|
||||
<div class="mobile-menu__panel">
|
||||
<header class="mobile-menu__header">
|
||||
<div class="site-brand site-brand--menu">
|
||||
<include href="partials/site_brand.html" />
|
||||
</div>
|
||||
|
||||
<label class="mobile-menu__close" for="nav-toggle">
|
||||
<span class="sr-only">Fermer le menu</span>
|
||||
<span class="mobile-menu__close-line"></span>
|
||||
<span class="mobile-menu__close-line"></span>
|
||||
</label>
|
||||
</header>
|
||||
|
||||
<nav class="mobile-menu__nav" aria-label="Navigation principale mobile">
|
||||
<include href="partials/nav_items.html" />
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
22
app/Views/site/home.html
Normal file
22
app/Views/site/home.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<section class="stack-lg" aria-labelledby="home-title">
|
||||
<header class="page-header">
|
||||
<h1 class="page-title" id="home-title">Articles</h1>
|
||||
</header>
|
||||
|
||||
<check if="{{ @posts }}">
|
||||
<true>
|
||||
<div class="card-grid">
|
||||
<repeat group="{{ @posts }}" value="{{ @post }}">
|
||||
<include href="partials/post_card.html" />
|
||||
</repeat>
|
||||
</div>
|
||||
<include href="partials/pagination.html" />
|
||||
</true>
|
||||
<false>
|
||||
<section class="empty-state" aria-labelledby="home-empty-title">
|
||||
<h2 class="card-title" id="home-empty-title">Aucun article</h2>
|
||||
<p>Le premier article arrivera bientôt.</p>
|
||||
</section>
|
||||
</false>
|
||||
</check>
|
||||
</section>
|
||||
24
app/Views/site/post.html
Normal file
24
app/Views/site/post.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<article class="article" aria-labelledby="post-title">
|
||||
<header class="article-header">
|
||||
<h1 class="article-title" id="post-title">{{ @post.title }}</h1>
|
||||
<p class="meta-text">
|
||||
Publié le <time datetime="{{ @post.created_at }}">{{ @post.created_at_label }}</time>
|
||||
<check if="{{ @post.has_updated_at }}">
|
||||
<true><br>Mis à jour le <time datetime="{{ @post.updated_at }}">{{ @post.updated_at_label }}</time></true>
|
||||
</check>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<check if="{{ @post.cover_url }}">
|
||||
<true>
|
||||
<img class="media-frame media-frame--large article-cover" src="{{ @post.cover_url }}"
|
||||
alt="{{ @post.title }}">
|
||||
</true>
|
||||
<false>
|
||||
<div class="media-frame media-frame--large media-frame--placeholder article-cover">Aucune image
|
||||
</div>
|
||||
</false>
|
||||
</check>
|
||||
|
||||
<div class="prose">{{ @post.body_html | raw }}</div>
|
||||
</article>
|
||||
Reference in New Issue
Block a user