feat(web): botón «Campos» que muestra el nombre del campo de SAP junto a cada etiqueta y en las cabeceras de tablas (ayuda del cursor siempre)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6887f9e474
commit
6ad824c6c1
19 files changed
+168
-37
No files matched your search
@@ -8,4 +8,6 @@ export abstract class FieldBase {
|
||||
readonly readOnly = input(false, { alias: 'readonly', transform: booleanAttribute });
|
||||
readonly disabled = input(false, { transform: booleanAttribute });
|
||||
readonly error = input<string>();
|
||||
/** Nombre del campo de SAP (ayuda y «Ver campos»). */
|
||||
readonly dbName = input<string>();
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import { SboField } from './sbo-field';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [SboField, TextInput],
|
||||
template: `
|
||||
<sbo-field [label]="label()" [required]="required()" [error]="error()">
|
||||
<sbo-field [label]="label()" [dbName]="dbName()" [required]="required()" [error]="error()">
|
||||
<sbo-input
|
||||
[(value)]="value"
|
||||
[readonly]="readOnly()"
|
||||
@@ -37,7 +37,7 @@ export class TextField extends FieldBase {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [SboField, CheckboxInput],
|
||||
template: `
|
||||
<sbo-field [label]="label()" [error]="error()">
|
||||
<sbo-field [label]="label()" [dbName]="dbName()" [error]="error()">
|
||||
<sbo-checkbox [(value)]="value" [readonly]="readOnly()" [disabled]="disabled()" />
|
||||
</sbo-field>
|
||||
`,
|
||||
@@ -52,7 +52,7 @@ export class CheckboxField extends FieldBase {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [SboField, TextareaInput],
|
||||
template: `
|
||||
<sbo-field [label]="label()" [required]="required()" [error]="error()">
|
||||
<sbo-field [label]="label()" [dbName]="dbName()" [required]="required()" [error]="error()">
|
||||
<sbo-textarea
|
||||
[(value)]="value"
|
||||
[readonly]="readOnly()"
|
||||
@@ -78,7 +78,7 @@ export class TextareaField extends FieldBase {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [SboField, NumberInput],
|
||||
template: `
|
||||
<sbo-field [label]="label()" [required]="required()" [error]="error()">
|
||||
<sbo-field [label]="label()" [dbName]="dbName()" [required]="required()" [error]="error()">
|
||||
<sbo-number
|
||||
[(value)]="value"
|
||||
[decimals]="decimals()"
|
||||
@@ -100,7 +100,7 @@ export class NumberField extends FieldBase {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [SboField, DateInput],
|
||||
template: `
|
||||
<sbo-field [label]="label()" [required]="required()" [error]="error()">
|
||||
<sbo-field [label]="label()" [dbName]="dbName()" [required]="required()" [error]="error()">
|
||||
<sbo-date [(value)]="value" [readonly]="readOnly()" [disabled]="disabled()" [required]="required()" />
|
||||
</sbo-field>
|
||||
`,
|
||||
@@ -115,7 +115,7 @@ export class DateField extends FieldBase {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [SboField, SelectInput],
|
||||
template: `
|
||||
<sbo-field [label]="label()" [required]="required()" [error]="error()">
|
||||
<sbo-field [label]="label()" [dbName]="dbName()" [required]="required()" [error]="error()">
|
||||
<sbo-select
|
||||
[(value)]="value"
|
||||
[options]="options()"
|
||||
|
||||
@@ -33,6 +33,7 @@ const NAMES = new Map<string, string>();
|
||||
template: `
|
||||
<sbo-field
|
||||
[label]="label()"
|
||||
[dbName]="dbName()"
|
||||
[required]="required()"
|
||||
[error]="error() || missingText()"
|
||||
action
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, booleanAttribute, input, output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, booleanAttribute, inject, input, output } from '@angular/core';
|
||||
import { FieldNames } from '../../core/field-names';
|
||||
|
||||
let nextId = 0;
|
||||
|
||||
@@ -19,8 +20,11 @@ let nextId = 0;
|
||||
[class.req]="required()"
|
||||
[class.ga]="action()"
|
||||
[class.link]="linkable()"
|
||||
[attr.title]="dbName() ?? null"
|
||||
(click)="labelClick.emit()"
|
||||
>{{ label() }}</label
|
||||
>{{ label() }}@if (dbName() && names.show()) {
|
||||
<span class="db">{{ dbName() }}</span>
|
||||
}</label
|
||||
>
|
||||
<div class="ctl">
|
||||
<div class="sbo-group"><ng-content /></div>
|
||||
@@ -65,6 +69,12 @@ let nextId = 0;
|
||||
border-top: 5px solid transparent;
|
||||
border-bottom: 5px solid transparent;
|
||||
}
|
||||
.db {
|
||||
display: block;
|
||||
margin-top: -2px;
|
||||
font: 10.5px/1.2 var(--mono, monospace);
|
||||
color: var(--quiet);
|
||||
}
|
||||
label.link {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -83,7 +93,10 @@ let nextId = 0;
|
||||
`,
|
||||
})
|
||||
export class SboField {
|
||||
protected readonly names = inject(FieldNames);
|
||||
readonly label = input.required<string>();
|
||||
/** Nombre del campo de SAP; sale como ayuda del cursor y, con «Ver campos», debajo de la etiqueta. */
|
||||
readonly dbName = input<string>();
|
||||
readonly required = input(false, { transform: booleanAttribute });
|
||||
readonly action = input(false, { transform: booleanAttribute });
|
||||
/** La etiqueta es un enlace (ej. abrir el socio de negocio). */
|
||||
|
||||
@@ -151,3 +151,10 @@ tr.none td {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
th .db {
|
||||
display: block;
|
||||
font: 10.5px/1.2 var(--mono, monospace);
|
||||
font-weight: 400;
|
||||
color: var(--quiet);
|
||||
}
|
||||
@@ -15,7 +15,9 @@
|
||||
<tr>
|
||||
<th class="rn">#</th>
|
||||
@for (c of columns(); track c.key; let j = $index) {
|
||||
<th sboColResize [style.width]="c.width" [style.text-align]="alignOf(c)" [class.req]="c.required">{{ c.title }}</th>
|
||||
<th sboColResize [style.width]="c.width" [style.text-align]="alignOf(c)" [class.req]="c.required" [attr.title]="c.key">{{ c.title }}@if (names.show()) {
|
||||
<span class="db">{{ c.key }}</span>
|
||||
}</th>
|
||||
@if (hasDetails() && j === detailAt()) {
|
||||
<th class="det">Lotes / Series</th>
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
output,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { FieldNames } from '../../core/field-names';
|
||||
import { formatNumber, parseDecimal } from '../../core/format';
|
||||
import { SlLookupApi } from '../../core/sl/sl-lookup';
|
||||
import type { ListRow } from '../list/list.types';
|
||||
@@ -57,6 +58,7 @@ export class LinesGrid {
|
||||
|
||||
private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);
|
||||
private readonly injector = inject(Injector);
|
||||
protected readonly names = inject(FieldNames);
|
||||
|
||||
constructor() {
|
||||
// las celdas de código con texto propio (la cuenta con su formato) piden los textos de las líneas que se cargan
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { ChangeDetectionStrategy, Component, computed, effect, inject, input, model, signal } from '@angular/core';
|
||||
import { API_URL } from '../../core/api';
|
||||
import { FieldNames } from '../../core/field-names';
|
||||
import { formatNumber, parseDecimal } from '../../core/format';
|
||||
import { SlLookupApi } from '../../core/sl/sl-lookup';
|
||||
import type { ListRow } from '../list/list.types';
|
||||
@@ -31,7 +32,9 @@ const dateOnly = (v: unknown): string => (typeof v === 'string' ? v.slice(0, 10)
|
||||
<tr>
|
||||
<th class="rn">#</th>
|
||||
@for (c of columns(); track c.key) {
|
||||
<th [style.width]="c.width" [class.req]="c.required">{{ c.title }}</th>
|
||||
<th [style.width]="c.width" [class.req]="c.required" [attr.title]="c.key">{{ c.title }}@if (fieldNames.show()) {
|
||||
<span class="db">{{ c.key }}</span>
|
||||
}</th>
|
||||
}
|
||||
@if (canRemove()) {
|
||||
<th class="del"></th>
|
||||
@@ -145,6 +148,7 @@ const dateOnly = (v: unknown): string => (typeof v === 'string' ? v.slice(0, 10)
|
||||
.menu button:hover { background: var(--btn-h); }
|
||||
table { width: 100%; min-width: max-content; border-collapse: collapse; }
|
||||
th { padding: 4px 6px; background: var(--grid-head); border: 1px solid var(--line); font-size: 12.5px; font-weight: 600; text-align: left; white-space: nowrap; }
|
||||
th .db { display: block; font: 10.5px/1.2 var(--mono, monospace); font-weight: 400; color: var(--quiet); }
|
||||
th.req::after { content: ' *'; color: var(--err); }
|
||||
td { padding: 0 2px; height: 27px; border: 1px solid var(--line-soft); white-space: nowrap; }
|
||||
td.ro { background: var(--field-ro); }
|
||||
@@ -181,6 +185,7 @@ export class RowsTable {
|
||||
return rows.filter((r) => keys.some((k) => Number(r[k]) !== 0 && Number.isFinite(Number(r[k]))));
|
||||
});
|
||||
|
||||
protected readonly fieldNames = inject(FieldNames);
|
||||
private readonly lookupApi = inject(SlLookupApi);
|
||||
private readonly http = inject(HttpClient);
|
||||
/** Nombres de los catálogos de las columnas `nameOf`, por recurso. */
|
||||
|
||||
@@ -75,3 +75,10 @@ tr.none td {
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th .db {
|
||||
display: block;
|
||||
font: 10.5px/1.2 var(--mono, monospace);
|
||||
font-weight: 400;
|
||||
color: var(--quiet);
|
||||
}
|
||||
@@ -19,10 +19,13 @@
|
||||
[style.text-align]="align(c)"
|
||||
[class.pick]="searchable(c)"
|
||||
[class.active]="c.key === activeColumnKey()"
|
||||
[attr.title]="searchable(c) ? 'Buscar en esta columna' : null"
|
||||
[attr.title]="(searchable(c) ? 'Buscar en esta columna · ' : '') + c.key"
|
||||
(click)="columnPicked.emit(c)"
|
||||
>
|
||||
{{ c.title }}
|
||||
@if (names.show()) {
|
||||
<span class="db">{{ c.key }}</span>
|
||||
}
|
||||
</th>
|
||||
}
|
||||
</tr>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, computed, inject, input, output } from '@angular/core';
|
||||
import { FieldNames } from '../../core/field-names';
|
||||
import { ColResize } from '../table/col-resize';
|
||||
import { Badge } from '../window/badge';
|
||||
import { type ListColumn, type ListRow, alignOf, cellText, isSearchable, type Tone } from './list.types';
|
||||
@@ -15,6 +16,8 @@ import { type ListColumn, type ListRow, alignOf, cellText, isSearchable, type To
|
||||
styleUrl: './data-table.css',
|
||||
})
|
||||
export class DataTable {
|
||||
protected readonly names = inject(FieldNames);
|
||||
|
||||
readonly columns = input.required<readonly ListColumn[]>();
|
||||
readonly rows = input.required<readonly ListRow[]>();
|
||||
/** Campo único de cada fila. */
|
||||
|
||||
Reference in new issue
Block a user