From bdce74ef083b31f0695e339b2e2a7b6bf048cc64 Mon Sep 17 00:00:00 2001
From: tcharlss <tcharlss@bravecassine.com>
Date: Sat, 1 Apr 2023 17:08:18 +0200
Subject: [PATCH] =?UTF-8?q?refactor(ux):=20page=20de=20contr=C3=B4le=20des?=
 =?UTF-8?q?=20t=C3=A2ches=20de=20fond?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* On fait une liste d'objet standard, avec son propre squelette
* Les arguments des fonctions ne sont visibles qu'au clic
* Les tâches en court et en retard sont exposées
* Ajout des éventuels contenus liés, dans la colonne descriptif
* Affichage des dates exactes au survol
* Ajustements cosmétiques : boutons, etc.

Refs: #5542
---
 ecrire/lang/ecrire_fr.php                   |  6 ++
 prive/objets/liste/jobs.html                | 74 +++++++++++++++++++++
 prive/squelettes/contenu/job_queue.html     | 40 +----------
 prive/squelettes/navigation/job_queue.html  |  6 ++
 prive/themes/spip/images/symbol-play-xx.svg |  1 +
 prive/themes/spip/layout.css                | 18 +++++
 prive/themes/spip/lists.css.html            | 35 ++++++++++
 7 files changed, 143 insertions(+), 37 deletions(-)
 create mode 100644 prive/objets/liste/jobs.html
 create mode 100644 prive/squelettes/navigation/job_queue.html
 create mode 100644 prive/themes/spip/images/symbol-play-xx.svg

diff --git a/ecrire/lang/ecrire_fr.php b/ecrire/lang/ecrire_fr.php
index 89b7ca24d7..2f491adb65 100644
--- a/ecrire/lang/ecrire_fr.php
+++ b/ecrire/lang/ecrire_fr.php
@@ -735,11 +735,17 @@ dans une couleur qui indique leur état :',
 	'queue_info_purger' => 'Vous pouvez supprimer toutes les tâches de fond en attente et réinitialiser la liste avec les tâches périodiques',
 	'queue_nb_jobs_in_queue' => '@nb@ tâches en attente',
 	'queue_next_job_in_nb_sec' => 'Prochaine tâche dans @nb@ s',
+	'queue_next_job_scheduled' => 'Prochaine tâche',
 	'queue_no_job_in_queue' => 'Aucune tâche en attente',
 	'queue_one_job_in_queue' => '1 tâche en attente',
 	'queue_priorite_tache' => 'priorité',
 	'queue_purger_queue' => 'Réinitialiser la liste des tâches',
 	'queue_titre' => 'Tâches de fond',
+	'queue_statut_en_cours' => 'En cours',
+	'queue_fonction_label' => 'Fonction',
+	'queue_args_fonction_label' => 'Arguments passés à la fonction @fonction@',
+	'queue_args_voir' => 'Voir les arguments',
+	'queue_liens_label' => 'Liens',
 
 	// R
 	'repertoire_plugins' => 'Répertoire :',
