@alexisapp/leave-core 0.0.1-beta.1
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 +260 -0
- package/dist/chunk-P5WZALLT.mjs +1633 -0
- package/dist/chunk-R7NHFDIU.mjs +53 -0
- package/dist/chunk-TUQKZ7GW.mjs +207 -0
- package/dist/chunk-VS74AXZ6.mjs +70 -0
- package/dist/components/index.d.ts +11 -0
- package/dist/components/index.mjs +23 -0
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.mjs +0 -0
- package/dist/en-GB-TSTNTOGN.mjs +81 -0
- package/dist/forms/index.d.ts +2 -0
- package/dist/forms/index.mjs +0 -0
- package/dist/graphql-BI4OTV8N.d.ts +1814 -0
- package/dist/hooks/index.d.ts +50 -0
- package/dist/hooks/index.mjs +106 -0
- package/dist/i18n/index.d.ts +18 -0
- package/dist/i18n/index.mjs +16 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.mjs +109 -0
- package/dist/leaveStatusUtils-C26heVdh.d.ts +11 -0
- package/dist/mutations/index.d.ts +2 -0
- package/dist/mutations/index.mjs +0 -0
- package/dist/queries/index.d.ts +489 -0
- package/dist/queries/index.mjs +15 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/index.mjs +0 -0
- package/dist/utils/index.d.ts +40 -0
- package/dist/utils/index.mjs +53 -0
- package/package.json +94 -0
- package/src/client/createKyInstance.ts +34 -0
- package/src/client/execute.ts +153 -0
- package/src/client/index.ts +4 -0
- package/src/client/initializeClient.ts +48 -0
- package/src/client/resetClient.ts +10 -0
- package/src/client/types.ts +12 -0
- package/src/components/AsyncBoundary.tsx +29 -0
- package/src/components/index.ts +1 -0
- package/src/domain/index.ts +2 -0
- package/src/errors/AuthError.ts +12 -0
- package/src/errors/DomainError.ts +15 -0
- package/src/errors/GraphQLError.ts +16 -0
- package/src/errors/LeaveError.ts +13 -0
- package/src/errors/NetworkError.ts +12 -0
- package/src/errors/classifyError.ts +46 -0
- package/src/errors/errorMessages.ts +69 -0
- package/src/errors/index.ts +13 -0
- package/src/forms/index.ts +2 -0
- package/src/graphql/codegen-gateway.ts +26 -0
- package/src/graphql/codegen-hr-core.ts +31 -0
- package/src/graphql/generated-gateway/fragment-masking.ts +84 -0
- package/src/graphql/generated-gateway/gql.ts +140 -0
- package/src/graphql/generated-gateway/graphql.ts +10828 -0
- package/src/graphql/generated-gateway/index.ts +2 -0
- package/src/graphql/generated-hr-core/fragment-masking.ts +84 -0
- package/src/graphql/generated-hr-core/gql.ts +185 -0
- package/src/graphql/generated-hr-core/graphql.ts +19385 -0
- package/src/graphql/generated-hr-core/index.ts +2 -0
- package/src/graphql/index.ts +278 -0
- package/src/graphql/operations/gateway/leave-change/mutations.graphql +74 -0
- package/src/graphql/operations/gateway/leave-change/queries.graphql +51 -0
- package/src/graphql/operations/gateway/leave-policy-employee-reference/mutations.graphql +26 -0
- package/src/graphql/operations/gateway/leave-self-certified/mutations.graphql +45 -0
- package/src/graphql/operations/gateway/leave-self-certified/queries.graphql +80 -0
- package/src/graphql/operations/gateway/leave-type-code/mutations.graphql +25 -0
- package/src/graphql/operations/gateway/self-certified-policy/mutations.graphql +29 -0
- package/src/graphql/operations/gateway/self-certified-policy/queries.graphql +34 -0
- package/src/graphql/operations/gateway/time-bank/mutations.graphql +23 -0
- package/src/graphql/operations/gateway/time-bank/queries.graphql +5 -0
- package/src/graphql/operations/gateway/time-off-settings/mutations.graphql +19 -0
- package/src/graphql/operations/gateway/time-off-settings/queries.graphql +15 -0
- package/src/graphql/operations/gateway/user/queries.graphql +11 -0
- package/src/graphql/operations/hr-core/balance/mutations.graphql +34 -0
- package/src/graphql/operations/hr-core/balance/queries.graphql +21 -0
- package/src/graphql/operations/hr-core/employee/queries.graphql +27 -0
- package/src/graphql/operations/hr-core/employment/queries.graphql +40 -0
- package/src/graphql/operations/hr-core/file/mutations.graphql +15 -0
- package/src/graphql/operations/hr-core/group/queries.graphql +13 -0
- package/src/graphql/operations/hr-core/leave/mutations.graphql +68 -0
- package/src/graphql/operations/hr-core/leave/queries.graphql +150 -0
- package/src/graphql/operations/hr-core/leave-type/queries.graphql +33 -0
- package/src/graphql/operations/hr-core/member/queries.graphql +58 -0
- package/src/graphql/operations/hr-core/office/queries.graphql +26 -0
- package/src/graphql/operations/hr-core/policy/mutations.graphql +43 -0
- package/src/graphql/operations/hr-core/policy/queries.graphql +46 -0
- package/src/graphql/operations/hr-core/scope/mutations.graphql +19 -0
- package/src/graphql/operations/hr-core/team/queries.graphql +14 -0
- package/src/graphql/operations/hr-core/user/queries.graphql +37 -0
- package/src/graphql/operations/hr-core/work-calendar/queries.graphql +60 -0
- package/src/graphql/operations/hr-core/work-week/queries.graphql +139 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useBalance.ts +58 -0
- package/src/hooks/useCurrentEmployeeId.ts +15 -0
- package/src/hooks/useLeaveList.ts +91 -0
- package/src/i18n/index.ts +2 -0
- package/src/i18n/instance.ts +52 -0
- package/src/i18n/locale.ts +23 -0
- package/src/i18n/translations/en-GB.json +67 -0
- package/src/index.ts +19 -0
- package/src/mutations/index.ts +2 -0
- package/src/queries/employeeQueryFactory.ts +97 -0
- package/src/queries/index.ts +5 -0
- package/src/queries/leaveQueryFactory.ts +171 -0
- package/src/queries/policyQueryFactory.ts +87 -0
- package/src/queries/settingsQueryFactory.ts +31 -0
- package/src/queries/userQueryFactory.ts +13 -0
- package/src/stores/index.ts +2 -0
- package/src/utils/__tests__/formatDateRangeUtils.test.ts +61 -0
- package/src/utils/__tests__/leaveStatusUtils.test.ts +27 -0
- package/src/utils/__tests__/splitLeaveSectionsUtils.test.ts +71 -0
- package/src/utils/formatDateRangeUtils.ts +71 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/leaveStatusUtils.ts +39 -0
- package/src/utils/splitLeaveSectionsUtils.ts +46 -0
- package/src/utils/typeSafeUtils.ts +4 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
query ListCompanyHolidays(
|
|
2
|
+
$filters: CompanyHolidayFilterInput
|
|
3
|
+
$limit: Int
|
|
4
|
+
$skip: Int
|
|
5
|
+
$sort: CompanyHolidaySortInput
|
|
6
|
+
) {
|
|
7
|
+
companyHolidays(filters: $filters, limit: $limit, skip: $skip, sort: $sort) {
|
|
8
|
+
rows {
|
|
9
|
+
id
|
|
10
|
+
name
|
|
11
|
+
startDate
|
|
12
|
+
endDate
|
|
13
|
+
created
|
|
14
|
+
updated
|
|
15
|
+
}
|
|
16
|
+
count
|
|
17
|
+
next
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
query ListPublicHolidays(
|
|
22
|
+
$filters: PublicHolidayFilterInput
|
|
23
|
+
$limit: Int
|
|
24
|
+
$skip: Int
|
|
25
|
+
$sort: PublicHolidaySortInput
|
|
26
|
+
) {
|
|
27
|
+
publicHolidays(filters: $filters, limit: $limit, skip: $skip, sort: $sort) {
|
|
28
|
+
rows {
|
|
29
|
+
id
|
|
30
|
+
name
|
|
31
|
+
label
|
|
32
|
+
date
|
|
33
|
+
country
|
|
34
|
+
public
|
|
35
|
+
calendarId
|
|
36
|
+
}
|
|
37
|
+
count
|
|
38
|
+
next
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
query ListRestrictedDates(
|
|
43
|
+
$filters: RestrictedDateFilterInput
|
|
44
|
+
$limit: Int
|
|
45
|
+
$skip: Int
|
|
46
|
+
$sort: RestrictedDateSortInput
|
|
47
|
+
) {
|
|
48
|
+
restrictedDates(filters: $filters, limit: $limit, skip: $skip, sort: $sort) {
|
|
49
|
+
rows {
|
|
50
|
+
id
|
|
51
|
+
name
|
|
52
|
+
startDate
|
|
53
|
+
endDate
|
|
54
|
+
created
|
|
55
|
+
updated
|
|
56
|
+
}
|
|
57
|
+
count
|
|
58
|
+
next
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
query GetWorkWeek($id: ID!) {
|
|
2
|
+
workWeek(id: $id) {
|
|
3
|
+
id
|
|
4
|
+
companyId
|
|
5
|
+
employeeId
|
|
6
|
+
officeId
|
|
7
|
+
effectiveFrom
|
|
8
|
+
effectiveTo
|
|
9
|
+
duration
|
|
10
|
+
timezone
|
|
11
|
+
monday {
|
|
12
|
+
working
|
|
13
|
+
duration
|
|
14
|
+
shifts {
|
|
15
|
+
startHour
|
|
16
|
+
startMinute
|
|
17
|
+
endHour
|
|
18
|
+
endMinute
|
|
19
|
+
duration
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
tuesday {
|
|
23
|
+
working
|
|
24
|
+
duration
|
|
25
|
+
shifts {
|
|
26
|
+
startHour
|
|
27
|
+
startMinute
|
|
28
|
+
endHour
|
|
29
|
+
endMinute
|
|
30
|
+
duration
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
wednesday {
|
|
34
|
+
working
|
|
35
|
+
duration
|
|
36
|
+
shifts {
|
|
37
|
+
startHour
|
|
38
|
+
startMinute
|
|
39
|
+
endHour
|
|
40
|
+
endMinute
|
|
41
|
+
duration
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
thursday {
|
|
45
|
+
working
|
|
46
|
+
duration
|
|
47
|
+
shifts {
|
|
48
|
+
startHour
|
|
49
|
+
startMinute
|
|
50
|
+
endHour
|
|
51
|
+
endMinute
|
|
52
|
+
duration
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
friday {
|
|
56
|
+
working
|
|
57
|
+
duration
|
|
58
|
+
shifts {
|
|
59
|
+
startHour
|
|
60
|
+
startMinute
|
|
61
|
+
endHour
|
|
62
|
+
endMinute
|
|
63
|
+
duration
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
saturday {
|
|
67
|
+
working
|
|
68
|
+
duration
|
|
69
|
+
shifts {
|
|
70
|
+
startHour
|
|
71
|
+
startMinute
|
|
72
|
+
endHour
|
|
73
|
+
endMinute
|
|
74
|
+
duration
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
sunday {
|
|
78
|
+
working
|
|
79
|
+
duration
|
|
80
|
+
shifts {
|
|
81
|
+
startHour
|
|
82
|
+
startMinute
|
|
83
|
+
endHour
|
|
84
|
+
endMinute
|
|
85
|
+
duration
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
query ListWorkWeeks(
|
|
92
|
+
$filters: WorkWeekFilterInput
|
|
93
|
+
$limit: Int
|
|
94
|
+
$skip: Int
|
|
95
|
+
$sort: WorkWeekSortInput
|
|
96
|
+
) {
|
|
97
|
+
workWeeks(filters: $filters, limit: $limit, skip: $skip, sort: $sort) {
|
|
98
|
+
rows {
|
|
99
|
+
id
|
|
100
|
+
companyId
|
|
101
|
+
employeeId
|
|
102
|
+
officeId
|
|
103
|
+
effectiveFrom
|
|
104
|
+
effectiveTo
|
|
105
|
+
duration
|
|
106
|
+
timezone
|
|
107
|
+
monday {
|
|
108
|
+
working
|
|
109
|
+
duration
|
|
110
|
+
}
|
|
111
|
+
tuesday {
|
|
112
|
+
working
|
|
113
|
+
duration
|
|
114
|
+
}
|
|
115
|
+
wednesday {
|
|
116
|
+
working
|
|
117
|
+
duration
|
|
118
|
+
}
|
|
119
|
+
thursday {
|
|
120
|
+
working
|
|
121
|
+
duration
|
|
122
|
+
}
|
|
123
|
+
friday {
|
|
124
|
+
working
|
|
125
|
+
duration
|
|
126
|
+
}
|
|
127
|
+
saturday {
|
|
128
|
+
working
|
|
129
|
+
duration
|
|
130
|
+
}
|
|
131
|
+
sunday {
|
|
132
|
+
working
|
|
133
|
+
duration
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
count
|
|
137
|
+
next
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useSuspenseQuery } from '@tanstack/react-query';
|
|
2
|
+
import { leaveQueries, policyQueries } from '../queries';
|
|
3
|
+
import { isDefined } from '../utils/typeSafeUtils';
|
|
4
|
+
import { useCurrentEmployeeId } from './useCurrentEmployeeId';
|
|
5
|
+
|
|
6
|
+
export interface BalanceData {
|
|
7
|
+
remaining: number;
|
|
8
|
+
spent: number;
|
|
9
|
+
saved: number | null;
|
|
10
|
+
unpaid: number | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fetches the current employee's vacation balance and policy,
|
|
15
|
+
* returning normalized tile values.
|
|
16
|
+
*
|
|
17
|
+
* Returns `null` when no balance data exists.
|
|
18
|
+
*
|
|
19
|
+
* Norwegian policies (`includeUnusedDaysInNewBalance`) use
|
|
20
|
+
* `balanceAmountPlusUnpaidLeft` for remaining and omit saved/unpaid
|
|
21
|
+
* (returned as `null`).
|
|
22
|
+
*/
|
|
23
|
+
export function useBalance(): BalanceData | null {
|
|
24
|
+
const employeeId = useCurrentEmployeeId();
|
|
25
|
+
|
|
26
|
+
const { data: balanceData } = useSuspenseQuery(
|
|
27
|
+
leaveQueries.balanceList({
|
|
28
|
+
filters: { employeeId: { eq: employeeId } },
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const rows = (balanceData.employeeVacationBalanceList?.rows ?? []).filter(isDefined);
|
|
33
|
+
|
|
34
|
+
const balance = rows[0];
|
|
35
|
+
const leavePolicyId = balance?.leavePolicyId ?? '';
|
|
36
|
+
|
|
37
|
+
const { data: policyData } = useSuspenseQuery(policyQueries.detail(leavePolicyId));
|
|
38
|
+
|
|
39
|
+
if (!balance) return null;
|
|
40
|
+
|
|
41
|
+
const isNorwegian = policyData.leavePolicy?.allowance?.includeUnusedDaysInNewBalance === true;
|
|
42
|
+
|
|
43
|
+
if (isNorwegian) {
|
|
44
|
+
return {
|
|
45
|
+
remaining: balance.balanceAmountPlusUnpaidLeft ?? 0,
|
|
46
|
+
spent: balance.spentAmount ?? 0,
|
|
47
|
+
saved: null,
|
|
48
|
+
unpaid: null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
remaining: balance.balanceAmount ?? 0,
|
|
54
|
+
spent: balance.spentAmount ?? 0,
|
|
55
|
+
saved: balance.savedDaysLeft ?? 0,
|
|
56
|
+
unpaid: balance.balanceAmountUnpaidLeft ?? 0,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useSuspenseQuery } from '@tanstack/react-query';
|
|
2
|
+
import { userQueries } from '../queries';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the current user's employeeId from the Gateway `me` query.
|
|
6
|
+
*/
|
|
7
|
+
export function useCurrentEmployeeId(): string {
|
|
8
|
+
const { data } = useSuspenseQuery(userQueries.me());
|
|
9
|
+
const employeeId = data.me.membership?.employeeId;
|
|
10
|
+
|
|
11
|
+
if (!employeeId) {
|
|
12
|
+
throw new Error('Current user has no employeeId');
|
|
13
|
+
}
|
|
14
|
+
return employeeId;
|
|
15
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { useSuspenseQuery } from '@tanstack/react-query';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { leaveQueries, policyQueries } from '../queries';
|
|
4
|
+
import type { DisplayStatus, LeaveChangeType } from '../utils/leaveStatusUtils';
|
|
5
|
+
import { resolveDisplayStatus } from '../utils/leaveStatusUtils';
|
|
6
|
+
import { getLocalToday, splitLeaveSections } from '../utils/splitLeaveSectionsUtils';
|
|
7
|
+
import { isDefined } from '../utils/typeSafeUtils';
|
|
8
|
+
import { useCurrentEmployeeId } from './useCurrentEmployeeId';
|
|
9
|
+
|
|
10
|
+
export interface LeaveListItem {
|
|
11
|
+
id: string;
|
|
12
|
+
typeName: string;
|
|
13
|
+
displayStatus: DisplayStatus;
|
|
14
|
+
localStartDate: string;
|
|
15
|
+
localEndDate: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fetches and merges all leave data sources into a unified list
|
|
20
|
+
* split into upcoming and history sections.
|
|
21
|
+
*
|
|
22
|
+
* Resolves the current employee internally — no args needed.
|
|
23
|
+
*
|
|
24
|
+
* Data sources:
|
|
25
|
+
* 1. Standard leaves (HR Core) — has type.name directly
|
|
26
|
+
* 2. Self-certified leaves (Gateway) — needs typeId→typeName join
|
|
27
|
+
* 3. Leave changes (Gateway) — for status overrides (pending change/revoke)
|
|
28
|
+
* 4. Employee leave types (HR Core) — provides typeId→typeName mapping
|
|
29
|
+
*/
|
|
30
|
+
export function useLeaveList(): {
|
|
31
|
+
upcoming: LeaveListItem[];
|
|
32
|
+
history: LeaveListItem[];
|
|
33
|
+
} {
|
|
34
|
+
const employeeId = useCurrentEmployeeId();
|
|
35
|
+
|
|
36
|
+
const { data: standardData } = useSuspenseQuery(leaveQueries.listVisible());
|
|
37
|
+
const { data: selfCertifiedData } = useSuspenseQuery(leaveQueries.selfCertifiedListVisible());
|
|
38
|
+
const { data: changesData } = useSuspenseQuery(
|
|
39
|
+
leaveQueries.approvalQueue({ filters: { status: { eq: 'PENDING' } } })
|
|
40
|
+
);
|
|
41
|
+
const { data: leaveTypesData } = useSuspenseQuery(policyQueries.employeeLeaveTypes(employeeId));
|
|
42
|
+
|
|
43
|
+
return useMemo(() => {
|
|
44
|
+
// 1. Build typeId → typeName map from employee leave types
|
|
45
|
+
const typeMap = new Map<string, string>();
|
|
46
|
+
for (const lt of leaveTypesData.employeeLeaveTypeList) {
|
|
47
|
+
if (lt?.id) {
|
|
48
|
+
typeMap.set(lt.id, lt.name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 2. Build leaveId → changeType map from pending leave changes
|
|
53
|
+
const changeMap = new Map<string, LeaveChangeType>();
|
|
54
|
+
for (const change of changesData.leaveChangeList.data) {
|
|
55
|
+
const changeType = change.type as LeaveChangeType;
|
|
56
|
+
if (changeType === 'UPDATE' || changeType === 'REVOKE') {
|
|
57
|
+
for (const leaveId of change.leaveIds) {
|
|
58
|
+
changeMap.set(leaveId, changeType);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 3. Normalize standard leaves
|
|
64
|
+
const standardItems: LeaveListItem[] = standardData.leaveListVisible
|
|
65
|
+
.filter(isDefined)
|
|
66
|
+
.map(item => ({
|
|
67
|
+
id: item.id ?? '',
|
|
68
|
+
typeName: item.type.name,
|
|
69
|
+
displayStatus: resolveDisplayStatus(item.id ?? '', item.status, changeMap),
|
|
70
|
+
localStartDate: item.localStartDate,
|
|
71
|
+
localEndDate: item.localEndDate,
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
// 4. Normalize self-certified leaves (resolve typeName via map)
|
|
75
|
+
const selfCertifiedItems: LeaveListItem[] = selfCertifiedData.leaveSelfCertifiedListVisible.data
|
|
76
|
+
.filter(item => item.status !== 'MIGRATED')
|
|
77
|
+
.map(item => ({
|
|
78
|
+
id: item.id,
|
|
79
|
+
typeName: typeMap.get(item.typeId) ?? item.typeId,
|
|
80
|
+
displayStatus: resolveDisplayStatus(item.id, item.status, changeMap),
|
|
81
|
+
localStartDate: item.localStartDate,
|
|
82
|
+
localEndDate: item.localEndDate ?? null,
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
// 5. Merge and split into sections
|
|
86
|
+
const allItems = [...standardItems, ...selfCertifiedItems];
|
|
87
|
+
const today = getLocalToday();
|
|
88
|
+
|
|
89
|
+
return splitLeaveSections(allItems, today);
|
|
90
|
+
}, [standardData, selfCertifiedData, changesData, leaveTypesData]);
|
|
91
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import i18next, { type i18n } from 'i18next';
|
|
2
|
+
import { Locale, supportedLocales } from './locale';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SINGLETON SCOPE CONSTRAINT (ADR-011):
|
|
6
|
+
* Same single-consumer constraint as kyInstance/clientConfig in execute.ts.
|
|
7
|
+
* Shared across all MF remotes importing @leave/core. Safe today (sole consumer).
|
|
8
|
+
*/
|
|
9
|
+
let instance: i18n | null = null;
|
|
10
|
+
|
|
11
|
+
export async function initLeaveI18n(locale: string = Locale.EN): Promise<i18n> {
|
|
12
|
+
if (instance) {
|
|
13
|
+
// Support locale change via remount (not hot-swap — see §12b Runtime Contracts)
|
|
14
|
+
if (instance.language !== locale) {
|
|
15
|
+
await instance.changeLanguage(locale);
|
|
16
|
+
}
|
|
17
|
+
return instance;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
instance = i18next.createInstance();
|
|
21
|
+
|
|
22
|
+
await instance.init({
|
|
23
|
+
compatibilityJSON: 'v4',
|
|
24
|
+
lng: locale,
|
|
25
|
+
fallbackLng: Locale.EN,
|
|
26
|
+
supportedLngs: supportedLocales as string[],
|
|
27
|
+
load: 'currentOnly',
|
|
28
|
+
interpolation: { escapeValue: false },
|
|
29
|
+
resources: {
|
|
30
|
+
'en-GB': {
|
|
31
|
+
translation: (await import('./translations/en-GB.json')).default,
|
|
32
|
+
},
|
|
33
|
+
// Phase 2: uncomment when sv translations are complete
|
|
34
|
+
// sv: {
|
|
35
|
+
// translation: (await import('./translations/sv.json')).default,
|
|
36
|
+
// },
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return instance;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getLeaveI18n(): i18n {
|
|
44
|
+
if (!instance) {
|
|
45
|
+
throw new Error('[LeavePackage] Leave i18n not initialized. Call initLeaveI18n() first.');
|
|
46
|
+
}
|
|
47
|
+
return instance;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function resetLeaveI18n(): void {
|
|
51
|
+
instance = null;
|
|
52
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const Locale = {
|
|
2
|
+
EN: 'en-GB',
|
|
3
|
+
SV: 'sv-SE',
|
|
4
|
+
NB: 'nb-NO',
|
|
5
|
+
DA: 'da-DK',
|
|
6
|
+
FI: 'fi-FI',
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
export type Locale = (typeof Locale)[keyof typeof Locale];
|
|
10
|
+
|
|
11
|
+
export const supportedLocales: readonly Locale[] = Object.values(Locale);
|
|
12
|
+
|
|
13
|
+
const LanguageCodeToLocale: Readonly<Record<string, Locale>> = {
|
|
14
|
+
en: Locale.EN,
|
|
15
|
+
sv: Locale.SV,
|
|
16
|
+
nb: Locale.NB,
|
|
17
|
+
da: Locale.DA,
|
|
18
|
+
fi: Locale.FI,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function resolveLocale(input: string): Locale {
|
|
22
|
+
return LanguageCodeToLocale[input] ?? Locale.EN;
|
|
23
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"UNEXPECTED_ERROR": {
|
|
4
|
+
"title": "Something went wrong",
|
|
5
|
+
"description": "An unexpected error occurred. Please try again."
|
|
6
|
+
},
|
|
7
|
+
"CONNECTION_FAILURE": {
|
|
8
|
+
"title": "Connection failed",
|
|
9
|
+
"description": "Unable to reach the server. Check your connection and try again."
|
|
10
|
+
},
|
|
11
|
+
"BAD_REQUEST": {
|
|
12
|
+
"title": "Invalid request",
|
|
13
|
+
"description": "The request could not be processed."
|
|
14
|
+
},
|
|
15
|
+
"NOT_FOUND": {
|
|
16
|
+
"title": "Not found",
|
|
17
|
+
"description": "The requested resource was not found."
|
|
18
|
+
},
|
|
19
|
+
"SYSTEM_FAILURE": {
|
|
20
|
+
"title": "Server error",
|
|
21
|
+
"description": "A server error occurred. Please try again later."
|
|
22
|
+
},
|
|
23
|
+
"UNAUTHORIZED": {
|
|
24
|
+
"title": "Session expired",
|
|
25
|
+
"description": "Your session has expired. Please sign in again."
|
|
26
|
+
},
|
|
27
|
+
"FORBIDDEN": {
|
|
28
|
+
"title": "Access denied",
|
|
29
|
+
"description": "You do not have permission to access this resource."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"retry": "Retry",
|
|
33
|
+
"close": "Close",
|
|
34
|
+
"balance": {
|
|
35
|
+
"remaining": {
|
|
36
|
+
"label": "Remaining",
|
|
37
|
+
"subtitle": "vacation days"
|
|
38
|
+
},
|
|
39
|
+
"spent": {
|
|
40
|
+
"label": "Spent",
|
|
41
|
+
"subtitle": "vacation days"
|
|
42
|
+
},
|
|
43
|
+
"saved": {
|
|
44
|
+
"label": "Saved",
|
|
45
|
+
"subtitle": "vacation days"
|
|
46
|
+
},
|
|
47
|
+
"unpaid": {
|
|
48
|
+
"label": "Unpaid",
|
|
49
|
+
"subtitle": "days left"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"leave": {
|
|
53
|
+
"upcoming": "Upcoming",
|
|
54
|
+
"history": "History",
|
|
55
|
+
"emptyTitle": "No leave requests",
|
|
56
|
+
"emptyDescription": "Your leave requests will appear here",
|
|
57
|
+
"ongoing": "ongoing",
|
|
58
|
+
"status": {
|
|
59
|
+
"APPROVED": "Approved",
|
|
60
|
+
"PENDING": "Pending",
|
|
61
|
+
"DENIED": "Denied",
|
|
62
|
+
"CANCELLED": "Cancelled",
|
|
63
|
+
"PENDING_CHANGES": "Pending changes",
|
|
64
|
+
"PENDING_REVOKE": "Pending revoke"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { initializeClient, resetClient, execute } from './client';
|
|
2
|
+
export type { ClientConfig } from './client';
|
|
3
|
+
|
|
4
|
+
export { initLeaveI18n, getLeaveI18n, resetLeaveI18n } from './i18n';
|
|
5
|
+
export { Locale, supportedLocales, resolveLocale } from './i18n';
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
LeaveError,
|
|
9
|
+
AuthError,
|
|
10
|
+
GraphQLError,
|
|
11
|
+
NetworkError,
|
|
12
|
+
DomainError,
|
|
13
|
+
classifyError,
|
|
14
|
+
ErrorCode,
|
|
15
|
+
getErrorCode,
|
|
16
|
+
isHttpError,
|
|
17
|
+
isRetryable,
|
|
18
|
+
getErrorMessage,
|
|
19
|
+
} from './errors';
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { queryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { execute } from '../client';
|
|
3
|
+
import type {
|
|
4
|
+
ListEmploymentsQueryVariables,
|
|
5
|
+
GetWorkWeekQueryVariables,
|
|
6
|
+
ListCompanyHolidaysQueryVariables,
|
|
7
|
+
ListPublicHolidaysQueryVariables,
|
|
8
|
+
ListRestrictedDatesQueryVariables,
|
|
9
|
+
} from '../graphql';
|
|
10
|
+
import {
|
|
11
|
+
GetEmployeeDocument,
|
|
12
|
+
GetEmploymentDocument,
|
|
13
|
+
ListEmploymentsDocument,
|
|
14
|
+
GetWorkWeekDocument,
|
|
15
|
+
ListCompanyHolidaysDocument,
|
|
16
|
+
ListPublicHolidaysDocument,
|
|
17
|
+
ListRestrictedDatesDocument,
|
|
18
|
+
} from '../graphql';
|
|
19
|
+
|
|
20
|
+
export const employeeQueries = {
|
|
21
|
+
all: ['employee'] as const,
|
|
22
|
+
|
|
23
|
+
details: () => [...employeeQueries.all, 'detail'] as const,
|
|
24
|
+
detail: (id: string) =>
|
|
25
|
+
queryOptions({
|
|
26
|
+
queryKey: [...employeeQueries.details(), id] as const,
|
|
27
|
+
queryFn: ({ signal }) =>
|
|
28
|
+
execute.hrCore({
|
|
29
|
+
query: GetEmployeeDocument,
|
|
30
|
+
variables: { id },
|
|
31
|
+
signal,
|
|
32
|
+
}),
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
employments: () => [...employeeQueries.all, 'employment'] as const,
|
|
36
|
+
employment: (id: string) =>
|
|
37
|
+
queryOptions({
|
|
38
|
+
queryKey: [...employeeQueries.employments(), id] as const,
|
|
39
|
+
queryFn: ({ signal }) =>
|
|
40
|
+
execute.hrCore({
|
|
41
|
+
query: GetEmploymentDocument,
|
|
42
|
+
variables: { id },
|
|
43
|
+
signal,
|
|
44
|
+
}),
|
|
45
|
+
}),
|
|
46
|
+
employmentList: (variables: ListEmploymentsQueryVariables) =>
|
|
47
|
+
queryOptions({
|
|
48
|
+
queryKey: [...employeeQueries.employments(), 'list', variables] as const,
|
|
49
|
+
queryFn: ({ signal }) =>
|
|
50
|
+
execute.hrCore({
|
|
51
|
+
query: ListEmploymentsDocument,
|
|
52
|
+
variables,
|
|
53
|
+
signal,
|
|
54
|
+
}),
|
|
55
|
+
}),
|
|
56
|
+
|
|
57
|
+
workWeeks: () => [...employeeQueries.all, 'work-week'] as const,
|
|
58
|
+
workWeek: (variables: GetWorkWeekQueryVariables) =>
|
|
59
|
+
queryOptions({
|
|
60
|
+
queryKey: [...employeeQueries.workWeeks(), variables] as const,
|
|
61
|
+
queryFn: ({ signal }) => execute.hrCore({ query: GetWorkWeekDocument, variables, signal }),
|
|
62
|
+
}),
|
|
63
|
+
|
|
64
|
+
holidays: () => [...employeeQueries.all, 'holiday'] as const,
|
|
65
|
+
companyHolidays: (variables: ListCompanyHolidaysQueryVariables) =>
|
|
66
|
+
queryOptions({
|
|
67
|
+
queryKey: [...employeeQueries.holidays(), 'company', variables] as const,
|
|
68
|
+
queryFn: ({ signal }) =>
|
|
69
|
+
execute.hrCore({
|
|
70
|
+
query: ListCompanyHolidaysDocument,
|
|
71
|
+
variables,
|
|
72
|
+
signal,
|
|
73
|
+
}),
|
|
74
|
+
}),
|
|
75
|
+
publicHolidays: (variables: ListPublicHolidaysQueryVariables) =>
|
|
76
|
+
queryOptions({
|
|
77
|
+
queryKey: [...employeeQueries.holidays(), 'public', variables] as const,
|
|
78
|
+
queryFn: ({ signal }) =>
|
|
79
|
+
execute.hrCore({
|
|
80
|
+
query: ListPublicHolidaysDocument,
|
|
81
|
+
variables,
|
|
82
|
+
signal,
|
|
83
|
+
}),
|
|
84
|
+
}),
|
|
85
|
+
|
|
86
|
+
restrictedDates: () => [...employeeQueries.all, 'restricted-date'] as const,
|
|
87
|
+
restrictedDateList: (variables: ListRestrictedDatesQueryVariables) =>
|
|
88
|
+
queryOptions({
|
|
89
|
+
queryKey: [...employeeQueries.restrictedDates(), variables] as const,
|
|
90
|
+
queryFn: ({ signal }) =>
|
|
91
|
+
execute.hrCore({
|
|
92
|
+
query: ListRestrictedDatesDocument,
|
|
93
|
+
variables,
|
|
94
|
+
signal,
|
|
95
|
+
}),
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { leaveQueries } from './leaveQueryFactory';
|
|
2
|
+
export { policyQueries } from './policyQueryFactory';
|
|
3
|
+
export { employeeQueries } from './employeeQueryFactory';
|
|
4
|
+
export { settingsQueries } from './settingsQueryFactory';
|
|
5
|
+
export { userQueries } from './userQueryFactory';
|