Deploy AIOS web (React glide grid + FastAPI slice)
Browse files- RELEASES.json +1 -1
- VERSION +1 -1
- web/src/customer-grid/display.ts +50 -0
RELEASES.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
{
|
| 2 |
-
"current": "
|
| 3 |
"releases": [
|
| 4 |
{
|
| 5 |
"version": "v29",
|
|
|
|
| 1 |
{
|
| 2 |
+
"current": "b9a3041",
|
| 3 |
"releases": [
|
| 4 |
{
|
| 5 |
"version": "v29",
|
VERSION
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
|
|
|
|
| 1 |
+
b9a3041
|
web/src/customer-grid/display.ts
CHANGED
|
@@ -437,6 +437,53 @@ export function codePreview(v: CellValue): string {
|
|
| 437 |
return n > 1 ? `${first} +${n - 1} more` : first;
|
| 438 |
}
|
| 439 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
/** One compact line for a json cell. See the note above for why each case reads as it does. */
|
| 441 |
export function jsonPreview(v: CellValue): string {
|
| 442 |
const raw = String(v ?? "").trim();
|
|
@@ -471,6 +518,9 @@ export function jsonPreview(v: CellValue): string {
|
|
| 471 |
return shown.posts.length === 0
|
| 472 |
? "No posts"
|
| 473 |
: `${shown.posts.length} post${shown.posts.length === 1 ? "" : "s"}`;
|
|
|
|
|
|
|
|
|
|
| 474 |
if (Array.isArray(value))
|
| 475 |
return value.length === 0 ? "[]" : `[β¦] ${value.length} item${value.length === 1 ? "" : "s"}`;
|
| 476 |
if (value !== null && typeof value === "object") {
|
|
|
|
| 437 |
return n > 1 ? `${first} +${n - 1} more` : first;
|
| 438 |
}
|
| 439 |
|
| 440 |
+
/**
|
| 441 |
+
* ββ W37-T14 / T15 β THE TWO SHAPES WAVE 37 ADDED, READ AS WHAT THEY ARE.
|
| 442 |
+
*
|
| 443 |
+
* β FOUND BY QA ON THE DEPLOYED BUILD, not by a gate: `1.5BOPP`'s Units cell rendered
|
| 444 |
+
* **`[β¦] 2 items`** and its Tier prices cell the same way, because both are `type: "json"` and
|
| 445 |
+
* both fell through to the generic array branch below. The server was serving the right data the
|
| 446 |
+
* whole time β `[{name:"SLEEVE",qty:12},{name:"Master",qty:144}]` β and the grid said nothing
|
| 447 |
+
* about it. A column whose entire job is to show which units a SKU sells in, showing a count of
|
| 448 |
+
* items, is the [[permitted-is-not-answerable]] shape in the RENDER layer.
|
| 449 |
+
*
|
| 450 |
+
* β KEYED ON THE SHAPE, NEVER ON THE COLUMN KEY. A key test (`field.key === "units"`) would
|
| 451 |
+
* miss the same document on a user database, on a rollup, or under any rename β and this file
|
| 452 |
+
* has no access to the field anyway. Both shapes are exact: EVERY element must be an object
|
| 453 |
+
* carrying exactly the pair, so an unrelated array of objects still falls through to the generic
|
| 454 |
+
* branch and is not silently relabelled.
|
| 455 |
+
*
|
| 456 |
+
* β `Γ` is U+00D7 MULTIPLICATION SIGN and `Β·` is U+00B7 MIDDLE DOT β neither is an em or en
|
| 457 |
+
* dash, so this copy is inside standing rule 2 rather than an exception to it.
|
| 458 |
+
*/
|
| 459 |
+
function namedPairList(value: unknown): string | null {
|
| 460 |
+
if (!Array.isArray(value) || value.length === 0) return null;
|
| 461 |
+
const rows = value as Record<string, unknown>[];
|
| 462 |
+
const every = (a: string, b: string) =>
|
| 463 |
+
rows.every(
|
| 464 |
+
(r) =>
|
| 465 |
+
r !== null &&
|
| 466 |
+
typeof r === "object" &&
|
| 467 |
+
!Array.isArray(r) &&
|
| 468 |
+
typeof r[a] === "string" &&
|
| 469 |
+
typeof r[b] === "number",
|
| 470 |
+
);
|
| 471 |
+
// W37-T15 β units of measure: the NAME and its conversion factor, which is the whole point.
|
| 472 |
+
if (every("name", "qty"))
|
| 473 |
+
return rows.map((r) => `${String(r.name)} Γ${trimNum(r.qty as number)}`).join(" Β· ");
|
| 474 |
+
// W37-T14 β the pricelists that really price this SKU today, each with its price.
|
| 475 |
+
if (every("pricelist", "unit_price"))
|
| 476 |
+
return rows
|
| 477 |
+
.map((r) => `${String(r.pricelist)} $${numberText(r.unit_price as number, { decimals: 2 })}`)
|
| 478 |
+
.join(" Β· ");
|
| 479 |
+
return null;
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
/** `12` not `12.0`, and `1.5` kept β a conversion factor is a count far more often than not. */
|
| 483 |
+
function trimNum(n: number): string {
|
| 484 |
+
return Number.isFinite(n) ? String(Number(n.toFixed(4))) : String(n);
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
/** One compact line for a json cell. See the note above for why each case reads as it does. */
|
| 488 |
export function jsonPreview(v: CellValue): string {
|
| 489 |
const raw = String(v ?? "").trim();
|
|
|
|
| 518 |
return shown.posts.length === 0
|
| 519 |
? "No posts"
|
| 520 |
: `${shown.posts.length} post${shown.posts.length === 1 ? "" : "s"}`;
|
| 521 |
+
// β W37-T14/T15 β the two known shapes read as themselves; anything else keeps the old count.
|
| 522 |
+
const pairs = namedPairList(value);
|
| 523 |
+
if (pairs) return clip(pairs, 60);
|
| 524 |
if (Array.isArray(value))
|
| 525 |
return value.length === 0 ? "[]" : `[β¦] ${value.length} item${value.length === 1 ? "" : "s"}`;
|
| 526 |
if (value !== null && typeof value === "object") {
|