diff --git a/prive/objets/liste/jobs.html b/prive/objets/liste/jobs.html
new file mode 100644
index 0000000000..633711169e
--- /dev/null
+++ b/prive/objets/liste/jobs.html
@@ -0,0 +1,74 @@
+[(#SET{defaut_tri,#ARRAY{
+	date,#ENV{date_sens,-1},
+	statut,1,
+	fonction,1,
+	descriptif,1,
+	id_job,1,
+	points,-1
+}})
+]<B_liste_jobs>
+#ANCRE_PAGINATION
+#SET{nb,#REM|queue_sleep_time_to_next_job}
+#SET{date_next,#VAL{Y-m-d H:i:s}|date{#ENV{date}|concat{' +',#GET{nb},' seconds'}|strtotime}|date_relative}
+<div class="liste-objets jobs mini">
+<table class="spip liste">
+[<caption><strong class="caption">
+	(#ENV*{titre,#GRAND_TOTAL|singulier_ou_pluriel{queue_one_job_in_queue,queue_nb_jobs_in_queue}})
+	<span class="next-job float-end"><:queue_next_job_scheduled|label_ponctuer:> [<abbr title="[(#GET{nb})s]">(#GET{date_next})</abbr>]</span>
+</strong></caption>]
+	<thead>
+		<tr class="first_row">
+			<th class="date principale" scope="col">[(#TRI{date,<:date:>,ajax})]</th>
+			<th class="fonction principale" scope="col">[(#TRI{fonction,<:queue_fonction_label:>,ajax})]</th>
+			<th class="descriptif secondaire" scope="col">[(#TRI{descriptif,<:info_descriptif|label_nettoyer:>,ajax})]</th>
+			<th class="action" scope="col"></th>
+		</tr>
+	</thead>
+	<tbody>
+	<BOUCLE_liste_jobs(jobs) {id_job?} {where?} {status?} {recherche?} {tri #ENV{par,date}} {pagination #ENV{nb,20}}>
+		[(#LANG|changer_typo)]
+		<tr class="[(#COMPTEUR_BOUCLE|alterner{row_odd,row_even})][ (#STATUS|?{is-scheduled,is-pending})][ (#DATE|<{#ENV{date}}|oui)is-late]">
+			<td class="date principale">
+				[<abbr title="[(#DATE|affdate_heure|attribut_html)]">(#DATE|date_relative|spip_ucfirst)</abbr>]
+				[(#STATUS|non)<br /><small class="etiquette etiquette_pending"><:queue_statut_en_cours:></small>]
+				[(#PRIORITE|oui)<br /><small class="etiquette etiquette_priorite"><:queue_priorite_tache|label_ponctuer:>&nbsp;#PRIORITE</small>]
+			</td>
+			<td class="fonction principale">
+				#SET{id_args,#VAL{job}|concat{#ID_JOB,_args}}
+				[(#AUTORISER{administrer,queue}|oui)<a class="mediabox" href="##GET{id_args}" title="<:queue_args_voir|attribut_html:>">]
+					<span class="fonction__nom">#FONCTION</span>
+				[(#AUTORISER{administrer,queue}|oui)
+					</a>
+					<div class="hidden" hidden>
+						[(#BOITE_OUVRIR{<:queue_args_fonction_label{fonction=#FONCTION}:>}|inserer_attribut{id,#GET{id_args}})]
+						<pre><code class="language-php">[(#ARGS*|unserialize|sinon{#ARRAY}|var_export{#EVAL{true}}|htmlspecialchars)]</code></pre>
+						#BOITE_FERMER
+					</div>
+				]
+			</td>
+			<td class="descriptif secondaire">
+				[(#DESCRIPTIF|ptobr)]
+				<B_liens><br /><:queue_liens_label|label_ponctuer:>
+				<BOUCLE_liens(jobs_liens) {id_job} {", "}>
+				<a href="[(#ID_OBJET|generer_objet_url_ecrire{#OBJET})]">[(#OBJET|objet_info{texte_objet}|_T)] <:info_numero_abbreviation:>#ID_OBJET</a>
+				</BOUCLE_liens>
+			</td>
+			<td class="action">
+				[(#AUTORISER{annuler,job,#ID_JOB}|oui)
+					<div class="btns">
+						[(#BOUTON_ACTION{[(#CHEMIN_IMAGE{symbol-play-16.svg}|balise_svg)]<span class="visually-hidden"><:queue_executer_maintenant:></span>,#URL_ACTION_AUTEUR{forcer_job,#ID_JOB,#SELF}, btn btn_icone btn_executer})]
+						[(#BOUTON_ACTION{[(#CHEMIN_IMAGE{supprimer-16.svg}|balise_img)]<span class="visually-hidden"><:annuler:></span>,#URL_ACTION_AUTEUR{annuler_job,#ID_JOB,#SELF},ajax btn btn_link btn_icone btn_annuler})]
+					</div>
+				]
+			</td>
+		</tr>
+	</BOUCLE_liste_jobs>
+	[(#REM|changer_typo)]
+	</tbody>
+</table>
+[<nav class="pagination">(#PAGINATION{#ENV{pagination,prive}})</nav>]
+</div>
+</B_liste_jobs>
+
+[<div class="liste-objets jobs caption-wrap"><strong class="caption">(#ENV*{sinon,<:queue_no_job_in_queue:>})</strong></div>]
+<//B_liste_jobs>
\ No newline at end of file
diff --git a/prive/squelettes/contenu/job_queue.html b/prive/squelettes/contenu/job_queue.html
index c8bb690733..4bac007ec1 100644
--- a/prive/squelettes/contenu/job_queue.html
+++ b/prive/squelettes/contenu/job_queue.html
@@ -2,43 +2,9 @@
 [(#AUTORISER{administrer,queue}|sinon_interdire_acces)]
 [(#REM|job_queue_block_and_watch)]
 <h1 class="grostitre"><:queue_titre:></h1>
-<B_jobs>
-	<div class="liste">
-		<h3>[(#GRAND_TOTAL|singulier_ou_pluriel{queue_one_job_in_queue,queue_nb_jobs_in_queue})]</h3>
-		<p>#SET{nb,#REM|queue_sleep_time_to_next_job}<:queue_next_job_in_nb_sec{nb=#GET{nb}}:></p>
-		[<nav class='pagination'>(#PAGINATION{prive})</nav>]
-		<ul class="liste-items">
-			<BOUCLE_jobs(jobs){par date}{pagination 20}>
-				<li class="item[ (#STATUS|?{'scheduled','pending'})]">
-					<div class="date">[(#DATE|date_relative|spip_ucfirst)][ (#STATUS|non)(en cours)] [(#PRIORITE|oui)\[<:queue_priorite_tache:> : #PRIORITE\]]</div>
-					[<strong class="description">(#DESCRIPTIF|PtoBR)</strong>]
-					[<span class="small">| (#FONCTION|job_queue_display_call{#ARGS})</span>]
-					[(#AUTORISER{'annuler','job',#ID_JOB}|oui)
-					<div class="actions">
-						[(#BOUTON_ACTION{<:annuler:>,#URL_ACTION_AUTEUR{annuler_job,#ID_JOB,#SELF},ajax})]
-						[(#BOUTON_ACTION{<:queue_executer_maintenant:>,#URL_ACTION_AUTEUR{forcer_job,#ID_JOB,#SELF}})]
-					</div>
-					]
-				</li>
-			</BOUCLE_jobs>
-		</ul>
-		[<nav class='pagination'>(#PAGINATION{prive})</nav>]
-		[(#AUTORISER{'purger','queue'}|oui)
-		<p><:queue_info_purger:></p>
-		<div class="actions">
-			[(#BOUTON_ACTION{<:queue_purger_queue:>,#URL_ACTION_AUTEUR{purger_queue,'x',#SELF},ajax})]
-		</div>
-		]
-		</div>
-</B_jobs>
-	<h3><:queue_no_job_in_queue:></h3>
-	[(#AUTORISER{'purger','queue'}|oui)
-	<p><:queue_info_purger:></p>
-	<div class="actions">
-		[(#BOUTON_ACTION{<:queue_purger_queue:>,#URL_ACTION_AUTEUR{purger_queue,'x',#SELF},ajax})]
-	</div>
-	]
-<//B_jobs>
+
+<INCLURE{fond=prive/objets/liste/jobs, ajax, env} />
+
 <script type="text/javascript">/*<![CDATA[*/
 function queue_reload(){jQuery('h1').ajaxReload();}
 if (window.jQuery) setTimeout(queue_reload,60000);
diff --git a/prive/squelettes/navigation/job_queue.html b/prive/squelettes/navigation/job_queue.html
new file mode 100644
index 0000000000..4f83fe2778
--- /dev/null
+++ b/prive/squelettes/navigation/job_queue.html
@@ -0,0 +1,6 @@
+[(#AUTORISER{purger,queue}|oui)
+	#BOITE_OUVRIR{'',raccourcis}
+		<p><:queue_info_purger:></p>
+		[(#BOUTON_ACTION{<:queue_purger_queue:>,#URL_ACTION_AUTEUR{purger_queue,'x',#SELF},ajax})]
+	#BOITE_FERMER
+]
\ No newline at end of file
diff --git a/prive/themes/spip/images/symbol-play-xx.svg b/prive/themes/spip/images/symbol-play-xx.svg
new file mode 100644
index 0000000000..d1ea055f47
--- /dev/null
+++ b/prive/themes/spip/images/symbol-play-xx.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m13.781 7.587-11-7.5a.501.501 0 0 0-.78.413v15a.5.5 0 0 0 .78.413l11-7.5a.5.5 0 0 0 0-.825z" style="fill:currentColor"/></svg>
\ No newline at end of file
diff --git a/prive/themes/spip/layout.css b/prive/themes/spip/layout.css
index 5fbc5ad772..c7024efd3b 100644
--- a/prive/themes/spip/layout.css
+++ b/prive/themes/spip/layout.css
@@ -267,6 +267,24 @@
 	}
 }
 
+
+/**
+ * Pages qui restent en 2 colonnes en écran large
+ * TODO : faire des classes génériques
+ */
+@media (min-width: 1200px) {
+	@supports (display: grid) {
+		.job_queue #conteneur:not(.pleine_largeur) {
+			grid-template-areas:
+				"navigation contenu"
+				"extra      contenu";
+			grid-template-columns: 25% 72%;
+		}
+	}
+}
+
+
+
 /**
  * =====================================
  * 2. Contenus textes limités en largeur
diff --git a/prive/themes/spip/lists.css.html b/prive/themes/spip/lists.css.html
index e8a5cc13dc..c61293fec1 100644
--- a/prive/themes/spip/lists.css.html
+++ b/prive/themes/spip/lists.css.html
@@ -575,6 +575,32 @@
 	background-position: var(--spip-left) var(--spip-list-spacing-x) center;
 }
 
+/* Tâches */
+.liste-objets.jobs th.date,
+.liste-objets.jobs td.date {
+	text-align: start;
+}
+.liste-objets.jobs tr.is-late td {
+	background-color: hsla(var(--spip-color-notice--h),var(--spip-color-notice--s),var(--spip-color-notice--l),0.2) !important;
+}
+.liste-objets.jobs tr.is-pending td,
+.liste-objets.jobs tr.is-late.is-pending td {
+	background-color: var(--spip-color-theme-lightest) !important;
+	font-weight: 500;
+}
+.liste-objets.jobs .etiquette {
+	background-color: var(--spip-color-gray-dark);
+	color: var(--spip-color-white);
+	border-radius: var(--spip-border-radius-mini);
+	padding: 0 0.2em;
+}
+.liste-objets.jobs .btns {
+	display: flex;
+}
+.liste-objets.jobs .btns > * {
+	flex: 1 0 auto;
+}
+
 
 /* Dans boîtes */
 .box.highlight .liste-objets {
@@ -880,6 +906,15 @@
 	text-align: var(--spip-right);
 }
 
+/* Liste de jobs */
+.jobs_liste {
+	margin: 0;
+}
+.liste_items.jobs .item {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+}
 
 /**
  * ================================
-- 
GitLab