@kernhq/module-hr 0.8.0 → 0.9.0

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.
Files changed (31) hide show
  1. package/package.json +22 -2
  2. package/src/client/api-instance.ts +29 -0
  3. package/src/client/components/ClockControls.svelte +127 -0
  4. package/src/client/components/HrSidebar.svelte +73 -0
  5. package/src/client/components/LeaveRequestDialog.svelte +169 -0
  6. package/src/client/components/PersonFormDialog.svelte +184 -0
  7. package/src/client/components/PersonInline.svelte +49 -0
  8. package/src/client/components/PersonPanel.svelte +328 -0
  9. package/src/client/core-api.ts +34 -0
  10. package/src/client/i18n.ts +640 -0
  11. package/src/client/index.ts +3 -0
  12. package/src/client/mock.ts +447 -0
  13. package/src/client/module.ts +295 -0
  14. package/src/client/pages/ApprovalsPage.svelte +129 -0
  15. package/src/client/pages/AttendancePage.svelte +147 -0
  16. package/src/client/pages/DirectoryPage.svelte +428 -0
  17. package/src/client/pages/LeavePage.svelte +180 -0
  18. package/src/client/pages/OfficesPage.svelte +119 -0
  19. package/src/client/permissions.ts +31 -0
  20. package/src/client/query.test.ts +70 -0
  21. package/src/client/query.ts +69 -0
  22. package/src/client/settings/CalendarsSettings.svelte +21 -0
  23. package/src/client/settings/CapabilitiesSettings.svelte +122 -0
  24. package/src/client/settings/LeaveSettings.svelte +21 -0
  25. package/src/client/settings/OfficesSettings.svelte +21 -0
  26. package/src/client/settings/SchedulesSettings.svelte +21 -0
  27. package/src/client/widgets/ApprovalsWidget.svelte +86 -0
  28. package/src/client/widgets/ClockWidget.svelte +9 -0
  29. package/src/client/widgets/HeadcountWidget.svelte +29 -0
  30. package/src/client/widgets/LeaveBalanceWidget.svelte +67 -0
  31. package/src/client/widgets/WhosOutWidget.svelte +75 -0
@@ -0,0 +1,67 @@
1
+ <script lang="ts">
2
+ import { EmptyState, Skeleton, type WidgetProps } from '@kernhq/ui'
3
+ import { createQuery } from '@tanstack/svelte-query'
4
+ import { getHrApi } from '../api-instance.js'
5
+ import { t } from '../i18n.js'
6
+ import { formatDays, hrKeys } from '../query.js'
7
+
8
+ /**
9
+ * What is left of each kind of leave.
10
+ *
11
+ * Shows `available`, not `balance`: pending requests are already spoken for, and a card promising
12
+ * twenty days when five are awaiting approval is how somebody books a week they do not have.
13
+ */
14
+ const { workspaceId }: WidgetProps = $props()
15
+ const api = getHrApi()
16
+
17
+ const balanceQuery = createQuery(() => ({
18
+ queryKey: hrKeys.leaveBalance(workspaceId, undefined),
19
+ enabled: Boolean(workspaceId),
20
+ queryFn: () => api.leave.balance.get({ workspaceId }),
21
+ }))
22
+ const balances = $derived(balanceQuery.data ?? [])
23
+ </script>
24
+
25
+ {#if balanceQuery.isLoading}
26
+ <Skeleton height="72px" />
27
+ {:else if balances.length === 0}
28
+ <EmptyState bare compact icon="tree-palm" title={t('leave_none')} />
29
+ {:else}
30
+ <ul>
31
+ {#each balances as b (b.leaveTypeId)}
32
+ <li>
33
+ <span class="name">{b.leaveTypeName}</span>
34
+ <span class="value">{formatDays(b.available)} <span class="unit">{t('days')}</span></span>
35
+ </li>
36
+ {/each}
37
+ </ul>
38
+ {/if}
39
+
40
+ <style>
41
+ ul {
42
+ display: grid;
43
+ gap: 8px;
44
+ list-style: none;
45
+ margin: 0;
46
+ padding: 0;
47
+ }
48
+ li {
49
+ display: flex;
50
+ align-items: baseline;
51
+ justify-content: space-between;
52
+ gap: 8px;
53
+ }
54
+ .name {
55
+ color: var(--kern-ink-500);
56
+ font-size: 12px;
57
+ min-width: 0;
58
+ }
59
+ .value {
60
+ font-size: 15px;
61
+ font-variant-numeric: tabular-nums;
62
+ }
63
+ .unit {
64
+ font-size: 12px;
65
+ color: var(--kern-ink-500);
66
+ }
67
+ </style>
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ import { Avatar, EmptyState, Skeleton, type WidgetProps } from '@kernhq/ui'
3
+ import { createQuery } from '@tanstack/svelte-query'
4
+ import { getHrApi } from '../api-instance.js'
5
+ import { t } from '../i18n.js'
6
+ import { hrKeys, isoDate } from '../query.js'
7
+
8
+ /**
9
+ * Who is away over the next fortnight.
10
+ *
11
+ * The leave *type* is shown only when the server sends it — most companies want the team to know
12
+ * somebody is out without knowing it is sick leave, so the decision is made server-side and this
13
+ * renders whatever it was given.
14
+ */
15
+ const { workspaceId }: WidgetProps = $props()
16
+ const api = getHrApi()
17
+
18
+ const from = isoDate()
19
+ const to = isoDate(new Date(Date.now() + 14 * 86_400_000))
20
+
21
+ const outQuery = createQuery(() => ({
22
+ queryKey: hrKeys.leaveCalendar(workspaceId, from, to),
23
+ enabled: Boolean(workspaceId),
24
+ queryFn: () => api.leave.team.calendar({ workspaceId, from, to }),
25
+ }))
26
+ const away = $derived(outQuery.data ?? [])
27
+
28
+ const range = (a: string, b: string) =>
29
+ new Intl.DateTimeFormat(undefined, { day: 'numeric', month: 'short' }).formatRange(
30
+ new Date(`${a}T00:00:00`),
31
+ new Date(`${b}T00:00:00`),
32
+ )
33
+ </script>
34
+
35
+ {#if outQuery.isLoading}
36
+ <Skeleton height="96px" />
37
+ {:else if away.length === 0}
38
+ <EmptyState bare compact icon="calendar-days" title={t('leave_none')} />
39
+ {:else}
40
+ <ul>
41
+ {#each away as person (person.requestId)}
42
+ <li>
43
+ <Avatar name={person.displayName} id={person.personId} size={24} />
44
+ <span class="name">{person.displayName}</span>
45
+ <span class="meta">{range(person.startsOn, person.endsOn)}</span>
46
+ </li>
47
+ {/each}
48
+ </ul>
49
+ {/if}
50
+
51
+ <style>
52
+ ul {
53
+ display: grid;
54
+ gap: 8px;
55
+ list-style: none;
56
+ margin: 0;
57
+ padding: 0;
58
+ }
59
+ li {
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 8px;
63
+ }
64
+ .name {
65
+ flex: 1;
66
+ min-width: 0;
67
+ overflow: hidden;
68
+ text-overflow: ellipsis;
69
+ white-space: nowrap;
70
+ }
71
+ .meta {
72
+ color: var(--kern-ink-500);
73
+ font-size: 12px;
74
+ }
75
+ </style>