@stapel/listings-react 0.25.10 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/CHANGELOG.md +259 -0
  2. package/MODULE.md +3 -1
  3. package/README.md +122 -9
  4. package/dist/api/generated/schema.d.ts +22 -12
  5. package/dist/api/generated/schema.d.ts.map +1 -1
  6. package/dist/default/CardBadges.d.ts.map +1 -1
  7. package/dist/default/CardBadges.js +3 -1
  8. package/dist/default/CardBadges.js.map +1 -1
  9. package/dist/default/ListingActions.d.ts +108 -0
  10. package/dist/default/ListingActions.d.ts.map +1 -0
  11. package/dist/default/ListingActions.js +29 -0
  12. package/dist/default/ListingActions.js.map +1 -0
  13. package/dist/default/ListingCard.d.ts.map +1 -1
  14. package/dist/default/ListingCard.js +15 -8
  15. package/dist/default/ListingCard.js.map +1 -1
  16. package/dist/default/ListingDetailPane.d.ts +170 -62
  17. package/dist/default/ListingDetailPane.d.ts.map +1 -1
  18. package/dist/default/ListingDetailPane.js +284 -28
  19. package/dist/default/ListingDetailPane.js.map +1 -1
  20. package/dist/default/ListingFeedCard.d.ts.map +1 -1
  21. package/dist/default/ListingFeedCard.js +2 -1
  22. package/dist/default/ListingFeedCard.js.map +1 -1
  23. package/dist/default/ListingPhoto.d.ts.map +1 -1
  24. package/dist/default/ListingPhoto.js +5 -2
  25. package/dist/default/ListingPhoto.js.map +1 -1
  26. package/dist/default/ListingSerpCard.d.ts.map +1 -1
  27. package/dist/default/ListingSerpCard.js +18 -4
  28. package/dist/default/ListingSerpCard.js.map +1 -1
  29. package/dist/default/MyListingsPane.d.ts.map +1 -1
  30. package/dist/default/MyListingsPane.js +49 -21
  31. package/dist/default/MyListingsPane.js.map +1 -1
  32. package/dist/default/ShareAction.d.ts +32 -0
  33. package/dist/default/ShareAction.d.ts.map +1 -0
  34. package/dist/default/ShareAction.js +116 -0
  35. package/dist/default/ShareAction.js.map +1 -0
  36. package/dist/default/actionRow.d.ts +58 -0
  37. package/dist/default/actionRow.d.ts.map +1 -0
  38. package/dist/default/actionRow.js +128 -0
  39. package/dist/default/actionRow.js.map +1 -0
  40. package/dist/default/cardGallery.d.ts +2 -0
  41. package/dist/default/cardGallery.d.ts.map +1 -1
  42. package/dist/default/cardGallery.js +31 -0
  43. package/dist/default/cardGallery.js.map +1 -1
  44. package/dist/default/detailGallery.d.ts +77 -0
  45. package/dist/default/detailGallery.d.ts.map +1 -0
  46. package/dist/default/detailGallery.js +91 -0
  47. package/dist/default/detailGallery.js.map +1 -0
  48. package/dist/default/favorite.d.ts +25 -0
  49. package/dist/default/favorite.d.ts.map +1 -1
  50. package/dist/default/favorite.js +29 -3
  51. package/dist/default/favorite.js.map +1 -1
  52. package/dist/default/icons.d.ts +19 -0
  53. package/dist/default/icons.d.ts.map +1 -1
  54. package/dist/default/icons.js +23 -0
  55. package/dist/default/icons.js.map +1 -1
  56. package/dist/default/index.d.ts +11 -2
  57. package/dist/default/index.d.ts.map +1 -1
  58. package/dist/default/index.js +10 -1
  59. package/dist/default/index.js.map +1 -1
  60. package/dist/default/movableCluster.d.ts +18 -0
  61. package/dist/default/movableCluster.d.ts.map +1 -0
  62. package/dist/default/movableCluster.js +110 -0
  63. package/dist/default/movableCluster.js.map +1 -0
  64. package/dist/default/notice.d.ts +14 -0
  65. package/dist/default/notice.d.ts.map +1 -0
  66. package/dist/default/notice.js +62 -0
  67. package/dist/default/notice.js.map +1 -0
  68. package/dist/headless/MyListings.d.ts +61 -19
  69. package/dist/headless/MyListings.d.ts.map +1 -1
  70. package/dist/headless/MyListings.js +58 -19
  71. package/dist/headless/MyListings.js.map +1 -1
  72. package/dist/headless/Share.d.ts +103 -0
  73. package/dist/headless/Share.d.ts.map +1 -0
  74. package/dist/headless/Share.js +190 -0
  75. package/dist/headless/Share.js.map +1 -0
  76. package/dist/i18n/es.d.ts.map +1 -1
  77. package/dist/i18n/es.js +21 -0
  78. package/dist/i18n/es.js.map +1 -1
  79. package/dist/i18n/generated/errors.es.gen.d.ts +1 -1
  80. package/dist/i18n/generated/errors.es.gen.js +1 -1
  81. package/dist/i18n/generated/errors.gen.d.ts +18 -0
  82. package/dist/i18n/generated/errors.gen.d.ts.map +1 -1
  83. package/dist/i18n/generated/errors.gen.js +9 -0
  84. package/dist/i18n/generated/errors.gen.js.map +1 -1
  85. package/dist/i18n/generated/errors.ru.gen.d.ts +1 -1
  86. package/dist/i18n/generated/errors.ru.gen.js +1 -1
  87. package/dist/i18n/keys.d.ts +45 -0
  88. package/dist/i18n/keys.d.ts.map +1 -1
  89. package/dist/i18n/keys.js +60 -0
  90. package/dist/i18n/keys.js.map +1 -1
  91. package/dist/i18n/ru.d.ts.map +1 -1
  92. package/dist/i18n/ru.js +21 -0
  93. package/dist/i18n/ru.js.map +1 -1
  94. package/dist/index.d.ts +5 -3
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +2 -1
  97. package/dist/index.js.map +1 -1
  98. package/dist/model/cardBadges.d.ts +52 -3
  99. package/dist/model/cardBadges.d.ts.map +1 -1
  100. package/dist/model/cardBadges.js +74 -12
  101. package/dist/model/cardBadges.js.map +1 -1
  102. package/dist/model/mineSource.d.ts +9 -3
  103. package/dist/model/mineSource.d.ts.map +1 -1
  104. package/dist/model/mineSource.js.map +1 -1
  105. package/dist/model/status.d.ts +79 -15
  106. package/dist/model/status.d.ts.map +1 -1
  107. package/dist/model/status.js +86 -17
  108. package/dist/model/status.js.map +1 -1
  109. package/llms.txt +8 -3
  110. package/manifest.json +96 -4
  111. package/nav-manifest.json +1 -1
  112. package/package.json +10 -10
  113. package/src/analytics/generated/events.json +1 -1
  114. package/src/api/generated/schema.ts +22 -12
  115. package/src/default/CardBadges.tsx +3 -1
  116. package/src/default/ListingActions.tsx +196 -0
  117. package/src/default/ListingCard.tsx +79 -43
  118. package/src/default/ListingDetailPane.tsx +520 -89
  119. package/src/default/ListingFeedCard.tsx +2 -0
  120. package/src/default/ListingPhoto.tsx +28 -0
  121. package/src/default/ListingSerpCard.tsx +39 -12
  122. package/src/default/MyListingsPane.tsx +81 -44
  123. package/src/default/ShareAction.tsx +260 -0
  124. package/src/default/actionRow.ts +131 -0
  125. package/src/default/cardGallery.ts +31 -0
  126. package/src/default/detailGallery.ts +97 -0
  127. package/src/default/favorite.tsx +68 -1
  128. package/src/default/icons.tsx +60 -0
  129. package/src/default/index.ts +37 -1
  130. package/src/default/movableCluster.tsx +137 -0
  131. package/src/default/notice.ts +68 -0
  132. package/src/headless/MyListings.tsx +130 -39
  133. package/src/headless/Share.tsx +278 -0
  134. package/src/i18n/es.ts +28 -0
  135. package/src/i18n/generated/errors.es.gen.ts +1 -1
  136. package/src/i18n/generated/errors.gen.ts +9 -0
  137. package/src/i18n/generated/errors.json +31 -0
  138. package/src/i18n/generated/errors.ru.gen.ts +1 -1
  139. package/src/i18n/keys.ts +63 -0
  140. package/src/i18n/ru.ts +28 -0
  141. package/src/index.ts +22 -0
  142. package/src/model/cardBadges.ts +124 -12
  143. package/src/model/mineSource.ts +9 -3
  144. package/src/model/status.ts +102 -20
