Files
drupal-figli-compta/web/modules/custom/figli_compta_ledger/css/home.css
T
bachir f260e8a605 Inline-edit the type badge directly in the table
Clicking a row's type badge swaps it for a native <select> in place;
picking a new value POSTs to a new endpoint
(LedgerActionsController::updateType) instead of opening the full
edit modal for this one field.

The endpoint goes through the normal node save() lifecycle, so
figli_compta_ledger_node_presave() still forces a proper revision and
still enforces the répartition invariant -- nothing here bypasses
that. It also clears a stale field_entree_liee when the new type is
no longer linkable (versement/achat/hébergement), mirroring the full
form's #states visibility rule. CSRF-protected via core's own
/session/token, scoped to CsrfRequestHeaderAccessCheck::TOKEN_KEY to
match what that endpoint actually generates. Verified end-to-end via
the real click flow: correct revision (user + timestamp), correct
optimistic UI update, correct field_entree_liee clearing, and 400/403
on invalid type / missing CSRF respectively.
2026-09-04 21:57:33 +02:00

347 lines
9.5 KiB
CSS

/* Self-contained colors (not relying on Gin CSS custom properties, which
don't reliably resolve on this route) + explicit dark-mode overrides,
since Gin's dark mode (html.gin--dark-mode) is enabled by default here. */
#figli-home-app {
--figli-bg: #ffffff;
--figli-bg-alt: #f5f6f8;
--figli-text: #1a1a1a;
--figli-text-light: #6b7280;
--figli-border: #dcdee2;
--figli-error: #c9312b;
--figli-positive: #1a7f37;
--figli-col-hover: rgba(15, 23, 42, 0.05);
font-family: Inter, -apple-system, sans-serif;
margin: 1rem 0;
color: var(--figli-text);
}
html.gin--dark-mode #figli-home-app {
--figli-bg: #232426;
--figli-bg-alt: #2c2d30;
--figli-text: #e8e9ea;
--figli-text-light: #a1a5ab;
--figli-border: #3d3e42;
--figli-error: #ff6b6b;
--figli-positive: #4ade80;
--figli-col-hover: rgba(255, 255, 255, 0.07);
}
#figli-home-app .figli-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem 1.25rem;
margin-bottom: 1rem;
padding: 0.75rem 1rem;
background: var(--figli-bg-alt);
border: 1px solid var(--figli-border);
border-radius: 6px;
color: var(--figli-text);
}
#figli-home-app .figli-toolbar label {
display: flex;
flex-direction: column;
font-size: 0.75rem;
font-weight: 600;
color: var(--figli-text-light);
gap: 0.15rem;
}
#figli-home-app .figli-toolbar select {
font-size: 0.85rem;
padding: 0.2rem 0.4rem;
background: var(--figli-bg);
color: var(--figli-text);
border: 1px solid var(--figli-border);
border-radius: 4px;
}
#figli-home-app .figli-checkbox {
flex-direction: row !important;
align-items: center;
gap: 0.35rem !important;
}
#figli-home-app .figli-count {
margin-left: auto;
font-size: 0.8rem;
color: var(--figli-text-light);
white-space: nowrap;
}
#figli-home-app .figli-table-wrap {
overflow-x: auto;
border: 1px solid var(--figli-border);
border-radius: 6px;
max-height: 75vh;
overflow-y: auto;
}
#figli-home-app table {
width: 100%;
border-collapse: collapse;
background: var(--figli-bg);
color: var(--figli-text);
font-size: 0.8rem;
white-space: nowrap;
}
#figli-home-app th,
#figli-home-app td {
text-align: left;
padding: 0.3rem 0.6rem;
border-bottom: 1px solid var(--figli-border);
color: var(--figli-text);
}
#figli-home-app thead th {
position: sticky;
top: 0;
background: var(--figli-bg-alt);
color: var(--figli-text);
font-weight: 600;
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.02em;
z-index: 2;
}
#figli-home-app td.amount,
#figli-home-app th.amount {
text-align: right;
font-variant-numeric: tabular-nums;
}
#figli-home-app td.figli-libelle {
white-space: normal;
min-width: 220px;
max-width: 340px;
}
#figli-home-app tr.figli-group-row td {
background: var(--figli-bg-alt);
color: var(--figli-text);
font-weight: 700;
position: sticky;
top: 2rem;
z-index: 1;
}
#figli-home-app tfoot {
position: sticky;
bottom: 0;
/* Higher than thead (2) and the sticky group-row headers (1) -- without
this, tfoot had no explicit z-index (auto), so a group-row header
scrolling underneath it painted on top instead of behind. */
z-index: 3;
}
#figli-home-app tr.figli-totals-row td {
background: var(--figli-bg-alt);
color: var(--figli-text);
font-weight: 700;
border-top: 2px solid var(--figli-border);
border-bottom: none;
}
#figli-home-app td.figli-solde-crediteur {
color: var(--figli-positive);
}
#figli-home-app td.figli-solde-debiteur {
color: var(--figli-error);
}
/* Footer totals row sets its own `color` (higher specificity: tr.class +
td beats a lone td.class) which otherwise overrides the solde colors
above -- repeat them scoped to the row so red/green still wins there. */
#figli-home-app tr.figli-totals-row td.figli-solde-crediteur {
color: var(--figli-positive);
}
#figli-home-app tr.figli-totals-row td.figli-solde-debiteur {
color: var(--figli-error);
}
/* Single colored + bold anchor per row (Montant HT only) so entrées vs
sorties are scannable at a glance -- everything else in the row (TTC,
the 8 compte columns) stays neutral, not a red/green garland. */
#figli-home-app td.figli-montant-positif {
color: var(--figli-positive);
font-weight: 700;
}
#figli-home-app td.figli-montant-negatif {
color: var(--figli-error);
font-weight: 700;
}
/* Error rows: a thin red outline around the row, not a background fill --
easier to read, doesn't fight with dark mode. */
#figli-home-app tr.figli-error-row td {
border-top: 1px solid var(--figli-error);
border-bottom: 1px solid var(--figli-error);
}
#figli-home-app tr.figli-error-row td:first-child {
border-left: 1px solid var(--figli-error);
}
#figli-home-app tr.figli-error-row td:last-child {
border-right: 1px solid var(--figli-error);
}
#figli-home-app td.figli-ecart {
color: var(--figli-error);
font-weight: 700;
}
/* Column highlight to pair with the row hover, forming a crosshair over
the hovered cell. box-shadow (not background) so it layers on top of
whatever the cell already has -- sticky header/footer backgrounds,
error-row outlines -- instead of overwriting them. */
#figli-home-app td.figli-col-hover,
#figli-home-app th.figli-col-hover {
box-shadow: inset 0 0 0 9999px var(--figli-col-hover);
}
#figli-home-app .figli-badge {
display: inline-block;
padding: 0.1rem 0.45rem;
border-radius: 10px;
font-size: 0.68rem;
font-weight: 600;
white-space: nowrap;
}
#figli-home-app .type-entree { background: #1a7f371a; color: var(--figli-positive); }
#figli-home-app .type-charge { background: #6b72801a; color: var(--figli-text-light); }
#figli-home-app .type-versement { background: #d97a0a1a; color: #d97a0a; }
#figli-home-app .type-achat { background: #1d4ed81a; color: #3b6fe0; }
#figli-home-app .type-hebergement { background: #0e91821a; color: #0e9182; }
#figli-home-app .type-ouverture { background: #7c3aed1a; color: #9061f0; }
#figli-home-app .type-autre { background: #6b72801a; color: var(--figli-text-light); }
/* Click-to-edit type badge -- swaps for a native <select> in place
(figli-type-select below), no modal needed for this one field. */
#figli-home-app .figli-badge-editable {
cursor: pointer;
border: 1px solid transparent;
}
#figli-home-app .figli-badge-editable:hover {
border-color: currentColor;
}
#figli-home-app .figli-type-select {
font-size: 0.72rem;
padding: 0.05rem 0.2rem;
background: var(--figli-bg);
color: var(--figli-text);
border: 1px solid var(--figli-border);
border-radius: 4px;
}
#figli-home-app .figli-note {
color: var(--figli-text-light);
font-weight: 400;
font-size: 0.75rem;
}
/* One column per icon (link, edit) rather than one shared cell -- with a
shared cell, text-align: center puts a lone button (rows with no link
icon) in a different spot than a row with both, so the edit pencil
never lined up from row to row. Separate columns line up by
construction, and get a tight, icon-sized width instead of the
table's default 0.6rem horizontal cell padding. */
#figli-home-app td.actions-col,
#figli-home-app th.actions-col {
width: 1%;
padding: 0.2rem 0.1rem;
text-align: center;
white-space: nowrap;
}
/* Buttons stay inline (not flex) inside the <td> -- display: flex on a
table cell breaks the table's own column-width calculation (observed:
a ~55px gap opening up between this column and the next, and the
buttons rendering outside their cell's actual boundary). */
#figli-home-app .figli-edit-btn,
#figli-home-app .figli-link-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.4rem;
height: 1.4rem;
padding: 0;
background: transparent;
border: 1px solid transparent;
border-radius: 4px;
color: var(--figli-text-light);
cursor: pointer;
vertical-align: middle;
}
#figli-home-app .figli-edit-btn:hover,
#figli-home-app .figli-link-btn:hover {
background: var(--figli-bg-alt);
border-color: var(--figli-border);
color: var(--figli-text);
}
#figli-home-app .figli-link-btn.is-linked {
color: #3b6fe0;
}
/* Reconciliation badge on entrée rows: répartition (money owed) vs the
combined répartition of every linked sortie (money actually paid out). */
#figli-home-app .figli-recon-badge {
display: inline-block;
margin-left: 0.4rem;
padding: 0.05rem 0.4rem;
border-radius: 10px;
font-size: 0.68rem;
font-weight: 600;
white-space: nowrap;
cursor: pointer;
background: #1a7f371a;
color: var(--figli-positive);
}
#figli-home-app .figli-recon-badge.is-reste {
background: #d97a0a1a;
color: #d97a0a;
}
#figli-home-app .figli-recon-badge.is-anomalie {
background: #c9312b1a;
color: var(--figli-error);
}
#figli-home-app .figli-clear-drilldown {
font-size: 0.8rem;
padding: 0.2rem 0.6rem;
}
/* Sliding-window edge markers (IntersectionObserver targets) -- kept
short so they don't add visible dead space when idle, tall enough
(min-height) to reliably intersect the observer's root margin. */
#figli-home-app tr.figli-sentinel-row td {
padding: 0.3rem 0.6rem;
min-height: 1.5rem;
text-align: center;
color: var(--figli-text-light);
font-size: 0.75rem;
font-style: italic;
}
#figli-home-app .figli-error {
background: #fde8e8;
border: 1px solid #f4a3a3;
color: #7a1a1a;
padding: 0.75rem 1rem;
border-radius: 6px;
}
/* Dismissible, doesn't replace the table like the top-level fetch error
does -- a failed inline type change is a small hiccup, not a reason to
hide everything that's already loaded. */
#figli-home-app .figli-inline-error {
margin-bottom: 1rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}