@managemint-solutions/sdk 0.39.0 → 0.40.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/README.md +75 -3
- package/dist/client.d.ts +3 -0
- package/dist/client.js +5 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +14 -1
- package/dist/leave/dto.d.ts +4 -0
- package/dist/leave/dto.js +24 -0
- package/dist/leave/leave-entitlements.d.ts +10 -3
- package/dist/leave/leave-entitlements.js +26 -6
- package/dist/leave/leave-requests.d.ts +2 -3
- package/dist/leave/leave-requests.js +2 -1
- package/dist/leave/leave-types.d.ts +2 -3
- package/dist/leave/leave-types.js +3 -0
- package/dist/leave/working-days.d.ts +4 -0
- package/dist/leave/working-days.js +13 -5
- package/dist/permissions/dto.d.ts +4 -0
- package/dist/permissions/dto.js +20 -0
- package/dist/service-client/index.d.ts +13 -0
- package/dist/service-client/index.js +22 -0
- package/dist/timesheets/dto.d.ts +42 -0
- package/dist/timesheets/dto.js +211 -0
- package/dist/timesheets/index.d.ts +4 -0
- package/dist/timesheets/index.js +27 -0
- package/dist/timesheets/timesheet-entries.d.ts +54 -0
- package/dist/timesheets/timesheet-entries.js +141 -0
- package/dist/timesheets/timesheets.d.ts +67 -0
- package/dist/timesheets/timesheets.js +201 -0
- package/dist/timesheets/weeks.d.ts +7 -0
- package/dist/timesheets/weeks.js +26 -0
- package/dist/transforms.d.ts +13 -0
- package/dist/transforms.js +24 -1
- package/dist/users/dto.d.ts +1 -0
- package/dist/users/dto.js +7 -0
- package/dist/users/index.d.ts +8 -1
- package/dist/users/index.js +17 -2
- package/dist/validators.d.ts +10 -0
- package/dist/validators.js +21 -1
- package/package.json +2 -2
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TimesheetsResource = void 0;
|
|
4
|
+
const enum_1 = require("@managemint-solutions/entities/audit-trail/enum");
|
|
5
|
+
const enum_2 = require("@managemint-solutions/entities/timesheets/enum");
|
|
6
|
+
const audit_1 = require("../audit");
|
|
7
|
+
const errors_1 = require("../errors");
|
|
8
|
+
const working_days_1 = require("../leave/working-days");
|
|
9
|
+
const transforms_1 = require("../transforms");
|
|
10
|
+
const timesheet_entries_1 = require("./timesheet-entries");
|
|
11
|
+
const weeks_1 = require("./weeks");
|
|
12
|
+
const TIMESHEET_SELECT = `
|
|
13
|
+
mms_id,
|
|
14
|
+
created_at,
|
|
15
|
+
created_by:user_profiles!created_by (*),
|
|
16
|
+
updated_at,
|
|
17
|
+
last_updated_by:user_profiles!last_updated_by (*),
|
|
18
|
+
user:user_profiles!user_id (*),
|
|
19
|
+
week_start,
|
|
20
|
+
status,
|
|
21
|
+
submitted_at,
|
|
22
|
+
reviewed_by:user_profiles!reviewed_by (*),
|
|
23
|
+
reviewed_at,
|
|
24
|
+
review_note
|
|
25
|
+
`;
|
|
26
|
+
class TimesheetsResource {
|
|
27
|
+
supabase;
|
|
28
|
+
auth;
|
|
29
|
+
table;
|
|
30
|
+
constructor(supabase, auth, audit) {
|
|
31
|
+
this.supabase = supabase;
|
|
32
|
+
this.auth = auth;
|
|
33
|
+
this.table = (0, audit_1.auditedTable)(supabase, auth, audit, {
|
|
34
|
+
table: 'timesheets',
|
|
35
|
+
entityType: enum_1.AuditTrailEntityType.TIMESHEETS,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async list(query, options = {}) {
|
|
39
|
+
const page = query.page;
|
|
40
|
+
const pageSize = query.page_size;
|
|
41
|
+
const offset = (page - 1) * pageSize;
|
|
42
|
+
let timesheetsQuery = this.supabase
|
|
43
|
+
.from('timesheets')
|
|
44
|
+
.select(TIMESHEET_SELECT, { count: 'estimated' })
|
|
45
|
+
.eq('organization_id', this.auth.organization_id)
|
|
46
|
+
.order('week_start', { ascending: false })
|
|
47
|
+
.order('created_at', { ascending: false })
|
|
48
|
+
.range(offset, offset + pageSize - 1);
|
|
49
|
+
if (options.userIds) {
|
|
50
|
+
timesheetsQuery = timesheetsQuery.in('user_id', options.userIds);
|
|
51
|
+
}
|
|
52
|
+
if (query.user_id) {
|
|
53
|
+
timesheetsQuery = timesheetsQuery.eq('user_id', query.user_id);
|
|
54
|
+
}
|
|
55
|
+
if (query.status) {
|
|
56
|
+
timesheetsQuery = timesheetsQuery.eq('status', query.status);
|
|
57
|
+
}
|
|
58
|
+
if (query.week_start) {
|
|
59
|
+
timesheetsQuery = timesheetsQuery.eq('week_start', query.week_start);
|
|
60
|
+
}
|
|
61
|
+
const { data, error, count } = await timesheetsQuery;
|
|
62
|
+
if (error)
|
|
63
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
64
|
+
const rows = await this.withTotals((data ?? []));
|
|
65
|
+
return (0, transforms_1.toMmsList)(rows, page, pageSize, count);
|
|
66
|
+
}
|
|
67
|
+
/** The week with its entries, by date then start time. */
|
|
68
|
+
async getById(timesheetId) {
|
|
69
|
+
const summary = await this.summaryById(timesheetId);
|
|
70
|
+
const { data, error } = await this.supabase
|
|
71
|
+
.from('timesheet_entries')
|
|
72
|
+
.select(timesheet_entries_1.TIMESHEET_ENTRY_SELECT)
|
|
73
|
+
.eq('organization_id', this.auth.organization_id)
|
|
74
|
+
.eq('timesheet_id', timesheetId)
|
|
75
|
+
.order('date')
|
|
76
|
+
.order('start_time');
|
|
77
|
+
if (error)
|
|
78
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
79
|
+
return { ...summary, entries: (data ?? []).map(timesheet_entries_1.withClockTimes) };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The week of `userId` starting on `weekStart`, or null when no entry has opened it yet. One
|
|
83
|
+
* row per user and Monday (the table's unique key), so there is at most one.
|
|
84
|
+
*/
|
|
85
|
+
async findFor(userId, weekStart) {
|
|
86
|
+
const { data, error } = await this.supabase
|
|
87
|
+
.from('timesheets')
|
|
88
|
+
.select('mms_id, status, week_start')
|
|
89
|
+
.eq('organization_id', this.auth.organization_id)
|
|
90
|
+
.eq('user_id', userId)
|
|
91
|
+
.eq('week_start', weekStart)
|
|
92
|
+
.maybeSingle();
|
|
93
|
+
if (error)
|
|
94
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
/** Opens a week as a draft. A second opener of the same week hits the unique key - the SDK's 409. */
|
|
98
|
+
async create(input) {
|
|
99
|
+
const row = await this.table.insert({
|
|
100
|
+
user_id: input.user_id,
|
|
101
|
+
week_start: input.week_start,
|
|
102
|
+
status: enum_2.TimesheetStatus.DRAFT,
|
|
103
|
+
organization_id: this.auth.organization_id,
|
|
104
|
+
created_by: this.auth.mms_id,
|
|
105
|
+
created_at: new Date().toISOString(),
|
|
106
|
+
});
|
|
107
|
+
return this.summaryById(row.mms_id);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Sends a draft or rejected week for review, clearing the previous decision. Resolves to null
|
|
111
|
+
* when the week was no longer in either state - submitted twice from two tabs, say.
|
|
112
|
+
*/
|
|
113
|
+
async submit(timesheetId) {
|
|
114
|
+
const now = new Date().toISOString();
|
|
115
|
+
const row = await this.table.updateWhere(timesheetId, (query) => query.in('status', [enum_2.TimesheetStatus.DRAFT, enum_2.TimesheetStatus.REJECTED]), {
|
|
116
|
+
status: enum_2.TimesheetStatus.SUBMITTED,
|
|
117
|
+
submitted_at: now,
|
|
118
|
+
reviewed_by: null,
|
|
119
|
+
reviewed_at: null,
|
|
120
|
+
review_note: null,
|
|
121
|
+
last_updated_by: this.auth.mms_id,
|
|
122
|
+
updated_at: now,
|
|
123
|
+
});
|
|
124
|
+
return row ? this.summaryById(timesheetId) : null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Approves or rejects a submitted week. Resolves to null when the week was no longer
|
|
128
|
+
* submitted - another reviewer got there first - so the caller can say so instead of
|
|
129
|
+
* overwriting their decision.
|
|
130
|
+
*/
|
|
131
|
+
async review(timesheetId, input) {
|
|
132
|
+
const now = new Date().toISOString();
|
|
133
|
+
const row = await this.table.updateWhere(timesheetId, (query) => query.eq('status', enum_2.TimesheetStatus.SUBMITTED), {
|
|
134
|
+
status: input.status,
|
|
135
|
+
reviewed_by: input.reviewed_by,
|
|
136
|
+
reviewed_at: now,
|
|
137
|
+
review_note: input.review_note,
|
|
138
|
+
last_updated_by: this.auth.mms_id,
|
|
139
|
+
updated_at: now,
|
|
140
|
+
});
|
|
141
|
+
return row ? this.summaryById(timesheetId) : null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The ids of `userIds`' approved weeks that could hold an entry dated `dateFrom`..`dateTo` -
|
|
145
|
+
* the weeks starting from the Monday of `dateFrom` up to `dateTo`. The report keeps only the
|
|
146
|
+
* entries of these weeks.
|
|
147
|
+
*/
|
|
148
|
+
async approvedIds(userIds, dateFrom, dateTo) {
|
|
149
|
+
if (userIds.length === 0)
|
|
150
|
+
return new Set();
|
|
151
|
+
const rows = await (0, transforms_1.allRows)((from, to) => this.supabase
|
|
152
|
+
.from('timesheets')
|
|
153
|
+
.select('mms_id')
|
|
154
|
+
.eq('organization_id', this.auth.organization_id)
|
|
155
|
+
.in('user_id', userIds)
|
|
156
|
+
.eq('status', enum_2.TimesheetStatus.APPROVED)
|
|
157
|
+
.gte('week_start', (0, weeks_1.weekStartOf)(dateFrom))
|
|
158
|
+
.lte('week_start', dateTo)
|
|
159
|
+
.order('mms_id')
|
|
160
|
+
.range(from, to));
|
|
161
|
+
return new Set(rows.map((row) => row.mms_id));
|
|
162
|
+
}
|
|
163
|
+
async summaryById(timesheetId) {
|
|
164
|
+
const { data, error } = await this.supabase
|
|
165
|
+
.from('timesheets')
|
|
166
|
+
.select(TIMESHEET_SELECT)
|
|
167
|
+
.eq('mms_id', timesheetId)
|
|
168
|
+
.eq('organization_id', this.auth.organization_id)
|
|
169
|
+
.single();
|
|
170
|
+
if (error)
|
|
171
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
172
|
+
const [row] = await this.withTotals([data]);
|
|
173
|
+
return row;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Adds `week_end` and the totals to each row from one query over the entries of these weeks.
|
|
177
|
+
* Nothing is stored: a total is always what the entries add up to.
|
|
178
|
+
*/
|
|
179
|
+
async withTotals(rows) {
|
|
180
|
+
if (rows.length === 0)
|
|
181
|
+
return [];
|
|
182
|
+
const timesheetIds = rows.map((row) => row.mms_id);
|
|
183
|
+
const entries = await (0, transforms_1.allRows)((from, to) => this.supabase
|
|
184
|
+
.from('timesheet_entries')
|
|
185
|
+
.select('timesheet_id, duration_minutes')
|
|
186
|
+
.eq('organization_id', this.auth.organization_id)
|
|
187
|
+
.in('timesheet_id', timesheetIds)
|
|
188
|
+
.order('mms_id')
|
|
189
|
+
.range(from, to));
|
|
190
|
+
return rows.map((row) => {
|
|
191
|
+
const mine = entries.filter((entry) => entry.timesheet_id === row.mms_id);
|
|
192
|
+
return {
|
|
193
|
+
...row,
|
|
194
|
+
week_end: (0, working_days_1.shiftDays)(row.week_start, 6),
|
|
195
|
+
total_minutes: mine.reduce((total, entry) => total + Number(entry.duration_minutes), 0),
|
|
196
|
+
entry_count: mine.length,
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
exports.TimesheetsResource = TimesheetsResource;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** The Monday of the week containing `isoDate` (a Sunday belongs to the week before it). */
|
|
2
|
+
export declare const weekStartOf: (isoDate: string) => string;
|
|
3
|
+
/**
|
|
4
|
+
* The minutes from `start` to `end` on one day; `end` may be `24:00` for midnight. Zero or
|
|
5
|
+
* negative when the end is not after the start - the caller rejects that, so it is not clamped.
|
|
6
|
+
*/
|
|
7
|
+
export declare const minutesBetween: (start: string, end: string) => number;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.minutesBetween = exports.weekStartOf = void 0;
|
|
4
|
+
const working_days_1 = require("../leave/working-days");
|
|
5
|
+
// The calendar arithmetic behind timesheets: which Monday-to-Sunday week a date belongs to, and
|
|
6
|
+
// how long an entry is. Pure functions over `YYYY-MM-DD` and `HH:MM` strings - the columns are
|
|
7
|
+
// `date` and `time`, so nothing here depends on the caller's clock zone.
|
|
8
|
+
/** The Monday of the week containing `isoDate` (a Sunday belongs to the week before it). */
|
|
9
|
+
const weekStartOf = (isoDate) => {
|
|
10
|
+
const date = (0, working_days_1.parseIsoDate)(isoDate);
|
|
11
|
+
// getUTCDay: 0 = Sunday .. 6 = Saturday; Monday is 0 days back, Sunday 6.
|
|
12
|
+
const daysSinceMonday = (date.getUTCDay() + 6) % 7;
|
|
13
|
+
date.setUTCDate(date.getUTCDate() - daysSinceMonday);
|
|
14
|
+
return (0, working_days_1.toIsoDate)(date);
|
|
15
|
+
};
|
|
16
|
+
exports.weekStartOf = weekStartOf;
|
|
17
|
+
const toMinutes = (clockTime) => {
|
|
18
|
+
const [hours, minutes] = clockTime.split(':').map(Number);
|
|
19
|
+
return hours * 60 + minutes;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The minutes from `start` to `end` on one day; `end` may be `24:00` for midnight. Zero or
|
|
23
|
+
* negative when the end is not after the start - the caller rejects that, so it is not clamped.
|
|
24
|
+
*/
|
|
25
|
+
const minutesBetween = (start, end) => toMinutes(end) - toMinutes(start);
|
|
26
|
+
exports.minutesBetween = minutesBetween;
|
package/dist/transforms.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PostgrestError } from '@supabase/supabase-js';
|
|
1
2
|
import type { MmsList } from '@managemint-solutions/entities/common';
|
|
2
3
|
/**
|
|
3
4
|
* The `client_id` / `project_id` list filters accept either a plain UUID v7 or the base64-JSON
|
|
@@ -55,3 +56,15 @@ export declare const restoredPatch: (by: string) => {
|
|
|
55
56
|
restored_at: string;
|
|
56
57
|
restored_by: string;
|
|
57
58
|
};
|
|
59
|
+
/** The most rows one PostgREST response carries (Supabase's `max-rows`); past it a result is cut short silently. */
|
|
60
|
+
export declare const ROW_CAP = 1000;
|
|
61
|
+
/**
|
|
62
|
+
* Every row of a query that may outgrow one response, fetched in pages of `ROW_CAP`. `build`
|
|
63
|
+
* returns the query for one page's `.range(from, to)`; the walk stops at the first short page.
|
|
64
|
+
* The callers are the unpaged reads a job or a rollup makes - a rollover pass, a report, the
|
|
65
|
+
* per-page totals - never a list the api pages itself.
|
|
66
|
+
*/
|
|
67
|
+
export declare const allRows: <T>(build: (from: number, to: number) => PromiseLike<{
|
|
68
|
+
data: unknown;
|
|
69
|
+
error: PostgrestError | null;
|
|
70
|
+
}>) => Promise<T[]>;
|
package/dist/transforms.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Small conversions shared by the DTOs' `@Transform`s, the resources' write payloads, their
|
|
3
3
|
// select strings and their list results.
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.restoredPatch = exports.deletedPatch = exports.unarchivedPatch = exports.archivedPatch = exports.toMmsList = exports.USER_PROFILE_EMBED = exports.toDateInput = exports.orIlike = exports.ilikeContains = exports.parseEntityIdFilter = void 0;
|
|
5
|
+
exports.allRows = exports.ROW_CAP = exports.restoredPatch = exports.deletedPatch = exports.unarchivedPatch = exports.archivedPatch = exports.toMmsList = exports.USER_PROFILE_EMBED = exports.toDateInput = exports.orIlike = exports.ilikeContains = exports.parseEntityIdFilter = void 0;
|
|
6
|
+
const errors_1 = require("./errors");
|
|
6
7
|
// v7, to match the `mms_id` columns these filters compare against - `public.uuidv7()` is the
|
|
7
8
|
// default on every table. This is the gate in front of `@IsUUID('7')`, so a stricter version
|
|
8
9
|
// here would null the id out before validation ever saw it, and a looser one would let a value
|
|
@@ -110,3 +111,25 @@ const restoredPatch = (by) => ({
|
|
|
110
111
|
restored_by: by,
|
|
111
112
|
});
|
|
112
113
|
exports.restoredPatch = restoredPatch;
|
|
114
|
+
/** The most rows one PostgREST response carries (Supabase's `max-rows`); past it a result is cut short silently. */
|
|
115
|
+
exports.ROW_CAP = 1000;
|
|
116
|
+
/**
|
|
117
|
+
* Every row of a query that may outgrow one response, fetched in pages of `ROW_CAP`. `build`
|
|
118
|
+
* returns the query for one page's `.range(from, to)`; the walk stops at the first short page.
|
|
119
|
+
* The callers are the unpaged reads a job or a rollup makes - a rollover pass, a report, the
|
|
120
|
+
* per-page totals - never a list the api pages itself.
|
|
121
|
+
*/
|
|
122
|
+
const allRows = async (build) => {
|
|
123
|
+
const rows = [];
|
|
124
|
+
for (let offset = 0;; offset += exports.ROW_CAP) {
|
|
125
|
+
const { data, error } = await build(offset, offset + exports.ROW_CAP - 1);
|
|
126
|
+
if (error)
|
|
127
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
128
|
+
const page = (data ?? []);
|
|
129
|
+
rows.push(...page);
|
|
130
|
+
if (page.length < exports.ROW_CAP)
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
return rows;
|
|
134
|
+
};
|
|
135
|
+
exports.allRows = allRows;
|
package/dist/users/dto.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export declare class UpdateUserDto implements UpdateUserDtoType {
|
|
|
38
38
|
readonly end_date?: string | null;
|
|
39
39
|
readonly job_title?: string | null;
|
|
40
40
|
readonly manager_id?: string | null;
|
|
41
|
+
readonly secondary_manager_id?: string | null;
|
|
41
42
|
readonly social_facebook?: string | null;
|
|
42
43
|
readonly social_linkedin?: string | null;
|
|
43
44
|
readonly social_twitter?: string | null;
|
package/dist/users/dto.js
CHANGED
|
@@ -137,6 +137,7 @@ class UpdateUserDto {
|
|
|
137
137
|
end_date;
|
|
138
138
|
job_title;
|
|
139
139
|
manager_id;
|
|
140
|
+
secondary_manager_id;
|
|
140
141
|
social_facebook;
|
|
141
142
|
social_linkedin;
|
|
142
143
|
social_twitter;
|
|
@@ -209,6 +210,12 @@ __decorate([
|
|
|
209
210
|
(0, class_validator_1.IsUUID)('4', { message: 'Invalid manager ID format' }),
|
|
210
211
|
__metadata("design:type", Object)
|
|
211
212
|
], UpdateUserDto.prototype, "manager_id", void 0);
|
|
213
|
+
__decorate([
|
|
214
|
+
(0, class_validator_1.IsOptional)(),
|
|
215
|
+
(0, validators_1.IsNullable)(),
|
|
216
|
+
(0, class_validator_1.IsUUID)('4', { message: 'Invalid secondary manager ID format' }),
|
|
217
|
+
__metadata("design:type", Object)
|
|
218
|
+
], UpdateUserDto.prototype, "secondary_manager_id", void 0);
|
|
212
219
|
__decorate([
|
|
213
220
|
(0, class_validator_1.IsOptional)(),
|
|
214
221
|
(0, validators_1.EmptyToNull)(),
|
package/dist/users/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './paths';
|
|
|
9
9
|
export type UserRow = Record<string, unknown> & {
|
|
10
10
|
mms_id: string;
|
|
11
11
|
manager_id: string | null;
|
|
12
|
+
secondary_manager_id: string | null;
|
|
12
13
|
created_by: string | null;
|
|
13
14
|
last_updated_by: string | null;
|
|
14
15
|
};
|
|
@@ -26,8 +27,14 @@ export declare class UsersResource {
|
|
|
26
27
|
* client - it needs no service role.
|
|
27
28
|
*/
|
|
28
29
|
identityById(userId?: string | null): Promise<UserIdentity | null>;
|
|
29
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* The users who report to `managerId` - everyone naming them as primary or secondary
|
|
32
|
+
* manager, the direct reports a leave or timesheet approver may act on. One level, and
|
|
33
|
+
* `active` is not consulted: a deactivated report's history stays visible to their manager.
|
|
34
|
+
*/
|
|
30
35
|
idsManagedBy(managerId: string): Promise<string[]>;
|
|
36
|
+
/** The ids of every active member - who the leave rollover grants a new period to. */
|
|
37
|
+
activeIds(): Promise<string[]>;
|
|
31
38
|
/** Seats in use. Counts rows rather than fetching them. */
|
|
32
39
|
activeCount(): Promise<number>;
|
|
33
40
|
isActive(userId: string): Promise<boolean>;
|
package/dist/users/index.js
CHANGED
|
@@ -92,13 +92,28 @@ class UsersResource {
|
|
|
92
92
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
93
93
|
return data;
|
|
94
94
|
}
|
|
95
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* The users who report to `managerId` - everyone naming them as primary or secondary
|
|
97
|
+
* manager, the direct reports a leave or timesheet approver may act on. One level, and
|
|
98
|
+
* `active` is not consulted: a deactivated report's history stays visible to their manager.
|
|
99
|
+
*/
|
|
96
100
|
async idsManagedBy(managerId) {
|
|
97
101
|
const { data, error } = await this.supabase
|
|
98
102
|
.from('users')
|
|
99
103
|
.select('mms_id')
|
|
100
104
|
.eq('organization_id', this.auth.organization_id)
|
|
101
|
-
.
|
|
105
|
+
.or(`manager_id.eq.${managerId},secondary_manager_id.eq.${managerId}`);
|
|
106
|
+
if (error)
|
|
107
|
+
throw (0, errors_1.mapPostgrestError)(error);
|
|
108
|
+
return (data ?? []).map((row) => row.mms_id);
|
|
109
|
+
}
|
|
110
|
+
/** The ids of every active member - who the leave rollover grants a new period to. */
|
|
111
|
+
async activeIds() {
|
|
112
|
+
const { data, error } = await this.supabase
|
|
113
|
+
.from('users')
|
|
114
|
+
.select('mms_id')
|
|
115
|
+
.eq('organization_id', this.auth.organization_id)
|
|
116
|
+
.eq('active', true);
|
|
102
117
|
if (error)
|
|
103
118
|
throw (0, errors_1.mapPostgrestError)(error);
|
|
104
119
|
return (data ?? []).map((row) => row.mms_id);
|
package/dist/validators.d.ts
CHANGED
|
@@ -7,6 +7,16 @@ export declare const isCalendarDate: (value: unknown) => value is string;
|
|
|
7
7
|
* conversion. Stricter on purpose than the `@IsDateString` the timestamp columns use.
|
|
8
8
|
*/
|
|
9
9
|
export declare const IsCalendarDate: (options?: ValidationOptions) => PropertyDecorator;
|
|
10
|
+
/** A wall-clock `HH:MM`; `24:00` as well when `allowEndOfDay` - a shift that ends at midnight. */
|
|
11
|
+
export declare const isClockTime: (value: unknown, allowEndOfDay?: boolean) => value is string;
|
|
12
|
+
/**
|
|
13
|
+
* Clock times, `HH:MM` and nothing more, for the timesheet columns that are `time`: a start is
|
|
14
|
+
* `00:00`..`23:59`, and an end may also be `24:00` (`allowEndOfDay`) so a shift can finish at
|
|
15
|
+
* midnight without crossing it. Seconds are never sent - the SDK trims them off on read.
|
|
16
|
+
*/
|
|
17
|
+
export declare const IsClockTime: ({ allowEndOfDay, ...options }?: ValidationOptions & {
|
|
18
|
+
allowEndOfDay?: boolean;
|
|
19
|
+
}) => PropertyDecorator;
|
|
10
20
|
export declare function IsNullable(validationOptions?: ValidationOptions): PropertyDecorator;
|
|
11
21
|
/**
|
|
12
22
|
* An empty string from a cleared form field means "none". It becomes null before validation,
|
package/dist/validators.js
CHANGED
|
@@ -6,7 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.IsCalendarDate = exports.isCalendarDate = void 0;
|
|
9
|
+
exports.IsClockTime = exports.isClockTime = exports.IsCalendarDate = exports.isCalendarDate = void 0;
|
|
10
10
|
exports.IsNullable = IsNullable;
|
|
11
11
|
exports.EmptyToNull = EmptyToNull;
|
|
12
12
|
const class_validator_1 = require("class-validator");
|
|
@@ -36,6 +36,26 @@ CalendarDateConstraint = __decorate([
|
|
|
36
36
|
*/
|
|
37
37
|
const IsCalendarDate = (options) => (0, class_validator_1.Validate)(CalendarDateConstraint, options);
|
|
38
38
|
exports.IsCalendarDate = IsCalendarDate;
|
|
39
|
+
const CLOCK_TIME = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
40
|
+
/** A wall-clock `HH:MM`; `24:00` as well when `allowEndOfDay` - a shift that ends at midnight. */
|
|
41
|
+
const isClockTime = (value, allowEndOfDay = false) => typeof value === 'string' && (CLOCK_TIME.test(value) || (allowEndOfDay && value === '24:00'));
|
|
42
|
+
exports.isClockTime = isClockTime;
|
|
43
|
+
let ClockTimeConstraint = class ClockTimeConstraint {
|
|
44
|
+
validate(value, args) {
|
|
45
|
+
const [allowEndOfDay] = args.constraints;
|
|
46
|
+
return (0, exports.isClockTime)(value, allowEndOfDay);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
ClockTimeConstraint = __decorate([
|
|
50
|
+
(0, class_validator_1.ValidatorConstraint)({ name: 'isClockTime' })
|
|
51
|
+
], ClockTimeConstraint);
|
|
52
|
+
/**
|
|
53
|
+
* Clock times, `HH:MM` and nothing more, for the timesheet columns that are `time`: a start is
|
|
54
|
+
* `00:00`..`23:59`, and an end may also be `24:00` (`allowEndOfDay`) so a shift can finish at
|
|
55
|
+
* midnight without crossing it. Seconds are never sent - the SDK trims them off on read.
|
|
56
|
+
*/
|
|
57
|
+
const IsClockTime = ({ allowEndOfDay = false, ...options } = {}) => (0, class_validator_1.Validate)(ClockTimeConstraint, [allowEndOfDay], options);
|
|
58
|
+
exports.IsClockTime = IsClockTime;
|
|
39
59
|
function IsNullable(validationOptions) {
|
|
40
60
|
return (0, class_validator_1.ValidateIf)((_object, value) => value !== null, validationOptions);
|
|
41
61
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@managemint-solutions/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "Typed Supabase data-access SDK for ManageMint Solutions",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Scott Bebington <scottbebington@gmail.com>",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"testEnvironment": "node"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@managemint-solutions/entities": "^1.
|
|
54
|
+
"@managemint-solutions/entities": "^1.23.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@nestjs/common": "^11.0.0",
|