rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61)
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61) Co-authored-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com> Co-committed-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com>
This commit was merged in pull request #61.
This commit is contained in:
@@ -1,13 +1,21 @@
|
||||
import { VacationType, VacationStatus } from "@capakraken/db";
|
||||
import type { Prisma, PrismaClient } from "@capakraken/db";
|
||||
import { toIsoDate } from "@capakraken/shared";
|
||||
import { VacationType, VacationStatus } from "@nexus/db";
|
||||
import type { Prisma, PrismaClient } from "@nexus/db";
|
||||
import { toIsoDate } from "@nexus/shared";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { type EntitlementSnapshot, type ResourceHolidayContext, type SyncEntitlementDeps, syncEntitlement } from "./sync-entitlement.js";
|
||||
import {
|
||||
type EntitlementSnapshot,
|
||||
type ResourceHolidayContext,
|
||||
type SyncEntitlementDeps,
|
||||
syncEntitlement,
|
||||
} from "./sync-entitlement.js";
|
||||
|
||||
/** Types that consume from annual leave balance */
|
||||
const BALANCE_TYPES: VacationType[] = [VacationType.ANNUAL, VacationType.OTHER];
|
||||
|
||||
type DbClient = Pick<PrismaClient, "vacation" | "vacationEntitlement" | "systemSettings" | "resource">;
|
||||
type DbClient = Pick<
|
||||
PrismaClient,
|
||||
"vacation" | "vacationEntitlement" | "systemSettings" | "resource"
|
||||
>;
|
||||
|
||||
type EntitlementVacationStatus = "APPROVED" | "PENDING";
|
||||
|
||||
@@ -113,13 +121,15 @@ function hasPersistedHolidaySnapshot(vacation: {
|
||||
holidayCalendarDates: Prisma.JsonValue | null;
|
||||
holidayLegacyPublicHolidayDates: Prisma.JsonValue | null;
|
||||
}): boolean {
|
||||
return vacation.deductedDays != null
|
||||
|| vacation.holidayCountryCode != null
|
||||
|| vacation.holidayCountryName != null
|
||||
|| vacation.holidayFederalState != null
|
||||
|| vacation.holidayMetroCityName != null
|
||||
|| vacation.holidayCalendarDates != null
|
||||
|| vacation.holidayLegacyPublicHolidayDates != null;
|
||||
return (
|
||||
vacation.deductedDays != null ||
|
||||
vacation.holidayCountryCode != null ||
|
||||
vacation.holidayCountryName != null ||
|
||||
vacation.holidayFederalState != null ||
|
||||
vacation.holidayMetroCityName != null ||
|
||||
vacation.holidayCalendarDates != null ||
|
||||
vacation.holidayLegacyPublicHolidayDates != null
|
||||
);
|
||||
}
|
||||
|
||||
function mapEntitlementVacationStatus(status: VacationStatus): EntitlementVacationStatus {
|
||||
@@ -133,49 +143,62 @@ function mapEntitlementVacationStatus(status: VacationStatus): EntitlementVacati
|
||||
});
|
||||
}
|
||||
|
||||
function buildEntitlementHolidayDateUnion(vacations: EntitlementVacationExplainability[]): string[] {
|
||||
return [...new Set(vacations.flatMap((vacation) => vacation.holidayDetails.map((detail) => detail.date)))].sort();
|
||||
function buildEntitlementHolidayDateUnion(
|
||||
vacations: EntitlementVacationExplainability[],
|
||||
): string[] {
|
||||
return [
|
||||
...new Set(
|
||||
vacations.flatMap((vacation) => vacation.holidayDetails.map((detail) => detail.date)),
|
||||
),
|
||||
].sort();
|
||||
}
|
||||
|
||||
function formatEntitlementHolidayBasis(vacation: Pick<
|
||||
EntitlementVacationExplainability,
|
||||
"holidayCountryName" | "holidayCountryCode" | "holidayFederalState" | "holidayMetroCityName"
|
||||
>): string {
|
||||
function formatEntitlementHolidayBasis(
|
||||
vacation: Pick<
|
||||
EntitlementVacationExplainability,
|
||||
"holidayCountryName" | "holidayCountryCode" | "holidayFederalState" | "holidayMetroCityName"
|
||||
>,
|
||||
): string {
|
||||
return [
|
||||
vacation.holidayCountryName ?? vacation.holidayCountryCode ?? null,
|
||||
vacation.holidayFederalState ?? null,
|
||||
vacation.holidayMetroCityName ?? null,
|
||||
].filter((value): value is string => Boolean(value)).join(" / ");
|
||||
]
|
||||
.filter((value): value is string => Boolean(value))
|
||||
.join(" / ");
|
||||
}
|
||||
|
||||
function mapBalanceDetail(resource: {
|
||||
displayName: string;
|
||||
eid: string;
|
||||
}, balance: {
|
||||
year: number;
|
||||
entitledDays: number;
|
||||
carryoverDays: number;
|
||||
usedDays: number;
|
||||
pendingDays: number;
|
||||
remainingDays: number;
|
||||
sickDays: number;
|
||||
deductionSummary?: {
|
||||
formula: string;
|
||||
approvedVacationCount: number;
|
||||
pendingVacationCount: number;
|
||||
approvedRequestedDays: number;
|
||||
pendingRequestedDays: number;
|
||||
approvedDeductedDays: number;
|
||||
pendingDeductedDays: number;
|
||||
excludedHolidayDates: string[];
|
||||
holidayBasisVariants: string[];
|
||||
sources: {
|
||||
hasCalendarHolidays: boolean;
|
||||
hasLegacyPublicHolidayEntries: boolean;
|
||||
function mapBalanceDetail(
|
||||
resource: {
|
||||
displayName: string;
|
||||
eid: string;
|
||||
},
|
||||
balance: {
|
||||
year: number;
|
||||
entitledDays: number;
|
||||
carryoverDays: number;
|
||||
usedDays: number;
|
||||
pendingDays: number;
|
||||
remainingDays: number;
|
||||
sickDays: number;
|
||||
deductionSummary?: {
|
||||
formula: string;
|
||||
approvedVacationCount: number;
|
||||
pendingVacationCount: number;
|
||||
approvedRequestedDays: number;
|
||||
pendingRequestedDays: number;
|
||||
approvedDeductedDays: number;
|
||||
pendingDeductedDays: number;
|
||||
excludedHolidayDates: string[];
|
||||
holidayBasisVariants: string[];
|
||||
sources: {
|
||||
hasCalendarHolidays: boolean;
|
||||
hasLegacyPublicHolidayEntries: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
vacations?: EntitlementVacationExplainability[];
|
||||
}) {
|
||||
vacations?: EntitlementVacationExplainability[];
|
||||
},
|
||||
) {
|
||||
return {
|
||||
resource: resource.displayName,
|
||||
eid: resource.eid,
|
||||
@@ -212,11 +235,13 @@ async function readBalanceSnapshot(
|
||||
});
|
||||
const sickVacations = Array.isArray(sickVacationsResult) ? sickVacationsResult : [];
|
||||
const sickDays = sickVacations.reduce(
|
||||
(sum, vacation) => sum + deps.countCalendarDaysInPeriod(
|
||||
vacation,
|
||||
new Date(`${input.year}-01-01T00:00:00.000Z`),
|
||||
new Date(`${input.year}-12-31T00:00:00.000Z`),
|
||||
),
|
||||
(sum, vacation) =>
|
||||
sum +
|
||||
deps.countCalendarDaysInPeriod(
|
||||
vacation,
|
||||
new Date(`${input.year}-01-01T00:00:00.000Z`),
|
||||
new Date(`${input.year}-12-31T00:00:00.000Z`),
|
||||
),
|
||||
0,
|
||||
);
|
||||
|
||||
@@ -268,68 +293,83 @@ async function readEntitlementVacationExplainability(
|
||||
orderBy: [{ startDate: "asc" }, { endDate: "asc" }],
|
||||
});
|
||||
|
||||
return Promise.all(vacations.map(async (vacation) => {
|
||||
const period = clampVacationPeriodToYear(vacation, yearStart, yearEnd);
|
||||
let vacationHolidayContextPromise: Promise<ResourceHolidayContext> | null = null;
|
||||
const getVacationHolidayContext = async () => {
|
||||
if (!vacationHolidayContextPromise) {
|
||||
vacationHolidayContextPromise = deps.loadResourceHolidayContext(
|
||||
db,
|
||||
input.resourceId,
|
||||
period.startDate,
|
||||
period.endDate,
|
||||
);
|
||||
}
|
||||
return vacationHolidayContextPromise;
|
||||
};
|
||||
const fallbackHolidayContext = await getVacationHolidayContext();
|
||||
const preview = deps.buildVacationPreview({
|
||||
type: vacation.type,
|
||||
startDate: period.startDate,
|
||||
endDate: period.endDate,
|
||||
isHalfDay: vacation.isHalfDay,
|
||||
holidayContext: hasPersistedHolidaySnapshot(vacation)
|
||||
? {
|
||||
countryCode: vacation.holidayCountryCode ?? fallbackHolidayContext.countryCode ?? null,
|
||||
countryName: vacation.holidayCountryName ?? fallbackHolidayContext.countryName ?? null,
|
||||
federalState: vacation.holidayFederalState ?? fallbackHolidayContext.federalState ?? null,
|
||||
metroCityName: vacation.holidayMetroCityName ?? fallbackHolidayContext.metroCityName ?? null,
|
||||
calendarHolidayStrings: filterIsoDatesToRange(
|
||||
deps.parseVacationSnapshotDateList(vacation.holidayCalendarDates),
|
||||
period.startDate,
|
||||
period.endDate,
|
||||
),
|
||||
publicHolidayStrings: filterIsoDatesToRange(
|
||||
deps.parseVacationSnapshotDateList(vacation.holidayLegacyPublicHolidayDates),
|
||||
period.startDate,
|
||||
period.endDate,
|
||||
),
|
||||
}
|
||||
: fallbackHolidayContext,
|
||||
});
|
||||
const persistedDeductedDays = deps.countVacationChargeableDaysFromSnapshot(vacation, yearStart, yearEnd);
|
||||
return {
|
||||
type: vacation.type,
|
||||
status: mapEntitlementVacationStatus(vacation.status),
|
||||
startDate: toIsoDate(vacation.startDate),
|
||||
endDate: toIsoDate(vacation.endDate),
|
||||
isHalfDay: vacation.isHalfDay,
|
||||
requestedDays: preview.requestedDays,
|
||||
deductedDays: persistedDeductedDays ?? preview.deductedDays,
|
||||
holidayCountryCode: preview.holidayContext.countryCode,
|
||||
holidayCountryName: preview.holidayContext.countryName,
|
||||
holidayFederalState: preview.holidayContext.federalState,
|
||||
holidayMetroCityName: preview.holidayContext.metroCityName,
|
||||
holidayCalendarDates: preview.holidayDetails
|
||||
.filter((detail) => detail.source === "CALENDAR" || detail.source === "CALENDAR_AND_LEGACY")
|
||||
.map((detail) => detail.date),
|
||||
holidayLegacyPublicHolidayDates: preview.holidayDetails
|
||||
.filter((detail) => detail.source === "LEGACY_PUBLIC_HOLIDAY" || detail.source === "CALENDAR_AND_LEGACY")
|
||||
.map((detail) => detail.date),
|
||||
holidayDetails: preview.holidayDetails,
|
||||
holidayContext: preview.holidayContext,
|
||||
};
|
||||
}));
|
||||
return Promise.all(
|
||||
vacations.map(async (vacation) => {
|
||||
const period = clampVacationPeriodToYear(vacation, yearStart, yearEnd);
|
||||
let vacationHolidayContextPromise: Promise<ResourceHolidayContext> | null = null;
|
||||
const getVacationHolidayContext = async () => {
|
||||
if (!vacationHolidayContextPromise) {
|
||||
vacationHolidayContextPromise = deps.loadResourceHolidayContext(
|
||||
db,
|
||||
input.resourceId,
|
||||
period.startDate,
|
||||
period.endDate,
|
||||
);
|
||||
}
|
||||
return vacationHolidayContextPromise;
|
||||
};
|
||||
const fallbackHolidayContext = await getVacationHolidayContext();
|
||||
const preview = deps.buildVacationPreview({
|
||||
type: vacation.type,
|
||||
startDate: period.startDate,
|
||||
endDate: period.endDate,
|
||||
isHalfDay: vacation.isHalfDay,
|
||||
holidayContext: hasPersistedHolidaySnapshot(vacation)
|
||||
? {
|
||||
countryCode:
|
||||
vacation.holidayCountryCode ?? fallbackHolidayContext.countryCode ?? null,
|
||||
countryName:
|
||||
vacation.holidayCountryName ?? fallbackHolidayContext.countryName ?? null,
|
||||
federalState:
|
||||
vacation.holidayFederalState ?? fallbackHolidayContext.federalState ?? null,
|
||||
metroCityName:
|
||||
vacation.holidayMetroCityName ?? fallbackHolidayContext.metroCityName ?? null,
|
||||
calendarHolidayStrings: filterIsoDatesToRange(
|
||||
deps.parseVacationSnapshotDateList(vacation.holidayCalendarDates),
|
||||
period.startDate,
|
||||
period.endDate,
|
||||
),
|
||||
publicHolidayStrings: filterIsoDatesToRange(
|
||||
deps.parseVacationSnapshotDateList(vacation.holidayLegacyPublicHolidayDates),
|
||||
period.startDate,
|
||||
period.endDate,
|
||||
),
|
||||
}
|
||||
: fallbackHolidayContext,
|
||||
});
|
||||
const persistedDeductedDays = deps.countVacationChargeableDaysFromSnapshot(
|
||||
vacation,
|
||||
yearStart,
|
||||
yearEnd,
|
||||
);
|
||||
return {
|
||||
type: vacation.type,
|
||||
status: mapEntitlementVacationStatus(vacation.status),
|
||||
startDate: toIsoDate(vacation.startDate),
|
||||
endDate: toIsoDate(vacation.endDate),
|
||||
isHalfDay: vacation.isHalfDay,
|
||||
requestedDays: preview.requestedDays,
|
||||
deductedDays: persistedDeductedDays ?? preview.deductedDays,
|
||||
holidayCountryCode: preview.holidayContext.countryCode,
|
||||
holidayCountryName: preview.holidayContext.countryName,
|
||||
holidayFederalState: preview.holidayContext.federalState,
|
||||
holidayMetroCityName: preview.holidayContext.metroCityName,
|
||||
holidayCalendarDates: preview.holidayDetails
|
||||
.filter(
|
||||
(detail) => detail.source === "CALENDAR" || detail.source === "CALENDAR_AND_LEGACY",
|
||||
)
|
||||
.map((detail) => detail.date),
|
||||
holidayLegacyPublicHolidayDates: preview.holidayDetails
|
||||
.filter(
|
||||
(detail) =>
|
||||
detail.source === "LEGACY_PUBLIC_HOLIDAY" || detail.source === "CALENDAR_AND_LEGACY",
|
||||
)
|
||||
.map((detail) => detail.date),
|
||||
holidayDetails: preview.holidayDetails,
|
||||
holidayContext: preview.holidayContext,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function readYearSummarySnapshot(
|
||||
@@ -415,8 +455,12 @@ export async function getEntitlementBalanceDetail(
|
||||
});
|
||||
}
|
||||
|
||||
const approvedVacations = vacations.filter((vacation) => vacation.status === VacationStatus.APPROVED);
|
||||
const pendingVacations = vacations.filter((vacation) => vacation.status === VacationStatus.PENDING);
|
||||
const approvedVacations = vacations.filter(
|
||||
(vacation) => vacation.status === VacationStatus.APPROVED,
|
||||
);
|
||||
const pendingVacations = vacations.filter(
|
||||
(vacation) => vacation.status === VacationStatus.PENDING,
|
||||
);
|
||||
|
||||
return mapBalanceDetail(resource, {
|
||||
...balance,
|
||||
@@ -424,15 +468,35 @@ export async function getEntitlementBalanceDetail(
|
||||
formula: "remaining = entitlement - taken - pending",
|
||||
approvedVacationCount: approvedVacations.length,
|
||||
pendingVacationCount: pendingVacations.length,
|
||||
approvedRequestedDays: approvedVacations.reduce((sum, vacation) => sum + vacation.requestedDays, 0),
|
||||
pendingRequestedDays: pendingVacations.reduce((sum, vacation) => sum + vacation.requestedDays, 0),
|
||||
approvedDeductedDays: approvedVacations.reduce((sum, vacation) => sum + vacation.deductedDays, 0),
|
||||
pendingDeductedDays: pendingVacations.reduce((sum, vacation) => sum + vacation.deductedDays, 0),
|
||||
approvedRequestedDays: approvedVacations.reduce(
|
||||
(sum, vacation) => sum + vacation.requestedDays,
|
||||
0,
|
||||
),
|
||||
pendingRequestedDays: pendingVacations.reduce(
|
||||
(sum, vacation) => sum + vacation.requestedDays,
|
||||
0,
|
||||
),
|
||||
approvedDeductedDays: approvedVacations.reduce(
|
||||
(sum, vacation) => sum + vacation.deductedDays,
|
||||
0,
|
||||
),
|
||||
pendingDeductedDays: pendingVacations.reduce(
|
||||
(sum, vacation) => sum + vacation.deductedDays,
|
||||
0,
|
||||
),
|
||||
excludedHolidayDates: buildEntitlementHolidayDateUnion(vacations),
|
||||
holidayBasisVariants: [...new Set(vacations.map(formatEntitlementHolidayBasis).filter((value) => value.length > 0))],
|
||||
holidayBasisVariants: [
|
||||
...new Set(
|
||||
vacations.map(formatEntitlementHolidayBasis).filter((value) => value.length > 0),
|
||||
),
|
||||
],
|
||||
sources: {
|
||||
hasCalendarHolidays: vacations.some((vacation) => vacation.holidayContext.sources.hasCalendarHolidays),
|
||||
hasLegacyPublicHolidayEntries: vacations.some((vacation) => vacation.holidayContext.sources.hasLegacyPublicHolidayEntries),
|
||||
hasCalendarHolidays: vacations.some(
|
||||
(vacation) => vacation.holidayContext.sources.hasCalendarHolidays,
|
||||
),
|
||||
hasLegacyPublicHolidayEntries: vacations.some(
|
||||
(vacation) => vacation.holidayContext.sources.hasLegacyPublicHolidayEntries,
|
||||
),
|
||||
},
|
||||
},
|
||||
vacations,
|
||||
@@ -462,10 +526,14 @@ export async function getEntitlementYearSummaryDetail(
|
||||
input: EntitlementYearSummaryDetailInput,
|
||||
deps: ReadEntitlementBalanceDeps,
|
||||
) {
|
||||
const summaries = await readYearSummarySnapshot(db, {
|
||||
year: input.year,
|
||||
...(input.chapter ? { chapter: input.chapter } : {}),
|
||||
}, deps);
|
||||
const summaries = await readYearSummarySnapshot(
|
||||
db,
|
||||
{
|
||||
year: input.year,
|
||||
...(input.chapter ? { chapter: input.chapter } : {}),
|
||||
},
|
||||
deps,
|
||||
);
|
||||
|
||||
const needle = input.resourceName?.toLowerCase();
|
||||
|
||||
@@ -474,8 +542,10 @@ export async function getEntitlementYearSummaryDetail(
|
||||
if (!needle) {
|
||||
return true;
|
||||
}
|
||||
return summary.displayName.toLowerCase().includes(needle)
|
||||
|| summary.eid.toLowerCase().includes(needle);
|
||||
return (
|
||||
summary.displayName.toLowerCase().includes(needle) ||
|
||||
summary.eid.toLowerCase().includes(needle)
|
||||
);
|
||||
})
|
||||
.slice(0, 50)
|
||||
.map((summary) => ({
|
||||
|
||||
Reference in New Issue
Block a user