@dragonmastery/dragoncore-api 0.0.2 → 0.0.4
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/LICENSE +2 -2
- package/dist/index.d.mts +1474 -626
- package/dist/index.mjs +2501 -633
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { drizzle } from "drizzle-orm/d1";
|
|
|
2
2
|
import { container, inject, injectable } from "tsyringe";
|
|
3
3
|
import { and, asc, desc, eq, getTableColumns, getTableName, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, notInArray, or, sql } from "drizzle-orm";
|
|
4
4
|
import { customAlphabet } from "nanoid";
|
|
5
|
-
import { AddCreditsSchema, ApproveSupportTicketSchema, AttachmentFiltersSchema, AttachmentFolderReadSchema, AttachmentPageSchema, CompleteSupportTicketSchema, CreditBalanceSchema, CreditTransactionFiltersSchema, CreditTransactionPageSchema, CustomerSupportTicketCreateSchema, CustomerSupportTicketFiltersSchema, CustomerSupportTicketPageSchema, CustomerSupportTicketReadSchema, CustomerSupportTicketUpdateSchema, NoteCreateSchema, NoteFiltersSchema, NoteReadSchema, NoteUpdateSchema, OPERATORS,
|
|
5
|
+
import { AddCreditsSchema, ApproveSupportTicketSchema, ArchiveSupportTicketSchema, AttachmentFiltersSchema, AttachmentFolderReadSchema, AttachmentPageSchema, CompleteSupportTicketSchema, CreditBalanceSchema, CreditTransactionFiltersSchema, CreditTransactionPageSchema, CustomerSupportTicketCreateSchema, CustomerSupportTicketFiltersSchema, CustomerSupportTicketPageSchema, CustomerSupportTicketReadSchema, CustomerSupportTicketUpdateSchema, DEFAULT_USER_TYPE, MAX_PRESETS_PER_CONTEXT, NoteCreateSchema, NoteFiltersSchema, NoteReadSchema, NoteUpdateSchema, OPERATORS, RecordConst, RecordConst as RecordConst$1, RecordSubscriberReadSchema, RejectSupportTicketSchema, RevertSupportTicketSchema, SUPPORT_TICKET_PRIORITY_TO_NUMBER, SavedFilterCreateSchema, SavedFilterReadSchema, SavedFilterUpdateSchema, SetMonthlyAllocationSchema, StaffSupportTicketCreateSchema, StaffSupportTicketFiltersSchema, StaffSupportTicketPageSchema, StaffSupportTicketReadSchema, StaffSupportTicketUpdateSchema, SupportTicketApprovalEnum, SupportTicketDevLifecycleEnum, SupportTicketSubscriberCreateSchema, SupportTicketTypeEnum, TeamCreateSchema, TeamFiltersSchema, TeamMemberCreateSchema, TeamMemberFiltersSchema, TeamMemberReadSchema, TeamMemberUpdateSchema, TeamPageSchema, TeamReadSchema, TeamUpdateSchema, USER_TYPES, UserProfileReadSchema, UserProfileUpdateSchema, UserReadSchema, UserTeamMembersSchema, UserTeamsSchema, UserUpdateSchema, changePasswordSchema, createPaginatedSchema, createUserSchema, createUserSchemaOutput, loginResponseSchema, loginSchema, recordVersionFiltersInputBreadcrumbSchema, recordVersionFiltersInputSchema, recordVersionPageBreadcrumbSchema, recordVersionPageSchema, recordVersionSchema, recordVersionTrackerActivityInputSchema, resetPasswordSchema, sanitizeFileName, signupSchema, supportTicketNumberToPriority, userSessionSchema } from "@dragonmastery/dragoncore-shared";
|
|
6
6
|
import { blob, index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
7
7
|
import { parseSigned, serializeSigned } from "hono/utils/cookie";
|
|
8
8
|
import { hashPassword, verifyPassword } from "worker-password-auth";
|
|
@@ -329,6 +329,139 @@ const OperationConst = {
|
|
|
329
329
|
INSERT: "insert"
|
|
330
330
|
};
|
|
331
331
|
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region src/db/schemas/app_setting/app_settings_table.ts
|
|
334
|
+
/**
|
|
335
|
+
* Application settings table for storing global configuration
|
|
336
|
+
* that doesn't need to be sharded.
|
|
337
|
+
*
|
|
338
|
+
* This includes default rates and other application-wide settings.
|
|
339
|
+
*/
|
|
340
|
+
const app_settings_table = sqliteTable("app_settings", {
|
|
341
|
+
key: text("key").primaryKey(),
|
|
342
|
+
value: text("value", { mode: "json" }).$type().notNull(),
|
|
343
|
+
created_at: text("created_at").notNull(),
|
|
344
|
+
created_by: text("created_by").notNull(),
|
|
345
|
+
updated_at: text("updated_at").notNull(),
|
|
346
|
+
updated_by: text("updated_by")
|
|
347
|
+
}, (table) => ({
|
|
348
|
+
created_at_idx: index("app_settings_created_at_idx").on(table.created_at),
|
|
349
|
+
updated_at_idx: index("app_settings_updated_at_idx").on(table.updated_at)
|
|
350
|
+
}));
|
|
351
|
+
/**
|
|
352
|
+
* Known setting keys for type safety
|
|
353
|
+
*/
|
|
354
|
+
const AppSettingKey = {
|
|
355
|
+
GLOBAL_TAX_SETTINGS: "global_tax_settings",
|
|
356
|
+
DEFAULT_PRICING_LEVEL: "default_pricing_level",
|
|
357
|
+
TERMS_OF_SERVICE: "terms_of_service",
|
|
358
|
+
PRIVACY_POLICY: "privacy_policy",
|
|
359
|
+
MAX_SUPPORT_TICKET_ITEMS: "max_support_ticket_items",
|
|
360
|
+
CUSTOMER_MONTHLY_BALANCE: "customer_monthly_balance",
|
|
361
|
+
CUSTOMER_ROLLOVER_BALANCE: "customer_rollover_balance",
|
|
362
|
+
CUSTOMER_MONTHLY_ALLOCATION: "customer_monthly_allocation",
|
|
363
|
+
LAST_SUPPORT_TICKET_ASSIGNEE: "last_support_ticket_assignee"
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/db/schemas/app_setting/app_settings_repo.ts
|
|
368
|
+
let AppSettingsRepo = class AppSettingsRepo$1 {
|
|
369
|
+
constructor(appSettingsDb) {
|
|
370
|
+
this.appSettingsDb = appSettingsDb;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Get a setting value by key
|
|
374
|
+
* @param key Setting key
|
|
375
|
+
* @returns The setting value or undefined if not found
|
|
376
|
+
*/
|
|
377
|
+
async readSetting(key) {
|
|
378
|
+
const [setting] = await this.appSettingsDb.select().from(app_settings_table).where(eq(app_settings_table.key, key)).limit(1);
|
|
379
|
+
return setting;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Save a setting value
|
|
383
|
+
* @param key Setting key
|
|
384
|
+
* @param value Setting value
|
|
385
|
+
* @param user User making the change
|
|
386
|
+
*/
|
|
387
|
+
async createSetting(key, value, user) {
|
|
388
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
389
|
+
await this.appSettingsDb.insert(app_settings_table).values({
|
|
390
|
+
key,
|
|
391
|
+
value,
|
|
392
|
+
created_at: now,
|
|
393
|
+
created_by: user.userId,
|
|
394
|
+
updated_at: now,
|
|
395
|
+
updated_by: user.userId
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
async updateSetting(key, value, user) {
|
|
399
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
400
|
+
const [updated] = await this.appSettingsDb.update(app_settings_table).set({
|
|
401
|
+
value,
|
|
402
|
+
updated_at: now,
|
|
403
|
+
updated_by: user.userId
|
|
404
|
+
}).where(eq(app_settings_table.key, key)).returning();
|
|
405
|
+
if (!updated) {
|
|
406
|
+
const [inserted] = await this.appSettingsDb.insert(app_settings_table).values({
|
|
407
|
+
key,
|
|
408
|
+
value,
|
|
409
|
+
created_at: now,
|
|
410
|
+
created_by: user.userId,
|
|
411
|
+
updated_at: now,
|
|
412
|
+
updated_by: user.userId
|
|
413
|
+
}).returning();
|
|
414
|
+
return inserted?.value;
|
|
415
|
+
}
|
|
416
|
+
return updated.value;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Delete a setting
|
|
420
|
+
* @param key Setting key
|
|
421
|
+
* @returns True if the setting was deleted, false if it didn't exist
|
|
422
|
+
*/
|
|
423
|
+
async deleteSetting(key) {
|
|
424
|
+
return (await this.appSettingsDb.delete(app_settings_table).where(eq(app_settings_table.key, key)).returning()).length > 0;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get all settings
|
|
428
|
+
* @returns All settings as a map of key to value
|
|
429
|
+
*/
|
|
430
|
+
async getAllSettings() {
|
|
431
|
+
const settings = await this.appSettingsDb.select().from(app_settings_table);
|
|
432
|
+
const settingsMap = /* @__PURE__ */ new Map();
|
|
433
|
+
for (const setting of settings) settingsMap.set(setting.key, setting.value);
|
|
434
|
+
return settingsMap;
|
|
435
|
+
}
|
|
436
|
+
async readMultipleSettings(keys) {
|
|
437
|
+
const settings = await this.appSettingsDb.select().from(app_settings_table).where(inArray(app_settings_table.key, keys));
|
|
438
|
+
const settingsMap = /* @__PURE__ */ new Map();
|
|
439
|
+
for (const setting of settings) settingsMap.set(setting.key, setting.value);
|
|
440
|
+
return settingsMap;
|
|
441
|
+
}
|
|
442
|
+
async getNextSequentialId(recordType, userId) {
|
|
443
|
+
const counterKey = `${recordType}:counter`;
|
|
444
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
445
|
+
const [result] = await this.appSettingsDb.insert(app_settings_table).values({
|
|
446
|
+
key: counterKey,
|
|
447
|
+
value: 1,
|
|
448
|
+
created_at: now,
|
|
449
|
+
created_by: userId,
|
|
450
|
+
updated_at: now,
|
|
451
|
+
updated_by: userId
|
|
452
|
+
}).onConflictDoUpdate({
|
|
453
|
+
target: app_settings_table.key,
|
|
454
|
+
set: {
|
|
455
|
+
value: sql`json(json_extract(${app_settings_table.value}, '$') + 1)`,
|
|
456
|
+
updated_at: now,
|
|
457
|
+
updated_by: userId
|
|
458
|
+
}
|
|
459
|
+
}).returning({ value: app_settings_table.value });
|
|
460
|
+
return String(result.value);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
AppSettingsRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IAppSettingsDb))], AppSettingsRepo);
|
|
464
|
+
|
|
332
465
|
//#endregion
|
|
333
466
|
//#region src/slices/record_version/db/record_version_table.ts
|
|
334
467
|
const record_version_table = sqliteTable("record_version", {
|
|
@@ -481,18 +614,81 @@ const note_table = sqliteTable("note", {
|
|
|
481
614
|
archived_at: text("archived_at"),
|
|
482
615
|
deleted_by: text("deleted_by"),
|
|
483
616
|
deleted_at: text("deleted_at")
|
|
617
|
+
}, (table) => [
|
|
618
|
+
index("note_record_id_idx").on(table.record_id),
|
|
619
|
+
index("note_record_type_idx").on(table.record_type),
|
|
620
|
+
index("note_tag_idx").on(table.tag),
|
|
621
|
+
index("note_title_idx").on(table.title),
|
|
622
|
+
index("note_is_internal_idx").on(table.is_internal),
|
|
623
|
+
index("note_created_at_idx").on(table.created_at),
|
|
624
|
+
index("note_updated_at_idx").on(table.updated_at),
|
|
625
|
+
index("note_deleted_at_idx").on(table.deleted_at),
|
|
626
|
+
index("note_deleted_updated_idx").on(table.deleted_at, table.updated_at)
|
|
627
|
+
]);
|
|
628
|
+
|
|
629
|
+
//#endregion
|
|
630
|
+
//#region src/slices/saved_filter/db/saved_filter_table.ts
|
|
631
|
+
const saved_filter_table = sqliteTable("saved_filter", {
|
|
632
|
+
id: text("id").primaryKey(),
|
|
633
|
+
user_id: text("user_id").notNull(),
|
|
634
|
+
name: text("name").notNull(),
|
|
635
|
+
context: text("context").notNull(),
|
|
636
|
+
route_path: text("route_path").notNull(),
|
|
637
|
+
filters: text("filters").notNull(),
|
|
638
|
+
sort_by: text("sort_by"),
|
|
639
|
+
sort_direction: text("sort_direction"),
|
|
640
|
+
created_at: text("created_at").notNull(),
|
|
641
|
+
updated_at: text("updated_at").notNull()
|
|
642
|
+
}, (table) => [
|
|
643
|
+
index("saved_filter_user_id_idx").on(table.user_id),
|
|
644
|
+
index("saved_filter_context_idx").on(table.context),
|
|
645
|
+
index("saved_filter_user_context_idx").on(table.user_id, table.context)
|
|
646
|
+
]);
|
|
647
|
+
|
|
648
|
+
//#endregion
|
|
649
|
+
//#region src/slices/saved_filter/db/user_pinned_preset_table.ts
|
|
650
|
+
const MAX_PINNED_PRESETS = 5;
|
|
651
|
+
const user_pinned_preset_table = sqliteTable("user_pinned_preset", {
|
|
652
|
+
id: text("id").primaryKey(),
|
|
653
|
+
user_id: text("user_id").notNull(),
|
|
654
|
+
preset_id: text("preset_id").notNull(),
|
|
655
|
+
position: integer("position").notNull()
|
|
656
|
+
}, (table) => [index("user_pinned_preset_user_id_idx").on(table.user_id), index("user_pinned_preset_preset_id_idx").on(table.preset_id)]);
|
|
657
|
+
|
|
658
|
+
//#endregion
|
|
659
|
+
//#region src/slices/record_subscriber/db/record_subscriber_table.ts
|
|
660
|
+
/**
|
|
661
|
+
* Generic record subscribers - users who receive notifications on record events.
|
|
662
|
+
* Reusable for support_ticket, tracker, and other record types.
|
|
663
|
+
* Email address comes from user's notification_email (user_profile) or user.email if not set.
|
|
664
|
+
*/
|
|
665
|
+
const record_subscriber_table = sqliteTable("record_subscriber", {
|
|
666
|
+
id: text("id").primaryKey(),
|
|
667
|
+
record_type: text("record_type").$type().notNull(),
|
|
668
|
+
record_id: text("record_id").notNull(),
|
|
669
|
+
user_id: text("user_id").notNull(),
|
|
670
|
+
subscribed_events: text("subscribed_events", { mode: "json" }).$type(),
|
|
671
|
+
created_at: text("created_at").notNull(),
|
|
672
|
+
created_by: text("created_by").notNull()
|
|
484
673
|
}, (table) => ({
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
title_idx: index("note_title_idx").on(table.title),
|
|
489
|
-
is_internal_idx: index("note_is_internal_idx").on(table.is_internal),
|
|
490
|
-
created_at_idx: index("note_created_at_idx").on(table.created_at),
|
|
491
|
-
updated_at_idx: index("note_updated_at_idx").on(table.updated_at),
|
|
492
|
-
deleted_at_idx: index("note_deleted_at_idx").on(table.deleted_at),
|
|
493
|
-
deletedUpdatedIdx: index("note_deleted_updated_idx").on(table.deleted_at, table.updated_at)
|
|
674
|
+
recordTypeIdIdx: index("record_subscriber_record_type_id_idx").on(table.record_type, table.record_id),
|
|
675
|
+
userIdIdx: index("record_subscriber_user_id_idx").on(table.user_id),
|
|
676
|
+
uniqueSubscriberIdx: index("record_subscriber_unique_idx").on(table.record_type, table.record_id, table.user_id)
|
|
494
677
|
}));
|
|
495
678
|
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region src/slices/support_staff/db/support_staff_table.ts
|
|
681
|
+
/**
|
|
682
|
+
* Support staff - users who can triage support tickets.
|
|
683
|
+
* Small table instead of flagging every user with can_triage.
|
|
684
|
+
*/
|
|
685
|
+
const support_staff_table = sqliteTable("support_staff", {
|
|
686
|
+
id: text("id").primaryKey(),
|
|
687
|
+
user_id: text("user_id").notNull().unique(),
|
|
688
|
+
created_at: text("created_at").notNull(),
|
|
689
|
+
created_by: text("created_by").notNull()
|
|
690
|
+
}, (table) => ({ userIdIdx: index("support_staff_user_id_idx").on(table.user_id) }));
|
|
691
|
+
|
|
496
692
|
//#endregion
|
|
497
693
|
//#region src/slices/support_ticket/db/support_ticket_table.ts
|
|
498
694
|
/**
|
|
@@ -504,10 +700,9 @@ const support_ticket_table = sqliteTable("support_ticket", {
|
|
|
504
700
|
title: text("title").notNull(),
|
|
505
701
|
description: text("description").notNull(),
|
|
506
702
|
type: text("type", { enum: SupportTicketTypeEnum }).notNull().default("FEATURE_REQUEST"),
|
|
507
|
-
priority:
|
|
703
|
+
priority: integer("priority", { mode: "number" }).notNull().default(2),
|
|
508
704
|
approval_status: text("approval_status", { enum: SupportTicketApprovalEnum }).notNull().default("PENDING"),
|
|
509
|
-
|
|
510
|
-
requester_name: text("requester_name").notNull(),
|
|
705
|
+
assigned_to: text("assigned_to"),
|
|
511
706
|
credit_value: text("credit_value"),
|
|
512
707
|
delivered_value: text("delivered_value"),
|
|
513
708
|
dev_lifecycle: text("dev_lifecycle", { enum: SupportTicketDevLifecycleEnum }),
|
|
@@ -531,8 +726,7 @@ const support_ticket_table = sqliteTable("support_ticket", {
|
|
|
531
726
|
priorityIdx: index("support_ticket_priority_idx").on(table.priority),
|
|
532
727
|
approvalStatusIdx: index("support_ticket_approval_status_idx").on(table.approval_status),
|
|
533
728
|
devLifecycleIdx: index("support_ticket_dev_lifecycle_idx").on(table.dev_lifecycle),
|
|
534
|
-
|
|
535
|
-
requesterNameIdx: index("support_ticket_requester_name_idx").on(table.requester_name),
|
|
729
|
+
assignedToIdx: index("support_ticket_assigned_to_idx").on(table.assigned_to),
|
|
536
730
|
creditValueIdx: index("support_ticket_credit_value_idx").on(table.credit_value),
|
|
537
731
|
deliveredValueIdx: index("support_ticket_delivered_value_idx").on(table.delivered_value),
|
|
538
732
|
createdAtIdx: index("support_ticket_created_at_idx").on(table.created_at),
|
|
@@ -659,6 +853,7 @@ const user_profile_table = sqliteTable("user_profile", {
|
|
|
659
853
|
last_name: text("last_name", { length: 255 }),
|
|
660
854
|
additional_name: text("additional_name", { length: 255 }),
|
|
661
855
|
avatar_url: text("avatar_url"),
|
|
856
|
+
notification_email: text("notification_email"),
|
|
662
857
|
gender: text("gender", { length: 255 }),
|
|
663
858
|
bio: text("bio"),
|
|
664
859
|
birthday: text("birthday"),
|
|
@@ -1351,6 +1546,14 @@ function resolveTeamAccess(userTeamIds, teamFilter) {
|
|
|
1351
1546
|
};
|
|
1352
1547
|
return { denied: true };
|
|
1353
1548
|
}
|
|
1549
|
+
if (teamFilter?.operator === "in" && teamFilter.values?.length) {
|
|
1550
|
+
const allowed = teamFilter.values.filter((id) => userTeamIds.includes(id));
|
|
1551
|
+
if (allowed.length === 0) return { denied: true };
|
|
1552
|
+
return {
|
|
1553
|
+
denied: false,
|
|
1554
|
+
effectiveTeamIds: allowed
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1354
1557
|
return {
|
|
1355
1558
|
denied: false,
|
|
1356
1559
|
effectiveTeamIds: userTeamIds
|
|
@@ -1616,6 +1819,11 @@ var CursorUtils = class {
|
|
|
1616
1819
|
* Get items AFTER this cursor position in the sorted order
|
|
1617
1820
|
*/
|
|
1618
1821
|
static buildAfterCondition(table, column, sortValue, id, isDesc) {
|
|
1822
|
+
if (sortValue === null || sortValue === void 0) {
|
|
1823
|
+
const sortCol = column.column;
|
|
1824
|
+
if (isDesc) return [and(isNull(sortCol), lt(table.id, id))];
|
|
1825
|
+
else return [and(isNull(sortCol), gt(table.id, id))];
|
|
1826
|
+
}
|
|
1619
1827
|
const { sortColumn, processedValue } = this.processSortColumn(column, sortValue);
|
|
1620
1828
|
if (isDesc) return [or(lt(sortColumn, processedValue), and(eq(sortColumn, processedValue), lt(table.id, id)))];
|
|
1621
1829
|
else return [or(gt(sortColumn, processedValue), and(eq(sortColumn, processedValue), gt(table.id, id)))];
|
|
@@ -1739,7 +1947,7 @@ var PaginationUtils = class {
|
|
|
1739
1947
|
responseState = BreadcrumbUtils.navigateNext(state, nextPageCursor);
|
|
1740
1948
|
responseState.currentPageIndex = state.currentPageIndex;
|
|
1741
1949
|
}
|
|
1742
|
-
const prevPageCursor = BreadcrumbUtils.canNavigatePrevious(state) ? state.cursors[state.currentPageIndex - 1]
|
|
1950
|
+
const prevPageCursor = BreadcrumbUtils.canNavigatePrevious(state) ? state.cursors[state.currentPageIndex - 1] : void 0;
|
|
1743
1951
|
return {
|
|
1744
1952
|
items,
|
|
1745
1953
|
pageInfo: {
|
|
@@ -2727,214 +2935,41 @@ function createIsAuthenticatedMiddleware(options) {
|
|
|
2727
2935
|
}
|
|
2728
2936
|
|
|
2729
2937
|
//#endregion
|
|
2730
|
-
//#region src/
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
updated_at_idx: index("app_settings_updated_at_idx").on(table.updated_at)
|
|
2747
|
-
}));
|
|
2748
|
-
/**
|
|
2749
|
-
* Known setting keys for type safety
|
|
2750
|
-
*/
|
|
2751
|
-
const AppSettingKey = {
|
|
2752
|
-
GLOBAL_TAX_SETTINGS: "global_tax_settings",
|
|
2753
|
-
DEFAULT_PRICING_LEVEL: "default_pricing_level",
|
|
2754
|
-
TERMS_OF_SERVICE: "terms_of_service",
|
|
2755
|
-
PRIVACY_POLICY: "privacy_policy",
|
|
2756
|
-
MAX_SUPPORT_TICKET_ITEMS: "max_support_ticket_items",
|
|
2757
|
-
CUSTOMER_MONTHLY_BALANCE: "customer_monthly_balance",
|
|
2758
|
-
CUSTOMER_ROLLOVER_BALANCE: "customer_rollover_balance",
|
|
2759
|
-
CUSTOMER_MONTHLY_ALLOCATION: "customer_monthly_allocation",
|
|
2760
|
-
SUPPORT_TICKET_NOTIFICATION_EMAILS: "support_ticket_notification_emails"
|
|
2938
|
+
//#region src/slices/app_settings/app_settings_container.ts
|
|
2939
|
+
function registerAppSettingsContainer() {}
|
|
2940
|
+
|
|
2941
|
+
//#endregion
|
|
2942
|
+
//#region src/slices/attachment/attachment_interfaces.ts
|
|
2943
|
+
const ATTACHMENT_TOKENS = {
|
|
2944
|
+
IAttachmentRepo: Symbol("IAttachmentRepo"),
|
|
2945
|
+
IReadAllAttachmentsByRecord: Symbol("IReadAllAttachmentsByRecord"),
|
|
2946
|
+
IReadAttachment: Symbol("IReadAttachment"),
|
|
2947
|
+
ICreateAttachment: Symbol("ICreateAttachment"),
|
|
2948
|
+
IUpdateAttachment: Symbol("IUpdateAttachment"),
|
|
2949
|
+
IDeleteAttachment: Symbol("IDeleteAttachment")
|
|
2950
|
+
};
|
|
2951
|
+
const ATTACHMENT_FOLDER_TOKENS = {
|
|
2952
|
+
IAttachmentFolderRepo: Symbol("IAttachmentFolderRepo"),
|
|
2953
|
+
ICreateAttachmentFolder: Symbol("ICreateAttachmentFolder")
|
|
2761
2954
|
};
|
|
2762
2955
|
|
|
2763
2956
|
//#endregion
|
|
2764
|
-
//#region src/
|
|
2765
|
-
let
|
|
2766
|
-
constructor(
|
|
2767
|
-
this.
|
|
2957
|
+
//#region src/slices/attachment/db/attachment_folder_repo.ts
|
|
2958
|
+
let AttachmentFolderRepo = class AttachmentFolderRepo$1 {
|
|
2959
|
+
constructor(router) {
|
|
2960
|
+
this.router = router;
|
|
2768
2961
|
}
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
const [setting] = await this.appSettingsDb.select().from(app_settings_table).where(eq(app_settings_table.key, key)).limit(1);
|
|
2776
|
-
return setting;
|
|
2962
|
+
async create_folder(folder) {
|
|
2963
|
+
const id = await this.router.generateId(RecordConst.ATTACHMENT_FOLDER);
|
|
2964
|
+
return (await this.router.queryLatest((db) => db.insert(attachment_folder_table).values({
|
|
2965
|
+
id,
|
|
2966
|
+
...folder
|
|
2967
|
+
}).returning()))[0];
|
|
2777
2968
|
}
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
* @param user User making the change
|
|
2783
|
-
*/
|
|
2784
|
-
async createSetting(key, value, user) {
|
|
2785
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2786
|
-
await this.appSettingsDb.insert(app_settings_table).values({
|
|
2787
|
-
key,
|
|
2788
|
-
value,
|
|
2789
|
-
created_at: now,
|
|
2790
|
-
created_by: user.email,
|
|
2791
|
-
updated_at: now,
|
|
2792
|
-
updated_by: user.email
|
|
2793
|
-
});
|
|
2794
|
-
}
|
|
2795
|
-
async updateSetting(key, value, user) {
|
|
2796
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2797
|
-
const [updated] = await this.appSettingsDb.update(app_settings_table).set({
|
|
2798
|
-
value,
|
|
2799
|
-
updated_at: now,
|
|
2800
|
-
updated_by: user.email
|
|
2801
|
-
}).where(eq(app_settings_table.key, key)).returning();
|
|
2802
|
-
if (!updated) {
|
|
2803
|
-
const [inserted] = await this.appSettingsDb.insert(app_settings_table).values({
|
|
2804
|
-
key,
|
|
2805
|
-
value,
|
|
2806
|
-
created_at: now,
|
|
2807
|
-
created_by: user.email,
|
|
2808
|
-
updated_at: now,
|
|
2809
|
-
updated_by: user.email
|
|
2810
|
-
}).returning();
|
|
2811
|
-
return inserted?.value;
|
|
2812
|
-
}
|
|
2813
|
-
return updated.value;
|
|
2814
|
-
}
|
|
2815
|
-
/**
|
|
2816
|
-
* Delete a setting
|
|
2817
|
-
* @param key Setting key
|
|
2818
|
-
* @returns True if the setting was deleted, false if it didn't exist
|
|
2819
|
-
*/
|
|
2820
|
-
async deleteSetting(key) {
|
|
2821
|
-
return (await this.appSettingsDb.delete(app_settings_table).where(eq(app_settings_table.key, key)).returning()).length > 0;
|
|
2822
|
-
}
|
|
2823
|
-
/**
|
|
2824
|
-
* Get all settings
|
|
2825
|
-
* @returns All settings as a map of key to value
|
|
2826
|
-
*/
|
|
2827
|
-
async getAllSettings() {
|
|
2828
|
-
const settings = await this.appSettingsDb.select().from(app_settings_table);
|
|
2829
|
-
const settingsMap = /* @__PURE__ */ new Map();
|
|
2830
|
-
for (const setting of settings) settingsMap.set(setting.key, setting.value);
|
|
2831
|
-
return settingsMap;
|
|
2832
|
-
}
|
|
2833
|
-
async readMultipleSettings(keys) {
|
|
2834
|
-
const settings = await this.appSettingsDb.select().from(app_settings_table).where(inArray(app_settings_table.key, keys));
|
|
2835
|
-
const settingsMap = /* @__PURE__ */ new Map();
|
|
2836
|
-
for (const setting of settings) settingsMap.set(setting.key, setting.value);
|
|
2837
|
-
return settingsMap;
|
|
2838
|
-
}
|
|
2839
|
-
async getNextSequentialId(recordType, userId) {
|
|
2840
|
-
const counterKey = `${recordType}:counter`;
|
|
2841
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2842
|
-
const [result] = await this.appSettingsDb.insert(app_settings_table).values({
|
|
2843
|
-
key: counterKey,
|
|
2844
|
-
value: 1,
|
|
2845
|
-
created_at: now,
|
|
2846
|
-
created_by: userId,
|
|
2847
|
-
updated_at: now,
|
|
2848
|
-
updated_by: userId
|
|
2849
|
-
}).onConflictDoUpdate({
|
|
2850
|
-
target: app_settings_table.key,
|
|
2851
|
-
set: {
|
|
2852
|
-
value: sql`json(json_extract(${app_settings_table.value}, '$') + 1)`,
|
|
2853
|
-
updated_at: now,
|
|
2854
|
-
updated_by: userId
|
|
2855
|
-
}
|
|
2856
|
-
}).returning({ value: app_settings_table.value });
|
|
2857
|
-
return String(result.value);
|
|
2858
|
-
}
|
|
2859
|
-
};
|
|
2860
|
-
AppSettingsRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IAppSettingsDb))], AppSettingsRepo);
|
|
2861
|
-
|
|
2862
|
-
//#endregion
|
|
2863
|
-
//#region src/slices/app_settings/app_settings_tokens.ts
|
|
2864
|
-
const APP_SETTINGS_TOKENS = {
|
|
2865
|
-
IGetNotificationEmailsFeature: Symbol("IGetNotificationEmailsFeature"),
|
|
2866
|
-
IUpdateNotificationEmailsFeature: Symbol("IUpdateNotificationEmailsFeature")
|
|
2867
|
-
};
|
|
2868
|
-
|
|
2869
|
-
//#endregion
|
|
2870
|
-
//#region src/slices/app_settings/features/get_notification_emails_feat.ts
|
|
2871
|
-
let GetNotificationEmailsFeat = class GetNotificationEmailsFeat$1 {
|
|
2872
|
-
constructor(appSettingsRepo) {
|
|
2873
|
-
this.appSettingsRepo = appSettingsRepo;
|
|
2874
|
-
}
|
|
2875
|
-
async execute() {
|
|
2876
|
-
return { emails: (await this.appSettingsRepo.readSetting(AppSettingKey.SUPPORT_TICKET_NOTIFICATION_EMAILS))?.value || [] };
|
|
2877
|
-
}
|
|
2878
|
-
};
|
|
2879
|
-
GetNotificationEmailsFeat = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IAppSettingsRepo))], GetNotificationEmailsFeat);
|
|
2880
|
-
|
|
2881
|
-
//#endregion
|
|
2882
|
-
//#region src/slices/app_settings/features/update_notification_emails_feat.ts
|
|
2883
|
-
let UpdateNotificationEmailsFeat = class UpdateNotificationEmailsFeat$1 {
|
|
2884
|
-
constructor(appSettingsRepo, session) {
|
|
2885
|
-
this.appSettingsRepo = appSettingsRepo;
|
|
2886
|
-
this.session = session;
|
|
2887
|
-
}
|
|
2888
|
-
async execute(input) {
|
|
2889
|
-
await this.appSettingsRepo.updateSetting(AppSettingKey.SUPPORT_TICKET_NOTIFICATION_EMAILS, input.emails.map((email) => email.email), this.session.user);
|
|
2890
|
-
return input;
|
|
2891
|
-
}
|
|
2892
|
-
};
|
|
2893
|
-
UpdateNotificationEmailsFeat = __decorate([
|
|
2894
|
-
injectable(),
|
|
2895
|
-
__decorateParam(0, inject(TOKENS.IAppSettingsRepo)),
|
|
2896
|
-
__decorateParam(1, injectSession())
|
|
2897
|
-
], UpdateNotificationEmailsFeat);
|
|
2898
|
-
|
|
2899
|
-
//#endregion
|
|
2900
|
-
//#region src/slices/app_settings/app_settings_container.ts
|
|
2901
|
-
function registerAppSettingsContainer() {
|
|
2902
|
-
container.registerSingleton(APP_SETTINGS_TOKENS.IGetNotificationEmailsFeature, GetNotificationEmailsFeat);
|
|
2903
|
-
container.registerSingleton(APP_SETTINGS_TOKENS.IUpdateNotificationEmailsFeature, UpdateNotificationEmailsFeat);
|
|
2904
|
-
}
|
|
2905
|
-
|
|
2906
|
-
//#endregion
|
|
2907
|
-
//#region src/slices/attachment/attachment_interfaces.ts
|
|
2908
|
-
const ATTACHMENT_TOKENS = {
|
|
2909
|
-
IAttachmentRepo: Symbol("IAttachmentRepo"),
|
|
2910
|
-
IReadAllAttachmentsByRecord: Symbol("IReadAllAttachmentsByRecord"),
|
|
2911
|
-
IReadAttachment: Symbol("IReadAttachment"),
|
|
2912
|
-
ICreateAttachment: Symbol("ICreateAttachment"),
|
|
2913
|
-
IUpdateAttachment: Symbol("IUpdateAttachment"),
|
|
2914
|
-
IDeleteAttachment: Symbol("IDeleteAttachment")
|
|
2915
|
-
};
|
|
2916
|
-
const ATTACHMENT_FOLDER_TOKENS = {
|
|
2917
|
-
IAttachmentFolderRepo: Symbol("IAttachmentFolderRepo"),
|
|
2918
|
-
ICreateAttachmentFolder: Symbol("ICreateAttachmentFolder")
|
|
2919
|
-
};
|
|
2920
|
-
|
|
2921
|
-
//#endregion
|
|
2922
|
-
//#region src/slices/attachment/db/attachment_folder_repo.ts
|
|
2923
|
-
let AttachmentFolderRepo = class AttachmentFolderRepo$1 {
|
|
2924
|
-
constructor(router) {
|
|
2925
|
-
this.router = router;
|
|
2926
|
-
}
|
|
2927
|
-
async create_folder(folder) {
|
|
2928
|
-
const id = await this.router.generateId(RecordConst.ATTACHMENT_FOLDER);
|
|
2929
|
-
return (await this.router.queryLatest((db) => db.insert(attachment_folder_table).values({
|
|
2930
|
-
id,
|
|
2931
|
-
...folder
|
|
2932
|
-
}).returning()))[0];
|
|
2933
|
-
}
|
|
2934
|
-
async read_folder(id) {
|
|
2935
|
-
const { ...rest } = getTableColumns(attachment_folder_table);
|
|
2936
|
-
const result = await this.router.queryById(id, (db) => db.select({ ...rest }).from(attachment_folder_table).where(and(eq(attachment_folder_table.id, id), isNull(attachment_folder_table.deleted_at))).limit(1));
|
|
2937
|
-
return result.length > 0 ? result[0] : null;
|
|
2969
|
+
async read_folder(id) {
|
|
2970
|
+
const { ...rest } = getTableColumns(attachment_folder_table);
|
|
2971
|
+
const result = await this.router.queryById(id, (db) => db.select({ ...rest }).from(attachment_folder_table).where(and(eq(attachment_folder_table.id, id), isNull(attachment_folder_table.deleted_at))).limit(1));
|
|
2972
|
+
return result.length > 0 ? result[0] : null;
|
|
2938
2973
|
}
|
|
2939
2974
|
async delete_folder(id) {
|
|
2940
2975
|
return (await this.router.queryById(id, (db) => db.update(attachment_folder_table).set({ deleted_at: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(attachment_folder_table.id, id)).returning())).length > 0;
|
|
@@ -3037,17 +3072,74 @@ let AttachmentRepo = class AttachmentRepo$1 {
|
|
|
3037
3072
|
};
|
|
3038
3073
|
AttachmentRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], AttachmentRepo);
|
|
3039
3074
|
|
|
3075
|
+
//#endregion
|
|
3076
|
+
//#region src/slices/record_version/record_version_interfaces.ts
|
|
3077
|
+
const RECORD_VERSION_TOKENS = {
|
|
3078
|
+
IRecordVersionsRepo: Symbol("IRecordVersionsRepo"),
|
|
3079
|
+
IReadAllRecordVersionsByRecord: Symbol("IReadAllRecordVersionsByRecord"),
|
|
3080
|
+
IReadAllRecordVersionsByRecordPaginated: Symbol("IReadAllRecordVersionsByRecordPaginated"),
|
|
3081
|
+
IReadAllRecordVersionsByRecordPaginatedCustomer: Symbol("IReadAllRecordVersionsByRecordPaginatedCustomer"),
|
|
3082
|
+
IReadTrackerActivityVersions: Symbol("IReadTrackerActivityVersions"),
|
|
3083
|
+
IReadRecordVersion: Symbol("IReadRecordVersion"),
|
|
3084
|
+
ICreateRecordVersion: Symbol("ICreateRecordVersion"),
|
|
3085
|
+
ICreateRecordVersionsBatch: Symbol("ICreateRecordVersionsBatch"),
|
|
3086
|
+
IUpdateRecordVersion: Symbol("IUpdateRecordVersion"),
|
|
3087
|
+
IDeleteRecordVersion: Symbol("IDeleteRecordVersion")
|
|
3088
|
+
};
|
|
3089
|
+
|
|
3090
|
+
//#endregion
|
|
3091
|
+
//#region src/slices/support_ticket/support_ticket_tokens.ts
|
|
3092
|
+
/**
|
|
3093
|
+
* Tokens for dependency injection
|
|
3094
|
+
* Organized by customer vs staff features
|
|
3095
|
+
*/
|
|
3096
|
+
const SUPPORT_TICKET_TOKENS = {
|
|
3097
|
+
ISupportTicketRepo: "ISupportTicketRepo",
|
|
3098
|
+
ISupportTicketNotificationService: "ISupportTicketNotificationService",
|
|
3099
|
+
ISupportTicketTriageService: "ISupportTicketTriageService",
|
|
3100
|
+
ICreateSupportTicketFeature: "ICreateSupportTicketFeature",
|
|
3101
|
+
IUpdateSupportTicketCustomerFeature: "IUpdateSupportTicketCustomerFeature",
|
|
3102
|
+
IGetSupportTicketCustomerFeature: "IGetSupportTicketCustomerFeature",
|
|
3103
|
+
IGetSupportTicketsCustomerFeature: "IGetSupportTicketsCustomerFeature",
|
|
3104
|
+
ICustomerToggleSubscriptionFeature: "ICustomerToggleSubscriptionFeature",
|
|
3105
|
+
IApproveSupportTicketFeature: "IApproveSupportTicketFeature",
|
|
3106
|
+
IRejectSupportTicketFeature: "IRejectSupportTicketFeature",
|
|
3107
|
+
IRevertSupportTicketFeature: "IRevertSupportTicketFeature",
|
|
3108
|
+
ICompleteSupportTicketFeature: "ICompleteSupportTicketFeature",
|
|
3109
|
+
IConvertToInternalFeature: "IConvertToInternalFeature",
|
|
3110
|
+
IConvertToCustomerFeature: "IConvertToCustomerFeature",
|
|
3111
|
+
IDeleteSupportTicketFeature: "IDeleteSupportTicketFeature",
|
|
3112
|
+
IArchiveSupportTicketFeature: "IArchiveSupportTicketFeature",
|
|
3113
|
+
ICancelInternalTaskFeature: "ICancelInternalTaskFeature",
|
|
3114
|
+
IReactivateInternalTaskFeature: "IReactivateInternalTaskFeature",
|
|
3115
|
+
ICreateSupportTicketAdminFeature: "ICreateSupportTicketAdminFeature",
|
|
3116
|
+
IUpdateSupportTicketAdminFeature: "IUpdateSupportTicketAdminFeature",
|
|
3117
|
+
IGetSupportTicketAdminFeature: "IGetSupportTicketAdminFeature",
|
|
3118
|
+
IGetSupportTicketsAdminFeature: "IGetSupportTicketsAdminFeature",
|
|
3119
|
+
IGetRequestorsForActiveSupportTicketsFeature: "IGetRequestorsForActiveSupportTicketsFeature",
|
|
3120
|
+
IGetSupportTicketNotesFeature: "IGetSupportTicketNotesFeature",
|
|
3121
|
+
IAddSupportTicketSubscriberFeature: "IAddSupportTicketSubscriberFeature",
|
|
3122
|
+
IListSupportTicketSubscribersFeature: "IListSupportTicketSubscribersFeature",
|
|
3123
|
+
IRemoveSupportTicketSubscriberFeature: "IRemoveSupportTicketSubscriberFeature",
|
|
3124
|
+
IFixSupportTicketUserIdsFeature: "IFixSupportTicketUserIdsFeature"
|
|
3125
|
+
};
|
|
3126
|
+
|
|
3040
3127
|
//#endregion
|
|
3041
3128
|
//#region src/slices/attachment/features/create_attachment.ts
|
|
3042
3129
|
let CreateAttachment = class CreateAttachment$1 {
|
|
3043
|
-
constructor(session, attachment_repo, attachment_folder_repo, env, logger$1) {
|
|
3130
|
+
constructor(session, attachment_repo, attachment_folder_repo, support_ticket_repo, create_record_version, env, logger$1) {
|
|
3044
3131
|
this.session = session;
|
|
3045
3132
|
this.attachment_repo = attachment_repo;
|
|
3046
3133
|
this.attachment_folder_repo = attachment_folder_repo;
|
|
3134
|
+
this.support_ticket_repo = support_ticket_repo;
|
|
3135
|
+
this.create_record_version = create_record_version;
|
|
3047
3136
|
this.env = env;
|
|
3048
3137
|
this.logger = logger$1;
|
|
3049
3138
|
}
|
|
3050
3139
|
async execute(attachment) {
|
|
3140
|
+
if (attachment.record_type === RecordConst$1.SUPPORT_TICKET) {
|
|
3141
|
+
if ((await this.support_ticket_repo.read(attachment.record_id))?.archived_at) throw new BusinessError("Cannot add attachments to an archived support ticket.");
|
|
3142
|
+
}
|
|
3051
3143
|
this.logger.debug("[CreateAttachment] Starting file upload");
|
|
3052
3144
|
if (!attachment.file) throw new Error("File is required");
|
|
3053
3145
|
const envWithOptional = this.env;
|
|
@@ -3090,6 +3182,17 @@ let CreateAttachment = class CreateAttachment$1 {
|
|
|
3090
3182
|
this.logger.debug("[CreateAttachment] Failed to upload file to R2");
|
|
3091
3183
|
throw new Error("Failed to upload file to storage");
|
|
3092
3184
|
} else this.logger.debug(`[CreateAttachment] File uploaded to R2 with etag: ${r2_res.etag}`);
|
|
3185
|
+
if (attachment.record_type === RecordConst$1.SUPPORT_TICKET) await this.create_record_version.execute({
|
|
3186
|
+
record_id: attachment.record_id,
|
|
3187
|
+
operation: OperationConst.UPDATE,
|
|
3188
|
+
recorded_at: now,
|
|
3189
|
+
record_type: RecordConst$1.SUPPORT_TICKET_ACTIVITY,
|
|
3190
|
+
record: { attachment_added: attachment.file_name },
|
|
3191
|
+
old_record: {},
|
|
3192
|
+
auth_uid: this.session.user.userId,
|
|
3193
|
+
auth_role: this.session.user.user_type ?? void 0,
|
|
3194
|
+
auth_username: this.session.user.username ?? void 0
|
|
3195
|
+
});
|
|
3093
3196
|
this.logger.debug("[CreateAttachment] Attachment created successfully");
|
|
3094
3197
|
return new_attachment;
|
|
3095
3198
|
}
|
|
@@ -3099,19 +3202,25 @@ CreateAttachment = __decorate([
|
|
|
3099
3202
|
__decorateParam(0, injectSession()),
|
|
3100
3203
|
__decorateParam(1, inject(ATTACHMENT_TOKENS.IAttachmentRepo)),
|
|
3101
3204
|
__decorateParam(2, inject(ATTACHMENT_FOLDER_TOKENS.IAttachmentFolderRepo)),
|
|
3102
|
-
__decorateParam(3, inject(
|
|
3103
|
-
__decorateParam(4, inject(
|
|
3205
|
+
__decorateParam(3, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
3206
|
+
__decorateParam(4, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
3207
|
+
__decorateParam(5, inject(TOKENS.ENV)),
|
|
3208
|
+
__decorateParam(6, inject(TOKENS.LOGGER))
|
|
3104
3209
|
], CreateAttachment);
|
|
3105
3210
|
|
|
3106
3211
|
//#endregion
|
|
3107
3212
|
//#region src/slices/attachment/features/create_attachment_folder.ts
|
|
3108
3213
|
let CreateAttachmentFolder = class CreateAttachmentFolder$1 {
|
|
3109
|
-
constructor(session, attachment_folder_repo, logger$1) {
|
|
3214
|
+
constructor(session, attachment_folder_repo, support_ticket_repo, logger$1) {
|
|
3110
3215
|
this.session = session;
|
|
3111
3216
|
this.attachment_folder_repo = attachment_folder_repo;
|
|
3217
|
+
this.support_ticket_repo = support_ticket_repo;
|
|
3112
3218
|
this.logger = logger$1;
|
|
3113
3219
|
}
|
|
3114
3220
|
async execute(input) {
|
|
3221
|
+
if (input.record_type === RecordConst$1.SUPPORT_TICKET) {
|
|
3222
|
+
if ((await this.support_ticket_repo.read(input.record_id))?.archived_at) throw new BusinessError("Cannot add folders to an archived support ticket.");
|
|
3223
|
+
}
|
|
3115
3224
|
this.logger.debug("[CreateAttachmentFolder] Starting folder creation");
|
|
3116
3225
|
if (!input.folder_name) throw new Error("Folder name is required");
|
|
3117
3226
|
const sanitized_name = sanitizeFileName(input.folder_name);
|
|
@@ -3159,28 +3268,52 @@ CreateAttachmentFolder = __decorate([
|
|
|
3159
3268
|
injectable(),
|
|
3160
3269
|
__decorateParam(0, injectSession()),
|
|
3161
3270
|
__decorateParam(1, inject(ATTACHMENT_FOLDER_TOKENS.IAttachmentFolderRepo)),
|
|
3162
|
-
__decorateParam(2, inject(
|
|
3271
|
+
__decorateParam(2, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
3272
|
+
__decorateParam(3, inject(TOKENS.LOGGER))
|
|
3163
3273
|
], CreateAttachmentFolder);
|
|
3164
3274
|
|
|
3165
3275
|
//#endregion
|
|
3166
3276
|
//#region src/slices/attachment/features/delete_attachment.ts
|
|
3167
3277
|
let DeleteAttachment = class DeleteAttachment$1 {
|
|
3168
|
-
constructor(attachment_repo, attachment_folder_repo) {
|
|
3278
|
+
constructor(session, attachment_repo, attachment_folder_repo, support_ticket_repo, create_record_version) {
|
|
3279
|
+
this.session = session;
|
|
3169
3280
|
this.attachment_repo = attachment_repo;
|
|
3170
3281
|
this.attachment_folder_repo = attachment_folder_repo;
|
|
3282
|
+
this.support_ticket_repo = support_ticket_repo;
|
|
3283
|
+
this.create_record_version = create_record_version;
|
|
3171
3284
|
}
|
|
3172
3285
|
async execute(id) {
|
|
3173
3286
|
const attachment = await this.attachment_repo.read_attachment(id);
|
|
3174
3287
|
if (!attachment) return false;
|
|
3288
|
+
if (attachment.record_type === RecordConst$1.SUPPORT_TICKET) {
|
|
3289
|
+
if ((await this.support_ticket_repo.read(attachment.record_id))?.archived_at) throw new BusinessError("Cannot delete attachments from an archived support ticket.");
|
|
3290
|
+
}
|
|
3175
3291
|
const deleted = await this.attachment_repo.delete_attachment(id);
|
|
3292
|
+
if (deleted && attachment.record_type === RecordConst$1.SUPPORT_TICKET) {
|
|
3293
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3294
|
+
await this.create_record_version.execute({
|
|
3295
|
+
record_id: attachment.record_id,
|
|
3296
|
+
operation: OperationConst.UPDATE,
|
|
3297
|
+
recorded_at: now,
|
|
3298
|
+
record_type: RecordConst$1.SUPPORT_TICKET_ACTIVITY,
|
|
3299
|
+
record: { attachment_removed: attachment.original_name },
|
|
3300
|
+
old_record: {},
|
|
3301
|
+
auth_uid: this.session.user.userId,
|
|
3302
|
+
auth_role: this.session.user.user_type ?? void 0,
|
|
3303
|
+
auth_username: this.session.user.username ?? void 0
|
|
3304
|
+
});
|
|
3305
|
+
}
|
|
3176
3306
|
if (deleted && attachment.folder_id) await this.attachment_folder_repo.recompute_file_count(attachment.folder_id);
|
|
3177
3307
|
return deleted;
|
|
3178
3308
|
}
|
|
3179
3309
|
};
|
|
3180
3310
|
DeleteAttachment = __decorate([
|
|
3181
3311
|
injectable(),
|
|
3182
|
-
__decorateParam(0,
|
|
3183
|
-
__decorateParam(1, inject(
|
|
3312
|
+
__decorateParam(0, injectSession()),
|
|
3313
|
+
__decorateParam(1, inject(ATTACHMENT_TOKENS.IAttachmentRepo)),
|
|
3314
|
+
__decorateParam(2, inject(ATTACHMENT_FOLDER_TOKENS.IAttachmentFolderRepo)),
|
|
3315
|
+
__decorateParam(3, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
3316
|
+
__decorateParam(4, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
3184
3317
|
], DeleteAttachment);
|
|
3185
3318
|
|
|
3186
3319
|
//#endregion
|
|
@@ -3474,7 +3607,7 @@ let CreditService = class CreditService$1 {
|
|
|
3474
3607
|
type: "DEDUCTION",
|
|
3475
3608
|
balance_after: CreditMath.toString(newMonthlyBalance + newRolloverBalance),
|
|
3476
3609
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3477
|
-
created_by: this.systemUser.
|
|
3610
|
+
created_by: this.systemUser.userId
|
|
3478
3611
|
};
|
|
3479
3612
|
await this.transactionRepo.createCreditTransaction(transaction);
|
|
3480
3613
|
return true;
|
|
@@ -3489,7 +3622,7 @@ let CreditService = class CreditService$1 {
|
|
|
3489
3622
|
type: "REFUND",
|
|
3490
3623
|
balance_after: CreditMath.toString(monthlyBalance + newRolloverBalance),
|
|
3491
3624
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3492
|
-
created_by: this.systemUser.
|
|
3625
|
+
created_by: this.systemUser.userId
|
|
3493
3626
|
};
|
|
3494
3627
|
await this.transactionRepo.createCreditTransaction(transaction);
|
|
3495
3628
|
}
|
|
@@ -3527,7 +3660,7 @@ let CreditService = class CreditService$1 {
|
|
|
3527
3660
|
type: "PURCHASE_ONETIME",
|
|
3528
3661
|
balance_after: CreditMath.toString(monthlyBalance + newRolloverBalance),
|
|
3529
3662
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3530
|
-
created_by: this.systemUser.
|
|
3663
|
+
created_by: this.systemUser.userId
|
|
3531
3664
|
};
|
|
3532
3665
|
await this.transactionRepo.createCreditTransaction(transaction);
|
|
3533
3666
|
}
|
|
@@ -3600,34 +3733,6 @@ BusinessLogicRouter = __decorate([
|
|
|
3600
3733
|
__decorateParam(3, inject("DeleteNoteSupportTicketProcessor"))
|
|
3601
3734
|
], BusinessLogicRouter);
|
|
3602
3735
|
|
|
3603
|
-
//#endregion
|
|
3604
|
-
//#region src/slices/support_ticket/support_ticket_tokens.ts
|
|
3605
|
-
/**
|
|
3606
|
-
* Tokens for dependency injection
|
|
3607
|
-
* Organized by customer vs staff features
|
|
3608
|
-
*/
|
|
3609
|
-
const SUPPORT_TICKET_TOKENS = {
|
|
3610
|
-
ISupportTicketRepo: "ISupportTicketRepo",
|
|
3611
|
-
ICreateSupportTicketFeature: "ICreateSupportTicketFeature",
|
|
3612
|
-
IUpdateSupportTicketCustomerFeature: "IUpdateSupportTicketCustomerFeature",
|
|
3613
|
-
IGetSupportTicketCustomerFeature: "IGetSupportTicketCustomerFeature",
|
|
3614
|
-
IGetSupportTicketsCustomerFeature: "IGetSupportTicketsCustomerFeature",
|
|
3615
|
-
IApproveSupportTicketFeature: "IApproveSupportTicketFeature",
|
|
3616
|
-
IRejectSupportTicketFeature: "IRejectSupportTicketFeature",
|
|
3617
|
-
IRevertSupportTicketFeature: "IRevertSupportTicketFeature",
|
|
3618
|
-
ICompleteSupportTicketFeature: "ICompleteSupportTicketFeature",
|
|
3619
|
-
IConvertToInternalFeature: "IConvertToInternalFeature",
|
|
3620
|
-
IConvertToCustomerFeature: "IConvertToCustomerFeature",
|
|
3621
|
-
IDeleteSupportTicketFeature: "IDeleteSupportTicketFeature",
|
|
3622
|
-
ICancelInternalTaskFeature: "ICancelInternalTaskFeature",
|
|
3623
|
-
IReactivateInternalTaskFeature: "IReactivateInternalTaskFeature",
|
|
3624
|
-
ICreateSupportTicketAdminFeature: "ICreateSupportTicketAdminFeature",
|
|
3625
|
-
IUpdateSupportTicketAdminFeature: "IUpdateSupportTicketAdminFeature",
|
|
3626
|
-
IGetSupportTicketAdminFeature: "IGetSupportTicketAdminFeature",
|
|
3627
|
-
IGetSupportTicketsAdminFeature: "IGetSupportTicketsAdminFeature",
|
|
3628
|
-
IGetSupportTicketNotesFeature: "IGetSupportTicketNotesFeature"
|
|
3629
|
-
};
|
|
3630
|
-
|
|
3631
3736
|
//#endregion
|
|
3632
3737
|
//#region src/slices/note/business_logic/support_ticket/create_note_support_ticket_processor.ts
|
|
3633
3738
|
let CreateNoteSupportTicketProcessor = class CreateNoteSupportTicketProcessor$1 {
|
|
@@ -3638,18 +3743,18 @@ let CreateNoteSupportTicketProcessor = class CreateNoteSupportTicketProcessor$1
|
|
|
3638
3743
|
async process(input) {
|
|
3639
3744
|
const supportTicket = await this.supportTicketRepo.read(input.record_id);
|
|
3640
3745
|
if (!supportTicket) throw new Error("Support ticket not found");
|
|
3746
|
+
if (supportTicket.archived_at) throw new Error("Cannot add comments to an archived ticket.");
|
|
3641
3747
|
const user = this.session.user;
|
|
3642
3748
|
if (user.user_type === "staff" || user.user_type === "super_admin") return await this.processStaffRequest(input, supportTicket);
|
|
3643
|
-
else if (user.user_type === "consumer") return await this.processConsumerRequest(input, supportTicket);
|
|
3644
|
-
else throw new
|
|
3749
|
+
else if (user.user_type === "consumer" || user.user_type === "lead") return await this.processConsumerRequest(input, supportTicket);
|
|
3750
|
+
else throw new BusinessError("Invalid user type");
|
|
3645
3751
|
}
|
|
3646
3752
|
async processStaffRequest(input, _supportTicket) {
|
|
3647
3753
|
return input;
|
|
3648
3754
|
}
|
|
3649
3755
|
async processConsumerRequest(input, supportTicket) {
|
|
3650
|
-
|
|
3651
|
-
if (
|
|
3652
|
-
if (input.is_internal) throw new Error("Consumers cannot create internal notes");
|
|
3756
|
+
if (supportTicket.approval_status === "INTERNAL") throw new BusinessError("You do not have access to this support ticket");
|
|
3757
|
+
if (input.is_internal) throw new BusinessError("Consumers cannot create internal notes");
|
|
3653
3758
|
return {
|
|
3654
3759
|
...input,
|
|
3655
3760
|
is_internal: false
|
|
@@ -3674,7 +3779,7 @@ const NOTE_TOKENS = {
|
|
|
3674
3779
|
IUpdateNoteFeature: "IUpdateNoteFeature",
|
|
3675
3780
|
IGetNotesFeature: "IGetNotesFeature",
|
|
3676
3781
|
IDeleteNoteFeature: "IDeleteNoteFeature",
|
|
3677
|
-
IBusinessLogicRouter: "IBusinessLogicRouter",
|
|
3782
|
+
IBusinessLogicRouter: "Note.IBusinessLogicRouter",
|
|
3678
3783
|
CreateNoteSupportTicketProcessor: "CreateNoteSupportTicketProcessor",
|
|
3679
3784
|
UpdateNoteSupportTicketProcessor: "UpdateNoteSupportTicketProcessor",
|
|
3680
3785
|
GetNotesSupportTicketProcessor: "GetNotesSupportTicketProcessor",
|
|
@@ -3691,21 +3796,27 @@ let DeleteNoteSupportTicketProcessor = class DeleteNoteSupportTicketProcessor$1
|
|
|
3691
3796
|
}
|
|
3692
3797
|
async process(id) {
|
|
3693
3798
|
const existingNote = await this.noteRepo.read(id);
|
|
3694
|
-
if (!existingNote) throw new
|
|
3695
|
-
if (existingNote.record_type !== "support_ticket") throw new
|
|
3799
|
+
if (!existingNote) throw new BusinessError("Note not found");
|
|
3800
|
+
if (existingNote.record_type !== "support_ticket") throw new BusinessError("This note does not belong to a support ticket");
|
|
3696
3801
|
const supportTicket = await this.supportTicketRepo.read(existingNote.record_id);
|
|
3697
|
-
if (!supportTicket) throw new
|
|
3802
|
+
if (!supportTicket) throw new BusinessError("Support ticket not found");
|
|
3698
3803
|
const user = this.session.user;
|
|
3699
3804
|
if (user.user_type === "staff" || user.user_type === "super_admin") return await this.processStaffRequest(id, existingNote, supportTicket);
|
|
3700
|
-
else if (user.user_type === "consumer") return await this.processConsumerRequest(id, existingNote, supportTicket);
|
|
3701
|
-
else throw new
|
|
3805
|
+
else if (user.user_type === "consumer" || user.user_type === "lead") return await this.processConsumerRequest(id, existingNote, supportTicket);
|
|
3806
|
+
else throw new BusinessError("Invalid user type");
|
|
3702
3807
|
}
|
|
3703
|
-
async processStaffRequest(
|
|
3704
|
-
if (
|
|
3705
|
-
|
|
3808
|
+
async processStaffRequest(id, existingNote, supportTicket) {
|
|
3809
|
+
if (existingNote.created_by !== this.session.user.userId) throw new BusinessError("You can only delete notes that you created");
|
|
3810
|
+
if (supportTicket.dev_lifecycle === "DEPLOYED" || supportTicket.approval_status === "REJECTED") throw new BusinessError("Cannot delete notes on completed or cancelled support tickets");
|
|
3811
|
+
if (supportTicket.archived_at) throw new BusinessError("Cannot delete comments on an archived ticket.");
|
|
3812
|
+
return id;
|
|
3706
3813
|
}
|
|
3707
|
-
async processConsumerRequest(
|
|
3708
|
-
|
|
3814
|
+
async processConsumerRequest(id, existingNote, supportTicket) {
|
|
3815
|
+
const user = this.session.user;
|
|
3816
|
+
if (supportTicket.approval_status === "INTERNAL") throw new BusinessError("You do not have access to this support ticket");
|
|
3817
|
+
if (existingNote.created_by !== user.userId) throw new BusinessError("You can only delete notes that you created");
|
|
3818
|
+
if (supportTicket.archived_at) throw new BusinessError("Cannot delete comments on an archived ticket.");
|
|
3819
|
+
return id;
|
|
3709
3820
|
}
|
|
3710
3821
|
};
|
|
3711
3822
|
DeleteNoteSupportTicketProcessor = __decorate([
|
|
@@ -3729,8 +3840,8 @@ let GetNotesSupportTicketProcessor = class GetNotesSupportTicketProcessor$1 {
|
|
|
3729
3840
|
const supportTicket = await this.supportTicketRepo.read(recordIdValue);
|
|
3730
3841
|
if (!supportTicket) throw new Error("Support ticket not found");
|
|
3731
3842
|
if (user.user_type === "staff" || user.user_type === "super_admin") return await this.processStaffRequest(filters, supportTicket);
|
|
3732
|
-
else if (user.user_type === "consumer") return await this.processConsumerRequest(filters, supportTicket);
|
|
3733
|
-
else throw new
|
|
3843
|
+
else if (user.user_type === "consumer" || user.user_type === "lead") return await this.processConsumerRequest(filters, supportTicket);
|
|
3844
|
+
else throw new BusinessError("Invalid user type");
|
|
3734
3845
|
}
|
|
3735
3846
|
return await this.applyVisibilityRules(filters);
|
|
3736
3847
|
}
|
|
@@ -3738,8 +3849,7 @@ let GetNotesSupportTicketProcessor = class GetNotesSupportTicketProcessor$1 {
|
|
|
3738
3849
|
return filters;
|
|
3739
3850
|
}
|
|
3740
3851
|
async processConsumerRequest(filters, supportTicket) {
|
|
3741
|
-
|
|
3742
|
-
if (supportTicket.created_by !== user.userId) throw new Error("You do not have access to this support ticket");
|
|
3852
|
+
if (supportTicket.approval_status === "INTERNAL") throw new BusinessError("You do not have access to this support ticket");
|
|
3743
3853
|
return {
|
|
3744
3854
|
...filters,
|
|
3745
3855
|
is_internal: {
|
|
@@ -3749,7 +3859,8 @@ let GetNotesSupportTicketProcessor = class GetNotesSupportTicketProcessor$1 {
|
|
|
3749
3859
|
};
|
|
3750
3860
|
}
|
|
3751
3861
|
async applyVisibilityRules(filters) {
|
|
3752
|
-
|
|
3862
|
+
const user = this.session.user;
|
|
3863
|
+
if (user.user_type === "consumer" || user.user_type === "lead") return {
|
|
3753
3864
|
...filters,
|
|
3754
3865
|
is_internal: {
|
|
3755
3866
|
operator: OPERATORS.EQUALS,
|
|
@@ -3775,25 +3886,26 @@ let UpdateNoteSupportTicketProcessor = class UpdateNoteSupportTicketProcessor$1
|
|
|
3775
3886
|
}
|
|
3776
3887
|
async process(input) {
|
|
3777
3888
|
const existingNote = await this.noteRepo.read(input.id);
|
|
3778
|
-
if (!existingNote) throw new
|
|
3779
|
-
if (existingNote.record_type !== "support_ticket") throw new
|
|
3889
|
+
if (!existingNote) throw new BusinessError("Note not found");
|
|
3890
|
+
if (existingNote.record_type !== "support_ticket") throw new BusinessError("This note does not belong to a support ticket");
|
|
3780
3891
|
const supportTicket = await this.supportTicketRepo.read(existingNote.record_id);
|
|
3781
|
-
if (!supportTicket) throw new
|
|
3892
|
+
if (!supportTicket) throw new BusinessError("Support ticket not found");
|
|
3893
|
+
if (supportTicket.archived_at) throw new BusinessError("Cannot edit comments on an archived ticket.");
|
|
3782
3894
|
const user = this.session.user;
|
|
3783
3895
|
if (user.user_type === "staff" || user.user_type === "super_admin") return await this.processStaffRequest(input, existingNote, supportTicket);
|
|
3784
|
-
else if (user.user_type === "consumer") return await this.processConsumerRequest(input, existingNote, supportTicket);
|
|
3785
|
-
else throw new
|
|
3896
|
+
else if (user.user_type === "consumer" || user.user_type === "lead") return await this.processConsumerRequest(input, existingNote, supportTicket);
|
|
3897
|
+
else throw new BusinessError("Invalid user type");
|
|
3786
3898
|
}
|
|
3787
3899
|
async processStaffRequest(input, existingNote, _supportTicket) {
|
|
3788
|
-
if (existingNote.created_by !== this.session.user.userId) throw new
|
|
3900
|
+
if (existingNote.created_by !== this.session.user.userId) throw new BusinessError("You can only update notes that you created");
|
|
3789
3901
|
return input;
|
|
3790
3902
|
}
|
|
3791
3903
|
async processConsumerRequest(input, existingNote, supportTicket) {
|
|
3792
3904
|
const user = this.session.user;
|
|
3793
|
-
if (supportTicket.
|
|
3794
|
-
if (existingNote.created_by !== user.userId) throw new
|
|
3795
|
-
if (existingNote.is_internal) throw new
|
|
3796
|
-
if (input.is_internal !== void 0 && input.is_internal !== existingNote.is_internal) throw new
|
|
3905
|
+
if (supportTicket.approval_status === "INTERNAL") throw new BusinessError("You do not have access to this support ticket");
|
|
3906
|
+
if (existingNote.created_by !== user.userId) throw new BusinessError("You can only update notes you created");
|
|
3907
|
+
if (existingNote.is_internal) throw new BusinessError("Consumers cannot update internal notes");
|
|
3908
|
+
if (input.is_internal !== void 0 && input.is_internal !== existingNote.is_internal) throw new BusinessError("Consumers cannot change note visibility");
|
|
3797
3909
|
return input;
|
|
3798
3910
|
}
|
|
3799
3911
|
};
|
|
@@ -3963,28 +4075,34 @@ let NoteRepo = class NoteRepo$1 {
|
|
|
3963
4075
|
NoteRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], NoteRepo);
|
|
3964
4076
|
|
|
3965
4077
|
//#endregion
|
|
3966
|
-
//#region src/slices/
|
|
3967
|
-
const
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
4078
|
+
//#region src/slices/user/user_interfaces.ts
|
|
4079
|
+
const USER_TOKENS = {
|
|
4080
|
+
IUserDisplayLookup: Symbol("IUserDisplayLookup"),
|
|
4081
|
+
IUsersRepo: Symbol("IUsersRepo"),
|
|
4082
|
+
ISignUpUser: Symbol("ISignUpUser"),
|
|
4083
|
+
IReadAllUsers: Symbol("IReadAllUsers"),
|
|
4084
|
+
IReadUser: Symbol("IReadUser"),
|
|
4085
|
+
ICreateUser: Symbol("ICreateUser"),
|
|
4086
|
+
IDeleteUser: Symbol("IDeleteUser"),
|
|
4087
|
+
IReadConsumers: Symbol("IReadConsumers"),
|
|
4088
|
+
IGetAllUsersFeature: "IGetAllUsersFeature",
|
|
4089
|
+
IGetUserFeature: "IGetUserFeature",
|
|
4090
|
+
IUpdateUserFeature: "IUpdateUserFeature",
|
|
4091
|
+
IGetUsersForSelectionFeature: "IGetUsersForSelectionFeature",
|
|
4092
|
+
IGetTriageUsersFeature: "IGetTriageUsersFeature"
|
|
3977
4093
|
};
|
|
3978
4094
|
|
|
3979
4095
|
//#endregion
|
|
3980
4096
|
//#region src/slices/note/features/create_note_feat.ts
|
|
3981
4097
|
let CreateNoteFeat = class CreateNoteFeat$1 {
|
|
3982
|
-
constructor(session, noteRepo, businessLogicRouter, create_record_version, supportTicketRepo) {
|
|
4098
|
+
constructor(session, noteRepo, businessLogicRouter, create_record_version, userDisplayLookup, supportTicketRepo, notificationService) {
|
|
3983
4099
|
this.session = session;
|
|
3984
4100
|
this.noteRepo = noteRepo;
|
|
3985
4101
|
this.businessLogicRouter = businessLogicRouter;
|
|
3986
4102
|
this.create_record_version = create_record_version;
|
|
4103
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
3987
4104
|
this.supportTicketRepo = supportTicketRepo;
|
|
4105
|
+
this.notificationService = notificationService;
|
|
3988
4106
|
}
|
|
3989
4107
|
async execute(input) {
|
|
3990
4108
|
const processor = this.businessLogicRouter.getCreateProcessor(input.record_type);
|
|
@@ -4014,7 +4132,13 @@ let CreateNoteFeat = class CreateNoteFeat$1 {
|
|
|
4014
4132
|
auth_role: this.session.user.user_type,
|
|
4015
4133
|
auth_username: this.session.user.username
|
|
4016
4134
|
});
|
|
4017
|
-
|
|
4135
|
+
await this.notifySupportTicketSubscribers(processedInput.record_type, processedInput.record_id, noteCreated.is_internal, noteCreated.body ?? "");
|
|
4136
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames([noteCreated.created_by]);
|
|
4137
|
+
return {
|
|
4138
|
+
...noteCreated,
|
|
4139
|
+
created_by_display_name: displayMap.get(noteCreated.created_by) ?? noteCreated.created_by,
|
|
4140
|
+
updated_by_display_name: displayMap.get(noteCreated.updated_by ?? "") ?? noteCreated.updated_by ?? null
|
|
4141
|
+
};
|
|
4018
4142
|
}
|
|
4019
4143
|
/**
|
|
4020
4144
|
* Bumps parent record's updated_at timestamp when note is created
|
|
@@ -4036,6 +4160,21 @@ let CreateNoteFeat = class CreateNoteFeat$1 {
|
|
|
4036
4160
|
default: break;
|
|
4037
4161
|
}
|
|
4038
4162
|
}
|
|
4163
|
+
/**
|
|
4164
|
+
* Notify support ticket subscribers when a note is added (best-effort)
|
|
4165
|
+
*/
|
|
4166
|
+
async notifySupportTicketSubscribers(recordType, recordId, isInternal, noteBody) {
|
|
4167
|
+
if (recordType !== RecordConst.SUPPORT_TICKET || !this.supportTicketRepo || !this.notificationService) return;
|
|
4168
|
+
try {
|
|
4169
|
+
const ticket = await this.supportTicketRepo.read(recordId);
|
|
4170
|
+
if (!ticket) return;
|
|
4171
|
+
const eventType = isInternal ? "NEW_INTERNAL_NOTE" : "NEW_CUSTOMER_NOTE";
|
|
4172
|
+
await this.notificationService.notifyFollowers(recordId, ticket, eventType, {
|
|
4173
|
+
noteBody: noteBody || void 0,
|
|
4174
|
+
actorUserId: this.session.user.userId
|
|
4175
|
+
});
|
|
4176
|
+
} catch {}
|
|
4177
|
+
}
|
|
4039
4178
|
};
|
|
4040
4179
|
CreateNoteFeat = __decorate([
|
|
4041
4180
|
injectable(),
|
|
@@ -4043,7 +4182,9 @@ CreateNoteFeat = __decorate([
|
|
|
4043
4182
|
__decorateParam(1, inject(NOTE_TOKENS.INoteRepo)),
|
|
4044
4183
|
__decorateParam(2, inject(NOTE_TOKENS.IBusinessLogicRouter)),
|
|
4045
4184
|
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
4046
|
-
__decorateParam(4, inject(
|
|
4185
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
4186
|
+
__decorateParam(5, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
4187
|
+
__decorateParam(6, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService))
|
|
4047
4188
|
], CreateNoteFeat);
|
|
4048
4189
|
|
|
4049
4190
|
//#endregion
|
|
@@ -4087,10 +4228,19 @@ DeleteNoteFeat = __decorate([
|
|
|
4087
4228
|
|
|
4088
4229
|
//#endregion
|
|
4089
4230
|
//#region src/slices/note/features/get_notes_feat.ts
|
|
4231
|
+
function collectUserIdsFromNotes(notes) {
|
|
4232
|
+
const ids = [];
|
|
4233
|
+
for (const n of notes) {
|
|
4234
|
+
if (n.created_by) ids.push(n.created_by);
|
|
4235
|
+
if (n.updated_by) ids.push(n.updated_by);
|
|
4236
|
+
}
|
|
4237
|
+
return ids;
|
|
4238
|
+
}
|
|
4090
4239
|
let GetNotesFeat = class GetNotesFeat$1 {
|
|
4091
|
-
constructor(noteRepo, businessLogicRouter) {
|
|
4240
|
+
constructor(noteRepo, businessLogicRouter, userDisplayLookup) {
|
|
4092
4241
|
this.noteRepo = noteRepo;
|
|
4093
4242
|
this.businessLogicRouter = businessLogicRouter;
|
|
4243
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
4094
4244
|
}
|
|
4095
4245
|
async execute(filters) {
|
|
4096
4246
|
let processedFilters = filters;
|
|
@@ -4101,23 +4251,36 @@ let GetNotesFeat = class GetNotesFeat$1 {
|
|
|
4101
4251
|
if (processor) processedFilters = await processor.process(filters);
|
|
4102
4252
|
}
|
|
4103
4253
|
}
|
|
4104
|
-
|
|
4254
|
+
const result = await this.noteRepo.read_all(processedFilters);
|
|
4255
|
+
const userIds = collectUserIdsFromNotes(result.items);
|
|
4256
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
4257
|
+
const enrichedItems = result.items.map((n) => ({
|
|
4258
|
+
...n,
|
|
4259
|
+
created_by_display_name: n.created_by ? displayMap.get(n.created_by) ?? n.created_by : null,
|
|
4260
|
+
updated_by_display_name: n.updated_by ? displayMap.get(n.updated_by) ?? n.updated_by : null
|
|
4261
|
+
}));
|
|
4262
|
+
return {
|
|
4263
|
+
...result,
|
|
4264
|
+
items: enrichedItems
|
|
4265
|
+
};
|
|
4105
4266
|
}
|
|
4106
4267
|
};
|
|
4107
4268
|
GetNotesFeat = __decorate([
|
|
4108
4269
|
injectable(),
|
|
4109
4270
|
__decorateParam(0, inject(NOTE_TOKENS.INoteRepo)),
|
|
4110
|
-
__decorateParam(1, inject(NOTE_TOKENS.IBusinessLogicRouter))
|
|
4271
|
+
__decorateParam(1, inject(NOTE_TOKENS.IBusinessLogicRouter)),
|
|
4272
|
+
__decorateParam(2, inject(USER_TOKENS.IUserDisplayLookup))
|
|
4111
4273
|
], GetNotesFeat);
|
|
4112
4274
|
|
|
4113
4275
|
//#endregion
|
|
4114
4276
|
//#region src/slices/note/features/update_note_feat.ts
|
|
4115
4277
|
let UpdateNoteFeat = class UpdateNoteFeat$1 {
|
|
4116
|
-
constructor(session, noteRepo, businessLogicRouter, create_record_version) {
|
|
4278
|
+
constructor(session, noteRepo, businessLogicRouter, create_record_version, userDisplayLookup) {
|
|
4117
4279
|
this.session = session;
|
|
4118
4280
|
this.noteRepo = noteRepo;
|
|
4119
4281
|
this.businessLogicRouter = businessLogicRouter;
|
|
4120
4282
|
this.create_record_version = create_record_version;
|
|
4283
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
4121
4284
|
}
|
|
4122
4285
|
async execute(input) {
|
|
4123
4286
|
const existingNote = await this.noteRepo.read(input.id);
|
|
@@ -4145,7 +4308,13 @@ let UpdateNoteFeat = class UpdateNoteFeat$1 {
|
|
|
4145
4308
|
auth_role: this.session.user.user_type,
|
|
4146
4309
|
auth_username: this.session.user.username
|
|
4147
4310
|
});
|
|
4148
|
-
|
|
4311
|
+
const userIds = [noteUpdated.created_by, noteUpdated.updated_by].filter(Boolean);
|
|
4312
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
4313
|
+
return {
|
|
4314
|
+
...noteUpdated,
|
|
4315
|
+
created_by_display_name: noteUpdated.created_by ? displayMap.get(noteUpdated.created_by) ?? noteUpdated.created_by : null,
|
|
4316
|
+
updated_by_display_name: noteUpdated.updated_by ? displayMap.get(noteUpdated.updated_by) ?? noteUpdated.updated_by : null
|
|
4317
|
+
};
|
|
4149
4318
|
}
|
|
4150
4319
|
};
|
|
4151
4320
|
UpdateNoteFeat = __decorate([
|
|
@@ -4153,7 +4322,8 @@ UpdateNoteFeat = __decorate([
|
|
|
4153
4322
|
__decorateParam(0, injectSession()),
|
|
4154
4323
|
__decorateParam(1, inject(NOTE_TOKENS.INoteRepo)),
|
|
4155
4324
|
__decorateParam(2, inject(NOTE_TOKENS.IBusinessLogicRouter)),
|
|
4156
|
-
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
4325
|
+
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
4326
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
4157
4327
|
], UpdateNoteFeat);
|
|
4158
4328
|
|
|
4159
4329
|
//#endregion
|
|
@@ -4200,22 +4370,6 @@ let PasswordResetTokenVerifier = class PasswordResetTokenVerifier$1 {
|
|
|
4200
4370
|
};
|
|
4201
4371
|
PasswordResetTokenVerifier = __decorate([injectable()], PasswordResetTokenVerifier);
|
|
4202
4372
|
|
|
4203
|
-
//#endregion
|
|
4204
|
-
//#region src/slices/user/user_interfaces.ts
|
|
4205
|
-
const USER_TOKENS = {
|
|
4206
|
-
IUsersRepo: Symbol("IUsersRepo"),
|
|
4207
|
-
ISignUpUser: Symbol("ISignUpUser"),
|
|
4208
|
-
IReadAllUsers: Symbol("IReadAllUsers"),
|
|
4209
|
-
IReadUser: Symbol("IReadUser"),
|
|
4210
|
-
ICreateUser: Symbol("ICreateUser"),
|
|
4211
|
-
IDeleteUser: Symbol("IDeleteUser"),
|
|
4212
|
-
IReadConsumers: Symbol("IReadConsumers"),
|
|
4213
|
-
IGetAllUsersFeature: "IGetAllUsersFeature",
|
|
4214
|
-
IGetUserFeature: "IGetUserFeature",
|
|
4215
|
-
IUpdateUserFeature: "IUpdateUserFeature",
|
|
4216
|
-
IGetUsersForSelectionFeature: "IGetUsersForSelectionFeature"
|
|
4217
|
-
};
|
|
4218
|
-
|
|
4219
4373
|
//#endregion
|
|
4220
4374
|
//#region src/slices/password_reset/features/change_password.ts
|
|
4221
4375
|
let ChangeUserPassword = class ChangeUserPassword$1 {
|
|
@@ -4360,6 +4514,51 @@ function registerPasswordResetContainer() {
|
|
|
4360
4514
|
container.registerSingleton(PASSWORD_RESET_TOKENS.IChangeUserPassword, ChangeUserPassword);
|
|
4361
4515
|
}
|
|
4362
4516
|
|
|
4517
|
+
//#endregion
|
|
4518
|
+
//#region src/slices/record_subscriber/db/record_subscriber_repo.ts
|
|
4519
|
+
let RecordSubscriberRepo = class RecordSubscriberRepo$1 {
|
|
4520
|
+
constructor(router) {
|
|
4521
|
+
this.router = router;
|
|
4522
|
+
}
|
|
4523
|
+
async create(entity) {
|
|
4524
|
+
const id = await this.router.generateId(RecordConst.RECORD_SUBSCRIBER);
|
|
4525
|
+
const [result] = await this.router.queryLatest((db) => db.insert(record_subscriber_table).values({
|
|
4526
|
+
id,
|
|
4527
|
+
...entity
|
|
4528
|
+
}).returning());
|
|
4529
|
+
return result;
|
|
4530
|
+
}
|
|
4531
|
+
async ensureSubscriber(entity) {
|
|
4532
|
+
const existing = await this.readByRecordAndUser(entity.record_type, entity.record_id, entity.user_id);
|
|
4533
|
+
if (existing) return existing;
|
|
4534
|
+
return this.create(entity);
|
|
4535
|
+
}
|
|
4536
|
+
async readByRecordId(recordType, recordId) {
|
|
4537
|
+
return this.router.queryAll((db) => db.select().from(record_subscriber_table).where(and(eq(record_subscriber_table.record_type, recordType), eq(record_subscriber_table.record_id, recordId))));
|
|
4538
|
+
}
|
|
4539
|
+
async readByRecordAndUser(recordType, recordId, userId) {
|
|
4540
|
+
return (await this.router.queryAll((db) => db.select().from(record_subscriber_table).where(and(eq(record_subscriber_table.record_type, recordType), eq(record_subscriber_table.record_id, recordId), eq(record_subscriber_table.user_id, userId))).limit(1)))[0];
|
|
4541
|
+
}
|
|
4542
|
+
async delete(id) {
|
|
4543
|
+
return (await this.router.queryLatest((db) => db.delete(record_subscriber_table).where(eq(record_subscriber_table.id, id)).returning())).length > 0;
|
|
4544
|
+
}
|
|
4545
|
+
async updateSubscribedEvents(id, subscribedEvents) {
|
|
4546
|
+
const [result] = await this.router.queryLatest((db) => db.update(record_subscriber_table).set({ subscribed_events: subscribedEvents }).where(eq(record_subscriber_table.id, id)).returning());
|
|
4547
|
+
return result;
|
|
4548
|
+
}
|
|
4549
|
+
};
|
|
4550
|
+
RecordSubscriberRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], RecordSubscriberRepo);
|
|
4551
|
+
|
|
4552
|
+
//#endregion
|
|
4553
|
+
//#region src/slices/record_subscriber/record_subscriber_tokens.ts
|
|
4554
|
+
const RECORD_SUBSCRIBER_TOKENS = { IRecordSubscriberRepo: "IRecordSubscriberRepo" };
|
|
4555
|
+
|
|
4556
|
+
//#endregion
|
|
4557
|
+
//#region src/slices/record_subscriber/record_subscriber_registration.ts
|
|
4558
|
+
function registerRecordSubscriberDependencies() {
|
|
4559
|
+
container.registerSingleton(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo, RecordSubscriberRepo);
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4363
4562
|
//#endregion
|
|
4364
4563
|
//#region src/slices/team/team_interfaces.ts
|
|
4365
4564
|
const TEAM_TOKENS = {
|
|
@@ -4407,10 +4606,11 @@ var RecordAccessValidatorRegistry = class {
|
|
|
4407
4606
|
}
|
|
4408
4607
|
};
|
|
4409
4608
|
let ValidateRecordAccess = class ValidateRecordAccess$1 {
|
|
4410
|
-
constructor(session, teamRepo, validatorRegistry) {
|
|
4609
|
+
constructor(session, teamRepo, validatorRegistry, logger$1) {
|
|
4411
4610
|
this.session = session;
|
|
4412
4611
|
this.teamRepo = teamRepo;
|
|
4413
4612
|
this.validatorRegistry = validatorRegistry;
|
|
4613
|
+
this.logger = logger$1;
|
|
4414
4614
|
}
|
|
4415
4615
|
async execute(record_id, record_type) {
|
|
4416
4616
|
const user = this.session.user;
|
|
@@ -4423,25 +4623,68 @@ let ValidateRecordAccess = class ValidateRecordAccess$1 {
|
|
|
4423
4623
|
case "team":
|
|
4424
4624
|
await this.validateTeamAccess(record_id, user.user_type);
|
|
4425
4625
|
break;
|
|
4426
|
-
default: if (user.user_type !== "lead" && user.user_type !== "staff" && user.user_type !== "super_admin")
|
|
4626
|
+
default: if (user.user_type !== "lead" && user.user_type !== "staff" && user.user_type !== "super_admin") {
|
|
4627
|
+
this.logger.warn("[ValidateRecordAccess] Access denied", {
|
|
4628
|
+
record_id,
|
|
4629
|
+
record_type,
|
|
4630
|
+
user_id: user.userId,
|
|
4631
|
+
user_type: user.user_type,
|
|
4632
|
+
reason: "Record type requires lead/staff/admin; no custom validator registered"
|
|
4633
|
+
});
|
|
4634
|
+
throw new BusinessError("Access denied: You do not have permission to view this record.");
|
|
4635
|
+
}
|
|
4427
4636
|
}
|
|
4428
4637
|
}
|
|
4638
|
+
async filterAllowedRecordTypes(record_id, record_types) {
|
|
4639
|
+
const allowed = (await Promise.all(record_types.map(async (rt) => {
|
|
4640
|
+
try {
|
|
4641
|
+
await this.execute(record_id, rt);
|
|
4642
|
+
return {
|
|
4643
|
+
rt,
|
|
4644
|
+
allowed: true
|
|
4645
|
+
};
|
|
4646
|
+
} catch {
|
|
4647
|
+
return {
|
|
4648
|
+
rt,
|
|
4649
|
+
allowed: false
|
|
4650
|
+
};
|
|
4651
|
+
}
|
|
4652
|
+
}))).filter((r) => r.allowed).map((r) => r.rt);
|
|
4653
|
+
if (allowed.length === 0) {
|
|
4654
|
+
this.logger.warn("[ValidateRecordAccess] Access denied - no requested record types allowed", {
|
|
4655
|
+
record_id,
|
|
4656
|
+
requested_record_types: record_types,
|
|
4657
|
+
user_id: this.session.user.userId,
|
|
4658
|
+
user_type: this.session.user.user_type
|
|
4659
|
+
});
|
|
4660
|
+
throw new BusinessError("Access denied: You do not have permission to access any of the requested record types.");
|
|
4661
|
+
}
|
|
4662
|
+
return allowed;
|
|
4663
|
+
}
|
|
4429
4664
|
/**
|
|
4430
4665
|
* Validates access to a team record
|
|
4431
4666
|
* Lead, staff, and admin have full access
|
|
4432
4667
|
* Consumers cannot access team records
|
|
4433
4668
|
*/
|
|
4434
4669
|
async validateTeamAccess(record_id, user_type) {
|
|
4435
|
-
if (!await this.teamRepo.read(record_id))
|
|
4670
|
+
if (!await this.teamRepo.read(record_id)) {
|
|
4671
|
+
this.logger.warn("[ValidateRecordAccess] Team not found", { record_id });
|
|
4672
|
+
throw new BusinessError("Team not found");
|
|
4673
|
+
}
|
|
4436
4674
|
if (user_type === "lead" || user_type === "staff" || user_type === "super_admin") return;
|
|
4437
|
-
|
|
4675
|
+
this.logger.warn("[ValidateRecordAccess] Access denied - team requires lead/staff", {
|
|
4676
|
+
record_id,
|
|
4677
|
+
user_type
|
|
4678
|
+
});
|
|
4679
|
+
throw new BusinessError("Access denied: Team records require lead or staff permissions");
|
|
4438
4680
|
}
|
|
4439
4681
|
};
|
|
4440
4682
|
ValidateRecordAccess = __decorate([
|
|
4441
4683
|
injectable(),
|
|
4442
4684
|
__decorateParam(0, injectSession()),
|
|
4443
4685
|
__decorateParam(1, inject(TEAM_TOKENS.ITeamRepository)),
|
|
4444
|
-
__decorateParam(2, inject(RECORD_VERSION_VALIDATION_TOKENS.ValidatorRegistry))
|
|
4686
|
+
__decorateParam(2, inject(RECORD_VERSION_VALIDATION_TOKENS.ValidatorRegistry)),
|
|
4687
|
+
__decorateParam(3, inject(TOKENS.LOGGER))
|
|
4445
4688
|
], ValidateRecordAccess);
|
|
4446
4689
|
|
|
4447
4690
|
//#endregion
|
|
@@ -4515,9 +4758,13 @@ let RecordVersionRepo = class RecordVersionRepo$1 {
|
|
|
4515
4758
|
/**
|
|
4516
4759
|
* NEW: Breadcrumb pagination for record versions
|
|
4517
4760
|
* Use this for new implementations requiring bidirectional navigation
|
|
4761
|
+
* Supports single record_type or multiple record_types (merged results)
|
|
4518
4762
|
*/
|
|
4519
|
-
async read_all_record_versions_paginated(record_id,
|
|
4520
|
-
const
|
|
4763
|
+
async read_all_record_versions_paginated(record_id, record_type_or_types, filters) {
|
|
4764
|
+
const record_types = Array.isArray(record_type_or_types) ? record_type_or_types : [record_type_or_types];
|
|
4765
|
+
const recordTypeCondition = record_types.length === 1 ? eq(record_version_table.record_type, record_types[0]) : inArray(record_version_table.record_type, record_types);
|
|
4766
|
+
const record_ids = filters?.record_ids;
|
|
4767
|
+
const baseConditions = [recordTypeCondition, record_ids && record_ids.length > 0 ? inArray(record_version_table.record_id, record_ids) : eq(record_version_table.record_id, record_id)];
|
|
4521
4768
|
if (filters?.start_date) baseConditions.push(gte(record_version_table.recorded_at, filters.start_date));
|
|
4522
4769
|
if (filters?.end_date) baseConditions.push(lte(record_version_table.recorded_at, filters.end_date));
|
|
4523
4770
|
return PaginationUtils.findAllPaginated({
|
|
@@ -4647,19 +4894,49 @@ ReadAllRecordVersionsByRecord = __decorate([
|
|
|
4647
4894
|
|
|
4648
4895
|
//#endregion
|
|
4649
4896
|
//#region src/slices/record_version/features/read_all_record_versions_by_record_paginated_customer_feat.ts
|
|
4897
|
+
const USER_ID_FIELDS$1 = new Set([
|
|
4898
|
+
"assigned_to",
|
|
4899
|
+
"created_by",
|
|
4900
|
+
"updated_by",
|
|
4901
|
+
"archived_by",
|
|
4902
|
+
"deleted_by"
|
|
4903
|
+
]);
|
|
4904
|
+
function collectUserIdsFromRecordVersions$1(items) {
|
|
4905
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4906
|
+
for (const item of items) for (const raw of [item.record, item.old_record]) {
|
|
4907
|
+
if (!raw || typeof raw !== "string") continue;
|
|
4908
|
+
try {
|
|
4909
|
+
const rec = JSON.parse(raw);
|
|
4910
|
+
for (const [key, val] of Object.entries(rec)) if (USER_ID_FIELDS$1.has(key) && typeof val === "string" && val.trim()) ids.add(val);
|
|
4911
|
+
} catch {}
|
|
4912
|
+
}
|
|
4913
|
+
return [...ids];
|
|
4914
|
+
}
|
|
4650
4915
|
let ReadAllRecordVersionsByRecordPaginatedCustomer = class ReadAllRecordVersionsByRecordPaginatedCustomer$1 {
|
|
4651
|
-
constructor(record_version_repo) {
|
|
4916
|
+
constructor(record_version_repo, userDisplayLookup) {
|
|
4652
4917
|
this.record_version_repo = record_version_repo;
|
|
4918
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
4653
4919
|
}
|
|
4654
4920
|
async execute(record_id, record_type, filters) {
|
|
4655
|
-
const
|
|
4656
|
-
records
|
|
4921
|
+
const record_types = filters?.record_types ?? [record_type];
|
|
4922
|
+
const records = await this.record_version_repo.read_all_record_versions_paginated(record_id, record_types, filters);
|
|
4923
|
+
const mappedRecords = records.items.map((record) => {
|
|
4924
|
+
const recordType = record.record_type;
|
|
4657
4925
|
return {
|
|
4658
4926
|
...record,
|
|
4659
|
-
record: this.filterFieldsByRecordType(JSON.stringify(record.record),
|
|
4660
|
-
old_record: this.filterFieldsByRecordType(JSON.stringify(record.old_record),
|
|
4927
|
+
record: this.filterFieldsByRecordType(JSON.stringify(record.record), recordType),
|
|
4928
|
+
old_record: this.filterFieldsByRecordType(JSON.stringify(record.old_record), recordType)
|
|
4661
4929
|
};
|
|
4662
4930
|
});
|
|
4931
|
+
records.items = mappedRecords;
|
|
4932
|
+
const userIds = collectUserIdsFromRecordVersions$1(mappedRecords);
|
|
4933
|
+
if (userIds.length > 0) {
|
|
4934
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
4935
|
+
return {
|
|
4936
|
+
...records,
|
|
4937
|
+
user_display_map: Object.fromEntries(displayMap)
|
|
4938
|
+
};
|
|
4939
|
+
}
|
|
4663
4940
|
return records;
|
|
4664
4941
|
}
|
|
4665
4942
|
/**
|
|
@@ -4669,7 +4946,9 @@ let ReadAllRecordVersionsByRecordPaginatedCustomer = class ReadAllRecordVersions
|
|
|
4669
4946
|
filterFieldsByRecordType(recordData, recordType) {
|
|
4670
4947
|
if (!recordData) return recordData;
|
|
4671
4948
|
switch (recordType) {
|
|
4672
|
-
case
|
|
4949
|
+
case RecordConst$1.SUPPORT_TICKET: return this.filterSupportTicketFields(recordData);
|
|
4950
|
+
case RecordConst$1.SUPPORT_TICKET_ACTIVITY: return recordData;
|
|
4951
|
+
case RecordConst$1.TRACKER_ACTIVITY: return recordData;
|
|
4673
4952
|
default: return recordData;
|
|
4674
4953
|
}
|
|
4675
4954
|
}
|
|
@@ -4704,26 +4983,60 @@ let ReadAllRecordVersionsByRecordPaginatedCustomer = class ReadAllRecordVersions
|
|
|
4704
4983
|
}
|
|
4705
4984
|
}
|
|
4706
4985
|
};
|
|
4707
|
-
ReadAllRecordVersionsByRecordPaginatedCustomer = __decorate([
|
|
4986
|
+
ReadAllRecordVersionsByRecordPaginatedCustomer = __decorate([
|
|
4987
|
+
injectable(),
|
|
4988
|
+
__decorateParam(0, inject(RECORD_VERSION_TOKENS.IRecordVersionsRepo)),
|
|
4989
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
4990
|
+
], ReadAllRecordVersionsByRecordPaginatedCustomer);
|
|
4708
4991
|
|
|
4709
4992
|
//#endregion
|
|
4710
4993
|
//#region src/slices/record_version/features/read_all_record_versions_by_record_paginated_feat.ts
|
|
4994
|
+
const USER_ID_FIELDS = new Set([
|
|
4995
|
+
"assigned_to",
|
|
4996
|
+
"created_by",
|
|
4997
|
+
"updated_by",
|
|
4998
|
+
"archived_by",
|
|
4999
|
+
"deleted_by"
|
|
5000
|
+
]);
|
|
5001
|
+
function collectUserIdsFromRecordVersions(items) {
|
|
5002
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5003
|
+
for (const item of items) for (const raw of [item.record, item.old_record]) {
|
|
5004
|
+
if (!raw || typeof raw !== "string") continue;
|
|
5005
|
+
try {
|
|
5006
|
+
const rec = JSON.parse(raw);
|
|
5007
|
+
for (const [key, val] of Object.entries(rec)) if (USER_ID_FIELDS.has(key) && typeof val === "string" && val.trim()) ids.add(val);
|
|
5008
|
+
} catch {}
|
|
5009
|
+
}
|
|
5010
|
+
return [...ids];
|
|
5011
|
+
}
|
|
4711
5012
|
let ReadAllRecordVersionsByRecordPaginated = class ReadAllRecordVersionsByRecordPaginated$1 {
|
|
4712
|
-
constructor(record_version_repo, validateRecordAccess, session) {
|
|
5013
|
+
constructor(record_version_repo, validateRecordAccess, userDisplayLookup, session) {
|
|
4713
5014
|
this.record_version_repo = record_version_repo;
|
|
4714
5015
|
this.validateRecordAccess = validateRecordAccess;
|
|
5016
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
4715
5017
|
this.session = session;
|
|
4716
5018
|
}
|
|
4717
5019
|
async execute(record_id, record_type, filters) {
|
|
4718
|
-
|
|
4719
|
-
const
|
|
4720
|
-
records
|
|
5020
|
+
const requested_types = filters?.record_types ?? [record_type];
|
|
5021
|
+
const allowed_record_types = await this.validateRecordAccess.filterAllowedRecordTypes(record_id, requested_types);
|
|
5022
|
+
const records = await this.record_version_repo.read_all_record_versions_paginated(record_id, allowed_record_types, filters);
|
|
5023
|
+
const mappedRecords = records.items.map((record) => {
|
|
5024
|
+
const recordType = record.record_type;
|
|
4721
5025
|
return {
|
|
4722
5026
|
...record,
|
|
4723
|
-
record: this.filterFieldsByRecordType(JSON.stringify(record.record),
|
|
4724
|
-
old_record: this.filterFieldsByRecordType(JSON.stringify(record.old_record),
|
|
5027
|
+
record: this.filterFieldsByRecordType(JSON.stringify(record.record), recordType),
|
|
5028
|
+
old_record: this.filterFieldsByRecordType(JSON.stringify(record.old_record), recordType)
|
|
4725
5029
|
};
|
|
4726
5030
|
});
|
|
5031
|
+
records.items = mappedRecords;
|
|
5032
|
+
const userIds = collectUserIdsFromRecordVersions(mappedRecords);
|
|
5033
|
+
if (userIds.length > 0) {
|
|
5034
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
5035
|
+
return {
|
|
5036
|
+
...records,
|
|
5037
|
+
user_display_map: Object.fromEntries(displayMap)
|
|
5038
|
+
};
|
|
5039
|
+
}
|
|
4727
5040
|
return records;
|
|
4728
5041
|
}
|
|
4729
5042
|
/**
|
|
@@ -4734,7 +5047,9 @@ let ReadAllRecordVersionsByRecordPaginated = class ReadAllRecordVersionsByRecord
|
|
|
4734
5047
|
if (!recordData) return recordData;
|
|
4735
5048
|
if (this.session.user.user_type === "staff" || this.session.user.user_type === "super_admin") return recordData;
|
|
4736
5049
|
switch (recordType) {
|
|
4737
|
-
case
|
|
5050
|
+
case RecordConst$1.SUPPORT_TICKET: return this.filterSupportTicketFields(recordData);
|
|
5051
|
+
case RecordConst$1.SUPPORT_TICKET_ACTIVITY: return recordData;
|
|
5052
|
+
case RecordConst$1.TRACKER_ACTIVITY: return recordData;
|
|
4738
5053
|
default: return recordData;
|
|
4739
5054
|
}
|
|
4740
5055
|
}
|
|
@@ -4773,7 +5088,8 @@ ReadAllRecordVersionsByRecordPaginated = __decorate([
|
|
|
4773
5088
|
injectable(),
|
|
4774
5089
|
__decorateParam(0, inject(RECORD_VERSION_TOKENS.IRecordVersionsRepo)),
|
|
4775
5090
|
__decorateParam(1, inject(RECORD_VERSION_VALIDATION_TOKENS.IValidateRecordAccess)),
|
|
4776
|
-
__decorateParam(2,
|
|
5091
|
+
__decorateParam(2, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
5092
|
+
__decorateParam(3, injectSession())
|
|
4777
5093
|
], ReadAllRecordVersionsByRecordPaginated);
|
|
4778
5094
|
|
|
4779
5095
|
//#endregion
|
|
@@ -4808,6 +5124,482 @@ function registerRecordVersionContainer() {
|
|
|
4808
5124
|
container.registerSingleton(RECORD_VERSION_VALIDATION_TOKENS.IValidateRecordAccess, ValidateRecordAccess);
|
|
4809
5125
|
}
|
|
4810
5126
|
|
|
5127
|
+
//#endregion
|
|
5128
|
+
//#region src/slices/saved_filter/saved_filter_interfaces.ts
|
|
5129
|
+
const SAVED_FILTER_TOKENS = {
|
|
5130
|
+
ISavedFilterRepo: Symbol("ISavedFilterRepo"),
|
|
5131
|
+
IListSavedFilters: Symbol("IListSavedFilters"),
|
|
5132
|
+
IListAllSavedFilters: Symbol("IListAllSavedFilters"),
|
|
5133
|
+
ICreateSavedFilter: Symbol("ICreateSavedFilter"),
|
|
5134
|
+
IUpdateSavedFilter: Symbol("IUpdateSavedFilter"),
|
|
5135
|
+
IDeleteSavedFilter: Symbol("IDeleteSavedFilter")
|
|
5136
|
+
};
|
|
5137
|
+
|
|
5138
|
+
//#endregion
|
|
5139
|
+
//#region src/slices/saved_filter/db/saved_filter_mapper.ts
|
|
5140
|
+
function mapSavedFilterEntityToDto(entity) {
|
|
5141
|
+
let filters;
|
|
5142
|
+
try {
|
|
5143
|
+
filters = JSON.parse(entity.filters);
|
|
5144
|
+
} catch {
|
|
5145
|
+
filters = {};
|
|
5146
|
+
}
|
|
5147
|
+
return {
|
|
5148
|
+
id: entity.id,
|
|
5149
|
+
user_id: entity.user_id,
|
|
5150
|
+
name: entity.name,
|
|
5151
|
+
context: entity.context,
|
|
5152
|
+
route_path: entity.route_path,
|
|
5153
|
+
filters,
|
|
5154
|
+
sort_by: entity.sort_by ?? void 0,
|
|
5155
|
+
sort_direction: entity.sort_direction ?? void 0,
|
|
5156
|
+
created_at: entity.created_at,
|
|
5157
|
+
updated_at: entity.updated_at
|
|
5158
|
+
};
|
|
5159
|
+
}
|
|
5160
|
+
|
|
5161
|
+
//#endregion
|
|
5162
|
+
//#region src/slices/saved_filter/features/create_saved_filter_feat.ts
|
|
5163
|
+
let CreateSavedFilterFeature = class CreateSavedFilterFeature$1 {
|
|
5164
|
+
constructor(session, repo) {
|
|
5165
|
+
this.session = session;
|
|
5166
|
+
this.repo = repo;
|
|
5167
|
+
}
|
|
5168
|
+
async execute(input) {
|
|
5169
|
+
const userId = this.session.user.userId;
|
|
5170
|
+
if ((await this.repo.listByUserAndContext(userId, input.context)).length >= MAX_PRESETS_PER_CONTEXT) throw new BusinessError(`You can have at most ${MAX_PRESETS_PER_CONTEXT} presets per page. Delete one to save a new preset.`);
|
|
5171
|
+
return mapSavedFilterEntityToDto(await this.repo.create({
|
|
5172
|
+
user_id: userId,
|
|
5173
|
+
name: input.name,
|
|
5174
|
+
context: input.context,
|
|
5175
|
+
route_path: input.route_path,
|
|
5176
|
+
filters: JSON.stringify(input.filters),
|
|
5177
|
+
sort_by: input.sort_by ?? null,
|
|
5178
|
+
sort_direction: input.sort_direction ?? null
|
|
5179
|
+
}));
|
|
5180
|
+
}
|
|
5181
|
+
};
|
|
5182
|
+
CreateSavedFilterFeature = __decorate([
|
|
5183
|
+
injectable(),
|
|
5184
|
+
__decorateParam(0, injectSession()),
|
|
5185
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5186
|
+
], CreateSavedFilterFeature);
|
|
5187
|
+
|
|
5188
|
+
//#endregion
|
|
5189
|
+
//#region src/slices/saved_filter/user_pinned_preset_interfaces.ts
|
|
5190
|
+
const USER_PINNED_PRESET_TOKENS = {
|
|
5191
|
+
IUserPinnedPresetRepo: Symbol("IUserPinnedPresetRepo"),
|
|
5192
|
+
IListPinnedPresets: Symbol("IListPinnedPresets"),
|
|
5193
|
+
IAddPinnedPreset: Symbol("IAddPinnedPreset"),
|
|
5194
|
+
IRemovePinnedPreset: Symbol("IRemovePinnedPreset"),
|
|
5195
|
+
IReorderPinnedPresets: Symbol("IRearrangePinnedPresets")
|
|
5196
|
+
};
|
|
5197
|
+
|
|
5198
|
+
//#endregion
|
|
5199
|
+
//#region src/slices/saved_filter/features/delete_saved_filter_feat.ts
|
|
5200
|
+
let DeleteSavedFilterFeature = class DeleteSavedFilterFeature$1 {
|
|
5201
|
+
constructor(session, repo, pinnedRepo) {
|
|
5202
|
+
this.session = session;
|
|
5203
|
+
this.repo = repo;
|
|
5204
|
+
this.pinnedRepo = pinnedRepo;
|
|
5205
|
+
}
|
|
5206
|
+
async execute(id) {
|
|
5207
|
+
const existing = await this.repo.read(id);
|
|
5208
|
+
if (!existing) return false;
|
|
5209
|
+
if (existing.user_id !== this.session.user.userId) throw new BusinessError("You can only delete your own saved filters");
|
|
5210
|
+
await this.pinnedRepo.deleteByPresetId(id);
|
|
5211
|
+
return await this.repo.delete(id);
|
|
5212
|
+
}
|
|
5213
|
+
};
|
|
5214
|
+
DeleteSavedFilterFeature = __decorate([
|
|
5215
|
+
injectable(),
|
|
5216
|
+
__decorateParam(0, injectSession()),
|
|
5217
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo)),
|
|
5218
|
+
__decorateParam(2, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo))
|
|
5219
|
+
], DeleteSavedFilterFeature);
|
|
5220
|
+
|
|
5221
|
+
//#endregion
|
|
5222
|
+
//#region src/slices/saved_filter/features/list_saved_filters_feat.ts
|
|
5223
|
+
let ListSavedFiltersFeature = class ListSavedFiltersFeature$1 {
|
|
5224
|
+
constructor(session, repo) {
|
|
5225
|
+
this.session = session;
|
|
5226
|
+
this.repo = repo;
|
|
5227
|
+
}
|
|
5228
|
+
async execute(context) {
|
|
5229
|
+
const userId = this.session.user.userId;
|
|
5230
|
+
return (await this.repo.listByUserAndContext(userId, context)).map(mapSavedFilterEntityToDto);
|
|
5231
|
+
}
|
|
5232
|
+
};
|
|
5233
|
+
ListSavedFiltersFeature = __decorate([
|
|
5234
|
+
injectable(),
|
|
5235
|
+
__decorateParam(0, injectSession()),
|
|
5236
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5237
|
+
], ListSavedFiltersFeature);
|
|
5238
|
+
|
|
5239
|
+
//#endregion
|
|
5240
|
+
//#region src/slices/saved_filter/features/update_saved_filter_feat.ts
|
|
5241
|
+
let UpdateSavedFilterFeature = class UpdateSavedFilterFeature$1 {
|
|
5242
|
+
constructor(session, repo) {
|
|
5243
|
+
this.session = session;
|
|
5244
|
+
this.repo = repo;
|
|
5245
|
+
}
|
|
5246
|
+
async execute(input) {
|
|
5247
|
+
const existing = await this.repo.read(input.id);
|
|
5248
|
+
if (!existing) return null;
|
|
5249
|
+
if (existing.user_id !== this.session.user.userId) throw new BusinessError("You can only update your own saved filters");
|
|
5250
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5251
|
+
return mapSavedFilterEntityToDto(await this.repo.update({
|
|
5252
|
+
id: input.id,
|
|
5253
|
+
...input.name !== void 0 && { name: input.name },
|
|
5254
|
+
...input.route_path !== void 0 && { route_path: input.route_path },
|
|
5255
|
+
...input.filters !== void 0 && { filters: JSON.stringify(input.filters) },
|
|
5256
|
+
...input.sort_by !== void 0 && { sort_by: input.sort_by },
|
|
5257
|
+
...input.sort_direction !== void 0 && { sort_direction: input.sort_direction },
|
|
5258
|
+
updated_at: now
|
|
5259
|
+
}));
|
|
5260
|
+
}
|
|
5261
|
+
};
|
|
5262
|
+
UpdateSavedFilterFeature = __decorate([
|
|
5263
|
+
injectable(),
|
|
5264
|
+
__decorateParam(0, injectSession()),
|
|
5265
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5266
|
+
], UpdateSavedFilterFeature);
|
|
5267
|
+
|
|
5268
|
+
//#endregion
|
|
5269
|
+
//#region src/slices/saved_filter/features/add_pinned_preset_feat.ts
|
|
5270
|
+
let AddPinnedPresetFeature = class AddPinnedPresetFeature$1 {
|
|
5271
|
+
constructor(session, pinnedRepo, savedFilterRepo) {
|
|
5272
|
+
this.session = session;
|
|
5273
|
+
this.pinnedRepo = pinnedRepo;
|
|
5274
|
+
this.savedFilterRepo = savedFilterRepo;
|
|
5275
|
+
}
|
|
5276
|
+
async execute(presetId) {
|
|
5277
|
+
const userId = this.session.user.userId;
|
|
5278
|
+
const preset = await this.savedFilterRepo.read(presetId);
|
|
5279
|
+
if (!preset) return null;
|
|
5280
|
+
if (preset.user_id !== userId) throw new BusinessError("You can only pin your own presets");
|
|
5281
|
+
if (await this.pinnedRepo.findByUserAndPreset(userId, presetId)) return mapSavedFilterEntityToDto(preset);
|
|
5282
|
+
const pins = await this.pinnedRepo.listByUser(userId);
|
|
5283
|
+
if (pins.length >= MAX_PINNED_PRESETS) throw new BusinessError(`You can pin at most ${MAX_PINNED_PRESETS} presets. Unpin one to add another.`);
|
|
5284
|
+
const position = pins.length;
|
|
5285
|
+
await this.pinnedRepo.create(userId, presetId, position);
|
|
5286
|
+
return mapSavedFilterEntityToDto(preset);
|
|
5287
|
+
}
|
|
5288
|
+
};
|
|
5289
|
+
AddPinnedPresetFeature = __decorate([
|
|
5290
|
+
injectable(),
|
|
5291
|
+
__decorateParam(0, injectSession()),
|
|
5292
|
+
__decorateParam(1, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo)),
|
|
5293
|
+
__decorateParam(2, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5294
|
+
], AddPinnedPresetFeature);
|
|
5295
|
+
|
|
5296
|
+
//#endregion
|
|
5297
|
+
//#region src/slices/saved_filter/features/list_all_saved_filters_feat.ts
|
|
5298
|
+
let ListAllSavedFiltersFeature = class ListAllSavedFiltersFeature$1 {
|
|
5299
|
+
constructor(session, repo) {
|
|
5300
|
+
this.session = session;
|
|
5301
|
+
this.repo = repo;
|
|
5302
|
+
}
|
|
5303
|
+
async execute() {
|
|
5304
|
+
const userId = this.session.user.userId;
|
|
5305
|
+
return (await this.repo.listByUser(userId)).map(mapSavedFilterEntityToDto);
|
|
5306
|
+
}
|
|
5307
|
+
};
|
|
5308
|
+
ListAllSavedFiltersFeature = __decorate([
|
|
5309
|
+
injectable(),
|
|
5310
|
+
__decorateParam(0, injectSession()),
|
|
5311
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5312
|
+
], ListAllSavedFiltersFeature);
|
|
5313
|
+
|
|
5314
|
+
//#endregion
|
|
5315
|
+
//#region src/slices/saved_filter/features/list_pinned_presets_feat.ts
|
|
5316
|
+
let ListPinnedPresetsFeature = class ListPinnedPresetsFeature$1 {
|
|
5317
|
+
constructor(session, pinnedRepo, savedFilterRepo) {
|
|
5318
|
+
this.session = session;
|
|
5319
|
+
this.pinnedRepo = pinnedRepo;
|
|
5320
|
+
this.savedFilterRepo = savedFilterRepo;
|
|
5321
|
+
}
|
|
5322
|
+
async execute() {
|
|
5323
|
+
const userId = this.session.user.userId;
|
|
5324
|
+
const pins = await this.pinnedRepo.listByUser(userId);
|
|
5325
|
+
if (pins.length === 0) return [];
|
|
5326
|
+
const presetIds = pins.map((p) => p.preset_id);
|
|
5327
|
+
const presets = await this.savedFilterRepo.readByIds(presetIds);
|
|
5328
|
+
const presetMap = new Map(presets.map((p) => [p.id, p]));
|
|
5329
|
+
const result = [];
|
|
5330
|
+
for (const pin of pins) {
|
|
5331
|
+
const preset = presetMap.get(pin.preset_id);
|
|
5332
|
+
if (preset && preset.user_id === userId) result.push(mapSavedFilterEntityToDto(preset));
|
|
5333
|
+
}
|
|
5334
|
+
return result;
|
|
5335
|
+
}
|
|
5336
|
+
};
|
|
5337
|
+
ListPinnedPresetsFeature = __decorate([
|
|
5338
|
+
injectable(),
|
|
5339
|
+
__decorateParam(0, injectSession()),
|
|
5340
|
+
__decorateParam(1, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo)),
|
|
5341
|
+
__decorateParam(2, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5342
|
+
], ListPinnedPresetsFeature);
|
|
5343
|
+
|
|
5344
|
+
//#endregion
|
|
5345
|
+
//#region src/slices/saved_filter/features/remove_pinned_preset_feat.ts
|
|
5346
|
+
let RemovePinnedPresetFeature = class RemovePinnedPresetFeature$1 {
|
|
5347
|
+
constructor(session, pinnedRepo) {
|
|
5348
|
+
this.session = session;
|
|
5349
|
+
this.pinnedRepo = pinnedRepo;
|
|
5350
|
+
}
|
|
5351
|
+
async execute(presetId) {
|
|
5352
|
+
const userId = this.session.user.userId;
|
|
5353
|
+
const pin = await this.pinnedRepo.findByUserAndPreset(userId, presetId);
|
|
5354
|
+
if (!pin) return false;
|
|
5355
|
+
return await this.pinnedRepo.delete(pin.id);
|
|
5356
|
+
}
|
|
5357
|
+
};
|
|
5358
|
+
RemovePinnedPresetFeature = __decorate([
|
|
5359
|
+
injectable(),
|
|
5360
|
+
__decorateParam(0, injectSession()),
|
|
5361
|
+
__decorateParam(1, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo))
|
|
5362
|
+
], RemovePinnedPresetFeature);
|
|
5363
|
+
|
|
5364
|
+
//#endregion
|
|
5365
|
+
//#region src/slices/saved_filter/features/reorder_pinned_presets_feat.ts
|
|
5366
|
+
let ReorderPinnedPresetsFeature = class ReorderPinnedPresetsFeature$1 {
|
|
5367
|
+
constructor(session, pinnedRepo) {
|
|
5368
|
+
this.session = session;
|
|
5369
|
+
this.pinnedRepo = pinnedRepo;
|
|
5370
|
+
}
|
|
5371
|
+
async execute(presetIds) {
|
|
5372
|
+
const userId = this.session.user.userId;
|
|
5373
|
+
for (let i = 0; i < presetIds.length; i++) {
|
|
5374
|
+
const presetId = presetIds[i];
|
|
5375
|
+
const pin = await this.pinnedRepo.findByUserAndPreset(userId, presetId);
|
|
5376
|
+
if (pin) await this.pinnedRepo.updatePosition(pin.id, i);
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5379
|
+
};
|
|
5380
|
+
ReorderPinnedPresetsFeature = __decorate([
|
|
5381
|
+
injectable(),
|
|
5382
|
+
__decorateParam(0, injectSession()),
|
|
5383
|
+
__decorateParam(1, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo))
|
|
5384
|
+
], ReorderPinnedPresetsFeature);
|
|
5385
|
+
|
|
5386
|
+
//#endregion
|
|
5387
|
+
//#region src/slices/saved_filter/db/saved_filter_repo.ts
|
|
5388
|
+
let SavedFilterRepository = class SavedFilterRepository$1 {
|
|
5389
|
+
constructor(router) {
|
|
5390
|
+
this.router = router;
|
|
5391
|
+
}
|
|
5392
|
+
async create(entity) {
|
|
5393
|
+
const id = await this.router.generateId(RecordConst.SAVED_FILTER);
|
|
5394
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5395
|
+
const [result] = await this.router.queryLatest((db) => db.insert(saved_filter_table).values({
|
|
5396
|
+
id,
|
|
5397
|
+
user_id: entity.user_id,
|
|
5398
|
+
name: entity.name,
|
|
5399
|
+
context: entity.context,
|
|
5400
|
+
route_path: entity.route_path,
|
|
5401
|
+
filters: entity.filters,
|
|
5402
|
+
sort_by: entity.sort_by ?? null,
|
|
5403
|
+
sort_direction: entity.sort_direction ?? null,
|
|
5404
|
+
created_at: now,
|
|
5405
|
+
updated_at: now
|
|
5406
|
+
}).returning());
|
|
5407
|
+
return result;
|
|
5408
|
+
}
|
|
5409
|
+
async read(id) {
|
|
5410
|
+
return (await this.router.queryById(id, (db) => db.select().from(saved_filter_table).where(eq(saved_filter_table.id, id)).limit(1)))[0] ?? null;
|
|
5411
|
+
}
|
|
5412
|
+
async readByIds(ids) {
|
|
5413
|
+
if (ids.length === 0) return [];
|
|
5414
|
+
return this.router.queryByIds(ids, (db, idsForShard) => db.select().from(saved_filter_table).where(inArray(saved_filter_table.id, idsForShard)));
|
|
5415
|
+
}
|
|
5416
|
+
async listByUser(userId) {
|
|
5417
|
+
return await this.router.queryAll((db) => db.select().from(saved_filter_table).where(eq(saved_filter_table.user_id, userId)));
|
|
5418
|
+
}
|
|
5419
|
+
async listByUserAndContext(userId, context) {
|
|
5420
|
+
return await this.router.queryAll((db) => db.select().from(saved_filter_table).where(and(eq(saved_filter_table.user_id, userId), eq(saved_filter_table.context, context))));
|
|
5421
|
+
}
|
|
5422
|
+
async update(entity) {
|
|
5423
|
+
const updated = (await this.router.queryById(entity.id, (db) => db.update(saved_filter_table).set({
|
|
5424
|
+
...entity.name !== void 0 && { name: entity.name },
|
|
5425
|
+
...entity.route_path !== void 0 && { route_path: entity.route_path },
|
|
5426
|
+
...entity.filters !== void 0 && { filters: entity.filters },
|
|
5427
|
+
...entity.sort_by !== void 0 && { sort_by: entity.sort_by },
|
|
5428
|
+
...entity.sort_direction !== void 0 && { sort_direction: entity.sort_direction },
|
|
5429
|
+
updated_at: entity.updated_at
|
|
5430
|
+
}).where(eq(saved_filter_table.id, entity.id)).returning()))[0];
|
|
5431
|
+
if (!updated) throw new Error("Saved filter not found or update failed");
|
|
5432
|
+
return updated;
|
|
5433
|
+
}
|
|
5434
|
+
async delete(id) {
|
|
5435
|
+
return (await this.router.queryById(id, (db) => db.delete(saved_filter_table).where(eq(saved_filter_table.id, id)).returning({ id: saved_filter_table.id }))).length > 0;
|
|
5436
|
+
}
|
|
5437
|
+
};
|
|
5438
|
+
SavedFilterRepository = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], SavedFilterRepository);
|
|
5439
|
+
|
|
5440
|
+
//#endregion
|
|
5441
|
+
//#region src/slices/saved_filter/db/user_pinned_preset_repo.ts
|
|
5442
|
+
let UserPinnedPresetRepository = class UserPinnedPresetRepository$1 {
|
|
5443
|
+
constructor(router) {
|
|
5444
|
+
this.router = router;
|
|
5445
|
+
}
|
|
5446
|
+
async create(userId, presetId, position) {
|
|
5447
|
+
const id = await this.router.generateId(RecordConst.USER_PINNED_PRESET);
|
|
5448
|
+
const [result] = await this.router.queryLatest((db) => db.insert(user_pinned_preset_table).values({
|
|
5449
|
+
id,
|
|
5450
|
+
user_id: userId,
|
|
5451
|
+
preset_id: presetId,
|
|
5452
|
+
position
|
|
5453
|
+
}).returning());
|
|
5454
|
+
return result;
|
|
5455
|
+
}
|
|
5456
|
+
async listByUser(userId) {
|
|
5457
|
+
return (await this.router.queryAll((db) => db.select().from(user_pinned_preset_table).where(eq(user_pinned_preset_table.user_id, userId)))).sort((a, b) => a.position - b.position);
|
|
5458
|
+
}
|
|
5459
|
+
async findByUserAndPreset(userId, presetId) {
|
|
5460
|
+
return (await this.router.queryAll((db) => db.select().from(user_pinned_preset_table).where(and(eq(user_pinned_preset_table.user_id, userId), eq(user_pinned_preset_table.preset_id, presetId))).limit(1)))[0] ?? null;
|
|
5461
|
+
}
|
|
5462
|
+
async updatePosition(id, position) {
|
|
5463
|
+
await this.router.queryById(id, (db) => db.update(user_pinned_preset_table).set({ position }).where(eq(user_pinned_preset_table.id, id)).returning({ id: user_pinned_preset_table.id }));
|
|
5464
|
+
}
|
|
5465
|
+
async delete(id) {
|
|
5466
|
+
return (await this.router.queryById(id, (db) => db.delete(user_pinned_preset_table).where(eq(user_pinned_preset_table.id, id)).returning({ id: user_pinned_preset_table.id }))).length > 0;
|
|
5467
|
+
}
|
|
5468
|
+
async deleteByPresetId(presetId) {
|
|
5469
|
+
const pins = await this.router.queryAll((db) => db.select().from(user_pinned_preset_table).where(eq(user_pinned_preset_table.preset_id, presetId)));
|
|
5470
|
+
let deleted = 0;
|
|
5471
|
+
for (const pin of pins) if (await this.delete(pin.id)) deleted++;
|
|
5472
|
+
return deleted;
|
|
5473
|
+
}
|
|
5474
|
+
};
|
|
5475
|
+
UserPinnedPresetRepository = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], UserPinnedPresetRepository);
|
|
5476
|
+
|
|
5477
|
+
//#endregion
|
|
5478
|
+
//#region src/slices/saved_filter/saved_filter_container.ts
|
|
5479
|
+
function registerSavedFilterContainer() {
|
|
5480
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.ISavedFilterRepo, SavedFilterRepository);
|
|
5481
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo, UserPinnedPresetRepository);
|
|
5482
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.IListSavedFilters, ListSavedFiltersFeature);
|
|
5483
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.IListAllSavedFilters, ListAllSavedFiltersFeature);
|
|
5484
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.ICreateSavedFilter, CreateSavedFilterFeature);
|
|
5485
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.IUpdateSavedFilter, UpdateSavedFilterFeature);
|
|
5486
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.IDeleteSavedFilter, DeleteSavedFilterFeature);
|
|
5487
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IListPinnedPresets, ListPinnedPresetsFeature);
|
|
5488
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IAddPinnedPreset, AddPinnedPresetFeature);
|
|
5489
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IRemovePinnedPreset, RemovePinnedPresetFeature);
|
|
5490
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IReorderPinnedPresets, ReorderPinnedPresetsFeature);
|
|
5491
|
+
}
|
|
5492
|
+
|
|
5493
|
+
//#endregion
|
|
5494
|
+
//#region src/slices/support_staff/db/support_staff_repo.ts
|
|
5495
|
+
let SupportStaffRepo = class SupportStaffRepo$1 {
|
|
5496
|
+
constructor(router) {
|
|
5497
|
+
this.router = router;
|
|
5498
|
+
}
|
|
5499
|
+
async readAll() {
|
|
5500
|
+
return this.router.queryAll((db) => db.select().from(support_staff_table).orderBy(support_staff_table.user_id));
|
|
5501
|
+
}
|
|
5502
|
+
async readSupportStaffMembers() {
|
|
5503
|
+
return this.router.queryAll((db) => db.select({
|
|
5504
|
+
id: support_staff_table.id,
|
|
5505
|
+
user_id: support_staff_table.user_id,
|
|
5506
|
+
email: user_table.email,
|
|
5507
|
+
created_at: support_staff_table.created_at
|
|
5508
|
+
}).from(support_staff_table).innerJoin(user_table, eq(support_staff_table.user_id, user_table.id)).orderBy(support_staff_table.user_id));
|
|
5509
|
+
}
|
|
5510
|
+
async readTriageUsers() {
|
|
5511
|
+
return this.router.queryAll((db) => db.select({
|
|
5512
|
+
id: user_table.id,
|
|
5513
|
+
email: user_table.email
|
|
5514
|
+
}).from(support_staff_table).innerJoin(user_table, eq(support_staff_table.user_id, user_table.id)).orderBy(support_staff_table.user_id));
|
|
5515
|
+
}
|
|
5516
|
+
async add(userId, createdBy) {
|
|
5517
|
+
const id = await this.router.generateId(RecordConst.SUPPORT_STAFF);
|
|
5518
|
+
const entity = {
|
|
5519
|
+
user_id: userId,
|
|
5520
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5521
|
+
created_by: createdBy
|
|
5522
|
+
};
|
|
5523
|
+
const [result] = await this.router.queryLatest((db) => db.insert(support_staff_table).values({
|
|
5524
|
+
id,
|
|
5525
|
+
...entity
|
|
5526
|
+
}).returning());
|
|
5527
|
+
return result;
|
|
5528
|
+
}
|
|
5529
|
+
async remove(userId) {
|
|
5530
|
+
return (await this.router.queryLatest((db) => db.delete(support_staff_table).where(eq(support_staff_table.user_id, userId)).returning({ id: support_staff_table.id }))).length > 0;
|
|
5531
|
+
}
|
|
5532
|
+
};
|
|
5533
|
+
SupportStaffRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], SupportStaffRepo);
|
|
5534
|
+
|
|
5535
|
+
//#endregion
|
|
5536
|
+
//#region src/slices/support_staff/support_staff_tokens.ts
|
|
5537
|
+
const SUPPORT_STAFF_TOKENS = {
|
|
5538
|
+
ISupportStaffRepo: Symbol("ISupportStaffRepo"),
|
|
5539
|
+
IListSupportStaffFeature: Symbol("IListSupportStaffFeature"),
|
|
5540
|
+
IAddSupportStaffFeature: Symbol("IAddSupportStaffFeature"),
|
|
5541
|
+
IRemoveSupportStaffFeature: Symbol("IRemoveSupportStaffFeature")
|
|
5542
|
+
};
|
|
5543
|
+
|
|
5544
|
+
//#endregion
|
|
5545
|
+
//#region src/slices/support_staff/features/add_support_staff_feat.ts
|
|
5546
|
+
let AddSupportStaffFeat = class AddSupportStaffFeat$1 {
|
|
5547
|
+
constructor(supportStaffRepo, userRepo, session) {
|
|
5548
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
5549
|
+
this.userRepo = userRepo;
|
|
5550
|
+
this.session = session;
|
|
5551
|
+
}
|
|
5552
|
+
async execute(userId) {
|
|
5553
|
+
const user = await this.userRepo.read_user(userId);
|
|
5554
|
+
if (!user) throw new Error("User not found");
|
|
5555
|
+
if (!["staff", "super_admin"].includes(user.user_type)) throw new BusinessError("Only staff and super_admin users can be added to support staff");
|
|
5556
|
+
if ((await this.supportStaffRepo.readAll()).some((s) => s.user_id === userId)) throw new Error("User is already support staff");
|
|
5557
|
+
const created = await this.supportStaffRepo.add(userId, this.session.user.userId);
|
|
5558
|
+
const member = (await this.supportStaffRepo.readSupportStaffMembers()).find((m) => m.id === created.id);
|
|
5559
|
+
if (!member) throw new Error("Failed to fetch created support staff member");
|
|
5560
|
+
return member;
|
|
5561
|
+
}
|
|
5562
|
+
};
|
|
5563
|
+
AddSupportStaffFeat = __decorate([
|
|
5564
|
+
injectable(),
|
|
5565
|
+
__decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo)),
|
|
5566
|
+
__decorateParam(1, inject(USER_TOKENS.IUsersRepo)),
|
|
5567
|
+
__decorateParam(2, inject(TOKENS.AUTHENTICATED_SESSION))
|
|
5568
|
+
], AddSupportStaffFeat);
|
|
5569
|
+
|
|
5570
|
+
//#endregion
|
|
5571
|
+
//#region src/slices/support_staff/features/list_support_staff_feat.ts
|
|
5572
|
+
let ListSupportStaffFeat = class ListSupportStaffFeat$1 {
|
|
5573
|
+
constructor(supportStaffRepo) {
|
|
5574
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
5575
|
+
}
|
|
5576
|
+
async execute() {
|
|
5577
|
+
return this.supportStaffRepo.readSupportStaffMembers();
|
|
5578
|
+
}
|
|
5579
|
+
};
|
|
5580
|
+
ListSupportStaffFeat = __decorate([injectable(), __decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo))], ListSupportStaffFeat);
|
|
5581
|
+
|
|
5582
|
+
//#endregion
|
|
5583
|
+
//#region src/slices/support_staff/features/remove_support_staff_feat.ts
|
|
5584
|
+
let RemoveSupportStaffFeat = class RemoveSupportStaffFeat$1 {
|
|
5585
|
+
constructor(supportStaffRepo) {
|
|
5586
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
5587
|
+
}
|
|
5588
|
+
async execute(userId) {
|
|
5589
|
+
if (!await this.supportStaffRepo.remove(userId)) throw new Error("Support staff member not found");
|
|
5590
|
+
}
|
|
5591
|
+
};
|
|
5592
|
+
RemoveSupportStaffFeat = __decorate([injectable(), __decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo))], RemoveSupportStaffFeat);
|
|
5593
|
+
|
|
5594
|
+
//#endregion
|
|
5595
|
+
//#region src/slices/support_staff/support_staff_registration.ts
|
|
5596
|
+
function registerSupportStaffDependencies() {
|
|
5597
|
+
container.registerSingleton(SUPPORT_STAFF_TOKENS.ISupportStaffRepo, SupportStaffRepo);
|
|
5598
|
+
container.registerSingleton(SUPPORT_STAFF_TOKENS.IListSupportStaffFeature, ListSupportStaffFeat);
|
|
5599
|
+
container.registerSingleton(SUPPORT_STAFF_TOKENS.IAddSupportStaffFeature, AddSupportStaffFeat);
|
|
5600
|
+
container.registerSingleton(SUPPORT_STAFF_TOKENS.IRemoveSupportStaffFeature, RemoveSupportStaffFeat);
|
|
5601
|
+
}
|
|
5602
|
+
|
|
4811
5603
|
//#endregion
|
|
4812
5604
|
//#region src/slices/support_ticket/db/support_ticket_query_config.ts
|
|
4813
5605
|
const supportTicketFields = {
|
|
@@ -4820,9 +5612,9 @@ const supportTicketFields = {
|
|
|
4820
5612
|
},
|
|
4821
5613
|
priority: {
|
|
4822
5614
|
column: support_ticket_table.priority,
|
|
4823
|
-
type: "
|
|
5615
|
+
type: "number",
|
|
4824
5616
|
filterable: true,
|
|
4825
|
-
searchable:
|
|
5617
|
+
searchable: false,
|
|
4826
5618
|
sortable: true
|
|
4827
5619
|
},
|
|
4828
5620
|
approval_status: {
|
|
@@ -4839,18 +5631,11 @@ const supportTicketFields = {
|
|
|
4839
5631
|
searchable: true,
|
|
4840
5632
|
sortable: true
|
|
4841
5633
|
},
|
|
4842
|
-
|
|
4843
|
-
column: support_ticket_table.
|
|
4844
|
-
type: "string",
|
|
4845
|
-
filterable: true,
|
|
4846
|
-
searchable: true,
|
|
4847
|
-
sortable: false
|
|
4848
|
-
},
|
|
4849
|
-
requester_name: {
|
|
4850
|
-
column: support_ticket_table.requester_name,
|
|
5634
|
+
created_by: {
|
|
5635
|
+
column: support_ticket_table.created_by,
|
|
4851
5636
|
type: "string",
|
|
4852
5637
|
filterable: true,
|
|
4853
|
-
searchable:
|
|
5638
|
+
searchable: false,
|
|
4854
5639
|
sortable: false
|
|
4855
5640
|
},
|
|
4856
5641
|
title: {
|
|
@@ -4909,20 +5694,6 @@ const supportTicketFields = {
|
|
|
4909
5694
|
searchable: false,
|
|
4910
5695
|
sortable: false
|
|
4911
5696
|
},
|
|
4912
|
-
requesterName: {
|
|
4913
|
-
column: support_ticket_table.requester_name,
|
|
4914
|
-
type: "string",
|
|
4915
|
-
filterable: false,
|
|
4916
|
-
searchable: true,
|
|
4917
|
-
sortable: false
|
|
4918
|
-
},
|
|
4919
|
-
requesterEmail: {
|
|
4920
|
-
column: support_ticket_table.requester_email,
|
|
4921
|
-
type: "string",
|
|
4922
|
-
filterable: false,
|
|
4923
|
-
searchable: true,
|
|
4924
|
-
sortable: false
|
|
4925
|
-
},
|
|
4926
5697
|
approvalStatus: {
|
|
4927
5698
|
column: support_ticket_table.approval_status,
|
|
4928
5699
|
type: "string",
|
|
@@ -4993,7 +5764,8 @@ const buildFieldFilters$1 = createFilterBuilder({
|
|
|
4993
5764
|
processedSeparately: [
|
|
4994
5765
|
"archived_at",
|
|
4995
5766
|
"status",
|
|
4996
|
-
"is_locked"
|
|
5767
|
+
"is_locked",
|
|
5768
|
+
"dev_lifecycle"
|
|
4997
5769
|
]
|
|
4998
5770
|
});
|
|
4999
5771
|
/**
|
|
@@ -5007,6 +5779,7 @@ function mapSupportTicketStatusToApprovalStatus(statuses) {
|
|
|
5007
5779
|
case "CANCELLED": return "REJECTED";
|
|
5008
5780
|
case "FOLLOWUP": return;
|
|
5009
5781
|
case "IN_PROGRESS": return;
|
|
5782
|
+
case "VERIFICATION": return;
|
|
5010
5783
|
case "COMPLETED": return;
|
|
5011
5784
|
default: throw new Error(`Invalid support ticket status: ${status}`);
|
|
5012
5785
|
}
|
|
@@ -5017,6 +5790,7 @@ function mapSupportTicketStatusToApprovalStatus(statuses) {
|
|
|
5017
5790
|
case "CANCELLED": return;
|
|
5018
5791
|
case "FOLLOWUP": return;
|
|
5019
5792
|
case "IN_PROGRESS": return;
|
|
5793
|
+
case "VERIFICATION": return "VERIFICATION";
|
|
5020
5794
|
case "COMPLETED": return "DEPLOYED";
|
|
5021
5795
|
default: throw new Error(`Invalid support ticket status: ${status}`);
|
|
5022
5796
|
}
|
|
@@ -5060,7 +5834,21 @@ function buildSupportTicketQuery(filters) {
|
|
|
5060
5834
|
} else if (statusValue === "IN_PROGRESS") {
|
|
5061
5835
|
conditions.push(eq(support_ticket_table.approval_status, "APPROVED"));
|
|
5062
5836
|
conditions.push(or(eq(support_ticket_table.dev_lifecycle, "DEVELOPMENT"), eq(support_ticket_table.dev_lifecycle, "CODE_REVIEW"), eq(support_ticket_table.dev_lifecycle, "TESTING"), eq(support_ticket_table.dev_lifecycle, "STAGING"), eq(support_ticket_table.dev_lifecycle, "PO_APPROVAL")));
|
|
5063
|
-
} else if (statusValue === "
|
|
5837
|
+
} else if (statusValue === "VERIFICATION") conditions.push(eq(support_ticket_table.dev_lifecycle, "VERIFICATION"));
|
|
5838
|
+
else if (statusValue === "COMPLETED") conditions.push(eq(support_ticket_table.dev_lifecycle, "DEPLOYED"));
|
|
5839
|
+
}
|
|
5840
|
+
}
|
|
5841
|
+
if (filters?.dev_lifecycle) {
|
|
5842
|
+
const dl = filters.dev_lifecycle;
|
|
5843
|
+
const col = support_ticket_table.dev_lifecycle;
|
|
5844
|
+
const isPending = (v) => v === "PENDING";
|
|
5845
|
+
if (dl.value !== void 0 && isPending(dl.value) && dl.operator === OPERATORS.EQUALS) conditions.push(isNull(col));
|
|
5846
|
+
else if (dl.values?.some(isPending) && dl.operator === OPERATORS.IS_ONE_OF) {
|
|
5847
|
+
const others = dl.values.filter((v) => !isPending(v));
|
|
5848
|
+
conditions.push(others.length ? or(isNull(col), inArray(col, others)) : isNull(col));
|
|
5849
|
+
} else {
|
|
5850
|
+
const cond = applyFilter(col, dl, "string");
|
|
5851
|
+
if (cond) conditions.push(cond);
|
|
5064
5852
|
}
|
|
5065
5853
|
}
|
|
5066
5854
|
if (filters?.is_locked) if (typeof filters.is_locked === "object" ? filters.is_locked.value : filters.is_locked) conditions.push(ne(support_ticket_table.approval_status, "PENDING"));
|
|
@@ -5102,7 +5890,7 @@ let SupportTicketRepo = class SupportTicketRepo$1 {
|
|
|
5102
5890
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5103
5891
|
const [result] = await this.router.queryById(support_ticket.id, (db) => db.update(support_ticket_table).set({
|
|
5104
5892
|
...support_ticket,
|
|
5105
|
-
updated_at:
|
|
5893
|
+
updated_at: now
|
|
5106
5894
|
}).where(eq(support_ticket_table.id, support_ticket.id)).returning());
|
|
5107
5895
|
return result;
|
|
5108
5896
|
}
|
|
@@ -5157,6 +5945,26 @@ let SupportTicketRepo = class SupportTicketRepo$1 {
|
|
|
5157
5945
|
return PaginationUtils.findAllPaginated(this.paginationConfig, filters || {}, conditions);
|
|
5158
5946
|
}
|
|
5159
5947
|
/**
|
|
5948
|
+
* Get distinct requestor (created_by) user IDs for active support tickets.
|
|
5949
|
+
* Active = not archived (archived_at IS NULL).
|
|
5950
|
+
* @param excludeInternal - when true, exclude internal tickets (for customer view)
|
|
5951
|
+
* Queries all shards.
|
|
5952
|
+
*/
|
|
5953
|
+
async read_distinct_requestors_for_active_tickets(options) {
|
|
5954
|
+
const conditions = [isNull(support_ticket_table.deleted_at), isNull(support_ticket_table.archived_at)];
|
|
5955
|
+
if (options?.excludeInternal) conditions.push(ne(support_ticket_table.approval_status, "INTERNAL"));
|
|
5956
|
+
const rows = await this.router.queryAll((db) => db.selectDistinct({ created_by: support_ticket_table.created_by }).from(support_ticket_table).where(and(...conditions)));
|
|
5957
|
+
return [...new Set(rows.map((r) => r.created_by).filter(Boolean))];
|
|
5958
|
+
}
|
|
5959
|
+
/**
|
|
5960
|
+
* Find support tickets with invalid user IDs (e.g. emails instead of 32-char universal IDs).
|
|
5961
|
+
* Queries all shards.
|
|
5962
|
+
*/
|
|
5963
|
+
async findWithInvalidUserIds() {
|
|
5964
|
+
const invalidLengthCondition = or(sql`LENGTH(${support_ticket_table.created_by}) != 32`, sql`LENGTH(${support_ticket_table.updated_by}) != 32`, sql`(${support_ticket_table.assigned_to} IS NOT NULL AND LENGTH(${support_ticket_table.assigned_to}) != 32)`, sql`(${support_ticket_table.archived_by} IS NOT NULL AND LENGTH(${support_ticket_table.archived_by}) != 32)`, sql`(${support_ticket_table.deleted_by} IS NOT NULL AND LENGTH(${support_ticket_table.deleted_by}) != 32)`);
|
|
5965
|
+
return this.router.queryAll((db) => db.select().from(support_ticket_table).where(and(isNull(support_ticket_table.deleted_at), invalidLengthCondition)));
|
|
5966
|
+
}
|
|
5967
|
+
/**
|
|
5160
5968
|
* Soft delete a support ticket by ID
|
|
5161
5969
|
*/
|
|
5162
5970
|
async soft_delete(id, deleted_by) {
|
|
@@ -5169,7 +5977,237 @@ let SupportTicketRepo = class SupportTicketRepo$1 {
|
|
|
5169
5977
|
return result[0];
|
|
5170
5978
|
}
|
|
5171
5979
|
};
|
|
5172
|
-
SupportTicketRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], SupportTicketRepo);
|
|
5980
|
+
SupportTicketRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], SupportTicketRepo);
|
|
5981
|
+
|
|
5982
|
+
//#endregion
|
|
5983
|
+
//#region src/slices/user_profile/user_profile_interfaces.ts
|
|
5984
|
+
const USER_PROFILE_TOKENS = {
|
|
5985
|
+
IUserProfilesRepo: Symbol("IUserProfilesRepo"),
|
|
5986
|
+
IReadUserProfilesByUser: Symbol("IReadUserProfilesByUser"),
|
|
5987
|
+
IReadUserProfile: Symbol("IReadUserProfile"),
|
|
5988
|
+
ICreateUserProfile: Symbol("ICreateUserProfile"),
|
|
5989
|
+
IUpdateUserProfile: Symbol("IUpdateUserProfile")
|
|
5990
|
+
};
|
|
5991
|
+
|
|
5992
|
+
//#endregion
|
|
5993
|
+
//#region src/slices/support_ticket/services/support_ticket_notification_service.ts
|
|
5994
|
+
const STAFF_USER_TYPES = ["staff", "super_admin"];
|
|
5995
|
+
/** Escape HTML for safe inclusion in email body. */
|
|
5996
|
+
function escapeHtmlForEmail(s) {
|
|
5997
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/\n/g, "<br>");
|
|
5998
|
+
}
|
|
5999
|
+
let SupportTicketNotificationService = class SupportTicketNotificationService$1 {
|
|
6000
|
+
constructor(subscriberRepo, userRepo, userProfileRepo, emailService, env, logger$1) {
|
|
6001
|
+
this.subscriberRepo = subscriberRepo;
|
|
6002
|
+
this.userRepo = userRepo;
|
|
6003
|
+
this.userProfileRepo = userProfileRepo;
|
|
6004
|
+
this.emailService = emailService;
|
|
6005
|
+
this.env = env;
|
|
6006
|
+
this.logger = logger$1;
|
|
6007
|
+
}
|
|
6008
|
+
async notifyAssignee(ticket, eventType, context) {
|
|
6009
|
+
try {
|
|
6010
|
+
const assigneeId = ticket.assigned_to;
|
|
6011
|
+
if (!assigneeId) return;
|
|
6012
|
+
if (context?.actorUserId && assigneeId === context.actorUserId) return;
|
|
6013
|
+
const [email] = await this.getNotificationEmailsForFollowers([assigneeId], context?.actorUserId);
|
|
6014
|
+
if (!email) return;
|
|
6015
|
+
const ticketUrl = `${this.env.WEBSITE_URL}/staff/support/${ticket.id}`;
|
|
6016
|
+
const { subject, bodyText, bodyHtml } = this.buildAssigneeEmailContent(ticket, eventType, ticketUrl, context);
|
|
6017
|
+
await this.emailService.sendBulkEmail([email], subject, bodyText, bodyHtml);
|
|
6018
|
+
this.logger.info(`Sent support ticket ${eventType} notification to assignee`);
|
|
6019
|
+
} catch (error) {
|
|
6020
|
+
this.logger.error("Failed to send support ticket assignee notification", {
|
|
6021
|
+
error,
|
|
6022
|
+
supportTicketId: ticket.id,
|
|
6023
|
+
eventType
|
|
6024
|
+
});
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
6027
|
+
async notifyFollowers(supportTicketId, ticket, eventType, context) {
|
|
6028
|
+
try {
|
|
6029
|
+
await this.ensureAssigneeIsSubscriber(ticket);
|
|
6030
|
+
const followers = await this.subscriberRepo.readByRecordId(RecordConst$1.SUPPORT_TICKET, supportTicketId);
|
|
6031
|
+
if (followers.length === 0) return;
|
|
6032
|
+
const filteredFollowers = eventType === "NEW_INTERNAL_NOTE" ? await this.filterStaffFollowersOnly(followers) : followers;
|
|
6033
|
+
if (filteredFollowers.length === 0) return;
|
|
6034
|
+
const subscribed = filteredFollowers.filter((f) => {
|
|
6035
|
+
const events = f.subscribed_events;
|
|
6036
|
+
if (!events || events.length === 0) return true;
|
|
6037
|
+
return events.includes(eventType);
|
|
6038
|
+
});
|
|
6039
|
+
if (subscribed.length === 0) return;
|
|
6040
|
+
const assigneeExcluded = eventType === "TICKET_CREATED" || eventType === "TICKET_ASSIGNED" ? subscribed.filter((f) => f.user_id !== ticket.assigned_to) : subscribed;
|
|
6041
|
+
const recipients = await this.getNotificationRecipientsForFollowers(assigneeExcluded.map((f) => f.user_id), context?.actorUserId);
|
|
6042
|
+
if (recipients.length === 0) return;
|
|
6043
|
+
const baseUrl = this.env.WEBSITE_URL;
|
|
6044
|
+
for (const { email, userType } of recipients) {
|
|
6045
|
+
const ticketUrl = STAFF_USER_TYPES.includes(userType) ? `${baseUrl}/staff/support/${ticket.id}` : `${baseUrl}/support/${ticket.id}`;
|
|
6046
|
+
const { subject, bodyText, bodyHtml } = this.buildEmailContent(ticket, eventType, ticketUrl, context);
|
|
6047
|
+
await this.emailService.sendBulkEmail([email], subject, bodyText, bodyHtml);
|
|
6048
|
+
}
|
|
6049
|
+
this.logger.info(`Sent support ticket ${eventType} notification to ${recipients.length} followers`);
|
|
6050
|
+
} catch (error) {
|
|
6051
|
+
this.logger.error("Failed to send support ticket follower notifications", {
|
|
6052
|
+
error,
|
|
6053
|
+
supportTicketId,
|
|
6054
|
+
eventType
|
|
6055
|
+
});
|
|
6056
|
+
}
|
|
6057
|
+
}
|
|
6058
|
+
/**
|
|
6059
|
+
* Ensures the assigned staff member is a subscriber so they receive the same
|
|
6060
|
+
* notifications as other followers (notes, approvals, etc.). Idempotent.
|
|
6061
|
+
*/
|
|
6062
|
+
async ensureAssigneeIsSubscriber(ticket) {
|
|
6063
|
+
const assigneeId = ticket.assigned_to;
|
|
6064
|
+
if (!assigneeId) return;
|
|
6065
|
+
try {
|
|
6066
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6067
|
+
await this.subscriberRepo.ensureSubscriber({
|
|
6068
|
+
record_type: RecordConst$1.SUPPORT_TICKET,
|
|
6069
|
+
record_id: ticket.id,
|
|
6070
|
+
user_id: assigneeId,
|
|
6071
|
+
subscribed_events: null,
|
|
6072
|
+
created_at: now,
|
|
6073
|
+
created_by: assigneeId
|
|
6074
|
+
});
|
|
6075
|
+
} catch (error) {
|
|
6076
|
+
this.logger.error("Failed to ensure assignee is subscriber", {
|
|
6077
|
+
error,
|
|
6078
|
+
supportTicketId: ticket.id,
|
|
6079
|
+
assigneeId
|
|
6080
|
+
});
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
async filterStaffFollowersOnly(followers) {
|
|
6084
|
+
if (followers.length === 0) return [];
|
|
6085
|
+
const userIds = followers.map((f) => f.user_id);
|
|
6086
|
+
const users = await this.userRepo.read_users_by_ids(userIds);
|
|
6087
|
+
const staffUserIds = new Set(users.filter((u) => STAFF_USER_TYPES.includes(u.user_type)).map((u) => u.id));
|
|
6088
|
+
return followers.filter((f) => staffUserIds.has(f.user_id));
|
|
6089
|
+
}
|
|
6090
|
+
async getNotificationEmailsForFollowers(userIds, excludeUserId) {
|
|
6091
|
+
return (await this.getNotificationRecipientsForFollowers(userIds, excludeUserId)).map((r) => r.email);
|
|
6092
|
+
}
|
|
6093
|
+
async getNotificationRecipientsForFollowers(userIds, excludeUserId) {
|
|
6094
|
+
const filteredIds = excludeUserId ? userIds.filter((id) => id !== excludeUserId) : userIds;
|
|
6095
|
+
if (filteredIds.length === 0) return [];
|
|
6096
|
+
const [users, profiles] = await Promise.all([this.userRepo.read_users_by_ids(filteredIds), this.userProfileRepo.read_user_profiles_by_user_ids(filteredIds)]);
|
|
6097
|
+
const profileByUserId = new Map(profiles.map((p) => [p.user_id, p]));
|
|
6098
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6099
|
+
const recipients = [];
|
|
6100
|
+
for (const user of users) {
|
|
6101
|
+
const email = profileByUserId.get(user.id)?.notification_email ?? user.email;
|
|
6102
|
+
if (!email || seen.has(email)) continue;
|
|
6103
|
+
seen.add(email);
|
|
6104
|
+
recipients.push({
|
|
6105
|
+
email,
|
|
6106
|
+
userType: user.user_type ?? DEFAULT_USER_TYPE
|
|
6107
|
+
});
|
|
6108
|
+
}
|
|
6109
|
+
return recipients;
|
|
6110
|
+
}
|
|
6111
|
+
buildEmailContent(ticket, eventType, ticketUrl, context) {
|
|
6112
|
+
const eventLabel = eventType.replace(/_/g, " ").toLowerCase();
|
|
6113
|
+
const subject = `Support Ticket #${ticket.display_id || ticket.id}: ${eventLabel}`;
|
|
6114
|
+
const details = [
|
|
6115
|
+
`Ticket #: ${ticket.display_id || ticket.id}`,
|
|
6116
|
+
`Title: ${ticket.title}`,
|
|
6117
|
+
`Priority: ${supportTicketNumberToPriority(ticket.priority)}`
|
|
6118
|
+
];
|
|
6119
|
+
if (context?.noteBody) details.push(`\nNote: ${context.noteBody}`);
|
|
6120
|
+
if (eventType === "TICKET_CREATED" && ticket.description?.trim()) details.push(`\nDescription:\n${ticket.description}`);
|
|
6121
|
+
return {
|
|
6122
|
+
subject,
|
|
6123
|
+
bodyText: `A support ticket you're following has been updated.\n\n${details.join("\n")}\n\nView ticket: ${ticketUrl}`,
|
|
6124
|
+
bodyHtml: `
|
|
6125
|
+
<h2>Support Ticket Update</h2>
|
|
6126
|
+
<p>A support ticket you're following has been updated (${eventLabel}).</p>
|
|
6127
|
+
<ul>
|
|
6128
|
+
<li><strong>Ticket #:</strong> ${ticket.display_id || ticket.id}</li>
|
|
6129
|
+
<li><strong>Title:</strong> ${ticket.title}</li>
|
|
6130
|
+
<li><strong>Priority:</strong> ${supportTicketNumberToPriority(ticket.priority)}</li>
|
|
6131
|
+
</ul>
|
|
6132
|
+
${eventType === "TICKET_CREATED" && ticket.description?.trim() ? `<p><strong>Description:</strong></p><p>${escapeHtmlForEmail(ticket.description)}</p>` : ""}
|
|
6133
|
+
${context?.noteBody ? `<p><strong>Note:</strong></p><p>${context.noteBody}</p>` : ""}
|
|
6134
|
+
<p><a href="${ticketUrl}">View Ticket</a></p>
|
|
6135
|
+
`.trim()
|
|
6136
|
+
};
|
|
6137
|
+
}
|
|
6138
|
+
buildAssigneeEmailContent(ticket, eventType, ticketUrl, context) {
|
|
6139
|
+
const eventLabel = eventType.replace(/_/g, " ").toLowerCase();
|
|
6140
|
+
const subject = `Support Ticket #${ticket.display_id || ticket.id}: ${eventLabel}`;
|
|
6141
|
+
const isAssigned = eventType === "TICKET_ASSIGNED";
|
|
6142
|
+
const intro = isAssigned ? "You have been assigned to a support ticket." : "A new support ticket has been assigned to you.";
|
|
6143
|
+
const details = [
|
|
6144
|
+
`Ticket #: ${ticket.display_id || ticket.id}`,
|
|
6145
|
+
`Title: ${ticket.title}`,
|
|
6146
|
+
`Priority: ${supportTicketNumberToPriority(ticket.priority)}`
|
|
6147
|
+
];
|
|
6148
|
+
if (context?.noteBody) details.push(`\nNote: ${context.noteBody}`);
|
|
6149
|
+
if (!isAssigned && ticket.description?.trim()) details.push(`\nDescription:\n${ticket.description}`);
|
|
6150
|
+
return {
|
|
6151
|
+
subject,
|
|
6152
|
+
bodyText: `${intro}\n\n${details.join("\n")}\n\nView ticket: ${ticketUrl}`,
|
|
6153
|
+
bodyHtml: `
|
|
6154
|
+
<h2>Support Ticket ${isAssigned ? "Assigned" : "Created"}</h2>
|
|
6155
|
+
<p>${intro}</p>
|
|
6156
|
+
<ul>
|
|
6157
|
+
<li><strong>Ticket #:</strong> ${ticket.display_id || ticket.id}</li>
|
|
6158
|
+
<li><strong>Title:</strong> ${ticket.title}</li>
|
|
6159
|
+
<li><strong>Priority:</strong> ${supportTicketNumberToPriority(ticket.priority)}</li>
|
|
6160
|
+
</ul>
|
|
6161
|
+
${!isAssigned && ticket.description?.trim() ? `<p><strong>Description:</strong></p><p>${escapeHtmlForEmail(ticket.description)}</p>` : ""}
|
|
6162
|
+
${context?.noteBody ? `<p><strong>Note:</strong></p><p>${context.noteBody}</p>` : ""}
|
|
6163
|
+
<p><a href="${ticketUrl}">View Ticket</a></p>
|
|
6164
|
+
`.trim()
|
|
6165
|
+
};
|
|
6166
|
+
}
|
|
6167
|
+
};
|
|
6168
|
+
SupportTicketNotificationService = __decorate([
|
|
6169
|
+
injectable(),
|
|
6170
|
+
__decorateParam(0, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6171
|
+
__decorateParam(1, inject(USER_TOKENS.IUsersRepo)),
|
|
6172
|
+
__decorateParam(2, inject(USER_PROFILE_TOKENS.IUserProfilesRepo)),
|
|
6173
|
+
__decorateParam(3, inject(TOKENS.IEmailService)),
|
|
6174
|
+
__decorateParam(4, inject(TOKENS.ENV)),
|
|
6175
|
+
__decorateParam(5, inject(TOKENS.LOGGER))
|
|
6176
|
+
], SupportTicketNotificationService);
|
|
6177
|
+
|
|
6178
|
+
//#endregion
|
|
6179
|
+
//#region src/slices/support_ticket/services/support_ticket_triage_service.ts
|
|
6180
|
+
let SupportTicketTriageService = class SupportTicketTriageService$1 {
|
|
6181
|
+
constructor(supportStaffRepo, appSettingsRepo, logger$1) {
|
|
6182
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
6183
|
+
this.appSettingsRepo = appSettingsRepo;
|
|
6184
|
+
this.logger = logger$1;
|
|
6185
|
+
}
|
|
6186
|
+
async getNextAssignee(updatedBy) {
|
|
6187
|
+
try {
|
|
6188
|
+
const triageUsers = await this.supportStaffRepo.readTriageUsers();
|
|
6189
|
+
if (triageUsers.length === 0) {
|
|
6190
|
+
this.logger.debug("No triage users found; skipping assignee");
|
|
6191
|
+
return null;
|
|
6192
|
+
}
|
|
6193
|
+
const ids = triageUsers.map((u) => u.id);
|
|
6194
|
+
const lastId = (await this.appSettingsRepo.readSetting(AppSettingKey.LAST_SUPPORT_TICKET_ASSIGNEE))?.value;
|
|
6195
|
+
const lastIndex = lastId ? ids.indexOf(lastId) : -1;
|
|
6196
|
+
const nextUserId = ids[lastIndex < ids.length - 1 ? lastIndex + 1 : 0];
|
|
6197
|
+
await this.appSettingsRepo.updateSetting(AppSettingKey.LAST_SUPPORT_TICKET_ASSIGNEE, nextUserId, updatedBy);
|
|
6198
|
+
return nextUserId;
|
|
6199
|
+
} catch (error) {
|
|
6200
|
+
this.logger.error("Failed to get next triage assignee", { error });
|
|
6201
|
+
return null;
|
|
6202
|
+
}
|
|
6203
|
+
}
|
|
6204
|
+
};
|
|
6205
|
+
SupportTicketTriageService = __decorate([
|
|
6206
|
+
injectable(),
|
|
6207
|
+
__decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo)),
|
|
6208
|
+
__decorateParam(1, inject(TOKENS.IAppSettingsRepo)),
|
|
6209
|
+
__decorateParam(2, inject(TOKENS.LOGGER))
|
|
6210
|
+
], SupportTicketTriageService);
|
|
5173
6211
|
|
|
5174
6212
|
//#endregion
|
|
5175
6213
|
//#region src/slices/support_ticket/mappers/support_ticket_mapper.ts
|
|
@@ -5188,6 +6226,7 @@ function computeStatus(approvalStatus, devLifecycle) {
|
|
|
5188
6226
|
case "TESTING":
|
|
5189
6227
|
case "STAGING":
|
|
5190
6228
|
case "PO_APPROVAL": return "IN_PROGRESS";
|
|
6229
|
+
case "VERIFICATION": return "VERIFICATION";
|
|
5191
6230
|
case "DEPLOYED": return "COMPLETED";
|
|
5192
6231
|
default: return "FOLLOWUP";
|
|
5193
6232
|
}
|
|
@@ -5221,11 +6260,10 @@ function toCustomerSupportTicketReadDto(entity) {
|
|
|
5221
6260
|
title: entity.title,
|
|
5222
6261
|
description: entity.description,
|
|
5223
6262
|
type: entity.type,
|
|
5224
|
-
priority: entity.priority,
|
|
6263
|
+
priority: supportTicketNumberToPriority(entity.priority),
|
|
5225
6264
|
status: computeStatus(entity.approval_status, entity.dev_lifecycle),
|
|
5226
6265
|
is_locked: !!entity.approval_status && entity.approval_status !== "PENDING",
|
|
5227
|
-
|
|
5228
|
-
requester_email: entity.requester_email,
|
|
6266
|
+
created_by_display_name: null,
|
|
5229
6267
|
credit_value: entity.credit_value,
|
|
5230
6268
|
start_at: startAt,
|
|
5231
6269
|
target_at: targetAt,
|
|
@@ -5234,7 +6272,8 @@ function toCustomerSupportTicketReadDto(entity) {
|
|
|
5234
6272
|
created_at: createdAt,
|
|
5235
6273
|
created_by: entity.created_by,
|
|
5236
6274
|
updated_at: updatedAt,
|
|
5237
|
-
updated_by: entity.updated_by
|
|
6275
|
+
updated_by: entity.updated_by,
|
|
6276
|
+
archived_at: entity.archived_at ?? null
|
|
5238
6277
|
};
|
|
5239
6278
|
}
|
|
5240
6279
|
/**
|
|
@@ -5257,13 +6296,13 @@ function toStaffSupportTicketReadDto(entity) {
|
|
|
5257
6296
|
title: entity.title,
|
|
5258
6297
|
description: entity.description,
|
|
5259
6298
|
type: entity.type,
|
|
5260
|
-
priority: entity.priority,
|
|
6299
|
+
priority: supportTicketNumberToPriority(entity.priority),
|
|
5261
6300
|
status: computeStatus(entity.approval_status, entity.dev_lifecycle),
|
|
5262
6301
|
approval_status: entity.approval_status,
|
|
5263
6302
|
is_locked: !!entity.approval_status && entity.approval_status !== "PENDING",
|
|
5264
6303
|
can_delete: canDelete,
|
|
5265
|
-
|
|
5266
|
-
|
|
6304
|
+
created_by_display_name: null,
|
|
6305
|
+
assigned_to: entity.assigned_to ?? null,
|
|
5267
6306
|
dev_lifecycle: entity.dev_lifecycle || "PENDING",
|
|
5268
6307
|
credit_value: entity.credit_value,
|
|
5269
6308
|
delivered_value: entity.delivered_value,
|
|
@@ -5274,7 +6313,9 @@ function toStaffSupportTicketReadDto(entity) {
|
|
|
5274
6313
|
created_at: createdAt,
|
|
5275
6314
|
created_by: entity.created_by,
|
|
5276
6315
|
updated_at: updatedAt,
|
|
5277
|
-
updated_by: entity.updated_by
|
|
6316
|
+
updated_by: entity.updated_by,
|
|
6317
|
+
archived_at: entity.archived_at ?? null,
|
|
6318
|
+
archived_by: entity.archived_by ?? null
|
|
5278
6319
|
};
|
|
5279
6320
|
}
|
|
5280
6321
|
function dateIsValid(date) {
|
|
@@ -5286,13 +6327,14 @@ function dateIsValid(date) {
|
|
|
5286
6327
|
//#endregion
|
|
5287
6328
|
//#region src/slices/support_ticket/features/customer/create_support_ticket_feat.ts
|
|
5288
6329
|
let CreateSupportTicketFeat = class CreateSupportTicketFeat$1 {
|
|
5289
|
-
constructor(session, support_ticketRepo,
|
|
6330
|
+
constructor(session, support_ticketRepo, subscriberRepo, notificationService, triageService, create_record_version, appSettingsRepo, logger$1) {
|
|
5290
6331
|
this.session = session;
|
|
5291
6332
|
this.support_ticketRepo = support_ticketRepo;
|
|
6333
|
+
this.subscriberRepo = subscriberRepo;
|
|
6334
|
+
this.notificationService = notificationService;
|
|
6335
|
+
this.triageService = triageService;
|
|
5292
6336
|
this.create_record_version = create_record_version;
|
|
5293
6337
|
this.appSettingsRepo = appSettingsRepo;
|
|
5294
|
-
this.emailService = emailService;
|
|
5295
|
-
this.env = env;
|
|
5296
6338
|
this.logger = logger$1;
|
|
5297
6339
|
}
|
|
5298
6340
|
/**
|
|
@@ -5303,9 +6345,8 @@ let CreateSupportTicketFeat = class CreateSupportTicketFeat$1 {
|
|
|
5303
6345
|
const userId = this.session.user.userId;
|
|
5304
6346
|
const frEntity = {
|
|
5305
6347
|
...input,
|
|
6348
|
+
priority: input.priority ?? SUPPORT_TICKET_PRIORITY_TO_NUMBER.MEDIUM,
|
|
5306
6349
|
description: input.description ?? "",
|
|
5307
|
-
requester_email: this.session.user.email,
|
|
5308
|
-
requester_name: this.session.user.username,
|
|
5309
6350
|
created_at: now,
|
|
5310
6351
|
created_by: userId,
|
|
5311
6352
|
updated_at: now,
|
|
@@ -5313,7 +6354,11 @@ let CreateSupportTicketFeat = class CreateSupportTicketFeat$1 {
|
|
|
5313
6354
|
};
|
|
5314
6355
|
const support_ticketCreated = await this.support_ticketRepo.create(frEntity);
|
|
5315
6356
|
support_ticketCreated.display_id = await this.appSettingsRepo.getNextSequentialId(RecordConst.SUPPORT_TICKET, this.session.user.userId);
|
|
5316
|
-
await this.
|
|
6357
|
+
const assigneeId = await this.triageService.getNextAssignee(this.session.user);
|
|
6358
|
+
if (assigneeId) {
|
|
6359
|
+
support_ticketCreated.assigned_to = assigneeId;
|
|
6360
|
+
await this.support_ticketRepo.update(support_ticketCreated);
|
|
6361
|
+
} else await this.support_ticketRepo.update(support_ticketCreated);
|
|
5317
6362
|
await this.create_record_version.execute({
|
|
5318
6363
|
record_id: support_ticketCreated.id,
|
|
5319
6364
|
operation: OperationConst.INSERT,
|
|
@@ -5324,58 +6369,30 @@ let CreateSupportTicketFeat = class CreateSupportTicketFeat$1 {
|
|
|
5324
6369
|
auth_role: this.session.user.user_type,
|
|
5325
6370
|
auth_username: this.session.user.username
|
|
5326
6371
|
});
|
|
5327
|
-
await this.
|
|
5328
|
-
|
|
6372
|
+
await this.addRequesterAsFollower(support_ticketCreated);
|
|
6373
|
+
await this.notificationService.notifyAssignee(support_ticketCreated, "TICKET_CREATED", { actorUserId: userId });
|
|
6374
|
+
await this.notificationService.notifyFollowers(support_ticketCreated.id, support_ticketCreated, "TICKET_CREATED", { actorUserId: userId });
|
|
6375
|
+
return {
|
|
6376
|
+
...toCustomerSupportTicketReadDto(support_ticketCreated),
|
|
6377
|
+
created_by_display_name: this.session.user.email
|
|
6378
|
+
};
|
|
5329
6379
|
}
|
|
5330
6380
|
/**
|
|
5331
|
-
*
|
|
6381
|
+
* Auto-add requester (created_by) as follower
|
|
5332
6382
|
*/
|
|
5333
|
-
async
|
|
6383
|
+
async addRequesterAsFollower(ticket) {
|
|
5334
6384
|
try {
|
|
5335
|
-
const
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
}
|
|
5345
|
-
const ticketUrl = `${this.env.WEBSITE_URL}/support-tickets/${ticket.id}`;
|
|
5346
|
-
const subject = `New Support Ticket Created: ${ticket.title}`;
|
|
5347
|
-
const bodyText = `
|
|
5348
|
-
A new support ticket has been created:
|
|
5349
|
-
|
|
5350
|
-
Ticket #${ticket.display_id || ticket.id}
|
|
5351
|
-
Title: ${ticket.title}
|
|
5352
|
-
Type: ${ticket.type}
|
|
5353
|
-
Priority: ${ticket.priority}
|
|
5354
|
-
Requester: ${ticket.requester_name} (${ticket.requester_email})
|
|
5355
|
-
|
|
5356
|
-
Description:
|
|
5357
|
-
${ticket.description}
|
|
5358
|
-
|
|
5359
|
-
View ticket: ${ticketUrl}
|
|
5360
|
-
`.trim();
|
|
5361
|
-
const bodyHtml = `
|
|
5362
|
-
<h2>New Support Ticket Created</h2>
|
|
5363
|
-
<p>A new support ticket has been created:</p>
|
|
5364
|
-
<ul>
|
|
5365
|
-
<li><strong>Ticket #:</strong> ${ticket.display_id || ticket.id}</li>
|
|
5366
|
-
<li><strong>Title:</strong> ${ticket.title}</li>
|
|
5367
|
-
<li><strong>Type:</strong> ${ticket.type}</li>
|
|
5368
|
-
<li><strong>Priority:</strong> ${ticket.priority}</li>
|
|
5369
|
-
<li><strong>Requester:</strong> ${ticket.requester_name} (${ticket.requester_email})</li>
|
|
5370
|
-
</ul>
|
|
5371
|
-
<p><strong>Description:</strong></p>
|
|
5372
|
-
<p>${ticket.description}</p>
|
|
5373
|
-
<p><a href="${ticketUrl}">View Ticket</a></p>
|
|
5374
|
-
`.trim();
|
|
5375
|
-
await this.emailService.sendBulkEmail(emails, subject, bodyText, bodyHtml);
|
|
5376
|
-
this.logger.info(`Sent support ticket notification to ${emails.length} recipients`);
|
|
6385
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6386
|
+
await this.subscriberRepo.ensureSubscriber({
|
|
6387
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6388
|
+
record_id: ticket.id,
|
|
6389
|
+
user_id: ticket.created_by,
|
|
6390
|
+
subscribed_events: null,
|
|
6391
|
+
created_at: now,
|
|
6392
|
+
created_by: ticket.created_by
|
|
6393
|
+
});
|
|
5377
6394
|
} catch (error) {
|
|
5378
|
-
this.logger.error("Failed to
|
|
6395
|
+
this.logger.error("Failed to add requester as follower", { error });
|
|
5379
6396
|
}
|
|
5380
6397
|
}
|
|
5381
6398
|
};
|
|
@@ -5383,19 +6400,87 @@ CreateSupportTicketFeat = __decorate([
|
|
|
5383
6400
|
injectable(),
|
|
5384
6401
|
__decorateParam(0, injectSession()),
|
|
5385
6402
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5386
|
-
__decorateParam(2, inject(
|
|
5387
|
-
__decorateParam(3, inject(
|
|
5388
|
-
__decorateParam(4, inject(
|
|
5389
|
-
__decorateParam(5, inject(
|
|
5390
|
-
__decorateParam(6, inject(TOKENS.
|
|
6403
|
+
__decorateParam(2, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6404
|
+
__decorateParam(3, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
6405
|
+
__decorateParam(4, inject(SUPPORT_TICKET_TOKENS.ISupportTicketTriageService)),
|
|
6406
|
+
__decorateParam(5, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6407
|
+
__decorateParam(6, inject(TOKENS.IAppSettingsRepo)),
|
|
6408
|
+
__decorateParam(7, inject(TOKENS.LOGGER))
|
|
5391
6409
|
], CreateSupportTicketFeat);
|
|
5392
6410
|
|
|
6411
|
+
//#endregion
|
|
6412
|
+
//#region src/slices/support_ticket/features/customer/customer_toggle_subscription_feat.ts
|
|
6413
|
+
let CustomerToggleSubscriptionFeat = class CustomerToggleSubscriptionFeat$1 {
|
|
6414
|
+
constructor(session, subscriberRepo, supportTicketRepo) {
|
|
6415
|
+
this.session = session;
|
|
6416
|
+
this.subscriberRepo = subscriberRepo;
|
|
6417
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
6418
|
+
}
|
|
6419
|
+
async execute(supportTicketId) {
|
|
6420
|
+
const ticket = await this.supportTicketRepo.read(supportTicketId);
|
|
6421
|
+
if (!ticket) throw new BusinessError("Support ticket not found");
|
|
6422
|
+
if (ticket.approval_status === "INTERNAL") throw new BusinessError("Support ticket not found");
|
|
6423
|
+
const subscription = await this.subscriberRepo.readByRecordAndUser(RecordConst.SUPPORT_TICKET, supportTicketId, this.session.user.userId);
|
|
6424
|
+
if (subscription) {
|
|
6425
|
+
if (!await this.subscriberRepo.delete(subscription.id)) throw new BusinessError("Failed to unsubscribe");
|
|
6426
|
+
return { subscribed: false };
|
|
6427
|
+
}
|
|
6428
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6429
|
+
const userId = this.session.user.userId;
|
|
6430
|
+
await this.subscriberRepo.ensureSubscriber({
|
|
6431
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6432
|
+
record_id: supportTicketId,
|
|
6433
|
+
user_id: userId,
|
|
6434
|
+
subscribed_events: null,
|
|
6435
|
+
created_at: now,
|
|
6436
|
+
created_by: userId
|
|
6437
|
+
});
|
|
6438
|
+
return { subscribed: true };
|
|
6439
|
+
}
|
|
6440
|
+
};
|
|
6441
|
+
CustomerToggleSubscriptionFeat = __decorate([
|
|
6442
|
+
injectable(),
|
|
6443
|
+
__decorateParam(0, injectSession()),
|
|
6444
|
+
__decorateParam(1, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6445
|
+
__decorateParam(2, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo))
|
|
6446
|
+
], CustomerToggleSubscriptionFeat);
|
|
6447
|
+
|
|
6448
|
+
//#endregion
|
|
6449
|
+
//#region src/slices/support_ticket/features/customer/enrich_customer_ticket.ts
|
|
6450
|
+
/**
|
|
6451
|
+
* Enriches a CustomerSupportTicketReadDto with requester info from created_by lookup.
|
|
6452
|
+
* Requester name/email are derived from created_by (user display = email).
|
|
6453
|
+
*/
|
|
6454
|
+
function enrichCustomerTicket(dto, displayMap) {
|
|
6455
|
+
const createdByDisplay = dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null;
|
|
6456
|
+
return {
|
|
6457
|
+
...dto,
|
|
6458
|
+
created_by_display_name: createdByDisplay ?? dto.created_by ?? "—"
|
|
6459
|
+
};
|
|
6460
|
+
}
|
|
6461
|
+
function collectUserIdsFromCustomerTicket(ticket) {
|
|
6462
|
+
return [ticket.created_by].filter((id) => Boolean(id));
|
|
6463
|
+
}
|
|
6464
|
+
|
|
5393
6465
|
//#endregion
|
|
5394
6466
|
//#region src/slices/support_ticket/features/customer/get_support_ticket_customer_feat.ts
|
|
6467
|
+
function toRecordSubscriberReadDto$2(e) {
|
|
6468
|
+
return {
|
|
6469
|
+
id: e.id,
|
|
6470
|
+
record_type: e.record_type,
|
|
6471
|
+
record_id: e.record_id,
|
|
6472
|
+
user_id: e.user_id,
|
|
6473
|
+
subscribed_events: e.subscribed_events ?? null,
|
|
6474
|
+
created_at: e.created_at,
|
|
6475
|
+
created_by: e.created_by
|
|
6476
|
+
};
|
|
6477
|
+
}
|
|
5395
6478
|
let GetSupportTicketCustomerFeature = class GetSupportTicketCustomerFeature$1 {
|
|
5396
|
-
constructor(session, support_ticketRepo, appSettingsRepo, logger$1) {
|
|
6479
|
+
constructor(session, support_ticketRepo, subscriberRepo, userDisplayLookup, appSettingsRepo, logger$1) {
|
|
5397
6480
|
this.session = session;
|
|
5398
6481
|
this.support_ticketRepo = support_ticketRepo;
|
|
6482
|
+
this.subscriberRepo = subscriberRepo;
|
|
6483
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5399
6484
|
this.appSettingsRepo = appSettingsRepo;
|
|
5400
6485
|
this.logger = logger$1;
|
|
5401
6486
|
}
|
|
@@ -5413,22 +6498,31 @@ let GetSupportTicketCustomerFeature = class GetSupportTicketCustomerFeature$1 {
|
|
|
5413
6498
|
updated_by: this.session.user.userId
|
|
5414
6499
|
});
|
|
5415
6500
|
}
|
|
5416
|
-
|
|
6501
|
+
const base = toCustomerSupportTicketReadDto(support_ticket);
|
|
6502
|
+
const enriched = enrichCustomerTicket(base, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromCustomerTicket(base)));
|
|
6503
|
+
const subscription = await this.subscriberRepo.readByRecordAndUser(RecordConst.SUPPORT_TICKET, id, this.session.user.userId);
|
|
6504
|
+
return {
|
|
6505
|
+
...enriched,
|
|
6506
|
+
my_subscription: subscription ? toRecordSubscriberReadDto$2(subscription) : null
|
|
6507
|
+
};
|
|
5417
6508
|
}
|
|
5418
6509
|
};
|
|
5419
6510
|
GetSupportTicketCustomerFeature = __decorate([
|
|
5420
6511
|
injectable(),
|
|
5421
6512
|
__decorateParam(0, injectSession()),
|
|
5422
6513
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5423
|
-
__decorateParam(2, inject(
|
|
5424
|
-
__decorateParam(3, inject(
|
|
6514
|
+
__decorateParam(2, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6515
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
6516
|
+
__decorateParam(4, inject(TOKENS.IAppSettingsRepo)),
|
|
6517
|
+
__decorateParam(5, inject(TOKENS.LOGGER))
|
|
5425
6518
|
], GetSupportTicketCustomerFeature);
|
|
5426
6519
|
|
|
5427
6520
|
//#endregion
|
|
5428
6521
|
//#region src/slices/support_ticket/features/customer/get_support_tickets_customer_feat.ts
|
|
5429
6522
|
let GetSupportTicketsCustomerFeature = class GetSupportTicketsCustomerFeature$1 {
|
|
5430
|
-
constructor(support_ticketRepo) {
|
|
6523
|
+
constructor(support_ticketRepo, userDisplayLookup) {
|
|
5431
6524
|
this.support_ticketRepo = support_ticketRepo;
|
|
6525
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5432
6526
|
}
|
|
5433
6527
|
async execute(filters) {
|
|
5434
6528
|
const customerFilters = {
|
|
@@ -5439,20 +6533,28 @@ let GetSupportTicketsCustomerFeature = class GetSupportTicketsCustomerFeature$1
|
|
|
5439
6533
|
}
|
|
5440
6534
|
};
|
|
5441
6535
|
const result = await this.support_ticketRepo.read_all(customerFilters);
|
|
6536
|
+
const dtos = result.items.map(toCustomerSupportTicketReadDto);
|
|
6537
|
+
const userIds = [...new Set(dtos.flatMap(collectUserIdsFromCustomerTicket))];
|
|
6538
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
5442
6539
|
return {
|
|
5443
|
-
items:
|
|
6540
|
+
items: dtos.map((dto) => enrichCustomerTicket(dto, displayMap)),
|
|
5444
6541
|
pageInfo: result.pageInfo
|
|
5445
6542
|
};
|
|
5446
6543
|
}
|
|
5447
6544
|
};
|
|
5448
|
-
GetSupportTicketsCustomerFeature = __decorate([
|
|
6545
|
+
GetSupportTicketsCustomerFeature = __decorate([
|
|
6546
|
+
injectable(),
|
|
6547
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6548
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6549
|
+
], GetSupportTicketsCustomerFeature);
|
|
5449
6550
|
|
|
5450
6551
|
//#endregion
|
|
5451
6552
|
//#region src/slices/support_ticket/features/customer/update_support_ticket_customer_feat.ts
|
|
5452
6553
|
let UpdateSupportTicketCustomerFeat = class UpdateSupportTicketCustomerFeat$1 {
|
|
5453
|
-
constructor(session, support_ticketRepo, create_record_version) {
|
|
6554
|
+
constructor(session, support_ticketRepo, userDisplayLookup, create_record_version) {
|
|
5454
6555
|
this.session = session;
|
|
5455
6556
|
this.support_ticketRepo = support_ticketRepo;
|
|
6557
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5456
6558
|
this.create_record_version = create_record_version;
|
|
5457
6559
|
}
|
|
5458
6560
|
/**
|
|
@@ -5460,8 +6562,9 @@ let UpdateSupportTicketCustomerFeat = class UpdateSupportTicketCustomerFeat$1 {
|
|
|
5460
6562
|
*/
|
|
5461
6563
|
async execute(input) {
|
|
5462
6564
|
const support_ticket = await this.support_ticketRepo.read(input.id);
|
|
5463
|
-
if (!support_ticket) throw new
|
|
5464
|
-
if (support_ticket.approval_status !== "PENDING") throw new
|
|
6565
|
+
if (!support_ticket) throw new BusinessError("SupportTicket not found");
|
|
6566
|
+
if (support_ticket.approval_status !== "PENDING") throw new BusinessError("SupportTicket is locked - cannot be edited");
|
|
6567
|
+
if (support_ticket.archived_at) throw new BusinessError("Cannot edit an archived support ticket.");
|
|
5465
6568
|
const frEntity = {
|
|
5466
6569
|
...support_ticket,
|
|
5467
6570
|
title: input.title,
|
|
@@ -5483,24 +6586,102 @@ let UpdateSupportTicketCustomerFeat = class UpdateSupportTicketCustomerFeat$1 {
|
|
|
5483
6586
|
auth_role: this.session.user.user_type,
|
|
5484
6587
|
auth_username: this.session.user.username
|
|
5485
6588
|
});
|
|
5486
|
-
|
|
6589
|
+
const base = toCustomerSupportTicketReadDto(await this.support_ticketRepo.update(frEntity));
|
|
6590
|
+
return enrichCustomerTicket(base, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromCustomerTicket(base)));
|
|
5487
6591
|
}
|
|
5488
6592
|
};
|
|
5489
6593
|
UpdateSupportTicketCustomerFeat = __decorate([
|
|
5490
6594
|
injectable(),
|
|
5491
6595
|
__decorateParam(0, injectSession()),
|
|
5492
6596
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5493
|
-
__decorateParam(2, inject(
|
|
6597
|
+
__decorateParam(2, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
6598
|
+
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
5494
6599
|
], UpdateSupportTicketCustomerFeat);
|
|
5495
6600
|
|
|
6601
|
+
//#endregion
|
|
6602
|
+
//#region src/slices/support_ticket/features/staff/add_support_ticket_subscriber_feat.ts
|
|
6603
|
+
function toRecordSubscriberReadDto$1(e) {
|
|
6604
|
+
return {
|
|
6605
|
+
id: e.id,
|
|
6606
|
+
record_type: e.record_type,
|
|
6607
|
+
record_id: e.record_id,
|
|
6608
|
+
user_id: e.user_id,
|
|
6609
|
+
subscribed_events: e.subscribed_events ?? null,
|
|
6610
|
+
created_at: e.created_at,
|
|
6611
|
+
created_by: e.created_by
|
|
6612
|
+
};
|
|
6613
|
+
}
|
|
6614
|
+
let AddSupportTicketSubscriberFeat = class AddSupportTicketSubscriberFeat$1 {
|
|
6615
|
+
constructor(subscriberRepo, supportTicketRepo, session, userDisplayLookup) {
|
|
6616
|
+
this.subscriberRepo = subscriberRepo;
|
|
6617
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
6618
|
+
this.session = session;
|
|
6619
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6620
|
+
}
|
|
6621
|
+
async execute(input) {
|
|
6622
|
+
if (!await this.supportTicketRepo.read(input.support_ticket_id)) throw new BusinessError("Support ticket not found");
|
|
6623
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6624
|
+
const dto = toRecordSubscriberReadDto$1(await this.subscriberRepo.ensureSubscriber({
|
|
6625
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6626
|
+
record_id: input.support_ticket_id,
|
|
6627
|
+
user_id: input.user_id,
|
|
6628
|
+
subscribed_events: input.subscribed_events ?? null,
|
|
6629
|
+
created_at: now,
|
|
6630
|
+
created_by: this.session.user.userId
|
|
6631
|
+
}));
|
|
6632
|
+
const userIds = [dto.user_id, dto.created_by].filter(Boolean);
|
|
6633
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
6634
|
+
return {
|
|
6635
|
+
...dto,
|
|
6636
|
+
user_id_display_name: dto.user_id ? displayMap.get(dto.user_id) ?? dto.user_id : null,
|
|
6637
|
+
created_by_display_name: dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null
|
|
6638
|
+
};
|
|
6639
|
+
}
|
|
6640
|
+
};
|
|
6641
|
+
AddSupportTicketSubscriberFeat = __decorate([
|
|
6642
|
+
injectable(),
|
|
6643
|
+
__decorateParam(0, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6644
|
+
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6645
|
+
__decorateParam(2, injectSession()),
|
|
6646
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6647
|
+
], AddSupportTicketSubscriberFeat);
|
|
6648
|
+
|
|
6649
|
+
//#endregion
|
|
6650
|
+
//#region src/slices/support_ticket/features/staff/enrich_staff_ticket.ts
|
|
6651
|
+
/**
|
|
6652
|
+
* Enriches a StaffSupportTicketReadDto with display names from a lookup map.
|
|
6653
|
+
* Used by all staff features that return a ticket.
|
|
6654
|
+
*/
|
|
6655
|
+
function enrichStaffTicket(dto, displayMap) {
|
|
6656
|
+
const createdByDisplay = dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null;
|
|
6657
|
+
return {
|
|
6658
|
+
...dto,
|
|
6659
|
+
assigned_to_display_name: dto.assigned_to ? displayMap.get(dto.assigned_to) ?? dto.assigned_to : null,
|
|
6660
|
+
created_by_display_name: createdByDisplay ?? dto.created_by ?? "—",
|
|
6661
|
+
updated_by_display_name: dto.updated_by ? displayMap.get(dto.updated_by) ?? dto.updated_by : null
|
|
6662
|
+
};
|
|
6663
|
+
}
|
|
6664
|
+
/**
|
|
6665
|
+
* Collects user IDs from a staff ticket for display name lookup.
|
|
6666
|
+
*/
|
|
6667
|
+
function collectUserIdsFromStaffTicket(ticket) {
|
|
6668
|
+
return [
|
|
6669
|
+
ticket.assigned_to,
|
|
6670
|
+
ticket.created_by,
|
|
6671
|
+
ticket.updated_by
|
|
6672
|
+
].filter((id) => Boolean(id));
|
|
6673
|
+
}
|
|
6674
|
+
|
|
5496
6675
|
//#endregion
|
|
5497
6676
|
//#region src/slices/support_ticket/features/staff/approve_support_ticket_feat.ts
|
|
5498
6677
|
let ApproveSupportTicketFeat = class ApproveSupportTicketFeat$1 {
|
|
5499
|
-
constructor(repo, creditService, create_record_version, session) {
|
|
6678
|
+
constructor(repo, notificationService, creditService, create_record_version, session, userDisplayLookup) {
|
|
5500
6679
|
this.repo = repo;
|
|
6680
|
+
this.notificationService = notificationService;
|
|
5501
6681
|
this.creditService = creditService;
|
|
5502
6682
|
this.create_record_version = create_record_version;
|
|
5503
6683
|
this.session = session;
|
|
6684
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5504
6685
|
}
|
|
5505
6686
|
/**
|
|
5506
6687
|
* Approves a support_ticket item, deducts credits, and locks it.
|
|
@@ -5516,15 +6697,18 @@ let ApproveSupportTicketFeat = class ApproveSupportTicketFeat$1 {
|
|
|
5516
6697
|
* @returns The updated support_ticket with full staff view.
|
|
5517
6698
|
*/
|
|
5518
6699
|
async execute(args) {
|
|
5519
|
-
const { id } = args;
|
|
6700
|
+
const { id, credit_value: creditValueFromArgs } = args;
|
|
5520
6701
|
const support_ticket = await this.repo.read(id);
|
|
5521
|
-
if (!support_ticket) throw new
|
|
5522
|
-
if (support_ticket.approval_status !== "PENDING") throw new
|
|
5523
|
-
|
|
5524
|
-
if (
|
|
5525
|
-
const
|
|
5526
|
-
|
|
5527
|
-
|
|
6702
|
+
if (!support_ticket) throw new BusinessError(`Support Ticket with ID ${id} not found`);
|
|
6703
|
+
if (support_ticket.approval_status !== "PENDING") throw new BusinessError("Support Ticket must be in PENDING status to be approved.");
|
|
6704
|
+
const creditAmount = parseFloat(creditValueFromArgs);
|
|
6705
|
+
if (isNaN(creditAmount) || creditAmount < 0) throw new BusinessError("Invalid credit value amount.");
|
|
6706
|
+
const ticketToUpdate = {
|
|
6707
|
+
...support_ticket,
|
|
6708
|
+
credit_value: creditValueFromArgs
|
|
6709
|
+
};
|
|
6710
|
+
if (support_ticket.credit_value?.trim() !== creditValueFromArgs) await this.repo.update(ticketToUpdate);
|
|
6711
|
+
if (!await this.creditService.deductCredits(creditValueFromArgs, support_ticket.id)) throw new BusinessError("Insufficient credits. Customer must purchase more credits or wait for monthly reset.");
|
|
5528
6712
|
const lockedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5529
6713
|
const changed_props = getChangedProperties(support_ticket, {
|
|
5530
6714
|
...support_ticket,
|
|
@@ -5549,25 +6733,101 @@ let ApproveSupportTicketFeat = class ApproveSupportTicketFeat$1 {
|
|
|
5549
6733
|
dev_lifecycle: "BACKLOG",
|
|
5550
6734
|
locked_approval_at: lockedAt
|
|
5551
6735
|
});
|
|
5552
|
-
if (!updatedSupportTicket) throw new
|
|
5553
|
-
|
|
6736
|
+
if (!updatedSupportTicket) throw new BusinessError("Failed to approve support_ticket.");
|
|
6737
|
+
await this.notificationService.notifyFollowers(id, updatedSupportTicket, "APPROVAL_STATUS_CHANGED", { actorUserId: this.session.user.userId });
|
|
6738
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
6739
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5554
6740
|
}
|
|
5555
6741
|
};
|
|
5556
6742
|
ApproveSupportTicketFeat = __decorate([
|
|
5557
6743
|
injectable(),
|
|
5558
6744
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5559
|
-
__decorateParam(1, inject(
|
|
5560
|
-
__decorateParam(2, inject(
|
|
5561
|
-
__decorateParam(3,
|
|
6745
|
+
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
6746
|
+
__decorateParam(2, inject(CREDIT_SERVICE_TOKEN)),
|
|
6747
|
+
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6748
|
+
__decorateParam(4, injectSession()),
|
|
6749
|
+
__decorateParam(5, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5562
6750
|
], ApproveSupportTicketFeat);
|
|
5563
6751
|
|
|
6752
|
+
//#endregion
|
|
6753
|
+
//#region src/slices/support_ticket/features/staff/archive_support_ticket_feat.ts
|
|
6754
|
+
let ArchiveSupportTicketFeat = class ArchiveSupportTicketFeat$1 {
|
|
6755
|
+
constructor(repo, create_record_version, session, userDisplayLookup) {
|
|
6756
|
+
this.repo = repo;
|
|
6757
|
+
this.create_record_version = create_record_version;
|
|
6758
|
+
this.session = session;
|
|
6759
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6760
|
+
}
|
|
6761
|
+
async execute(id) {
|
|
6762
|
+
const support_ticket = await this.repo.read(id);
|
|
6763
|
+
if (!support_ticket) throw new Error(`Support ticket with id ${id} not found`);
|
|
6764
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6765
|
+
const userId = this.session.user.userId;
|
|
6766
|
+
if (support_ticket.archived_at) {
|
|
6767
|
+
await this.create_record_version.execute({
|
|
6768
|
+
record_id: id,
|
|
6769
|
+
operation: OperationConst.UPDATE,
|
|
6770
|
+
recorded_at: now,
|
|
6771
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6772
|
+
record: {
|
|
6773
|
+
archived_at: null,
|
|
6774
|
+
archived_by: null
|
|
6775
|
+
},
|
|
6776
|
+
old_record: support_ticket,
|
|
6777
|
+
auth_uid: userId,
|
|
6778
|
+
auth_role: this.session.user.user_type,
|
|
6779
|
+
auth_username: this.session.user.username
|
|
6780
|
+
});
|
|
6781
|
+
const updated$1 = await this.repo.update({
|
|
6782
|
+
...support_ticket,
|
|
6783
|
+
archived_at: null,
|
|
6784
|
+
archived_by: null
|
|
6785
|
+
});
|
|
6786
|
+
if (!updated$1) throw new Error("Failed to unarchive support ticket.");
|
|
6787
|
+
const dto$1 = toStaffSupportTicketReadDto(updated$1);
|
|
6788
|
+
return enrichStaffTicket(dto$1, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto$1)));
|
|
6789
|
+
}
|
|
6790
|
+
if (!(support_ticket.dev_lifecycle === "DEPLOYED" || support_ticket.approval_status === "REJECTED")) throw new Error("Only completed or rejected tickets can be archived. Complete or reject the ticket first.");
|
|
6791
|
+
await this.create_record_version.execute({
|
|
6792
|
+
record_id: id,
|
|
6793
|
+
operation: OperationConst.UPDATE,
|
|
6794
|
+
recorded_at: now,
|
|
6795
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6796
|
+
record: {
|
|
6797
|
+
archived_at: now,
|
|
6798
|
+
archived_by: userId
|
|
6799
|
+
},
|
|
6800
|
+
old_record: support_ticket,
|
|
6801
|
+
auth_uid: userId,
|
|
6802
|
+
auth_role: this.session.user.user_type,
|
|
6803
|
+
auth_username: this.session.user.username
|
|
6804
|
+
});
|
|
6805
|
+
const updated = await this.repo.update({
|
|
6806
|
+
...support_ticket,
|
|
6807
|
+
archived_at: now,
|
|
6808
|
+
archived_by: userId
|
|
6809
|
+
});
|
|
6810
|
+
if (!updated) throw new Error("Failed to archive support ticket.");
|
|
6811
|
+
const dto = toStaffSupportTicketReadDto(updated);
|
|
6812
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6813
|
+
}
|
|
6814
|
+
};
|
|
6815
|
+
ArchiveSupportTicketFeat = __decorate([
|
|
6816
|
+
injectable(),
|
|
6817
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6818
|
+
__decorateParam(1, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6819
|
+
__decorateParam(2, injectSession()),
|
|
6820
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6821
|
+
], ArchiveSupportTicketFeat);
|
|
6822
|
+
|
|
5564
6823
|
//#endregion
|
|
5565
6824
|
//#region src/slices/support_ticket/features/staff/cancel_internal_task_feat.ts
|
|
5566
6825
|
let CancelInternalTaskFeat = class CancelInternalTaskFeat$1 {
|
|
5567
|
-
constructor(repo, create_record_version, session) {
|
|
6826
|
+
constructor(repo, create_record_version, session, userDisplayLookup) {
|
|
5568
6827
|
this.repo = repo;
|
|
5569
6828
|
this.create_record_version = create_record_version;
|
|
5570
6829
|
this.session = session;
|
|
6830
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5571
6831
|
}
|
|
5572
6832
|
/**
|
|
5573
6833
|
* Cancels an internal task by setting dev_lifecycle to CANCELLED.
|
|
@@ -5612,23 +6872,26 @@ let CancelInternalTaskFeat = class CancelInternalTaskFeat$1 {
|
|
|
5612
6872
|
completed_at: completedAt
|
|
5613
6873
|
});
|
|
5614
6874
|
if (!updatedSupportTicket) throw new Error("Failed to cancel internal task.");
|
|
5615
|
-
|
|
6875
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
6876
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5616
6877
|
}
|
|
5617
6878
|
};
|
|
5618
6879
|
CancelInternalTaskFeat = __decorate([
|
|
5619
6880
|
injectable(),
|
|
5620
6881
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5621
6882
|
__decorateParam(1, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
5622
|
-
__decorateParam(2, injectSession())
|
|
6883
|
+
__decorateParam(2, injectSession()),
|
|
6884
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5623
6885
|
], CancelInternalTaskFeat);
|
|
5624
6886
|
|
|
5625
6887
|
//#endregion
|
|
5626
6888
|
//#region src/slices/support_ticket/features/staff/complete_support_ticket_feat.ts
|
|
5627
6889
|
let CompleteSupportTicketFeat = class CompleteSupportTicketFeat$1 {
|
|
5628
|
-
constructor(repo, create_record_version, session) {
|
|
6890
|
+
constructor(repo, create_record_version, session, userDisplayLookup) {
|
|
5629
6891
|
this.repo = repo;
|
|
5630
6892
|
this.create_record_version = create_record_version;
|
|
5631
6893
|
this.session = session;
|
|
6894
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5632
6895
|
}
|
|
5633
6896
|
/**
|
|
5634
6897
|
* Completes a support_ticket item by setting it to DEPLOYED with delivered value.
|
|
@@ -5674,23 +6937,26 @@ let CompleteSupportTicketFeat = class CompleteSupportTicketFeat$1 {
|
|
|
5674
6937
|
completed_at: completedAt
|
|
5675
6938
|
});
|
|
5676
6939
|
if (!updatedSupportTicket) throw new Error("Failed to complete support_ticket.");
|
|
5677
|
-
|
|
6940
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
6941
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5678
6942
|
}
|
|
5679
6943
|
};
|
|
5680
6944
|
CompleteSupportTicketFeat = __decorate([
|
|
5681
6945
|
injectable(),
|
|
5682
6946
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5683
6947
|
__decorateParam(1, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
5684
|
-
__decorateParam(2, injectSession())
|
|
6948
|
+
__decorateParam(2, injectSession()),
|
|
6949
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5685
6950
|
], CompleteSupportTicketFeat);
|
|
5686
6951
|
|
|
5687
6952
|
//#endregion
|
|
5688
6953
|
//#region src/slices/support_ticket/features/staff/convert_to_customer_feat.ts
|
|
5689
6954
|
let ConvertToCustomerFeat = class ConvertToCustomerFeat$1 {
|
|
5690
|
-
constructor(session, support_ticketRepo, create_record_version) {
|
|
6955
|
+
constructor(session, support_ticketRepo, create_record_version, userDisplayLookup) {
|
|
5691
6956
|
this.session = session;
|
|
5692
6957
|
this.support_ticketRepo = support_ticketRepo;
|
|
5693
6958
|
this.create_record_version = create_record_version;
|
|
6959
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5694
6960
|
}
|
|
5695
6961
|
async execute(support_ticket_id) {
|
|
5696
6962
|
const existing = await this.support_ticketRepo.read(support_ticket_id);
|
|
@@ -5701,7 +6967,7 @@ let ConvertToCustomerFeat = class ConvertToCustomerFeat$1 {
|
|
|
5701
6967
|
approval_status: "PENDING",
|
|
5702
6968
|
dev_lifecycle: null,
|
|
5703
6969
|
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5704
|
-
updated_by: this.session.user.
|
|
6970
|
+
updated_by: this.session.user.userId
|
|
5705
6971
|
};
|
|
5706
6972
|
const updated = await this.support_ticketRepo.update(updateEntity);
|
|
5707
6973
|
await this.create_record_version.execute({
|
|
@@ -5715,23 +6981,26 @@ let ConvertToCustomerFeat = class ConvertToCustomerFeat$1 {
|
|
|
5715
6981
|
auth_role: this.session.user.user_type,
|
|
5716
6982
|
auth_username: this.session.user.username
|
|
5717
6983
|
});
|
|
5718
|
-
|
|
6984
|
+
const dto = toStaffSupportTicketReadDto(updated);
|
|
6985
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5719
6986
|
}
|
|
5720
6987
|
};
|
|
5721
6988
|
ConvertToCustomerFeat = __decorate([
|
|
5722
6989
|
injectable(),
|
|
5723
6990
|
__decorateParam(0, injectSession()),
|
|
5724
6991
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5725
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
6992
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6993
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5726
6994
|
], ConvertToCustomerFeat);
|
|
5727
6995
|
|
|
5728
6996
|
//#endregion
|
|
5729
6997
|
//#region src/slices/support_ticket/features/staff/convert_to_internal_feat.ts
|
|
5730
6998
|
let ConvertToInternalFeat = class ConvertToInternalFeat$1 {
|
|
5731
|
-
constructor(session, support_ticketRepo, create_record_version) {
|
|
6999
|
+
constructor(session, support_ticketRepo, create_record_version, userDisplayLookup) {
|
|
5732
7000
|
this.session = session;
|
|
5733
7001
|
this.support_ticketRepo = support_ticketRepo;
|
|
5734
7002
|
this.create_record_version = create_record_version;
|
|
7003
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5735
7004
|
}
|
|
5736
7005
|
async execute(support_ticket_id) {
|
|
5737
7006
|
const existing = await this.support_ticketRepo.read(support_ticket_id);
|
|
@@ -5743,7 +7012,7 @@ let ConvertToInternalFeat = class ConvertToInternalFeat$1 {
|
|
|
5743
7012
|
credit_value: null,
|
|
5744
7013
|
dev_lifecycle: "BACKLOG",
|
|
5745
7014
|
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5746
|
-
updated_by: this.session.user.
|
|
7015
|
+
updated_by: this.session.user.userId
|
|
5747
7016
|
};
|
|
5748
7017
|
const updated = await this.support_ticketRepo.update(updateEntity);
|
|
5749
7018
|
await this.create_record_version.execute({
|
|
@@ -5757,26 +7026,31 @@ let ConvertToInternalFeat = class ConvertToInternalFeat$1 {
|
|
|
5757
7026
|
auth_role: this.session.user.user_type,
|
|
5758
7027
|
auth_username: this.session.user.username
|
|
5759
7028
|
});
|
|
5760
|
-
|
|
7029
|
+
const dto = toStaffSupportTicketReadDto(updated);
|
|
7030
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5761
7031
|
}
|
|
5762
7032
|
};
|
|
5763
7033
|
ConvertToInternalFeat = __decorate([
|
|
5764
7034
|
injectable(),
|
|
5765
7035
|
__decorateParam(0, injectSession()),
|
|
5766
7036
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5767
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
7037
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7038
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5768
7039
|
], ConvertToInternalFeat);
|
|
5769
7040
|
|
|
5770
7041
|
//#endregion
|
|
5771
7042
|
//#region src/slices/support_ticket/features/staff/create_support_ticket_admin_feat.ts
|
|
5772
7043
|
let CreateSupportTicketAdminFeat = class CreateSupportTicketAdminFeat$1 {
|
|
5773
|
-
constructor(session, support_ticketRepo,
|
|
7044
|
+
constructor(session, support_ticketRepo, subscriberRepo, notificationService, triageService, supportStaffRepo, userDisplayLookup, create_record_version, appSettingsRepo, logger$1) {
|
|
5774
7045
|
this.session = session;
|
|
5775
7046
|
this.support_ticketRepo = support_ticketRepo;
|
|
7047
|
+
this.subscriberRepo = subscriberRepo;
|
|
7048
|
+
this.notificationService = notificationService;
|
|
7049
|
+
this.triageService = triageService;
|
|
7050
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
7051
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5776
7052
|
this.create_record_version = create_record_version;
|
|
5777
7053
|
this.appSettingsRepo = appSettingsRepo;
|
|
5778
|
-
this.emailService = emailService;
|
|
5779
|
-
this.env = env;
|
|
5780
7054
|
this.logger = logger$1;
|
|
5781
7055
|
}
|
|
5782
7056
|
/**
|
|
@@ -5789,10 +7063,8 @@ let CreateSupportTicketAdminFeat = class CreateSupportTicketAdminFeat$1 {
|
|
|
5789
7063
|
title: input.title || "",
|
|
5790
7064
|
description: input.description || "",
|
|
5791
7065
|
type: input.type || "FEATURE_REQUEST",
|
|
5792
|
-
priority: input.priority
|
|
7066
|
+
priority: input.priority ?? SUPPORT_TICKET_PRIORITY_TO_NUMBER.MEDIUM,
|
|
5793
7067
|
approval_status: input.is_internal ? "INTERNAL" : "PENDING",
|
|
5794
|
-
requester_email: this.session.user.email,
|
|
5795
|
-
requester_name: this.session.user.username,
|
|
5796
7068
|
dev_lifecycle: input.dev_lifecycle || null,
|
|
5797
7069
|
credit_value: input.is_internal ? null : formatCreditValue(input.credit_value),
|
|
5798
7070
|
delivered_value: input.delivered_value || null,
|
|
@@ -5807,6 +7079,12 @@ let CreateSupportTicketAdminFeat = class CreateSupportTicketAdminFeat$1 {
|
|
|
5807
7079
|
};
|
|
5808
7080
|
const support_ticketCreated = await this.support_ticketRepo.create(support_ticketEntity);
|
|
5809
7081
|
support_ticketCreated.display_id = await this.appSettingsRepo.getNextSequentialId(RecordConst.SUPPORT_TICKET, this.session.user.userId);
|
|
7082
|
+
let assigneeId = null;
|
|
7083
|
+
if (input.assigned_to?.trim()) {
|
|
7084
|
+
if (!(await this.supportStaffRepo.readTriageUsers()).some((u) => u.id === input.assigned_to)) throw new BusinessError("Selected assignee is not a support staff member");
|
|
7085
|
+
assigneeId = input.assigned_to;
|
|
7086
|
+
} else assigneeId = await this.triageService.getNextAssignee(this.session.user);
|
|
7087
|
+
if (assigneeId) support_ticketCreated.assigned_to = assigneeId;
|
|
5810
7088
|
await this.support_ticketRepo.update(support_ticketCreated);
|
|
5811
7089
|
await this.create_record_version.execute({
|
|
5812
7090
|
record_id: support_ticketCreated.id,
|
|
@@ -5818,58 +7096,30 @@ let CreateSupportTicketAdminFeat = class CreateSupportTicketAdminFeat$1 {
|
|
|
5818
7096
|
auth_role: this.session.user.user_type,
|
|
5819
7097
|
auth_username: this.session.user.username
|
|
5820
7098
|
});
|
|
5821
|
-
if (!input.is_internal)
|
|
5822
|
-
|
|
7099
|
+
if (!input.is_internal) {
|
|
7100
|
+
await this.addRequesterAsFollower(support_ticketCreated);
|
|
7101
|
+
await this.notificationService.notifyAssignee(support_ticketCreated, "TICKET_CREATED", { actorUserId: userId });
|
|
7102
|
+
await this.notificationService.notifyFollowers(support_ticketCreated.id, support_ticketCreated, "TICKET_CREATED", { actorUserId: userId });
|
|
7103
|
+
}
|
|
7104
|
+
const dto = toStaffSupportTicketReadDto(support_ticketCreated);
|
|
7105
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5823
7106
|
}
|
|
5824
7107
|
/**
|
|
5825
|
-
*
|
|
7108
|
+
* Auto-add requester (created_by) as follower
|
|
5826
7109
|
*/
|
|
5827
|
-
async
|
|
7110
|
+
async addRequesterAsFollower(ticket) {
|
|
5828
7111
|
try {
|
|
5829
|
-
const
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
}
|
|
5839
|
-
const ticketUrl = `${this.env.WEBSITE_URL}/support/${ticket.id}`;
|
|
5840
|
-
const subject = `New Support Ticket Created: ${ticket.title}`;
|
|
5841
|
-
const bodyText = `
|
|
5842
|
-
A new support ticket has been created:
|
|
5843
|
-
|
|
5844
|
-
Ticket #${ticket.display_id || ticket.id}
|
|
5845
|
-
Title: ${ticket.title}
|
|
5846
|
-
Type: ${ticket.type}
|
|
5847
|
-
Priority: ${ticket.priority}
|
|
5848
|
-
Requester: ${ticket.requester_name} (${ticket.requester_email})
|
|
5849
|
-
|
|
5850
|
-
Description:
|
|
5851
|
-
${ticket.description}
|
|
5852
|
-
|
|
5853
|
-
View ticket: ${ticketUrl}
|
|
5854
|
-
`.trim();
|
|
5855
|
-
const bodyHtml = `
|
|
5856
|
-
<h2>New Support Ticket Created</h2>
|
|
5857
|
-
<p>A new support ticket has been created:</p>
|
|
5858
|
-
<ul>
|
|
5859
|
-
<li><strong>Ticket #:</strong> ${ticket.display_id || ticket.id}</li>
|
|
5860
|
-
<li><strong>Title:</strong> ${ticket.title}</li>
|
|
5861
|
-
<li><strong>Type:</strong> ${ticket.type}</li>
|
|
5862
|
-
<li><strong>Priority:</strong> ${ticket.priority}</li>
|
|
5863
|
-
<li><strong>Requester:</strong> ${ticket.requester_name} (${ticket.requester_email})</li>
|
|
5864
|
-
</ul>
|
|
5865
|
-
<p><strong>Description:</strong></p>
|
|
5866
|
-
<p>${ticket.description}</p>
|
|
5867
|
-
<p><a href="${ticketUrl}">View Ticket</a></p>
|
|
5868
|
-
`.trim();
|
|
5869
|
-
await this.emailService.sendBulkEmail(emails, subject, bodyText, bodyHtml);
|
|
5870
|
-
this.logger.info(`Sent support ticket notification to ${emails.length} recipients`);
|
|
7112
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7113
|
+
await this.subscriberRepo.ensureSubscriber({
|
|
7114
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
7115
|
+
record_id: ticket.id,
|
|
7116
|
+
user_id: ticket.created_by,
|
|
7117
|
+
subscribed_events: null,
|
|
7118
|
+
created_at: now,
|
|
7119
|
+
created_by: ticket.created_by
|
|
7120
|
+
});
|
|
5871
7121
|
} catch (error) {
|
|
5872
|
-
this.logger.error("Failed to
|
|
7122
|
+
this.logger.error("Failed to add requester as follower", { error });
|
|
5873
7123
|
}
|
|
5874
7124
|
}
|
|
5875
7125
|
};
|
|
@@ -5877,11 +7127,14 @@ CreateSupportTicketAdminFeat = __decorate([
|
|
|
5877
7127
|
injectable(),
|
|
5878
7128
|
__decorateParam(0, injectSession()),
|
|
5879
7129
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5880
|
-
__decorateParam(2, inject(
|
|
5881
|
-
__decorateParam(3, inject(
|
|
5882
|
-
__decorateParam(4, inject(
|
|
5883
|
-
__decorateParam(5, inject(
|
|
5884
|
-
__decorateParam(6, inject(
|
|
7130
|
+
__decorateParam(2, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
7131
|
+
__decorateParam(3, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
7132
|
+
__decorateParam(4, inject(SUPPORT_TICKET_TOKENS.ISupportTicketTriageService)),
|
|
7133
|
+
__decorateParam(5, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo)),
|
|
7134
|
+
__decorateParam(6, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
7135
|
+
__decorateParam(7, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7136
|
+
__decorateParam(8, inject(TOKENS.IAppSettingsRepo)),
|
|
7137
|
+
__decorateParam(9, inject(TOKENS.LOGGER))
|
|
5885
7138
|
], CreateSupportTicketAdminFeat);
|
|
5886
7139
|
|
|
5887
7140
|
//#endregion
|
|
@@ -5928,13 +7181,93 @@ DeleteSupportTicketFeat = __decorate([
|
|
|
5928
7181
|
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
5929
7182
|
], DeleteSupportTicketFeat);
|
|
5930
7183
|
|
|
7184
|
+
//#endregion
|
|
7185
|
+
//#region src/slices/support_ticket/features/staff/fix_support_ticket_user_ids_feat.ts
|
|
7186
|
+
const UNIVERSAL_ID_LENGTH = 32;
|
|
7187
|
+
let FixSupportTicketUserIdsFeature = class FixSupportTicketUserIdsFeature$1 {
|
|
7188
|
+
constructor(supportTicketRepo, userRepo) {
|
|
7189
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
7190
|
+
this.userRepo = userRepo;
|
|
7191
|
+
}
|
|
7192
|
+
async execute() {
|
|
7193
|
+
const tickets = await this.supportTicketRepo.findWithInvalidUserIds();
|
|
7194
|
+
if (tickets.length === 0) return {
|
|
7195
|
+
ticketsScanned: 0,
|
|
7196
|
+
ticketsFixed: 0,
|
|
7197
|
+
ticketsSkipped: 0
|
|
7198
|
+
};
|
|
7199
|
+
const emailToUserId = await this.buildEmailToUserIdMap(tickets);
|
|
7200
|
+
let ticketsFixed = 0;
|
|
7201
|
+
let ticketsSkipped = 0;
|
|
7202
|
+
for (const ticket of tickets) {
|
|
7203
|
+
const updates = this.buildUpdates(ticket, emailToUserId);
|
|
7204
|
+
if (Object.keys(updates).length === 0) {
|
|
7205
|
+
ticketsSkipped++;
|
|
7206
|
+
continue;
|
|
7207
|
+
}
|
|
7208
|
+
const updated = {
|
|
7209
|
+
...ticket,
|
|
7210
|
+
...updates
|
|
7211
|
+
};
|
|
7212
|
+
await this.supportTicketRepo.update(updated);
|
|
7213
|
+
ticketsFixed++;
|
|
7214
|
+
}
|
|
7215
|
+
return {
|
|
7216
|
+
ticketsScanned: tickets.length,
|
|
7217
|
+
ticketsFixed,
|
|
7218
|
+
ticketsSkipped
|
|
7219
|
+
};
|
|
7220
|
+
}
|
|
7221
|
+
async buildEmailToUserIdMap(tickets) {
|
|
7222
|
+
const fieldsToFix = [
|
|
7223
|
+
"created_by",
|
|
7224
|
+
"updated_by",
|
|
7225
|
+
"assigned_to",
|
|
7226
|
+
"archived_by",
|
|
7227
|
+
"deleted_by"
|
|
7228
|
+
];
|
|
7229
|
+
const emails = /* @__PURE__ */ new Set();
|
|
7230
|
+
for (const ticket of tickets) for (const field of fieldsToFix) {
|
|
7231
|
+
const value = ticket[field];
|
|
7232
|
+
if (value != null && value.length !== UNIVERSAL_ID_LENGTH) emails.add(value);
|
|
7233
|
+
}
|
|
7234
|
+
if (emails.size === 0) return /* @__PURE__ */ new Map();
|
|
7235
|
+
const users = await this.userRepo.read_users_by_emails_case_insensitive([...emails]);
|
|
7236
|
+
const map = /* @__PURE__ */ new Map();
|
|
7237
|
+
for (const u of users) map.set(u.email.toLowerCase(), u.id);
|
|
7238
|
+
return map;
|
|
7239
|
+
}
|
|
7240
|
+
buildUpdates(ticket, emailToUserId) {
|
|
7241
|
+
const updates = {};
|
|
7242
|
+
for (const field of [
|
|
7243
|
+
"created_by",
|
|
7244
|
+
"updated_by",
|
|
7245
|
+
"assigned_to",
|
|
7246
|
+
"archived_by",
|
|
7247
|
+
"deleted_by"
|
|
7248
|
+
]) {
|
|
7249
|
+
const value = ticket[field];
|
|
7250
|
+
if (value == null || value.length === UNIVERSAL_ID_LENGTH) continue;
|
|
7251
|
+
const userId = emailToUserId.get(value.toLowerCase());
|
|
7252
|
+
if (userId) updates[field] = userId;
|
|
7253
|
+
}
|
|
7254
|
+
return updates;
|
|
7255
|
+
}
|
|
7256
|
+
};
|
|
7257
|
+
FixSupportTicketUserIdsFeature = __decorate([
|
|
7258
|
+
injectable(),
|
|
7259
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
7260
|
+
__decorateParam(1, inject(USER_TOKENS.IUsersRepo))
|
|
7261
|
+
], FixSupportTicketUserIdsFeature);
|
|
7262
|
+
|
|
5931
7263
|
//#endregion
|
|
5932
7264
|
//#region src/slices/support_ticket/features/staff/get_support_ticket_admin_feat.ts
|
|
5933
7265
|
let GetSupportTicketAdminFeature = class GetSupportTicketAdminFeature$1 {
|
|
5934
|
-
constructor(session, support_ticketRepo, appSettingsRepo) {
|
|
7266
|
+
constructor(session, support_ticketRepo, appSettingsRepo, userDisplayLookup) {
|
|
5935
7267
|
this.session = session;
|
|
5936
7268
|
this.support_ticketRepo = support_ticketRepo;
|
|
5937
7269
|
this.appSettingsRepo = appSettingsRepo;
|
|
7270
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5938
7271
|
}
|
|
5939
7272
|
async execute(id) {
|
|
5940
7273
|
let support_ticket = await this.support_ticketRepo.read(id);
|
|
@@ -5948,39 +7281,110 @@ let GetSupportTicketAdminFeature = class GetSupportTicketAdminFeature$1 {
|
|
|
5948
7281
|
updated_by: this.session.user.userId
|
|
5949
7282
|
});
|
|
5950
7283
|
}
|
|
5951
|
-
|
|
7284
|
+
const dto = toStaffSupportTicketReadDto(support_ticket);
|
|
7285
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5952
7286
|
}
|
|
5953
7287
|
};
|
|
5954
7288
|
GetSupportTicketAdminFeature = __decorate([
|
|
5955
7289
|
injectable(),
|
|
5956
7290
|
__decorateParam(0, injectSession()),
|
|
5957
7291
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5958
|
-
__decorateParam(2, inject(TOKENS.IAppSettingsRepo))
|
|
7292
|
+
__decorateParam(2, inject(TOKENS.IAppSettingsRepo)),
|
|
7293
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5959
7294
|
], GetSupportTicketAdminFeature);
|
|
5960
7295
|
|
|
7296
|
+
//#endregion
|
|
7297
|
+
//#region src/slices/support_ticket/features/staff/get_requestors_for_active_support_tickets_feat.ts
|
|
7298
|
+
let GetRequestorsForActiveSupportTicketsFeat = class GetRequestorsForActiveSupportTicketsFeat$1 {
|
|
7299
|
+
constructor(supportTicketRepo, userRepo) {
|
|
7300
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
7301
|
+
this.userRepo = userRepo;
|
|
7302
|
+
}
|
|
7303
|
+
async execute(options) {
|
|
7304
|
+
const requestorIds = await this.supportTicketRepo.read_distinct_requestors_for_active_tickets(options);
|
|
7305
|
+
if (requestorIds.length === 0) return [];
|
|
7306
|
+
return (await this.userRepo.read_users_by_ids(requestorIds)).map((u) => ({
|
|
7307
|
+
id: u.id,
|
|
7308
|
+
email: u.email
|
|
7309
|
+
}));
|
|
7310
|
+
}
|
|
7311
|
+
};
|
|
7312
|
+
GetRequestorsForActiveSupportTicketsFeat = __decorate([
|
|
7313
|
+
injectable(),
|
|
7314
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
7315
|
+
__decorateParam(1, inject(USER_TOKENS.IUsersRepo))
|
|
7316
|
+
], GetRequestorsForActiveSupportTicketsFeat);
|
|
7317
|
+
|
|
5961
7318
|
//#endregion
|
|
5962
7319
|
//#region src/slices/support_ticket/features/staff/get_support_tickets_admin_feat.ts
|
|
5963
7320
|
let GetSupportTicketsAdminFeature = class GetSupportTicketsAdminFeature$1 {
|
|
5964
|
-
constructor(support_ticketRepo) {
|
|
7321
|
+
constructor(support_ticketRepo, userDisplayLookup) {
|
|
5965
7322
|
this.support_ticketRepo = support_ticketRepo;
|
|
7323
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5966
7324
|
}
|
|
5967
7325
|
async execute(filters) {
|
|
5968
7326
|
const result = await this.support_ticketRepo.read_all(filters);
|
|
7327
|
+
const dtos = result.items.map(toStaffSupportTicketReadDto);
|
|
7328
|
+
const userIds = [...new Set(dtos.flatMap(collectUserIdsFromStaffTicket))];
|
|
7329
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
5969
7330
|
return {
|
|
5970
|
-
items:
|
|
7331
|
+
items: dtos.map((t) => enrichStaffTicket(t, displayMap)),
|
|
5971
7332
|
pageInfo: result.pageInfo
|
|
5972
7333
|
};
|
|
5973
7334
|
}
|
|
5974
7335
|
};
|
|
5975
|
-
GetSupportTicketsAdminFeature = __decorate([
|
|
7336
|
+
GetSupportTicketsAdminFeature = __decorate([
|
|
7337
|
+
injectable(),
|
|
7338
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
7339
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
7340
|
+
], GetSupportTicketsAdminFeature);
|
|
7341
|
+
|
|
7342
|
+
//#endregion
|
|
7343
|
+
//#region src/slices/support_ticket/features/staff/list_support_ticket_subscribers_feat.ts
|
|
7344
|
+
function toRecordSubscriberReadDto(e) {
|
|
7345
|
+
return {
|
|
7346
|
+
id: e.id,
|
|
7347
|
+
record_type: e.record_type,
|
|
7348
|
+
record_id: e.record_id,
|
|
7349
|
+
user_id: e.user_id,
|
|
7350
|
+
subscribed_events: e.subscribed_events ?? null,
|
|
7351
|
+
created_at: e.created_at,
|
|
7352
|
+
created_by: e.created_by
|
|
7353
|
+
};
|
|
7354
|
+
}
|
|
7355
|
+
let ListSupportTicketSubscribersFeat = class ListSupportTicketSubscribersFeat$1 {
|
|
7356
|
+
constructor(subscriberRepo, supportTicketRepo, userDisplayLookup) {
|
|
7357
|
+
this.subscriberRepo = subscriberRepo;
|
|
7358
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
7359
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
7360
|
+
}
|
|
7361
|
+
async execute(supportTicketId) {
|
|
7362
|
+
if (!await this.supportTicketRepo.read(supportTicketId)) throw new BusinessError("Support ticket not found");
|
|
7363
|
+
const dtos = (await this.subscriberRepo.readByRecordId(RecordConst.SUPPORT_TICKET, supportTicketId)).map(toRecordSubscriberReadDto);
|
|
7364
|
+
const userIds = [...new Set(dtos.flatMap((s) => [s.user_id, s.created_by].filter(Boolean)))];
|
|
7365
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
7366
|
+
return dtos.map((s) => ({
|
|
7367
|
+
...s,
|
|
7368
|
+
user_id_display_name: s.user_id ? displayMap.get(s.user_id) ?? s.user_id : null,
|
|
7369
|
+
created_by_display_name: s.created_by ? displayMap.get(s.created_by) ?? s.created_by : null
|
|
7370
|
+
}));
|
|
7371
|
+
}
|
|
7372
|
+
};
|
|
7373
|
+
ListSupportTicketSubscribersFeat = __decorate([
|
|
7374
|
+
injectable(),
|
|
7375
|
+
__decorateParam(0, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
7376
|
+
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
7377
|
+
__decorateParam(2, inject(USER_TOKENS.IUserDisplayLookup))
|
|
7378
|
+
], ListSupportTicketSubscribersFeat);
|
|
5976
7379
|
|
|
5977
7380
|
//#endregion
|
|
5978
7381
|
//#region src/slices/support_ticket/features/staff/reactivate_internal_task_feat.ts
|
|
5979
7382
|
let ReactivateInternalTaskFeat = class ReactivateInternalTaskFeat$1 {
|
|
5980
|
-
constructor(repo, create_record_version, session) {
|
|
7383
|
+
constructor(repo, create_record_version, session, userDisplayLookup) {
|
|
5981
7384
|
this.repo = repo;
|
|
5982
7385
|
this.create_record_version = create_record_version;
|
|
5983
7386
|
this.session = session;
|
|
7387
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5984
7388
|
}
|
|
5985
7389
|
/**
|
|
5986
7390
|
* Reactivates a terminal internal task.
|
|
@@ -6028,23 +7432,27 @@ let ReactivateInternalTaskFeat = class ReactivateInternalTaskFeat$1 {
|
|
|
6028
7432
|
completed_at: null
|
|
6029
7433
|
});
|
|
6030
7434
|
if (!updatedSupportTicket) throw new Error("Failed to reactivate internal task.");
|
|
6031
|
-
|
|
7435
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
7436
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6032
7437
|
}
|
|
6033
7438
|
};
|
|
6034
7439
|
ReactivateInternalTaskFeat = __decorate([
|
|
6035
7440
|
injectable(),
|
|
6036
7441
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6037
7442
|
__decorateParam(1, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6038
|
-
__decorateParam(2, injectSession())
|
|
7443
|
+
__decorateParam(2, injectSession()),
|
|
7444
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6039
7445
|
], ReactivateInternalTaskFeat);
|
|
6040
7446
|
|
|
6041
7447
|
//#endregion
|
|
6042
7448
|
//#region src/slices/support_ticket/features/staff/reject_support_ticket_feat.ts
|
|
6043
7449
|
let RejectSupportTicketFeat = class RejectSupportTicketFeat$1 {
|
|
6044
|
-
constructor(repo, create_record_version, session) {
|
|
7450
|
+
constructor(repo, notificationService, create_record_version, session, userDisplayLookup) {
|
|
6045
7451
|
this.repo = repo;
|
|
7452
|
+
this.notificationService = notificationService;
|
|
6046
7453
|
this.create_record_version = create_record_version;
|
|
6047
7454
|
this.session = session;
|
|
7455
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6048
7456
|
}
|
|
6049
7457
|
/**
|
|
6050
7458
|
* Rejects a support_ticket item and locks it.
|
|
@@ -6085,24 +7493,41 @@ let RejectSupportTicketFeat = class RejectSupportTicketFeat$1 {
|
|
|
6085
7493
|
locked_approval_at: lockedAt
|
|
6086
7494
|
});
|
|
6087
7495
|
if (!updatedSupportTicket) throw new Error("Failed to reject support_ticket.");
|
|
6088
|
-
|
|
7496
|
+
await this.notificationService.notifyFollowers(id, updatedSupportTicket, "APPROVAL_STATUS_CHANGED", { actorUserId: this.session.user.userId });
|
|
7497
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
7498
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6089
7499
|
}
|
|
6090
7500
|
};
|
|
6091
7501
|
RejectSupportTicketFeat = __decorate([
|
|
6092
7502
|
injectable(),
|
|
6093
7503
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6094
|
-
__decorateParam(1, inject(
|
|
6095
|
-
__decorateParam(2,
|
|
7504
|
+
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
7505
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7506
|
+
__decorateParam(3, injectSession()),
|
|
7507
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6096
7508
|
], RejectSupportTicketFeat);
|
|
6097
7509
|
|
|
7510
|
+
//#endregion
|
|
7511
|
+
//#region src/slices/support_ticket/features/staff/remove_support_ticket_subscriber_feat.ts
|
|
7512
|
+
let RemoveSupportTicketSubscriberFeat = class RemoveSupportTicketSubscriberFeat$1 {
|
|
7513
|
+
constructor(subscriberRepo) {
|
|
7514
|
+
this.subscriberRepo = subscriberRepo;
|
|
7515
|
+
}
|
|
7516
|
+
async execute(supportTicketId, subscriberId) {
|
|
7517
|
+
if (!await this.subscriberRepo.delete(subscriberId)) throw new BusinessError("Subscriber not found or already removed");
|
|
7518
|
+
}
|
|
7519
|
+
};
|
|
7520
|
+
RemoveSupportTicketSubscriberFeat = __decorate([injectable(), __decorateParam(0, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo))], RemoveSupportTicketSubscriberFeat);
|
|
7521
|
+
|
|
6098
7522
|
//#endregion
|
|
6099
7523
|
//#region src/slices/support_ticket/features/staff/revert_support_ticket_feat.ts
|
|
6100
7524
|
let RevertSupportTicketFeat = class RevertSupportTicketFeat$1 {
|
|
6101
|
-
constructor(repo, creditService, create_record_version, session) {
|
|
7525
|
+
constructor(repo, creditService, create_record_version, session, userDisplayLookup) {
|
|
6102
7526
|
this.repo = repo;
|
|
6103
7527
|
this.creditService = creditService;
|
|
6104
7528
|
this.create_record_version = create_record_version;
|
|
6105
7529
|
this.session = session;
|
|
7530
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6106
7531
|
}
|
|
6107
7532
|
/**
|
|
6108
7533
|
* Reverts a support_ticket item back to PENDING status and refunds credits.
|
|
@@ -6151,7 +7576,8 @@ let RevertSupportTicketFeat = class RevertSupportTicketFeat$1 {
|
|
|
6151
7576
|
locked_approval_at: null
|
|
6152
7577
|
});
|
|
6153
7578
|
if (!updatedSupportTicket) throw new Error("Failed to revert support_ticket.");
|
|
6154
|
-
|
|
7579
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
7580
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6155
7581
|
}
|
|
6156
7582
|
};
|
|
6157
7583
|
RevertSupportTicketFeat = __decorate([
|
|
@@ -6159,7 +7585,8 @@ RevertSupportTicketFeat = __decorate([
|
|
|
6159
7585
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6160
7586
|
__decorateParam(1, inject(CREDIT_SERVICE_TOKEN)),
|
|
6161
7587
|
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6162
|
-
__decorateParam(3, injectSession())
|
|
7588
|
+
__decorateParam(3, injectSession()),
|
|
7589
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6163
7590
|
], RevertSupportTicketFeat);
|
|
6164
7591
|
|
|
6165
7592
|
//#endregion
|
|
@@ -6168,17 +7595,18 @@ RevertSupportTicketFeat = __decorate([
|
|
|
6168
7595
|
* Validate business rules for staff updates based on permissions matrix
|
|
6169
7596
|
*/
|
|
6170
7597
|
const validateBusinessRules = (input, existing) => {
|
|
7598
|
+
if (existing.archived_at) throw new BusinessError("Cannot edit an archived support ticket.");
|
|
6171
7599
|
const currentApproval = existing.approval_status;
|
|
6172
7600
|
const currentDevLifecycle = existing.dev_lifecycle;
|
|
6173
7601
|
if (input.dev_lifecycle !== void 0 && input.dev_lifecycle !== currentDevLifecycle) {
|
|
6174
|
-
if (!currentApproval || !["APPROVED", "INTERNAL"].includes(currentApproval)) throw new
|
|
7602
|
+
if (!currentApproval || !["APPROVED", "INTERNAL"].includes(currentApproval)) throw new BusinessError("Can only set dev lifecycle when support_ticket is APPROVED or INTERNAL");
|
|
6175
7603
|
}
|
|
6176
7604
|
if (input.credit_value !== void 0 && input.credit_value !== existing.credit_value) {
|
|
6177
|
-
if (currentApproval === "INTERNAL") throw new
|
|
6178
|
-
if (currentApproval !== "PENDING") throw new
|
|
7605
|
+
if (currentApproval === "INTERNAL") throw new BusinessError("Internal support_ticket cannot have credit values");
|
|
7606
|
+
if (currentApproval !== "PENDING") throw new BusinessError("Can only edit credit value when support_ticket is PENDING");
|
|
6179
7607
|
}
|
|
6180
7608
|
if (input.delivered_value !== void 0 && input.delivered_value !== existing.delivered_value) {
|
|
6181
|
-
if (currentDevLifecycle !== "DEPLOYED") throw new
|
|
7609
|
+
if (currentDevLifecycle !== "DEPLOYED") throw new BusinessError("Can only set delivered value when dev lifecycle is DEPLOYED");
|
|
6182
7610
|
}
|
|
6183
7611
|
const lockedDevStages = [
|
|
6184
7612
|
"DEVELOPMENT",
|
|
@@ -6186,21 +7614,22 @@ const validateBusinessRules = (input, existing) => {
|
|
|
6186
7614
|
"TESTING",
|
|
6187
7615
|
"STAGING",
|
|
6188
7616
|
"PO_APPROVAL",
|
|
7617
|
+
"VERIFICATION",
|
|
6189
7618
|
"DEPLOYED"
|
|
6190
7619
|
];
|
|
6191
7620
|
if (input.start_at !== void 0 && input.start_at !== existing.start_at) {
|
|
6192
|
-
if (currentApproval === "REJECTED") throw new
|
|
6193
|
-
if (currentDevLifecycle && lockedDevStages.includes(currentDevLifecycle)) throw new
|
|
7621
|
+
if (currentApproval === "REJECTED") throw new BusinessError("Cannot edit start date when support_ticket is REJECTED");
|
|
7622
|
+
if (currentDevLifecycle && lockedDevStages.includes(currentDevLifecycle)) throw new BusinessError("Cannot edit start date once development has started (DEVELOPMENT or higher)");
|
|
6194
7623
|
}
|
|
6195
7624
|
if (input.target_at !== void 0 && input.target_at !== existing.target_at) {
|
|
6196
|
-
if (currentApproval === "REJECTED") throw new
|
|
6197
|
-
if (currentDevLifecycle && lockedDevStages.includes(currentDevLifecycle)) throw new
|
|
7625
|
+
if (currentApproval === "REJECTED") throw new BusinessError("Cannot edit target date when support_ticket is REJECTED");
|
|
7626
|
+
if (currentDevLifecycle && lockedDevStages.includes(currentDevLifecycle)) throw new BusinessError("Cannot edit target date once development has started (DEVELOPMENT or higher)");
|
|
6198
7627
|
}
|
|
6199
7628
|
if (input.completed_at !== void 0 && input.completed_at !== existing.completed_at) {
|
|
6200
|
-
if (!currentApproval || !["APPROVED", "INTERNAL"].includes(currentApproval) || !currentDevLifecycle || !["DEPLOYED", "CANCELLED"].includes(currentDevLifecycle)) throw new
|
|
7629
|
+
if (!currentApproval || !["APPROVED", "INTERNAL"].includes(currentApproval) || !currentDevLifecycle || !["DEPLOYED", "CANCELLED"].includes(currentDevLifecycle)) throw new BusinessError("Can only set completed date when dev lifecycle is DEPLOYED or CANCELLED");
|
|
6201
7630
|
}
|
|
6202
7631
|
if (currentApproval === "REJECTED") {
|
|
6203
|
-
if (input.title !== void 0 && input.title !== existing.title || input.description !== void 0 && input.description !== existing.description || input.type !== void 0 && input.type !== existing.type || input.priority !== void 0 && input.priority !== existing.priority || input.dev_lifecycle !== void 0 && input.dev_lifecycle !== existing.dev_lifecycle || input.credit_value !== void 0 && input.credit_value !== existing.credit_value || input.delivered_value !== void 0 && input.delivered_value !== existing.delivered_value) throw new
|
|
7632
|
+
if (input.title !== void 0 && input.title !== existing.title || input.description !== void 0 && input.description !== existing.description || input.type !== void 0 && input.type !== existing.type || input.priority !== void 0 && input.priority !== existing.priority || input.dev_lifecycle !== void 0 && input.dev_lifecycle !== existing.dev_lifecycle || input.credit_value !== void 0 && input.credit_value !== existing.credit_value || input.delivered_value !== void 0 && input.delivered_value !== existing.delivered_value) throw new BusinessError("Cannot edit REJECTED support_ticket. Use revert workflow to unlock");
|
|
6204
7633
|
}
|
|
6205
7634
|
};
|
|
6206
7635
|
|
|
@@ -6213,24 +7642,26 @@ function shouldUpdateCreditsSetAt(oldValue, newValue) {
|
|
|
6213
7642
|
return isCreditValueEmpty(oldValue) !== isCreditValueEmpty(newValue);
|
|
6214
7643
|
}
|
|
6215
7644
|
let UpdateSupportTicketAdminFeat = class UpdateSupportTicketAdminFeat$1 {
|
|
6216
|
-
constructor(session, support_ticketRepo, create_record_version) {
|
|
7645
|
+
constructor(session, support_ticketRepo, create_record_version, notificationService, userDisplayLookup) {
|
|
6217
7646
|
this.session = session;
|
|
6218
7647
|
this.support_ticketRepo = support_ticketRepo;
|
|
6219
7648
|
this.create_record_version = create_record_version;
|
|
7649
|
+
this.notificationService = notificationService;
|
|
7650
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6220
7651
|
}
|
|
6221
7652
|
/**
|
|
6222
7653
|
* Update support_ticket - comprehensive update for staff
|
|
6223
7654
|
*/
|
|
6224
7655
|
async execute(input) {
|
|
6225
7656
|
const support_ticket = await this.support_ticketRepo.read(input.id);
|
|
6226
|
-
if (!support_ticket) throw new
|
|
7657
|
+
if (!support_ticket) throw new BusinessError("SupportTicket not found");
|
|
6227
7658
|
validateBusinessRules(input, support_ticket);
|
|
6228
7659
|
const isoTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
6229
7660
|
const frEntity = {
|
|
6230
7661
|
...support_ticket,
|
|
6231
7662
|
id: input.id,
|
|
6232
7663
|
updated_at: isoTime,
|
|
6233
|
-
updated_by: this.session.user.
|
|
7664
|
+
updated_by: this.session.user.userId
|
|
6234
7665
|
};
|
|
6235
7666
|
if (input.title !== void 0 && input.title !== null) frEntity.title = input.title;
|
|
6236
7667
|
if (input.description !== void 0 && input.description !== null) frEntity.description = input.description;
|
|
@@ -6245,6 +7676,7 @@ let UpdateSupportTicketAdminFeat = class UpdateSupportTicketAdminFeat$1 {
|
|
|
6245
7676
|
if (input.start_at !== void 0) frEntity.start_at = input.start_at ?? void 0;
|
|
6246
7677
|
if (input.target_at !== void 0) frEntity.target_at = input.target_at ?? void 0;
|
|
6247
7678
|
if (input.completed_at !== void 0) frEntity.completed_at = input.completed_at ?? void 0;
|
|
7679
|
+
if (input.assigned_to !== void 0) frEntity.assigned_to = input.assigned_to?.trim() || null;
|
|
6248
7680
|
const changed_props = getChangedProperties(support_ticket, frEntity);
|
|
6249
7681
|
if (Object.keys(changed_props).length > 0) await this.create_record_version.execute({
|
|
6250
7682
|
record_id: frEntity.id,
|
|
@@ -6257,14 +7689,22 @@ let UpdateSupportTicketAdminFeat = class UpdateSupportTicketAdminFeat$1 {
|
|
|
6257
7689
|
auth_role: this.session.user.user_type,
|
|
6258
7690
|
auth_username: this.session.user.username
|
|
6259
7691
|
});
|
|
6260
|
-
|
|
7692
|
+
const support_ticketUpdated = await this.support_ticketRepo.update(frEntity);
|
|
7693
|
+
if (input.assigned_to !== void 0 && support_ticket.assigned_to !== support_ticketUpdated.assigned_to && support_ticketUpdated.assigned_to) {
|
|
7694
|
+
await this.notificationService.notifyAssignee(support_ticketUpdated, "TICKET_ASSIGNED", { actorUserId: this.session.user.userId });
|
|
7695
|
+
await this.notificationService.notifyFollowers(support_ticketUpdated.id, support_ticketUpdated, "TICKET_ASSIGNED", { actorUserId: this.session.user.userId });
|
|
7696
|
+
}
|
|
7697
|
+
const dto = toStaffSupportTicketReadDto(support_ticketUpdated);
|
|
7698
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6261
7699
|
}
|
|
6262
7700
|
};
|
|
6263
7701
|
UpdateSupportTicketAdminFeat = __decorate([
|
|
6264
7702
|
injectable(),
|
|
6265
7703
|
__decorateParam(0, injectSession()),
|
|
6266
7704
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6267
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
7705
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7706
|
+
__decorateParam(3, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
7707
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6268
7708
|
], UpdateSupportTicketAdminFeat);
|
|
6269
7709
|
|
|
6270
7710
|
//#endregion
|
|
@@ -6274,12 +7714,16 @@ UpdateSupportTicketAdminFeat = __decorate([
|
|
|
6274
7714
|
*/
|
|
6275
7715
|
function registerSupportTicketDependencies() {
|
|
6276
7716
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.ISupportTicketRepo, SupportTicketRepo);
|
|
7717
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService, SupportTicketNotificationService);
|
|
7718
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.ISupportTicketTriageService, SupportTicketTriageService);
|
|
6277
7719
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.ICreateSupportTicketFeature, CreateSupportTicketFeat);
|
|
6278
7720
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IUpdateSupportTicketCustomerFeature, UpdateSupportTicketCustomerFeat);
|
|
6279
7721
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetSupportTicketCustomerFeature, GetSupportTicketCustomerFeature);
|
|
6280
7722
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetSupportTicketsCustomerFeature, GetSupportTicketsCustomerFeature);
|
|
7723
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.ICustomerToggleSubscriptionFeature, CustomerToggleSubscriptionFeat);
|
|
6281
7724
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IApproveSupportTicketFeature, ApproveSupportTicketFeat);
|
|
6282
7725
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IDeleteSupportTicketFeature, DeleteSupportTicketFeat);
|
|
7726
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IArchiveSupportTicketFeature, ArchiveSupportTicketFeat);
|
|
6283
7727
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IRejectSupportTicketFeature, RejectSupportTicketFeat);
|
|
6284
7728
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IRevertSupportTicketFeature, RevertSupportTicketFeat);
|
|
6285
7729
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.ICompleteSupportTicketFeature, CompleteSupportTicketFeat);
|
|
@@ -6291,6 +7735,11 @@ function registerSupportTicketDependencies() {
|
|
|
6291
7735
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IUpdateSupportTicketAdminFeature, UpdateSupportTicketAdminFeat);
|
|
6292
7736
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetSupportTicketAdminFeature, GetSupportTicketAdminFeature);
|
|
6293
7737
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetSupportTicketsAdminFeature, GetSupportTicketsAdminFeature);
|
|
7738
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetRequestorsForActiveSupportTicketsFeature, GetRequestorsForActiveSupportTicketsFeat);
|
|
7739
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IAddSupportTicketSubscriberFeature, AddSupportTicketSubscriberFeat);
|
|
7740
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IListSupportTicketSubscribersFeature, ListSupportTicketSubscribersFeat);
|
|
7741
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IRemoveSupportTicketSubscriberFeature, RemoveSupportTicketSubscriberFeat);
|
|
7742
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IFixSupportTicketUserIdsFeature, FixSupportTicketUserIdsFeature);
|
|
6294
7743
|
}
|
|
6295
7744
|
|
|
6296
7745
|
//#endregion
|
|
@@ -6571,14 +8020,34 @@ function mapTeamEntityToDto(entity) {
|
|
|
6571
8020
|
};
|
|
6572
8021
|
}
|
|
6573
8022
|
|
|
8023
|
+
//#endregion
|
|
8024
|
+
//#region src/slices/team/features/enrich_team.ts
|
|
8025
|
+
/**
|
|
8026
|
+
* Enriches a TeamReadDto with display names from a lookup map.
|
|
8027
|
+
*/
|
|
8028
|
+
function enrichTeam(dto, displayMap) {
|
|
8029
|
+
return {
|
|
8030
|
+
...dto,
|
|
8031
|
+
created_by_display_name: dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null,
|
|
8032
|
+
updated_by_display_name: dto.updated_by ? displayMap.get(dto.updated_by) ?? dto.updated_by : null
|
|
8033
|
+
};
|
|
8034
|
+
}
|
|
8035
|
+
/**
|
|
8036
|
+
* Collects user IDs from a team for display name lookup.
|
|
8037
|
+
*/
|
|
8038
|
+
function collectUserIdsFromTeam(team) {
|
|
8039
|
+
return [team.created_by, team.updated_by].filter((id) => Boolean(id));
|
|
8040
|
+
}
|
|
8041
|
+
|
|
6574
8042
|
//#endregion
|
|
6575
8043
|
//#region src/slices/team/features/create_team_feat.ts
|
|
6576
8044
|
let CreateTeamFeatureImpl = class CreateTeamFeatureImpl$1 {
|
|
6577
|
-
constructor(teamRepo, session, create_record_version, createTeamMemberFeature) {
|
|
8045
|
+
constructor(teamRepo, session, create_record_version, createTeamMemberFeature, userDisplayLookup) {
|
|
6578
8046
|
this.teamRepo = teamRepo;
|
|
6579
8047
|
this.session = session;
|
|
6580
8048
|
this.create_record_version = create_record_version;
|
|
6581
8049
|
this.createTeamMemberFeature = createTeamMemberFeature;
|
|
8050
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6582
8051
|
}
|
|
6583
8052
|
async execute(input) {
|
|
6584
8053
|
if (await this.teamRepo.findByUniqueName(input.unique_name || "")) throw new Error("Team with this name already exists");
|
|
@@ -6633,7 +8102,8 @@ let CreateTeamFeatureImpl = class CreateTeamFeatureImpl$1 {
|
|
|
6633
8102
|
website_address: null,
|
|
6634
8103
|
time_zone: null
|
|
6635
8104
|
});
|
|
6636
|
-
|
|
8105
|
+
const dto = mapTeamEntityToDto(teamEntity);
|
|
8106
|
+
return enrichTeam(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeam(dto)));
|
|
6637
8107
|
}
|
|
6638
8108
|
/**
|
|
6639
8109
|
* Compute path from unique name (e.g., "My Team" -> "my-team")
|
|
@@ -6647,7 +8117,8 @@ CreateTeamFeatureImpl = __decorate([
|
|
|
6647
8117
|
__decorateParam(0, inject(TEAM_TOKENS.ITeamRepository)),
|
|
6648
8118
|
__decorateParam(1, injectSession()),
|
|
6649
8119
|
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6650
|
-
__decorateParam(3, inject(TEAM_MEMBER_TOKENS.ICreateTeamMemberFeature))
|
|
8120
|
+
__decorateParam(3, inject(TEAM_MEMBER_TOKENS.ICreateTeamMemberFeature)),
|
|
8121
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6651
8122
|
], CreateTeamFeatureImpl);
|
|
6652
8123
|
|
|
6653
8124
|
//#endregion
|
|
@@ -6685,16 +8156,20 @@ DeleteTeamFeatureImpl = __decorate([
|
|
|
6685
8156
|
//#endregion
|
|
6686
8157
|
//#region src/slices/team/features/read_all_teams_feat.ts
|
|
6687
8158
|
let ReadAllTeamsFeatureImpl = class ReadAllTeamsFeatureImpl$1 {
|
|
6688
|
-
constructor(teamRepo, session, teamMemberRepo) {
|
|
8159
|
+
constructor(teamRepo, session, teamMemberRepo, userDisplayLookup) {
|
|
6689
8160
|
this.teamRepo = teamRepo;
|
|
6690
8161
|
this.session = session;
|
|
6691
8162
|
this.teamMemberRepo = teamMemberRepo;
|
|
8163
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6692
8164
|
}
|
|
6693
8165
|
async execute(filters) {
|
|
6694
8166
|
const processedFilters = await this.applyConsumerFiltering(filters);
|
|
6695
8167
|
const result = await this.teamRepo.read_all(processedFilters);
|
|
8168
|
+
const dtos = result.items.map((teamEntity) => mapTeamEntityToDto(teamEntity));
|
|
8169
|
+
const userIds = [...new Set(dtos.flatMap(collectUserIdsFromTeam))];
|
|
8170
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
6696
8171
|
return {
|
|
6697
|
-
items:
|
|
8172
|
+
items: dtos.map((t) => enrichTeam(t, displayMap)),
|
|
6698
8173
|
pageInfo: result.pageInfo
|
|
6699
8174
|
};
|
|
6700
8175
|
}
|
|
@@ -6721,30 +8196,38 @@ ReadAllTeamsFeatureImpl = __decorate([
|
|
|
6721
8196
|
injectable(),
|
|
6722
8197
|
__decorateParam(0, inject(TEAM_TOKENS.ITeamRepository)),
|
|
6723
8198
|
__decorateParam(1, injectSession()),
|
|
6724
|
-
__decorateParam(2, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo))
|
|
8199
|
+
__decorateParam(2, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo)),
|
|
8200
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6725
8201
|
], ReadAllTeamsFeatureImpl);
|
|
6726
8202
|
|
|
6727
8203
|
//#endregion
|
|
6728
8204
|
//#region src/slices/team/features/read_team_feat.ts
|
|
6729
8205
|
let ReadTeamFeatureImpl = class ReadTeamFeatureImpl$1 {
|
|
6730
|
-
constructor(teamRepo) {
|
|
8206
|
+
constructor(teamRepo, userDisplayLookup) {
|
|
6731
8207
|
this.teamRepo = teamRepo;
|
|
8208
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6732
8209
|
}
|
|
6733
8210
|
async execute(id) {
|
|
6734
8211
|
const teamEntity = await this.teamRepo.read(id);
|
|
6735
8212
|
if (!teamEntity) return null;
|
|
6736
|
-
|
|
8213
|
+
const dto = mapTeamEntityToDto(teamEntity);
|
|
8214
|
+
return enrichTeam(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeam(dto)));
|
|
6737
8215
|
}
|
|
6738
8216
|
};
|
|
6739
|
-
ReadTeamFeatureImpl = __decorate([
|
|
8217
|
+
ReadTeamFeatureImpl = __decorate([
|
|
8218
|
+
injectable(),
|
|
8219
|
+
__decorateParam(0, inject(TEAM_TOKENS.ITeamRepository)),
|
|
8220
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
8221
|
+
], ReadTeamFeatureImpl);
|
|
6740
8222
|
|
|
6741
8223
|
//#endregion
|
|
6742
8224
|
//#region src/slices/team/features/update_team_feat.ts
|
|
6743
8225
|
let UpdateTeamFeatureImpl = class UpdateTeamFeatureImpl$1 {
|
|
6744
|
-
constructor(teamRepo, session, create_record_version) {
|
|
8226
|
+
constructor(teamRepo, session, create_record_version, userDisplayLookup) {
|
|
6745
8227
|
this.teamRepo = teamRepo;
|
|
6746
8228
|
this.session = session;
|
|
6747
8229
|
this.create_record_version = create_record_version;
|
|
8230
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6748
8231
|
}
|
|
6749
8232
|
async execute(input) {
|
|
6750
8233
|
const existingTeam = await this.teamRepo.read(input.id);
|
|
@@ -6796,7 +8279,8 @@ let UpdateTeamFeatureImpl = class UpdateTeamFeatureImpl$1 {
|
|
|
6796
8279
|
auth_role: this.session.user.user_type,
|
|
6797
8280
|
auth_username: this.session.user.username
|
|
6798
8281
|
});
|
|
6799
|
-
|
|
8282
|
+
const dto = mapTeamEntityToDto(teamEntity);
|
|
8283
|
+
return enrichTeam(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeam(dto)));
|
|
6800
8284
|
}
|
|
6801
8285
|
/**
|
|
6802
8286
|
* Compute path from unique name (e.g., "My Team" -> "my-team")
|
|
@@ -6809,7 +8293,8 @@ UpdateTeamFeatureImpl = __decorate([
|
|
|
6809
8293
|
injectable(),
|
|
6810
8294
|
__decorateParam(0, inject(TEAM_TOKENS.ITeamRepository)),
|
|
6811
8295
|
__decorateParam(1, injectSession()),
|
|
6812
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
8296
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
8297
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6813
8298
|
], UpdateTeamFeatureImpl);
|
|
6814
8299
|
|
|
6815
8300
|
//#endregion
|
|
@@ -7027,13 +8512,33 @@ let TeamMemberRepo = class TeamMemberRepo$1 {
|
|
|
7027
8512
|
};
|
|
7028
8513
|
TeamMemberRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], TeamMemberRepo);
|
|
7029
8514
|
|
|
8515
|
+
//#endregion
|
|
8516
|
+
//#region src/slices/team_member/features/enrich_team_member.ts
|
|
8517
|
+
/**
|
|
8518
|
+
* Enriches a TeamMemberReadDto with display names from a lookup map.
|
|
8519
|
+
*/
|
|
8520
|
+
function enrichTeamMember(dto, displayMap) {
|
|
8521
|
+
return {
|
|
8522
|
+
...dto,
|
|
8523
|
+
created_by_display_name: dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null,
|
|
8524
|
+
updated_by_display_name: dto.updated_by ? displayMap.get(dto.updated_by) ?? dto.updated_by : null
|
|
8525
|
+
};
|
|
8526
|
+
}
|
|
8527
|
+
/**
|
|
8528
|
+
* Collects user IDs from a team member for display name lookup.
|
|
8529
|
+
*/
|
|
8530
|
+
function collectUserIdsFromTeamMember(member) {
|
|
8531
|
+
return [member.created_by, member.updated_by].filter((id) => Boolean(id));
|
|
8532
|
+
}
|
|
8533
|
+
|
|
7030
8534
|
//#endregion
|
|
7031
8535
|
//#region src/slices/team_member/features/create_team_member_feat.ts
|
|
7032
8536
|
let CreateTeamMemberFeat = class CreateTeamMemberFeat$1 {
|
|
7033
|
-
constructor(session, teamMemberRepo, create_record_version) {
|
|
8537
|
+
constructor(session, teamMemberRepo, create_record_version, userDisplayLookup) {
|
|
7034
8538
|
this.session = session;
|
|
7035
8539
|
this.teamMemberRepo = teamMemberRepo;
|
|
7036
8540
|
this.create_record_version = create_record_version;
|
|
8541
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
7037
8542
|
}
|
|
7038
8543
|
async execute(input) {
|
|
7039
8544
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7065,14 +8570,16 @@ let CreateTeamMemberFeat = class CreateTeamMemberFeat$1 {
|
|
|
7065
8570
|
auth_role: this.session.user.user_type,
|
|
7066
8571
|
auth_username: this.session.user.username
|
|
7067
8572
|
});
|
|
7068
|
-
|
|
8573
|
+
const dto = teamMemberCreated;
|
|
8574
|
+
return enrichTeamMember(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeamMember(dto)));
|
|
7069
8575
|
}
|
|
7070
8576
|
};
|
|
7071
8577
|
CreateTeamMemberFeat = __decorate([
|
|
7072
8578
|
injectable(),
|
|
7073
8579
|
__decorateParam(0, injectSession()),
|
|
7074
8580
|
__decorateParam(1, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo)),
|
|
7075
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
8581
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
8582
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
7076
8583
|
], CreateTeamMemberFeat);
|
|
7077
8584
|
|
|
7078
8585
|
//#endregion
|
|
@@ -7126,14 +8633,27 @@ DeleteTeamMemberFeat = __decorate([
|
|
|
7126
8633
|
//#endregion
|
|
7127
8634
|
//#region src/slices/team_member/features/get_team_members_feat.ts
|
|
7128
8635
|
let GetTeamMembersFeat = class GetTeamMembersFeat$1 {
|
|
7129
|
-
constructor(teamMemberRepo) {
|
|
8636
|
+
constructor(teamMemberRepo, userDisplayLookup) {
|
|
7130
8637
|
this.teamMemberRepo = teamMemberRepo;
|
|
8638
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
7131
8639
|
}
|
|
7132
8640
|
async execute(filters) {
|
|
7133
|
-
|
|
8641
|
+
const result = await this.teamMemberRepo.getAll(filters);
|
|
8642
|
+
const items = result.items;
|
|
8643
|
+
const userIds = [...new Set(items.flatMap(collectUserIdsFromTeamMember))];
|
|
8644
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
8645
|
+
const enrichedItems = items.map((m) => enrichTeamMember(m, displayMap));
|
|
8646
|
+
return {
|
|
8647
|
+
...result,
|
|
8648
|
+
items: enrichedItems
|
|
8649
|
+
};
|
|
7134
8650
|
}
|
|
7135
8651
|
};
|
|
7136
|
-
GetTeamMembersFeat = __decorate([
|
|
8652
|
+
GetTeamMembersFeat = __decorate([
|
|
8653
|
+
injectable(),
|
|
8654
|
+
__decorateParam(0, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo)),
|
|
8655
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
8656
|
+
], GetTeamMembersFeat);
|
|
7137
8657
|
|
|
7138
8658
|
//#endregion
|
|
7139
8659
|
//#region src/slices/team_member/features/get_user_team_members_feat.ts
|
|
@@ -7200,10 +8720,11 @@ GetUserTeamsFeat = __decorate([
|
|
|
7200
8720
|
//#endregion
|
|
7201
8721
|
//#region src/slices/team_member/features/update_team_member_feat.ts
|
|
7202
8722
|
let UpdateTeamMemberFeat = class UpdateTeamMemberFeat$1 {
|
|
7203
|
-
constructor(session, teamMemberRepo, create_record_version) {
|
|
8723
|
+
constructor(session, teamMemberRepo, create_record_version, userDisplayLookup) {
|
|
7204
8724
|
this.session = session;
|
|
7205
8725
|
this.teamMemberRepo = teamMemberRepo;
|
|
7206
8726
|
this.create_record_version = create_record_version;
|
|
8727
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
7207
8728
|
}
|
|
7208
8729
|
async execute(input) {
|
|
7209
8730
|
const existingTeamMember = await this.teamMemberRepo.getById(input.id);
|
|
@@ -7254,14 +8775,16 @@ let UpdateTeamMemberFeat = class UpdateTeamMemberFeat$1 {
|
|
|
7254
8775
|
auth_role: this.session.user.user_type,
|
|
7255
8776
|
auth_username: this.session.user.username
|
|
7256
8777
|
});
|
|
7257
|
-
|
|
8778
|
+
const dto = teamMemberUpdated;
|
|
8779
|
+
return enrichTeamMember(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeamMember(dto)));
|
|
7258
8780
|
}
|
|
7259
8781
|
};
|
|
7260
8782
|
UpdateTeamMemberFeat = __decorate([
|
|
7261
8783
|
injectable(),
|
|
7262
8784
|
__decorateParam(0, injectSession()),
|
|
7263
8785
|
__decorateParam(1, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo)),
|
|
7264
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
8786
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
8787
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
7265
8788
|
], UpdateTeamMemberFeat);
|
|
7266
8789
|
|
|
7267
8790
|
//#endregion
|
|
@@ -7325,6 +8848,21 @@ let UserRepo = class UserRepo$1 {
|
|
|
7325
8848
|
this.logger.perf(`UserRepo.read_user_by_email: ${queryTime.toFixed(2)}ms for email ${email}`);
|
|
7326
8849
|
return result[0];
|
|
7327
8850
|
}
|
|
8851
|
+
async read_users_by_emails(emails) {
|
|
8852
|
+
if (emails.length === 0) return [];
|
|
8853
|
+
const { ...rest } = getTableColumns(user_table);
|
|
8854
|
+
return this.router.queryAll((db) => db.select({ ...rest }).from(user_table).where(inArray(user_table.email, emails)));
|
|
8855
|
+
}
|
|
8856
|
+
/**
|
|
8857
|
+
* Case-insensitive email lookup. Use when matching against values that may
|
|
8858
|
+
* differ in casing (e.g. emails stored in other tables).
|
|
8859
|
+
*/
|
|
8860
|
+
async read_users_by_emails_case_insensitive(emails) {
|
|
8861
|
+
if (emails.length === 0) return [];
|
|
8862
|
+
const { ...rest } = getTableColumns(user_table);
|
|
8863
|
+
const lowerEmails = [...new Set(emails.map((e) => e.toLowerCase()))];
|
|
8864
|
+
return this.router.queryAll((db) => db.select({ ...rest }).from(user_table).where(sql`LOWER(${user_table.email}) IN (${sql.join(lowerEmails.map((e) => sql`${e}`), sql`, `)})`));
|
|
8865
|
+
}
|
|
7328
8866
|
async read_user_by_username(username) {
|
|
7329
8867
|
const { ...rest } = getTableColumns(user_table);
|
|
7330
8868
|
return (await this.router.queryAll((db) => db.select({ ...rest }).from(user_table).where(eq(user_table.username, username)).limit(1)))[0];
|
|
@@ -7434,6 +8972,26 @@ CreateUser = __decorate([
|
|
|
7434
8972
|
__decorateParam(3, inject(TOKENS.LOGGER))
|
|
7435
8973
|
], CreateUser);
|
|
7436
8974
|
|
|
8975
|
+
//#endregion
|
|
8976
|
+
//#region src/slices/user/features/user_display_lookup_feat.ts
|
|
8977
|
+
let UserDisplayLookupFeat = class UserDisplayLookupFeat$1 {
|
|
8978
|
+
constructor(userRepo) {
|
|
8979
|
+
this.userRepo = userRepo;
|
|
8980
|
+
}
|
|
8981
|
+
async lookupDisplayNames(userIds) {
|
|
8982
|
+
const validIds = [...new Set(userIds)].filter((id) => !!id?.trim()).filter((id) => id.length === 32);
|
|
8983
|
+
if (validIds.length === 0) return /* @__PURE__ */ new Map();
|
|
8984
|
+
const users = await this.userRepo.read_users_by_ids(validIds);
|
|
8985
|
+
const map = /* @__PURE__ */ new Map();
|
|
8986
|
+
for (const u of users) {
|
|
8987
|
+
const display = u.email ?? u.id;
|
|
8988
|
+
map.set(u.id, display);
|
|
8989
|
+
}
|
|
8990
|
+
return map;
|
|
8991
|
+
}
|
|
8992
|
+
};
|
|
8993
|
+
UserDisplayLookupFeat = __decorate([injectable(), __decorateParam(0, inject(USER_TOKENS.IUsersRepo))], UserDisplayLookupFeat);
|
|
8994
|
+
|
|
7437
8995
|
//#endregion
|
|
7438
8996
|
//#region src/slices/user/features/delete_user.ts
|
|
7439
8997
|
let DeleteUser = class DeleteUser$1 {
|
|
@@ -7511,6 +9069,18 @@ let GetUserFeat = class GetUserFeat$1 {
|
|
|
7511
9069
|
};
|
|
7512
9070
|
GetUserFeat = __decorate([injectable(), __decorateParam(0, inject(USER_TOKENS.IUsersRepo))], GetUserFeat);
|
|
7513
9071
|
|
|
9072
|
+
//#endregion
|
|
9073
|
+
//#region src/slices/user/features/get_triage_users_feat.ts
|
|
9074
|
+
let GetTriageUsersFeat = class GetTriageUsersFeat$1 {
|
|
9075
|
+
constructor(supportStaffRepo) {
|
|
9076
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
9077
|
+
}
|
|
9078
|
+
async execute() {
|
|
9079
|
+
return this.supportStaffRepo.readTriageUsers();
|
|
9080
|
+
}
|
|
9081
|
+
};
|
|
9082
|
+
GetTriageUsersFeat = __decorate([injectable(), __decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo))], GetTriageUsersFeat);
|
|
9083
|
+
|
|
7514
9084
|
//#endregion
|
|
7515
9085
|
//#region src/slices/user/features/get_users_for_selection_feat.ts
|
|
7516
9086
|
let GetUsersForSelectionFeat = class GetUsersForSelectionFeat$1 {
|
|
@@ -7518,7 +9088,7 @@ let GetUsersForSelectionFeat = class GetUsersForSelectionFeat$1 {
|
|
|
7518
9088
|
this.repo = repo;
|
|
7519
9089
|
}
|
|
7520
9090
|
async execute() {
|
|
7521
|
-
return (await this.repo.read_users_by_types([
|
|
9091
|
+
return (await this.repo.read_users_by_types([...USER_TYPES])).map((user) => ({
|
|
7522
9092
|
id: user.id,
|
|
7523
9093
|
email: user.email
|
|
7524
9094
|
}));
|
|
@@ -7662,6 +9232,7 @@ UpdateUserFeat = __decorate([injectable(), __decorateParam(0, inject(USER_TOKENS
|
|
|
7662
9232
|
//#region src/slices/user/user_container.ts
|
|
7663
9233
|
function registerUserContainer() {
|
|
7664
9234
|
container.registerSingleton(USER_TOKENS.IUsersRepo, UserRepo);
|
|
9235
|
+
container.registerSingleton(USER_TOKENS.IUserDisplayLookup, UserDisplayLookupFeat);
|
|
7665
9236
|
container.registerSingleton(USER_TOKENS.IReadAllUsers, ReadAllUsers);
|
|
7666
9237
|
container.registerSingleton(USER_TOKENS.ISignUpUser, SignUpUser);
|
|
7667
9238
|
container.registerSingleton(USER_TOKENS.IDeleteUser, DeleteUser);
|
|
@@ -7672,6 +9243,7 @@ function registerUserContainer() {
|
|
|
7672
9243
|
container.registerSingleton(USER_TOKENS.IGetUserFeature, GetUserFeat);
|
|
7673
9244
|
container.registerSingleton(USER_TOKENS.IUpdateUserFeature, UpdateUserFeat);
|
|
7674
9245
|
container.registerSingleton(USER_TOKENS.IGetUsersForSelectionFeature, GetUsersForSelectionFeat);
|
|
9246
|
+
container.registerSingleton(USER_TOKENS.IGetTriageUsersFeature, GetTriageUsersFeat);
|
|
7675
9247
|
}
|
|
7676
9248
|
|
|
7677
9249
|
//#endregion
|
|
@@ -7691,6 +9263,11 @@ let UserProfileRepo = class UserProfileRepo$1 {
|
|
|
7691
9263
|
const { ...rest } = getTableColumns(user_profile_table);
|
|
7692
9264
|
return (await this.router.queryAll((db) => db.select({ ...rest }).from(user_profile_table).where(eq(user_profile_table.user_id, user_id)).limit(1)))[0];
|
|
7693
9265
|
}
|
|
9266
|
+
async read_user_profiles_by_user_ids(user_ids) {
|
|
9267
|
+
if (user_ids.length === 0) return [];
|
|
9268
|
+
const { ...rest } = getTableColumns(user_profile_table);
|
|
9269
|
+
return this.router.queryAll((db) => db.select({ ...rest }).from(user_profile_table).where(inArray(user_profile_table.user_id, user_ids)));
|
|
9270
|
+
}
|
|
7694
9271
|
async update_user_profile(user_profileRecord) {
|
|
7695
9272
|
user_profileRecord.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
7696
9273
|
const { created_at, ...rest } = user_profileRecord;
|
|
@@ -7699,16 +9276,6 @@ let UserProfileRepo = class UserProfileRepo$1 {
|
|
|
7699
9276
|
};
|
|
7700
9277
|
UserProfileRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], UserProfileRepo);
|
|
7701
9278
|
|
|
7702
|
-
//#endregion
|
|
7703
|
-
//#region src/slices/user_profile/user_profile_interfaces.ts
|
|
7704
|
-
const USER_PROFILE_TOKENS = {
|
|
7705
|
-
IUserProfilesRepo: Symbol("IUserProfilesRepo"),
|
|
7706
|
-
IReadUserProfilesByUser: Symbol("IReadUserProfilesByUser"),
|
|
7707
|
-
IReadUserProfile: Symbol("IReadUserProfile"),
|
|
7708
|
-
ICreateUserProfile: Symbol("ICreateUserProfile"),
|
|
7709
|
-
IUpdateUserProfile: Symbol("IUpdateUserProfile")
|
|
7710
|
-
};
|
|
7711
|
-
|
|
7712
9279
|
//#endregion
|
|
7713
9280
|
//#region src/slices/user_profile/features/read_user_profile.ts
|
|
7714
9281
|
let ReadUserProfile = class ReadUserProfile$1 {
|
|
@@ -8271,14 +9838,17 @@ function registerCoreContainer() {
|
|
|
8271
9838
|
container.registerSingleton(DISPLAY_ID_PREFIX_TOKENS.PrefixRegistry, DisplayIdPrefixRegistry);
|
|
8272
9839
|
container.registerSingleton(DISPLAY_ID_PREFIX_TOKENS.IDisplayIdPrefixService, DisplayIdPrefixService);
|
|
8273
9840
|
registerPasswordResetContainer();
|
|
9841
|
+
registerRecordSubscriberDependencies();
|
|
8274
9842
|
registerRecordVersionContainer();
|
|
9843
|
+
registerSavedFilterContainer();
|
|
9844
|
+
registerSupportStaffDependencies();
|
|
8275
9845
|
registerUserContainer();
|
|
8276
9846
|
registerUserProfileContainer();
|
|
8277
9847
|
registerUserSessionContainer();
|
|
8278
9848
|
registerCustomerDependencies();
|
|
8279
9849
|
registerSupportTicketDependencies();
|
|
8280
9850
|
registerAttachmentContainer();
|
|
8281
|
-
registerAppSettingsContainer();
|
|
9851
|
+
/* @__PURE__ */ registerAppSettingsContainer();
|
|
8282
9852
|
registerNoteContainer();
|
|
8283
9853
|
registerTeamDependencies();
|
|
8284
9854
|
registerTeamMemberContainer();
|
|
@@ -8315,37 +9885,15 @@ function createRequestContainer(config, additionalRegistrations) {
|
|
|
8315
9885
|
//#endregion
|
|
8316
9886
|
//#region src/slices/app_settings/app-settings-api.server.ts
|
|
8317
9887
|
var AppSettingsApiServer = class extends RpcTarget {
|
|
8318
|
-
constructor(
|
|
9888
|
+
constructor(_container) {
|
|
8319
9889
|
super();
|
|
8320
|
-
this.container = container$1;
|
|
8321
|
-
}
|
|
8322
|
-
async getNotificationEmails() {
|
|
8323
|
-
return rpcMethodPartial({
|
|
8324
|
-
auth: "admin",
|
|
8325
|
-
container: this.container,
|
|
8326
|
-
context: "AppSettingsApiServer.getNotificationEmails",
|
|
8327
|
-
input: void 0,
|
|
8328
|
-
outputSchema: ReadNotificationEmailsSchema,
|
|
8329
|
-
execute: async (_, container$1) => {
|
|
8330
|
-
return { emails: (await container$1.resolve(APP_SETTINGS_TOKENS.IGetNotificationEmailsFeature).execute()).emails.map((e) => ({ email: e.email })) };
|
|
8331
|
-
}
|
|
8332
|
-
});
|
|
8333
|
-
}
|
|
8334
|
-
async updateNotificationEmails(input) {
|
|
8335
|
-
return rpcMethod({
|
|
8336
|
-
auth: "admin",
|
|
8337
|
-
container: this.container,
|
|
8338
|
-
context: "AppSettingsApiServer.updateNotificationEmails",
|
|
8339
|
-
input,
|
|
8340
|
-
inputSchema: UpdateNotificationEmailsSchema,
|
|
8341
|
-
outputSchema: UpdateNotificationEmailsSchema,
|
|
8342
|
-
execute: async (input$1, container$1) => {
|
|
8343
|
-
return await container$1.resolve(APP_SETTINGS_TOKENS.IUpdateNotificationEmailsFeature).execute(input$1);
|
|
8344
|
-
}
|
|
8345
|
-
});
|
|
8346
9890
|
}
|
|
8347
9891
|
};
|
|
8348
9892
|
|
|
9893
|
+
//#endregion
|
|
9894
|
+
//#region src/slices/app_settings/app_settings_tokens.ts
|
|
9895
|
+
const APP_SETTINGS_TOKENS = {};
|
|
9896
|
+
|
|
8349
9897
|
//#endregion
|
|
8350
9898
|
//#region src/slices/attachment/attachment-api.server.ts
|
|
8351
9899
|
const CreateFolderInputSchema = z.object({
|
|
@@ -8669,12 +10217,211 @@ var RecordVersionApiServer = class extends RpcTarget {
|
|
|
8669
10217
|
}
|
|
8670
10218
|
});
|
|
8671
10219
|
}
|
|
10220
|
+
async listTrackerActivityVersions(trackerId, filters) {
|
|
10221
|
+
return rpcMethod({
|
|
10222
|
+
auth: "protected",
|
|
10223
|
+
container: this.container,
|
|
10224
|
+
context: "RecordVersionApiServer.listTrackerActivityVersions",
|
|
10225
|
+
input: {
|
|
10226
|
+
tracker_id: trackerId,
|
|
10227
|
+
filters
|
|
10228
|
+
},
|
|
10229
|
+
inputSchema: recordVersionTrackerActivityInputSchema,
|
|
10230
|
+
outputSchema: recordVersionPageBreadcrumbSchema,
|
|
10231
|
+
execute: async ({ tracker_id, filters: f }, container$1) => {
|
|
10232
|
+
return await container$1.resolve(RECORD_VERSION_TOKENS.IReadTrackerActivityVersions).execute(tracker_id, f || {});
|
|
10233
|
+
}
|
|
10234
|
+
});
|
|
10235
|
+
}
|
|
10236
|
+
};
|
|
10237
|
+
|
|
10238
|
+
//#endregion
|
|
10239
|
+
//#region src/slices/support_staff/support-staff-api.server.ts
|
|
10240
|
+
const SupportStaffMemberSchema = z.object({
|
|
10241
|
+
id: z.string(),
|
|
10242
|
+
user_id: z.string(),
|
|
10243
|
+
email: z.string(),
|
|
10244
|
+
created_at: z.string()
|
|
10245
|
+
});
|
|
10246
|
+
var SupportStaffApiServer = class extends RpcTarget {
|
|
10247
|
+
constructor(container$1) {
|
|
10248
|
+
super();
|
|
10249
|
+
this.container = container$1;
|
|
10250
|
+
}
|
|
10251
|
+
async listSupportStaff() {
|
|
10252
|
+
return rpcMethodPartial({
|
|
10253
|
+
auth: "admin",
|
|
10254
|
+
container: this.container,
|
|
10255
|
+
context: "SupportStaffApiServer.listSupportStaff",
|
|
10256
|
+
input: void 0,
|
|
10257
|
+
outputSchema: z.array(SupportStaffMemberSchema),
|
|
10258
|
+
execute: async (_, container$1) => {
|
|
10259
|
+
return container$1.resolve(SUPPORT_STAFF_TOKENS.IListSupportStaffFeature).execute();
|
|
10260
|
+
}
|
|
10261
|
+
});
|
|
10262
|
+
}
|
|
10263
|
+
async addSupportStaff(userId) {
|
|
10264
|
+
return rpcMethod({
|
|
10265
|
+
auth: "admin",
|
|
10266
|
+
container: this.container,
|
|
10267
|
+
context: "SupportStaffApiServer.addSupportStaff",
|
|
10268
|
+
input: userId,
|
|
10269
|
+
inputSchema: z.string(),
|
|
10270
|
+
outputSchema: SupportStaffMemberSchema,
|
|
10271
|
+
execute: async (userId$1, container$1) => {
|
|
10272
|
+
return container$1.resolve(SUPPORT_STAFF_TOKENS.IAddSupportStaffFeature).execute(userId$1);
|
|
10273
|
+
}
|
|
10274
|
+
});
|
|
10275
|
+
}
|
|
10276
|
+
async removeSupportStaff(userId) {
|
|
10277
|
+
return rpcMethod({
|
|
10278
|
+
auth: "admin",
|
|
10279
|
+
container: this.container,
|
|
10280
|
+
context: "SupportStaffApiServer.removeSupportStaff",
|
|
10281
|
+
input: userId,
|
|
10282
|
+
inputSchema: z.string(),
|
|
10283
|
+
outputSchema: z.void(),
|
|
10284
|
+
execute: async (userId$1, container$1) => {
|
|
10285
|
+
await container$1.resolve(SUPPORT_STAFF_TOKENS.IRemoveSupportStaffFeature).execute(userId$1);
|
|
10286
|
+
}
|
|
10287
|
+
});
|
|
10288
|
+
}
|
|
10289
|
+
};
|
|
10290
|
+
|
|
10291
|
+
//#endregion
|
|
10292
|
+
//#region src/slices/saved_filter/saved-filter-api.server.ts
|
|
10293
|
+
var SavedFilterApiServer = class extends RpcTarget {
|
|
10294
|
+
constructor(container$1) {
|
|
10295
|
+
super();
|
|
10296
|
+
this.container = container$1;
|
|
10297
|
+
}
|
|
10298
|
+
async listSavedFilters(context) {
|
|
10299
|
+
return rpcMethod({
|
|
10300
|
+
auth: "protected",
|
|
10301
|
+
container: this.container,
|
|
10302
|
+
context: "SavedFilterApiServer.listSavedFilters",
|
|
10303
|
+
input: context,
|
|
10304
|
+
inputSchema: z.string(),
|
|
10305
|
+
outputSchema: z.array(SavedFilterReadSchema),
|
|
10306
|
+
execute: async (context$1, container$1) => {
|
|
10307
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.IListSavedFilters).execute(context$1);
|
|
10308
|
+
}
|
|
10309
|
+
});
|
|
10310
|
+
}
|
|
10311
|
+
async listAllSavedFilters() {
|
|
10312
|
+
return rpcMethod({
|
|
10313
|
+
auth: "protected",
|
|
10314
|
+
container: this.container,
|
|
10315
|
+
context: "SavedFilterApiServer.listAllSavedFilters",
|
|
10316
|
+
input: void 0,
|
|
10317
|
+
inputSchema: z.undefined(),
|
|
10318
|
+
outputSchema: z.array(SavedFilterReadSchema),
|
|
10319
|
+
execute: async (_input, container$1) => {
|
|
10320
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.IListAllSavedFilters).execute();
|
|
10321
|
+
}
|
|
10322
|
+
});
|
|
10323
|
+
}
|
|
10324
|
+
async createSavedFilter(input) {
|
|
10325
|
+
return rpcMethod({
|
|
10326
|
+
auth: "protected",
|
|
10327
|
+
container: this.container,
|
|
10328
|
+
context: "SavedFilterApiServer.createSavedFilter",
|
|
10329
|
+
input,
|
|
10330
|
+
inputSchema: SavedFilterCreateSchema,
|
|
10331
|
+
outputSchema: SavedFilterReadSchema,
|
|
10332
|
+
execute: async (input$1, container$1) => {
|
|
10333
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.ICreateSavedFilter).execute(input$1);
|
|
10334
|
+
}
|
|
10335
|
+
});
|
|
10336
|
+
}
|
|
10337
|
+
async updateSavedFilter(input) {
|
|
10338
|
+
return rpcMethod({
|
|
10339
|
+
auth: "protected",
|
|
10340
|
+
container: this.container,
|
|
10341
|
+
context: "SavedFilterApiServer.updateSavedFilter",
|
|
10342
|
+
input,
|
|
10343
|
+
inputSchema: SavedFilterUpdateSchema,
|
|
10344
|
+
outputSchema: SavedFilterReadSchema.nullable(),
|
|
10345
|
+
execute: async (input$1, container$1) => {
|
|
10346
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.IUpdateSavedFilter).execute(input$1);
|
|
10347
|
+
}
|
|
10348
|
+
});
|
|
10349
|
+
}
|
|
10350
|
+
async deleteSavedFilter(id) {
|
|
10351
|
+
return rpcMethod({
|
|
10352
|
+
auth: "protected",
|
|
10353
|
+
container: this.container,
|
|
10354
|
+
context: "SavedFilterApiServer.deleteSavedFilter",
|
|
10355
|
+
input: id,
|
|
10356
|
+
inputSchema: z.string(),
|
|
10357
|
+
outputSchema: z.boolean(),
|
|
10358
|
+
execute: async (id$1, container$1) => {
|
|
10359
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.IDeleteSavedFilter).execute(id$1);
|
|
10360
|
+
}
|
|
10361
|
+
});
|
|
10362
|
+
}
|
|
10363
|
+
async listPinnedPresets() {
|
|
10364
|
+
return rpcMethod({
|
|
10365
|
+
auth: "protected",
|
|
10366
|
+
container: this.container,
|
|
10367
|
+
context: "SavedFilterApiServer.listPinnedPresets",
|
|
10368
|
+
input: void 0,
|
|
10369
|
+
inputSchema: z.undefined(),
|
|
10370
|
+
outputSchema: z.array(SavedFilterReadSchema),
|
|
10371
|
+
execute: async (_input, container$1) => {
|
|
10372
|
+
return await container$1.resolve(USER_PINNED_PRESET_TOKENS.IListPinnedPresets).execute();
|
|
10373
|
+
}
|
|
10374
|
+
});
|
|
10375
|
+
}
|
|
10376
|
+
async addPinnedPreset(presetId) {
|
|
10377
|
+
return rpcMethod({
|
|
10378
|
+
auth: "protected",
|
|
10379
|
+
container: this.container,
|
|
10380
|
+
context: "SavedFilterApiServer.addPinnedPreset",
|
|
10381
|
+
input: presetId,
|
|
10382
|
+
inputSchema: z.string(),
|
|
10383
|
+
outputSchema: SavedFilterReadSchema.nullable(),
|
|
10384
|
+
execute: async (presetId$1, container$1) => {
|
|
10385
|
+
return await container$1.resolve(USER_PINNED_PRESET_TOKENS.IAddPinnedPreset).execute(presetId$1);
|
|
10386
|
+
}
|
|
10387
|
+
});
|
|
10388
|
+
}
|
|
10389
|
+
async removePinnedPreset(presetId) {
|
|
10390
|
+
return rpcMethod({
|
|
10391
|
+
auth: "protected",
|
|
10392
|
+
container: this.container,
|
|
10393
|
+
context: "SavedFilterApiServer.removePinnedPreset",
|
|
10394
|
+
input: presetId,
|
|
10395
|
+
inputSchema: z.string(),
|
|
10396
|
+
outputSchema: z.boolean(),
|
|
10397
|
+
execute: async (presetId$1, container$1) => {
|
|
10398
|
+
return await container$1.resolve(USER_PINNED_PRESET_TOKENS.IRemovePinnedPreset).execute(presetId$1);
|
|
10399
|
+
}
|
|
10400
|
+
});
|
|
10401
|
+
}
|
|
10402
|
+
async reorderPinnedPresets(presetIds) {
|
|
10403
|
+
return rpcMethod({
|
|
10404
|
+
auth: "protected",
|
|
10405
|
+
container: this.container,
|
|
10406
|
+
context: "SavedFilterApiServer.reorderPinnedPresets",
|
|
10407
|
+
input: presetIds,
|
|
10408
|
+
inputSchema: z.array(z.string()),
|
|
10409
|
+
outputSchema: z.void(),
|
|
10410
|
+
execute: async (presetIds$1, container$1) => {
|
|
10411
|
+
return await container$1.resolve(USER_PINNED_PRESET_TOKENS.IReorderPinnedPresets).execute(presetIds$1);
|
|
10412
|
+
}
|
|
10413
|
+
});
|
|
10414
|
+
}
|
|
8672
10415
|
};
|
|
8673
10416
|
|
|
8674
10417
|
//#endregion
|
|
8675
10418
|
//#region src/slices/support_ticket/support-ticket-api.server.ts
|
|
8676
10419
|
const CancelInternalTaskInputSchema = z.object({ id: z.string() });
|
|
8677
10420
|
const ReactivateInternalTaskInputSchema = z.object({ id: z.string() });
|
|
10421
|
+
const UsersForSelectionSchema$1 = z.array(z.object({
|
|
10422
|
+
id: z.string(),
|
|
10423
|
+
email: z.string()
|
|
10424
|
+
}));
|
|
8678
10425
|
var SupportTicketApiServer = class extends RpcTarget {
|
|
8679
10426
|
constructor(container$1) {
|
|
8680
10427
|
super();
|
|
@@ -8732,6 +10479,31 @@ var SupportTicketApiServer = class extends RpcTarget {
|
|
|
8732
10479
|
}
|
|
8733
10480
|
});
|
|
8734
10481
|
}
|
|
10482
|
+
async toggleSubscription(supportTicketId) {
|
|
10483
|
+
return rpcMethod({
|
|
10484
|
+
auth: "protected",
|
|
10485
|
+
container: this.container,
|
|
10486
|
+
context: "SupportTicketApiServer.toggleSubscription",
|
|
10487
|
+
input: supportTicketId,
|
|
10488
|
+
inputSchema: z.string(),
|
|
10489
|
+
outputSchema: z.object({ subscribed: z.boolean() }),
|
|
10490
|
+
execute: async (id, container$1) => {
|
|
10491
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.ICustomerToggleSubscriptionFeature).execute(id);
|
|
10492
|
+
}
|
|
10493
|
+
});
|
|
10494
|
+
}
|
|
10495
|
+
async getRequestorsForActiveTickets() {
|
|
10496
|
+
return rpcMethodPartial({
|
|
10497
|
+
auth: "protected",
|
|
10498
|
+
container: this.container,
|
|
10499
|
+
context: "SupportTicketApiServer.getRequestorsForActiveTickets",
|
|
10500
|
+
input: void 0,
|
|
10501
|
+
outputSchema: UsersForSelectionSchema$1,
|
|
10502
|
+
execute: async (_, container$1) => {
|
|
10503
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IGetRequestorsForActiveSupportTicketsFeature).execute({ excludeInternal: true });
|
|
10504
|
+
}
|
|
10505
|
+
});
|
|
10506
|
+
}
|
|
8735
10507
|
async staffCreateTicket(input) {
|
|
8736
10508
|
return rpcMethod({
|
|
8737
10509
|
auth: "admin",
|
|
@@ -8784,6 +10556,18 @@ var SupportTicketApiServer = class extends RpcTarget {
|
|
|
8784
10556
|
}
|
|
8785
10557
|
});
|
|
8786
10558
|
}
|
|
10559
|
+
async staffGetRequestorsForActiveTickets() {
|
|
10560
|
+
return rpcMethodPartial({
|
|
10561
|
+
auth: "staff",
|
|
10562
|
+
container: this.container,
|
|
10563
|
+
context: "SupportTicketApiServer.staffGetRequestorsForActiveTickets",
|
|
10564
|
+
input: void 0,
|
|
10565
|
+
outputSchema: UsersForSelectionSchema$1,
|
|
10566
|
+
execute: async (_, container$1) => {
|
|
10567
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IGetRequestorsForActiveSupportTicketsFeature).execute({ excludeInternal: false });
|
|
10568
|
+
}
|
|
10569
|
+
});
|
|
10570
|
+
}
|
|
8787
10571
|
async approveTicket(input) {
|
|
8788
10572
|
return rpcMethod({
|
|
8789
10573
|
auth: "admin",
|
|
@@ -8901,6 +10685,78 @@ var SupportTicketApiServer = class extends RpcTarget {
|
|
|
8901
10685
|
}
|
|
8902
10686
|
});
|
|
8903
10687
|
}
|
|
10688
|
+
async archiveTicket(input) {
|
|
10689
|
+
return rpcMethod({
|
|
10690
|
+
auth: "admin",
|
|
10691
|
+
container: this.container,
|
|
10692
|
+
context: "SupportTicketApiServer.archiveTicket",
|
|
10693
|
+
input,
|
|
10694
|
+
inputSchema: ArchiveSupportTicketSchema,
|
|
10695
|
+
outputSchema: StaffSupportTicketReadSchema,
|
|
10696
|
+
execute: async (input$1, container$1) => {
|
|
10697
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IArchiveSupportTicketFeature).execute(input$1.id);
|
|
10698
|
+
}
|
|
10699
|
+
});
|
|
10700
|
+
}
|
|
10701
|
+
async staffListSubscribers(supportTicketId) {
|
|
10702
|
+
return rpcMethod({
|
|
10703
|
+
auth: "admin",
|
|
10704
|
+
container: this.container,
|
|
10705
|
+
context: "SupportTicketApiServer.staffListSubscribers",
|
|
10706
|
+
input: supportTicketId,
|
|
10707
|
+
inputSchema: z.string(),
|
|
10708
|
+
outputSchema: z.array(RecordSubscriberReadSchema),
|
|
10709
|
+
execute: async (id, container$1) => {
|
|
10710
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IListSupportTicketSubscribersFeature).execute(id);
|
|
10711
|
+
}
|
|
10712
|
+
});
|
|
10713
|
+
}
|
|
10714
|
+
async staffAddSubscriber(input) {
|
|
10715
|
+
return rpcMethod({
|
|
10716
|
+
auth: "admin",
|
|
10717
|
+
container: this.container,
|
|
10718
|
+
context: "SupportTicketApiServer.staffAddSubscriber",
|
|
10719
|
+
input,
|
|
10720
|
+
inputSchema: SupportTicketSubscriberCreateSchema,
|
|
10721
|
+
outputSchema: RecordSubscriberReadSchema,
|
|
10722
|
+
execute: async (validated, container$1) => {
|
|
10723
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IAddSupportTicketSubscriberFeature).execute(validated);
|
|
10724
|
+
}
|
|
10725
|
+
});
|
|
10726
|
+
}
|
|
10727
|
+
async staffRemoveSubscriber(input) {
|
|
10728
|
+
return rpcMethod({
|
|
10729
|
+
auth: "admin",
|
|
10730
|
+
container: this.container,
|
|
10731
|
+
context: "SupportTicketApiServer.staffRemoveSubscriber",
|
|
10732
|
+
input,
|
|
10733
|
+
inputSchema: z.object({
|
|
10734
|
+
supportTicketId: z.string(),
|
|
10735
|
+
subscriberId: z.string()
|
|
10736
|
+
}),
|
|
10737
|
+
outputSchema: z.void(),
|
|
10738
|
+
execute: async (validated, container$1) => {
|
|
10739
|
+
await container$1.resolve(SUPPORT_TICKET_TOKENS.IRemoveSupportTicketSubscriberFeature).execute(validated.supportTicketId, validated.subscriberId);
|
|
10740
|
+
}
|
|
10741
|
+
});
|
|
10742
|
+
}
|
|
10743
|
+
async staffFixSupportTicketUserIds() {
|
|
10744
|
+
return rpcMethod({
|
|
10745
|
+
auth: "admin",
|
|
10746
|
+
container: this.container,
|
|
10747
|
+
context: "SupportTicketApiServer.staffFixSupportTicketUserIds",
|
|
10748
|
+
input: void 0,
|
|
10749
|
+
inputSchema: z.undefined(),
|
|
10750
|
+
outputSchema: z.object({
|
|
10751
|
+
ticketsScanned: z.number(),
|
|
10752
|
+
ticketsFixed: z.number(),
|
|
10753
|
+
ticketsSkipped: z.number()
|
|
10754
|
+
}),
|
|
10755
|
+
execute: async (_input, container$1) => {
|
|
10756
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IFixSupportTicketUserIdsFeature).execute();
|
|
10757
|
+
}
|
|
10758
|
+
});
|
|
10759
|
+
}
|
|
8904
10760
|
};
|
|
8905
10761
|
|
|
8906
10762
|
//#endregion
|
|
@@ -9189,6 +11045,18 @@ var UserApiServer = class extends RpcTarget {
|
|
|
9189
11045
|
}
|
|
9190
11046
|
});
|
|
9191
11047
|
}
|
|
11048
|
+
async getTriageUsers() {
|
|
11049
|
+
return rpcMethodPartial({
|
|
11050
|
+
auth: "admin",
|
|
11051
|
+
container: this.container,
|
|
11052
|
+
context: "UserApiServer.getTriageUsers",
|
|
11053
|
+
input: void 0,
|
|
11054
|
+
outputSchema: UsersForSelectionSchema,
|
|
11055
|
+
execute: async (_, container$1) => {
|
|
11056
|
+
return await container$1.resolve(USER_TOKENS.IGetTriageUsersFeature).execute();
|
|
11057
|
+
}
|
|
11058
|
+
});
|
|
11059
|
+
}
|
|
9192
11060
|
};
|
|
9193
11061
|
|
|
9194
11062
|
//#endregion
|
|
@@ -9380,5 +11248,5 @@ var UserSessionApiServer = class extends RpcTarget {
|
|
|
9380
11248
|
};
|
|
9381
11249
|
|
|
9382
11250
|
//#endregion
|
|
9383
|
-
export { APP_SETTINGS_TOKENS, ATTACHMENT_FOLDER_TOKENS, ATTACHMENT_TOKENS, AalLevel, AddCreditsFeat, AppSettingsApiServer, AttachmentApiServer, AttachmentFolderRepo, AttachmentRepo, AuthErrorCode, AuthenticationError, BreadcrumbUtils, BusinessError, CREDIT_SERVICE_TOKEN, CREDIT_TRANSACTION_REPO_TOKEN, CUSTOMER_TOKENS, ChangeUserPassword, CodeChallengeMethod, CookieService, CreateTeamFeatureImpl, CreateTeamMemberFeat, CreditService, CreditTransactionRepo, CursorUtils, CustomerApiServer, DISPLAY_ID_PREFIX_TOKENS, DatabaseRouter, DeleteTeamFeatureImpl, DeleteTeamMemberFeat, DirectDatabaseAdapter, DisplayIdPrefixRegistry, DisplayIdPrefixService, EXTENSION_INTERVAL_MS, EmailService, FactorStatus, FactorType, ForgotPassword, GetCreditTransactionsFeat, GetTeamMembersFeat, GetUserTeamMembersFeat, GetUserTeamsFeat, ID_ERRORS, INACTIVITY_TIMEOUT_MS, IdComponentType, InternalServerError, InvalidCredentialsError, InvalidRefreshTokenError, JWT_TOKENS, KeyStatus, KeyType, LOG_LEVEL, Logger, LoginUserSession, MAX_SESSION_LIFETIME_MS, NOTE_TOKENS, NoteApiServer, NoteRepo, OperationConst, PASSWORD_RESET_TOKENS, PaginationUtils, PasswordResetApiServer, PasswordService, PricingPlanInterval, PricingType, RECORD_VERSION_TOKENS, RECORD_VERSION_VALIDATION_TOKENS, ReadAllTeamsFeatureImpl, ReadAllUserSessions, RecordAccessValidatorRegistry, RecordConst, RecordVersionApiServer, RefreshTokenRepo, RefreshTokenSession, RequestStatus, ResetMonthlyBalanceFeat, ResetPassword, RevokeRefreshToken, SUPPORT_TICKET_TOKENS, SessionNotFoundError, SetMonthlyAllocationFeat, SubscriptionStatus, SupportTicketApiServer, SupportTicketRepo, TEAM_MEMBER_TOKENS, TEAM_TOKENS, TOKENS, TeamApiServer, TeamMemberApiServer, TeamMemberRepo, TeamRepositoryImpl, TenantContext, TokenFamilyReusedError, TransactionTypeEnum, USER_PROFILE_TOKENS, USER_SESSION_TOKENS, USER_TOKENS, UniversalIdGenerator, UpdateTeamFeatureImpl, UpdateTeamMemberFeat, UserApiServer, UserNotFoundError, UserProfileApiServer, UserRepo, UserSessionApiServer, ValidationError, applyCookiesToResponse, applyFilter, archiveConditions, attachment_folder_table, attachment_table, buildContainerFactories, checkAuth, combineConditions, createAuthenticatedState, createBackendRegistry, createContainerSetupMiddleware, createDefaultContainerFactories, createDefaultContainerSetupConfig, createDefaultSessionValidationConfig, createExpiredState, createFilterBuilder, createHonoErrorFactory, createIsAuthenticatedMiddleware, createLoggerHelpers, createRequestContainer, createRevokedState, createUnauthenticatedState, credit_transaction_table, customNanoid, custom_long_nanoid, deriveColumnMap, findR2Bucket, formatCreditValue, generateAccessToken, generatePasswordResetToken, generateRefreshToken, generateUserDetailsToken, getAuthenticatedSession, getChangedProperties, getR2BucketBindingName, get_aliased_table_columns, injectSession, isCreditValueEmpty, logger, mfa_secret_table, note_table, oauth_provider_table, record_version_table, refresh_token_table, registerAppSettingsContainer, registerAttachmentContainer, registerAuthenticatedSession, registerCoreContainer, registerCustomerDependencies, registerDisplayIdPrefixesFromMap, registerNoteContainer, registerPasswordResetContainer, registerRecordVersionContainer, registerSupportTicketDependencies, registerTeamDependencies, registerTeamMemberContainer, registerUserContainer, registerUserProfileContainer, registerUserSessionContainer, requireAdmin, requireAuthenticated, requireLeadOrStaff, requireStaff, resolveTeamAccess, rpcMethod, rpcMethodPartial, rpcMethodSimple, searchOrCondition, support_ticket_table, teamCondition, team_member_table, team_table, user_oauth_account_table, user_profile_table, user_table, validateInput, validateOutput, validateSessionFromJWT, verifyToken, withErrorHandling };
|
|
11251
|
+
export { APP_SETTINGS_TOKENS, ATTACHMENT_FOLDER_TOKENS, ATTACHMENT_TOKENS, AalLevel, AddCreditsFeat, AppSettingKey, AppSettingsApiServer, AppSettingsRepo, AttachmentApiServer, AttachmentFolderRepo, AttachmentRepo, AuthErrorCode, AuthenticationError, BreadcrumbUtils, BusinessError, CREDIT_SERVICE_TOKEN, CREDIT_TRANSACTION_REPO_TOKEN, CUSTOMER_TOKENS, ChangeUserPassword, CodeChallengeMethod, CookieService, CreateTeamFeatureImpl, CreateTeamMemberFeat, CreditService, CreditTransactionRepo, CursorUtils, CustomerApiServer, DISPLAY_ID_PREFIX_TOKENS, DatabaseRouter, DeleteTeamFeatureImpl, DeleteTeamMemberFeat, DirectDatabaseAdapter, DisplayIdPrefixRegistry, DisplayIdPrefixService, EXTENSION_INTERVAL_MS, EmailService, FactorStatus, FactorType, ForgotPassword, GetCreditTransactionsFeat, GetTeamMembersFeat, GetUserTeamMembersFeat, GetUserTeamsFeat, ID_ERRORS, INACTIVITY_TIMEOUT_MS, IdComponentType, InternalServerError, InvalidCredentialsError, InvalidRefreshTokenError, JWT_TOKENS, KeyStatus, KeyType, LOG_LEVEL, Logger, LoginUserSession, MAX_PINNED_PRESETS, MAX_SESSION_LIFETIME_MS, NOTE_TOKENS, NoteApiServer, NoteRepo, OperationConst, PASSWORD_RESET_TOKENS, PaginationUtils, PasswordResetApiServer, PasswordService, PricingPlanInterval, PricingType, RECORD_SUBSCRIBER_TOKENS, RECORD_VERSION_TOKENS, RECORD_VERSION_VALIDATION_TOKENS, ReadAllTeamsFeatureImpl, ReadAllUserSessions, RecordAccessValidatorRegistry, RecordConst, RecordSubscriberRepo, RecordVersionApiServer, RefreshTokenRepo, RefreshTokenSession, RequestStatus, ResetMonthlyBalanceFeat, ResetPassword, RevokeRefreshToken, SAVED_FILTER_TOKENS, SUPPORT_STAFF_TOKENS, SUPPORT_TICKET_TOKENS, SavedFilterApiServer, SavedFilterRepository, SessionNotFoundError, SetMonthlyAllocationFeat, SubscriptionStatus, SupportStaffApiServer, SupportStaffRepo, SupportTicketApiServer, SupportTicketRepo, TEAM_MEMBER_TOKENS, TEAM_TOKENS, TOKENS, TeamApiServer, TeamMemberApiServer, TeamMemberRepo, TeamRepositoryImpl, TenantContext, TokenFamilyReusedError, TransactionTypeEnum, USER_PROFILE_TOKENS, USER_SESSION_TOKENS, USER_TOKENS, UniversalIdGenerator, UpdateTeamFeatureImpl, UpdateTeamMemberFeat, UserApiServer, UserDisplayLookupFeat, UserNotFoundError, UserProfileApiServer, UserRepo, UserSessionApiServer, ValidationError, app_settings_table, applyCookiesToResponse, applyFilter, archiveConditions, attachment_folder_table, attachment_table, buildContainerFactories, checkAuth, combineConditions, createAuthenticatedState, createBackendRegistry, createContainerSetupMiddleware, createDefaultContainerFactories, createDefaultContainerSetupConfig, createDefaultSessionValidationConfig, createExpiredState, createFilterBuilder, createHonoErrorFactory, createIsAuthenticatedMiddleware, createLoggerHelpers, createRequestContainer, createRevokedState, createUnauthenticatedState, credit_transaction_table, customNanoid, custom_long_nanoid, deriveColumnMap, findR2Bucket, formatCreditValue, generateAccessToken, generatePasswordResetToken, generateRefreshToken, generateUserDetailsToken, getAuthenticatedSession, getChangedProperties, getR2BucketBindingName, get_aliased_table_columns, injectSession, isCreditValueEmpty, logger, mfa_secret_table, note_table, oauth_provider_table, record_subscriber_table, record_version_table, refresh_token_table, registerAppSettingsContainer, registerAttachmentContainer, registerAuthenticatedSession, registerCoreContainer, registerCustomerDependencies, registerDisplayIdPrefixesFromMap, registerNoteContainer, registerPasswordResetContainer, registerRecordSubscriberDependencies, registerRecordVersionContainer, registerSupportTicketDependencies, registerTeamDependencies, registerTeamMemberContainer, registerUserContainer, registerUserProfileContainer, registerUserSessionContainer, requireAdmin, requireAuthenticated, requireLeadOrStaff, requireStaff, resolveTeamAccess, rpcMethod, rpcMethodPartial, rpcMethodSimple, saved_filter_table, searchOrCondition, support_staff_table, support_ticket_table, teamCondition, team_member_table, team_table, user_oauth_account_table, user_pinned_preset_table, user_profile_table, user_table, validateInput, validateOutput, validateSessionFromJWT, verifyToken, withErrorHandling };
|
|
9384
11252
|
//# sourceMappingURL=index.mjs.map
|