@@ -285,7 +285,8 @@ export function listingStatusView(
285
285
  }
286
286
 
287
287
  /**
288
- * The dashboard tabs, and the statuses each one folds together.
288
+ * The three tabs whose ROWS come off a keyset page, and the statuses each one
289
+ * folds together.
289
290
  *
290
291
  * The grouping is the SERVER's (`views.my_counters`), copied here so a tab's
291
292
  * caption and its count cannot describe different sets: `active` includes
@@ -293,41 +294,122 @@ export function listingStatusView(
293
294
  * both because a listing in those states is something the owner is still
294
295
  * working on. A client that grouped them its own way would show "3 active"
295
296
  * over two rows.
297
+ *
298
+ * The name is about the SOURCE, not about the counter: `MyCountersResponse`
299
+ * carries four integers since stapel-listings 0.22.4 (`blocked` joined the
300
+ * three), and this is still the set a {@link MyListingsCountedTab} —
301
+ * `MyListingsSource`'s whole parameter type — may name. The removed tab reads
302
+ * its rows off `?status=blocked` directly and unpaged, so a host that
303
+ * implemented the seam before the fourth tab existed is never handed it.
296
304
  */
297
- export const MY_LISTINGS_TABS = ["active", "drafts", "archived"] as const;
305
+ export const MY_LISTINGS_COUNTED_TABS = ["active", "drafts", "archived"] as const;
298
306
 
