For "versement freelance" rows specifically: an amber outline plus a
badge when the versement either isn't linked to any entrée client at
all ("Non liée"), or is linked but reconciliationByEntree still shows
a residual on that entrée ("Reste à verser" / "Sur-versé", with the
amount in the tooltip). The residual belongs to the entrée as a whole,
not to any one sortie -- when several versements share an entrée, each
shows the same aggregate figure, since there's no way to attribute the
shortfall to one specific payment. No highlight when the linked entrée
isn't in the currently loaded window (can't tell either way -- same
accepted trade-off as reconciliationByEntree itself).
Verified: filtering to "Versement freelance" shows 21 of 25 loaded
rows flagged (17 unlinked, 4 with a reste-à-verser residual), the
remaining 4 fully-reconciled rows correctly unflagged, and the amber
outline resolves correctly in dark mode.
370 lines
11 KiB
CSS
370 lines
11 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-warning: #d97a0a;
|
|
--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-warning: #f0a94e;
|
|
--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);
|
|
}
|
|
|
|
/* Versement freelance not (fully) backed by its entrée: same thin-outline
|
|
treatment as an error row, but amber -- it's not the ligne itself
|
|
that's inconsistent (répartition still matches montant HT), it's the
|
|
entrée/sortie relationship that isn't settled yet. Doesn't stack with
|
|
figli-error-row's red in practice (different conditions), but scoped
|
|
to :not() just in case a row is somehow both, so one outline wins
|
|
rather than the borders visually competing. */
|
|
#figli-home-app tr.figli-versement-warning-row:not(.figli-error-row) td {
|
|
border-top: 1px solid var(--figli-warning);
|
|
border-bottom: 1px solid var(--figli-warning);
|
|
}
|
|
#figli-home-app tr.figli-versement-warning-row:not(.figli-error-row) td:first-child {
|
|
border-left: 1px solid var(--figli-warning);
|
|
}
|
|
#figli-home-app tr.figli-versement-warning-row:not(.figli-error-row) td:last-child {
|
|
border-right: 1px solid var(--figli-warning);
|
|
}
|
|
|
|
#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-sous_traitant { background: #c9312b1a; color: #c9312b; }
|
|
#figli-home-app .type-salaire_stage { background: #0891b21a; color: #0891b2; }
|
|
#figli-home-app .type-charges_local_pro { background: #65a30d1a; color: #65a30d; }
|
|
#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;
|
|
}
|