@fabriktor/fx 0.0.145 → 0.0.147
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/src/chat/forms.d.ts +9 -0
- package/dist/src/chat/forms.js +16 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/routes/availability.d.ts +7 -0
- package/dist/src/routes/availability.js +14 -0
- package/dist/src/users/users.d.ts +2 -0
- package/dist/src/users/users.js +17 -9
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { User } from "@fabriktor/schema";
|
|
2
|
+
import type { EmploymentActor } from "../contacts/scope.js";
|
|
3
|
+
export type ChatFormRecipient = Pick<User, "id" | "account_identity">;
|
|
4
|
+
export type ChatFormPolicy = {
|
|
5
|
+
readonly sender_modes: readonly User["active_mode"][];
|
|
6
|
+
readonly recipient_modes: readonly User["active_mode"][];
|
|
7
|
+
readonly allow_group: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function canSendChatForm(policy: ChatFormPolicy, actor: EmploymentActor, recipients: readonly ChatFormRecipient[]): boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { normalizeIDs, resolvedID } from "../core/id.js";
|
|
2
|
+
import { isFabriktorUser, userSupportsMode } from "../users/users.js";
|
|
3
|
+
// Recipients are all other members of the conversation, resolved before use.
|
|
4
|
+
// Sender modes apply now; recipient modes describe what their identity supports.
|
|
5
|
+
// Do not depend on the recipient's current mode, which Public() omits.
|
|
6
|
+
export function canSendChatForm(policy, actor, recipients) {
|
|
7
|
+
return (!!resolvedID(actor.owner_id) &&
|
|
8
|
+
policy.sender_modes.includes(actor.active_mode) &&
|
|
9
|
+
recipients.length > 0 &&
|
|
10
|
+
(policy.allow_group || recipients.length === 1) &&
|
|
11
|
+
normalizeIDs(recipients.map((recipient) => recipient.id)).length ===
|
|
12
|
+
recipients.length &&
|
|
13
|
+
recipients.every((recipient) => recipient.id !== actor.owner_id &&
|
|
14
|
+
isFabriktorUser(recipient) &&
|
|
15
|
+
policy.recipient_modes.some((mode) => userSupportsMode(recipient, mode))));
|
|
16
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./billable/billable.js";
|
|
|
3
3
|
export * from "./calls/calls.js";
|
|
4
4
|
export * from "./chat/chat.js";
|
|
5
5
|
export * from "./chat/data.js";
|
|
6
|
+
export * from "./chat/forms.js";
|
|
6
7
|
export * from "./chat/optimistic.js";
|
|
7
8
|
export * from "./chat/send-queue.js";
|
|
8
9
|
export * from "./contacts/contacts.js";
|
package/dist/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./billable/billable.js";
|
|
|
3
3
|
export * from "./calls/calls.js";
|
|
4
4
|
export * from "./chat/chat.js";
|
|
5
5
|
export * from "./chat/data.js";
|
|
6
|
+
export * from "./chat/forms.js";
|
|
6
7
|
export * from "./chat/optimistic.js";
|
|
7
8
|
export * from "./chat/send-queue.js";
|
|
8
9
|
export * from "./contacts/contacts.js";
|
|
@@ -2,8 +2,14 @@ import type { JobsOperator, RoutesOperator, UsersOperator } from "@fabriktor/cli
|
|
|
2
2
|
import { type ClientAvailability, type InClientAvailability, type Location, type PrefCompany, type User } from "@fabriktor/schema";
|
|
3
3
|
import type { PreferencesFXOperator } from "../preferences/preferences.js";
|
|
4
4
|
import { type ChatData } from "../chat/data.js";
|
|
5
|
+
import { type ChatFormRecipient } from "../chat/forms.js";
|
|
5
6
|
import { type RoutesActor } from "./workspace.js";
|
|
6
7
|
export declare const clientAvailabilityMinimumWindows = 3;
|
|
8
|
+
export declare const availabilityRequestPolicy: {
|
|
9
|
+
readonly sender_modes: readonly ["employer"];
|
|
10
|
+
readonly recipient_modes: readonly ["client"];
|
|
11
|
+
readonly allow_group: false;
|
|
12
|
+
};
|
|
7
13
|
export declare const RoutesChatDataType: {
|
|
8
14
|
readonly AvailabilityRequest: "routes.availability.request";
|
|
9
15
|
readonly AvailabilitySubmitted: "routes.availability.submitted";
|
|
@@ -25,6 +31,7 @@ export type AvailabilityReaders = {
|
|
|
25
31
|
};
|
|
26
32
|
export declare function availabilityReference(data: ChatData): AvailabilityReference | undefined;
|
|
27
33
|
export declare function availabilityRequest(location: Location, actor: RoutesActor): ChatData;
|
|
34
|
+
export declare function canSendAvailabilityRequest(data: ChatData, actor: RoutesActor, recipients: readonly ChatFormRecipient[]): boolean;
|
|
28
35
|
export declare function availabilitySubmission(reference: AvailabilityReference, request_id: string, ids: readonly string[]): ChatData;
|
|
29
36
|
export declare function loadAvailabilityContext(readers: AvailabilityReaders, actor: RoutesActor, reference: AvailabilityReference, signal?: AbortSignal): Promise<AvailabilityContext>;
|
|
30
37
|
export declare function validateAvailabilityWindows(inputs: readonly InClientAvailability[], context: AvailabilityContext, minimum?: number): void;
|
|
@@ -5,9 +5,15 @@ import { hoursPerDay, minutesOfDay, minutesPerHour, sameCalendarDay, } from "../
|
|
|
5
5
|
import { errValidation } from "../core/err.js";
|
|
6
6
|
import { normalizeIDs, resolvedID } from "../core/id.js";
|
|
7
7
|
import { newChatData } from "../chat/data.js";
|
|
8
|
+
import { canSendChatForm, } from "../chat/forms.js";
|
|
8
9
|
import { routeCanPlan } from "./workspace.js";
|
|
9
10
|
import { validateClientAvailabilityInput } from "./validation.js";
|
|
10
11
|
export const clientAvailabilityMinimumWindows = 3;
|
|
12
|
+
export const availabilityRequestPolicy = {
|
|
13
|
+
sender_modes: [UserTypeEmployer],
|
|
14
|
+
recipient_modes: [UserTypeClient],
|
|
15
|
+
allow_group: false,
|
|
16
|
+
};
|
|
11
17
|
export const RoutesChatDataType = {
|
|
12
18
|
AvailabilityRequest: "routes.availability.request",
|
|
13
19
|
AvailabilitySubmitted: "routes.availability.submitted",
|
|
@@ -55,6 +61,14 @@ export function availabilityRequest(location, actor) {
|
|
|
55
61
|
location_id: location.id,
|
|
56
62
|
});
|
|
57
63
|
}
|
|
64
|
+
export function canSendAvailabilityRequest(data, actor, recipients) {
|
|
65
|
+
const reference = availabilityReference(data);
|
|
66
|
+
return (data.type === RoutesChatDataType.AvailabilityRequest &&
|
|
67
|
+
!!reference &&
|
|
68
|
+
reference.employer_id === actor.owner_id &&
|
|
69
|
+
canSendChatForm(availabilityRequestPolicy, actor, recipients) &&
|
|
70
|
+
reference.client_id === recipients[0].id);
|
|
71
|
+
}
|
|
58
72
|
export function availabilitySubmission(reference, request_id, ids) {
|
|
59
73
|
const data = newChatData(RoutesChatDataType.AvailabilitySubmitted, {
|
|
60
74
|
...reference,
|
|
@@ -15,6 +15,7 @@ export type UpdateExternalUserOptions = {
|
|
|
15
15
|
owner_id: string;
|
|
16
16
|
in: ExternalUserInput;
|
|
17
17
|
};
|
|
18
|
+
export declare function userSupportsMode(user: Pick<User, "account_identity"> | null | undefined, mode: User["active_mode"]): boolean;
|
|
18
19
|
export declare function isFabriktorUser(user: Pick<User, "account_identity"> | null | undefined): boolean;
|
|
19
20
|
export declare function canManageExternalUser(user: User | null | undefined, owner_id: string): boolean;
|
|
20
21
|
export type SearchExternalUsersOptions = SearchUsersOptions;
|
|
@@ -84,3 +85,4 @@ export declare class UsersFX implements UsersFXOperator {
|
|
|
84
85
|
private updatedUser;
|
|
85
86
|
}
|
|
86
87
|
export declare function newUsersFX(opts: UsersFXOptions): UsersFX;
|
|
88
|
+
export declare function isWorkAccountIdentity(value: string): value is WorkAccountIdentity;
|
package/dist/src/users/users.js
CHANGED
|
@@ -27,11 +27,22 @@ const msgWorkIdentityRequiresClient = "A work identity can only be established f
|
|
|
27
27
|
const msgInvalidWorkIdentity = "Work identity must be employee or employer.";
|
|
28
28
|
const resourceUsername = "username";
|
|
29
29
|
const resourceUser = "user";
|
|
30
|
+
// Mirrors User.ValAccountMode. Public users expose identity, not active mode.
|
|
31
|
+
// A work identity retains Client mode even while its owner is working.
|
|
32
|
+
const accountModes = {
|
|
33
|
+
[UserTypeClient]: [UserTypeClient],
|
|
34
|
+
[UserTypeEmployee]: [UserTypeEmployee, UserTypeClient],
|
|
35
|
+
[UserTypeEmployer]: [UserTypeEmployer, UserTypeClient],
|
|
36
|
+
[UserTypeExternal]: [UserTypeExternal],
|
|
37
|
+
};
|
|
38
|
+
export function userSupportsMode(user, mode) {
|
|
39
|
+
return (!!user &&
|
|
40
|
+
Object.hasOwn(accountModes, user.account_identity) &&
|
|
41
|
+
accountModes[user.account_identity].includes(mode));
|
|
42
|
+
}
|
|
30
43
|
// External records are private contacts, not messaging accounts.
|
|
31
44
|
export function isFabriktorUser(user) {
|
|
32
|
-
return (user
|
|
33
|
-
user?.account_identity === UserTypeEmployee ||
|
|
34
|
-
user?.account_identity === UserTypeEmployer);
|
|
45
|
+
return userSupportsMode(user, UserTypeClient);
|
|
35
46
|
}
|
|
36
47
|
export function canManageExternalUser(user, owner_id) {
|
|
37
48
|
return (!!resolvedID(owner_id) &&
|
|
@@ -384,15 +395,12 @@ function errPhoneCodeRequired(field) {
|
|
|
384
395
|
msg: msgPhoneCodeRequired,
|
|
385
396
|
});
|
|
386
397
|
}
|
|
387
|
-
function isWorkAccountIdentity(value) {
|
|
398
|
+
export function isWorkAccountIdentity(value) {
|
|
388
399
|
return value === UserTypeEmployee || value === UserTypeEmployer;
|
|
389
400
|
}
|
|
390
401
|
function validateActiveMode(accountIdentity, activeMode) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
(accountIdentity === UserTypeEmployee &&
|
|
394
|
-
(activeMode === UserTypeEmployee || activeMode === UserTypeClient)) ||
|
|
395
|
-
(accountIdentity === UserTypeClient && activeMode === UserTypeClient)) {
|
|
402
|
+
const user = { account_identity: accountIdentity };
|
|
403
|
+
if (isFabriktorUser(user) && userSupportsMode(user, activeMode)) {
|
|
396
404
|
return;
|
|
397
405
|
}
|
|
398
406
|
throw errValidation({
|