@happyvertical/smrt-projects 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/AGENTS.md +31 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +97 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/collections/Issues.d.ts +107 -0
  8. package/dist/collections/Issues.d.ts.map +1 -0
  9. package/dist/collections/Projects.d.ts +90 -0
  10. package/dist/collections/Projects.d.ts.map +1 -0
  11. package/dist/collections/PullRequests.d.ts +107 -0
  12. package/dist/collections/PullRequests.d.ts.map +1 -0
  13. package/dist/collections/Repositories.d.ts +77 -0
  14. package/dist/collections/Repositories.d.ts.map +1 -0
  15. package/dist/constants.d.ts +9 -0
  16. package/dist/constants.d.ts.map +1 -0
  17. package/dist/index.d.ts +14 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +2477 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/manifest.json +4193 -0
  22. package/dist/models/Comment.d.ts +77 -0
  23. package/dist/models/Comment.d.ts.map +1 -0
  24. package/dist/models/Issue.d.ts +200 -0
  25. package/dist/models/Issue.d.ts.map +1 -0
  26. package/dist/models/Label.d.ts +63 -0
  27. package/dist/models/Label.d.ts.map +1 -0
  28. package/dist/models/Project.d.ts +183 -0
  29. package/dist/models/Project.d.ts.map +1 -0
  30. package/dist/models/PullRequest.d.ts +114 -0
  31. package/dist/models/PullRequest.d.ts.map +1 -0
  32. package/dist/models/Repository.d.ts +141 -0
  33. package/dist/models/Repository.d.ts.map +1 -0
  34. package/dist/playground.d.ts +2 -0
  35. package/dist/playground.d.ts.map +1 -0
  36. package/dist/playground.js +129 -0
  37. package/dist/playground.js.map +1 -0
  38. package/dist/prompts.d.ts +2 -0
  39. package/dist/prompts.d.ts.map +1 -0
  40. package/dist/smrt-knowledge.json +1956 -0
  41. package/dist/svelte/components/ApprovalActions.svelte +213 -0
  42. package/dist/svelte/components/ApprovalActions.svelte.d.ts +17 -0
  43. package/dist/svelte/components/ApprovalActions.svelte.d.ts.map +1 -0
  44. package/dist/svelte/components/BulkActions.svelte +224 -0
  45. package/dist/svelte/components/BulkActions.svelte.d.ts +14 -0
  46. package/dist/svelte/components/BulkActions.svelte.d.ts.map +1 -0
  47. package/dist/svelte/components/DurationDisplay.svelte +68 -0
  48. package/dist/svelte/components/DurationDisplay.svelte.d.ts +11 -0
  49. package/dist/svelte/components/DurationDisplay.svelte.d.ts.map +1 -0
  50. package/dist/svelte/components/RejectDialog.svelte +250 -0
  51. package/dist/svelte/components/RejectDialog.svelte.d.ts +17 -0
  52. package/dist/svelte/components/RejectDialog.svelte.d.ts.map +1 -0
  53. package/dist/svelte/components/TimeEntryCard.svelte +294 -0
  54. package/dist/svelte/components/TimeEntryCard.svelte.d.ts +17 -0
  55. package/dist/svelte/components/TimeEntryCard.svelte.d.ts.map +1 -0
  56. package/dist/svelte/components/TimeEntryList.svelte +351 -0
  57. package/dist/svelte/components/TimeEntryList.svelte.d.ts +17 -0
  58. package/dist/svelte/components/TimeEntryList.svelte.d.ts.map +1 -0
  59. package/dist/svelte/components/TimeSummary.svelte +165 -0
  60. package/dist/svelte/components/TimeSummary.svelte.d.ts +19 -0
  61. package/dist/svelte/components/TimeSummary.svelte.d.ts.map +1 -0
  62. package/dist/svelte/components/__tests__/ApprovalActions.test.js +41 -0
  63. package/dist/svelte/components/__tests__/BulkActions.test.js +46 -0
  64. package/dist/svelte/components/__tests__/DurationDisplay.test.js +23 -0
  65. package/dist/svelte/components/__tests__/RejectDialog.test.js +45 -0
  66. package/dist/svelte/components/__tests__/TimeEntryCard.test.js +45 -0
  67. package/dist/svelte/components/__tests__/TimeEntryList.test.js +50 -0
  68. package/dist/svelte/components/__tests__/TimeSummary.test.js +39 -0
  69. package/dist/svelte/components/utils.d.ts +42 -0
  70. package/dist/svelte/components/utils.d.ts.map +1 -0
  71. package/dist/svelte/components/utils.js +43 -0
  72. package/dist/svelte/i18n.d.ts +18 -0
  73. package/dist/svelte/i18n.d.ts.map +1 -0
  74. package/dist/svelte/i18n.js +18 -0
  75. package/dist/svelte/index.d.ts +26 -0
  76. package/dist/svelte/index.d.ts.map +1 -0
  77. package/dist/svelte/index.js +31 -0
  78. package/dist/svelte/playground.d.ts +122 -0
  79. package/dist/svelte/playground.d.ts.map +1 -0
  80. package/dist/svelte/playground.js +114 -0
  81. package/dist/svelte/utils.d.ts +42 -0
  82. package/dist/svelte/utils.d.ts.map +1 -0
  83. package/dist/svelte/utils.js +43 -0
  84. package/dist/types.d.ts +54 -0
  85. package/dist/types.d.ts.map +1 -0
  86. package/dist/types.js +2 -0
  87. package/dist/types.js.map +1 -0
  88. package/dist/ui.d.ts +10 -0
  89. package/dist/ui.d.ts.map +1 -0
  90. package/dist/ui.js +85 -0
  91. package/dist/ui.js.map +1 -0
  92. package/package.json +100 -0
