@happyvertical/smrt-projects 0.40.66 → 0.40.67

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 (43) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +489 -420
  4. package/dist/index.js.map +1 -1
  5. package/dist/managed-client.d.ts +9 -0
  6. package/dist/managed-client.d.ts.map +1 -1
  7. package/dist/manifest.json +1 -1
  8. package/dist/playground.js +48 -3
  9. package/dist/playground.js.map +1 -1
  10. package/dist/project-board-types.d.ts +12 -0
  11. package/dist/project-board-types.d.ts.map +1 -0
  12. package/dist/project-board-types.js +0 -0
  13. package/dist/services/delivery-service.d.ts +1 -1
  14. package/dist/services/delivery-service.d.ts.map +1 -1
  15. package/dist/services/index.d.ts +1 -0
  16. package/dist/services/index.d.ts.map +1 -1
  17. package/dist/services/project-board-service.d.ts +32 -0
  18. package/dist/services/project-board-service.d.ts.map +1 -0
  19. package/dist/smrt-knowledge.json +5 -3
  20. package/dist/svelte/DevelopmentBoard.svelte +54 -74
  21. package/dist/svelte/DevelopmentBoard.svelte.d.ts.map +1 -1
  22. package/dist/svelte/ProjectBoard.svelte +157 -0
  23. package/dist/svelte/ProjectBoard.svelte.d.ts +32 -0
  24. package/dist/svelte/ProjectBoard.svelte.d.ts.map +1 -0
  25. package/dist/svelte/components/__tests__/ProjectBoard.test.js +156 -0
  26. package/dist/svelte/i18n.d.ts +2 -0
  27. package/dist/svelte/i18n.d.ts.map +1 -1
  28. package/dist/svelte/i18n.js +2 -0
  29. package/dist/svelte/index.d.ts +4 -1
  30. package/dist/svelte/index.d.ts.map +1 -1
  31. package/dist/svelte/index.js +3 -1
  32. package/dist/svelte/playground.d.ts +99 -0
  33. package/dist/svelte/playground.d.ts.map +1 -1
  34. package/dist/svelte/playground.js +49 -3
  35. package/dist/svelte/project-board-types.d.ts +3 -0
  36. package/dist/svelte/project-board-types.d.ts.map +1 -0
  37. package/dist/svelte/project-board-types.js +1 -0
  38. package/dist/types.d.ts +1 -1
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/ui.d.ts.map +1 -1
  41. package/dist/ui.js +10 -1
  42. package/dist/ui.js.map +1 -1
  43. package/package.json +11 -10
@@ -1,4 +1,5 @@
1
1
  import { DatabaseInterface } from '@happyvertical/sql';
2
+ import { ProjectBoardMoveIntent } from './services/project-board-service';
2
3
  import { ManagedDevelopmentRequestCreateInput } from './types';
3
4
  export declare class ManagedProjectClient {
4
5
  #private;
@@ -9,5 +10,13 @@ export declare class ManagedProjectClient {
9
10
  }): Promise<ManagedProjectClient>;
10
11
  createRequest(input: Omit<ManagedDevelopmentRequestCreateInput, 'requesterId'>): Promise<import('.').DevelopmentRequest>;
11
12
  listRequests(): Promise<import('.').DevelopmentRequest[]>;
13
+ /**
14
+ * Move a project-board item through this authenticated integration.
15
+ *
16
+ * The browser submits only the move intent. This client keeps the credential
17
+ * authentication boundary server-side and ProjectBoardService reloads active
18
+ * capability state again immediately before the provider mutation.
19
+ */
20
+ moveProjectBoardItem(input: ProjectBoardMoveIntent): Promise<import('./services').ProjectBoardMoveResult>;
12
21
  }
13
22
  //# sourceMappingURL=managed-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"managed-client.d.ts","sourceRoot":"","sources":["../src/managed-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI5D,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,SAAS,CAAC;AAEpE,qBAAa,oBAAoB;;IAK/B,OAAO;WASM,YAAY,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;QAAE,EAAE,EAAE,iBAAiB,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GACtD,OAAO,CAAC,oBAAoB,CAAC;IAc1B,aAAa,CACjB,KAAK,EAAE,IAAI,CAAC,oCAAoC,EAAE,aAAa,CAAC;IAmB5D,YAAY;CAgCnB"}
