@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.
Files changed (52) hide show
  1. package/dist/api.d.ts +57 -0
  2. package/dist/api.js +76 -0
  3. package/dist/components/FeedbackButton.svelte +3 -3
  4. package/dist/components/FeedbackButton.svelte.d.ts +1 -1
  5. package/dist/components/FeedbackForm.svelte +3 -3
  6. package/dist/components/FeedbackForm.svelte.d.ts +1 -1
  7. package/dist/components/StatusBanner.svelte +36 -3
  8. package/dist/components/StatusBanner.svelte.d.ts +7 -1
  9. package/dist/components/StatusPage.svelte +150 -83
  10. package/dist/components/StatusPage.svelte.d.ts +10 -3
  11. package/dist/index.d.ts +6 -9
  12. package/dist/index.js +5 -8
  13. package/dist/types.d.ts +0 -6
  14. package/package.json +2 -13
  15. package/dist/components/admin/AdminDashboard.svelte +0 -274
  16. package/dist/components/admin/AdminDashboard.svelte.d.ts +0 -9
  17. package/dist/components/admin/ApiKeyManager.svelte +0 -68
  18. package/dist/components/admin/ApiKeyManager.svelte.d.ts +0 -7
  19. package/dist/components/admin/FeedbackList.svelte +0 -160
  20. package/dist/components/admin/FeedbackList.svelte.d.ts +0 -17
  21. package/dist/components/admin/IncidentForm.svelte +0 -135
  22. package/dist/components/admin/IncidentForm.svelte.d.ts +0 -14
  23. package/dist/components/admin/IncidentManager.svelte +0 -153
  24. package/dist/components/admin/IncidentManager.svelte.d.ts +0 -13
  25. package/dist/components/admin/ProjectForm.svelte +0 -106
  26. package/dist/components/admin/ProjectForm.svelte.d.ts +0 -13
  27. package/dist/components/admin/ProjectList.svelte +0 -57
  28. package/dist/components/admin/ProjectList.svelte.d.ts +0 -10
  29. package/dist/components/admin/StatusControl.svelte +0 -62
  30. package/dist/components/admin/StatusControl.svelte.d.ts +0 -8
  31. package/dist/components/admin/SystemManager.svelte +0 -119
  32. package/dist/components/admin/SystemManager.svelte.d.ts +0 -13
  33. package/dist/components/admin/index.d.ts +0 -9
  34. package/dist/components/admin/index.js +0 -9
  35. package/dist/server/db.d.ts +0 -17
  36. package/dist/server/db.js +0 -68
  37. package/dist/server/feedback.d.ts +0 -27
  38. package/dist/server/feedback.js +0 -124
  39. package/dist/server/handlers.d.ts +0 -41
  40. package/dist/server/handlers.js +0 -251
  41. package/dist/server/incidents.d.ts +0 -27
  42. package/dist/server/incidents.js +0 -180
  43. package/dist/server/index.d.ts +0 -101
  44. package/dist/server/index.js +0 -71
  45. package/dist/server/projects.d.ts +0 -45
  46. package/dist/server/projects.js +0 -156
  47. package/dist/server/schema.d.ts +0 -9
  48. package/dist/server/schema.js +0 -94
  49. package/dist/server/systems.d.ts +0 -20
  50. package/dist/server/systems.js +0 -146
  51. package/dist/server/types.d.ts +0 -4
  52. package/dist/server/types.js +0 -4
