Aller au contenu

3. Les écrans

Un écran est un fichier JSON dans views/, nommé <modèle>.<genre>.json. Pas un gabarit, pas un composant, pas de CSS.

Huit genres se déclarent. Les voici tous, sur le module de bibliothèque.

La liste

{
"model": "library_title",
"kind": "list",
"fields": ["name", "author", "genre", "published_year", "isbn"]
}

La liste du catalogue : titre, auteur, genre, année, ISBN

Vous avez déclaré cinq colonnes. La pagination, le tri, le filtre, la sélection multiple, le sélecteur de modèle, le bouton « New » et le sélecteur de genre de vue viennent du moteur.

Le formulaire

{
"model": "library_title",
"kind": "form",
"sections": [
{ "title": "section.work", "fields": ["name", "author", "genre", "published_year"] },
{ "title": "section.reference", "fields": ["isbn", "summary"] }
]
}

Le formulaire d&#x27;une œuvre, en deux sections numérotées

Chaque champ a pris le contrôle qui correspond à son type : liste déroulante pour la sélection, champ numérique pour l’entier, zone de texte pour le résumé. Vous n’avez rien choisi.

layout accepte cards, accordion ou tabs si vous voulez une autre disposition des sections.

Le kanban

{
"model": "library_loan",
"kind": "kanban",
"groups_by": "state",
"fields": ["copy_id", "member_id", "due_on"]
}

Le kanban des emprunts : trois colonnes Out, Returned, Lost, avec le compte et les cartes

groups_by prend un champ de sélection ou un many2one : ses valeurs deviennent les colonnes. Les cartes se déplacent d’une colonne à l’autre, et le déplacement écrit le champ.

Remarquez que les en-têtes portent les libellés traduits (« Out », « Returned », « Lost ») et leur compte, et que les cartes affichent le nom calculé de l’exemplaire plutôt qu’un identifiant.

Le même genre, sur le catalogue, groupé par genre littéraire :

Le kanban du catalogue, groupé par genre

Le calendrier

{
"model": "library_loan",
"kind": "calendar",
"date_field": "due_on",
"fields": ["copy_id", "member_id"]
}

Le calendrier des retours attendus, avec navigation par mois

Un seul champ obligatoire : la date qui place l’enregistrement. Ici, la date de retour prévue, ce qui donne au bibliothécaire la vue qui l’intéresse.

La frise

{
"model": "library_loan",
"kind": "timeline",
"start_field": "taken_on",
"end_field": "due_on",
"label_field": "member_id",
"group_by": "state",
"default_zoom": "week"
}

La frise des emprunts, groupée par état, avec les niveaux de zoom

Chaque enregistrement devient une barre entre deux dates. group_by fabrique les lignes, default_zoom choisit l’échelle initiale parmi jour, semaine, mois, trimestre et année.

La frise accepte aussi progress_field (une barre de progression dans la barre), dependency_field (des flèches entre les barres) et milestone_field (des jalons).

Le tableau croisé

{
"model": "library_loan",
"kind": "pivot",
"row_field": "state",
"col_field": "member_id",
"agg": "count",
"chart": "bar"
}

Le tableau croisé des emprunts, état par adhérent

row_field seul suffit ; col_field ajoute la seconde dimension. agg accepte count, sum, avg, min et max, et measure_field désigne alors le champ mesuré. chart affiche en plus un graphique.

Le tableau de bord

{
"model": "library_title",
"kind": "dashboard",
"title": "library_title.dashboard.title",
"kpis": [
{ "key": "titles", "label": "library_title.dashboard.titles", "op": "count" }
],
"charts": [
{ "key": "by_genre", "label": "library_title.dashboard.by_genre",
"chart_type": "column", "groupby": "genre", "op": "count" }
]
}

Des indicateurs et des graphiques, sur un seul modèle. op accepte count, sum et avg ; chart_type accepte column, bar, line, area, pie et donut.

Notez que label est une clé de traduction, ici comme partout.

La charge

{
"model": "library_loan",
"kind": "workload",
"group_by": "member_id",
"start_field": "taken_on",
"end_field": "due_on",
"measure_field": "fine_amount",
"capacity_per_period": 40,
"default_zoom": "week"
}

Une frise qui cumule : elle répond à « qui est surchargé sur quelle période ». capacity_per_period trace le seuil au-delà duquel la période est en dépassement.

Le tableau des huit

GenreObligatoireOptionnel
listfieldsname
formfieldssections, layout
kanbangroups_by, fieldsname
calendardate_fieldfields
timelinestart_field, end_fieldlabel_field, group_by, progress_field, dependency_field, milestone_field, default_zoom
workloadgroup_by, start_field, end_fieldmeasure_field, capacity_per_period, default_zoom
pivotrow_fieldcol_field, measure_field, agg, chart
dashboardkpis, chartstitle

Deux genres de plus existent, alphabetic et gallery, mais ne se déclarent pas dans un fichier : le moteur les propose quand le modèle s’y prête.

Ce que le serveur envoie vraiment

C’est ici que le modèle prend tout son sens.

Fenêtre de terminal
curl -H "Authorization: Bearer $TOKEN" \
"http://127.0.0.1:8080/api/v1/views/library_loan/effective?kind=list"
{"model":"library_loan","kind":"list","body":{"columns":[
{"name":"copy_id","label":"Copy id","label_i18n_key":"library_loan.copy_id",
"type":"many2one","required":true,"target_model":"library_copy","origin":"system"},
{"name":"state","label":"State","label_i18n_key":"library_loan.state",
"type":"selection","options":[
{"value":"out","label":"Out","i18n_key":"library_loan.state.out"}]}]}}

Le serveur ne renvoie pas seulement des données : il renvoie le type de chaque colonne, son libellé, sa clé de traduction, sa cible de relation et ses options. L’administration n’a besoin de connaître aucun nom de champ à l’avance.

C’est pour cette raison qu’un module apparaît sans toucher au frontend, et c’est aussi pour cette raison qu’un module ne peut pas imposer sa propre mise en page : il décrit, le moteur rend.

Et ensuite

Le rendre visible. Parce qu’à ce stade, votre module a quatorze écrans que personne ne trouvera.