@dragonmastery/dragoncore-api 0.0.2 → 0.0.5
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 +1482 -622
- package/dist/index.mjs +2517 -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
|
};
|
|
@@ -3949,6 +4061,15 @@ let NoteRepo = class NoteRepo$1 {
|
|
|
3949
4061
|
return result[0];
|
|
3950
4062
|
}
|
|
3951
4063
|
/**
|
|
4064
|
+
* Get all note IDs for records (e.g. tracker + its followups).
|
|
4065
|
+
* Used to include note activity in tracker activity timeline.
|
|
4066
|
+
*/
|
|
4067
|
+
async readAllIdsForRecords(records) {
|
|
4068
|
+
if (records.length === 0) return [];
|
|
4069
|
+
const conditions = [isNull(note_table.deleted_at), or(...records.map((r) => and(eq(note_table.record_id, r.record_id), eq(note_table.record_type, r.record_type))))];
|
|
4070
|
+
return (await this.router.queryLatest((db) => db.select({ id: note_table.id }).from(note_table).where(and(...conditions)))).map((r) => r.id);
|
|
4071
|
+
}
|
|
4072
|
+
/**
|
|
3952
4073
|
* Get unique tags for notes
|
|
3953
4074
|
*/
|
|
3954
4075
|
async getUniqueTags() {
|
|
@@ -3963,28 +4084,34 @@ let NoteRepo = class NoteRepo$1 {
|
|
|
3963
4084
|
NoteRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], NoteRepo);
|
|
3964
4085
|
|
|
3965
4086
|
//#endregion
|
|
3966
|
-
//#region src/slices/
|
|
3967
|
-
const
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
4087
|
+
//#region src/slices/user/user_interfaces.ts
|
|
4088
|
+
const USER_TOKENS = {
|
|
4089
|
+
IUserDisplayLookup: Symbol("IUserDisplayLookup"),
|
|
4090
|
+
IUsersRepo: Symbol("IUsersRepo"),
|
|
4091
|
+
ISignUpUser: Symbol("ISignUpUser"),
|
|
4092
|
+
IReadAllUsers: Symbol("IReadAllUsers"),
|
|
4093
|
+
IReadUser: Symbol("IReadUser"),
|
|
4094
|
+
ICreateUser: Symbol("ICreateUser"),
|
|
4095
|
+
IDeleteUser: Symbol("IDeleteUser"),
|
|
4096
|
+
IReadConsumers: Symbol("IReadConsumers"),
|
|
4097
|
+
IGetAllUsersFeature: "IGetAllUsersFeature",
|
|
4098
|
+
IGetUserFeature: "IGetUserFeature",
|
|
4099
|
+
IUpdateUserFeature: "IUpdateUserFeature",
|
|
4100
|
+
IGetUsersForSelectionFeature: "IGetUsersForSelectionFeature",
|
|
4101
|
+
IGetTriageUsersFeature: "IGetTriageUsersFeature"
|
|
3977
4102
|
};
|
|
3978
4103
|
|
|
3979
4104
|
//#endregion
|
|
3980
4105
|
//#region src/slices/note/features/create_note_feat.ts
|
|
3981
4106
|
let CreateNoteFeat = class CreateNoteFeat$1 {
|
|
3982
|
-
constructor(session, noteRepo, businessLogicRouter, create_record_version, supportTicketRepo) {
|
|
4107
|
+
constructor(session, noteRepo, businessLogicRouter, create_record_version, userDisplayLookup, supportTicketRepo, notificationService) {
|
|
3983
4108
|
this.session = session;
|
|
3984
4109
|
this.noteRepo = noteRepo;
|
|
3985
4110
|
this.businessLogicRouter = businessLogicRouter;
|
|
3986
4111
|
this.create_record_version = create_record_version;
|
|
4112
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
3987
4113
|
this.supportTicketRepo = supportTicketRepo;
|
|
4114
|
+
this.notificationService = notificationService;
|
|
3988
4115
|
}
|
|
3989
4116
|
async execute(input) {
|
|
3990
4117
|
const processor = this.businessLogicRouter.getCreateProcessor(input.record_type);
|
|
@@ -4014,7 +4141,13 @@ let CreateNoteFeat = class CreateNoteFeat$1 {
|
|
|
4014
4141
|
auth_role: this.session.user.user_type,
|
|
4015
4142
|
auth_username: this.session.user.username
|
|
4016
4143
|
});
|
|
4017
|
-
|
|
4144
|
+
await this.notifySupportTicketSubscribers(processedInput.record_type, processedInput.record_id, noteCreated.is_internal, noteCreated.body ?? "");
|
|
4145
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames([noteCreated.created_by]);
|
|
4146
|
+
return {
|
|
4147
|
+
...noteCreated,
|
|
4148
|
+
created_by_display_name: displayMap.get(noteCreated.created_by) ?? noteCreated.created_by,
|
|
4149
|
+
updated_by_display_name: displayMap.get(noteCreated.updated_by ?? "") ?? noteCreated.updated_by ?? null
|
|
4150
|
+
};
|
|
4018
4151
|
}
|
|
4019
4152
|
/**
|
|
4020
4153
|
* Bumps parent record's updated_at timestamp when note is created
|
|
@@ -4036,6 +4169,21 @@ let CreateNoteFeat = class CreateNoteFeat$1 {
|
|
|
4036
4169
|
default: break;
|
|
4037
4170
|
}
|
|
4038
4171
|
}
|
|
4172
|
+
/**
|
|
4173
|
+
* Notify support ticket subscribers when a note is added (best-effort)
|
|
4174
|
+
*/
|
|
4175
|
+
async notifySupportTicketSubscribers(recordType, recordId, isInternal, noteBody) {
|
|
4176
|
+
if (recordType !== RecordConst.SUPPORT_TICKET || !this.supportTicketRepo || !this.notificationService) return;
|
|
4177
|
+
try {
|
|
4178
|
+
const ticket = await this.supportTicketRepo.read(recordId);
|
|
4179
|
+
if (!ticket) return;
|
|
4180
|
+
const eventType = isInternal ? "NEW_INTERNAL_NOTE" : "NEW_CUSTOMER_NOTE";
|
|
4181
|
+
await this.notificationService.notifyFollowers(recordId, ticket, eventType, {
|
|
4182
|
+
noteBody: noteBody || void 0,
|
|
4183
|
+
actorUserId: this.session.user.userId
|
|
4184
|
+
});
|
|
4185
|
+
} catch {}
|
|
4186
|
+
}
|
|
4039
4187
|
};
|
|
4040
4188
|
CreateNoteFeat = __decorate([
|
|
4041
4189
|
injectable(),
|
|
@@ -4043,7 +4191,9 @@ CreateNoteFeat = __decorate([
|
|
|
4043
4191
|
__decorateParam(1, inject(NOTE_TOKENS.INoteRepo)),
|
|
4044
4192
|
__decorateParam(2, inject(NOTE_TOKENS.IBusinessLogicRouter)),
|
|
4045
4193
|
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
4046
|
-
__decorateParam(4, inject(
|
|
4194
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
4195
|
+
__decorateParam(5, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
4196
|
+
__decorateParam(6, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService))
|
|
4047
4197
|
], CreateNoteFeat);
|
|
4048
4198
|
|
|
4049
4199
|
//#endregion
|
|
@@ -4087,10 +4237,19 @@ DeleteNoteFeat = __decorate([
|
|
|
4087
4237
|
|
|
4088
4238
|
//#endregion
|
|
4089
4239
|
//#region src/slices/note/features/get_notes_feat.ts
|
|
4240
|
+
function collectUserIdsFromNotes(notes) {
|
|
4241
|
+
const ids = [];
|
|
4242
|
+
for (const n of notes) {
|
|
4243
|
+
if (n.created_by) ids.push(n.created_by);
|
|
4244
|
+
if (n.updated_by) ids.push(n.updated_by);
|
|
4245
|
+
}
|
|
4246
|
+
return ids;
|
|
4247
|
+
}
|
|
4090
4248
|
let GetNotesFeat = class GetNotesFeat$1 {
|
|
4091
|
-
constructor(noteRepo, businessLogicRouter) {
|
|
4249
|
+
constructor(noteRepo, businessLogicRouter, userDisplayLookup) {
|
|
4092
4250
|
this.noteRepo = noteRepo;
|
|
4093
4251
|
this.businessLogicRouter = businessLogicRouter;
|
|
4252
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
4094
4253
|
}
|
|
4095
4254
|
async execute(filters) {
|
|
4096
4255
|
let processedFilters = filters;
|
|
@@ -4101,23 +4260,36 @@ let GetNotesFeat = class GetNotesFeat$1 {
|
|
|
4101
4260
|
if (processor) processedFilters = await processor.process(filters);
|
|
4102
4261
|
}
|
|
4103
4262
|
}
|
|
4104
|
-
|
|
4263
|
+
const result = await this.noteRepo.read_all(processedFilters);
|
|
4264
|
+
const userIds = collectUserIdsFromNotes(result.items);
|
|
4265
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
4266
|
+
const enrichedItems = result.items.map((n) => ({
|
|
4267
|
+
...n,
|
|
4268
|
+
created_by_display_name: n.created_by ? displayMap.get(n.created_by) ?? n.created_by : null,
|
|
4269
|
+
updated_by_display_name: n.updated_by ? displayMap.get(n.updated_by) ?? n.updated_by : null
|
|
4270
|
+
}));
|
|
4271
|
+
return {
|
|
4272
|
+
...result,
|
|
4273
|
+
items: enrichedItems
|
|
4274
|
+
};
|
|
4105
4275
|
}
|
|
4106
4276
|
};
|
|
4107
4277
|
GetNotesFeat = __decorate([
|
|
4108
4278
|
injectable(),
|
|
4109
4279
|
__decorateParam(0, inject(NOTE_TOKENS.INoteRepo)),
|
|
4110
|
-
__decorateParam(1, inject(NOTE_TOKENS.IBusinessLogicRouter))
|
|
4280
|
+
__decorateParam(1, inject(NOTE_TOKENS.IBusinessLogicRouter)),
|
|
4281
|
+
__decorateParam(2, inject(USER_TOKENS.IUserDisplayLookup))
|
|
4111
4282
|
], GetNotesFeat);
|
|
4112
4283
|
|
|
4113
4284
|
//#endregion
|
|
4114
4285
|
//#region src/slices/note/features/update_note_feat.ts
|
|
4115
4286
|
let UpdateNoteFeat = class UpdateNoteFeat$1 {
|
|
4116
|
-
constructor(session, noteRepo, businessLogicRouter, create_record_version) {
|
|
4287
|
+
constructor(session, noteRepo, businessLogicRouter, create_record_version, userDisplayLookup) {
|
|
4117
4288
|
this.session = session;
|
|
4118
4289
|
this.noteRepo = noteRepo;
|
|
4119
4290
|
this.businessLogicRouter = businessLogicRouter;
|
|
4120
4291
|
this.create_record_version = create_record_version;
|
|
4292
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
4121
4293
|
}
|
|
4122
4294
|
async execute(input) {
|
|
4123
4295
|
const existingNote = await this.noteRepo.read(input.id);
|
|
@@ -4145,7 +4317,13 @@ let UpdateNoteFeat = class UpdateNoteFeat$1 {
|
|
|
4145
4317
|
auth_role: this.session.user.user_type,
|
|
4146
4318
|
auth_username: this.session.user.username
|
|
4147
4319
|
});
|
|
4148
|
-
|
|
4320
|
+
const userIds = [noteUpdated.created_by, noteUpdated.updated_by].filter(Boolean);
|
|
4321
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
4322
|
+
return {
|
|
4323
|
+
...noteUpdated,
|
|
4324
|
+
created_by_display_name: noteUpdated.created_by ? displayMap.get(noteUpdated.created_by) ?? noteUpdated.created_by : null,
|
|
4325
|
+
updated_by_display_name: noteUpdated.updated_by ? displayMap.get(noteUpdated.updated_by) ?? noteUpdated.updated_by : null
|
|
4326
|
+
};
|
|
4149
4327
|
}
|
|
4150
4328
|
};
|
|
4151
4329
|
UpdateNoteFeat = __decorate([
|
|
@@ -4153,7 +4331,8 @@ UpdateNoteFeat = __decorate([
|
|
|
4153
4331
|
__decorateParam(0, injectSession()),
|
|
4154
4332
|
__decorateParam(1, inject(NOTE_TOKENS.INoteRepo)),
|
|
4155
4333
|
__decorateParam(2, inject(NOTE_TOKENS.IBusinessLogicRouter)),
|
|
4156
|
-
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
4334
|
+
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
4335
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
4157
4336
|
], UpdateNoteFeat);
|
|
4158
4337
|
|
|
4159
4338
|
//#endregion
|
|
@@ -4200,22 +4379,6 @@ let PasswordResetTokenVerifier = class PasswordResetTokenVerifier$1 {
|
|
|
4200
4379
|
};
|
|
4201
4380
|
PasswordResetTokenVerifier = __decorate([injectable()], PasswordResetTokenVerifier);
|
|
4202
4381
|
|
|
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
4382
|
//#endregion
|
|
4220
4383
|
//#region src/slices/password_reset/features/change_password.ts
|
|
4221
4384
|
let ChangeUserPassword = class ChangeUserPassword$1 {
|
|
@@ -4360,6 +4523,51 @@ function registerPasswordResetContainer() {
|
|
|
4360
4523
|
container.registerSingleton(PASSWORD_RESET_TOKENS.IChangeUserPassword, ChangeUserPassword);
|
|
4361
4524
|
}
|
|
4362
4525
|
|
|
4526
|
+
//#endregion
|
|
4527
|
+
//#region src/slices/record_subscriber/db/record_subscriber_repo.ts
|
|
4528
|
+
let RecordSubscriberRepo = class RecordSubscriberRepo$1 {
|
|
4529
|
+
constructor(router) {
|
|
4530
|
+
this.router = router;
|
|
4531
|
+
}
|
|
4532
|
+
async create(entity) {
|
|
4533
|
+
const id = await this.router.generateId(RecordConst.RECORD_SUBSCRIBER);
|
|
4534
|
+
const [result] = await this.router.queryLatest((db) => db.insert(record_subscriber_table).values({
|
|
4535
|
+
id,
|
|
4536
|
+
...entity
|
|
4537
|
+
}).returning());
|
|
4538
|
+
return result;
|
|
4539
|
+
}
|
|
4540
|
+
async ensureSubscriber(entity) {
|
|
4541
|
+
const existing = await this.readByRecordAndUser(entity.record_type, entity.record_id, entity.user_id);
|
|
4542
|
+
if (existing) return existing;
|
|
4543
|
+
return this.create(entity);
|
|
4544
|
+
}
|
|
4545
|
+
async readByRecordId(recordType, recordId) {
|
|
4546
|
+
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))));
|
|
4547
|
+
}
|
|
4548
|
+
async readByRecordAndUser(recordType, recordId, userId) {
|
|
4549
|
+
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];
|
|
4550
|
+
}
|
|
4551
|
+
async delete(id) {
|
|
4552
|
+
return (await this.router.queryLatest((db) => db.delete(record_subscriber_table).where(eq(record_subscriber_table.id, id)).returning())).length > 0;
|
|
4553
|
+
}
|
|
4554
|
+
async updateSubscribedEvents(id, subscribedEvents) {
|
|
4555
|
+
const [result] = await this.router.queryLatest((db) => db.update(record_subscriber_table).set({ subscribed_events: subscribedEvents }).where(eq(record_subscriber_table.id, id)).returning());
|
|
4556
|
+
return result;
|
|
4557
|
+
}
|
|
4558
|
+
};
|
|
4559
|
+
RecordSubscriberRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], RecordSubscriberRepo);
|
|
4560
|
+
|
|
4561
|
+
//#endregion
|
|
4562
|
+
//#region src/slices/record_subscriber/record_subscriber_tokens.ts
|
|
4563
|
+
const RECORD_SUBSCRIBER_TOKENS = { IRecordSubscriberRepo: "IRecordSubscriberRepo" };
|
|
4564
|
+
|
|
4565
|
+
//#endregion
|
|
4566
|
+
//#region src/slices/record_subscriber/record_subscriber_registration.ts
|
|
4567
|
+
function registerRecordSubscriberDependencies() {
|
|
4568
|
+
container.registerSingleton(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo, RecordSubscriberRepo);
|
|
4569
|
+
}
|
|
4570
|
+
|
|
4363
4571
|
//#endregion
|
|
4364
4572
|
//#region src/slices/team/team_interfaces.ts
|
|
4365
4573
|
const TEAM_TOKENS = {
|
|
@@ -4407,10 +4615,11 @@ var RecordAccessValidatorRegistry = class {
|
|
|
4407
4615
|
}
|
|
4408
4616
|
};
|
|
4409
4617
|
let ValidateRecordAccess = class ValidateRecordAccess$1 {
|
|
4410
|
-
constructor(session, teamRepo, validatorRegistry) {
|
|
4618
|
+
constructor(session, teamRepo, validatorRegistry, logger$1) {
|
|
4411
4619
|
this.session = session;
|
|
4412
4620
|
this.teamRepo = teamRepo;
|
|
4413
4621
|
this.validatorRegistry = validatorRegistry;
|
|
4622
|
+
this.logger = logger$1;
|
|
4414
4623
|
}
|
|
4415
4624
|
async execute(record_id, record_type) {
|
|
4416
4625
|
const user = this.session.user;
|
|
@@ -4423,8 +4632,43 @@ let ValidateRecordAccess = class ValidateRecordAccess$1 {
|
|
|
4423
4632
|
case "team":
|
|
4424
4633
|
await this.validateTeamAccess(record_id, user.user_type);
|
|
4425
4634
|
break;
|
|
4426
|
-
default: if (user.user_type !== "lead" && user.user_type !== "staff" && user.user_type !== "super_admin")
|
|
4635
|
+
default: if (user.user_type !== "lead" && user.user_type !== "staff" && user.user_type !== "super_admin") {
|
|
4636
|
+
this.logger.warn("[ValidateRecordAccess] Access denied", {
|
|
4637
|
+
record_id,
|
|
4638
|
+
record_type,
|
|
4639
|
+
user_id: user.userId,
|
|
4640
|
+
user_type: user.user_type,
|
|
4641
|
+
reason: "Record type requires lead/staff/admin; no custom validator registered"
|
|
4642
|
+
});
|
|
4643
|
+
throw new BusinessError("Access denied: You do not have permission to view this record.");
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
async filterAllowedRecordTypes(record_id, record_types) {
|
|
4648
|
+
const allowed = (await Promise.all(record_types.map(async (rt) => {
|
|
4649
|
+
try {
|
|
4650
|
+
await this.execute(record_id, rt);
|
|
4651
|
+
return {
|
|
4652
|
+
rt,
|
|
4653
|
+
allowed: true
|
|
4654
|
+
};
|
|
4655
|
+
} catch {
|
|
4656
|
+
return {
|
|
4657
|
+
rt,
|
|
4658
|
+
allowed: false
|
|
4659
|
+
};
|
|
4660
|
+
}
|
|
4661
|
+
}))).filter((r) => r.allowed).map((r) => r.rt);
|
|
4662
|
+
if (allowed.length === 0) {
|
|
4663
|
+
this.logger.warn("[ValidateRecordAccess] Access denied - no requested record types allowed", {
|
|
4664
|
+
record_id,
|
|
4665
|
+
requested_record_types: record_types,
|
|
4666
|
+
user_id: this.session.user.userId,
|
|
4667
|
+
user_type: this.session.user.user_type
|
|
4668
|
+
});
|
|
4669
|
+
throw new BusinessError("Access denied: You do not have permission to access any of the requested record types.");
|
|
4427
4670
|
}
|
|
4671
|
+
return allowed;
|
|
4428
4672
|
}
|
|
4429
4673
|
/**
|
|
4430
4674
|
* Validates access to a team record
|
|
@@ -4432,16 +4676,24 @@ let ValidateRecordAccess = class ValidateRecordAccess$1 {
|
|
|
4432
4676
|
* Consumers cannot access team records
|
|
4433
4677
|
*/
|
|
4434
4678
|
async validateTeamAccess(record_id, user_type) {
|
|
4435
|
-
if (!await this.teamRepo.read(record_id))
|
|
4679
|
+
if (!await this.teamRepo.read(record_id)) {
|
|
4680
|
+
this.logger.warn("[ValidateRecordAccess] Team not found", { record_id });
|
|
4681
|
+
throw new BusinessError("Team not found");
|
|
4682
|
+
}
|
|
4436
4683
|
if (user_type === "lead" || user_type === "staff" || user_type === "super_admin") return;
|
|
4437
|
-
|
|
4684
|
+
this.logger.warn("[ValidateRecordAccess] Access denied - team requires lead/staff", {
|
|
4685
|
+
record_id,
|
|
4686
|
+
user_type
|
|
4687
|
+
});
|
|
4688
|
+
throw new BusinessError("Access denied: Team records require lead or staff permissions");
|
|
4438
4689
|
}
|
|
4439
4690
|
};
|
|
4440
4691
|
ValidateRecordAccess = __decorate([
|
|
4441
4692
|
injectable(),
|
|
4442
4693
|
__decorateParam(0, injectSession()),
|
|
4443
4694
|
__decorateParam(1, inject(TEAM_TOKENS.ITeamRepository)),
|
|
4444
|
-
__decorateParam(2, inject(RECORD_VERSION_VALIDATION_TOKENS.ValidatorRegistry))
|
|
4695
|
+
__decorateParam(2, inject(RECORD_VERSION_VALIDATION_TOKENS.ValidatorRegistry)),
|
|
4696
|
+
__decorateParam(3, inject(TOKENS.LOGGER))
|
|
4445
4697
|
], ValidateRecordAccess);
|
|
4446
4698
|
|
|
4447
4699
|
//#endregion
|
|
@@ -4515,9 +4767,13 @@ let RecordVersionRepo = class RecordVersionRepo$1 {
|
|
|
4515
4767
|
/**
|
|
4516
4768
|
* NEW: Breadcrumb pagination for record versions
|
|
4517
4769
|
* Use this for new implementations requiring bidirectional navigation
|
|
4770
|
+
* Supports single record_type or multiple record_types (merged results)
|
|
4518
4771
|
*/
|
|
4519
|
-
async read_all_record_versions_paginated(record_id,
|
|
4520
|
-
const
|
|
4772
|
+
async read_all_record_versions_paginated(record_id, record_type_or_types, filters) {
|
|
4773
|
+
const record_types = Array.isArray(record_type_or_types) ? record_type_or_types : [record_type_or_types];
|
|
4774
|
+
const recordTypeCondition = record_types.length === 1 ? eq(record_version_table.record_type, record_types[0]) : inArray(record_version_table.record_type, record_types);
|
|
4775
|
+
const record_ids = filters?.record_ids;
|
|
4776
|
+
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
4777
|
if (filters?.start_date) baseConditions.push(gte(record_version_table.recorded_at, filters.start_date));
|
|
4522
4778
|
if (filters?.end_date) baseConditions.push(lte(record_version_table.recorded_at, filters.end_date));
|
|
4523
4779
|
return PaginationUtils.findAllPaginated({
|
|
@@ -4647,19 +4903,49 @@ ReadAllRecordVersionsByRecord = __decorate([
|
|
|
4647
4903
|
|
|
4648
4904
|
//#endregion
|
|
4649
4905
|
//#region src/slices/record_version/features/read_all_record_versions_by_record_paginated_customer_feat.ts
|
|
4906
|
+
const USER_ID_FIELDS$1 = new Set([
|
|
4907
|
+
"assigned_to",
|
|
4908
|
+
"created_by",
|
|
4909
|
+
"updated_by",
|
|
4910
|
+
"archived_by",
|
|
4911
|
+
"deleted_by"
|
|
4912
|
+
]);
|
|
4913
|
+
function collectUserIdsFromRecordVersions$1(items) {
|
|
4914
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4915
|
+
for (const item of items) for (const raw of [item.record, item.old_record]) {
|
|
4916
|
+
if (!raw || typeof raw !== "string") continue;
|
|
4917
|
+
try {
|
|
4918
|
+
const rec = JSON.parse(raw);
|
|
4919
|
+
for (const [key, val] of Object.entries(rec)) if (USER_ID_FIELDS$1.has(key) && typeof val === "string" && val.trim()) ids.add(val);
|
|
4920
|
+
} catch {}
|
|
4921
|
+
}
|
|
4922
|
+
return [...ids];
|
|
4923
|
+
}
|
|
4650
4924
|
let ReadAllRecordVersionsByRecordPaginatedCustomer = class ReadAllRecordVersionsByRecordPaginatedCustomer$1 {
|
|
4651
|
-
constructor(record_version_repo) {
|
|
4925
|
+
constructor(record_version_repo, userDisplayLookup) {
|
|
4652
4926
|
this.record_version_repo = record_version_repo;
|
|
4927
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
4653
4928
|
}
|
|
4654
4929
|
async execute(record_id, record_type, filters) {
|
|
4655
|
-
const
|
|
4656
|
-
records
|
|
4930
|
+
const record_types = filters?.record_types ?? [record_type];
|
|
4931
|
+
const records = await this.record_version_repo.read_all_record_versions_paginated(record_id, record_types, filters);
|
|
4932
|
+
const mappedRecords = records.items.map((record) => {
|
|
4933
|
+
const recordType = record.record_type;
|
|
4657
4934
|
return {
|
|
4658
4935
|
...record,
|
|
4659
|
-
record: this.filterFieldsByRecordType(JSON.stringify(record.record),
|
|
4660
|
-
old_record: this.filterFieldsByRecordType(JSON.stringify(record.old_record),
|
|
4936
|
+
record: this.filterFieldsByRecordType(JSON.stringify(record.record), recordType),
|
|
4937
|
+
old_record: this.filterFieldsByRecordType(JSON.stringify(record.old_record), recordType)
|
|
4661
4938
|
};
|
|
4662
4939
|
});
|
|
4940
|
+
records.items = mappedRecords;
|
|
4941
|
+
const userIds = collectUserIdsFromRecordVersions$1(mappedRecords);
|
|
4942
|
+
if (userIds.length > 0) {
|
|
4943
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
4944
|
+
return {
|
|
4945
|
+
...records,
|
|
4946
|
+
user_display_map: Object.fromEntries(displayMap)
|
|
4947
|
+
};
|
|
4948
|
+
}
|
|
4663
4949
|
return records;
|
|
4664
4950
|
}
|
|
4665
4951
|
/**
|
|
@@ -4669,7 +4955,9 @@ let ReadAllRecordVersionsByRecordPaginatedCustomer = class ReadAllRecordVersions
|
|
|
4669
4955
|
filterFieldsByRecordType(recordData, recordType) {
|
|
4670
4956
|
if (!recordData) return recordData;
|
|
4671
4957
|
switch (recordType) {
|
|
4672
|
-
case
|
|
4958
|
+
case RecordConst$1.SUPPORT_TICKET: return this.filterSupportTicketFields(recordData);
|
|
4959
|
+
case RecordConst$1.SUPPORT_TICKET_ACTIVITY: return recordData;
|
|
4960
|
+
case RecordConst$1.TRACKER_ACTIVITY: return recordData;
|
|
4673
4961
|
default: return recordData;
|
|
4674
4962
|
}
|
|
4675
4963
|
}
|
|
@@ -4704,26 +4992,60 @@ let ReadAllRecordVersionsByRecordPaginatedCustomer = class ReadAllRecordVersions
|
|
|
4704
4992
|
}
|
|
4705
4993
|
}
|
|
4706
4994
|
};
|
|
4707
|
-
ReadAllRecordVersionsByRecordPaginatedCustomer = __decorate([
|
|
4995
|
+
ReadAllRecordVersionsByRecordPaginatedCustomer = __decorate([
|
|
4996
|
+
injectable(),
|
|
4997
|
+
__decorateParam(0, inject(RECORD_VERSION_TOKENS.IRecordVersionsRepo)),
|
|
4998
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
4999
|
+
], ReadAllRecordVersionsByRecordPaginatedCustomer);
|
|
4708
5000
|
|
|
4709
5001
|
//#endregion
|
|
4710
5002
|
//#region src/slices/record_version/features/read_all_record_versions_by_record_paginated_feat.ts
|
|
5003
|
+
const USER_ID_FIELDS = new Set([
|
|
5004
|
+
"assigned_to",
|
|
5005
|
+
"created_by",
|
|
5006
|
+
"updated_by",
|
|
5007
|
+
"archived_by",
|
|
5008
|
+
"deleted_by"
|
|
5009
|
+
]);
|
|
5010
|
+
function collectUserIdsFromRecordVersions(items) {
|
|
5011
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5012
|
+
for (const item of items) for (const raw of [item.record, item.old_record]) {
|
|
5013
|
+
if (!raw || typeof raw !== "string") continue;
|
|
5014
|
+
try {
|
|
5015
|
+
const rec = JSON.parse(raw);
|
|
5016
|
+
for (const [key, val] of Object.entries(rec)) if (USER_ID_FIELDS.has(key) && typeof val === "string" && val.trim()) ids.add(val);
|
|
5017
|
+
} catch {}
|
|
5018
|
+
}
|
|
5019
|
+
return [...ids];
|
|
5020
|
+
}
|
|
4711
5021
|
let ReadAllRecordVersionsByRecordPaginated = class ReadAllRecordVersionsByRecordPaginated$1 {
|
|
4712
|
-
constructor(record_version_repo, validateRecordAccess, session) {
|
|
5022
|
+
constructor(record_version_repo, validateRecordAccess, userDisplayLookup, session) {
|
|
4713
5023
|
this.record_version_repo = record_version_repo;
|
|
4714
5024
|
this.validateRecordAccess = validateRecordAccess;
|
|
5025
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
4715
5026
|
this.session = session;
|
|
4716
5027
|
}
|
|
4717
5028
|
async execute(record_id, record_type, filters) {
|
|
4718
|
-
|
|
4719
|
-
const
|
|
4720
|
-
records
|
|
5029
|
+
const requested_types = filters?.record_types ?? [record_type];
|
|
5030
|
+
const allowed_record_types = await this.validateRecordAccess.filterAllowedRecordTypes(record_id, requested_types);
|
|
5031
|
+
const records = await this.record_version_repo.read_all_record_versions_paginated(record_id, allowed_record_types, filters);
|
|
5032
|
+
const mappedRecords = records.items.map((record) => {
|
|
5033
|
+
const recordType = record.record_type;
|
|
4721
5034
|
return {
|
|
4722
5035
|
...record,
|
|
4723
|
-
record: this.filterFieldsByRecordType(JSON.stringify(record.record),
|
|
4724
|
-
old_record: this.filterFieldsByRecordType(JSON.stringify(record.old_record),
|
|
5036
|
+
record: this.filterFieldsByRecordType(JSON.stringify(record.record), recordType),
|
|
5037
|
+
old_record: this.filterFieldsByRecordType(JSON.stringify(record.old_record), recordType)
|
|
4725
5038
|
};
|
|
4726
5039
|
});
|
|
5040
|
+
records.items = mappedRecords;
|
|
5041
|
+
const userIds = collectUserIdsFromRecordVersions(mappedRecords);
|
|
5042
|
+
if (userIds.length > 0) {
|
|
5043
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
5044
|
+
return {
|
|
5045
|
+
...records,
|
|
5046
|
+
user_display_map: Object.fromEntries(displayMap)
|
|
5047
|
+
};
|
|
5048
|
+
}
|
|
4727
5049
|
return records;
|
|
4728
5050
|
}
|
|
4729
5051
|
/**
|
|
@@ -4734,7 +5056,9 @@ let ReadAllRecordVersionsByRecordPaginated = class ReadAllRecordVersionsByRecord
|
|
|
4734
5056
|
if (!recordData) return recordData;
|
|
4735
5057
|
if (this.session.user.user_type === "staff" || this.session.user.user_type === "super_admin") return recordData;
|
|
4736
5058
|
switch (recordType) {
|
|
4737
|
-
case
|
|
5059
|
+
case RecordConst$1.SUPPORT_TICKET: return this.filterSupportTicketFields(recordData);
|
|
5060
|
+
case RecordConst$1.SUPPORT_TICKET_ACTIVITY: return recordData;
|
|
5061
|
+
case RecordConst$1.TRACKER_ACTIVITY: return recordData;
|
|
4738
5062
|
default: return recordData;
|
|
4739
5063
|
}
|
|
4740
5064
|
}
|
|
@@ -4773,7 +5097,8 @@ ReadAllRecordVersionsByRecordPaginated = __decorate([
|
|
|
4773
5097
|
injectable(),
|
|
4774
5098
|
__decorateParam(0, inject(RECORD_VERSION_TOKENS.IRecordVersionsRepo)),
|
|
4775
5099
|
__decorateParam(1, inject(RECORD_VERSION_VALIDATION_TOKENS.IValidateRecordAccess)),
|
|
4776
|
-
__decorateParam(2,
|
|
5100
|
+
__decorateParam(2, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
5101
|
+
__decorateParam(3, injectSession())
|
|
4777
5102
|
], ReadAllRecordVersionsByRecordPaginated);
|
|
4778
5103
|
|
|
4779
5104
|
//#endregion
|
|
@@ -4808,9 +5133,492 @@ function registerRecordVersionContainer() {
|
|
|
4808
5133
|
container.registerSingleton(RECORD_VERSION_VALIDATION_TOKENS.IValidateRecordAccess, ValidateRecordAccess);
|
|
4809
5134
|
}
|
|
4810
5135
|
|
|
5136
|
+
//#endregion
|
|
5137
|
+
//#region src/slices/saved_filter/saved_filter_interfaces.ts
|
|
5138
|
+
const SAVED_FILTER_TOKENS = {
|
|
5139
|
+
ISavedFilterRepo: Symbol("ISavedFilterRepo"),
|
|
5140
|
+
IListSavedFilters: Symbol("IListSavedFilters"),
|
|
5141
|
+
IListAllSavedFilters: Symbol("IListAllSavedFilters"),
|
|
5142
|
+
ICreateSavedFilter: Symbol("ICreateSavedFilter"),
|
|
5143
|
+
IUpdateSavedFilter: Symbol("IUpdateSavedFilter"),
|
|
5144
|
+
IDeleteSavedFilter: Symbol("IDeleteSavedFilter")
|
|
5145
|
+
};
|
|
5146
|
+
|
|
5147
|
+
//#endregion
|
|
5148
|
+
//#region src/slices/saved_filter/db/saved_filter_mapper.ts
|
|
5149
|
+
function mapSavedFilterEntityToDto(entity) {
|
|
5150
|
+
let filters;
|
|
5151
|
+
try {
|
|
5152
|
+
filters = JSON.parse(entity.filters);
|
|
5153
|
+
} catch {
|
|
5154
|
+
filters = {};
|
|
5155
|
+
}
|
|
5156
|
+
return {
|
|
5157
|
+
id: entity.id,
|
|
5158
|
+
user_id: entity.user_id,
|
|
5159
|
+
name: entity.name,
|
|
5160
|
+
context: entity.context,
|
|
5161
|
+
route_path: entity.route_path,
|
|
5162
|
+
filters,
|
|
5163
|
+
sort_by: entity.sort_by ?? void 0,
|
|
5164
|
+
sort_direction: entity.sort_direction ?? void 0,
|
|
5165
|
+
created_at: entity.created_at,
|
|
5166
|
+
updated_at: entity.updated_at
|
|
5167
|
+
};
|
|
5168
|
+
}
|
|
5169
|
+
|
|
5170
|
+
//#endregion
|
|
5171
|
+
//#region src/slices/saved_filter/features/create_saved_filter_feat.ts
|
|
5172
|
+
let CreateSavedFilterFeature = class CreateSavedFilterFeature$1 {
|
|
5173
|
+
constructor(session, repo) {
|
|
5174
|
+
this.session = session;
|
|
5175
|
+
this.repo = repo;
|
|
5176
|
+
}
|
|
5177
|
+
async execute(input) {
|
|
5178
|
+
const userId = this.session.user.userId;
|
|
5179
|
+
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.`);
|
|
5180
|
+
return mapSavedFilterEntityToDto(await this.repo.create({
|
|
5181
|
+
user_id: userId,
|
|
5182
|
+
name: input.name,
|
|
5183
|
+
context: input.context,
|
|
5184
|
+
route_path: input.route_path,
|
|
5185
|
+
filters: JSON.stringify(input.filters),
|
|
5186
|
+
sort_by: input.sort_by ?? null,
|
|
5187
|
+
sort_direction: input.sort_direction ?? null
|
|
5188
|
+
}));
|
|
5189
|
+
}
|
|
5190
|
+
};
|
|
5191
|
+
CreateSavedFilterFeature = __decorate([
|
|
5192
|
+
injectable(),
|
|
5193
|
+
__decorateParam(0, injectSession()),
|
|
5194
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5195
|
+
], CreateSavedFilterFeature);
|
|
5196
|
+
|
|
5197
|
+
//#endregion
|
|
5198
|
+
//#region src/slices/saved_filter/user_pinned_preset_interfaces.ts
|
|
5199
|
+
const USER_PINNED_PRESET_TOKENS = {
|
|
5200
|
+
IUserPinnedPresetRepo: Symbol("IUserPinnedPresetRepo"),
|
|
5201
|
+
IListPinnedPresets: Symbol("IListPinnedPresets"),
|
|
5202
|
+
IAddPinnedPreset: Symbol("IAddPinnedPreset"),
|
|
5203
|
+
IRemovePinnedPreset: Symbol("IRemovePinnedPreset"),
|
|
5204
|
+
IReorderPinnedPresets: Symbol("IRearrangePinnedPresets")
|
|
5205
|
+
};
|
|
5206
|
+
|
|
5207
|
+
//#endregion
|
|
5208
|
+
//#region src/slices/saved_filter/features/delete_saved_filter_feat.ts
|
|
5209
|
+
let DeleteSavedFilterFeature = class DeleteSavedFilterFeature$1 {
|
|
5210
|
+
constructor(session, repo, pinnedRepo) {
|
|
5211
|
+
this.session = session;
|
|
5212
|
+
this.repo = repo;
|
|
5213
|
+
this.pinnedRepo = pinnedRepo;
|
|
5214
|
+
}
|
|
5215
|
+
async execute(id) {
|
|
5216
|
+
const existing = await this.repo.read(id);
|
|
5217
|
+
if (!existing) return false;
|
|
5218
|
+
if (existing.user_id !== this.session.user.userId) throw new BusinessError("You can only delete your own saved filters");
|
|
5219
|
+
await this.pinnedRepo.deleteByPresetId(id);
|
|
5220
|
+
return await this.repo.delete(id);
|
|
5221
|
+
}
|
|
5222
|
+
};
|
|
5223
|
+
DeleteSavedFilterFeature = __decorate([
|
|
5224
|
+
injectable(),
|
|
5225
|
+
__decorateParam(0, injectSession()),
|
|
5226
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo)),
|
|
5227
|
+
__decorateParam(2, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo))
|
|
5228
|
+
], DeleteSavedFilterFeature);
|
|
5229
|
+
|
|
5230
|
+
//#endregion
|
|
5231
|
+
//#region src/slices/saved_filter/features/list_saved_filters_feat.ts
|
|
5232
|
+
let ListSavedFiltersFeature = class ListSavedFiltersFeature$1 {
|
|
5233
|
+
constructor(session, repo) {
|
|
5234
|
+
this.session = session;
|
|
5235
|
+
this.repo = repo;
|
|
5236
|
+
}
|
|
5237
|
+
async execute(context) {
|
|
5238
|
+
const userId = this.session.user.userId;
|
|
5239
|
+
return (await this.repo.listByUserAndContext(userId, context)).map(mapSavedFilterEntityToDto);
|
|
5240
|
+
}
|
|
5241
|
+
};
|
|
5242
|
+
ListSavedFiltersFeature = __decorate([
|
|
5243
|
+
injectable(),
|
|
5244
|
+
__decorateParam(0, injectSession()),
|
|
5245
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5246
|
+
], ListSavedFiltersFeature);
|
|
5247
|
+
|
|
5248
|
+
//#endregion
|
|
5249
|
+
//#region src/slices/saved_filter/features/update_saved_filter_feat.ts
|
|
5250
|
+
let UpdateSavedFilterFeature = class UpdateSavedFilterFeature$1 {
|
|
5251
|
+
constructor(session, repo) {
|
|
5252
|
+
this.session = session;
|
|
5253
|
+
this.repo = repo;
|
|
5254
|
+
}
|
|
5255
|
+
async execute(input) {
|
|
5256
|
+
const existing = await this.repo.read(input.id);
|
|
5257
|
+
if (!existing) return null;
|
|
5258
|
+
if (existing.user_id !== this.session.user.userId) throw new BusinessError("You can only update your own saved filters");
|
|
5259
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5260
|
+
return mapSavedFilterEntityToDto(await this.repo.update({
|
|
5261
|
+
id: input.id,
|
|
5262
|
+
...input.name !== void 0 && { name: input.name },
|
|
5263
|
+
...input.route_path !== void 0 && { route_path: input.route_path },
|
|
5264
|
+
...input.filters !== void 0 && { filters: JSON.stringify(input.filters) },
|
|
5265
|
+
...input.sort_by !== void 0 && { sort_by: input.sort_by },
|
|
5266
|
+
...input.sort_direction !== void 0 && { sort_direction: input.sort_direction },
|
|
5267
|
+
updated_at: now
|
|
5268
|
+
}));
|
|
5269
|
+
}
|
|
5270
|
+
};
|
|
5271
|
+
UpdateSavedFilterFeature = __decorate([
|
|
5272
|
+
injectable(),
|
|
5273
|
+
__decorateParam(0, injectSession()),
|
|
5274
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5275
|
+
], UpdateSavedFilterFeature);
|
|
5276
|
+
|
|
5277
|
+
//#endregion
|
|
5278
|
+
//#region src/slices/saved_filter/features/add_pinned_preset_feat.ts
|
|
5279
|
+
let AddPinnedPresetFeature = class AddPinnedPresetFeature$1 {
|
|
5280
|
+
constructor(session, pinnedRepo, savedFilterRepo) {
|
|
5281
|
+
this.session = session;
|
|
5282
|
+
this.pinnedRepo = pinnedRepo;
|
|
5283
|
+
this.savedFilterRepo = savedFilterRepo;
|
|
5284
|
+
}
|
|
5285
|
+
async execute(presetId) {
|
|
5286
|
+
const userId = this.session.user.userId;
|
|
5287
|
+
const preset = await this.savedFilterRepo.read(presetId);
|
|
5288
|
+
if (!preset) return null;
|
|
5289
|
+
if (preset.user_id !== userId) throw new BusinessError("You can only pin your own presets");
|
|
5290
|
+
if (await this.pinnedRepo.findByUserAndPreset(userId, presetId)) return mapSavedFilterEntityToDto(preset);
|
|
5291
|
+
const pins = await this.pinnedRepo.listByUser(userId);
|
|
5292
|
+
if (pins.length >= MAX_PINNED_PRESETS) throw new BusinessError(`You can pin at most ${MAX_PINNED_PRESETS} presets. Unpin one to add another.`);
|
|
5293
|
+
const position = pins.length;
|
|
5294
|
+
await this.pinnedRepo.create(userId, presetId, position);
|
|
5295
|
+
return mapSavedFilterEntityToDto(preset);
|
|
5296
|
+
}
|
|
5297
|
+
};
|
|
5298
|
+
AddPinnedPresetFeature = __decorate([
|
|
5299
|
+
injectable(),
|
|
5300
|
+
__decorateParam(0, injectSession()),
|
|
5301
|
+
__decorateParam(1, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo)),
|
|
5302
|
+
__decorateParam(2, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5303
|
+
], AddPinnedPresetFeature);
|
|
5304
|
+
|
|
5305
|
+
//#endregion
|
|
5306
|
+
//#region src/slices/saved_filter/features/list_all_saved_filters_feat.ts
|
|
5307
|
+
let ListAllSavedFiltersFeature = class ListAllSavedFiltersFeature$1 {
|
|
5308
|
+
constructor(session, repo) {
|
|
5309
|
+
this.session = session;
|
|
5310
|
+
this.repo = repo;
|
|
5311
|
+
}
|
|
5312
|
+
async execute() {
|
|
5313
|
+
const userId = this.session.user.userId;
|
|
5314
|
+
return (await this.repo.listByUser(userId)).map(mapSavedFilterEntityToDto);
|
|
5315
|
+
}
|
|
5316
|
+
};
|
|
5317
|
+
ListAllSavedFiltersFeature = __decorate([
|
|
5318
|
+
injectable(),
|
|
5319
|
+
__decorateParam(0, injectSession()),
|
|
5320
|
+
__decorateParam(1, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5321
|
+
], ListAllSavedFiltersFeature);
|
|
5322
|
+
|
|
5323
|
+
//#endregion
|
|
5324
|
+
//#region src/slices/saved_filter/features/list_pinned_presets_feat.ts
|
|
5325
|
+
let ListPinnedPresetsFeature = class ListPinnedPresetsFeature$1 {
|
|
5326
|
+
constructor(session, pinnedRepo, savedFilterRepo) {
|
|
5327
|
+
this.session = session;
|
|
5328
|
+
this.pinnedRepo = pinnedRepo;
|
|
5329
|
+
this.savedFilterRepo = savedFilterRepo;
|
|
5330
|
+
}
|
|
5331
|
+
async execute() {
|
|
5332
|
+
const userId = this.session.user.userId;
|
|
5333
|
+
const pins = await this.pinnedRepo.listByUser(userId);
|
|
5334
|
+
if (pins.length === 0) return [];
|
|
5335
|
+
const presetIds = pins.map((p) => p.preset_id);
|
|
5336
|
+
const presets = await this.savedFilterRepo.readByIds(presetIds);
|
|
5337
|
+
const presetMap = new Map(presets.map((p) => [p.id, p]));
|
|
5338
|
+
const result = [];
|
|
5339
|
+
for (const pin of pins) {
|
|
5340
|
+
const preset = presetMap.get(pin.preset_id);
|
|
5341
|
+
if (preset && preset.user_id === userId) result.push(mapSavedFilterEntityToDto(preset));
|
|
5342
|
+
}
|
|
5343
|
+
return result;
|
|
5344
|
+
}
|
|
5345
|
+
};
|
|
5346
|
+
ListPinnedPresetsFeature = __decorate([
|
|
5347
|
+
injectable(),
|
|
5348
|
+
__decorateParam(0, injectSession()),
|
|
5349
|
+
__decorateParam(1, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo)),
|
|
5350
|
+
__decorateParam(2, inject(SAVED_FILTER_TOKENS.ISavedFilterRepo))
|
|
5351
|
+
], ListPinnedPresetsFeature);
|
|
5352
|
+
|
|
5353
|
+
//#endregion
|
|
5354
|
+
//#region src/slices/saved_filter/features/remove_pinned_preset_feat.ts
|
|
5355
|
+
let RemovePinnedPresetFeature = class RemovePinnedPresetFeature$1 {
|
|
5356
|
+
constructor(session, pinnedRepo) {
|
|
5357
|
+
this.session = session;
|
|
5358
|
+
this.pinnedRepo = pinnedRepo;
|
|
5359
|
+
}
|
|
5360
|
+
async execute(presetId) {
|
|
5361
|
+
const userId = this.session.user.userId;
|
|
5362
|
+
const pin = await this.pinnedRepo.findByUserAndPreset(userId, presetId);
|
|
5363
|
+
if (!pin) return false;
|
|
5364
|
+
return await this.pinnedRepo.delete(pin.id);
|
|
5365
|
+
}
|
|
5366
|
+
};
|
|
5367
|
+
RemovePinnedPresetFeature = __decorate([
|
|
5368
|
+
injectable(),
|
|
5369
|
+
__decorateParam(0, injectSession()),
|
|
5370
|
+
__decorateParam(1, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo))
|
|
5371
|
+
], RemovePinnedPresetFeature);
|
|
5372
|
+
|
|
5373
|
+
//#endregion
|
|
5374
|
+
//#region src/slices/saved_filter/features/reorder_pinned_presets_feat.ts
|
|
5375
|
+
let ReorderPinnedPresetsFeature = class ReorderPinnedPresetsFeature$1 {
|
|
5376
|
+
constructor(session, pinnedRepo) {
|
|
5377
|
+
this.session = session;
|
|
5378
|
+
this.pinnedRepo = pinnedRepo;
|
|
5379
|
+
}
|
|
5380
|
+
async execute(presetIds) {
|
|
5381
|
+
const userId = this.session.user.userId;
|
|
5382
|
+
for (let i = 0; i < presetIds.length; i++) {
|
|
5383
|
+
const presetId = presetIds[i];
|
|
5384
|
+
const pin = await this.pinnedRepo.findByUserAndPreset(userId, presetId);
|
|
5385
|
+
if (pin) await this.pinnedRepo.updatePosition(pin.id, i);
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
};
|
|
5389
|
+
ReorderPinnedPresetsFeature = __decorate([
|
|
5390
|
+
injectable(),
|
|
5391
|
+
__decorateParam(0, injectSession()),
|
|
5392
|
+
__decorateParam(1, inject(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo))
|
|
5393
|
+
], ReorderPinnedPresetsFeature);
|
|
5394
|
+
|
|
5395
|
+
//#endregion
|
|
5396
|
+
//#region src/slices/saved_filter/db/saved_filter_repo.ts
|
|
5397
|
+
let SavedFilterRepository = class SavedFilterRepository$1 {
|
|
5398
|
+
constructor(router) {
|
|
5399
|
+
this.router = router;
|
|
5400
|
+
}
|
|
5401
|
+
async create(entity) {
|
|
5402
|
+
const id = await this.router.generateId(RecordConst.SAVED_FILTER);
|
|
5403
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5404
|
+
const [result] = await this.router.queryLatest((db) => db.insert(saved_filter_table).values({
|
|
5405
|
+
id,
|
|
5406
|
+
user_id: entity.user_id,
|
|
5407
|
+
name: entity.name,
|
|
5408
|
+
context: entity.context,
|
|
5409
|
+
route_path: entity.route_path,
|
|
5410
|
+
filters: entity.filters,
|
|
5411
|
+
sort_by: entity.sort_by ?? null,
|
|
5412
|
+
sort_direction: entity.sort_direction ?? null,
|
|
5413
|
+
created_at: now,
|
|
5414
|
+
updated_at: now
|
|
5415
|
+
}).returning());
|
|
5416
|
+
return result;
|
|
5417
|
+
}
|
|
5418
|
+
async read(id) {
|
|
5419
|
+
return (await this.router.queryById(id, (db) => db.select().from(saved_filter_table).where(eq(saved_filter_table.id, id)).limit(1)))[0] ?? null;
|
|
5420
|
+
}
|
|
5421
|
+
async readByIds(ids) {
|
|
5422
|
+
if (ids.length === 0) return [];
|
|
5423
|
+
return this.router.queryByIds(ids, (db, idsForShard) => db.select().from(saved_filter_table).where(inArray(saved_filter_table.id, idsForShard)));
|
|
5424
|
+
}
|
|
5425
|
+
async listByUser(userId) {
|
|
5426
|
+
return await this.router.queryAll((db) => db.select().from(saved_filter_table).where(eq(saved_filter_table.user_id, userId)));
|
|
5427
|
+
}
|
|
5428
|
+
async listByUserAndContext(userId, context) {
|
|
5429
|
+
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))));
|
|
5430
|
+
}
|
|
5431
|
+
async update(entity) {
|
|
5432
|
+
const updated = (await this.router.queryById(entity.id, (db) => db.update(saved_filter_table).set({
|
|
5433
|
+
...entity.name !== void 0 && { name: entity.name },
|
|
5434
|
+
...entity.route_path !== void 0 && { route_path: entity.route_path },
|
|
5435
|
+
...entity.filters !== void 0 && { filters: entity.filters },
|
|
5436
|
+
...entity.sort_by !== void 0 && { sort_by: entity.sort_by },
|
|
5437
|
+
...entity.sort_direction !== void 0 && { sort_direction: entity.sort_direction },
|
|
5438
|
+
updated_at: entity.updated_at
|
|
5439
|
+
}).where(eq(saved_filter_table.id, entity.id)).returning()))[0];
|
|
5440
|
+
if (!updated) throw new Error("Saved filter not found or update failed");
|
|
5441
|
+
return updated;
|
|
5442
|
+
}
|
|
5443
|
+
async delete(id) {
|
|
5444
|
+
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;
|
|
5445
|
+
}
|
|
5446
|
+
};
|
|
5447
|
+
SavedFilterRepository = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], SavedFilterRepository);
|
|
5448
|
+
|
|
5449
|
+
//#endregion
|
|
5450
|
+
//#region src/slices/saved_filter/db/user_pinned_preset_repo.ts
|
|
5451
|
+
let UserPinnedPresetRepository = class UserPinnedPresetRepository$1 {
|
|
5452
|
+
constructor(router) {
|
|
5453
|
+
this.router = router;
|
|
5454
|
+
}
|
|
5455
|
+
async create(userId, presetId, position) {
|
|
5456
|
+
const id = await this.router.generateId(RecordConst.USER_PINNED_PRESET);
|
|
5457
|
+
const [result] = await this.router.queryLatest((db) => db.insert(user_pinned_preset_table).values({
|
|
5458
|
+
id,
|
|
5459
|
+
user_id: userId,
|
|
5460
|
+
preset_id: presetId,
|
|
5461
|
+
position
|
|
5462
|
+
}).returning());
|
|
5463
|
+
return result;
|
|
5464
|
+
}
|
|
5465
|
+
async listByUser(userId) {
|
|
5466
|
+
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);
|
|
5467
|
+
}
|
|
5468
|
+
async findByUserAndPreset(userId, presetId) {
|
|
5469
|
+
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;
|
|
5470
|
+
}
|
|
5471
|
+
async updatePosition(id, position) {
|
|
5472
|
+
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 }));
|
|
5473
|
+
}
|
|
5474
|
+
async delete(id) {
|
|
5475
|
+
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;
|
|
5476
|
+
}
|
|
5477
|
+
async deleteByPresetId(presetId) {
|
|
5478
|
+
const pins = await this.router.queryAll((db) => db.select().from(user_pinned_preset_table).where(eq(user_pinned_preset_table.preset_id, presetId)));
|
|
5479
|
+
let deleted = 0;
|
|
5480
|
+
for (const pin of pins) if (await this.delete(pin.id)) deleted++;
|
|
5481
|
+
return deleted;
|
|
5482
|
+
}
|
|
5483
|
+
};
|
|
5484
|
+
UserPinnedPresetRepository = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], UserPinnedPresetRepository);
|
|
5485
|
+
|
|
5486
|
+
//#endregion
|
|
5487
|
+
//#region src/slices/saved_filter/saved_filter_container.ts
|
|
5488
|
+
function registerSavedFilterContainer() {
|
|
5489
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.ISavedFilterRepo, SavedFilterRepository);
|
|
5490
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IUserPinnedPresetRepo, UserPinnedPresetRepository);
|
|
5491
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.IListSavedFilters, ListSavedFiltersFeature);
|
|
5492
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.IListAllSavedFilters, ListAllSavedFiltersFeature);
|
|
5493
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.ICreateSavedFilter, CreateSavedFilterFeature);
|
|
5494
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.IUpdateSavedFilter, UpdateSavedFilterFeature);
|
|
5495
|
+
container.registerSingleton(SAVED_FILTER_TOKENS.IDeleteSavedFilter, DeleteSavedFilterFeature);
|
|
5496
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IListPinnedPresets, ListPinnedPresetsFeature);
|
|
5497
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IAddPinnedPreset, AddPinnedPresetFeature);
|
|
5498
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IRemovePinnedPreset, RemovePinnedPresetFeature);
|
|
5499
|
+
container.registerSingleton(USER_PINNED_PRESET_TOKENS.IReorderPinnedPresets, ReorderPinnedPresetsFeature);
|
|
5500
|
+
}
|
|
5501
|
+
|
|
5502
|
+
//#endregion
|
|
5503
|
+
//#region src/slices/support_staff/db/support_staff_repo.ts
|
|
5504
|
+
let SupportStaffRepo = class SupportStaffRepo$1 {
|
|
5505
|
+
constructor(router) {
|
|
5506
|
+
this.router = router;
|
|
5507
|
+
}
|
|
5508
|
+
async readAll() {
|
|
5509
|
+
return this.router.queryAll((db) => db.select().from(support_staff_table).orderBy(support_staff_table.user_id));
|
|
5510
|
+
}
|
|
5511
|
+
async readSupportStaffMembers() {
|
|
5512
|
+
return this.router.queryAll((db) => db.select({
|
|
5513
|
+
id: support_staff_table.id,
|
|
5514
|
+
user_id: support_staff_table.user_id,
|
|
5515
|
+
email: user_table.email,
|
|
5516
|
+
created_at: support_staff_table.created_at
|
|
5517
|
+
}).from(support_staff_table).innerJoin(user_table, eq(support_staff_table.user_id, user_table.id)).orderBy(support_staff_table.user_id));
|
|
5518
|
+
}
|
|
5519
|
+
async readTriageUsers() {
|
|
5520
|
+
return this.router.queryAll((db) => db.select({
|
|
5521
|
+
id: user_table.id,
|
|
5522
|
+
email: user_table.email
|
|
5523
|
+
}).from(support_staff_table).innerJoin(user_table, eq(support_staff_table.user_id, user_table.id)).orderBy(support_staff_table.user_id));
|
|
5524
|
+
}
|
|
5525
|
+
async add(userId, createdBy) {
|
|
5526
|
+
const id = await this.router.generateId(RecordConst.SUPPORT_STAFF);
|
|
5527
|
+
const entity = {
|
|
5528
|
+
user_id: userId,
|
|
5529
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5530
|
+
created_by: createdBy
|
|
5531
|
+
};
|
|
5532
|
+
const [result] = await this.router.queryLatest((db) => db.insert(support_staff_table).values({
|
|
5533
|
+
id,
|
|
5534
|
+
...entity
|
|
5535
|
+
}).returning());
|
|
5536
|
+
return result;
|
|
5537
|
+
}
|
|
5538
|
+
async remove(userId) {
|
|
5539
|
+
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;
|
|
5540
|
+
}
|
|
5541
|
+
};
|
|
5542
|
+
SupportStaffRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], SupportStaffRepo);
|
|
5543
|
+
|
|
5544
|
+
//#endregion
|
|
5545
|
+
//#region src/slices/support_staff/support_staff_tokens.ts
|
|
5546
|
+
const SUPPORT_STAFF_TOKENS = {
|
|
5547
|
+
ISupportStaffRepo: Symbol("ISupportStaffRepo"),
|
|
5548
|
+
IListSupportStaffFeature: Symbol("IListSupportStaffFeature"),
|
|
5549
|
+
IAddSupportStaffFeature: Symbol("IAddSupportStaffFeature"),
|
|
5550
|
+
IRemoveSupportStaffFeature: Symbol("IRemoveSupportStaffFeature")
|
|
5551
|
+
};
|
|
5552
|
+
|
|
5553
|
+
//#endregion
|
|
5554
|
+
//#region src/slices/support_staff/features/add_support_staff_feat.ts
|
|
5555
|
+
let AddSupportStaffFeat = class AddSupportStaffFeat$1 {
|
|
5556
|
+
constructor(supportStaffRepo, userRepo, session) {
|
|
5557
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
5558
|
+
this.userRepo = userRepo;
|
|
5559
|
+
this.session = session;
|
|
5560
|
+
}
|
|
5561
|
+
async execute(userId) {
|
|
5562
|
+
const user = await this.userRepo.read_user(userId);
|
|
5563
|
+
if (!user) throw new Error("User not found");
|
|
5564
|
+
if (!["staff", "super_admin"].includes(user.user_type)) throw new BusinessError("Only staff and super_admin users can be added to support staff");
|
|
5565
|
+
if ((await this.supportStaffRepo.readAll()).some((s) => s.user_id === userId)) throw new Error("User is already support staff");
|
|
5566
|
+
const created = await this.supportStaffRepo.add(userId, this.session.user.userId);
|
|
5567
|
+
const member = (await this.supportStaffRepo.readSupportStaffMembers()).find((m) => m.id === created.id);
|
|
5568
|
+
if (!member) throw new Error("Failed to fetch created support staff member");
|
|
5569
|
+
return member;
|
|
5570
|
+
}
|
|
5571
|
+
};
|
|
5572
|
+
AddSupportStaffFeat = __decorate([
|
|
5573
|
+
injectable(),
|
|
5574
|
+
__decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo)),
|
|
5575
|
+
__decorateParam(1, inject(USER_TOKENS.IUsersRepo)),
|
|
5576
|
+
__decorateParam(2, inject(TOKENS.AUTHENTICATED_SESSION))
|
|
5577
|
+
], AddSupportStaffFeat);
|
|
5578
|
+
|
|
5579
|
+
//#endregion
|
|
5580
|
+
//#region src/slices/support_staff/features/list_support_staff_feat.ts
|
|
5581
|
+
let ListSupportStaffFeat = class ListSupportStaffFeat$1 {
|
|
5582
|
+
constructor(supportStaffRepo) {
|
|
5583
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
5584
|
+
}
|
|
5585
|
+
async execute() {
|
|
5586
|
+
return this.supportStaffRepo.readSupportStaffMembers();
|
|
5587
|
+
}
|
|
5588
|
+
};
|
|
5589
|
+
ListSupportStaffFeat = __decorate([injectable(), __decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo))], ListSupportStaffFeat);
|
|
5590
|
+
|
|
5591
|
+
//#endregion
|
|
5592
|
+
//#region src/slices/support_staff/features/remove_support_staff_feat.ts
|
|
5593
|
+
let RemoveSupportStaffFeat = class RemoveSupportStaffFeat$1 {
|
|
5594
|
+
constructor(supportStaffRepo) {
|
|
5595
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
5596
|
+
}
|
|
5597
|
+
async execute(userId) {
|
|
5598
|
+
if (!await this.supportStaffRepo.remove(userId)) throw new Error("Support staff member not found");
|
|
5599
|
+
}
|
|
5600
|
+
};
|
|
5601
|
+
RemoveSupportStaffFeat = __decorate([injectable(), __decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo))], RemoveSupportStaffFeat);
|
|
5602
|
+
|
|
5603
|
+
//#endregion
|
|
5604
|
+
//#region src/slices/support_staff/support_staff_registration.ts
|
|
5605
|
+
function registerSupportStaffDependencies() {
|
|
5606
|
+
container.registerSingleton(SUPPORT_STAFF_TOKENS.ISupportStaffRepo, SupportStaffRepo);
|
|
5607
|
+
container.registerSingleton(SUPPORT_STAFF_TOKENS.IListSupportStaffFeature, ListSupportStaffFeat);
|
|
5608
|
+
container.registerSingleton(SUPPORT_STAFF_TOKENS.IAddSupportStaffFeature, AddSupportStaffFeat);
|
|
5609
|
+
container.registerSingleton(SUPPORT_STAFF_TOKENS.IRemoveSupportStaffFeature, RemoveSupportStaffFeat);
|
|
5610
|
+
}
|
|
5611
|
+
|
|
4811
5612
|
//#endregion
|
|
4812
5613
|
//#region src/slices/support_ticket/db/support_ticket_query_config.ts
|
|
4813
5614
|
const supportTicketFields = {
|
|
5615
|
+
display_id: {
|
|
5616
|
+
column: support_ticket_table.display_id,
|
|
5617
|
+
type: "number",
|
|
5618
|
+
filterable: false,
|
|
5619
|
+
searchable: true,
|
|
5620
|
+
sortable: true
|
|
5621
|
+
},
|
|
4814
5622
|
type: {
|
|
4815
5623
|
column: support_ticket_table.type,
|
|
4816
5624
|
type: "string",
|
|
@@ -4820,9 +5628,9 @@ const supportTicketFields = {
|
|
|
4820
5628
|
},
|
|
4821
5629
|
priority: {
|
|
4822
5630
|
column: support_ticket_table.priority,
|
|
4823
|
-
type: "
|
|
5631
|
+
type: "number",
|
|
4824
5632
|
filterable: true,
|
|
4825
|
-
searchable:
|
|
5633
|
+
searchable: false,
|
|
4826
5634
|
sortable: true
|
|
4827
5635
|
},
|
|
4828
5636
|
approval_status: {
|
|
@@ -4839,18 +5647,11 @@ const supportTicketFields = {
|
|
|
4839
5647
|
searchable: true,
|
|
4840
5648
|
sortable: true
|
|
4841
5649
|
},
|
|
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,
|
|
5650
|
+
created_by: {
|
|
5651
|
+
column: support_ticket_table.created_by,
|
|
4851
5652
|
type: "string",
|
|
4852
5653
|
filterable: true,
|
|
4853
|
-
searchable:
|
|
5654
|
+
searchable: false,
|
|
4854
5655
|
sortable: false
|
|
4855
5656
|
},
|
|
4856
5657
|
title: {
|
|
@@ -4909,20 +5710,6 @@ const supportTicketFields = {
|
|
|
4909
5710
|
searchable: false,
|
|
4910
5711
|
sortable: false
|
|
4911
5712
|
},
|
|
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
5713
|
approvalStatus: {
|
|
4927
5714
|
column: support_ticket_table.approval_status,
|
|
4928
5715
|
type: "string",
|
|
@@ -4993,7 +5780,8 @@ const buildFieldFilters$1 = createFilterBuilder({
|
|
|
4993
5780
|
processedSeparately: [
|
|
4994
5781
|
"archived_at",
|
|
4995
5782
|
"status",
|
|
4996
|
-
"is_locked"
|
|
5783
|
+
"is_locked",
|
|
5784
|
+
"dev_lifecycle"
|
|
4997
5785
|
]
|
|
4998
5786
|
});
|
|
4999
5787
|
/**
|
|
@@ -5007,6 +5795,7 @@ function mapSupportTicketStatusToApprovalStatus(statuses) {
|
|
|
5007
5795
|
case "CANCELLED": return "REJECTED";
|
|
5008
5796
|
case "FOLLOWUP": return;
|
|
5009
5797
|
case "IN_PROGRESS": return;
|
|
5798
|
+
case "VERIFICATION": return;
|
|
5010
5799
|
case "COMPLETED": return;
|
|
5011
5800
|
default: throw new Error(`Invalid support ticket status: ${status}`);
|
|
5012
5801
|
}
|
|
@@ -5017,6 +5806,7 @@ function mapSupportTicketStatusToApprovalStatus(statuses) {
|
|
|
5017
5806
|
case "CANCELLED": return;
|
|
5018
5807
|
case "FOLLOWUP": return;
|
|
5019
5808
|
case "IN_PROGRESS": return;
|
|
5809
|
+
case "VERIFICATION": return "VERIFICATION";
|
|
5020
5810
|
case "COMPLETED": return "DEPLOYED";
|
|
5021
5811
|
default: throw new Error(`Invalid support ticket status: ${status}`);
|
|
5022
5812
|
}
|
|
@@ -5060,7 +5850,21 @@ function buildSupportTicketQuery(filters) {
|
|
|
5060
5850
|
} else if (statusValue === "IN_PROGRESS") {
|
|
5061
5851
|
conditions.push(eq(support_ticket_table.approval_status, "APPROVED"));
|
|
5062
5852
|
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 === "
|
|
5853
|
+
} else if (statusValue === "VERIFICATION") conditions.push(eq(support_ticket_table.dev_lifecycle, "VERIFICATION"));
|
|
5854
|
+
else if (statusValue === "COMPLETED") conditions.push(eq(support_ticket_table.dev_lifecycle, "DEPLOYED"));
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
if (filters?.dev_lifecycle) {
|
|
5858
|
+
const dl = filters.dev_lifecycle;
|
|
5859
|
+
const col = support_ticket_table.dev_lifecycle;
|
|
5860
|
+
const isPending = (v) => v === "PENDING";
|
|
5861
|
+
if (dl.value !== void 0 && isPending(dl.value) && dl.operator === OPERATORS.EQUALS) conditions.push(isNull(col));
|
|
5862
|
+
else if (dl.values?.some(isPending) && dl.operator === OPERATORS.IS_ONE_OF) {
|
|
5863
|
+
const others = dl.values.filter((v) => !isPending(v));
|
|
5864
|
+
conditions.push(others.length ? or(isNull(col), inArray(col, others)) : isNull(col));
|
|
5865
|
+
} else {
|
|
5866
|
+
const cond = applyFilter(col, dl, "string");
|
|
5867
|
+
if (cond) conditions.push(cond);
|
|
5064
5868
|
}
|
|
5065
5869
|
}
|
|
5066
5870
|
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 +5906,7 @@ let SupportTicketRepo = class SupportTicketRepo$1 {
|
|
|
5102
5906
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5103
5907
|
const [result] = await this.router.queryById(support_ticket.id, (db) => db.update(support_ticket_table).set({
|
|
5104
5908
|
...support_ticket,
|
|
5105
|
-
updated_at:
|
|
5909
|
+
updated_at: now
|
|
5106
5910
|
}).where(eq(support_ticket_table.id, support_ticket.id)).returning());
|
|
5107
5911
|
return result;
|
|
5108
5912
|
}
|
|
@@ -5157,6 +5961,26 @@ let SupportTicketRepo = class SupportTicketRepo$1 {
|
|
|
5157
5961
|
return PaginationUtils.findAllPaginated(this.paginationConfig, filters || {}, conditions);
|
|
5158
5962
|
}
|
|
5159
5963
|
/**
|
|
5964
|
+
* Get distinct requestor (created_by) user IDs for active support tickets.
|
|
5965
|
+
* Active = not archived (archived_at IS NULL).
|
|
5966
|
+
* @param excludeInternal - when true, exclude internal tickets (for customer view)
|
|
5967
|
+
* Queries all shards.
|
|
5968
|
+
*/
|
|
5969
|
+
async read_distinct_requestors_for_active_tickets(options) {
|
|
5970
|
+
const conditions = [isNull(support_ticket_table.deleted_at), isNull(support_ticket_table.archived_at)];
|
|
5971
|
+
if (options?.excludeInternal) conditions.push(ne(support_ticket_table.approval_status, "INTERNAL"));
|
|
5972
|
+
const rows = await this.router.queryAll((db) => db.selectDistinct({ created_by: support_ticket_table.created_by }).from(support_ticket_table).where(and(...conditions)));
|
|
5973
|
+
return [...new Set(rows.map((r) => r.created_by).filter(Boolean))];
|
|
5974
|
+
}
|
|
5975
|
+
/**
|
|
5976
|
+
* Find support tickets with invalid user IDs (e.g. emails instead of 32-char universal IDs).
|
|
5977
|
+
* Queries all shards.
|
|
5978
|
+
*/
|
|
5979
|
+
async findWithInvalidUserIds() {
|
|
5980
|
+
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)`);
|
|
5981
|
+
return this.router.queryAll((db) => db.select().from(support_ticket_table).where(and(isNull(support_ticket_table.deleted_at), invalidLengthCondition)));
|
|
5982
|
+
}
|
|
5983
|
+
/**
|
|
5160
5984
|
* Soft delete a support ticket by ID
|
|
5161
5985
|
*/
|
|
5162
5986
|
async soft_delete(id, deleted_by) {
|
|
@@ -5169,7 +5993,237 @@ let SupportTicketRepo = class SupportTicketRepo$1 {
|
|
|
5169
5993
|
return result[0];
|
|
5170
5994
|
}
|
|
5171
5995
|
};
|
|
5172
|
-
SupportTicketRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], SupportTicketRepo);
|
|
5996
|
+
SupportTicketRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], SupportTicketRepo);
|
|
5997
|
+
|
|
5998
|
+
//#endregion
|
|
5999
|
+
//#region src/slices/user_profile/user_profile_interfaces.ts
|
|
6000
|
+
const USER_PROFILE_TOKENS = {
|
|
6001
|
+
IUserProfilesRepo: Symbol("IUserProfilesRepo"),
|
|
6002
|
+
IReadUserProfilesByUser: Symbol("IReadUserProfilesByUser"),
|
|
6003
|
+
IReadUserProfile: Symbol("IReadUserProfile"),
|
|
6004
|
+
ICreateUserProfile: Symbol("ICreateUserProfile"),
|
|
6005
|
+
IUpdateUserProfile: Symbol("IUpdateUserProfile")
|
|
6006
|
+
};
|
|
6007
|
+
|
|
6008
|
+
//#endregion
|
|
6009
|
+
//#region src/slices/support_ticket/services/support_ticket_notification_service.ts
|
|
6010
|
+
const STAFF_USER_TYPES = ["staff", "super_admin"];
|
|
6011
|
+
/** Escape HTML for safe inclusion in email body. */
|
|
6012
|
+
function escapeHtmlForEmail(s) {
|
|
6013
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/\n/g, "<br>");
|
|
6014
|
+
}
|
|
6015
|
+
let SupportTicketNotificationService = class SupportTicketNotificationService$1 {
|
|
6016
|
+
constructor(subscriberRepo, userRepo, userProfileRepo, emailService, env, logger$1) {
|
|
6017
|
+
this.subscriberRepo = subscriberRepo;
|
|
6018
|
+
this.userRepo = userRepo;
|
|
6019
|
+
this.userProfileRepo = userProfileRepo;
|
|
6020
|
+
this.emailService = emailService;
|
|
6021
|
+
this.env = env;
|
|
6022
|
+
this.logger = logger$1;
|
|
6023
|
+
}
|
|
6024
|
+
async notifyAssignee(ticket, eventType, context) {
|
|
6025
|
+
try {
|
|
6026
|
+
const assigneeId = ticket.assigned_to;
|
|
6027
|
+
if (!assigneeId) return;
|
|
6028
|
+
if (context?.actorUserId && assigneeId === context.actorUserId) return;
|
|
6029
|
+
const [email] = await this.getNotificationEmailsForFollowers([assigneeId], context?.actorUserId);
|
|
6030
|
+
if (!email) return;
|
|
6031
|
+
const ticketUrl = `${this.env.WEBSITE_URL}/staff/support/${ticket.id}`;
|
|
6032
|
+
const { subject, bodyText, bodyHtml } = this.buildAssigneeEmailContent(ticket, eventType, ticketUrl, context);
|
|
6033
|
+
await this.emailService.sendBulkEmail([email], subject, bodyText, bodyHtml);
|
|
6034
|
+
this.logger.info(`Sent support ticket ${eventType} notification to assignee`);
|
|
6035
|
+
} catch (error) {
|
|
6036
|
+
this.logger.error("Failed to send support ticket assignee notification", {
|
|
6037
|
+
error,
|
|
6038
|
+
supportTicketId: ticket.id,
|
|
6039
|
+
eventType
|
|
6040
|
+
});
|
|
6041
|
+
}
|
|
6042
|
+
}
|
|
6043
|
+
async notifyFollowers(supportTicketId, ticket, eventType, context) {
|
|
6044
|
+
try {
|
|
6045
|
+
await this.ensureAssigneeIsSubscriber(ticket);
|
|
6046
|
+
const followers = await this.subscriberRepo.readByRecordId(RecordConst$1.SUPPORT_TICKET, supportTicketId);
|
|
6047
|
+
if (followers.length === 0) return;
|
|
6048
|
+
const filteredFollowers = eventType === "NEW_INTERNAL_NOTE" ? await this.filterStaffFollowersOnly(followers) : followers;
|
|
6049
|
+
if (filteredFollowers.length === 0) return;
|
|
6050
|
+
const subscribed = filteredFollowers.filter((f) => {
|
|
6051
|
+
const events = f.subscribed_events;
|
|
6052
|
+
if (!events || events.length === 0) return true;
|
|
6053
|
+
return events.includes(eventType);
|
|
6054
|
+
});
|
|
6055
|
+
if (subscribed.length === 0) return;
|
|
6056
|
+
const assigneeExcluded = eventType === "TICKET_CREATED" || eventType === "TICKET_ASSIGNED" ? subscribed.filter((f) => f.user_id !== ticket.assigned_to) : subscribed;
|
|
6057
|
+
const recipients = await this.getNotificationRecipientsForFollowers(assigneeExcluded.map((f) => f.user_id), context?.actorUserId);
|
|
6058
|
+
if (recipients.length === 0) return;
|
|
6059
|
+
const baseUrl = this.env.WEBSITE_URL;
|
|
6060
|
+
for (const { email, userType } of recipients) {
|
|
6061
|
+
const ticketUrl = STAFF_USER_TYPES.includes(userType) ? `${baseUrl}/staff/support/${ticket.id}` : `${baseUrl}/support/${ticket.id}`;
|
|
6062
|
+
const { subject, bodyText, bodyHtml } = this.buildEmailContent(ticket, eventType, ticketUrl, context);
|
|
6063
|
+
await this.emailService.sendBulkEmail([email], subject, bodyText, bodyHtml);
|
|
6064
|
+
}
|
|
6065
|
+
this.logger.info(`Sent support ticket ${eventType} notification to ${recipients.length} followers`);
|
|
6066
|
+
} catch (error) {
|
|
6067
|
+
this.logger.error("Failed to send support ticket follower notifications", {
|
|
6068
|
+
error,
|
|
6069
|
+
supportTicketId,
|
|
6070
|
+
eventType
|
|
6071
|
+
});
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
/**
|
|
6075
|
+
* Ensures the assigned staff member is a subscriber so they receive the same
|
|
6076
|
+
* notifications as other followers (notes, approvals, etc.). Idempotent.
|
|
6077
|
+
*/
|
|
6078
|
+
async ensureAssigneeIsSubscriber(ticket) {
|
|
6079
|
+
const assigneeId = ticket.assigned_to;
|
|
6080
|
+
if (!assigneeId) return;
|
|
6081
|
+
try {
|
|
6082
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6083
|
+
await this.subscriberRepo.ensureSubscriber({
|
|
6084
|
+
record_type: RecordConst$1.SUPPORT_TICKET,
|
|
6085
|
+
record_id: ticket.id,
|
|
6086
|
+
user_id: assigneeId,
|
|
6087
|
+
subscribed_events: null,
|
|
6088
|
+
created_at: now,
|
|
6089
|
+
created_by: assigneeId
|
|
6090
|
+
});
|
|
6091
|
+
} catch (error) {
|
|
6092
|
+
this.logger.error("Failed to ensure assignee is subscriber", {
|
|
6093
|
+
error,
|
|
6094
|
+
supportTicketId: ticket.id,
|
|
6095
|
+
assigneeId
|
|
6096
|
+
});
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
6099
|
+
async filterStaffFollowersOnly(followers) {
|
|
6100
|
+
if (followers.length === 0) return [];
|
|
6101
|
+
const userIds = followers.map((f) => f.user_id);
|
|
6102
|
+
const users = await this.userRepo.read_users_by_ids(userIds);
|
|
6103
|
+
const staffUserIds = new Set(users.filter((u) => STAFF_USER_TYPES.includes(u.user_type)).map((u) => u.id));
|
|
6104
|
+
return followers.filter((f) => staffUserIds.has(f.user_id));
|
|
6105
|
+
}
|
|
6106
|
+
async getNotificationEmailsForFollowers(userIds, excludeUserId) {
|
|
6107
|
+
return (await this.getNotificationRecipientsForFollowers(userIds, excludeUserId)).map((r) => r.email);
|
|
6108
|
+
}
|
|
6109
|
+
async getNotificationRecipientsForFollowers(userIds, excludeUserId) {
|
|
6110
|
+
const filteredIds = excludeUserId ? userIds.filter((id) => id !== excludeUserId) : userIds;
|
|
6111
|
+
if (filteredIds.length === 0) return [];
|
|
6112
|
+
const [users, profiles] = await Promise.all([this.userRepo.read_users_by_ids(filteredIds), this.userProfileRepo.read_user_profiles_by_user_ids(filteredIds)]);
|
|
6113
|
+
const profileByUserId = new Map(profiles.map((p) => [p.user_id, p]));
|
|
6114
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6115
|
+
const recipients = [];
|
|
6116
|
+
for (const user of users) {
|
|
6117
|
+
const email = profileByUserId.get(user.id)?.notification_email ?? user.email;
|
|
6118
|
+
if (!email || seen.has(email)) continue;
|
|
6119
|
+
seen.add(email);
|
|
6120
|
+
recipients.push({
|
|
6121
|
+
email,
|
|
6122
|
+
userType: user.user_type ?? DEFAULT_USER_TYPE
|
|
6123
|
+
});
|
|
6124
|
+
}
|
|
6125
|
+
return recipients;
|
|
6126
|
+
}
|
|
6127
|
+
buildEmailContent(ticket, eventType, ticketUrl, context) {
|
|
6128
|
+
const eventLabel = eventType.replace(/_/g, " ").toLowerCase();
|
|
6129
|
+
const subject = `Support Ticket #${ticket.display_id || ticket.id}: ${eventLabel}`;
|
|
6130
|
+
const details = [
|
|
6131
|
+
`Ticket #: ${ticket.display_id || ticket.id}`,
|
|
6132
|
+
`Title: ${ticket.title}`,
|
|
6133
|
+
`Priority: ${supportTicketNumberToPriority(ticket.priority)}`
|
|
6134
|
+
];
|
|
6135
|
+
if (context?.noteBody) details.push(`\nNote: ${context.noteBody}`);
|
|
6136
|
+
if (eventType === "TICKET_CREATED" && ticket.description?.trim()) details.push(`\nDescription:\n${ticket.description}`);
|
|
6137
|
+
return {
|
|
6138
|
+
subject,
|
|
6139
|
+
bodyText: `A support ticket you're following has been updated.\n\n${details.join("\n")}\n\nView ticket: ${ticketUrl}`,
|
|
6140
|
+
bodyHtml: `
|
|
6141
|
+
<h2>Support Ticket Update</h2>
|
|
6142
|
+
<p>A support ticket you're following has been updated (${eventLabel}).</p>
|
|
6143
|
+
<ul>
|
|
6144
|
+
<li><strong>Ticket #:</strong> ${ticket.display_id || ticket.id}</li>
|
|
6145
|
+
<li><strong>Title:</strong> ${ticket.title}</li>
|
|
6146
|
+
<li><strong>Priority:</strong> ${supportTicketNumberToPriority(ticket.priority)}</li>
|
|
6147
|
+
</ul>
|
|
6148
|
+
${eventType === "TICKET_CREATED" && ticket.description?.trim() ? `<p><strong>Description:</strong></p><p>${escapeHtmlForEmail(ticket.description)}</p>` : ""}
|
|
6149
|
+
${context?.noteBody ? `<p><strong>Note:</strong></p><p>${context.noteBody}</p>` : ""}
|
|
6150
|
+
<p><a href="${ticketUrl}">View Ticket</a></p>
|
|
6151
|
+
`.trim()
|
|
6152
|
+
};
|
|
6153
|
+
}
|
|
6154
|
+
buildAssigneeEmailContent(ticket, eventType, ticketUrl, context) {
|
|
6155
|
+
const eventLabel = eventType.replace(/_/g, " ").toLowerCase();
|
|
6156
|
+
const subject = `Support Ticket #${ticket.display_id || ticket.id}: ${eventLabel}`;
|
|
6157
|
+
const isAssigned = eventType === "TICKET_ASSIGNED";
|
|
6158
|
+
const intro = isAssigned ? "You have been assigned to a support ticket." : "A new support ticket has been assigned to you.";
|
|
6159
|
+
const details = [
|
|
6160
|
+
`Ticket #: ${ticket.display_id || ticket.id}`,
|
|
6161
|
+
`Title: ${ticket.title}`,
|
|
6162
|
+
`Priority: ${supportTicketNumberToPriority(ticket.priority)}`
|
|
6163
|
+
];
|
|
6164
|
+
if (context?.noteBody) details.push(`\nNote: ${context.noteBody}`);
|
|
6165
|
+
if (!isAssigned && ticket.description?.trim()) details.push(`\nDescription:\n${ticket.description}`);
|
|
6166
|
+
return {
|
|
6167
|
+
subject,
|
|
6168
|
+
bodyText: `${intro}\n\n${details.join("\n")}\n\nView ticket: ${ticketUrl}`,
|
|
6169
|
+
bodyHtml: `
|
|
6170
|
+
<h2>Support Ticket ${isAssigned ? "Assigned" : "Created"}</h2>
|
|
6171
|
+
<p>${intro}</p>
|
|
6172
|
+
<ul>
|
|
6173
|
+
<li><strong>Ticket #:</strong> ${ticket.display_id || ticket.id}</li>
|
|
6174
|
+
<li><strong>Title:</strong> ${ticket.title}</li>
|
|
6175
|
+
<li><strong>Priority:</strong> ${supportTicketNumberToPriority(ticket.priority)}</li>
|
|
6176
|
+
</ul>
|
|
6177
|
+
${!isAssigned && ticket.description?.trim() ? `<p><strong>Description:</strong></p><p>${escapeHtmlForEmail(ticket.description)}</p>` : ""}
|
|
6178
|
+
${context?.noteBody ? `<p><strong>Note:</strong></p><p>${context.noteBody}</p>` : ""}
|
|
6179
|
+
<p><a href="${ticketUrl}">View Ticket</a></p>
|
|
6180
|
+
`.trim()
|
|
6181
|
+
};
|
|
6182
|
+
}
|
|
6183
|
+
};
|
|
6184
|
+
SupportTicketNotificationService = __decorate([
|
|
6185
|
+
injectable(),
|
|
6186
|
+
__decorateParam(0, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6187
|
+
__decorateParam(1, inject(USER_TOKENS.IUsersRepo)),
|
|
6188
|
+
__decorateParam(2, inject(USER_PROFILE_TOKENS.IUserProfilesRepo)),
|
|
6189
|
+
__decorateParam(3, inject(TOKENS.IEmailService)),
|
|
6190
|
+
__decorateParam(4, inject(TOKENS.ENV)),
|
|
6191
|
+
__decorateParam(5, inject(TOKENS.LOGGER))
|
|
6192
|
+
], SupportTicketNotificationService);
|
|
6193
|
+
|
|
6194
|
+
//#endregion
|
|
6195
|
+
//#region src/slices/support_ticket/services/support_ticket_triage_service.ts
|
|
6196
|
+
let SupportTicketTriageService = class SupportTicketTriageService$1 {
|
|
6197
|
+
constructor(supportStaffRepo, appSettingsRepo, logger$1) {
|
|
6198
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
6199
|
+
this.appSettingsRepo = appSettingsRepo;
|
|
6200
|
+
this.logger = logger$1;
|
|
6201
|
+
}
|
|
6202
|
+
async getNextAssignee(updatedBy) {
|
|
6203
|
+
try {
|
|
6204
|
+
const triageUsers = await this.supportStaffRepo.readTriageUsers();
|
|
6205
|
+
if (triageUsers.length === 0) {
|
|
6206
|
+
this.logger.debug("No triage users found; skipping assignee");
|
|
6207
|
+
return null;
|
|
6208
|
+
}
|
|
6209
|
+
const ids = triageUsers.map((u) => u.id);
|
|
6210
|
+
const lastId = (await this.appSettingsRepo.readSetting(AppSettingKey.LAST_SUPPORT_TICKET_ASSIGNEE))?.value;
|
|
6211
|
+
const lastIndex = lastId ? ids.indexOf(lastId) : -1;
|
|
6212
|
+
const nextUserId = ids[lastIndex < ids.length - 1 ? lastIndex + 1 : 0];
|
|
6213
|
+
await this.appSettingsRepo.updateSetting(AppSettingKey.LAST_SUPPORT_TICKET_ASSIGNEE, nextUserId, updatedBy);
|
|
6214
|
+
return nextUserId;
|
|
6215
|
+
} catch (error) {
|
|
6216
|
+
this.logger.error("Failed to get next triage assignee", { error });
|
|
6217
|
+
return null;
|
|
6218
|
+
}
|
|
6219
|
+
}
|
|
6220
|
+
};
|
|
6221
|
+
SupportTicketTriageService = __decorate([
|
|
6222
|
+
injectable(),
|
|
6223
|
+
__decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo)),
|
|
6224
|
+
__decorateParam(1, inject(TOKENS.IAppSettingsRepo)),
|
|
6225
|
+
__decorateParam(2, inject(TOKENS.LOGGER))
|
|
6226
|
+
], SupportTicketTriageService);
|
|
5173
6227
|
|
|
5174
6228
|
//#endregion
|
|
5175
6229
|
//#region src/slices/support_ticket/mappers/support_ticket_mapper.ts
|
|
@@ -5188,6 +6242,7 @@ function computeStatus(approvalStatus, devLifecycle) {
|
|
|
5188
6242
|
case "TESTING":
|
|
5189
6243
|
case "STAGING":
|
|
5190
6244
|
case "PO_APPROVAL": return "IN_PROGRESS";
|
|
6245
|
+
case "VERIFICATION": return "VERIFICATION";
|
|
5191
6246
|
case "DEPLOYED": return "COMPLETED";
|
|
5192
6247
|
default: return "FOLLOWUP";
|
|
5193
6248
|
}
|
|
@@ -5221,11 +6276,10 @@ function toCustomerSupportTicketReadDto(entity) {
|
|
|
5221
6276
|
title: entity.title,
|
|
5222
6277
|
description: entity.description,
|
|
5223
6278
|
type: entity.type,
|
|
5224
|
-
priority: entity.priority,
|
|
6279
|
+
priority: supportTicketNumberToPriority(entity.priority),
|
|
5225
6280
|
status: computeStatus(entity.approval_status, entity.dev_lifecycle),
|
|
5226
6281
|
is_locked: !!entity.approval_status && entity.approval_status !== "PENDING",
|
|
5227
|
-
|
|
5228
|
-
requester_email: entity.requester_email,
|
|
6282
|
+
created_by_display_name: null,
|
|
5229
6283
|
credit_value: entity.credit_value,
|
|
5230
6284
|
start_at: startAt,
|
|
5231
6285
|
target_at: targetAt,
|
|
@@ -5234,7 +6288,8 @@ function toCustomerSupportTicketReadDto(entity) {
|
|
|
5234
6288
|
created_at: createdAt,
|
|
5235
6289
|
created_by: entity.created_by,
|
|
5236
6290
|
updated_at: updatedAt,
|
|
5237
|
-
updated_by: entity.updated_by
|
|
6291
|
+
updated_by: entity.updated_by,
|
|
6292
|
+
archived_at: entity.archived_at ?? null
|
|
5238
6293
|
};
|
|
5239
6294
|
}
|
|
5240
6295
|
/**
|
|
@@ -5257,13 +6312,13 @@ function toStaffSupportTicketReadDto(entity) {
|
|
|
5257
6312
|
title: entity.title,
|
|
5258
6313
|
description: entity.description,
|
|
5259
6314
|
type: entity.type,
|
|
5260
|
-
priority: entity.priority,
|
|
6315
|
+
priority: supportTicketNumberToPriority(entity.priority),
|
|
5261
6316
|
status: computeStatus(entity.approval_status, entity.dev_lifecycle),
|
|
5262
6317
|
approval_status: entity.approval_status,
|
|
5263
6318
|
is_locked: !!entity.approval_status && entity.approval_status !== "PENDING",
|
|
5264
6319
|
can_delete: canDelete,
|
|
5265
|
-
|
|
5266
|
-
|
|
6320
|
+
created_by_display_name: null,
|
|
6321
|
+
assigned_to: entity.assigned_to ?? null,
|
|
5267
6322
|
dev_lifecycle: entity.dev_lifecycle || "PENDING",
|
|
5268
6323
|
credit_value: entity.credit_value,
|
|
5269
6324
|
delivered_value: entity.delivered_value,
|
|
@@ -5274,7 +6329,9 @@ function toStaffSupportTicketReadDto(entity) {
|
|
|
5274
6329
|
created_at: createdAt,
|
|
5275
6330
|
created_by: entity.created_by,
|
|
5276
6331
|
updated_at: updatedAt,
|
|
5277
|
-
updated_by: entity.updated_by
|
|
6332
|
+
updated_by: entity.updated_by,
|
|
6333
|
+
archived_at: entity.archived_at ?? null,
|
|
6334
|
+
archived_by: entity.archived_by ?? null
|
|
5278
6335
|
};
|
|
5279
6336
|
}
|
|
5280
6337
|
function dateIsValid(date) {
|
|
@@ -5286,13 +6343,14 @@ function dateIsValid(date) {
|
|
|
5286
6343
|
//#endregion
|
|
5287
6344
|
//#region src/slices/support_ticket/features/customer/create_support_ticket_feat.ts
|
|
5288
6345
|
let CreateSupportTicketFeat = class CreateSupportTicketFeat$1 {
|
|
5289
|
-
constructor(session, support_ticketRepo,
|
|
6346
|
+
constructor(session, support_ticketRepo, subscriberRepo, notificationService, triageService, create_record_version, appSettingsRepo, logger$1) {
|
|
5290
6347
|
this.session = session;
|
|
5291
6348
|
this.support_ticketRepo = support_ticketRepo;
|
|
6349
|
+
this.subscriberRepo = subscriberRepo;
|
|
6350
|
+
this.notificationService = notificationService;
|
|
6351
|
+
this.triageService = triageService;
|
|
5292
6352
|
this.create_record_version = create_record_version;
|
|
5293
6353
|
this.appSettingsRepo = appSettingsRepo;
|
|
5294
|
-
this.emailService = emailService;
|
|
5295
|
-
this.env = env;
|
|
5296
6354
|
this.logger = logger$1;
|
|
5297
6355
|
}
|
|
5298
6356
|
/**
|
|
@@ -5303,9 +6361,8 @@ let CreateSupportTicketFeat = class CreateSupportTicketFeat$1 {
|
|
|
5303
6361
|
const userId = this.session.user.userId;
|
|
5304
6362
|
const frEntity = {
|
|
5305
6363
|
...input,
|
|
6364
|
+
priority: input.priority ?? SUPPORT_TICKET_PRIORITY_TO_NUMBER.MEDIUM,
|
|
5306
6365
|
description: input.description ?? "",
|
|
5307
|
-
requester_email: this.session.user.email,
|
|
5308
|
-
requester_name: this.session.user.username,
|
|
5309
6366
|
created_at: now,
|
|
5310
6367
|
created_by: userId,
|
|
5311
6368
|
updated_at: now,
|
|
@@ -5313,7 +6370,11 @@ let CreateSupportTicketFeat = class CreateSupportTicketFeat$1 {
|
|
|
5313
6370
|
};
|
|
5314
6371
|
const support_ticketCreated = await this.support_ticketRepo.create(frEntity);
|
|
5315
6372
|
support_ticketCreated.display_id = await this.appSettingsRepo.getNextSequentialId(RecordConst.SUPPORT_TICKET, this.session.user.userId);
|
|
5316
|
-
await this.
|
|
6373
|
+
const assigneeId = await this.triageService.getNextAssignee(this.session.user);
|
|
6374
|
+
if (assigneeId) {
|
|
6375
|
+
support_ticketCreated.assigned_to = assigneeId;
|
|
6376
|
+
await this.support_ticketRepo.update(support_ticketCreated);
|
|
6377
|
+
} else await this.support_ticketRepo.update(support_ticketCreated);
|
|
5317
6378
|
await this.create_record_version.execute({
|
|
5318
6379
|
record_id: support_ticketCreated.id,
|
|
5319
6380
|
operation: OperationConst.INSERT,
|
|
@@ -5324,58 +6385,30 @@ let CreateSupportTicketFeat = class CreateSupportTicketFeat$1 {
|
|
|
5324
6385
|
auth_role: this.session.user.user_type,
|
|
5325
6386
|
auth_username: this.session.user.username
|
|
5326
6387
|
});
|
|
5327
|
-
await this.
|
|
5328
|
-
|
|
6388
|
+
await this.addRequesterAsFollower(support_ticketCreated);
|
|
6389
|
+
await this.notificationService.notifyAssignee(support_ticketCreated, "TICKET_CREATED", { actorUserId: userId });
|
|
6390
|
+
await this.notificationService.notifyFollowers(support_ticketCreated.id, support_ticketCreated, "TICKET_CREATED", { actorUserId: userId });
|
|
6391
|
+
return {
|
|
6392
|
+
...toCustomerSupportTicketReadDto(support_ticketCreated),
|
|
6393
|
+
created_by_display_name: this.session.user.email
|
|
6394
|
+
};
|
|
5329
6395
|
}
|
|
5330
6396
|
/**
|
|
5331
|
-
*
|
|
6397
|
+
* Auto-add requester (created_by) as follower
|
|
5332
6398
|
*/
|
|
5333
|
-
async
|
|
6399
|
+
async addRequesterAsFollower(ticket) {
|
|
5334
6400
|
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`);
|
|
6401
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6402
|
+
await this.subscriberRepo.ensureSubscriber({
|
|
6403
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6404
|
+
record_id: ticket.id,
|
|
6405
|
+
user_id: ticket.created_by,
|
|
6406
|
+
subscribed_events: null,
|
|
6407
|
+
created_at: now,
|
|
6408
|
+
created_by: ticket.created_by
|
|
6409
|
+
});
|
|
5377
6410
|
} catch (error) {
|
|
5378
|
-
this.logger.error("Failed to
|
|
6411
|
+
this.logger.error("Failed to add requester as follower", { error });
|
|
5379
6412
|
}
|
|
5380
6413
|
}
|
|
5381
6414
|
};
|
|
@@ -5383,19 +6416,87 @@ CreateSupportTicketFeat = __decorate([
|
|
|
5383
6416
|
injectable(),
|
|
5384
6417
|
__decorateParam(0, injectSession()),
|
|
5385
6418
|
__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.
|
|
6419
|
+
__decorateParam(2, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6420
|
+
__decorateParam(3, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
6421
|
+
__decorateParam(4, inject(SUPPORT_TICKET_TOKENS.ISupportTicketTriageService)),
|
|
6422
|
+
__decorateParam(5, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6423
|
+
__decorateParam(6, inject(TOKENS.IAppSettingsRepo)),
|
|
6424
|
+
__decorateParam(7, inject(TOKENS.LOGGER))
|
|
5391
6425
|
], CreateSupportTicketFeat);
|
|
5392
6426
|
|
|
6427
|
+
//#endregion
|
|
6428
|
+
//#region src/slices/support_ticket/features/customer/customer_toggle_subscription_feat.ts
|
|
6429
|
+
let CustomerToggleSubscriptionFeat = class CustomerToggleSubscriptionFeat$1 {
|
|
6430
|
+
constructor(session, subscriberRepo, supportTicketRepo) {
|
|
6431
|
+
this.session = session;
|
|
6432
|
+
this.subscriberRepo = subscriberRepo;
|
|
6433
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
6434
|
+
}
|
|
6435
|
+
async execute(supportTicketId) {
|
|
6436
|
+
const ticket = await this.supportTicketRepo.read(supportTicketId);
|
|
6437
|
+
if (!ticket) throw new BusinessError("Support ticket not found");
|
|
6438
|
+
if (ticket.approval_status === "INTERNAL") throw new BusinessError("Support ticket not found");
|
|
6439
|
+
const subscription = await this.subscriberRepo.readByRecordAndUser(RecordConst.SUPPORT_TICKET, supportTicketId, this.session.user.userId);
|
|
6440
|
+
if (subscription) {
|
|
6441
|
+
if (!await this.subscriberRepo.delete(subscription.id)) throw new BusinessError("Failed to unsubscribe");
|
|
6442
|
+
return { subscribed: false };
|
|
6443
|
+
}
|
|
6444
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6445
|
+
const userId = this.session.user.userId;
|
|
6446
|
+
await this.subscriberRepo.ensureSubscriber({
|
|
6447
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6448
|
+
record_id: supportTicketId,
|
|
6449
|
+
user_id: userId,
|
|
6450
|
+
subscribed_events: null,
|
|
6451
|
+
created_at: now,
|
|
6452
|
+
created_by: userId
|
|
6453
|
+
});
|
|
6454
|
+
return { subscribed: true };
|
|
6455
|
+
}
|
|
6456
|
+
};
|
|
6457
|
+
CustomerToggleSubscriptionFeat = __decorate([
|
|
6458
|
+
injectable(),
|
|
6459
|
+
__decorateParam(0, injectSession()),
|
|
6460
|
+
__decorateParam(1, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6461
|
+
__decorateParam(2, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo))
|
|
6462
|
+
], CustomerToggleSubscriptionFeat);
|
|
6463
|
+
|
|
6464
|
+
//#endregion
|
|
6465
|
+
//#region src/slices/support_ticket/features/customer/enrich_customer_ticket.ts
|
|
6466
|
+
/**
|
|
6467
|
+
* Enriches a CustomerSupportTicketReadDto with requester info from created_by lookup.
|
|
6468
|
+
* Requester name/email are derived from created_by (user display = email).
|
|
6469
|
+
*/
|
|
6470
|
+
function enrichCustomerTicket(dto, displayMap) {
|
|
6471
|
+
const createdByDisplay = dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null;
|
|
6472
|
+
return {
|
|
6473
|
+
...dto,
|
|
6474
|
+
created_by_display_name: createdByDisplay ?? dto.created_by ?? "—"
|
|
6475
|
+
};
|
|
6476
|
+
}
|
|
6477
|
+
function collectUserIdsFromCustomerTicket(ticket) {
|
|
6478
|
+
return [ticket.created_by].filter((id) => Boolean(id));
|
|
6479
|
+
}
|
|
6480
|
+
|
|
5393
6481
|
//#endregion
|
|
5394
6482
|
//#region src/slices/support_ticket/features/customer/get_support_ticket_customer_feat.ts
|
|
6483
|
+
function toRecordSubscriberReadDto$2(e) {
|
|
6484
|
+
return {
|
|
6485
|
+
id: e.id,
|
|
6486
|
+
record_type: e.record_type,
|
|
6487
|
+
record_id: e.record_id,
|
|
6488
|
+
user_id: e.user_id,
|
|
6489
|
+
subscribed_events: e.subscribed_events ?? null,
|
|
6490
|
+
created_at: e.created_at,
|
|
6491
|
+
created_by: e.created_by
|
|
6492
|
+
};
|
|
6493
|
+
}
|
|
5395
6494
|
let GetSupportTicketCustomerFeature = class GetSupportTicketCustomerFeature$1 {
|
|
5396
|
-
constructor(session, support_ticketRepo, appSettingsRepo, logger$1) {
|
|
6495
|
+
constructor(session, support_ticketRepo, subscriberRepo, userDisplayLookup, appSettingsRepo, logger$1) {
|
|
5397
6496
|
this.session = session;
|
|
5398
6497
|
this.support_ticketRepo = support_ticketRepo;
|
|
6498
|
+
this.subscriberRepo = subscriberRepo;
|
|
6499
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5399
6500
|
this.appSettingsRepo = appSettingsRepo;
|
|
5400
6501
|
this.logger = logger$1;
|
|
5401
6502
|
}
|
|
@@ -5413,22 +6514,31 @@ let GetSupportTicketCustomerFeature = class GetSupportTicketCustomerFeature$1 {
|
|
|
5413
6514
|
updated_by: this.session.user.userId
|
|
5414
6515
|
});
|
|
5415
6516
|
}
|
|
5416
|
-
|
|
6517
|
+
const base = toCustomerSupportTicketReadDto(support_ticket);
|
|
6518
|
+
const enriched = enrichCustomerTicket(base, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromCustomerTicket(base)));
|
|
6519
|
+
const subscription = await this.subscriberRepo.readByRecordAndUser(RecordConst.SUPPORT_TICKET, id, this.session.user.userId);
|
|
6520
|
+
return {
|
|
6521
|
+
...enriched,
|
|
6522
|
+
my_subscription: subscription ? toRecordSubscriberReadDto$2(subscription) : null
|
|
6523
|
+
};
|
|
5417
6524
|
}
|
|
5418
6525
|
};
|
|
5419
6526
|
GetSupportTicketCustomerFeature = __decorate([
|
|
5420
6527
|
injectable(),
|
|
5421
6528
|
__decorateParam(0, injectSession()),
|
|
5422
6529
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5423
|
-
__decorateParam(2, inject(
|
|
5424
|
-
__decorateParam(3, inject(
|
|
6530
|
+
__decorateParam(2, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6531
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
6532
|
+
__decorateParam(4, inject(TOKENS.IAppSettingsRepo)),
|
|
6533
|
+
__decorateParam(5, inject(TOKENS.LOGGER))
|
|
5425
6534
|
], GetSupportTicketCustomerFeature);
|
|
5426
6535
|
|
|
5427
6536
|
//#endregion
|
|
5428
6537
|
//#region src/slices/support_ticket/features/customer/get_support_tickets_customer_feat.ts
|
|
5429
6538
|
let GetSupportTicketsCustomerFeature = class GetSupportTicketsCustomerFeature$1 {
|
|
5430
|
-
constructor(support_ticketRepo) {
|
|
6539
|
+
constructor(support_ticketRepo, userDisplayLookup) {
|
|
5431
6540
|
this.support_ticketRepo = support_ticketRepo;
|
|
6541
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5432
6542
|
}
|
|
5433
6543
|
async execute(filters) {
|
|
5434
6544
|
const customerFilters = {
|
|
@@ -5439,20 +6549,28 @@ let GetSupportTicketsCustomerFeature = class GetSupportTicketsCustomerFeature$1
|
|
|
5439
6549
|
}
|
|
5440
6550
|
};
|
|
5441
6551
|
const result = await this.support_ticketRepo.read_all(customerFilters);
|
|
6552
|
+
const dtos = result.items.map(toCustomerSupportTicketReadDto);
|
|
6553
|
+
const userIds = [...new Set(dtos.flatMap(collectUserIdsFromCustomerTicket))];
|
|
6554
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
5442
6555
|
return {
|
|
5443
|
-
items:
|
|
6556
|
+
items: dtos.map((dto) => enrichCustomerTicket(dto, displayMap)),
|
|
5444
6557
|
pageInfo: result.pageInfo
|
|
5445
6558
|
};
|
|
5446
6559
|
}
|
|
5447
6560
|
};
|
|
5448
|
-
GetSupportTicketsCustomerFeature = __decorate([
|
|
6561
|
+
GetSupportTicketsCustomerFeature = __decorate([
|
|
6562
|
+
injectable(),
|
|
6563
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6564
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6565
|
+
], GetSupportTicketsCustomerFeature);
|
|
5449
6566
|
|
|
5450
6567
|
//#endregion
|
|
5451
6568
|
//#region src/slices/support_ticket/features/customer/update_support_ticket_customer_feat.ts
|
|
5452
6569
|
let UpdateSupportTicketCustomerFeat = class UpdateSupportTicketCustomerFeat$1 {
|
|
5453
|
-
constructor(session, support_ticketRepo, create_record_version) {
|
|
6570
|
+
constructor(session, support_ticketRepo, userDisplayLookup, create_record_version) {
|
|
5454
6571
|
this.session = session;
|
|
5455
6572
|
this.support_ticketRepo = support_ticketRepo;
|
|
6573
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5456
6574
|
this.create_record_version = create_record_version;
|
|
5457
6575
|
}
|
|
5458
6576
|
/**
|
|
@@ -5460,8 +6578,9 @@ let UpdateSupportTicketCustomerFeat = class UpdateSupportTicketCustomerFeat$1 {
|
|
|
5460
6578
|
*/
|
|
5461
6579
|
async execute(input) {
|
|
5462
6580
|
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
|
|
6581
|
+
if (!support_ticket) throw new BusinessError("SupportTicket not found");
|
|
6582
|
+
if (support_ticket.approval_status !== "PENDING") throw new BusinessError("SupportTicket is locked - cannot be edited");
|
|
6583
|
+
if (support_ticket.archived_at) throw new BusinessError("Cannot edit an archived support ticket.");
|
|
5465
6584
|
const frEntity = {
|
|
5466
6585
|
...support_ticket,
|
|
5467
6586
|
title: input.title,
|
|
@@ -5483,24 +6602,102 @@ let UpdateSupportTicketCustomerFeat = class UpdateSupportTicketCustomerFeat$1 {
|
|
|
5483
6602
|
auth_role: this.session.user.user_type,
|
|
5484
6603
|
auth_username: this.session.user.username
|
|
5485
6604
|
});
|
|
5486
|
-
|
|
6605
|
+
const base = toCustomerSupportTicketReadDto(await this.support_ticketRepo.update(frEntity));
|
|
6606
|
+
return enrichCustomerTicket(base, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromCustomerTicket(base)));
|
|
5487
6607
|
}
|
|
5488
6608
|
};
|
|
5489
6609
|
UpdateSupportTicketCustomerFeat = __decorate([
|
|
5490
6610
|
injectable(),
|
|
5491
6611
|
__decorateParam(0, injectSession()),
|
|
5492
6612
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5493
|
-
__decorateParam(2, inject(
|
|
6613
|
+
__decorateParam(2, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
6614
|
+
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
5494
6615
|
], UpdateSupportTicketCustomerFeat);
|
|
5495
6616
|
|
|
6617
|
+
//#endregion
|
|
6618
|
+
//#region src/slices/support_ticket/features/staff/add_support_ticket_subscriber_feat.ts
|
|
6619
|
+
function toRecordSubscriberReadDto$1(e) {
|
|
6620
|
+
return {
|
|
6621
|
+
id: e.id,
|
|
6622
|
+
record_type: e.record_type,
|
|
6623
|
+
record_id: e.record_id,
|
|
6624
|
+
user_id: e.user_id,
|
|
6625
|
+
subscribed_events: e.subscribed_events ?? null,
|
|
6626
|
+
created_at: e.created_at,
|
|
6627
|
+
created_by: e.created_by
|
|
6628
|
+
};
|
|
6629
|
+
}
|
|
6630
|
+
let AddSupportTicketSubscriberFeat = class AddSupportTicketSubscriberFeat$1 {
|
|
6631
|
+
constructor(subscriberRepo, supportTicketRepo, session, userDisplayLookup) {
|
|
6632
|
+
this.subscriberRepo = subscriberRepo;
|
|
6633
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
6634
|
+
this.session = session;
|
|
6635
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6636
|
+
}
|
|
6637
|
+
async execute(input) {
|
|
6638
|
+
if (!await this.supportTicketRepo.read(input.support_ticket_id)) throw new BusinessError("Support ticket not found");
|
|
6639
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6640
|
+
const dto = toRecordSubscriberReadDto$1(await this.subscriberRepo.ensureSubscriber({
|
|
6641
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6642
|
+
record_id: input.support_ticket_id,
|
|
6643
|
+
user_id: input.user_id,
|
|
6644
|
+
subscribed_events: input.subscribed_events ?? null,
|
|
6645
|
+
created_at: now,
|
|
6646
|
+
created_by: this.session.user.userId
|
|
6647
|
+
}));
|
|
6648
|
+
const userIds = [dto.user_id, dto.created_by].filter(Boolean);
|
|
6649
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
6650
|
+
return {
|
|
6651
|
+
...dto,
|
|
6652
|
+
user_id_display_name: dto.user_id ? displayMap.get(dto.user_id) ?? dto.user_id : null,
|
|
6653
|
+
created_by_display_name: dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null
|
|
6654
|
+
};
|
|
6655
|
+
}
|
|
6656
|
+
};
|
|
6657
|
+
AddSupportTicketSubscriberFeat = __decorate([
|
|
6658
|
+
injectable(),
|
|
6659
|
+
__decorateParam(0, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
6660
|
+
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6661
|
+
__decorateParam(2, injectSession()),
|
|
6662
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6663
|
+
], AddSupportTicketSubscriberFeat);
|
|
6664
|
+
|
|
6665
|
+
//#endregion
|
|
6666
|
+
//#region src/slices/support_ticket/features/staff/enrich_staff_ticket.ts
|
|
6667
|
+
/**
|
|
6668
|
+
* Enriches a StaffSupportTicketReadDto with display names from a lookup map.
|
|
6669
|
+
* Used by all staff features that return a ticket.
|
|
6670
|
+
*/
|
|
6671
|
+
function enrichStaffTicket(dto, displayMap) {
|
|
6672
|
+
const createdByDisplay = dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null;
|
|
6673
|
+
return {
|
|
6674
|
+
...dto,
|
|
6675
|
+
assigned_to_display_name: dto.assigned_to ? displayMap.get(dto.assigned_to) ?? dto.assigned_to : null,
|
|
6676
|
+
created_by_display_name: createdByDisplay ?? dto.created_by ?? "—",
|
|
6677
|
+
updated_by_display_name: dto.updated_by ? displayMap.get(dto.updated_by) ?? dto.updated_by : null
|
|
6678
|
+
};
|
|
6679
|
+
}
|
|
6680
|
+
/**
|
|
6681
|
+
* Collects user IDs from a staff ticket for display name lookup.
|
|
6682
|
+
*/
|
|
6683
|
+
function collectUserIdsFromStaffTicket(ticket) {
|
|
6684
|
+
return [
|
|
6685
|
+
ticket.assigned_to,
|
|
6686
|
+
ticket.created_by,
|
|
6687
|
+
ticket.updated_by
|
|
6688
|
+
].filter((id) => Boolean(id));
|
|
6689
|
+
}
|
|
6690
|
+
|
|
5496
6691
|
//#endregion
|
|
5497
6692
|
//#region src/slices/support_ticket/features/staff/approve_support_ticket_feat.ts
|
|
5498
6693
|
let ApproveSupportTicketFeat = class ApproveSupportTicketFeat$1 {
|
|
5499
|
-
constructor(repo, creditService, create_record_version, session) {
|
|
6694
|
+
constructor(repo, notificationService, creditService, create_record_version, session, userDisplayLookup) {
|
|
5500
6695
|
this.repo = repo;
|
|
6696
|
+
this.notificationService = notificationService;
|
|
5501
6697
|
this.creditService = creditService;
|
|
5502
6698
|
this.create_record_version = create_record_version;
|
|
5503
6699
|
this.session = session;
|
|
6700
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5504
6701
|
}
|
|
5505
6702
|
/**
|
|
5506
6703
|
* Approves a support_ticket item, deducts credits, and locks it.
|
|
@@ -5516,15 +6713,18 @@ let ApproveSupportTicketFeat = class ApproveSupportTicketFeat$1 {
|
|
|
5516
6713
|
* @returns The updated support_ticket with full staff view.
|
|
5517
6714
|
*/
|
|
5518
6715
|
async execute(args) {
|
|
5519
|
-
const { id } = args;
|
|
6716
|
+
const { id, credit_value: creditValueFromArgs } = args;
|
|
5520
6717
|
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
|
-
|
|
6718
|
+
if (!support_ticket) throw new BusinessError(`Support Ticket with ID ${id} not found`);
|
|
6719
|
+
if (support_ticket.approval_status !== "PENDING") throw new BusinessError("Support Ticket must be in PENDING status to be approved.");
|
|
6720
|
+
const creditAmount = parseFloat(creditValueFromArgs);
|
|
6721
|
+
if (isNaN(creditAmount) || creditAmount < 0) throw new BusinessError("Invalid credit value amount.");
|
|
6722
|
+
const ticketToUpdate = {
|
|
6723
|
+
...support_ticket,
|
|
6724
|
+
credit_value: creditValueFromArgs
|
|
6725
|
+
};
|
|
6726
|
+
if (support_ticket.credit_value?.trim() !== creditValueFromArgs) await this.repo.update(ticketToUpdate);
|
|
6727
|
+
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
6728
|
const lockedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5529
6729
|
const changed_props = getChangedProperties(support_ticket, {
|
|
5530
6730
|
...support_ticket,
|
|
@@ -5549,25 +6749,101 @@ let ApproveSupportTicketFeat = class ApproveSupportTicketFeat$1 {
|
|
|
5549
6749
|
dev_lifecycle: "BACKLOG",
|
|
5550
6750
|
locked_approval_at: lockedAt
|
|
5551
6751
|
});
|
|
5552
|
-
if (!updatedSupportTicket) throw new
|
|
5553
|
-
|
|
6752
|
+
if (!updatedSupportTicket) throw new BusinessError("Failed to approve support_ticket.");
|
|
6753
|
+
await this.notificationService.notifyFollowers(id, updatedSupportTicket, "APPROVAL_STATUS_CHANGED", { actorUserId: this.session.user.userId });
|
|
6754
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
6755
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5554
6756
|
}
|
|
5555
6757
|
};
|
|
5556
6758
|
ApproveSupportTicketFeat = __decorate([
|
|
5557
6759
|
injectable(),
|
|
5558
6760
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5559
|
-
__decorateParam(1, inject(
|
|
5560
|
-
__decorateParam(2, inject(
|
|
5561
|
-
__decorateParam(3,
|
|
6761
|
+
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
6762
|
+
__decorateParam(2, inject(CREDIT_SERVICE_TOKEN)),
|
|
6763
|
+
__decorateParam(3, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6764
|
+
__decorateParam(4, injectSession()),
|
|
6765
|
+
__decorateParam(5, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5562
6766
|
], ApproveSupportTicketFeat);
|
|
5563
6767
|
|
|
6768
|
+
//#endregion
|
|
6769
|
+
//#region src/slices/support_ticket/features/staff/archive_support_ticket_feat.ts
|
|
6770
|
+
let ArchiveSupportTicketFeat = class ArchiveSupportTicketFeat$1 {
|
|
6771
|
+
constructor(repo, create_record_version, session, userDisplayLookup) {
|
|
6772
|
+
this.repo = repo;
|
|
6773
|
+
this.create_record_version = create_record_version;
|
|
6774
|
+
this.session = session;
|
|
6775
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6776
|
+
}
|
|
6777
|
+
async execute(id) {
|
|
6778
|
+
const support_ticket = await this.repo.read(id);
|
|
6779
|
+
if (!support_ticket) throw new Error(`Support ticket with id ${id} not found`);
|
|
6780
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6781
|
+
const userId = this.session.user.userId;
|
|
6782
|
+
if (support_ticket.archived_at) {
|
|
6783
|
+
await this.create_record_version.execute({
|
|
6784
|
+
record_id: id,
|
|
6785
|
+
operation: OperationConst.UPDATE,
|
|
6786
|
+
recorded_at: now,
|
|
6787
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6788
|
+
record: {
|
|
6789
|
+
archived_at: null,
|
|
6790
|
+
archived_by: null
|
|
6791
|
+
},
|
|
6792
|
+
old_record: support_ticket,
|
|
6793
|
+
auth_uid: userId,
|
|
6794
|
+
auth_role: this.session.user.user_type,
|
|
6795
|
+
auth_username: this.session.user.username
|
|
6796
|
+
});
|
|
6797
|
+
const updated$1 = await this.repo.update({
|
|
6798
|
+
...support_ticket,
|
|
6799
|
+
archived_at: null,
|
|
6800
|
+
archived_by: null
|
|
6801
|
+
});
|
|
6802
|
+
if (!updated$1) throw new Error("Failed to unarchive support ticket.");
|
|
6803
|
+
const dto$1 = toStaffSupportTicketReadDto(updated$1);
|
|
6804
|
+
return enrichStaffTicket(dto$1, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto$1)));
|
|
6805
|
+
}
|
|
6806
|
+
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.");
|
|
6807
|
+
await this.create_record_version.execute({
|
|
6808
|
+
record_id: id,
|
|
6809
|
+
operation: OperationConst.UPDATE,
|
|
6810
|
+
recorded_at: now,
|
|
6811
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
6812
|
+
record: {
|
|
6813
|
+
archived_at: now,
|
|
6814
|
+
archived_by: userId
|
|
6815
|
+
},
|
|
6816
|
+
old_record: support_ticket,
|
|
6817
|
+
auth_uid: userId,
|
|
6818
|
+
auth_role: this.session.user.user_type,
|
|
6819
|
+
auth_username: this.session.user.username
|
|
6820
|
+
});
|
|
6821
|
+
const updated = await this.repo.update({
|
|
6822
|
+
...support_ticket,
|
|
6823
|
+
archived_at: now,
|
|
6824
|
+
archived_by: userId
|
|
6825
|
+
});
|
|
6826
|
+
if (!updated) throw new Error("Failed to archive support ticket.");
|
|
6827
|
+
const dto = toStaffSupportTicketReadDto(updated);
|
|
6828
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6829
|
+
}
|
|
6830
|
+
};
|
|
6831
|
+
ArchiveSupportTicketFeat = __decorate([
|
|
6832
|
+
injectable(),
|
|
6833
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6834
|
+
__decorateParam(1, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6835
|
+
__decorateParam(2, injectSession()),
|
|
6836
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6837
|
+
], ArchiveSupportTicketFeat);
|
|
6838
|
+
|
|
5564
6839
|
//#endregion
|
|
5565
6840
|
//#region src/slices/support_ticket/features/staff/cancel_internal_task_feat.ts
|
|
5566
6841
|
let CancelInternalTaskFeat = class CancelInternalTaskFeat$1 {
|
|
5567
|
-
constructor(repo, create_record_version, session) {
|
|
6842
|
+
constructor(repo, create_record_version, session, userDisplayLookup) {
|
|
5568
6843
|
this.repo = repo;
|
|
5569
6844
|
this.create_record_version = create_record_version;
|
|
5570
6845
|
this.session = session;
|
|
6846
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5571
6847
|
}
|
|
5572
6848
|
/**
|
|
5573
6849
|
* Cancels an internal task by setting dev_lifecycle to CANCELLED.
|
|
@@ -5612,23 +6888,26 @@ let CancelInternalTaskFeat = class CancelInternalTaskFeat$1 {
|
|
|
5612
6888
|
completed_at: completedAt
|
|
5613
6889
|
});
|
|
5614
6890
|
if (!updatedSupportTicket) throw new Error("Failed to cancel internal task.");
|
|
5615
|
-
|
|
6891
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
6892
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5616
6893
|
}
|
|
5617
6894
|
};
|
|
5618
6895
|
CancelInternalTaskFeat = __decorate([
|
|
5619
6896
|
injectable(),
|
|
5620
6897
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5621
6898
|
__decorateParam(1, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
5622
|
-
__decorateParam(2, injectSession())
|
|
6899
|
+
__decorateParam(2, injectSession()),
|
|
6900
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5623
6901
|
], CancelInternalTaskFeat);
|
|
5624
6902
|
|
|
5625
6903
|
//#endregion
|
|
5626
6904
|
//#region src/slices/support_ticket/features/staff/complete_support_ticket_feat.ts
|
|
5627
6905
|
let CompleteSupportTicketFeat = class CompleteSupportTicketFeat$1 {
|
|
5628
|
-
constructor(repo, create_record_version, session) {
|
|
6906
|
+
constructor(repo, create_record_version, session, userDisplayLookup) {
|
|
5629
6907
|
this.repo = repo;
|
|
5630
6908
|
this.create_record_version = create_record_version;
|
|
5631
6909
|
this.session = session;
|
|
6910
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5632
6911
|
}
|
|
5633
6912
|
/**
|
|
5634
6913
|
* Completes a support_ticket item by setting it to DEPLOYED with delivered value.
|
|
@@ -5674,23 +6953,26 @@ let CompleteSupportTicketFeat = class CompleteSupportTicketFeat$1 {
|
|
|
5674
6953
|
completed_at: completedAt
|
|
5675
6954
|
});
|
|
5676
6955
|
if (!updatedSupportTicket) throw new Error("Failed to complete support_ticket.");
|
|
5677
|
-
|
|
6956
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
6957
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5678
6958
|
}
|
|
5679
6959
|
};
|
|
5680
6960
|
CompleteSupportTicketFeat = __decorate([
|
|
5681
6961
|
injectable(),
|
|
5682
6962
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5683
6963
|
__decorateParam(1, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
5684
|
-
__decorateParam(2, injectSession())
|
|
6964
|
+
__decorateParam(2, injectSession()),
|
|
6965
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5685
6966
|
], CompleteSupportTicketFeat);
|
|
5686
6967
|
|
|
5687
6968
|
//#endregion
|
|
5688
6969
|
//#region src/slices/support_ticket/features/staff/convert_to_customer_feat.ts
|
|
5689
6970
|
let ConvertToCustomerFeat = class ConvertToCustomerFeat$1 {
|
|
5690
|
-
constructor(session, support_ticketRepo, create_record_version) {
|
|
6971
|
+
constructor(session, support_ticketRepo, create_record_version, userDisplayLookup) {
|
|
5691
6972
|
this.session = session;
|
|
5692
6973
|
this.support_ticketRepo = support_ticketRepo;
|
|
5693
6974
|
this.create_record_version = create_record_version;
|
|
6975
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5694
6976
|
}
|
|
5695
6977
|
async execute(support_ticket_id) {
|
|
5696
6978
|
const existing = await this.support_ticketRepo.read(support_ticket_id);
|
|
@@ -5701,7 +6983,7 @@ let ConvertToCustomerFeat = class ConvertToCustomerFeat$1 {
|
|
|
5701
6983
|
approval_status: "PENDING",
|
|
5702
6984
|
dev_lifecycle: null,
|
|
5703
6985
|
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5704
|
-
updated_by: this.session.user.
|
|
6986
|
+
updated_by: this.session.user.userId
|
|
5705
6987
|
};
|
|
5706
6988
|
const updated = await this.support_ticketRepo.update(updateEntity);
|
|
5707
6989
|
await this.create_record_version.execute({
|
|
@@ -5715,23 +6997,26 @@ let ConvertToCustomerFeat = class ConvertToCustomerFeat$1 {
|
|
|
5715
6997
|
auth_role: this.session.user.user_type,
|
|
5716
6998
|
auth_username: this.session.user.username
|
|
5717
6999
|
});
|
|
5718
|
-
|
|
7000
|
+
const dto = toStaffSupportTicketReadDto(updated);
|
|
7001
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5719
7002
|
}
|
|
5720
7003
|
};
|
|
5721
7004
|
ConvertToCustomerFeat = __decorate([
|
|
5722
7005
|
injectable(),
|
|
5723
7006
|
__decorateParam(0, injectSession()),
|
|
5724
7007
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5725
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
7008
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7009
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5726
7010
|
], ConvertToCustomerFeat);
|
|
5727
7011
|
|
|
5728
7012
|
//#endregion
|
|
5729
7013
|
//#region src/slices/support_ticket/features/staff/convert_to_internal_feat.ts
|
|
5730
7014
|
let ConvertToInternalFeat = class ConvertToInternalFeat$1 {
|
|
5731
|
-
constructor(session, support_ticketRepo, create_record_version) {
|
|
7015
|
+
constructor(session, support_ticketRepo, create_record_version, userDisplayLookup) {
|
|
5732
7016
|
this.session = session;
|
|
5733
7017
|
this.support_ticketRepo = support_ticketRepo;
|
|
5734
7018
|
this.create_record_version = create_record_version;
|
|
7019
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5735
7020
|
}
|
|
5736
7021
|
async execute(support_ticket_id) {
|
|
5737
7022
|
const existing = await this.support_ticketRepo.read(support_ticket_id);
|
|
@@ -5743,7 +7028,7 @@ let ConvertToInternalFeat = class ConvertToInternalFeat$1 {
|
|
|
5743
7028
|
credit_value: null,
|
|
5744
7029
|
dev_lifecycle: "BACKLOG",
|
|
5745
7030
|
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5746
|
-
updated_by: this.session.user.
|
|
7031
|
+
updated_by: this.session.user.userId
|
|
5747
7032
|
};
|
|
5748
7033
|
const updated = await this.support_ticketRepo.update(updateEntity);
|
|
5749
7034
|
await this.create_record_version.execute({
|
|
@@ -5757,26 +7042,31 @@ let ConvertToInternalFeat = class ConvertToInternalFeat$1 {
|
|
|
5757
7042
|
auth_role: this.session.user.user_type,
|
|
5758
7043
|
auth_username: this.session.user.username
|
|
5759
7044
|
});
|
|
5760
|
-
|
|
7045
|
+
const dto = toStaffSupportTicketReadDto(updated);
|
|
7046
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5761
7047
|
}
|
|
5762
7048
|
};
|
|
5763
7049
|
ConvertToInternalFeat = __decorate([
|
|
5764
7050
|
injectable(),
|
|
5765
7051
|
__decorateParam(0, injectSession()),
|
|
5766
7052
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5767
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
7053
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7054
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5768
7055
|
], ConvertToInternalFeat);
|
|
5769
7056
|
|
|
5770
7057
|
//#endregion
|
|
5771
7058
|
//#region src/slices/support_ticket/features/staff/create_support_ticket_admin_feat.ts
|
|
5772
7059
|
let CreateSupportTicketAdminFeat = class CreateSupportTicketAdminFeat$1 {
|
|
5773
|
-
constructor(session, support_ticketRepo,
|
|
7060
|
+
constructor(session, support_ticketRepo, subscriberRepo, notificationService, triageService, supportStaffRepo, userDisplayLookup, create_record_version, appSettingsRepo, logger$1) {
|
|
5774
7061
|
this.session = session;
|
|
5775
7062
|
this.support_ticketRepo = support_ticketRepo;
|
|
7063
|
+
this.subscriberRepo = subscriberRepo;
|
|
7064
|
+
this.notificationService = notificationService;
|
|
7065
|
+
this.triageService = triageService;
|
|
7066
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
7067
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5776
7068
|
this.create_record_version = create_record_version;
|
|
5777
7069
|
this.appSettingsRepo = appSettingsRepo;
|
|
5778
|
-
this.emailService = emailService;
|
|
5779
|
-
this.env = env;
|
|
5780
7070
|
this.logger = logger$1;
|
|
5781
7071
|
}
|
|
5782
7072
|
/**
|
|
@@ -5789,10 +7079,8 @@ let CreateSupportTicketAdminFeat = class CreateSupportTicketAdminFeat$1 {
|
|
|
5789
7079
|
title: input.title || "",
|
|
5790
7080
|
description: input.description || "",
|
|
5791
7081
|
type: input.type || "FEATURE_REQUEST",
|
|
5792
|
-
priority: input.priority
|
|
7082
|
+
priority: input.priority ?? SUPPORT_TICKET_PRIORITY_TO_NUMBER.MEDIUM,
|
|
5793
7083
|
approval_status: input.is_internal ? "INTERNAL" : "PENDING",
|
|
5794
|
-
requester_email: this.session.user.email,
|
|
5795
|
-
requester_name: this.session.user.username,
|
|
5796
7084
|
dev_lifecycle: input.dev_lifecycle || null,
|
|
5797
7085
|
credit_value: input.is_internal ? null : formatCreditValue(input.credit_value),
|
|
5798
7086
|
delivered_value: input.delivered_value || null,
|
|
@@ -5807,6 +7095,12 @@ let CreateSupportTicketAdminFeat = class CreateSupportTicketAdminFeat$1 {
|
|
|
5807
7095
|
};
|
|
5808
7096
|
const support_ticketCreated = await this.support_ticketRepo.create(support_ticketEntity);
|
|
5809
7097
|
support_ticketCreated.display_id = await this.appSettingsRepo.getNextSequentialId(RecordConst.SUPPORT_TICKET, this.session.user.userId);
|
|
7098
|
+
let assigneeId = null;
|
|
7099
|
+
if (input.assigned_to?.trim()) {
|
|
7100
|
+
if (!(await this.supportStaffRepo.readTriageUsers()).some((u) => u.id === input.assigned_to)) throw new BusinessError("Selected assignee is not a support staff member");
|
|
7101
|
+
assigneeId = input.assigned_to;
|
|
7102
|
+
} else assigneeId = await this.triageService.getNextAssignee(this.session.user);
|
|
7103
|
+
if (assigneeId) support_ticketCreated.assigned_to = assigneeId;
|
|
5810
7104
|
await this.support_ticketRepo.update(support_ticketCreated);
|
|
5811
7105
|
await this.create_record_version.execute({
|
|
5812
7106
|
record_id: support_ticketCreated.id,
|
|
@@ -5818,58 +7112,30 @@ let CreateSupportTicketAdminFeat = class CreateSupportTicketAdminFeat$1 {
|
|
|
5818
7112
|
auth_role: this.session.user.user_type,
|
|
5819
7113
|
auth_username: this.session.user.username
|
|
5820
7114
|
});
|
|
5821
|
-
if (!input.is_internal)
|
|
5822
|
-
|
|
7115
|
+
if (!input.is_internal) {
|
|
7116
|
+
await this.addRequesterAsFollower(support_ticketCreated);
|
|
7117
|
+
await this.notificationService.notifyAssignee(support_ticketCreated, "TICKET_CREATED", { actorUserId: userId });
|
|
7118
|
+
await this.notificationService.notifyFollowers(support_ticketCreated.id, support_ticketCreated, "TICKET_CREATED", { actorUserId: userId });
|
|
7119
|
+
}
|
|
7120
|
+
const dto = toStaffSupportTicketReadDto(support_ticketCreated);
|
|
7121
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5823
7122
|
}
|
|
5824
7123
|
/**
|
|
5825
|
-
*
|
|
7124
|
+
* Auto-add requester (created_by) as follower
|
|
5826
7125
|
*/
|
|
5827
|
-
async
|
|
7126
|
+
async addRequesterAsFollower(ticket) {
|
|
5828
7127
|
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`);
|
|
7128
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7129
|
+
await this.subscriberRepo.ensureSubscriber({
|
|
7130
|
+
record_type: RecordConst.SUPPORT_TICKET,
|
|
7131
|
+
record_id: ticket.id,
|
|
7132
|
+
user_id: ticket.created_by,
|
|
7133
|
+
subscribed_events: null,
|
|
7134
|
+
created_at: now,
|
|
7135
|
+
created_by: ticket.created_by
|
|
7136
|
+
});
|
|
5871
7137
|
} catch (error) {
|
|
5872
|
-
this.logger.error("Failed to
|
|
7138
|
+
this.logger.error("Failed to add requester as follower", { error });
|
|
5873
7139
|
}
|
|
5874
7140
|
}
|
|
5875
7141
|
};
|
|
@@ -5877,11 +7143,14 @@ CreateSupportTicketAdminFeat = __decorate([
|
|
|
5877
7143
|
injectable(),
|
|
5878
7144
|
__decorateParam(0, injectSession()),
|
|
5879
7145
|
__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(
|
|
7146
|
+
__decorateParam(2, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
7147
|
+
__decorateParam(3, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
7148
|
+
__decorateParam(4, inject(SUPPORT_TICKET_TOKENS.ISupportTicketTriageService)),
|
|
7149
|
+
__decorateParam(5, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo)),
|
|
7150
|
+
__decorateParam(6, inject(USER_TOKENS.IUserDisplayLookup)),
|
|
7151
|
+
__decorateParam(7, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7152
|
+
__decorateParam(8, inject(TOKENS.IAppSettingsRepo)),
|
|
7153
|
+
__decorateParam(9, inject(TOKENS.LOGGER))
|
|
5885
7154
|
], CreateSupportTicketAdminFeat);
|
|
5886
7155
|
|
|
5887
7156
|
//#endregion
|
|
@@ -5928,13 +7197,93 @@ DeleteSupportTicketFeat = __decorate([
|
|
|
5928
7197
|
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
5929
7198
|
], DeleteSupportTicketFeat);
|
|
5930
7199
|
|
|
7200
|
+
//#endregion
|
|
7201
|
+
//#region src/slices/support_ticket/features/staff/fix_support_ticket_user_ids_feat.ts
|
|
7202
|
+
const UNIVERSAL_ID_LENGTH = 32;
|
|
7203
|
+
let FixSupportTicketUserIdsFeature = class FixSupportTicketUserIdsFeature$1 {
|
|
7204
|
+
constructor(supportTicketRepo, userRepo) {
|
|
7205
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
7206
|
+
this.userRepo = userRepo;
|
|
7207
|
+
}
|
|
7208
|
+
async execute() {
|
|
7209
|
+
const tickets = await this.supportTicketRepo.findWithInvalidUserIds();
|
|
7210
|
+
if (tickets.length === 0) return {
|
|
7211
|
+
ticketsScanned: 0,
|
|
7212
|
+
ticketsFixed: 0,
|
|
7213
|
+
ticketsSkipped: 0
|
|
7214
|
+
};
|
|
7215
|
+
const emailToUserId = await this.buildEmailToUserIdMap(tickets);
|
|
7216
|
+
let ticketsFixed = 0;
|
|
7217
|
+
let ticketsSkipped = 0;
|
|
7218
|
+
for (const ticket of tickets) {
|
|
7219
|
+
const updates = this.buildUpdates(ticket, emailToUserId);
|
|
7220
|
+
if (Object.keys(updates).length === 0) {
|
|
7221
|
+
ticketsSkipped++;
|
|
7222
|
+
continue;
|
|
7223
|
+
}
|
|
7224
|
+
const updated = {
|
|
7225
|
+
...ticket,
|
|
7226
|
+
...updates
|
|
7227
|
+
};
|
|
7228
|
+
await this.supportTicketRepo.update(updated);
|
|
7229
|
+
ticketsFixed++;
|
|
7230
|
+
}
|
|
7231
|
+
return {
|
|
7232
|
+
ticketsScanned: tickets.length,
|
|
7233
|
+
ticketsFixed,
|
|
7234
|
+
ticketsSkipped
|
|
7235
|
+
};
|
|
7236
|
+
}
|
|
7237
|
+
async buildEmailToUserIdMap(tickets) {
|
|
7238
|
+
const fieldsToFix = [
|
|
7239
|
+
"created_by",
|
|
7240
|
+
"updated_by",
|
|
7241
|
+
"assigned_to",
|
|
7242
|
+
"archived_by",
|
|
7243
|
+
"deleted_by"
|
|
7244
|
+
];
|
|
7245
|
+
const emails = /* @__PURE__ */ new Set();
|
|
7246
|
+
for (const ticket of tickets) for (const field of fieldsToFix) {
|
|
7247
|
+
const value = ticket[field];
|
|
7248
|
+
if (value != null && value.length !== UNIVERSAL_ID_LENGTH) emails.add(value);
|
|
7249
|
+
}
|
|
7250
|
+
if (emails.size === 0) return /* @__PURE__ */ new Map();
|
|
7251
|
+
const users = await this.userRepo.read_users_by_emails_case_insensitive([...emails]);
|
|
7252
|
+
const map = /* @__PURE__ */ new Map();
|
|
7253
|
+
for (const u of users) map.set(u.email.toLowerCase(), u.id);
|
|
7254
|
+
return map;
|
|
7255
|
+
}
|
|
7256
|
+
buildUpdates(ticket, emailToUserId) {
|
|
7257
|
+
const updates = {};
|
|
7258
|
+
for (const field of [
|
|
7259
|
+
"created_by",
|
|
7260
|
+
"updated_by",
|
|
7261
|
+
"assigned_to",
|
|
7262
|
+
"archived_by",
|
|
7263
|
+
"deleted_by"
|
|
7264
|
+
]) {
|
|
7265
|
+
const value = ticket[field];
|
|
7266
|
+
if (value == null || value.length === UNIVERSAL_ID_LENGTH) continue;
|
|
7267
|
+
const userId = emailToUserId.get(value.toLowerCase());
|
|
7268
|
+
if (userId) updates[field] = userId;
|
|
7269
|
+
}
|
|
7270
|
+
return updates;
|
|
7271
|
+
}
|
|
7272
|
+
};
|
|
7273
|
+
FixSupportTicketUserIdsFeature = __decorate([
|
|
7274
|
+
injectable(),
|
|
7275
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
7276
|
+
__decorateParam(1, inject(USER_TOKENS.IUsersRepo))
|
|
7277
|
+
], FixSupportTicketUserIdsFeature);
|
|
7278
|
+
|
|
5931
7279
|
//#endregion
|
|
5932
7280
|
//#region src/slices/support_ticket/features/staff/get_support_ticket_admin_feat.ts
|
|
5933
7281
|
let GetSupportTicketAdminFeature = class GetSupportTicketAdminFeature$1 {
|
|
5934
|
-
constructor(session, support_ticketRepo, appSettingsRepo) {
|
|
7282
|
+
constructor(session, support_ticketRepo, appSettingsRepo, userDisplayLookup) {
|
|
5935
7283
|
this.session = session;
|
|
5936
7284
|
this.support_ticketRepo = support_ticketRepo;
|
|
5937
7285
|
this.appSettingsRepo = appSettingsRepo;
|
|
7286
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5938
7287
|
}
|
|
5939
7288
|
async execute(id) {
|
|
5940
7289
|
let support_ticket = await this.support_ticketRepo.read(id);
|
|
@@ -5948,39 +7297,110 @@ let GetSupportTicketAdminFeature = class GetSupportTicketAdminFeature$1 {
|
|
|
5948
7297
|
updated_by: this.session.user.userId
|
|
5949
7298
|
});
|
|
5950
7299
|
}
|
|
5951
|
-
|
|
7300
|
+
const dto = toStaffSupportTicketReadDto(support_ticket);
|
|
7301
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
5952
7302
|
}
|
|
5953
7303
|
};
|
|
5954
7304
|
GetSupportTicketAdminFeature = __decorate([
|
|
5955
7305
|
injectable(),
|
|
5956
7306
|
__decorateParam(0, injectSession()),
|
|
5957
7307
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
5958
|
-
__decorateParam(2, inject(TOKENS.IAppSettingsRepo))
|
|
7308
|
+
__decorateParam(2, inject(TOKENS.IAppSettingsRepo)),
|
|
7309
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
5959
7310
|
], GetSupportTicketAdminFeature);
|
|
5960
7311
|
|
|
7312
|
+
//#endregion
|
|
7313
|
+
//#region src/slices/support_ticket/features/staff/get_requestors_for_active_support_tickets_feat.ts
|
|
7314
|
+
let GetRequestorsForActiveSupportTicketsFeat = class GetRequestorsForActiveSupportTicketsFeat$1 {
|
|
7315
|
+
constructor(supportTicketRepo, userRepo) {
|
|
7316
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
7317
|
+
this.userRepo = userRepo;
|
|
7318
|
+
}
|
|
7319
|
+
async execute(options) {
|
|
7320
|
+
const requestorIds = await this.supportTicketRepo.read_distinct_requestors_for_active_tickets(options);
|
|
7321
|
+
if (requestorIds.length === 0) return [];
|
|
7322
|
+
return (await this.userRepo.read_users_by_ids(requestorIds)).map((u) => ({
|
|
7323
|
+
id: u.id,
|
|
7324
|
+
email: u.email
|
|
7325
|
+
}));
|
|
7326
|
+
}
|
|
7327
|
+
};
|
|
7328
|
+
GetRequestorsForActiveSupportTicketsFeat = __decorate([
|
|
7329
|
+
injectable(),
|
|
7330
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
7331
|
+
__decorateParam(1, inject(USER_TOKENS.IUsersRepo))
|
|
7332
|
+
], GetRequestorsForActiveSupportTicketsFeat);
|
|
7333
|
+
|
|
5961
7334
|
//#endregion
|
|
5962
7335
|
//#region src/slices/support_ticket/features/staff/get_support_tickets_admin_feat.ts
|
|
5963
7336
|
let GetSupportTicketsAdminFeature = class GetSupportTicketsAdminFeature$1 {
|
|
5964
|
-
constructor(support_ticketRepo) {
|
|
7337
|
+
constructor(support_ticketRepo, userDisplayLookup) {
|
|
5965
7338
|
this.support_ticketRepo = support_ticketRepo;
|
|
7339
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5966
7340
|
}
|
|
5967
7341
|
async execute(filters) {
|
|
5968
7342
|
const result = await this.support_ticketRepo.read_all(filters);
|
|
7343
|
+
const dtos = result.items.map(toStaffSupportTicketReadDto);
|
|
7344
|
+
const userIds = [...new Set(dtos.flatMap(collectUserIdsFromStaffTicket))];
|
|
7345
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
5969
7346
|
return {
|
|
5970
|
-
items:
|
|
7347
|
+
items: dtos.map((t) => enrichStaffTicket(t, displayMap)),
|
|
5971
7348
|
pageInfo: result.pageInfo
|
|
5972
7349
|
};
|
|
5973
7350
|
}
|
|
5974
7351
|
};
|
|
5975
|
-
GetSupportTicketsAdminFeature = __decorate([
|
|
7352
|
+
GetSupportTicketsAdminFeature = __decorate([
|
|
7353
|
+
injectable(),
|
|
7354
|
+
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
7355
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
7356
|
+
], GetSupportTicketsAdminFeature);
|
|
7357
|
+
|
|
7358
|
+
//#endregion
|
|
7359
|
+
//#region src/slices/support_ticket/features/staff/list_support_ticket_subscribers_feat.ts
|
|
7360
|
+
function toRecordSubscriberReadDto(e) {
|
|
7361
|
+
return {
|
|
7362
|
+
id: e.id,
|
|
7363
|
+
record_type: e.record_type,
|
|
7364
|
+
record_id: e.record_id,
|
|
7365
|
+
user_id: e.user_id,
|
|
7366
|
+
subscribed_events: e.subscribed_events ?? null,
|
|
7367
|
+
created_at: e.created_at,
|
|
7368
|
+
created_by: e.created_by
|
|
7369
|
+
};
|
|
7370
|
+
}
|
|
7371
|
+
let ListSupportTicketSubscribersFeat = class ListSupportTicketSubscribersFeat$1 {
|
|
7372
|
+
constructor(subscriberRepo, supportTicketRepo, userDisplayLookup) {
|
|
7373
|
+
this.subscriberRepo = subscriberRepo;
|
|
7374
|
+
this.supportTicketRepo = supportTicketRepo;
|
|
7375
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
7376
|
+
}
|
|
7377
|
+
async execute(supportTicketId) {
|
|
7378
|
+
if (!await this.supportTicketRepo.read(supportTicketId)) throw new BusinessError("Support ticket not found");
|
|
7379
|
+
const dtos = (await this.subscriberRepo.readByRecordId(RecordConst.SUPPORT_TICKET, supportTicketId)).map(toRecordSubscriberReadDto);
|
|
7380
|
+
const userIds = [...new Set(dtos.flatMap((s) => [s.user_id, s.created_by].filter(Boolean)))];
|
|
7381
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
7382
|
+
return dtos.map((s) => ({
|
|
7383
|
+
...s,
|
|
7384
|
+
user_id_display_name: s.user_id ? displayMap.get(s.user_id) ?? s.user_id : null,
|
|
7385
|
+
created_by_display_name: s.created_by ? displayMap.get(s.created_by) ?? s.created_by : null
|
|
7386
|
+
}));
|
|
7387
|
+
}
|
|
7388
|
+
};
|
|
7389
|
+
ListSupportTicketSubscribersFeat = __decorate([
|
|
7390
|
+
injectable(),
|
|
7391
|
+
__decorateParam(0, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo)),
|
|
7392
|
+
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
7393
|
+
__decorateParam(2, inject(USER_TOKENS.IUserDisplayLookup))
|
|
7394
|
+
], ListSupportTicketSubscribersFeat);
|
|
5976
7395
|
|
|
5977
7396
|
//#endregion
|
|
5978
7397
|
//#region src/slices/support_ticket/features/staff/reactivate_internal_task_feat.ts
|
|
5979
7398
|
let ReactivateInternalTaskFeat = class ReactivateInternalTaskFeat$1 {
|
|
5980
|
-
constructor(repo, create_record_version, session) {
|
|
7399
|
+
constructor(repo, create_record_version, session, userDisplayLookup) {
|
|
5981
7400
|
this.repo = repo;
|
|
5982
7401
|
this.create_record_version = create_record_version;
|
|
5983
7402
|
this.session = session;
|
|
7403
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
5984
7404
|
}
|
|
5985
7405
|
/**
|
|
5986
7406
|
* Reactivates a terminal internal task.
|
|
@@ -6028,23 +7448,27 @@ let ReactivateInternalTaskFeat = class ReactivateInternalTaskFeat$1 {
|
|
|
6028
7448
|
completed_at: null
|
|
6029
7449
|
});
|
|
6030
7450
|
if (!updatedSupportTicket) throw new Error("Failed to reactivate internal task.");
|
|
6031
|
-
|
|
7451
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
7452
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6032
7453
|
}
|
|
6033
7454
|
};
|
|
6034
7455
|
ReactivateInternalTaskFeat = __decorate([
|
|
6035
7456
|
injectable(),
|
|
6036
7457
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6037
7458
|
__decorateParam(1, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6038
|
-
__decorateParam(2, injectSession())
|
|
7459
|
+
__decorateParam(2, injectSession()),
|
|
7460
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6039
7461
|
], ReactivateInternalTaskFeat);
|
|
6040
7462
|
|
|
6041
7463
|
//#endregion
|
|
6042
7464
|
//#region src/slices/support_ticket/features/staff/reject_support_ticket_feat.ts
|
|
6043
7465
|
let RejectSupportTicketFeat = class RejectSupportTicketFeat$1 {
|
|
6044
|
-
constructor(repo, create_record_version, session) {
|
|
7466
|
+
constructor(repo, notificationService, create_record_version, session, userDisplayLookup) {
|
|
6045
7467
|
this.repo = repo;
|
|
7468
|
+
this.notificationService = notificationService;
|
|
6046
7469
|
this.create_record_version = create_record_version;
|
|
6047
7470
|
this.session = session;
|
|
7471
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6048
7472
|
}
|
|
6049
7473
|
/**
|
|
6050
7474
|
* Rejects a support_ticket item and locks it.
|
|
@@ -6085,24 +7509,41 @@ let RejectSupportTicketFeat = class RejectSupportTicketFeat$1 {
|
|
|
6085
7509
|
locked_approval_at: lockedAt
|
|
6086
7510
|
});
|
|
6087
7511
|
if (!updatedSupportTicket) throw new Error("Failed to reject support_ticket.");
|
|
6088
|
-
|
|
7512
|
+
await this.notificationService.notifyFollowers(id, updatedSupportTicket, "APPROVAL_STATUS_CHANGED", { actorUserId: this.session.user.userId });
|
|
7513
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
7514
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6089
7515
|
}
|
|
6090
7516
|
};
|
|
6091
7517
|
RejectSupportTicketFeat = __decorate([
|
|
6092
7518
|
injectable(),
|
|
6093
7519
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6094
|
-
__decorateParam(1, inject(
|
|
6095
|
-
__decorateParam(2,
|
|
7520
|
+
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
7521
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7522
|
+
__decorateParam(3, injectSession()),
|
|
7523
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6096
7524
|
], RejectSupportTicketFeat);
|
|
6097
7525
|
|
|
7526
|
+
//#endregion
|
|
7527
|
+
//#region src/slices/support_ticket/features/staff/remove_support_ticket_subscriber_feat.ts
|
|
7528
|
+
let RemoveSupportTicketSubscriberFeat = class RemoveSupportTicketSubscriberFeat$1 {
|
|
7529
|
+
constructor(subscriberRepo) {
|
|
7530
|
+
this.subscriberRepo = subscriberRepo;
|
|
7531
|
+
}
|
|
7532
|
+
async execute(supportTicketId, subscriberId) {
|
|
7533
|
+
if (!await this.subscriberRepo.delete(subscriberId)) throw new BusinessError("Subscriber not found or already removed");
|
|
7534
|
+
}
|
|
7535
|
+
};
|
|
7536
|
+
RemoveSupportTicketSubscriberFeat = __decorate([injectable(), __decorateParam(0, inject(RECORD_SUBSCRIBER_TOKENS.IRecordSubscriberRepo))], RemoveSupportTicketSubscriberFeat);
|
|
7537
|
+
|
|
6098
7538
|
//#endregion
|
|
6099
7539
|
//#region src/slices/support_ticket/features/staff/revert_support_ticket_feat.ts
|
|
6100
7540
|
let RevertSupportTicketFeat = class RevertSupportTicketFeat$1 {
|
|
6101
|
-
constructor(repo, creditService, create_record_version, session) {
|
|
7541
|
+
constructor(repo, creditService, create_record_version, session, userDisplayLookup) {
|
|
6102
7542
|
this.repo = repo;
|
|
6103
7543
|
this.creditService = creditService;
|
|
6104
7544
|
this.create_record_version = create_record_version;
|
|
6105
7545
|
this.session = session;
|
|
7546
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6106
7547
|
}
|
|
6107
7548
|
/**
|
|
6108
7549
|
* Reverts a support_ticket item back to PENDING status and refunds credits.
|
|
@@ -6151,7 +7592,8 @@ let RevertSupportTicketFeat = class RevertSupportTicketFeat$1 {
|
|
|
6151
7592
|
locked_approval_at: null
|
|
6152
7593
|
});
|
|
6153
7594
|
if (!updatedSupportTicket) throw new Error("Failed to revert support_ticket.");
|
|
6154
|
-
|
|
7595
|
+
const dto = toStaffSupportTicketReadDto(updatedSupportTicket);
|
|
7596
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6155
7597
|
}
|
|
6156
7598
|
};
|
|
6157
7599
|
RevertSupportTicketFeat = __decorate([
|
|
@@ -6159,7 +7601,8 @@ RevertSupportTicketFeat = __decorate([
|
|
|
6159
7601
|
__decorateParam(0, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6160
7602
|
__decorateParam(1, inject(CREDIT_SERVICE_TOKEN)),
|
|
6161
7603
|
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6162
|
-
__decorateParam(3, injectSession())
|
|
7604
|
+
__decorateParam(3, injectSession()),
|
|
7605
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6163
7606
|
], RevertSupportTicketFeat);
|
|
6164
7607
|
|
|
6165
7608
|
//#endregion
|
|
@@ -6168,17 +7611,18 @@ RevertSupportTicketFeat = __decorate([
|
|
|
6168
7611
|
* Validate business rules for staff updates based on permissions matrix
|
|
6169
7612
|
*/
|
|
6170
7613
|
const validateBusinessRules = (input, existing) => {
|
|
7614
|
+
if (existing.archived_at) throw new BusinessError("Cannot edit an archived support ticket.");
|
|
6171
7615
|
const currentApproval = existing.approval_status;
|
|
6172
7616
|
const currentDevLifecycle = existing.dev_lifecycle;
|
|
6173
7617
|
if (input.dev_lifecycle !== void 0 && input.dev_lifecycle !== currentDevLifecycle) {
|
|
6174
|
-
if (!currentApproval || !["APPROVED", "INTERNAL"].includes(currentApproval)) throw new
|
|
7618
|
+
if (!currentApproval || !["APPROVED", "INTERNAL"].includes(currentApproval)) throw new BusinessError("Can only set dev lifecycle when support_ticket is APPROVED or INTERNAL");
|
|
6175
7619
|
}
|
|
6176
7620
|
if (input.credit_value !== void 0 && input.credit_value !== existing.credit_value) {
|
|
6177
|
-
if (currentApproval === "INTERNAL") throw new
|
|
6178
|
-
if (currentApproval !== "PENDING") throw new
|
|
7621
|
+
if (currentApproval === "INTERNAL") throw new BusinessError("Internal support_ticket cannot have credit values");
|
|
7622
|
+
if (currentApproval !== "PENDING") throw new BusinessError("Can only edit credit value when support_ticket is PENDING");
|
|
6179
7623
|
}
|
|
6180
7624
|
if (input.delivered_value !== void 0 && input.delivered_value !== existing.delivered_value) {
|
|
6181
|
-
if (currentDevLifecycle !== "DEPLOYED") throw new
|
|
7625
|
+
if (currentDevLifecycle !== "DEPLOYED") throw new BusinessError("Can only set delivered value when dev lifecycle is DEPLOYED");
|
|
6182
7626
|
}
|
|
6183
7627
|
const lockedDevStages = [
|
|
6184
7628
|
"DEVELOPMENT",
|
|
@@ -6186,21 +7630,22 @@ const validateBusinessRules = (input, existing) => {
|
|
|
6186
7630
|
"TESTING",
|
|
6187
7631
|
"STAGING",
|
|
6188
7632
|
"PO_APPROVAL",
|
|
7633
|
+
"VERIFICATION",
|
|
6189
7634
|
"DEPLOYED"
|
|
6190
7635
|
];
|
|
6191
7636
|
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
|
|
7637
|
+
if (currentApproval === "REJECTED") throw new BusinessError("Cannot edit start date when support_ticket is REJECTED");
|
|
7638
|
+
if (currentDevLifecycle && lockedDevStages.includes(currentDevLifecycle)) throw new BusinessError("Cannot edit start date once development has started (DEVELOPMENT or higher)");
|
|
6194
7639
|
}
|
|
6195
7640
|
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
|
|
7641
|
+
if (currentApproval === "REJECTED") throw new BusinessError("Cannot edit target date when support_ticket is REJECTED");
|
|
7642
|
+
if (currentDevLifecycle && lockedDevStages.includes(currentDevLifecycle)) throw new BusinessError("Cannot edit target date once development has started (DEVELOPMENT or higher)");
|
|
6198
7643
|
}
|
|
6199
7644
|
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
|
|
7645
|
+
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
7646
|
}
|
|
6202
7647
|
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
|
|
7648
|
+
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
7649
|
}
|
|
6205
7650
|
};
|
|
6206
7651
|
|
|
@@ -6213,24 +7658,26 @@ function shouldUpdateCreditsSetAt(oldValue, newValue) {
|
|
|
6213
7658
|
return isCreditValueEmpty(oldValue) !== isCreditValueEmpty(newValue);
|
|
6214
7659
|
}
|
|
6215
7660
|
let UpdateSupportTicketAdminFeat = class UpdateSupportTicketAdminFeat$1 {
|
|
6216
|
-
constructor(session, support_ticketRepo, create_record_version) {
|
|
7661
|
+
constructor(session, support_ticketRepo, create_record_version, notificationService, userDisplayLookup) {
|
|
6217
7662
|
this.session = session;
|
|
6218
7663
|
this.support_ticketRepo = support_ticketRepo;
|
|
6219
7664
|
this.create_record_version = create_record_version;
|
|
7665
|
+
this.notificationService = notificationService;
|
|
7666
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6220
7667
|
}
|
|
6221
7668
|
/**
|
|
6222
7669
|
* Update support_ticket - comprehensive update for staff
|
|
6223
7670
|
*/
|
|
6224
7671
|
async execute(input) {
|
|
6225
7672
|
const support_ticket = await this.support_ticketRepo.read(input.id);
|
|
6226
|
-
if (!support_ticket) throw new
|
|
7673
|
+
if (!support_ticket) throw new BusinessError("SupportTicket not found");
|
|
6227
7674
|
validateBusinessRules(input, support_ticket);
|
|
6228
7675
|
const isoTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
6229
7676
|
const frEntity = {
|
|
6230
7677
|
...support_ticket,
|
|
6231
7678
|
id: input.id,
|
|
6232
7679
|
updated_at: isoTime,
|
|
6233
|
-
updated_by: this.session.user.
|
|
7680
|
+
updated_by: this.session.user.userId
|
|
6234
7681
|
};
|
|
6235
7682
|
if (input.title !== void 0 && input.title !== null) frEntity.title = input.title;
|
|
6236
7683
|
if (input.description !== void 0 && input.description !== null) frEntity.description = input.description;
|
|
@@ -6245,6 +7692,7 @@ let UpdateSupportTicketAdminFeat = class UpdateSupportTicketAdminFeat$1 {
|
|
|
6245
7692
|
if (input.start_at !== void 0) frEntity.start_at = input.start_at ?? void 0;
|
|
6246
7693
|
if (input.target_at !== void 0) frEntity.target_at = input.target_at ?? void 0;
|
|
6247
7694
|
if (input.completed_at !== void 0) frEntity.completed_at = input.completed_at ?? void 0;
|
|
7695
|
+
if (input.assigned_to !== void 0) frEntity.assigned_to = input.assigned_to?.trim() || null;
|
|
6248
7696
|
const changed_props = getChangedProperties(support_ticket, frEntity);
|
|
6249
7697
|
if (Object.keys(changed_props).length > 0) await this.create_record_version.execute({
|
|
6250
7698
|
record_id: frEntity.id,
|
|
@@ -6257,14 +7705,22 @@ let UpdateSupportTicketAdminFeat = class UpdateSupportTicketAdminFeat$1 {
|
|
|
6257
7705
|
auth_role: this.session.user.user_type,
|
|
6258
7706
|
auth_username: this.session.user.username
|
|
6259
7707
|
});
|
|
6260
|
-
|
|
7708
|
+
const support_ticketUpdated = await this.support_ticketRepo.update(frEntity);
|
|
7709
|
+
if (input.assigned_to !== void 0 && support_ticket.assigned_to !== support_ticketUpdated.assigned_to && support_ticketUpdated.assigned_to) {
|
|
7710
|
+
await this.notificationService.notifyAssignee(support_ticketUpdated, "TICKET_ASSIGNED", { actorUserId: this.session.user.userId });
|
|
7711
|
+
await this.notificationService.notifyFollowers(support_ticketUpdated.id, support_ticketUpdated, "TICKET_ASSIGNED", { actorUserId: this.session.user.userId });
|
|
7712
|
+
}
|
|
7713
|
+
const dto = toStaffSupportTicketReadDto(support_ticketUpdated);
|
|
7714
|
+
return enrichStaffTicket(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromStaffTicket(dto)));
|
|
6261
7715
|
}
|
|
6262
7716
|
};
|
|
6263
7717
|
UpdateSupportTicketAdminFeat = __decorate([
|
|
6264
7718
|
injectable(),
|
|
6265
7719
|
__decorateParam(0, injectSession()),
|
|
6266
7720
|
__decorateParam(1, inject(SUPPORT_TICKET_TOKENS.ISupportTicketRepo)),
|
|
6267
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
7721
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
7722
|
+
__decorateParam(3, inject(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService)),
|
|
7723
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6268
7724
|
], UpdateSupportTicketAdminFeat);
|
|
6269
7725
|
|
|
6270
7726
|
//#endregion
|
|
@@ -6274,12 +7730,16 @@ UpdateSupportTicketAdminFeat = __decorate([
|
|
|
6274
7730
|
*/
|
|
6275
7731
|
function registerSupportTicketDependencies() {
|
|
6276
7732
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.ISupportTicketRepo, SupportTicketRepo);
|
|
7733
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.ISupportTicketNotificationService, SupportTicketNotificationService);
|
|
7734
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.ISupportTicketTriageService, SupportTicketTriageService);
|
|
6277
7735
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.ICreateSupportTicketFeature, CreateSupportTicketFeat);
|
|
6278
7736
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IUpdateSupportTicketCustomerFeature, UpdateSupportTicketCustomerFeat);
|
|
6279
7737
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetSupportTicketCustomerFeature, GetSupportTicketCustomerFeature);
|
|
6280
7738
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetSupportTicketsCustomerFeature, GetSupportTicketsCustomerFeature);
|
|
7739
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.ICustomerToggleSubscriptionFeature, CustomerToggleSubscriptionFeat);
|
|
6281
7740
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IApproveSupportTicketFeature, ApproveSupportTicketFeat);
|
|
6282
7741
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IDeleteSupportTicketFeature, DeleteSupportTicketFeat);
|
|
7742
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IArchiveSupportTicketFeature, ArchiveSupportTicketFeat);
|
|
6283
7743
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IRejectSupportTicketFeature, RejectSupportTicketFeat);
|
|
6284
7744
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IRevertSupportTicketFeature, RevertSupportTicketFeat);
|
|
6285
7745
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.ICompleteSupportTicketFeature, CompleteSupportTicketFeat);
|
|
@@ -6291,6 +7751,11 @@ function registerSupportTicketDependencies() {
|
|
|
6291
7751
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IUpdateSupportTicketAdminFeature, UpdateSupportTicketAdminFeat);
|
|
6292
7752
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetSupportTicketAdminFeature, GetSupportTicketAdminFeature);
|
|
6293
7753
|
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetSupportTicketsAdminFeature, GetSupportTicketsAdminFeature);
|
|
7754
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IGetRequestorsForActiveSupportTicketsFeature, GetRequestorsForActiveSupportTicketsFeat);
|
|
7755
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IAddSupportTicketSubscriberFeature, AddSupportTicketSubscriberFeat);
|
|
7756
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IListSupportTicketSubscribersFeature, ListSupportTicketSubscribersFeat);
|
|
7757
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IRemoveSupportTicketSubscriberFeature, RemoveSupportTicketSubscriberFeat);
|
|
7758
|
+
container.registerSingleton(SUPPORT_TICKET_TOKENS.IFixSupportTicketUserIdsFeature, FixSupportTicketUserIdsFeature);
|
|
6294
7759
|
}
|
|
6295
7760
|
|
|
6296
7761
|
//#endregion
|
|
@@ -6571,14 +8036,34 @@ function mapTeamEntityToDto(entity) {
|
|
|
6571
8036
|
};
|
|
6572
8037
|
}
|
|
6573
8038
|
|
|
8039
|
+
//#endregion
|
|
8040
|
+
//#region src/slices/team/features/enrich_team.ts
|
|
8041
|
+
/**
|
|
8042
|
+
* Enriches a TeamReadDto with display names from a lookup map.
|
|
8043
|
+
*/
|
|
8044
|
+
function enrichTeam(dto, displayMap) {
|
|
8045
|
+
return {
|
|
8046
|
+
...dto,
|
|
8047
|
+
created_by_display_name: dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null,
|
|
8048
|
+
updated_by_display_name: dto.updated_by ? displayMap.get(dto.updated_by) ?? dto.updated_by : null
|
|
8049
|
+
};
|
|
8050
|
+
}
|
|
8051
|
+
/**
|
|
8052
|
+
* Collects user IDs from a team for display name lookup.
|
|
8053
|
+
*/
|
|
8054
|
+
function collectUserIdsFromTeam(team) {
|
|
8055
|
+
return [team.created_by, team.updated_by].filter((id) => Boolean(id));
|
|
8056
|
+
}
|
|
8057
|
+
|
|
6574
8058
|
//#endregion
|
|
6575
8059
|
//#region src/slices/team/features/create_team_feat.ts
|
|
6576
8060
|
let CreateTeamFeatureImpl = class CreateTeamFeatureImpl$1 {
|
|
6577
|
-
constructor(teamRepo, session, create_record_version, createTeamMemberFeature) {
|
|
8061
|
+
constructor(teamRepo, session, create_record_version, createTeamMemberFeature, userDisplayLookup) {
|
|
6578
8062
|
this.teamRepo = teamRepo;
|
|
6579
8063
|
this.session = session;
|
|
6580
8064
|
this.create_record_version = create_record_version;
|
|
6581
8065
|
this.createTeamMemberFeature = createTeamMemberFeature;
|
|
8066
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6582
8067
|
}
|
|
6583
8068
|
async execute(input) {
|
|
6584
8069
|
if (await this.teamRepo.findByUniqueName(input.unique_name || "")) throw new Error("Team with this name already exists");
|
|
@@ -6633,7 +8118,8 @@ let CreateTeamFeatureImpl = class CreateTeamFeatureImpl$1 {
|
|
|
6633
8118
|
website_address: null,
|
|
6634
8119
|
time_zone: null
|
|
6635
8120
|
});
|
|
6636
|
-
|
|
8121
|
+
const dto = mapTeamEntityToDto(teamEntity);
|
|
8122
|
+
return enrichTeam(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeam(dto)));
|
|
6637
8123
|
}
|
|
6638
8124
|
/**
|
|
6639
8125
|
* Compute path from unique name (e.g., "My Team" -> "my-team")
|
|
@@ -6647,7 +8133,8 @@ CreateTeamFeatureImpl = __decorate([
|
|
|
6647
8133
|
__decorateParam(0, inject(TEAM_TOKENS.ITeamRepository)),
|
|
6648
8134
|
__decorateParam(1, injectSession()),
|
|
6649
8135
|
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
6650
|
-
__decorateParam(3, inject(TEAM_MEMBER_TOKENS.ICreateTeamMemberFeature))
|
|
8136
|
+
__decorateParam(3, inject(TEAM_MEMBER_TOKENS.ICreateTeamMemberFeature)),
|
|
8137
|
+
__decorateParam(4, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6651
8138
|
], CreateTeamFeatureImpl);
|
|
6652
8139
|
|
|
6653
8140
|
//#endregion
|
|
@@ -6685,16 +8172,20 @@ DeleteTeamFeatureImpl = __decorate([
|
|
|
6685
8172
|
//#endregion
|
|
6686
8173
|
//#region src/slices/team/features/read_all_teams_feat.ts
|
|
6687
8174
|
let ReadAllTeamsFeatureImpl = class ReadAllTeamsFeatureImpl$1 {
|
|
6688
|
-
constructor(teamRepo, session, teamMemberRepo) {
|
|
8175
|
+
constructor(teamRepo, session, teamMemberRepo, userDisplayLookup) {
|
|
6689
8176
|
this.teamRepo = teamRepo;
|
|
6690
8177
|
this.session = session;
|
|
6691
8178
|
this.teamMemberRepo = teamMemberRepo;
|
|
8179
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6692
8180
|
}
|
|
6693
8181
|
async execute(filters) {
|
|
6694
8182
|
const processedFilters = await this.applyConsumerFiltering(filters);
|
|
6695
8183
|
const result = await this.teamRepo.read_all(processedFilters);
|
|
8184
|
+
const dtos = result.items.map((teamEntity) => mapTeamEntityToDto(teamEntity));
|
|
8185
|
+
const userIds = [...new Set(dtos.flatMap(collectUserIdsFromTeam))];
|
|
8186
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
6696
8187
|
return {
|
|
6697
|
-
items:
|
|
8188
|
+
items: dtos.map((t) => enrichTeam(t, displayMap)),
|
|
6698
8189
|
pageInfo: result.pageInfo
|
|
6699
8190
|
};
|
|
6700
8191
|
}
|
|
@@ -6721,30 +8212,38 @@ ReadAllTeamsFeatureImpl = __decorate([
|
|
|
6721
8212
|
injectable(),
|
|
6722
8213
|
__decorateParam(0, inject(TEAM_TOKENS.ITeamRepository)),
|
|
6723
8214
|
__decorateParam(1, injectSession()),
|
|
6724
|
-
__decorateParam(2, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo))
|
|
8215
|
+
__decorateParam(2, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo)),
|
|
8216
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6725
8217
|
], ReadAllTeamsFeatureImpl);
|
|
6726
8218
|
|
|
6727
8219
|
//#endregion
|
|
6728
8220
|
//#region src/slices/team/features/read_team_feat.ts
|
|
6729
8221
|
let ReadTeamFeatureImpl = class ReadTeamFeatureImpl$1 {
|
|
6730
|
-
constructor(teamRepo) {
|
|
8222
|
+
constructor(teamRepo, userDisplayLookup) {
|
|
6731
8223
|
this.teamRepo = teamRepo;
|
|
8224
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6732
8225
|
}
|
|
6733
8226
|
async execute(id) {
|
|
6734
8227
|
const teamEntity = await this.teamRepo.read(id);
|
|
6735
8228
|
if (!teamEntity) return null;
|
|
6736
|
-
|
|
8229
|
+
const dto = mapTeamEntityToDto(teamEntity);
|
|
8230
|
+
return enrichTeam(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeam(dto)));
|
|
6737
8231
|
}
|
|
6738
8232
|
};
|
|
6739
|
-
ReadTeamFeatureImpl = __decorate([
|
|
8233
|
+
ReadTeamFeatureImpl = __decorate([
|
|
8234
|
+
injectable(),
|
|
8235
|
+
__decorateParam(0, inject(TEAM_TOKENS.ITeamRepository)),
|
|
8236
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
8237
|
+
], ReadTeamFeatureImpl);
|
|
6740
8238
|
|
|
6741
8239
|
//#endregion
|
|
6742
8240
|
//#region src/slices/team/features/update_team_feat.ts
|
|
6743
8241
|
let UpdateTeamFeatureImpl = class UpdateTeamFeatureImpl$1 {
|
|
6744
|
-
constructor(teamRepo, session, create_record_version) {
|
|
8242
|
+
constructor(teamRepo, session, create_record_version, userDisplayLookup) {
|
|
6745
8243
|
this.teamRepo = teamRepo;
|
|
6746
8244
|
this.session = session;
|
|
6747
8245
|
this.create_record_version = create_record_version;
|
|
8246
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
6748
8247
|
}
|
|
6749
8248
|
async execute(input) {
|
|
6750
8249
|
const existingTeam = await this.teamRepo.read(input.id);
|
|
@@ -6796,7 +8295,8 @@ let UpdateTeamFeatureImpl = class UpdateTeamFeatureImpl$1 {
|
|
|
6796
8295
|
auth_role: this.session.user.user_type,
|
|
6797
8296
|
auth_username: this.session.user.username
|
|
6798
8297
|
});
|
|
6799
|
-
|
|
8298
|
+
const dto = mapTeamEntityToDto(teamEntity);
|
|
8299
|
+
return enrichTeam(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeam(dto)));
|
|
6800
8300
|
}
|
|
6801
8301
|
/**
|
|
6802
8302
|
* Compute path from unique name (e.g., "My Team" -> "my-team")
|
|
@@ -6809,7 +8309,8 @@ UpdateTeamFeatureImpl = __decorate([
|
|
|
6809
8309
|
injectable(),
|
|
6810
8310
|
__decorateParam(0, inject(TEAM_TOKENS.ITeamRepository)),
|
|
6811
8311
|
__decorateParam(1, injectSession()),
|
|
6812
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
8312
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
8313
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
6813
8314
|
], UpdateTeamFeatureImpl);
|
|
6814
8315
|
|
|
6815
8316
|
//#endregion
|
|
@@ -7027,13 +8528,33 @@ let TeamMemberRepo = class TeamMemberRepo$1 {
|
|
|
7027
8528
|
};
|
|
7028
8529
|
TeamMemberRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], TeamMemberRepo);
|
|
7029
8530
|
|
|
8531
|
+
//#endregion
|
|
8532
|
+
//#region src/slices/team_member/features/enrich_team_member.ts
|
|
8533
|
+
/**
|
|
8534
|
+
* Enriches a TeamMemberReadDto with display names from a lookup map.
|
|
8535
|
+
*/
|
|
8536
|
+
function enrichTeamMember(dto, displayMap) {
|
|
8537
|
+
return {
|
|
8538
|
+
...dto,
|
|
8539
|
+
created_by_display_name: dto.created_by ? displayMap.get(dto.created_by) ?? dto.created_by : null,
|
|
8540
|
+
updated_by_display_name: dto.updated_by ? displayMap.get(dto.updated_by) ?? dto.updated_by : null
|
|
8541
|
+
};
|
|
8542
|
+
}
|
|
8543
|
+
/**
|
|
8544
|
+
* Collects user IDs from a team member for display name lookup.
|
|
8545
|
+
*/
|
|
8546
|
+
function collectUserIdsFromTeamMember(member) {
|
|
8547
|
+
return [member.created_by, member.updated_by].filter((id) => Boolean(id));
|
|
8548
|
+
}
|
|
8549
|
+
|
|
7030
8550
|
//#endregion
|
|
7031
8551
|
//#region src/slices/team_member/features/create_team_member_feat.ts
|
|
7032
8552
|
let CreateTeamMemberFeat = class CreateTeamMemberFeat$1 {
|
|
7033
|
-
constructor(session, teamMemberRepo, create_record_version) {
|
|
8553
|
+
constructor(session, teamMemberRepo, create_record_version, userDisplayLookup) {
|
|
7034
8554
|
this.session = session;
|
|
7035
8555
|
this.teamMemberRepo = teamMemberRepo;
|
|
7036
8556
|
this.create_record_version = create_record_version;
|
|
8557
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
7037
8558
|
}
|
|
7038
8559
|
async execute(input) {
|
|
7039
8560
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7065,14 +8586,16 @@ let CreateTeamMemberFeat = class CreateTeamMemberFeat$1 {
|
|
|
7065
8586
|
auth_role: this.session.user.user_type,
|
|
7066
8587
|
auth_username: this.session.user.username
|
|
7067
8588
|
});
|
|
7068
|
-
|
|
8589
|
+
const dto = teamMemberCreated;
|
|
8590
|
+
return enrichTeamMember(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeamMember(dto)));
|
|
7069
8591
|
}
|
|
7070
8592
|
};
|
|
7071
8593
|
CreateTeamMemberFeat = __decorate([
|
|
7072
8594
|
injectable(),
|
|
7073
8595
|
__decorateParam(0, injectSession()),
|
|
7074
8596
|
__decorateParam(1, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo)),
|
|
7075
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
8597
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
8598
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
7076
8599
|
], CreateTeamMemberFeat);
|
|
7077
8600
|
|
|
7078
8601
|
//#endregion
|
|
@@ -7126,14 +8649,27 @@ DeleteTeamMemberFeat = __decorate([
|
|
|
7126
8649
|
//#endregion
|
|
7127
8650
|
//#region src/slices/team_member/features/get_team_members_feat.ts
|
|
7128
8651
|
let GetTeamMembersFeat = class GetTeamMembersFeat$1 {
|
|
7129
|
-
constructor(teamMemberRepo) {
|
|
8652
|
+
constructor(teamMemberRepo, userDisplayLookup) {
|
|
7130
8653
|
this.teamMemberRepo = teamMemberRepo;
|
|
8654
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
7131
8655
|
}
|
|
7132
8656
|
async execute(filters) {
|
|
7133
|
-
|
|
8657
|
+
const result = await this.teamMemberRepo.getAll(filters);
|
|
8658
|
+
const items = result.items;
|
|
8659
|
+
const userIds = [...new Set(items.flatMap(collectUserIdsFromTeamMember))];
|
|
8660
|
+
const displayMap = await this.userDisplayLookup.lookupDisplayNames(userIds);
|
|
8661
|
+
const enrichedItems = items.map((m) => enrichTeamMember(m, displayMap));
|
|
8662
|
+
return {
|
|
8663
|
+
...result,
|
|
8664
|
+
items: enrichedItems
|
|
8665
|
+
};
|
|
7134
8666
|
}
|
|
7135
8667
|
};
|
|
7136
|
-
GetTeamMembersFeat = __decorate([
|
|
8668
|
+
GetTeamMembersFeat = __decorate([
|
|
8669
|
+
injectable(),
|
|
8670
|
+
__decorateParam(0, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo)),
|
|
8671
|
+
__decorateParam(1, inject(USER_TOKENS.IUserDisplayLookup))
|
|
8672
|
+
], GetTeamMembersFeat);
|
|
7137
8673
|
|
|
7138
8674
|
//#endregion
|
|
7139
8675
|
//#region src/slices/team_member/features/get_user_team_members_feat.ts
|
|
@@ -7200,10 +8736,11 @@ GetUserTeamsFeat = __decorate([
|
|
|
7200
8736
|
//#endregion
|
|
7201
8737
|
//#region src/slices/team_member/features/update_team_member_feat.ts
|
|
7202
8738
|
let UpdateTeamMemberFeat = class UpdateTeamMemberFeat$1 {
|
|
7203
|
-
constructor(session, teamMemberRepo, create_record_version) {
|
|
8739
|
+
constructor(session, teamMemberRepo, create_record_version, userDisplayLookup) {
|
|
7204
8740
|
this.session = session;
|
|
7205
8741
|
this.teamMemberRepo = teamMemberRepo;
|
|
7206
8742
|
this.create_record_version = create_record_version;
|
|
8743
|
+
this.userDisplayLookup = userDisplayLookup;
|
|
7207
8744
|
}
|
|
7208
8745
|
async execute(input) {
|
|
7209
8746
|
const existingTeamMember = await this.teamMemberRepo.getById(input.id);
|
|
@@ -7254,14 +8791,16 @@ let UpdateTeamMemberFeat = class UpdateTeamMemberFeat$1 {
|
|
|
7254
8791
|
auth_role: this.session.user.user_type,
|
|
7255
8792
|
auth_username: this.session.user.username
|
|
7256
8793
|
});
|
|
7257
|
-
|
|
8794
|
+
const dto = teamMemberUpdated;
|
|
8795
|
+
return enrichTeamMember(dto, await this.userDisplayLookup.lookupDisplayNames(collectUserIdsFromTeamMember(dto)));
|
|
7258
8796
|
}
|
|
7259
8797
|
};
|
|
7260
8798
|
UpdateTeamMemberFeat = __decorate([
|
|
7261
8799
|
injectable(),
|
|
7262
8800
|
__decorateParam(0, injectSession()),
|
|
7263
8801
|
__decorateParam(1, inject(TEAM_MEMBER_TOKENS.ITeamMemberRepo)),
|
|
7264
|
-
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion))
|
|
8802
|
+
__decorateParam(2, inject(RECORD_VERSION_TOKENS.ICreateRecordVersion)),
|
|
8803
|
+
__decorateParam(3, inject(USER_TOKENS.IUserDisplayLookup))
|
|
7265
8804
|
], UpdateTeamMemberFeat);
|
|
7266
8805
|
|
|
7267
8806
|
//#endregion
|
|
@@ -7325,6 +8864,21 @@ let UserRepo = class UserRepo$1 {
|
|
|
7325
8864
|
this.logger.perf(`UserRepo.read_user_by_email: ${queryTime.toFixed(2)}ms for email ${email}`);
|
|
7326
8865
|
return result[0];
|
|
7327
8866
|
}
|
|
8867
|
+
async read_users_by_emails(emails) {
|
|
8868
|
+
if (emails.length === 0) return [];
|
|
8869
|
+
const { ...rest } = getTableColumns(user_table);
|
|
8870
|
+
return this.router.queryAll((db) => db.select({ ...rest }).from(user_table).where(inArray(user_table.email, emails)));
|
|
8871
|
+
}
|
|
8872
|
+
/**
|
|
8873
|
+
* Case-insensitive email lookup. Use when matching against values that may
|
|
8874
|
+
* differ in casing (e.g. emails stored in other tables).
|
|
8875
|
+
*/
|
|
8876
|
+
async read_users_by_emails_case_insensitive(emails) {
|
|
8877
|
+
if (emails.length === 0) return [];
|
|
8878
|
+
const { ...rest } = getTableColumns(user_table);
|
|
8879
|
+
const lowerEmails = [...new Set(emails.map((e) => e.toLowerCase()))];
|
|
8880
|
+
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`, `)})`));
|
|
8881
|
+
}
|
|
7328
8882
|
async read_user_by_username(username) {
|
|
7329
8883
|
const { ...rest } = getTableColumns(user_table);
|
|
7330
8884
|
return (await this.router.queryAll((db) => db.select({ ...rest }).from(user_table).where(eq(user_table.username, username)).limit(1)))[0];
|
|
@@ -7434,6 +8988,26 @@ CreateUser = __decorate([
|
|
|
7434
8988
|
__decorateParam(3, inject(TOKENS.LOGGER))
|
|
7435
8989
|
], CreateUser);
|
|
7436
8990
|
|
|
8991
|
+
//#endregion
|
|
8992
|
+
//#region src/slices/user/features/user_display_lookup_feat.ts
|
|
8993
|
+
let UserDisplayLookupFeat = class UserDisplayLookupFeat$1 {
|
|
8994
|
+
constructor(userRepo) {
|
|
8995
|
+
this.userRepo = userRepo;
|
|
8996
|
+
}
|
|
8997
|
+
async lookupDisplayNames(userIds) {
|
|
8998
|
+
const validIds = [...new Set(userIds)].filter((id) => !!id?.trim()).filter((id) => id.length === 32);
|
|
8999
|
+
if (validIds.length === 0) return /* @__PURE__ */ new Map();
|
|
9000
|
+
const users = await this.userRepo.read_users_by_ids(validIds);
|
|
9001
|
+
const map = /* @__PURE__ */ new Map();
|
|
9002
|
+
for (const u of users) {
|
|
9003
|
+
const display = u.email ?? u.id;
|
|
9004
|
+
map.set(u.id, display);
|
|
9005
|
+
}
|
|
9006
|
+
return map;
|
|
9007
|
+
}
|
|
9008
|
+
};
|
|
9009
|
+
UserDisplayLookupFeat = __decorate([injectable(), __decorateParam(0, inject(USER_TOKENS.IUsersRepo))], UserDisplayLookupFeat);
|
|
9010
|
+
|
|
7437
9011
|
//#endregion
|
|
7438
9012
|
//#region src/slices/user/features/delete_user.ts
|
|
7439
9013
|
let DeleteUser = class DeleteUser$1 {
|
|
@@ -7511,6 +9085,18 @@ let GetUserFeat = class GetUserFeat$1 {
|
|
|
7511
9085
|
};
|
|
7512
9086
|
GetUserFeat = __decorate([injectable(), __decorateParam(0, inject(USER_TOKENS.IUsersRepo))], GetUserFeat);
|
|
7513
9087
|
|
|
9088
|
+
//#endregion
|
|
9089
|
+
//#region src/slices/user/features/get_triage_users_feat.ts
|
|
9090
|
+
let GetTriageUsersFeat = class GetTriageUsersFeat$1 {
|
|
9091
|
+
constructor(supportStaffRepo) {
|
|
9092
|
+
this.supportStaffRepo = supportStaffRepo;
|
|
9093
|
+
}
|
|
9094
|
+
async execute() {
|
|
9095
|
+
return this.supportStaffRepo.readTriageUsers();
|
|
9096
|
+
}
|
|
9097
|
+
};
|
|
9098
|
+
GetTriageUsersFeat = __decorate([injectable(), __decorateParam(0, inject(SUPPORT_STAFF_TOKENS.ISupportStaffRepo))], GetTriageUsersFeat);
|
|
9099
|
+
|
|
7514
9100
|
//#endregion
|
|
7515
9101
|
//#region src/slices/user/features/get_users_for_selection_feat.ts
|
|
7516
9102
|
let GetUsersForSelectionFeat = class GetUsersForSelectionFeat$1 {
|
|
@@ -7518,7 +9104,7 @@ let GetUsersForSelectionFeat = class GetUsersForSelectionFeat$1 {
|
|
|
7518
9104
|
this.repo = repo;
|
|
7519
9105
|
}
|
|
7520
9106
|
async execute() {
|
|
7521
|
-
return (await this.repo.read_users_by_types([
|
|
9107
|
+
return (await this.repo.read_users_by_types([...USER_TYPES])).map((user) => ({
|
|
7522
9108
|
id: user.id,
|
|
7523
9109
|
email: user.email
|
|
7524
9110
|
}));
|
|
@@ -7662,6 +9248,7 @@ UpdateUserFeat = __decorate([injectable(), __decorateParam(0, inject(USER_TOKENS
|
|
|
7662
9248
|
//#region src/slices/user/user_container.ts
|
|
7663
9249
|
function registerUserContainer() {
|
|
7664
9250
|
container.registerSingleton(USER_TOKENS.IUsersRepo, UserRepo);
|
|
9251
|
+
container.registerSingleton(USER_TOKENS.IUserDisplayLookup, UserDisplayLookupFeat);
|
|
7665
9252
|
container.registerSingleton(USER_TOKENS.IReadAllUsers, ReadAllUsers);
|
|
7666
9253
|
container.registerSingleton(USER_TOKENS.ISignUpUser, SignUpUser);
|
|
7667
9254
|
container.registerSingleton(USER_TOKENS.IDeleteUser, DeleteUser);
|
|
@@ -7672,6 +9259,7 @@ function registerUserContainer() {
|
|
|
7672
9259
|
container.registerSingleton(USER_TOKENS.IGetUserFeature, GetUserFeat);
|
|
7673
9260
|
container.registerSingleton(USER_TOKENS.IUpdateUserFeature, UpdateUserFeat);
|
|
7674
9261
|
container.registerSingleton(USER_TOKENS.IGetUsersForSelectionFeature, GetUsersForSelectionFeat);
|
|
9262
|
+
container.registerSingleton(USER_TOKENS.IGetTriageUsersFeature, GetTriageUsersFeat);
|
|
7675
9263
|
}
|
|
7676
9264
|
|
|
7677
9265
|
//#endregion
|
|
@@ -7691,6 +9279,11 @@ let UserProfileRepo = class UserProfileRepo$1 {
|
|
|
7691
9279
|
const { ...rest } = getTableColumns(user_profile_table);
|
|
7692
9280
|
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
9281
|
}
|
|
9282
|
+
async read_user_profiles_by_user_ids(user_ids) {
|
|
9283
|
+
if (user_ids.length === 0) return [];
|
|
9284
|
+
const { ...rest } = getTableColumns(user_profile_table);
|
|
9285
|
+
return this.router.queryAll((db) => db.select({ ...rest }).from(user_profile_table).where(inArray(user_profile_table.user_id, user_ids)));
|
|
9286
|
+
}
|
|
7694
9287
|
async update_user_profile(user_profileRecord) {
|
|
7695
9288
|
user_profileRecord.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
7696
9289
|
const { created_at, ...rest } = user_profileRecord;
|
|
@@ -7699,16 +9292,6 @@ let UserProfileRepo = class UserProfileRepo$1 {
|
|
|
7699
9292
|
};
|
|
7700
9293
|
UserProfileRepo = __decorate([injectable(), __decorateParam(0, inject(TOKENS.IDatabaseRouter))], UserProfileRepo);
|
|
7701
9294
|
|
|
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
9295
|
//#endregion
|
|
7713
9296
|
//#region src/slices/user_profile/features/read_user_profile.ts
|
|
7714
9297
|
let ReadUserProfile = class ReadUserProfile$1 {
|
|
@@ -8271,14 +9854,17 @@ function registerCoreContainer() {
|
|
|
8271
9854
|
container.registerSingleton(DISPLAY_ID_PREFIX_TOKENS.PrefixRegistry, DisplayIdPrefixRegistry);
|
|
8272
9855
|
container.registerSingleton(DISPLAY_ID_PREFIX_TOKENS.IDisplayIdPrefixService, DisplayIdPrefixService);
|
|
8273
9856
|
registerPasswordResetContainer();
|
|
9857
|
+
registerRecordSubscriberDependencies();
|
|
8274
9858
|
registerRecordVersionContainer();
|
|
9859
|
+
registerSavedFilterContainer();
|
|
9860
|
+
registerSupportStaffDependencies();
|
|
8275
9861
|
registerUserContainer();
|
|
8276
9862
|
registerUserProfileContainer();
|
|
8277
9863
|
registerUserSessionContainer();
|
|
8278
9864
|
registerCustomerDependencies();
|
|
8279
9865
|
registerSupportTicketDependencies();
|
|
8280
9866
|
registerAttachmentContainer();
|
|
8281
|
-
registerAppSettingsContainer();
|
|
9867
|
+
/* @__PURE__ */ registerAppSettingsContainer();
|
|
8282
9868
|
registerNoteContainer();
|
|
8283
9869
|
registerTeamDependencies();
|
|
8284
9870
|
registerTeamMemberContainer();
|
|
@@ -8315,37 +9901,15 @@ function createRequestContainer(config, additionalRegistrations) {
|
|
|
8315
9901
|
//#endregion
|
|
8316
9902
|
//#region src/slices/app_settings/app-settings-api.server.ts
|
|
8317
9903
|
var AppSettingsApiServer = class extends RpcTarget {
|
|
8318
|
-
constructor(
|
|
9904
|
+
constructor(_container) {
|
|
8319
9905
|
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
9906
|
}
|
|
8347
9907
|
};
|
|
8348
9908
|
|
|
9909
|
+
//#endregion
|
|
9910
|
+
//#region src/slices/app_settings/app_settings_tokens.ts
|
|
9911
|
+
const APP_SETTINGS_TOKENS = {};
|
|
9912
|
+
|
|
8349
9913
|
//#endregion
|
|
8350
9914
|
//#region src/slices/attachment/attachment-api.server.ts
|
|
8351
9915
|
const CreateFolderInputSchema = z.object({
|
|
@@ -8669,12 +10233,211 @@ var RecordVersionApiServer = class extends RpcTarget {
|
|
|
8669
10233
|
}
|
|
8670
10234
|
});
|
|
8671
10235
|
}
|
|
10236
|
+
async listTrackerActivityVersions(trackerId, filters) {
|
|
10237
|
+
return rpcMethod({
|
|
10238
|
+
auth: "protected",
|
|
10239
|
+
container: this.container,
|
|
10240
|
+
context: "RecordVersionApiServer.listTrackerActivityVersions",
|
|
10241
|
+
input: {
|
|
10242
|
+
tracker_id: trackerId,
|
|
10243
|
+
filters
|
|
10244
|
+
},
|
|
10245
|
+
inputSchema: recordVersionTrackerActivityInputSchema,
|
|
10246
|
+
outputSchema: recordVersionPageBreadcrumbSchema,
|
|
10247
|
+
execute: async ({ tracker_id, filters: f }, container$1) => {
|
|
10248
|
+
return await container$1.resolve(RECORD_VERSION_TOKENS.IReadTrackerActivityVersions).execute(tracker_id, f || {});
|
|
10249
|
+
}
|
|
10250
|
+
});
|
|
10251
|
+
}
|
|
10252
|
+
};
|
|
10253
|
+
|
|
10254
|
+
//#endregion
|
|
10255
|
+
//#region src/slices/support_staff/support-staff-api.server.ts
|
|
10256
|
+
const SupportStaffMemberSchema = z.object({
|
|
10257
|
+
id: z.string(),
|
|
10258
|
+
user_id: z.string(),
|
|
10259
|
+
email: z.string(),
|
|
10260
|
+
created_at: z.string()
|
|
10261
|
+
});
|
|
10262
|
+
var SupportStaffApiServer = class extends RpcTarget {
|
|
10263
|
+
constructor(container$1) {
|
|
10264
|
+
super();
|
|
10265
|
+
this.container = container$1;
|
|
10266
|
+
}
|
|
10267
|
+
async listSupportStaff() {
|
|
10268
|
+
return rpcMethodPartial({
|
|
10269
|
+
auth: "admin",
|
|
10270
|
+
container: this.container,
|
|
10271
|
+
context: "SupportStaffApiServer.listSupportStaff",
|
|
10272
|
+
input: void 0,
|
|
10273
|
+
outputSchema: z.array(SupportStaffMemberSchema),
|
|
10274
|
+
execute: async (_, container$1) => {
|
|
10275
|
+
return container$1.resolve(SUPPORT_STAFF_TOKENS.IListSupportStaffFeature).execute();
|
|
10276
|
+
}
|
|
10277
|
+
});
|
|
10278
|
+
}
|
|
10279
|
+
async addSupportStaff(userId) {
|
|
10280
|
+
return rpcMethod({
|
|
10281
|
+
auth: "admin",
|
|
10282
|
+
container: this.container,
|
|
10283
|
+
context: "SupportStaffApiServer.addSupportStaff",
|
|
10284
|
+
input: userId,
|
|
10285
|
+
inputSchema: z.string(),
|
|
10286
|
+
outputSchema: SupportStaffMemberSchema,
|
|
10287
|
+
execute: async (userId$1, container$1) => {
|
|
10288
|
+
return container$1.resolve(SUPPORT_STAFF_TOKENS.IAddSupportStaffFeature).execute(userId$1);
|
|
10289
|
+
}
|
|
10290
|
+
});
|
|
10291
|
+
}
|
|
10292
|
+
async removeSupportStaff(userId) {
|
|
10293
|
+
return rpcMethod({
|
|
10294
|
+
auth: "admin",
|
|
10295
|
+
container: this.container,
|
|
10296
|
+
context: "SupportStaffApiServer.removeSupportStaff",
|
|
10297
|
+
input: userId,
|
|
10298
|
+
inputSchema: z.string(),
|
|
10299
|
+
outputSchema: z.void(),
|
|
10300
|
+
execute: async (userId$1, container$1) => {
|
|
10301
|
+
await container$1.resolve(SUPPORT_STAFF_TOKENS.IRemoveSupportStaffFeature).execute(userId$1);
|
|
10302
|
+
}
|
|
10303
|
+
});
|
|
10304
|
+
}
|
|
10305
|
+
};
|
|
10306
|
+
|
|
10307
|
+
//#endregion
|
|
10308
|
+
//#region src/slices/saved_filter/saved-filter-api.server.ts
|
|
10309
|
+
var SavedFilterApiServer = class extends RpcTarget {
|
|
10310
|
+
constructor(container$1) {
|
|
10311
|
+
super();
|
|
10312
|
+
this.container = container$1;
|
|
10313
|
+
}
|
|
10314
|
+
async listSavedFilters(context) {
|
|
10315
|
+
return rpcMethod({
|
|
10316
|
+
auth: "protected",
|
|
10317
|
+
container: this.container,
|
|
10318
|
+
context: "SavedFilterApiServer.listSavedFilters",
|
|
10319
|
+
input: context,
|
|
10320
|
+
inputSchema: z.string(),
|
|
10321
|
+
outputSchema: z.array(SavedFilterReadSchema),
|
|
10322
|
+
execute: async (context$1, container$1) => {
|
|
10323
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.IListSavedFilters).execute(context$1);
|
|
10324
|
+
}
|
|
10325
|
+
});
|
|
10326
|
+
}
|
|
10327
|
+
async listAllSavedFilters() {
|
|
10328
|
+
return rpcMethod({
|
|
10329
|
+
auth: "protected",
|
|
10330
|
+
container: this.container,
|
|
10331
|
+
context: "SavedFilterApiServer.listAllSavedFilters",
|
|
10332
|
+
input: void 0,
|
|
10333
|
+
inputSchema: z.undefined(),
|
|
10334
|
+
outputSchema: z.array(SavedFilterReadSchema),
|
|
10335
|
+
execute: async (_input, container$1) => {
|
|
10336
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.IListAllSavedFilters).execute();
|
|
10337
|
+
}
|
|
10338
|
+
});
|
|
10339
|
+
}
|
|
10340
|
+
async createSavedFilter(input) {
|
|
10341
|
+
return rpcMethod({
|
|
10342
|
+
auth: "protected",
|
|
10343
|
+
container: this.container,
|
|
10344
|
+
context: "SavedFilterApiServer.createSavedFilter",
|
|
10345
|
+
input,
|
|
10346
|
+
inputSchema: SavedFilterCreateSchema,
|
|
10347
|
+
outputSchema: SavedFilterReadSchema,
|
|
10348
|
+
execute: async (input$1, container$1) => {
|
|
10349
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.ICreateSavedFilter).execute(input$1);
|
|
10350
|
+
}
|
|
10351
|
+
});
|
|
10352
|
+
}
|
|
10353
|
+
async updateSavedFilter(input) {
|
|
10354
|
+
return rpcMethod({
|
|
10355
|
+
auth: "protected",
|
|
10356
|
+
container: this.container,
|
|
10357
|
+
context: "SavedFilterApiServer.updateSavedFilter",
|
|
10358
|
+
input,
|
|
10359
|
+
inputSchema: SavedFilterUpdateSchema,
|
|
10360
|
+
outputSchema: SavedFilterReadSchema.nullable(),
|
|
10361
|
+
execute: async (input$1, container$1) => {
|
|
10362
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.IUpdateSavedFilter).execute(input$1);
|
|
10363
|
+
}
|
|
10364
|
+
});
|
|
10365
|
+
}
|
|
10366
|
+
async deleteSavedFilter(id) {
|
|
10367
|
+
return rpcMethod({
|
|
10368
|
+
auth: "protected",
|
|
10369
|
+
container: this.container,
|
|
10370
|
+
context: "SavedFilterApiServer.deleteSavedFilter",
|
|
10371
|
+
input: id,
|
|
10372
|
+
inputSchema: z.string(),
|
|
10373
|
+
outputSchema: z.boolean(),
|
|
10374
|
+
execute: async (id$1, container$1) => {
|
|
10375
|
+
return await container$1.resolve(SAVED_FILTER_TOKENS.IDeleteSavedFilter).execute(id$1);
|
|
10376
|
+
}
|
|
10377
|
+
});
|
|
10378
|
+
}
|
|
10379
|
+
async listPinnedPresets() {
|
|
10380
|
+
return rpcMethod({
|
|
10381
|
+
auth: "protected",
|
|
10382
|
+
container: this.container,
|
|
10383
|
+
context: "SavedFilterApiServer.listPinnedPresets",
|
|
10384
|
+
input: void 0,
|
|
10385
|
+
inputSchema: z.undefined(),
|
|
10386
|
+
outputSchema: z.array(SavedFilterReadSchema),
|
|
10387
|
+
execute: async (_input, container$1) => {
|
|
10388
|
+
return await container$1.resolve(USER_PINNED_PRESET_TOKENS.IListPinnedPresets).execute();
|
|
10389
|
+
}
|
|
10390
|
+
});
|
|
10391
|
+
}
|
|
10392
|
+
async addPinnedPreset(presetId) {
|
|
10393
|
+
return rpcMethod({
|
|
10394
|
+
auth: "protected",
|
|
10395
|
+
container: this.container,
|
|
10396
|
+
context: "SavedFilterApiServer.addPinnedPreset",
|
|
10397
|
+
input: presetId,
|
|
10398
|
+
inputSchema: z.string(),
|
|
10399
|
+
outputSchema: SavedFilterReadSchema.nullable(),
|
|
10400
|
+
execute: async (presetId$1, container$1) => {
|
|
10401
|
+
return await container$1.resolve(USER_PINNED_PRESET_TOKENS.IAddPinnedPreset).execute(presetId$1);
|
|
10402
|
+
}
|
|
10403
|
+
});
|
|
10404
|
+
}
|
|
10405
|
+
async removePinnedPreset(presetId) {
|
|
10406
|
+
return rpcMethod({
|
|
10407
|
+
auth: "protected",
|
|
10408
|
+
container: this.container,
|
|
10409
|
+
context: "SavedFilterApiServer.removePinnedPreset",
|
|
10410
|
+
input: presetId,
|
|
10411
|
+
inputSchema: z.string(),
|
|
10412
|
+
outputSchema: z.boolean(),
|
|
10413
|
+
execute: async (presetId$1, container$1) => {
|
|
10414
|
+
return await container$1.resolve(USER_PINNED_PRESET_TOKENS.IRemovePinnedPreset).execute(presetId$1);
|
|
10415
|
+
}
|
|
10416
|
+
});
|
|
10417
|
+
}
|
|
10418
|
+
async reorderPinnedPresets(presetIds) {
|
|
10419
|
+
return rpcMethod({
|
|
10420
|
+
auth: "protected",
|
|
10421
|
+
container: this.container,
|
|
10422
|
+
context: "SavedFilterApiServer.reorderPinnedPresets",
|
|
10423
|
+
input: presetIds,
|
|
10424
|
+
inputSchema: z.array(z.string()),
|
|
10425
|
+
outputSchema: z.void(),
|
|
10426
|
+
execute: async (presetIds$1, container$1) => {
|
|
10427
|
+
return await container$1.resolve(USER_PINNED_PRESET_TOKENS.IReorderPinnedPresets).execute(presetIds$1);
|
|
10428
|
+
}
|
|
10429
|
+
});
|
|
10430
|
+
}
|
|
8672
10431
|
};
|
|
8673
10432
|
|
|
8674
10433
|
//#endregion
|
|
8675
10434
|
//#region src/slices/support_ticket/support-ticket-api.server.ts
|
|
8676
10435
|
const CancelInternalTaskInputSchema = z.object({ id: z.string() });
|
|
8677
10436
|
const ReactivateInternalTaskInputSchema = z.object({ id: z.string() });
|
|
10437
|
+
const UsersForSelectionSchema$1 = z.array(z.object({
|
|
10438
|
+
id: z.string(),
|
|
10439
|
+
email: z.string()
|
|
10440
|
+
}));
|
|
8678
10441
|
var SupportTicketApiServer = class extends RpcTarget {
|
|
8679
10442
|
constructor(container$1) {
|
|
8680
10443
|
super();
|
|
@@ -8732,6 +10495,31 @@ var SupportTicketApiServer = class extends RpcTarget {
|
|
|
8732
10495
|
}
|
|
8733
10496
|
});
|
|
8734
10497
|
}
|
|
10498
|
+
async toggleSubscription(supportTicketId) {
|
|
10499
|
+
return rpcMethod({
|
|
10500
|
+
auth: "protected",
|
|
10501
|
+
container: this.container,
|
|
10502
|
+
context: "SupportTicketApiServer.toggleSubscription",
|
|
10503
|
+
input: supportTicketId,
|
|
10504
|
+
inputSchema: z.string(),
|
|
10505
|
+
outputSchema: z.object({ subscribed: z.boolean() }),
|
|
10506
|
+
execute: async (id, container$1) => {
|
|
10507
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.ICustomerToggleSubscriptionFeature).execute(id);
|
|
10508
|
+
}
|
|
10509
|
+
});
|
|
10510
|
+
}
|
|
10511
|
+
async getRequestorsForActiveTickets() {
|
|
10512
|
+
return rpcMethodPartial({
|
|
10513
|
+
auth: "protected",
|
|
10514
|
+
container: this.container,
|
|
10515
|
+
context: "SupportTicketApiServer.getRequestorsForActiveTickets",
|
|
10516
|
+
input: void 0,
|
|
10517
|
+
outputSchema: UsersForSelectionSchema$1,
|
|
10518
|
+
execute: async (_, container$1) => {
|
|
10519
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IGetRequestorsForActiveSupportTicketsFeature).execute({ excludeInternal: true });
|
|
10520
|
+
}
|
|
10521
|
+
});
|
|
10522
|
+
}
|
|
8735
10523
|
async staffCreateTicket(input) {
|
|
8736
10524
|
return rpcMethod({
|
|
8737
10525
|
auth: "admin",
|
|
@@ -8784,6 +10572,18 @@ var SupportTicketApiServer = class extends RpcTarget {
|
|
|
8784
10572
|
}
|
|
8785
10573
|
});
|
|
8786
10574
|
}
|
|
10575
|
+
async staffGetRequestorsForActiveTickets() {
|
|
10576
|
+
return rpcMethodPartial({
|
|
10577
|
+
auth: "staff",
|
|
10578
|
+
container: this.container,
|
|
10579
|
+
context: "SupportTicketApiServer.staffGetRequestorsForActiveTickets",
|
|
10580
|
+
input: void 0,
|
|
10581
|
+
outputSchema: UsersForSelectionSchema$1,
|
|
10582
|
+
execute: async (_, container$1) => {
|
|
10583
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IGetRequestorsForActiveSupportTicketsFeature).execute({ excludeInternal: false });
|
|
10584
|
+
}
|
|
10585
|
+
});
|
|
10586
|
+
}
|
|
8787
10587
|
async approveTicket(input) {
|
|
8788
10588
|
return rpcMethod({
|
|
8789
10589
|
auth: "admin",
|
|
@@ -8901,6 +10701,78 @@ var SupportTicketApiServer = class extends RpcTarget {
|
|
|
8901
10701
|
}
|
|
8902
10702
|
});
|
|
8903
10703
|
}
|
|
10704
|
+
async archiveTicket(input) {
|
|
10705
|
+
return rpcMethod({
|
|
10706
|
+
auth: "admin",
|
|
10707
|
+
container: this.container,
|
|
10708
|
+
context: "SupportTicketApiServer.archiveTicket",
|
|
10709
|
+
input,
|
|
10710
|
+
inputSchema: ArchiveSupportTicketSchema,
|
|
10711
|
+
outputSchema: StaffSupportTicketReadSchema,
|
|
10712
|
+
execute: async (input$1, container$1) => {
|
|
10713
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IArchiveSupportTicketFeature).execute(input$1.id);
|
|
10714
|
+
}
|
|
10715
|
+
});
|
|
10716
|
+
}
|
|
10717
|
+
async staffListSubscribers(supportTicketId) {
|
|
10718
|
+
return rpcMethod({
|
|
10719
|
+
auth: "admin",
|
|
10720
|
+
container: this.container,
|
|
10721
|
+
context: "SupportTicketApiServer.staffListSubscribers",
|
|
10722
|
+
input: supportTicketId,
|
|
10723
|
+
inputSchema: z.string(),
|
|
10724
|
+
outputSchema: z.array(RecordSubscriberReadSchema),
|
|
10725
|
+
execute: async (id, container$1) => {
|
|
10726
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IListSupportTicketSubscribersFeature).execute(id);
|
|
10727
|
+
}
|
|
10728
|
+
});
|
|
10729
|
+
}
|
|
10730
|
+
async staffAddSubscriber(input) {
|
|
10731
|
+
return rpcMethod({
|
|
10732
|
+
auth: "admin",
|
|
10733
|
+
container: this.container,
|
|
10734
|
+
context: "SupportTicketApiServer.staffAddSubscriber",
|
|
10735
|
+
input,
|
|
10736
|
+
inputSchema: SupportTicketSubscriberCreateSchema,
|
|
10737
|
+
outputSchema: RecordSubscriberReadSchema,
|
|
10738
|
+
execute: async (validated, container$1) => {
|
|
10739
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IAddSupportTicketSubscriberFeature).execute(validated);
|
|
10740
|
+
}
|
|
10741
|
+
});
|
|
10742
|
+
}
|
|
10743
|
+
async staffRemoveSubscriber(input) {
|
|
10744
|
+
return rpcMethod({
|
|
10745
|
+
auth: "admin",
|
|
10746
|
+
container: this.container,
|
|
10747
|
+
context: "SupportTicketApiServer.staffRemoveSubscriber",
|
|
10748
|
+
input,
|
|
10749
|
+
inputSchema: z.object({
|
|
10750
|
+
supportTicketId: z.string(),
|
|
10751
|
+
subscriberId: z.string()
|
|
10752
|
+
}),
|
|
10753
|
+
outputSchema: z.void(),
|
|
10754
|
+
execute: async (validated, container$1) => {
|
|
10755
|
+
await container$1.resolve(SUPPORT_TICKET_TOKENS.IRemoveSupportTicketSubscriberFeature).execute(validated.supportTicketId, validated.subscriberId);
|
|
10756
|
+
}
|
|
10757
|
+
});
|
|
10758
|
+
}
|
|
10759
|
+
async staffFixSupportTicketUserIds() {
|
|
10760
|
+
return rpcMethod({
|
|
10761
|
+
auth: "admin",
|
|
10762
|
+
container: this.container,
|
|
10763
|
+
context: "SupportTicketApiServer.staffFixSupportTicketUserIds",
|
|
10764
|
+
input: void 0,
|
|
10765
|
+
inputSchema: z.undefined(),
|
|
10766
|
+
outputSchema: z.object({
|
|
10767
|
+
ticketsScanned: z.number(),
|
|
10768
|
+
ticketsFixed: z.number(),
|
|
10769
|
+
ticketsSkipped: z.number()
|
|
10770
|
+
}),
|
|
10771
|
+
execute: async (_input, container$1) => {
|
|
10772
|
+
return await container$1.resolve(SUPPORT_TICKET_TOKENS.IFixSupportTicketUserIdsFeature).execute();
|
|
10773
|
+
}
|
|
10774
|
+
});
|
|
10775
|
+
}
|
|
8904
10776
|
};
|
|
8905
10777
|
|
|
8906
10778
|
//#endregion
|
|
@@ -9189,6 +11061,18 @@ var UserApiServer = class extends RpcTarget {
|
|
|
9189
11061
|
}
|
|
9190
11062
|
});
|
|
9191
11063
|
}
|
|
11064
|
+
async getTriageUsers() {
|
|
11065
|
+
return rpcMethodPartial({
|
|
11066
|
+
auth: "admin",
|
|
11067
|
+
container: this.container,
|
|
11068
|
+
context: "UserApiServer.getTriageUsers",
|
|
11069
|
+
input: void 0,
|
|
11070
|
+
outputSchema: UsersForSelectionSchema,
|
|
11071
|
+
execute: async (_, container$1) => {
|
|
11072
|
+
return await container$1.resolve(USER_TOKENS.IGetTriageUsersFeature).execute();
|
|
11073
|
+
}
|
|
11074
|
+
});
|
|
11075
|
+
}
|
|
9192
11076
|
};
|
|
9193
11077
|
|
|
9194
11078
|
//#endregion
|
|
@@ -9380,5 +11264,5 @@ var UserSessionApiServer = class extends RpcTarget {
|
|
|
9380
11264
|
};
|
|
9381
11265
|
|
|
9382
11266
|
//#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 };
|
|
11267
|
+
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
11268
|
//# sourceMappingURL=index.mjs.map
|