@@ -1,119 +0,0 @@
1
- <script lang="ts">
2
- import type { System, SystemStatus } from '../../types.js';
3
- import StatusIndicator from '../shared/StatusIndicator.svelte';
4
-
5
- interface Props {
6
- systems: System[];
7
- onAdd?: (data: { name: string; description: string }) => Promise<void>;
8
- onUpdateStatus?: (systemId: string, status: SystemStatus) => Promise<void>;
9
- onDelete?: (systemId: string) => Promise<void>;
10
- }
11
-
12
- let { systems, onAdd, onUpdateStatus, onDelete }: Props = $props();
13
-
14
- let showAddForm = $state(false);
15
- let newName = $state('');
16
- let newDescription = $state('');
17
- let adding = $state(false);
18
-
19
- const statuses: SystemStatus[] = ['operational', 'degraded', 'outage', 'maintenance'];
20
-
21
- async function handleAdd(e: Event) {
22
- e.preventDefault();
23
- if (!newName.trim()) return;
24
- adding = true;
25
- try {
26
- await onAdd?.({ name: newName.trim(), description: newDescription.trim() });
27
- newName = '';
28
- newDescription = '';
29
- showAddForm = false;
30
- } finally {
31
- adding = false;
32
- }
33
- }
34
-
35
- async function handleDelete(systemId: string, systemName: string) {
36
- if (!confirm(`Delete "${systemName}"? This cannot be undone.`)) return;
37
- await onDelete?.(systemId);
38
- }
39
- </script>
40
-
41
- <div class="space-y-4">
42
- <div class="flex items-center justify-between">
43
- <h3 class="text-base font-semibold text-foreground">
44
- <i class="fa-solid fa-server mr-1.5 text-foreground-tertiary"></i>
45
- Systems
46
- </h3>
47
- <button
48
- onclick={() => (showAddForm = !showAddForm)}
49
- class="rounded-md border border-border px-2.5 py-1.5 text-xs text-foreground-secondary transition-colors hover:bg-surface-hover"
50
- >
51
- <i class="fa-solid {showAddForm ? 'fa-minus' : 'fa-plus'} mr-1"></i>
52
- {showAddForm ? 'Cancel' : 'Add System'}
53
- </button>
54
- </div>
55
-
56
- {#if showAddForm}
57
- <form onsubmit={handleAdd} class="rounded-lg border border-border bg-surface p-3 space-y-3">
58
- <input
59
- type="text"
60
- bind:value={newName}
61
- placeholder="System name"
62
- required
63
- class="w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground"
64
- />
65
- <input
66
- type="text"
67
- bind:value={newDescription}
68
- placeholder="Description (optional)"
69
- class="w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground"
70
- />
71
- <button
72
- type="submit"
73
- disabled={adding || !newName.trim()}
74
- class="rounded-md bg-primary px-3 py-1.5 text-xs font-semibold text-primary-foreground disabled:opacity-50"
75
- >
76
- {adding ? 'Adding...' : 'Add System'}
77
- </button>
78
- </form>
79
- {/if}
80
-
81
- {#if systems.length === 0}
82
- <p class="text-sm text-foreground-tertiary">No systems defined yet.</p>
83
- {:else}
84
- <div class="space-y-2">
85
- {#each systems as system}
86
- <div class="rounded-lg border border-border bg-surface p-3">
87
- <div class="flex items-center justify-between">
88
- <div class="flex items-center gap-2">
89
- <StatusIndicator status={system.status} size="sm" />
90
- <span class="text-sm font-semibold text-foreground">{system.name}</span>
91
- </div>
92
- <button
93
- onclick={() => handleDelete(system.id, system.name)}
94
- class="text-xs text-foreground-tertiary transition-colors hover:text-red-500"
95
- aria-label="Delete system"
96
- >
97
- <i class="fa-solid fa-trash"></i>
98
- </button>
99
- </div>
100
- {#if system.description}
101
- <p class="mt-1 text-xs text-foreground-tertiary">{system.description}</p>
102
- {/if}
103
- <div class="mt-2 flex gap-1">
104
- {#each statuses as s}
105
- <button
106
- onclick={() => onUpdateStatus?.(system.id, s)}
107
- class="rounded px-2 py-1 text-xs transition-colors {system.status === s
108
- ? 'bg-primary text-primary-foreground'
109
- : 'border border-border text-foreground-secondary hover:bg-surface-hover'}"
110
- >
111
- {s}
112
- </button>
113
- {/each}
114
- </div>
115
- </div>
116
- {/each}
117
- </div>
118
- {/if}
119
- </div>
@@ -1,13 +0,0 @@
1
- import type { System, SystemStatus } from '../../types.js';
2
- interface Props {
3
- systems: System[];
4
- onAdd?: (data: {
5
- name: string;
6
- description: string;
7
- }) => Promise<void>;
8
- onUpdateStatus?: (systemId: string, status: SystemStatus) => Promise<void>;
9
- onDelete?: (systemId: string) => Promise<void>;
10
- }
11
- declare const SystemManager: import("svelte").Component<Props, {}, "">;
12
- type SystemManager = ReturnType<typeof SystemManager>;
13
- export default SystemManager;
@@ -1,9 +0,0 @@
1
- export { default as AdminDashboard } from './AdminDashboard.svelte';
2
- export { default as ProjectList } from './ProjectList.svelte';
3
- export { default as ProjectForm } from './ProjectForm.svelte';
4
- export { default as SystemManager } from './SystemManager.svelte';
5
- export { default as StatusControl } from './StatusControl.svelte';
6
- export { default as IncidentManager } from './IncidentManager.svelte';
7
- export { default as IncidentForm } from './IncidentForm.svelte';
8
- export { default as FeedbackList } from './FeedbackList.svelte';
9
- export { default as ApiKeyManager } from './ApiKeyManager.svelte';
@@ -1,9 +0,0 @@
1
- export { default as AdminDashboard } from './AdminDashboard.svelte';
2
- export { default as ProjectList } from './ProjectList.svelte';
3
- export { default as ProjectForm } from './ProjectForm.svelte';
4
- export { default as SystemManager } from './SystemManager.svelte';
5
- export { default as StatusControl } from './StatusControl.svelte';
6
- export { default as IncidentManager } from './IncidentManager.svelte';
7
- export { default as IncidentForm } from './IncidentForm.svelte';
8
- export { default as FeedbackList } from './FeedbackList.svelte';
9
- export { default as ApiKeyManager } from './ApiKeyManager.svelte';
@@ -1,17 +0,0 @@
1
- /**
2
- * Database connection management
3
- * Provides a singleton connection pool for MSSQL
4
- */
5
- import type { ConnectionPool } from 'mssql';
6
- /**
7
- * Get the database connection pool. Creates one if it doesn't exist.
8
- */
9
- export declare function getPool(connectionString: string): Promise<ConnectionPool>;
10
- /**
11
- * Initialize the database: connect and run migrations
12
- */
13
- export declare function initDatabase(connectionString: string): Promise<ConnectionPool>;
14
- /**
15
- * Close the database connection
16
- */
17
- export declare function closeDatabase(): Promise<void>;
package/dist/server/db.js DELETED
@@ -1,68 +0,0 @@
1
- /**
2
- * Database connection management
3
- * Provides a singleton connection pool for MSSQL
4
- */
5
- import sql from 'mssql';
6
- import { runMigrations } from './schema.js';
7
- let _pool = null;
8
- let _initialized = false;
9
- /**
10
- * Parse a connection string into mssql config
11
- */
12
- function parseConnectionString(connectionString) {
13
- const params = {};
14
- for (const part of connectionString.split(';')) {
15
- const [key, ...valueParts] = part.split('=');
16
- if (key && valueParts.length > 0) {
17
- params[key.trim().toLowerCase()] = valueParts.join('=').trim();
18
- }
19
- }
20
- return {
21
- server: params['server'] || params['data source'] || '',
22
- database: params['initial catalog'] || params['database'] || '',
23
- user: params['user id'] || params['uid'] || '',
24
- password: params['password'] || params['pwd'] || '',
25
- options: {
26
- encrypt: params['encrypt']?.toLowerCase() !== 'false',
27
- trustServerCertificate: params['trustservercertificate']?.toLowerCase() === 'true',
28
- },
29
- pool: {
30
- max: 10,
31
- min: 0,
32
- idleTimeoutMillis: 30000,
33
- },
34
- requestTimeout: parseInt(params['connection timeout'] || '30') * 1000,
35
- };
36
- }
37
- /**
38
- * Get the database connection pool. Creates one if it doesn't exist.
39
- */
40
- export async function getPool(connectionString) {
41
- if (_pool?.connected) {
42
- return _pool;
43
- }
44
- const config = parseConnectionString(connectionString);
45
- _pool = await sql.connect(config);
46
- return _pool;
47
- }
48
- /**
49
- * Initialize the database: connect and run migrations
50
- */
51
- export async function initDatabase(connectionString) {
52
- const pool = await getPool(connectionString);
53
- if (!_initialized) {
54
- await runMigrations(pool);
55
- _initialized = true;
56
- }
57
- return pool;
58
- }
59
- /**
60
- * Close the database connection
61
- */
62
- export async function closeDatabase() {
63
- if (_pool) {
64
- await _pool.close();
65
- _pool = null;
66
- _initialized = false;
67
- }
68
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Feedback CRUD
3
- */
4
- import type { ConnectionPool } from 'mssql';
5
- import type { Feedback, FeedbackStatus } from './types.js';
6
- export declare function submitFeedback(pool: ConnectionPool, projectId: string, data: {
7
- category?: string;
8
- message: string;
9
- email?: string;
10
- userAgent?: string;
11
- pageUrl?: string;
12
- metadata?: Record<string, unknown>;
13
- }): Promise<Feedback>;
14
- export declare function getFeedbackById(pool: ConnectionPool, id: string): Promise<Feedback | null>;
15
- export declare function getFeedbackByProject(pool: ConnectionPool, projectId: string, options?: {
16
- limit?: number;
17
- offset?: number;
18
- category?: string;
19
- status?: FeedbackStatus;
20
- sortBy?: 'created_at' | 'status';
21
- sortOrder?: 'ASC' | 'DESC';
22
- }): Promise<{
23
- items: Feedback[];
24
- total: number;
25
- }>;
26
- export declare function updateFeedbackStatus(pool: ConnectionPool, id: string, status: FeedbackStatus): Promise<Feedback | null>;
27
- export declare function deleteFeedback(pool: ConnectionPool, id: string): Promise<boolean>;
@@ -1,124 +0,0 @@
1
- /**
2
- * Feedback CRUD
3
- */
4
- import { nanoid } from 'nanoid';
5
- // ── CRUD ──
6
- export async function submitFeedback(pool, projectId, data) {
7
- const id = nanoid(16);
8
- const now = Date.now();
9
- await pool
10
- .request()
11
- .input('id', id)
12
- .input('projectId', projectId)
13
- .input('category', data.category || null)
14
- .input('message', data.message)
15
- .input('email', data.email || null)
16
- .input('userAgent', data.userAgent || null)
17
- .input('pageUrl', data.pageUrl || null)
18
- .input('metadata', data.metadata ? JSON.stringify(data.metadata) : null)
19
- .input('createdAt', now)
20
- .query(`
21
- INSERT INTO sf_feedback (id, project_id, category, message, email, user_agent, page_url, metadata, created_at)
22
- VALUES (@id, @projectId, @category, @message, @email, @userAgent, @pageUrl, @metadata, @createdAt)
23
- `);
24
- return {
25
- id,
26
- projectId,
27
- category: data.category || null,
28
- message: data.message,
29
- email: data.email || null,
30
- userAgent: data.userAgent || null,
31
- pageUrl: data.pageUrl || null,
32
- metadata: data.metadata || null,
33
- status: 'new',
34
- createdAt: now,
35
- };
36
- }
37
- export async function getFeedbackById(pool, id) {
38
- const result = await pool
39
- .request()
40
- .input('id', id)
41
- .query('SELECT * FROM sf_feedback WHERE id = @id');
42
- if (result.recordset.length === 0)
43
- return null;
44
- return mapFeedbackRow(result.recordset[0]);
45
- }
46
- export async function getFeedbackByProject(pool, projectId, options = {}) {
47
- const limit = options.limit ?? 50;
48
- const offset = options.offset ?? 0;
49
- const sortBy = options.sortBy ?? 'created_at';
50
- const sortOrder = options.sortOrder ?? 'DESC';
51
- // Build WHERE clause
52
- const conditions = ['project_id = @projectId'];
53
- if (options.category)
54
- conditions.push('category = @category');
55
- if (options.status)
56
- conditions.push('status = @status');
57
- const where = conditions.join(' AND ');
58
- // Get total count
59
- const countResult = await pool
60
- .request()
61
- .input('projectId', projectId)
62
- .input('category', options.category || null)
63
- .input('status', options.status || null)
64
- .query(`SELECT COUNT(*) as total FROM sf_feedback WHERE ${where}`);
65
- const total = countResult.recordset[0].total;
66
- // Get paginated results
67
- const result = await pool
68
- .request()
69
- .input('projectId', projectId)
70
- .input('category', options.category || null)
71
- .input('status', options.status || null)
72
- .input('limit', limit)
73
- .input('offset', offset)
74
- .query(`
75
- SELECT * FROM sf_feedback
76
- WHERE ${where}
77
- ORDER BY ${sortBy} ${sortOrder}
78
- OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY
79
- `);
80
- return {
81
- items: result.recordset.map(mapFeedbackRow),
82
- total,
83
- };
84
- }
85
- export async function updateFeedbackStatus(pool, id, status) {
86
- const existing = await getFeedbackById(pool, id);
87
- if (!existing)
88
- return null;
89
- await pool
90
- .request()
91
- .input('id', id)
92
- .input('status', status)
93
- .query('UPDATE sf_feedback SET status = @status WHERE id = @id');
94
- return { ...existing, status };
95
- }
96
- export async function deleteFeedback(pool, id) {
97
- await pool.request().input('id', id).query('DELETE FROM sf_feedback WHERE id = @id');
98
- return true;
99
- }
100
- // ── Helpers ──
101
- function mapFeedbackRow(row) {
102
- return {
103
- id: row.id,
104
- projectId: row.project_id,
105
- category: row.category || null,
106
- message: row.message,
107
- email: row.email || null,
108
- userAgent: row.user_agent || null,
109
- pageUrl: row.page_url || null,
110
- metadata: parseJsonObject(row.metadata),
111
- status: row.status,
112
- createdAt: row.created_at,
113
- };
114
- }
115
- function parseJsonObject(value) {
116
- if (!value)
117
- return null;
118
- try {
119
- return JSON.parse(value);
120
- }
121
- catch {
122
- return null;
123
- }
124
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Composable API route handlers for SvelteKit
3
- *
4
- * Usage in consuming app:
5
- * // src/routes/api/status/[...path]/+server.ts
6
- * import { statusServer } from './status';
7
- * const handlers = statusServer.createHandlers();
8
- * export const GET = handlers.GET;
9
- * export const POST = handlers.POST;
10
- * export const PATCH = handlers.PATCH;
11
- * export const DELETE = handlers.DELETE;
12
- */
13
- import type { ConnectionPool } from 'mssql';
14
- type RequestEvent = {
15
- request: Request;
16
- params: Record<string, string>;
17
- url: URL;
18
- };
19
- type Handler = (event: RequestEvent) => Promise<Response>;
20
- export interface RouteHandlers {
21
- GET: Handler;
22
- POST: Handler;
23
- PATCH: Handler;
24
- DELETE: Handler;
25
- }
26
- /**
27
- * Create composable route handlers
28
- *
29
- * Routes:
30
- * GET /systems — list systems for project
31
- * GET /systems/:id — get system details
32
- * GET /systems/:id/history — get status history (90 days)
33
- * GET /incidents — list incidents for project
34
- * GET /incidents/:id — get incident with updates
35
- * GET /feedback — list feedback (admin)
36
- * POST /feedback — submit feedback
37
- * PATCH /systems/:id/status — update system status (admin)
38
- * PATCH /feedback/:id/status — update feedback status (admin)
39
- */
40
- export declare function createRouteHandlers(pool: ConnectionPool): RouteHandlers;
41
- export {};