@managemint-solutions/sdk 0.68.0 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/additional-details/dto.d.ts +9 -1
- package/dist/additional-details/dto.js +46 -1
- package/dist/additional-details/index.d.ts +8 -0
- package/dist/additional-details/index.js +12 -0
- package/dist/audit/index.d.ts +6 -0
- package/dist/audit/index.js +47 -0
- package/dist/configs/dto.d.ts +5 -1
- package/dist/configs/dto.js +10 -1
- package/dist/crons/dto.d.ts +9 -0
- package/dist/crons/dto.js +43 -0
- package/dist/crons/index.d.ts +38 -0
- package/dist/crons/index.js +109 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +12 -4
- package/dist/notes/dto.d.ts +9 -1
- package/dist/notes/dto.js +46 -1
- package/dist/notes/index.d.ts +9 -0
- package/dist/notes/index.js +13 -0
- package/dist/service-client/index.d.ts +4 -0
- package/dist/service-client/index.js +6 -0
- package/dist/staff-client.d.ts +18 -1
- package/dist/staff-client.js +30 -0
- package/dist/statuses/dto.d.ts +9 -1
- package/dist/statuses/dto.js +46 -1
- package/dist/statuses/index.d.ts +7 -2
- package/dist/statuses/index.js +18 -10
- package/dist/supporting-files/dto.d.ts +9 -1
- package/dist/supporting-files/dto.js +46 -1
- package/dist/supporting-files/index.d.ts +5 -0
- package/dist/supporting-files/index.js +9 -0
- package/dist/tenancy.d.ts +12 -0
- package/dist/tenancy.js +22 -1
- package/package.json +2 -2
package/dist/notes/dto.js
CHANGED
|
@@ -9,9 +9,11 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.AddNoteDto = exports.GetNotesDto = exports.NoteIdDto = void 0;
|
|
12
|
+
exports.GetAllNotesDto = exports.AddNoteDto = exports.GetNotesDto = exports.NoteIdDto = void 0;
|
|
13
13
|
const class_validator_1 = require("class-validator");
|
|
14
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
15
|
const enum_1 = require("@managemint-solutions/entities/notes/enum");
|
|
16
|
+
const validators_1 = require("../validators");
|
|
15
17
|
// class-validator versions of the shared DTO types from @managemint-solutions/entities.
|
|
16
18
|
// Each class `implements` its entities type so the wire contract cannot drift.
|
|
17
19
|
class NoteIdDto {
|
|
@@ -45,3 +47,46 @@ __decorate([
|
|
|
45
47
|
(0, class_validator_1.MaxLength)(255, { message: 'Content cannot exceed 255 characters' }),
|
|
46
48
|
__metadata("design:type", String)
|
|
47
49
|
], AddNoteDto.prototype, "content", void 0);
|
|
50
|
+
/** The admin api's GET /notes: every organization's notes, a page at a time. */
|
|
51
|
+
class GetAllNotesDto {
|
|
52
|
+
page = 1;
|
|
53
|
+
page_size = 25;
|
|
54
|
+
organization_id;
|
|
55
|
+
entity_type;
|
|
56
|
+
search;
|
|
57
|
+
}
|
|
58
|
+
exports.GetAllNotesDto = GetAllNotesDto;
|
|
59
|
+
__decorate([
|
|
60
|
+
(0, class_validator_1.IsOptional)(),
|
|
61
|
+
(0, class_transformer_1.Transform)(({ value }) => (value ? Number(value) : 1), { toClassOnly: true }),
|
|
62
|
+
(0, class_validator_1.IsNumber)({}, { message: 'Page must be a number' }),
|
|
63
|
+
(0, class_validator_1.Min)(1, { message: 'Page must be at least 1' }),
|
|
64
|
+
__metadata("design:type", Number)
|
|
65
|
+
], GetAllNotesDto.prototype, "page", void 0);
|
|
66
|
+
__decorate([
|
|
67
|
+
(0, class_validator_1.IsOptional)(),
|
|
68
|
+
(0, class_transformer_1.Transform)(({ value }) => (value ? Number(value) : 25), { toClassOnly: true }),
|
|
69
|
+
(0, class_validator_1.IsNumber)({}, { message: 'Page size must be a number' }),
|
|
70
|
+
(0, class_validator_1.Min)(1, { message: 'Page size must be at least 1' }),
|
|
71
|
+
(0, class_validator_1.Max)(100, { message: 'Page size cannot exceed 100' }),
|
|
72
|
+
__metadata("design:type", Number)
|
|
73
|
+
], GetAllNotesDto.prototype, "page_size", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
(0, class_validator_1.IsOptional)(),
|
|
76
|
+
(0, validators_1.IsNullable)(),
|
|
77
|
+
(0, class_validator_1.IsUUID)('7', { message: 'Invalid organization ID format' }),
|
|
78
|
+
__metadata("design:type", Object)
|
|
79
|
+
], GetAllNotesDto.prototype, "organization_id", void 0);
|
|
80
|
+
__decorate([
|
|
81
|
+
(0, class_validator_1.IsOptional)(),
|
|
82
|
+
(0, validators_1.IsNullable)(),
|
|
83
|
+
(0, class_validator_1.IsEnum)(enum_1.NotesEntityTypes, { message: 'Invalid record type' }),
|
|
84
|
+
__metadata("design:type", Object)
|
|
85
|
+
], GetAllNotesDto.prototype, "entity_type", void 0);
|
|
86
|
+
__decorate([
|
|
87
|
+
(0, class_validator_1.IsOptional)(),
|
|
88
|
+
(0, validators_1.IsNullable)(),
|
|
89
|
+
(0, class_validator_1.IsString)({ message: 'Invalid search' }),
|
|
90
|
+
(0, class_validator_1.MaxLength)(200, { message: 'Search cannot exceed 200 characters' }),
|
|
91
|
+
__metadata("design:type", Object)
|
|
92
|
+
], GetAllNotesDto.prototype, "search", void 0);
|
package/dist/notes/index.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { NoteEntity } from '@managemint-solutions/entities/notes';
|
|
2
|
+
import type { StaffNoteRow } from '@managemint-solutions/entities/notes';
|
|
3
|
+
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
2
4
|
import type { AddNoteDto } from '@managemint-solutions/entities/notes/dto';
|
|
3
5
|
import { NotesEntityTypes } from '@managemint-solutions/entities/notes/enum';
|
|
4
6
|
import type { ClientContext } from '../auth';
|
|
5
7
|
import { type AuditResource } from '../audit';
|
|
6
8
|
import type { TypedSupabaseClient } from '../client';
|
|
7
9
|
export * from './dto';
|
|
10
|
+
import type { GetAllNotesDto } from './dto';
|
|
8
11
|
export declare class NotesResource {
|
|
9
12
|
private readonly supabase;
|
|
10
13
|
private readonly auth;
|
|
@@ -12,6 +15,12 @@ export declare class NotesResource {
|
|
|
12
15
|
constructor(supabase: TypedSupabaseClient, auth: ClientContext, audit: AuditResource);
|
|
13
16
|
getById(noteId: string): Promise<NoteEntity>;
|
|
14
17
|
list(entity: NotesEntityTypes, entityId: string): Promise<NoteEntity[]>;
|
|
18
|
+
/**
|
|
19
|
+
* The admin portal's Notes list: every note on a record type in `entityTypes` (the ones the
|
|
20
|
+
* caller may read), across the organizations staff can see, newest first. `record` is left
|
|
21
|
+
* for the caller to name.
|
|
22
|
+
*/
|
|
23
|
+
listAll(query: GetAllNotesDto, entityTypes: string[]): Promise<MmsList<StaffNoteRow>>;
|
|
15
24
|
create(entity: NotesEntityTypes, entityId: string, input: AddNoteDto): Promise<NoteEntity>;
|
|
16
25
|
togglePin(noteId: string): Promise<NoteEntity>;
|
|
17
26
|
remove(noteId: string): Promise<void>;
|
package/dist/notes/index.js
CHANGED
|
@@ -21,11 +21,13 @@ const audit_1 = require("../audit");
|
|
|
21
21
|
const errors_1 = require("../errors");
|
|
22
22
|
const won_1 = require("../leads/won");
|
|
23
23
|
const tenancy_1 = require("../tenancy");
|
|
24
|
+
const transforms_1 = require("../transforms");
|
|
24
25
|
__exportStar(require("./dto"), exports);
|
|
25
26
|
// The public contract is the shared NoteEntity from @managemint-solutions/entities; the casts at
|
|
26
27
|
// the returns declare the shape the select string below produces (the columns are defined in the
|
|
27
28
|
// api's supabase/migrations).
|
|
28
29
|
const NOTE_SELECT = 'mms_id, created_at, content, pinned, user:user_profiles!user_id(*)';
|
|
30
|
+
const NOTE_LIST_SELECT = `${NOTE_SELECT}, entity_type, entity_id, organization:organizations!organization_id(mms_id, name)`;
|
|
29
31
|
class NotesResource {
|
|
30
32
|
supabase;
|
|
31
33
|
auth;
|
|
@@ -55,6 +57,17 @@ class NotesResource {
|
|
|
55
57
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
56
58
|
return data;
|
|
57
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* The admin portal's Notes list: every note on a record type in `entityTypes` (the ones the
|
|
62
|
+
* caller may read), across the organizations staff can see, newest first. `record` is left
|
|
63
|
+
* for the caller to name.
|
|
64
|
+
*/
|
|
65
|
+
async listAll(query, entityTypes) {
|
|
66
|
+
const { data, error, count } = await (0, tenancy_1.filterStaffList)(this.supabase.from('notes').select(NOTE_LIST_SELECT, { count: 'exact' }), query, ['content'], entityTypes).order('created_at', { ascending: false });
|
|
67
|
+
if (error)
|
|
68
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
69
|
+
return (0, transforms_1.toMmsList)(data, query.page, query.page_size, count);
|
|
70
|
+
}
|
|
58
71
|
async create(entity, entityId, input) {
|
|
59
72
|
const organizationId = (0, tenancy_1.requireOrganization)(this.auth);
|
|
60
73
|
if (entity === enum_1.NotesEntityTypes.LEAD) {
|
|
@@ -4,6 +4,7 @@ import { BillingResource } from '../billing';
|
|
|
4
4
|
import { InvoicesResource } from '../billing/invoices';
|
|
5
5
|
import { ClientsResource } from '../clients';
|
|
6
6
|
import { ConfigsResource } from '../configs';
|
|
7
|
+
import { CronRunsResource, CronsResource } from '../crons';
|
|
7
8
|
import { DocumentsResource } from '../documents';
|
|
8
9
|
import { EmulationHandshakesResource } from '../emulation-handshakes';
|
|
9
10
|
import { FeatureRequestsResource } from '../feature-requests';
|
|
@@ -112,6 +113,9 @@ export declare class SupabaseServiceClient {
|
|
|
112
113
|
readonly notifications: NotificationsOutboxResource;
|
|
113
114
|
readonly featureRequests: FeatureRequestsResource;
|
|
114
115
|
readonly configs: ConfigsResource;
|
|
116
|
+
/** The api's cron routes record each run here. */
|
|
117
|
+
readonly crons: CronsResource;
|
|
118
|
+
readonly cronRuns: CronRunsResource;
|
|
115
119
|
readonly signup: OrganizationSignupResource;
|
|
116
120
|
readonly paystackEvents: WebhookEventsResource;
|
|
117
121
|
readonly resendEvents: WebhookEventsResource;
|
|
@@ -9,6 +9,7 @@ const billing_1 = require("../billing");
|
|
|
9
9
|
const invoices_1 = require("../billing/invoices");
|
|
10
10
|
const clients_1 = require("../clients");
|
|
11
11
|
const configs_1 = require("../configs");
|
|
12
|
+
const crons_1 = require("../crons");
|
|
12
13
|
const documents_1 = require("../documents");
|
|
13
14
|
const emulation_handshakes_1 = require("../emulation-handshakes");
|
|
14
15
|
const feature_requests_1 = require("../feature-requests");
|
|
@@ -159,6 +160,9 @@ class SupabaseServiceClient {
|
|
|
159
160
|
featureRequests;
|
|
160
161
|
// Global, with no organization on the row, so it hangs off the client rather than a scope.
|
|
161
162
|
configs;
|
|
163
|
+
/** The api's cron routes record each run here. */
|
|
164
|
+
crons;
|
|
165
|
+
cronRuns;
|
|
162
166
|
signup;
|
|
163
167
|
paystackEvents;
|
|
164
168
|
resendEvents;
|
|
@@ -173,6 +177,8 @@ class SupabaseServiceClient {
|
|
|
173
177
|
this.resendEvents = new webhook_events_1.WebhookEventsResource(this.supabase, 'resend_webhook_events');
|
|
174
178
|
this.featureRequests = new feature_requests_1.FeatureRequestsResource(this.supabase);
|
|
175
179
|
this.configs = new configs_1.ConfigsResource(this.supabase);
|
|
180
|
+
this.crons = new crons_1.CronsResource(this.supabase);
|
|
181
|
+
this.cronRuns = new crons_1.CronRunsResource(this.supabase);
|
|
176
182
|
this.signup = new organizations_1.OrganizationSignupResource(this.supabase, (organizationId) => this.forOrganization(organizationId).audit);
|
|
177
183
|
// The outbox spans organizations - the row it is about carries its own - so it takes a
|
|
178
184
|
// factory and scopes per write rather than being built inside one organization's scope.
|
package/dist/staff-client.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { type StaffContext } from './auth';
|
|
2
2
|
import { AuditResource } from './audit';
|
|
3
3
|
import { AdditionalDetailsResource } from './additional-details';
|
|
4
|
-
import type
|
|
4
|
+
import { SupabaseClient, type SupabaseClientConfig, type TypedSupabaseClient } from './client';
|
|
5
5
|
import { ClientsResource } from './clients';
|
|
6
|
+
import { ConfigsResource } from './configs';
|
|
7
|
+
import { CronRunsResource, CronsResource } from './crons';
|
|
6
8
|
import { DocumentsResource, DocumentTemplatesResource } from './documents';
|
|
7
9
|
import { LeadsResource } from './leads';
|
|
8
10
|
import { NotesResource } from './notes';
|
|
9
11
|
import { OrganizationsResource } from './organizations';
|
|
10
12
|
import { ProjectsResource } from './projects';
|
|
11
13
|
import { StaffPermissionsResource, StaffResource } from './staff';
|
|
14
|
+
import { StatusesResource } from './statuses';
|
|
12
15
|
import { SupportingFilesResource } from './supporting-files';
|
|
13
16
|
import { TasksResource } from './tasks';
|
|
14
17
|
import { UserApiCredentialsReads } from './user-api-credentials';
|
|
@@ -28,6 +31,10 @@ export declare class SupabaseStaffClient {
|
|
|
28
31
|
readonly audit: AuditResource;
|
|
29
32
|
readonly additionalDetails: AdditionalDetailsResource;
|
|
30
33
|
readonly clients: ClientsResource;
|
|
34
|
+
/** Reads only: the `staff` role has no write grant, so an edit goes through the service client. */
|
|
35
|
+
readonly configs: ConfigsResource;
|
|
36
|
+
readonly crons: CronsResource;
|
|
37
|
+
readonly cronRuns: CronRunsResource;
|
|
31
38
|
readonly leads: LeadsResource;
|
|
32
39
|
readonly notes: NotesResource;
|
|
33
40
|
readonly projects: ProjectsResource;
|
|
@@ -38,10 +45,20 @@ export declare class SupabaseStaffClient {
|
|
|
38
45
|
readonly organizations: OrganizationsResource;
|
|
39
46
|
readonly staff: StaffResource;
|
|
40
47
|
readonly staffPermissions: StaffPermissionsResource;
|
|
48
|
+
/** Every organization's statuses (`listAll`); a write is refused, there being no organization. */
|
|
49
|
+
readonly statuses: StatusesResource;
|
|
41
50
|
/** Lists and parents only: Storage names no staff, so a download is signed on the service client. */
|
|
42
51
|
readonly supportingFiles: SupportingFilesResource;
|
|
43
52
|
readonly userApiCredentials: UserApiCredentialsReads;
|
|
44
53
|
constructor({ url, key, accessToken, auth, client }: SupabaseStaffClientConfig);
|
|
54
|
+
/**
|
|
55
|
+
* The tenant client's resources, reading one organization as this staff member: the same
|
|
56
|
+
* staff token, so the `staff` role's policies still decide what comes back, and every write
|
|
57
|
+
* is still refused at the database (the role holds SELECT alone). For the modules whose
|
|
58
|
+
* resources scope every query to the caller's organization - HR, inventory, documents,
|
|
59
|
+
* billing, statuses and labels - which a staff client, having none, cannot read through.
|
|
60
|
+
*/
|
|
61
|
+
forOrganization(organizationId: string): SupabaseClient;
|
|
45
62
|
}
|
|
46
63
|
export declare const createSupabaseStaffClient: (config: SupabaseStaffClientConfig) => SupabaseStaffClient;
|
|
47
64
|
export {};
|
package/dist/staff-client.js
CHANGED
|
@@ -4,13 +4,17 @@ exports.createSupabaseStaffClient = exports.SupabaseStaffClient = void 0;
|
|
|
4
4
|
const auth_1 = require("./auth");
|
|
5
5
|
const audit_1 = require("./audit");
|
|
6
6
|
const additional_details_1 = require("./additional-details");
|
|
7
|
+
const client_1 = require("./client");
|
|
7
8
|
const clients_1 = require("./clients");
|
|
9
|
+
const configs_1 = require("./configs");
|
|
10
|
+
const crons_1 = require("./crons");
|
|
8
11
|
const documents_1 = require("./documents");
|
|
9
12
|
const leads_1 = require("./leads");
|
|
10
13
|
const notes_1 = require("./notes");
|
|
11
14
|
const organizations_1 = require("./organizations");
|
|
12
15
|
const projects_1 = require("./projects");
|
|
13
16
|
const staff_1 = require("./staff");
|
|
17
|
+
const statuses_1 = require("./statuses");
|
|
14
18
|
const supporting_files_1 = require("./supporting-files");
|
|
15
19
|
const tasks_1 = require("./tasks");
|
|
16
20
|
const user_api_credentials_1 = require("./user-api-credentials");
|
|
@@ -27,6 +31,10 @@ class SupabaseStaffClient {
|
|
|
27
31
|
audit;
|
|
28
32
|
additionalDetails;
|
|
29
33
|
clients;
|
|
34
|
+
/** Reads only: the `staff` role has no write grant, so an edit goes through the service client. */
|
|
35
|
+
configs;
|
|
36
|
+
crons;
|
|
37
|
+
cronRuns;
|
|
30
38
|
leads;
|
|
31
39
|
notes;
|
|
32
40
|
projects;
|
|
@@ -37,6 +45,8 @@ class SupabaseStaffClient {
|
|
|
37
45
|
organizations;
|
|
38
46
|
staff;
|
|
39
47
|
staffPermissions;
|
|
48
|
+
/** Every organization's statuses (`listAll`); a write is refused, there being no organization. */
|
|
49
|
+
statuses;
|
|
40
50
|
/** Lists and parents only: Storage names no staff, so a download is signed on the service client. */
|
|
41
51
|
supportingFiles;
|
|
42
52
|
userApiCredentials;
|
|
@@ -45,6 +55,9 @@ class SupabaseStaffClient {
|
|
|
45
55
|
this.auth = auth;
|
|
46
56
|
this.audit = new audit_1.AuditResource(this.supabase, auth);
|
|
47
57
|
this.clients = new clients_1.ClientsResource(this.supabase, auth, this.audit);
|
|
58
|
+
this.configs = new configs_1.ConfigsResource(this.supabase);
|
|
59
|
+
this.crons = new crons_1.CronsResource(this.supabase);
|
|
60
|
+
this.cronRuns = new crons_1.CronRunsResource(this.supabase);
|
|
48
61
|
this.additionalDetails = new additional_details_1.AdditionalDetailsResource(this.supabase, auth, this.audit);
|
|
49
62
|
this.leads = new leads_1.LeadsResource(this.supabase, auth, this.audit, this.additionalDetails);
|
|
50
63
|
this.notes = new notes_1.NotesResource(this.supabase, auth, this.audit);
|
|
@@ -56,9 +69,26 @@ class SupabaseStaffClient {
|
|
|
56
69
|
this.organizations = new organizations_1.OrganizationsResource(this.supabase, auth, this.audit);
|
|
57
70
|
this.staff = new staff_1.StaffResource(this.supabase);
|
|
58
71
|
this.staffPermissions = new staff_1.StaffPermissionsResource(this.supabase);
|
|
72
|
+
this.statuses = new statuses_1.StatusesResource(this.supabase, auth, this.audit);
|
|
59
73
|
this.supportingFiles = new supporting_files_1.SupportingFilesResource(this.supabase, auth, this.audit);
|
|
60
74
|
this.userApiCredentials = new user_api_credentials_1.UserApiCredentialsReads(this.supabase);
|
|
61
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* The tenant client's resources, reading one organization as this staff member: the same
|
|
78
|
+
* staff token, so the `staff` role's policies still decide what comes back, and every write
|
|
79
|
+
* is still refused at the database (the role holds SELECT alone). For the modules whose
|
|
80
|
+
* resources scope every query to the caller's organization - HR, inventory, documents,
|
|
81
|
+
* billing, statuses and labels - which a staff client, having none, cannot read through.
|
|
82
|
+
*/
|
|
83
|
+
forOrganization(organizationId) {
|
|
84
|
+
return new client_1.SupabaseClient({
|
|
85
|
+
url: '',
|
|
86
|
+
key: '',
|
|
87
|
+
accessToken: '',
|
|
88
|
+
client: this.supabase,
|
|
89
|
+
auth: { mms_id: this.auth.mms_id, organization_id: organizationId },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
62
92
|
}
|
|
63
93
|
exports.SupabaseStaffClient = SupabaseStaffClient;
|
|
64
94
|
const createSupabaseStaffClient = (config) => new SupabaseStaffClient(config);
|
package/dist/statuses/dto.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AddStatusDto as AddStatusDtoType, GetStatusesDto as GetStatusesDtoType, ReorderStatusesDto as ReorderStatusesDtoType, StatusIdDto as StatusIdDtoType, UpdateStatusDto as UpdateStatusDtoType } from '@managemint-solutions/entities/status/dto';
|
|
1
|
+
import type { AddStatusDto as AddStatusDtoType, GetAllStatusesDto as GetAllStatusesDtoType, GetStatusesDto as GetStatusesDtoType, ReorderStatusesDto as ReorderStatusesDtoType, StatusIdDto as StatusIdDtoType, UpdateStatusDto as UpdateStatusDtoType } from '@managemint-solutions/entities/status/dto';
|
|
2
2
|
import { StatusEntityTypes } from '@managemint-solutions/entities/status/enum';
|
|
3
3
|
export declare class StatusIdDto implements StatusIdDtoType {
|
|
4
4
|
readonly statusId: string;
|
|
@@ -20,3 +20,11 @@ export declare class UpdateStatusDto implements UpdateStatusDtoType {
|
|
|
20
20
|
readonly colour: string;
|
|
21
21
|
readonly is_complete?: boolean;
|
|
22
22
|
}
|
|
23
|
+
/** The admin api's GET /statuses: every organization's statuses, a page at a time. */
|
|
24
|
+
export declare class GetAllStatusesDto implements GetAllStatusesDtoType {
|
|
25
|
+
readonly page: number;
|
|
26
|
+
readonly page_size: number;
|
|
27
|
+
readonly organization_id?: string | null;
|
|
28
|
+
readonly entity_type?: StatusEntityTypes | null;
|
|
29
|
+
readonly search?: string | null;
|
|
30
|
+
}
|
package/dist/statuses/dto.js
CHANGED
|
@@ -9,9 +9,11 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.UpdateStatusDto = exports.ReorderStatusesDto = exports.AddStatusDto = exports.GetStatusesDto = exports.StatusIdDto = void 0;
|
|
12
|
+
exports.GetAllStatusesDto = exports.UpdateStatusDto = exports.ReorderStatusesDto = exports.AddStatusDto = exports.GetStatusesDto = exports.StatusIdDto = void 0;
|
|
13
13
|
const class_validator_1 = require("class-validator");
|
|
14
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
15
|
const enum_1 = require("@managemint-solutions/entities/status/enum");
|
|
16
|
+
const validators_1 = require("../validators");
|
|
15
17
|
// class-validator versions of the shared DTO types from @managemint-solutions/entities.
|
|
16
18
|
// Each class `implements` its entities type so the wire contract cannot drift.
|
|
17
19
|
class StatusIdDto {
|
|
@@ -100,3 +102,46 @@ __decorate([
|
|
|
100
102
|
(0, class_validator_1.IsBoolean)({ message: 'is_complete must be true or false' }),
|
|
101
103
|
__metadata("design:type", Boolean)
|
|
102
104
|
], UpdateStatusDto.prototype, "is_complete", void 0);
|
|
105
|
+
/** The admin api's GET /statuses: every organization's statuses, a page at a time. */
|
|
106
|
+
class GetAllStatusesDto {
|
|
107
|
+
page = 1;
|
|
108
|
+
page_size = 25;
|
|
109
|
+
organization_id;
|
|
110
|
+
entity_type;
|
|
111
|
+
search;
|
|
112
|
+
}
|
|
113
|
+
exports.GetAllStatusesDto = GetAllStatusesDto;
|
|
114
|
+
__decorate([
|
|
115
|
+
(0, class_validator_1.IsOptional)(),
|
|
116
|
+
(0, class_transformer_1.Transform)(({ value }) => (value ? Number(value) : 1), { toClassOnly: true }),
|
|
117
|
+
(0, class_validator_1.IsNumber)({}, { message: 'Page must be a number' }),
|
|
118
|
+
(0, class_validator_1.Min)(1, { message: 'Page must be at least 1' }),
|
|
119
|
+
__metadata("design:type", Number)
|
|
120
|
+
], GetAllStatusesDto.prototype, "page", void 0);
|
|
121
|
+
__decorate([
|
|
122
|
+
(0, class_validator_1.IsOptional)(),
|
|
123
|
+
(0, class_transformer_1.Transform)(({ value }) => (value ? Number(value) : 25), { toClassOnly: true }),
|
|
124
|
+
(0, class_validator_1.IsNumber)({}, { message: 'Page size must be a number' }),
|
|
125
|
+
(0, class_validator_1.Min)(1, { message: 'Page size must be at least 1' }),
|
|
126
|
+
(0, class_validator_1.Max)(100, { message: 'Page size cannot exceed 100' }),
|
|
127
|
+
__metadata("design:type", Number)
|
|
128
|
+
], GetAllStatusesDto.prototype, "page_size", void 0);
|
|
129
|
+
__decorate([
|
|
130
|
+
(0, class_validator_1.IsOptional)(),
|
|
131
|
+
(0, validators_1.IsNullable)(),
|
|
132
|
+
(0, class_validator_1.IsUUID)('7', { message: 'Invalid organization ID format' }),
|
|
133
|
+
__metadata("design:type", Object)
|
|
134
|
+
], GetAllStatusesDto.prototype, "organization_id", void 0);
|
|
135
|
+
__decorate([
|
|
136
|
+
(0, class_validator_1.IsOptional)(),
|
|
137
|
+
(0, validators_1.IsNullable)(),
|
|
138
|
+
(0, class_validator_1.IsEnum)(enum_1.StatusEntityTypes, { message: 'Invalid record type' }),
|
|
139
|
+
__metadata("design:type", Object)
|
|
140
|
+
], GetAllStatusesDto.prototype, "entity_type", void 0);
|
|
141
|
+
__decorate([
|
|
142
|
+
(0, class_validator_1.IsOptional)(),
|
|
143
|
+
(0, validators_1.IsNullable)(),
|
|
144
|
+
(0, class_validator_1.IsString)({ message: 'Invalid search' }),
|
|
145
|
+
(0, class_validator_1.MaxLength)(200, { message: 'Search cannot exceed 200 characters' }),
|
|
146
|
+
__metadata("design:type", Object)
|
|
147
|
+
], GetAllStatusesDto.prototype, "search", void 0);
|
package/dist/statuses/index.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
|
+
import type { StaffStatusRow } from '@managemint-solutions/entities/status';
|
|
2
|
+
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
1
3
|
import type { StatusEntity } from '@managemint-solutions/entities/status';
|
|
2
4
|
import type { AddStatusDto, ReorderStatusesDto, UpdateStatusDto } from '@managemint-solutions/entities/status/dto';
|
|
3
5
|
import type { StatusEntityTypes } from '@managemint-solutions/entities/status/enum';
|
|
4
|
-
import type {
|
|
6
|
+
import type { ClientContext } from '../auth';
|
|
5
7
|
import { type AuditResource } from '../audit';
|
|
6
8
|
import type { TypedSupabaseClient } from '../client';
|
|
7
9
|
export * from './dto';
|
|
10
|
+
import type { GetAllStatusesDto } from './dto';
|
|
8
11
|
export declare class StatusesResource {
|
|
9
12
|
private readonly supabase;
|
|
10
13
|
private readonly auth;
|
|
11
14
|
private readonly table;
|
|
12
|
-
constructor(supabase: TypedSupabaseClient, auth:
|
|
15
|
+
constructor(supabase: TypedSupabaseClient, auth: ClientContext, audit: AuditResource);
|
|
13
16
|
/** The catalogue in its order: `position` first, then insertion order for the seeded ties. */
|
|
14
17
|
list(entity: StatusEntityTypes): Promise<StatusEntity[]>;
|
|
18
|
+
/** The admin portal's Statuses list: every organization's statuses, by organization, record type and position. */
|
|
19
|
+
listAll(query: GetAllStatusesDto): Promise<MmsList<StaffStatusRow>>;
|
|
15
20
|
/** A new status goes last: one past the highest position its entity type has. */
|
|
16
21
|
create(input: AddStatusDto): Promise<StatusEntity>;
|
|
17
22
|
/**
|
package/dist/statuses/index.js
CHANGED
|
@@ -18,12 +18,15 @@ exports.StatusesResource = void 0;
|
|
|
18
18
|
const enum_1 = require("@managemint-solutions/entities/audit-trail/enum");
|
|
19
19
|
const audit_1 = require("../audit");
|
|
20
20
|
const errors_1 = require("../errors");
|
|
21
|
+
const tenancy_1 = require("../tenancy");
|
|
22
|
+
const transforms_1 = require("../transforms");
|
|
21
23
|
__exportStar(require("./dto"), exports);
|
|
22
24
|
const refuseWon = (message) => new errors_1.SupabaseClientError({ status: 400, error: 'Database Error', message });
|
|
23
25
|
// The public contract is the shared StatusEntity from @managemint-solutions/entities; the
|
|
24
26
|
// `as StatusEntity` casts declare the shape the select string below produces (the columns are
|
|
25
27
|
// defined in the api's supabase/migrations).
|
|
26
28
|
const STATUS_SELECT = 'mms_id, label, colour, is_complete, is_fail, is_triage, is_won, position';
|
|
29
|
+
const STATUS_LIST_SELECT = `${STATUS_SELECT}, entity_type, organization:organizations!organization_id(mms_id, name)`;
|
|
27
30
|
// The audited write returns the whole row; the wire shape is the STATUS_SELECT columns.
|
|
28
31
|
const toStatus = (row) => ({
|
|
29
32
|
mms_id: row.mms_id,
|
|
@@ -49,10 +52,7 @@ class StatusesResource {
|
|
|
49
52
|
}
|
|
50
53
|
/** The catalogue in its order: `position` first, then insertion order for the seeded ties. */
|
|
51
54
|
async list(entity) {
|
|
52
|
-
const { data, error } = await this.supabase
|
|
53
|
-
.from('statuses')
|
|
54
|
-
.select(STATUS_SELECT)
|
|
55
|
-
.eq('organization_id', this.auth.organization_id)
|
|
55
|
+
const { data, error } = await (0, tenancy_1.scopeToOrganization)(this.supabase.from('statuses').select(STATUS_SELECT), this.auth)
|
|
56
56
|
.eq('entity_type', entity)
|
|
57
57
|
.order('position')
|
|
58
58
|
.order('created_at')
|
|
@@ -61,12 +61,23 @@ class StatusesResource {
|
|
|
61
61
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
62
62
|
return data;
|
|
63
63
|
}
|
|
64
|
+
/** The admin portal's Statuses list: every organization's statuses, by organization, record type and position. */
|
|
65
|
+
async listAll(query) {
|
|
66
|
+
const { data, error, count } = await (0, tenancy_1.filterStaffList)(this.supabase.from('statuses').select(STATUS_LIST_SELECT, { count: 'exact' }), query, ['label'])
|
|
67
|
+
.order('organization_id')
|
|
68
|
+
.order('entity_type')
|
|
69
|
+
.order('position');
|
|
70
|
+
if (error)
|
|
71
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
72
|
+
return (0, transforms_1.toMmsList)(data, query.page, query.page_size, count);
|
|
73
|
+
}
|
|
64
74
|
/** A new status goes last: one past the highest position its entity type has. */
|
|
65
75
|
async create(input) {
|
|
76
|
+
const organizationId = (0, tenancy_1.requireOrganization)(this.auth);
|
|
66
77
|
const { data, error } = await this.supabase
|
|
67
78
|
.from('statuses')
|
|
68
79
|
.select('position')
|
|
69
|
-
.eq('organization_id',
|
|
80
|
+
.eq('organization_id', organizationId)
|
|
70
81
|
.eq('entity_type', input.entity)
|
|
71
82
|
.order('position', { ascending: false })
|
|
72
83
|
.limit(1)
|
|
@@ -80,7 +91,7 @@ class StatusesResource {
|
|
|
80
91
|
entity_type: input.entity,
|
|
81
92
|
is_complete: input.is_complete ?? false,
|
|
82
93
|
position: last === undefined ? 0 : last + 1,
|
|
83
|
-
organization_id:
|
|
94
|
+
organization_id: organizationId,
|
|
84
95
|
created_by: this.auth.mms_id,
|
|
85
96
|
});
|
|
86
97
|
return toStatus(row);
|
|
@@ -142,10 +153,7 @@ class StatusesResource {
|
|
|
142
153
|
* status the organization marks complete included - is ordinary.
|
|
143
154
|
*/
|
|
144
155
|
async isWon(statusId) {
|
|
145
|
-
const { data, error } = await this.supabase
|
|
146
|
-
.from('statuses')
|
|
147
|
-
.select('is_won')
|
|
148
|
-
.eq('organization_id', this.auth.organization_id)
|
|
156
|
+
const { data, error } = await (0, tenancy_1.scopeToOrganization)(this.supabase.from('statuses').select('is_won'), this.auth)
|
|
149
157
|
.eq('mms_id', statusId)
|
|
150
158
|
.maybeSingle();
|
|
151
159
|
if (error)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GetSupportingFilesDto as GetSupportingFilesDtoType, SupportingFileIdDto as SupportingFileIdDtoType, SupportingFileNamesDto as SupportingFileNamesDtoType } from '@managemint-solutions/entities/supporting-files/dto';
|
|
1
|
+
import type { GetAllSupportingFilesDto as GetAllSupportingFilesDtoType, GetSupportingFilesDto as GetSupportingFilesDtoType, SupportingFileIdDto as SupportingFileIdDtoType, SupportingFileNamesDto as SupportingFileNamesDtoType } from '@managemint-solutions/entities/supporting-files/dto';
|
|
2
2
|
import { SupportingFilesEntityTypes } from '@managemint-solutions/entities/supporting-files/enum';
|
|
3
3
|
/** Files accepted in one upload. */
|
|
4
4
|
export declare const MAX_FILES_PER_UPLOAD = 10;
|
|
@@ -12,3 +12,11 @@ export declare class GetSupportingFilesDto implements GetSupportingFilesDtoType
|
|
|
12
12
|
export declare class SupportingFileNamesDto implements SupportingFileNamesDtoType {
|
|
13
13
|
readonly names: string[];
|
|
14
14
|
}
|
|
15
|
+
/** The admin api's GET /supporting-files: every organization's supporting files, a page at a time. */
|
|
16
|
+
export declare class GetAllSupportingFilesDto implements GetAllSupportingFilesDtoType {
|
|
17
|
+
readonly page: number;
|
|
18
|
+
readonly page_size: number;
|
|
19
|
+
readonly organization_id?: string | null;
|
|
20
|
+
readonly entity_type?: SupportingFilesEntityTypes | null;
|
|
21
|
+
readonly search?: string | null;
|
|
22
|
+
}
|
|
@@ -9,9 +9,11 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.SupportingFileNamesDto = exports.GetSupportingFilesDto = exports.SupportingFileIdDto = exports.MAX_FILES_PER_UPLOAD = void 0;
|
|
12
|
+
exports.GetAllSupportingFilesDto = exports.SupportingFileNamesDto = exports.GetSupportingFilesDto = exports.SupportingFileIdDto = exports.MAX_FILES_PER_UPLOAD = void 0;
|
|
13
13
|
const class_validator_1 = require("class-validator");
|
|
14
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
15
|
const enum_1 = require("@managemint-solutions/entities/supporting-files/enum");
|
|
16
|
+
const validators_1 = require("../validators");
|
|
15
17
|
// class-validator versions of the shared DTO types from @managemint-solutions/entities.
|
|
16
18
|
// Each class `implements` its entities type so the wire contract cannot drift.
|
|
17
19
|
/** Files accepted in one upload. */
|
|
@@ -51,3 +53,46 @@ __decorate([
|
|
|
51
53
|
(0, class_validator_1.IsNotEmpty)({ each: true, message: 'A file name cannot be empty' }),
|
|
52
54
|
__metadata("design:type", Array)
|
|
53
55
|
], SupportingFileNamesDto.prototype, "names", void 0);
|
|
56
|
+
/** The admin api's GET /supporting-files: every organization's supporting files, a page at a time. */
|
|
57
|
+
class GetAllSupportingFilesDto {
|
|
58
|
+
page = 1;
|
|
59
|
+
page_size = 25;
|
|
60
|
+
organization_id;
|
|
61
|
+
entity_type;
|
|
62
|
+
search;
|
|
63
|
+
}
|
|
64
|
+
exports.GetAllSupportingFilesDto = GetAllSupportingFilesDto;
|
|
65
|
+
__decorate([
|
|
66
|
+
(0, class_validator_1.IsOptional)(),
|
|
67
|
+
(0, class_transformer_1.Transform)(({ value }) => (value ? Number(value) : 1), { toClassOnly: true }),
|
|
68
|
+
(0, class_validator_1.IsNumber)({}, { message: 'Page must be a number' }),
|
|
69
|
+
(0, class_validator_1.Min)(1, { message: 'Page must be at least 1' }),
|
|
70
|
+
__metadata("design:type", Number)
|
|
71
|
+
], GetAllSupportingFilesDto.prototype, "page", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
(0, class_validator_1.IsOptional)(),
|
|
74
|
+
(0, class_transformer_1.Transform)(({ value }) => (value ? Number(value) : 25), { toClassOnly: true }),
|
|
75
|
+
(0, class_validator_1.IsNumber)({}, { message: 'Page size must be a number' }),
|
|
76
|
+
(0, class_validator_1.Min)(1, { message: 'Page size must be at least 1' }),
|
|
77
|
+
(0, class_validator_1.Max)(100, { message: 'Page size cannot exceed 100' }),
|
|
78
|
+
__metadata("design:type", Number)
|
|
79
|
+
], GetAllSupportingFilesDto.prototype, "page_size", void 0);
|
|
80
|
+
__decorate([
|
|
81
|
+
(0, class_validator_1.IsOptional)(),
|
|
82
|
+
(0, validators_1.IsNullable)(),
|
|
83
|
+
(0, class_validator_1.IsUUID)('7', { message: 'Invalid organization ID format' }),
|
|
84
|
+
__metadata("design:type", Object)
|
|
85
|
+
], GetAllSupportingFilesDto.prototype, "organization_id", void 0);
|
|
86
|
+
__decorate([
|
|
87
|
+
(0, class_validator_1.IsOptional)(),
|
|
88
|
+
(0, validators_1.IsNullable)(),
|
|
89
|
+
(0, class_validator_1.IsEnum)(enum_1.SupportingFilesEntityTypes, { message: 'Invalid record type' }),
|
|
90
|
+
__metadata("design:type", Object)
|
|
91
|
+
], GetAllSupportingFilesDto.prototype, "entity_type", void 0);
|
|
92
|
+
__decorate([
|
|
93
|
+
(0, class_validator_1.IsOptional)(),
|
|
94
|
+
(0, validators_1.IsNullable)(),
|
|
95
|
+
(0, class_validator_1.IsString)({ message: 'Invalid search' }),
|
|
96
|
+
(0, class_validator_1.MaxLength)(200, { message: 'Search cannot exceed 200 characters' }),
|
|
97
|
+
__metadata("design:type", Object)
|
|
98
|
+
], GetAllSupportingFilesDto.prototype, "search", void 0);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { SupportingFileEntity, SupportingFilePresignedUrl, SupportingFileUploadTarget } from '@managemint-solutions/entities/supporting-files';
|
|
2
|
+
import type { StaffSupportingFileRow } from '@managemint-solutions/entities/supporting-files';
|
|
3
|
+
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
2
4
|
import { SupportingFilesEntityTypes } from '@managemint-solutions/entities/supporting-files/enum';
|
|
3
5
|
import { type AuditResource, type ResourceActor } from '../audit';
|
|
4
6
|
import type { TypedSupabaseClient } from '../client';
|
|
5
7
|
export * from './dto';
|
|
8
|
+
import type { GetAllSupportingFilesDto } from './dto';
|
|
6
9
|
export * from './paths';
|
|
7
10
|
export declare class SupportingFilesResource {
|
|
8
11
|
private readonly supabase;
|
|
@@ -25,6 +28,8 @@ export declare class SupportingFilesResource {
|
|
|
25
28
|
organization_id: string;
|
|
26
29
|
}>;
|
|
27
30
|
list(entity: SupportingFilesEntityTypes, entityId: string): Promise<SupportingFileEntity[]>;
|
|
31
|
+
/** The admin portal's Supporting Files list: every file on a record type in `entityTypes`, newest first. */
|
|
32
|
+
listAll(query: GetAllSupportingFilesDto, entityTypes: string[]): Promise<MmsList<StaffSupportingFileRow>>;
|
|
28
33
|
/**
|
|
29
34
|
* Where the caller puts each file: one signed upload URL per name, in request order, valid
|
|
30
35
|
* for two hours, into the organization's prefix. Storage checks the bucket's INSERT policy
|
|
@@ -21,9 +21,11 @@ const audit_1 = require("../audit");
|
|
|
21
21
|
const errors_1 = require("../errors");
|
|
22
22
|
const won_1 = require("../leads/won");
|
|
23
23
|
const tenancy_1 = require("../tenancy");
|
|
24
|
+
const transforms_1 = require("../transforms");
|
|
24
25
|
const paths_1 = require("./paths");
|
|
25
26
|
__exportStar(require("./dto"), exports);
|
|
26
27
|
__exportStar(require("./paths"), exports);
|
|
28
|
+
const SUPPORTING_FILE_LIST_SELECT = 'mms_id, created_at, name, extension, entity_type, entity_id, created_by:user_profiles!created_by(*), organization:organizations!organization_id(mms_id, name)';
|
|
27
29
|
/** How long a presigned download URL stays valid. */
|
|
28
30
|
const SIGNED_URL_TTL_SECONDS = 300;
|
|
29
31
|
/** Storage calls per file (minting an upload URL, checking an object) run concurrently in batches of this size. */
|
|
@@ -98,6 +100,13 @@ class SupportingFilesResource {
|
|
|
98
100
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
99
101
|
return data;
|
|
100
102
|
}
|
|
103
|
+
/** The admin portal's Supporting Files list: every file on a record type in `entityTypes`, newest first. */
|
|
104
|
+
async listAll(query, entityTypes) {
|
|
105
|
+
const { data, error, count } = await (0, tenancy_1.filterStaffList)(this.supabase.from('supporting_files').select(SUPPORTING_FILE_LIST_SELECT, { count: 'exact' }), query, ['name'], entityTypes).order('created_at', { ascending: false });
|
|
106
|
+
if (error)
|
|
107
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
108
|
+
return (0, transforms_1.toMmsList)(data, query.page, query.page_size, count);
|
|
109
|
+
}
|
|
101
110
|
/**
|
|
102
111
|
* Where the caller puts each file: one signed upload URL per name, in request order, valid
|
|
103
112
|
* for two hours, into the organization's prefix. Storage checks the bucket's INSERT policy
|
package/dist/tenancy.d.ts
CHANGED
|
@@ -11,3 +11,15 @@ export declare const requireOrganization: (actor: {
|
|
|
11
11
|
export declare const withOrganization: (select: string, actor: {
|
|
12
12
|
organization_id: string | null;
|
|
13
13
|
}) => string;
|
|
14
|
+
/**
|
|
15
|
+
* A staff list's page with its filters applied: the organization, the record type, a text search
|
|
16
|
+
* over `searchColumns`, and when given, the record types a row may belong to (those the caller
|
|
17
|
+
* may read). The read spans every organization the caller's policies let it see.
|
|
18
|
+
*/
|
|
19
|
+
export declare const filterStaffList: <Q extends PostgrestFilterBuilder<any, any, any, any, any, any, any>>(query: Q, filters: {
|
|
20
|
+
page: number;
|
|
21
|
+
page_size: number;
|
|
22
|
+
organization_id?: string | null;
|
|
23
|
+
entity_type?: string | null;
|
|
24
|
+
search?: string | null;
|
|
25
|
+
}, searchColumns: readonly string[], entityTypes?: string[]) => Q;
|
package/dist/tenancy.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withOrganization = exports.requireOrganization = exports.scopeToOrganization = void 0;
|
|
3
|
+
exports.filterStaffList = exports.withOrganization = exports.requireOrganization = exports.scopeToOrganization = void 0;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
|
+
const transforms_1 = require("./transforms");
|
|
5
6
|
// How a resource scopes itself to the caller's organization. A tenant (`AuthContext`) reads and
|
|
6
7
|
// writes inside their own; staff (`StaffContext`, `organization_id: null`) read across every
|
|
7
8
|
// organization, as their role's policies allow, and write nothing.
|
|
@@ -23,3 +24,23 @@ exports.requireOrganization = requireOrganization;
|
|
|
23
24
|
/** A select, plus the row's organization on a staff read so the row says whose it is. */
|
|
24
25
|
const withOrganization = (select, actor) => actor.organization_id === null ? `${select}, organization:organizations!organization_id(mms_id, name)` : select;
|
|
25
26
|
exports.withOrganization = withOrganization;
|
|
27
|
+
/**
|
|
28
|
+
* A staff list's page with its filters applied: the organization, the record type, a text search
|
|
29
|
+
* over `searchColumns`, and when given, the record types a row may belong to (those the caller
|
|
30
|
+
* may read). The read spans every organization the caller's policies let it see.
|
|
31
|
+
*/
|
|
32
|
+
const filterStaffList = (query, filters, searchColumns, entityTypes) => {
|
|
33
|
+
const from = (filters.page - 1) * filters.page_size;
|
|
34
|
+
let builder = query.range(from, from + filters.page_size - 1);
|
|
35
|
+
if (filters.organization_id)
|
|
36
|
+
builder = builder.eq('organization_id', filters.organization_id);
|
|
37
|
+
if (filters.entity_type)
|
|
38
|
+
builder = builder.eq('entity_type', filters.entity_type);
|
|
39
|
+
if (entityTypes)
|
|
40
|
+
builder = builder.in('entity_type', entityTypes);
|
|
41
|
+
const search = filters.search?.trim();
|
|
42
|
+
if (search)
|
|
43
|
+
builder = builder.or((0, transforms_1.orIlike)(searchColumns, search));
|
|
44
|
+
return builder;
|
|
45
|
+
};
|
|
46
|
+
exports.filterStaffList = filterStaffList;
|