1df208dbcc
Allocation bars that have active optimistic overrides (post-drag, awaiting server confirmation) now pulse subtly via animate-pulse. The pending set is derived from the existing optimisticAllocations map keys, requiring no additional state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
114 lines
3.9 KiB
TypeScript
114 lines
3.9 KiB
TypeScript
import { fmtEur } from "../lib/format-utils.js";
|
|
export {
|
|
RESOURCE_DIRECTORY_SELECT,
|
|
RESOURCE_IDENTIFIER_DETAIL_SELECT,
|
|
RESOURCE_IDENTIFIER_SELECT,
|
|
RESOURCE_SUMMARY_DETAIL_SELECT,
|
|
RESOURCE_SUMMARY_SELECT,
|
|
} from "../db/selects.js";
|
|
|
|
export function mapResourceSummary(resource: {
|
|
id: string;
|
|
eid: string;
|
|
displayName: string;
|
|
chapter: string | null;
|
|
isActive: boolean;
|
|
areaRole: { name: string } | null;
|
|
country: { code: string; name: string } | null;
|
|
metroCity: { name: string } | null;
|
|
orgUnit: { name: string } | null;
|
|
}) {
|
|
return {
|
|
id: resource.id,
|
|
eid: resource.eid,
|
|
name: resource.displayName,
|
|
chapter: resource.chapter,
|
|
role: resource.areaRole?.name ?? null,
|
|
country: resource.country?.name ?? resource.country?.code ?? null,
|
|
countryCode: resource.country?.code ?? null,
|
|
metroCity: resource.metroCity?.name ?? null,
|
|
orgUnit: resource.orgUnit?.name ?? null,
|
|
active: resource.isActive,
|
|
};
|
|
}
|
|
|
|
export function mapResourceSummaryDetail(resource: {
|
|
id: string;
|
|
eid: string;
|
|
displayName: string;
|
|
chapter: string | null;
|
|
fte: number | null;
|
|
lcrCents: number | null;
|
|
chargeabilityTarget: number | null;
|
|
isActive: boolean;
|
|
areaRole: { name: string } | null;
|
|
country: { code: string; name: string } | null;
|
|
metroCity: { name: string } | null;
|
|
orgUnit: { name: string } | null;
|
|
}) {
|
|
return {
|
|
id: resource.id,
|
|
eid: resource.eid,
|
|
name: resource.displayName,
|
|
chapter: resource.chapter,
|
|
role: resource.areaRole?.name ?? null,
|
|
country: resource.country?.name ?? resource.country?.code ?? null,
|
|
countryCode: resource.country?.code ?? null,
|
|
metroCity: resource.metroCity?.name ?? null,
|
|
orgUnit: resource.orgUnit?.name ?? null,
|
|
fte: resource.fte,
|
|
lcr: resource.lcrCents != null ? fmtEur(resource.lcrCents) : null,
|
|
chargeabilityTarget: resource.chargeabilityTarget != null ? `${resource.chargeabilityTarget}%` : null,
|
|
active: resource.isActive,
|
|
};
|
|
}
|
|
|
|
export function mapResourceDetail(resource: {
|
|
id: string;
|
|
eid: string;
|
|
displayName: string;
|
|
email: string | null;
|
|
chapter: string | null;
|
|
fte: number | null;
|
|
lcrCents: number | null;
|
|
ucrCents: number | null;
|
|
chargeabilityTarget: number | null;
|
|
isActive: boolean;
|
|
skills: unknown;
|
|
postalCode: string | null;
|
|
federalState: string | null;
|
|
areaRole: { name: string; color: string | null } | null;
|
|
country: { code: string; name: string; dailyWorkingHours: number | null } | null;
|
|
metroCity: { name: string } | null;
|
|
managementLevelGroup: { name: string; targetPercentage: number | null } | null;
|
|
orgUnit: { name: string; level: number } | null;
|
|
_count: { assignments: number; vacations: number };
|
|
}) {
|
|
const skills = Array.isArray(resource.skills) ? resource.skills as { name?: string; level?: number }[] : [];
|
|
return {
|
|
id: resource.id,
|
|
eid: resource.eid,
|
|
name: resource.displayName,
|
|
email: resource.email,
|
|
chapter: resource.chapter,
|
|
role: resource.areaRole?.name ?? null,
|
|
country: resource.country?.name ?? resource.country?.code ?? null,
|
|
countryCode: resource.country?.code ?? null,
|
|
countryHours: resource.country?.dailyWorkingHours ?? 8,
|
|
metroCity: resource.metroCity?.name ?? null,
|
|
fte: resource.fte,
|
|
lcr: resource.lcrCents != null ? fmtEur(resource.lcrCents) : null,
|
|
ucr: resource.ucrCents != null ? fmtEur(resource.ucrCents) : null,
|
|
chargeabilityTarget: resource.chargeabilityTarget != null ? `${resource.chargeabilityTarget}%` : null,
|
|
managementLevel: resource.managementLevelGroup?.name ?? null,
|
|
orgUnit: resource.orgUnit?.name ?? null,
|
|
postalCode: resource.postalCode,
|
|
federalState: resource.federalState,
|
|
active: resource.isActive,
|
|
totalAssignments: resource._count.assignments,
|
|
totalVacations: resource._count.vacations,
|
|
skillCount: skills.length,
|
|
topSkills: skills.slice(0, 10).map((skill) => `${skill.name ?? "?"} (${skill.level ?? "?"})`),
|
|
};
|
|
}
|