@hed-hog/operations 0.0.325 → 0.0.326

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 (32) hide show
  1. package/dist/controllers/operations-collaborators.controller.d.ts +5 -0
  2. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  3. package/dist/operations.service.d.ts +9 -1
  4. package/dist/operations.service.d.ts.map +1 -1
  5. package/dist/operations.service.js +140 -26
  6. package/dist/operations.service.js.map +1 -1
  7. package/hedhog/data/integration_event_catalog.yaml +313 -0
  8. package/hedhog/data/setting_group.yaml +21 -0
  9. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +410 -23
  10. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +504 -375
  11. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +258 -230
  12. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +225 -162
  13. package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +484 -230
  14. package/hedhog/frontend/app/_lib/api.ts.ejs +13 -4
  15. package/hedhog/frontend/app/_lib/hooks/use-mention-items.ts.ejs +28 -0
  16. package/hedhog/frontend/app/_lib/types.ts.ejs +30 -29
  17. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +347 -236
  18. package/hedhog/frontend/app/reports/projects/page.tsx.ejs +31 -7
  19. package/hedhog/frontend/messages/en.json +38 -55
  20. package/hedhog/frontend/messages/en.json.ejs +21 -4
  21. package/hedhog/frontend/messages/pt.json +36 -55
  22. package/hedhog/frontend/messages/pt.json.ejs +14 -3
  23. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.d.ts +1 -0
  24. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.d.ts.map +1 -1
  25. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.ts +1 -0
  26. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.d.ts +1 -0
  27. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.d.ts.map +1 -1
  28. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.ts +1 -0
  29. package/hedhog/table/operations_collaborator.yaml +5 -0
  30. package/hedhog/table/operations_collaborator_compensation_history.yaml +4 -0
  31. package/package.json +5 -5
  32. package/src/operations.service.ts +202 -26
@@ -41,6 +41,14 @@ import {
41
41
  SelectValue,
42
42
  } from '@/components/ui/select';
43
43
  import { Switch } from '@/components/ui/switch';
44
+ import {
45
+ Table,
46
+ TableBody,
47
+ TableCell,
48
+ TableHead,
49
+ TableHeader,
50
+ TableRow,
51
+ } from '@/components/ui/table';
44
52
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
45
53
  import { Textarea } from '@/components/ui/textarea';
