Modernise l'UI du formulaire modal d'ajout/édition de ligne comptable
Le formulaire natif Drupal (Menu settings, URL alias, Authoring information, tabledrag/drag-handle pour des tableaux de 1-3 lignes, "Title" affiché tel quel) était pensé pour un éditeur de contenu générique, pas pour la saisie numérique quotidienne d'une ligne comptable. Sans toucher au Form API (validation, structure des champs inchangées) : - Grille compacte à 3 colonnes (Date+Type, Client, Montant HT/Facture/ TTC en ligne), au lieu de l'empilement vertical par défaut. - Menu settings / URL alias / Authoring information / Published masqués via #access -- jamais utilisés pour une ligne_comptable. - "Title" relabellisé en "Libellé court" avec une description qui explicite son rôle de repli quand "Notes / détail" est vide (déjà le comportement de home.js/dashboard*.js, jusque-là invisible côté formulaire). - Répartition / Entrée liée : poignée de glisser-déposer et bascule "Show row weights" masquées (l'ordre n'affecte jamais la somme ni l'affichage), boutons Ajouter/Retirer/Dupliquer restylés. Classe CSS partagée `figli-ledger-form` ajoutée par form_alter plutôt que de cibler la classe générée par Drupal, différente entre le formulaire d'ajout (node-ligne-comptable-form) et celui d'édition (node-ligne-comptable-edit-form).
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
/* Ligne comptable add/edit form (node/add/ligne_comptable, node/X/edit --
|
||||
usually seen inside the "+ Ajouter une ligne" / "Modifier" AJAX modal
|
||||
from /lignes, see js/home.js's openAddForm/openEditForm). The default
|
||||
Drupal/Gin admin form widgets (tabledrag drag handles, the paragraphs
|
||||
"Toggle Actions" dropdown, vertical tabs rendered as an accordion) are
|
||||
built for content editors managing arbitrary nodes, not for the small
|
||||
numeric entry form associates fill in daily. This only restyles it
|
||||
into a compact grid -- no Form API behavior, validation, or field
|
||||
structure changes here (those live in figli_compta_ledger.module,
|
||||
which also hides the "advanced" accordion and "Published" checkbox
|
||||
entirely rather than just visually, since neither is ever used for a
|
||||
ligne_comptable).
|
||||
|
||||
Explicit local colors rather than Gin's --gin-* custom properties --
|
||||
this form isn't scoped inside #figli-home-app/#figli-dashboard-app, so
|
||||
the same "don't trust Gin's vars on custom routes" lesson from
|
||||
home.css/dashboard.css applies here too. */
|
||||
.figli-ledger-form {
|
||||
--flform-border: #dcdee2;
|
||||
--flform-border-soft: #e8eaed;
|
||||
--flform-label: #4b5563;
|
||||
--flform-text: #1a1a1a;
|
||||
--flform-bg-subtle: #f7f8fa;
|
||||
--flform-accent: #2f6f4f;
|
||||
--flform-danger: #b3261e;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.9rem 1rem;
|
||||
max-width: 44rem;
|
||||
margin: 0 auto;
|
||||
padding: 0.15rem 0.1rem 0.5rem;
|
||||
color: var(--flform-text);
|
||||
}
|
||||
html.gin--dark-mode .figli-ledger-form {
|
||||
--flform-border: #3d3e42;
|
||||
--flform-border-soft: #333438;
|
||||
--flform-label: #a1a5ab;
|
||||
--flform-text: #e8e9ea;
|
||||
--flform-bg-subtle: #2a2b2e;
|
||||
--flform-accent: #5fb98a;
|
||||
--flform-danger: #ff6b6b;
|
||||
}
|
||||
|
||||
/* Wrapper divs Drupal still renders even though they're always empty
|
||||
here (single-language site, path aliases handled elsewhere, status
|
||||
hidden via #access in the module) -- without this they'd each still
|
||||
claim an empty grid cell. */
|
||||
.figli-ledger-form > .field--name-langcode,
|
||||
.figli-ledger-form > .field--name-path,
|
||||
.figli-ledger-form > .entity-content-form-footer,
|
||||
.figli-ledger-form > .form-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Row layout. Everything not listed here (Montant HT / N° Facture /
|
||||
Montant TTC) is left to grid auto-placement, which -- given they're
|
||||
the only three single-column items appearing consecutively in DOM
|
||||
order -- lines them up as one row of three for free. */
|
||||
.figli-ledger-form > .field--name-title,
|
||||
.figli-ledger-form > .field--name-field-client,
|
||||
.figli-ledger-form > .field--name-field-entree-liee,
|
||||
.figli-ledger-form > .field--name-field-repartition,
|
||||
.figli-ledger-form > .field--name-field-notes,
|
||||
.figli-ledger-form > .field--name-field-flag {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.figli-ledger-form > .field--name-field-date-ligne {
|
||||
grid-column: 1 / 2;
|
||||
}
|
||||
.figli-ledger-form > .field--name-field-type-ligne {
|
||||
grid-column: 2 / 4;
|
||||
}
|
||||
|
||||
/* Field basics */
|
||||
.figli-ledger-form .form-item__label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
color: var(--flform-label);
|
||||
margin-bottom: 0.2rem;
|
||||
display: inline-block;
|
||||
}
|
||||
.figli-ledger-form .form-item__description {
|
||||
font-size: 0.72rem;
|
||||
color: var(--flform-label);
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
.figli-ledger-form input.form-element,
|
||||
.figli-ledger-form select.form-element,
|
||||
.figli-ledger-form textarea.form-element {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--flform-border);
|
||||
border-radius: 6px;
|
||||
padding: 0.4rem 0.55rem;
|
||||
font-size: 0.88rem;
|
||||
background: transparent;
|
||||
color: var(--flform-text);
|
||||
}
|
||||
.figli-ledger-form input.form-element:focus,
|
||||
.figli-ledger-form select.form-element:focus,
|
||||
.figli-ledger-form textarea.form-element:focus {
|
||||
outline: 2px solid var(--flform-accent);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.figli-ledger-form input[type="date"].form-element {
|
||||
width: auto;
|
||||
min-width: 9.5rem;
|
||||
}
|
||||
.figli-ledger-form textarea.form-element {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* Multi-value tables (Répartition, Entrée client liée): drop the drag
|
||||
handle and the "Show row weights" accessibility toggle for it -- row
|
||||
order never affects the répartition sum or anything displayed
|
||||
downstream, so with the usual 1-3 rows this is pure clutter. Remove
|
||||
isn't affected: it stays in the per-row "Toggle Actions" menu. */
|
||||
.figli-ledger-form .tabledrag-toggle-weight-wrapper,
|
||||
.figli-ledger-form td.field-multiple-drag {
|
||||
display: none;
|
||||
}
|
||||
.figli-ledger-form table.field-multiple-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--flform-bg-subtle);
|
||||
border: 1px solid var(--flform-border-soft);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.figli-ledger-form table.field-multiple-table thead th {
|
||||
text-align: left;
|
||||
padding: 0.5rem 0.6rem 0.35rem;
|
||||
}
|
||||
.figli-ledger-form table.field-multiple-table tbody td {
|
||||
padding: 0.5rem 0.6rem;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid var(--flform-border-soft);
|
||||
}
|
||||
.figli-ledger-form .field-multiple-table .form-item__label {
|
||||
visibility: visible;
|
||||
}
|
||||
.figli-ledger-form .paragraphs-subform {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
.figli-ledger-form .paragraphs-subform > .js-form-wrapper {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Buttons: "Ajouter Répartition" / "Add another item", and the per-row
|
||||
Remove/Duplicate/Collapse actions -- all plain Drupal form-submit
|
||||
buttons by default, restyled as a small, consistent set instead of
|
||||
browser-default grey buttons of varying sizes. */
|
||||
.figli-ledger-form input.button {
|
||||
font-size: 0.78rem;
|
||||
padding: 0.3rem 0.65rem;
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--flform-border);
|
||||
background: transparent;
|
||||
color: var(--flform-text);
|
||||
cursor: pointer;
|
||||
}
|
||||
.figli-ledger-form input.button:hover {
|
||||
border-color: var(--flform-accent);
|
||||
color: var(--flform-accent);
|
||||
}
|
||||
.figli-ledger-form .field-add-more-submit {
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
.figli-ledger-form .paragraphs-dropdown-toggle {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--flform-label);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
padding: 0.1rem 0.3rem;
|
||||
}
|
||||
.figli-ledger-form .paragraphs-dropdown-toggle:hover {
|
||||
color: var(--flform-accent);
|
||||
}
|
||||
.figli-ledger-form .paragraphs-dropdown-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
padding: 0.3rem;
|
||||
border: 1px solid var(--flform-border);
|
||||
border-radius: 6px;
|
||||
background: var(--flform-bg-subtle);
|
||||
}
|
||||
|
||||
/* Field group heading style (the Répartition table's own label, inside
|
||||
its <th>) -- match the rest of the form's label typography instead of
|
||||
Drupal's default <h4>. */
|
||||
.figli-ledger-form table.field-multiple-table h4.form-item__label {
|
||||
font-size: 0.8rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* A répartition row flagged by the sum-mismatch #validate error (see
|
||||
figli_compta_ledger_validate_repartition()) -- kept subtle (a red
|
||||
outline, not a solid fill) to match the same red-liseré convention
|
||||
used for écarts in the /lignes table itself. */
|
||||
.figli-ledger-form tr.error,
|
||||
.figli-ledger-form .has-error {
|
||||
outline: 1px solid var(--flform-danger);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
/* Form actions (Save), rendered by the AJAX dialog's own button pane
|
||||
rather than here in most cases, but keep a sane baseline for the
|
||||
rare full-page (non-modal) view of this form. */
|
||||
.figli-ledger-form .form-actions input.button--primary {
|
||||
background: var(--flform-accent);
|
||||
color: #fff;
|
||||
border-color: var(--flform-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -35,6 +35,13 @@ dashboard_compte:
|
||||
- core/drupal
|
||||
- figli_compta_ledger/vue
|
||||
|
||||
ledger_form:
|
||||
css:
|
||||
theme:
|
||||
css/ledger-form.css: {}
|
||||
dependencies:
|
||||
- core/drupal
|
||||
|
||||
admin_chrome:
|
||||
css:
|
||||
theme:
|
||||
|
||||
@@ -35,6 +35,11 @@ function figli_compta_ledger_form_alter(&$form, FormStateInterface $form_state,
|
||||
if (!in_array($form_id, ['node_ligne_comptable_form', 'node_ligne_comptable_edit_form'], TRUE)) {
|
||||
return;
|
||||
}
|
||||
// The add and edit forms get different auto-generated classes
|
||||
// (node-ligne-comptable-form vs. node-ligne-comptable-edit-form) --
|
||||
// one shared class so css/ledger-form.css can target both without
|
||||
// duplicating every selector.
|
||||
$form['#attributes']['class'][] = 'figli-ledger-form';
|
||||
$form['#validate'][] = 'figli_compta_ledger_validate_repartition';
|
||||
|
||||
// Every save must create a revision, unconditionally -- there's no
|
||||
@@ -71,6 +76,38 @@ function figli_compta_ledger_form_alter(&$form, FormStateInterface $form_state,
|
||||
];
|
||||
}
|
||||
|
||||
// "Title" is generic Drupal boilerplate that means nothing to a
|
||||
// freelancer entering an accounting line -- but home.js/dashboard*.js
|
||||
// do fall back to it as the displayed "Libellé" when Notes / détail is
|
||||
// left empty, so it can't just be hidden. Relabel it so that
|
||||
// relationship is explicit instead of a mystery required field.
|
||||
if (isset($form['title']['widget'][0]['value'])) {
|
||||
$form['title']['widget'][0]['value']['#title'] = t('Libellé court');
|
||||
$form['title']['widget'][0]['value']['#description'] = t('Affiché dans le grand livre si "Notes / détail" ci-dessous est laissé vide.');
|
||||
}
|
||||
|
||||
// Menu settings / URL alias / Authoring information: none of this is
|
||||
// ever used for a ligne_comptable (no menu links, no path aliases, and
|
||||
// the acting user + revision timestamp are already captured by the
|
||||
// forced revision in figli_compta_ledger_node_presave() below) -- just
|
||||
// noise in a form meant to be a quick numeric entry, not a full content
|
||||
// editing screen. #access rather than deleting the keys: safe no-op if
|
||||
// a future field ever gets #group => 'advanced'.
|
||||
if (isset($form['advanced'])) {
|
||||
$form['advanced']['#access'] = FALSE;
|
||||
}
|
||||
|
||||
// "Published" is meaningless here too -- there's no draft/unpublished
|
||||
// workflow for ledger lines, every one of them is expected to count
|
||||
// immediately. Same pattern as the 'revision' field above: force the
|
||||
// value rather than exposing a checkbox nobody should ever touch.
|
||||
if (isset($form['status'])) {
|
||||
$form['status']['widget']['value']['#default_value'] = TRUE;
|
||||
$form['status']['#access'] = FALSE;
|
||||
}
|
||||
|
||||
$form['#attached']['library'][] = 'figli_compta_ledger/ledger_form';
|
||||
|
||||
$request = \Drupal::request();
|
||||
$wrapper_formats = ['drupal_ajax', 'drupal_modal', 'drupal_dialog'];
|
||||
$is_ajax_modal = in_array($request->query->get('_wrapper_format'), $wrapper_formats, TRUE)
|
||||
|
||||
Reference in New Issue
Block a user