@mohasinac/appkit 4.41.15 โ†’ 4.41.17

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.
@@ -64,6 +64,32 @@ export declare function listPublicEvents(params?: string | {
64
64
  page?: number;
65
65
  pageSize?: number;
66
66
  }): Promise<FirebaseSieveResult<EventDocument>>;
67
+ /**
68
+ * ๐Ÿ›‘ ONLY `draft` IS HIDDEN. Every other status is public history.
69
+ *
70
+ * This read `status !== "active"`, which collapsed four distinct states into
71
+ * "this event does not exist":
72
+ *
73
+ * ended a finished raffle โ€” the thing people come back to read, and the
74
+ * only place `raffleWinnerDisplayName` is ever shown. The seeded
75
+ * `event-won-original-set-raffle` has a real recorded winner and
76
+ * its page answered 404.
77
+ * cancelled should say it was cancelled. Vanishing tells a visitor holding
78
+ * the link that they misremembered it, not that it was called off.
79
+ * paused temporarily not accepting entries, which is not the same as gone.
80
+ * draft genuinely not published yet โ€” the one case 404 is right for.
81
+ *
82
+ * The rendering layer was ALREADY built for this: `events/[id]/layout.tsx`
83
+ * computes `eventStatus`, derives `isActive`, and gates `showParticipateTab`
84
+ * on it. Participation is refused by that gate, not by hiding the page โ€” so
85
+ * this filter was the only thing standing between a visitor and content the
86
+ * UI knew how to show.
87
+ *
88
+ * The API (`GET /api/events/[id]`) already returned cancelled and ended events
89
+ * with 200 while the page 404'd them, so the two layers disagreed about what
90
+ * exists. Measured live before the fix:
91
+ * active 200/renders ยท cancelled 200/404 ยท ended 200/404 ยท paused 404/404
92
+ */
67
93
  export declare function getPublicEventById(id: string): Promise<EventDocument | null>;
68
94
  export declare function getEventLeaderboard(eventId: string): Promise<import("../types").LeaderboardEntry[]>;
69
95
  /**
@@ -140,9 +140,35 @@ export async function listPublicEvents(params) {
140
140
  // PUBLIC by name โ€” it backs the homepage strip and /events.
141
141
  return { ...result, items: hidePublicTestData(result.items) };
142
142
  }
143
+ /**
144
+ * ๐Ÿ›‘ ONLY `draft` IS HIDDEN. Every other status is public history.
145
+ *
146
+ * This read `status !== "active"`, which collapsed four distinct states into
147
+ * "this event does not exist":
148
+ *
149
+ * ended a finished raffle โ€” the thing people come back to read, and the
150
+ * only place `raffleWinnerDisplayName` is ever shown. The seeded
151
+ * `event-won-original-set-raffle` has a real recorded winner and
152
+ * its page answered 404.
153
+ * cancelled should say it was cancelled. Vanishing tells a visitor holding
154
+ * the link that they misremembered it, not that it was called off.
155
+ * paused temporarily not accepting entries, which is not the same as gone.
156
+ * draft genuinely not published yet โ€” the one case 404 is right for.
157
+ *
158
+ * The rendering layer was ALREADY built for this: `events/[id]/layout.tsx`
159
+ * computes `eventStatus`, derives `isActive`, and gates `showParticipateTab`
160
+ * on it. Participation is refused by that gate, not by hiding the page โ€” so
161
+ * this filter was the only thing standing between a visitor and content the
162
+ * UI knew how to show.
163
+ *
164
+ * The API (`GET /api/events/[id]`) already returned cancelled and ended events
165
+ * with 200 while the page 404'd them, so the two layers disagreed about what
166
+ * exists. Measured live before the fix:
167
+ * active 200/renders ยท cancelled 200/404 ยท ended 200/404 ยท paused 404/404
168
+ */
143
169
  export async function getPublicEventById(id) {
144
170
  const event = await eventRepository.findByIdOrSlug(id);
145
- if (!event || event.status !== "active")
171
+ if (!event || event.status === "draft")
146
172
  return null;
147
173
  return event;
148
174
  }
