@djangocfg/layouts 2.1.511 → 2.1.513

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.511",
3
+ "version": "2.1.513",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -89,12 +89,12 @@
89
89
  "check": "tsc --noEmit"
90
90
  },
91
91
  "peerDependencies": {
92
- "@djangocfg/analytics": "^2.1.511",
93
- "@djangocfg/api": "^2.1.511",
94
- "@djangocfg/centrifugo": "^2.1.511",
95
- "@djangocfg/devtools": "^2.1.511",
96
- "@djangocfg/i18n": "^2.1.511",
97
- "@djangocfg/ui-core": "^2.1.511",
92
+ "@djangocfg/analytics": "^2.1.513",
93
+ "@djangocfg/api": "^2.1.513",
94
+ "@djangocfg/centrifugo": "^2.1.513",
95
+ "@djangocfg/devtools": "^2.1.513",
96
+ "@djangocfg/i18n": "^2.1.513",
97
+ "@djangocfg/ui-core": "^2.1.513",
98
98
  "@hookform/resolvers": "^5.2.2",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -124,14 +124,14 @@
124
124
  "uuid": "^11.1.1"
125
125
  },
126
126
  "devDependencies": {
127
- "@djangocfg/analytics": "^2.1.511",
128
- "@djangocfg/api": "^2.1.511",
129
- "@djangocfg/centrifugo": "^2.1.511",
130
- "@djangocfg/devtools": "^2.1.511",
131
- "@djangocfg/i18n": "^2.1.511",
132
- "@djangocfg/typescript-config": "^2.1.511",
133
- "@djangocfg/ui-core": "^2.1.511",
134
- "@djangocfg/ui-tools": "^2.1.511",
127
+ "@djangocfg/analytics": "^2.1.513",
128
+ "@djangocfg/api": "^2.1.513",
129
+ "@djangocfg/centrifugo": "^2.1.513",
130
+ "@djangocfg/devtools": "^2.1.513",
131
+ "@djangocfg/i18n": "^2.1.513",
132
+ "@djangocfg/typescript-config": "^2.1.513",
133
+ "@djangocfg/ui-core": "^2.1.513",
134
+ "@djangocfg/ui-tools": "^2.1.513",
135
135
  "@types/node": "^25.9.5",
136
136
  "@types/react": "19.2.15",
137
137
  "@types/react-dom": "19.2.3",
@@ -39,6 +39,20 @@ export interface ErrorLayoutProps {
39
39
  illustration?: React.ReactNode;
40
40
  /** Support email for contact link */
41
41
  supportEmail?: string;
42
+ /**
43
+ * Public service-status page (e.g. `https://status.example.com`).
44
+ *
45
+ * Rendered as a secondary recovery link beside the support line, answering
46
+ * the question a 500 actually raises — "is it me or is it them?". No default:
47
+ * this package is generic, so the URL belongs to the consuming product, and
48
+ * an app that passes nothing gets exactly the previous behaviour.
49
+ *
50
+ * Pass this ONLY on server-fault pages (500/502/503/504, `global-error`).
51
+ * Not on a 404: telling someone who mistyped a URL to check whether our
52
+ * servers are down is noise, and it teaches people to ignore the link when
53
+ * it finally matters.
54
+ */
55
+ statusUrl?: string;
42
56
  }
43
57
 
44
58
  /**
@@ -230,6 +244,7 @@ export function ErrorLayout({
230
244
  showDefaultActions = true,
231
245
  illustration,
232
246
  supportEmail = 'support@example.com',
247
+ statusUrl,
233
248
  }: ErrorLayoutProps) {
234
249
  const t = useAppT();
235
250
 
@@ -238,6 +253,7 @@ export function ErrorLayout({
238
253
  goHome: t('layouts.errors.goHome'),
239
254
  needHelp: t('layouts.errors.needHelp'),
240
255
  contactSupport: t('layouts.errors.contactSupport'),
256
+ checkStatus: t('layouts.errors.checkStatus'),
241
257
  error: t('layouts.errors.error'),
242
258
  }), [t]);
243
259
 
@@ -319,7 +335,14 @@ export function ErrorLayout({
319
335
  )}
320
336
  </div>
321
337
 
322
- {/* Additional Info */}
338
+ {/* Additional Info
339
+ *
340
+ * Both links are TERTIARY on purpose. The visitor's one real intent on
341
+ * an error page is to get back to what they were doing, which is what
342
+ * the buttons above serve; a third equal-weight button would make the
343
+ * common case harder in order to serve the rare one. The status link
344
+ * answers a secondary question for whoever is asking it, and is quiet
345
+ * for everyone else. */}
323
346
  <div className="pt-8 text-sm text-muted-foreground">