@@ -0,0 +1,45 @@
1
+ // @vitest-environment jsdom
2
+ /**
3
+ * Component coverage for RejectDialog via the shared S11 harness (#1416).
4
+ */
5
+ import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
6
+ import { describe, expect, it, vi } from 'vitest';
7
+ import RejectDialog from '../RejectDialog.svelte';
8
+ const baseProps = (over = {}) => ({
9
+ open: true,
10
+ onconfirm: vi.fn(),
11
+ oncancel: vi.fn(),
12
+ ...over,
13
+ });
14
+ describe('RejectDialog', () => {
15
+ it('renders the dialog with a reason field when open', () => {
16
+ render(RejectDialog, { props: baseProps() });
17
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
18
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
19
+ expect(screen.getByRole('button', { name: 'Reject' })).toBeInTheDocument();
20
+ });
21
+ it('renders nothing when closed', () => {
22
+ render(RejectDialog, { props: baseProps({ open: false }) });
23
+ expect(screen.queryByRole('dialog')).toBeNull();
24
+ });
25
+ it('requires a reason before the confirm button is enabled', async () => {
26
+ const onconfirm = vi.fn();
27
+ render(RejectDialog, { props: baseProps({ onconfirm }) });
28
+ const confirm = screen.getByRole('button', { name: 'Reject' });
29
+ expect(confirm).toBeDisabled();
30
+ await userEvent.type(screen.getByRole('textbox'), 'Insufficient detail');
31
+ expect(confirm).toBeEnabled();
32
+ await userEvent.click(confirm);
33
+ expect(onconfirm).toHaveBeenCalledWith('Insufficient detail');
34
+ });
35
+ it('cancels via the cancel button', async () => {
36
+ const oncancel = vi.fn();
37
+ render(RejectDialog, { props: baseProps({ oncancel }) });
38
+ await userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
39
+ expect(oncancel).toHaveBeenCalledTimes(1);
40
+ });
41
+ it('is axe-clean', async () => {
42
+ const { container } = render(RejectDialog, { props: baseProps() });
43
+ await expectNoA11yViolations(container);
44
+ });
45
+ });
@@ -0,0 +1,45 @@
1
+ // @vitest-environment jsdom
2
+ /**
3
+ * Component coverage for TimeEntryCard via the shared S11 harness (#1416).
4
+ */
5
+ import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
6
+ import { describe, expect, it, vi } from 'vitest';
7
+ import TimeEntryCard from '../TimeEntryCard.svelte';
8
+ const entry = {
9
+ id: 'e1',
10
+ date: '2026-06-01',
11
+ description: 'Implement feature',
12
+ status: 'draft',
13
+ hours: 3,
14
+ hourlyRate: 100,
15
+ amount: 300,
16
+ workerName: 'Ada Lovelace',
17
+ };
18
+ describe('TimeEntryCard', () => {
19
+ it('renders the entry description, status, and worker', () => {
20
+ render(TimeEntryCard, { props: { entry } });
21
+ expect(screen.getByText('Implement feature')).toBeInTheDocument();
22
+ expect(screen.getByText('DRAFT')).toBeInTheDocument();
23
+ expect(screen.getByText('Ada Lovelace')).toBeInTheDocument();
24
+ });
25
+ it('forwards checkbox selection through onselect', async () => {
26
+ const onselect = vi.fn();
27
+ render(TimeEntryCard, {
28
+ props: { entry, selectable: true, onselect },
29
+ });
30
+ await userEvent.click(screen.getByLabelText('Select time entry for Implement feature'));
31
+ expect(onselect).toHaveBeenCalledWith('e1', true);
32
+ });
33
+ it('invokes onclick when the card is activated', async () => {
34
+ const onclick = vi.fn();
35
+ render(TimeEntryCard, { props: { entry, onclick } });
36
+ await userEvent.click(screen.getByText('Implement feature'));
37
+ expect(onclick).toHaveBeenCalledTimes(1);
38
+ });
39
+ it('is axe-clean', async () => {
40
+ const { container } = render(TimeEntryCard, {
41
+ props: { entry, selectable: true, onselect: vi.fn() },
42
+ });
43
+ await expectNoA11yViolations(container);
44
+ });
45
+ });
@@ -0,0 +1,50 @@
1
+ // @vitest-environment jsdom
2
+ /**
3
+ * Component coverage for TimeEntryList via the shared S11 harness (#1416).
4
+ */
5
+ import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
6
+ import { describe, expect, it, vi } from 'vitest';
7
+ import TimeEntryList from '../TimeEntryList.svelte';
8
+ const entries = [
9
+ {
10
+ id: 'e1',
11
+ date: '2026-06-01',
12
+ description: 'First task',
13
+ status: 'draft',
14
+ hours: 2,
15
+ },
16
+ {
17
+ id: 'e2',
18
+ date: '2026-06-02',
19
+ description: 'Second task',
20
+ status: 'draft',
21
+ hours: 4,
22
+ },
23
+ ];
24
+ describe('TimeEntryList', () => {
25
+ it('renders a card per entry', () => {
26
+ render(TimeEntryList, { props: { entries } });
27
+ expect(screen.getByText('First task')).toBeInTheDocument();
28
+ expect(screen.getByText('Second task')).toBeInTheDocument();
29
+ });
30
+ it('shows the empty message when there are no entries', () => {
31
+ render(TimeEntryList, {
32
+ props: { entries: [], emptyMessage: 'No time entries' },
33
+ });
34
+ expect(screen.getByText('No time entries')).toBeInTheDocument();
35
+ });
36
+ it('selects all entries via the header checkbox', async () => {
37
+ const onselectionchange = vi.fn();
38
+ render(TimeEntryList, {
39
+ props: { entries, selectable: true, onselectionchange },
40
+ });
41
+ await userEvent.click(screen.getByLabelText('Select all time entries'));
42
+ expect(onselectionchange).toHaveBeenCalledWith(['e1', 'e2']);
43
+ });
44
+ it('is axe-clean', async () => {
45
+ const { container } = render(TimeEntryList, {
46
+ props: { entries, selectable: true, onselectionchange: vi.fn() },
47
+ });
48
+ await expectNoA11yViolations(container);
49
+ });
50
+ });
@@ -0,0 +1,39 @@
1
+ // @vitest-environment jsdom
2
+ /**
3
+ * Component coverage for TimeSummary via the shared S11 harness (#1416).
4
+ */
5
+ import { expectNoA11yViolations, render, screen, } from '@happyvertical/smrt-vitest/svelte';
6
+ import { describe, expect, it } from 'vitest';
7
+ import TimeSummary from '../TimeSummary.svelte';
8
+ describe('TimeSummary', () => {
9
+ it('renders the total hours and value cards', () => {
10
+ render(TimeSummary, {
11
+ props: { totalHours: 12, totalAmount: 600, entryCount: 3 },
12
+ });
13
+ expect(screen.getByText('Total Hours')).toBeInTheDocument();
14
+ expect(screen.getByText('Total Value')).toBeInTheDocument();
15
+ expect(screen.getByText('3 entries')).toBeInTheDocument();
16
+ });
17
+ it('shows pending and approved cards when enabled and non-zero', () => {
18
+ render(TimeSummary, {
19
+ props: {
20
+ totalHours: 12,
21
+ totalAmount: 600,
22
+ pendingHours: 4,
23
+ pendingAmount: 200,
24
+ approvedHours: 8,
25
+ approvedAmount: 400,
26
+ showPending: true,
27
+ showApproved: true,
28
+ },
29
+ });
30
+ expect(screen.getByText('Pending Approval')).toBeInTheDocument();
31
+ expect(screen.getByText('Approved')).toBeInTheDocument();
32
+ });
33
+ it('is axe-clean', async () => {
34
+ const { container } = render(TimeSummary, {
35
+ props: { totalHours: 12, totalAmount: 600 },
36
+ });
37
+ await expectNoA11yViolations(container);
38
+ });
39
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shared utilities for time tracking components
3
+ */
4
+ export type Currency = 'CAD' | 'USD';
5
+ export type TimeEntryStatus = 'draft' | 'submitted' | 'approved' | 'rejected';
6
+ /** Alias for TimeEntryStatus for approval workflows */
7
+ export type ApprovalStatus = TimeEntryStatus;
8
+ /**
9
+ * Time entry data structure
10
+ */
11
+ export interface TimeEntry {
12
+ id: string;
13
+ date: Date | string;
14
+ hours: number;
15
+ description: string;
16
+ status: TimeEntryStatus;
17
+ amount?: number;
18
+ workerName?: string;
19
+ mileage?: number;
20
+ hourlyRate?: number;
21
+ }
22
+ /**
23
+ * Status color mapping using M3 design tokens
24
+ */
25
+ export declare const statusColors: Record<TimeEntryStatus, string>;
26
+ /**
27
+ * Format a date for display (e.g., "Jan 5")
28
+ */
29
+ export declare function formatDate(date: Date | string): string;
30
+ /**
31
+ * Format currency amount for display
32
+ */
33
+ export declare function formatCurrency(amount: number, currency?: Currency): string;
34
+ /**
35
+ * Format hours for display (e.g., "8.5h")
36
+ */
37
+ export declare function formatHours(hours: number): string;
38
+ /**
39
+ * Format hours in HH:MM format (e.g., "8:30")
40
+ */
41
+ export declare function formatHoursHHMM(hours: number): string;
42
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC;AAErC,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9E,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAKxD,CAAC;AAEF;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAGtD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,QAAgB,GACzB,MAAM,CAMR;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrD"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Shared utilities for time tracking components
3
+ */
4
+ /**
5
+ * Status color mapping using M3 design tokens
6
+ */
7
+ export const statusColors = {
8
+ draft: 'var(--md-sys-color-outline)',
9
+ submitted: 'var(--md-sys-color-tertiary)',
10
+ approved: 'var(--md-sys-color-primary)',
11
+ rejected: 'var(--md-sys-color-error)',
12
+ };
13
+ /**
14
+ * Format a date for display (e.g., "Jan 5")
15
+ */
16
+ export function formatDate(date) {
17
+ const d = typeof date === 'string' ? new Date(date) : date;
18
+ return d.toLocaleDateString('en-CA', { month: 'short', day: 'numeric' });
19
+ }
20
+ /**
21
+ * Format currency amount for display
22
+ */
23
+ export function formatCurrency(amount, currency = 'CAD') {
24
+ return new Intl.NumberFormat('en-CA', {
25
+ style: 'currency',
26
+ currency,
27
+ minimumFractionDigits: 2,
28
+ }).format(amount);
29
+ }
30
+ /**
31
+ * Format hours for display (e.g., "8.5h")
32
+ */
33
+ export function formatHours(hours) {
34
+ return `${hours.toFixed(1)}h`;
35
+ }
36
+ /**
37
+ * Format hours in HH:MM format (e.g., "8:30")
38
+ */
39
+ export function formatHoursHHMM(hours) {
40
+ const h = Math.floor(hours);
41
+ const m = Math.round((hours - h) * 60);
42
+ return `${h}:${m.toString().padStart(2, '0')}`;
43
+ }
@@ -0,0 +1,18 @@
1
+ export declare const M: {
2
+ readonly 'projects.approval_actions.approved_message': "projects.approval_actions.approved_message";
3
+ readonly 'projects.approval_actions.rejected_message': "projects.approval_actions.rejected_message";
4
+ readonly 'projects.bulk_actions.approve_all': "projects.bulk_actions.approve_all";
5
+ readonly 'projects.bulk_actions.reject_all': "projects.bulk_actions.reject_all";
6
+ readonly 'projects.reject_dialog.reason_required': "projects.reject_dialog.reason_required";
7
+ readonly 'projects.reject_dialog.close_dialog': "projects.reject_dialog.close_dialog";
8
+ readonly 'projects.time_entry_card.mileage': "projects.time_entry_card.mileage";
9
+ readonly 'projects.time_entry_card.select_entry': "projects.time_entry_card.select_entry";
10
+ readonly 'projects.time_entry_list.select_all': "projects.time_entry_list.select_all";
11
+ readonly 'projects.time_entry_list.selection_count': "projects.time_entry_list.selection_count";
12
+ readonly 'projects.time_entry_list.select_all_aria': "projects.time_entry_list.select_all_aria";
13
+ readonly 'projects.time_entry_list.select_entry_aria': "projects.time_entry_list.select_entry_aria";
14
+ readonly 'projects.time_summary.total_hours': "projects.time_summary.total_hours";
15
+ readonly 'projects.time_summary.total_value': "projects.time_summary.total_value";
16
+ readonly 'projects.time_summary.pending_approval': "projects.time_summary.pending_approval";
17
+ };
18
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;CAiBZ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { defineMessages } from '@happyvertical/smrt-ui/i18n';
2
+ export const M = defineMessages({
3
+ 'projects.approval_actions.approved_message': 'This entry has been approved',
4
+ 'projects.approval_actions.rejected_message': 'This entry was rejected',
5
+ 'projects.bulk_actions.approve_all': 'Approve All',
6
+ 'projects.bulk_actions.reject_all': 'Reject All',
7
+ 'projects.reject_dialog.reason_required': 'A reason is required to reject',
8
+ 'projects.reject_dialog.close_dialog': 'Close dialog',
9
+ 'projects.time_entry_card.mileage': '+ {mileage} km mileage',
10
+ 'projects.time_entry_card.select_entry': 'Select time entry for {description}',
11
+ 'projects.time_entry_list.select_all': 'Select All',
12
+ 'projects.time_entry_list.selection_count': '{selected} of {total} selected',
13
+ 'projects.time_entry_list.select_all_aria': 'Select all time entries',
14
+ 'projects.time_entry_list.select_entry_aria': 'Select entry: {description}',
15
+ 'projects.time_summary.total_hours': 'Total Hours',
16
+ 'projects.time_summary.total_value': 'Total Value',
17
+ 'projects.time_summary.pending_approval': 'Pending Approval',
18
+ });
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Projects Module Svelte Components
3
+ *
4
+ * Optional Svelte UI components for time tracking and project management.
5
+ * Auto-registers components with ModuleUIRegistry on import.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import type { ComponentProps } from 'svelte';
10
+ import ApprovalActions from './components/ApprovalActions.svelte';
11
+ import BulkActions from './components/BulkActions.svelte';
12
+ import DurationDisplay from './components/DurationDisplay.svelte';
13
+ import RejectDialog from './components/RejectDialog.svelte';
14
+ import TimeEntryCard from './components/TimeEntryCard.svelte';
15
+ import TimeEntryList from './components/TimeEntryList.svelte';
16
+ import TimeSummary from './components/TimeSummary.svelte';
17
+ export { ApprovalActions, BulkActions, DurationDisplay, RejectDialog, TimeEntryCard, TimeEntryList, TimeSummary, };
18
+ export type ApprovalActionsProps = ComponentProps<typeof ApprovalActions>;
19
+ export type BulkActionsProps = ComponentProps<typeof BulkActions>;
20
+ export type DurationDisplayProps = ComponentProps<typeof DurationDisplay>;
21
+ export type RejectDialogProps = ComponentProps<typeof RejectDialog>;
22
+ export type TimeEntryCardProps = ComponentProps<typeof TimeEntryCard>;
23
+ export type TimeEntryListProps = ComponentProps<typeof TimeEntryList>;
24
+ export type TimeSummaryProps = ComponentProps<typeof TimeSummary>;
25
+ export { type ApprovalStatus, type Currency, formatCurrency, formatDate, formatHours, formatHoursHHMM, statusColors, type TimeEntry, type TimeEntryStatus, } from './utils.js';
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Projects Module Svelte Components
3
+ *
4
+ * Optional Svelte UI components for time tracking and project management.
5
+ * Auto-registers components with ModuleUIRegistry on import.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import { ModuleUIRegistry } from '@happyvertical/smrt-ui/registry';
10
+ import { PROJECTS_MODULE_META } from '../ui.js';
11
+ // Import components
12
+ import ApprovalActions from './components/ApprovalActions.svelte';
13
+ import BulkActions from './components/BulkActions.svelte';
14
+ import DurationDisplay from './components/DurationDisplay.svelte';
15
+ import RejectDialog from './components/RejectDialog.svelte';
16
+ import TimeEntryCard from './components/TimeEntryCard.svelte';
17
+ import TimeEntryList from './components/TimeEntryList.svelte';
18
+ import TimeSummary from './components/TimeSummary.svelte';
19
+ // Export components
20
+ export { ApprovalActions, BulkActions, DurationDisplay, RejectDialog, TimeEntryCard, TimeEntryList, TimeSummary, };
21
+ // Export types and utilities
22
+ export { formatCurrency, formatDate, formatHours, formatHoursHHMM, statusColors, } from './utils.js';
23
+ // Auto-register with ModuleUIRegistry
24
+ ModuleUIRegistry.registerModule(PROJECTS_MODULE_META);
25
+ ModuleUIRegistry.register('@happyvertical/smrt-projects', 'time-entry-card', TimeEntryCard);
26
+ ModuleUIRegistry.register('@happyvertical/smrt-projects', 'time-entry-list', TimeEntryList);
27
+ ModuleUIRegistry.register('@happyvertical/smrt-projects', 'time-summary', TimeSummary);
28
+ ModuleUIRegistry.register('@happyvertical/smrt-projects', 'duration-display', DurationDisplay);
29
+ ModuleUIRegistry.register('@happyvertical/smrt-projects', 'approval-actions', ApprovalActions);
30
+ ModuleUIRegistry.register('@happyvertical/smrt-projects', 'bulk-actions', BulkActions);
31
+ ModuleUIRegistry.register('@happyvertical/smrt-projects', 'reject-dialog', RejectDialog);
@@ -0,0 +1,122 @@
1
+ declare const _default: {
2
+ packageName: string;
3
+ displayName: string;
4
+ description: string | undefined;
5
+ moduleMeta: import("@happyvertical/smrt-types").SmrtModuleMeta;
6
+ entries: ({
7
+ id: string;
8
+ title: string;
9
+ description: string;
10
+ loadComponent: () => Promise<typeof import("./components/TimeEntryList.svelte")>;
11
+ order: number;
12
+ props: {
13
+ entries: ({
14
+ id: string;
15
+ date: string;
16
+ hours: number;
17
+ description: string;
18
+ status: string;
19
+ amount: number;
20
+ workerName: string;
21
+ hourlyRate: number;
22
+ mileage: number;
23
+ } | {
24
+ id: string;
25
+ date: string;
26
+ hours: number;
27
+ description: string;
28
+ status: string;
29
+ amount: number;
30
+ workerName: string;
31
+ hourlyRate: number;
32
+ mileage?: undefined;
33
+ })[];
34
+ selectable: boolean;
35
+ selectedIds: string[];
36
+ currency: string;
37
+ onselectionchange: () => void;
38
+ baseHref: string;
39
+ totalHours?: undefined;
40
+ totalAmount?: undefined;
41
+ pendingHours?: undefined;
42
+ pendingAmount?: undefined;
43
+ approvedHours?: undefined;
44
+ approvedAmount?: undefined;
45
+ entryCount?: undefined;
46
+ showApproved?: undefined;
47
+ status?: undefined;
48
+ onapprove?: undefined;
49
+ onreject?: undefined;
50
+ onedit?: undefined;
51
+ };
52
+ modes: {
53
+ mock: {
54
+ label: string;
55
+ };
56
+ };
57
+ } | {
58
+ id: string;
59
+ title: string;
60
+ description: string;
61
+ loadComponent: () => Promise<typeof import("./components/TimeSummary.svelte")>;
62
+ order: number;
63
+ props: {
64
+ totalHours: number;
65
+ totalAmount: number;
66
+ pendingHours: number;
67
+ pendingAmount: number;
68
+ approvedHours: number;
69
+ approvedAmount: number;
70
+ entryCount: number;
71
+ currency: string;
72
+ showApproved: boolean;
73
+ entries?: undefined;
74
+ selectable?: undefined;
75
+ selectedIds?: undefined;
76
+ onselectionchange?: undefined;
77
+ baseHref?: undefined;
78
+ status?: undefined;
79
+ onapprove?: undefined;
80
+ onreject?: undefined;
81
+ onedit?: undefined;
82
+ };
83
+ modes: {
84
+ mock: {
85
+ label: string;
86
+ };
87
+ };
88
+ } | {
89
+ id: string;
90
+ title: string;
91
+ description: string;
92
+ loadComponent: () => Promise<typeof import("./components/ApprovalActions.svelte")>;
93
+ order: number;
94
+ props: {
95
+ status: string;
96
+ onapprove: () => void;
97
+ onreject: () => void;
98
+ onedit: () => void;
99
+ entries?: undefined;
100
+ selectable?: undefined;
101
+ selectedIds?: undefined;
102
+ currency?: undefined;
103
+ onselectionchange?: undefined;
104
+ baseHref?: undefined;
105
+ totalHours?: undefined;
106
+ totalAmount?: undefined;
107
+ pendingHours?: undefined;
108
+ pendingAmount?: undefined;
109
+ approvedHours?: undefined;
110
+ approvedAmount?: undefined;
111
+ entryCount?: undefined;
112
+ showApproved?: undefined;
113
+ };
114
+ modes: {
115
+ mock: {
116
+ label: string;
117
+ };
118
+ };
119
+ })[];
120
+ };
121
+ export default _default;
122
+ //# sourceMappingURL=playground.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../src/svelte/playground.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,wBAgFE"}
@@ -0,0 +1,114 @@
1
+ import { PROJECTS_MODULE_META } from '../ui.js';
2
+ const noop = () => { };
3
+ const sampleTimeEntries = [
4
+ {
5
+ id: 'time-entry-1',
6
+ date: '2026-03-18T16:00:00.000Z',
7
+ hours: 6.5,
8
+ description: 'Editorial governance review and fact-linking pass',
9
+ status: 'submitted',
10
+ amount: 780,
11
+ workerName: 'Taylor Rowan',
12
+ hourlyRate: 120,
13
+ mileage: 18,
14
+ },
15
+ {
16
+ id: 'time-entry-2',
17
+ date: '2026-03-19T16:00:00.000Z',
18
+ hours: 4.25,
19
+ description: 'Content workflow instrumentation and approval QA',
20
+ status: 'approved',
21
+ amount: 510,
22
+ workerName: 'Jordan Lee',
23
+ hourlyRate: 120,
24
+ },
25
+ {
26
+ id: 'time-entry-3',
27
+ date: '2026-03-20T16:00:00.000Z',
28
+ hours: 2,
29
+ description: 'Follow-up revisions for contributor feedback',
30
+ status: 'draft',
31
+ amount: 240,
32
+ workerName: 'Casey Tran',
33
+ hourlyRate: 120,
34
+ },
35
+ ];
36
+ const totalHours = sampleTimeEntries.reduce((sum, entry) => sum + entry.hours, 0);
37
+ const totalAmount = sampleTimeEntries.reduce((sum, entry) => sum + (entry.amount ?? 0), 0);
38
+ const pendingEntries = sampleTimeEntries.filter((entry) => entry.status === 'submitted');
39
+ const loadApprovalActions = () => import('./components/ApprovalActions.svelte');
40
+ const loadTimeEntryList = () => import('./components/TimeEntryList.svelte');
41
+ const loadTimeSummary = () => import('./components/TimeSummary.svelte');
42
+ export default {
43
+ packageName: '@happyvertical/smrt-projects',
44
+ displayName: PROJECTS_MODULE_META.displayName,
45
+ description: PROJECTS_MODULE_META.description,
46
+ moduleMeta: PROJECTS_MODULE_META,
47
+ entries: [
48
+ {
49
+ id: 'time-entry-list',
50
+ title: 'Time Entry List',
51
+ description: 'Selectable list view for recent time entries with billing context.',
52
+ loadComponent: loadTimeEntryList,
53
+ order: 1,
54
+ props: {
55
+ entries: sampleTimeEntries,
56
+ selectable: true,
57
+ selectedIds: ['time-entry-1'],
58
+ currency: 'CAD',
59
+ onselectionchange: noop,
60
+ baseHref: '/projects/time-entries',
61
+ },
62
+ modes: {
63
+ mock: {
64
+ label: 'Mock',
65
+ },
66
+ },
67
+ },
68
+ {
69
+ id: 'time-summary',
70
+ title: 'Time Summary',
71
+ description: 'Hours and value summary cards for a project review period.',
72
+ loadComponent: loadTimeSummary,
73
+ order: 2,
74
+ props: {
75
+ totalHours,
76
+ totalAmount,
77
+ pendingHours: pendingEntries.reduce((sum, entry) => sum + entry.hours, 0),
78
+ pendingAmount: pendingEntries.reduce((sum, entry) => sum + (entry.amount ?? 0), 0),
79
+ approvedHours: sampleTimeEntries
80
+ .filter((entry) => entry.status === 'approved')
81
+ .reduce((sum, entry) => sum + entry.hours, 0),
82
+ approvedAmount: sampleTimeEntries
83
+ .filter((entry) => entry.status === 'approved')
84
+ .reduce((sum, entry) => sum + (entry.amount ?? 0), 0),
85
+ entryCount: sampleTimeEntries.length,
86
+ currency: 'CAD',
87
+ showApproved: true,
88
+ },
89
+ modes: {
90
+ mock: {
91
+ label: 'Mock',
92
+ },
93
+ },
94
+ },
95
+ {
96
+ id: 'approval-actions',
97
+ title: 'Approval Actions',
98
+ description: 'Status-sensitive action row for approving or returning submitted work.',
99
+ loadComponent: loadApprovalActions,
100
+ order: 3,
101
+ props: {
102
+ status: 'submitted',
103
+ onapprove: noop,
104
+ onreject: noop,
105
+ onedit: noop,
106
+ },
107
+ modes: {
108
+ mock: {
109
+ label: 'Mock',
110
+ },
111
+ },
112
+ },
113
+ ],
114
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shared utilities for time tracking components
3
+ */
4
+ export type Currency = 'CAD' | 'USD';
5
+ export type TimeEntryStatus = 'draft' | 'submitted' | 'approved' | 'rejected';
6
+ /** Alias for TimeEntryStatus for approval workflows */
7
+ export type ApprovalStatus = TimeEntryStatus;
8
+ /**
9
+ * Time entry data structure
10
+ */
11
+ export interface TimeEntry {
12
+ id: string;
13
+ date: Date | string;
14
+ hours: number;
15
+ description: string;
16
+ status: TimeEntryStatus;
17
+ amount?: number;
18
+ workerName?: string;
19
+ mileage?: number;
20
+ hourlyRate?: number;
21
+ }
22
+ /**
23
+ * Status color mapping using M3 design tokens
24
+ */
25
+ export declare const statusColors: Record<TimeEntryStatus, string>;
26
+ /**
27
+ * Format a date for display (e.g., "Jan 5")
28
+ */
29
+ export declare function formatDate(date: Date | string): string;
30
+ /**
31
+ * Format currency amount for display
32
+ */
33
+ export declare function formatCurrency(amount: number, currency?: Currency): string;
34
+ /**
35
+ * Format hours for display (e.g., "8.5h")
36
+ */
37
+ export declare function formatHours(hours: number): string;
38
+ /**
39
+ * Format hours in HH:MM format (e.g., "8:30")
40
+ */
41
+ export declare function formatHoursHHMM(hours: number): string;
42
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/svelte/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC;AAErC,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9E,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAKxD,CAAC;AAEF;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAGtD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,QAAgB,GACzB,MAAM,CAMR;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrD"}