@amiable-dev/docusaurus-plugin-stentorosaur 0.20.0 → 0.21.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.
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Copyright (c) Your Organization
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ /**
8
+ * ADR-004: StatusDataProvider Context
9
+ *
10
+ * Centralized context provider for status data fetching.
11
+ * Fetches daily-summary.json and current.json once, provides merged data to children.
12
+ *
13
+ * @see docs/adrs/ADR-004-simplified-status-card-ux.md
14
+ */
15
+ import React, { type ReactNode } from 'react';
16
+ import type { DailySummaryFile } from '../types';
17
+ /**
18
+ * Compact reading format from current.json
19
+ */
20
+ interface CurrentReading {
21
+ t: number;
22
+ svc: string;
23
+ state: 'up' | 'down' | 'degraded' | 'maintenance';
24
+ code: number;
25
+ lat: number;
26
+ err?: string;
27
+ }
28
+ /**
29
+ * Current status file structure
30
+ */
31
+ interface CurrentStatus {
32
+ readings: CurrentReading[];
33
+ }
34
+ /**
35
+ * Day status for UptimeBar visualization
36
+ */
37
+ export interface DayStatus {
38
+ /** ISO date string (YYYY-MM-DD) */
39
+ date: string;
40
+ /** Uptime percentage (0-100) */
41
+ uptimePercent: number;
42
+ /** Number of incidents */
43
+ incidents: number;
44
+ /** Total checks performed */
45
+ checksTotal: number;
46
+ /** Successful checks */
47
+ checksPassed: number;
48
+ /** Computed status based on uptime thresholds */
49
+ status: 'operational' | 'degraded' | 'outage' | 'no-data';
50
+ /** Average latency in ms (optional) */
51
+ avgLatencyMs?: number | null;
52
+ /** P95 latency in ms (optional) */
53
+ p95LatencyMs?: number | null;
54
+ }
55
+ /**
56
+ * Context value shape for StatusDataProvider
57
+ */
58
+ export interface StatusDataContextValue {
59
+ /** Daily summary data (days 1-89) */
60
+ dailySummary: DailySummaryFile | null;
61
+ /** Current status data (today's readings) */
62
+ currentStatus: CurrentStatus | null;
63
+ /** Whether data is being fetched */
64
+ loading: boolean;
65
+ /** Error if data fetch failed */
66
+ error: Error | null;
67
+ /**
68
+ * Get merged 90-day data for a service.
69
+ * Combines today from current.json with history from daily-summary.json.
70
+ */
71
+ getMerged90Days: (serviceName: string) => DayStatus[];
72
+ /** Refetch data */
73
+ refresh: () => Promise<void>;
74
+ }
75
+ /**
76
+ * Props for StatusDataProvider
77
+ */
78
+ export interface StatusDataProviderProps {
79
+ /** Base URL for status data files (e.g., '/status-data') */
80
+ baseUrl: string;
81
+ /** Children to render */
82
+ children: ReactNode;
83
+ }
84
+ /**
85
+ * StatusDataProvider component
86
+ *
87
+ * Provides centralized status data fetching and caching for child components.
88
+ * Implements the hybrid read pattern from ADR-002:
89
+ * - Fetches daily-summary.json for historical data (days 1-89)
90
+ * - Fetches current.json for today's live data
91
+ * - Merges them for complete 90-day view
92
+ */
93
+ export declare function StatusDataProvider({ baseUrl, children, }: StatusDataProviderProps): React.ReactElement;
94
+ /**
95
+ * Hook to access status data context
96
+ *
97
+ * @throws Error if used outside StatusDataProvider
98
+ */
99
+ export declare function useStatusData(): StatusDataContextValue;
100
+ export type { CurrentReading, CurrentStatus };
101
+ //# sourceMappingURL=StatusDataProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusDataProvider.d.ts","sourceRoot":"","sources":["../../src/context/StatusDataProvider.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,UAAU,CAAC;AAEpE;;GAEG;AACH,UAAU,cAAc;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,CAAC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,UAAU,aAAa;IACrB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,MAAM,EAAE,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1D,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mCAAmC;IACnC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,qCAAqC;IACrC,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtC,6CAA6C;IAC7C,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,iCAAiC;IACjC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB;;;OAGG;IACH,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;IACtD,mBAAmB;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAgID;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,QAAQ,GACT,EAAE,uBAAuB,GAAG,KAAK,CAAC,YAAY,CAgK9C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,sBAAsB,CAMtD;AAGD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StatusDataProvider = StatusDataProvider;
4
+ exports.useStatusData = useStatusData;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ /**
7
+ * Copyright (c) Your Organization
8
+ *
9
+ * This source code is licensed under the MIT license found in the
10
+ * LICENSE file in the root directory of this source tree.
11
+ */
12
+ /**
13
+ * ADR-004: StatusDataProvider Context
14
+ *
15
+ * Centralized context provider for status data fetching.
16
+ * Fetches daily-summary.json and current.json once, provides merged data to children.
17
+ *
18
+ * @see docs/adrs/ADR-004-simplified-status-card-ux.md
19
+ */
20
+ const react_1 = require("react");
21
+ // Create context with undefined default (will throw if used outside provider)
22
+ const StatusDataContext = (0, react_1.createContext)(undefined);
23
+ /**
24
+ * Uptime thresholds for status calculation (from ADR-004)
25
+ */
26
+ const THRESHOLDS = {
27
+ OPERATIONAL: 99, // >= 99% = operational
28
+ DEGRADED: 95, // >= 95% = degraded, < 95% = outage
29
+ };
30
+ /**
31
+ * Stale data threshold (24 hours in milliseconds)
32
+ */
33
+ const STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
34
+ /**
35
+ * Calculate status from uptime percentage
36
+ */
37
+ function calculateStatus(uptimePercent, checksTotal) {
38
+ if (checksTotal === 0)
39
+ return 'no-data';
40
+ if (uptimePercent >= THRESHOLDS.OPERATIONAL)
41
+ return 'operational';
42
+ if (uptimePercent >= THRESHOLDS.DEGRADED)
43
+ return 'degraded';
44
+ return 'outage';
45
+ }
46
+ /**
47
+ * Calculate p95 latency from an array of latency values
48
+ */
49
+ function calculateP95(latencies) {
50
+ if (latencies.length === 0)
51
+ return null;
52
+ const sorted = [...latencies].sort((a, b) => a - b);
53
+ const index = Math.ceil(sorted.length * 0.95) - 1;
54
+ return sorted[Math.max(0, index)];
55
+ }
56
+ /**
57
+ * Group readings by date for a specific service
58
+ */
59
+ function groupReadingsByDate(readings, serviceName) {
60
+ const groups = new Map();
61
+ const lowerServiceName = serviceName.toLowerCase();
62
+ for (const reading of readings) {
63
+ if (reading.svc.toLowerCase() !== lowerServiceName)
64
+ continue;
65
+ const date = new Date(reading.t).toISOString().split('T')[0];
66
+ if (!groups.has(date)) {
67
+ groups.set(date, []);
68
+ }
69
+ groups.get(date).push(reading);
70
+ }
71
+ return groups;
72
+ }
73
+ /**
74
+ * Aggregate readings for a specific day into a DayStatus
75
+ */
76
+ function aggregateDayReadings(date, readings) {
77
+ const checksTotal = readings.length;
78
+ const checksPassed = readings.filter((r) => r.state === 'up' || r.state === 'maintenance').length;
79
+ const uptimePercent = checksTotal > 0 ? (checksPassed / checksTotal) * 100 : 0;
80
+ const latencies = readings.filter((r) => r.state === 'up').map((r) => r.lat);
81
+ const avgLatencyMs = latencies.length > 0
82
+ ? Math.round(latencies.reduce((sum, lat) => sum + lat, 0) / latencies.length)
83
+ : null;
84
+ const p95LatencyMs = calculateP95(latencies);
85
+ // Count incidents (transitions from up to down)
86
+ let incidentCount = 0;
87
+ for (let i = 1; i < readings.length; i++) {
88
+ if (readings[i - 1].state === 'up' && readings[i].state === 'down') {
89
+ incidentCount++;
90
+ }
91
+ }
92
+ return {
93
+ date,
94
+ uptimePercent,
95
+ incidents: incidentCount,
96
+ checksTotal,
97
+ checksPassed,
98
+ status: calculateStatus(uptimePercent, checksTotal),
99
+ avgLatencyMs,
100
+ p95LatencyMs,
101
+ };
102
+ }
103
+ /**
104
+ * Convert DailySummaryEntry to DayStatus
105
+ */
106
+ function summaryEntryToDayStatus(entry) {
107
+ const uptimePercent = entry.uptimePct * 100;
108
+ return {
109
+ date: entry.date,
110
+ uptimePercent,
111
+ incidents: entry.incidentCount,
112
+ checksTotal: entry.checksTotal,
113
+ checksPassed: entry.checksPassed,
114
+ status: calculateStatus(uptimePercent, entry.checksTotal),
115
+ avgLatencyMs: entry.avgLatencyMs,
116
+ p95LatencyMs: entry.p95LatencyMs,
117
+ };
118
+ }
119
+ /**
120
+ * StatusDataProvider component
121
+ *
122
+ * Provides centralized status data fetching and caching for child components.
123
+ * Implements the hybrid read pattern from ADR-002:
124
+ * - Fetches daily-summary.json for historical data (days 1-89)
125
+ * - Fetches current.json for today's live data
126
+ * - Merges them for complete 90-day view
127
+ */
128
+ function StatusDataProvider({ baseUrl, children, }) {
129
+ const [dailySummary, setDailySummary] = (0, react_1.useState)(null);
130
+ const [currentStatus, setCurrentStatus] = (0, react_1.useState)(null);
131
+ const [loading, setLoading] = (0, react_1.useState)(true);
132
+ const [error, setError] = (0, react_1.useState)(null);
133
+ const [summaryFailed, setSummaryFailed] = (0, react_1.useState)(false);
134
+ const [currentFailed, setCurrentFailed] = (0, react_1.useState)(false);
135
+ /**
136
+ * Fetch both data files in parallel
137
+ */
138
+ const fetchData = (0, react_1.useCallback)(async () => {
139
+ setLoading(true);
140
+ setError(null);
141
+ setSummaryFailed(false);
142
+ setCurrentFailed(false);
143
+ let summaryOk = false;
144
+ let currentOk = false;
145
+ try {
146
+ // Fetch both files in parallel
147
+ const [summaryResponse, currentResponse] = await Promise.all([
148
+ fetch(`${baseUrl}/daily-summary.json`).catch(() => null),
149
+ fetch(`${baseUrl}/current.json`).catch(() => null),
150
+ ]);
151
+ // Handle summary response
152
+ if (summaryResponse?.ok) {
153
+ try {
154
+ const data = await summaryResponse.json();
155
+ setDailySummary(data);
156
+ summaryOk = true;
157
+ // Check for stale data
158
+ if (data.lastUpdated) {
159
+ const lastUpdatedTime = new Date(data.lastUpdated).getTime();
160
+ const now = Date.now();
161
+ if (now - lastUpdatedTime > STALE_THRESHOLD_MS) {
162
+ console.warn(`[StatusDataProvider] Data is stale (last updated: ${data.lastUpdated})`);
163
+ }
164
+ }
165
+ }
166
+ catch {
167
+ setSummaryFailed(true);
168
+ }
169
+ }
170
+ else {
171
+ setSummaryFailed(true);
172
+ }
173
+ // Handle current response
174
+ if (currentResponse?.ok) {
175
+ try {
176
+ const data = await currentResponse.json();
177
+ // Handle both array and object with readings property
178
+ const readings = Array.isArray(data) ? data : data.readings || [];
179
+ setCurrentStatus({ readings });
180
+ currentOk = true;
181
+ }
182
+ catch {
183
+ setCurrentFailed(true);
184
+ }
185
+ }
186
+ else {
187
+ setCurrentFailed(true);
188
+ }
189
+ // Only set error if both files failed
190
+ if (!summaryOk && !currentOk) {
191
+ setError(new Error('No data available'));
192
+ }
193
+ setLoading(false);
194
+ }
195
+ catch (err) {
196
+ setError(err instanceof Error ? err : new Error('Network error'));
197
+ setLoading(false);
198
+ }
199
+ }, [baseUrl]);
200
+ // Fetch data on mount
201
+ (0, react_1.useEffect)(() => {
202
+ fetchData();
203
+ }, [fetchData]);
204
+ /**
205
+ * Get merged 90-day data for a service
206
+ */
207
+ const getMerged90Days = (0, react_1.useCallback)((serviceName) => {
208
+ // Return empty array if no data or unknown service
209
+ const lowerServiceName = serviceName.toLowerCase();
210
+ const entries = [];
211
+ const today = new Date().toISOString().split('T')[0];
212
+ // Get historical data from summary
213
+ const historicalEntries = dailySummary?.services?.[lowerServiceName] ||
214
+ dailySummary?.services?.[serviceName] ||
215
+ [];
216
+ // Get today's readings from current.json
217
+ const todayReadings = currentStatus?.readings
218
+ ? groupReadingsByDate(currentStatus.readings, serviceName).get(today) ||
219
+ []
220
+ : [];
221
+ // Add today's aggregated data if we have readings
222
+ if (todayReadings.length > 0) {
223
+ // Sort by timestamp for accurate incident counting
224
+ todayReadings.sort((a, b) => a.t - b.t);
225
+ entries.push(aggregateDayReadings(today, todayReadings));
226
+ }
227
+ // Add historical entries (filter out today if it exists in summary)
228
+ for (const entry of historicalEntries) {
229
+ if (entry.date !== today) {
230
+ entries.push(summaryEntryToDayStatus(entry));
231
+ }
232
+ }
233
+ // Sort by date descending (most recent first)
234
+ entries.sort((a, b) => b.date.localeCompare(a.date));
235
+ // Limit to 90 days
236
+ return entries.slice(0, 90);
237
+ }, [dailySummary, currentStatus]);
238
+ /**
239
+ * Refresh data (re-fetch)
240
+ */
241
+ const refresh = (0, react_1.useCallback)(async () => {
242
+ await fetchData();
243
+ }, [fetchData]);
244
+ // Memoize context value
245
+ const value = (0, react_1.useMemo)(() => ({
246
+ dailySummary,
247
+ currentStatus,
248
+ loading,
249
+ error,
250
+ getMerged90Days,
251
+ refresh,
252
+ }), [dailySummary, currentStatus, loading, error, getMerged90Days, refresh]);
253
+ return ((0, jsx_runtime_1.jsx)(StatusDataContext.Provider, { value: value, children: children }));
254
+ }
255
+ /**
256
+ * Hook to access status data context
257
+ *
258
+ * @throws Error if used outside StatusDataProvider
259
+ */
260
+ function useStatusData() {
261
+ const context = (0, react_1.useContext)(StatusDataContext);
262
+ if (context === undefined) {
263
+ throw new Error('useStatusData must be used within a StatusDataProvider');
264
+ }
265
+ return context;
266
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAC,GAAG,EAAC,MAAM,8BAA8B,CAAC;AACjD,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAC,aAAa,EAAc,UAAU,EAAC,MAAM,SAAS,CAAC;AAEnE,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,aAAa,CAqBlD,CAAC;AAmIF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,6BA2CzB,CAAC;AAEL;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,UAAU,CAoCb;AA8CD,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,OAAO,GACR,EAAE,uBAAuB,CAAC,aAAa,EAAE,aAAa,CAAC,GAAG,aAAa,CAEvE"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAC,GAAG,EAAC,MAAM,8BAA8B,CAAC;AACjD,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAC,aAAa,EAAc,UAAU,EAAC,MAAM,SAAS,CAAC;AAEnE,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,aAAa,CAqBlD,CAAC;AAmIF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,6BA2CzB,CAAC;AAEL;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,UAAU,CAoCb;AA+CD,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,OAAO,GACR,EAAE,uBAAuB,CAAC,aAAa,EAAE,aAAa,CAAC,GAAG,aAAa,CAEvE"}
package/lib/options.js CHANGED
@@ -257,6 +257,7 @@ const pluginOptionsSchema = utils_validation_1.Joi.object({
257
257
  defaultSLO: utils_validation_1.Joi.number().min(0).max(100).default(exports.DEFAULT_OPTIONS.defaultSLO),
258
258
  systemSLOs: utils_validation_1.Joi.object().pattern(utils_validation_1.Joi.string(), utils_validation_1.Joi.number().min(0).max(100)).default(exports.DEFAULT_OPTIONS.systemSLOs),
259
259
  statusView: utils_validation_1.Joi.string().valid('default', 'upptime').default('default'),
260
+ statusCardLayout: utils_validation_1.Joi.string().valid('minimal', 'detailed').default('minimal'),
260
261
  uptimeConfig: utils_validation_1.Joi.object({
261
262
  sections: utils_validation_1.Joi.array().items(utils_validation_1.Joi.object({
262
263
  id: utils_validation_1.Joi.string().valid('active-incidents', 'live-status', 'charts', 'scheduled-maintenance', 'past-maintenance', 'past-incidents').required(),
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Copyright (c) Your Organization
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ /**
8
+ * ADR-004: StatusBadge Component
9
+ *
10
+ * Status indicator badge with status text and icon.
11
+ * Supports i18n via customizable labels and size variants.
12
+ *
13
+ * @see docs/adrs/ADR-004-simplified-status-card-ux.md
14
+ */
15
+ import React from 'react';
16
+ /**
17
+ * Status types supported by the badge
18
+ */
19
+ export type StatusType = 'up' | 'degraded' | 'down' | 'maintenance';
20
+ /**
21
+ * Custom labels for i18n support
22
+ */
23
+ export interface StatusLabels {
24
+ up?: string;
25
+ degraded?: string;
26
+ down?: string;
27
+ maintenance?: string;
28
+ }
29
+ /**
30
+ * Size variants
31
+ */
32
+ export type StatusBadgeSize = 'sm' | 'md' | 'lg';
33
+ /**
34
+ * Props for StatusBadge component
35
+ */
36
+ export interface StatusBadgeProps {
37
+ /** Current status */
38
+ status: StatusType;
39
+ /** Custom labels for i18n */
40
+ labels?: StatusLabels;
41
+ /** Size variant (default: 'md') */
42
+ size?: StatusBadgeSize;
43
+ /** Optional additional className */
44
+ className?: string;
45
+ }
46
+ /**
47
+ * StatusBadge Component
48
+ *
49
+ * Displays a status indicator with colored dot and label text.
50
+ * Designed for accessibility with ARIA role and visual icon.
51
+ */
52
+ export declare function StatusBadge({ status, labels, size, className, }: StatusBadgeProps): React.ReactElement;
53
+ export default StatusBadge;
54
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/theme/StatusBadge/index.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,mCAAmC;IACnC,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyCD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,MAAM,EACN,IAAW,EACX,SAAS,GACV,EAAE,gBAAgB,GAAG,KAAK,CAAC,YAAY,CAuBvC;AAGD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.StatusBadge = StatusBadge;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const styles_module_css_1 = __importDefault(require("./styles.module.css"));
9
+ /**
10
+ * Default labels for each status
11
+ */
12
+ const DEFAULT_LABELS = {
13
+ up: 'Operational',
14
+ degraded: 'Degraded',
15
+ down: 'Major Outage',
16
+ maintenance: 'Maintenance',
17
+ };
18
+ /**
19
+ * Status icons for visual indication (accessible, works with or without color)
20
+ */
21
+ const STATUS_ICONS = {
22
+ up: '●',
23
+ degraded: '●',
24
+ down: '●',
25
+ maintenance: '●',
26
+ };
27
+ /**
28
+ * CSS class mapping for status types
29
+ */
30
+ const STATUS_CLASS_MAP = {
31
+ up: styles_module_css_1.default.statusUp,
32
+ degraded: styles_module_css_1.default.statusDegraded,
33
+ down: styles_module_css_1.default.statusDown,
34
+ maintenance: styles_module_css_1.default.statusMaintenance,
35
+ };
36
+ /**
37
+ * CSS class mapping for sizes
38
+ */
39
+ const SIZE_CLASS_MAP = {
40
+ sm: styles_module_css_1.default.sizeSm,
41
+ md: styles_module_css_1.default.sizeMd,
42
+ lg: styles_module_css_1.default.sizeLg,
43
+ };
44
+ /**
45
+ * StatusBadge Component
46
+ *
47
+ * Displays a status indicator with colored dot and label text.
48
+ * Designed for accessibility with ARIA role and visual icon.
49
+ */
50
+ function StatusBadge({ status, labels, size = 'md', className, }) {
51
+ // Merge custom labels with defaults
52
+ const resolvedLabels = {
53
+ ...DEFAULT_LABELS,
54
+ ...labels,
55
+ };
56
+ const label = resolvedLabels[status];
57
+ const icon = STATUS_ICONS[status];
58
+ const statusClass = STATUS_CLASS_MAP[status];
59
+ const sizeClass = SIZE_CLASS_MAP[size];
60
+ return ((0, jsx_runtime_1.jsxs)("span", { className: `${styles_module_css_1.default.statusBadge} ${statusClass} ${sizeClass} ${className || ''}`, role: "status", children: [(0, jsx_runtime_1.jsx)("span", { className: styles_module_css_1.default.statusIcon, "aria-hidden": "true", children: icon }), (0, jsx_runtime_1.jsx)("span", { className: styles_module_css_1.default.statusLabel, children: label })] }));
61
+ }
62
+ // Default export for easier imports
63
+ exports.default = StatusBadge;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * ADR-004: StatusBadge Styles
3
+ *
4
+ * Status indicator badge with status text and icon.
5
+ * Uses CSS variables for theming.
6
+ */
7
+
8
+ /* CSS Variables for status colors (can be overridden by theme) */
9
+ :root {
10
+ --status-operational: #22c55e;
11
+ --status-degraded: #eab308;
12
+ --status-outage: #ef4444;
13
+ --status-maintenance: #3b82f6;
14
+ --status-no-data: #9ca3af;
15
+ }
16
+
17
+ /* Base badge styles */
18
+ .statusBadge {
19
+ display: inline-flex;
20
+ align-items: center;
21
+ gap: 0.375rem;
22
+ font-weight: 500;
23
+ border-radius: 9999px;
24
+ white-space: nowrap;
25
+ }
26
+
27
+ /* Status icon (colored dot) */
28
+ .statusIcon {
29
+ display: inline-flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ border-radius: 50%;
33
+ }
34
+
35
+ /* Status label */
36
+ .statusLabel {
37
+ font-family: inherit;
38
+ }
39
+
40
+ /* Size variants */
41
+ .sizeSm {
42
+ font-size: 0.75rem;
43
+ padding: 0.125rem 0.5rem;
44
+ }
45
+
46
+ .sizeSm .statusIcon {
47
+ font-size: 0.5rem;
48
+ }
49
+
50
+ .sizeMd {
51
+ font-size: 0.875rem;
52
+ padding: 0.25rem 0.75rem;
53
+ }
54
+
55
+ .sizeMd .statusIcon {
56
+ font-size: 0.625rem;
57
+ }
58
+
59
+ .sizeLg {
60
+ font-size: 1rem;
61
+ padding: 0.375rem 1rem;
62
+ }
63
+
64
+ .sizeLg .statusIcon {
65
+ font-size: 0.75rem;
66
+ }
67
+
68
+ /* Status colors */
69
+ .statusUp {
70
+ color: var(--status-operational);
71
+ background-color: rgba(34, 197, 94, 0.1);
72
+ }
73
+
74
+ .statusUp .statusIcon {
75
+ color: var(--status-operational);
76
+ }
77
+
78
+ .statusDegraded {
79
+ color: var(--status-degraded);
80
+ background-color: rgba(234, 179, 8, 0.1);
81
+ }
82
+
83
+ .statusDegraded .statusIcon {
84
+ color: var(--status-degraded);
85
+ }
86
+
87
+ .statusDown {
88
+ color: var(--status-outage);
89
+ background-color: rgba(239, 68, 68, 0.1);
90
+ }
91
+
92
+ .statusDown .statusIcon {
93
+ color: var(--status-outage);
94
+ }
95
+
96
+ .statusMaintenance {
97
+ color: var(--status-maintenance);
98
+ background-color: rgba(59, 130, 246, 0.1);
99
+ }
100
+
101
+ .statusMaintenance .statusIcon {
102
+ color: var(--status-maintenance);
103
+ }
104
+
105
+ /* Dark mode support */
106
+ [data-theme='dark'] .statusUp {
107
+ background-color: rgba(34, 197, 94, 0.15);
108
+ }
109
+
110
+ [data-theme='dark'] .statusDegraded {
111
+ background-color: rgba(234, 179, 8, 0.15);
112
+ }
113
+
114
+ [data-theme='dark'] .statusDown {
115
+ background-color: rgba(239, 68, 68, 0.15);
116
+ }
117
+
118
+ [data-theme='dark'] .statusMaintenance {
119
+ background-color: rgba(59, 130, 246, 0.15);
120
+ }