@happyvertical/smrt-support 0.39.2
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/AGENTS.md +270 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4358 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +6770 -0
- package/dist/models/index.d.ts +17 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/service-time-entry.d.ts +75 -0
- package/dist/models/service-time-entry.d.ts.map +1 -0
- package/dist/models/support-ai-run.d.ts +45 -0
- package/dist/models/support-ai-run.d.ts.map +1 -0
- package/dist/models/support-case-event.d.ts +25 -0
- package/dist/models/support-case-event.d.ts.map +1 -0
- package/dist/models/support-case.d.ts +125 -0
- package/dist/models/support-case.d.ts.map +1 -0
- package/dist/models/support-channel-binding.d.ts +47 -0
- package/dist/models/support-channel-binding.d.ts.map +1 -0
- package/dist/models/support-compensation-plan.d.ts +42 -0
- package/dist/models/support-compensation-plan.d.ts.map +1 -0
- package/dist/models/support-interaction.d.ts +49 -0
- package/dist/models/support-interaction.d.ts.map +1 -0
- package/dist/models/support-plan.d.ts +92 -0
- package/dist/models/support-plan.d.ts.map +1 -0
- package/dist/models/support-policy.d.ts +84 -0
- package/dist/models/support-policy.d.ts.map +1 -0
- package/dist/models/support-service-target.d.ts +90 -0
- package/dist/models/support-service-target.d.ts.map +1 -0
- package/dist/models/support-settlement.d.ts +58 -0
- package/dist/models/support-settlement.d.ts.map +1 -0
- package/dist/models/support-specialist.d.ts +71 -0
- package/dist/models/support-specialist.d.ts.map +1 -0
- package/dist/models/support-work-link.d.ts +31 -0
- package/dist/models/support-work-link.d.ts.map +1 -0
- package/dist/permissions.d.ts +32 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/services/coverage-calendar.d.ts +53 -0
- package/dist/services/coverage-calendar.d.ts.map +1 -0
- package/dist/services/human-handoff-service.d.ts +113 -0
- package/dist/services/human-handoff-service.d.ts.map +1 -0
- package/dist/services/index.d.ts +15 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/keyed-mutex.d.ts +22 -0
- package/dist/services/keyed-mutex.d.ts.map +1 -0
- package/dist/services/service-target-engine.d.ts +133 -0
- package/dist/services/service-target-engine.d.ts.map +1 -0
- package/dist/services/service-time-entry-service.d.ts +73 -0
- package/dist/services/service-time-entry-service.d.ts.map +1 -0
- package/dist/services/support-ai-workflow.d.ts +178 -0
- package/dist/services/support-ai-workflow.d.ts.map +1 -0
- package/dist/services/support-case-service.d.ts +162 -0
- package/dist/services/support-case-service.d.ts.map +1 -0
- package/dist/services/support-intake-service.d.ts +106 -0
- package/dist/services/support-intake-service.d.ts.map +1 -0
- package/dist/services/support-plan-admin-service.d.ts +45 -0
- package/dist/services/support-plan-admin-service.d.ts.map +1 -0
- package/dist/services/support-routing-service.d.ts +117 -0
- package/dist/services/support-routing-service.d.ts.map +1 -0
- package/dist/services/time-entry-approval-service.d.ts +162 -0
- package/dist/services/time-entry-approval-service.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +4242 -0
- package/dist/svelte/components/CaseDetail.svelte +277 -0
- package/dist/svelte/components/CaseDetail.svelte.d.ts +10 -0
- package/dist/svelte/components/CaseDetail.svelte.d.ts.map +1 -0
- package/dist/svelte/components/CaseQueue.svelte +148 -0
- package/dist/svelte/components/CaseQueue.svelte.d.ts +12 -0
- package/dist/svelte/components/CaseQueue.svelte.d.ts.map +1 -0
- package/dist/svelte/components/RoutingRationale.svelte +131 -0
- package/dist/svelte/components/RoutingRationale.svelte.d.ts +11 -0
- package/dist/svelte/components/RoutingRationale.svelte.d.ts.map +1 -0
- package/dist/svelte/components/TargetList.svelte +127 -0
- package/dist/svelte/components/TargetList.svelte.d.ts +9 -0
- package/dist/svelte/components/TargetList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/TimeEntryApprovalQueue.svelte +226 -0
- package/dist/svelte/components/TimeEntryApprovalQueue.svelte.d.ts +11 -0
- package/dist/svelte/components/TimeEntryApprovalQueue.svelte.d.ts.map +1 -0
- package/dist/svelte/components/__tests__/CaseDetail.test.js +86 -0
- package/dist/svelte/components/__tests__/CaseQueue.test.js +67 -0
- package/dist/svelte/components/__tests__/RoutingRationale.test.js +69 -0
- package/dist/svelte/components/__tests__/TargetList.test.js +84 -0
- package/dist/svelte/components/__tests__/TimeEntryApprovalQueue.test.js +105 -0
- package/dist/svelte/index.d.ts +25 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +18 -0
- package/dist/svelte/types.d.ts +129 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +182 -0
- package/dist/types.d.ts +130 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +109 -0
- package/dist/types.js.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* Component coverage for TimeEntryApprovalQueue via the shared S11 harness
|
|
4
|
+
* (#1416): rendering, the approve/reject callbacks (reject collects a
|
|
5
|
+
* reason first), and accessibility.
|
|
6
|
+
*/
|
|
7
|
+
import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
|
|
8
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
9
|
+
import TimeEntryApprovalQueue from '../TimeEntryApprovalQueue.svelte';
|
|
10
|
+
function entryView(overrides = {}) {
|
|
11
|
+
return {
|
|
12
|
+
id: 'te-1',
|
|
13
|
+
date: '2026-07-01',
|
|
14
|
+
hours: 1.5,
|
|
15
|
+
description: 'Debugged the login loop',
|
|
16
|
+
status: 'submitted',
|
|
17
|
+
amount: 180,
|
|
18
|
+
workerName: 'Ada',
|
|
19
|
+
hourlyRate: 120,
|
|
20
|
+
source: 'timer',
|
|
21
|
+
participantKind: 'human',
|
|
22
|
+
caseId: 'case-1',
|
|
23
|
+
...overrides,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
describe('TimeEntryApprovalQueue', () => {
|
|
27
|
+
it('renders date, hours, description, worker, status, and amount', async () => {
|
|
28
|
+
const { container } = render(TimeEntryApprovalQueue, {
|
|
29
|
+
props: { entries: [entryView()] },
|
|
30
|
+
});
|
|
31
|
+
expect(screen.getByText('2026-07-01')).toBeInTheDocument();
|
|
32
|
+
expect(screen.getByText('1.5h')).toBeInTheDocument();
|
|
33
|
+
expect(screen.getByText('Debugged the login loop')).toBeInTheDocument();
|
|
34
|
+
expect(screen.getByText('Ada')).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByText('submitted')).toBeInTheDocument();
|
|
36
|
+
expect(screen.getByText('180.00')).toBeInTheDocument();
|
|
37
|
+
await expectNoA11yViolations(container);
|
|
38
|
+
});
|
|
39
|
+
it('omits the amount when no charge has derived yet', () => {
|
|
40
|
+
render(TimeEntryApprovalQueue, {
|
|
41
|
+
props: { entries: [entryView({ amount: undefined })] },
|
|
42
|
+
});
|
|
43
|
+
expect(screen.queryByText('180.00')).not.toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
it('invokes onapprove with the entry id', async () => {
|
|
46
|
+
const onapprove = vi.fn();
|
|
47
|
+
render(TimeEntryApprovalQueue, {
|
|
48
|
+
props: { entries: [entryView()], onapprove },
|
|
49
|
+
});
|
|
50
|
+
await userEvent.click(screen.getByRole('button', {
|
|
51
|
+
name: 'Approve time entry: Debugged the login loop',
|
|
52
|
+
}));
|
|
53
|
+
expect(onapprove).toHaveBeenCalledWith('te-1');
|
|
54
|
+
});
|
|
55
|
+
it('collects a reason before invoking onreject', async () => {
|
|
56
|
+
const onreject = vi.fn();
|
|
57
|
+
const { container } = render(TimeEntryApprovalQueue, {
|
|
58
|
+
props: { entries: [entryView()], onreject },
|
|
59
|
+
});
|
|
60
|
+
await userEvent.click(screen.getByRole('button', {
|
|
61
|
+
name: 'Reject time entry: Debugged the login loop',
|
|
62
|
+
}));
|
|
63
|
+
const confirmButton = screen.getByRole('button', {
|
|
64
|
+
name: 'Confirm rejection of: Debugged the login loop',
|
|
65
|
+
});
|
|
66
|
+
expect(confirmButton).toBeDisabled();
|
|
67
|
+
await userEvent.type(screen.getByRole('textbox', {
|
|
68
|
+
name: 'Rejection reason for: Debugged the login loop',
|
|
69
|
+
}), 'duplicate entry');
|
|
70
|
+
await expectNoA11yViolations(container);
|
|
71
|
+
await userEvent.click(confirmButton);
|
|
72
|
+
expect(onreject).toHaveBeenCalledWith('te-1', 'duplicate entry');
|
|
73
|
+
});
|
|
74
|
+
it('cancelling a rejection restores the actions without firing onreject', async () => {
|
|
75
|
+
const onreject = vi.fn();
|
|
76
|
+
render(TimeEntryApprovalQueue, {
|
|
77
|
+
props: { entries: [entryView()], onreject },
|
|
78
|
+
});
|
|
79
|
+
await userEvent.click(screen.getByRole('button', {
|
|
80
|
+
name: 'Reject time entry: Debugged the login loop',
|
|
81
|
+
}));
|
|
82
|
+
await userEvent.click(screen.getByRole('button', {
|
|
83
|
+
name: 'Cancel rejecting: Debugged the login loop',
|
|
84
|
+
}));
|
|
85
|
+
expect(onreject).not.toHaveBeenCalled();
|
|
86
|
+
expect(screen.getByRole('button', {
|
|
87
|
+
name: 'Approve time entry: Debugged the login loop',
|
|
88
|
+
})).toBeInTheDocument();
|
|
89
|
+
});
|
|
90
|
+
it('shows no actions on rows that are not submitted', () => {
|
|
91
|
+
render(TimeEntryApprovalQueue, {
|
|
92
|
+
props: {
|
|
93
|
+
entries: [entryView({ id: 'te-2', status: 'approved' })],
|
|
94
|
+
onapprove: vi.fn(),
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
98
|
+
});
|
|
99
|
+
it('shows the empty message when there are no entries', () => {
|
|
100
|
+
render(TimeEntryApprovalQueue, {
|
|
101
|
+
props: { entries: [], emptyMessage: 'Nothing awaiting review' },
|
|
102
|
+
});
|
|
103
|
+
expect(screen.getByText('Nothing awaiting review')).toBeInTheDocument();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @happyvertical/smrt-support/svelte
|
|
3
|
+
*
|
|
4
|
+
* Reusable Svelte 5 surfaces for Managed Support: a case queue and case
|
|
5
|
+
* detail view (issue #1926), Service Target clocks and the explainable
|
|
6
|
+
* routing ranking (issue #1929), and a time-entry approval queue (issue
|
|
7
|
+
* #1930). Presentational only — hosts load data through the package services
|
|
8
|
+
* and adapt models with the exported view mappers.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import type { ComponentProps } from 'svelte';
|
|
13
|
+
import CaseDetail from './components/CaseDetail.svelte';
|
|
14
|
+
import CaseQueue from './components/CaseQueue.svelte';
|
|
15
|
+
import RoutingRationale from './components/RoutingRationale.svelte';
|
|
16
|
+
import TargetList from './components/TargetList.svelte';
|
|
17
|
+
import TimeEntryApprovalQueue from './components/TimeEntryApprovalQueue.svelte';
|
|
18
|
+
export { CaseDetail, CaseQueue, RoutingRationale, TargetList, TimeEntryApprovalQueue, };
|
|
19
|
+
export type CaseQueueProps = ComponentProps<typeof CaseQueue>;
|
|
20
|
+
export type CaseDetailProps = ComponentProps<typeof CaseDetail>;
|
|
21
|
+
export type TargetListProps = ComponentProps<typeof TargetList>;
|
|
22
|
+
export type RoutingRationaleProps = ComponentProps<typeof RoutingRationale>;
|
|
23
|
+
export type TimeEntryApprovalQueueProps = ComponentProps<typeof TimeEntryApprovalQueue>;
|
|
24
|
+
export { type CaseTimelineItemView, caseStatusBadgeKey, humanizeStatus, priorityBadgeKey, type RankedSpecialistView, type ServiceTargetView, type SupportCaseView, type SupportTimeEntryView, type SupportWorkLinkView, targetStatusBadgeKey, timeEntryStatusBadgeKey, toCaseTimelineItemView, toServiceTargetView, toSupportCaseView, toSupportTimeEntryView, } from './types.js';
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,UAAU,MAAM,gCAAgC,CAAC;AACxD,OAAO,SAAS,MAAM,+BAA+B,CAAC;AACtD,OAAO,gBAAgB,MAAM,sCAAsC,CAAC;AACpE,OAAO,UAAU,MAAM,gCAAgC,CAAC;AACxD,OAAO,sBAAsB,MAAM,4CAA4C,CAAC;AAEhF,OAAO,EACL,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,sBAAsB,GACvB,CAAC;AACF,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9D,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,UAAU,CAAC,CAAC;AAChE,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,UAAU,CAAC,CAAC;AAChE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5E,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,sBAAsB,CAC9B,CAAC;AAEF,OAAO,EACL,KAAK,oBAAoB,EACzB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @happyvertical/smrt-support/svelte
|
|
3
|
+
*
|
|
4
|
+
* Reusable Svelte 5 surfaces for Managed Support: a case queue and case
|
|
5
|
+
* detail view (issue #1926), Service Target clocks and the explainable
|
|
6
|
+
* routing ranking (issue #1929), and a time-entry approval queue (issue
|
|
7
|
+
* #1930). Presentational only — hosts load data through the package services
|
|
8
|
+
* and adapt models with the exported view mappers.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import CaseDetail from './components/CaseDetail.svelte';
|
|
13
|
+
import CaseQueue from './components/CaseQueue.svelte';
|
|
14
|
+
import RoutingRationale from './components/RoutingRationale.svelte';
|
|
15
|
+
import TargetList from './components/TargetList.svelte';
|
|
16
|
+
import TimeEntryApprovalQueue from './components/TimeEntryApprovalQueue.svelte';
|
|
17
|
+
export { CaseDetail, CaseQueue, RoutingRationale, TargetList, TimeEntryApprovalQueue, };
|
|
18
|
+
export { caseStatusBadgeKey, humanizeStatus, priorityBadgeKey, targetStatusBadgeKey, timeEntryStatusBadgeKey, toCaseTimelineItemView, toServiceTargetView, toSupportCaseView, toSupportTimeEntryView, } from './types.js';
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serializable view types for the support Svelte surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Components take plain view objects (not model instances) so hosts can pass
|
|
5
|
+
* data across the server/client boundary; `toSupportCaseView` /
|
|
6
|
+
* `toCaseTimelineItemView` adapt the models. Explicit interfaces (not inline
|
|
7
|
+
* intersections) keep Svelte 5 prop type evaluation cheap.
|
|
8
|
+
*/
|
|
9
|
+
import type { ServiceTimeEntry } from '../models/service-time-entry.js';
|
|
10
|
+
import type { SupportCase } from '../models/support-case.js';
|
|
11
|
+
import type { SupportServiceTarget } from '../models/support-service-target.js';
|
|
12
|
+
import type { SupportCharge } from '../models/support-settlement.js';
|
|
13
|
+
import type { CaseTimelineItem } from '../services/support-case-service.js';
|
|
14
|
+
export interface SupportCaseView {
|
|
15
|
+
id: string;
|
|
16
|
+
caseNumber: string;
|
|
17
|
+
subject: string;
|
|
18
|
+
status: string;
|
|
19
|
+
priority: string;
|
|
20
|
+
severity: string;
|
|
21
|
+
channelKind: string;
|
|
22
|
+
clientProfileId: string | null;
|
|
23
|
+
projectId: string | null;
|
|
24
|
+
assignedSpecialistId: string | null;
|
|
25
|
+
assignedSpecialistName: string | null;
|
|
26
|
+
reopenCount: number;
|
|
27
|
+
createdAt: string | null;
|
|
28
|
+
updatedAt: string | null;
|
|
29
|
+
resolutionSummary: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CaseTimelineItemView {
|
|
32
|
+
kind: 'interaction' | 'event';
|
|
33
|
+
occurredAt: string;
|
|
34
|
+
actorKind: string;
|
|
35
|
+
summary: string;
|
|
36
|
+
body: string;
|
|
37
|
+
direction: string | null;
|
|
38
|
+
channelKind: string | null;
|
|
39
|
+
eventType: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface SupportWorkLinkView {
|
|
42
|
+
id: string;
|
|
43
|
+
linkKind: string;
|
|
44
|
+
targetLabel: string;
|
|
45
|
+
externalUrl: string;
|
|
46
|
+
status: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* View shape for a Service Time Entry in approval/review surfaces. The first
|
|
50
|
+
* eight fields deliberately match `smrt-projects`' presentation `TimeEntry`
|
|
51
|
+
* contract (`packages/projects/src/svelte/utils.ts`) — smrt-support promotes
|
|
52
|
+
* that presentation-only shape into persisted models, so hosts can reuse
|
|
53
|
+
* either package's time surfaces over the same view rows.
|
|
54
|
+
*/
|
|
55
|
+
export interface SupportTimeEntryView {
|
|
56
|
+
id: string;
|
|
57
|
+
/** ISO date part (`YYYY-MM-DD`) of the work period start. */
|
|
58
|
+
date: string;
|
|
59
|
+
/** Worked duration in decimal hours, rounded to 2 decimals. */
|
|
60
|
+
hours: number;
|
|
61
|
+
description: string;
|
|
62
|
+
status: string;
|
|
63
|
+
/** Client charge amount, when a derived {@link SupportCharge} is supplied. */
|
|
64
|
+
amount?: number;
|
|
65
|
+
workerName?: string;
|
|
66
|
+
/** Hourly rate from the charge's frozen `rateSnapshot`, when supplied. */
|
|
67
|
+
hourlyRate?: number;
|
|
68
|
+
source: string;
|
|
69
|
+
participantKind: string;
|
|
70
|
+
caseId: string | null;
|
|
71
|
+
}
|
|
72
|
+
/** Adapt a SupportCase model to the queue/detail view shape. */
|
|
73
|
+
export declare function toSupportCaseView(supportCase: SupportCase, options?: {
|
|
74
|
+
assignedSpecialistName?: string | null;
|
|
75
|
+
}): SupportCaseView;
|
|
76
|
+
/** Adapt one merged timeline item (interaction or event) to its view. */
|
|
77
|
+
export declare function toCaseTimelineItemView(item: CaseTimelineItem): CaseTimelineItemView;
|
|
78
|
+
/**
|
|
79
|
+
* Map a case status onto smrt-ui `StatusBadge`'s default color-scheme keys
|
|
80
|
+
* (`active` | `inactive` | `pending` | `error` | `success` | `warning`); pass
|
|
81
|
+
* the real status as the badge `label`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function caseStatusBadgeKey(status: string): string;
|
|
84
|
+
/** Map a case priority onto `StatusBadge` default color-scheme keys. */
|
|
85
|
+
export declare function priorityBadgeKey(priority: string): string;
|
|
86
|
+
/** Human-readable label for a snake_case status value. */
|
|
87
|
+
export declare function humanizeStatus(value: string): string;
|
|
88
|
+
/** Adapt a ServiceTimeEntry model (plus its optional charge) to the view. */
|
|
89
|
+
export declare function toSupportTimeEntryView(entry: ServiceTimeEntry, opts?: {
|
|
90
|
+
charge?: SupportCharge | null;
|
|
91
|
+
workerName?: string | null;
|
|
92
|
+
}): SupportTimeEntryView;
|
|
93
|
+
/** Map a time-entry status onto `StatusBadge` default color-scheme keys. */
|
|
94
|
+
export declare function timeEntryStatusBadgeKey(status: string): string;
|
|
95
|
+
/** Serializable Service Target clock view for `TargetList` (#1929). */
|
|
96
|
+
export interface ServiceTargetView {
|
|
97
|
+
id: string;
|
|
98
|
+
targetType: string;
|
|
99
|
+
cycle: number;
|
|
100
|
+
status: string;
|
|
101
|
+
severity: string;
|
|
102
|
+
baseMinutes: number;
|
|
103
|
+
startedAt: string | null;
|
|
104
|
+
dueAt: string | null;
|
|
105
|
+
satisfiedAt: string | null;
|
|
106
|
+
breachedAt: string | null;
|
|
107
|
+
/** Whether the clock is currently frozen by the plan's pause rules. */
|
|
108
|
+
paused: boolean;
|
|
109
|
+
}
|
|
110
|
+
/** Adapt a SupportServiceTarget model to the `TargetList` view shape. */
|
|
111
|
+
export declare function toServiceTargetView(target: SupportServiceTarget): ServiceTargetView;
|
|
112
|
+
/**
|
|
113
|
+
* Map a Service Target status onto smrt-ui `StatusBadge` default
|
|
114
|
+
* color-scheme keys: running/paused clocks read as attention-colored,
|
|
115
|
+
* satisfied as success, breached as error.
|
|
116
|
+
*/
|
|
117
|
+
export declare function targetStatusBadgeKey(status: string): string;
|
|
118
|
+
/**
|
|
119
|
+
* Serializable ranked-specialist view for `RoutingRationale` (#1929) —
|
|
120
|
+
* mirrors the routing service's `RankedSpecialist` result shape.
|
|
121
|
+
*/
|
|
122
|
+
export interface RankedSpecialistView {
|
|
123
|
+
specialistId: string;
|
|
124
|
+
displayName: string;
|
|
125
|
+
score: number;
|
|
126
|
+
eligible: boolean;
|
|
127
|
+
factors: Record<string, number | string | boolean>;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/svelte/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serializable view types for the support Svelte surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Components take plain view objects (not model instances) so hosts can pass
|
|
5
|
+
* data across the server/client boundary; `toSupportCaseView` /
|
|
6
|
+
* `toCaseTimelineItemView` adapt the models. Explicit interfaces (not inline
|
|
7
|
+
* intersections) keep Svelte 5 prop type evaluation cheap.
|
|
8
|
+
*/
|
|
9
|
+
function toIso(value) {
|
|
10
|
+
if (!value)
|
|
11
|
+
return null;
|
|
12
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
13
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
14
|
+
}
|
|
15
|
+
/** Adapt a SupportCase model to the queue/detail view shape. */
|
|
16
|
+
export function toSupportCaseView(supportCase, options = {}) {
|
|
17
|
+
return {
|
|
18
|
+
id: supportCase.id ?? '',
|
|
19
|
+
caseNumber: supportCase.caseNumber,
|
|
20
|
+
subject: supportCase.subject,
|
|
21
|
+
status: supportCase.status,
|
|
22
|
+
priority: supportCase.priority,
|
|
23
|
+
severity: supportCase.severity,
|
|
24
|
+
channelKind: supportCase.channelKind,
|
|
25
|
+
clientProfileId: supportCase.clientProfileId,
|
|
26
|
+
projectId: supportCase.projectId,
|
|
27
|
+
assignedSpecialistId: supportCase.assignedSpecialistId,
|
|
28
|
+
assignedSpecialistName: options.assignedSpecialistName ?? null,
|
|
29
|
+
reopenCount: supportCase.reopenCount,
|
|
30
|
+
createdAt: toIso(supportCase.created_at),
|
|
31
|
+
updatedAt: toIso(supportCase.updated_at),
|
|
32
|
+
resolutionSummary: supportCase.resolutionSummary,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Adapt one merged timeline item (interaction or event) to its view. */
|
|
36
|
+
export function toCaseTimelineItemView(item) {
|
|
37
|
+
if (item.kind === 'interaction' && item.interaction) {
|
|
38
|
+
const interaction = item.interaction;
|
|
39
|
+
return {
|
|
40
|
+
kind: 'interaction',
|
|
41
|
+
occurredAt: toIso(interaction.occurredAt) ?? '',
|
|
42
|
+
actorKind: interaction.actorKind,
|
|
43
|
+
summary: `${interaction.direction} ${interaction.channelKind}`,
|
|
44
|
+
body: interaction.body,
|
|
45
|
+
direction: interaction.direction,
|
|
46
|
+
channelKind: interaction.channelKind,
|
|
47
|
+
eventType: null,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const event = item.event;
|
|
51
|
+
return {
|
|
52
|
+
kind: 'event',
|
|
53
|
+
occurredAt: toIso(event.occurredAt) ?? '',
|
|
54
|
+
actorKind: event.actorKind,
|
|
55
|
+
summary: event.summary,
|
|
56
|
+
body: '',
|
|
57
|
+
direction: null,
|
|
58
|
+
channelKind: null,
|
|
59
|
+
eventType: event.eventType,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Map a case status onto smrt-ui `StatusBadge`'s default color-scheme keys
|
|
64
|
+
* (`active` | `inactive` | `pending` | `error` | `success` | `warning`); pass
|
|
65
|
+
* the real status as the badge `label`.
|
|
66
|
+
*/
|
|
67
|
+
export function caseStatusBadgeKey(status) {
|
|
68
|
+
switch (status) {
|
|
69
|
+
case 'new':
|
|
70
|
+
case 'triaged':
|
|
71
|
+
return 'pending';
|
|
72
|
+
case 'assigned':
|
|
73
|
+
case 'in_progress':
|
|
74
|
+
return 'active';
|
|
75
|
+
case 'waiting_on_client':
|
|
76
|
+
return 'warning';
|
|
77
|
+
case 'escalated':
|
|
78
|
+
return 'error';
|
|
79
|
+
case 'resolved':
|
|
80
|
+
return 'success';
|
|
81
|
+
case 'closed':
|
|
82
|
+
return 'inactive';
|
|
83
|
+
default:
|
|
84
|
+
return 'pending';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Map a case priority onto `StatusBadge` default color-scheme keys. */
|
|
88
|
+
export function priorityBadgeKey(priority) {
|
|
89
|
+
switch (priority) {
|
|
90
|
+
case 'urgent':
|
|
91
|
+
return 'error';
|
|
92
|
+
case 'high':
|
|
93
|
+
return 'warning';
|
|
94
|
+
case 'low':
|
|
95
|
+
return 'inactive';
|
|
96
|
+
default:
|
|
97
|
+
return 'active';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Human-readable label for a snake_case status value. */
|
|
101
|
+
export function humanizeStatus(value) {
|
|
102
|
+
return (value ?? '').replace(/_/g, ' ');
|
|
103
|
+
}
|
|
104
|
+
/** Adapt a ServiceTimeEntry model (plus its optional charge) to the view. */
|
|
105
|
+
export function toSupportTimeEntryView(entry, opts = {}) {
|
|
106
|
+
const startIso = toIso(entry.startedAt ?? entry.created_at ?? null);
|
|
107
|
+
const view = {
|
|
108
|
+
id: entry.id ?? '',
|
|
109
|
+
date: startIso ? startIso.slice(0, 10) : '',
|
|
110
|
+
hours: Math.round((entry.durationSeconds / 3600) * 100) / 100,
|
|
111
|
+
description: entry.description,
|
|
112
|
+
status: entry.status,
|
|
113
|
+
source: entry.source,
|
|
114
|
+
participantKind: entry.participantKind,
|
|
115
|
+
caseId: entry.caseId,
|
|
116
|
+
};
|
|
117
|
+
if (opts.charge) {
|
|
118
|
+
view.amount = opts.charge.amount;
|
|
119
|
+
const hourlyRate = opts.charge.getRateSnapshot().hourlyRate;
|
|
120
|
+
if (typeof hourlyRate === 'number') {
|
|
121
|
+
view.hourlyRate = hourlyRate;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (opts.workerName) {
|
|
125
|
+
view.workerName = opts.workerName;
|
|
126
|
+
}
|
|
127
|
+
return view;
|
|
128
|
+
}
|
|
129
|
+
/** Map a time-entry status onto `StatusBadge` default color-scheme keys. */
|
|
130
|
+
export function timeEntryStatusBadgeKey(status) {
|
|
131
|
+
switch (status) {
|
|
132
|
+
case 'draft':
|
|
133
|
+
return 'inactive';
|
|
134
|
+
case 'submitted':
|
|
135
|
+
return 'pending';
|
|
136
|
+
case 'approved':
|
|
137
|
+
return 'success';
|
|
138
|
+
case 'rejected':
|
|
139
|
+
return 'error';
|
|
140
|
+
case 'corrected':
|
|
141
|
+
return 'warning';
|
|
142
|
+
default:
|
|
143
|
+
return 'pending';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Adapt a SupportServiceTarget model to the `TargetList` view shape. */
|
|
147
|
+
export function toServiceTargetView(target) {
|
|
148
|
+
return {
|
|
149
|
+
id: target.id ?? '',
|
|
150
|
+
targetType: target.targetType,
|
|
151
|
+
cycle: target.cycle,
|
|
152
|
+
status: target.status,
|
|
153
|
+
severity: target.severity,
|
|
154
|
+
baseMinutes: target.baseMinutes,
|
|
155
|
+
startedAt: toIso(target.startedAt),
|
|
156
|
+
dueAt: toIso(target.dueAt),
|
|
157
|
+
satisfiedAt: toIso(target.satisfiedAt),
|
|
158
|
+
breachedAt: toIso(target.breachedAt),
|
|
159
|
+
paused: target.status === 'paused',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Map a Service Target status onto smrt-ui `StatusBadge` default
|
|
164
|
+
* color-scheme keys: running/paused clocks read as attention-colored,
|
|
165
|
+
* satisfied as success, breached as error.
|
|
166
|
+
*/
|
|
167
|
+
export function targetStatusBadgeKey(status) {
|
|
168
|
+
switch (status) {
|
|
169
|
+
case 'pending':
|
|
170
|
+
return 'pending';
|
|
171
|
+
case 'paused':
|
|
172
|
+
return 'warning';
|
|
173
|
+
case 'satisfied':
|
|
174
|
+
return 'success';
|
|
175
|
+
case 'breached':
|
|
176
|
+
return 'error';
|
|
177
|
+
case 'cancelled':
|
|
178
|
+
return 'inactive';
|
|
179
|
+
default:
|
|
180
|
+
return 'pending';
|
|
181
|
+
}
|
|
182
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types, enums, and lifecycle maps for `@happyvertical/smrt-support`.
|
|
3
|
+
*
|
|
4
|
+
* Domain language follows the HappyVertical support glossary: a **Support
|
|
5
|
+
* Case** is the canonical record of a client support request; **Support
|
|
6
|
+
* Interactions** are channel transports attached to it; **Service Targets**
|
|
7
|
+
* are operational clocks (not contractual SLAs); a **Managed Support Plan**
|
|
8
|
+
* carries the client-facing terms while a **Support Compensation Plan**
|
|
9
|
+
* carries the provider-facing terms — the two are never merged.
|
|
10
|
+
*/
|
|
11
|
+
/** Support Case lifecycle states (FR-29b). */
|
|
12
|
+
export type SupportCaseStatus = 'new' | 'triaged' | 'assigned' | 'in_progress' | 'waiting_on_client' | 'escalated' | 'resolved' | 'closed';
|
|
13
|
+
/**
|
|
14
|
+
* Legal Support Case status transitions, enforced at save time by
|
|
15
|
+
* {@link SupportCase} (S5 guarded-lifecycle idiom — raw mass-assignment on a
|
|
16
|
+
* generated route cannot skip states). Reopening (`resolved`/`closed` back to
|
|
17
|
+
* an active state) is legal here; the reopen bookkeeping (count, history
|
|
18
|
+
* event) is layered by `SupportCaseService.reopen()`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const SUPPORT_CASE_STATUS_TRANSITIONS: Record<SupportCaseStatus, SupportCaseStatus[]>;
|
|
21
|
+
/** Case statuses considered "open" for create-or-join intake dedup. */
|
|
22
|
+
export declare const OPEN_SUPPORT_CASE_STATUSES: SupportCaseStatus[];
|
|
23
|
+
/** Client-facing priority (ordering/urgency inside a queue). */
|
|
24
|
+
export type SupportCasePriority = 'low' | 'normal' | 'high' | 'urgent';
|
|
25
|
+
/** Transport kind a Support Interaction arrived through. */
|
|
26
|
+
export type SupportChannelKind = 'chat' | 'email' | 'phone' | 'voice' | 'web' | 'api';
|
|
27
|
+
/** Direction of a Support Interaction relative to the Client. */
|
|
28
|
+
export type SupportInteractionDirection = 'inbound' | 'outbound' | 'internal';
|
|
29
|
+
/** Who performed an interaction or case event. */
|
|
30
|
+
export type SupportActorKind = 'client' | 'specialist' | 'agent' | 'system';
|
|
31
|
+
/** Append-only case audit event types. */
|
|
32
|
+
export type SupportCaseEventType = 'created' | 'interaction' | 'transition' | 'assignment' | 'ai_run' | 'handoff' | 'human_requested' | 'escalation' | 'target_scheduled' | 'target_satisfied' | 'target_breached' | 'target_paused' | 'target_resumed' | 'work_linked' | 'reopened' | 'plan_applied' | 'time_recorded' | 'note';
|
|
33
|
+
/** What kind of transport container a channel binding points at. */
|
|
34
|
+
export type SupportBindingKind = 'chat_room' | 'email_account';
|
|
35
|
+
/** Operational Work Item link kinds carried by a {@link SupportWorkLink}. */
|
|
36
|
+
export type SupportWorkLinkKind = 'support_work_item' | 'development_work_item';
|
|
37
|
+
/** Service Target clock types (FR-30/FR-31). */
|
|
38
|
+
export type ServiceTargetType = 'acknowledgement' | 'response' | 'update' | 'resolution';
|
|
39
|
+
/** Service Target clock states. */
|
|
40
|
+
export type ServiceTargetStatus = 'pending' | 'paused' | 'satisfied' | 'breached' | 'cancelled';
|
|
41
|
+
/** Why a case was escalated. */
|
|
42
|
+
export type SupportEscalationReason = 'target_breach' | 'target_risk' | 'manual' | 'ai_trigger';
|
|
43
|
+
/** AI support workflow phases (FR-28a). */
|
|
44
|
+
export type SupportAiRunPhase = 'acknowledge' | 'classify' | 'answer' | 'troubleshoot' | 'resolve';
|
|
45
|
+
/** Terminal outcome of one AI workflow phase run. */
|
|
46
|
+
export type SupportAiRunOutcome = 'completed' | 'skipped' | 'failed' | 'handed_off';
|
|
47
|
+
/** What triggered a Human Handoff (FR-28b). */
|
|
48
|
+
export type HumanHandoffTrigger = 'client_request' | 'low_confidence' | 'high_severity' | 'sensitive' | 'failed_resolution' | 'target_risk' | 'policy' | 'manual';
|
|
49
|
+
/** Service Time Entry lifecycle (FR-36). */
|
|
50
|
+
export type ServiceTimeEntryStatus = 'draft' | 'submitted' | 'approved' | 'rejected' | 'corrected';
|
|
51
|
+
/**
|
|
52
|
+
* Legal Service Time Entry status transitions. `approved → corrected` is the
|
|
53
|
+
* only exit from `approved` and is reachable exclusively through the explicit
|
|
54
|
+
* correction path (the approved snapshot itself stays frozen).
|
|
55
|
+
*/
|
|
56
|
+
export declare const SERVICE_TIME_ENTRY_STATUS_TRANSITIONS: Record<ServiceTimeEntryStatus, ServiceTimeEntryStatus[]>;
|
|
57
|
+
/** How a Service Time Entry was produced (FR-40). */
|
|
58
|
+
export type ServiceTimeEntrySource = 'timer' | 'manual' | 'import' | 'agent';
|
|
59
|
+
/** Who/what delivered the work behind a Service Time Entry. */
|
|
60
|
+
export type SupportParticipantKind = 'human' | 'agent';
|
|
61
|
+
/** Which approval path accepted a Service Time Entry (FR-36). */
|
|
62
|
+
export type TimeEntryApprovalPath = 'automatic' | 'operator' | 'client' | 'threshold';
|
|
63
|
+
/** Derived commercial snapshot lifecycle (charges and compensation). */
|
|
64
|
+
export type SupportSettlementStatus = 'pending' | 'final' | 'corrected' | 'voided';
|
|
65
|
+
/** Support Specialist availability window kinds. */
|
|
66
|
+
export type SupportAvailabilityKind = 'weekly' | 'on_call' | 'time_off';
|
|
67
|
+
/** Support Specialist activation state. */
|
|
68
|
+
export type SupportSpecialistStatus = 'active' | 'inactive';
|
|
69
|
+
/** Project Support Qualification levels. */
|
|
70
|
+
export type SupportQualificationLevel = 'trainee' | 'qualified' | 'expert';
|
|
71
|
+
/** A weekly coverage window in a plan's coverage calendar. */
|
|
72
|
+
export interface CoverageWindow {
|
|
73
|
+
/** 0 = Sunday … 6 = Saturday (JS `Date#getDay` convention). */
|
|
74
|
+
weekday: number;
|
|
75
|
+
/** Minutes from midnight, inclusive start. */
|
|
76
|
+
startMinute: number;
|
|
77
|
+
/** Minutes from midnight, exclusive end. */
|
|
78
|
+
endMinute: number;
|
|
79
|
+
}
|
|
80
|
+
/** Per-severity Service Target minutes; `null` disables that clock. */
|
|
81
|
+
export interface ServiceTargetMinutes {
|
|
82
|
+
acknowledgementMinutes: number | null;
|
|
83
|
+
responseMinutes: number | null;
|
|
84
|
+
updateMinutes: number | null;
|
|
85
|
+
resolutionMinutes: number | null;
|
|
86
|
+
}
|
|
87
|
+
/** One step of a plan's escalation policy, applied in `level` order. */
|
|
88
|
+
export interface EscalationStep {
|
|
89
|
+
level: number;
|
|
90
|
+
action: 'notify' | 'reassign';
|
|
91
|
+
/** Profile ids to notify (recorded on the escalation; delivery is app-side). */
|
|
92
|
+
notifyProfileIds?: string[];
|
|
93
|
+
/** Minutes after the previous step before this one fires (0 = immediately). */
|
|
94
|
+
delayMinutes?: number;
|
|
95
|
+
}
|
|
96
|
+
/** A severity definition entry in a plan (key → labels). */
|
|
97
|
+
export interface SeverityDefinition {
|
|
98
|
+
label: string;
|
|
99
|
+
description?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Default severity vocabulary used when a plan doesn't define its own.
|
|
103
|
+
* `sev1` is the most severe.
|
|
104
|
+
*/
|
|
105
|
+
export declare const DEFAULT_SEVERITY_DEFINITIONS: Record<string, SeverityDefinition>;
|
|
106
|
+
/** Time-entry approval policy carried by a Managed Support Plan (FR-36). */
|
|
107
|
+
export interface TimeApprovalPolicy {
|
|
108
|
+
/** Base path when no threshold applies. */
|
|
109
|
+
mode: 'automatic' | 'operator' | 'client';
|
|
110
|
+
/** Entries longer than this require review even under `automatic`. */
|
|
111
|
+
thresholdMinutes?: number;
|
|
112
|
+
/** Entries pricing above this amount require review even under `automatic`. */
|
|
113
|
+
thresholdAmount?: number;
|
|
114
|
+
}
|
|
115
|
+
/** One piece of work evidence attached to a Service Time Entry. */
|
|
116
|
+
export interface TimeEntryEvidence {
|
|
117
|
+
kind: string;
|
|
118
|
+
ref: string;
|
|
119
|
+
label?: string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Parse a JSON value stored as text, returning the fallback on malformed
|
|
123
|
+
* input (never throws — stored JSON may predate schema changes). Accepts an
|
|
124
|
+
* already-parsed value (e.g. a `type: 'json'` column that hydrated as an
|
|
125
|
+
* object) and hands it back unchanged.
|
|
126
|
+
*/
|
|
127
|
+
export declare function parseJsonField<T>(raw: unknown, fallback: T): T;
|
|
128
|
+
/** Parse a JSON array-of-strings field, tolerating malformed input. */
|
|
129
|
+
export declare function parseStringArrayField(raw: string | null | undefined): string[];
|
|
130
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|