@@ -252,11 +252,27 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, }) {
252
252
  .filter(Boolean)
253
253
  .join(" ยท ")
254
254
  : "";
255
+ /*
256
+ * ๐Ÿ›‘ THE STATUS GOES IN THE VISIBLE LINE, NOT ONLY IN `status`.
257
+ *
258
+ * `status` is mapped correctly and the API does emit it (measured:
259
+ * "published" on every row) โ€” but this view's right-hand slot holds
260
+ * Edit / Duplicate / Delete, so unlike the admin table there is no
261
+ * status chip and the value was never rendered anywhere. A seller
262
+ * could not tell which of their listings were live without opening
263
+ * each one, and a checklist case that begins "find a listing whose
264
+ * status reads Published" had nothing to read.
265
+ *
266
+ * Same family as Root Cause #52: the data was already on the row and
267
+ * the UI simply never presented it.
268
+ */
269
+ const statusLabel = toStringValue(item.status, "draft");
270
+ const secondaryBase = auctionSecondary || toStringValue(item.condition, "");
255
271
  return {
256
272
  id: toStringValue(item.id, `product-${index}`),
257
273
  primary: toStringValue(item.title ?? item.name, "Untitled product"),
258
- secondary: auctionSecondary || toStringValue(item.condition, ""),
259
- status: toStringValue(item.status, "draft"),
274
+ secondary: [secondaryBase, statusLabel].filter(Boolean).join(" ยท "),
275
+ status: statusLabel,
260
276
  updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
261
277
  imageUrl: toStringValue(item.mainImage ?? item.images?.[0], undefined),
262
278
  image: toStringValue(item.mainImage ?? item.images?.[0], undefined),
@@ -74,7 +74,26 @@ export const authored = {
74
74
  steps: [
75
75
  "Open /auth/register.",
76
76
  "Type QA Signup Tester in the 'Full name' field.",
77
- "Type qa-signup-1@mailnull.com in the 'Email address' field โ€” if that address already has an account from a previous run, increment the number and use qa-signup-2@mailnull.com, and so on.",
77
+ /*
78
+ * ๐Ÿ›‘ SUBSTITUTE THE RUN ID โ€” this is the one input in the catalogue that
79
+ * is deliberately not literal.
80
+ *
81
+ * `users` is PRESERVE tier: it survives every catalogue wipe, so an
82
+ * account created here is permanent and the harness cannot clean it up.
83
+ * The previous instruction ("increment the number if it already exists")
84
+ * made that explicit and unbounded โ€” one more dead account per run,
85
+ * forever.
86
+ *
87
+ * A run-stamped address under `letitrip-qa.test` fixes both halves:
88
+ * the address is unique per run so signup never collides, and
89
+ * `.test` is an RFC 2606 reserved TLD that can never be registered and
90
+ * can never receive mail โ€” so no real person can ever hold one, which is
91
+ * what makes `tester/scripts/purge-qa-signups.mjs` safe to point at the
92
+ * PRESERVE tier at all.
93
+ *
94
+ * Run that purge after the run to remove exactly these accounts.
95
+ */
96
+ "Type qa-signup+<runId>@letitrip-qa.test in the 'Email address' field, substituting the current run id โ€” e.g. qa-signup+run-1789483929743@letitrip-qa.test.",
78
97
  "Type TestPass123! in the 'Password' field.",
79
98
  "Type TestPass123! in the 'Confirm password' field.",
80
99
  "Tick the 'I accept the terms' checkbox.",
@@ -83,7 +102,7 @@ export const authored = {
83
102
  ],
84
103
  inputs: {
85
104
  fullName: "QA Signup Tester",
86
- email: "qa-signup-1@mailnull.com",
105
+ email: "qa-signup+<runId>@letitrip-qa.test",
87
106
  password: "TestPass123!",
88
107
  },
89
108
  expectedBehaviour: "A Firebase Auth record and a matching Firestore user document are created, keyed on the Auth uid rather than on a generated user-* slug. A verification email is dispatched. The browser is signed in and moved off /auth/register.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.41.15",
3
+ "version": "4.41.17",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"