mirror of
https://github.com/Sea-Haven-Industries/seahaven-door-unlock-api.git
synced 2026-09-30 11:43:12 +00:00
Some checks failed
Deploy / deploy (push) Has been cancelled
* feat(3cx): sync office department BLFs via XAPI Keep unlock and lockdown keys in the templates and write colleague plus shared-parking BLFs per extension so phones skip their own line. * fix(3cx): preserve parking BLF IDs and fail the job on PATCH errors
95 lines
2.3 KiB
TypeScript
95 lines
2.3 KiB
TypeScript
export interface UserGroup {
|
|
Id?: number;
|
|
Name?: string;
|
|
}
|
|
|
|
export interface SyncUser {
|
|
Id: number;
|
|
Number: string;
|
|
FirstName?: string;
|
|
LastName?: string;
|
|
PrimaryGroupId?: number;
|
|
Groups?: UserGroup[];
|
|
Blfs?: string;
|
|
Phones?: Array<{
|
|
TemplateName?: string;
|
|
Name?: string;
|
|
}>;
|
|
}
|
|
|
|
const QUEUE_EXTENSION_MIN = 800;
|
|
|
|
export const ALWAYS_INCLUDE_EXTENSIONS = ["100"] as const;
|
|
|
|
export interface ColleagueRef {
|
|
id: number;
|
|
number: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
}
|
|
|
|
export function splitPeerName(name: string | undefined): { firstName: string; lastName: string } {
|
|
const parts = (name ?? "").trim().split(/\s+/).filter(Boolean);
|
|
if (parts.length === 0) {
|
|
return { firstName: "", lastName: "" };
|
|
}
|
|
if (parts.length === 1) {
|
|
return { firstName: parts[0], lastName: "" };
|
|
}
|
|
return { firstName: parts.slice(0, -1).join(" "), lastName: parts[parts.length - 1] };
|
|
}
|
|
|
|
export function addAlwaysIncludedColleagues(
|
|
colleagues: ColleagueRef[],
|
|
extras: ColleagueRef[]
|
|
): ColleagueRef[] {
|
|
const next = [...colleagues];
|
|
for (const extra of extras) {
|
|
if (!next.some((c) => c.number === extra.number)) {
|
|
next.push(extra);
|
|
}
|
|
}
|
|
return next;
|
|
}
|
|
|
|
export function isEligibleColleague(user: SyncUser): boolean {
|
|
if (!/^\d+$/.test(user.Number)) {
|
|
return false;
|
|
}
|
|
if (Number.parseInt(user.Number, 10) >= QUEUE_EXTENSION_MIN) {
|
|
return false;
|
|
}
|
|
const first = (user.FirstName ?? "").trim();
|
|
const last = (user.LastName ?? "").trim();
|
|
if (!first && !last) {
|
|
return false;
|
|
}
|
|
const full = `${first} ${last}`.trim().toLowerCase();
|
|
if (full === "voicemail") {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export function primaryDepartmentName(user: SyncUser): string | undefined {
|
|
const groups = user.Groups ?? [];
|
|
const named = groups.find((g) => (g.Name ?? "").toUpperCase() !== "DEFAULT" && (g.Name ?? "").trim() !== "");
|
|
if (named?.Name) {
|
|
return named.Name;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function groupUsersByDepartment(users: SyncUser[]): Map<string, SyncUser[]> {
|
|
const byDept = new Map<string, SyncUser[]>();
|
|
for (const user of users) {
|
|
const dept = primaryDepartmentName(user);
|
|
if (!dept) {
|
|
continue;
|
|
}
|
|
const list = byDept.get(dept) ?? [];
|
|
list.push(user);
|
|
byDept.set(dept, list);
|
|
}
|
|
return byDept;
|
|
}
|