@happyvertical/smrt-projects 0.39.4 → 0.39.7

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 (83) hide show
  1. package/AGENTS.md +36 -1
  2. package/SERVICE_TIME_MIGRATION.md +23 -0
  3. package/dist/collections/DevelopmentRequestHistories.d.ts +7 -0
  4. package/dist/collections/DevelopmentRequestHistories.d.ts.map +1 -0
  5. package/dist/collections/DevelopmentRequests.d.ts +20 -0
  6. package/dist/collections/DevelopmentRequests.d.ts.map +1 -0
  7. package/dist/collections/ProjectIntegrationAudits.d.ts +7 -0
  8. package/dist/collections/ProjectIntegrationAudits.d.ts.map +1 -0
  9. package/dist/collections/ProjectIntegrations.d.ts +28 -0
  10. package/dist/collections/ProjectIntegrations.d.ts.map +1 -0
  11. package/dist/index.d.ts +13 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1673 -33
  14. package/dist/index.js.map +1 -1
  15. package/dist/managed-client.d.ts +13 -0
  16. package/dist/managed-client.d.ts.map +1 -0
  17. package/dist/manifest.json +4790 -1109
  18. package/dist/models/DevelopmentRequest.d.ts +35 -0
  19. package/dist/models/DevelopmentRequest.d.ts.map +1 -0
  20. package/dist/models/DevelopmentRequestHistory.d.ts +22 -0
  21. package/dist/models/DevelopmentRequestHistory.d.ts.map +1 -0
  22. package/dist/models/ProjectIntegration.d.ts +24 -0
  23. package/dist/models/ProjectIntegration.d.ts.map +1 -0
  24. package/dist/models/ProjectIntegrationAudit.d.ts +16 -0
  25. package/dist/models/ProjectIntegrationAudit.d.ts.map +1 -0
  26. package/dist/models/delivery-control-plane.d.ts +83 -0
  27. package/dist/models/delivery-control-plane.d.ts.map +1 -0
  28. package/dist/models/service-evidence.d.ts +79 -0
  29. package/dist/models/service-evidence.d.ts.map +1 -0
  30. package/dist/services/assistance-request-service.d.ts +45 -0
  31. package/dist/services/assistance-request-service.d.ts.map +1 -0
  32. package/dist/services/delivery-service.d.ts +55 -0
  33. package/dist/services/delivery-service.d.ts.map +1 -0
  34. package/dist/services/development-request-service.d.ts +61 -0
  35. package/dist/services/development-request-service.d.ts.map +1 -0
  36. package/dist/services/index.d.ts +6 -0
  37. package/dist/services/index.d.ts.map +1 -0
  38. package/dist/services/service-evidence-service.d.ts +48 -0
  39. package/dist/services/service-evidence-service.d.ts.map +1 -0
  40. package/dist/services/subscription-commercial-resolver.d.ts +17 -0
  41. package/dist/services/subscription-commercial-resolver.d.ts.map +1 -0
  42. package/dist/smrt-knowledge.json +1823 -148
  43. package/dist/svelte/AssistanceLauncher.svelte +96 -0
  44. package/dist/svelte/AssistanceLauncher.svelte.d.ts +12 -0
  45. package/dist/svelte/AssistanceLauncher.svelte.d.ts.map +1 -0
  46. package/dist/svelte/DeliveryStatus.svelte +71 -0
  47. package/dist/svelte/DeliveryStatus.svelte.d.ts +8 -0
  48. package/dist/svelte/DeliveryStatus.svelte.d.ts.map +1 -0
  49. package/dist/svelte/DevelopmentBoard.svelte +103 -0
  50. package/dist/svelte/DevelopmentBoard.svelte.d.ts +10 -0
  51. package/dist/svelte/DevelopmentBoard.svelte.d.ts.map +1 -0
  52. package/dist/svelte/DevelopmentRequestDetail.svelte +92 -0
  53. package/dist/svelte/DevelopmentRequestDetail.svelte.d.ts +9 -0
  54. package/dist/svelte/DevelopmentRequestDetail.svelte.d.ts.map +1 -0
  55. package/dist/svelte/PreviewApprovalPanel.svelte +63 -0
  56. package/dist/svelte/PreviewApprovalPanel.svelte.d.ts +10 -0
  57. package/dist/svelte/PreviewApprovalPanel.svelte.d.ts.map +1 -0
  58. package/dist/svelte/ServiceEvidenceList.svelte +103 -0
  59. package/dist/svelte/ServiceEvidenceList.svelte.d.ts +8 -0
  60. package/dist/svelte/ServiceEvidenceList.svelte.d.ts.map +1 -0
  61. package/dist/svelte/components/DevelopmentRequestForm.svelte +70 -0
  62. package/dist/svelte/components/DevelopmentRequestForm.svelte.d.ts +19 -0
  63. package/dist/svelte/components/DevelopmentRequestForm.svelte.d.ts.map +1 -0
  64. package/dist/svelte/components/DevelopmentRequestList.svelte +218 -0
  65. package/dist/svelte/components/DevelopmentRequestList.svelte.d.ts +22 -0
  66. package/dist/svelte/components/DevelopmentRequestList.svelte.d.ts.map +1 -0
  67. package/dist/svelte/components/__tests__/DeliverySurfaces.test.js +98 -0
  68. package/dist/svelte/components/__tests__/DevelopmentRequest.test.js +52 -0
  69. package/dist/svelte/delivery-types.d.ts +36 -0
  70. package/dist/svelte/delivery-types.d.ts.map +1 -0
  71. package/dist/svelte/delivery-types.js +1 -0
  72. package/dist/svelte/i18n.d.ts +30 -0
  73. package/dist/svelte/i18n.d.ts.map +1 -1
  74. package/dist/svelte/i18n.js +30 -0
  75. package/dist/svelte/index.d.ts +18 -1
  76. package/dist/svelte/index.d.ts.map +1 -1
  77. package/dist/svelte/index.js +18 -1
  78. package/dist/types.d.ts +79 -0
  79. package/dist/types.d.ts.map +1 -1
  80. package/dist/ui.d.ts.map +1 -1
  81. package/dist/ui.js +106 -10
  82. package/dist/ui.js.map +1 -1
  83. package/package.json +11 -9
