@kelpie/server 0.12.0 → 0.13.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.
- package/dist/app.d.ts +13 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +8 -1
- package/dist/app.js.map +1 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/boot.js +1 -0
- package/dist/boot.js.map +1 -1
- package/dist/lib/apiKeyScopes.d.ts.map +1 -1
- package/dist/lib/apiKeyScopes.js +6 -0
- package/dist/lib/apiKeyScopes.js.map +1 -1
- package/dist/lib/columns.d.ts +6 -0
- package/dist/lib/columns.d.ts.map +1 -1
- package/dist/lib/columns.js +5 -3
- package/dist/lib/columns.js.map +1 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +3 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/egress.d.ts.map +1 -1
- package/dist/lib/egress.js +8 -4
- package/dist/lib/egress.js.map +1 -1
- package/dist/lib/errors.d.ts +3 -3
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +13 -5
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/ids.d.ts +11 -0
- package/dist/lib/ids.d.ts.map +1 -1
- package/dist/lib/ids.js +11 -0
- package/dist/lib/ids.js.map +1 -1
- package/dist/lib/kelpieConfigFile.d.ts +15 -1
- package/dist/lib/kelpieConfigFile.d.ts.map +1 -1
- package/dist/lib/kelpieConfigFile.js +33 -1
- package/dist/lib/kelpieConfigFile.js.map +1 -1
- package/dist/modules/agent-tasks/catalog.d.ts.map +1 -1
- package/dist/modules/agent-tasks/catalog.js +32 -1
- package/dist/modules/agent-tasks/catalog.js.map +1 -1
- package/dist/modules/agent-tasks/index.d.ts.map +1 -1
- package/dist/modules/agent-tasks/index.js +1 -0
- package/dist/modules/agent-tasks/index.js.map +1 -1
- package/dist/modules/agent-tasks/resolve.d.ts.map +1 -1
- package/dist/modules/agent-tasks/resolve.js +5 -0
- package/dist/modules/agent-tasks/resolve.js.map +1 -1
- package/dist/modules/agent-tasks/routes.d.ts +2 -0
- package/dist/modules/agent-tasks/routes.d.ts.map +1 -1
- package/dist/modules/attachedRecords.d.ts +22 -8
- package/dist/modules/attachedRecords.d.ts.map +1 -1
- package/dist/modules/attachedRecords.js +42 -5
- package/dist/modules/attachedRecords.js.map +1 -1
- package/dist/modules/auth/index.d.ts.map +1 -1
- package/dist/modules/auth/index.js +1 -0
- package/dist/modules/auth/index.js.map +1 -1
- package/dist/modules/auth/repository.d.ts +14 -0
- package/dist/modules/auth/repository.d.ts.map +1 -1
- package/dist/modules/auth/repository.js +20 -2
- package/dist/modules/auth/repository.js.map +1 -1
- package/dist/modules/auth/routes.d.ts.map +1 -1
- package/dist/modules/auth/routes.js +30 -0
- package/dist/modules/auth/routes.js.map +1 -1
- package/dist/modules/auth/service.d.ts +28 -0
- package/dist/modules/auth/service.d.ts.map +1 -1
- package/dist/modules/auth/service.js +72 -4
- package/dist/modules/auth/service.js.map +1 -1
- package/dist/modules/companies/repository.d.ts.map +1 -1
- package/dist/modules/companies/repository.js +1 -1
- package/dist/modules/companies/repository.js.map +1 -1
- package/dist/modules/companies/routes.d.ts +2 -4
- package/dist/modules/companies/routes.d.ts.map +1 -1
- package/dist/modules/companies/routes.js +6 -11
- package/dist/modules/companies/routes.js.map +1 -1
- package/dist/modules/companies/schema.d.ts +13 -28
- package/dist/modules/companies/schema.d.ts.map +1 -1
- package/dist/modules/companies/schema.js +2 -2
- package/dist/modules/companies/schema.js.map +1 -1
- package/dist/modules/companies/service.d.ts +2 -3
- package/dist/modules/companies/service.d.ts.map +1 -1
- package/dist/modules/companies/service.js +4 -7
- package/dist/modules/companies/service.js.map +1 -1
- package/dist/modules/core.d.ts.map +1 -1
- package/dist/modules/core.js +2 -0
- package/dist/modules/core.js.map +1 -1
- package/dist/modules/custom-fields/repository.d.ts.map +1 -1
- package/dist/modules/custom-fields/repository.js +2 -0
- package/dist/modules/custom-fields/repository.js.map +1 -1
- package/dist/modules/custom-fields/routes.d.ts +1 -0
- package/dist/modules/custom-fields/routes.d.ts.map +1 -1
- package/dist/modules/dashboard/index.d.ts.map +1 -1
- package/dist/modules/dashboard/index.js +1 -0
- package/dist/modules/dashboard/index.js.map +1 -1
- package/dist/modules/dashboard/repository.d.ts +13 -0
- package/dist/modules/dashboard/repository.d.ts.map +1 -1
- package/dist/modules/dashboard/repository.js +36 -0
- package/dist/modules/dashboard/repository.js.map +1 -1
- package/dist/modules/dashboard/routes.d.ts.map +1 -1
- package/dist/modules/dashboard/routes.js +13 -0
- package/dist/modules/dashboard/routes.js.map +1 -1
- package/dist/modules/dashboard/service.d.ts +11 -0
- package/dist/modules/dashboard/service.d.ts.map +1 -1
- package/dist/modules/dashboard/service.js +24 -1
- package/dist/modules/dashboard/service.js.map +1 -1
- package/dist/modules/decisions/repository.d.ts.map +1 -1
- package/dist/modules/decisions/repository.js +2 -0
- package/dist/modules/decisions/repository.js.map +1 -1
- package/dist/modules/decisions/routes.d.ts +2 -0
- package/dist/modules/decisions/routes.d.ts.map +1 -1
- package/dist/modules/events/attendances.d.ts +35 -0
- package/dist/modules/events/attendances.d.ts.map +1 -0
- package/dist/modules/events/attendances.js +157 -0
- package/dist/modules/events/attendances.js.map +1 -0
- package/dist/modules/events/catalog.d.ts +35 -0
- package/dist/modules/events/catalog.d.ts.map +1 -0
- package/dist/modules/events/catalog.js +11 -0
- package/dist/modules/events/catalog.js.map +1 -0
- package/dist/modules/events/index.d.ts +10 -0
- package/dist/modules/events/index.d.ts.map +1 -0
- package/dist/modules/events/index.js +47 -0
- package/dist/modules/events/index.js.map +1 -0
- package/dist/modules/events/repository.d.ts +67 -0
- package/dist/modules/events/repository.d.ts.map +1 -0
- package/dist/modules/events/repository.js +212 -0
- package/dist/modules/events/repository.js.map +1 -0
- package/dist/modules/events/routes.d.ts +136 -0
- package/dist/modules/events/routes.d.ts.map +1 -0
- package/dist/modules/events/routes.js +280 -0
- package/dist/modules/events/routes.js.map +1 -0
- package/dist/modules/events/schema.d.ts +626 -0
- package/dist/modules/events/schema.d.ts.map +1 -0
- package/dist/modules/events/schema.js +93 -0
- package/dist/modules/events/schema.js.map +1 -0
- package/dist/modules/events/service.d.ts +68 -0
- package/dist/modules/events/service.d.ts.map +1 -0
- package/dist/modules/events/service.js +262 -0
- package/dist/modules/events/service.js.map +1 -0
- package/dist/modules/events/tools.d.ts +8 -0
- package/dist/modules/events/tools.d.ts.map +1 -0
- package/dist/modules/events/tools.js +80 -0
- package/dist/modules/events/tools.js.map +1 -0
- package/dist/modules/forms/applySubmissionFields.d.ts.map +1 -1
- package/dist/modules/forms/applySubmissionFields.js +7 -5
- package/dist/modules/forms/applySubmissionFields.js.map +1 -1
- package/dist/modules/forms/fillAddresses.d.ts +4 -0
- package/dist/modules/forms/fillAddresses.d.ts.map +1 -0
- package/dist/modules/forms/fillAddresses.js +93 -0
- package/dist/modules/forms/fillAddresses.js.map +1 -0
- package/dist/modules/forms/repository.d.ts +2 -2
- package/dist/modules/forms/repository.d.ts.map +1 -1
- package/dist/modules/forms/repository.js.map +1 -1
- package/dist/modules/forms/routes.d.ts +2 -0
- package/dist/modules/forms/routes.d.ts.map +1 -1
- package/dist/modules/forms/routes.js +2 -2
- package/dist/modules/forms/routes.js.map +1 -1
- package/dist/modules/forms/schema.js +2 -2
- package/dist/modules/forms/schema.js.map +1 -1
- package/dist/modules/forms/service.js +2 -2
- package/dist/modules/forms/service.js.map +1 -1
- package/dist/modules/forms/submission.d.ts +1 -0
- package/dist/modules/forms/submission.d.ts.map +1 -1
- package/dist/modules/forms/submission.js +27 -4
- package/dist/modules/forms/submission.js.map +1 -1
- package/dist/modules/hiring/roles.d.ts.map +1 -1
- package/dist/modules/hiring/roles.js +2 -1
- package/dist/modules/hiring/roles.js.map +1 -1
- package/dist/modules/import-export/addresses.d.ts +22 -0
- package/dist/modules/import-export/addresses.d.ts.map +1 -0
- package/dist/modules/import-export/addresses.js +83 -0
- package/dist/modules/import-export/addresses.js.map +1 -0
- package/dist/modules/import-export/drafts.d.ts +3 -4
- package/dist/modules/import-export/drafts.d.ts.map +1 -1
- package/dist/modules/import-export/drafts.js +4 -5
- package/dist/modules/import-export/drafts.js.map +1 -1
- package/dist/modules/import-export/exportRows.d.ts.map +1 -1
- package/dist/modules/import-export/exportRows.js +3 -3
- package/dist/modules/import-export/exportRows.js.map +1 -1
- package/dist/modules/import-export/plan.d.ts.map +1 -1
- package/dist/modules/import-export/plan.js +17 -2
- package/dist/modules/import-export/plan.js.map +1 -1
- package/dist/modules/import-export/presets.d.ts.map +1 -1
- package/dist/modules/import-export/presets.js +52 -7
- package/dist/modules/import-export/presets.js.map +1 -1
- package/dist/modules/import-export/repository.d.ts +3 -4
- package/dist/modules/import-export/repository.d.ts.map +1 -1
- package/dist/modules/import-export/repository.js +2 -3
- package/dist/modules/import-export/repository.js.map +1 -1
- package/dist/modules/import-export/writes.d.ts.map +1 -1
- package/dist/modules/import-export/writes.js +8 -4
- package/dist/modules/import-export/writes.js.map +1 -1
- package/dist/modules/lists/routes.d.ts +4 -0
- package/dist/modules/lists/routes.d.ts.map +1 -1
- package/dist/modules/notes/routes.d.ts +2 -0
- package/dist/modules/notes/routes.d.ts.map +1 -1
- package/dist/modules/people/repository.d.ts.map +1 -1
- package/dist/modules/people/repository.js +1 -1
- package/dist/modules/people/repository.js.map +1 -1
- package/dist/modules/people/routes.d.ts +2 -2
- package/dist/modules/people/routes.js +6 -6
- package/dist/modules/people/routes.js.map +1 -1
- package/dist/modules/people/schema.d.ts +17 -14
- package/dist/modules/people/schema.d.ts.map +1 -1
- package/dist/modules/people/schema.js +6 -4
- package/dist/modules/people/schema.js.map +1 -1
- package/dist/modules/people/service.d.ts +2 -2
- package/dist/modules/people/service.d.ts.map +1 -1
- package/dist/modules/people/service.js +5 -4
- package/dist/modules/people/service.js.map +1 -1
- package/dist/modules/plans/repository.d.ts +2 -2
- package/dist/modules/plans/repository.d.ts.map +1 -1
- package/dist/modules/plans/routes.d.ts +1 -0
- package/dist/modules/plans/routes.d.ts.map +1 -1
- package/dist/modules/plans/routes.js +4 -4
- package/dist/modules/plans/routes.js.map +1 -1
- package/dist/modules/plans/schema.d.ts +2 -2
- package/dist/modules/plans/schema.d.ts.map +1 -1
- package/dist/modules/plans/schema.js +3 -3
- package/dist/modules/plans/schema.js.map +1 -1
- package/dist/modules/plans/service.d.ts +2 -2
- package/dist/modules/plans/service.d.ts.map +1 -1
- package/dist/modules/plans/service.js.map +1 -1
- package/dist/modules/plans/tools.js +2 -2
- package/dist/modules/plans/tools.js.map +1 -1
- package/dist/modules/recordTargets.d.ts.map +1 -1
- package/dist/modules/recordTargets.js +11 -0
- package/dist/modules/recordTargets.js.map +1 -1
- package/dist/modules/references.d.ts.map +1 -1
- package/dist/modules/references.js +2 -0
- package/dist/modules/references.js.map +1 -1
- package/dist/modules/sample-data/fixture.d.ts +26 -4
- package/dist/modules/sample-data/fixture.d.ts.map +1 -1
- package/dist/modules/sample-data/fixture.js +157 -23
- package/dist/modules/sample-data/fixture.js.map +1 -1
- package/dist/modules/sample-data/index.d.ts +5 -2
- package/dist/modules/sample-data/index.d.ts.map +1 -1
- package/dist/modules/sample-data/index.js +7 -2
- package/dist/modules/sample-data/index.js.map +1 -1
- package/dist/modules/sample-data/routes.d.ts.map +1 -1
- package/dist/modules/sample-data/routes.js +2 -0
- package/dist/modules/sample-data/routes.js.map +1 -1
- package/dist/modules/sample-data/service.d.ts +11 -3
- package/dist/modules/sample-data/service.d.ts.map +1 -1
- package/dist/modules/sample-data/service.js +452 -363
- package/dist/modules/sample-data/service.js.map +1 -1
- package/dist/modules/sample-data/tools.d.ts.map +1 -1
- package/dist/modules/sample-data/tools.js +4 -3
- package/dist/modules/sample-data/tools.js.map +1 -1
- package/dist/modules/search/index.d.ts +1 -1
- package/dist/modules/search/index.d.ts.map +1 -1
- package/dist/modules/search/index.js +2 -1
- package/dist/modules/search/index.js.map +1 -1
- package/dist/modules/search/repository.d.ts +1 -0
- package/dist/modules/search/repository.d.ts.map +1 -1
- package/dist/modules/search/repository.js +22 -0
- package/dist/modules/search/repository.js.map +1 -1
- package/dist/modules/search/service.d.ts +2 -2
- package/dist/modules/search/service.d.ts.map +1 -1
- package/dist/modules/search/service.js +4 -3
- package/dist/modules/search/service.js.map +1 -1
- package/dist/modules/smtp-email/index.d.ts +12 -4
- package/dist/modules/smtp-email/index.d.ts.map +1 -1
- package/dist/modules/smtp-email/index.js +25 -5
- package/dist/modules/smtp-email/index.js.map +1 -1
- package/dist/modules/workspace/repository.d.ts +2 -0
- package/dist/modules/workspace/repository.d.ts.map +1 -1
- package/dist/modules/workspace/repository.js +12 -0
- package/dist/modules/workspace/repository.js.map +1 -1
- package/dist/modules/workspace/routes.d.ts.map +1 -1
- package/dist/modules/workspace/routes.js +13 -0
- package/dist/modules/workspace/routes.js.map +1 -1
- package/dist/modules/workspace/service.d.ts +10 -0
- package/dist/modules/workspace/service.d.ts.map +1 -1
- package/dist/modules/workspace/service.js +34 -10
- package/dist/modules/workspace/service.js.map +1 -1
- package/dist/modules/workspace/starters.d.ts +17 -2
- package/dist/modules/workspace/starters.d.ts.map +1 -1
- package/dist/modules/workspace/starters.js +141 -14
- package/dist/modules/workspace/starters.js.map +1 -1
- package/dist/modules/workspace/workspaceAccessMiddleware.d.ts.map +1 -1
- package/dist/modules/workspace/workspaceAccessMiddleware.js +3 -4
- package/dist/modules/workspace/workspaceAccessMiddleware.js.map +1 -1
- package/dist/runtime/events.d.ts +1 -1
- package/dist/runtime/events.d.ts.map +1 -1
- package/dist/runtime/events.js +2 -0
- package/dist/runtime/events.js.map +1 -1
- package/dist/runtime/module.d.ts +8 -0
- package/dist/runtime/module.d.ts.map +1 -1
- package/dist/runtime/registry.d.ts.map +1 -1
- package/dist/runtime/registry.js +102 -7
- package/dist/runtime/registry.js.map +1 -1
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/testing/app.d.ts +8 -0
- package/dist/testing/app.d.ts.map +1 -1
- package/dist/testing/app.js +6 -1
- package/dist/testing/app.js.map +1 -1
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +9 -1
- package/dist/testing/client.js.map +1 -1
- package/migrations/0044_events.sql +79 -0
- package/migrations/0045_addresses.sql +66 -0
- package/migrations/0046_drop_company_website.sql +66 -0
- package/migrations/meta/_journal.json +21 -0
- package/package.json +3 -3
- package/src/app.ts +25 -1
- package/src/boot.ts +1 -0
- package/src/lib/apiKeyScopes.ts +6 -0
- package/src/lib/columns.ts +12 -3
- package/src/lib/config.ts +21 -0
- package/src/lib/egress.ts +8 -4
- package/src/lib/errors.ts +16 -9
- package/src/lib/ids.ts +11 -0
- package/src/lib/kelpieConfigFile.ts +69 -1
- package/src/modules/agent-tasks/catalog.ts +33 -1
- package/src/modules/agent-tasks/index.ts +1 -0
- package/src/modules/agent-tasks/resolve.ts +11 -0
- package/src/modules/attachedRecords.ts +85 -13
- package/src/modules/auth/index.ts +1 -0
- package/src/modules/auth/repository.ts +34 -3
- package/src/modules/auth/routes.ts +44 -1
- package/src/modules/auth/service.ts +118 -4
- package/src/modules/companies/repository.ts +1 -0
- package/src/modules/companies/routes.ts +6 -11
- package/src/modules/companies/schema.ts +3 -3
- package/src/modules/companies/service.ts +6 -10
- package/src/modules/core.ts +2 -0
- package/src/modules/custom-fields/repository.ts +2 -0
- package/src/modules/dashboard/index.ts +1 -0
- package/src/modules/dashboard/repository.ts +64 -0
- package/src/modules/dashboard/routes.ts +15 -0
- package/src/modules/dashboard/service.ts +48 -0
- package/src/modules/decisions/repository.ts +2 -0
- package/src/modules/events/attendances.ts +245 -0
- package/src/modules/events/catalog.ts +36 -0
- package/src/modules/events/index.ts +54 -0
- package/src/modules/events/repository.ts +396 -0
- package/src/modules/events/routes.ts +405 -0
- package/src/modules/events/schema.ts +148 -0
- package/src/modules/events/service.ts +443 -0
- package/src/modules/events/tools.ts +118 -0
- package/src/modules/forms/applySubmissionFields.ts +7 -5
- package/src/modules/forms/fillAddresses.ts +185 -0
- package/src/modules/forms/repository.ts +3 -3
- package/src/modules/forms/routes.ts +2 -2
- package/src/modules/forms/schema.ts +2 -2
- package/src/modules/forms/service.ts +2 -2
- package/src/modules/forms/submission.ts +34 -4
- package/src/modules/hiring/roles.ts +2 -1
- package/src/modules/import-export/addresses.ts +150 -0
- package/src/modules/import-export/drafts.ts +7 -9
- package/src/modules/import-export/exportRows.ts +3 -3
- package/src/modules/import-export/plan.ts +30 -2
- package/src/modules/import-export/presets.ts +55 -7
- package/src/modules/import-export/repository.ts +5 -7
- package/src/modules/import-export/writes.ts +8 -4
- package/src/modules/people/repository.ts +1 -0
- package/src/modules/people/routes.ts +6 -6
- package/src/modules/people/schema.ts +7 -5
- package/src/modules/people/service.ts +7 -5
- package/src/modules/plans/repository.ts +2 -2
- package/src/modules/plans/routes.ts +6 -6
- package/src/modules/plans/schema.ts +4 -4
- package/src/modules/plans/service.ts +3 -3
- package/src/modules/plans/tools.ts +2 -2
- package/src/modules/recordTargets.ts +14 -0
- package/src/modules/references.ts +2 -0
- package/src/modules/sample-data/fixture.ts +208 -27
- package/src/modules/sample-data/index.ts +7 -2
- package/src/modules/sample-data/routes.ts +2 -0
- package/src/modules/sample-data/service.ts +166 -49
- package/src/modules/sample-data/tools.ts +4 -3
- package/src/modules/search/index.ts +2 -1
- package/src/modules/search/repository.ts +32 -1
- package/src/modules/search/service.ts +6 -4
- package/src/modules/smtp-email/index.ts +32 -10
- package/src/modules/workspace/repository.ts +18 -0
- package/src/modules/workspace/routes.ts +16 -0
- package/src/modules/workspace/service.ts +59 -14
- package/src/modules/workspace/starters.ts +275 -14
- package/src/modules/workspace/workspaceAccessMiddleware.ts +3 -4
- package/src/runtime/events.ts +2 -0
- package/src/runtime/module.ts +8 -0
- package/src/runtime/registry.ts +122 -8
- package/src/schema/index.ts +1 -0
- package/src/testing/app.ts +14 -1
- package/src/testing/client.ts +11 -1
|
@@ -9,6 +9,7 @@ import { activities } from '../activities/schema.ts'
|
|
|
9
9
|
import { decisions } from '../decisions/schema.ts'
|
|
10
10
|
import { deals } from '../deals/schema.ts'
|
|
11
11
|
import { enquiries } from '../enquiries/schema.ts'
|
|
12
|
+
import { attendances, events } from '../events/schema.ts'
|
|
12
13
|
import { notes } from '../notes/schema.ts'
|
|
13
14
|
import { opportunities } from '../opportunities/schema.ts'
|
|
14
15
|
import { partnerships } from '../partnerships/schema.ts'
|
|
@@ -242,6 +243,69 @@ export async function listPartnershipTouchpoints(
|
|
|
242
243
|
)
|
|
243
244
|
}
|
|
244
245
|
|
|
246
|
+
/** A scheduled Event whose start is inside `[from, to)`. */
|
|
247
|
+
export interface UpcomingEventRecord {
|
|
248
|
+
readonly id: string
|
|
249
|
+
readonly name: string
|
|
250
|
+
readonly startsAt: Date
|
|
251
|
+
readonly endsAt: Date
|
|
252
|
+
readonly format: string
|
|
253
|
+
readonly location: string
|
|
254
|
+
readonly attendeeCount: number
|
|
255
|
+
readonly ownerId: string | null
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function upcomingEventConditions(workspaceId: string, from: Date, to: Date): (SQL | undefined)[] {
|
|
259
|
+
return [
|
|
260
|
+
eq(events.workspaceId, workspaceId),
|
|
261
|
+
eq(events.status, 'scheduled'),
|
|
262
|
+
gte(events.startsAt, from),
|
|
263
|
+
lt(events.startsAt, to),
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export async function countUpcomingEvents(
|
|
268
|
+
db: Queryable,
|
|
269
|
+
workspaceId: string,
|
|
270
|
+
from: Date,
|
|
271
|
+
to: Date,
|
|
272
|
+
): Promise<number> {
|
|
273
|
+
const [row] = await db
|
|
274
|
+
.select({ total: count() })
|
|
275
|
+
.from(events)
|
|
276
|
+
.where(and(...upcomingEventConditions(workspaceId, from, to)))
|
|
277
|
+
|
|
278
|
+
return row?.total ?? 0
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export async function listUpcomingEvents(
|
|
282
|
+
db: Queryable,
|
|
283
|
+
workspaceId: string,
|
|
284
|
+
from: Date,
|
|
285
|
+
to: Date,
|
|
286
|
+
limit: number,
|
|
287
|
+
): Promise<readonly UpcomingEventRecord[]> {
|
|
288
|
+
const rows = await db
|
|
289
|
+
.select({
|
|
290
|
+
id: events.id,
|
|
291
|
+
name: events.name,
|
|
292
|
+
startsAt: events.startsAt,
|
|
293
|
+
endsAt: events.endsAt,
|
|
294
|
+
format: events.format,
|
|
295
|
+
location: events.location,
|
|
296
|
+
ownerId: events.ownerId,
|
|
297
|
+
attendeeCount: sql<number>`coalesce(count(${attendances.id}) filter (where ${attendances.status} <> 'cancelled'), 0)::int`,
|
|
298
|
+
})
|
|
299
|
+
.from(events)
|
|
300
|
+
.leftJoin(attendances, eq(attendances.eventId, events.id))
|
|
301
|
+
.where(and(...upcomingEventConditions(workspaceId, from, to)))
|
|
302
|
+
.groupBy(events.id)
|
|
303
|
+
.orderBy(asc(events.startsAt), asc(events.id))
|
|
304
|
+
.limit(limit)
|
|
305
|
+
|
|
306
|
+
return rows
|
|
307
|
+
}
|
|
308
|
+
|
|
245
309
|
/**
|
|
246
310
|
* The calendar day a contact happened on, in the workspace's zone.
|
|
247
311
|
*
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
SignalList,
|
|
16
16
|
StaleContactSignal,
|
|
17
17
|
TouchpointSignal,
|
|
18
|
+
UpcomingEventSignal,
|
|
18
19
|
} from './service.ts'
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -83,6 +84,19 @@ function touchpointBody(touchpoint: TouchpointSignal): Record<string, unknown> {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
function upcomingEventBody(event: UpcomingEventSignal): Record<string, unknown> {
|
|
88
|
+
return {
|
|
89
|
+
id: event.id,
|
|
90
|
+
name: event.name,
|
|
91
|
+
starts_at: event.startsAt.toISOString(),
|
|
92
|
+
ends_at: event.endsAt.toISOString(),
|
|
93
|
+
format: event.format,
|
|
94
|
+
location: event.location,
|
|
95
|
+
attendee_count: event.attendeeCount,
|
|
96
|
+
owner_id: event.ownerId,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
86
100
|
function staleContactBody(contact: StaleContactSignal): Record<string, unknown> {
|
|
87
101
|
return {
|
|
88
102
|
id: contact.id,
|
|
@@ -147,6 +161,7 @@ export function dashboardResponse(snapshot: DashboardSnapshot): Record<string, u
|
|
|
147
161
|
overdue_plan_items: signalListBody(snapshot.overduePlanItems, planItemBody),
|
|
148
162
|
due_soon_plan_items: signalListBody(snapshot.dueSoonPlanItems, planItemBody),
|
|
149
163
|
partnership_touchpoints: signalListBody(snapshot.partnershipTouchpoints, touchpointBody),
|
|
164
|
+
upcoming_events: signalListBody(snapshot.upcomingEvents, upcomingEventBody),
|
|
150
165
|
stale_contacts: signalListBody(snapshot.staleContacts, staleContactBody),
|
|
151
166
|
recent_activity: snapshot.recentActivity.map(activityBody),
|
|
152
167
|
recent_notes: snapshot.recentNotes.map(noteBody),
|
|
@@ -117,6 +117,17 @@ export interface DecisionSignal extends TargetRef {
|
|
|
117
117
|
readonly dueAt: Date | null
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
export interface UpcomingEventSignal {
|
|
121
|
+
readonly id: string
|
|
122
|
+
readonly name: string
|
|
123
|
+
readonly startsAt: Date
|
|
124
|
+
readonly endsAt: Date
|
|
125
|
+
readonly format: string
|
|
126
|
+
readonly location: string
|
|
127
|
+
readonly attendeeCount: number
|
|
128
|
+
readonly ownerId: string | null
|
|
129
|
+
}
|
|
130
|
+
|
|
120
131
|
export interface DashboardSnapshot {
|
|
121
132
|
readonly generatedAt: Date
|
|
122
133
|
/** The day everything below was computed against, `YYYY-MM-DD`. */
|
|
@@ -129,6 +140,7 @@ export interface DashboardSnapshot {
|
|
|
129
140
|
readonly overduePlanItems: SignalList<PlanItemSignal>
|
|
130
141
|
readonly dueSoonPlanItems: SignalList<PlanItemSignal>
|
|
131
142
|
readonly partnershipTouchpoints: SignalList<TouchpointSignal>
|
|
143
|
+
readonly upcomingEvents: SignalList<UpcomingEventSignal>
|
|
132
144
|
readonly staleContacts: SignalList<StaleContactSignal>
|
|
133
145
|
readonly recentActivity: readonly ActivitySignal[]
|
|
134
146
|
readonly recentNotes: readonly NoteSignal[]
|
|
@@ -155,6 +167,10 @@ interface DayBounds {
|
|
|
155
167
|
readonly through: string
|
|
156
168
|
/** The first day a contact still counts as fresh. */
|
|
157
169
|
readonly staleCutoff: string
|
|
170
|
+
/** Instant the upcoming Events window starts (now). */
|
|
171
|
+
readonly fromInstant: Date
|
|
172
|
+
/** Instant the upcoming Events window ends (now plus `UPCOMING_DAYS`). */
|
|
173
|
+
readonly throughInstant: Date
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
/** Everything read from the database, before any of it is named or shaped. */
|
|
@@ -163,6 +179,7 @@ interface SignalRows {
|
|
|
163
179
|
readonly overduePlanItems: SignalList<repository.PlanItemRecord>
|
|
164
180
|
readonly dueSoonPlanItems: SignalList<repository.PlanItemRecord>
|
|
165
181
|
readonly partnershipTouchpoints: SignalList<repository.TouchpointRecord>
|
|
182
|
+
readonly upcomingEvents: SignalList<repository.UpcomingEventRecord>
|
|
166
183
|
readonly staleContacts: SignalList<repository.StaleContactRecord>
|
|
167
184
|
readonly recentActivity: readonly repository.ActivityRecord[]
|
|
168
185
|
readonly recentNotes: readonly repository.NoteRecord[]
|
|
@@ -201,6 +218,19 @@ async function readTouchpoints(
|
|
|
201
218
|
}
|
|
202
219
|
}
|
|
203
220
|
|
|
221
|
+
async function readUpcomingEvents(
|
|
222
|
+
db: Database,
|
|
223
|
+
workspaceId: string,
|
|
224
|
+
from: Date,
|
|
225
|
+
to: Date,
|
|
226
|
+
limit: number,
|
|
227
|
+
): Promise<SignalList<repository.UpcomingEventRecord>> {
|
|
228
|
+
return {
|
|
229
|
+
total: await repository.countUpcomingEvents(db, workspaceId, from, to),
|
|
230
|
+
items: await repository.listUpcomingEvents(db, workspaceId, from, to, limit),
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
204
234
|
async function readStaleContacts(
|
|
205
235
|
db: Database,
|
|
206
236
|
workspaceId: string,
|
|
@@ -236,6 +266,7 @@ async function readSignals(
|
|
|
236
266
|
overduePlanItems,
|
|
237
267
|
dueSoonPlanItems,
|
|
238
268
|
partnershipTouchpoints,
|
|
269
|
+
upcomingEvents,
|
|
239
270
|
staleContacts,
|
|
240
271
|
recentActivity,
|
|
241
272
|
recentNotes,
|
|
@@ -245,6 +276,7 @@ async function readSignals(
|
|
|
245
276
|
readPlanItems(db, workspaceId, { before: days.today }, limit),
|
|
246
277
|
readPlanItems(db, workspaceId, { from: days.today, to: days.through }, limit),
|
|
247
278
|
readTouchpoints(db, workspaceId, days.through, limit),
|
|
279
|
+
readUpcomingEvents(db, workspaceId, days.fromInstant, days.throughInstant, limit),
|
|
248
280
|
readStaleContacts(db, workspaceId, timezone, days.staleCutoff, limit),
|
|
249
281
|
repository.listRecentActivity(db, workspaceId, limit),
|
|
250
282
|
repository.listRecentNotes(db, workspaceId, limit),
|
|
@@ -256,6 +288,7 @@ async function readSignals(
|
|
|
256
288
|
overduePlanItems,
|
|
257
289
|
dueSoonPlanItems,
|
|
258
290
|
partnershipTouchpoints,
|
|
291
|
+
upcomingEvents,
|
|
259
292
|
staleContacts,
|
|
260
293
|
recentActivity,
|
|
261
294
|
recentNotes,
|
|
@@ -376,6 +409,19 @@ function toSnapshot(
|
|
|
376
409
|
overduePlanItems: toPlanItemSignal(rows.overduePlanItems, nameOf),
|
|
377
410
|
dueSoonPlanItems: toPlanItemSignal(rows.dueSoonPlanItems, nameOf),
|
|
378
411
|
partnershipTouchpoints: toTouchpointSignal(rows.partnershipTouchpoints, days.today),
|
|
412
|
+
upcomingEvents: {
|
|
413
|
+
total: rows.upcomingEvents.total,
|
|
414
|
+
items: rows.upcomingEvents.items.map((row) => ({
|
|
415
|
+
id: row.id,
|
|
416
|
+
name: row.name,
|
|
417
|
+
startsAt: row.startsAt,
|
|
418
|
+
endsAt: row.endsAt,
|
|
419
|
+
format: row.format,
|
|
420
|
+
location: row.location,
|
|
421
|
+
attendeeCount: row.attendeeCount,
|
|
422
|
+
ownerId: row.ownerId,
|
|
423
|
+
})),
|
|
424
|
+
},
|
|
379
425
|
staleContacts: toStaleContactSignal(rows.staleContacts, days.today),
|
|
380
426
|
recentActivity: rows.recentActivity.map((row) => ({
|
|
381
427
|
id: row.id,
|
|
@@ -436,6 +482,8 @@ export function createDashboardService(dependencies: DashboardDependencies): Das
|
|
|
436
482
|
today,
|
|
437
483
|
through: upcomingEnd(today),
|
|
438
484
|
staleCutoff: staleContactCutoff(today),
|
|
485
|
+
fromInstant: generatedAt,
|
|
486
|
+
throughInstant: new Date(generatedAt.getTime() + UPCOMING_DAYS * 24 * 60 * 60 * 1000),
|
|
439
487
|
}
|
|
440
488
|
|
|
441
489
|
const rows = await readSignals(dependencies.db, workspaceId, timezone, days, limit)
|
|
@@ -8,6 +8,7 @@ import type { Queryable } from '../../runtime/transaction.ts'
|
|
|
8
8
|
import { companies } from '../companies/schema.ts'
|
|
9
9
|
import { deals } from '../deals/schema.ts'
|
|
10
10
|
import { enquiries } from '../enquiries/schema.ts'
|
|
11
|
+
import { events } from '../events/schema.ts'
|
|
11
12
|
import { opportunities } from '../opportunities/schema.ts'
|
|
12
13
|
import { partnerships } from '../partnerships/schema.ts'
|
|
13
14
|
import { people } from '../people/schema.ts'
|
|
@@ -57,6 +58,7 @@ const NAMED_TARGET_TABLES = [
|
|
|
57
58
|
{ table: partnerships, id: partnerships.id, workspaceId: partnerships.workspaceId, name: partnerships.name },
|
|
58
59
|
{ table: raises, id: raises.id, workspaceId: raises.workspaceId, name: raises.name },
|
|
59
60
|
{ table: enquiries, id: enquiries.id, workspaceId: enquiries.workspaceId, name: enquiries.name },
|
|
61
|
+
{ table: events, id: events.id, workspaceId: events.workspaceId, name: events.name },
|
|
60
62
|
]
|
|
61
63
|
|
|
62
64
|
/**
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { ATTENDANCE_STATUS_LABELS } from '@kelpie/schemas'
|
|
2
|
+
import type { AttendanceSource, AttendanceStatus } from '@kelpie/schemas'
|
|
3
|
+
|
|
4
|
+
import { changedKeys } from '../../lib/changes.ts'
|
|
5
|
+
import { UNIQUE_VIOLATION, postgresErrorCode } from '../../lib/database.ts'
|
|
6
|
+
import type { Database } from '../../lib/database.ts'
|
|
7
|
+
import { AppError } from '../../lib/errors.ts'
|
|
8
|
+
import type { IdFactory } from '../../lib/ids.ts'
|
|
9
|
+
import { mapPage, readListWindow, toPage } from '../../lib/pagination.ts'
|
|
10
|
+
import type { ListQueryParameters, Page } from '../../lib/pagination.ts'
|
|
11
|
+
import { toEventActor } from '../../lib/actor.ts'
|
|
12
|
+
import type { TransactionScope } from '../../runtime/transaction.ts'
|
|
13
|
+
import type { ActivityRecorder } from '../activities/recorder.ts'
|
|
14
|
+
import { describeLink, describeUnlink, describeUpdate } from '../activities/wording.ts'
|
|
15
|
+
import type { FieldLabels } from '../activities/wording.ts'
|
|
16
|
+
import type { Actor } from '../auth/actor.ts'
|
|
17
|
+
import { requireWorkspaceId } from '../auth/actor.ts'
|
|
18
|
+
import { deleteRecordsAttachedTo } from '../attachedRecords.ts'
|
|
19
|
+
import * as personLinks from '../personLinks.ts'
|
|
20
|
+
import './catalog.ts'
|
|
21
|
+
import * as repository from './repository.ts'
|
|
22
|
+
import { ATTENDANCE_SORTS, DEFAULT_ATTENDANCE_SORT } from './repository.ts'
|
|
23
|
+
import type { AttendanceFilters, AttendanceRecord } from './repository.ts'
|
|
24
|
+
|
|
25
|
+
export interface AttendancesDependencies {
|
|
26
|
+
readonly db: Database
|
|
27
|
+
readonly transaction: TransactionScope
|
|
28
|
+
readonly createId: IdFactory
|
|
29
|
+
readonly now: () => Date
|
|
30
|
+
readonly recordActivity: ActivityRecorder
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ATTENDANCE_FIELD_LABELS: FieldLabels = {
|
|
34
|
+
status: 'Attendance status',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type AttendanceView = Omit<AttendanceRecord, 'workspaceId'>
|
|
38
|
+
|
|
39
|
+
export interface CreateAttendanceInput {
|
|
40
|
+
readonly eventId: string
|
|
41
|
+
readonly personId: string
|
|
42
|
+
readonly status: AttendanceStatus
|
|
43
|
+
readonly source: AttendanceSource
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface UpdateAttendanceInput {
|
|
47
|
+
readonly status?: AttendanceStatus | undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface AttendancesService {
|
|
51
|
+
list(
|
|
52
|
+
actor: Actor,
|
|
53
|
+
filters: AttendanceFilters,
|
|
54
|
+
query: ListQueryParameters,
|
|
55
|
+
): Promise<Page<AttendanceView>>
|
|
56
|
+
get(actor: Actor, id: string): Promise<AttendanceView>
|
|
57
|
+
create(actor: Actor, input: CreateAttendanceInput): Promise<AttendanceView>
|
|
58
|
+
update(actor: Actor, id: string, changes: UpdateAttendanceInput): Promise<AttendanceView>
|
|
59
|
+
remove(actor: Actor, id: string): Promise<void>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function toView(record: AttendanceRecord): AttendanceView {
|
|
63
|
+
const { workspaceId: _workspaceId, ...view } = record
|
|
64
|
+
|
|
65
|
+
return view
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function duplicateAttendance(): AppError {
|
|
69
|
+
return AppError.conflict('That person is already registered for that event', [
|
|
70
|
+
{ field: 'person_id', message: 'Already registered for this event' },
|
|
71
|
+
])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function createAttendancesService(
|
|
75
|
+
dependencies: AttendancesDependencies,
|
|
76
|
+
): AttendancesService {
|
|
77
|
+
async function require(workspaceId: string, id: string): Promise<AttendanceRecord> {
|
|
78
|
+
const row = await repository.findAttendance(dependencies.db, workspaceId, id)
|
|
79
|
+
|
|
80
|
+
if (row === undefined) {
|
|
81
|
+
throw AppError.notFound('Attendance not found')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return row
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function requireEvent(workspaceId: string, eventId: string): Promise<string> {
|
|
88
|
+
const event = await repository.findEvent(dependencies.db, workspaceId, eventId)
|
|
89
|
+
|
|
90
|
+
if (event === undefined) {
|
|
91
|
+
throw AppError.notFound('Event not found')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return event.name
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function requirePerson(workspaceId: string, personId: string): Promise<string> {
|
|
98
|
+
const named = await personLinks.findPeopleNamed(dependencies.db, workspaceId, [personId])
|
|
99
|
+
const name = named.get(personId)
|
|
100
|
+
|
|
101
|
+
if (name === undefined) {
|
|
102
|
+
throw AppError.notFound('Person not found')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return name
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
async list(actor, filters, query) {
|
|
110
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
111
|
+
const window = readListWindow(query, ATTENDANCE_SORTS, DEFAULT_ATTENDANCE_SORT)
|
|
112
|
+
const rows = await repository.listAttendances(dependencies.db, workspaceId, filters, window)
|
|
113
|
+
|
|
114
|
+
return mapPage(toPage(rows, window, (row) => row.id), toView)
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
async get(actor, id) {
|
|
118
|
+
return toView(await require(requireWorkspaceId(actor), id))
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
async create(actor, input) {
|
|
122
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
123
|
+
const eventName = await requireEvent(workspaceId, input.eventId)
|
|
124
|
+
const personName = await requirePerson(workspaceId, input.personId)
|
|
125
|
+
const id = dependencies.createId('attendance')
|
|
126
|
+
|
|
127
|
+
return dependencies.transaction(
|
|
128
|
+
async ({ tx, events }) => {
|
|
129
|
+
let created: AttendanceRecord
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
created = await repository.insertAttendance(tx, {
|
|
133
|
+
id,
|
|
134
|
+
workspaceId,
|
|
135
|
+
eventId: input.eventId,
|
|
136
|
+
personId: input.personId,
|
|
137
|
+
status: input.status,
|
|
138
|
+
source: input.source,
|
|
139
|
+
})
|
|
140
|
+
} catch (error: unknown) {
|
|
141
|
+
if (postgresErrorCode(error) === UNIQUE_VIOLATION) {
|
|
142
|
+
throw duplicateAttendance()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
throw error
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await dependencies.recordActivity(tx, workspaceId, actor, {
|
|
149
|
+
targetType: 'event',
|
|
150
|
+
targetId: input.eventId,
|
|
151
|
+
kind: 'linked',
|
|
152
|
+
...describeLink('person', personName),
|
|
153
|
+
})
|
|
154
|
+
await dependencies.recordActivity(tx, workspaceId, actor, {
|
|
155
|
+
targetType: 'person',
|
|
156
|
+
targetId: input.personId,
|
|
157
|
+
kind: 'linked',
|
|
158
|
+
...describeLink('event', eventName),
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
events.emit('events.attendance.created', { type: 'attendance', id }, {})
|
|
162
|
+
|
|
163
|
+
return toView(created)
|
|
164
|
+
},
|
|
165
|
+
{ workspaceId, actor: toEventActor(actor) },
|
|
166
|
+
)
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
async update(actor, id, changes) {
|
|
170
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
171
|
+
const existing = await require(workspaceId, id)
|
|
172
|
+
const columns: Partial<repository.AttendanceColumns> = {
|
|
173
|
+
...(changes.status === undefined ? {} : { status: changes.status }),
|
|
174
|
+
}
|
|
175
|
+
const changed = changedKeys(existing, columns)
|
|
176
|
+
|
|
177
|
+
if (changed.length === 0) {
|
|
178
|
+
return toView(existing)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return dependencies.transaction(
|
|
182
|
+
async ({ tx, events }) => {
|
|
183
|
+
const updated = await repository.updateAttendance(tx, workspaceId, id, {
|
|
184
|
+
...columns,
|
|
185
|
+
updatedAt: dependencies.now(),
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
if (updated === undefined) {
|
|
189
|
+
throw AppError.notFound('Attendance not found')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const before = {
|
|
193
|
+
status: ATTENDANCE_STATUS_LABELS[existing.status as AttendanceStatus],
|
|
194
|
+
}
|
|
195
|
+
const after = {
|
|
196
|
+
status:
|
|
197
|
+
ATTENDANCE_STATUS_LABELS[(columns.status as AttendanceStatus | undefined) ?? existing.status as AttendanceStatus],
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
await dependencies.recordActivity(tx, workspaceId, actor, {
|
|
201
|
+
targetType: 'attendance',
|
|
202
|
+
targetId: id,
|
|
203
|
+
kind: 'updated',
|
|
204
|
+
...describeUpdate(changed, ATTENDANCE_FIELD_LABELS, before, after),
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
events.emit('events.attendance.updated', { type: 'attendance', id }, { changed })
|
|
208
|
+
|
|
209
|
+
return toView(updated)
|
|
210
|
+
},
|
|
211
|
+
{ workspaceId, actor: toEventActor(actor) },
|
|
212
|
+
)
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
async remove(actor, id) {
|
|
216
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
217
|
+
const existing = await require(workspaceId, id)
|
|
218
|
+
const eventName = await requireEvent(workspaceId, existing.eventId)
|
|
219
|
+
const personName = await requirePerson(workspaceId, existing.personId)
|
|
220
|
+
|
|
221
|
+
await dependencies.transaction(
|
|
222
|
+
async ({ tx, events }) => {
|
|
223
|
+
await deleteRecordsAttachedTo(tx, workspaceId, 'attendance', id)
|
|
224
|
+
await repository.deleteAttendance(tx, workspaceId, id)
|
|
225
|
+
|
|
226
|
+
await dependencies.recordActivity(tx, workspaceId, actor, {
|
|
227
|
+
targetType: 'event',
|
|
228
|
+
targetId: existing.eventId,
|
|
229
|
+
kind: 'unlinked',
|
|
230
|
+
...describeUnlink('person', personName),
|
|
231
|
+
})
|
|
232
|
+
await dependencies.recordActivity(tx, workspaceId, actor, {
|
|
233
|
+
targetType: 'person',
|
|
234
|
+
targetId: existing.personId,
|
|
235
|
+
kind: 'unlinked',
|
|
236
|
+
...describeUnlink('event', eventName),
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
events.emit('events.attendance.deleted', { type: 'attendance', id }, {})
|
|
240
|
+
},
|
|
241
|
+
{ workspaceId, actor: toEventActor(actor) },
|
|
242
|
+
)
|
|
243
|
+
},
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import type { ModuleEventCatalog } from '../../runtime/module.ts'
|
|
4
|
+
|
|
5
|
+
/** Domain events published by the events module (CRM gatherings + attendances). */
|
|
6
|
+
|
|
7
|
+
export const crmEventsCatalog = {
|
|
8
|
+
'events.event.created': z.object({}).strict(),
|
|
9
|
+
'events.event.updated': z.object({ changed: z.array(z.string()).readonly() }),
|
|
10
|
+
'events.event.deleted': z.object({}).strict(),
|
|
11
|
+
'events.attendance.created': z.object({}).strict(),
|
|
12
|
+
'events.attendance.updated': z.object({ changed: z.array(z.string()).readonly() }),
|
|
13
|
+
'events.attendance.deleted': z.object({}).strict(),
|
|
14
|
+
} satisfies ModuleEventCatalog
|
|
15
|
+
|
|
16
|
+
export type CrmEventCreatedData = Record<string, never>
|
|
17
|
+
export interface CrmEventUpdatedData {
|
|
18
|
+
readonly changed: readonly string[]
|
|
19
|
+
}
|
|
20
|
+
export type CrmEventDeletedData = Record<string, never>
|
|
21
|
+
export type AttendanceCreatedData = Record<string, never>
|
|
22
|
+
export interface AttendanceUpdatedData {
|
|
23
|
+
readonly changed: readonly string[]
|
|
24
|
+
}
|
|
25
|
+
export type AttendanceDeletedData = Record<string, never>
|
|
26
|
+
|
|
27
|
+
declare module '../../runtime/events.ts' {
|
|
28
|
+
interface KelpieEventMap {
|
|
29
|
+
'events.event.created': CrmEventCreatedData
|
|
30
|
+
'events.event.updated': CrmEventUpdatedData
|
|
31
|
+
'events.event.deleted': CrmEventDeletedData
|
|
32
|
+
'events.attendance.created': AttendanceCreatedData
|
|
33
|
+
'events.attendance.updated': AttendanceUpdatedData
|
|
34
|
+
'events.attendance.deleted': AttendanceDeletedData
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { KelpieModule } from '../../runtime/module.ts'
|
|
2
|
+
import { createActivityRecorder } from '../activities/index.ts'
|
|
3
|
+
import { createCustomFieldValues } from '../custom-fields/index.ts'
|
|
4
|
+
import { createAttendancesService } from './attendances.ts'
|
|
5
|
+
import { crmEventsCatalog } from './catalog.ts'
|
|
6
|
+
import { mountEventsRoutes } from './routes.ts'
|
|
7
|
+
import * as schema from './schema.ts'
|
|
8
|
+
import { createEventsService } from './service.ts'
|
|
9
|
+
import { registerEventsTools } from './tools.ts'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Events: dated gatherings, attendances, and Event-only associations.
|
|
13
|
+
*
|
|
14
|
+
* Requires people (attendees), activities (timeline), and custom-fields
|
|
15
|
+
* (workspace-defined attributes on the Event). Associations resolve other
|
|
16
|
+
* modules' tables directly, the way notes resolve polymorphic targets.
|
|
17
|
+
*/
|
|
18
|
+
export function createEventsModule(migrationsDirectory: string): KelpieModule {
|
|
19
|
+
return {
|
|
20
|
+
id: 'events',
|
|
21
|
+
requires: ['people', 'activities', 'custom-fields'],
|
|
22
|
+
events: crmEventsCatalog,
|
|
23
|
+
|
|
24
|
+
register(context) {
|
|
25
|
+
const recordActivity = createActivityRecorder({
|
|
26
|
+
createId: context.createId,
|
|
27
|
+
now: context.now,
|
|
28
|
+
})
|
|
29
|
+
const shared = {
|
|
30
|
+
db: context.db,
|
|
31
|
+
transaction: context.transaction,
|
|
32
|
+
createId: context.createId,
|
|
33
|
+
now: context.now,
|
|
34
|
+
recordActivity,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
context.schema(schema, migrationsDirectory)
|
|
38
|
+
|
|
39
|
+
const events = createEventsService({
|
|
40
|
+
...shared,
|
|
41
|
+
customFields: createCustomFieldValues({ db: context.db }),
|
|
42
|
+
})
|
|
43
|
+
const attendances = createAttendancesService(shared)
|
|
44
|
+
|
|
45
|
+
context.routes((router) => {
|
|
46
|
+
mountEventsRoutes(router, { db: context.db, now: context.now, events, attendances })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
registerEventsTools(context.mcp, { events, attendances })
|
|
50
|
+
|
|
51
|
+
return Promise.resolve()
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
}
|