diff --git a/web/modules/custom/figli_compta_ledger/css/ledger-form.css b/web/modules/custom/figli_compta_ledger/css/ledger-form.css new file mode 100644 index 0000000..2f2534c --- /dev/null +++ b/web/modules/custom/figli_compta_ledger/css/ledger-form.css @@ -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 ) -- match the rest of the form's label typography instead of + Drupal's default

. */ +.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; +} diff --git a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.libraries.yml b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.libraries.yml index b1a89d9..b66eab1 100644 --- a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.libraries.yml +++ b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.libraries.yml @@ -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: diff --git a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.module b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.module index 092e6f6..ff6dbae 100644 --- a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.module +++ b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.module @@ -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)