@@ -0,0 +1,70 @@
1
+ <script lang="ts">
2
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
+ import { M } from '../i18n.js';
4
+
5
+ export interface DevelopmentRequestSubmission {
6
+ type: string;
7
+ description: string;
8
+ evidence: Array<{ url: string; label?: string }>;
9
+ visibility: 'requester' | 'workspace' | 'public';
10
+ origin: string;
11
+ discussion: string;
12
+ }
13
+ export interface Props {
14
+ onsubmit: (request: DevelopmentRequestSubmission) => void | Promise<void>;
15
+ disabled?: boolean;
16
+ }
17
+ let { onsubmit, disabled = false }: Props = $props();
18
+ const { t } = useI18n();
19
+ let type = $state('feature');
20
+ let description = $state('');
21
+ let discussion = $state('');
22
+ let evidenceUrl = $state('');
23
+
24
+ function submit(event: SubmitEvent) {
25
+ event.preventDefault();
26
+ if (!description.trim()) return;
27
+ void onsubmit({
28
+ type,
29
+ description: description.trim(),
30
+ evidence: evidenceUrl.trim() ? [{ url: evidenceUrl.trim() }] : [],
31
+ visibility: 'requester',
32
+ origin: 'managed-app',
33
+ discussion: discussion.trim(),
34
+ });
35
+ }
36
+ </script>
37
+
38
+ <!-- raw-primitive-allow: provider-free embeddable request form boundary -->
39
+ <form onsubmit={submit} class="request-form">
40
+ <label>{t(M['projects.development_request.type'])}
41
+ <!-- raw-primitive-allow: package has no provider-free select primitive -->
42
+ <select bind:value={type} disabled={disabled}>
43
+ <option value="feature">{t(M['projects.development_request.feature'])}</option>
44
+ <option value="bug">{t(M['projects.development_request.bug'])}</option>
45
+ <option value="support">{t(M['projects.development_request.support'])}</option>
46
+ </select>
47
+ </label>
48
+ <label>{t(M['projects.development_request.description'])}
49
+ <!-- raw-primitive-allow: reusable embeddable form keeps native controls -->
50
+ <textarea bind:value={description} required disabled={disabled}></textarea>
51
+ </label>
52
+ <label>{t(M['projects.development_request.evidence_url'])}
53
+ <!-- raw-primitive-allow: URL input has no provider-free primitive -->
54
+ <input type="url" bind:value={evidenceUrl} disabled={disabled} />
55
+ </label>
56
+ <label>{t(M['projects.development_request.discussion'])}
57
+ <!-- raw-primitive-allow: reusable embeddable form keeps native controls -->
58
+ <textarea bind:value={discussion} disabled={disabled}></textarea>
59
+ </label>
60
+ <!-- raw-primitive-allow: submit button is required in provider-free package surface -->
61
+ <button type="submit" disabled={disabled || !description.trim()}>{t(M['projects.development_request.submit'])}</button>
62
+ </form>
63
+
64
+ <style>
65
+ .request-form { display: grid; gap: var(--smrt-spacing-4); }
66
+ label { display: grid; gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); }
67
+ input, select, textarea { padding: var(--smrt-spacing-2); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-medium); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); }
68
+ textarea { min-height: 6rem; }
69
+ button { padding: var(--smrt-spacing-2) var(--smrt-spacing-4); border: 0; border-radius: var(--smrt-radius-medium); background: var(--smrt-color-primary); color: var(--smrt-color-on-primary); }
70
+ </style>
@@ -0,0 +1,19 @@
1
+ export interface DevelopmentRequestSubmission {
2
+ type: string;
3
+ description: string;
4
+ evidence: Array<{
5
+ url: string;
6
+ label?: string;
7
+ }>;
8
+ visibility: 'requester' | 'workspace' | 'public';
9
+ origin: string;
10
+ discussion: string;
11
+ }
12
+ export interface Props {
13
+ onsubmit: (request: DevelopmentRequestSubmission) => void | Promise<void>;
14
+ disabled?: boolean;
15
+ }
16
+ declare const DevelopmentRequestForm: import("svelte").Component<Props, {}, "">;
17
+ type DevelopmentRequestForm = ReturnType<typeof DevelopmentRequestForm>;
18
+ export default DevelopmentRequestForm;
19
+ //# sourceMappingURL=DevelopmentRequestForm.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DevelopmentRequestForm.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/DevelopmentRequestForm.svelte.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,UAAU,EAAE,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AACD,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,4BAA4B,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAyDD,QAAA,MAAM,sBAAsB,2CAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,218 @@
1
+ <script lang="ts">
2
+ import type {
3
+ DevelopmentRequestEvidence,
4
+ DevelopmentRequestOrigin,
5
+ DevelopmentRequestStatus,
6
+ DevelopmentRequestType,
7
+ DevelopmentRequestVisibility,
8
+ } from '../../types.js';
9
+
10
+ export interface DevelopmentRequestListItem {
11
+ id: string;
12
+ requesterId: string;
13
+ type: DevelopmentRequestType;
14
+ description: string;
15
+ status: DevelopmentRequestStatus;
16
+ visibility?: DevelopmentRequestVisibility;
17
+ origin?: DevelopmentRequestOrigin;
18
+ discussion?: string;
19
+ evidence?: DevelopmentRequestEvidence[];
20
+ createdAt?: Date | string;
21
+ }
22
+
23
+ export interface Props {
24
+ requesterId?: string;
25
+ requests: DevelopmentRequestListItem[];
26
+ emptyMessage?: string;
27
+ }
28
+
29
+ let {
30
+ requesterId,
31
+ requests,
32
+ emptyMessage = 'No development requests yet',
33
+ }: Props = $props();
34
+
35
+ const visibleRequests = $derived(
36
+ requesterId
37
+ ? requests.filter((request) => request.requesterId === requesterId)
38
+ : requests,
39
+ );
40
+
41
+ function formatWhen(value?: Date | string): string {
42
+ if (!value) return '';
43
+ const date = value instanceof Date ? value : new Date(value);
44
+ if (Number.isNaN(date.getTime())) return '';
45
+ return date.toLocaleDateString('en-CA', {
46
+ year: 'numeric',
47
+ month: 'short',
48
+ day: 'numeric',
49
+ });
50
+ }
51
+ </script>
52
+
53
+ {#if visibleRequests.length === 0}
54
+ <div class="empty-state">
55
+ <p>{emptyMessage}</p>
56
+ </div>
57
+ {:else}
58
+ <div class="request-list">
59
+ {#each visibleRequests as request (request.id)}
60
+ <article class="request-card">
61
+ <header>
62
+ <div>
63
+ <p class="eyebrow">{request.type}</p>
64
+ <h3>{request.description}</h3>
65
+ </div>
66
+ <span class="status status-{request.status}">{request.status}</span>
67
+ </header>
68
+
69
+ <dl class="meta">
70
+ <div>
71
+ <dt>Visibility</dt>
72
+ <dd>{request.visibility ?? 'requester'}</dd>
73
+ </div>
74
+ {#if request.origin}
75
+ <div>
76
+ <dt>Origin</dt>
77
+ <dd>{request.origin}</dd>
78
+ </div>
79
+ {/if}
80
+ {#if formatWhen(request.createdAt)}
81
+ <div>
82
+ <dt>Submitted</dt>
83
+ <dd>{formatWhen(request.createdAt)}</dd>
84
+ </div>
85
+ {/if}
86
+ <div>
87
+ <dt>Evidence</dt>
88
+ <dd>{request.evidence?.length ?? 0}</dd>
89
+ </div>
90
+ </dl>
91
+
92
+ {#if request.discussion}
93
+ <p class="discussion">{request.discussion}</p>
94
+ {/if}
95
+
96
+ {#if request.evidence?.length}
97
+ <ul class="evidence-list">
98
+ {#each request.evidence as item, index (item.url + index)}
99
+ <li>
100
+ <a href={item.url}>{item.label ?? item.url}</a>
101
+ </li>
102
+ {/each}
103
+ </ul>
104
+ {/if}
105
+ </article>
106
+ {/each}
107
+ </div>
108
+ {/if}
109
+
110
+ <style>
111
+ .empty-state {
112
+ padding: 2rem 1rem;
113
+ text-align: center;
114
+ color: var(--smrt-color-on-surface-variant, #5f6472);
115
+ }
116
+
117
+ .request-list {
118
+ display: grid;
119
+ gap: 1rem;
120
+ }
121
+
122
+ .request-card {
123
+ padding: 1rem;
124
+ border: 1px solid var(--smrt-color-outline-variant, #d5d8e1);
125
+ border-radius: var(--smrt-radius-large, 16px);
126
+ background: linear-gradient(
127
+ 180deg,
128
+ var(--smrt-color-surface, #fff),
129
+ var(--smrt-color-surface-container-lowest, #f8f9fc)
130
+ );
131
+ }
132
+
133
+ header {
134
+ display: flex;
135
+ justify-content: space-between;
136
+ gap: 1rem;
137
+ align-items: start;
138
+ }
139
+
140
+ h3 {
141
+ margin: 0.15rem 0 0;
142
+ font-size: 1rem;
143
+ }
144
+
145
+ .eyebrow {
146
+ margin: 0;
147
+ text-transform: uppercase;
148
+ letter-spacing: 0.08em;
149
+ font-size: 0.75rem;
150
+ color: var(--smrt-color-on-surface-variant, #5f6472);
151
+ }
152
+
153
+ .status {
154
+ padding: 0.3rem 0.65rem;
155
+ border-radius: 999px;
156
+ background: var(--smrt-color-primary-container);
157
+ color: var(--smrt-color-on-primary-container);
158
+ font-size: 0.82rem;
159
+ white-space: nowrap;
160
+ }
161
+
162
+ .status-completed {
163
+ background: var(--smrt-color-success-container);
164
+ color: var(--smrt-color-on-success-container);
165
+ }
166
+
167
+ .status-declined {
168
+ background: var(--smrt-color-error-container);
169
+ color: var(--smrt-color-on-error-container);
170
+ }
171
+
172
+ .status-in_progress {
173
+ background: var(--smrt-color-warning-container);
174
+ color: var(--smrt-color-on-warning-container);
175
+ }
176
+
177
+ .meta {
178
+ display: grid;
179
+ gap: 0.75rem;
180
+ grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
181
+ margin: 1rem 0 0;
182
+ }
183
+
184
+ .meta div {
185
+ margin: 0;
186
+ }
187
+
188
+ dt {
189
+ font-size: 0.75rem;
190
+ text-transform: uppercase;
191
+ letter-spacing: 0.06em;
192
+ color: var(--smrt-color-on-surface-variant, #5f6472);
193
+ }
194
+
195
+ dd {
196
+ margin: 0.15rem 0 0;
197
+ }
198
+
199
+ .discussion {
200
+ margin: 1rem 0 0;
201
+ color: var(--smrt-color-on-surface-variant, #3d4454);
202
+ }
203
+
204
+ .evidence-list {
205
+ margin: 0.85rem 0 0;
206
+ padding-left: 1.2rem;
207
+ }
208
+
209
+ a {
210
+ color: var(--smrt-color-primary, #1f6feb);
211
+ }
212
+
213
+ @media (max-width: 700px) {
214
+ header {
215
+ flex-direction: column;
216
+ }
217
+ }
218
+ </style>
@@ -0,0 +1,22 @@
1
+ import type { DevelopmentRequestEvidence, DevelopmentRequestOrigin, DevelopmentRequestStatus, DevelopmentRequestType, DevelopmentRequestVisibility } from '../../types.js';
2
+ export interface DevelopmentRequestListItem {
3
+ id: string;
4
+ requesterId: string;
5
+ type: DevelopmentRequestType;
6
+ description: string;
7
+ status: DevelopmentRequestStatus;
8
+ visibility?: DevelopmentRequestVisibility;
9
+ origin?: DevelopmentRequestOrigin;
10
+ discussion?: string;
11
+ evidence?: DevelopmentRequestEvidence[];
12
+ createdAt?: Date | string;
13
+ }
14
+ export interface Props {
15
+ requesterId?: string;
16
+ requests: DevelopmentRequestListItem[];
17
+ emptyMessage?: string;
18
+ }
19
+ declare const DevelopmentRequestList: import("svelte").Component<Props, {}, "">;
20
+ type DevelopmentRequestList = ReturnType<typeof DevelopmentRequestList>;
21
+ export default DevelopmentRequestList;
22
+ //# sourceMappingURL=DevelopmentRequestList.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DevelopmentRequestList.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/DevelopmentRequestList.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,EACtB,4BAA4B,EAC7B,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,sBAAsB,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,wBAAwB,CAAC;IACjC,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACxC,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,KAAK;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,0BAA0B,EAAE,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA2FD,QAAA,MAAM,sBAAsB,2CAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,98 @@
1
+ // @vitest-environment jsdom
2
+ import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import AssistanceLauncher from '../../AssistanceLauncher.svelte';
5
+ import DevelopmentBoard from '../../DevelopmentBoard.svelte';
6
+ import DevelopmentRequestDetail from '../../DevelopmentRequestDetail.svelte';
7
+ import PreviewApprovalPanel from '../../PreviewApprovalPanel.svelte';
8
+ describe('managed application delivery surfaces', () => {
9
+ it('submits Assistance intake without dropping requester context or evidence', async () => {
10
+ const onsubmit = vi.fn();
11
+ const { container } = render(AssistanceLauncher, {
12
+ props: {
13
+ requesterId: 'requester-1',
14
+ applicationContext: { route: '/invoices' },
15
+ evidence: [{ url: 'https://evidence.invalid/screenshot' }],
16
+ onsubmit,
17
+ },
18
+ });
19
+ await userEvent.type(screen.getByLabelText('Subject'), 'Export failed');
20
+ await userEvent.type(screen.getByLabelText('What do you need?'), 'The CSV export returns an error.');
21
+ await userEvent.click(screen.getByRole('button', { name: 'Send request' }));
22
+ expect(onsubmit).toHaveBeenCalledWith({
23
+ requesterId: 'requester-1',
24
+ subject: 'Export failed',
25
+ applicationContext: { route: '/invoices' },
26
+ conversation: [{ body: 'The CSV export returns an error.' }],
27
+ evidence: [{ url: 'https://evidence.invalid/screenshot' }],
28
+ });
29
+ await expectNoA11yViolations(container);
30
+ });
31
+ it('renders projected board work and selects a request', async () => {
32
+ const onselect = vi.fn();
33
+ const { container } = render(DevelopmentBoard, {
34
+ props: {
35
+ requests: [
36
+ {
37
+ id: 'r1',
38
+ description: 'Add CSV export',
39
+ type: 'feature',
40
+ status: 'planned',
41
+ },
42
+ ],
43
+ onselect,
44
+ },
45
+ });
46
+ await userEvent.click(screen.getByRole('button', { name: /Add CSV export/i }));
47
+ expect(onselect).toHaveBeenCalledWith(expect.objectContaining({ id: 'r1' }));
48
+ await expectNoA11yViolations(container);
49
+ });
50
+ it('renders an accessible empty state and provider-neutral issue detail', async () => {
51
+ const { container, rerender } = render(DevelopmentBoard);
52
+ expect(screen.getByText('No visible development work')).toBeVisible();
53
+ await expectNoA11yViolations(container);
54
+ await rerender({ requests: [] });
55
+ const detail = render(DevelopmentRequestDetail, {
56
+ props: {
57
+ request: {
58
+ id: 'r2',
59
+ title: 'Export invoices',
60
+ description: 'Add an invoice export.',
61
+ type: 'feature',
62
+ status: 'planned',
63
+ visibility: 'workspace',
64
+ requesterLabel: 'Client owner',
65
+ },
66
+ events: [
67
+ {
68
+ id: 'e1',
69
+ sequence: 1,
70
+ type: 'pull_request',
71
+ label: 'Pull request opened',
72
+ occurredAt: '2026-07-11T12:00:00Z',
73
+ },
74
+ ],
75
+ },
76
+ });
77
+ expect(screen.getByRole('heading', { name: 'Export invoices' })).toBeVisible();
78
+ expect(screen.getByText('Pull request opened')).toBeVisible();
79
+ await expectNoA11yViolations(detail.container);
80
+ });
81
+ it('allows one preview decision and hides actions after completion', async () => {
82
+ const ondecide = vi.fn();
83
+ const { container, rerender } = render(PreviewApprovalPanel, {
84
+ props: {
85
+ preview: { id: 'a1', previewId: 'pv1', status: 'pending' },
86
+ ondecide,
87
+ },
88
+ });
89
+ await userEvent.click(screen.getByRole('button', { name: 'Approve preview' }));
90
+ expect(ondecide).toHaveBeenCalledWith(true);
91
+ await rerender({
92
+ preview: { id: 'a1', previewId: 'pv1', status: 'approved' },
93
+ ondecide,
94
+ });
95
+ expect(screen.queryByRole('button', { name: 'Approve preview' })).not.toBeInTheDocument();
96
+ await expectNoA11yViolations(container);
97
+ });
98
+ });
@@ -0,0 +1,52 @@
1
+ // @vitest-environment jsdom
2
+ import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import DevelopmentRequestForm from '../DevelopmentRequestForm.svelte';
5
+ import DevelopmentRequestList from '../DevelopmentRequestList.svelte';
6
+ describe('development request components', () => {
7
+ it('submits a request through the browser-safe callback surface', async () => {
8
+ const onsubmit = vi.fn();
9
+ render(DevelopmentRequestForm, { props: { onsubmit } });
10
+ await userEvent.selectOptions(screen.getByLabelText('Request type'), 'bug');
11
+ await userEvent.type(screen.getByLabelText('Description'), 'The save button is broken');
12
+ await userEvent.click(screen.getByRole('button', { name: 'Submit request' }));
13
+ expect(onsubmit).toHaveBeenCalledWith(expect.objectContaining({
14
+ type: 'bug',
15
+ description: 'The save button is broken',
16
+ visibility: 'requester',
17
+ origin: 'managed-app',
18
+ }));
19
+ });
20
+ it('renders requester-scoped status without exposing another requester', () => {
21
+ render(DevelopmentRequestList, {
22
+ props: {
23
+ requesterId: 'user-1',
24
+ requests: [
25
+ {
26
+ id: 'r1',
27
+ requesterId: 'user-1',
28
+ type: 'feature',
29
+ description: 'Dark mode',
30
+ status: 'planned',
31
+ },
32
+ {
33
+ id: 'r2',
34
+ requesterId: 'user-2',
35
+ type: 'bug',
36
+ description: 'Private bug',
37
+ status: 'submitted',
38
+ },
39
+ ],
40
+ },
41
+ });
42
+ expect(screen.getByText('Dark mode')).toBeInTheDocument();
43
+ expect(screen.getByText('planned')).toBeInTheDocument();
44
+ expect(screen.queryByText('Private bug')).not.toBeInTheDocument();
45
+ });
46
+ it('is accessible', async () => {
47
+ const form = render(DevelopmentRequestForm, {
48
+ props: { onsubmit: vi.fn() },
49
+ });
50
+ await expectNoA11yViolations(form.container);
51
+ });
52
+ });
@@ -0,0 +1,36 @@
1
+ export interface DevelopmentRequestView {
2
+ id: string;
3
+ title?: string;
4
+ description: string;
5
+ type: string;
6
+ status: string;
7
+ deliveryStatus?: string;
8
+ visibility?: string;
9
+ requesterLabel?: string;
10
+ submittedAt?: Date | string;
11
+ }
12
+ export interface DeliveryEventView {
13
+ id: string;
14
+ sequence: number;
15
+ type: string;
16
+ label?: string;
17
+ occurredAt: Date | string;
18
+ detail?: string;
19
+ }
20
+ export interface PreviewApprovalView {
21
+ id: string;
22
+ previewId: string;
23
+ previewUrl?: string;
24
+ status: string;
25
+ }
26
+ export interface ServiceEvidenceView {
27
+ id: string;
28
+ context: string;
29
+ participant: string;
30
+ durationSeconds: number;
31
+ status: string;
32
+ chargeAmount?: number;
33
+ compensationAmount?: number;
34
+ currency?: string;
35
+ }
36
+ //# sourceMappingURL=delivery-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivery-types.d.ts","sourceRoot":"","sources":["../../src/svelte/delivery-types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CAC7B;AACD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,IAAI,GAAG,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AACD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,34 @@
1
1
  export declare const M: {
2
+ readonly 'projects.development_request.type': "projects.development_request.type";
3
+ readonly 'projects.development_request.feature': "projects.development_request.feature";
4
+ readonly 'projects.development_request.bug': "projects.development_request.bug";
5
+ readonly 'projects.development_request.support': "projects.development_request.support";
6
+ readonly 'projects.development_request.description': "projects.development_request.description";
7
+ readonly 'projects.development_request.evidence_url': "projects.development_request.evidence_url";
8
+ readonly 'projects.development_request.discussion': "projects.development_request.discussion";
9
+ readonly 'projects.development_request.submit': "projects.development_request.submit";
10
+ readonly 'projects.development_request.empty': "projects.development_request.empty";
11
+ readonly 'projects.development_request.list_aria': "projects.development_request.list_aria";
12
+ readonly 'projects.development_board.aria': "projects.development_board.aria";
13
+ readonly 'projects.development_board.empty': "projects.development_board.empty";
14
+ readonly 'projects.development_request.detail_aria': "projects.development_request.detail_aria";
15
+ readonly 'projects.development_request.status': "projects.development_request.status";
16
+ readonly 'projects.development_request.visibility': "projects.development_request.visibility";
17
+ readonly 'projects.development_request.requester': "projects.development_request.requester";
18
+ readonly 'projects.delivery_status.aria': "projects.delivery_status.aria";
19
+ readonly 'projects.preview.label': "projects.preview.label";
20
+ readonly 'projects.preview.open': "projects.preview.open";
21
+ readonly 'projects.preview.reject': "projects.preview.reject";
22
+ readonly 'projects.preview.approve': "projects.preview.approve";
23
+ readonly 'projects.assistance.title': "projects.assistance.title";
24
+ readonly 'projects.assistance.description': "projects.assistance.description";
25
+ readonly 'projects.assistance.subject': "projects.assistance.subject";
26
+ readonly 'projects.assistance.need': "projects.assistance.need";
27
+ readonly 'projects.assistance.submit': "projects.assistance.submit";
28
+ readonly 'projects.service_evidence.aria': "projects.service_evidence.aria";
29
+ readonly 'projects.service_evidence.client': "projects.service_evidence.client";
30
+ readonly 'projects.service_evidence.provider': "projects.service_evidence.provider";
31
+ readonly 'projects.service_evidence.margin': "projects.service_evidence.margin";
2
32
  readonly 'projects.approval_actions.approved_message': "projects.approval_actions.approved_message";
3
33
  readonly 'projects.approval_actions.rejected_message': "projects.approval_actions.rejected_message";
4
34
  readonly 'projects.bulk_actions.approve_all': "projects.bulk_actions.approve_all";
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;CAiBZ,CAAC"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDZ,CAAC"}
@@ -1,5 +1,35 @@
1
1
  import { defineMessages } from '@happyvertical/smrt-ui/i18n';
2
2
  export const M = defineMessages({
3
+ 'projects.development_request.type': 'Request type',
4
+ 'projects.development_request.feature': 'Feature',
5
+ 'projects.development_request.bug': 'Bug',
6
+ 'projects.development_request.support': 'Support',
7
+ 'projects.development_request.description': 'Description',
8
+ 'projects.development_request.evidence_url': 'Evidence URL',
9
+ 'projects.development_request.discussion': 'Discussion',
10
+ 'projects.development_request.submit': 'Submit request',
11
+ 'projects.development_request.empty': 'No development requests',
12
+ 'projects.development_request.list_aria': 'Development requests',
13
+ 'projects.development_board.aria': 'Development board',
14
+ 'projects.development_board.empty': 'No visible development work',
15
+ 'projects.development_request.detail_aria': 'Development request details',
16
+ 'projects.development_request.status': 'Status',
17
+ 'projects.development_request.visibility': 'Visibility',
18
+ 'projects.development_request.requester': 'Requester',
19
+ 'projects.delivery_status.aria': 'Delivery status',
20
+ 'projects.preview.label': 'Preview {id}',
21
+ 'projects.preview.open': 'Open preview',
22
+ 'projects.preview.reject': 'Reject',
23
+ 'projects.preview.approve': 'Approve preview',
24
+ 'projects.assistance.title': 'Help & requests',
25
+ 'projects.assistance.description': 'Ask for support, report a defect, or propose a change in one place.',
26
+ 'projects.assistance.subject': 'Subject',
27
+ 'projects.assistance.need': 'What do you need?',
28
+ 'projects.assistance.submit': 'Send request',
29
+ 'projects.service_evidence.aria': 'Service evidence',
30
+ 'projects.service_evidence.client': 'Client',
31
+ 'projects.service_evidence.provider': 'Provider',
32
+ 'projects.service_evidence.margin': 'Margin',
3
33
  'projects.approval_actions.approved_message': 'This entry has been approved',
4
34
  'projects.approval_actions.rejected_message': 'This entry was rejected',
5
35
  'projects.bulk_actions.approve_all': 'Approve All',
@@ -7,20 +7,37 @@
7
7
  * @packageDocumentation
8
8
  */
9
9
  import type { ComponentProps } from 'svelte';
10
+ import AssistanceLauncher from './AssistanceLauncher.svelte';
10
11
  import ApprovalActions from './components/ApprovalActions.svelte';
11
12
  import BulkActions from './components/BulkActions.svelte';
13
+ import DevelopmentRequestForm from './components/DevelopmentRequestForm.svelte';
14
+ import DevelopmentRequestList from './components/DevelopmentRequestList.svelte';
12
15
  import DurationDisplay from './components/DurationDisplay.svelte';
13
16
  import RejectDialog from './components/RejectDialog.svelte';
14
17
  import TimeEntryCard from './components/TimeEntryCard.svelte';
15
18
  import TimeEntryList from './components/TimeEntryList.svelte';
16
19
  import TimeSummary from './components/TimeSummary.svelte';
17
- export { ApprovalActions, BulkActions, DurationDisplay, RejectDialog, TimeEntryCard, TimeEntryList, TimeSummary, };
20
+ import DeliveryStatus from './DeliveryStatus.svelte';
21
+ import DevelopmentBoard from './DevelopmentBoard.svelte';
22
+ import DevelopmentRequestDetail from './DevelopmentRequestDetail.svelte';
23
+ import PreviewApprovalPanel from './PreviewApprovalPanel.svelte';
24
+ import ServiceEvidenceList from './ServiceEvidenceList.svelte';
25
+ export { ApprovalActions, AssistanceLauncher, BulkActions, DeliveryStatus, DevelopmentBoard, DevelopmentRequestDetail, DevelopmentRequestForm, DevelopmentRequestList, DurationDisplay, PreviewApprovalPanel, RejectDialog, ServiceEvidenceList, TimeEntryCard, TimeEntryList, TimeSummary, };
18
26
  export type ApprovalActionsProps = ComponentProps<typeof ApprovalActions>;
27
+ export type AssistanceLauncherProps = ComponentProps<typeof AssistanceLauncher>;
19
28
  export type BulkActionsProps = ComponentProps<typeof BulkActions>;
29
+ export type DeliveryStatusProps = ComponentProps<typeof DeliveryStatus>;
30
+ export type DevelopmentBoardProps = ComponentProps<typeof DevelopmentBoard>;
31
+ export type DevelopmentRequestFormProps = ComponentProps<typeof DevelopmentRequestForm>;
32
+ export type DevelopmentRequestDetailProps = ComponentProps<typeof DevelopmentRequestDetail>;
33
+ export type DevelopmentRequestListProps = ComponentProps<typeof DevelopmentRequestList>;
20
34
  export type DurationDisplayProps = ComponentProps<typeof DurationDisplay>;
21
35
  export type RejectDialogProps = ComponentProps<typeof RejectDialog>;
36
+ export type PreviewApprovalPanelProps = ComponentProps<typeof PreviewApprovalPanel>;
37
+ export type ServiceEvidenceListProps = ComponentProps<typeof ServiceEvidenceList>;
22
38
  export type TimeEntryCardProps = ComponentProps<typeof TimeEntryCard>;
23
39
  export type TimeEntryListProps = ComponentProps<typeof TimeEntryList>;
24
40
  export type TimeSummaryProps = ComponentProps<typeof TimeSummary>;
41
+ export * from './delivery-types.js';
25
42
  export { type ApprovalStatus, type Currency, formatCurrency, formatDate, formatHours, formatHoursHHMM, statusColors, type TimeEntry, type TimeEntryStatus, } from './utils.js';
26
43
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAI7C,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAC1D,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAG1D,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,YAAY,EACZ,aAAa,EACb,aAAa,EACb,WAAW,GACZ,CAAC;AAGF,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1E,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAClE,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1E,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC;AACpE,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAGlE,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,cAAc,EACd,UAAU,EACV,WAAW,EACX,eAAe,EACf,YAAY,EACZ,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,kBAAkB,MAAM,6BAA6B,CAAC;AAE7D,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAC1D,OAAO,sBAAsB,MAAM,4CAA4C,CAAC;AAChF,OAAO,sBAAsB,MAAM,4CAA4C,CAAC;AAChF,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAC1D,OAAO,cAAc,MAAM,yBAAyB,CAAC;AACrD,OAAO,gBAAgB,MAAM,2BAA2B,CAAC;AACzD,OAAO,wBAAwB,MAAM,mCAAmC,CAAC;AACzE,OAAO,oBAAoB,MAAM,+BAA+B,CAAC;AACjE,OAAO,mBAAmB,MAAM,8BAA8B,CAAC;AAG/D,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,YAAY,EACZ,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,WAAW,GACZ,CAAC;AAGF,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1E,MAAM,MAAM,uBAAuB,GAAG,cAAc,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChF,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAClE,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,cAAc,CAAC,CAAC;AACxE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5E,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,sBAAsB,CAC9B,CAAC;AACF,MAAM,MAAM,6BAA6B,GAAG,cAAc,CACxD,OAAO,wBAAwB,CAChC,CAAC;AACF,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,sBAAsB,CAC9B,CAAC;AACF,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1E,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC;AACpE,MAAM,MAAM,yBAAyB,GAAG,cAAc,CACpD,OAAO,oBAAoB,CAC5B,CAAC;AACF,MAAM,MAAM,wBAAwB,GAAG,cAAc,CACnD,OAAO,mBAAmB,CAC3B,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAElE,cAAc,qBAAqB,CAAC;AAEpC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,cAAc,EACd,UAAU,EACV,WAAW,EACX,eAAe,EACf,YAAY,EACZ,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC"}