@alliance-droid/status-feedback-system 0.1.0 → 0.1.1
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/dist/api.d.ts +57 -0
- package/dist/api.js +76 -0
- package/dist/components/FeedbackButton.svelte +3 -3
- package/dist/components/FeedbackButton.svelte.d.ts +1 -1
- package/dist/components/FeedbackForm.svelte +3 -3
- package/dist/components/FeedbackForm.svelte.d.ts +1 -1
- package/dist/components/StatusBanner.svelte +36 -3
- package/dist/components/StatusBanner.svelte.d.ts +7 -1
- package/dist/components/StatusPage.svelte +150 -83
- package/dist/components/StatusPage.svelte.d.ts +10 -3
- package/dist/index.d.ts +6 -9
- package/dist/index.js +5 -8
- package/dist/types.d.ts +0 -6
- package/package.json +2 -13
- package/dist/components/admin/AdminDashboard.svelte +0 -274
- package/dist/components/admin/AdminDashboard.svelte.d.ts +0 -9
- package/dist/components/admin/ApiKeyManager.svelte +0 -68
- package/dist/components/admin/ApiKeyManager.svelte.d.ts +0 -7
- package/dist/components/admin/FeedbackList.svelte +0 -160
- package/dist/components/admin/FeedbackList.svelte.d.ts +0 -17
- package/dist/components/admin/IncidentForm.svelte +0 -135
- package/dist/components/admin/IncidentForm.svelte.d.ts +0 -14
- package/dist/components/admin/IncidentManager.svelte +0 -153
- package/dist/components/admin/IncidentManager.svelte.d.ts +0 -13
- package/dist/components/admin/ProjectForm.svelte +0 -106
- package/dist/components/admin/ProjectForm.svelte.d.ts +0 -13
- package/dist/components/admin/ProjectList.svelte +0 -57
- package/dist/components/admin/ProjectList.svelte.d.ts +0 -10
- package/dist/components/admin/StatusControl.svelte +0 -62
- package/dist/components/admin/StatusControl.svelte.d.ts +0 -8
- package/dist/components/admin/SystemManager.svelte +0 -119
- package/dist/components/admin/SystemManager.svelte.d.ts +0 -13
- package/dist/components/admin/index.d.ts +0 -9
- package/dist/components/admin/index.js +0 -9
- package/dist/server/db.d.ts +0 -17
- package/dist/server/db.js +0 -68
- package/dist/server/feedback.d.ts +0 -27
- package/dist/server/feedback.js +0 -124
- package/dist/server/handlers.d.ts +0 -41
- package/dist/server/handlers.js +0 -251
- package/dist/server/incidents.d.ts +0 -27
- package/dist/server/incidents.js +0 -180
- package/dist/server/index.d.ts +0 -101
- package/dist/server/index.js +0 -71
- package/dist/server/projects.d.ts +0 -45
- package/dist/server/projects.js +0 -156
- package/dist/server/schema.d.ts +0 -9
- package/dist/server/schema.js +0 -94
- package/dist/server/systems.d.ts +0 -20
- package/dist/server/systems.js +0 -146
- package/dist/server/types.d.ts +0 -4
- package/dist/server/types.js +0 -4
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API client for communicating with the status-feedback-admin backend.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { createStatusClient } from '@alliance-droid/status-feedback-system';
|
|
6
|
+
*
|
|
7
|
+
* const client = createStatusClient({
|
|
8
|
+
* apiUrl: 'https://status-admin.yourcompany.com',
|
|
9
|
+
* apiKey: 'sf_abc123...',
|
|
10
|
+
* });
|
|
11
|
+
*
|
|
12
|
+
* const { systems, incidents } = await client.getStatus();
|
|
13
|
+
*/
|
|
14
|
+
import type { System, Incident, StatusHistoryEntry, Feedback } from './types.js';
|
|
15
|
+
export interface StatusClientConfig {
|
|
16
|
+
/** Base URL of the status-feedback-admin app */
|
|
17
|
+
apiUrl: string;
|
|
18
|
+
/** API key for the project (sf_ prefixed) */
|
|
19
|
+
apiKey: string;
|
|
20
|
+
/** Optional custom fetch function (for SSR) */
|
|
21
|
+
fetch?: typeof globalThis.fetch;
|
|
22
|
+
}
|
|
23
|
+
export interface StatusData {
|
|
24
|
+
systems: System[];
|
|
25
|
+
overallStatus: string;
|
|
26
|
+
}
|
|
27
|
+
export interface IncidentData {
|
|
28
|
+
incidents: Incident[];
|
|
29
|
+
}
|
|
30
|
+
export interface UptimeData {
|
|
31
|
+
history: Record<string, StatusHistoryEntry[]>;
|
|
32
|
+
}
|
|
33
|
+
export interface StatusClient {
|
|
34
|
+
/** Get current system statuses */
|
|
35
|
+
getStatus(): Promise<StatusData>;
|
|
36
|
+
/** Get incidents (active + recent resolved) */
|
|
37
|
+
getIncidents(options?: {
|
|
38
|
+
limit?: number;
|
|
39
|
+
includeResolved?: boolean;
|
|
40
|
+
}): Promise<IncidentData>;
|
|
41
|
+
/** Get uptime history for all systems (90 days) */
|
|
42
|
+
getUptime(options?: {
|
|
43
|
+
days?: number;
|
|
44
|
+
}): Promise<UptimeData>;
|
|
45
|
+
/** Submit user feedback */
|
|
46
|
+
submitFeedback(data: {
|
|
47
|
+
message: string;
|
|
48
|
+
category?: string;
|
|
49
|
+
email?: string;
|
|
50
|
+
pageUrl?: string;
|
|
51
|
+
metadata?: Record<string, unknown>;
|
|
52
|
+
}): Promise<Feedback>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create a status API client instance
|
|
56
|
+
*/
|
|
57
|
+
export declare function createStatusClient(config: StatusClientConfig): StatusClient;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API client for communicating with the status-feedback-admin backend.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { createStatusClient } from '@alliance-droid/status-feedback-system';
|
|
6
|
+
*
|
|
7
|
+
* const client = createStatusClient({
|
|
8
|
+
* apiUrl: 'https://status-admin.yourcompany.com',
|
|
9
|
+
* apiKey: 'sf_abc123...',
|
|
10
|
+
* });
|
|
11
|
+
*
|
|
12
|
+
* const { systems, incidents } = await client.getStatus();
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Create a status API client instance
|
|
16
|
+
*/
|
|
17
|
+
export function createStatusClient(config) {
|
|
18
|
+
const { apiUrl, apiKey } = config;
|
|
19
|
+
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
20
|
+
const baseUrl = apiUrl.replace(/\/$/, '');
|
|
21
|
+
async function request(path, options) {
|
|
22
|
+
const url = `${baseUrl}${path}${path.includes('?') ? '&' : '?'}key=${encodeURIComponent(apiKey)}`;
|
|
23
|
+
const response = await fetchFn(url, {
|
|
24
|
+
...options,
|
|
25
|
+
headers: {
|
|
26
|
+
'Content-Type': 'application/json',
|
|
27
|
+
...options?.headers,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const body = await response.text();
|
|
32
|
+
let message = `API error: ${response.status}`;
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(body);
|
|
35
|
+
if (parsed.error)
|
|
36
|
+
message = parsed.error;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// ignore parse errors
|
|
40
|
+
}
|
|
41
|
+
throw new Error(message);
|
|
42
|
+
}
|
|
43
|
+
const json = (await response.json());
|
|
44
|
+
if (!json.success) {
|
|
45
|
+
throw new Error(json.error ?? 'Unknown API error');
|
|
46
|
+
}
|
|
47
|
+
return json.data;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
async getStatus() {
|
|
51
|
+
return request('/api/status');
|
|
52
|
+
},
|
|
53
|
+
async getIncidents(options) {
|
|
54
|
+
const params = new URLSearchParams();
|
|
55
|
+
if (options?.limit)
|
|
56
|
+
params.set('limit', String(options.limit));
|
|
57
|
+
if (options?.includeResolved !== undefined)
|
|
58
|
+
params.set('includeResolved', String(options.includeResolved));
|
|
59
|
+
const qs = params.toString();
|
|
60
|
+
return request(`/api/incidents${qs ? `?${qs}` : ''}`);
|
|
61
|
+
},
|
|
62
|
+
async getUptime(options) {
|
|
63
|
+
const params = new URLSearchParams();
|
|
64
|
+
if (options?.days)
|
|
65
|
+
params.set('days', String(options.days));
|
|
66
|
+
const qs = params.toString();
|
|
67
|
+
return request(`/api/uptime${qs ? `?${qs}` : ''}`);
|
|
68
|
+
},
|
|
69
|
+
async submitFeedback(data) {
|
|
70
|
+
return request('/api/feedback', {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
body: JSON.stringify(data),
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
5
|
apiKey: string;
|
|
6
|
-
|
|
6
|
+
apiUrl: string;
|
|
7
7
|
categories?: string[];
|
|
8
8
|
userEmail?: string;
|
|
9
9
|
position?: 'bottom-right' | 'bottom-left';
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
let {
|
|
13
13
|
apiKey,
|
|
14
|
-
|
|
14
|
+
apiUrl,
|
|
15
15
|
categories,
|
|
16
16
|
userEmail,
|
|
17
17
|
position = 'bottom-right',
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
<div class="p-4">
|
|
52
52
|
<FeedbackForm
|
|
53
53
|
{apiKey}
|
|
54
|
-
{
|
|
54
|
+
{apiUrl}
|
|
55
55
|
{categories}
|
|
56
56
|
{userEmail}
|
|
57
57
|
onSuccess={() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
interface Props {
|
|
3
3
|
apiKey: string;
|
|
4
|
-
|
|
4
|
+
apiUrl: string;
|
|
5
5
|
categories?: string[];
|
|
6
6
|
userEmail?: string;
|
|
7
7
|
onSuccess?: () => void;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
let {
|
|
12
12
|
apiKey,
|
|
13
|
-
|
|
13
|
+
apiUrl,
|
|
14
14
|
categories = ['Bug', 'Feature Request', 'General'],
|
|
15
15
|
userEmail,
|
|
16
16
|
onSuccess,
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
errorMsg = '';
|
|
33
33
|
|
|
34
34
|
try {
|
|
35
|
-
const res = await fetch(`${
|
|
35
|
+
const res = await fetch(`${apiUrl.replace(/\/$/, '')}/api/feedback`, {
|
|
36
36
|
method: 'POST',
|
|
37
37
|
headers: {
|
|
38
38
|
'Content-Type': 'application/json',
|
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
2
3
|
import type { System, SystemStatus } from '../types.js';
|
|
4
|
+
import { createStatusClient } from '../api.js';
|
|
3
5
|
import StatusIndicator from './shared/StatusIndicator.svelte';
|
|
4
6
|
|
|
5
7
|
interface Props {
|
|
6
|
-
|
|
8
|
+
/** Systems data (pass-through mode) */
|
|
9
|
+
systems?: System[];
|
|
10
|
+
/** API URL for self-fetching mode */
|
|
11
|
+
apiUrl?: string;
|
|
12
|
+
/** API key for self-fetching mode */
|
|
13
|
+
apiKey?: string;
|
|
14
|
+
/** Link to full status page */
|
|
7
15
|
statusUrl?: string;
|
|
8
16
|
}
|
|
9
17
|
|
|
10
|
-
let { systems, statusUrl }: Props = $props();
|
|
18
|
+
let { systems: systemsProp, apiUrl, apiKey, statusUrl }: Props = $props();
|
|
19
|
+
|
|
20
|
+
let systems: System[] = $state(systemsProp ?? []);
|
|
21
|
+
let loading = $state(!systemsProp && !!apiUrl && !!apiKey);
|
|
22
|
+
|
|
23
|
+
onMount(async () => {
|
|
24
|
+
if (systemsProp || !apiUrl || !apiKey) return;
|
|
25
|
+
try {
|
|
26
|
+
const client = createStatusClient({ apiUrl, apiKey });
|
|
27
|
+
const data = await client.getStatus();
|
|
28
|
+
systems = data.systems;
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error('StatusBanner fetch error:', err);
|
|
31
|
+
} finally {
|
|
32
|
+
loading = false;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
$effect(() => {
|
|
37
|
+
if (systemsProp) systems = systemsProp;
|
|
38
|
+
});
|
|
11
39
|
|
|
12
40
|
let overallStatus = $derived.by((): SystemStatus => {
|
|
13
41
|
if (systems.length === 0) return 'operational';
|
|
@@ -25,7 +53,12 @@
|
|
|
25
53
|
};
|
|
26
54
|
</script>
|
|
27
55
|
|
|
28
|
-
{#if
|
|
56
|
+
{#if loading}
|
|
57
|
+
<div class="inline-flex items-center gap-2 rounded-full border border-border px-3 py-1.5 text-xs text-foreground-tertiary">
|
|
58
|
+
<div class="h-2 w-2 animate-pulse rounded-full bg-foreground-tertiary"></div>
|
|
59
|
+
<span>Loading...</span>
|
|
60
|
+
</div>
|
|
61
|
+
{:else if statusUrl}
|
|
29
62
|
<a
|
|
30
63
|
href={statusUrl}
|
|
31
64
|
class="inline-flex items-center gap-2 rounded-full border border-border px-3 py-1.5 text-xs text-foreground-secondary transition-colors hover:bg-surface-hover"
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { System } from '../types.js';
|
|
2
2
|
interface Props {
|
|
3
|
-
|
|
3
|
+
/** Systems data (pass-through mode) */
|
|
4
|
+
systems?: System[];
|
|
5
|
+
/** API URL for self-fetching mode */
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
/** API key for self-fetching mode */
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
/** Link to full status page */
|
|
4
10
|
statusUrl?: string;
|
|
5
11
|
}
|
|
6
12
|
declare const StatusBanner: import("svelte").Component<Props, {}, "">;
|
|
@@ -1,26 +1,81 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
2
3
|
import type { System, Incident, StatusHistoryEntry, SystemStatus } from '../types.js';
|
|
4
|
+
import { createStatusClient } from '../api.js';
|
|
3
5
|
import StatusIndicator from './shared/StatusIndicator.svelte';
|
|
4
6
|
import SystemCard from './shared/SystemCard.svelte';
|
|
5
7
|
import IncidentTimeline from './shared/IncidentTimeline.svelte';
|
|
6
8
|
|
|
7
9
|
interface Props {
|
|
10
|
+
/** Project display name */
|
|
8
11
|
projectName: string;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
+
/** API URL for fetching data (self-fetching mode) */
|
|
13
|
+
apiUrl?: string;
|
|
14
|
+
/** API key for fetching data (self-fetching mode) */
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
/** Systems data (pass-through mode — overrides API fetching) */
|
|
17
|
+
systems?: System[];
|
|
18
|
+
/** Incidents data (pass-through mode) */
|
|
19
|
+
incidents?: Incident[];
|
|
20
|
+
/** Map of systemId → StatusHistoryEntry[] for uptime bars (pass-through mode) */
|
|
12
21
|
statusHistory?: Record<string, StatusHistoryEntry[]>;
|
|
13
22
|
showUptime?: boolean;
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
let {
|
|
17
26
|
projectName,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
apiUrl,
|
|
28
|
+
apiKey,
|
|
29
|
+
systems: systemsProp,
|
|
30
|
+
incidents: incidentsProp,
|
|
31
|
+
statusHistory: statusHistoryProp,
|
|
21
32
|
showUptime = true,
|
|
22
33
|
}: Props = $props();
|
|
23
34
|
|
|
35
|
+
// ── State ──
|
|
36
|
+
let systems: System[] = $state(systemsProp ?? []);
|
|
37
|
+
let incidents: Incident[] = $state(incidentsProp ?? []);
|
|
38
|
+
let statusHistory: Record<string, StatusHistoryEntry[]> = $state(statusHistoryProp ?? {});
|
|
39
|
+
let loading = $state(!systemsProp);
|
|
40
|
+
let error = $state('');
|
|
41
|
+
|
|
42
|
+
// ── Self-fetching mode ──
|
|
43
|
+
const usesApi = $derived(!!apiUrl && !!apiKey && !systemsProp);
|
|
44
|
+
|
|
45
|
+
onMount(async () => {
|
|
46
|
+
if (!usesApi) return;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const client = createStatusClient({ apiUrl: apiUrl!, apiKey: apiKey! });
|
|
50
|
+
|
|
51
|
+
const [statusData, incidentData, uptimeData] = await Promise.all([
|
|
52
|
+
client.getStatus(),
|
|
53
|
+
client.getIncidents(),
|
|
54
|
+
client.getUptime(),
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
systems = statusData.systems;
|
|
58
|
+
incidents = incidentData.incidents;
|
|
59
|
+
statusHistory = uptimeData.history;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
error = err instanceof Error ? err.message : 'Failed to load status data';
|
|
62
|
+
console.error('StatusPage fetch error:', err);
|
|
63
|
+
} finally {
|
|
64
|
+
loading = false;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ── Sync props when in pass-through mode ──
|
|
69
|
+
$effect(() => {
|
|
70
|
+
if (systemsProp) systems = systemsProp;
|
|
71
|
+
});
|
|
72
|
+
$effect(() => {
|
|
73
|
+
if (incidentsProp) incidents = incidentsProp;
|
|
74
|
+
});
|
|
75
|
+
$effect(() => {
|
|
76
|
+
if (statusHistoryProp) statusHistory = statusHistoryProp;
|
|
77
|
+
});
|
|
78
|
+
|
|
24
79
|
// ── Derived state ──
|
|
25
80
|
|
|
26
81
|
let overallStatus = $derived.by((): SystemStatus => {
|
|
@@ -65,92 +120,104 @@
|
|
|
65
120
|
<h1 class="text-2xl font-semibold text-foreground">{projectName} Status</h1>
|
|
66
121
|
</div>
|
|
67
122
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<
|
|
72
|
-
<span class="text-lg font-semibold text-foreground">{overallMessages[overallStatus]}</span>
|
|
123
|
+
{#if loading}
|
|
124
|
+
<div class="rounded-lg border border-border bg-surface p-12 text-center">
|
|
125
|
+
<div class="mx-auto mb-3 h-8 w-8 animate-spin rounded-full border-2 border-foreground-tertiary border-t-primary"></div>
|
|
126
|
+
<p class="text-sm text-foreground-secondary">Loading status...</p>
|
|
73
127
|
</div>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{showUptime}
|
|
86
|
-
/>
|
|
87
|
-
{/each}
|
|
128
|
+
{:else if error}
|
|
129
|
+
<div class="rounded-lg border border-red-500/30 bg-red-500/10 p-6 text-center">
|
|
130
|
+
<i class="fa-solid fa-circle-exclamation mb-2 text-2xl text-red-500"></i>
|
|
131
|
+
<p class="text-sm text-red-400">{error}</p>
|
|
132
|
+
</div>
|
|
133
|
+
{:else}
|
|
134
|
+
<!-- Overall Status Banner -->
|
|
135
|
+
<div class="rounded-lg border p-4 text-center {overallBgClasses[overallStatus]}">
|
|
136
|
+
<div class="flex items-center justify-center gap-2">
|
|
137
|
+
<StatusIndicator status={overallStatus} size="lg" />
|
|
138
|
+
<span class="text-lg font-semibold text-foreground">{overallMessages[overallStatus]}</span>
|
|
88
139
|
</div>
|
|
89
|
-
</
|
|
90
|
-
{/if}
|
|
140
|
+
</div>
|
|
91
141
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
142
|
+
<!-- Systems -->
|
|
143
|
+
{#if systems.length > 0}
|
|
144
|
+
<section>
|
|
145
|
+
<h2 class="mb-4 text-lg font-semibold text-foreground">Systems</h2>
|
|
146
|
+
<div class="space-y-3">
|
|
147
|
+
{#each systems as system}
|
|
148
|
+
<SystemCard
|
|
149
|
+
{system}
|
|
150
|
+
history={statusHistory[system.id] || []}
|
|
151
|
+
{showUptime}
|
|
152
|
+
/>
|
|
153
|
+
{/each}
|
|
154
|
+
</div>
|
|
155
|
+
</section>
|
|
156
|
+
{/if}
|
|
157
|
+
|
|
158
|
+
<!-- Active Incidents -->
|
|
159
|
+
{#if activeIncidents.length > 0}
|
|
160
|
+
<section>
|
|
161
|
+
<h2 class="mb-4 text-lg font-semibold text-foreground">Active Incidents</h2>
|
|
162
|
+
<div class="space-y-4">
|
|
163
|
+
{#each activeIncidents as incident}
|
|
164
|
+
<div class="rounded-lg border border-border bg-surface p-4">
|
|
165
|
+
<div class="flex items-start justify-between">
|
|
166
|
+
<div>
|
|
167
|
+
<h3 class="font-semibold text-foreground">{incident.title}</h3>
|
|
168
|
+
<div class="mt-1 flex items-center gap-2 text-xs text-foreground-tertiary">
|
|
169
|
+
<span class="rounded bg-red-500/10 px-1.5 py-0.5 text-red-500">
|
|
170
|
+
{incident.severity}
|
|
171
|
+
</span>
|
|
172
|
+
<span>{formatDate(incident.createdAt)}</span>
|
|
173
|
+
</div>
|
|
107
174
|
</div>
|
|
175
|
+
<StatusIndicator status={incident.status === 'resolved' ? 'operational' : 'outage'} size="sm" />
|
|
108
176
|
</div>
|
|
109
|
-
|
|
177
|
+
{#if incident.updates && incident.updates.length > 0}
|
|
178
|
+
<div class="mt-4 border-t border-border pt-4">
|
|
179
|
+
<IncidentTimeline updates={incident.updates} />
|
|
180
|
+
</div>
|
|
181
|
+
{/if}
|
|
110
182
|
</div>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{/if}
|
|
116
|
-
</div>
|
|
117
|
-
{/each}
|
|
118
|
-
</div>
|
|
119
|
-
</section>
|
|
120
|
-
{/if}
|
|
183
|
+
{/each}
|
|
184
|
+
</div>
|
|
185
|
+
</section>
|
|
186
|
+
{/if}
|
|
121
187
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
188
|
+
<!-- Resolved Incidents -->
|
|
189
|
+
{#if recentResolved.length > 0}
|
|
190
|
+
<section>
|
|
191
|
+
<h2 class="mb-4 text-lg font-semibold text-foreground">Past Incidents</h2>
|
|
192
|
+
<div class="space-y-4">
|
|
193
|
+
{#each recentResolved as incident}
|
|
194
|
+
<div class="rounded-lg border border-border bg-surface p-4">
|
|
195
|
+
<div class="flex items-start justify-between">
|
|
196
|
+
<div>
|
|
197
|
+
<h3 class="font-semibold text-foreground">{incident.title}</h3>
|
|
198
|
+
<p class="mt-1 text-xs text-foreground-tertiary">
|
|
199
|
+
Resolved {incident.resolvedAt ? formatDate(incident.resolvedAt) : ''}
|
|
200
|
+
</p>
|
|
201
|
+
</div>
|
|
202
|
+
<StatusIndicator status="operational" size="sm" />
|
|
135
203
|
</div>
|
|
136
|
-
|
|
204
|
+
{#if incident.updates && incident.updates.length > 0}
|
|
205
|
+
<div class="mt-4 border-t border-border pt-4">
|
|
206
|
+
<IncidentTimeline updates={incident.updates} />
|
|
207
|
+
</div>
|
|
208
|
+
{/if}
|
|
137
209
|
</div>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
{/if}
|
|
143
|
-
</div>
|
|
144
|
-
{/each}
|
|
145
|
-
</div>
|
|
146
|
-
</section>
|
|
147
|
-
{/if}
|
|
210
|
+
{/each}
|
|
211
|
+
</div>
|
|
212
|
+
</section>
|
|
213
|
+
{/if}
|
|
148
214
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
215
|
+
<!-- No Incidents -->
|
|
216
|
+
{#if activeIncidents.length === 0 && recentResolved.length === 0}
|
|
217
|
+
<div class="rounded-lg border border-border bg-surface p-8 text-center">
|
|
218
|
+
<i class="fa-solid fa-circle-check mb-2 text-2xl text-green-500"></i>
|
|
219
|
+
<p class="text-sm text-foreground-secondary">No incidents reported</p>
|
|
220
|
+
</div>
|
|
221
|
+
{/if}
|
|
155
222
|
{/if}
|
|
156
223
|
</div>
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { System, Incident, StatusHistoryEntry } from '../types.js';
|
|
2
2
|
interface Props {
|
|
3
|
+
/** Project display name */
|
|
3
4
|
projectName: string;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/**
|
|
5
|
+
/** API URL for fetching data (self-fetching mode) */
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
/** API key for fetching data (self-fetching mode) */
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
/** Systems data (pass-through mode — overrides API fetching) */
|
|
10
|
+
systems?: System[];
|
|
11
|
+
/** Incidents data (pass-through mode) */
|
|
12
|
+
incidents?: Incident[];
|
|
13
|
+
/** Map of systemId → StatusHistoryEntry[] for uptime bars (pass-through mode) */
|
|
7
14
|
statusHistory?: Record<string, StatusHistoryEntry[]>;
|
|
8
15
|
showUptime?: boolean;
|
|
9
16
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
* @alliance-droid/status-feedback-system
|
|
3
3
|
*
|
|
4
4
|
* Drop-in system status pages and user feedback forms for SvelteKit apps.
|
|
5
|
+
* All data is fetched from a remote status-feedback-admin backend via API.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
* import { StatusPage,
|
|
8
|
-
*
|
|
9
|
-
* Server exports:
|
|
10
|
-
* import { createStatusServer } from '@alliance-droid/status-feedback-system/server';
|
|
11
|
-
*
|
|
12
|
-
* Admin exports:
|
|
13
|
-
* import { AdminDashboard } from '@alliance-droid/status-feedback-system/admin';
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { StatusPage, FeedbackButton, createStatusClient } from '@alliance-droid/status-feedback-system';
|
|
14
9
|
*/
|
|
15
|
-
export type { Project, System, Incident, IncidentUpdate, Feedback, StatusHistoryEntry, SystemStatus, IncidentStatus, IncidentSeverity, FeedbackStatus,
|
|
10
|
+
export type { Project, System, Incident, IncidentUpdate, Feedback, StatusHistoryEntry, SystemStatus, IncidentStatus, IncidentSeverity, FeedbackStatus, ApiResponse, } from './types.js';
|
|
11
|
+
export { createStatusClient } from './api.js';
|
|
12
|
+
export type { StatusClient, StatusClientConfig, StatusData, IncidentData, UptimeData } from './api.js';
|
|
16
13
|
export { default as StatusPage } from './components/StatusPage.svelte';
|
|
17
14
|
export { default as StatusBanner } from './components/StatusBanner.svelte';
|
|
18
15
|
export { default as FeedbackForm } from './components/FeedbackForm.svelte';
|
package/dist/index.js
CHANGED
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
* @alliance-droid/status-feedback-system
|
|
3
3
|
*
|
|
4
4
|
* Drop-in system status pages and user feedback forms for SvelteKit apps.
|
|
5
|
+
* All data is fetched from a remote status-feedback-admin backend via API.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
* import { StatusPage,
|
|
8
|
-
*
|
|
9
|
-
* Server exports:
|
|
10
|
-
* import { createStatusServer } from '@alliance-droid/status-feedback-system/server';
|
|
11
|
-
*
|
|
12
|
-
* Admin exports:
|
|
13
|
-
* import { AdminDashboard } from '@alliance-droid/status-feedback-system/admin';
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { StatusPage, FeedbackButton, createStatusClient } from '@alliance-droid/status-feedback-system';
|
|
14
9
|
*/
|
|
10
|
+
// ── API Client ──
|
|
11
|
+
export { createStatusClient } from './api.js';
|
|
15
12
|
// ── Client Components ──
|
|
16
13
|
export { default as StatusPage } from './components/StatusPage.svelte';
|
|
17
14
|
export { default as StatusBanner } from './components/StatusBanner.svelte';
|
package/dist/types.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ export type FeedbackStatus = 'new' | 'reviewed' | 'resolved' | 'archived';
|
|
|
8
8
|
export interface Project {
|
|
9
9
|
id: string;
|
|
10
10
|
name: string;
|
|
11
|
-
apiKey: string;
|
|
12
11
|
description: string | null;
|
|
13
12
|
allowedOrigins: string[];
|
|
14
13
|
createdAt: number;
|
|
@@ -61,11 +60,6 @@ export interface StatusHistoryEntry {
|
|
|
61
60
|
status: SystemStatus;
|
|
62
61
|
changedAt: number;
|
|
63
62
|
}
|
|
64
|
-
export interface StatusServerConfig {
|
|
65
|
-
database: {
|
|
66
|
-
connectionString: string;
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
63
|
export interface ApiResponse<T = unknown> {
|
|
70
64
|
success: boolean;
|
|
71
65
|
data?: T;
|