324
347
  <p>
325
348
  {labels.needHelp}{' '}
@@ -330,6 +353,37 @@ export function ErrorLayout({
330
353
  {labels.contactSupport}
331
354
  </a>
332
355
  </p>
356
+
357
+ {statusUrl && (
358
+ <p className="mt-2">
359
+ <a
360
+ href={statusUrl}
361
+ target="_blank"
362
+ rel="noreferrer noopener"
363
+ className="inline-flex items-center gap-1.5 text-primary hover:underline"
364
+ >
365
+ {labels.checkStatus}
366
+ {/* Inline SVG, not an icon package. `ErrorLayout` renders inside
367
+ * Next.js error boundaries where the tree is partially blown
368
+ * out and a provider-dependent icon runtime may fail to
369
+ * resolve — see the glyph note above. */}
370
+ <svg
371
+ className="size-3.5"
372
+ viewBox="0 0 24 24"
373
+ fill="none"
374
+ stroke="currentColor"
375
+ strokeWidth={1.5}
376
+ strokeLinecap="round"
377
+ strokeLinejoin="round"
378
+ aria-hidden="true"
379
+ >
380
+ <path d="M14 5h5v5" />
381
+ <path d="M19 5l-8 8" />
382
+ <path d="M18 14v4.5A1.5 1.5 0 0 1 16.5 20h-11A1.5 1.5 0 0 1 4 18.5v-11A1.5 1.5 0 0 1 5.5 6H10" />
383
+ </svg>
384
+ </a>
385
+ </p>
386
+ )}
333
387
  </div>
334
388
  </div>
335
389
  </div>
@@ -16,6 +16,32 @@ All boundaries here build on the single `Boundary` primitive in
16
16
  | `ErrorLayout.tsx` | Full-page error layout (`getErrorContent` / `ERROR_CODES`). |
17
17
  | `ErrorsTracker/` | Global runtime tracker — listens for error CustomEvents and shows toasts. |
18
18
 
19
+ ## ErrorLayout — `statusUrl`
20
+
21
+ `ErrorLayout` takes an optional `statusUrl`, rendered as a secondary link beside
22
+ the support line (`layouts.errors.checkStatus`). It answers the question a 500
23
+ actually raises — *is it me or is it them?*
24
+
25
+ ```tsx
26
+ <ErrorLayout code={500} supportEmail={…} statusUrl="https://status.example.com" />
27
+ ```
28
+
29
+ There is **no default**: this package is generic, so the URL belongs to the
30
+ consuming product. Omit it and the link does not render.
31
+
32
+ Two rules:
33
+
34
+ - **Server faults only** (500/502/503/504, `global-error`). Never on a 404 —
35
+ sending someone who mistyped a URL to check for an outage is noise, and it
36
+ teaches people to ignore the link when it matters.
37
+ - **Never fetch it to show status inline.** An error page renders when the
38
+ infrastructure is already unhealthy, so that request is the least likely to
39
+ succeed — and a failed one would either reassure falsely or raise a second
40
+ error inside the first. A link always works.
41
+
42
+ This is a *user-facing recovery* path. `ErrorsTracker` below is the
43
+ *developer-facing debugging* path. Different audiences — keep them apart.
44
+
19
45
  ## ErrorsTracker
20
46
 
21
47
  `ErrorTrackingProvider` listens on `window` for typed error events dispatched by