46
54
  import {
@@ -62,6 +70,16 @@ import { useTranslations } from 'next-intl';
62
70
  import Link from 'next/link';
63
71
  import { useRouter } from 'next/navigation';
64
72
  import { useEffect, useMemo, useState } from 'react';
73
+ import {
74
+ CartesianGrid,
75
+ Legend,
76
+ Line,
77
+ LineChart,
78
+ ResponsiveContainer,
79
+ Tooltip as RechartsTooltip,
80
+ XAxis,
81
+ YAxis,
82
+ } from 'recharts';
65
83
  import { PersonPicker } from '../../contact/_components/person-picker';
66
84
  import {
67
85
  fetchOperations,
@@ -80,6 +98,7 @@ import type {
80
98
  PaginatedResponse,
81
99
  } from '../_lib/types';
82
100
  import {
101
+ formatCurrency,
83
102
  formatDate,
84
103
  formatEnumLabel,
85
104
  formatHours,
@@ -229,6 +248,8 @@ const EQUITY_ENABLED_TYPE_SLUGS = new Set([
229
248
  'parceiro',
230
249
  ]);
231
250
 
251
+ const HOURLY_RATE_SLUGS = new Set(['pj', 'freelancer']);
252
+
232
253
  type CollaboratorFormState = {
233
254
  userId: string;
234
255
  personId: string;
@@ -244,6 +265,9 @@ type CollaboratorFormState = {
244
265
  leftAt: string;
245
266
  supervisorCollaboratorId: string;
246
267
  compensationAmount: string;
268
+ hourlyRate: string;
269
+ compensationEffectiveDate: string;
270
+ compensationNotes: string;
247
271
  contractId: string;
248
272
  notes: string;
249
273
  equityParticipation: {
@@ -289,6 +313,9 @@ function buildEmptyForm(): CollaboratorFormState {
289
313
  leftAt: '',
290
314
  supervisorCollaboratorId: 'none',
291
315
  compensationAmount: '',
316
+ hourlyRate: '',
317
+ compensationEffectiveDate: '',
318
+ compensationNotes: '',
292
319
  contractId: '',
293
320
  notes: '',
294
321
  equityParticipation: {
@@ -362,6 +389,12 @@ function toFormState(
362
389
  collaborator.relatedContracts?.[0]?.budgetAmount !== undefined
363
390
  ? String(collaborator.relatedContracts[0].budgetAmount)
364
391
  : '',
392
+ hourlyRate:
393
+ collaborator.hourlyRate !== null && collaborator.hourlyRate !== undefined
394
+ ? String(collaborator.hourlyRate)
395
+ : '',
396
+ compensationEffectiveDate: '',
397
+ compensationNotes: '',
365
398
  contractId: collaborator.relatedContracts?.[0]?.id
366
399
  ? String(collaborator.relatedContracts[0].id)
367
400
  : '',
@@ -561,7 +594,8 @@ export function CollaboratorFormScreen({
561
594
  const t = useTranslations('operations.CollaboratorFormPage');
562
595
  const commonT = useTranslations('operations.Common');
563
596
  const detailsT = useTranslations('operations.CollaboratorDetailsPage');
564
- const { request, showToastHandler, currentLocaleCode } = useApp();
597
+ const { request, showToastHandler, currentLocaleCode, getSettingValue } =
598
+ useApp();
565
599
  const access = useOperationsAccess();
566
600
  const router = useRouter();
567
601
  const [form, setForm] = useState<CollaboratorFormState>(buildEmptyForm());
@@ -706,6 +740,34 @@ export function CollaboratorFormScreen({
706
740
  placeholderData: (previous) => previous ?? [],
707
741
  });
708
742
 
743
+ type CompensationHistoryEntry = {
744
+ id: number;
745
+ collaboratorId: number;
746
+ amount: string;
747
+ effectiveDate: string | null;
748
+ actorUserId: number | null;
749
+ actorName: string | null;
750
+ notes: string | null;
751
+ createdAt: string;
752
+ };
753
+
754
+ const {
755
+ data: compensationHistory = [],
756
+ refetch: refetchCompensationHistory,
757
+ } = useQuery<CompensationHistoryEntry[]>({
758
+ queryKey: [
759
+ 'operations-collaborator-compensation-history',
760
+ currentLocaleCode,
761
+ collaboratorId,
762
+ ],
763
+ enabled: !isCreateMode && access.isDirector,
764
+ queryFn: () =>
765
+ fetchOperations<CompensationHistoryEntry[]>(
766
+ request,
767
+ `/operations/collaborators/${collaboratorId}/compensation-history`
768
+ ),
769
+ });
770
+
709
771
  const { data: documentTypes = [] } = useQuery<DocumentTypeOption[]>({
710
772
  queryKey: ['contact-person-document-types', currentLocaleCode],
711
773
  enabled: personSheetOpen,
@@ -826,6 +888,10 @@ export function CollaboratorFormScreen({
826
888
  EQUITY_ENABLED_TYPE_SLUGS.has(selectedCollaboratorType.slug)
827
889
  );
828
890
 
891
+ const isHourlyType = HOURLY_RATE_SLUGS.has(
892
+ selectedCollaboratorType?.slug ?? ''
893
+ );
894
+
829
895
  const getCollaboratorTypeLabel = (
830
896
  value?: string | null,
831
897
  fallbackName?: string | null
@@ -1017,7 +1083,10 @@ export function CollaboratorFormScreen({
1017
1083
  const onSubmit = async () => {
1018
1084
  const userId = parseNumberInput(form.userId);
1019
1085
  const personId = parseNumberInput(form.personId);
1020
- const compensationAmount = parseNumberInput(form.compensationAmount);
1086
+ const compensationAmount = isHourlyType
1087
+ ? null
1088
+ : parseNumberInput(form.compensationAmount);
1089
+ const hourlyRate = isHourlyType ? parseNumberInput(form.hourlyRate) : null;
1021
1090
 
1022
1091
  if (!personId) {
1023
1092
  showToastHandler?.('error', t('messages.personRequired'));
@@ -1052,6 +1121,9 @@ export function CollaboratorFormScreen({
1052
1121
  : parseNumberInput(form.supervisorCollaboratorId),
1053
1122
  compensationAmount:
1054
1123
  compensationAmount !== null ? compensationAmount : null,
1124
+ hourlyRate: hourlyRate !== null ? hourlyRate : null,
1125
+ compensationEffectiveDate: trimToNull(form.compensationEffectiveDate),
1126
+ compensationNotes: trimToNull(form.compensationNotes),
1055
1127
  contractId: parseNumberInput(form.contractId),
1056
1128
  notes: trimToNull(form.notes),
1057
1129
  equityParticipation: shouldShowEquitySection
@@ -1092,10 +1164,16 @@ export function CollaboratorFormScreen({
1092
1164
  payload
1093
1165
  );
1094
1166
 
1095
- setForm(toFormState(response));
1167
+ setForm((prev) => ({
1168
+ ...toFormState(response),
1169
+ compensationEffectiveDate: '',
1170
+ compensationNotes: '',
1171
+ weeklySchedule: prev.weeklySchedule,
1172
+ }));
1096
1173
 
1097
1174
  await Promise.all([
1098
1175
  collaboratorId ? refetchCollaborator() : Promise.resolve(),
1176
+ collaboratorId ? refetchCompensationHistory() : Promise.resolve(),
1099
1177
  refetchCollaboratorTypes(),
1100
1178
  refetchDepartments(),
1101
1179
  refetchCollaborators(),
@@ -1590,21 +1668,70 @@ export function CollaboratorFormScreen({
1590
1668
  }
1591
1669
  />
1592
1670
  </div>
1671
+ {isHourlyType ? (
1672
+ <div className="space-y-2">
1673
+ <label className="text-sm font-medium">
1674
+ {t('fields.hourlyRate')}
1675
+ </label>
1676
+ <InputMoney
1677
+ step="0.01"
1678
+ value={form.hourlyRate === '' ? '' : Number(form.hourlyRate)}
1679
+ onValueChange={(value) =>
1680
+ setForm((current) => ({
1681
+ ...current,
1682
+ hourlyRate: value !== null ? String(value) : '',
1683
+ }))
1684
+ }
1685
+ />
1686
+ </div>
1687
+ ) : (
1688
+ <div className="space-y-2">
1689
+ <label className="text-sm font-medium">
1690
+ {t('fields.compensationAmount')}
1691
+ </label>
1692
+ <InputMoney
1693
+ step="0.01"
1694
+ value={
1695
+ form.compensationAmount === ''
1696
+ ? ''
1697
+ : Number(form.compensationAmount)
1698
+ }
1699
+ onValueChange={(value) =>
1700
+ setForm((current) => ({
1701
+ ...current,
1702
+ compensationAmount: value !== null ? String(value) : '',
1703
+ }))
1704
+ }
1705
+ />
1706
+ </div>
1707
+ )}
1593
1708
  <div className="space-y-2">
1594
1709
  <label className="text-sm font-medium">
1595
- {t('fields.compensationAmount')}
1710
+ {t('fields.compensationEffectiveDate')}
1596
1711
  </label>
1597
- <InputMoney
1598
- step="0.01"
1599
- value={
1600
- form.compensationAmount === ''
1601
- ? ''
1602
- : Number(form.compensationAmount)
1712
+ <Input
1713
+ type="date"
1714
+ value={form.compensationEffectiveDate}
1715
+ onChange={(event) =>
1716
+ setForm((current) => ({
1717
+ ...current,
1718
+ compensationEffectiveDate: event.target.value,
1719
+ }))
1603
1720
  }
1604
- onValueChange={(value) =>
1721
+ />
1722
+ </div>
1723
+ <div className="space-y-2">
1724
+ <label className="text-sm font-medium">
1725
+ {t('fields.compensationNotes')}
1726
+ </label>
1727
+ <Textarea
1728
+ rows={2}
1729
+ value={form.compensationNotes}
1730
+ placeholder={t('placeholders.compensationNotes')}
1731
+ onChange={(event) =>
1605
1732
  setForm((current) => ({
1606
1733
  ...current,
1607
- compensationAmount: value !== null ? String(value) : '',
1734
+ compensationNotes: event.target.value,
1608
1735
  }))
1609
1736
  }
1610
1737
  />
@@ -1780,21 +1907,70 @@ export function CollaboratorFormScreen({
1780
1907
  }
1781
1908
  />
1782
1909
  </div>
1910
+ {isHourlyType ? (
1911
+ <div className="space-y-2">
1912
+ <label className="text-sm font-medium">
1913
+ {t('fields.hourlyRate')}
1914
+ </label>
1915
+ <InputMoney
1916
+ step="0.01"
1917
+ value={form.hourlyRate === '' ? '' : Number(form.hourlyRate)}
1918
+ onValueChange={(value) =>
1919
+ setForm((current) => ({
1920
+ ...current,
1921
+ hourlyRate: value !== null ? String(value) : '',
1922
+ }))
1923
+ }
1924
+ />
1925
+ </div>
1926
+ ) : (
1927
+ <div className="space-y-2">
1928
+ <label className="text-sm font-medium">
1929
+ {t('fields.compensationAmount')}
1930
+ </label>
1931
+ <InputMoney
1932
+ step="0.01"
1933
+ value={
1934
+ form.compensationAmount === ''
1935
+ ? ''
1936
+ : Number(form.compensationAmount)
1937
+ }
1938
+ onValueChange={(value) =>
1939
+ setForm((current) => ({
1940
+ ...current,
1941
+ compensationAmount: value !== null ? String(value) : '',
1942
+ }))
1943
+ }
1944
+ />
1945
+ </div>
1946
+ )}
1783
1947
  <div className="space-y-2">
1784
1948
  <label className="text-sm font-medium">
1785
- {t('fields.compensationAmount')}
1949
+ {t('fields.compensationEffectiveDate')}
1786
1950
  </label>
1787
- <InputMoney
1788
- step="0.01"
1789
- value={
1790
- form.compensationAmount === ''
1791
- ? ''
1792
- : Number(form.compensationAmount)
1951
+ <Input
1952
+ type="date"
1953
+ value={form.compensationEffectiveDate}
1954
+ onChange={(event) =>
1955
+ setForm((current) => ({
1956
+ ...current,
1957
+ compensationEffectiveDate: event.target.value,
1958
+ }))
1793
1959
  }
1794
- onValueChange={(value) =>
1960
+ />
1961
+ </div>
1962
+ <div className="space-y-2">
1963
+ <label className="text-sm font-medium">
1964
+ {t('fields.compensationNotes')}
1965
+ </label>
1966
+ <Textarea
1967
+ rows={2}
1968
+ value={form.compensationNotes}
1969
+ placeholder={t('placeholders.compensationNotes')}
1970
+ onChange={(event) =>
1795
1971
  setForm((current) => ({
1796
1972
  ...current,
1797
- compensationAmount: value !== null ? String(value) : '',
1973
+ compensationNotes: event.target.value,
1798
1974
  }))
1799
1975
  }
1800
1976
  />
@@ -1952,6 +2128,58 @@ export function CollaboratorFormScreen({
1952
2128
  </div>
1953
2129
  );
1954
2130
 
2131
+ const salaryChartData = useMemo(() => {
2132
+ if (!compensationHistory.length) return [];
2133
+
2134
+ const sorted = [...compensationHistory].sort(
2135
+ (a, b) =>
2136
+ new Date(a.effectiveDate ?? a.createdAt).getTime() -
2137
+ new Date(b.effectiveDate ?? b.createdAt).getTime()
2138
+ );
2139
+
2140
+ const historicalPoints = sorted.map((entry) => ({
2141
+ date: (entry.effectiveDate ?? entry.createdAt).slice(0, 10),
2142
+ valor: Number(entry.amount),
2143
+ projecao: null as number | null,
2144
+ }));
2145
+
2146
+ const lastEntry = sorted[sorted.length - 1];
2147
+ if (!lastEntry) return [];
2148
+ const lastAmount = Number(lastEntry.amount);
2149
+ const prevEntry = sorted.length > 1 ? sorted[sorted.length - 2] : null;
2150
+ const growthRate =
2151
+ prevEntry && Number(prevEntry.amount) > 0
2152
+ ? (lastAmount - Number(prevEntry.amount)) / Number(prevEntry.amount)
2153
+ : 0;
2154
+
2155
+ const projectionPoints: { date: string; valor: null; projecao: number }[] =
2156
+ [];
2157
+ const baseDate = new Date(
2158
+ (lastEntry.effectiveDate ?? lastEntry.createdAt).slice(0, 10) +
2159
+ 'T00:00:00'
2160
+ );
2161
+
2162
+ for (let i = 1; i <= 12; i++) {
2163
+ const d = new Date(baseDate);
2164
+ d.setMonth(d.getMonth() + i);
2165
+ projectionPoints.push({
2166
+ date: d.toISOString().slice(0, 10),
2167
+ valor: null,
2168
+ projecao:
2169
+ Math.round(lastAmount * Math.pow(1 + growthRate, i / 12) * 100) / 100,
2170
+ });
2171
+ }
2172
+
2173
+ return [
2174
+ ...historicalPoints,
2175
+ {
2176
+ ...historicalPoints[historicalPoints.length - 1],
2177
+ projecao: lastAmount,
2178
+ },
2179
+ ...projectionPoints,
2180
+ ];
2181
+ }, [compensationHistory]);
2182
+
1955
2183
  const costsSection = (
1956
2184
  <Accordion
1957
2185
  type="single"
@@ -1986,13 +2214,22 @@ export function CollaboratorFormScreen({
1986
2214
 
1987
2215
  const formContent = isSheetMode ? (
1988
2216
  <Tabs defaultValue="details" className="w-full">
1989
- <TabsList className="mx-4 mb-2 grid w-[calc(100%-2rem)] grid-cols-6">
2217
+ <TabsList
2218
+ className={`mx-4 mb-2 grid w-[calc(100%-2rem)] ${!isCreateMode && !isHourlyType ? 'grid-cols-7' : isCreateMode && isHourlyType ? 'grid-cols-5' : 'grid-cols-6'}`}
2219
+ >
1990
2220
  <TabsTrigger value="details">{t('tabs.details')}</TabsTrigger>
1991
2221
  <TabsTrigger value="costs">{t('tabs.costs')}</TabsTrigger>
1992
- <TabsTrigger value="schedule">{t('tabs.defaultSchedule')}</TabsTrigger>
2222
+ {!isHourlyType ? (
2223
+ <TabsTrigger value="schedule">
2224
+ {t('tabs.defaultSchedule')}
2225
+ </TabsTrigger>
2226
+ ) : null}
1993
2227
  <TabsTrigger value="projects">{t('tabs.projects')}</TabsTrigger>
1994
2228
  <TabsTrigger value="tasks">{t('tabs.tasks')}</TabsTrigger>
1995
2229
  <TabsTrigger value="timesheets">{t('tabs.timesheets')}</TabsTrigger>
2230
+ {!isCreateMode ? (
2231
+ <TabsTrigger value="salary">{t('tabs.salary')}</TabsTrigger>
2232
+ ) : null}
1996
2233
  </TabsList>
1997
2234
  <TabsContent value="details" className="mt-0 space-y-4 px-4 pt-2">
1998
2235
  {profileContent}
@@ -2060,6 +2297,156 @@ export function CollaboratorFormScreen({
2060
2297
  />
2061
2298
  )}
2062
2299
  </TabsContent>
2300
+ {!isCreateMode ? (
2301
+ <TabsContent value="salary" className="mt-0 px-4 pt-2 space-y-6">
2302
+ {access.isDirector ? (
2303
+ <>
2304
+ <div>
2305
+ <h3 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-3">
2306
+ {isHourlyType
2307
+ ? t('chart.hourlyHistory')
2308
+ : t('chart.salaryHistory')}
2309
+ </h3>
2310
+ {salaryChartData.length > 0 ? (
2311
+ <ResponsiveContainer width="100%" height={260}>
2312
+ <LineChart
2313
+ data={salaryChartData}
2314
+ margin={{ top: 8, right: 16, left: 8, bottom: 8 }}
2315
+ >
2316
+ <CartesianGrid
2317
+ strokeDasharray="3 3"
2318
+ className="stroke-muted"
2319
+ />
2320
+ <XAxis
2321
+ dataKey="date"
2322
+ tick={{ fontSize: 11 }}
2323
+ tickFormatter={(v: string) => {
2324
+ const [year, month] = v.split('-');
2325
+ return `${month}/${year?.slice(2)}`;
2326
+ }}
2327
+ />
2328
+ <YAxis
2329
+ tick={{ fontSize: 11 }}
2330
+ tickFormatter={(v: number) =>
2331
+ formatCurrency(v, getSettingValue, currentLocaleCode)
2332
+ }
2333
+ width={90}
2334
+ />
2335
+ <RechartsTooltip
2336
+ formatter={(value: number) => [
2337
+ formatCurrency(
2338
+ value,
2339
+ getSettingValue,
2340
+ currentLocaleCode
2341
+ ),
2342
+ ]}
2343
+ labelFormatter={(label: string) => label}
2344
+ />
2345
+ <Legend />
2346
+ <Line
2347
+ type="monotone"
2348
+ dataKey="valor"
2349
+ name={t('chart.actual')}
2350
+ stroke="hsl(var(--primary))"
2351
+ strokeWidth={2}
2352
+ dot={{ r: 4 }}
2353
+ activeDot={{ r: 6 }}
2354
+ isAnimationActive
2355
+ animationDuration={800}
2356
+ connectNulls={false}
2357
+ />
2358
+ <Line
2359
+ type="monotone"
2360
+ dataKey="projecao"
2361
+ name={t('chart.projection')}
2362
+ stroke="hsl(var(--muted-foreground))"
2363
+ strokeWidth={2}
2364
+ strokeDasharray="5 5"
2365
+ dot={false}
2366
+ isAnimationActive
2367
+ animationDuration={800}
2368
+ connectNulls={false}
2369
+ />
2370
+ </LineChart>
2371
+ </ResponsiveContainer>
2372
+ ) : (
2373
+ <p className="text-sm text-muted-foreground">
2374
+ {detailsT('noCompensationHistory')}
2375
+ </p>
2376
+ )}
2377
+ </div>
2378
+ <div>
2379
+ {compensationHistory.length > 0 ? (
2380
+ <div className="overflow-x-auto rounded-md border">
2381
+ <Table>
2382
+ <TableHeader>
2383
+ <TableRow>
2384
+ <TableHead>
2385
+ {detailsT('compensationHistory.amount')}
2386
+ </TableHead>
2387
+ <TableHead>
2388
+ {detailsT('compensationHistory.effectiveDate')}
2389
+ </TableHead>
2390
+ <TableHead>
2391
+ {detailsT('compensationHistory.recordedAt')}
2392
+ </TableHead>
2393
+ <TableHead>
2394
+ {detailsT('compensationHistory.changedBy')}
2395
+ </TableHead>
2396
+ <TableHead>
2397
+ {detailsT('compensationHistory.notes')}
2398
+ </TableHead>
2399
+ </TableRow>
2400
+ </TableHeader>
2401
+ <TableBody>
2402
+ {compensationHistory.map((entry) => (
2403
+ <TableRow key={entry.id}>
2404
+ <TableCell className="font-medium">
2405
+ {formatCurrency(
2406
+ Number(entry.amount),
2407
+ getSettingValue,
2408
+ currentLocaleCode
2409
+ )}
2410
+ </TableCell>
2411
+ <TableCell>
2412
+ {entry.effectiveDate
2413
+ ? formatDate(
2414
+ entry.effectiveDate,
2415
+ getSettingValue,
2416
+ currentLocaleCode
2417
+ )
2418
+ : '—'}
2419
+ </TableCell>
2420
+ <TableCell>
2421
+ {formatDate(
2422
+ entry.createdAt,
2423
+ getSettingValue,
2424
+ currentLocaleCode
2425
+ )}
2426
+ </TableCell>
2427
+ <TableCell>{entry.actorName ?? '—'}</TableCell>
2428
+ <TableCell className="text-muted-foreground">
2429
+ {entry.notes ?? '—'}
2430
+ </TableCell>
2431
+ </TableRow>
2432
+ ))}
2433
+ </TableBody>
2434
+ </Table>
2435
+ </div>
2436
+ ) : (
2437
+ <p className="text-sm text-muted-foreground">
2438
+ {detailsT('noCompensationHistory')}
2439
+ </p>
2440
+ )}
2441
+ </div>
2442
+ </>
2443
+ ) : (
2444
+ <p className="text-sm text-muted-foreground">
2445
+ {t('noDirectorAccess')}
2446
+ </p>
2447
+ )}
2448
+ </TabsContent>
2449
+ ) : null}
2063
2450
  </Tabs>
2064
2451
  ) : (
2065
2452
  <div className="space-y-4 px-4">