@open-mercato/core 0.9.0 → 0.9.1-develop.7459.1.99e3accd30

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 (243) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/customer_dictionary_entry/index.js +2 -0
  3. package/dist/generated/entities/customer_dictionary_entry/index.js.map +2 -2
  4. package/dist/generated/entities/customer_interaction/index.js +2 -0
  5. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +2 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/catalog/commands/categories.js +1 -1
  9. package/dist/modules/catalog/commands/categories.js.map +2 -2
  10. package/dist/modules/catalog/commands/productDeleteChildren.js +389 -0
  11. package/dist/modules/catalog/commands/productDeleteChildren.js.map +7 -0
  12. package/dist/modules/catalog/commands/products.js +52 -17
  13. package/dist/modules/catalog/commands/products.js.map +2 -2
  14. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +4 -6
  15. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
  16. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +4 -6
  17. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +2 -2
  18. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +47 -11
  19. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +3 -3
  20. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +5 -1
  21. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
  22. package/dist/modules/communication_channels/lib/inbound-message-origin.js +1 -1
  23. package/dist/modules/communication_channels/lib/inbound-message-origin.js.map +2 -2
  24. package/dist/modules/communication_channels/lib/oauth-state.js +4 -1
  25. package/dist/modules/communication_channels/lib/oauth-state.js.map +2 -2
  26. package/dist/modules/communication_channels/lib/use-connect-channel.js +10 -0
  27. package/dist/modules/communication_channels/lib/use-connect-channel.js.map +2 -2
  28. package/dist/modules/customers/api/activity-types/route.js +151 -0
  29. package/dist/modules/customers/api/activity-types/route.js.map +7 -0
  30. package/dist/modules/customers/api/deals/[id]/people/route.js +58 -5
  31. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  32. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +13 -5
  33. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  34. package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -5
  35. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  36. package/dist/modules/customers/api/interactions/route.js +3 -0
  37. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  38. package/dist/modules/customers/api/interactions/tasks/route.js +1 -0
  39. package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
  40. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +18 -0
  41. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +2 -2
  42. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +0 -23
  43. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
  44. package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -17
  45. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  46. package/dist/modules/customers/calendar-event-types.js +548 -0
  47. package/dist/modules/customers/calendar-event-types.js.map +7 -0
  48. package/dist/modules/customers/commands/dictionaries.js +70 -10
  49. package/dist/modules/customers/commands/dictionaries.js.map +2 -2
  50. package/dist/modules/customers/commands/interactions.js +187 -22
  51. package/dist/modules/customers/commands/interactions.js.map +3 -3
  52. package/dist/modules/customers/components/ActivityTypeEditor.js +288 -0
  53. package/dist/modules/customers/components/ActivityTypeEditor.js.map +7 -0
  54. package/dist/modules/customers/components/DictionarySettings.js +3 -1
  55. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  56. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +391 -297
  57. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +3 -3
  58. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +9 -74
  59. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
  60. package/dist/modules/customers/components/calendar/TimeGrid.js +20 -16
  61. package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
  62. package/dist/modules/customers/components/calendar/editor/EventTypePanel.js +218 -0
  63. package/dist/modules/customers/components/calendar/editor/EventTypePanel.js.map +7 -0
  64. package/dist/modules/customers/components/calendar/editor/LocationField.js +7 -3
  65. package/dist/modules/customers/components/calendar/editor/LocationField.js.map +2 -2
  66. package/dist/modules/customers/components/calendar/editor/PeopleField.js +5 -2
  67. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  68. package/dist/modules/customers/components/calendar/editor/PriorityField.js +3 -1
  69. package/dist/modules/customers/components/calendar/editor/PriorityField.js.map +2 -2
  70. package/dist/modules/customers/components/calendar/editor/RepeatField.js +9 -1
  71. package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +2 -2
  72. package/dist/modules/customers/components/calendar/editor/ResourcesField.js +4 -1
  73. package/dist/modules/customers/components/calendar/editor/ResourcesField.js.map +2 -2
  74. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +15 -7
  75. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
  76. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +3 -1
  77. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +2 -2
  78. package/dist/modules/customers/components/calendar/editor/hooks.js +28 -13
  79. package/dist/modules/customers/components/calendar/editor/hooks.js.map +3 -3
  80. package/dist/modules/customers/components/calendar/editor/inputs.js +14 -6
  81. package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
  82. package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js +54 -0
  83. package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js.map +7 -0
  84. package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js +82 -0
  85. package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js.map +7 -0
  86. package/dist/modules/customers/components/calendar/types.js +1 -0
  87. package/dist/modules/customers/components/calendar/types.js.map +2 -2
  88. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +78 -343
  89. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  90. package/dist/modules/customers/components/detail/CreatePersonDialog.js +12 -11
  91. package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +2 -2
  92. package/dist/modules/customers/components/detail/DealForm.js +7 -3
  93. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  94. package/dist/modules/customers/components/detail/DealPeopleSection.js +206 -0
  95. package/dist/modules/customers/components/detail/DealPeopleSection.js.map +7 -0
  96. package/dist/modules/customers/components/detail/LinkedPeopleSection.js +342 -0
  97. package/dist/modules/customers/components/detail/LinkedPeopleSection.js.map +7 -0
  98. package/dist/modules/customers/components/detail/PersonCard.js +5 -2
  99. package/dist/modules/customers/components/detail/PersonCard.js.map +2 -2
  100. package/dist/modules/customers/components/linking/adapters/personAdapter.js +3 -0
  101. package/dist/modules/customers/components/linking/adapters/personAdapter.js.map +2 -2
  102. package/dist/modules/customers/data/entities.js +6 -0
  103. package/dist/modules/customers/data/entities.js.map +2 -2
  104. package/dist/modules/customers/data/validators.js +39 -10
  105. package/dist/modules/customers/data/validators.js.map +2 -2
  106. package/dist/modules/customers/di.js +20 -1
  107. package/dist/modules/customers/di.js.map +2 -2
  108. package/dist/modules/customers/extension-points.js +13 -0
  109. package/dist/modules/customers/extension-points.js.map +2 -2
  110. package/dist/modules/customers/lib/calendar/conflicts.js +59 -16
  111. package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
  112. package/dist/modules/customers/lib/calendar/editorPayload.js +34 -16
  113. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  114. package/dist/modules/customers/lib/calendar/eventTypeResolver.js +197 -0
  115. package/dist/modules/customers/lib/calendar/eventTypeResolver.js.map +7 -0
  116. package/dist/modules/customers/lib/calendar/interactionApplicability.js +67 -0
  117. package/dist/modules/customers/lib/calendar/interactionApplicability.js.map +7 -0
  118. package/dist/modules/customers/lib/calendar/mapItem.js +5 -2
  119. package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
  120. package/dist/modules/customers/lib/calendar/recurrence.js +62 -1
  121. package/dist/modules/customers/lib/calendar/recurrence.js.map +2 -2
  122. package/dist/modules/customers/lib/calendar/timezone.js +111 -0
  123. package/dist/modules/customers/lib/calendar/timezone.js.map +7 -0
  124. package/dist/modules/customers/lib/interactionReadModel.js +1 -0
  125. package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
  126. package/dist/modules/customers/lib/todoCompatibility.js +1 -0
  127. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  128. package/dist/modules/customers/migrations/Migration20260928175058_customers.js +17 -0
  129. package/dist/modules/customers/migrations/Migration20260928175058_customers.js.map +7 -0
  130. package/dist/modules/customers/migrations/Migration20260930110235_customers.js +17 -0
  131. package/dist/modules/customers/migrations/Migration20260930110235_customers.js.map +7 -0
  132. package/dist/modules/dictionaries/components/DictionaryTable.js +60 -51
  133. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  134. package/dist/modules/messages/subscribers/message-notification.js +13 -1
  135. package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
  136. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +201 -53
  137. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
  138. package/dist/modules/payment_gateways/lib/gateway-service.js +37 -11
  139. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  140. package/dist/modules/planner/lib/availabilityMerge.js +101 -38
  141. package/dist/modules/planner/lib/availabilityMerge.js.map +3 -3
  142. package/dist/modules/planner/services/plannerAvailabilityService.js.map +2 -2
  143. package/dist/modules/sales/components/documents/LineItemDialog.js +159 -81
  144. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  145. package/dist/modules/sales/components/documents/lineItemUtils.js +7 -0
  146. package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
  147. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +12 -7
  148. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
  149. package/dist/modules/sync_excel/lib/adapters/customers.js +28 -25
  150. package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
  151. package/generated/entities/customer_dictionary_entry/index.ts +1 -0
  152. package/generated/entities/customer_interaction/index.ts +1 -0
  153. package/generated/entity-fields-registry.ts +2 -0
  154. package/package.json +9 -8
  155. package/src/modules/auth/i18n/de.json +3 -0
  156. package/src/modules/auth/i18n/en.json +3 -0
  157. package/src/modules/auth/i18n/es.json +3 -0
  158. package/src/modules/auth/i18n/ko.json +3 -0
  159. package/src/modules/auth/i18n/pl.json +3 -0
  160. package/src/modules/catalog/commands/categories.ts +1 -1
  161. package/src/modules/catalog/commands/productDeleteChildren.ts +557 -0
  162. package/src/modules/catalog/commands/products.ts +58 -17
  163. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +4 -6
  164. package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +4 -6
  165. package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +46 -10
  166. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +6 -1
  167. package/src/modules/communication_channels/i18n/de.json +4 -0
  168. package/src/modules/communication_channels/i18n/en.json +4 -0
  169. package/src/modules/communication_channels/i18n/es.json +4 -0
  170. package/src/modules/communication_channels/i18n/ko.json +4 -0
  171. package/src/modules/communication_channels/i18n/pl.json +4 -0
  172. package/src/modules/communication_channels/lib/inbound-message-origin.ts +2 -6
  173. package/src/modules/communication_channels/lib/oauth-state.ts +4 -1
  174. package/src/modules/communication_channels/lib/use-connect-channel.ts +10 -0
  175. package/src/modules/customers/api/activity-types/route.ts +154 -0
  176. package/src/modules/customers/api/deals/[id]/people/route.ts +82 -3
  177. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -1
  178. package/src/modules/customers/api/dictionaries/[kind]/route.ts +18 -2
  179. package/src/modules/customers/api/interactions/route.ts +4 -0
  180. package/src/modules/customers/api/interactions/tasks/route.ts +1 -0
  181. package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +20 -0
  182. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +0 -29
  183. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -17
  184. package/src/modules/customers/calendar-event-types.ts +674 -0
  185. package/src/modules/customers/commands/dictionaries.ts +84 -8
  186. package/src/modules/customers/commands/interactions.ts +244 -21
  187. package/src/modules/customers/components/ActivityTypeEditor.tsx +239 -0
  188. package/src/modules/customers/components/DictionarySettings.tsx +5 -3
  189. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +347 -218
  190. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +18 -96
  191. package/src/modules/customers/components/calendar/TimeGrid.tsx +20 -18
  192. package/src/modules/customers/components/calendar/editor/EventTypePanel.tsx +230 -0
  193. package/src/modules/customers/components/calendar/editor/LocationField.tsx +7 -1
  194. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +4 -0
  195. package/src/modules/customers/components/calendar/editor/PriorityField.tsx +3 -0
  196. package/src/modules/customers/components/calendar/editor/RepeatField.tsx +10 -0
  197. package/src/modules/customers/components/calendar/editor/ResourcesField.tsx +4 -0
  198. package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +16 -4
  199. package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +3 -0
  200. package/src/modules/customers/components/calendar/editor/hooks.ts +29 -13
  201. package/src/modules/customers/components/calendar/editor/inputs.tsx +17 -5
  202. package/src/modules/customers/components/calendar/editor/useCalendarCustomFields.ts +49 -0
  203. package/src/modules/customers/components/calendar/editor/useEventTypeCatalog.ts +100 -0
  204. package/src/modules/customers/components/calendar/types.ts +1 -0
  205. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +94 -388
  206. package/src/modules/customers/components/detail/CreatePersonDialog.tsx +39 -19
  207. package/src/modules/customers/components/detail/DealForm.tsx +7 -3
  208. package/src/modules/customers/components/detail/DealPeopleSection.tsx +256 -0
  209. package/src/modules/customers/components/detail/LinkedPeopleSection.tsx +474 -0
  210. package/src/modules/customers/components/detail/PersonCard.tsx +12 -4
  211. package/src/modules/customers/components/detail/types.ts +1 -0
  212. package/src/modules/customers/components/linking/adapters/personAdapter.tsx +4 -0
  213. package/src/modules/customers/data/entities.ts +8 -1
  214. package/src/modules/customers/data/validators.ts +42 -7
  215. package/src/modules/customers/di.ts +21 -0
  216. package/src/modules/customers/extension-points.ts +13 -0
  217. package/src/modules/customers/i18n/de.json +104 -8
  218. package/src/modules/customers/i18n/en.json +104 -8
  219. package/src/modules/customers/i18n/es.json +104 -8
  220. package/src/modules/customers/i18n/ko.json +104 -8
  221. package/src/modules/customers/i18n/pl.json +104 -8
  222. package/src/modules/customers/lib/calendar/conflicts.ts +106 -27
  223. package/src/modules/customers/lib/calendar/editorPayload.ts +39 -19
  224. package/src/modules/customers/lib/calendar/eventTypeResolver.ts +258 -0
  225. package/src/modules/customers/lib/calendar/interactionApplicability.ts +99 -0
  226. package/src/modules/customers/lib/calendar/mapItem.ts +6 -2
  227. package/src/modules/customers/lib/calendar/recurrence.ts +65 -1
  228. package/src/modules/customers/lib/calendar/timezone.ts +117 -0
  229. package/src/modules/customers/lib/interactionReadModel.ts +1 -0
  230. package/src/modules/customers/lib/todoCompatibility.ts +2 -0
  231. package/src/modules/customers/migrations/.snapshot-open-mercato.json +34 -0
  232. package/src/modules/customers/migrations/Migration20260928175058_customers.ts +15 -0
  233. package/src/modules/customers/migrations/Migration20260930110235_customers.ts +15 -0
  234. package/src/modules/dictionaries/components/DictionaryTable.tsx +63 -56
  235. package/src/modules/messages/subscribers/message-notification.ts +13 -3
  236. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +243 -62
  237. package/src/modules/payment_gateways/lib/gateway-service.ts +39 -12
  238. package/src/modules/planner/lib/availabilityMerge.ts +106 -41
  239. package/src/modules/planner/services/plannerAvailabilityService.ts +4 -0
  240. package/src/modules/sales/components/documents/LineItemDialog.tsx +194 -111
  241. package/src/modules/sales/components/documents/lineItemUtils.ts +18 -0
  242. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +25 -13
  243. package/src/modules/sync_excel/lib/adapters/customers.ts +33 -27
@@ -7,6 +7,12 @@ function normalizeNumber(value, fallback = 0) {
7
7
  }
8
8
  return fallback;
9
9
  }