1
+ {"version":3,"file":"managed-client.d.ts","sourceRoot":"","sources":["../src/managed-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI5D,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,SAAS,CAAC;AAEpE,qBAAa,oBAAoB;;IAK/B,OAAO;WASM,YAAY,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;QAAE,EAAE,EAAE,iBAAiB,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GACtD,OAAO,CAAC,oBAAoB,CAAC;IAc1B,aAAa,CACjB,KAAK,EAAE,IAAI,CAAC,oCAAoC,EAAE,aAAa,CAAC;IAmB5D,YAAY;IAYlB;;;;;;OAMG;IACG,oBAAoB,CAAC,KAAK,EAAE,sBAAsB;CA0BzD"}
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-projects",
5
- "packageVersion": "0.40.66",
5
+ "packageVersion": "0.40.67",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-projects:DevelopmentRequestHistoryCollection": {
8
8
  "name": "developmentrequesthistorycollection",
@@ -40,18 +40,63 @@ var pendingEntries = sampleTimeEntries.filter((entry) => entry.status === "submi
40
40
  var loadApprovalActions = () => import("./svelte/components/ApprovalActions.svelte");
41
41
  var loadTimeEntryList = () => import("./svelte/components/TimeEntryList.svelte");
42
42
  var loadTimeSummary = () => import("./svelte/components/TimeSummary.svelte");
43
+ var loadDevelopmentBoard = () => import("./svelte/DevelopmentBoard.svelte");
44
+ var loadProjectBoard = () => import("./svelte/ProjectBoard.svelte");
43
45
  var playground_default = {
44
46
  packageName: "@happyvertical/smrt-projects",
45
47
  displayName: PROJECTS_MODULE_META.displayName,
46
48
  description: PROJECTS_MODULE_META.description,
47
49
  moduleMeta: PROJECTS_MODULE_META,
48
50
  entries: [
51
+ {
52
+ id: "project-board",
53
+ title: "Project Board",
54
+ description: "Controlled provider project board with canonical statuses.",
55
+ loadComponent: loadProjectBoard,
56
+ order: 0,
57
+ props: {
58
+ projectId: "project-demo",
59
+ statuses: [{
60
+ id: "todo",
61
+ name: "Todo",
62
+ order: 0
63
+ }, {
64
+ id: "done",
65
+ name: "Done",
66
+ order: 1
67
+ }],
68
+ items: [{
69
+ id: "item-demo",
70
+ contentId: "content-demo",
71
+ title: "Publish project board",
72
+ status: "Todo",
73
+ fields: {},
74
+ type: "Issue"
75
+ }]
76
+ },
77
+ modes: { mock: { label: "Mock" } }
78
+ },
79
+ {
80
+ id: "development-board",
81
+ title: "Development Board",
82
+ description: "Read-only delivery-status projection.",
83
+ loadComponent: loadDevelopmentBoard,
84
+ order: 1,
85
+ props: { requests: [{
86
+ id: "request-demo",
87
+ description: "Publish project board",
88
+ type: "feature",
89
+ status: "planned",
90
+ requesterLabel: "Project owner"
91
+ }] },
92
+ modes: { mock: { label: "Mock" } }
93
+ },
49
94
  {
50
95
  id: "time-entry-list",
51
96
  title: "Time Entry List",
52
97
  description: "Selectable list view for recent time entries with billing context.",
53
98
  loadComponent: loadTimeEntryList,
54
- order: 1,
99
+ order: 2,
55
100
  props: {
56
101
  entries: sampleTimeEntries,
57
102
  selectable: true,
@@ -67,7 +112,7 @@ var playground_default = {
67
112
  title: "Time Summary",
68
113
  description: "Hours and value summary cards for a project review period.",
69
114
  loadComponent: loadTimeSummary,
70
- order: 2,
115
+ order: 3,
71
116
  props: {
72
117
  totalHours,
73
118
  totalAmount,
@@ -86,7 +131,7 @@ var playground_default = {
86
131
  title: "Approval Actions",
87
132
  description: "Status-sensitive action row for approving or returning submitted work.",
88
133
  loadComponent: loadApprovalActions,
89
- order: 3,
134
+ order: 4,
90
135
  props: {
91
136
  status: "submitted",
92
137
  onapprove: noop,
@@ -1 +1 @@
1
- {"version":3,"file":"playground.js","names":[],"sources":["../src/svelte/playground.ts"],"sourcesContent":["import { PROJECTS_MODULE_META } from '../ui.js';\n\nconst noop = () => {};\n\nconst sampleTimeEntries = [\n {\n id: 'time-entry-1',\n date: '2026-03-18T16:00:00.000Z',\n hours: 6.5,\n description: 'Editorial governance review and fact-linking pass',\n status: 'submitted',\n amount: 780,\n workerName: 'Taylor Rowan',\n hourlyRate: 120,\n mileage: 18,\n },\n {\n id: 'time-entry-2',\n date: '2026-03-19T16:00:00.000Z',\n hours: 4.25,\n description: 'Content workflow instrumentation and approval QA',\n status: 'approved',\n amount: 510,\n workerName: 'Jordan Lee',\n hourlyRate: 120,\n },\n {\n id: 'time-entry-3',\n date: '2026-03-20T16:00:00.000Z',\n hours: 2,\n description: 'Follow-up revisions for contributor feedback',\n status: 'draft',\n amount: 240,\n workerName: 'Casey Tran',\n hourlyRate: 120,\n },\n];\n\nconst totalHours = sampleTimeEntries.reduce(\n (sum, entry) => sum + entry.hours,\n 0,\n);\nconst totalAmount = sampleTimeEntries.reduce(\n (sum, entry) => sum + (entry.amount ?? 0),\n 0,\n);\nconst pendingEntries = sampleTimeEntries.filter(\n (entry) => entry.status === 'submitted',\n);\n\nconst loadApprovalActions = () => import('./components/ApprovalActions.svelte');\nconst loadTimeEntryList = () => import('./components/TimeEntryList.svelte');\nconst loadTimeSummary = () => import('./components/TimeSummary.svelte');\n\nexport default {\n packageName: '@happyvertical/smrt-projects',\n displayName: PROJECTS_MODULE_META.displayName,\n description: PROJECTS_MODULE_META.description,\n moduleMeta: PROJECTS_MODULE_META,\n entries: [\n {\n id: 'time-entry-list',\n title: 'Time Entry List',\n description:\n 'Selectable list view for recent time entries with billing context.',\n loadComponent: loadTimeEntryList,\n order: 1,\n props: {\n entries: sampleTimeEntries,\n selectable: true,\n selectedIds: ['time-entry-1'],\n currency: 'CAD',\n onselectionchange: noop,\n baseHref: '/projects/time-entries',\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'time-summary',\n title: 'Time Summary',\n description: 'Hours and value summary cards for a project review period.',\n loadComponent: loadTimeSummary,\n order: 2,\n props: {\n totalHours,\n totalAmount,\n pendingHours: pendingEntries.reduce(\n (sum, entry) => sum + entry.hours,\n 0,\n ),\n pendingAmount: pendingEntries.reduce(\n (sum, entry) => sum + (entry.amount ?? 0),\n 0,\n ),\n approvedHours: sampleTimeEntries\n .filter((entry) => entry.status === 'approved')\n .reduce((sum, entry) => sum + entry.hours, 0),\n approvedAmount: sampleTimeEntries\n .filter((entry) => entry.status === 'approved')\n .reduce((sum, entry) => sum + (entry.amount ?? 0), 0),\n entryCount: sampleTimeEntries.length,\n currency: 'CAD',\n showApproved: true,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'approval-actions',\n title: 'Approval Actions',\n description:\n 'Status-sensitive action row for approving or returning submitted work.',\n loadComponent: loadApprovalActions,\n order: 3,\n props: {\n status: 'submitted',\n onapprove: noop,\n onreject: noop,\n onedit: noop,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n ],\n};\n"],"mappings":";;AAEA,IAAM,aAAa,CAAC;AAEpB,IAAM,oBAAoB;CACxB;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,aAAa;EACb,QAAQ;EACR,QAAQ;EACR,YAAY;EACZ,YAAY;EACZ,SAAS;CACX;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,aAAa;EACb,QAAQ;EACR,QAAQ;EACR,YAAY;EACZ,YAAY;CACd;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,aAAa;EACb,QAAQ;EACR,QAAQ;EACR,YAAY;EACZ,YAAY;CACd;AACF;AAEA,IAAM,aAAa,kBAAkB,QAClC,KAAK,UAAU,MAAM,MAAM,OAC5B,CACF;AACA,IAAM,cAAc,kBAAkB,QACnC,KAAK,UAAU,OAAO,MAAM,UAAU,IACvC,CACF;AACA,IAAM,iBAAiB,kBAAkB,QACtC,UAAU,MAAM,WAAW,WAC9B;AAEA,IAAM,4BAA4B,OAAO;AACzC,IAAM,0BAA0B,OAAO;AACvC,IAAM,wBAAwB,OAAO;AAErC,IAAA,qBAAe;CACb,aAAa;CACb,aAAa,qBAAqB;CAClC,aAAa,qBAAqB;CAClC,YAAY;CACZ,SAAS;EACP;GACE,IAAI;GACJ,OAAO;GACP,aACE;GACF,eAAe;GACf,OAAO;GACP,OAAO;IACL,SAAS;IACT,YAAY;IACZ,aAAa,CAAC,cAAc;IAC5B,UAAU;IACV,mBAAmB;IACnB,UAAU;GACZ;GACA,OAAO,EACL,MAAM,EACJ,OAAO,OACT,EACF;EACF;EACA;GACE,IAAI;GACJ,OAAO;GACP,aAAa;GACb,eAAe;GACf,OAAO;GACP,OAAO;IACL;IACA;IACA,cAAc,eAAe,QAC1B,KAAK,UAAU,MAAM,MAAM,OAC5B,CACF;IACA,eAAe,eAAe,QAC3B,KAAK,UAAU,OAAO,MAAM,UAAU,IACvC,CACF;IACA,eAAe,kBACZ,QAAQ,UAAU,MAAM,WAAW,UAAU,CAAA,CAC7C,QAAQ,KAAK,UAAU,MAAM,MAAM,OAAO,CAAC;IAC9C,gBAAgB,kBACb,QAAQ,UAAU,MAAM,WAAW,UAAU,CAAA,CAC7C,QAAQ,KAAK,UAAU,OAAO,MAAM,UAAU,IAAI,CAAC;IACtD,YAAY,kBAAkB;IAC9B,UAAU;IACV,cAAc;GAChB;GACA,OAAO,EACL,MAAM,EACJ,OAAO,OACT,EACF;EACF;EACA;GACE,IAAI;GACJ,OAAO;GACP,aACE;GACF,eAAe;GACf,OAAO;GACP,OAAO;IACL,QAAQ;IACR,WAAW;IACX,UAAU;IACV,QAAQ;GACV;GACA,OAAO,EACL,MAAM,EACJ,OAAO,OACT,EACF;EACF;CACF;AACF"}
1
+ {"version":3,"file":"playground.js","names":[],"sources":["../src/svelte/playground.ts"],"sourcesContent":["import { PROJECTS_MODULE_META } from '../ui.js';\n\nconst noop = () => {};\n\nconst sampleTimeEntries = [\n {\n id: 'time-entry-1',\n date: '2026-03-18T16:00:00.000Z',\n hours: 6.5,\n description: 'Editorial governance review and fact-linking pass',\n status: 'submitted',\n amount: 780,\n workerName: 'Taylor Rowan',\n hourlyRate: 120,\n mileage: 18,\n },\n {\n id: 'time-entry-2',\n date: '2026-03-19T16:00:00.000Z',\n hours: 4.25,\n description: 'Content workflow instrumentation and approval QA',\n status: 'approved',\n amount: 510,\n workerName: 'Jordan Lee',\n hourlyRate: 120,\n },\n {\n id: 'time-entry-3',\n date: '2026-03-20T16:00:00.000Z',\n hours: 2,\n description: 'Follow-up revisions for contributor feedback',\n status: 'draft',\n amount: 240,\n workerName: 'Casey Tran',\n hourlyRate: 120,\n },\n];\n\nconst totalHours = sampleTimeEntries.reduce(\n (sum, entry) => sum + entry.hours,\n 0,\n);\nconst totalAmount = sampleTimeEntries.reduce(\n (sum, entry) => sum + (entry.amount ?? 0),\n 0,\n);\nconst pendingEntries = sampleTimeEntries.filter(\n (entry) => entry.status === 'submitted',\n);\n\nconst loadApprovalActions = () => import('./components/ApprovalActions.svelte');\nconst loadTimeEntryList = () => import('./components/TimeEntryList.svelte');\nconst loadTimeSummary = () => import('./components/TimeSummary.svelte');\nconst loadDevelopmentBoard = () => import('./DevelopmentBoard.svelte');\nconst loadProjectBoard = () => import('./ProjectBoard.svelte');\n\nexport default {\n packageName: '@happyvertical/smrt-projects',\n displayName: PROJECTS_MODULE_META.displayName,\n description: PROJECTS_MODULE_META.description,\n moduleMeta: PROJECTS_MODULE_META,\n entries: [\n {\n id: 'project-board',\n title: 'Project Board',\n description: 'Controlled provider project board with canonical statuses.',\n loadComponent: loadProjectBoard,\n order: 0,\n props: {\n projectId: 'project-demo',\n statuses: [\n { id: 'todo', name: 'Todo', order: 0 },\n { id: 'done', name: 'Done', order: 1 },\n ],\n items: [\n {\n id: 'item-demo',\n contentId: 'content-demo',\n title: 'Publish project board',\n status: 'Todo',\n fields: {},\n type: 'Issue',\n },\n ],\n },\n modes: { mock: { label: 'Mock' } },\n },\n {\n id: 'development-board',\n title: 'Development Board',\n description: 'Read-only delivery-status projection.',\n loadComponent: loadDevelopmentBoard,\n order: 1,\n props: {\n requests: [\n {\n id: 'request-demo',\n description: 'Publish project board',\n type: 'feature',\n status: 'planned',\n requesterLabel: 'Project owner',\n },\n ],\n },\n modes: { mock: { label: 'Mock' } },\n },\n {\n id: 'time-entry-list',\n title: 'Time Entry List',\n description:\n 'Selectable list view for recent time entries with billing context.',\n loadComponent: loadTimeEntryList,\n order: 2,\n props: {\n entries: sampleTimeEntries,\n selectable: true,\n selectedIds: ['time-entry-1'],\n currency: 'CAD',\n onselectionchange: noop,\n baseHref: '/projects/time-entries',\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'time-summary',\n title: 'Time Summary',\n description: 'Hours and value summary cards for a project review period.',\n loadComponent: loadTimeSummary,\n order: 3,\n props: {\n totalHours,\n totalAmount,\n pendingHours: pendingEntries.reduce(\n (sum, entry) => sum + entry.hours,\n 0,\n ),\n pendingAmount: pendingEntries.reduce(\n (sum, entry) => sum + (entry.amount ?? 0),\n 0,\n ),\n approvedHours: sampleTimeEntries\n .filter((entry) => entry.status === 'approved')\n .reduce((sum, entry) => sum + entry.hours, 0),\n approvedAmount: sampleTimeEntries\n .filter((entry) => entry.status === 'approved')\n .reduce((sum, entry) => sum + (entry.amount ?? 0), 0),\n entryCount: sampleTimeEntries.length,\n currency: 'CAD',\n showApproved: true,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'approval-actions',\n title: 'Approval Actions',\n description:\n 'Status-sensitive action row for approving or returning submitted work.',\n loadComponent: loadApprovalActions,\n order: 4,\n props: {\n status: 'submitted',\n onapprove: noop,\n onreject: noop,\n onedit: noop,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n ],\n};\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Browser-safe project-board move payload.
3
+ *
4
+ * This is the only board mutation data permitted to cross the browser/server
5
+ * boundary. Authorization and provider access are established server-side.
6
+ */
7
+ export interface ProjectBoardMoveIntent {
8
+ projectId: string;
9
+ itemId: string;
10
+ status: string;
11
+ }
12
+ //# sourceMappingURL=project-board-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-board-types.d.ts","sourceRoot":"","sources":["../src/project-board-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB"}
File without changes
@@ -51,5 +51,5 @@ export declare class ProjectDeliveryService {
51
51
  private upsertPreview;
52
52
  }
53
53
  export declare function requireIntegrationCapability(integration: ProjectIntegration, capability: string): void;
54
- export declare function requireActiveIntegrationCapability(integrations: ProjectIntegrationCollection, integration: ProjectIntegration, capability: string): Promise<ProjectIntegration>;
54
+ export declare function requireActiveIntegrationCapability(integrations: ProjectIntegrationCollection, integration: Pick<ProjectIntegration, 'id' | 'tenantId'>, capability: string): Promise<ProjectIntegration>;
55
55
  //# sourceMappingURL=delivery-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delivery-service.d.ts","sourceRoot":"","sources":["../../src/services/delivery-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AACrF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AAErF,OAAO,EACL,KAAK,eAAe,EACpB,yBAAyB,EACzB,KAAK,oBAAoB,EACzB,8BAA8B,EAE/B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAE/E,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,KAAK,EAAE;QAChB,WAAW,EAAE,kBAAkB,CAAC;QAChC,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB;AAED,qBAAa,sBAAsB;IAE/B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;gBALf,YAAY,EAAE,4BAA4B,EAC1C,QAAQ,EAAE,4BAA4B,EACtC,MAAM,EAAE,8BAA8B,EACtC,SAAS,EAAE,yBAAyB,EACpC,WAAW,CAAC,EAAE,sBAAsB,YAAA,EACpC,cAAc,CAAC,EAAE,cAAc,YAAA;WAGrC,MAAM,CACjB,OAAO,GAAE,gBAAqB,EAC9B,YAAY,GAAE;QACZ,WAAW,CAAC,EAAE,sBAAsB,CAAC;QACrC,cAAc,CAAC,EAAE,cAAc,CAAC;KAC5B,GACL,OAAO,CAAC,sBAAsB,CAAC;IAkB5B,MAAM,CAAC,KAAK,EAAE;QAClB,WAAW,EAAE,kBAAkB,CAAC;QAChC,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,iBAAiB,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,UAAU,CAAC,EAAE,IAAI,CAAC;KACnB,GAAG,OAAO,CAAC,oBAAoB,CAAC;YA4CnB,gBAAgB;IAuCxB,kBAAkB,CACtB,WAAW,EAAE,kBAAkB,EAC/B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAe5B,cAAc,CAClB,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC;IA4BV,MAAM,CACV,MAAM,EAAE,mBAAmB,EAC3B,WAAW,EAAE,kBAAkB,EAC/B,aAAa,SAAK,GACjB,OAAO,CAAC,IAAI,CAAC;IAiBV,aAAa,CACjB,WAAW,EAAE,kBAAkB,EAC/B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAC7D,OAAO,CAAC,eAAe,CAAC;IAsCrB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;YAS5D,qBAAqB;YAkBrB,qBAAqB;YAmBrB,aAAa;CA2B5B;AAED,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,kBAAkB,EAC/B,UAAU,EAAE,MAAM,GACjB,IAAI,CAKN;AAED,wBAAsB,kCAAkC,CACtD,YAAY,EAAE,4BAA4B,EAC1C,WAAW,EAAE,kBAAkB,EAC/B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAS7B"}
1
+ {"version":3,"file":"delivery-service.d.ts","sourceRoot":"","sources":["../../src/services/delivery-service.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export * from './assistance-request-service.js';
2
2
  export * from './delivery-service.js';
3
3
  export * from './development-request-service.js';
4
+ export * from './project-board-service.js';
4
5
  export * from './service-evidence-service.js';
5
6
  export * from './subscription-commercial-resolver.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,uBAAuB,CAAC;AACtC,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,uBAAuB,CAAC;AACtC,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { SmrtClassOptions } from '@happyvertical/smrt-core';
2
+ import { ProjectIntegrationCollection } from '../collections/ProjectIntegrations.js';
3
+ import { ProjectCollection } from '../collections/Projects.js';
4
+ import { ProjectIntegration } from '../models/ProjectIntegration.js';
5
+ import { ProjectBoardMoveIntent } from '../project-board-types.js';
6
+ export type { ProjectBoardMoveIntent } from '../project-board-types.js';
7
+ /** Pure data returned after a provider-backed project board move. */
8
+ export interface ProjectBoardMoveResult {
9
+ projectId: string;
10
+ itemId: string;
11
+ previousStatus: string | null;
12
+ status: string;
13
+ }
14
+ /**
15
+ * Capability-checked server boundary for project-board mutations.
16
+ *
17
+ * This service intentionally exposes neither provider credentials nor the
18
+ * provider client. It reloads the supplied server-derived integration before
19
+ * every mutation, verifies its project binding and capability, validates the
20
+ * requested item and status against the authoritative Project, then delegates
21
+ * persistence to Project.updateItemStatus().
22
+ */
23
+ export declare class ProjectBoardService {
24
+ private readonly integrations;
25
+ private readonly projects;
26
+ constructor(integrations: ProjectIntegrationCollection, projects: ProjectCollection);
27
+ static create(options?: SmrtClassOptions): Promise<ProjectBoardService>;
28
+ moveItem(integration: ProjectIntegration, input: ProjectBoardMoveIntent): Promise<ProjectBoardMoveResult>;
29
+ private requireAuthorizedIntegration;
30
+ private findProject;
31
+ }
32
+ //# sourceMappingURL=project-board-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-board-service.d.ts","sourceRoot":"","sources":["../../src/services/project-board-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAGxE,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAExE,qEAAqE;AACrE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,YAAY,EAAE,4BAA4B,EAC1C,QAAQ,EAAE,iBAAiB;WAGjC,MAAM,CACjB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,mBAAmB,CAAC;IASzB,QAAQ,CACZ,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,sBAAsB,CAAC;YAiCpB,4BAA4B;YAoB5B,WAAW;CAW1B"}
@@ -3,12 +3,12 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-projects",
6
- "packageVersion": "0.40.66",
6
+ "packageVersion": "0.40.67",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "6640c91b498d3769bfec83253d2601aa0a91ab4bc900c8ba8929f23a758621cd",
11
- "packageJson": "4ca79cf42a952beeb70a730cbc694486446f4bd1b1557df69ed00e3fb6f91525",
10
+ "manifest": "2196f921a200570d4fd4f6930c60472f7ef200d0feb8b9412a7b346b178d8523",
11
+ "packageJson": "b4023165676e3a4805b862e110a0f8cb70f1e82b6d103b90df4a0db10cebe40b",
12
12
  "agents": "413522a1eeb6062d2011e201074bf783cc501b21d07ca616aacc062db486fe7d"
13
13
  },
14
14
  "exports": [
@@ -27,6 +27,7 @@
27
27
  "@happyvertical/smrt-config": "workspace:*",
28
28
  "@happyvertical/smrt-core": "workspace:*",
29
29
  "@happyvertical/smrt-prompts": "workspace:*",
30
+ "@happyvertical/smrt-svelte": "workspace:*",
30
31
  "@happyvertical/smrt-subscriptions": "workspace:*",
31
32
  "@happyvertical/smrt-tenancy": "workspace:*",
32
33
  "@happyvertical/smrt-types": "workspace:*",
@@ -51,6 +52,7 @@
51
52
  "@happyvertical/smrt-core",
52
53
  "@happyvertical/smrt-prompts",
53
54
  "@happyvertical/smrt-subscriptions",
55
+ "@happyvertical/smrt-svelte",
54
56
  "@happyvertical/smrt-tenancy",
55
57
  "@happyvertical/smrt-types",
56
58
  "@happyvertical/smrt-ui",
@@ -1,8 +1,18 @@
1
1
  <script lang="ts">
2
+ import {
3
+ Board,
4
+ type BoardCard,
5
+ type BoardColumn,
6
+ } from '@happyvertical/smrt-svelte/board';
2
7
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
8
  import type { DevelopmentRequestView } from './delivery-types.js';
4
9
  import { M } from './i18n.js';
5
10
 
11
+ interface DevelopmentBoardCard extends BoardCard {
12
+ request: DevelopmentRequestView;
13
+ columnId: string;
14
+ }
15
+
6
16
  export interface Props {
7
17
  requests?: DevelopmentRequestView[];
8
18
  columns?: string[];
@@ -15,89 +25,59 @@ let {
15
25
  onselect,
16
26
  }: Props = $props();
17
27
  const { t } = useI18n();
18
- const inColumn = (column: string) =>
19
- requests.filter(
20
- (request) =>
21
- (request.deliveryStatus || request.status).toLowerCase() ===
22
- column.toLowerCase(),
23
- );
28
+ const boardColumns = $derived<BoardColumn[]>(
29
+ columns.map((column) => ({
30
+ id: column.toLowerCase(),
31
+ label: column.replaceAll('_', ' '),
32
+ })),
33
+ );
34
+ const cards = $derived<DevelopmentBoardCard[]>(
35
+ requests.map((request) => ({
36
+ id: request.id,
37
+ request,
38
+ columnId: (request.deliveryStatus || request.status).toLowerCase(),
39
+ })),
40
+ );
41
+ const cardLabel = (card: DevelopmentBoardCard) =>
42
+ card.request.title ?? card.request.description;
24
43
  </script>
25
44
 
26
- <section class="board" aria-label={t(M['projects.development_board.aria'])}>
27
- {#if requests.length === 0}
28
- <p class="empty">{t(M['projects.development_board.empty'])}</p>
29
- {:else}
30
- {#each columns as column (column)}
31
- <div class="lane">
32
- <header>
33
- <h3>{column.replaceAll('_', ' ')}</h3>
34
- <span>{inColumn(column).length}</span>
35
- </header>
36
- <div class="lane__items">
37
- {#each inColumn(column) as request (request.id)}
38
- <!-- raw-primitive-allow: each board row is a semantic selection control -->
39
- <button type="button" onclick={() => onselect?.(request)}>
40
- <strong>{request.title ?? request.description}</strong>
41
- <small>
42
- {request.type}
43
- {request.requesterLabel ? ` · ${request.requesterLabel}` : ''}
44
- </small>
45
- </button>
46
- {/each}
47
- </div>
48
- </div>
49
- {/each}
50
- {/if}
51
- </section>
45
+ {#if requests.length === 0}
46
+ <section
47
+ class="empty"
48
+ aria-label={t(M['projects.development_board.aria'])}
49
+ >
50
+ <p>{t(M['projects.development_board.empty'])}</p>
51
+ </section>
52
+ {:else}
53
+ <Board
54
+ columns={boardColumns}
55
+ {cards}
56
+ label={t(M['projects.development_board.aria'])}
57
+ getCardColumnId={(card) => card.columnId}
58
+ setCardColumnId={(card, columnId) => ({ ...card, columnId })}
59
+ getCardLabel={cardLabel}
60
+ onselect={(card) => onselect?.(card.request)}
61
+ >
62
+ {#snippet card({ card })}
63
+ <strong>{card.request.title ?? card.request.description}</strong>
64
+ <small>
65
+ {card.request.type}
66
+ {card.request.requesterLabel
67
+ ? ` · ${card.request.requesterLabel}`
68
+ : ''}
69
+ </small>
70
+ {/snippet}
71
+ </Board>
72
+ {/if}
52
73
 
53
74
  <style>
54
- .board {
55
- display: grid;
56
- gap: var(--smrt-spacing-5);
57
- grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
58
- overflow-x: auto;
59
- }
60
- .lane {
61
- min-width: 0;
62
- }
63
75
  .empty {
64
76
  color: var(--smrt-color-on-surface-variant);
65
- grid-column: 1 / -1;
66
77
  margin: 0;
67
78
  padding: var(--smrt-spacing-5) var(--smrt-spacing-1);
68
79
  }
69
- .lane header {
70
- align-items: center;
71
- border-bottom: 2px solid var(--smrt-color-primary);
72
- display: flex;
73
- justify-content: space-between;
74
- padding: var(--smrt-spacing-2) var(--smrt-spacing-1);
75
- }
76
- .lane h3 {
77
- font-size: var(--smrt-typography-label-medium-size);
78
- margin: 0;
79
- text-transform: uppercase;
80
- }
81
- .lane__items {
82
- display: grid;
83
- }
84
- .lane__items button {
85
- background: transparent;
86
- border: 0;
87
- border-bottom: 1px solid var(--smrt-color-outline-variant);
88
- color: inherit;
89
- cursor: pointer;
90
- display: grid;
91
- gap: var(--smrt-spacing-1);
92
- padding: var(--smrt-spacing-4) var(--smrt-spacing-1);
93
- text-align: left;
94
- transition: transform 120ms ease;
95
- }
96
- .lane__items button:hover,
97
- .lane__items button:focus-visible {
98
- transform: translateX(var(--smrt-spacing-1));
99
- }
100
- .lane small {
80
+ small {
101
81
  color: var(--smrt-color-on-surface-variant);
102
82
  }
103
83
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"DevelopmentBoard.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/DevelopmentBoard.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAIlE,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACtD;AAoDD,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"DevelopmentBoard.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/DevelopmentBoard.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AASlE,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACtD;AAsDD,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,157 @@
1
+ <script lang="ts">
2
+ import {
3
+ Board,
4
+ type BoardCard,
5
+ type BoardColumn,
6
+ type BoardMoveIntent,
7
+ } from '@happyvertical/smrt-svelte/board';
8
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
9
+ import type { ProjectItem, ProjectStatus } from '../types.js';
10
+ import { M } from './i18n.js';
11
+ import type { ProjectBoardMoveIntent } from './project-board-types.js';
12
+
13
+ interface ProjectBoardColumn extends BoardColumn {
14
+ status?: string;
15
+ }
16
+
17
+ interface ProjectBoardCard extends BoardCard {
18
+ item: ProjectItem;
19
+ columnId: string;
20
+ }
21
+
22
+ interface ProjectBoardBaseProps {
23
+ /** Provider project identity, forwarded only to the injected move action. */
24
+ projectId: string;
25
+ /** Canonical, ordered provider statuses. Their supplied order is preserved. */
26
+ statuses: readonly ProjectStatus[];
27
+ /** Authoritative provider items; this adapter never reorders them locally. */
28
+ items: readonly ProjectItem[];
29
+ onselect?: (item: ProjectItem) => void;
30
+ label?: string;
31
+ }
32
+
33
+ interface ReadOnlyProjectBoardProps {
34
+ /** Omit both callbacks to expose a read-only controlled board. */
35
+ onmove?: never;
36
+ onrefresh?: never;
37
+ }
38
+
39
+ interface MovableProjectBoardProps {
40
+ /**
41
+ * Browser-safe mutation boundary. Its consumer attaches authorization in a
42
+ * server action before it calls ProjectBoardService.
43
+ */
44
+ onmove: (intent: ProjectBoardMoveIntent) => void | Promise<void>;
45
+ /** Required reconciliation of controlled cards after every move attempt. */
46
+ onrefresh: () => void | Promise<void>;
47
+ }
48
+
49
+ /** A controlled project board is movable only with an authoritative refresh. */
50
+ export type Props = ProjectBoardBaseProps &
51
+ (ReadOnlyProjectBoardProps | MovableProjectBoardProps);
52
+
53
+ let { projectId, statuses, items, onmove, onrefresh, onselect, label }: Props =
54
+ $props();
55
+
56
+ const { t } = useI18n();
57
+ const unmatchedColumnId = '__smrt_projects_unmatched__';
58
+ const statusByName = $derived(
59
+ new Map(statuses.map((status) => [status.name, status])),
60
+ );
61
+ const hasUnmatchedItems = $derived(
62
+ items.some((item) => !statusByName.has(item.status ?? '')),
63
+ );
64
+ const movementEnabled = $derived(
65
+ onmove !== undefined && onrefresh !== undefined,
66
+ );
67
+ const columns = $derived<ProjectBoardColumn[]>([
68
+ ...statuses.map((status) => ({
69
+ id: status.id,
70
+ label: status.name,
71
+ status: status.name,
72
+ })),
73
+ ...(hasUnmatchedItems
74
+ ? [
75
+ {
76
+ id: unmatchedColumnId,
77
+ label: t(M['projects.project_board.unassigned']),
78
+ disabled: true,
79
+ },
80
+ ]
81
+ : []),
82
+ ]);
83
+ const cards = $derived<ProjectBoardCard[]>(
84
+ items.map((item) => ({
85
+ id: item.id,
86
+ item,
87
+ columnId: statusByName.has(item.status ?? '')
88
+ ? (statusByName.get(item.status ?? '')?.id ?? unmatchedColumnId)
89
+ : unmatchedColumnId,
90
+ })),
91
+ );
92
+
93
+ async function refresh(): Promise<void> {
94
+ if (!onrefresh) throw new Error('Project board refresh is required.');
95
+ await onrefresh();
96
+ }
97
+
98
+ async function move(
99
+ intent: BoardMoveIntent<ProjectBoardCard, ProjectBoardColumn>,
100
+ ): Promise<void> {
101
+ const target = columns.find((column) => column.id === intent.target.columnId);
102
+ if (!target?.status || target.disabled || !onmove || !onrefresh) {
103
+ throw new Error('Project board move failed.');
104
+ }
105
+
106
+ try {
107
+ await onmove({
108
+ projectId,
109
+ itemId: intent.card.item.id,
110
+ status: target.status,
111
+ });
112
+ await refresh();
113
+ } catch {
114
+ // Controlled cards never become local truth. Re-fetch even when the
115
+ // mutation fails so a provider-side partial success is corrected.
116
+ try {
117
+ await refresh();
118
+ } catch {
119
+ // The original operation's failure is intentionally not exposed to the
120
+ // browser; it may contain provider details.
121
+ }
122
+ // Board owns the single live-region announcement and focus restoration.
123
+ throw new Error('Project board move failed.');
124
+ }
125
+ }
126
+
127
+ function cardLabel(card: ProjectBoardCard): string {
128
+ return card.item.title ?? card.item.id;
129
+ }
130
+ </script>
131
+
132
+ <Board
133
+ {columns}
134
+ {cards}
135
+ label={label ?? t(M['projects.project_board.aria'])}
136
+ getCardColumnId={(card) => card.columnId}
137
+ setCardColumnId={(card, columnId) => ({ ...card, columnId })}
138
+ getCardLabel={cardLabel}
139
+ allowSameColumnReorder={false}
140
+ onmove={movementEnabled ? move : undefined}
141
+ onselect={(card) => onselect?.(card.item)}
142
+ >
143
+ {#snippet card({ card })}
144
+ <!-- raw-primitive-allow: each board card is a semantic project selection control -->
145
+ <strong>{card.item.title ?? card.item.id}</strong>
146
+ <small>{card.item.type}</small>
147
+ {#if card.item.assignees?.length}
148
+ <small>{card.item.assignees.join(', ')}</small>
149
+ {/if}
150
+ {/snippet}
151
+ </Board>
152
+
153
+ <style>
154
+ small {
155
+ color: var(--smrt-color-on-surface-variant);
156
+ }
157
+ </style>
@@ -0,0 +1,32 @@
1
+ import type { ProjectItem, ProjectStatus } from '../types.js';
2
+ import type { ProjectBoardMoveIntent } from './project-board-types.js';
3
+ interface ProjectBoardBaseProps {
4
+ /** Provider project identity, forwarded only to the injected move action. */
5
+ projectId: string;
6
+ /** Canonical, ordered provider statuses. Their supplied order is preserved. */
7
+ statuses: readonly ProjectStatus[];
8
+ /** Authoritative provider items; this adapter never reorders them locally. */
9
+ items: readonly ProjectItem[];
10
+ onselect?: (item: ProjectItem) => void;
11
+ label?: string;
12
+ }
13
+ interface ReadOnlyProjectBoardProps {
14
+ /** Omit both callbacks to expose a read-only controlled board. */
15
+ onmove?: never;
16
+ onrefresh?: never;
17
+ }
18
+ interface MovableProjectBoardProps {
19
+ /**
20
+ * Browser-safe mutation boundary. Its consumer attaches authorization in a
21
+ * server action before it calls ProjectBoardService.
22
+ */
23
+ onmove: (intent: ProjectBoardMoveIntent) => void | Promise<void>;
24
+ /** Required reconciliation of controlled cards after every move attempt. */
25
+ onrefresh: () => void | Promise<void>;
26
+ }
27
+ /** A controlled project board is movable only with an authoritative refresh. */
28
+ export type Props = ProjectBoardBaseProps & (ReadOnlyProjectBoardProps | MovableProjectBoardProps);
29
+ declare const ProjectBoard: import("svelte").Component<Props, {}, "">;
30
+ type ProjectBoard = ReturnType<typeof ProjectBoard>;
31
+ export default ProjectBoard;
32
+ //# sourceMappingURL=ProjectBoard.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectBoard.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/ProjectBoard.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAYvE,UAAU,qBAAqB;IAC7B,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,8EAA8E;IAC9E,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,yBAAyB;IACjC,kEAAkE;IAClE,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,SAAS,CAAC,EAAE,KAAK,CAAC;CACnB;AAED,UAAU,wBAAwB;IAChC;;;OAGG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,4EAA4E;IAC5E,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,gFAAgF;AAChF,MAAM,MAAM,KAAK,GAAG,qBAAqB,GACvC,CAAC,yBAAyB,GAAG,wBAAwB,CAAC,CAAC;AA4GzD,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}