299
- export type MyListingsTab = (typeof MY_LISTINGS_TABS)[number];
307
+ /** One of the three tabs a `MyListingsSource` is asked for. */
308
+ export type MyListingsCountedTab = (typeof MY_LISTINGS_COUNTED_TABS)[number];
300
309
 
301
- export const MY_LISTINGS_TAB_STATUSES: Readonly<
302
- Record<MyListingsTab, readonly ListingLifecycleStatus[]>
310
+ const COUNTED_TAB_STATUSES: Readonly<
311
+ Record<MyListingsCountedTab, readonly ListingLifecycleStatus[]>
303
312
  > = {
304
313
  active: ["published", "pending"],
305
314
  drafts: ["draft", "rejected"],
306
315
  archived: ["archived", "paused", "expired", "sold"],
307
316
  };
308
317
 
309
- /** Which tab a listing belongs to, or `undefined` for BLOCKED — the one
310
- * status `my/counters` counts in no tab at all. A dashboard that silently
311
- * dropped it would hide exactly the listing whose owner most needs to know. */
312
- export function tabOf(
318
+ /** Which of the three keyset-paged tabs a status belongs to, or `undefined`
319
+ * for one none of them folds in `blocked`, today, which has a tab and a
320
+ * counter of its own and a different row source under it. */
321
+ export function countedTabOf(
313
322
  status: ListingLifecycleStatus
314
- ): MyListingsTab | undefined {
315
- for (const tab of MY_LISTINGS_TABS) {
316
- if (MY_LISTINGS_TAB_STATUSES[tab].includes(status)) return tab;
323
+ ): MyListingsCountedTab | undefined {
324
+ for (const tab of MY_LISTINGS_COUNTED_TABS) {
325
+ if (COUNTED_TAB_STATUSES[tab].includes(status)) return tab;
317
326
  }
318
327
  return undefined;
319
328
  }
320
329
 
321
330
  /**
322
- * The statuses no tab folds in — `blocked`, and nothing else today.
331
+ * The statuses none of the three paged tabs folds in — `blocked`, and nothing
332
+ * else today.
323
333
  *
324
334
  * DERIVED, not written down a second time: a status added upstream and left
325
- * out of the counter groupings lands here automatically and gets shown, which
326
- * is the opposite of what a hardcoded `["blocked"]` would do the day it goes
327
- * stale. The dashboard renders these rows OUTSIDE the tabs (see
328
- * `default/MyListingsPane.tsx`) rather than folding them into one, because a
329
- * tab's rows and its `my/counters` badge have to describe the same set and
330
- * `my/counters` counts a takedown in no tab at all.
335
+ * out of the tab groupings lands here automatically and gets shown, which is
336
+ * the opposite of what a hardcoded `["blocked"]` would do the day it goes
337
+ * stale. That the server now has a `blocked` COUNTER for the same set does not
338
+ * make the derivation redundant: the counter names one status and this names
339
+ * whatever the three groupings leave over, which is the set the fourth tab
340
+ * must ASK for.
331
341
  */
332
342
  export const MY_LISTINGS_UNTABBED_STATUSES: readonly ListingLifecycleStatus[] =
333
- LISTING_STATUSES.filter((status) => tabOf(status) === undefined);
343
+ LISTING_STATUSES.filter((status) => countedTabOf(status) === undefined);
344
+
345
+ /**
346
+ * The fourth tab: the rows `my/counters` counts in NO tab at all.
347
+ *
348
+ * ── What was on screen (D407) ────────────────────────────────────────────
349
+ *
350
+ * A listing pulled by moderation showed "Taken down by a moderator" over a
351
+ * counter row reading "Active 0 · Drafts 0 · Archived 0", beside the active
352
+ * tab's own "nothing of yours is live". The object was on the page and in no
353
+ * tab and in no number — three statements, and the two loudest of them said
354
+ * the seller had nothing.
355
+ *
356
+ * The takedowns had a home before this (a block above the tabs) and that was
357
+ * the half that was wrong: a row outside the tab strip is a row the counters
358
+ * do not describe, and a person reads the counters. So they get a TAB, with a
359
+ * count, like every other state a listing can be in.
360
+ *
361
+ * ── Why a fourth tab and not the archive ─────────────────────────────────
362
+ *
363
+ * Folding `blocked` into `archived` is the other shape this could take, and
364
+ * it cost the count: when D407 was written `my/counters` had three integers
365
+ * and no fourth, so an archive tab holding takedowns would have read the
366
+ * server's `archived` number — `0` — until the tab was opened and its rows
367
+ * could raise it. The fourth tab was counted from its OWN read
368
+ * (`?status=blocked`, unpaged) so that the number was right while the seller
369
+ * was looking at a different tab, which is exactly the moment D407 was
370
+ * measured at. stapel-listings 0.22.4 puts `blocked` in the counter, so the
371
+ * number is the server's now and that read is only the tab's rows — but the
372
+ * property it was there to defend is the same one, and it is now defended by
373
+ * the wire instead of by a page.
374
+ *
375
+ * The archive keeps its meaning too, which is not nothing: "I put this away"
376
+ * and "a moderator took this down" are not the same sentence and a tab that
377
+ * said one over rows that meant the other would be the D407 defect wearing a
378
+ * label.
379
+ */
380
+ export const MY_LISTINGS_REMOVED_TAB = "removed";
381
+
382
+ /**
383
+ * Every tab the dashboard can show: the server's three, then the removed one.
384
+ *
385
+ * The removed tab is DRAWN only where there is something in it (see
386
+ * `headless/MyListings.tsx`) — an empty "Taken down" tab is a scare — but it
387
+ * is in this list unconditionally, because `?tab=removed` must parse and a
388
+ * host must be able to name it.
389
+ */
390
+ export const MY_LISTINGS_TABS: readonly [
391
+ ...typeof MY_LISTINGS_COUNTED_TABS,
392
+ typeof MY_LISTINGS_REMOVED_TAB,
393
+ ] = [...MY_LISTINGS_COUNTED_TABS, MY_LISTINGS_REMOVED_TAB];
394
+
395
+ export type MyListingsTab = (typeof MY_LISTINGS_TABS)[number];
396
+
397
+ export const MY_LISTINGS_TAB_STATUSES: Readonly<
398
+ Record<MyListingsTab, readonly ListingLifecycleStatus[]>
399
+ > = {
400
+ ...COUNTED_TAB_STATUSES,
401
+ [MY_LISTINGS_REMOVED_TAB]: MY_LISTINGS_UNTABBED_STATUSES,
402
+ };
403
+
404
+ /** Which tab a listing belongs to. Every status has one: the three counted
405
+ * groupings, and the removed tab for whatever they leave out. `undefined` is
406
+ * unreachable today and stays in the signature so a status this build has
407
+ * never heard of cannot be filed under a tab by accident. */
408
+ export function tabOf(
409
+ status: ListingLifecycleStatus
410
+ ): MyListingsTab | undefined {
411
+ for (const tab of MY_LISTINGS_TABS) {
412
+ if (MY_LISTINGS_TAB_STATUSES[tab].includes(status)) return tab;
413
+ }
414
+ return undefined;
415
+ }