10
+ function resolveCatalogPriceAmount(amountNet, amountGross, mode) {
11
+ const isPresent = (value) => typeof value === "number" && Number.isFinite(value);
12
+ const [preferred, fallback] = mode === "net" ? [amountNet, amountGross] : [amountGross, amountNet];
13
+ const amount = isPresent(preferred) ? preferred : isPresent(fallback) ? fallback : null;
14
+ return amount !== null && amount > 0 ? amount : null;
15
+ }
10
16
  function formatMoney(value, currency, locale) {
11
17
  if (!currency) return value.toFixed(2);
12
18
  return new Intl.NumberFormat(locale, { style: "currency", currency }).format(value);
@@ -39,6 +45,7 @@ function resolveLineDiscountDisplay(line) {
39
45
  export {
40
46
  formatMoney,
41
47
  normalizeNumber,
48
+ resolveCatalogPriceAmount,
42
49
  resolveLineDiscountDisplay
43
50
  };
44
51
  //# sourceMappingURL=lineItemUtils.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/components/documents/lineItemUtils.ts"],
4
- "sourcesContent": ["\"use client\"\n\nexport function normalizeNumber(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (!Number.isNaN(parsed)) return parsed\n }\n return fallback\n}\n\n/**\n * Pass `locale` to format in the application locale (`useLocale()` in client components).\n * Omitting it keeps the runtime default, which varies per machine and is therefore not\n * assertable in tests.\n */\nexport function formatMoney(value: number, currency: string | null | undefined, locale?: string): string {\n if (!currency) return value.toFixed(2)\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value)\n}\n\nexport type LineDiscountDisplay = {\n amount: number | null\n percent: number | null\n}\n\ntype LineDiscountSource = {\n discountAmount?: unknown\n discountPercent?: unknown\n unitPriceNet?: unknown\n quantity?: unknown\n}\n\nconst DISCOUNT_MATCH_TOLERANCE = 0.01\nconst DISCOUNT_MATCH_TOLERANCE_PER_UNIT = 0.0001\n\nfunction percentAccountsForAmount(\n line: LineDiscountSource,\n percent: number,\n amount: number,\n): boolean {\n if (percent <= 0) return false\n const quantity = normalizeNumber(line.quantity, 0)\n const netBeforeDiscount = normalizeNumber(line.unitPriceNet, 0) * quantity\n if (netBeforeDiscount <= 0) return false\n const tolerance = Math.max(\n DISCOUNT_MATCH_TOLERANCE,\n quantity * DISCOUNT_MATCH_TOLERANCE_PER_UNIT,\n )\n return Math.abs((percent / 100) * netBeforeDiscount - amount) <= tolerance\n}\n\nexport function resolveLineDiscountDisplay(\n line: LineDiscountSource,\n): LineDiscountDisplay | null {\n const amount = normalizeNumber(line.discountAmount, 0)\n const percent = normalizeNumber(line.discountPercent, 0)\n if (amount > 0) {\n return {\n amount,\n percent: percentAccountsForAmount(line, percent, amount) ? percent : null,\n }\n }\n if (percent > 0) return { amount: null, percent }\n return null\n}\n"],
5
- "mappings": ";AAEO,SAAS,gBAAgB,OAAgB,WAAW,GAAW;AACpE,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,QAAQ;AACpD,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,CAAC,OAAO,MAAM,MAAM,EAAG,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAOO,SAAS,YAAY,OAAe,UAAqC,QAAyB;AACvG,MAAI,CAAC,SAAU,QAAO,MAAM,QAAQ,CAAC;AACrC,SAAO,IAAI,KAAK,aAAa,QAAQ,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,KAAK;AACpF;AAcA,MAAM,2BAA2B;AACjC,MAAM,oCAAoC;AAE1C,SAAS,yBACP,MACA,SACA,QACS;AACT,MAAI,WAAW,EAAG,QAAO;AACzB,QAAM,WAAW,gBAAgB,KAAK,UAAU,CAAC;AACjD,QAAM,oBAAoB,gBAAgB,KAAK,cAAc,CAAC,IAAI;AAClE,MAAI,qBAAqB,EAAG,QAAO;AACnC,QAAM,YAAY,KAAK;AAAA,IACrB;AAAA,IACA,WAAW;AAAA,EACb;AACA,SAAO,KAAK,IAAK,UAAU,MAAO,oBAAoB,MAAM,KAAK;AACnE;AAEO,SAAS,2BACd,MAC4B;AAC5B,QAAM,SAAS,gBAAgB,KAAK,gBAAgB,CAAC;AACrD,QAAM,UAAU,gBAAgB,KAAK,iBAAiB,CAAC;AACvD,MAAI,SAAS,GAAG;AACd,WAAO;AAAA,MACL;AAAA,MACA,SAAS,yBAAyB,MAAM,SAAS,MAAM,IAAI,UAAU;AAAA,IACvE;AAAA,EACF;AACA,MAAI,UAAU,EAAG,QAAO,EAAE,QAAQ,MAAM,QAAQ;AAChD,SAAO;AACT;",
4
+ "sourcesContent": ["\"use client\"\n\nexport function normalizeNumber(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (!Number.isNaN(parsed)) return parsed\n }\n return fallback\n}\n\n/**\n * A catalog price is only usable on a line when it carries a positive amount, because line\n * submission rejects a unit price of zero. Returns the amount for the requested mode, falling\n * back to the other mode only when the requested amount is missing, or `null` when the price\n * has no positive amount to use.\n */\nexport function resolveCatalogPriceAmount(\n amountNet: number | null | undefined,\n amountGross: number | null | undefined,\n mode: 'net' | 'gross',\n): number | null {\n const isPresent = (value: number | null | undefined): value is number =>\n typeof value === 'number' && Number.isFinite(value)\n const [preferred, fallback] = mode === 'net' ? [amountNet, amountGross] : [amountGross, amountNet]\n const amount = isPresent(preferred) ? preferred : isPresent(fallback) ? fallback : null\n return amount !== null && amount > 0 ? amount : null\n}\n\n/**\n * Pass `locale` to format in the application locale (`useLocale()` in client components).\n * Omitting it keeps the runtime default, which varies per machine and is therefore not\n * assertable in tests.\n */\nexport function formatMoney(value: number, currency: string | null | undefined, locale?: string): string {\n if (!currency) return value.toFixed(2)\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value)\n}\n\nexport type LineDiscountDisplay = {\n amount: number | null\n percent: number | null\n}\n\ntype LineDiscountSource = {\n discountAmount?: unknown\n discountPercent?: unknown\n unitPriceNet?: unknown\n quantity?: unknown\n}\n\nconst DISCOUNT_MATCH_TOLERANCE = 0.01\nconst DISCOUNT_MATCH_TOLERANCE_PER_UNIT = 0.0001\n\nfunction percentAccountsForAmount(\n line: LineDiscountSource,\n percent: number,\n amount: number,\n): boolean {\n if (percent <= 0) return false\n const quantity = normalizeNumber(line.quantity, 0)\n const netBeforeDiscount = normalizeNumber(line.unitPriceNet, 0) * quantity\n if (netBeforeDiscount <= 0) return false\n const tolerance = Math.max(\n DISCOUNT_MATCH_TOLERANCE,\n quantity * DISCOUNT_MATCH_TOLERANCE_PER_UNIT,\n )\n return Math.abs((percent / 100) * netBeforeDiscount - amount) <= tolerance\n}\n\nexport function resolveLineDiscountDisplay(\n line: LineDiscountSource,\n): LineDiscountDisplay | null {\n const amount = normalizeNumber(line.discountAmount, 0)\n const percent = normalizeNumber(line.discountPercent, 0)\n if (amount > 0) {\n return {\n amount,\n percent: percentAccountsForAmount(line, percent, amount) ? percent : null,\n }\n }\n if (percent > 0) return { amount: null, percent }\n return null\n}\n"],
5
+ "mappings": ";AAEO,SAAS,gBAAgB,OAAgB,WAAW,GAAW;AACpE,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,QAAQ;AACpD,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,CAAC,OAAO,MAAM,MAAM,EAAG,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAQO,SAAS,0BACd,WACA,aACA,MACe;AACf,QAAM,YAAY,CAAC,UACjB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AACpD,QAAM,CAAC,WAAW,QAAQ,IAAI,SAAS,QAAQ,CAAC,WAAW,WAAW,IAAI,CAAC,aAAa,SAAS;AACjG,QAAM,SAAS,UAAU,SAAS,IAAI,YAAY,UAAU,QAAQ,IAAI,WAAW;AACnF,SAAO,WAAW,QAAQ,SAAS,IAAI,SAAS;AAClD;AAOO,SAAS,YAAY,OAAe,UAAqC,QAAyB;AACvG,MAAI,CAAC,SAAU,QAAO,MAAM,QAAQ,CAAC;AACrC,SAAO,IAAI,KAAK,aAAa,QAAQ,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,KAAK;AACpF;AAcA,MAAM,2BAA2B;AACjC,MAAM,oCAAoC;AAE1C,SAAS,yBACP,MACA,SACA,QACS;AACT,MAAI,WAAW,EAAG,QAAO;AACzB,QAAM,WAAW,gBAAgB,KAAK,UAAU,CAAC;AACjD,QAAM,oBAAoB,gBAAgB,KAAK,cAAc,CAAC,IAAI;AAClE,MAAI,qBAAqB,EAAG,QAAO;AACnC,QAAM,YAAY,KAAK;AAAA,IACrB;AAAA,IACA,WAAW;AAAA,EACb;AACA,SAAO,KAAK,IAAK,UAAU,MAAO,oBAAoB,MAAM,KAAK;AACnE;AAEO,SAAS,2BACd,MAC4B;AAC5B,QAAM,SAAS,gBAAgB,KAAK,gBAAgB,CAAC;AACrD,QAAM,UAAU,gBAAgB,KAAK,iBAAiB,CAAC;AACvD,MAAI,SAAS,GAAG;AACd,WAAO;AAAA,MACL;AAAA,MACA,SAAS,yBAAyB,MAAM,SAAS,MAAM,IAAI,UAAU;AAAA,IACvE;AAAA,EACF;AACA,MAAI,UAAU,EAAG,QAAO,EAAE,QAAQ,MAAM,QAAQ;AAChD,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -71,7 +71,9 @@ function TimesheetProjectDetailPage({ params }) {
71
71
  const [loading, setLoading] = React.useState(true);
72
72
  const [error, setError] = React.useState(null);
73
73
  const [isNotFound, setIsNotFound] = React.useState(false);
74
- const [accessDenied, setAccessDenied] = React.useState(false);
74
+ const [accessDeniedProjectId, setAccessDeniedProjectId] = React.useState(null);
75
+ const accessDenied = accessDeniedProjectId !== null && accessDeniedProjectId === projectId;
76
+ const [settledProjectId, setSettledProjectId] = React.useState(null);
75
77
  const [employees, setEmployees] = React.useState([]);
76
78
  const [employeesLoading, setEmployeesLoading] = React.useState(false);
77
79
  const [expandedCards, setExpandedCards] = React.useState(/* @__PURE__ */ new Set());
@@ -232,16 +234,16 @@ function TimesheetProjectDetailPage({ params }) {
232
234
  setLoading(true);
233
235
  setError(null);
234
236
  setIsNotFound(false);
235
- setAccessDenied(false);
236
237
  try {
237
238
  const queryParams = new URLSearchParams({ page: "1", pageSize: "1", ids: projectId });
238
239
  const call = await apiCall(
239
240
  `/api/staff/timesheets/time-projects?${queryParams.toString()}`
240
241
  );
241
242
  if (call.status === 404 && readDenialReason(call.result) === NO_PROJECT_ACCESS_REASON) {
242
- if (!cancelled) setAccessDenied(true);
243
+ if (!cancelled) setAccessDeniedProjectId(projectId ?? null);
243
244
  return;
244
245
  }
246
+ if (!cancelled) setAccessDeniedProjectId(null);
245
247
  if (!call.ok) {
246
248
  throw new Error(t("staff.timesheets.projects.errors.load", "Failed to load project."));
247
249
  }
@@ -257,7 +259,10 @@ function TimesheetProjectDetailPage({ params }) {
257
259
  setError(loadError instanceof Error ? loadError.message : t("staff.timesheets.projects.errors.load", "Failed to load project."));
258
260
  }
259
261
  } finally {
260
- if (!cancelled) setLoading(false);
262
+ if (!cancelled) {
263
+ setLoading(false);
264
+ setSettledProjectId(projectId ?? null);
265
+ }
261
266
  }
262
267
  }
263
268
  loadProject();
@@ -450,12 +455,12 @@ function TimesheetProjectDetailPage({ params }) {
450
455
  void handleAddEmployee();
451
456
  }
452
457
  }, [handleAddEmployee]);
453
- if (loading) {
454
- return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(LoadingMessage, { label: t("staff.timesheets.projects.loading", "Loading project...") }) }) });
455
- }
456
458
  if (accessDenied) {
457
459
  return /* @__PURE__ */ jsx(NoProjectAccess, { timeProjectId: projectId });
458
460
  }
461
+ if (loading || settledProjectId !== (projectId ?? null)) {
462
+ return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(LoadingMessage, { label: t("staff.timesheets.projects.loading", "Loading project...") }) }) });
463
+ }
459
464
  if (isNotFound) {
460
465
  return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
461
466
  RecordNotFoundState,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/staff/backend/staff/time-tracking/projects/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { LookupSelect } from '@open-mercato/ui/backend/inputs/LookupSelect'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud, createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { ArrowLeft, Plus, Pencil, ChevronDown, Trash2, User, Users } from 'lucide-react'\nimport Link from 'next/link'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { formatCurrency } from '@open-mercato/ui/utils/format'\nimport { KpiCard, Sparkline } from '@open-mercato/ui/backend/charts'\nimport { ProjectTeamDrawer } from '../../../../../lib/time-tracking-ui/ProjectTeamDrawer'\nimport { NoProjectAccess } from '../../../../../lib/time-tracking-ui/NoProjectAccess'\nimport { ProjectBudgetCell } from '../../../../../lib/timesheets-projects-ui/ProjectBudgetCell'\nimport { computeBudgetBurn } from '../../../../../lib/timesheets-projects/budgetBurn'\nimport { resolveProjectColorHex } from '../../../../../lib/timesheets-ui/colors'\n\nconst logger = createLogger('staff')\n\nconst BACK_HREF = '/backend/staff/time-tracking/projects'\n\nconst EMPLOYEE_MUTATION_CONTEXT_ID = 'staff-time-project-detail:employees'\nconst EMPLOYEE_RESOURCE_KIND = 'staff.time_project_member'\n\ntype EmployeeMutationContext = {\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n}\n\n/**\n * Mirrors `NO_PROJECT_ACCESS_REASON` from\n * `api/timesheets/time-projects/route.ts`. The route module is server-only, so\n * the literal is restated here rather than imported into the client bundle;\n * `__tests__/no-project-access-contract.test.ts` pins the two together.\n */\nconst NO_PROJECT_ACCESS_REASON = 'no_project_access'\n\nconst PROJECT_ENTITY_ID = 'staff:staff_time_project'\n\ntype ProjectDetailInjectionContext = {\n entityId: string\n recordId: string | null\n projectId: string | null\n path: string | null\n retryLastMutation: () => Promise<boolean>\n}\n\ntype ProjectRecord = {\n id: string\n name: string\n code: string\n description?: string | null\n projectType?: string | null\n project_type?: string | null\n startDate?: string | null\n start_date?: string | null\n costCenter?: string | null\n cost_center?: string | null\n status?: string | null\n customerId?: string | null\n customer_id?: string | null\n} & Record<string, unknown>\n\ntype ProjectResponse = {\n items?: ProjectRecord[]\n}\n\nfunction readDenialReason(payload: unknown): string | null {\n if (!payload || typeof payload !== 'object') return null\n const reason = (payload as { reason?: unknown }).reason\n return typeof reason === 'string' ? reason : null\n}\n\ntype EmployeeAssignment = {\n id: string\n staffMemberId: string\n role: string | null\n status: string | null\n assignedStartDate: string | null\n assignedEndDate: string | null\n displayName: string | null\n teamName: string | null\n}\n\ntype EmployeesResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n}\n\ntype StaffMemberRecord = {\n id: string\n display_name?: string\n displayName?: string\n team?: { id: string; name: string } | null\n}\n\ntype StaffMembersResponse = {\n items?: StaffMemberRecord[]\n}\n\ntype ProjectEntryRow = {\n id: string\n date: string\n description: string | null\n durationMinutes: number\n isBillable: boolean\n taskId: string | null\n staffMemberId: string | null\n}\n\ntype ProjectTaskRow = {\n id: string\n reference: string | null\n title: string\n statusId: string | null\n assigneeStaffMemberId: string | null\n loggedMinutes: number | null\n}\n\n/**\n * A group heading inside the reference rail. The rail replaces three full-width\n * cards, so the groups carry the separation those card borders used to.\n */\nfunction RailGroup({ label, first }: { label: string; first?: boolean }) {\n return (\n <div\n className={`border-b pb-1.5 text-overline font-semibold uppercase tracking-widest text-muted-foreground ${\n first ? '' : 'pt-4'\n }`}\n >\n {label}\n </div>\n )\n}\n\nfunction RailFact({ label, value }: { label: string; value: React.ReactNode }) {\n return (\n <div className=\"flex items-baseline justify-between gap-3 border-b py-2 text-sm last:border-b-0\">\n <dt className=\"shrink-0 text-muted-foreground\">{label}</dt>\n <dd className=\"text-right font-medium tabular-nums\">{value}</dd>\n </div>\n )\n}\n\nexport default function TimesheetProjectDetailPage({ params }: { params?: { id?: string } }) {\n const projectId = params?.id\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n\n const [project, setProject] = React.useState<ProjectRecord | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n const [accessDenied, setAccessDenied] = React.useState(false)\n\n const [employees, setEmployees] = React.useState<EmployeeAssignment[]>([])\n const [employeesLoading, setEmployeesLoading] = React.useState(false)\n const [expandedCards, setExpandedCards] = React.useState<Set<string>>(new Set())\n const [reloadToken, setReloadToken] = React.useState(0)\n\n // Tabs govern the working column only \u2014 the details rail stays visible on every\n // tab, because reference facts are what you check *while* reading the tab you\n // are on, not a destination of their own.\n const [activeTab, setActiveTab] = React.useState<string>('team')\n const [recentEntries, setRecentEntries] = React.useState<ProjectEntryRow[]>([])\n const [recentEntriesLoading, setRecentEntriesLoading] = React.useState(false)\n const [projectTasks, setProjectTasks] = React.useState<ProjectTaskRow[]>([])\n const [projectTasksLoading, setProjectTasksLoading] = React.useState(false)\n\n const [canManageProjects, setCanManageProjects] = React.useState(false)\n // Money is gated on the same feature the portfolio gates it on; a rate hidden\n // in the list must not be readable one click deeper.\n const [canSeeMoney, setCanSeeMoney] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n void (async () => {\n try {\n const res = await apiCall<{ ok: boolean; granted: string[] }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n features: ['staff.timesheets.projects.manage', 'staff.timesheets.rates.view'],\n }),\n })\n if (!cancelled) {\n const granted = new Set(res.result?.granted ?? [])\n setCanManageProjects(granted.has('staff.timesheets.projects.manage'))\n setCanSeeMoney(granted.has('staff.timesheets.rates.view'))\n }\n } catch {\n // default: no manage access, no money\n }\n })()\n return () => { cancelled = true }\n }, [])\n\n const [addDialogOpen, setAddDialogOpen] = React.useState(false)\n const [addStaffMemberId, setAddStaffMemberId] = React.useState<string | null>(null)\n const [addRole, setAddRole] = React.useState('')\n const [addStartDate, setAddStartDate] = React.useState('')\n const [addSaving, setAddSaving] = React.useState(false)\n const [staffLookupError, setStaffLookupError] = React.useState<string | null>(null)\n\n // Fetched per tab rather than up front: a project page opened to check a rate\n // should not pay for two lists nobody looked at.\n React.useEffect(() => {\n if (!projectId || activeTab !== 'time') return\n let cancelled = false\n void (async () => {\n setRecentEntriesLoading(true)\n try {\n const res = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/staff/timesheets/time-entries?projectId=${encodeURIComponent(projectId)}&page=1&pageSize=25&sortField=date&sortDir=desc`,\n )\n if (cancelled || !res.ok) return\n const items = Array.isArray(res.result?.items) ? res.result.items : []\n setRecentEntries(\n items\n .map((row): ProjectEntryRow | null => {\n const id = typeof row.id === 'string' ? row.id : null\n if (!id) return null\n const raw = row as Record<string, unknown>\n const minutes = raw.duration_minutes ?? raw.durationMinutes\n return {\n id,\n date: typeof raw.date === 'string' ? raw.date.slice(0, 10) : '',\n description:\n typeof raw.description === 'string'\n ? raw.description\n : typeof raw.notes === 'string'\n ? raw.notes\n : null,\n durationMinutes: typeof minutes === 'number' ? minutes : Number(minutes ?? 0) || 0,\n isBillable: raw.is_billable === true || raw.isBillable === true,\n taskId: typeof (raw.task_id ?? raw.taskId) === 'string' ? String(raw.task_id ?? raw.taskId) : null,\n staffMemberId:\n typeof (raw.staff_member_id ?? raw.staffMemberId) === 'string'\n ? String(raw.staff_member_id ?? raw.staffMemberId)\n : null,\n }\n })\n .filter((row): row is ProjectEntryRow => row !== null),\n )\n } catch (err) {\n logger.warn('staff.timesheets.projects.detail recent entries failed', { err })\n } finally {\n if (!cancelled) setRecentEntriesLoading(false)\n }\n })()\n return () => { cancelled = true }\n }, [projectId, activeTab, reloadToken])\n\n React.useEffect(() => {\n if (!projectId || activeTab !== 'tasks') return\n let cancelled = false\n void (async () => {\n setProjectTasksLoading(true)\n try {\n const res = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n // `timeProjectId`, not `projectId`: the tasks list schema declares the\n // former and is `.passthrough()`, so the wrong name is accepted, never\n // read, and the tab silently lists every task in the tenant.\n `/api/staff/timesheets/tasks?timeProjectId=${encodeURIComponent(projectId)}&page=1&pageSize=50&sortField=reference&sortDir=asc`,\n )\n if (cancelled || !res.ok) return\n const items = Array.isArray(res.result?.items) ? res.result.items : []\n setProjectTasks(\n items\n .map((row): ProjectTaskRow | null => {\n const id = typeof row.id === 'string' ? row.id : null\n const title = typeof row.title === 'string' ? row.title : null\n if (!id || !title) return null\n const raw = row as Record<string, unknown>\n const logged = raw.loggedMinutes ?? raw.logged_minutes\n return {\n id,\n reference: typeof raw.reference === 'string' ? raw.reference : null,\n title,\n statusId:\n typeof (raw.task_status_id ?? raw.taskStatusId) === 'string'\n ? String(raw.task_status_id ?? raw.taskStatusId)\n : null,\n assigneeStaffMemberId:\n typeof (raw.assignee_staff_member_id ?? raw.assigneeStaffMemberId) === 'string'\n ? String(raw.assignee_staff_member_id ?? raw.assigneeStaffMemberId)\n : null,\n loggedMinutes: typeof logged === 'number' ? logged : null,\n }\n })\n .filter((row): row is ProjectTaskRow => row !== null),\n )\n } catch (err) {\n logger.warn('staff.timesheets.projects.detail tasks failed', { err })\n } finally {\n if (!cancelled) setProjectTasksLoading(false)\n }\n })()\n return () => { cancelled = true }\n }, [projectId, activeTab, reloadToken])\n\n const employeeNameById = React.useMemo(\n () => new Map(employees.map((emp) => [emp.staffMemberId, emp.displayName ?? ''])),\n [employees],\n )\n const activeCount = employees.filter((emp) => emp.status === 'active').length\n const inactiveCount = employees.length - activeCount\n\n const recentEntriesColumns = React.useMemo<ColumnDef<ProjectEntryRow>[]>(\n () => [\n {\n accessorKey: 'date',\n header: t('staff.timesheets.projects.detail.colDate', 'Date'),\n cell: ({ row }) => <span className=\"whitespace-nowrap\">{row.original.date}</span>,\n },\n {\n id: 'person',\n header: t('staff.timesheets.projects.detail.colWho', 'Person'),\n cell: ({ row }) => (\n <span className=\"whitespace-nowrap\">\n {employeeNameById.get(row.original.staffMemberId ?? '') ?? '\u2014'}\n </span>\n ),\n },\n {\n accessorKey: 'description',\n header: t('staff.timesheets.projects.detail.colDescription', 'Description'),\n cell: ({ row }) => (\n <span className=\"flex items-center gap-2\">\n <span className=\"truncate\">{row.original.description ?? '\u2014'}</span>\n {row.original.isBillable ? null : (\n <Badge variant=\"neutral\">\n {t('staff.timesheets.projects.detail.nonBillableTag', 'non-billable')}\n </Badge>\n )}\n </span>\n ),\n },\n {\n id: 'hours',\n header: () => (\n <span className=\"block text-right\">\n {t('staff.timesheets.projects.detail.colHours', 'Hours')}\n </span>\n ),\n cell: ({ row }) => (\n <span className=\"block whitespace-nowrap text-right font-mono tabular-nums\">\n {formatHours(row.original.durationMinutes)}\n </span>\n ),\n },\n ],\n [employeeNameById, t],\n )\n\n // --- Load project ---\n React.useEffect(() => {\n if (!projectId) return\n let cancelled = false\n async function loadProject() {\n setLoading(true)\n setError(null)\n setIsNotFound(false)\n setAccessDenied(false)\n try {\n const queryParams = new URLSearchParams({ page: '1', pageSize: '1', ids: projectId! })\n const call = await apiCall<ProjectResponse>(\n `/api/staff/timesheets/time-projects?${queryParams.toString()}`,\n )\n // Screen 17: the route answers 404 with this discriminator for a project\n // the caller is not a member of, without naming it.\n if (call.status === 404 && readDenialReason(call.result) === NO_PROJECT_ACCESS_REASON) {\n if (!cancelled) setAccessDenied(true)\n return\n }\n if (!call.ok) {\n throw new Error(t('staff.timesheets.projects.errors.load', 'Failed to load project.'))\n }\n const payload = call.result\n const record = Array.isArray(payload?.items) ? payload.items[0] : null\n if (!record) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n if (!cancelled) setProject(record)\n } catch (loadError) {\n if (!cancelled) {\n setError(loadError instanceof Error ? loadError.message : t('staff.timesheets.projects.errors.load', 'Failed to load project.'))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n loadProject()\n return () => { cancelled = true }\n }, [projectId, t, scopeVersion])\n\n // --- Load employees with name resolution ---\n const loadEmployees = React.useCallback(async () => {\n if (!projectId || accessDenied) return\n setEmployeesLoading(true)\n try {\n const payload = await readApiResultOrThrow<EmployeesResponse>(\n `/api/staff/timesheets/time-projects/${projectId}/employees?page=1&pageSize=100`,\n undefined,\n { errorMessage: t('staff.timesheets.projects.employees.empty', 'No employees assigned yet.'), fallback: { items: [], total: 0 } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n\n const staffMemberIds = items\n .map((item) => String(item.staff_member_id ?? item.staffMemberId ?? ''))\n .filter((id) => id.length > 0)\n\n let staffMap = new Map<string, StaffMemberRecord>()\n if (staffMemberIds.length > 0) {\n try {\n const staffRes = await apiCall<StaffMembersResponse>(\n `/api/staff/team-members?ids=${staffMemberIds.join(',')}&pageSize=100`,\n )\n if (staffRes.ok) {\n const staffItems = Array.isArray(staffRes.result?.items) ? staffRes.result.items : []\n staffMap = new Map(staffItems.map((member) => [member.id, member]))\n }\n } catch {\n // name resolution failed (e.g. 403 for employees) \u2014 show IDs as fallback\n }\n }\n\n const mapped: EmployeeAssignment[] = items.map((item) => {\n const staffMemberId = String(item.staff_member_id ?? item.staffMemberId ?? '')\n const staff = staffMap.get(staffMemberId)\n return {\n id: String(item.id ?? ''),\n staffMemberId,\n role: typeof item.role === 'string' ? item.role : null,\n status: typeof item.status === 'string' ? item.status : null,\n assignedStartDate: String(item.assigned_start_date ?? item.assignedStartDate ?? ''),\n assignedEndDate: typeof (item.assigned_end_date ?? item.assignedEndDate) === 'string'\n ? String(item.assigned_end_date ?? item.assignedEndDate)\n : null,\n displayName: staff?.display_name ?? staff?.displayName ?? null,\n teamName: staff?.team?.name ?? null,\n }\n })\n\n setEmployees(mapped)\n } catch (loadError) {\n logger.error('staff.timesheets.projects.employees.list', { err: loadError })\n } finally {\n setEmployeesLoading(false)\n }\n }, [projectId, accessDenied, t])\n\n React.useEffect(() => {\n void loadEmployees()\n }, [loadEmployees, reloadToken, scopeVersion])\n\n // --- Toggle card expand/collapse ---\n const toggleCard = React.useCallback((employeeId: string) => {\n setExpandedCards((prev) => {\n const next = new Set(prev)\n if (next.has(employeeId)) next.delete(employeeId)\n else next.add(employeeId)\n return next\n })\n }, [])\n\n const { runMutation, retryLastMutation } = useGuardedMutation<EmployeeMutationContext>({\n contextId: EMPLOYEE_MUTATION_CONTEXT_ID,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const projectInjectionContext = React.useMemo<ProjectDetailInjectionContext>(\n () => ({\n entityId: PROJECT_ENTITY_ID,\n recordId: projectId ?? null,\n projectId: projectId ?? null,\n path: pathname ?? null,\n retryLastMutation,\n }),\n [pathname, projectId, retryLastMutation],\n )\n\n const { widgets: injectedTabWidgets } = useInjectionWidgets<ProjectDetailInjectionContext>(\n extensionPoints.hosts.projectDetailTabs.spotId,\n { context: projectInjectionContext, triggerOnLoad: true },\n )\n\n const employeeMutationContext = React.useCallback(\n (resourceId: string): EmployeeMutationContext => ({\n formId: EMPLOYEE_MUTATION_CONTEXT_ID,\n resourceKind: EMPLOYEE_RESOURCE_KIND,\n resourceId,\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n\n // optimistic-lock-exempt: the employee add/remove calls below mutate the\n // time-project \u2194 staff-member assignment junction (createCrud/deleteCrud of a\n // membership row), not a shared editable aggregate. Junction add/remove is exempt\n // from OSS optimistic locking per the data-integrity contract.\n // --- Remove employee ---\n const handleRemoveEmployee = React.useCallback(async (emp: EmployeeAssignment) => {\n const confirmed = await confirm({\n title: t('staff.timesheets.projects.employees.remove', 'Remove'),\n text: t('staff.timesheets.projects.employees.removeConfirm', 'Remove this employee from the project?'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutation({\n operation: () =>\n deleteCrud(\n `staff/timesheets/time-projects/${projectId}/employees`,\n emp.id,\n { errorMessage: t('staff.timesheets.projects.employees.removeError', 'Failed to remove employee.') },\n ),\n context: employeeMutationContext(emp.id),\n mutationPayload: { id: emp.id },\n })\n flash(t('staff.timesheets.projects.employees.removed', 'Employee removed.'), 'success')\n setReloadToken((token) => token + 1)\n } catch (removeError) {\n if (surfaceRecordConflict(removeError, t)) return\n flash(t('staff.timesheets.projects.employees.removeError', 'Failed to remove employee.'), 'error')\n }\n }, [projectId, confirm, employeeMutationContext, runMutation, t])\n\n // --- Add employee dialog ---\n const openAddDialog = React.useCallback(() => {\n setAddStaffMemberId(null)\n setAddRole('')\n setAddStartDate(new Date().toISOString().slice(0, 10))\n setStaffLookupError(null)\n setAddDialogOpen(true)\n }, [])\n\n // A failed lookup is not an empty roster: swallowing it here left the picker\n // claiming \"No team members found\" while the request had actually errored.\n const staffLookupErrorMessage = t(\n 'staff.timesheets.projects.employees.loadError',\n 'Could not load team members. Check your connection and try again.',\n )\n\n const fetchStaffMembers = React.useCallback(async (query: string) => {\n const params = new URLSearchParams({ search: query, pageSize: '20', isActive: 'true' })\n try {\n const payload = await readApiResultOrThrow<StaffMembersResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: staffLookupErrorMessage, fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setStaffLookupError(null)\n return items.map((member) => ({\n id: member.id,\n title: member.display_name ?? member.displayName ?? member.id,\n subtitle: member.team?.name ?? null,\n }))\n } catch (lookupError) {\n logger.error('staff.timesheets.projects.employees.lookup', { err: lookupError })\n setStaffLookupError(staffLookupErrorMessage)\n throw lookupError\n }\n }, [staffLookupErrorMessage])\n\n const handleAddEmployee = React.useCallback(async () => {\n if (!projectId || !addStaffMemberId || !addStartDate) return\n const payload = {\n staffMemberId: addStaffMemberId,\n timeProjectId: projectId,\n role: addRole.trim() || null,\n assignedStartDate: addStartDate,\n }\n setAddSaving(true)\n try {\n await runMutation({\n operation: () =>\n createCrud(`staff/timesheets/time-projects/${projectId}/employees`, payload, {\n errorMessage: t('staff.timesheets.projects.employees.addError', 'Failed to add employee.'),\n }),\n context: employeeMutationContext(addStaffMemberId),\n mutationPayload: payload,\n })\n flash(t('staff.timesheets.projects.employees.added', 'Employee added.'), 'success')\n setAddDialogOpen(false)\n setReloadToken((token) => token + 1)\n } catch (addError) {\n if (surfaceRecordConflict(addError, t)) return\n flash(addError instanceof Error ? addError.message : t('staff.timesheets.projects.employees.addError', 'Failed to add employee.'), 'error')\n } finally {\n setAddSaving(false)\n }\n }, [projectId, addStaffMemberId, addRole, addStartDate, employeeMutationContext, runMutation, t])\n\n // Screen 5: the team drawer lives behind `?panel=team` so the project form\n // link and the access-request notification (D-6, `&requesterUserId=`) can both\n // deep-link into it. Assignment is a Team-Leader affordance, so the panel only\n // mounts for holders of `staff.timesheets.projects.manage`.\n const teamPanelRequested = searchParams?.get('panel') === 'team'\n const teamDrawerOpen = teamPanelRequested && canManageProjects\n const requesterUserId = searchParams?.get('requesterUserId') ?? null\n\n const openTeamDrawer = React.useCallback(() => {\n router.replace(`${pathname}?panel=team`)\n }, [pathname, router])\n\n const handleTeamDrawerOpenChange = React.useCallback((next: boolean) => {\n if (next) openTeamDrawer()\n else router.replace(pathname ?? BACK_HREF)\n }, [openTeamDrawer, pathname, router])\n\n const handleTeamSaved = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleAddDialogKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n void handleAddEmployee()\n }\n }, [handleAddEmployee])\n\n // --- Render ---\n if (loading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.timesheets.projects.loading', 'Loading project...')} />\n </PageBody>\n </Page>\n )\n }\n\n // Screen 17 \u2014 the caller may not open this project. The guard state names\n // neither the customer nor the project (note 1), and the surrounding shell\n // (sidebar, topbar) is untouched because the caller still has other projects\n // (note 3).\n if (accessDenied) {\n return <NoProjectAccess timeProjectId={projectId} />\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('staff.timesheets.projects.errors.notFound', 'Project not found.')}\n backHref={BACK_HREF}\n backLabel={t('staff.timesheets.projects.actions.backToList', 'Back to projects')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !project) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('staff.timesheets.projects.errors.load', 'Failed to load project.')} />\n </PageBody>\n </Page>\n )\n }\n\n const projectStatus = project.status ?? 'active'\n const projectType = project.projectType ?? project.project_type ?? null\n const projectStartDate = project.startDate ?? project.start_date ?? null\n const projectCode = project.code\n\n // Everything below already arrives in the list projection this page reads with\n // `?ids=`; the card used to render five of the ~22 columns and drop the rest,\n // so rate, budget, customer and cost centre were only visible by opening Edit.\n const readString = (...keys: string[]): string | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n if (typeof raw === 'string' && raw.trim().length > 0) return raw.trim()\n if (typeof raw === 'number' && Number.isFinite(raw)) return String(raw)\n }\n return null\n }\n const readNumber = (...keys: string[]): number | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n const parsed = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number(raw) : NaN\n if (Number.isFinite(parsed)) return parsed\n }\n return null\n }\n const readBool = (...keys: string[]): boolean | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n if (typeof raw === 'boolean') return raw\n }\n return null\n }\n\n const enrichment = ((project as Record<string, unknown>)._staff ?? {}) as Record<string, unknown>\n const readEnriched = (key: string): number | null => {\n const raw = enrichment[key]\n return typeof raw === 'number' && Number.isFinite(raw) ? raw : null\n }\n\n const customerSnapshot = ((project as Record<string, unknown>).customerSnapshot\n ?? (project as Record<string, unknown>).customer_snapshot) as Record<string, unknown> | null | undefined\n const customerName =\n (typeof customerSnapshot?.name === 'string' ? customerSnapshot.name : null)\n ?? (typeof enrichment.customerName === 'string' ? enrichment.customerName : null)\n const customerId = readString('customerId', 'customer_id')\n const projectColor = readString('color')\n const costCenter = readString('costCenter', 'cost_center')\n const currencyCode = readString('currencyCode', 'currency_code')\n const hourlyRate = readNumber('hourlyRate', 'hourly_rate')\n const billableByDefault = readBool('billableByDefault', 'billable_by_default')\n const createdAt = readString('createdAt', 'created_at')\n const updatedAt = readString('updatedAt', 'updated_at')\n const entryCount = readNumber('entryCount')\n const currencyLocked = readBool('currencyLocked') ?? false\n const lockedEntryCount = readNumber('lockedEntryCount')\n const totalMinutes = readEnriched('totalMinutes') ?? 0\n const billableMinutes = readEnriched('billableMinutes')\n const cost = readEnriched('cost')\n const nonBillableMinutes = billableMinutes === null ? null : Math.max(0, totalMinutes - billableMinutes)\n const billablePercent =\n billableMinutes === null || totalMinutes <= 0 ? null : Math.round((billableMinutes / totalMinutes) * 100)\n const hoursTrend = Array.isArray(enrichment.hoursTrend)\n ? (enrichment.hoursTrend as unknown[]).filter((value): value is number => typeof value === 'number')\n : []\n\n const budgetKind = readString('budgetKind', 'budget_kind')\n const budgetValue = readNumber('budgetValue', 'budget_value')\n const budgetWarnAt = readNumber('budgetWarnAtPercent', 'budget_warn_at_percent')\n const budgetBurn = computeBudgetBurn({\n kind: budgetKind as 'none' | 'hours' | 'amount' | null,\n budgetValue,\n warnAtPercent: budgetWarnAt,\n totalMinutes,\n cost,\n })\n\n const formatHours = (minutes: number | null): string => {\n if (minutes === null) return '\u2014'\n const sign = minutes < 0 ? '-' : ''\n const abs = Math.abs(minutes)\n return `${sign}${Math.floor(abs / 60)}:${String(abs % 60).padStart(2, '0')}`\n }\n const formatDateTime = (value: string | null): string => {\n if (!value) return '\u2014'\n // The query engine returns `2026-05-24 00:00:00+00` \u2014 a space separator and a\n // two-digit offset, neither of which `Date` accepts, so both need widening\n // before parsing or every timestamp renders raw.\n //\n // The offset is only widened when a clock precedes it: anchoring on the\n // trailing `-24` alone also matches the day of a date-only `2026-05-24`,\n // which turns a valid date into `2026-05-24:00` and renders it raw.\n const normalized = value\n .replace(' ', 'T')\n .replace(/(T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?)([+-]\\d{2})$/, '$1$2:00')\n const parsed = new Date(normalized)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n }\n const budgetUsageLabel = budgetBurn\n ? budgetBurn.kind === 'hours'\n ? t('staff.timesheets.projects.detail.budgetHours', '{percent}% of {value} h', {\n percent: budgetBurn.percent,\n value: budgetBurn.budgetValue,\n })\n : t('staff.timesheets.projects.detail.budgetAmount', '{percent}% of {value}', {\n percent: budgetBurn.percent,\n value: formatCurrency(budgetBurn.budgetValue, currencyCode ?? undefined) ?? String(budgetBurn.budgetValue),\n })\n : null\n\n const injectedTabs = injectedTabWidgets\n .filter((widget) => (widget.placement?.kind ?? 'tab') === 'tab')\n .map((widget) => {\n const tabId = widget.placement?.groupId ?? widget.widgetId\n const groupLabel = widget.placement?.groupLabel\n const label = groupLabel ? t(groupLabel, groupLabel) : widget.module.metadata.title ?? tabId\n const priority = typeof widget.placement?.priority === 'number' ? widget.placement.priority : 0\n return { tabId, label, priority, Widget: widget.module.Widget }\n })\n .sort((left, right) => right.priority - left.priority)\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n {/*\n * Identity first: colour, name, and the two states worth knowing before\n * anything else \u2014 is it running, and is the budget in trouble. The code,\n * customer, type and start date sit under it as one meta line rather than\n * four labelled rows, because none of them is a decision on its own.\n */}\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <Button variant=\"ghost\" size=\"icon\" asChild>\n <Link href={BACK_HREF}>\n <ArrowLeft className=\"h-4 w-4\" aria-hidden />\n </Link>\n </Button>\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span\n className=\"size-3 shrink-0 rounded-sm border border-border\"\n style={{ backgroundColor: resolveProjectColorHex(projectColor, project.name) }}\n aria-hidden\n />\n <h1 className=\"text-xl font-semibold\">{project.name}</h1>\n <Badge\n variant={projectStatus === 'active' ? 'success' : projectStatus === 'on_hold' ? 'warning' : 'neutral'}\n className=\"capitalize\"\n >\n {projectStatus}\n </Badge>\n {canSeeMoney && budgetBurn && budgetBurn.tone !== 'accent' ? (\n <Badge variant={budgetBurn.tone === 'destructive' ? 'destructive' : 'warning'}>\n {t('staff.timesheets.projects.detail.budgetBadge', 'Budget {percent}%', {\n percent: budgetBurn.percent,\n })}\n </Badge>\n ) : null}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailStatusBadges.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <span className=\"rounded-sm border border-border bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground\">\n {projectCode}\n </span>\n {customerName ? (\n <>\n <span aria-hidden>\u00B7</span>\n {customerId ? (\n <Link className=\"underline underline-offset-2\" href={`/backend/customers/companies/${customerId}`}>\n {customerName}\n </Link>\n ) : (\n <span>{customerName}</span>\n )}\n </>\n ) : null}\n {projectType ? (<><span aria-hidden>\u00B7</span><span>{projectType}</span></>) : null}\n {projectStartDate ? (\n <>\n <span aria-hidden>\u00B7</span>\n <span>\n {t('staff.timesheets.projects.detail.startedOn', 'Started {date}', {\n date: formatDateTime(projectStartDate),\n })}\n </span>\n </>\n ) : null}\n </div>\n </div>\n </div>\n {canManageProjects && (\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={openTeamDrawer}>\n <Users className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.time_tracking.projects.team.manage', 'Manage team')}\n </Button>\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link href={`/backend/staff/time-tracking/projects/${projectId}/edit`}>\n <Pencil className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.timesheets.projects.form.actions.edit', 'Edit Project')}\n </Link>\n </Button>\n </div>\n )}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailHeader.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n\n {/*\n * Summary before detail. Four figures across the full width, in the order\n * somebody asks them: how much time, how much of it billable, what it\n * cost, how close the budget is. The three stacked label/value cards this\n * replaces gave all fourteen fields identical weight.\n */}\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-4\">\n <KpiCard\n title={t('staff.timesheets.projects.detail.loggedHours', 'Logged hours')}\n value={totalMinutes / 60}\n formatValue={() => formatHours(totalMinutes)}\n footer={\n hoursTrend.length > 0 ? (\n <Sparkline\n values={hoursTrend}\n ariaLabel={t('staff.timesheets.projects.detail.trendAria', 'Hours per week, last 7 weeks')}\n />\n ) : undefined\n }\n headerAction={\n entryCount !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {entryCount === 1\n ? t('staff.timesheets.projects.detail.entryCountOne', '1 entry')\n : t('staff.timesheets.projects.detail.entryCountShort', '{count} entries', { count: entryCount })}\n </span>\n ) : undefined\n }\n />\n <KpiCard\n title={t('staff.timesheets.projects.detail.billableHours', 'Billable hours')}\n value={(billableMinutes ?? 0) / 60}\n formatValue={() => formatHours(billableMinutes)}\n headerAction={\n billablePercent !== null ? (\n <Badge variant={billablePercent >= 80 ? 'success' : 'neutral'}>{`${billablePercent}%`}</Badge>\n ) : undefined\n }\n footer={\n nonBillableMinutes !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.nonBillable', '{hours} non-billable', {\n hours: formatHours(nonBillableMinutes),\n })}\n </span>\n ) : undefined\n }\n />\n {canSeeMoney ? (\n <KpiCard\n title={t('staff.timesheets.projects.detail.cost', 'Cost to date')}\n value={cost}\n formatValue={(value) => formatCurrency(value, currencyCode ?? undefined) ?? String(value)}\n footer={\n hourlyRate !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.costBasis', '{hours} \u00D7 {rate}', {\n hours: formatHours(billableMinutes),\n rate: `${formatCurrency(hourlyRate, currencyCode ?? undefined) ?? hourlyRate}/h`,\n })}\n </span>\n ) : undefined\n }\n />\n ) : null}\n {canSeeMoney ? (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-sm font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.form.budget', 'Budget')}\n </span>\n {budgetBurn ? (\n <Badge\n variant={\n budgetBurn.tone === 'destructive'\n ? 'destructive'\n : budgetBurn.tone === 'warning'\n ? 'warning'\n : 'neutral'\n }\n >\n {`${budgetBurn.percent}%`}\n </Badge>\n ) : (\n <Badge variant=\"neutral\">{t('staff.timesheets.projects.detail.noBudget', 'No budget')}</Badge>\n )}\n </div>\n <div className=\"mt-2 space-y-2\">\n <p className=\"text-2xl font-semibold tabular-nums\">\n {budgetBurn ? `${budgetBurn.percent}%` : '\u2014'}\n </p>\n <ProjectBudgetCell\n burn={budgetBurn}\n usageLabel={budgetUsageLabel}\n noBudgetLabel={t('staff.timesheets.projects.detail.budgetHint', 'Set one to track burn')}\n ariaLabel={t('staff.timesheets.projects.detail.budgetUsage', 'Budget usage')}\n />\n </div>\n </div>\n ) : null}\n </div>\n\n {/*\n * Working content left, reference right. Everything static \u2014 rate,\n * currency, cost centre, timestamps \u2014 collapses into one rail instead of\n * three full-width cards, which is what left the right half of a wide\n * screen carrying nothing.\n */}\n <div className=\"grid items-start gap-4 lg:grid-cols-[minmax(0,2fr)_minmax(280px,1fr)]\">\n <Tabs\n value={activeTab}\n onValueChange={setActiveTab}\n variant=\"underline\"\n className=\"min-w-0\"\n >\n <TabsList aria-label={t('staff.timesheets.projects.detail.tabs', 'Project sections')}>\n <TabsTrigger value=\"team\" count={activeCount}>\n {t('staff.timesheets.projects.detail.tabTeam', 'Team')}\n </TabsTrigger>\n <TabsTrigger value=\"time\" count={entryCount ?? undefined}>\n {t('staff.timesheets.projects.detail.tabTime', 'Time')}\n </TabsTrigger>\n <TabsTrigger value=\"tasks\">\n {t('staff.timesheets.projects.detail.tabTasks', 'Tasks')}\n </TabsTrigger>\n {injectedTabs.map((tab) => (\n <TabsTrigger key={tab.tabId} value={tab.tabId}>\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n\n <TabsContent value=\"team\">\n {/* Assigned Employees \u2014 collapsible cards */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"sr-only\">\n {t('staff.timesheets.projects.employees.title', 'Assigned Employees')}\n </h2>\n <span />\n {canManageProjects && (\n <Button size=\"sm\" onClick={openAddDialog}>\n <Plus className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.timesheets.projects.add_employee', 'Add Employee')}\n </Button>\n )}\n </div>\n\n {employeesLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.employees.loading', 'Loading employees...')} />\n ) : employees.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <User className=\"mx-auto h-8 w-8 text-muted-foreground/50\" aria-hidden />\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t('staff.timesheets.projects.employees.empty', 'No employees assigned yet.')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {employees.map((emp) => {\n const isExpanded = expandedCards.has(emp.id)\n const isInactive = emp.status !== 'active'\n return (\n <div\n key={emp.id}\n className={`rounded-lg border ${isInactive ? 'opacity-60' : ''}`}\n >\n {/* Collapsed view: name, role, status badge, start date, expand toggle */}\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between p-4 text-left hover:bg-muted/50 transition-colors\"\n onClick={() => toggleCard(emp.id)}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted\">\n <User className=\"h-4 w-4 text-muted-foreground\" aria-hidden />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">\n {emp.displayName ?? emp.staffMemberId}\n </p>\n {emp.role ? (\n <p className=\"text-xs text-muted-foreground truncate\">{emp.role}</p>\n ) : null}\n </div>\n </div>\n <div className=\"flex items-center gap-3 shrink-0\">\n <Badge\n variant={(emp.status ?? 'active') === 'active' ? 'success' : 'neutral'}\n className=\"capitalize\"\n >\n {emp.status ?? 'active'}\n </Badge>\n {emp.assignedStartDate ? (\n <span className=\"text-xs text-muted-foreground hidden sm:inline\">\n {emp.assignedStartDate}\n </span>\n ) : null}\n <ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform ${isExpanded ? 'rotate-180' : ''}`} aria-hidden />\n </div>\n </button>\n\n {/* Expanded view: assignment details */}\n {isExpanded ? (\n <div className=\"border-t px-4 pb-4 pt-3 space-y-3\">\n <dl className=\"grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.role', 'Role')}\n </dt>\n <dd>{emp.role || '-'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.status', 'Status')}\n </dt>\n <dd className=\"capitalize\">{emp.status ?? 'active'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.startDate', 'Assignment start')}\n </dt>\n <dd>{emp.assignedStartDate || '-'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.endDate', 'Assignment end')}\n </dt>\n <dd>{emp.assignedEndDate || '-'}</dd>\n </div>\n {emp.teamName ? (\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.department', 'Department')}\n </dt>\n <dd>{emp.teamName}</dd>\n </div>\n ) : null}\n </dl>\n {canManageProjects && (\n <div className=\"flex justify-end\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"text-destructive hover:text-destructive\"\n onClick={() => { void handleRemoveEmployee(emp) }}\n >\n <Trash2 className=\"mr-2 h-3.5 w-3.5\" aria-hidden />\n {t('staff.timesheets.projects.employees.remove', 'Remove')}\n </Button>\n </div>\n )}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )}\n </div>\n </TabsContent>\n\n <TabsContent value=\"time\">\n {recentEntriesLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.detail.loadingTime', 'Loading time entries\u2026')} />\n ) : recentEntries.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <p className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.detail.noTime', 'No time logged yet')}\n </p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.noTimeHint', 'Hours logged against this project appear here.')}\n </p>\n </div>\n ) : (\n <DataTable<ProjectEntryRow>\n extensionTableId={extensionPoints.hosts.projectDetailTimeEntriesTable.tableId}\n disableRowClick\n columns={recentEntriesColumns}\n data={recentEntries}\n />\n )}\n </TabsContent>\n\n <TabsContent value=\"tasks\">\n {projectTasksLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.detail.loadingTasks', 'Loading tasks\u2026')} />\n ) : projectTasks.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <p className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.detail.noTasks', 'No tasks yet')}\n </p>\n </div>\n ) : (\n <div className=\"divide-y rounded-lg border\">\n {projectTasks.map((task) => (\n <div key={task.id} className=\"flex items-center gap-3 px-3 py-2 text-sm\">\n {task.reference ? (\n <span className=\"shrink-0 rounded-sm border border-border bg-muted px-1.5 py-0.5 font-mono text-xs\">\n {task.reference}\n </span>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{task.title}</span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">\n {employeeNameById.get(task.assigneeStaffMemberId ?? '') ?? ''}\n </span>\n {task.loggedMinutes !== null ? (\n <span className=\"shrink-0 font-mono text-xs tabular-nums text-muted-foreground\">\n {formatHours(task.loggedMinutes)}\n </span>\n ) : null}\n </div>\n ))}\n </div>\n )}\n </TabsContent>\n\n {injectedTabs.map((tab) => (\n <TabsContent key={tab.tabId} value={tab.tabId}>\n <tab.Widget context={projectInjectionContext} data={project} />\n </TabsContent>\n ))}\n </Tabs>\n\n {/* Reference rail \u2014 one card, grouped, no longer three stacked sections. */}\n <div className=\"rounded-lg border bg-card\">\n <div className=\"border-b px-4 py-3\">\n <h2 className=\"text-sm font-semibold\">\n {t('staff.timesheets.projects.detail.details', 'Details')}\n </h2>\n </div>\n <div className=\"px-4 py-3\">\n {canSeeMoney ? (\n <>\n <RailGroup label={t('staff.timesheets.projects.detail.commercials', 'Billing')} first />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.form.hourlyRate', 'Hourly rate')}\n value={\n hourlyRate === null\n ? '\u2014'\n : `${formatCurrency(hourlyRate, currencyCode ?? undefined) ?? hourlyRate} / h`\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.form.currency', 'Currency')}\n value={\n <span className=\"inline-flex items-center gap-1.5\">\n {currencyCode ?? '\u2014'}\n {currencyLocked ? (\n <Badge\n variant=\"neutral\"\n title={t(\n 'staff.timesheets.projects.detail.currencyLockedHint',\n 'Locked because hours are already logged against this project.',\n )}\n >\n {t('staff.timesheets.projects.detail.locked', 'locked')}\n </Badge>\n ) : null}\n </span>\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.form.billableByDefault', 'Billable by default')}\n value={\n billableByDefault === null ? (\n '\u2014'\n ) : (\n <Badge variant={billableByDefault ? 'success' : 'neutral'}>\n {billableByDefault\n ? t('staff.timesheets.projects.detail.yes', 'Yes')\n : t('staff.timesheets.projects.detail.no', 'No')}\n </Badge>\n )\n }\n />\n </dl>\n </>\n ) : null}\n\n <RailGroup\n label={t('staff.timesheets.projects.detail.delivery', 'Delivery')}\n first={!canSeeMoney}\n />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.detail.entryCount', 'Time entries')}\n value={entryCount ?? '\u2014'}\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.lockedEntries', 'Locked in reports')}\n value={lockedEntryCount ?? '\u2014'}\n />\n </dl>\n\n <RailGroup label={t('staff.timesheets.projects.detail.administration', 'Administration')} />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.form.costCenter', 'Cost center')}\n value={costCenter ?? '\u2014'}\n />\n <RailFact\n label={t('staff.timesheets.projects.form.color', 'Project color')}\n value={\n <span className=\"inline-flex items-center gap-1.5 capitalize\">\n <span\n className=\"size-2.5 rounded-full border border-border\"\n style={{ backgroundColor: resolveProjectColorHex(projectColor, project.name) }}\n aria-hidden\n />\n {projectColor ?? t('staff.timesheets.projects.detail.autoColor', 'Auto')}\n </span>\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.createdAt', 'Created')}\n value={formatDateTime(createdAt)}\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.updatedAt', 'Last updated')}\n value={formatDateTime(updatedAt)}\n />\n </dl>\n\n {project.description ? (\n <>\n <RailGroup label={t('staff.timesheets.projects.form.description', 'Description')} />\n <p className=\"whitespace-pre-wrap pt-2 text-sm text-muted-foreground\">{project.description}</p>\n </>\n ) : null}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailSidebar.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n </div>\n </div>\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailFooter.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n\n {ConfirmDialogElement}\n\n {projectId && teamDrawerOpen ? (\n <ProjectTeamDrawer\n projectId={projectId}\n projectName={project.name}\n open={teamDrawerOpen}\n onOpenChange={handleTeamDrawerOpenChange}\n preselectUserId={requesterUserId}\n onSaved={handleTeamSaved}\n />\n ) : null}\n\n {/* Add Employee Dialog */}\n <Dialog open={addDialogOpen} onOpenChange={setAddDialogOpen}>\n <DialogContent className=\"sm:max-w-md\" onKeyDown={handleAddDialogKeyDown}>\n <DialogHeader>\n <DialogTitle>{t('staff.timesheets.projects.add_employee', 'Add Employee')}</DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 pt-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.employees.selectEmployee', 'Select Employee')}\n </label>\n <LookupSelect\n value={addStaffMemberId}\n onChange={setAddStaffMemberId}\n fetchItems={fetchStaffMembers}\n searchPlaceholder={t('staff.timesheets.projects.employees.searchEmployee', 'Search team members...')}\n emptyLabel={t('staff.timesheets.projects.employees.noResults', 'No team members found')}\n />\n {staffLookupError ? (\n <p className=\"text-xs text-status-error-text\" role=\"alert\">\n {staffLookupError}\n </p>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"add-role\">\n {t('staff.timesheets.projects.employees.roleOnProject', 'Role on Project')}\n </label>\n <Input\n id=\"add-role\"\n value={addRole}\n onChange={(event) => setAddRole(event.target.value)}\n placeholder={t('staff.timesheets.projects.employees.rolePlaceholder', 'e.g. Developer, Designer...')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"add-start-date\">\n {t('staff.timesheets.projects.employees.assignmentStartDate', 'Assignment Start Date')}\n </label>\n <Input\n id=\"add-start-date\"\n type=\"date\"\n value={addStartDate}\n onChange={(event) => setAddStartDate(event.target.value)}\n />\n </div>\n <div className=\"flex justify-end gap-2 pt-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setAddDialogOpen(false)}>\n {t('staff.timesheets.projects.form.actions.cancel', 'Cancel')}\n </Button>\n <Button\n size=\"sm\"\n disabled={addSaving || !addStaffMemberId || !addStartDate}\n onClick={handleAddEmployee}\n >\n {addSaving ? <Spinner className=\"size-4\" /> : null}\n {t('staff.timesheets.projects.add_employee', 'Add Employee')}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { LookupSelect } from '@open-mercato/ui/backend/inputs/LookupSelect'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud, createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { ArrowLeft, Plus, Pencil, ChevronDown, Trash2, User, Users } from 'lucide-react'\nimport Link from 'next/link'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { formatCurrency } from '@open-mercato/ui/utils/format'\nimport { KpiCard, Sparkline } from '@open-mercato/ui/backend/charts'\nimport { ProjectTeamDrawer } from '../../../../../lib/time-tracking-ui/ProjectTeamDrawer'\nimport { NoProjectAccess } from '../../../../../lib/time-tracking-ui/NoProjectAccess'\nimport { ProjectBudgetCell } from '../../../../../lib/timesheets-projects-ui/ProjectBudgetCell'\nimport { computeBudgetBurn } from '../../../../../lib/timesheets-projects/budgetBurn'\nimport { resolveProjectColorHex } from '../../../../../lib/timesheets-ui/colors'\n\nconst logger = createLogger('staff')\n\nconst BACK_HREF = '/backend/staff/time-tracking/projects'\n\nconst EMPLOYEE_MUTATION_CONTEXT_ID = 'staff-time-project-detail:employees'\nconst EMPLOYEE_RESOURCE_KIND = 'staff.time_project_member'\n\ntype EmployeeMutationContext = {\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n}\n\n/**\n * Mirrors `NO_PROJECT_ACCESS_REASON` from\n * `api/timesheets/time-projects/route.ts`. The route module is server-only, so\n * the literal is restated here rather than imported into the client bundle;\n * `__tests__/no-project-access-contract.test.ts` pins the two together.\n */\nconst NO_PROJECT_ACCESS_REASON = 'no_project_access'\n\nconst PROJECT_ENTITY_ID = 'staff:staff_time_project'\n\ntype ProjectDetailInjectionContext = {\n entityId: string\n recordId: string | null\n projectId: string | null\n path: string | null\n retryLastMutation: () => Promise<boolean>\n}\n\ntype ProjectRecord = {\n id: string\n name: string\n code: string\n description?: string | null\n projectType?: string | null\n project_type?: string | null\n startDate?: string | null\n start_date?: string | null\n costCenter?: string | null\n cost_center?: string | null\n status?: string | null\n customerId?: string | null\n customer_id?: string | null\n} & Record<string, unknown>\n\ntype ProjectResponse = {\n items?: ProjectRecord[]\n}\n\nfunction readDenialReason(payload: unknown): string | null {\n if (!payload || typeof payload !== 'object') return null\n const reason = (payload as { reason?: unknown }).reason\n return typeof reason === 'string' ? reason : null\n}\n\ntype EmployeeAssignment = {\n id: string\n staffMemberId: string\n role: string | null\n status: string | null\n assignedStartDate: string | null\n assignedEndDate: string | null\n displayName: string | null\n teamName: string | null\n}\n\ntype EmployeesResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n}\n\ntype StaffMemberRecord = {\n id: string\n display_name?: string\n displayName?: string\n team?: { id: string; name: string } | null\n}\n\ntype StaffMembersResponse = {\n items?: StaffMemberRecord[]\n}\n\ntype ProjectEntryRow = {\n id: string\n date: string\n description: string | null\n durationMinutes: number\n isBillable: boolean\n taskId: string | null\n staffMemberId: string | null\n}\n\ntype ProjectTaskRow = {\n id: string\n reference: string | null\n title: string\n statusId: string | null\n assigneeStaffMemberId: string | null\n loggedMinutes: number | null\n}\n\n/**\n * A group heading inside the reference rail. The rail replaces three full-width\n * cards, so the groups carry the separation those card borders used to.\n */\nfunction RailGroup({ label, first }: { label: string; first?: boolean }) {\n return (\n <div\n className={`border-b pb-1.5 text-overline font-semibold uppercase tracking-widest text-muted-foreground ${\n first ? '' : 'pt-4'\n }`}\n >\n {label}\n </div>\n )\n}\n\nfunction RailFact({ label, value }: { label: string; value: React.ReactNode }) {\n return (\n <div className=\"flex items-baseline justify-between gap-3 border-b py-2 text-sm last:border-b-0\">\n <dt className=\"shrink-0 text-muted-foreground\">{label}</dt>\n <dd className=\"text-right font-medium tabular-nums\">{value}</dd>\n </div>\n )\n}\n\nexport default function TimesheetProjectDetailPage({ params }: { params?: { id?: string } }) {\n const projectId = params?.id\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n\n const [project, setProject] = React.useState<ProjectRecord | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n // Keyed by project id so a route change to another project never shows the\n // previous project's guard (with a live request button) while the new one loads.\n const [accessDeniedProjectId, setAccessDeniedProjectId] = React.useState<string | null>(null)\n const accessDenied = accessDeniedProjectId !== null && accessDeniedProjectId === projectId\n // The id the last finished load was for. Until it matches the route, the page\n // is still loading \u2014 never the previous project's error or empty state.\n const [settledProjectId, setSettledProjectId] = React.useState<string | null>(null)\n\n const [employees, setEmployees] = React.useState<EmployeeAssignment[]>([])\n const [employeesLoading, setEmployeesLoading] = React.useState(false)\n const [expandedCards, setExpandedCards] = React.useState<Set<string>>(new Set())\n const [reloadToken, setReloadToken] = React.useState(0)\n\n // Tabs govern the working column only \u2014 the details rail stays visible on every\n // tab, because reference facts are what you check *while* reading the tab you\n // are on, not a destination of their own.\n const [activeTab, setActiveTab] = React.useState<string>('team')\n const [recentEntries, setRecentEntries] = React.useState<ProjectEntryRow[]>([])\n const [recentEntriesLoading, setRecentEntriesLoading] = React.useState(false)\n const [projectTasks, setProjectTasks] = React.useState<ProjectTaskRow[]>([])\n const [projectTasksLoading, setProjectTasksLoading] = React.useState(false)\n\n const [canManageProjects, setCanManageProjects] = React.useState(false)\n // Money is gated on the same feature the portfolio gates it on; a rate hidden\n // in the list must not be readable one click deeper.\n const [canSeeMoney, setCanSeeMoney] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n void (async () => {\n try {\n const res = await apiCall<{ ok: boolean; granted: string[] }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n features: ['staff.timesheets.projects.manage', 'staff.timesheets.rates.view'],\n }),\n })\n if (!cancelled) {\n const granted = new Set(res.result?.granted ?? [])\n setCanManageProjects(granted.has('staff.timesheets.projects.manage'))\n setCanSeeMoney(granted.has('staff.timesheets.rates.view'))\n }\n } catch {\n // default: no manage access, no money\n }\n })()\n return () => { cancelled = true }\n }, [])\n\n const [addDialogOpen, setAddDialogOpen] = React.useState(false)\n const [addStaffMemberId, setAddStaffMemberId] = React.useState<string | null>(null)\n const [addRole, setAddRole] = React.useState('')\n const [addStartDate, setAddStartDate] = React.useState('')\n const [addSaving, setAddSaving] = React.useState(false)\n const [staffLookupError, setStaffLookupError] = React.useState<string | null>(null)\n\n // Fetched per tab rather than up front: a project page opened to check a rate\n // should not pay for two lists nobody looked at.\n React.useEffect(() => {\n if (!projectId || activeTab !== 'time') return\n let cancelled = false\n void (async () => {\n setRecentEntriesLoading(true)\n try {\n const res = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/staff/timesheets/time-entries?projectId=${encodeURIComponent(projectId)}&page=1&pageSize=25&sortField=date&sortDir=desc`,\n )\n if (cancelled || !res.ok) return\n const items = Array.isArray(res.result?.items) ? res.result.items : []\n setRecentEntries(\n items\n .map((row): ProjectEntryRow | null => {\n const id = typeof row.id === 'string' ? row.id : null\n if (!id) return null\n const raw = row as Record<string, unknown>\n const minutes = raw.duration_minutes ?? raw.durationMinutes\n return {\n id,\n date: typeof raw.date === 'string' ? raw.date.slice(0, 10) : '',\n description:\n typeof raw.description === 'string'\n ? raw.description\n : typeof raw.notes === 'string'\n ? raw.notes\n : null,\n durationMinutes: typeof minutes === 'number' ? minutes : Number(minutes ?? 0) || 0,\n isBillable: raw.is_billable === true || raw.isBillable === true,\n taskId: typeof (raw.task_id ?? raw.taskId) === 'string' ? String(raw.task_id ?? raw.taskId) : null,\n staffMemberId:\n typeof (raw.staff_member_id ?? raw.staffMemberId) === 'string'\n ? String(raw.staff_member_id ?? raw.staffMemberId)\n : null,\n }\n })\n .filter((row): row is ProjectEntryRow => row !== null),\n )\n } catch (err) {\n logger.warn('staff.timesheets.projects.detail recent entries failed', { err })\n } finally {\n if (!cancelled) setRecentEntriesLoading(false)\n }\n })()\n return () => { cancelled = true }\n }, [projectId, activeTab, reloadToken])\n\n React.useEffect(() => {\n if (!projectId || activeTab !== 'tasks') return\n let cancelled = false\n void (async () => {\n setProjectTasksLoading(true)\n try {\n const res = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n // `timeProjectId`, not `projectId`: the tasks list schema declares the\n // former and is `.passthrough()`, so the wrong name is accepted, never\n // read, and the tab silently lists every task in the tenant.\n `/api/staff/timesheets/tasks?timeProjectId=${encodeURIComponent(projectId)}&page=1&pageSize=50&sortField=reference&sortDir=asc`,\n )\n if (cancelled || !res.ok) return\n const items = Array.isArray(res.result?.items) ? res.result.items : []\n setProjectTasks(\n items\n .map((row): ProjectTaskRow | null => {\n const id = typeof row.id === 'string' ? row.id : null\n const title = typeof row.title === 'string' ? row.title : null\n if (!id || !title) return null\n const raw = row as Record<string, unknown>\n const logged = raw.loggedMinutes ?? raw.logged_minutes\n return {\n id,\n reference: typeof raw.reference === 'string' ? raw.reference : null,\n title,\n statusId:\n typeof (raw.task_status_id ?? raw.taskStatusId) === 'string'\n ? String(raw.task_status_id ?? raw.taskStatusId)\n : null,\n assigneeStaffMemberId:\n typeof (raw.assignee_staff_member_id ?? raw.assigneeStaffMemberId) === 'string'\n ? String(raw.assignee_staff_member_id ?? raw.assigneeStaffMemberId)\n : null,\n loggedMinutes: typeof logged === 'number' ? logged : null,\n }\n })\n .filter((row): row is ProjectTaskRow => row !== null),\n )\n } catch (err) {\n logger.warn('staff.timesheets.projects.detail tasks failed', { err })\n } finally {\n if (!cancelled) setProjectTasksLoading(false)\n }\n })()\n return () => { cancelled = true }\n }, [projectId, activeTab, reloadToken])\n\n const employeeNameById = React.useMemo(\n () => new Map(employees.map((emp) => [emp.staffMemberId, emp.displayName ?? ''])),\n [employees],\n )\n const activeCount = employees.filter((emp) => emp.status === 'active').length\n const inactiveCount = employees.length - activeCount\n\n const recentEntriesColumns = React.useMemo<ColumnDef<ProjectEntryRow>[]>(\n () => [\n {\n accessorKey: 'date',\n header: t('staff.timesheets.projects.detail.colDate', 'Date'),\n cell: ({ row }) => <span className=\"whitespace-nowrap\">{row.original.date}</span>,\n },\n {\n id: 'person',\n header: t('staff.timesheets.projects.detail.colWho', 'Person'),\n cell: ({ row }) => (\n <span className=\"whitespace-nowrap\">\n {employeeNameById.get(row.original.staffMemberId ?? '') ?? '\u2014'}\n </span>\n ),\n },\n {\n accessorKey: 'description',\n header: t('staff.timesheets.projects.detail.colDescription', 'Description'),\n cell: ({ row }) => (\n <span className=\"flex items-center gap-2\">\n <span className=\"truncate\">{row.original.description ?? '\u2014'}</span>\n {row.original.isBillable ? null : (\n <Badge variant=\"neutral\">\n {t('staff.timesheets.projects.detail.nonBillableTag', 'non-billable')}\n </Badge>\n )}\n </span>\n ),\n },\n {\n id: 'hours',\n header: () => (\n <span className=\"block text-right\">\n {t('staff.timesheets.projects.detail.colHours', 'Hours')}\n </span>\n ),\n cell: ({ row }) => (\n <span className=\"block whitespace-nowrap text-right font-mono tabular-nums\">\n {formatHours(row.original.durationMinutes)}\n </span>\n ),\n },\n ],\n [employeeNameById, t],\n )\n\n // --- Load project ---\n React.useEffect(() => {\n if (!projectId) return\n let cancelled = false\n async function loadProject() {\n setLoading(true)\n setError(null)\n setIsNotFound(false)\n try {\n const queryParams = new URLSearchParams({ page: '1', pageSize: '1', ids: projectId! })\n const call = await apiCall<ProjectResponse>(\n `/api/staff/timesheets/time-projects?${queryParams.toString()}`,\n )\n // Screen 17: the route answers 404 with this discriminator for a project\n // the caller is not a member of, without naming it.\n if (call.status === 404 && readDenialReason(call.result) === NO_PROJECT_ACCESS_REASON) {\n if (!cancelled) setAccessDeniedProjectId(projectId ?? null)\n return\n }\n if (!cancelled) setAccessDeniedProjectId(null)\n if (!call.ok) {\n throw new Error(t('staff.timesheets.projects.errors.load', 'Failed to load project.'))\n }\n const payload = call.result\n const record = Array.isArray(payload?.items) ? payload.items[0] : null\n if (!record) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n if (!cancelled) setProject(record)\n } catch (loadError) {\n if (!cancelled) {\n setError(loadError instanceof Error ? loadError.message : t('staff.timesheets.projects.errors.load', 'Failed to load project.'))\n }\n } finally {\n if (!cancelled) {\n setLoading(false)\n setSettledProjectId(projectId ?? null)\n }\n }\n }\n loadProject()\n return () => { cancelled = true }\n }, [projectId, t, scopeVersion])\n\n // --- Load employees with name resolution ---\n const loadEmployees = React.useCallback(async () => {\n if (!projectId || accessDenied) return\n setEmployeesLoading(true)\n try {\n const payload = await readApiResultOrThrow<EmployeesResponse>(\n `/api/staff/timesheets/time-projects/${projectId}/employees?page=1&pageSize=100`,\n undefined,\n { errorMessage: t('staff.timesheets.projects.employees.empty', 'No employees assigned yet.'), fallback: { items: [], total: 0 } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n\n const staffMemberIds = items\n .map((item) => String(item.staff_member_id ?? item.staffMemberId ?? ''))\n .filter((id) => id.length > 0)\n\n let staffMap = new Map<string, StaffMemberRecord>()\n if (staffMemberIds.length > 0) {\n try {\n const staffRes = await apiCall<StaffMembersResponse>(\n `/api/staff/team-members?ids=${staffMemberIds.join(',')}&pageSize=100`,\n )\n if (staffRes.ok) {\n const staffItems = Array.isArray(staffRes.result?.items) ? staffRes.result.items : []\n staffMap = new Map(staffItems.map((member) => [member.id, member]))\n }\n } catch {\n // name resolution failed (e.g. 403 for employees) \u2014 show IDs as fallback\n }\n }\n\n const mapped: EmployeeAssignment[] = items.map((item) => {\n const staffMemberId = String(item.staff_member_id ?? item.staffMemberId ?? '')\n const staff = staffMap.get(staffMemberId)\n return {\n id: String(item.id ?? ''),\n staffMemberId,\n role: typeof item.role === 'string' ? item.role : null,\n status: typeof item.status === 'string' ? item.status : null,\n assignedStartDate: String(item.assigned_start_date ?? item.assignedStartDate ?? ''),\n assignedEndDate: typeof (item.assigned_end_date ?? item.assignedEndDate) === 'string'\n ? String(item.assigned_end_date ?? item.assignedEndDate)\n : null,\n displayName: staff?.display_name ?? staff?.displayName ?? null,\n teamName: staff?.team?.name ?? null,\n }\n })\n\n setEmployees(mapped)\n } catch (loadError) {\n logger.error('staff.timesheets.projects.employees.list', { err: loadError })\n } finally {\n setEmployeesLoading(false)\n }\n }, [projectId, accessDenied, t])\n\n React.useEffect(() => {\n void loadEmployees()\n }, [loadEmployees, reloadToken, scopeVersion])\n\n // --- Toggle card expand/collapse ---\n const toggleCard = React.useCallback((employeeId: string) => {\n setExpandedCards((prev) => {\n const next = new Set(prev)\n if (next.has(employeeId)) next.delete(employeeId)\n else next.add(employeeId)\n return next\n })\n }, [])\n\n const { runMutation, retryLastMutation } = useGuardedMutation<EmployeeMutationContext>({\n contextId: EMPLOYEE_MUTATION_CONTEXT_ID,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const projectInjectionContext = React.useMemo<ProjectDetailInjectionContext>(\n () => ({\n entityId: PROJECT_ENTITY_ID,\n recordId: projectId ?? null,\n projectId: projectId ?? null,\n path: pathname ?? null,\n retryLastMutation,\n }),\n [pathname, projectId, retryLastMutation],\n )\n\n const { widgets: injectedTabWidgets } = useInjectionWidgets<ProjectDetailInjectionContext>(\n extensionPoints.hosts.projectDetailTabs.spotId,\n { context: projectInjectionContext, triggerOnLoad: true },\n )\n\n const employeeMutationContext = React.useCallback(\n (resourceId: string): EmployeeMutationContext => ({\n formId: EMPLOYEE_MUTATION_CONTEXT_ID,\n resourceKind: EMPLOYEE_RESOURCE_KIND,\n resourceId,\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n\n // optimistic-lock-exempt: the employee add/remove calls below mutate the\n // time-project \u2194 staff-member assignment junction (createCrud/deleteCrud of a\n // membership row), not a shared editable aggregate. Junction add/remove is exempt\n // from OSS optimistic locking per the data-integrity contract.\n // --- Remove employee ---\n const handleRemoveEmployee = React.useCallback(async (emp: EmployeeAssignment) => {\n const confirmed = await confirm({\n title: t('staff.timesheets.projects.employees.remove', 'Remove'),\n text: t('staff.timesheets.projects.employees.removeConfirm', 'Remove this employee from the project?'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutation({\n operation: () =>\n deleteCrud(\n `staff/timesheets/time-projects/${projectId}/employees`,\n emp.id,\n { errorMessage: t('staff.timesheets.projects.employees.removeError', 'Failed to remove employee.') },\n ),\n context: employeeMutationContext(emp.id),\n mutationPayload: { id: emp.id },\n })\n flash(t('staff.timesheets.projects.employees.removed', 'Employee removed.'), 'success')\n setReloadToken((token) => token + 1)\n } catch (removeError) {\n if (surfaceRecordConflict(removeError, t)) return\n flash(t('staff.timesheets.projects.employees.removeError', 'Failed to remove employee.'), 'error')\n }\n }, [projectId, confirm, employeeMutationContext, runMutation, t])\n\n // --- Add employee dialog ---\n const openAddDialog = React.useCallback(() => {\n setAddStaffMemberId(null)\n setAddRole('')\n setAddStartDate(new Date().toISOString().slice(0, 10))\n setStaffLookupError(null)\n setAddDialogOpen(true)\n }, [])\n\n // A failed lookup is not an empty roster: swallowing it here left the picker\n // claiming \"No team members found\" while the request had actually errored.\n const staffLookupErrorMessage = t(\n 'staff.timesheets.projects.employees.loadError',\n 'Could not load team members. Check your connection and try again.',\n )\n\n const fetchStaffMembers = React.useCallback(async (query: string) => {\n const params = new URLSearchParams({ search: query, pageSize: '20', isActive: 'true' })\n try {\n const payload = await readApiResultOrThrow<StaffMembersResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: staffLookupErrorMessage, fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setStaffLookupError(null)\n return items.map((member) => ({\n id: member.id,\n title: member.display_name ?? member.displayName ?? member.id,\n subtitle: member.team?.name ?? null,\n }))\n } catch (lookupError) {\n logger.error('staff.timesheets.projects.employees.lookup', { err: lookupError })\n setStaffLookupError(staffLookupErrorMessage)\n throw lookupError\n }\n }, [staffLookupErrorMessage])\n\n const handleAddEmployee = React.useCallback(async () => {\n if (!projectId || !addStaffMemberId || !addStartDate) return\n const payload = {\n staffMemberId: addStaffMemberId,\n timeProjectId: projectId,\n role: addRole.trim() || null,\n assignedStartDate: addStartDate,\n }\n setAddSaving(true)\n try {\n await runMutation({\n operation: () =>\n createCrud(`staff/timesheets/time-projects/${projectId}/employees`, payload, {\n errorMessage: t('staff.timesheets.projects.employees.addError', 'Failed to add employee.'),\n }),\n context: employeeMutationContext(addStaffMemberId),\n mutationPayload: payload,\n })\n flash(t('staff.timesheets.projects.employees.added', 'Employee added.'), 'success')\n setAddDialogOpen(false)\n setReloadToken((token) => token + 1)\n } catch (addError) {\n if (surfaceRecordConflict(addError, t)) return\n flash(addError instanceof Error ? addError.message : t('staff.timesheets.projects.employees.addError', 'Failed to add employee.'), 'error')\n } finally {\n setAddSaving(false)\n }\n }, [projectId, addStaffMemberId, addRole, addStartDate, employeeMutationContext, runMutation, t])\n\n // Screen 5: the team drawer lives behind `?panel=team` so the project form\n // link and the access-request notification (D-6, `&requesterUserId=`) can both\n // deep-link into it. Assignment is a Team-Leader affordance, so the panel only\n // mounts for holders of `staff.timesheets.projects.manage`.\n const teamPanelRequested = searchParams?.get('panel') === 'team'\n const teamDrawerOpen = teamPanelRequested && canManageProjects\n const requesterUserId = searchParams?.get('requesterUserId') ?? null\n\n const openTeamDrawer = React.useCallback(() => {\n router.replace(`${pathname}?panel=team`)\n }, [pathname, router])\n\n const handleTeamDrawerOpenChange = React.useCallback((next: boolean) => {\n if (next) openTeamDrawer()\n else router.replace(pathname ?? BACK_HREF)\n }, [openTeamDrawer, pathname, router])\n\n const handleTeamSaved = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleAddDialogKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n void handleAddEmployee()\n }\n }, [handleAddEmployee])\n\n // --- Render ---\n // Screen 17 \u2014 the caller may not open this project. The guard state names\n // neither the customer nor the project (note 1), and the surrounding shell\n // (sidebar, topbar) is untouched because the caller still has other projects\n // (note 3). It is checked before `loading` and only cleared by a response that\n // grants access, so a background reload (e.g. the organization scope\n // resolving late) keeps the guard mounted instead of resetting a request the\n // user has just sent.\n if (accessDenied) {\n return <NoProjectAccess timeProjectId={projectId} />\n }\n\n if (loading || settledProjectId !== (projectId ?? null)) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.timesheets.projects.loading', 'Loading project...')} />\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('staff.timesheets.projects.errors.notFound', 'Project not found.')}\n backHref={BACK_HREF}\n backLabel={t('staff.timesheets.projects.actions.backToList', 'Back to projects')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !project) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('staff.timesheets.projects.errors.load', 'Failed to load project.')} />\n </PageBody>\n </Page>\n )\n }\n\n const projectStatus = project.status ?? 'active'\n const projectType = project.projectType ?? project.project_type ?? null\n const projectStartDate = project.startDate ?? project.start_date ?? null\n const projectCode = project.code\n\n // Everything below already arrives in the list projection this page reads with\n // `?ids=`; the card used to render five of the ~22 columns and drop the rest,\n // so rate, budget, customer and cost centre were only visible by opening Edit.\n const readString = (...keys: string[]): string | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n if (typeof raw === 'string' && raw.trim().length > 0) return raw.trim()\n if (typeof raw === 'number' && Number.isFinite(raw)) return String(raw)\n }\n return null\n }\n const readNumber = (...keys: string[]): number | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n const parsed = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number(raw) : NaN\n if (Number.isFinite(parsed)) return parsed\n }\n return null\n }\n const readBool = (...keys: string[]): boolean | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n if (typeof raw === 'boolean') return raw\n }\n return null\n }\n\n const enrichment = ((project as Record<string, unknown>)._staff ?? {}) as Record<string, unknown>\n const readEnriched = (key: string): number | null => {\n const raw = enrichment[key]\n return typeof raw === 'number' && Number.isFinite(raw) ? raw : null\n }\n\n const customerSnapshot = ((project as Record<string, unknown>).customerSnapshot\n ?? (project as Record<string, unknown>).customer_snapshot) as Record<string, unknown> | null | undefined\n const customerName =\n (typeof customerSnapshot?.name === 'string' ? customerSnapshot.name : null)\n ?? (typeof enrichment.customerName === 'string' ? enrichment.customerName : null)\n const customerId = readString('customerId', 'customer_id')\n const projectColor = readString('color')\n const costCenter = readString('costCenter', 'cost_center')\n const currencyCode = readString('currencyCode', 'currency_code')\n const hourlyRate = readNumber('hourlyRate', 'hourly_rate')\n const billableByDefault = readBool('billableByDefault', 'billable_by_default')\n const createdAt = readString('createdAt', 'created_at')\n const updatedAt = readString('updatedAt', 'updated_at')\n const entryCount = readNumber('entryCount')\n const currencyLocked = readBool('currencyLocked') ?? false\n const lockedEntryCount = readNumber('lockedEntryCount')\n const totalMinutes = readEnriched('totalMinutes') ?? 0\n const billableMinutes = readEnriched('billableMinutes')\n const cost = readEnriched('cost')\n const nonBillableMinutes = billableMinutes === null ? null : Math.max(0, totalMinutes - billableMinutes)\n const billablePercent =\n billableMinutes === null || totalMinutes <= 0 ? null : Math.round((billableMinutes / totalMinutes) * 100)\n const hoursTrend = Array.isArray(enrichment.hoursTrend)\n ? (enrichment.hoursTrend as unknown[]).filter((value): value is number => typeof value === 'number')\n : []\n\n const budgetKind = readString('budgetKind', 'budget_kind')\n const budgetValue = readNumber('budgetValue', 'budget_value')\n const budgetWarnAt = readNumber('budgetWarnAtPercent', 'budget_warn_at_percent')\n const budgetBurn = computeBudgetBurn({\n kind: budgetKind as 'none' | 'hours' | 'amount' | null,\n budgetValue,\n warnAtPercent: budgetWarnAt,\n totalMinutes,\n cost,\n })\n\n const formatHours = (minutes: number | null): string => {\n if (minutes === null) return '\u2014'\n const sign = minutes < 0 ? '-' : ''\n const abs = Math.abs(minutes)\n return `${sign}${Math.floor(abs / 60)}:${String(abs % 60).padStart(2, '0')}`\n }\n const formatDateTime = (value: string | null): string => {\n if (!value) return '\u2014'\n // The query engine returns `2026-05-24 00:00:00+00` \u2014 a space separator and a\n // two-digit offset, neither of which `Date` accepts, so both need widening\n // before parsing or every timestamp renders raw.\n //\n // The offset is only widened when a clock precedes it: anchoring on the\n // trailing `-24` alone also matches the day of a date-only `2026-05-24`,\n // which turns a valid date into `2026-05-24:00` and renders it raw.\n const normalized = value\n .replace(' ', 'T')\n .replace(/(T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?)([+-]\\d{2})$/, '$1$2:00')\n const parsed = new Date(normalized)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n }\n const budgetUsageLabel = budgetBurn\n ? budgetBurn.kind === 'hours'\n ? t('staff.timesheets.projects.detail.budgetHours', '{percent}% of {value} h', {\n percent: budgetBurn.percent,\n value: budgetBurn.budgetValue,\n })\n : t('staff.timesheets.projects.detail.budgetAmount', '{percent}% of {value}', {\n percent: budgetBurn.percent,\n value: formatCurrency(budgetBurn.budgetValue, currencyCode ?? undefined) ?? String(budgetBurn.budgetValue),\n })\n : null\n\n const injectedTabs = injectedTabWidgets\n .filter((widget) => (widget.placement?.kind ?? 'tab') === 'tab')\n .map((widget) => {\n const tabId = widget.placement?.groupId ?? widget.widgetId\n const groupLabel = widget.placement?.groupLabel\n const label = groupLabel ? t(groupLabel, groupLabel) : widget.module.metadata.title ?? tabId\n const priority = typeof widget.placement?.priority === 'number' ? widget.placement.priority : 0\n return { tabId, label, priority, Widget: widget.module.Widget }\n })\n .sort((left, right) => right.priority - left.priority)\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n {/*\n * Identity first: colour, name, and the two states worth knowing before\n * anything else \u2014 is it running, and is the budget in trouble. The code,\n * customer, type and start date sit under it as one meta line rather than\n * four labelled rows, because none of them is a decision on its own.\n */}\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <Button variant=\"ghost\" size=\"icon\" asChild>\n <Link href={BACK_HREF}>\n <ArrowLeft className=\"h-4 w-4\" aria-hidden />\n </Link>\n </Button>\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span\n className=\"size-3 shrink-0 rounded-sm border border-border\"\n style={{ backgroundColor: resolveProjectColorHex(projectColor, project.name) }}\n aria-hidden\n />\n <h1 className=\"text-xl font-semibold\">{project.name}</h1>\n <Badge\n variant={projectStatus === 'active' ? 'success' : projectStatus === 'on_hold' ? 'warning' : 'neutral'}\n className=\"capitalize\"\n >\n {projectStatus}\n </Badge>\n {canSeeMoney && budgetBurn && budgetBurn.tone !== 'accent' ? (\n <Badge variant={budgetBurn.tone === 'destructive' ? 'destructive' : 'warning'}>\n {t('staff.timesheets.projects.detail.budgetBadge', 'Budget {percent}%', {\n percent: budgetBurn.percent,\n })}\n </Badge>\n ) : null}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailStatusBadges.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <span className=\"rounded-sm border border-border bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground\">\n {projectCode}\n </span>\n {customerName ? (\n <>\n <span aria-hidden>\u00B7</span>\n {customerId ? (\n <Link className=\"underline underline-offset-2\" href={`/backend/customers/companies/${customerId}`}>\n {customerName}\n </Link>\n ) : (\n <span>{customerName}</span>\n )}\n </>\n ) : null}\n {projectType ? (<><span aria-hidden>\u00B7</span><span>{projectType}</span></>) : null}\n {projectStartDate ? (\n <>\n <span aria-hidden>\u00B7</span>\n <span>\n {t('staff.timesheets.projects.detail.startedOn', 'Started {date}', {\n date: formatDateTime(projectStartDate),\n })}\n </span>\n </>\n ) : null}\n </div>\n </div>\n </div>\n {canManageProjects && (\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={openTeamDrawer}>\n <Users className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.time_tracking.projects.team.manage', 'Manage team')}\n </Button>\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link href={`/backend/staff/time-tracking/projects/${projectId}/edit`}>\n <Pencil className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.timesheets.projects.form.actions.edit', 'Edit Project')}\n </Link>\n </Button>\n </div>\n )}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailHeader.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n\n {/*\n * Summary before detail. Four figures across the full width, in the order\n * somebody asks them: how much time, how much of it billable, what it\n * cost, how close the budget is. The three stacked label/value cards this\n * replaces gave all fourteen fields identical weight.\n */}\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-4\">\n <KpiCard\n title={t('staff.timesheets.projects.detail.loggedHours', 'Logged hours')}\n value={totalMinutes / 60}\n formatValue={() => formatHours(totalMinutes)}\n footer={\n hoursTrend.length > 0 ? (\n <Sparkline\n values={hoursTrend}\n ariaLabel={t('staff.timesheets.projects.detail.trendAria', 'Hours per week, last 7 weeks')}\n />\n ) : undefined\n }\n headerAction={\n entryCount !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {entryCount === 1\n ? t('staff.timesheets.projects.detail.entryCountOne', '1 entry')\n : t('staff.timesheets.projects.detail.entryCountShort', '{count} entries', { count: entryCount })}\n </span>\n ) : undefined\n }\n />\n <KpiCard\n title={t('staff.timesheets.projects.detail.billableHours', 'Billable hours')}\n value={(billableMinutes ?? 0) / 60}\n formatValue={() => formatHours(billableMinutes)}\n headerAction={\n billablePercent !== null ? (\n <Badge variant={billablePercent >= 80 ? 'success' : 'neutral'}>{`${billablePercent}%`}</Badge>\n ) : undefined\n }\n footer={\n nonBillableMinutes !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.nonBillable', '{hours} non-billable', {\n hours: formatHours(nonBillableMinutes),\n })}\n </span>\n ) : undefined\n }\n />\n {canSeeMoney ? (\n <KpiCard\n title={t('staff.timesheets.projects.detail.cost', 'Cost to date')}\n value={cost}\n formatValue={(value) => formatCurrency(value, currencyCode ?? undefined) ?? String(value)}\n footer={\n hourlyRate !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.costBasis', '{hours} \u00D7 {rate}', {\n hours: formatHours(billableMinutes),\n rate: `${formatCurrency(hourlyRate, currencyCode ?? undefined) ?? hourlyRate}/h`,\n })}\n </span>\n ) : undefined\n }\n />\n ) : null}\n {canSeeMoney ? (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-sm font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.form.budget', 'Budget')}\n </span>\n {budgetBurn ? (\n <Badge\n variant={\n budgetBurn.tone === 'destructive'\n ? 'destructive'\n : budgetBurn.tone === 'warning'\n ? 'warning'\n : 'neutral'\n }\n >\n {`${budgetBurn.percent}%`}\n </Badge>\n ) : (\n <Badge variant=\"neutral\">{t('staff.timesheets.projects.detail.noBudget', 'No budget')}</Badge>\n )}\n </div>\n <div className=\"mt-2 space-y-2\">\n <p className=\"text-2xl font-semibold tabular-nums\">\n {budgetBurn ? `${budgetBurn.percent}%` : '\u2014'}\n </p>\n <ProjectBudgetCell\n burn={budgetBurn}\n usageLabel={budgetUsageLabel}\n noBudgetLabel={t('staff.timesheets.projects.detail.budgetHint', 'Set one to track burn')}\n ariaLabel={t('staff.timesheets.projects.detail.budgetUsage', 'Budget usage')}\n />\n </div>\n </div>\n ) : null}\n </div>\n\n {/*\n * Working content left, reference right. Everything static \u2014 rate,\n * currency, cost centre, timestamps \u2014 collapses into one rail instead of\n * three full-width cards, which is what left the right half of a wide\n * screen carrying nothing.\n */}\n <div className=\"grid items-start gap-4 lg:grid-cols-[minmax(0,2fr)_minmax(280px,1fr)]\">\n <Tabs\n value={activeTab}\n onValueChange={setActiveTab}\n variant=\"underline\"\n className=\"min-w-0\"\n >\n <TabsList aria-label={t('staff.timesheets.projects.detail.tabs', 'Project sections')}>\n <TabsTrigger value=\"team\" count={activeCount}>\n {t('staff.timesheets.projects.detail.tabTeam', 'Team')}\n </TabsTrigger>\n <TabsTrigger value=\"time\" count={entryCount ?? undefined}>\n {t('staff.timesheets.projects.detail.tabTime', 'Time')}\n </TabsTrigger>\n <TabsTrigger value=\"tasks\">\n {t('staff.timesheets.projects.detail.tabTasks', 'Tasks')}\n </TabsTrigger>\n {injectedTabs.map((tab) => (\n <TabsTrigger key={tab.tabId} value={tab.tabId}>\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n\n <TabsContent value=\"team\">\n {/* Assigned Employees \u2014 collapsible cards */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"sr-only\">\n {t('staff.timesheets.projects.employees.title', 'Assigned Employees')}\n </h2>\n <span />\n {canManageProjects && (\n <Button size=\"sm\" onClick={openAddDialog}>\n <Plus className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.timesheets.projects.add_employee', 'Add Employee')}\n </Button>\n )}\n </div>\n\n {employeesLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.employees.loading', 'Loading employees...')} />\n ) : employees.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <User className=\"mx-auto h-8 w-8 text-muted-foreground/50\" aria-hidden />\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t('staff.timesheets.projects.employees.empty', 'No employees assigned yet.')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {employees.map((emp) => {\n const isExpanded = expandedCards.has(emp.id)\n const isInactive = emp.status !== 'active'\n return (\n <div\n key={emp.id}\n className={`rounded-lg border ${isInactive ? 'opacity-60' : ''}`}\n >\n {/* Collapsed view: name, role, status badge, start date, expand toggle */}\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between p-4 text-left hover:bg-muted/50 transition-colors\"\n onClick={() => toggleCard(emp.id)}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted\">\n <User className=\"h-4 w-4 text-muted-foreground\" aria-hidden />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">\n {emp.displayName ?? emp.staffMemberId}\n </p>\n {emp.role ? (\n <p className=\"text-xs text-muted-foreground truncate\">{emp.role}</p>\n ) : null}\n </div>\n </div>\n <div className=\"flex items-center gap-3 shrink-0\">\n <Badge\n variant={(emp.status ?? 'active') === 'active' ? 'success' : 'neutral'}\n className=\"capitalize\"\n >\n {emp.status ?? 'active'}\n </Badge>\n {emp.assignedStartDate ? (\n <span className=\"text-xs text-muted-foreground hidden sm:inline\">\n {emp.assignedStartDate}\n </span>\n ) : null}\n <ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform ${isExpanded ? 'rotate-180' : ''}`} aria-hidden />\n </div>\n </button>\n\n {/* Expanded view: assignment details */}\n {isExpanded ? (\n <div className=\"border-t px-4 pb-4 pt-3 space-y-3\">\n <dl className=\"grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.role', 'Role')}\n </dt>\n <dd>{emp.role || '-'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.status', 'Status')}\n </dt>\n <dd className=\"capitalize\">{emp.status ?? 'active'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.startDate', 'Assignment start')}\n </dt>\n <dd>{emp.assignedStartDate || '-'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.endDate', 'Assignment end')}\n </dt>\n <dd>{emp.assignedEndDate || '-'}</dd>\n </div>\n {emp.teamName ? (\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.department', 'Department')}\n </dt>\n <dd>{emp.teamName}</dd>\n </div>\n ) : null}\n </dl>\n {canManageProjects && (\n <div className=\"flex justify-end\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"text-destructive hover:text-destructive\"\n onClick={() => { void handleRemoveEmployee(emp) }}\n >\n <Trash2 className=\"mr-2 h-3.5 w-3.5\" aria-hidden />\n {t('staff.timesheets.projects.employees.remove', 'Remove')}\n </Button>\n </div>\n )}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )}\n </div>\n </TabsContent>\n\n <TabsContent value=\"time\">\n {recentEntriesLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.detail.loadingTime', 'Loading time entries\u2026')} />\n ) : recentEntries.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <p className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.detail.noTime', 'No time logged yet')}\n </p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.noTimeHint', 'Hours logged against this project appear here.')}\n </p>\n </div>\n ) : (\n <DataTable<ProjectEntryRow>\n extensionTableId={extensionPoints.hosts.projectDetailTimeEntriesTable.tableId}\n disableRowClick\n columns={recentEntriesColumns}\n data={recentEntries}\n />\n )}\n </TabsContent>\n\n <TabsContent value=\"tasks\">\n {projectTasksLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.detail.loadingTasks', 'Loading tasks\u2026')} />\n ) : projectTasks.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <p className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.detail.noTasks', 'No tasks yet')}\n </p>\n </div>\n ) : (\n <div className=\"divide-y rounded-lg border\">\n {projectTasks.map((task) => (\n <div key={task.id} className=\"flex items-center gap-3 px-3 py-2 text-sm\">\n {task.reference ? (\n <span className=\"shrink-0 rounded-sm border border-border bg-muted px-1.5 py-0.5 font-mono text-xs\">\n {task.reference}\n </span>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{task.title}</span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">\n {employeeNameById.get(task.assigneeStaffMemberId ?? '') ?? ''}\n </span>\n {task.loggedMinutes !== null ? (\n <span className=\"shrink-0 font-mono text-xs tabular-nums text-muted-foreground\">\n {formatHours(task.loggedMinutes)}\n </span>\n ) : null}\n </div>\n ))}\n </div>\n )}\n </TabsContent>\n\n {injectedTabs.map((tab) => (\n <TabsContent key={tab.tabId} value={tab.tabId}>\n <tab.Widget context={projectInjectionContext} data={project} />\n </TabsContent>\n ))}\n </Tabs>\n\n {/* Reference rail \u2014 one card, grouped, no longer three stacked sections. */}\n <div className=\"rounded-lg border bg-card\">\n <div className=\"border-b px-4 py-3\">\n <h2 className=\"text-sm font-semibold\">\n {t('staff.timesheets.projects.detail.details', 'Details')}\n </h2>\n </div>\n <div className=\"px-4 py-3\">\n {canSeeMoney ? (\n <>\n <RailGroup label={t('staff.timesheets.projects.detail.commercials', 'Billing')} first />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.form.hourlyRate', 'Hourly rate')}\n value={\n hourlyRate === null\n ? '\u2014'\n : `${formatCurrency(hourlyRate, currencyCode ?? undefined) ?? hourlyRate} / h`\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.form.currency', 'Currency')}\n value={\n <span className=\"inline-flex items-center gap-1.5\">\n {currencyCode ?? '\u2014'}\n {currencyLocked ? (\n <Badge\n variant=\"neutral\"\n title={t(\n 'staff.timesheets.projects.detail.currencyLockedHint',\n 'Locked because hours are already logged against this project.',\n )}\n >\n {t('staff.timesheets.projects.detail.locked', 'locked')}\n </Badge>\n ) : null}\n </span>\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.form.billableByDefault', 'Billable by default')}\n value={\n billableByDefault === null ? (\n '\u2014'\n ) : (\n <Badge variant={billableByDefault ? 'success' : 'neutral'}>\n {billableByDefault\n ? t('staff.timesheets.projects.detail.yes', 'Yes')\n : t('staff.timesheets.projects.detail.no', 'No')}\n </Badge>\n )\n }\n />\n </dl>\n </>\n ) : null}\n\n <RailGroup\n label={t('staff.timesheets.projects.detail.delivery', 'Delivery')}\n first={!canSeeMoney}\n />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.detail.entryCount', 'Time entries')}\n value={entryCount ?? '\u2014'}\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.lockedEntries', 'Locked in reports')}\n value={lockedEntryCount ?? '\u2014'}\n />\n </dl>\n\n <RailGroup label={t('staff.timesheets.projects.detail.administration', 'Administration')} />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.form.costCenter', 'Cost center')}\n value={costCenter ?? '\u2014'}\n />\n <RailFact\n label={t('staff.timesheets.projects.form.color', 'Project color')}\n value={\n <span className=\"inline-flex items-center gap-1.5 capitalize\">\n <span\n className=\"size-2.5 rounded-full border border-border\"\n style={{ backgroundColor: resolveProjectColorHex(projectColor, project.name) }}\n aria-hidden\n />\n {projectColor ?? t('staff.timesheets.projects.detail.autoColor', 'Auto')}\n </span>\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.createdAt', 'Created')}\n value={formatDateTime(createdAt)}\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.updatedAt', 'Last updated')}\n value={formatDateTime(updatedAt)}\n />\n </dl>\n\n {project.description ? (\n <>\n <RailGroup label={t('staff.timesheets.projects.form.description', 'Description')} />\n <p className=\"whitespace-pre-wrap pt-2 text-sm text-muted-foreground\">{project.description}</p>\n </>\n ) : null}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailSidebar.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n </div>\n </div>\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailFooter.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n\n {ConfirmDialogElement}\n\n {projectId && teamDrawerOpen ? (\n <ProjectTeamDrawer\n projectId={projectId}\n projectName={project.name}\n open={teamDrawerOpen}\n onOpenChange={handleTeamDrawerOpenChange}\n preselectUserId={requesterUserId}\n onSaved={handleTeamSaved}\n />\n ) : null}\n\n {/* Add Employee Dialog */}\n <Dialog open={addDialogOpen} onOpenChange={setAddDialogOpen}>\n <DialogContent className=\"sm:max-w-md\" onKeyDown={handleAddDialogKeyDown}>\n <DialogHeader>\n <DialogTitle>{t('staff.timesheets.projects.add_employee', 'Add Employee')}</DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 pt-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.employees.selectEmployee', 'Select Employee')}\n </label>\n <LookupSelect\n value={addStaffMemberId}\n onChange={setAddStaffMemberId}\n fetchItems={fetchStaffMembers}\n searchPlaceholder={t('staff.timesheets.projects.employees.searchEmployee', 'Search team members...')}\n emptyLabel={t('staff.timesheets.projects.employees.noResults', 'No team members found')}\n />\n {staffLookupError ? (\n <p className=\"text-xs text-status-error-text\" role=\"alert\">\n {staffLookupError}\n </p>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"add-role\">\n {t('staff.timesheets.projects.employees.roleOnProject', 'Role on Project')}\n </label>\n <Input\n id=\"add-role\"\n value={addRole}\n onChange={(event) => setAddRole(event.target.value)}\n placeholder={t('staff.timesheets.projects.employees.rolePlaceholder', 'e.g. Developer, Designer...')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"add-start-date\">\n {t('staff.timesheets.projects.employees.assignmentStartDate', 'Assignment Start Date')}\n </label>\n <Input\n id=\"add-start-date\"\n type=\"date\"\n value={addStartDate}\n onChange={(event) => setAddStartDate(event.target.value)}\n />\n </div>\n <div className=\"flex justify-end gap-2 pt-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setAddDialogOpen(false)}>\n {t('staff.timesheets.projects.form.actions.cancel', 'Cancel')}\n </Button>\n <Button\n size=\"sm\"\n disabled={addSaving || !addStaffMemberId || !addStartDate}\n onClick={handleAddEmployee}\n >\n {addSaving ? <Spinner className=\"size-4\" /> : null}\n {t('staff.timesheets.projects.add_employee', 'Add Employee')}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["params"]
7
7
  }