@dragonmastery/dragoncore-vue 0.0.21 → 0.0.23

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 (245) hide show
  1. package/dist/{AppLink-CHMMrSFI.js → AppLink-FcNGKgvG.js} +1 -1
  2. package/dist/{AppLink-CHMMrSFI.js.map → AppLink-FcNGKgvG.js.map} +1 -1
  3. package/dist/Appearance-Ch4zfWZ3.js +3 -0
  4. package/dist/{Appearance-DxWTyx1M.js → Appearance-shr0Aql0.js} +1 -1
  5. package/dist/{Appearance-DxWTyx1M.js.map → Appearance-shr0Aql0.js.map} +1 -1
  6. package/dist/{ChangePasswordPage--3XwluwE.js → ChangePasswordPage-C633yQiU.js} +2 -2
  7. package/dist/{ChangePasswordPage--3XwluwE.js.map → ChangePasswordPage-C633yQiU.js.map} +1 -1
  8. package/dist/ChangePasswordPage-CYuCCosx.js +7 -0
  9. package/dist/ConfirmDialog-DjthOYU6.js +85 -0
  10. package/dist/ConfirmDialog-DjthOYU6.js.map +1 -0
  11. package/dist/ConsentRequired-BPjsZoPY.js +211 -0
  12. package/dist/ConsentRequired-BPjsZoPY.js.map +1 -0
  13. package/dist/CreateTeamForm-CeaC41VY.js +142 -0
  14. package/dist/CreateTeamForm-CeaC41VY.js.map +1 -0
  15. package/dist/CreateTeamForm-DfgCaUwX.js +12 -0
  16. package/dist/CreateTeamMemberForm-Bv9gNG4z.js +140 -0
  17. package/dist/CreateTeamMemberForm-Bv9gNG4z.js.map +1 -0
  18. package/dist/CreateTeamMemberForm-CnHfpob_.js +12 -0
  19. package/dist/CreateUserPage-C9uOeYDJ.js +7 -0
  20. package/dist/{CreateUserPage-DLwXeLAq.js → CreateUserPage-CqKcY7_X.js} +2 -2
  21. package/dist/{CreateUserPage-DLwXeLAq.js.map → CreateUserPage-CqKcY7_X.js.map} +1 -1
  22. package/dist/CreditBalanceDashboard-0HiJE_OS.js +13 -0
  23. package/dist/CreditBalanceDashboard-k_orNn4h.js +28 -0
  24. package/dist/CreditBalanceDashboard-k_orNn4h.js.map +1 -0
  25. package/dist/CreditManagement-BVBZQDI4.js +356 -0
  26. package/dist/CreditManagement-BVBZQDI4.js.map +1 -0
  27. package/dist/CreditManagement-DXdIN-0d.js +13 -0
  28. package/dist/CreditTransactionHistory-DSu-6aDi.js +229 -0
  29. package/dist/CreditTransactionHistory-DSu-6aDi.js.map +1 -0
  30. package/dist/CustomerCreateSupportTicketForm-BsjX8Pja.js +14 -0
  31. package/dist/CustomerCreateSupportTicketForm-kf8YIGjx.js +158 -0
  32. package/dist/CustomerCreateSupportTicketForm-kf8YIGjx.js.map +1 -0
  33. package/dist/{CustomerSupportTicketDetailPage-BdyaKG1v.js → CustomerSupportTicketDetailPage-C_-YoAaP.js} +12 -9
  34. package/dist/{CustomerSupportTicketDetailPage-BdyaKG1v.js.map → CustomerSupportTicketDetailPage-C_-YoAaP.js.map} +1 -1
  35. package/dist/CustomerSupportTicketList-DdACn3ug.js +63 -0
  36. package/dist/{CustomerSupportTicketParent-HIxwSVdu.js → CustomerSupportTicketParent-BpBuYCrP.js} +4 -4
  37. package/dist/{CustomerSupportTicketParent-HIxwSVdu.js.map → CustomerSupportTicketParent-BpBuYCrP.js.map} +1 -1
  38. package/dist/CustomerSupportTicketParent-Djy7pNqO.js +8 -0
  39. package/dist/CustomerSupportTicketSuccess-DsFzpJFU.js +12 -0
  40. package/dist/CustomerSupportTicketSuccess-cumNSGdx.js +54 -0
  41. package/dist/CustomerSupportTicketSuccess-cumNSGdx.js.map +1 -0
  42. package/dist/EditTeamForm-BE3iX2x3.js +12 -0
  43. package/dist/EditTeamForm-BxRN338L.js +163 -0
  44. package/dist/EditTeamForm-BxRN338L.js.map +1 -0
  45. package/dist/EditTeamMemberForm-D7D1Zddh.js +9 -0
  46. package/dist/{EditTeamMemberForm-CaS2GLjV.js → EditTeamMemberForm-DfgJr5Cy.js} +7 -72
  47. package/dist/EditTeamMemberForm-DfgJr5Cy.js.map +1 -0
  48. package/dist/EditUserPage-C0K7EGjM.js +8 -0
  49. package/dist/{EditUserPage-DURc5rmi.js → EditUserPage-CI_jtU8P.js} +4 -4
  50. package/dist/{EditUserPage-DURc5rmi.js.map → EditUserPage-CI_jtU8P.js.map} +1 -1
  51. package/dist/EnhancedRefreshTokenHandler-C6tZCcfX.js +189 -0
  52. package/dist/EnhancedRefreshTokenHandler-C6tZCcfX.js.map +1 -0
  53. package/dist/FieldsetSection-Cd4B8Ad7.js +27 -0
  54. package/dist/FieldsetSection-Cd4B8Ad7.js.map +1 -0
  55. package/dist/{ForgotPassword-OjIPi9s9.js → ForgotPassword-Ckb9Z-wb.js} +4 -4
  56. package/dist/{ForgotPassword-OjIPi9s9.js.map → ForgotPassword-Ckb9Z-wb.js.map} +1 -1
  57. package/dist/ForgotPassword-tJVSg7PB.js +8 -0
  58. package/dist/{TimelineSystemEvent-BHzFr46C.js → InlineAttachments-DAn_QknY.js} +60 -661
  59. package/dist/InlineAttachments-DAn_QknY.js.map +1 -0
  60. package/dist/{LoginForm-9UFnA-fO.js → LoginForm-CMN2T1fA.js} +5 -5
  61. package/dist/{LoginForm-9UFnA-fO.js.map → LoginForm-CMN2T1fA.js.map} +1 -1
  62. package/dist/LoginForm-QFJ8NHww.js +8 -0
  63. package/dist/{Logout-YgTgOFUH.js → Logout-CFLYHlLr.js} +5 -5
  64. package/dist/{Logout-YgTgOFUH.js.map → Logout-CFLYHlLr.js.map} +1 -1
  65. package/dist/Logout-CjDBff3W.js +8 -0
  66. package/dist/MfaSetup-BZcoxJx-.js +9 -0
  67. package/dist/{MfaSetup-RtFMY_dj.js → MfaSetup-XqoAwBXx.js} +5 -5
  68. package/dist/{MfaSetup-RtFMY_dj.js.map → MfaSetup-XqoAwBXx.js.map} +1 -1
  69. package/dist/{MfaVerify-Cvhe8bEM.js → MfaVerify-C-A75TFZ.js} +6 -6
  70. package/dist/{MfaVerify-Cvhe8bEM.js.map → MfaVerify-C-A75TFZ.js.map} +1 -1
  71. package/dist/MfaVerify-Dy2aV5Gk.js +9 -0
  72. package/dist/{RecordVersionViewer-BWZ78vvE.js → RecordVersionViewer-D2j10HdK.js} +1 -1
  73. package/dist/{RecordVersionViewer-BWZ78vvE.js.map → RecordVersionViewer-D2j10HdK.js.map} +1 -1
  74. package/dist/{ResetPassword-BE4mXK9q.js → ResetPassword-Cd-Yxp8E.js} +4 -4
  75. package/dist/{ResetPassword-BE4mXK9q.js.map → ResetPassword-Cd-Yxp8E.js.map} +1 -1
  76. package/dist/ResetPassword-D6to3G6a.js +8 -0
  77. package/dist/{SavedFiltersPage-DQt6uc8m.js → SavedFiltersPage-DM5DvAFa.js} +62 -34
  78. package/dist/{SavedFiltersPage-DQt6uc8m.js.map → SavedFiltersPage-DM5DvAFa.js.map} +1 -1
  79. package/dist/{Signup-9TjMMnU4.js → Signup-2pqvJiVt.js} +57 -57
  80. package/dist/Signup-2pqvJiVt.js.map +1 -0
  81. package/dist/Signup-XdImA1os.js +9 -0
  82. package/dist/{SignupConsentFlow-QUZGKjdB.js → SignupConsentFlow-X3kXuviv.js} +106 -70
  83. package/dist/SignupConsentFlow-X3kXuviv.js.map +1 -0
  84. package/dist/{SignupRequirementsPage-DfbYmpQD.js → SignupRequirementsPage-Cf-ElkEq.js} +9 -8
  85. package/dist/{SignupRequirementsPage-DfbYmpQD.js.map → SignupRequirementsPage-Cf-ElkEq.js.map} +1 -1
  86. package/dist/StaffCreateSupportTicketForm-BlUP2XXy.js +14 -0
  87. package/dist/StaffCreateSupportTicketForm-D2nn4rTU.js +255 -0
  88. package/dist/StaffCreateSupportTicketForm-D2nn4rTU.js.map +1 -0
  89. package/dist/{StaffSupportTicketDetailPage-DQdfh6H1.js → StaffSupportTicketDetailPage-MFtm06BE.js} +14 -11
  90. package/dist/{StaffSupportTicketDetailPage-DQdfh6H1.js.map → StaffSupportTicketDetailPage-MFtm06BE.js.map} +1 -1
  91. package/dist/StaffSupportTicketList-LfLx0pYP.js +63 -0
  92. package/dist/StaffSupportTicketParent-B7mEN1oD.js +8 -0
  93. package/dist/{StaffSupportTicketParent-CilR4RGM.js → StaffSupportTicketParent-BvPwgOqH.js} +4 -4
  94. package/dist/{StaffSupportTicketParent-CilR4RGM.js.map → StaffSupportTicketParent-BvPwgOqH.js.map} +1 -1
  95. package/dist/StaffSupportTicketSuccess-BMCOP3ko.js +12 -0
  96. package/dist/StaffSupportTicketSuccess-Ca2WrcRg.js +54 -0
  97. package/dist/StaffSupportTicketSuccess-Ca2WrcRg.js.map +1 -0
  98. package/dist/{SupportStaffPage-KKugAnFm.js → SupportStaffPage-B69-kuvg.js} +8 -7
  99. package/dist/{SupportStaffPage-KKugAnFm.js.map → SupportStaffPage-B69-kuvg.js.map} +1 -1
  100. package/dist/{SupportTicketDevLifecycleBadge-EMrQHfyG.js → SupportTicketDevLifecycleBadge-BoAjMb08.js} +1 -1
  101. package/dist/{SupportTicketDevLifecycleBadge-EMrQHfyG.js.map → SupportTicketDevLifecycleBadge-BoAjMb08.js.map} +1 -1
  102. package/dist/{SupportTicketMaintenancePage-smItdkrD.js → SupportTicketMaintenancePage-Bptja-xb.js} +5 -4
  103. package/dist/{SupportTicketMaintenancePage-smItdkrD.js.map → SupportTicketMaintenancePage-Bptja-xb.js.map} +1 -1
  104. package/dist/TeamAttachmentsTab-Dk3LxX3n.js +63 -0
  105. package/dist/TeamHistoryTab-CRONdHcL.js +6 -0
  106. package/dist/{TeamHistoryTab-D5biUPmq.js → TeamHistoryTab-DM8KBEG1.js} +7 -19
  107. package/dist/TeamHistoryTab-DM8KBEG1.js.map +1 -0
  108. package/dist/TeamList-DYm_vQ2z.js +8 -0
  109. package/dist/TeamList-qdwlMuJv.js +141 -0
  110. package/dist/TeamList-qdwlMuJv.js.map +1 -0
  111. package/dist/TeamMemberList-4LRLT_7Z.js +7 -0
  112. package/dist/TeamMemberList-DyI1U1t_.js +166 -0
  113. package/dist/TeamMemberList-DyI1U1t_.js.map +1 -0
  114. package/dist/TeamMemberParent-B63pRfI6.js +10 -0
  115. package/dist/TeamMemberParent-D9Fxu7GD.js +83 -0
  116. package/dist/TeamMemberParent-D9Fxu7GD.js.map +1 -0
  117. package/dist/TeamMembersTab-BGcdyEE8.js +3 -0
  118. package/dist/{TeamMembersTab-4gmnP9sD.js → TeamMembersTab-BigqpBDH.js} +1 -1
  119. package/dist/{TeamMembersTab-4gmnP9sD.js.map → TeamMembersTab-BigqpBDH.js.map} +1 -1
  120. package/dist/{TeamNotesTab-BzGZZ1h8.js → TeamNotesTab-BgxleidZ.js} +6 -5
  121. package/dist/{TeamNotesTab-BzGZZ1h8.js.map → TeamNotesTab-BgxleidZ.js.map} +1 -1
  122. package/dist/TeamNotesTab-o7glfjoY.js +8 -0
  123. package/dist/TeamParent-BwXqA3rj.js +83 -0
  124. package/dist/TeamParent-BwXqA3rj.js.map +1 -0
  125. package/dist/TeamParent-CFOmyKPz.js +11 -0
  126. package/dist/{TimelineNoteInput-0p-M4Qie.js → TimelineNoteInput-DXaodm43.js} +3 -2
  127. package/dist/{TimelineNoteInput-0p-M4Qie.js.map → TimelineNoteInput-DXaodm43.js.map} +1 -1
  128. package/dist/TimelineSystemEvent-zCMUx5Zz.js +525 -0
  129. package/dist/TimelineSystemEvent-zCMUx5Zz.js.map +1 -0
  130. package/dist/UserListPage-Bmwg0an5.js +5 -0
  131. package/dist/{UserListPage-DUE5gJTo.js → UserListPage-DtA8tLff.js} +4 -3
  132. package/dist/{UserListPage-DUE5gJTo.js.map → UserListPage-DtA8tLff.js.map} +1 -1
  133. package/dist/UserProfilePage-DRbCAr9H.js +8 -0
  134. package/dist/{UserProfilePage-C3b93Keh.js → UserProfilePage-g4-VEDXo.js} +4 -4
  135. package/dist/{UserProfilePage-C3b93Keh.js.map → UserProfilePage-g4-VEDXo.js.map} +1 -1
  136. package/dist/{VerifyEmail-DlOmWGG-.js → VerifyEmail-CM5ehFB8.js} +7 -7
  137. package/dist/{VerifyEmail-DlOmWGG-.js.map → VerifyEmail-CM5ehFB8.js.map} +1 -1
  138. package/dist/VerifyEmail-DMHczC9f.js +10 -0
  139. package/dist/ViewTeam-CXyABxE6.js +8 -0
  140. package/dist/ViewTeam-DSbKV60o.js +220 -0
  141. package/dist/ViewTeam-DSbKV60o.js.map +1 -0
  142. package/dist/ViewTeamMember-BB0nvPOe.js +167 -0
  143. package/dist/ViewTeamMember-BB0nvPOe.js.map +1 -0
  144. package/dist/ViewTeamMember-jrOnBaDh.js +7 -0
  145. package/dist/ZiniaContainer-CjVhCnGB.js +18 -0
  146. package/dist/ZiniaContainer-CjVhCnGB.js.map +1 -0
  147. package/dist/{convertToLocalDateTime-CFhtN6PI.js → convertToLocalDateTime-BF25N4xd.js} +1 -2
  148. package/dist/convertToLocalDateTime-BF25N4xd.js.map +1 -0
  149. package/dist/customerSupportTicketRoutes-C7OxGAGl.js +142 -0
  150. package/dist/customerSupportTicketRoutes-C7OxGAGl.js.map +1 -0
  151. package/dist/{displayIdFormatter-Dz900Awr.js → displayIdFormatter-B-_WQHOr.js} +1 -1
  152. package/dist/{displayIdFormatter-Dz900Awr.js.map → displayIdFormatter-B-_WQHOr.js.map} +1 -1
  153. package/dist/{extractRpcErrorMessage-Di8E8-Wh.js → extractRpcErrorMessage-diUBl6Ij.js} +1 -1
  154. package/dist/{extractRpcErrorMessage-Di8E8-Wh.js.map → extractRpcErrorMessage-diUBl6Ij.js.map} +1 -1
  155. package/dist/index.d.ts +793 -834
  156. package/dist/index.js +66 -38
  157. package/dist/{mfaSchema-BnRWf0ma.js → mfaSchema-Ukqzdyck.js} +1 -1
  158. package/dist/{mfaSchema-BnRWf0ma.js.map → mfaSchema-Ukqzdyck.js.map} +1 -1
  159. package/dist/saved_filter-CfzH0BzK.js +1210 -0
  160. package/dist/saved_filter-CfzH0BzK.js.map +1 -0
  161. package/dist/signupConsentStorage-DS9vCUuC.js +27 -0
  162. package/dist/signupConsentStorage-DS9vCUuC.js.map +1 -0
  163. package/dist/{src-CEBiyg_f.css → src-C6ZmNSSU.css} +1 -1
  164. package/dist/{src-CEBiyg_f.css.map → src-C6ZmNSSU.css.map} +1 -1
  165. package/dist/src-DVe_0RO9.js +4654 -0
  166. package/dist/src-DVe_0RO9.js.map +1 -0
  167. package/dist/staffSupportTicketRoutes-CWutoQWp.js +135 -0
  168. package/dist/staffSupportTicketRoutes-CWutoQWp.js.map +1 -0
  169. package/dist/teamMemberMetadata-CQnbVepq.js +49 -0
  170. package/dist/teamMemberMetadata-CQnbVepq.js.map +1 -0
  171. package/dist/teamMetadata-DlvwO5V0.js +53 -0
  172. package/dist/teamMetadata-DlvwO5V0.js.map +1 -0
  173. package/dist/teamRoutes-KFgnsdDP.js +192 -0
  174. package/dist/teamRoutes-KFgnsdDP.js.map +1 -0
  175. package/dist/team_memberRoutes-Cjpw_ql6.js +84 -0
  176. package/dist/team_memberRoutes-Cjpw_ql6.js.map +1 -0
  177. package/dist/{useBreadcrumbs-qB6ghsAf.js → useBreadcrumbs-DIqU5AAp.js} +1 -1
  178. package/dist/{useBreadcrumbs-qB6ghsAf.js.map → useBreadcrumbs-DIqU5AAp.js.map} +1 -1
  179. package/dist/{useEmailVerificationChannel-BNi926Ho.js → useEmailVerificationChannel-B51z65PN.js} +3 -3
  180. package/dist/{useEmailVerificationChannel-BNi926Ho.js.map → useEmailVerificationChannel-B51z65PN.js.map} +1 -1
  181. package/dist/{useMutation-BTsyHKyn.js → useMutation-BLNuJoYl.js} +6 -3
  182. package/dist/useMutation-BLNuJoYl.js.map +1 -0
  183. package/dist/{useQuery-BggIE52P.js → useQuery-BzUGEOj0.js} +4 -3
  184. package/dist/{useQuery-BggIE52P.js.map → useQuery-BzUGEOj0.js.map} +1 -1
  185. package/dist/{useQueryCache-Bjm-S8v5.js → useQueryCache-alzaRWEb.js} +2 -2
  186. package/dist/{useQueryCache-Bjm-S8v5.js.map → useQueryCache-alzaRWEb.js.map} +1 -1
  187. package/dist/{useReturnUrl-qFeazn-G.js → useReturnUrl-B5V3SJf5.js} +1 -1
  188. package/dist/{useReturnUrl-qFeazn-G.js.map → useReturnUrl-B5V3SJf5.js.map} +1 -1
  189. package/dist/{useRpcAuth-rmHf7bYx.js → useRpcAuth-CJtq1dqM.js} +25 -194
  190. package/dist/useRpcAuth-CJtq1dqM.js.map +1 -0
  191. package/dist/userAuthorized-C09FHWGL.js +185 -0
  192. package/dist/userAuthorized-C09FHWGL.js.map +1 -0
  193. package/package.json +3 -3
  194. package/dist/Appearance-D5pwxuf4.js +0 -3
  195. package/dist/ChangePasswordPage-CpDPmEml.js +0 -6
  196. package/dist/ConsentRequired-C4IRMA0c.js +0 -213
  197. package/dist/ConsentRequired-C4IRMA0c.js.map +0 -1
  198. package/dist/CreateTeamForm-B4cIuYAf.js +0 -35
  199. package/dist/CreateTeamMemberForm-Chrw1y00.js +0 -35
  200. package/dist/CreateUserPage-WruMs7WP.js +0 -6
  201. package/dist/CreditBalanceDashboard-CkcsrZ_e.js +0 -35
  202. package/dist/CreditManagement-Ddvu9dMw.js +0 -35
  203. package/dist/CustomerCreateSupportTicketForm-BKperKGS.js +0 -35
  204. package/dist/CustomerSupportTicketList-DcbrjDa9.js +0 -35
  205. package/dist/CustomerSupportTicketParent-BeNzUwuP.js +0 -7
  206. package/dist/CustomerSupportTicketSuccess-CC967u3y.js +0 -35
  207. package/dist/EditTeamForm-B5Tee5wL.js +0 -35
  208. package/dist/EditTeamMemberForm-CaS2GLjV.js.map +0 -1
  209. package/dist/EditTeamMemberForm-OtcS8QWt.js +0 -6
  210. package/dist/EditUserPage-T4DQlKhf.js +0 -7
  211. package/dist/ForgotPassword-CUifhmqP.js +0 -7
  212. package/dist/LoginForm-Bg7GoZEA.js +0 -7
  213. package/dist/Logout-Bs92csWH.js +0 -7
  214. package/dist/MfaSetup-BACX5XP-.js +0 -8
  215. package/dist/MfaVerify-ak4iSdQ2.js +0 -8
  216. package/dist/ResetPassword-pY1uhTdl.js +0 -7
  217. package/dist/Signup-9TjMMnU4.js.map +0 -1
  218. package/dist/Signup-Bq-G3D-s.js +0 -9
  219. package/dist/SignupConsentFlow-QUZGKjdB.js.map +0 -1
  220. package/dist/StaffCreateSupportTicketForm-D0ZuisDk.js +0 -35
  221. package/dist/StaffSupportTicketList-CiqC05XB.js +0 -35
  222. package/dist/StaffSupportTicketParent-DkV329NI.js +0 -7
  223. package/dist/StaffSupportTicketSuccess-CUYnimaI.js +0 -35
  224. package/dist/TeamAttachmentsTab-DUtCD1Yi.js +0 -35
  225. package/dist/TeamHistoryTab-BsUoH4VK.js +0 -4
  226. package/dist/TeamHistoryTab-D5biUPmq.js.map +0 -1
  227. package/dist/TeamList-BkPIqZ8V.js +0 -35
  228. package/dist/TeamMemberList-1mxUGCNa.js +0 -35
  229. package/dist/TeamMemberParent-DzeBIElY.js +0 -35
  230. package/dist/TeamMembersTab-CBB2Yl_I.js +0 -3
  231. package/dist/TeamNotesTab-ClHl2nXd.js +0 -7
  232. package/dist/TeamParent-DJa9UZTP.js +0 -35
  233. package/dist/TimelineSystemEvent-BHzFr46C.js.map +0 -1
  234. package/dist/UserListPage-BTLE4J0s.js +0 -4
  235. package/dist/UserProfilePage-CVTORtSx.js +0 -7
  236. package/dist/VerifyEmail-DCP4DWIw.js +0 -9
  237. package/dist/ViewTeam-DVfnLMhV.js +0 -35
  238. package/dist/ViewTeamMember-L4v3gCIn.js +0 -35
  239. package/dist/convertToLocalDateTime-CFhtN6PI.js.map +0 -1
  240. package/dist/src-QZJyMfGX.js +0 -8951
  241. package/dist/src-QZJyMfGX.js.map +0 -1
  242. package/dist/useMutation-BTsyHKyn.js.map +0 -1
  243. package/dist/useRpcAuth-rmHf7bYx.js.map +0 -1
  244. package/dist/useSignupPendingData-BWHwUHhL.js +0 -47
  245. package/dist/useSignupPendingData-BWHwUHhL.js.map +0 -1
@@ -0,0 +1,4654 @@
1
+ import { d as useEnv, i as setRouter, o as useUserSessionStore, r as executeWithAuth } from "./useRpcAuth-CJtq1dqM.js";
2
+ import { i as setRefreshTokenHandler, r as getRefreshTokenHandler } from "./EnhancedRefreshTokenHandler-C6tZCcfX.js";
3
+ import { t as useMutation } from "./useMutation-BLNuJoYl.js";
4
+ import { t as useQuery } from "./useQuery-BzUGEOj0.js";
5
+ import { t as AppLink_default } from "./AppLink-FcNGKgvG.js";
6
+ import { a as useSavedFilters, c as buildQueryWithFilters, f as PINNED_PRESETS_KEY, m as usePinnedPresets, p as useInjectedPinnedPresets, t as SavedFilterPresets_default, u as extractFiltersFromQuery } from "./saved_filter-CfzH0BzK.js";
7
+ import { t as ConfirmDialog_default } from "./ConfirmDialog-DjthOYU6.js";
8
+ import { n as export_helper_default } from "./TeamMembersTab-BigqpBDH.js";
9
+ import { n as getBackLinkFromRoute, o as withReturnUrl, r as getValidReturnUrl } from "./useReturnUrl-B5V3SJf5.js";
10
+ import { t as BREADCRUMB_KEY } from "./useBreadcrumbs-DIqU5AAp.js";
11
+ import { t as ZiniaContainer_default } from "./ZiniaContainer-CjVhCnGB.js";
12
+ import { i as formatUserDate, t as formatSystemTimestamp } from "./convertToLocalDateTime-BF25N4xd.js";
13
+ import { t as extractRpcErrorMessage } from "./extractRpcErrorMessage-diUBl6Ij.js";
14
+ import { c as userAuthenticated, l as userIsSuperAdmin, o as leadOrStaffOnly } from "./userAuthorized-C09FHWGL.js";
15
+ import { t as staffSupportPaths } from "./staffSupportTicketRoutes-CWutoQWp.js";
16
+ import { a as SupportTicketTypeBadge_default, c as formatStaffCreditValue, i as SupportTicketApprovalBadge_default, o as SupportTicketPriorityBadge_default, s as formatCustomerCreditValue } from "./TimelineSystemEvent-zCMUx5Zz.js";
17
+ import { t as formatTicketDisplayId } from "./displayIdFormatter-B-_WQHOr.js";
18
+ import { t as SupportTicketDevLifecycleBadge_default } from "./SupportTicketDevLifecycleBadge-BoAjMb08.js";
19
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, isRef, nextTick, normalizeClass, onMounted, openBlock, provide, readonly, ref, renderList, renderSlot, resolveComponent, toDisplayString, unref, vModelCheckbox, vModelText, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
20
+ import { RouterView, useRoute, useRouter } from "vue-router";
21
+ import { toast } from "vue3-toastify";
22
+ import { CompleteSupportTicketSchema, CustomerSupportTicketReadSchema, OPERATORS, SUPPORT_TICKET_APPROVAL_FILTER_OPTIONS, SUPPORT_TICKET_DEV_LIFECYCLE_FILTER_OPTIONS, SUPPORT_TICKET_PRIORITY_FILTER_OPTIONS, SUPPORT_TICKET_STATUS_FILTER_OPTIONS, SUPPORT_TICKET_TYPE_FILTER_OPTIONS, StaffSupportTicketFiltersSchema, StaffSupportTicketReadSchema, SupportTicketDevLifecycleUpdateEnum, TeamFiltersSchema, TeamMemberFiltersSchema, supportTicketPriorityToNumber } from "@dragonmastery/dragoncore-shared";
23
+ import { ActionIcons, useCursorDataTable, useDataTableUrlSync, useForm, withMetadata } from "@dragonmastery/zinia-forms-core";
24
+
25
+ //#region src/plugin.ts
26
+ /**
27
+ * Dragoncore Vue Plugin
28
+ *
29
+ * Provides RPC authentication and data fetching capabilities to your Vue app.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { createApp } from 'vue';
34
+ * import { DragoncoreVue } from '@dragonmastery/dragoncore-vue';
35
+ * import router from './router';
36
+ * import { createRefreshTokenHandler } from '@dragonmastery/dragoncore-vue';
37
+ * import type { AppApi } from '@follow-zap/shared';
38
+ *
39
+ * const app = createApp(App);
40
+ * const refreshTokenHandler = createRefreshTokenHandler<AppApi>();
41
+ *
42
+ * app.use(DragoncoreVue, {
43
+ * router,
44
+ * refreshTokenHandler,
45
+ * });
46
+ * ```
47
+ */
48
+ const DragoncoreVue = { install(_app, options) {
49
+ if (!options.router) throw new Error("DragoncoreVue plugin: router is required");
50
+ if (!options.refreshTokenHandler) throw new Error("DragoncoreVue plugin: refreshTokenHandler is required");
51
+ setRouter(options.router);
52
+ setRefreshTokenHandler(options.refreshTokenHandler);
53
+ } };
54
+
55
+ //#endregion
56
+ //#region src/components/AppTabNavigation.vue
57
+ const _hoisted_1$29 = { class: "w-full" };
58
+ const _hoisted_2$26 = { class: "md:hidden relative" };
59
+ const _hoisted_3$26 = { class: "font-medium" };
60
+ const _hoisted_4$21 = {
61
+ key: 0,
62
+ class: "absolute z-20 w-full bg-base-100 shadow-lg"
63
+ };
64
+ const _hoisted_5$17 = ["onClick"];
65
+ const _hoisted_6$15 = { class: "hidden md:flex items-center overflow-x-auto" };
66
+ const _hoisted_7$13 = { class: "flex-1 flex" };
67
+ const _hoisted_8$13 = ["onClick"];
68
+ const _hoisted_9$12 = { class: "ml-auto shrink-0" };
69
+ const _sfc_main$34 = /* @__PURE__ */ defineComponent({
70
+ __name: "AppTabNavigation",
71
+ props: {
72
+ tabs: {},
73
+ activeTab: { default: "" }
74
+ },
75
+ emits: ["tab-click"],
76
+ setup(__props, { emit: __emit }) {
77
+ const props = __props;
78
+ const emit = __emit;
79
+ const route = useRoute();
80
+ const isMenuOpen = ref(false);
81
+ const handleTabClick = (tabId) => {
82
+ emit("tab-click", tabId);
83
+ isMenuOpen.value = false;
84
+ };
85
+ const activeTabLabel = computed(() => {
86
+ return props.tabs.find((tab) => {
87
+ if (tab.to && isActiveTab(tab)) return true;
88
+ return tab.id === props.activeTab;
89
+ })?.label || "Navigation";
90
+ });
91
+ const isActiveTab = (tab) => {
92
+ if (props.activeTab && tab.id === props.activeTab) return true;
93
+ if (tab.to) {
94
+ if (route.meta.currentTab && tab.id === route.meta.currentTab) return true;
95
+ if (tab.to.name && route.name === tab.to.name) {
96
+ if (tab.to.params) {
97
+ const toParams = tab.to.params;
98
+ const routeParams = route.params;
99
+ for (const key in toParams) if (routeParams[key] !== toParams[key]) return false;
100
+ return Object.keys(toParams).length > 0;
101
+ }
102
+ return true;
103
+ }
104
+ if (tab.to.path && route.path === tab.to.path) return true;
105
+ }
106
+ return false;
107
+ };
108
+ return (_ctx, _cache) => {
109
+ const _component_router_link = resolveComponent("router-link");
110
+ return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" Mobile-first tab navigation with responsive grid layout "), createElementVNode("div", _hoisted_1$29, [
111
+ createCommentVNode(" Mobile view: Show dropdown on small screens "),
112
+ createElementVNode("div", _hoisted_2$26, [
113
+ createElementVNode("button", {
114
+ onClick: _cache[0] || (_cache[0] = ($event) => isMenuOpen.value = !isMenuOpen.value),
115
+ class: "w-full flex items-center justify-between px-4 py-3 bg-base-100"
116
+ }, [createElementVNode("span", _hoisted_3$26, toDisplayString(activeTabLabel.value), 1), _cache[2] || (_cache[2] = createElementVNode("svg", {
117
+ xmlns: "http://www.w3.org/2000/svg",
118
+ class: "h-5 w-5",
119
+ fill: "none",
120
+ viewBox: "0 0 24 24",
121
+ stroke: "currentColor"
122
+ }, [createElementVNode("path", {
123
+ "stroke-linecap": "round",
124
+ "stroke-linejoin": "round",
125
+ "stroke-width": "2",
126
+ d: "M19 9l-7 7-7-7"
127
+ })], -1))]),
128
+ createCommentVNode(" Dropdown menu "),
129
+ isMenuOpen.value ? (openBlock(), createElementBlock("div", _hoisted_4$21, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.tabs, (tab, index) => {
130
+ return openBlock(), createElementBlock(Fragment, { key: index }, [tab.to ? (openBlock(), createBlock(_component_router_link, {
131
+ key: 0,
132
+ to: tab.to,
133
+ class: normalizeClass(["block px-4 py-3 hover:bg-base-200", [isActiveTab(tab) ? "text-primary font-medium" : "text-base-content/70"]]),
134
+ onClick: _cache[1] || (_cache[1] = ($event) => isMenuOpen.value = false)
135
+ }, {
136
+ default: withCtx(() => [createTextVNode(toDisplayString(tab.label), 1)]),
137
+ _: 2
138
+ }, 1032, ["to", "class"])) : (openBlock(), createElementBlock("button", {
139
+ key: 1,
140
+ onClick: ($event) => handleTabClick(tab.id),
141
+ class: normalizeClass(["block w-full text-left px-4 py-3 hover:bg-base-200", [__props.activeTab === tab.id ? "text-primary font-medium" : "text-base-content/70"]])
142
+ }, toDisplayString(tab.label), 11, _hoisted_5$17))], 64);
143
+ }), 128))])) : createCommentVNode("v-if", true)
144
+ ]),
145
+ createCommentVNode(" Desktop view: Show horizontal tabs on medium screens and up "),
146
+ createElementVNode("div", _hoisted_6$15, [
147
+ createElementVNode("div", _hoisted_7$13, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.tabs, (tab, index) => {
148
+ return openBlock(), createElementBlock(Fragment, { key: index }, [tab.to ? (openBlock(), createBlock(_component_router_link, {
149
+ key: 0,
150
+ to: tab.to,
151
+ class: normalizeClass(["px-4 py-3 border-b-2 whitespace-nowrap flex items-center", [isActiveTab(tab) ? "font-medium text-primary border-primary" : "border-transparent hover:border-base-300 text-base-content/70 hover:text-base-content"]])
152
+ }, {
153
+ default: withCtx(() => [renderSlot(_ctx.$slots, `icon-${tab.id}`), createElementVNode("span", null, toDisplayString(tab.label), 1)]),
154
+ _: 2
155
+ }, 1032, ["to", "class"])) : (openBlock(), createElementBlock("button", {
156
+ key: 1,
157
+ onClick: ($event) => handleTabClick(tab.id),
158
+ class: normalizeClass(["px-4 py-3 border-b-2 whitespace-nowrap flex items-center", [__props.activeTab === tab.id ? "font-medium text-primary border-primary" : "border-transparent hover:border-base-300 text-base-content/70 hover:text-base-content"]])
159
+ }, [renderSlot(_ctx.$slots, `icon-${tab.id}`), createElementVNode("span", null, toDisplayString(tab.label), 1)], 10, _hoisted_8$13))], 64);
160
+ }), 128))]),
161
+ createCommentVNode(" Optional slot for additional content "),
162
+ createElementVNode("div", _hoisted_9$12, [renderSlot(_ctx.$slots, "actions")])
163
+ ])
164
+ ])], 2112);
165
+ };
166
+ }
167
+ });
168
+ var AppTabNavigation_default = _sfc_main$34;
169
+
170
+ //#endregion
171
+ //#region src/components/AppHeader.vue
172
+ const _hoisted_1$28 = { class: "bg-base-100 border-b border-base-300 z-10 w-full" };
173
+ const _hoisted_2$25 = {
174
+ key: 0,
175
+ class: "w-full"
176
+ };
177
+ const _hoisted_3$25 = { class: "max-w-7xl mx-auto px-3 sm:px-4 md:px-6" };
178
+ const _sfc_main$33 = /* @__PURE__ */ defineComponent({
179
+ __name: "AppHeader",
180
+ props: {
181
+ tabs: {},
182
+ activeTab: { default: "" }
183
+ },
184
+ emits: ["tab-click"],
185
+ setup(__props) {
186
+ return (_ctx, _cache) => {
187
+ return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" Full-width background with constrained content "), createElementVNode("div", _hoisted_1$28, [
188
+ createCommentVNode(" Tab navigation - only shown when tabs are provided "),
189
+ __props.tabs.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_2$25, [createElementVNode("div", _hoisted_3$25, [createVNode(AppTabNavigation_default, {
190
+ tabs: __props.tabs,
191
+ "active-tab": __props.activeTab,
192
+ onTabClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("tab-click", $event))
193
+ }, createSlots({
194
+ actions: withCtx(() => [renderSlot(_ctx.$slots, "actions")]),
195
+ _: 2
196
+ }, [renderList(__props.tabs, (tab) => {
197
+ return {
198
+ name: `icon-${tab.id}`,
199
+ fn: withCtx(() => [renderSlot(_ctx.$slots, `icon-${tab.id}`)])
200
+ };
201
+ })]), 1032, ["tabs", "active-tab"])])])) : createCommentVNode("v-if", true),
202
+ createCommentVNode(" Status badge slot (moved from breadcrumbs) "),
203
+ renderSlot(_ctx.$slots, "status")
204
+ ])], 2112);
205
+ };
206
+ }
207
+ });
208
+ var AppHeader_default = _sfc_main$33;
209
+
210
+ //#endregion
211
+ //#region src/components/InputModal.vue
212
+ const _hoisted_1$27 = { class: "modal-box" };
213
+ const _hoisted_2$24 = { class: "font-bold text-lg" };
214
+ const _hoisted_3$24 = { class: "py-4" };
215
+ const _hoisted_4$20 = { class: "form-control w-full" };
216
+ const _hoisted_5$16 = { class: "label" };
217
+ const _hoisted_6$14 = { class: "label-text" };
218
+ const _hoisted_7$12 = ["placeholder"];
219
+ const _hoisted_8$12 = {
220
+ key: 0,
221
+ class: "label"
222
+ };
223
+ const _hoisted_9$11 = { class: "label-text-alt text-error" };
224
+ const _hoisted_10$8 = { class: "modal-action" };
225
+ const _hoisted_11$8 = ["disabled"];
226
+ const _hoisted_12$7 = ["disabled"];
227
+ const _sfc_main$32 = /* @__PURE__ */ defineComponent({
228
+ __name: "InputModal",
229
+ props: {
230
+ modelValue: { type: Boolean },
231
+ title: { default: "Input" },
232
+ label: { default: "Value" },
233
+ placeholder: { default: "Enter value" },
234
+ confirmText: { default: "Confirm" },
235
+ cancelText: { default: "Cancel" },
236
+ processingText: { default: "Processing..." },
237
+ confirmButtonClass: { default: "btn-primary" },
238
+ isProcessing: {
239
+ type: Boolean,
240
+ default: false
241
+ },
242
+ error: { default: "" },
243
+ initialValue: { default: "" }
244
+ },
245
+ emits: [
246
+ "update:modelValue",
247
+ "confirm",
248
+ "cancel"
249
+ ],
250
+ setup(__props, { emit: __emit }) {
251
+ const props = __props;
252
+ const emit = __emit;
253
+ const inputValue = ref(props.initialValue);
254
+ const inputRef = ref(null);
255
+ watch(() => props.modelValue, (newValue) => {
256
+ if (newValue) {
257
+ inputValue.value = props.initialValue || "";
258
+ nextTick(() => {
259
+ inputRef.value?.focus();
260
+ inputRef.value?.select();
261
+ });
262
+ }
263
+ });
264
+ watch(() => props.initialValue, (newValue) => {
265
+ if (props.modelValue) inputValue.value = newValue || "";
266
+ });
267
+ const handleConfirm = () => {
268
+ const value = inputValue.value.trim();
269
+ if (value) emit("confirm", value);
270
+ };
271
+ const handleCancel = () => {
272
+ emit("update:modelValue", false);
273
+ emit("cancel");
274
+ inputValue.value = "";
275
+ };
276
+ return (_ctx, _cache) => {
277
+ return openBlock(), createElementBlock("div", { class: normalizeClass(["modal", { "modal-open": __props.modelValue }]) }, [createElementVNode("div", _hoisted_1$27, [
278
+ createElementVNode("h3", _hoisted_2$24, toDisplayString(__props.title), 1),
279
+ createElementVNode("div", _hoisted_3$24, [createElementVNode("label", _hoisted_4$20, [
280
+ createElementVNode("div", _hoisted_5$16, [createElementVNode("span", _hoisted_6$14, toDisplayString(__props.label), 1)]),
281
+ withDirectives(createElementVNode("input", {
282
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => inputValue.value = $event),
283
+ type: "text",
284
+ placeholder: __props.placeholder,
285
+ class: "input input-bordered w-full",
286
+ onKeyup: [withKeys(handleConfirm, ["enter"]), withKeys(handleCancel, ["escape"])],
287
+ ref_key: "inputRef",
288
+ ref: inputRef
289
+ }, null, 40, _hoisted_7$12), [[vModelText, inputValue.value]]),
290
+ __props.error ? (openBlock(), createElementBlock("div", _hoisted_8$12, [createElementVNode("span", _hoisted_9$11, toDisplayString(__props.error), 1)])) : createCommentVNode("v-if", true)
291
+ ])]),
292
+ createElementVNode("div", _hoisted_10$8, [createElementVNode("button", {
293
+ class: "btn",
294
+ onClick: handleCancel,
295
+ disabled: __props.isProcessing
296
+ }, toDisplayString(__props.cancelText), 9, _hoisted_11$8), createElementVNode("button", {
297
+ class: normalizeClass(["btn", __props.confirmButtonClass]),
298
+ onClick: handleConfirm,
299
+ disabled: !inputValue.value || inputValue.value.trim() === "" || __props.isProcessing
300
+ }, toDisplayString(__props.isProcessing ? __props.processingText : __props.confirmText), 11, _hoisted_12$7)])
301
+ ]), createElementVNode("div", {
302
+ class: "modal-backdrop",
303
+ onClick: handleCancel
304
+ })], 2);
305
+ };
306
+ }
307
+ });
308
+ var InputModal_default = _sfc_main$32;
309
+
310
+ //#endregion
311
+ //#region src/components/LoginButton.vue
312
+ const _sfc_main$31 = {};
313
+ function _sfc_render(_ctx, _cache) {
314
+ const _component_router_link = resolveComponent("router-link");
315
+ return openBlock(), createBlock(_component_router_link, { to: "/auth/login" }, {
316
+ default: withCtx(() => [..._cache[0] || (_cache[0] = [createElementVNode("button", { class: "btn-primary btn" }, "Login", -1)])]),
317
+ _: 1
318
+ });
319
+ }
320
+ var LoginButton_default = /* @__PURE__ */ export_helper_default(_sfc_main$31, [["render", _sfc_render]]);
321
+
322
+ //#endregion
323
+ //#region src/components/ui/LogoPlaceholder.vue
324
+ const _sfc_main$30 = /* @__PURE__ */ defineComponent({
325
+ __name: "LogoPlaceholder",
326
+ props: { size: { default: "md" } },
327
+ setup(__props) {
328
+ return (_ctx, _cache) => {
329
+ return openBlock(), createElementBlock("div", { class: normalizeClass(["flex items-center justify-center rounded-lg bg-base-300 text-base-content/50", __props.size === "xs" ? "h-10 w-10 md:h-12 md:w-12 text-xs" : __props.size === "sm" ? "h-12 w-24 text-xs" : "h-24 w-32 text-sm"]) }, [..._cache[0] || (_cache[0] = [createElementVNode("span", { class: "font-medium" }, "Logo", -1)])], 2);
330
+ };
331
+ }
332
+ });
333
+ var LogoPlaceholder_default = _sfc_main$30;
334
+
335
+ //#endregion
336
+ //#region src/components/Navbar.vue
337
+ const _hoisted_1$26 = { class: "sticky top-0 flex h-16 w-full justify-center border-b-2 border-solid border-b-base-300 bg-base-200 bg-opacity-90 text-base-content shadow-xs z-10" };
338
+ const _hoisted_2$23 = { class: "navbar w-full" };
339
+ const _hoisted_3$23 = { class: "flex flex-1 md:gap-1 lg:gap-2" };
340
+ const _hoisted_4$19 = { class: "items-center gap-2 flex" };
341
+ const _hoisted_5$15 = { class: "font-title inline-flex text-lg text-primary transition-all duration-200 md:text-3xl" };
342
+ const _hoisted_6$13 = ["src"];
343
+ const _hoisted_7$11 = {
344
+ key: 0,
345
+ class: "hidden md:flex items-center ml-4"
346
+ };
347
+ const _hoisted_8$11 = { class: "text-sm text-base-content/70 flex items-center gap-2" };
348
+ const _hoisted_9$10 = {
349
+ key: 0,
350
+ class: "loading loading-spinner loading-xs"
351
+ };
352
+ const _hoisted_10$7 = { class: "font-medium" };
353
+ const _hoisted_11$7 = {
354
+ key: 2,
355
+ class: "text-base-content/50"
356
+ };
357
+ const _hoisted_12$6 = {
358
+ key: 1,
359
+ class: "flex md:hidden flex-col justify-center ml-2 leading-tight"
360
+ };
361
+ const _hoisted_13$5 = { class: "font-medium text-sm truncate max-w-[150px]" };
362
+ const _hoisted_14$4 = {
363
+ key: 0,
364
+ class: "loading loading-spinner loading-xs"
365
+ };
366
+ const _hoisted_15$3 = { class: "flex-none gap-2" };
367
+ const _hoisted_16$3 = {
368
+ key: 0,
369
+ class: "loading btn-primary btn"
370
+ };
371
+ const _hoisted_17$2 = {
372
+ key: 1,
373
+ for: "my-drawer-4",
374
+ tabindex: "0",
375
+ class: "btn-ghost btn-circle avatar btn placeholder",
376
+ id: "right-nav-label"
377
+ };
378
+ const _sfc_main$29 = /* @__PURE__ */ defineComponent({
379
+ __name: "Navbar",
380
+ props: {
381
+ breadcrumbs: {},
382
+ logoSrc: {}
383
+ },
384
+ setup(__props) {
385
+ const imageLoadFailed = ref(false);
386
+ const loading = ref(true);
387
+ loading.value = true;
388
+ const user = useUserSessionStore();
389
+ loading.value = false;
390
+ return (_ctx, _cache) => {
391
+ const _component_router_link = resolveComponent("router-link");
392
+ return openBlock(), createElementBlock("header", _hoisted_1$26, [createElementVNode("nav", _hoisted_2$23, [createElementVNode("div", _hoisted_3$23, [_cache[3] || (_cache[3] = createElementVNode("label", {
393
+ for: "drawer",
394
+ class: "btn-ghost drawer-button btn-square btn"
395
+ }, [createElementVNode("svg", {
396
+ xmlns: "http://www.w3.org/2000/svg",
397
+ fill: "none",
398
+ viewBox: "0 0 24 24",
399
+ "stroke-width": "1.5",
400
+ stroke: "currentColor",
401
+ class: "h-6 w-6"
402
+ }, [createElementVNode("path", {
403
+ "stroke-linecap": "round",
404
+ "stroke-linejoin": "round",
405
+ d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
406
+ })])], -1)), createElementVNode("div", _hoisted_4$19, [
407
+ createVNode(AppLink_default, {
408
+ to: "/",
409
+ "aria-current": "page",
410
+ "aria-label": "Homepage"
411
+ }, {
412
+ default: withCtx(() => [createElementVNode("div", _hoisted_5$15, [!__props.logoSrc || imageLoadFailed.value ? (openBlock(), createBlock(LogoPlaceholder_default, {
413
+ key: 0,
414
+ size: "xs"
415
+ })) : (openBlock(), createElementBlock("img", {
416
+ key: 1,
417
+ src: __props.logoSrc,
418
+ alt: "logo",
419
+ class: "h-10 w-10 md:h-12 md:w-12",
420
+ onError: _cache[0] || (_cache[0] = ($event) => imageLoadFailed.value = true)
421
+ }, null, 40, _hoisted_6$13))])]),
422
+ _: 1
423
+ }),
424
+ createCommentVNode(" Breadcrumbs in navbar - Desktop view "),
425
+ __props.breadcrumbs && __props.breadcrumbs.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_7$11, [createElementVNode("div", _hoisted_8$11, [
426
+ (openBlock(true), createElementBlock(Fragment, null, renderList(__props.breadcrumbs, (crumb, index) => {
427
+ return openBlock(), createElementBlock(Fragment, { key: index }, [
428
+ createCommentVNode(" Link for all but the last item "),
429
+ index < __props.breadcrumbs.length - 1 ? (openBlock(), createBlock(_component_router_link, {
430
+ key: 0,
431
+ to: crumb.to,
432
+ class: "hover:text-primary"
433
+ }, {
434
+ default: withCtx(() => [createTextVNode(toDisplayString(crumb.label), 1)]),
435
+ _: 2
436
+ }, 1032, ["to"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
437
+ createCommentVNode(" Current page (not a link) "),
438
+ createCommentVNode(" Show loading spinner if explicitly loading "),
439
+ crumb.loading ? (openBlock(), createElementBlock("span", _hoisted_9$10)) : crumb.label ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" Show label if provided "), createElementVNode("span", _hoisted_10$7, toDisplayString(crumb.label), 1)], 2112)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" Fallback if neither loading nor label "), _cache[1] || (_cache[1] = createElementVNode("span", { class: "font-medium text-base-content/50" }, "...", -1))], 2112))
440
+ ], 64)),
441
+ createCommentVNode(" Separator between items "),
442
+ index < __props.breadcrumbs.length - 1 ? (openBlock(), createElementBlock("span", _hoisted_11$7, "/")) : createCommentVNode("v-if", true)
443
+ ], 64);
444
+ }), 128)),
445
+ createCommentVNode(" Optional status badge "),
446
+ renderSlot(_ctx.$slots, "status")
447
+ ])])) : createCommentVNode("v-if", true),
448
+ createCommentVNode(" Breadcrumbs in navbar - Mobile view (GitHub style) "),
449
+ __props.breadcrumbs && __props.breadcrumbs.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_12$6, [
450
+ createCommentVNode(" Parent/previous breadcrumb (if exists) "),
451
+ __props.breadcrumbs.length > 1 && __props.breadcrumbs[__props.breadcrumbs.length - 2] ? (openBlock(), createBlock(_component_router_link, {
452
+ key: 0,
453
+ to: __props.breadcrumbs[__props.breadcrumbs.length - 2].to,
454
+ class: "text-xs text-base-content/70 hover:text-primary truncate max-w-[120px]"
455
+ }, {
456
+ default: withCtx(() => [createTextVNode(toDisplayString(__props.breadcrumbs[__props.breadcrumbs.length - 2].label), 1)]),
457
+ _: 1
458
+ }, 8, ["to"])) : createCommentVNode("v-if", true),
459
+ createCommentVNode(" Current page (last breadcrumb) "),
460
+ createElementVNode("div", _hoisted_13$5, [createCommentVNode(" Show loading spinner if explicitly loading "), __props.breadcrumbs[__props.breadcrumbs.length - 1]?.loading ? (openBlock(), createElementBlock("span", _hoisted_14$4)) : __props.breadcrumbs[__props.breadcrumbs.length - 1]?.label ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" Show label if provided "), createTextVNode(toDisplayString(__props.breadcrumbs[__props.breadcrumbs.length - 1].label), 1)], 64)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" Fallback "), _cache[2] || (_cache[2] = createElementVNode("span", { class: "text-base-content/50" }, "...", -1))], 2112))]),
461
+ createCommentVNode(" Optional status badge "),
462
+ renderSlot(_ctx.$slots, "mobile-status")
463
+ ])) : createCommentVNode("v-if", true)
464
+ ])]), createElementVNode("div", _hoisted_15$3, [_cache[5] || (_cache[5] = createElementVNode("div", { class: "form-control hidden sm:flex" }, [createCommentVNode(" <input\n type=\"text\"\n placeholder=\"Search\"\n class=\"input-bordered input input-sm\"\n /> ")], -1)), loading.value ? (openBlock(), createElementBlock("button", _hoisted_16$3)) : unref(user).currentSession ? (openBlock(), createElementBlock("label", _hoisted_17$2, [..._cache[4] || (_cache[4] = [createElementVNode("div", { class: "bg-neutral-focus text-neutral-content w-10 rounded-full" }, [createElementVNode("span", null, "U")], -1)])])) : (openBlock(), createBlock(LoginButton_default, { key: 2 }))])])]);
465
+ };
466
+ }
467
+ });
468
+ var Navbar_default = _sfc_main$29;
469
+
470
+ //#endregion
471
+ //#region src/components/NotFound.vue
472
+ const _hoisted_1$25 = { class: "flex items-center justify-center bg-base-100 mt-4" };
473
+ const _hoisted_2$22 = { class: "max-w-md mx-auto text-center px-4" };
474
+ const _hoisted_3$22 = { class: "flex flex-col sm:flex-row gap-4 justify-center" };
475
+ const _sfc_main$28 = /* @__PURE__ */ defineComponent({
476
+ __name: "NotFound",
477
+ setup(__props) {
478
+ const router = useRouter();
479
+ const goBack = () => {
480
+ if (window.history.length > 1) router.go(-1);
481
+ else router.push({ name: "Home" });
482
+ };
483
+ return (_ctx, _cache) => {
484
+ const _component_router_link = resolveComponent("router-link");
485
+ return openBlock(), createElementBlock("div", _hoisted_1$25, [createElementVNode("div", _hoisted_2$22, [_cache[1] || (_cache[1] = createElementVNode("div", { class: "mb-8" }, [
486
+ createElementVNode("h1", { class: "text-9xl font-bold text-primary mb-4" }, "404"),
487
+ createElementVNode("h2", { class: "text-3xl font-bold mb-4" }, "Page Not Found"),
488
+ createElementVNode("p", { class: "text-base-content/70 mb-8" }, " Sorry, we couldn't find the page you're looking for. The page may have been moved, deleted, or the URL might be incorrect. ")
489
+ ], -1)), createElementVNode("div", _hoisted_3$22, [createVNode(_component_router_link, {
490
+ to: { name: "Home" },
491
+ class: "btn btn-primary"
492
+ }, {
493
+ default: withCtx(() => [..._cache[0] || (_cache[0] = [createTextVNode(" Go to Home ", -1)])]),
494
+ _: 1
495
+ }), createElementVNode("button", {
496
+ onClick: goBack,
497
+ class: "btn btn-outline"
498
+ }, "Go Back")])])]);
499
+ };
500
+ }
501
+ });
502
+ var NotFound_default = _sfc_main$28;
503
+
504
+ //#endregion
505
+ //#region src/components/RightSidebar.vue
506
+ const _hoisted_1$24 = { class: "drawer-side z-50" };
507
+ const _hoisted_2$21 = { class: "menu p-3 w-80 h-full bg-base-200 text-base-content" };
508
+ const _hoisted_3$21 = { class: "flex pb-2" };
509
+ const _hoisted_4$18 = { class: "pl-3" };
510
+ const _sfc_main$27 = /* @__PURE__ */ defineComponent({
511
+ __name: "RightSidebar",
512
+ props: { logoutReturnUrl: { default: "/" } },
513
+ setup(__props) {
514
+ const userSession = useUserSessionStore();
515
+ /**
516
+ * Where to redirect after logout. Default '/' (homepage) so user can browse anonymously.
517
+ * Apps can pass a different path via the logoutReturnUrl prop if needed.
518
+ */
519
+ const props = __props;
520
+ const logoutLink = computed(() => withReturnUrl("/auth/logout", props.logoutReturnUrl));
521
+ let checked = ref(false);
522
+ function closeDrawer(_) {
523
+ checked.value = false;
524
+ }
525
+ return (_ctx, _cache) => {
526
+ return openBlock(), createElementBlock(Fragment, null, [withDirectives(createElementVNode("input", {
527
+ id: "my-drawer-4",
528
+ type: "checkbox",
529
+ class: "drawer-toggle",
530
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(checked) ? checked.value = $event : checked = $event)
531
+ }, null, 512), [[vModelCheckbox, unref(checked)]]), createElementVNode("div", _hoisted_1$24, [_cache[7] || (_cache[7] = createElementVNode("label", {
532
+ for: "my-drawer-4",
533
+ class: "drawer-overlay"
534
+ }, null, -1)), createElementVNode("ul", _hoisted_2$21, [
535
+ createElementVNode("div", _hoisted_3$21, [
536
+ _cache[1] || (_cache[1] = createElementVNode("div", { class: "avatar placeholder" }, [createElementVNode("div", { class: "bg-neutral-focus text-neutral-content w-10 h-10 rounded-full" }, [createElementVNode("span", null, "U")])], -1)),
537
+ createElementVNode("label", _hoisted_4$18, toDisplayString(unref(userSession).currentSession?.user?.username), 1),
538
+ _cache[2] || (_cache[2] = createElementVNode("div", { class: "grow" }, null, -1)),
539
+ _cache[3] || (_cache[3] = createElementVNode("label", {
540
+ for: "my-drawer-4",
541
+ class: "btn-square btn btn-outline btn-sm"
542
+ }, " X ", -1))
543
+ ]),
544
+ createElementVNode("li", { onClick: closeDrawer }, [createVNode(AppLink_default, { to: "/user/profile" }, {
545
+ default: withCtx(() => [..._cache[4] || (_cache[4] = [createTextVNode("My User Profile", -1)])]),
546
+ _: 1
547
+ })]),
548
+ _cache[6] || (_cache[6] = createElementVNode("div", { class: "divider" }, null, -1)),
549
+ createElementVNode("li", { onClick: closeDrawer }, [createVNode(AppLink_default, { to: logoutLink.value }, {
550
+ default: withCtx(() => [..._cache[5] || (_cache[5] = [createTextVNode("Logout", -1)])]),
551
+ _: 1
552
+ }, 8, ["to"])])
553
+ ])])], 64);
554
+ };
555
+ }
556
+ });
557
+ var RightSidebar_default = _sfc_main$27;
558
+
559
+ //#endregion
560
+ //#region src/composables/useBuildTag.ts
561
+ function useBuildTag() {
562
+ return readonly({ build_tag: import.meta.env.VITE_BUILD_TAG });
563
+ }
564
+
565
+ //#endregion
566
+ //#region src/components/Sidebar.vue
567
+ const _hoisted_1$23 = {
568
+ class: "drawer-side z-50",
569
+ style: {
570
+ "scroll-behavior": "smooth",
571
+ "scroll-padding-top": "5rem"
572
+ },
573
+ ref: "drawersidebar"
574
+ };
575
+ const _hoisted_2$20 = { class: "w-80 bg-base-200 h-full flex flex-col" };
576
+ const _hoisted_3$20 = { class: "flex-1 overflow-y-auto overflow-x-hidden min-h-0" };
577
+ const _hoisted_4$17 = { class: "menu p-3 w-full text-base-content" };
578
+ const _hoisted_5$14 = { class: "flex" };
579
+ const _hoisted_6$12 = { class: "font-title inline-flex text-2xl text-primary transition-all duration-200" };
580
+ const _hoisted_7$10 = ["src"];
581
+ const _hoisted_8$10 = {
582
+ key: 0,
583
+ class: "w-full"
584
+ };
585
+ const _hoisted_9$9 = { class: "menu-title" };
586
+ const _hoisted_10$6 = { class: "flex flex-col items-center justify-center w-full h-16" };
587
+ const _hoisted_11$6 = { class: "text-xs text-neutral-500" };
588
+ const _sfc_main$26 = /* @__PURE__ */ defineComponent({
589
+ __name: "Sidebar",
590
+ props: { logoSrc: {} },
591
+ setup(__props) {
592
+ const user = useUserSessionStore();
593
+ const pinnedPresets = useInjectedPinnedPresets();
594
+ const router = useRouter();
595
+ const tag = useBuildTag();
596
+ const close_button = ref(null);
597
+ const imageLoadFailed = ref(false);
598
+ const sidebarSections = computed(() => {
599
+ const allRoutes = router.getRoutes();
600
+ let sections = {};
601
+ allRoutes.forEach((route) => {
602
+ const meta = route.meta;
603
+ if (meta.side_bar?.section) {
604
+ const side_bar = meta.side_bar;
605
+ const section = meta.side_bar.section;
606
+ const userType = user.currentSession?.user.user_type;
607
+ if (!userType || !side_bar.visible_to?.includes(userType)) return;
608
+ if (!sections[section]) sections[section] = [];
609
+ sections[section].push(route);
610
+ }
611
+ });
612
+ sections = Object.keys(sections).sort().reduce((obj, key) => {
613
+ obj[key] = sections[key] || [];
614
+ return obj;
615
+ }, {});
616
+ return sections;
617
+ });
618
+ function capitalizeSection(section) {
619
+ return section.trim().toUpperCase();
620
+ }
621
+ function closeDrawer(_) {
622
+ close_button.value?.click();
623
+ }
624
+ function getRouteLink(route) {
625
+ return route.path;
626
+ }
627
+ return (_ctx, _cache) => {
628
+ const _component_router_link = resolveComponent("router-link");
629
+ return openBlock(), createElementBlock("div", _hoisted_1$23, [_cache[3] || (_cache[3] = createElementVNode("label", {
630
+ for: "drawer",
631
+ class: "drawer-overlay"
632
+ }, null, -1)), createElementVNode("aside", _hoisted_2$20, [createElementVNode("div", _hoisted_3$20, [createElementVNode("ul", _hoisted_4$17, [
633
+ createElementVNode("div", _hoisted_5$14, [
634
+ createElementVNode("div", _hoisted_6$12, [renderSlot(_ctx.$slots, "logo", {}, () => [!__props.logoSrc || imageLoadFailed.value ? (openBlock(), createBlock(LogoPlaceholder_default, {
635
+ key: 0,
636
+ size: "sm"
637
+ })) : (openBlock(), createElementBlock("img", {
638
+ key: 1,
639
+ src: __props.logoSrc,
640
+ alt: "logo",
641
+ class: "h-12",
642
+ onError: _cache[0] || (_cache[0] = ($event) => imageLoadFailed.value = true)
643
+ }, null, 40, _hoisted_7$10))])]),
644
+ _cache[1] || (_cache[1] = createElementVNode("div", { class: "grow" }, null, -1)),
645
+ createElementVNode("label", {
646
+ for: "drawer",
647
+ class: "btn-square btn btn-outline btn-sm",
648
+ ref_key: "close_button",
649
+ ref: close_button
650
+ }, " X ", 512)
651
+ ]),
652
+ createCommentVNode(" Favorites section (pinned presets) at top "),
653
+ unref(pinnedPresets).pinned.value.length > 0 ? (openBlock(), createElementBlock("li", _hoisted_8$10, [_cache[2] || (_cache[2] = createElementVNode("div", { class: "menu-title" }, [createElementVNode("span", null, "FAVORITES")], -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(pinnedPresets).pinned.value, (preset) => {
654
+ return openBlock(), createBlock(_component_router_link, {
655
+ key: preset.id,
656
+ to: unref(pinnedPresets).getPresetLink(preset),
657
+ class: "whitespace-nowrap block w-full pl-6",
658
+ onClick: closeDrawer
659
+ }, {
660
+ default: withCtx(() => [createTextVNode(toDisplayString(preset.name), 1)]),
661
+ _: 2
662
+ }, 1032, ["to"]);
663
+ }), 128))])) : createCommentVNode("v-if", true),
664
+ (openBlock(true), createElementBlock(Fragment, null, renderList(sidebarSections.value, (routes, section) => {
665
+ return openBlock(), createElementBlock("li", {
666
+ key: section,
667
+ class: "w-full"
668
+ }, [createElementVNode("div", _hoisted_9$9, [createElementVNode("span", null, toDisplayString(capitalizeSection(section)), 1)]), (openBlock(true), createElementBlock(Fragment, null, renderList(routes, (route) => {
669
+ return openBlock(), createBlock(AppLink_default, {
670
+ key: route.path,
671
+ to: getRouteLink(route),
672
+ class: "whitespace-nowrap block w-full pl-6",
673
+ onClick: closeDrawer
674
+ }, {
675
+ default: withCtx(() => [createTextVNode(toDisplayString(route.meta?.title || route.name), 1)]),
676
+ _: 2
677
+ }, 1032, ["to"]);
678
+ }), 128))]);
679
+ }), 128))
680
+ ])]), createElementVNode("div", _hoisted_10$6, [createElementVNode("div", _hoisted_11$6, "Version: " + toDisplayString(unref(tag).build_tag), 1)])])], 512);
681
+ };
682
+ }
683
+ });
684
+ var Sidebar_default = _sfc_main$26;
685
+
686
+ //#endregion
687
+ //#region src/composables/useEmailVerificationGuard.ts
688
+ /**
689
+ * Composable for email verification checks in soft/strict mode.
690
+ * Use to restrict features or show UI based on verification status.
691
+ */
692
+ function useEmailVerificationGuard() {
693
+ const { emailVerificationMode } = useEnv();
694
+ const session = useUserSessionStore();
695
+ const requiresVerification = computed(() => emailVerificationMode === "soft" || emailVerificationMode === "strict");
696
+ const isVerified = computed(() => session.userSession?.user.email_verified ?? true);
697
+ return {
698
+ requiresVerification,
699
+ isVerified,
700
+ showVerificationPrompt: computed(() => requiresVerification.value && !isVerified.value && !!session.userSession),
701
+ emailVerificationMode
702
+ };
703
+ }
704
+
705
+ //#endregion
706
+ //#region src/components/UnverifiedEmailBanner.vue
707
+ const _hoisted_1$22 = {
708
+ key: 0,
709
+ class: "bg-warning border border-warning text-warning-content px-4 py-2 flex items-center justify-between gap-4"
710
+ };
711
+ const _hoisted_2$19 = { class: "text-sm" };
712
+ const _hoisted_3$19 = ["disabled"];
713
+ const _sfc_main$25 = /* @__PURE__ */ defineComponent({
714
+ __name: "UnverifiedEmailBanner",
715
+ setup(__props) {
716
+ const { showVerificationPrompt } = useEmailVerificationGuard();
717
+ const session = useUserSessionStore();
718
+ const resendLoading = ref(false);
719
+ const { mutate: resendMutate } = useMutation((api, _input) => {
720
+ const verifyEmail = api.verifyEmail;
721
+ if (!verifyEmail) throw new Error("Email verification is not configured");
722
+ return verifyEmail.resendVerificationEmail();
723
+ }, { skipAuthCheck: false });
724
+ const resendVerification = async () => {
725
+ try {
726
+ resendLoading.value = true;
727
+ if ((await resendMutate(void 0))?.ok) {
728
+ toast.success("Verification email sent. Please check your inbox.");
729
+ await session.refreshToken();
730
+ } else toast.error("Failed to resend verification email.");
731
+ } catch (error) {
732
+ toast.error(error instanceof Error ? error.message : "Failed to resend");
733
+ } finally {
734
+ resendLoading.value = false;
735
+ }
736
+ };
737
+ return (_ctx, _cache) => {
738
+ return unref(showVerificationPrompt) ? (openBlock(), createElementBlock("div", _hoisted_1$22, [createElementVNode("p", _hoisted_2$19, [
739
+ _cache[0] || (_cache[0] = createTextVNode(" Please verify your email. Check your inbox or ", -1)),
740
+ createElementVNode("button", {
741
+ type: "button",
742
+ class: "link link-hover font-medium underline",
743
+ disabled: resendLoading.value,
744
+ onClick: resendVerification
745
+ }, toDisplayString(resendLoading.value ? "Sending..." : "resend verification email"), 9, _hoisted_3$19),
746
+ _cache[1] || (_cache[1] = createTextVNode(". ", -1))
747
+ ])])) : createCommentVNode("v-if", true);
748
+ };
749
+ }
750
+ });
751
+ var UnverifiedEmailBanner_default = _sfc_main$25;
752
+
753
+ //#endregion
754
+ //#region src/layouts/Admin.vue
755
+ const _hoisted_1$21 = { class: "drawer drawer-end" };
756
+ const _hoisted_2$18 = { class: "drawer h-[calc(100vh_-_64px_-_64px)] bg-base-100" };
757
+ const _hoisted_3$18 = { class: "drawer-content" };
758
+ const _hoisted_4$16 = { class: "pr-1 pl-1 md:pr-4 md:pl-4" };
759
+ const _sfc_main$24 = /* @__PURE__ */ defineComponent({
760
+ __name: "Admin",
761
+ props: {
762
+ logoSrc: {},
763
+ navbarLogoSrc: {}
764
+ },
765
+ setup(__props) {
766
+ return (_ctx, _cache) => {
767
+ return openBlock(), createElementBlock(Fragment, null, [createElementVNode("div", _hoisted_1$21, [createVNode(RightSidebar_default)]), createElementVNode("div", _hoisted_2$18, [
768
+ _cache[0] || (_cache[0] = createElementVNode("input", {
769
+ id: "my-drawer-4",
770
+ type: "checkbox",
771
+ class: "drawer-toggle"
772
+ }, null, -1)),
773
+ createElementVNode("div", _hoisted_3$18, [createVNode(Navbar_default, { logoSrc: __props.navbarLogoSrc }, null, 8, ["logoSrc"]), createElementVNode("div", _hoisted_4$16, [createVNode(unref(RouterView))])]),
774
+ createVNode(Sidebar_default, { logoSrc: __props.logoSrc }, createSlots({ _: 2 }, [_ctx.$slots.sidebarLogo ? {
775
+ name: "logo",
776
+ fn: withCtx(() => [renderSlot(_ctx.$slots, "sidebarLogo")]),
777
+ key: "0"
778
+ } : void 0]), 1032, ["logoSrc"])
779
+ ])], 64);
780
+ };
781
+ }
782
+ });
783
+ var Admin_default = _sfc_main$24;
784
+
785
+ //#endregion
786
+ //#region src/layouts/Auth.vue
787
+ const _hoisted_1$20 = { class: "m-2 sm:m-4" };
788
+ const _hoisted_2$17 = {
789
+ key: 0,
790
+ class: "flex justify-center mb-2"
791
+ };
792
+ const _hoisted_3$17 = { class: "flex flex-wrap gap-4 text-sm" };
793
+ const _hoisted_4$15 = { class: "flex justify-center mb-6" };
794
+ const _hoisted_5$13 = ["src"];
795
+ const _hoisted_6$11 = { class: "min-h-screen bg-base-100 flex flex-col" };
796
+ const DEFAULT_SIGNUP_CONSENT_BEFORE = "By signing up you agree to these terms:";
797
+ const DEFAULT_SIGNUP_CONSENT_AFTER = "After you click Sign Up, we'll show you each document to review and accept.";
798
+ const _sfc_main$23 = /* @__PURE__ */ defineComponent({
799
+ __name: "Auth",
800
+ props: {
801
+ logoSrc: {},
802
+ backLinks: {},
803
+ signupConsentBeforeText: {},
804
+ signupConsentAfterText: {}
805
+ },
806
+ setup(__props) {
807
+ const props = __props;
808
+ const route = useRoute();
809
+ const imageLoadFailed = ref(false);
810
+ const backLinks = computed(() => {
811
+ const fromQuery = getBackLinkFromRoute(route);
812
+ if (fromQuery) return [fromQuery];
813
+ const fromProps = props.backLinks;
814
+ return !fromProps ? [] : Array.isArray(fromProps) ? fromProps : [fromProps];
815
+ });
816
+ const hasBackLinks = computed(() => backLinks.value.length > 0);
817
+ provide("signupConsentBeforeText", props.signupConsentBeforeText ?? DEFAULT_SIGNUP_CONSENT_BEFORE);
818
+ provide("signupConsentAfterText", props.signupConsentAfterText ?? DEFAULT_SIGNUP_CONSENT_AFTER);
819
+ return (_ctx, _cache) => {
820
+ return openBlock(), createElementBlock("div", _hoisted_1$20, [
821
+ hasBackLinks.value ? (openBlock(), createElementBlock("div", _hoisted_2$17, [createElementVNode("nav", _hoisted_3$17, [(openBlock(true), createElementBlock(Fragment, null, renderList(backLinks.value, (link, i) => {
822
+ return openBlock(), createBlock(AppLink_default, {
823
+ key: i,
824
+ to: link.to,
825
+ class: "link link-hover"
826
+ }, {
827
+ default: withCtx(() => [createTextVNode(toDisplayString(link.label ?? "Back to site"), 1)]),
828
+ _: 2
829
+ }, 1032, ["to"]);
830
+ }), 128))])])) : createCommentVNode("v-if", true),
831
+ createElementVNode("div", _hoisted_4$15, [renderSlot(_ctx.$slots, "logo", {}, () => [!__props.logoSrc || imageLoadFailed.value ? (openBlock(), createBlock(LogoPlaceholder_default, {
832
+ key: 0,
833
+ size: "md"
834
+ })) : (openBlock(), createElementBlock("img", {
835
+ key: 1,
836
+ src: __props.logoSrc,
837
+ alt: "logo",
838
+ class: "h-24",
839
+ onError: _cache[0] || (_cache[0] = ($event) => imageLoadFailed.value = true)
840
+ }, null, 40, _hoisted_5$13))])]),
841
+ createElementVNode("div", _hoisted_6$11, [createVNode(unref(RouterView))])
842
+ ]);
843
+ };
844
+ }
845
+ });
846
+ var Auth_default = _sfc_main$23;
847
+
848
+ //#endregion
849
+ //#region src/layouts/Default.vue
850
+ const _hoisted_1$19 = { class: "m-4" };
851
+ const _sfc_main$22 = /* @__PURE__ */ defineComponent({
852
+ __name: "Default",
853
+ setup(__props) {
854
+ return (_ctx, _cache) => {
855
+ return openBlock(), createElementBlock("div", _hoisted_1$19, [createVNode(AppLink_default, {
856
+ to: "/auth/login",
857
+ "aria-current": "page",
858
+ "aria-label": "Homepage",
859
+ class: "flex-0 btn px-2 btn-accent"
860
+ }, {
861
+ default: withCtx(() => [..._cache[0] || (_cache[0] = [createElementVNode("div", { class: "text-lg md:text-3xl" }, [createElementVNode("span", null, "Login")], -1)])]),
862
+ _: 1
863
+ }), createVNode(unref(RouterView))]);
864
+ };
865
+ }
866
+ });
867
+ var Default_default = _sfc_main$22;
868
+
869
+ //#endregion
870
+ //#region src/layouts/InApp.vue
871
+ const _hoisted_1$18 = { class: "drawer drawer-end" };
872
+ const _hoisted_2$16 = { class: "drawer h-[calc(100vh_)]" };
873
+ const _hoisted_3$16 = { class: "drawer-content flex flex-col" };
874
+ const _hoisted_4$14 = {
875
+ key: 0,
876
+ class: "z-20"
877
+ };
878
+ const _hoisted_5$12 = { class: "flex-1 px-3 sm:px-4 md:px-6 pb-4" };
879
+ const _sfc_main$21 = /* @__PURE__ */ defineComponent({
880
+ __name: "InApp",
881
+ props: {
882
+ logoSrc: {},
883
+ navbarLogoSrc: {}
884
+ },
885
+ setup(__props) {
886
+ const route = useRoute();
887
+ const user = useUserSessionStore();
888
+ provide(PINNED_PRESETS_KEY, usePinnedPresets());
889
+ const componentBreadcrumbs = ref(null);
890
+ const breadcrumbSegments = ref(/* @__PURE__ */ new Map());
891
+ provide(BREADCRUMB_KEY, {
892
+ setBreadcrumbs: (breadcrumbs) => {
893
+ componentBreadcrumbs.value = breadcrumbs;
894
+ },
895
+ setBreadcrumbSegment: (componentId, breadcrumbs) => {
896
+ if (breadcrumbs === null) breadcrumbSegments.value.delete(componentId);
897
+ else breadcrumbSegments.value.set(componentId, breadcrumbs);
898
+ }
899
+ });
900
+ const shouldShowAppHeader = computed(() => {
901
+ return currentSectionTabs.value.length > 0;
902
+ });
903
+ const currentBreadcrumbs = computed(() => {
904
+ if (breadcrumbSegments.value.size > 0) {
905
+ const merged = [];
906
+ for (const segment of breadcrumbSegments.value.values()) merged.push(...segment);
907
+ return merged;
908
+ }
909
+ if (componentBreadcrumbs.value !== null) return componentBreadcrumbs.value;
910
+ if (typeof route.meta.getBreadcrumbs === "function") return route.meta.getBreadcrumbs(route);
911
+ if (route.meta.breadcrumbs) return route.meta.breadcrumbs;
912
+ const breadcrumbs = [];
913
+ breadcrumbs.push({
914
+ label: "Home",
915
+ to: { name: "Home" }
916
+ });
917
+ if (route.meta.section) breadcrumbs.push({
918
+ label: route.meta.section,
919
+ to: { name: route.meta.sectionRoute }
920
+ });
921
+ if (route.meta.title && route.name !== "Home") breadcrumbs.push({
922
+ label: route.meta.title,
923
+ to: route
924
+ });
925
+ return breadcrumbs;
926
+ });
927
+ const currentSectionTabs = computed(() => {
928
+ if (route.meta.sectionTabs) {
929
+ const allTabs = route.meta.sectionTabs;
930
+ const userType = user.currentSession?.user.user_type;
931
+ return allTabs.filter((tab) => {
932
+ if (tab.visible_to && userType) return tab.visible_to.includes(userType);
933
+ return true;
934
+ });
935
+ }
936
+ return [];
937
+ });
938
+ const currentActiveTab = computed(() => {
939
+ return route.meta.currentTab || "";
940
+ });
941
+ return (_ctx, _cache) => {
942
+ return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" Right sidebar drawer "), createElementVNode("div", _hoisted_1$18, [
943
+ createVNode(RightSidebar_default),
944
+ createCommentVNode(" Left sidebar drawer "),
945
+ createElementVNode("div", _hoisted_2$16, [
946
+ _cache[0] || (_cache[0] = createElementVNode("input", {
947
+ id: "drawer",
948
+ type: "checkbox",
949
+ class: "drawer-toggle"
950
+ }, null, -1)),
951
+ createElementVNode("div", _hoisted_3$16, [
952
+ createCommentVNode(" Fixed navbar at top with breadcrumbs "),
953
+ createVNode(Navbar_default, {
954
+ class: "sticky top-0 z-30",
955
+ breadcrumbs: currentBreadcrumbs.value,
956
+ logoSrc: __props.navbarLogoSrc
957
+ }, null, 8, ["breadcrumbs", "logoSrc"]),
958
+ createVNode(UnverifiedEmailBanner_default),
959
+ createCommentVNode(" Unified navigation with AppHeader (only shown when needed) "),
960
+ shouldShowAppHeader.value ? (openBlock(), createElementBlock("div", _hoisted_4$14, [createVNode(AppHeader_default, {
961
+ tabs: currentSectionTabs.value,
962
+ "active-tab": currentActiveTab.value
963
+ }, null, 8, ["tabs", "active-tab"])])) : createCommentVNode("v-if", true),
964
+ createCommentVNode(" Main content "),
965
+ createElementVNode("div", _hoisted_5$12, [createVNode(unref(RouterView))])
966
+ ]),
967
+ createCommentVNode(" Left sidebar "),
968
+ createVNode(Sidebar_default, { logoSrc: __props.logoSrc }, createSlots({ _: 2 }, [_ctx.$slots.sidebarLogo ? {
969
+ name: "logo",
970
+ fn: withCtx(() => [renderSlot(_ctx.$slots, "sidebarLogo")]),
971
+ key: "0"
972
+ } : void 0]), 1032, ["logoSrc"])
973
+ ])
974
+ ])], 2112);
975
+ };
976
+ }
977
+ });
978
+ var InApp_default = _sfc_main$21;
979
+
980
+ //#endregion
981
+ //#region src/components/ui/FieldDisplay.vue
982
+ const _hoisted_1$17 = { class: "label" };
983
+ const _hoisted_2$15 = { class: "label-text font-medium" };
984
+ const _hoisted_3$15 = { class: "text-base-content" };
985
+ const _sfc_main$20 = /* @__PURE__ */ defineComponent({
986
+ __name: "FieldDisplay",
987
+ props: {
988
+ label: {},
989
+ value: {},
990
+ fullWidth: { type: Boolean }
991
+ },
992
+ setup(__props) {
993
+ return (_ctx, _cache) => {
994
+ return openBlock(), createElementBlock("div", { class: normalizeClass(["form-control", { "md:col-span-2": __props.fullWidth }]) }, [createElementVNode("label", _hoisted_1$17, [createElementVNode("span", _hoisted_2$15, toDisplayString(__props.label), 1)]), createElementVNode("div", _hoisted_3$15, toDisplayString(__props.value ?? "-"), 1)], 2);
995
+ };
996
+ }
997
+ });
998
+ var FieldDisplay_default = _sfc_main$20;
999
+
1000
+ //#endregion
1001
+ //#region src/components/ui/FieldGroup.vue
1002
+ const _hoisted_1$16 = { class: "card bg-base-100 shadow-sm" };
1003
+ const _hoisted_2$14 = { class: "card-body" };
1004
+ const _hoisted_3$14 = { class: "card-title text-lg mb-4" };
1005
+ const _hoisted_4$13 = { class: "grid grid-cols-1 md:grid-cols-2 gap-4" };
1006
+ const _sfc_main$19 = /* @__PURE__ */ defineComponent({
1007
+ __name: "FieldGroup",
1008
+ props: { title: {} },
1009
+ setup(__props) {
1010
+ return (_ctx, _cache) => {
1011
+ return openBlock(), createElementBlock("div", _hoisted_1$16, [createElementVNode("div", _hoisted_2$14, [createElementVNode("h2", _hoisted_3$14, toDisplayString(__props.title), 1), createElementVNode("div", _hoisted_4$13, [renderSlot(_ctx.$slots, "default")])])]);
1012
+ };
1013
+ }
1014
+ });
1015
+ var FieldGroup_default = _sfc_main$19;
1016
+
1017
+ //#endregion
1018
+ //#region src/components/ui/FileManager.vue
1019
+ const _hoisted_1$15 = { class: "w-full px-2 sm:px-0" };
1020
+ const _hoisted_2$13 = { class: "mb-4" };
1021
+ const _hoisted_3$13 = { class: "flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-3" };
1022
+ const _hoisted_4$12 = { class: "text-lg font-medium" };
1023
+ const _hoisted_5$11 = { class: "flex gap-2 sm:hidden" };
1024
+ const _hoisted_6$10 = ["disabled"];
1025
+ const _hoisted_7$9 = { class: "breadcrumbs text-xs sm:text-sm overflow-x-auto -mx-2 px-2" };
1026
+ const _hoisted_8$9 = { class: "min-w-max" };
1027
+ const _hoisted_9$8 = ["onClick", "title"];
1028
+ const _hoisted_10$5 = ["accept"];
1029
+ const _hoisted_11$5 = { class: "flex flex-col items-center justify-center py-4" };
1030
+ const _hoisted_12$5 = { class: "text-lg font-medium" };
1031
+ const _hoisted_13$4 = { class: "text-sm text-base-content/70 mt-1" };
1032
+ const _hoisted_14$3 = {
1033
+ key: 0,
1034
+ class: "text-xs text-base-content/50 mt-3"
1035
+ };
1036
+ const _hoisted_15$2 = {
1037
+ key: 0,
1038
+ class: "mb-4"
1039
+ };
1040
+ const _hoisted_16$2 = { class: "space-y-3" };
1041
+ const _hoisted_17$1 = { class: "flex justify-between items-start mb-2" };
1042
+ const _hoisted_18$1 = { class: "flex-1" };
1043
+ const _hoisted_19$1 = { class: "flex items-center gap-2" };
1044
+ const _hoisted_20$1 = {
1045
+ key: 0,
1046
+ class: "loading loading-spinner loading-xs text-primary"
1047
+ };
1048
+ const _hoisted_21$1 = {
1049
+ key: 1,
1050
+ xmlns: "http://www.w3.org/2000/svg",
1051
+ class: "h-4 w-4 text-success",
1052
+ fill: "none",
1053
+ viewBox: "0 0 24 24",
1054
+ stroke: "currentColor"
1055
+ };
1056
+ const _hoisted_22$1 = {
1057
+ key: 2,
1058
+ xmlns: "http://www.w3.org/2000/svg",
1059
+ class: "h-4 w-4 text-error",
1060
+ fill: "none",
1061
+ viewBox: "0 0 24 24",
1062
+ stroke: "currentColor"
1063
+ };
1064
+ const _hoisted_23$1 = {
1065
+ key: 3,
1066
+ class: "h-4 w-4 rounded-full bg-base-300"
1067
+ };
1068
+ const _hoisted_24$1 = { class: "min-w-0 flex-1" };
1069
+ const _hoisted_25$1 = { class: "font-medium text-sm break-all" };
1070
+ const _hoisted_26$1 = { class: "text-xs text-base-content/70 mt-1 ml-6" };
1071
+ const _hoisted_27$1 = {
1072
+ key: 0,
1073
+ class: "text-xs text-error mt-1 ml-6"
1074
+ };
1075
+ const _hoisted_28$1 = ["onClick"];
1076
+ const _hoisted_29$1 = { key: 0 };
1077
+ const _hoisted_30$1 = { class: "flex justify-between text-xs mb-1" };
1078
+ const _hoisted_31$1 = { key: 0 };
1079
+ const _hoisted_32$1 = ["value"];
1080
+ const _hoisted_33$1 = {
1081
+ key: 1,
1082
+ class: "text-xs text-success mt-1"
1083
+ };
1084
+ const _hoisted_34$1 = {
1085
+ key: 2,
1086
+ class: "text-xs text-error mt-1"
1087
+ };
1088
+ const _hoisted_35$1 = {
1089
+ key: 1,
1090
+ class: "block sm:hidden mb-4"
1091
+ };
1092
+ const _hoisted_36 = ["onClick"];
1093
+ const _hoisted_37 = { class: "card-body py-4" };
1094
+ const _hoisted_38 = { class: "flex items-center gap-3" };
1095
+ const _hoisted_39 = { class: "card-title text-base break-words flex-1" };
1096
+ const _hoisted_40 = {
1097
+ key: 2,
1098
+ class: "block sm:hidden"
1099
+ };
1100
+ const _hoisted_41 = { class: "card-body" };
1101
+ const _hoisted_42 = { class: "flex items-center gap-2 mb-2" };
1102
+ const _hoisted_43 = { class: "card-title text-base break-all" };
1103
+ const _hoisted_44 = { class: "grid grid-cols-2 text-sm mb-3" };
1104
+ const _hoisted_45 = { class: "card-actions justify-between mt-3 gap-2" };
1105
+ const _hoisted_46 = ["onClick"];
1106
+ const _hoisted_47 = ["onClick"];
1107
+ const _hoisted_48 = {
1108
+ key: 3,
1109
+ class: "hidden sm:block overflow-x-auto"
1110
+ };
1111
+ const _hoisted_49 = { class: "table table-zebra w-full" };
1112
+ const _hoisted_50 = ["onClick"];
1113
+ const _hoisted_51 = { class: "flex items-center gap-3" };
1114
+ const _hoisted_52 = { class: "font-medium break-all max-w-[200px] md:max-w-[500px]" };
1115
+ const _hoisted_53 = {
1116
+ key: 0,
1117
+ class: "text-xs text-base-content/60 ml-2"
1118
+ };
1119
+ const _hoisted_54 = { class: "flex items-center gap-3" };
1120
+ const _hoisted_55 = { class: "font-medium break-all max-w-[200px] md:max-w-[500px]" };
1121
+ const _hoisted_56 = { class: "flex gap-2" };
1122
+ const _hoisted_57 = ["onClick"];
1123
+ const _hoisted_58 = ["onClick"];
1124
+ const _hoisted_59 = { class: "text-center py-8" };
1125
+ const _hoisted_60 = { class: "text-base-content/70 mt-2" };
1126
+ const _hoisted_61 = { class: "text-center py-8" };
1127
+ const _hoisted_62 = { class: "text-base-content/70" };
1128
+ const _hoisted_63 = { class: "hidden sm:block text-sm text-base-content/70 mt-2" };
1129
+ const _hoisted_64 = { class: "text-center py-8" };
1130
+ const _hoisted_65 = { class: "text-error" };
1131
+ const _hoisted_66 = { class: "mb-4" };
1132
+ const _hoisted_67 = {
1133
+ key: 0,
1134
+ class: "bg-base-200 rounded-lg p-3 space-y-1 text-sm"
1135
+ };
1136
+ const _hoisted_68 = { class: "flex items-center gap-2" };
1137
+ const _hoisted_69 = { class: "font-medium" };
1138
+ const _hoisted_70 = { class: "text-base-content/70" };
1139
+ const DEFAULT_MAX_FILE_SIZE = 50 * 1024 * 1024;
1140
+ const _sfc_main$18 = /* @__PURE__ */ defineComponent({
1141
+ __name: "FileManager",
1142
+ props: {
1143
+ recordId: {},
1144
+ recordType: {},
1145
+ config: {}
1146
+ },
1147
+ setup(__props) {
1148
+ const props = __props;
1149
+ const config = computed(() => props.config || {});
1150
+ const envConfig = useEnv();
1151
+ const maxFileSize = computed(() => config.value.maxFileSize ?? envConfig.maxAttachmentFileSize ?? DEFAULT_MAX_FILE_SIZE);
1152
+ const route = useRoute();
1153
+ const router = useRouter();
1154
+ const fileInput = ref(null);
1155
+ const isDragging = ref(false);
1156
+ const isUploading = ref(false);
1157
+ const uploadQueue = ref([]);
1158
+ const currentUploadId = ref(null);
1159
+ const attachments = ref([]);
1160
+ const folders = ref([]);
1161
+ const currentFolderId = ref(null);
1162
+ const folderPath = ref([]);
1163
+ const showDeleteModal = ref(false);
1164
+ const showCreateFolderModal = ref(false);
1165
+ const fileToDelete = ref(null);
1166
+ const { mutate: deleteAttachment } = useMutation((api, input) => api.attachments.deleteAttachment(input.id), { invalidate: /^attachments?:/ });
1167
+ const { mutate: createFolderMutation } = useMutation((api, input) => api.attachments.createFolder(input), { invalidate: /^attachments?:/ });
1168
+ const { data: attachmentsData, loading: fetchingAttachments, error: attachmentsError, refetch: refetchAttachments } = useQuery(async (api) => {
1169
+ const filters = {
1170
+ limit: 50,
1171
+ include_folders: true
1172
+ };
1173
+ if (currentFolderId.value === null) filters.folder_id = null;
1174
+ else if (currentFolderId.value) filters.folder_id = currentFolderId.value;
1175
+ return await api.attachments.listAttachments({
1176
+ record_id: props.recordId,
1177
+ record_type: props.recordType,
1178
+ filters
1179
+ });
1180
+ }, { staleTime: 120 * 1e3 });
1181
+ const { data: allFoldersData, refetch: refetchAllFolders } = useQuery(async (api) => {
1182
+ return await api.attachments.listAttachments({
1183
+ record_id: props.recordId,
1184
+ record_type: props.recordType,
1185
+ filters: {
1186
+ limit: 1e3,
1187
+ include_folders: true
1188
+ }
1189
+ });
1190
+ }, {
1191
+ enabled: computed(() => !!currentFolderId.value && folderPath.value.length === 0),
1192
+ watch: [currentFolderId, folderPath],
1193
+ staleTime: 300 * 1e3
1194
+ });
1195
+ const transformAttachments = computed(() => {
1196
+ if (!attachmentsData.value) return [];
1197
+ const files = [];
1198
+ const folderList = [];
1199
+ const folderItems = attachmentsData.value.folders || [];
1200
+ for (const item of folderItems) folderList.push({
1201
+ id: item.id,
1202
+ name: item.original_name,
1203
+ parent_folder_id: item.parent_folder_id ?? null,
1204
+ file_count: item.file_count ?? 0
1205
+ });
1206
+ const fileItems = attachmentsData.value.files || [];
1207
+ for (const item of fileItems) files.push({
1208
+ id: item.id,
1209
+ name: item.original_name,
1210
+ size: parseInt(item.file_size || "0"),
1211
+ type: item.content_type || "",
1212
+ uploadedAt: new Date(item.created_at)
1213
+ });
1214
+ folders.value = folderList;
1215
+ return files;
1216
+ });
1217
+ onMounted(() => {
1218
+ const folderIdFromUrl = route.query.folder_id;
1219
+ if (folderIdFromUrl) {
1220
+ currentFolderId.value = folderIdFromUrl;
1221
+ refetchAllFolders();
1222
+ }
1223
+ });
1224
+ watch(() => route.query.folder_id, (newFolderId) => {
1225
+ const folderId = newFolderId || null;
1226
+ if (folderId !== currentFolderId.value) {
1227
+ currentFolderId.value = folderId;
1228
+ folderPath.value = [];
1229
+ if (folderId) refetchAllFolders();
1230
+ refreshAttachments();
1231
+ }
1232
+ });
1233
+ watch(() => allFoldersData.value?.folders, (allFoldersList) => {
1234
+ if (currentFolderId.value && folderPath.value.length === 0 && allFoldersList) {
1235
+ const allFoldersMap = new Map(allFoldersList.map((f) => [f.id, {
1236
+ id: f.id,
1237
+ name: f.original_name,
1238
+ parent_folder_id: f.parent_folder_id
1239
+ }]));
1240
+ const path = [];
1241
+ let currentFolderIdToFind = currentFolderId.value;
1242
+ const visited = /* @__PURE__ */ new Set();
1243
+ while (currentFolderIdToFind) {
1244
+ if (visited.has(currentFolderIdToFind)) break;
1245
+ visited.add(currentFolderIdToFind);
1246
+ const folder = allFoldersMap.get(currentFolderIdToFind);
1247
+ if (!folder) break;
1248
+ path.unshift({
1249
+ id: folder.id,
1250
+ name: folder.name
1251
+ });
1252
+ if (!folder.parent_folder_id) break;
1253
+ currentFolderIdToFind = folder.parent_folder_id;
1254
+ }
1255
+ folderPath.value = path;
1256
+ }
1257
+ });
1258
+ watch(transformAttachments, (newAttachments) => {
1259
+ attachments.value = newAttachments;
1260
+ }, { immediate: true });
1261
+ const updateUrlFolder = (folderId) => {
1262
+ const query = { ...route.query };
1263
+ if (folderId) query.folder_id = folderId;
1264
+ else delete query.folder_id;
1265
+ router.replace({ query });
1266
+ };
1267
+ const enterFolder = (folderId) => {
1268
+ const folder = folders.value.find((f) => f.id === folderId);
1269
+ if (folder) {
1270
+ folderPath.value.push({
1271
+ id: folder.id,
1272
+ name: folder.name
1273
+ });
1274
+ currentFolderId.value = folderId;
1275
+ updateUrlFolder(folderId);
1276
+ refreshAttachments();
1277
+ }
1278
+ };
1279
+ const navigateToFolder = (folderId) => {
1280
+ if (folderId === null) {
1281
+ folderPath.value = [];
1282
+ currentFolderId.value = null;
1283
+ } else {
1284
+ const index = folderPath.value.findIndex((f) => f.id === folderId);
1285
+ if (index !== -1) {
1286
+ folderPath.value = folderPath.value.slice(0, index + 1);
1287
+ currentFolderId.value = folderId;
1288
+ } else {
1289
+ const folder = folders.value.find((f) => f.id === folderId);
1290
+ if (folder) {
1291
+ folderPath.value = [{
1292
+ id: folder.id,
1293
+ name: folder.name
1294
+ }];
1295
+ currentFolderId.value = folderId;
1296
+ }
1297
+ }
1298
+ }
1299
+ updateUrlFolder(folderId);
1300
+ refreshAttachments();
1301
+ };
1302
+ const refreshAttachments = () => {
1303
+ refetchAttachments();
1304
+ };
1305
+ const openFileSelector = () => {
1306
+ fileInput.value?.click();
1307
+ };
1308
+ const handleFileChange = async (event) => {
1309
+ const target = event.target;
1310
+ const files = target.files;
1311
+ if (files && files.length > 0) {
1312
+ for (let i = 0; i < files.length; i++) {
1313
+ const file = files[i];
1314
+ if (file) {
1315
+ if (file.size > maxFileSize.value) {
1316
+ uploadQueue.value.push({
1317
+ id: crypto.randomUUID(),
1318
+ file,
1319
+ status: "error",
1320
+ progress: 0,
1321
+ errorMessage: `File size (${formatFileSize(file.size)}) exceeds maximum allowed size (${formatFileSize(maxFileSize.value)})`
1322
+ });
1323
+ continue;
1324
+ }
1325
+ uploadQueue.value.push({
1326
+ id: crypto.randomUUID(),
1327
+ file,
1328
+ status: "pending",
1329
+ progress: 0
1330
+ });
1331
+ }
1332
+ }
1333
+ if (!isUploading.value) processUploadQueue();
1334
+ target.value = "";
1335
+ }
1336
+ };
1337
+ const handleFileDrop = async (event) => {
1338
+ isDragging.value = false;
1339
+ const files = event.dataTransfer?.files;
1340
+ if (files && files.length > 0) {
1341
+ for (let i = 0; i < files.length; i++) {
1342
+ const file = files[i];
1343
+ if (file) {
1344
+ if (file.size > maxFileSize.value) {
1345
+ uploadQueue.value.push({
1346
+ id: crypto.randomUUID(),
1347
+ file,
1348
+ status: "error",
1349
+ progress: 0,
1350
+ errorMessage: `File size (${formatFileSize(file.size)}) exceeds maximum allowed size (${formatFileSize(maxFileSize.value)})`
1351
+ });
1352
+ continue;
1353
+ }
1354
+ uploadQueue.value.push({
1355
+ id: crypto.randomUUID(),
1356
+ file,
1357
+ status: "pending",
1358
+ progress: 0
1359
+ });
1360
+ }
1361
+ }
1362
+ if (!isUploading.value) processUploadQueue();
1363
+ }
1364
+ };
1365
+ const removeFromQueue = (id) => {
1366
+ const index = uploadQueue.value.findIndex((item) => item.id === id);
1367
+ if (index !== -1) uploadQueue.value.splice(index, 1);
1368
+ };
1369
+ const processUploadQueue = async () => {
1370
+ const nextItem = uploadQueue.value.find((item) => item.status === "pending");
1371
+ if (!nextItem) {
1372
+ isUploading.value = false;
1373
+ return;
1374
+ }
1375
+ isUploading.value = true;
1376
+ currentUploadId.value = nextItem.id;
1377
+ nextItem.status = "uploading";
1378
+ await uploadFile(nextItem);
1379
+ if (uploadQueue.value.some((item) => item.status === "pending")) processUploadQueue();
1380
+ else {
1381
+ isUploading.value = false;
1382
+ currentUploadId.value = null;
1383
+ }
1384
+ };
1385
+ const uploadFile = async (item) => {
1386
+ const progressInterval = setInterval(() => {
1387
+ item.progress += Math.random() * 10;
1388
+ item.progress = Math.round(item.progress * 100) / 100;
1389
+ if (item.progress >= 99) item.progress = 99;
1390
+ }, 200);
1391
+ try {
1392
+ const env$1 = useEnv();
1393
+ const userStore = useUserSessionStore();
1394
+ const formData = new FormData();
1395
+ formData.append("file", item.file);
1396
+ formData.append("file_name", item.file.name);
1397
+ formData.append("record_id", props.recordId);
1398
+ formData.append("record_type", props.recordType);
1399
+ if (currentFolderId.value) formData.append("folder_id", currentFolderId.value);
1400
+ const response = await fetch(`${env$1.restApiClient.apiUrl}/attachments/${props.recordType}/${props.recordId}`, {
1401
+ method: "POST",
1402
+ headers: { Authorization: `Bearer ${userStore.accessToken}` },
1403
+ body: formData
1404
+ });
1405
+ if (!response.ok) throw new Error(`Upload failed: ${response.statusText}`);
1406
+ const result = await response.json();
1407
+ if (result && result.id) {
1408
+ clearInterval(progressInterval);
1409
+ item.progress = 100;
1410
+ item.status = "success";
1411
+ refreshAttachments();
1412
+ } else {
1413
+ clearInterval(progressInterval);
1414
+ item.status = "error";
1415
+ item.errorMessage = "Upload failed: Invalid response";
1416
+ }
1417
+ } catch (error) {
1418
+ console.error("Upload failed:", error);
1419
+ clearInterval(progressInterval);
1420
+ item.status = "error";
1421
+ item.errorMessage = error instanceof Error ? error.message : "Upload failed";
1422
+ }
1423
+ };
1424
+ const createFolder = () => {
1425
+ showCreateFolderModal.value = true;
1426
+ };
1427
+ const handleCreateFolder = async (folderName) => {
1428
+ try {
1429
+ await createFolderMutation({
1430
+ record_id: props.recordId,
1431
+ record_type: props.recordType,
1432
+ folder_name: folderName,
1433
+ parent_folder_id: currentFolderId.value || null
1434
+ });
1435
+ showCreateFolderModal.value = false;
1436
+ refreshAttachments();
1437
+ } catch (error) {
1438
+ console.error("Create folder failed:", error);
1439
+ }
1440
+ };
1441
+ const env = useEnv();
1442
+ const useSession = useUserSessionStore();
1443
+ const downloadFile = async (file) => {
1444
+ if (!file) return;
1445
+ const res = await fetch(`${env.restApiClient.apiUrl}/attachments/${props.recordType}/${file.id}`, {
1446
+ method: "GET",
1447
+ headers: {
1448
+ "Content-Type": "application/json",
1449
+ Authorization: `Bearer ${useSession.accessToken}`
1450
+ }
1451
+ });
1452
+ if (!res.ok) throw new Error("Failed to download file");
1453
+ const blob = await res.blob();
1454
+ const url = URL.createObjectURL(blob);
1455
+ const a = document.createElement("a");
1456
+ a.href = url;
1457
+ a.download = file.name;
1458
+ document.body.appendChild(a);
1459
+ a.click();
1460
+ a.remove();
1461
+ URL.revokeObjectURL(url);
1462
+ };
1463
+ const confirmDeleteFile = (file) => {
1464
+ fileToDelete.value = file;
1465
+ showDeleteModal.value = true;
1466
+ };
1467
+ const deleteFile = async () => {
1468
+ if (!fileToDelete.value) return;
1469
+ try {
1470
+ await deleteAttachment({ id: fileToDelete.value.id });
1471
+ refreshAttachments();
1472
+ showDeleteModal.value = false;
1473
+ fileToDelete.value = null;
1474
+ } catch (error) {
1475
+ console.error("Delete failed:", error);
1476
+ }
1477
+ };
1478
+ const formatFileSize = (bytes) => {
1479
+ if (bytes === 0) return "0 Bytes";
1480
+ const k = 1024;
1481
+ const sizes = [
1482
+ "Bytes",
1483
+ "KB",
1484
+ "MB",
1485
+ "GB"
1486
+ ];
1487
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1488
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
1489
+ };
1490
+ const formatDate = (date) => {
1491
+ return new Intl.DateTimeFormat("en-US", {
1492
+ year: "numeric",
1493
+ month: "short",
1494
+ day: "numeric"
1495
+ }).format(date);
1496
+ };
1497
+ const getFileTypeLabel = (type) => {
1498
+ if (type.includes("pdf")) return "PDF";
1499
+ if (type.includes("word") || type.includes("doc")) return "DOC";
1500
+ if (type.includes("sheet") || type.includes("excel") || type.includes("xls")) return "XLS";
1501
+ if (type.includes("image")) return "IMG";
1502
+ return "FILE";
1503
+ };
1504
+ const getFileTypeBadgeClass = (type) => {
1505
+ if (type.includes("pdf")) return "badge-error";
1506
+ if (type.includes("word") || type.includes("doc")) return "badge-primary";
1507
+ if (type.includes("sheet") || type.includes("excel") || type.includes("xls")) return "badge-success";
1508
+ if (type.includes("image")) return "badge-warning";
1509
+ return "badge-neutral";
1510
+ };
1511
+ return (_ctx, _cache) => {
1512
+ return openBlock(), createElementBlock("div", _hoisted_1$15, [
1513
+ createCommentVNode(" File Manager Header "),
1514
+ createElementVNode("div", _hoisted_2$13, [
1515
+ createCommentVNode(" Mobile: Stacked layout "),
1516
+ createElementVNode("div", _hoisted_3$13, [
1517
+ createElementVNode("h3", _hoisted_4$12, toDisplayString(config.value.title || "Attachments"), 1),
1518
+ createCommentVNode(" Mobile: Action buttons "),
1519
+ createElementVNode("div", _hoisted_5$11, [createElementVNode("button", {
1520
+ class: "btn btn-sm btn-primary flex-1",
1521
+ onClick: openFileSelector,
1522
+ disabled: isUploading.value
1523
+ }, [_cache[5] || (_cache[5] = createElementVNode("svg", {
1524
+ xmlns: "http://www.w3.org/2000/svg",
1525
+ class: "h-4 w-4 mr-1",
1526
+ fill: "none",
1527
+ viewBox: "0 0 24 24",
1528
+ stroke: "currentColor"
1529
+ }, [createElementVNode("path", {
1530
+ "stroke-linecap": "round",
1531
+ "stroke-linejoin": "round",
1532
+ "stroke-width": "2",
1533
+ d: "M12 4v16m8-8H4"
1534
+ })], -1)), createTextVNode(" " + toDisplayString(config.value.addButtonText || "Add Files"), 1)], 8, _hoisted_6$10), createElementVNode("button", {
1535
+ class: "btn btn-sm btn-info flex-1",
1536
+ onClick: createFolder
1537
+ }, [..._cache[6] || (_cache[6] = [createElementVNode("svg", {
1538
+ xmlns: "http://www.w3.org/2000/svg",
1539
+ class: "h-4 w-4 mr-1",
1540
+ fill: "none",
1541
+ viewBox: "0 0 24 24",
1542
+ stroke: "currentColor"
1543
+ }, [createElementVNode("path", {
1544
+ "stroke-linecap": "round",
1545
+ "stroke-linejoin": "round",
1546
+ "stroke-width": "2",
1547
+ d: "M12 4v16m8-8H4"
1548
+ })], -1), createTextVNode(" New Folder ", -1)])])]),
1549
+ createCommentVNode(" Desktop: Action buttons "),
1550
+ createElementVNode("div", { class: "hidden sm:flex gap-2" }, [createElementVNode("button", {
1551
+ class: "btn btn-sm btn-info",
1552
+ onClick: createFolder
1553
+ }, "New Folder")])
1554
+ ]),
1555
+ createCommentVNode(" Breadcrumb Navigation - Mobile optimized "),
1556
+ createElementVNode("div", _hoisted_7$9, [createElementVNode("ul", _hoisted_8$9, [createElementVNode("li", null, [createElementVNode("button", {
1557
+ class: normalizeClass(["btn btn-sm sm:btn-xs btn-ghost min-h-[2.5rem] sm:min-h-0 py-2 sm:py-1", { "btn-active": !currentFolderId.value }]),
1558
+ onClick: _cache[0] || (_cache[0] = ($event) => navigateToFolder(null))
1559
+ }, [..._cache[7] || (_cache[7] = [createElementVNode("svg", {
1560
+ xmlns: "http://www.w3.org/2000/svg",
1561
+ class: "h-4 w-4 sm:mr-1",
1562
+ fill: "none",
1563
+ viewBox: "0 0 24 24",
1564
+ stroke: "currentColor"
1565
+ }, [createElementVNode("path", {
1566
+ "stroke-linecap": "round",
1567
+ "stroke-linejoin": "round",
1568
+ "stroke-width": "2",
1569
+ d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
1570
+ })], -1), createElementVNode("span", { class: "hidden sm:inline" }, "Root", -1)])], 2)]), (openBlock(true), createElementBlock(Fragment, null, renderList(folderPath.value, (folder, index) => {
1571
+ return openBlock(), createElementBlock("li", { key: folder.id }, [createElementVNode("button", {
1572
+ class: normalizeClass(["btn btn-sm sm:btn-xs btn-ghost min-h-[2.5rem] sm:min-h-0 py-2 sm:py-1 max-w-[8rem] sm:max-w-none truncate", { "btn-active": index === folderPath.value.length - 1 }]),
1573
+ onClick: ($event) => navigateToFolder(folder.id),
1574
+ title: folder.name
1575
+ }, toDisplayString(folder.name), 11, _hoisted_9$8)]);
1576
+ }), 128))])])
1577
+ ]),
1578
+ createElementVNode("input", {
1579
+ ref_key: "fileInput",
1580
+ ref: fileInput,
1581
+ type: "file",
1582
+ class: "hidden",
1583
+ onChange: handleFileChange,
1584
+ multiple: "",
1585
+ accept: config.value.acceptedTypes
1586
+ }, null, 40, _hoisted_10$5),
1587
+ createCommentVNode(" Drag & Drop Zone (hidden on mobile) "),
1588
+ createElementVNode("div", {
1589
+ class: normalizeClass(["hidden sm:block border-2 border-dashed rounded-lg p-6 text-center mb-4 transition-colors", {
1590
+ "border-primary bg-primary/5 cursor-pointer": isDragging.value,
1591
+ "border-base-300 hover:border-primary/50": !isDragging.value
1592
+ }]),
1593
+ onDragover: _cache[1] || (_cache[1] = withModifiers(($event) => isDragging.value = true, ["prevent"])),
1594
+ onDragleave: _cache[2] || (_cache[2] = withModifiers(($event) => isDragging.value = false, ["prevent"])),
1595
+ onDrop: withModifiers(handleFileDrop, ["prevent"]),
1596
+ onClick: openFileSelector
1597
+ }, [createElementVNode("div", _hoisted_11$5, [
1598
+ _cache[8] || (_cache[8] = createElementVNode("svg", {
1599
+ xmlns: "http://www.w3.org/2000/svg",
1600
+ class: "h-12 w-12 mb-2 text-primary",
1601
+ fill: "none",
1602
+ viewBox: "0 0 24 24",
1603
+ stroke: "currentColor"
1604
+ }, [createElementVNode("path", {
1605
+ "stroke-linecap": "round",
1606
+ "stroke-linejoin": "round",
1607
+ "stroke-width": "2",
1608
+ d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
1609
+ })], -1)),
1610
+ createElementVNode("p", _hoisted_12$5, toDisplayString(config.value.dropText || "Drag and drop files here"), 1),
1611
+ createElementVNode("p", _hoisted_13$4, toDisplayString(config.value.browseText || "or click to browse files"), 1),
1612
+ config.value.acceptText ? (openBlock(), createElementBlock("p", _hoisted_14$3, toDisplayString(config.value.acceptText), 1)) : createCommentVNode("v-if", true)
1613
+ ])], 34),
1614
+ createCommentVNode(" Upload Queue "),
1615
+ uploadQueue.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_15$2, [_cache[12] || (_cache[12] = createElementVNode("div", { class: "text-sm font-medium mb-2" }, "File uploads", -1)), createElementVNode("div", _hoisted_16$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(uploadQueue.value, (item) => {
1616
+ return openBlock(), createElementBlock("div", {
1617
+ key: item.id,
1618
+ class: normalizeClass(["border border-base-200 rounded-lg p-3", {
1619
+ "bg-success/5 border-success/30": item.status === "success",
1620
+ "bg-error/5 border-error/30": item.status === "error"
1621
+ }])
1622
+ }, [
1623
+ createElementVNode("div", _hoisted_17$1, [
1624
+ createElementVNode("div", _hoisted_18$1, [
1625
+ createElementVNode("div", _hoisted_19$1, [
1626
+ createCommentVNode(" Status Icon "),
1627
+ item.status === "uploading" ? (openBlock(), createElementBlock("div", _hoisted_20$1)) : item.status === "success" ? (openBlock(), createElementBlock("svg", _hoisted_21$1, [..._cache[9] || (_cache[9] = [createElementVNode("path", {
1628
+ "stroke-linecap": "round",
1629
+ "stroke-linejoin": "round",
1630
+ "stroke-width": "2",
1631
+ d: "M5 13l4 4L19 7"
1632
+ }, null, -1)])])) : item.status === "error" ? (openBlock(), createElementBlock("svg", _hoisted_22$1, [..._cache[10] || (_cache[10] = [createElementVNode("path", {
1633
+ "stroke-linecap": "round",
1634
+ "stroke-linejoin": "round",
1635
+ "stroke-width": "2",
1636
+ d: "M6 18L18 6M6 6l12 12"
1637
+ }, null, -1)])])) : (openBlock(), createElementBlock("div", _hoisted_23$1)),
1638
+ createCommentVNode(" Filename "),
1639
+ createElementVNode("div", _hoisted_24$1, [createElementVNode("div", _hoisted_25$1, toDisplayString(item.file.name), 1)])
1640
+ ]),
1641
+ createElementVNode("div", _hoisted_26$1, toDisplayString(formatFileSize(item.file.size)), 1),
1642
+ item.status === "error" && item.errorMessage ? (openBlock(), createElementBlock("div", _hoisted_27$1, toDisplayString(item.errorMessage), 1)) : createCommentVNode("v-if", true)
1643
+ ]),
1644
+ createCommentVNode(" Dismiss Button (X) "),
1645
+ item.status === "success" || item.status === "error" ? (openBlock(), createElementBlock("button", {
1646
+ key: 0,
1647
+ class: "btn btn-xs btn-ghost btn-circle",
1648
+ onClick: ($event) => removeFromQueue(item.id),
1649
+ "aria-label": "Dismiss notification"
1650
+ }, [..._cache[11] || (_cache[11] = [createElementVNode("svg", {
1651
+ xmlns: "http://www.w3.org/2000/svg",
1652
+ class: "h-4 w-4",
1653
+ fill: "none",
1654
+ viewBox: "0 0 24 24",
1655
+ stroke: "currentColor"
1656
+ }, [createElementVNode("path", {
1657
+ "stroke-linecap": "round",
1658
+ "stroke-linejoin": "round",
1659
+ "stroke-width": "2",
1660
+ d: "M6 18L18 6M6 6l12 12"
1661
+ })], -1)])], 8, _hoisted_28$1)) : createCommentVNode("v-if", true)
1662
+ ]),
1663
+ createCommentVNode(" Progress Bar "),
1664
+ item.status === "uploading" || item.status === "pending" ? (openBlock(), createElementBlock("div", _hoisted_29$1, [createElementVNode("div", _hoisted_30$1, [createElementVNode("span", null, toDisplayString(item.status === "uploading" ? "Uploading..." : "Waiting..."), 1), item.status === "uploading" ? (openBlock(), createElementBlock("span", _hoisted_31$1, toDisplayString(item.progress.toFixed(2)) + "%", 1)) : createCommentVNode("v-if", true)]), createElementVNode("progress", {
1665
+ class: normalizeClass(["progress w-full", {
1666
+ "progress-primary": item.status === "uploading",
1667
+ "progress-secondary opacity-50": item.status === "pending"
1668
+ }]),
1669
+ value: item.status === "uploading" ? item.progress : 0,
1670
+ max: "100"
1671
+ }, null, 10, _hoisted_32$1)])) : createCommentVNode("v-if", true),
1672
+ createCommentVNode(" Success/Error Message "),
1673
+ item.status === "success" ? (openBlock(), createElementBlock("div", _hoisted_33$1, " Upload complete ")) : createCommentVNode("v-if", true),
1674
+ item.status === "error" ? (openBlock(), createElementBlock("div", _hoisted_34$1, " Upload failed - dismiss and try again ")) : createCommentVNode("v-if", true)
1675
+ ], 2);
1676
+ }), 128))])])) : createCommentVNode("v-if", true),
1677
+ createCommentVNode(" Folder list (mobile) "),
1678
+ folders.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_35$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(folders.value, (folder) => {
1679
+ return openBlock(), createElementBlock("div", {
1680
+ key: folder.id,
1681
+ class: "card card-compact bg-base-100 shadow-sm mb-3 border border-base-200 active:scale-[0.98] transition-transform",
1682
+ onClick: ($event) => enterFolder(folder.id)
1683
+ }, [createElementVNode("div", _hoisted_37, [createElementVNode("div", _hoisted_38, [
1684
+ _cache[13] || (_cache[13] = createElementVNode("div", { class: "badge badge-secondary badge-lg" }, "FOLDER", -1)),
1685
+ createElementVNode("h3", _hoisted_39, toDisplayString(folder.name), 1),
1686
+ _cache[14] || (_cache[14] = createElementVNode("svg", {
1687
+ xmlns: "http://www.w3.org/2000/svg",
1688
+ class: "h-5 w-5 text-base-content/50",
1689
+ fill: "none",
1690
+ viewBox: "0 0 24 24",
1691
+ stroke: "currentColor"
1692
+ }, [createElementVNode("path", {
1693
+ "stroke-linecap": "round",
1694
+ "stroke-linejoin": "round",
1695
+ "stroke-width": "2",
1696
+ d: "M9 5l7 7-7 7"
1697
+ })], -1))
1698
+ ])])], 8, _hoisted_36);
1699
+ }), 128))])) : createCommentVNode("v-if", true),
1700
+ createCommentVNode(" File List for Mobile "),
1701
+ attachments.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_40, [(openBlock(true), createElementBlock(Fragment, null, renderList(attachments.value, (file, index) => {
1702
+ return openBlock(), createElementBlock("div", {
1703
+ key: index,
1704
+ class: "card card-compact bg-base-100 shadow-sm mb-3 border border-base-200"
1705
+ }, [createElementVNode("div", _hoisted_41, [
1706
+ createElementVNode("div", _hoisted_42, [createElementVNode("div", { class: normalizeClass(["badge", getFileTypeBadgeClass(file.type)]) }, toDisplayString(getFileTypeLabel(file.type)), 3), createElementVNode("h3", _hoisted_43, toDisplayString(file.name), 1)]),
1707
+ createElementVNode("div", _hoisted_44, [createElementVNode("div", null, [_cache[15] || (_cache[15] = createElementVNode("span", { class: "text-base-content/70" }, "Size:", -1)), createTextVNode(" " + toDisplayString(formatFileSize(file.size)), 1)]), createElementVNode("div", null, [_cache[16] || (_cache[16] = createElementVNode("span", { class: "text-base-content/70" }, "Uploaded:", -1)), createTextVNode(" " + toDisplayString(formatDate(file.uploadedAt)), 1)])]),
1708
+ createElementVNode("div", _hoisted_45, [createElementVNode("button", {
1709
+ class: "btn btn-sm flex-1 min-h-[2.75rem]",
1710
+ onClick: ($event) => downloadFile(file)
1711
+ }, [..._cache[17] || (_cache[17] = [createElementVNode("svg", {
1712
+ xmlns: "http://www.w3.org/2000/svg",
1713
+ class: "h-4 w-4 mr-1",
1714
+ fill: "none",
1715
+ viewBox: "0 0 24 24",
1716
+ stroke: "currentColor"
1717
+ }, [createElementVNode("path", {
1718
+ "stroke-linecap": "round",
1719
+ "stroke-linejoin": "round",
1720
+ "stroke-width": "2",
1721
+ d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
1722
+ })], -1), createTextVNode(" Download ", -1)])], 8, _hoisted_46), createElementVNode("button", {
1723
+ class: "btn btn-sm btn-outline btn-error flex-1 min-h-[2.75rem]",
1724
+ onClick: ($event) => confirmDeleteFile(file)
1725
+ }, [..._cache[18] || (_cache[18] = [createElementVNode("svg", {
1726
+ xmlns: "http://www.w3.org/2000/svg",
1727
+ class: "h-4 w-4 mr-1",
1728
+ fill: "none",
1729
+ viewBox: "0 0 24 24",
1730
+ stroke: "currentColor"
1731
+ }, [createElementVNode("path", {
1732
+ "stroke-linecap": "round",
1733
+ "stroke-linejoin": "round",
1734
+ "stroke-width": "2",
1735
+ d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
1736
+ })], -1), createTextVNode(" Delete ", -1)])], 8, _hoisted_47)])
1737
+ ])]);
1738
+ }), 128))])) : createCommentVNode("v-if", true),
1739
+ createCommentVNode(" Folder and File List for Tablet/Desktop "),
1740
+ folders.value.length > 0 || attachments.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_48, [createElementVNode("table", _hoisted_49, [_cache[25] || (_cache[25] = createElementVNode("thead", null, [createElementVNode("tr", null, [
1741
+ createElementVNode("th", null, "Name"),
1742
+ createElementVNode("th", null, "Size"),
1743
+ createElementVNode("th", null, "Uploaded"),
1744
+ createElementVNode("th", null, "Actions")
1745
+ ])], -1)), createElementVNode("tbody", null, [
1746
+ createCommentVNode(" Folders "),
1747
+ (openBlock(true), createElementBlock(Fragment, null, renderList(folders.value, (folder) => {
1748
+ return openBlock(), createElementBlock("tr", {
1749
+ key: folder.id,
1750
+ class: "cursor-pointer",
1751
+ onClick: ($event) => enterFolder(folder.id)
1752
+ }, [
1753
+ createElementVNode("td", null, [createElementVNode("div", _hoisted_51, [_cache[19] || (_cache[19] = createElementVNode("div", { class: "badge badge-secondary badge-sm" }, "FOLDER", -1)), createElementVNode("div", _hoisted_52, [createTextVNode(toDisplayString(folder.name) + " ", 1), folder.file_count !== void 0 ? (openBlock(), createElementBlock("span", _hoisted_53, " (" + toDisplayString(folder.file_count) + ") ", 1)) : createCommentVNode("v-if", true)])])]),
1754
+ _cache[20] || (_cache[20] = createElementVNode("td", null, "—", -1)),
1755
+ _cache[21] || (_cache[21] = createElementVNode("td", null, "—", -1)),
1756
+ _cache[22] || (_cache[22] = createElementVNode("td", null, "—", -1))
1757
+ ], 8, _hoisted_50);
1758
+ }), 128)),
1759
+ createCommentVNode(" Files "),
1760
+ (openBlock(true), createElementBlock(Fragment, null, renderList(attachments.value, (file, index) => {
1761
+ return openBlock(), createElementBlock("tr", { key: index }, [
1762
+ createElementVNode("td", null, [createElementVNode("div", _hoisted_54, [createElementVNode("div", { class: normalizeClass(["badge badge-sm", getFileTypeBadgeClass(file.type)]) }, toDisplayString(getFileTypeLabel(file.type)), 3), createElementVNode("div", _hoisted_55, toDisplayString(file.name), 1)])]),
1763
+ createElementVNode("td", null, toDisplayString(formatFileSize(file.size)), 1),
1764
+ createElementVNode("td", null, toDisplayString(formatDate(file.uploadedAt)), 1),
1765
+ createElementVNode("td", null, [createElementVNode("div", _hoisted_56, [createElementVNode("button", {
1766
+ class: "btn btn-sm btn-ghost btn-circle",
1767
+ onClick: ($event) => downloadFile(file)
1768
+ }, [..._cache[23] || (_cache[23] = [createElementVNode("svg", {
1769
+ xmlns: "http://www.w3.org/2000/svg",
1770
+ class: "h-4 w-4",
1771
+ fill: "none",
1772
+ viewBox: "0 0 24 24",
1773
+ stroke: "currentColor"
1774
+ }, [createElementVNode("path", {
1775
+ "stroke-linecap": "round",
1776
+ "stroke-linejoin": "round",
1777
+ "stroke-width": "2",
1778
+ d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
1779
+ })], -1)])], 8, _hoisted_57), createElementVNode("button", {
1780
+ class: "btn btn-sm btn-ghost btn-circle text-error",
1781
+ onClick: ($event) => confirmDeleteFile(file)
1782
+ }, [..._cache[24] || (_cache[24] = [createElementVNode("svg", {
1783
+ xmlns: "http://www.w3.org/2000/svg",
1784
+ class: "h-4 w-4",
1785
+ fill: "none",
1786
+ viewBox: "0 0 24 24",
1787
+ stroke: "currentColor"
1788
+ }, [createElementVNode("path", {
1789
+ "stroke-linecap": "round",
1790
+ "stroke-linejoin": "round",
1791
+ "stroke-width": "2",
1792
+ d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
1793
+ })], -1)])], 8, _hoisted_58)])])
1794
+ ]);
1795
+ }), 128))
1796
+ ])])])) : unref(fetchingAttachments) ? (openBlock(), createElementBlock(Fragment, { key: 4 }, [createCommentVNode(" Loading State "), createElementVNode("div", _hoisted_59, [_cache[26] || (_cache[26] = createElementVNode("div", { class: "flex justify-center" }, [createElementVNode("span", { class: "loading loading-spinner loading-md" })], -1)), createElementVNode("p", _hoisted_60, toDisplayString(config.value.loadingText || "Loading attachments..."), 1)])], 2112)) : !isUploading.value ? (openBlock(), createElementBlock(Fragment, { key: 5 }, [createCommentVNode(" Empty State "), createElementVNode("div", _hoisted_61, [
1797
+ _cache[27] || (_cache[27] = createElementVNode("svg", {
1798
+ xmlns: "http://www.w3.org/2000/svg",
1799
+ class: "h-12 w-12 mx-auto mb-3 text-base-content/30",
1800
+ fill: "none",
1801
+ viewBox: "0 0 24 24",
1802
+ stroke: "currentColor"
1803
+ }, [createElementVNode("path", {
1804
+ "stroke-linecap": "round",
1805
+ "stroke-linejoin": "round",
1806
+ "stroke-width": "2",
1807
+ d: "M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
1808
+ })], -1)),
1809
+ createElementVNode("p", _hoisted_62, toDisplayString(config.value.emptyText || "No attachments yet"), 1),
1810
+ createCommentVNode(" Desktop instruction "),
1811
+ createElementVNode("p", _hoisted_63, toDisplayString(config.value.instructionText || "Use the drag and drop area above to add files"), 1)
1812
+ ])], 2112)) : unref(attachmentsError) ? (openBlock(), createElementBlock(Fragment, { key: 6 }, [createCommentVNode(" Error State "), createElementVNode("div", _hoisted_64, [createElementVNode("p", _hoisted_65, toDisplayString(config.value.errorText || "Failed to load attachments"), 1), createElementVNode("button", {
1813
+ class: "btn btn-sm btn-outline mt-2",
1814
+ onClick: refreshAttachments
1815
+ }, toDisplayString(config.value.retryText || "Try Again"), 1)])], 2112)) : createCommentVNode("v-if", true),
1816
+ createCommentVNode(" Create Folder Modal "),
1817
+ createVNode(InputModal_default, {
1818
+ modelValue: showCreateFolderModal.value,
1819
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => showCreateFolderModal.value = $event),
1820
+ title: config.value.createFolderTitle || "Create New Folder",
1821
+ label: config.value.folderNameLabel || "Folder Name",
1822
+ placeholder: "Enter folder name",
1823
+ "confirm-text": config.value.createFolderButtonText || "Create",
1824
+ "cancel-text": config.value.cancelText || "Cancel",
1825
+ onConfirm: handleCreateFolder
1826
+ }, null, 8, [
1827
+ "modelValue",
1828
+ "title",
1829
+ "label",
1830
+ "confirm-text",
1831
+ "cancel-text"
1832
+ ]),
1833
+ createCommentVNode(" Delete Confirmation Modal "),
1834
+ createVNode(ConfirmDialog_default, {
1835
+ modelValue: showDeleteModal.value,
1836
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => showDeleteModal.value = $event),
1837
+ title: config.value.deleteTitle || "Confirm Deletion",
1838
+ "confirm-text": config.value.confirmDeleteText || "Delete",
1839
+ "cancel-text": config.value.cancelText || "Cancel",
1840
+ "confirm-button-class": "btn-error",
1841
+ onConfirm: deleteFile
1842
+ }, {
1843
+ message: withCtx(() => [createElementVNode("p", _hoisted_66, toDisplayString(config.value.deleteMessage || "Are you sure you want to delete this file? This action cannot be undone."), 1), fileToDelete.value ? (openBlock(), createElementBlock("div", _hoisted_67, [createElementVNode("div", _hoisted_68, [createElementVNode("div", { class: normalizeClass(["badge badge-sm", getFileTypeBadgeClass(fileToDelete.value.type)]) }, toDisplayString(getFileTypeLabel(fileToDelete.value.type)), 3), createElementVNode("span", _hoisted_69, toDisplayString(fileToDelete.value.name), 1)]), createElementVNode("div", _hoisted_70, [createElementVNode("div", null, [_cache[28] || (_cache[28] = createElementVNode("span", { class: "font-medium" }, "Size:", -1)), createTextVNode(" " + toDisplayString(formatFileSize(fileToDelete.value.size)), 1)]), createElementVNode("div", null, [_cache[29] || (_cache[29] = createElementVNode("span", { class: "font-medium" }, "Uploaded:", -1)), createTextVNode(" " + toDisplayString(formatDate(fileToDelete.value.uploadedAt)), 1)])])])) : createCommentVNode("v-if", true)]),
1844
+ _: 1
1845
+ }, 8, [
1846
+ "modelValue",
1847
+ "title",
1848
+ "confirm-text",
1849
+ "cancel-text"
1850
+ ])
1851
+ ]);
1852
+ };
1853
+ }
1854
+ });
1855
+ var FileManager_default = _sfc_main$18;
1856
+
1857
+ //#endregion
1858
+ //#region src/components/ui/KeyValueEditor.vue
1859
+ const _hoisted_1$14 = { class: "key-value-editor col-span-full" };
1860
+ const _hoisted_2$12 = {
1861
+ key: 0,
1862
+ class: "mb-2"
1863
+ };
1864
+ const _hoisted_3$12 = {
1865
+ key: 0,
1866
+ class: "label p-0"
1867
+ };
1868
+ const _hoisted_4$11 = { class: "label-text" };
1869
+ const _hoisted_5$10 = {
1870
+ key: 1,
1871
+ class: "text-xs text-base-content/60 mt-1"
1872
+ };
1873
+ const _hoisted_6$9 = {
1874
+ key: 1,
1875
+ class: "text-error text-sm mb-2"
1876
+ };
1877
+ const _hoisted_7$8 = { class: "space-y-3" };
1878
+ const _hoisted_8$8 = { class: "flex-1" };
1879
+ const _hoisted_9$7 = [
1880
+ "onUpdate:modelValue",
1881
+ "placeholder",
1882
+ "onInput"
1883
+ ];
1884
+ const _hoisted_10$4 = { class: "flex-1" };
1885
+ const _hoisted_11$4 = [
1886
+ "onUpdate:modelValue",
1887
+ "placeholder",
1888
+ "onInput"
1889
+ ];
1890
+ const _hoisted_12$4 = ["onClick", "disabled"];
1891
+ const _hoisted_13$3 = {
1892
+ key: 2,
1893
+ class: "text-center py-4 text-base-content/60"
1894
+ };
1895
+ const _sfc_main$17 = /* @__PURE__ */ defineComponent({
1896
+ __name: "KeyValueEditor",
1897
+ props: {
1898
+ modelValue: { default: null },
1899
+ label: { default: "Key-Value Pairs" },
1900
+ helpText: { default: "Add custom metadata as key-value pairs" },
1901
+ keyPlaceholder: { default: "Key" },
1902
+ valuePlaceholder: { default: "Value" },
1903
+ error: { default: void 0 }
1904
+ },
1905
+ emits: ["update:modelValue"],
1906
+ setup(__props, { emit: __emit }) {
1907
+ const props = __props;
1908
+ const emit = __emit;
1909
+ const parseJsonToPairs = (jsonString) => {
1910
+ if (!jsonString || jsonString.trim() === "") return [{
1911
+ key: "",
1912
+ value: ""
1913
+ }];
1914
+ try {
1915
+ const parsed = JSON.parse(jsonString);
1916
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return [{
1917
+ key: "",
1918
+ value: ""
1919
+ }];
1920
+ const pairs$1 = Object.entries(parsed).map(([key, value]) => ({
1921
+ key,
1922
+ value: String(value)
1923
+ }));
1924
+ return pairs$1.length > 0 ? pairs$1 : [{
1925
+ key: "",
1926
+ value: ""
1927
+ }];
1928
+ } catch {
1929
+ return [{
1930
+ key: "",
1931
+ value: ""
1932
+ }];
1933
+ }
1934
+ };
1935
+ const pairsToJson = (pairs$1) => {
1936
+ const validPairs = pairs$1.filter((pair) => pair.key.trim() !== "" || pair.value.trim() !== "");
1937
+ if (validPairs.length === 0) return null;
1938
+ const obj = {};
1939
+ for (const pair of validPairs) if (pair.key.trim() !== "") obj[pair.key.trim()] = pair.value.trim();
1940
+ return Object.keys(obj).length === 0 ? null : JSON.stringify(obj);
1941
+ };
1942
+ const pairs = ref(parseJsonToPairs(props.modelValue));
1943
+ const isInternalUpdate = ref(false);
1944
+ watch(() => props.modelValue, (newValue) => {
1945
+ if (isInternalUpdate.value) return;
1946
+ const newPairs = parseJsonToPairs(newValue);
1947
+ if (pairsToJson(pairs.value) !== newValue) pairs.value = newPairs;
1948
+ }, { immediate: true });
1949
+ watch(pairs, async (newPairs) => {
1950
+ isInternalUpdate.value = true;
1951
+ emit("update:modelValue", pairsToJson(newPairs));
1952
+ await nextTick();
1953
+ isInternalUpdate.value = false;
1954
+ }, { deep: true });
1955
+ const addPair = () => {
1956
+ pairs.value.push({
1957
+ key: "",
1958
+ value: ""
1959
+ });
1960
+ };
1961
+ const removePair = (index) => {
1962
+ if (pairs.value.length > 1) pairs.value.splice(index, 1);
1963
+ else pairs.value[0] = {
1964
+ key: "",
1965
+ value: ""
1966
+ };
1967
+ };
1968
+ const updatePair = (index, field, value) => {
1969
+ const pair = pairs.value[index];
1970
+ if (!pair) return;
1971
+ pair[field] = value;
1972
+ };
1973
+ return (_ctx, _cache) => {
1974
+ return openBlock(), createElementBlock("div", _hoisted_1$14, [
1975
+ __props.label || __props.helpText ? (openBlock(), createElementBlock("div", _hoisted_2$12, [__props.label ? (openBlock(), createElementBlock("label", _hoisted_3$12, [createElementVNode("span", _hoisted_4$11, toDisplayString(__props.label), 1)])) : createCommentVNode("v-if", true), __props.helpText ? (openBlock(), createElementBlock("p", _hoisted_5$10, toDisplayString(__props.helpText), 1)) : createCommentVNode("v-if", true)])) : createCommentVNode("v-if", true),
1976
+ __props.error ? (openBlock(), createElementBlock("div", _hoisted_6$9, toDisplayString(__props.error), 1)) : createCommentVNode("v-if", true),
1977
+ createElementVNode("div", _hoisted_7$8, [
1978
+ createCommentVNode(" Key-Value Pairs List "),
1979
+ (openBlock(true), createElementBlock(Fragment, null, renderList(pairs.value, (pair, index) => {
1980
+ return openBlock(), createElementBlock("div", {
1981
+ key: index,
1982
+ class: "flex gap-2 items-start"
1983
+ }, [
1984
+ createElementVNode("div", _hoisted_8$8, [withDirectives(createElementVNode("input", {
1985
+ "onUpdate:modelValue": ($event) => pair.key = $event,
1986
+ type: "text",
1987
+ placeholder: __props.keyPlaceholder,
1988
+ class: "input input-bordered w-full",
1989
+ onInput: ($event) => updatePair(index, "key", $event.target.value)
1990
+ }, null, 40, _hoisted_9$7), [[vModelText, pair.key]])]),
1991
+ createElementVNode("div", _hoisted_10$4, [withDirectives(createElementVNode("input", {
1992
+ "onUpdate:modelValue": ($event) => pair.value = $event,
1993
+ type: "text",
1994
+ placeholder: __props.valuePlaceholder,
1995
+ class: "input input-bordered w-full",
1996
+ onInput: ($event) => updatePair(index, "value", $event.target.value)
1997
+ }, null, 40, _hoisted_11$4), [[vModelText, pair.value]])]),
1998
+ createElementVNode("button", {
1999
+ type: "button",
2000
+ class: "btn btn-error btn-sm",
2001
+ onClick: ($event) => removePair(index),
2002
+ disabled: pairs.value.length === 1 && !pair.key && !pair.value
2003
+ }, [..._cache[0] || (_cache[0] = [createElementVNode("svg", {
2004
+ xmlns: "http://www.w3.org/2000/svg",
2005
+ class: "h-4 w-4",
2006
+ fill: "none",
2007
+ viewBox: "0 0 24 24",
2008
+ stroke: "currentColor"
2009
+ }, [createElementVNode("path", {
2010
+ "stroke-linecap": "round",
2011
+ "stroke-linejoin": "round",
2012
+ "stroke-width": "2",
2013
+ d: "M6 18L18 6M6 6l12 12"
2014
+ })], -1)])], 8, _hoisted_12$4)
2015
+ ]);
2016
+ }), 128)),
2017
+ createCommentVNode(" Add New Pair Button "),
2018
+ createElementVNode("button", {
2019
+ type: "button",
2020
+ class: "btn btn-outline btn-sm w-full",
2021
+ onClick: addPair
2022
+ }, [..._cache[1] || (_cache[1] = [createElementVNode("svg", {
2023
+ xmlns: "http://www.w3.org/2000/svg",
2024
+ class: "h-4 w-4 mr-1",
2025
+ fill: "none",
2026
+ viewBox: "0 0 24 24",
2027
+ stroke: "currentColor"
2028
+ }, [createElementVNode("path", {
2029
+ "stroke-linecap": "round",
2030
+ "stroke-linejoin": "round",
2031
+ "stroke-width": "2",
2032
+ d: "M12 4v16m8-8H4"
2033
+ })], -1), createTextVNode(" Add Key-Value Pair ", -1)])])
2034
+ ]),
2035
+ createCommentVNode(" Empty State "),
2036
+ pairs.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_13$3, " No key-value pairs. Click \"Add Key-Value Pair\" to get started. ")) : createCommentVNode("v-if", true)
2037
+ ]);
2038
+ };
2039
+ }
2040
+ });
2041
+ var KeyValueEditor_default = /* @__PURE__ */ export_helper_default(_sfc_main$17, [["__scopeId", "data-v-010818ef"]]);
2042
+
2043
+ //#endregion
2044
+ //#region src/components/ui/SummarySection.vue
2045
+ const _hoisted_1$13 = { class: "bg-base-100 p-2 md:p-4 rounded-box overflow-hidden" };
2046
+ const _hoisted_2$11 = { class: "text-lg font-bold mb-2" };
2047
+ const _hoisted_3$11 = { class: "grid grid-auto-rows auto-rows-auto gap-x-4 gap-y-1" };
2048
+ const _sfc_main$16 = /* @__PURE__ */ defineComponent({
2049
+ __name: "SummarySection",
2050
+ props: { title: {} },
2051
+ setup(__props) {
2052
+ return (_ctx, _cache) => {
2053
+ return openBlock(), createElementBlock("div", _hoisted_1$13, [createElementVNode("h2", _hoisted_2$11, toDisplayString(__props.title), 1), createElementVNode("div", _hoisted_3$11, [renderSlot(_ctx.$slots, "default")])]);
2054
+ };
2055
+ }
2056
+ });
2057
+ var SummarySection_default = _sfc_main$16;
2058
+
2059
+ //#endregion
2060
+ //#region src/utils/timezones.ts
2061
+ const timezones = [
2062
+ {
2063
+ label: "",
2064
+ value: ""
2065
+ },
2066
+ {
2067
+ label: "US/Eastern (-04:00)",
2068
+ value: "US/Eastern (-04:00)"
2069
+ },
2070
+ {
2071
+ label: "US/East-Indiana (-04:00)",
2072
+ value: "US/East-Indiana (-04:00)"
2073
+ },
2074
+ {
2075
+ label: "US/Central (-05:00)",
2076
+ value: "US/Central (-05:00)"
2077
+ },
2078
+ {
2079
+ label: "US/Indiana-Starke (-05:00)",
2080
+ value: "US/Indiana-Starke (-05:00)"
2081
+ },
2082
+ {
2083
+ label: "US/Mountain (-06:00)",
2084
+ value: "US/Mountain (-06:00)"
2085
+ },
2086
+ {
2087
+ label: "US/Arizona (-07:00)",
2088
+ value: "US/Arizona (-07:00)"
2089
+ },
2090
+ {
2091
+ label: "US/Pacific (-07:00)",
2092
+ value: "US/Pacific (-07:00)"
2093
+ },
2094
+ {
2095
+ label: "US/Alaska (-08:00)",
2096
+ value: "US/Alaska (-08:00)"
2097
+ },
2098
+ {
2099
+ label: "US/Aleutian (-09:00)",
2100
+ value: "US/Aleutian (-09:00)"
2101
+ },
2102
+ {
2103
+ label: "US/Hawaii (-10:00)",
2104
+ value: "US/Hawaii (-10:00)"
2105
+ },
2106
+ {
2107
+ label: "US/Samoa (-11:00)",
2108
+ value: "US/Samoa (-11:00)"
2109
+ }
2110
+ ];
2111
+
2112
+ //#endregion
2113
+ //#region src/middleware/userAlreadyLoggedIn.ts
2114
+ const userAlreadyLoggedIn = (_to, _from, next) => {
2115
+ if (useUserSessionStore().currentSession) next("/");
2116
+ else next();
2117
+ };
2118
+
2119
+ //#endregion
2120
+ //#region src/slices/user/userRoutes.ts
2121
+ const userRoutes = [{
2122
+ path: "/user",
2123
+ name: "User",
2124
+ beforeEnter: [userAuthenticated],
2125
+ meta: { sectionTabs: [
2126
+ {
2127
+ id: "profile",
2128
+ label: "Profile",
2129
+ to: { name: "Profile" }
2130
+ },
2131
+ {
2132
+ id: "appearance",
2133
+ label: "Appearance",
2134
+ to: { name: "Appearance" }
2135
+ },
2136
+ {
2137
+ id: "password",
2138
+ label: "Password",
2139
+ to: { name: "Password" }
2140
+ }
2141
+ ] },
2142
+ children: [
2143
+ {
2144
+ path: "appearance",
2145
+ name: "Appearance",
2146
+ component: () => import("./Appearance-Ch4zfWZ3.js"),
2147
+ meta: {
2148
+ title: "Appearance",
2149
+ description: "Appearance page for Category 5 App"
2150
+ }
2151
+ },
2152
+ {
2153
+ path: "profile",
2154
+ name: "Profile",
2155
+ component: () => import("./UserProfilePage-DRbCAr9H.js"),
2156
+ meta: {
2157
+ title: "Profile",
2158
+ description: "Profile page for Category 5 App"
2159
+ }
2160
+ },
2161
+ {
2162
+ path: "password",
2163
+ name: "Password",
2164
+ component: () => import("./ChangePasswordPage-CYuCCosx.js"),
2165
+ meta: {
2166
+ title: "Password",
2167
+ description: "Password page for Category 5 App"
2168
+ }
2169
+ }
2170
+ ]
2171
+ }];
2172
+
2173
+ //#endregion
2174
+ //#region src/slices/team/components/TeamAttachments.vue
2175
+ const _sfc_main$15 = /* @__PURE__ */ defineComponent({
2176
+ __name: "TeamAttachments",
2177
+ setup(__props) {
2178
+ const teamId = useRoute().params.id;
2179
+ const config = { title: "Team Attachments" };
2180
+ return (_ctx, _cache) => {
2181
+ return openBlock(), createBlock(FileManager_default, {
2182
+ "record-id": unref(teamId),
2183
+ "record-type": "team",
2184
+ config
2185
+ }, null, 8, ["record-id"]);
2186
+ };
2187
+ }
2188
+ });
2189
+ var TeamAttachments_default = _sfc_main$15;
2190
+
2191
+ //#endregion
2192
+ //#region src/slices/team/TeamAttachmentsTab.vue
2193
+ const _sfc_main$14 = /* @__PURE__ */ defineComponent({
2194
+ __name: "TeamAttachmentsTab",
2195
+ setup(__props) {
2196
+ return (_ctx, _cache) => {
2197
+ return openBlock(), createBlock(TeamAttachments_default);
2198
+ };
2199
+ }
2200
+ });
2201
+ var TeamAttachmentsTab_default = _sfc_main$14;
2202
+
2203
+ //#endregion
2204
+ //#region src/slices/team/teamFiltersMetadata.ts
2205
+ const teamFiltersSchemaWithMetadata = withMetadata(TeamFiltersSchema, "teamFilters", {
2206
+ unique_name: {
2207
+ label: "Unique Name",
2208
+ inputType: "text"
2209
+ },
2210
+ display_name: {
2211
+ label: "Display Name",
2212
+ inputType: "text"
2213
+ },
2214
+ legal_name: {
2215
+ label: "Legal Name",
2216
+ inputType: "text"
2217
+ },
2218
+ contact_email: {
2219
+ label: "Contact Email",
2220
+ inputType: "text"
2221
+ },
2222
+ address_city: {
2223
+ label: "City",
2224
+ inputType: "text"
2225
+ },
2226
+ includeArchived: {
2227
+ label: "Include Archived",
2228
+ inputType: "checkbox"
2229
+ }
2230
+ });
2231
+
2232
+ //#endregion
2233
+ //#region src/slices/team/team_member/teamMemberFiltersMetadata.ts
2234
+ const teamMemberFiltersSchemaWithMetadata = withMetadata(TeamMemberFiltersSchema, "teamMemberFilters", {
2235
+ display_name: {
2236
+ label: "Display Name",
2237
+ inputType: "text"
2238
+ },
2239
+ role: {
2240
+ label: "Role",
2241
+ inputType: "text"
2242
+ },
2243
+ email_address: {
2244
+ label: "Email Address",
2245
+ inputType: "text"
2246
+ },
2247
+ business_phone: {
2248
+ label: "Business Phone",
2249
+ inputType: "text"
2250
+ },
2251
+ mobile_phone: {
2252
+ label: "Mobile Phone",
2253
+ inputType: "text"
2254
+ },
2255
+ time_zone: {
2256
+ label: "Time Zone",
2257
+ inputType: "text"
2258
+ }
2259
+ });
2260
+
2261
+ //#endregion
2262
+ //#region src/slices/support_ticket/customer/components/SupportTicketTimeline.vue
2263
+ const _hoisted_1$12 = { class: "mt-8" };
2264
+ const _hoisted_2$10 = { class: "relative" };
2265
+ const _hoisted_3$10 = { class: "space-y-6 relative" };
2266
+ const _hoisted_4$10 = { class: "flex items-start group" };
2267
+ const _hoisted_5$9 = { class: "ml-4" };
2268
+ const _hoisted_6$8 = { class: "text-xs text-base-content/40 mt-1" };
2269
+ const _hoisted_7$7 = { class: "flex items-start group" };
2270
+ const _hoisted_8$7 = {
2271
+ key: 0,
2272
+ xmlns: "http://www.w3.org/2000/svg",
2273
+ class: "h-4 w-4 animate-pulse",
2274
+ fill: "none",
2275
+ viewBox: "0 0 24 24",
2276
+ stroke: "currentColor"
2277
+ };
2278
+ const _hoisted_9$6 = {
2279
+ key: 1,
2280
+ xmlns: "http://www.w3.org/2000/svg",
2281
+ class: "h-4 w-4",
2282
+ fill: "none",
2283
+ viewBox: "0 0 24 24",
2284
+ stroke: "currentColor"
2285
+ };
2286
+ const _hoisted_10$3 = {
2287
+ key: 2,
2288
+ xmlns: "http://www.w3.org/2000/svg",
2289
+ class: "h-4 w-4",
2290
+ fill: "none",
2291
+ viewBox: "0 0 24 24",
2292
+ stroke: "currentColor"
2293
+ };
2294
+ const _hoisted_11$3 = { class: "ml-4" };
2295
+ const _hoisted_12$3 = { class: "font-medium text-base-content" };
2296
+ const _hoisted_13$2 = { class: "text-sm text-base-content/60" };
2297
+ const _hoisted_14$2 = {
2298
+ key: 0,
2299
+ class: "text-xs text-base-content/40 mt-1"
2300
+ };
2301
+ const _hoisted_15$1 = {
2302
+ key: 0,
2303
+ class: "flex items-start group"
2304
+ };
2305
+ const _hoisted_16$1 = { class: "ml-4" };
2306
+ const _sfc_main$13 = /* @__PURE__ */ defineComponent({
2307
+ __name: "SupportTicketTimeline",
2308
+ props: { support_ticket: {} },
2309
+ setup(__props) {
2310
+ return (_ctx, _cache) => {
2311
+ return openBlock(), createElementBlock("div", _hoisted_1$12, [_cache[8] || (_cache[8] = createElementVNode("h3", { class: "text-lg font-semibold text-base-content mb-4" }, "SupportTicket Progress", -1)), createElementVNode("div", _hoisted_2$10, [
2312
+ createCommentVNode(" Timeline line "),
2313
+ _cache[7] || (_cache[7] = createElementVNode("div", { class: "absolute left-4 top-0 bottom-0 w-0.5 bg-base-300" }, null, -1)),
2314
+ createCommentVNode(" Timeline items "),
2315
+ createElementVNode("div", _hoisted_3$10, [
2316
+ createCommentVNode(" Submitted "),
2317
+ createElementVNode("div", _hoisted_4$10, [createElementVNode("div", { class: normalizeClass(["flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center z-10", {
2318
+ "bg-primary text-primary-content": [
2319
+ "PENDING",
2320
+ "FOLLOWUP",
2321
+ "IN_PROGRESS",
2322
+ "COMPLETED",
2323
+ "CANCELLED"
2324
+ ].includes(__props.support_ticket.status),
2325
+ "bg-base-300 text-base-content/40": ![
2326
+ "PENDING",
2327
+ "FOLLOWUP",
2328
+ "IN_PROGRESS",
2329
+ "COMPLETED",
2330
+ "CANCELLED"
2331
+ ].includes(__props.support_ticket.status)
2332
+ }]) }, [..._cache[0] || (_cache[0] = [createElementVNode("svg", {
2333
+ xmlns: "http://www.w3.org/2000/svg",
2334
+ class: "h-4 w-4",
2335
+ fill: "none",
2336
+ viewBox: "0 0 24 24",
2337
+ stroke: "currentColor"
2338
+ }, [createElementVNode("path", {
2339
+ "stroke-linecap": "round",
2340
+ "stroke-linejoin": "round",
2341
+ "stroke-width": "2",
2342
+ d: "M5 13l4 4L19 7"
2343
+ })], -1)])], 2), createElementVNode("div", _hoisted_5$9, [
2344
+ _cache[1] || (_cache[1] = createElementVNode("p", { class: "font-medium text-base-content" }, "Submitted", -1)),
2345
+ _cache[2] || (_cache[2] = createElementVNode("p", { class: "text-sm text-base-content/60" }, "Your ticket has been received", -1)),
2346
+ createElementVNode("p", _hoisted_6$8, toDisplayString(unref(formatSystemTimestamp)(__props.support_ticket.created_at)), 1)
2347
+ ])]),
2348
+ createCommentVNode(" Under Review / In Progress / Completed "),
2349
+ createElementVNode("div", _hoisted_7$7, [createElementVNode("div", { class: normalizeClass(["flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center z-10", {
2350
+ "bg-primary text-primary-content": [
2351
+ "FOLLOWUP",
2352
+ "IN_PROGRESS",
2353
+ "COMPLETED",
2354
+ "CANCELLED"
2355
+ ].includes(__props.support_ticket.status),
2356
+ "bg-base-300 text-base-content/40": ![
2357
+ "FOLLOWUP",
2358
+ "IN_PROGRESS",
2359
+ "COMPLETED",
2360
+ "CANCELLED"
2361
+ ].includes(__props.support_ticket.status)
2362
+ }]) }, [__props.support_ticket.status === "PENDING" ? (openBlock(), createElementBlock("svg", _hoisted_8$7, [..._cache[3] || (_cache[3] = [createElementVNode("path", {
2363
+ "stroke-linecap": "round",
2364
+ "stroke-linejoin": "round",
2365
+ "stroke-width": "2",
2366
+ d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
2367
+ }, null, -1)])])) : __props.support_ticket.status === "CANCELLED" ? (openBlock(), createElementBlock("svg", _hoisted_9$6, [..._cache[4] || (_cache[4] = [createElementVNode("path", {
2368
+ "stroke-linecap": "round",
2369
+ "stroke-linejoin": "round",
2370
+ "stroke-width": "2",
2371
+ d: "M6 18L18 6M6 6l12 12"
2372
+ }, null, -1)])])) : (openBlock(), createElementBlock("svg", _hoisted_10$3, [..._cache[5] || (_cache[5] = [createElementVNode("path", {
2373
+ "stroke-linecap": "round",
2374
+ "stroke-linejoin": "round",
2375
+ "stroke-width": "2",
2376
+ d: "M5 13l4 4L19 7"
2377
+ }, null, -1)])]))], 2), createElementVNode("div", _hoisted_11$3, [
2378
+ createElementVNode("p", _hoisted_12$3, [__props.support_ticket.status === "PENDING" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode("Under Review")], 64)) : __props.support_ticket.status === "FOLLOWUP" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode("Approved")], 64)) : __props.support_ticket.status === "IN_PROGRESS" ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createTextVNode("In Development")], 64)) : __props.support_ticket.status === "COMPLETED" ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [createTextVNode("Completed")], 64)) : __props.support_ticket.status === "CANCELLED" ? (openBlock(), createElementBlock(Fragment, { key: 4 }, [createTextVNode("Not Approved")], 64)) : createCommentVNode("v-if", true)]),
2379
+ createElementVNode("p", _hoisted_13$2, [__props.support_ticket.status === "PENDING" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode("Our team is reviewing your support_ticket")], 64)) : __props.support_ticket.status === "FOLLOWUP" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode("Your ticket has been approved and is in our backlog")], 64)) : __props.support_ticket.status === "IN_PROGRESS" ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createTextVNode("We're currently working on implementing this")], 64)) : __props.support_ticket.status === "COMPLETED" ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [createTextVNode("This has been implemented and deployed")], 64)) : __props.support_ticket.status === "CANCELLED" ? (openBlock(), createElementBlock(Fragment, { key: 4 }, [createTextVNode("This ticket wasn't approved for implementation")], 64)) : createCommentVNode("v-if", true)]),
2380
+ __props.support_ticket.updated_at && __props.support_ticket.updated_at !== __props.support_ticket.created_at ? (openBlock(), createElementBlock("p", _hoisted_14$2, toDisplayString(unref(formatSystemTimestamp)(__props.support_ticket.updated_at)), 1)) : createCommentVNode("v-if", true)
2381
+ ])]),
2382
+ createCommentVNode(" Credit Information "),
2383
+ unref(formatCustomerCreditValue)(__props.support_ticket.credit_value) !== "TBD" && unref(formatCustomerCreditValue)(__props.support_ticket.credit_value) !== "N/A" ? (openBlock(), createElementBlock("div", _hoisted_15$1, [createElementVNode("div", { class: normalizeClass(["flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center z-10", {
2384
+ "bg-error text-error-content": [
2385
+ "FOLLOWUP",
2386
+ "IN_PROGRESS",
2387
+ "COMPLETED",
2388
+ "CANCELLED"
2389
+ ].includes(__props.support_ticket.status),
2390
+ "bg-warning text-warning-content": ![
2391
+ "FOLLOWUP",
2392
+ "IN_PROGRESS",
2393
+ "COMPLETED",
2394
+ "CANCELLED"
2395
+ ].includes(__props.support_ticket.status)
2396
+ }]) }, [..._cache[6] || (_cache[6] = [createElementVNode("svg", {
2397
+ xmlns: "http://www.w3.org/2000/svg",
2398
+ class: "h-4 w-4",
2399
+ fill: "none",
2400
+ viewBox: "0 0 24 24",
2401
+ stroke: "currentColor"
2402
+ }, [createElementVNode("path", {
2403
+ "stroke-linecap": "round",
2404
+ "stroke-linejoin": "round",
2405
+ "stroke-width": "2",
2406
+ d: "M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
2407
+ })], -1)])], 2), createElementVNode("div", _hoisted_16$1, [createElementVNode("p", { class: normalizeClass(["font-medium", {
2408
+ "text-error": [
2409
+ "FOLLOWUP",
2410
+ "IN_PROGRESS",
2411
+ "COMPLETED",
2412
+ "CANCELLED"
2413
+ ].includes(__props.support_ticket.status),
2414
+ "text-warning": ![
2415
+ "FOLLOWUP",
2416
+ "IN_PROGRESS",
2417
+ "COMPLETED",
2418
+ "CANCELLED"
2419
+ ].includes(__props.support_ticket.status)
2420
+ }]) }, toDisplayString([
2421
+ "FOLLOWUP",
2422
+ "IN_PROGRESS",
2423
+ "COMPLETED",
2424
+ "CANCELLED"
2425
+ ].includes(__props.support_ticket.status) ? "Credit Deducted" : "Credit Will Be Deducted"), 3), createElementVNode("p", { class: normalizeClass(["text-sm", {
2426
+ "text-error/70": [
2427
+ "FOLLOWUP",
2428
+ "IN_PROGRESS",
2429
+ "COMPLETED",
2430
+ "CANCELLED"
2431
+ ].includes(__props.support_ticket.status),
2432
+ "text-warning/70": ![
2433
+ "FOLLOWUP",
2434
+ "IN_PROGRESS",
2435
+ "COMPLETED",
2436
+ "CANCELLED"
2437
+ ].includes(__props.support_ticket.status)
2438
+ }]) }, [[
2439
+ "FOLLOWUP",
2440
+ "IN_PROGRESS",
2441
+ "COMPLETED",
2442
+ "CANCELLED"
2443
+ ].includes(__props.support_ticket.status) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(unref(formatCustomerCreditValue)(__props.support_ticket.credit_value)) + " credit(s) have been deducted from your account for this ticket ", 1)], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(unref(formatCustomerCreditValue)(__props.support_ticket.credit_value)) + " credit(s) will be deducted from your account once this ticket is approved ", 1)], 64))], 2)])])) : createCommentVNode("v-if", true)
2444
+ ])
2445
+ ])]);
2446
+ };
2447
+ }
2448
+ });
2449
+ var SupportTicketTimeline_default = _sfc_main$13;
2450
+
2451
+ //#endregion
2452
+ //#region src/slices/support_ticket/shared/SupportTicketStatusBadge.vue
2453
+ const _hoisted_1$11 = ["aria-label"];
2454
+ const _sfc_main$12 = /* @__PURE__ */ defineComponent({
2455
+ __name: "SupportTicketStatusBadge",
2456
+ props: {
2457
+ status: {},
2458
+ size: { default: "md" },
2459
+ variant: { default: "default" }
2460
+ },
2461
+ setup(__props) {
2462
+ /**
2463
+ * SupportTicketStatusBadge - A reusable Vue component for displaying support ticket status
2464
+ * as color-coded badges with consistent DaisyUI styling and accessibility features.
2465
+ *
2466
+ * @example
2467
+ * <SupportTicketStatusBadge :status="'PENDING'" size="md" />
2468
+ * <SupportTicketStatusBadge :status="'COMPLETED'" size="sm" variant="outline" />
2469
+ */
2470
+ const props = __props;
2471
+ /**
2472
+ * Configuration for each status badge
2473
+ */
2474
+ const statusConfig = {
2475
+ PENDING: {
2476
+ color: "badge-warning",
2477
+ text: "Pending",
2478
+ ariaLabel: "Status: Pending review by support team"
2479
+ },
2480
+ FOLLOWUP: {
2481
+ color: "badge-info",
2482
+ text: "To Do",
2483
+ ariaLabel: "Status: Approved and ready to begin work"
2484
+ },
2485
+ IN_PROGRESS: {
2486
+ color: "badge-secondary",
2487
+ text: "Active",
2488
+ ariaLabel: "Status: Currently being worked on"
2489
+ },
2490
+ VERIFICATION: {
2491
+ color: "badge-warning",
2492
+ text: "Verification",
2493
+ ariaLabel: "Status: Please verify and leave a comment to confirm completion"
2494
+ },
2495
+ COMPLETED: {
2496
+ color: "badge-success",
2497
+ text: "Done",
2498
+ ariaLabel: "Status: Successfully completed"
2499
+ },
2500
+ CANCELLED: {
2501
+ color: "badge-error",
2502
+ text: "Cancelled",
2503
+ ariaLabel: "Status: Cancelled or rejected"
2504
+ }
2505
+ };
2506
+ const getStatusConfig = (status) => {
2507
+ const config$1 = statusConfig[status];
2508
+ if (!config$1) return {
2509
+ color: "badge-neutral",
2510
+ text: status || "Unknown",
2511
+ ariaLabel: `Status: ${status || "Unknown status"}`
2512
+ };
2513
+ return config$1;
2514
+ };
2515
+ const config = computed(() => getStatusConfig(props.status));
2516
+ const badgeClasses = computed(() => {
2517
+ const baseClasses = ["badge", "text-xs"];
2518
+ baseClasses.push(config.value.color);
2519
+ if (props.size === "sm") baseClasses.push("badge-sm", "text-xs");
2520
+ else if (props.size === "lg") baseClasses.push("badge-lg", "text-sm");
2521
+ else baseClasses.push("text-xs", "sm:text-sm");
2522
+ if (props.variant === "outline") baseClasses.push("badge-outline");
2523
+ return baseClasses.join(" ");
2524
+ });
2525
+ const displayText = computed(() => config.value.text);
2526
+ const ariaLabel = computed(() => config.value.ariaLabel);
2527
+ return (_ctx, _cache) => {
2528
+ return openBlock(), createElementBlock("div", {
2529
+ class: normalizeClass(badgeClasses.value),
2530
+ "aria-label": ariaLabel.value,
2531
+ role: "status"
2532
+ }, toDisplayString(displayText.value), 11, _hoisted_1$11);
2533
+ };
2534
+ }
2535
+ });
2536
+ var SupportTicketStatusBadge_default = _sfc_main$12;
2537
+
2538
+ //#endregion
2539
+ //#region src/slices/support_ticket/customer/customerSupportTicketRowSchema.ts
2540
+ /**
2541
+ * Reuse the existing CustomerSupportTicketReadSchema from the validation library
2542
+ * for the data table rows - don't recreate it!
2543
+ */
2544
+ const customerSupportTicketRowSchemaWithMetadata = withMetadata(CustomerSupportTicketReadSchema, "customerSupportTicketRow", {});
2545
+
2546
+ //#endregion
2547
+ //#region src/slices/support_ticket/customer/CustomerSupportTicketList.vue
2548
+ const _hoisted_1$10 = { class: "mt-2" };
2549
+ const _hoisted_2$9 = { class: "flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4 mb-4" };
2550
+ const _hoisted_3$9 = { class: "flex items-center gap-2" };
2551
+ const _hoisted_4$9 = { class: "flex gap-2" };
2552
+ const _sfc_main$11 = /* @__PURE__ */ defineComponent({
2553
+ __name: "CustomerSupportTicketList",
2554
+ setup(__props) {
2555
+ const route = useRoute();
2556
+ const router = useRouter();
2557
+ const savedFilters = useSavedFilters({
2558
+ context: "support_ticket_customer",
2559
+ routePath: "/support",
2560
+ getFiltersFromTable: () => extractFiltersFromQuery(route.query),
2561
+ applyFiltersToTable: (filters) => {
2562
+ router.push({ query: buildQueryWithFilters(route.query, filters) });
2563
+ }
2564
+ });
2565
+ const hasSupportFilters = computed(() => {
2566
+ const filters = extractFiltersFromQuery(route.query);
2567
+ return Object.keys(filters).length > 0;
2568
+ });
2569
+ const currentPaginationToken = ref();
2570
+ const cursorTable = useCursorDataTable(customerSupportTicketRowSchemaWithMetadata, {
2571
+ fetchData: async ({ cursor, pageSize, sort, filters, search }) => {
2572
+ const variables = {
2573
+ first: pageSize,
2574
+ sortBy: sort?.field ? sort.field : "updated_at",
2575
+ sortDirection: sort?.direction === "asc" ? "asc" : "desc",
2576
+ ...cursor ? { after: cursor } : {},
2577
+ ...currentPaginationToken.value ? { paginationToken: currentPaginationToken.value } : {},
2578
+ ...search ? { search: typeof search === "string" ? {
2579
+ query: search,
2580
+ searchableFields: [
2581
+ "display_id",
2582
+ "title",
2583
+ "description"
2584
+ ]
2585
+ } : search } : {},
2586
+ ...filters
2587
+ };
2588
+ if (!filters || !filters.status) variables.status = {
2589
+ operator: OPERATORS.IS_NOT_ONE_OF,
2590
+ values: ["COMPLETED", "CANCELLED"]
2591
+ };
2592
+ const result = await executeWithAuth(async (api) => {
2593
+ return await api.supportTickets.listTickets(variables ?? {});
2594
+ }, { refreshTokenHandler: getRefreshTokenHandler() });
2595
+ currentPaginationToken.value = result?.pageInfo?.paginationToken;
2596
+ return {
2597
+ data: result.items || [],
2598
+ hasNextPage: result.pageInfo?.hasNextPage ?? false,
2599
+ hasPreviousPage: result.pageInfo?.hasPreviousPage ?? false,
2600
+ prevPageCursor: result.pageInfo?.prevPageCursor ?? void 0,
2601
+ nextPageCursor: result.pageInfo?.nextPageCursor ?? void 0
2602
+ };
2603
+ },
2604
+ columns: {
2605
+ display_id: {
2606
+ label: "Ticket ID",
2607
+ field: "display_id",
2608
+ sortable: true
2609
+ },
2610
+ title: {
2611
+ label: "Title",
2612
+ field: "title",
2613
+ sortable: true
2614
+ },
2615
+ type: {
2616
+ label: "Type",
2617
+ field: "type",
2618
+ sortable: true,
2619
+ filterable: true,
2620
+ filterType: "select",
2621
+ filterOptions: [...SUPPORT_TICKET_TYPE_FILTER_OPTIONS]
2622
+ },
2623
+ priority: {
2624
+ label: "Priority",
2625
+ field: "priority",
2626
+ sortable: true,
2627
+ filterable: true,
2628
+ filterType: "select",
2629
+ filterOptions: [...SUPPORT_TICKET_PRIORITY_FILTER_OPTIONS]
2630
+ },
2631
+ status: {
2632
+ label: "Status",
2633
+ field: "status",
2634
+ filterable: true,
2635
+ filterType: "select",
2636
+ filterOptions: [...SUPPORT_TICKET_STATUS_FILTER_OPTIONS]
2637
+ },
2638
+ credit_value: {
2639
+ label: "Credits",
2640
+ field: "credit_value",
2641
+ sortable: false
2642
+ },
2643
+ target_at: {
2644
+ label: "Delivery Est.",
2645
+ field: "target_at",
2646
+ sortable: true
2647
+ },
2648
+ updated_at: {
2649
+ label: "Last Updated",
2650
+ field: "updated_at",
2651
+ sortable: true
2652
+ },
2653
+ created_by: {
2654
+ label: "Requester",
2655
+ field: "created_by",
2656
+ sortable: false,
2657
+ filterable: true,
2658
+ filterType: "select",
2659
+ filterOptionsLoader: async () => {
2660
+ return (await executeWithAuth((api) => api.supportTickets.getRequestorsForActiveTickets(), { refreshTokenHandler: getRefreshTokenHandler() }) || []).map((u) => ({
2661
+ value: u.id,
2662
+ label: u.email
2663
+ }));
2664
+ }
2665
+ }
2666
+ },
2667
+ actions: { items: [{
2668
+ key: "view",
2669
+ icon: ActionIcons.eye,
2670
+ size: "xs",
2671
+ variant: "primary",
2672
+ type: "link",
2673
+ href: (row) => `/support/${row.id}`
2674
+ }] },
2675
+ search: { searchableFields: ["title"] },
2676
+ pagination: { pageSize: 25 },
2677
+ autoLoad: false
2678
+ });
2679
+ const { ZiniaDataTable } = cursorTable;
2680
+ useDataTableUrlSync(cursorTable.table, router);
2681
+ const previousFilters = ref({});
2682
+ onMounted(() => {
2683
+ previousFilters.value = extractFiltersFromQuery(route.query);
2684
+ });
2685
+ watch(() => route.query, (query) => {
2686
+ const filters = extractFiltersFromQuery(query);
2687
+ const hadPrevious = Object.keys(previousFilters.value).length > 0;
2688
+ if (!(Object.keys(filters).length > 0) && hadPrevious) savedFilters.clearLastUsed();
2689
+ previousFilters.value = filters;
2690
+ });
2691
+ return (_ctx, _cache) => {
2692
+ const _component_router_link = resolveComponent("router-link");
2693
+ return openBlock(), createElementBlock("div", _hoisted_1$10, [createElementVNode("div", _hoisted_2$9, [createElementVNode("div", _hoisted_3$9, [createVNode(unref(SavedFilterPresets_default), {
2694
+ presets: unref(savedFilters).presets.value,
2695
+ "presets-loading": unref(savedFilters).presetsLoading.value,
2696
+ creating: unref(savedFilters).creating.value,
2697
+ deleting: unref(savedFilters).deleting.value,
2698
+ renaming: unref(savedFilters).updating.value,
2699
+ "has-filters": hasSupportFilters.value,
2700
+ "active-preset": unref(savedFilters).activePreset.value,
2701
+ "apply-preset": unref(savedFilters).applyPreset,
2702
+ "clear-preset": unref(savedFilters).clearPreset,
2703
+ "save-current-filters": unref(savedFilters).saveCurrentFilters,
2704
+ "rename-preset": unref(savedFilters).renamePreset,
2705
+ "remove-preset": unref(savedFilters).removePreset
2706
+ }, null, 8, [
2707
+ "presets",
2708
+ "presets-loading",
2709
+ "creating",
2710
+ "deleting",
2711
+ "renaming",
2712
+ "has-filters",
2713
+ "active-preset",
2714
+ "apply-preset",
2715
+ "clear-preset",
2716
+ "save-current-filters",
2717
+ "rename-preset",
2718
+ "remove-preset"
2719
+ ]), _cache[0] || (_cache[0] = createElementVNode("h1", { class: "text-xl sm:text-2xl font-bold" }, "Support Tickets", -1))]), createElementVNode("div", _hoisted_4$9, [createVNode(_component_router_link, {
2720
+ to: { name: "CustomerCreateSupportTicket" },
2721
+ class: "btn btn-primary btn-sm sm:btn-md"
2722
+ }, {
2723
+ default: withCtx(() => [..._cache[1] || (_cache[1] = [createTextVNode(" Create ", -1)])]),
2724
+ _: 1
2725
+ })])]), createVNode(unref(ZiniaDataTable), null, {
2726
+ "cell-displayId": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatTicketDisplayId)(row.display_id, row.display_id_prefix, row.id)), 1)]),
2727
+ "cell-type": withCtx(({ row }) => [createVNode(SupportTicketTypeBadge_default, {
2728
+ type: row.type,
2729
+ size: "sm"
2730
+ }, null, 8, ["type"])]),
2731
+ "cell-priority": withCtx(({ row }) => [createVNode(SupportTicketPriorityBadge_default, {
2732
+ priority: row.priority,
2733
+ size: "sm"
2734
+ }, null, 8, ["priority"])]),
2735
+ "cell-status": withCtx(({ row }) => [createVNode(SupportTicketStatusBadge_default, {
2736
+ status: row.status,
2737
+ size: "sm"
2738
+ }, null, 8, ["status"])]),
2739
+ "cell-credit_value": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatCustomerCreditValue)(row.credit_value)), 1)]),
2740
+ "cell-created_by": withCtx(({ row }) => [createTextVNode(toDisplayString(row.created_by_display_name), 1)]),
2741
+ "cell-target_at": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatUserDate)(row.target_at) || "TBD"), 1)]),
2742
+ "cell-created_at": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatSystemTimestamp)(row.created_at)), 1)]),
2743
+ "cell-updated_at": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatSystemTimestamp)(row.updated_at)), 1)]),
2744
+ "cell-actions": withCtx(({ row }) => [createVNode(_component_router_link, {
2745
+ to: {
2746
+ name: "EditSupportTicket",
2747
+ params: { id: row.id }
2748
+ },
2749
+ class: "btn btn-sm btn-primary"
2750
+ }, {
2751
+ default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode(" View ", -1)])]),
2752
+ _: 1
2753
+ }, 8, ["to"])]),
2754
+ _: 1
2755
+ })]);
2756
+ };
2757
+ }
2758
+ });
2759
+ var CustomerSupportTicketList_default = _sfc_main$11;
2760
+
2761
+ //#endregion
2762
+ //#region src/slices/support_ticket/staff/components/CustomerCreditBalance.vue
2763
+ const _hoisted_1$9 = {
2764
+ key: 0,
2765
+ class: "flex items-center gap-2"
2766
+ };
2767
+ const _hoisted_2$8 = { class: "space-y-2" };
2768
+ const _hoisted_3$8 = { class: "grid grid-cols-2 gap-2 text-sm" };
2769
+ const _hoisted_4$8 = { class: "font-bold ml-2" };
2770
+ const _hoisted_5$8 = { class: "font-bold ml-2" };
2771
+ const _hoisted_6$7 = {
2772
+ key: 0,
2773
+ class: "alert alert-warning alert-sm"
2774
+ };
2775
+ const _sfc_main$10 = /* @__PURE__ */ defineComponent({
2776
+ __name: "CustomerCreditBalance",
2777
+ setup(__props) {
2778
+ const { data, loading, error } = useQuery((api) => api.customer.getCreditBalance(), { staleTime: 120 * 1e3 });
2779
+ const creditBalance = computed(() => data.value ?? null);
2780
+ const totalAvailable = computed(() => {
2781
+ if (!creditBalance.value) return 0;
2782
+ return (parseFloat(creditBalance.value.monthly) || 0) + (parseFloat(creditBalance.value.rollover) || 0);
2783
+ });
2784
+ return (_ctx, _cache) => {
2785
+ return openBlock(), createElementBlock("div", null, [createCommentVNode(" Loading State "), unref(loading) ? (openBlock(), createElementBlock("div", _hoisted_1$9, [..._cache[0] || (_cache[0] = [createElementVNode("span", { class: "loading loading-spinner loading-sm" }, null, -1), createElementVNode("span", { class: "text-sm" }, "Loading customer balance...", -1)])])) : unref(error) ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" Error State "), _cache[1] || (_cache[1] = createElementVNode("div", { class: "alert alert-error alert-sm" }, [createElementVNode("span", { class: "text-xs" }, "Failed to load customer balance")], -1))], 2112)) : creditBalance.value ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" Balance Display "), createElementVNode("div", _hoisted_2$8, [
2786
+ createElementVNode("div", _hoisted_3$8, [createElementVNode("div", null, [_cache[2] || (_cache[2] = createElementVNode("span", { class: "text-base-content/60" }, "Monthly:", -1)), createElementVNode("span", _hoisted_4$8, toDisplayString(unref(formatStaffCreditValue)(creditBalance.value.monthly)), 1)]), createElementVNode("div", null, [_cache[3] || (_cache[3] = createElementVNode("span", { class: "text-base-content/60" }, "Rollover:", -1)), createElementVNode("span", _hoisted_5$8, toDisplayString(unref(formatStaffCreditValue)(creditBalance.value.rollover)), 1)])]),
2787
+ createElementVNode("div", { class: normalizeClass(["flex items-center justify-between p-2 rounded", {
2788
+ "bg-success/20": totalAvailable.value > 0,
2789
+ "bg-error/20": totalAvailable.value === 0
2790
+ }]) }, [_cache[4] || (_cache[4] = createElementVNode("span", { class: "text-sm font-medium" }, "Total Available:", -1)), createElementVNode("span", { class: normalizeClass(["font-bold", {
2791
+ "text-success": totalAvailable.value > 0,
2792
+ "text-error": totalAvailable.value === 0
2793
+ }]) }, toDisplayString(unref(formatStaffCreditValue)(totalAvailable.value.toString())), 3)], 2),
2794
+ createCommentVNode(" Warning if no balance "),
2795
+ totalAvailable.value === 0 ? (openBlock(), createElementBlock("div", _hoisted_6$7, [..._cache[5] || (_cache[5] = [createElementVNode("svg", {
2796
+ xmlns: "http://www.w3.org/2000/svg",
2797
+ class: "stroke-current shrink-0 h-4 w-4",
2798
+ fill: "none",
2799
+ viewBox: "0 0 24 24"
2800
+ }, [createElementVNode("path", {
2801
+ "stroke-linecap": "round",
2802
+ "stroke-linejoin": "round",
2803
+ "stroke-width": "2",
2804
+ d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
2805
+ })], -1), createElementVNode("span", { class: "text-xs" }, "Customer has insufficient credits", -1)])])) : createCommentVNode("v-if", true)
2806
+ ])], 2112)) : createCommentVNode("v-if", true)]);
2807
+ };
2808
+ }
2809
+ });
2810
+ var CustomerCreditBalance_default = _sfc_main$10;
2811
+
2812
+ //#endregion
2813
+ //#region src/slices/support_ticket/staff/components/ApproveRejectActions.vue
2814
+ const _hoisted_1$8 = {
2815
+ key: 0,
2816
+ class: "alert alert-warning py-2"
2817
+ };
2818
+ const _hoisted_2$7 = {
2819
+ key: 1,
2820
+ class: "space-y-3"
2821
+ };
2822
+ const _hoisted_3$7 = { class: "alert alert-info py-2" };
2823
+ const _hoisted_4$7 = { class: "text-xs sm:text-sm" };
2824
+ const _hoisted_5$7 = { class: "bg-base-100 rounded-lg p-3" };
2825
+ const _hoisted_6$6 = { class: "flex flex-col sm:flex-row gap-2 sm:gap-3 justify-end mt-4" };
2826
+ const _hoisted_7$6 = ["disabled"];
2827
+ const _hoisted_8$6 = {
2828
+ key: 0,
2829
+ class: "loading loading-spinner loading-sm mr-2"
2830
+ };
2831
+ const _hoisted_9$5 = ["disabled"];
2832
+ const _hoisted_10$2 = {
2833
+ key: 0,
2834
+ class: "loading loading-spinner loading-sm mr-2"
2835
+ };
2836
+ const _hoisted_11$2 = {
2837
+ key: 2,
2838
+ class: "alert alert-error mt-2 py-2"
2839
+ };
2840
+ const _hoisted_12$2 = { class: "text-sm" };
2841
+ const _hoisted_13$1 = { class: "alert alert-warning py-2" };
2842
+ const _hoisted_14$1 = { class: "text-xs sm:text-sm" };
2843
+ const _hoisted_15 = { class: "font-semibold" };
2844
+ const _hoisted_16 = {
2845
+ key: 0,
2846
+ class: "alert alert-info py-2"
2847
+ };
2848
+ const _hoisted_17 = { class: "text-xs sm:text-sm" };
2849
+ const _hoisted_18 = { class: "flex mt-4" };
2850
+ const _hoisted_19 = ["disabled"];
2851
+ const _hoisted_20 = {
2852
+ key: 0,
2853
+ class: "loading loading-spinner loading-sm mr-2"
2854
+ };
2855
+ const _hoisted_21 = {
2856
+ key: 1,
2857
+ class: "alert alert-error mt-2 py-2"
2858
+ };
2859
+ const _hoisted_22 = { class: "text-sm" };
2860
+ const _hoisted_23 = { class: "modal-box" };
2861
+ const _hoisted_24 = { class: "font-bold text-lg" };
2862
+ const _hoisted_25 = { class: "py-4 whitespace-pre-line" };
2863
+ const _hoisted_26 = { class: "modal-action" };
2864
+ const _hoisted_27 = { class: "modal-box" };
2865
+ const _hoisted_28 = { class: "form-control py-4" };
2866
+ const _hoisted_29 = ["disabled"];
2867
+ const _hoisted_30 = {
2868
+ key: 0,
2869
+ class: "alert alert-error py-2 mb-4"
2870
+ };
2871
+ const _hoisted_31 = { class: "text-sm" };
2872
+ const _hoisted_32 = { class: "modal-action" };
2873
+ const _hoisted_33 = ["disabled"];
2874
+ const _hoisted_34 = ["disabled"];
2875
+ const _hoisted_35 = {
2876
+ key: 0,
2877
+ class: "loading loading-spinner loading-xs mr-2"
2878
+ };
2879
+ const _sfc_main$9 = /* @__PURE__ */ defineComponent({
2880
+ __name: "ApproveRejectActions",
2881
+ props: { support_ticket: {} },
2882
+ emits: ["update"],
2883
+ setup(__props, { expose: __expose, emit: __emit }) {
2884
+ const props = __props;
2885
+ const emit = __emit;
2886
+ const error = ref(null);
2887
+ const confirmModal = ref(null);
2888
+ const modalTitle = ref("");
2889
+ const modalMessage = ref("");
2890
+ const modalButtonText = ref("");
2891
+ const modalButtonClass = ref("");
2892
+ const modalCallback = ref(null);
2893
+ const rejectModal = ref(null);
2894
+ const rejectReason = ref("");
2895
+ const rejectError = ref(null);
2896
+ const { mutate: approveSupportTicket, loading: isApproving } = useMutation((api, input) => api.supportTickets.approveTicket(input), { invalidate: /^support-tickets?:/ });
2897
+ const { mutate: rejectSupportTicket, loading: isRejecting } = useMutation((api, input) => api.supportTickets.rejectTicket(input), { invalidate: /^support-tickets?:/ });
2898
+ const { mutate: createNote } = useMutation((api, input) => api.notes.createNote(input), { invalidate: /^notes?:/ });
2899
+ const { mutate: revertSupportTicket, loading: isReverting } = useMutation((api, input) => api.supportTickets.revertTicket(input), { invalidate: /^support-tickets?:/ });
2900
+ const showApproveModal = () => {
2901
+ const creditDisplay = formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status);
2902
+ const credits = creditDisplay === "TBD" || creditDisplay === "N/A" ? 0 : parseFloat(creditDisplay);
2903
+ modalTitle.value = "Approve Support Ticket?";
2904
+ modalMessage.value = `This will:\n\n• Deduct ${credits.toFixed(2)} credits from customer\n• Lock the support ticket item\n• Start development workflow\n\nThis action cannot be undone.`;
2905
+ modalButtonText.value = "Approve & Deduct";
2906
+ modalButtonClass.value = "btn-success";
2907
+ modalCallback.value = handleApprove;
2908
+ confirmModal.value?.showModal();
2909
+ };
2910
+ const showRejectModal = () => {
2911
+ rejectReason.value = "";
2912
+ rejectError.value = null;
2913
+ rejectModal.value?.showModal();
2914
+ };
2915
+ const showRevertModal = () => {
2916
+ modalTitle.value = "Revert Support Ticket to PENDING?";
2917
+ const creditDisplay = formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status);
2918
+ modalMessage.value = "This will:\n\n• Change status back to PENDING\n• Unlock the support ticket item\n" + (creditDisplay !== "TBD" && creditDisplay !== "N/A" ? `• Refund ${creditDisplay} credits to customer\n` : "") + "• Allow the customer to edit again\n\nAre you sure you want to revert this decision?";
2919
+ modalButtonText.value = "Revert to PENDING";
2920
+ modalButtonClass.value = "btn-warning";
2921
+ modalCallback.value = handleRevert;
2922
+ confirmModal.value?.showModal();
2923
+ };
2924
+ const closeModal = () => {
2925
+ confirmModal.value?.close();
2926
+ modalCallback.value = null;
2927
+ };
2928
+ const closeRejectModal = () => {
2929
+ rejectModal.value?.close();
2930
+ rejectReason.value = "";
2931
+ rejectError.value = null;
2932
+ };
2933
+ const handleApprove = async () => {
2934
+ error.value = null;
2935
+ const creditValue = props.support_ticket.credit_value?.trim();
2936
+ if (!creditValue) {
2937
+ error.value = "Credit value is required to approve";
2938
+ toast.error("Credit value is required to approve");
2939
+ return;
2940
+ }
2941
+ closeModal();
2942
+ try {
2943
+ const response = await approveSupportTicket({
2944
+ id: props.support_ticket.id,
2945
+ credit_value: creditValue
2946
+ });
2947
+ toast.success(`Support Ticket approved! ${formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status)} credits deducted.`);
2948
+ emit("update", response);
2949
+ } catch (e) {
2950
+ const errorMessage = extractRpcErrorMessage(e, "Failed to approve");
2951
+ error.value = errorMessage;
2952
+ toast.error(errorMessage);
2953
+ }
2954
+ };
2955
+ const handleRejectWithReason = async () => {
2956
+ rejectError.value = null;
2957
+ error.value = null;
2958
+ try {
2959
+ const reasonTrimmed = rejectReason.value.trim();
2960
+ if (reasonTrimmed) await createNote({
2961
+ record_id: props.support_ticket.id,
2962
+ record_type: "support_ticket",
2963
+ body: reasonTrimmed,
2964
+ tag: null,
2965
+ is_internal: false
2966
+ });
2967
+ const response = await rejectSupportTicket({ id: props.support_ticket.id });
2968
+ toast.info("Support Ticket rejected.");
2969
+ closeRejectModal();
2970
+ emit("update", response);
2971
+ } catch (e) {
2972
+ const errorMessage = extractRpcErrorMessage(e, "Failed to reject");
2973
+ rejectError.value = errorMessage;
2974
+ toast.error(errorMessage);
2975
+ }
2976
+ };
2977
+ const handleRevert = async () => {
2978
+ error.value = null;
2979
+ closeModal();
2980
+ try {
2981
+ const response = await revertSupportTicket({ id: props.support_ticket.id });
2982
+ const creditDisplay = formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status);
2983
+ const refundMessage = creditDisplay !== "TBD" && creditDisplay !== "N/A" ? ` ${creditDisplay} credits refunded.` : "";
2984
+ toast.success(`Support Ticket reverted to PENDING.${refundMessage}`);
2985
+ emit("update", response);
2986
+ } catch (e) {
2987
+ const errorMessage = extractRpcErrorMessage(e, "Failed to revert");
2988
+ error.value = errorMessage;
2989
+ toast.error(errorMessage);
2990
+ }
2991
+ };
2992
+ __expose({
2993
+ showApproveModal,
2994
+ showRejectModal
2995
+ });
2996
+ return (_ctx, _cache) => {
2997
+ return openBlock(), createElementBlock(Fragment, null, [
2998
+ createCommentVNode(" Approve/Reject Section (PENDING only) "),
2999
+ props.support_ticket.approval_status === "PENDING" ? (openBlock(), createBlock(ZiniaContainer_default, { key: 0 }, {
3000
+ default: withCtx(() => [
3001
+ _cache[4] || (_cache[4] = createElementVNode("h3", { class: "card-title text-base sm:text-lg" }, "Approve or Reject", -1)),
3002
+ unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status) === "TBD" ? (openBlock(), createElementBlock("div", _hoisted_1$8, [..._cache[2] || (_cache[2] = [createElementVNode("div", null, [createElementVNode("svg", {
3003
+ xmlns: "http://www.w3.org/2000/svg",
3004
+ class: "stroke-current shrink-0 h-5 w-5",
3005
+ fill: "none",
3006
+ viewBox: "0 0 24 24"
3007
+ }, [createElementVNode("path", {
3008
+ "stroke-linecap": "round",
3009
+ "stroke-linejoin": "round",
3010
+ "stroke-width": "2",
3011
+ d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
3012
+ })]), createElementVNode("span", { class: "text-xs sm:text-sm" }, "You must set an estimate before approving")], -1)])])) : (openBlock(), createElementBlock("div", _hoisted_2$7, [
3013
+ createElementVNode("div", _hoisted_3$7, [createElementVNode("div", null, [createElementVNode("span", _hoisted_4$7, "Estimate: " + toDisplayString(unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status)) + " credits", 1)])]),
3014
+ createCommentVNode(" Show Customer's Credit Balance "),
3015
+ createElementVNode("div", _hoisted_5$7, [_cache[3] || (_cache[3] = createElementVNode("h4", { class: "text-xs sm:text-sm font-medium mb-2" }, "Customer Balance Check", -1)), createVNode(CustomerCreditBalance_default)])
3016
+ ])),
3017
+ createElementVNode("div", _hoisted_6$6, [createElementVNode("button", {
3018
+ class: "btn btn-error btn-outline w-full sm:w-auto order-2 sm:order-1",
3019
+ onClick: withModifiers(showRejectModal, ["prevent"]),
3020
+ disabled: unref(isApproving) || unref(isRejecting)
3021
+ }, [unref(isRejecting) ? (openBlock(), createElementBlock("span", _hoisted_8$6)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isRejecting) ? "Rejecting..." : "Reject"), 1)], 8, _hoisted_7$6), createElementVNode("button", {
3022
+ class: "btn btn-success w-full sm:w-auto order-1 sm:order-2",
3023
+ onClick: withModifiers(showApproveModal, ["prevent"]),
3024
+ disabled: unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status) === "TBD" || unref(isApproving) || unref(isRejecting)
3025
+ }, [unref(isApproving) ? (openBlock(), createElementBlock("span", _hoisted_10$2)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isApproving) ? "Approving..." : "Approve & Deduct"), 1)], 8, _hoisted_9$5)]),
3026
+ error.value ? (openBlock(), createElementBlock("div", _hoisted_11$2, [createElementVNode("span", _hoisted_12$2, toDisplayString(error.value), 1)])) : createCommentVNode("v-if", true)
3027
+ ]),
3028
+ _: 1
3029
+ })) : props.support_ticket.approval_status === "APPROVED" || props.support_ticket.approval_status === "REJECTED" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" Revert Section (APPROVED/REJECTED only) "), createVNode(ZiniaContainer_default, null, {
3030
+ default: withCtx(() => [
3031
+ _cache[9] || (_cache[9] = createElementVNode("h3", { class: "card-title text-base sm:text-lg" }, "Revert Decision", -1)),
3032
+ createElementVNode("div", _hoisted_13$1, [_cache[8] || (_cache[8] = createElementVNode("svg", {
3033
+ xmlns: "http://www.w3.org/2000/svg",
3034
+ class: "stroke-current shrink-0 h-5 w-5",
3035
+ fill: "none",
3036
+ viewBox: "0 0 24 24"
3037
+ }, [createElementVNode("path", {
3038
+ "stroke-linecap": "round",
3039
+ "stroke-linejoin": "round",
3040
+ "stroke-width": "2",
3041
+ d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
3042
+ })], -1)), createElementVNode("div", _hoisted_14$1, [_cache[7] || (_cache[7] = createElementVNode("div", { class: "font-medium mb-1" }, "Admin Action Required", -1)), createElementVNode("div", null, [
3043
+ _cache[5] || (_cache[5] = createTextVNode(" This support ticket is currently ", -1)),
3044
+ createElementVNode("span", _hoisted_15, toDisplayString(props.support_ticket.approval_status), 1),
3045
+ _cache[6] || (_cache[6] = createTextVNode(". You can revert it back to PENDING status. ", -1))
3046
+ ])])]),
3047
+ unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status) !== "TBD" && unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status) !== "N/A" ? (openBlock(), createElementBlock("div", _hoisted_16, [createElementVNode("div", _hoisted_17, " Credits: " + toDisplayString(unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status)) + " will be refunded to customer ", 1)])) : createCommentVNode("v-if", true),
3048
+ createElementVNode("div", _hoisted_18, [createElementVNode("button", {
3049
+ class: "btn btn-warning w-full sm:w-auto",
3050
+ onClick: withModifiers(showRevertModal, ["prevent"]),
3051
+ disabled: unref(isReverting)
3052
+ }, [unref(isReverting) ? (openBlock(), createElementBlock("span", _hoisted_20)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isReverting) ? "Reverting..." : "Revert to PENDING"), 1)], 8, _hoisted_19)]),
3053
+ error.value ? (openBlock(), createElementBlock("div", _hoisted_21, [createElementVNode("span", _hoisted_22, toDisplayString(error.value), 1)])) : createCommentVNode("v-if", true)
3054
+ ]),
3055
+ _: 1
3056
+ })], 2112)) : createCommentVNode("v-if", true),
3057
+ createCommentVNode(" Confirmation Modal (for Approve and Revert) "),
3058
+ createElementVNode("dialog", {
3059
+ ref_key: "confirmModal",
3060
+ ref: confirmModal,
3061
+ class: "modal"
3062
+ }, [createElementVNode("div", _hoisted_23, [
3063
+ createElementVNode("h3", _hoisted_24, toDisplayString(modalTitle.value), 1),
3064
+ createElementVNode("p", _hoisted_25, toDisplayString(modalMessage.value), 1),
3065
+ createElementVNode("div", _hoisted_26, [createElementVNode("button", {
3066
+ class: "btn btn-ghost",
3067
+ onClick: closeModal
3068
+ }, "Cancel"), createElementVNode("button", {
3069
+ class: normalizeClass(["btn", modalButtonClass.value]),
3070
+ onClick: _cache[0] || (_cache[0] = () => modalCallback.value?.())
3071
+ }, toDisplayString(modalButtonText.value), 3)])
3072
+ ]), createElementVNode("form", {
3073
+ method: "dialog",
3074
+ class: "modal-backdrop"
3075
+ }, [createElementVNode("button", { onClick: closeModal }, "close")])], 512),
3076
+ createCommentVNode(" Reject Modal with Reason "),
3077
+ createElementVNode("dialog", {
3078
+ ref_key: "rejectModal",
3079
+ ref: rejectModal,
3080
+ class: "modal"
3081
+ }, [createElementVNode("div", _hoisted_27, [
3082
+ _cache[11] || (_cache[11] = createElementVNode("h3", { class: "font-bold text-lg" }, "Reject Ticket", -1)),
3083
+ _cache[12] || (_cache[12] = createElementVNode("p", { class: "text-sm text-base-content/70 py-2" }, " This will lock the ticket and notify the requester. ", -1)),
3084
+ createElementVNode("div", _hoisted_28, [_cache[10] || (_cache[10] = createElementVNode("label", { class: "label" }, [createElementVNode("span", { class: "label-text text-sm font-medium" }, "Reason (optional)")], -1)), withDirectives(createElementVNode("textarea", {
3085
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => rejectReason.value = $event),
3086
+ class: "textarea textarea-bordered w-full",
3087
+ rows: "3",
3088
+ placeholder: "Why is this ticket being rejected?",
3089
+ disabled: unref(isRejecting)
3090
+ }, null, 8, _hoisted_29), [[vModelText, rejectReason.value]])]),
3091
+ rejectError.value ? (openBlock(), createElementBlock("div", _hoisted_30, [createElementVNode("span", _hoisted_31, toDisplayString(rejectError.value), 1)])) : createCommentVNode("v-if", true),
3092
+ createElementVNode("div", _hoisted_32, [createElementVNode("button", {
3093
+ class: "btn btn-ghost",
3094
+ onClick: closeRejectModal,
3095
+ disabled: unref(isRejecting)
3096
+ }, " Cancel ", 8, _hoisted_33), createElementVNode("button", {
3097
+ class: "btn btn-error",
3098
+ onClick: handleRejectWithReason,
3099
+ disabled: unref(isRejecting)
3100
+ }, [unref(isRejecting) ? (openBlock(), createElementBlock("span", _hoisted_35)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isRejecting) ? "Rejecting..." : "Reject Ticket"), 1)], 8, _hoisted_34)])
3101
+ ]), createElementVNode("form", {
3102
+ method: "dialog",
3103
+ class: "modal-backdrop"
3104
+ }, [createElementVNode("button", { onClick: closeRejectModal }, "close")])], 512)
3105
+ ], 64);
3106
+ };
3107
+ }
3108
+ });
3109
+ var ApproveRejectActions_default = _sfc_main$9;
3110
+
3111
+ //#endregion
3112
+ //#region src/slices/support_ticket/staff/components/CancelInternalTaskWorkflow.vue
3113
+ const _hoisted_1$7 = { class: "flex justify-end mt-4" };
3114
+ const _hoisted_2$6 = ["disabled"];
3115
+ const _hoisted_3$6 = {
3116
+ key: 0,
3117
+ class: "loading loading-spinner loading-sm mr-2"
3118
+ };
3119
+ const _hoisted_4$6 = {
3120
+ key: 0,
3121
+ class: "alert alert-error mt-2 py-2"
3122
+ };
3123
+ const _hoisted_5$6 = { class: "text-sm" };
3124
+ const _hoisted_6$5 = { class: "modal-box" };
3125
+ const _hoisted_7$5 = { class: "font-bold text-lg" };
3126
+ const _hoisted_8$5 = { class: "py-4 whitespace-pre-line" };
3127
+ const _hoisted_9$4 = { class: "modal-action" };
3128
+ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
3129
+ __name: "CancelInternalTaskWorkflow",
3130
+ props: { support_ticket: {} },
3131
+ emits: ["update"],
3132
+ setup(__props, { emit: __emit }) {
3133
+ const props = __props;
3134
+ const emit = __emit;
3135
+ const error = ref(null);
3136
+ const confirmModal = ref(null);
3137
+ const modalTitle = ref("");
3138
+ const modalMessage = ref("");
3139
+ const modalButtonText = ref("");
3140
+ const modalCallback = ref(null);
3141
+ const { mutate: cancelInternalTask, loading: isCancelling } = useMutation((api, input) => api.supportTickets.cancelInternalTask(input), { invalidate: /^support-tickets?:/ });
3142
+ const showCancelModal = () => {
3143
+ modalTitle.value = "Cancel Internal Task?";
3144
+ modalMessage.value = "This will:\n\n• Set dev lifecycle to CANCELLED\n• Record completion timestamp\n• Mark task as complete (terminal state)\n\nThis action cannot be undone. The task will remain in the system as cancelled.";
3145
+ modalButtonText.value = "Cancel Task";
3146
+ modalCallback.value = handleCancel;
3147
+ confirmModal.value?.showModal();
3148
+ };
3149
+ const handleCancel = async () => {
3150
+ error.value = null;
3151
+ try {
3152
+ const result = await cancelInternalTask({ id: props.support_ticket.id });
3153
+ toast.success("✅ Internal task cancelled successfully");
3154
+ closeModal();
3155
+ emit("update", result);
3156
+ } catch (err) {
3157
+ error.value = err instanceof Error ? err.message : "Failed to cancel task";
3158
+ toast.error(`❌ ${error.value}`);
3159
+ }
3160
+ };
3161
+ const closeModal = () => {
3162
+ confirmModal.value?.close();
3163
+ };
3164
+ return (_ctx, _cache) => {
3165
+ return openBlock(), createElementBlock(Fragment, null, [
3166
+ createCommentVNode(" Cancel Internal Task Section (INTERNAL only, not CANCELLED) "),
3167
+ props.support_ticket.approval_status === "INTERNAL" && props.support_ticket.dev_lifecycle !== "CANCELLED" ? (openBlock(), createBlock(ZiniaContainer_default, { key: 0 }, {
3168
+ default: withCtx(() => [
3169
+ _cache[1] || (_cache[1] = createElementVNode("h3", { class: "card-title text-base sm:text-lg" }, "Cancel Internal Task", -1)),
3170
+ _cache[2] || (_cache[2] = createElementVNode("div", { class: "alert alert-warning py-2" }, [createElementVNode("div", { class: "flex flex-col gap-2" }, [createElementVNode("span", { class: "font-semibold text-sm" }, "What happens when you cancel:"), createElementVNode("ul", { class: "text-xs sm:text-sm space-y-1 ml-4 list-disc" }, [
3171
+ createElementVNode("li", null, "Sets dev lifecycle to CANCELLED"),
3172
+ createElementVNode("li", null, "Records completion timestamp"),
3173
+ createElementVNode("li", null, "Task is marked as terminal state"),
3174
+ createElementVNode("li", null, "No credit impact (internal tasks have no credits)")
3175
+ ])])], -1)),
3176
+ createElementVNode("div", _hoisted_1$7, [createElementVNode("button", {
3177
+ class: "btn btn-error w-full sm:w-auto",
3178
+ onClick: withModifiers(showCancelModal, ["prevent"]),
3179
+ disabled: unref(isCancelling)
3180
+ }, [unref(isCancelling) ? (openBlock(), createElementBlock("span", _hoisted_3$6)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isCancelling) ? "Cancelling..." : "Cancel Internal Task"), 1)], 8, _hoisted_2$6)]),
3181
+ error.value ? (openBlock(), createElementBlock("div", _hoisted_4$6, [createElementVNode("span", _hoisted_5$6, toDisplayString(error.value), 1)])) : createCommentVNode("v-if", true)
3182
+ ]),
3183
+ _: 1
3184
+ })) : createCommentVNode("v-if", true),
3185
+ createCommentVNode(" Confirmation Modal "),
3186
+ createElementVNode("dialog", {
3187
+ ref_key: "confirmModal",
3188
+ ref: confirmModal,
3189
+ class: "modal"
3190
+ }, [createElementVNode("div", _hoisted_6$5, [
3191
+ createElementVNode("h3", _hoisted_7$5, toDisplayString(modalTitle.value), 1),
3192
+ createElementVNode("p", _hoisted_8$5, toDisplayString(modalMessage.value), 1),
3193
+ createElementVNode("div", _hoisted_9$4, [createElementVNode("button", {
3194
+ class: "btn btn-ghost",
3195
+ onClick: closeModal
3196
+ }, "Cancel"), createElementVNode("button", {
3197
+ class: "btn btn-error",
3198
+ onClick: _cache[0] || (_cache[0] = () => modalCallback.value?.())
3199
+ }, toDisplayString(modalButtonText.value), 1)])
3200
+ ]), createElementVNode("form", {
3201
+ method: "dialog",
3202
+ class: "modal-backdrop"
3203
+ }, [createElementVNode("button", { onClick: closeModal }, "close")])], 512)
3204
+ ], 64);
3205
+ };
3206
+ }
3207
+ });
3208
+ var CancelInternalTaskWorkflow_default = _sfc_main$8;
3209
+
3210
+ //#endregion
3211
+ //#region src/slices/support_ticket/staff/components/CompleteSupportTicketForm.vue
3212
+ const _hoisted_1$6 = { class: "text-xs sm:text-sm text-base-content/70 mb-4" };
3213
+ const _hoisted_2$5 = { class: "font-semibold" };
3214
+ const _hoisted_3$5 = { class: "text-xs sm:text-sm" };
3215
+ const _hoisted_4$5 = { class: "grid grid-cols-2 gap-2 sm:gap-4 mt-2" };
3216
+ const _hoisted_5$5 = { class: "bg-base-100 p-3 rounded-lg" };
3217
+ const _hoisted_6$4 = { class: "text-lg sm:text-xl font-bold text-primary" };
3218
+ const _hoisted_7$4 = { class: "bg-base-100 p-3 rounded-lg" };
3219
+ const _hoisted_8$4 = { class: "text-xs text-base-content/60" };
3220
+ const _sfc_main$7 = /* @__PURE__ */ defineComponent({
3221
+ __name: "CompleteSupportTicketForm",
3222
+ props: { support_ticket: {} },
3223
+ emits: ["update"],
3224
+ setup(__props, { emit: __emit }) {
3225
+ const props = __props;
3226
+ const emit = __emit;
3227
+ const { form, zinia, ZiniaSubmitButton, ZiniaForm } = useForm(withMetadata(CompleteSupportTicketSchema.pick({ delivered_value: true }), "completeSupportTicketSchema", { delivered_value: {
3228
+ inputType: "text",
3229
+ placeholder: "100.00"
3230
+ } }), {
3231
+ storeName: `complete-support_ticket-${props.support_ticket.id}`,
3232
+ persistToLocalStorage: false,
3233
+ renderStyle: "daisy_ui",
3234
+ initialValues: { delivered_value: formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status) === "TBD" || formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status) === "N/A" ? "0.00" : formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status) }
3235
+ });
3236
+ const { mutate: completeSupportTicket } = useMutation((api, input) => api.supportTickets.completeTicket(input), { invalidate: /^support-tickets?:/ });
3237
+ const isCompleted = computed(() => props.support_ticket.status === "COMPLETED");
3238
+ const getComparisonClass = () => {
3239
+ const creditDisplay = formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status);
3240
+ if (!form.values.delivered_value || creditDisplay === "TBD" || creditDisplay === "N/A") return "";
3241
+ const actual = parseFloat(form.values.delivered_value);
3242
+ const final = parseFloat(creditDisplay);
3243
+ if (isNaN(actual) || isNaN(final)) return "";
3244
+ if (actual > final) return "alert-warning";
3245
+ if (actual === final) return "alert-success";
3246
+ return "alert-info";
3247
+ };
3248
+ const getComparisonMessage = () => {
3249
+ const creditDisplay = formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status);
3250
+ if (!form.values.delivered_value || creditDisplay === "TBD" || creditDisplay === "N/A") return "";
3251
+ const actual = parseFloat(form.values.delivered_value);
3252
+ const final = parseFloat(creditDisplay);
3253
+ if (isNaN(actual) || isNaN(final)) return "";
3254
+ const variance = ((actual - final) / final * 100).toFixed(1);
3255
+ if (actual > final) return `📈 Took ${(actual - final).toFixed(2)} more effort (+${variance}%)`;
3256
+ else if (actual === final) return "🎯 Exactly on estimate!";
3257
+ else return `📉 Took ${(final - actual).toFixed(2)} less effort (${variance}%)`;
3258
+ };
3259
+ const getActualClass = () => {
3260
+ const creditDisplay = formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status);
3261
+ if (!props.support_ticket.delivered_value || creditDisplay === "TBD" || creditDisplay === "N/A") return "";
3262
+ const actual = parseFloat(props.support_ticket.delivered_value);
3263
+ const final = parseFloat(creditDisplay);
3264
+ if (isNaN(actual) || isNaN(final)) return "";
3265
+ if (actual > final) return "text-warning";
3266
+ if (actual === final) return "text-success";
3267
+ return "text-info";
3268
+ };
3269
+ const getVarianceLabel = () => {
3270
+ const creditDisplay = formatStaffCreditValue(props.support_ticket.credit_value, props.support_ticket.approval_status);
3271
+ if (!props.support_ticket.delivered_value || creditDisplay === "TBD" || creditDisplay === "N/A") return "Work performed";
3272
+ const actual = parseFloat(props.support_ticket.delivered_value);
3273
+ const final = parseFloat(creditDisplay);
3274
+ if (isNaN(actual) || isNaN(final)) return "Work performed";
3275
+ const diff = actual - final;
3276
+ const variance = (diff / final * 100).toFixed(1);
3277
+ if (diff > 0) return `+${variance}% over estimate`;
3278
+ if (diff < 0) return `${variance}% under estimate`;
3279
+ return "On target";
3280
+ };
3281
+ const handleComplete = async (formData) => {
3282
+ const response = await completeSupportTicket({
3283
+ id: props.support_ticket.id,
3284
+ delivered_value: formData.delivered_value
3285
+ });
3286
+ if (!response) throw new Error("Failed to complete support ticket");
3287
+ return response;
3288
+ };
3289
+ const handleSuccess = () => {
3290
+ toast.success("SupportTicket marked as completed!", {});
3291
+ emit("update");
3292
+ };
3293
+ const handleError = (error) => {
3294
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred.";
3295
+ toast.error(errorMessage);
3296
+ };
3297
+ return (_ctx, _cache) => {
3298
+ return ["APPROVED", "INTERNAL"].includes(props.support_ticket.approval_status) && !isCompleted.value ? (openBlock(), createBlock(unref(ZiniaForm), {
3299
+ key: 0,
3300
+ onHandleSubmit: handleComplete,
3301
+ onSuccess: handleSuccess,
3302
+ onError: handleError
3303
+ }, {
3304
+ default: withCtx(() => [
3305
+ _cache[2] || (_cache[2] = createElementVNode("h3", { class: "card-title text-base sm:text-lg" }, "Complete Support Ticket", -1)),
3306
+ createElementVNode("p", _hoisted_1$6, [
3307
+ _cache[0] || (_cache[0] = createTextVNode(" Customer was charged ", -1)),
3308
+ createElementVNode("span", _hoisted_2$5, toDisplayString(unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status)), 1),
3309
+ _cache[1] || (_cache[1] = createTextVNode(" credits ", -1))
3310
+ ]),
3311
+ createCommentVNode(" Delivered Value Field "),
3312
+ createVNode(unref(zinia).DeliveredValueField, {
3313
+ label: "Actual Effort Delivered",
3314
+ placeholder: "100.00",
3315
+ hint: "How much effort did it actually take?"
3316
+ }),
3317
+ createCommentVNode(" Variance Indicator "),
3318
+ unref(form).values.delivered_value && unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status) !== "TBD" && unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status) !== "N/A" ? (openBlock(), createElementBlock("div", {
3319
+ key: 0,
3320
+ class: normalizeClass(["alert py-2", getComparisonClass()])
3321
+ }, [createElementVNode("span", _hoisted_3$5, toDisplayString(getComparisonMessage()), 1)], 2)) : createCommentVNode("v-if", true),
3322
+ createVNode(unref(ZiniaSubmitButton), {
3323
+ submitText: "Mark as Completed",
3324
+ submittingText: "Completing..."
3325
+ })
3326
+ ]),
3327
+ _: 1
3328
+ })) : isCompleted.value ? (openBlock(), createBlock(ZiniaContainer_default, { key: 1 }, {
3329
+ default: withCtx(() => [_cache[6] || (_cache[6] = createElementVNode("h3", { class: "card-title text-success text-base sm:text-lg" }, "✅ Completed", -1)), createElementVNode("div", _hoisted_4$5, [createElementVNode("div", _hoisted_5$5, [
3330
+ _cache[3] || (_cache[3] = createElementVNode("div", { class: "text-xs text-base-content/70" }, "Customer Charged", -1)),
3331
+ createElementVNode("div", _hoisted_6$4, toDisplayString(unref(formatStaffCreditValue)(props.support_ticket.credit_value, props.support_ticket.approval_status)), 1),
3332
+ _cache[4] || (_cache[4] = createElementVNode("div", { class: "text-xs text-base-content/60" }, "Fixed price", -1))
3333
+ ]), createElementVNode("div", _hoisted_7$4, [
3334
+ _cache[5] || (_cache[5] = createElementVNode("div", { class: "text-xs text-base-content/70" }, "Actual Effort", -1)),
3335
+ createElementVNode("div", { class: normalizeClass(["text-lg sm:text-xl font-bold", getActualClass()]) }, toDisplayString(props.support_ticket.delivered_value ?? "N/A"), 3),
3336
+ createElementVNode("div", _hoisted_8$4, toDisplayString(getVarianceLabel()), 1)
3337
+ ])])]),
3338
+ _: 1
3339
+ })) : createCommentVNode("v-if", true);
3340
+ };
3341
+ }
3342
+ });
3343
+ var CompleteSupportTicketForm_default = _sfc_main$7;
3344
+
3345
+ //#endregion
3346
+ //#region src/slices/support_ticket/staff/components/ConvertToCustomerWorkflow.vue
3347
+ const _hoisted_1$5 = { class: "flex justify-end mt-4" };
3348
+ const _hoisted_2$4 = ["disabled"];
3349
+ const _hoisted_3$4 = {
3350
+ key: 0,
3351
+ class: "loading loading-spinner loading-sm mr-2"
3352
+ };
3353
+ const _hoisted_4$4 = {
3354
+ key: 0,
3355
+ class: "alert alert-error mt-2 py-2"
3356
+ };
3357
+ const _hoisted_5$4 = { class: "text-sm" };
3358
+ const _hoisted_6$3 = { class: "modal-box" };
3359
+ const _hoisted_7$3 = { class: "font-bold text-lg" };
3360
+ const _hoisted_8$3 = { class: "py-4 whitespace-pre-line" };
3361
+ const _hoisted_9$3 = { class: "modal-action" };
3362
+ const _sfc_main$6 = /* @__PURE__ */ defineComponent({
3363
+ __name: "ConvertToCustomerWorkflow",
3364
+ props: { support_ticket: {} },
3365
+ emits: ["update"],
3366
+ setup(__props, { emit: __emit }) {
3367
+ const props = __props;
3368
+ const emit = __emit;
3369
+ const error = ref(null);
3370
+ const confirmModal = ref(null);
3371
+ const modalTitle = ref("");
3372
+ const modalMessage = ref("");
3373
+ const modalButtonText = ref("");
3374
+ const modalCallback = ref(null);
3375
+ const { mutate: convertToCustomer, loading: isConverting } = useMutation((api, input) => api.supportTickets.convertToCustomer(input.id), { invalidate: /^support-tickets?:/ });
3376
+ const showConvertModal = () => {
3377
+ modalTitle.value = "Convert to Customer Support Ticket?";
3378
+ modalMessage.value = "This will:\n\n• Change status to PENDING\n• Require approval workflow\n• Clear dev lifecycle stage\n• Reset to planning phase\n\nYou'll need to set a credit estimate before the support ticket can be approved.";
3379
+ modalButtonText.value = "Convert to Customer Support Ticket";
3380
+ modalCallback.value = handleConvert;
3381
+ confirmModal.value?.showModal();
3382
+ };
3383
+ const handleConvert = async () => {
3384
+ error.value = null;
3385
+ try {
3386
+ const result = await convertToCustomer({ id: props.support_ticket.id });
3387
+ toast.success("✅ Converted to customer support ticket successfully");
3388
+ closeModal();
3389
+ emit("update", result);
3390
+ } catch (err) {
3391
+ error.value = err instanceof Error ? err.message : "Failed to convert";
3392
+ toast.error(`❌ ${error.value}`);
3393
+ }
3394
+ };
3395
+ const closeModal = () => {
3396
+ confirmModal.value?.close();
3397
+ };
3398
+ return (_ctx, _cache) => {
3399
+ return openBlock(), createElementBlock(Fragment, null, [
3400
+ createCommentVNode(" Convert to Customer SupportTicket Section (INTERNAL only) "),
3401
+ props.support_ticket.approval_status === "INTERNAL" ? (openBlock(), createBlock(ZiniaContainer_default, { key: 0 }, {
3402
+ default: withCtx(() => [
3403
+ _cache[1] || (_cache[1] = createElementVNode("h3", { class: "card-title text-base sm:text-lg" }, "Convert to Customer SupportTicket", -1)),
3404
+ _cache[2] || (_cache[2] = createElementVNode("div", { class: "alert alert-info py-2" }, [createElementVNode("div", { class: "flex flex-col gap-2" }, [createElementVNode("span", { class: "font-semibold text-sm" }, "What happens when you convert:"), createElementVNode("ul", { class: "text-xs sm:text-sm space-y-1 ml-4 list-disc" }, [
3405
+ createElementVNode("li", null, "Changes status from internal task to customer support_ticket"),
3406
+ createElementVNode("li", null, "Requires approval workflow (goes to PENDING)"),
3407
+ createElementVNode("li", null, "Clears dev lifecycle (back to planning)"),
3408
+ createElementVNode("li", null, "Staff must set credit estimate before approval")
3409
+ ])])], -1)),
3410
+ createElementVNode("div", _hoisted_1$5, [createElementVNode("button", {
3411
+ class: "btn btn-primary w-full sm:w-auto",
3412
+ onClick: withModifiers(showConvertModal, ["prevent"]),
3413
+ disabled: unref(isConverting)
3414
+ }, [unref(isConverting) ? (openBlock(), createElementBlock("span", _hoisted_3$4)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isConverting) ? "Converting..." : "Convert to Customer SupportTicket"), 1)], 8, _hoisted_2$4)]),
3415
+ error.value ? (openBlock(), createElementBlock("div", _hoisted_4$4, [createElementVNode("span", _hoisted_5$4, toDisplayString(error.value), 1)])) : createCommentVNode("v-if", true)
3416
+ ]),
3417
+ _: 1
3418
+ })) : createCommentVNode("v-if", true),
3419
+ createCommentVNode(" Confirmation Modal "),
3420
+ createElementVNode("dialog", {
3421
+ ref_key: "confirmModal",
3422
+ ref: confirmModal,
3423
+ class: "modal"
3424
+ }, [createElementVNode("div", _hoisted_6$3, [
3425
+ createElementVNode("h3", _hoisted_7$3, toDisplayString(modalTitle.value), 1),
3426
+ createElementVNode("p", _hoisted_8$3, toDisplayString(modalMessage.value), 1),
3427
+ createElementVNode("div", _hoisted_9$3, [createElementVNode("button", {
3428
+ class: "btn btn-ghost",
3429
+ onClick: closeModal
3430
+ }, "Cancel"), createElementVNode("button", {
3431
+ class: "btn btn-primary",
3432
+ onClick: _cache[0] || (_cache[0] = () => modalCallback.value?.())
3433
+ }, toDisplayString(modalButtonText.value), 1)])
3434
+ ]), createElementVNode("form", {
3435
+ method: "dialog",
3436
+ class: "modal-backdrop"
3437
+ }, [createElementVNode("button", { onClick: closeModal }, "close")])], 512)
3438
+ ], 64);
3439
+ };
3440
+ }
3441
+ });
3442
+ var ConvertToCustomerWorkflow_default = _sfc_main$6;
3443
+
3444
+ //#endregion
3445
+ //#region src/slices/support_ticket/staff/components/ConvertToInternalWorkflow.vue
3446
+ const _hoisted_1$4 = { class: "flex justify-end mt-4" };
3447
+ const _hoisted_2$3 = ["disabled"];
3448
+ const _hoisted_3$3 = {
3449
+ key: 0,
3450
+ class: "loading loading-spinner loading-sm mr-2"
3451
+ };
3452
+ const _hoisted_4$3 = {
3453
+ key: 0,
3454
+ class: "alert alert-error mt-2 py-2"
3455
+ };
3456
+ const _hoisted_5$3 = { class: "text-sm" };
3457
+ const _hoisted_6$2 = { class: "modal-box" };
3458
+ const _hoisted_7$2 = { class: "font-bold text-lg" };
3459
+ const _hoisted_8$2 = { class: "py-4 whitespace-pre-line" };
3460
+ const _hoisted_9$2 = { class: "modal-action" };
3461
+ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
3462
+ __name: "ConvertToInternalWorkflow",
3463
+ props: { support_ticket: {} },
3464
+ emits: ["update"],
3465
+ setup(__props, { emit: __emit }) {
3466
+ const props = __props;
3467
+ const emit = __emit;
3468
+ const error = ref(null);
3469
+ const confirmModal = ref(null);
3470
+ const modalTitle = ref("");
3471
+ const modalMessage = ref("");
3472
+ const modalButtonText = ref("");
3473
+ const modalCallback = ref(null);
3474
+ const { mutate: convertToInternal, loading: isConverting } = useMutation((api, input) => api.supportTickets.convertToInternal(input.id), { invalidate: /^support-tickets?:/ });
3475
+ const showConvertModal = () => {
3476
+ const hasCredits = props.support_ticket.credit_value && parseFloat(props.support_ticket.credit_value) > 0;
3477
+ modalTitle.value = "Convert to Internal Task?";
3478
+ modalMessage.value = "This will:\n\n• Change status to INTERNAL\n• Bypass customer approval workflow\n" + (hasCredits ? `• Clear ${props.support_ticket.credit_value} credit estimate\n` : "") + "• Set dev lifecycle to BACKLOG\n• Allow immediate development start\n\nThis change can be useful for converting customer ideas into internal improvements.";
3479
+ modalButtonText.value = "Convert to Internal";
3480
+ modalCallback.value = handleConvert;
3481
+ confirmModal.value?.showModal();
3482
+ };
3483
+ const handleConvert = async () => {
3484
+ error.value = null;
3485
+ try {
3486
+ const result = await convertToInternal({ id: props.support_ticket.id });
3487
+ toast.success("✅ Converted to internal task successfully");
3488
+ closeModal();
3489
+ emit("update", result);
3490
+ } catch (err) {
3491
+ error.value = err instanceof Error ? err.message : "Failed to convert";
3492
+ toast.error(`❌ ${error.value}`);
3493
+ }
3494
+ };
3495
+ const closeModal = () => {
3496
+ confirmModal.value?.close();
3497
+ };
3498
+ return (_ctx, _cache) => {
3499
+ return openBlock(), createElementBlock(Fragment, null, [
3500
+ createCommentVNode(" Convert to Internal Section (PENDING only) "),
3501
+ props.support_ticket.approval_status === "PENDING" ? (openBlock(), createBlock(ZiniaContainer_default, { key: 0 }, {
3502
+ default: withCtx(() => [
3503
+ _cache[1] || (_cache[1] = createElementVNode("h3", { class: "card-title text-base sm:text-lg" }, "Convert to Internal Task", -1)),
3504
+ _cache[2] || (_cache[2] = createElementVNode("div", { class: "alert alert-info py-2" }, [createElementVNode("div", { class: "flex flex-col gap-2" }, [createElementVNode("span", { class: "font-semibold text-sm" }, "What happens when you convert:"), createElementVNode("ul", { class: "text-xs sm:text-sm space-y-1 ml-4 list-disc" }, [
3505
+ createElementVNode("li", null, "Changes status from customer support_ticket to internal task"),
3506
+ createElementVNode("li", null, "Bypasses approval workflow entirely"),
3507
+ createElementVNode("li", null, "Automatically clears any credit estimates (internal tasks = no credits)"),
3508
+ createElementVNode("li", null, "Can start development immediately")
3509
+ ])])], -1)),
3510
+ createElementVNode("div", _hoisted_1$4, [createElementVNode("button", {
3511
+ class: "btn btn-primary w-full sm:w-auto",
3512
+ onClick: withModifiers(showConvertModal, ["prevent"]),
3513
+ disabled: unref(isConverting)
3514
+ }, [unref(isConverting) ? (openBlock(), createElementBlock("span", _hoisted_3$3)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isConverting) ? "Converting..." : "Convert to Internal Task"), 1)], 8, _hoisted_2$3)]),
3515
+ error.value ? (openBlock(), createElementBlock("div", _hoisted_4$3, [createElementVNode("span", _hoisted_5$3, toDisplayString(error.value), 1)])) : createCommentVNode("v-if", true)
3516
+ ]),
3517
+ _: 1
3518
+ })) : createCommentVNode("v-if", true),
3519
+ createCommentVNode(" Confirmation Modal "),
3520
+ createElementVNode("dialog", {
3521
+ ref_key: "confirmModal",
3522
+ ref: confirmModal,
3523
+ class: "modal"
3524
+ }, [createElementVNode("div", _hoisted_6$2, [
3525
+ createElementVNode("h3", _hoisted_7$2, toDisplayString(modalTitle.value), 1),
3526
+ createElementVNode("p", _hoisted_8$2, toDisplayString(modalMessage.value), 1),
3527
+ createElementVNode("div", _hoisted_9$2, [createElementVNode("button", {
3528
+ class: "btn btn-ghost",
3529
+ onClick: closeModal
3530
+ }, "Cancel"), createElementVNode("button", {
3531
+ class: "btn btn-primary",
3532
+ onClick: _cache[0] || (_cache[0] = () => modalCallback.value?.())
3533
+ }, toDisplayString(modalButtonText.value), 1)])
3534
+ ]), createElementVNode("form", {
3535
+ method: "dialog",
3536
+ class: "modal-backdrop"
3537
+ }, [createElementVNode("button", { onClick: closeModal }, "close")])], 512)
3538
+ ], 64);
3539
+ };
3540
+ }
3541
+ });
3542
+ var ConvertToInternalWorkflow_default = _sfc_main$5;
3543
+
3544
+ //#endregion
3545
+ //#region src/slices/support_ticket/staff/components/ReactivateInternalTaskWorkflow.vue
3546
+ const _hoisted_1$3 = { class: "alert alert-info py-2" };
3547
+ const _hoisted_2$2 = { class: "flex flex-col gap-2" };
3548
+ const _hoisted_3$2 = { class: "text-xs sm:text-sm space-y-1 ml-4 list-disc" };
3549
+ const _hoisted_4$2 = { key: 0 };
3550
+ const _hoisted_5$2 = { key: 1 };
3551
+ const _hoisted_6$1 = { class: "flex justify-end mt-4" };
3552
+ const _hoisted_7$1 = ["disabled"];
3553
+ const _hoisted_8$1 = {
3554
+ key: 0,
3555
+ class: "loading loading-spinner loading-sm mr-2"
3556
+ };
3557
+ const _hoisted_9$1 = {
3558
+ key: 0,
3559
+ class: "alert alert-error mt-2 py-2"
3560
+ };
3561
+ const _hoisted_10$1 = { class: "text-sm" };
3562
+ const _hoisted_11$1 = { class: "modal-box" };
3563
+ const _hoisted_12$1 = { class: "font-bold text-lg" };
3564
+ const _hoisted_13 = { class: "py-4 whitespace-pre-line" };
3565
+ const _hoisted_14 = { class: "modal-action" };
3566
+ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
3567
+ __name: "ReactivateInternalTaskWorkflow",
3568
+ props: { support_ticket: {} },
3569
+ emits: ["update"],
3570
+ setup(__props, { emit: __emit }) {
3571
+ const props = __props;
3572
+ const emit = __emit;
3573
+ const error = ref(null);
3574
+ const confirmModal = ref(null);
3575
+ const modalTitle = ref("");
3576
+ const modalMessage = ref("");
3577
+ const modalButtonText = ref("");
3578
+ const modalCallback = ref(null);
3579
+ const { mutate: reactivateInternalTask, loading: isReactivating } = useMutation((api, input) => api.supportTickets.reactivateInternalTask(input), { invalidate: /^support-tickets?:/ });
3580
+ const showReactivateModal = () => {
3581
+ const isCancelled = props.support_ticket.dev_lifecycle === "CANCELLED";
3582
+ const targetState = isCancelled ? "BACKLOG" : "PO_APPROVAL";
3583
+ modalTitle.value = `Reopen ${isCancelled ? "Cancelled" : "Deployed"} Task?`;
3584
+ modalMessage.value = `This will:\n\n• Set dev lifecycle to ${targetState}\n• Clear completion timestamp\n• Resume task as active\n\n` + (isCancelled ? `Task will restart from backlog for fresh planning.` : `Task will go to PO approval stage for review/fixes.`);
3585
+ modalButtonText.value = "Reopen Task";
3586
+ modalCallback.value = handleReactivate;
3587
+ confirmModal.value?.showModal();
3588
+ };
3589
+ const handleReactivate = async () => {
3590
+ error.value = null;
3591
+ try {
3592
+ const result = await reactivateInternalTask({ id: props.support_ticket.id });
3593
+ toast.success("✅ Internal task reopened successfully");
3594
+ closeModal();
3595
+ emit("update", result);
3596
+ } catch (err) {
3597
+ error.value = err instanceof Error ? err.message : "Failed to reactivate task";
3598
+ toast.error(`❌ ${error.value}`);
3599
+ }
3600
+ };
3601
+ const closeModal = () => {
3602
+ confirmModal.value?.close();
3603
+ };
3604
+ return (_ctx, _cache) => {
3605
+ return openBlock(), createElementBlock(Fragment, null, [
3606
+ createCommentVNode(" Reactivate Terminal Internal Task Section (INTERNAL + CANCELLED or DEPLOYED) "),
3607
+ props.support_ticket.approval_status === "INTERNAL" && (props.support_ticket.dev_lifecycle === "CANCELLED" || props.support_ticket.dev_lifecycle === "DEPLOYED") ? (openBlock(), createBlock(ZiniaContainer_default, { key: 0 }, {
3608
+ default: withCtx(() => [
3609
+ _cache[5] || (_cache[5] = createElementVNode("h3", { class: "card-title text-base sm:text-lg" }, "Reopen Internal Task", -1)),
3610
+ createElementVNode("div", _hoisted_1$3, [createElementVNode("div", _hoisted_2$2, [_cache[4] || (_cache[4] = createElementVNode("span", { class: "font-semibold text-sm" }, "What happens when you reopen:", -1)), createElementVNode("ul", _hoisted_3$2, [
3611
+ props.support_ticket.dev_lifecycle === "CANCELLED" ? (openBlock(), createElementBlock("li", _hoisted_4$2, " Sets dev lifecycle to BACKLOG (fresh start) ")) : (openBlock(), createElementBlock("li", _hoisted_5$2, "Sets dev lifecycle to PO_APPROVAL (ready for review/fixes)")),
3612
+ _cache[1] || (_cache[1] = createElementVNode("li", null, "Clears completion timestamp", -1)),
3613
+ _cache[2] || (_cache[2] = createElementVNode("li", null, "Task becomes active again", -1)),
3614
+ _cache[3] || (_cache[3] = createElementVNode("li", null, "You can resume work or make additional changes", -1))
3615
+ ])])]),
3616
+ createElementVNode("div", _hoisted_6$1, [createElementVNode("button", {
3617
+ class: "btn btn-success w-full sm:w-auto",
3618
+ onClick: withModifiers(showReactivateModal, ["prevent"]),
3619
+ disabled: unref(isReactivating)
3620
+ }, [unref(isReactivating) ? (openBlock(), createElementBlock("span", _hoisted_8$1)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isReactivating) ? "Reopening..." : "Reopen Task"), 1)], 8, _hoisted_7$1)]),
3621
+ error.value ? (openBlock(), createElementBlock("div", _hoisted_9$1, [createElementVNode("span", _hoisted_10$1, toDisplayString(error.value), 1)])) : createCommentVNode("v-if", true)
3622
+ ]),
3623
+ _: 1
3624
+ })) : createCommentVNode("v-if", true),
3625
+ createCommentVNode(" Confirmation Modal "),
3626
+ createElementVNode("dialog", {
3627
+ ref_key: "confirmModal",
3628
+ ref: confirmModal,
3629
+ class: "modal"
3630
+ }, [createElementVNode("div", _hoisted_11$1, [
3631
+ createElementVNode("h3", _hoisted_12$1, toDisplayString(modalTitle.value), 1),
3632
+ createElementVNode("p", _hoisted_13, toDisplayString(modalMessage.value), 1),
3633
+ createElementVNode("div", _hoisted_14, [createElementVNode("button", {
3634
+ class: "btn btn-ghost",
3635
+ onClick: closeModal
3636
+ }, "Cancel"), createElementVNode("button", {
3637
+ class: "btn btn-success",
3638
+ onClick: _cache[0] || (_cache[0] = () => modalCallback.value?.())
3639
+ }, toDisplayString(modalButtonText.value), 1)])
3640
+ ]), createElementVNode("form", {
3641
+ method: "dialog",
3642
+ class: "modal-backdrop"
3643
+ }, [createElementVNode("button", { onClick: closeModal }, "close")])], 512)
3644
+ ], 64);
3645
+ };
3646
+ }
3647
+ });
3648
+ var ReactivateInternalTaskWorkflow_default = _sfc_main$4;
3649
+
3650
+ //#endregion
3651
+ //#region src/slices/support_ticket/staff/StaffSupportTicketRowSchema.ts
3652
+ /**
3653
+ * Reuse the existing StaffSupportTicketReadSchema from the validation library
3654
+ * for the data table rows - don't recreate it!
3655
+ */
3656
+ const adminSupportTicketRowSchemaWithMetadata = withMetadata(StaffSupportTicketReadSchema, "adminSupportTicketRow", {});
3657
+
3658
+ //#endregion
3659
+ //#region src/slices/support_ticket/staff/StaffSupportTicketList.vue
3660
+ const _hoisted_1$2 = { class: "flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4 mb-4" };
3661
+ const _hoisted_2$1 = { class: "flex items-center gap-2" };
3662
+ const _hoisted_3$1 = { class: "flex flex-col sm:flex-row gap-2" };
3663
+ const _hoisted_4$1 = [
3664
+ "value",
3665
+ "disabled",
3666
+ "onChange"
3667
+ ];
3668
+ const _hoisted_5$1 = ["value"];
3669
+ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
3670
+ __name: "StaffSupportTicketList",
3671
+ setup(__props) {
3672
+ const route = useRoute();
3673
+ const router = useRouter();
3674
+ const savedFilters = useSavedFilters({
3675
+ context: "support_ticket_staff",
3676
+ routePath: "/staff/support",
3677
+ getFiltersFromTable: () => extractFiltersFromQuery(route.query),
3678
+ applyFiltersToTable: (filters) => {
3679
+ router.push({ query: buildQueryWithFilters(route.query, filters) });
3680
+ }
3681
+ });
3682
+ const hasSupportFilters = computed(() => {
3683
+ const filters = extractFiltersFromQuery(route.query);
3684
+ return Object.keys(filters).length > 0;
3685
+ });
3686
+ const currentPaginationToken = ref();
3687
+ const devLifecycleUpdateOptions = SUPPORT_TICKET_DEV_LIFECYCLE_FILTER_OPTIONS.filter((opt) => SupportTicketDevLifecycleUpdateEnum.includes(opt.value));
3688
+ const updatingDevLifecycleId = ref(null);
3689
+ const { mutate: updateTicket } = useMutation((api, input) => api.supportTickets.staffUpdateTicket(input), { invalidate: /^support-tickets?:/ });
3690
+ function canEditDevLifecycle(row) {
3691
+ if (!row.approval_status) return false;
3692
+ if (!["APPROVED", "INTERNAL"].includes(row.approval_status)) return false;
3693
+ const dl = row.dev_lifecycle;
3694
+ if (dl === "DEPLOYED" || dl === "CANCELLED") return false;
3695
+ return true;
3696
+ }
3697
+ const cursorTable = useCursorDataTable(adminSupportTicketRowSchemaWithMetadata, {
3698
+ fetchData: async ({ cursor, pageSize, sort, filters, search }) => {
3699
+ const variables = {
3700
+ first: pageSize,
3701
+ sortBy: sort?.field ? sort.field : "updated_at",
3702
+ sortDirection: sort?.direction === "asc" ? "asc" : "desc",
3703
+ ...cursor ? { after: cursor } : {},
3704
+ ...currentPaginationToken.value ? { paginationToken: currentPaginationToken.value } : {},
3705
+ ...search ? { search: typeof search === "string" ? {
3706
+ query: search,
3707
+ searchableFields: [
3708
+ "display_id",
3709
+ "title",
3710
+ "description"
3711
+ ]
3712
+ } : search } : {},
3713
+ ...filters
3714
+ };
3715
+ if (!filters || !filters.dev_lifecycle) variables.dev_lifecycle = {
3716
+ operator: OPERATORS.IS_NOT_ONE_OF,
3717
+ values: ["DEPLOYED", "CANCELLED"]
3718
+ };
3719
+ const result = await executeWithAuth(async (api) => {
3720
+ return await api.supportTickets.staffListTickets(variables ?? {});
3721
+ }, { refreshTokenHandler: getRefreshTokenHandler() });
3722
+ currentPaginationToken.value = result?.pageInfo?.paginationToken;
3723
+ return {
3724
+ data: result.items || [],
3725
+ hasNextPage: result.pageInfo?.hasNextPage ?? false,
3726
+ hasPreviousPage: result.pageInfo?.hasPreviousPage ?? false,
3727
+ prevPageCursor: result.pageInfo?.prevPageCursor ?? void 0,
3728
+ nextPageCursor: result.pageInfo?.nextPageCursor ?? void 0
3729
+ };
3730
+ },
3731
+ columns: {
3732
+ display_id: {
3733
+ label: "Ticket ID",
3734
+ field: "display_id",
3735
+ sortable: true
3736
+ },
3737
+ title: {
3738
+ label: "Title",
3739
+ field: "title",
3740
+ sortable: true
3741
+ },
3742
+ type: {
3743
+ label: "Type",
3744
+ field: "type",
3745
+ sortable: true,
3746
+ filterable: true,
3747
+ filterType: "select",
3748
+ filterOptions: [...SUPPORT_TICKET_TYPE_FILTER_OPTIONS]
3749
+ },
3750
+ priority: {
3751
+ label: "Priority",
3752
+ field: "priority",
3753
+ sortable: true,
3754
+ filterable: true,
3755
+ filterType: "select",
3756
+ filterOptions: [...SUPPORT_TICKET_PRIORITY_FILTER_OPTIONS]
3757
+ },
3758
+ approval_status: {
3759
+ label: "Approval",
3760
+ field: "approval_status",
3761
+ sortable: true,
3762
+ filterable: true,
3763
+ filterType: "select",
3764
+ filterOptions: [...SUPPORT_TICKET_APPROVAL_FILTER_OPTIONS]
3765
+ },
3766
+ dev_lifecycle: {
3767
+ label: "Dev Stage",
3768
+ field: "dev_lifecycle",
3769
+ sortable: true,
3770
+ filterable: true,
3771
+ filterType: "select",
3772
+ filterOptions: [...SUPPORT_TICKET_DEV_LIFECYCLE_FILTER_OPTIONS]
3773
+ },
3774
+ credit_value: {
3775
+ label: "Credits",
3776
+ field: "credit_value",
3777
+ sortable: false
3778
+ },
3779
+ target_at: {
3780
+ label: "Delivery Est.",
3781
+ field: "target_at",
3782
+ sortable: true
3783
+ },
3784
+ updated_at: {
3785
+ label: "Last Updated",
3786
+ field: "updated_at",
3787
+ sortable: true
3788
+ },
3789
+ created_by: {
3790
+ label: "Requester",
3791
+ field: "created_by",
3792
+ sortable: false,
3793
+ filterable: true,
3794
+ filterType: "select",
3795
+ filterOptionsLoader: async () => {
3796
+ return (await executeWithAuth((api) => api.supportTickets.staffGetRequestorsForActiveTickets(), { refreshTokenHandler: getRefreshTokenHandler() }) || []).map((u) => ({
3797
+ value: u.id,
3798
+ label: u.email
3799
+ }));
3800
+ }
3801
+ }
3802
+ },
3803
+ actions: { items: [{
3804
+ key: "manage",
3805
+ icon: ActionIcons.edit,
3806
+ size: "xs",
3807
+ variant: "primary",
3808
+ type: "link",
3809
+ href: (row) => `/staff/support/${row.id}`
3810
+ }] },
3811
+ search: { searchableFields: ["title"] },
3812
+ pagination: { pageSize: 25 },
3813
+ autoLoad: false
3814
+ });
3815
+ const { ZiniaDataTable, refresh } = cursorTable;
3816
+ async function handleDevLifecycleChange(row, newDevLifecycle) {
3817
+ if (!SupportTicketDevLifecycleUpdateEnum.includes(newDevLifecycle)) return;
3818
+ updatingDevLifecycleId.value = row.id;
3819
+ try {
3820
+ const priority = typeof row.priority === "number" ? row.priority : supportTicketPriorityToNumber(row.priority);
3821
+ await updateTicket({
3822
+ id: row.id,
3823
+ title: row.title,
3824
+ type: row.type,
3825
+ priority,
3826
+ dev_lifecycle: newDevLifecycle
3827
+ });
3828
+ await refresh();
3829
+ } catch {
3830
+ await refresh();
3831
+ } finally {
3832
+ updatingDevLifecycleId.value = null;
3833
+ }
3834
+ }
3835
+ useDataTableUrlSync(cursorTable.table, router);
3836
+ const previousFilters = ref({});
3837
+ onMounted(() => {
3838
+ previousFilters.value = extractFiltersFromQuery(route.query);
3839
+ });
3840
+ watch(() => route.query, (query) => {
3841
+ const filters = extractFiltersFromQuery(query);
3842
+ const hadPrevious = Object.keys(previousFilters.value).length > 0;
3843
+ if (!(Object.keys(filters).length > 0) && hadPrevious) savedFilters.clearLastUsed();
3844
+ previousFilters.value = filters;
3845
+ });
3846
+ return (_ctx, _cache) => {
3847
+ const _component_router_link = resolveComponent("router-link");
3848
+ return openBlock(), createElementBlock("div", null, [createElementVNode("div", _hoisted_1$2, [createElementVNode("div", _hoisted_2$1, [createVNode(unref(SavedFilterPresets_default), {
3849
+ presets: unref(savedFilters).presets.value,
3850
+ "presets-loading": unref(savedFilters).presetsLoading.value,
3851
+ creating: unref(savedFilters).creating.value,
3852
+ deleting: unref(savedFilters).deleting.value,
3853
+ renaming: unref(savedFilters).updating.value,
3854
+ "has-filters": hasSupportFilters.value,
3855
+ "active-preset": unref(savedFilters).activePreset.value,
3856
+ "apply-preset": unref(savedFilters).applyPreset,
3857
+ "clear-preset": unref(savedFilters).clearPreset,
3858
+ "save-current-filters": unref(savedFilters).saveCurrentFilters,
3859
+ "rename-preset": unref(savedFilters).renamePreset,
3860
+ "remove-preset": unref(savedFilters).removePreset
3861
+ }, null, 8, [
3862
+ "presets",
3863
+ "presets-loading",
3864
+ "creating",
3865
+ "deleting",
3866
+ "renaming",
3867
+ "has-filters",
3868
+ "active-preset",
3869
+ "apply-preset",
3870
+ "clear-preset",
3871
+ "save-current-filters",
3872
+ "rename-preset",
3873
+ "remove-preset"
3874
+ ]), _cache[0] || (_cache[0] = createElementVNode("h1", { class: "text-xl sm:text-2xl font-bold" }, "Manage Support Tickets", -1))]), createElementVNode("div", _hoisted_3$1, [createVNode(_component_router_link, {
3875
+ to: { name: "CustomerSupportTicketList" },
3876
+ class: "btn btn-outline btn-sm sm:btn-md"
3877
+ }, {
3878
+ default: withCtx(() => [..._cache[1] || (_cache[1] = [createTextVNode(" Customer View ", -1)])]),
3879
+ _: 1
3880
+ }), createVNode(_component_router_link, {
3881
+ to: { name: unref(staffSupportPaths).staff_create.name },
3882
+ class: "btn btn-primary btn-sm sm:btn-md"
3883
+ }, {
3884
+ default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode(" Create ", -1)])]),
3885
+ _: 1
3886
+ }, 8, ["to"])])]), createVNode(unref(ZiniaDataTable), null, {
3887
+ "cell-displayId": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatTicketDisplayId)(row.display_id, row.display_id_prefix, row.id)), 1)]),
3888
+ "cell-type": withCtx(({ row }) => [createVNode(SupportTicketTypeBadge_default, {
3889
+ type: row.type,
3890
+ size: "sm"
3891
+ }, null, 8, ["type"])]),
3892
+ "cell-priority": withCtx(({ row }) => [createVNode(SupportTicketPriorityBadge_default, {
3893
+ priority: row.priority,
3894
+ size: "sm"
3895
+ }, null, 8, ["priority"])]),
3896
+ "cell-approval_status": withCtx(({ row }) => [createVNode(SupportTicketApprovalBadge_default, {
3897
+ approvalStatus: row.approval_status,
3898
+ size: "sm"
3899
+ }, null, 8, ["approvalStatus"])]),
3900
+ "cell-dev_lifecycle": withCtx(({ row }) => [canEditDevLifecycle(row) ? (openBlock(), createElementBlock("select", {
3901
+ key: 0,
3902
+ value: row.dev_lifecycle,
3903
+ class: "select select-sm select-bordered min-w-[7rem]",
3904
+ disabled: updatingDevLifecycleId.value === row.id,
3905
+ onChange: ($event) => handleDevLifecycleChange(row, $event.target.value)
3906
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(devLifecycleUpdateOptions), (opt) => {
3907
+ return openBlock(), createElementBlock("option", {
3908
+ key: opt.value,
3909
+ value: opt.value
3910
+ }, toDisplayString(opt.label), 9, _hoisted_5$1);
3911
+ }), 128))], 40, _hoisted_4$1)) : (openBlock(), createBlock(SupportTicketDevLifecycleBadge_default, {
3912
+ key: 1,
3913
+ devLifecycle: row.dev_lifecycle || "PENDING",
3914
+ size: "sm"
3915
+ }, null, 8, ["devLifecycle"]))]),
3916
+ "cell-credit_value": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatStaffCreditValue)(row.credit_value, row.approval_status)), 1)]),
3917
+ "cell-created_by": withCtx(({ row }) => [createTextVNode(toDisplayString(row.created_by_display_name), 1)]),
3918
+ "cell-target_at": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatUserDate)(row.target_at) || "TBD"), 1)]),
3919
+ "cell-updated_at": withCtx(({ row }) => [createTextVNode(toDisplayString(unref(formatSystemTimestamp)(row.updated_at)), 1)]),
3920
+ "cell-actions": withCtx(({ row }) => [createVNode(_component_router_link, {
3921
+ to: {
3922
+ name: "AdminEditSupportTicket",
3923
+ params: { id: row.id }
3924
+ },
3925
+ class: "btn btn-sm btn-primary"
3926
+ }, {
3927
+ default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode(" Manage ", -1)])]),
3928
+ _: 1
3929
+ }, 8, ["to"])]),
3930
+ _: 1
3931
+ })]);
3932
+ };
3933
+ }
3934
+ });
3935
+ var StaffSupportTicketList_default = _sfc_main$3;
3936
+
3937
+ //#endregion
3938
+ //#region src/slices/support_ticket/shared/CreditBalanceWidget.vue
3939
+ const _hoisted_1$1 = {
3940
+ key: 0,
3941
+ class: "flex items-center gap-2"
3942
+ };
3943
+ const _hoisted_2 = { class: "flex items-center gap-2" };
3944
+ const _hoisted_3 = { class: "font-bold" };
3945
+ const _hoisted_4 = { class: "space-y-3" };
3946
+ const _hoisted_5 = {
3947
+ key: 0,
3948
+ class: "text-sm font-medium text-base-content/60"
3949
+ };
3950
+ const _hoisted_6 = { class: "space-y-2" };
3951
+ const _hoisted_7 = { class: "flex justify-between items-center p-3 bg-base-200 rounded-lg" };
3952
+ const _hoisted_8 = { class: "font-bold" };
3953
+ const _hoisted_9 = { class: "flex justify-between items-center p-3 bg-base-200 rounded-lg" };
3954
+ const _hoisted_10 = { class: "font-bold" };
3955
+ const _hoisted_11 = {
3956
+ key: 1,
3957
+ class: "alert alert-warning"
3958
+ };
3959
+ const _hoisted_12 = {
3960
+ key: 2,
3961
+ class: "pt-2"
3962
+ };
3963
+ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
3964
+ __name: "CreditBalanceWidget",
3965
+ props: {
3966
+ variant: { default: "full" },
3967
+ expandable: {
3968
+ type: Boolean,
3969
+ default: false
3970
+ },
3971
+ showTitle: {
3972
+ type: Boolean,
3973
+ default: true
3974
+ },
3975
+ showHistoryLink: {
3976
+ type: Boolean,
3977
+ default: true
3978
+ }
3979
+ },
3980
+ setup(__props) {
3981
+ const expanded = ref(false);
3982
+ const { data, loading, error } = useQuery((api) => api.customer.getCreditBalance(), { staleTime: 120 * 1e3 });
3983
+ const creditBalance = computed(() => data.value ?? null);
3984
+ const totalAvailable = computed(() => {
3985
+ if (!creditBalance.value) return 0;
3986
+ return (parseFloat(creditBalance.value.monthly) || 0) + (parseFloat(creditBalance.value.rollover) || 0);
3987
+ });
3988
+ return (_ctx, _cache) => {
3989
+ const _component_router_link = resolveComponent("router-link");
3990
+ return openBlock(), createElementBlock("div", { class: normalizeClass(["credit-balance-widget", __props.variant === "compact" ? "compact" : ""]) }, [createCommentVNode(" Loading State "), unref(loading) ? (openBlock(), createElementBlock("div", _hoisted_1$1, [..._cache[1] || (_cache[1] = [createElementVNode("span", { class: "loading loading-spinner loading-sm" }, null, -1), createElementVNode("span", { class: "text-sm" }, "Loading credits...", -1)])])) : unref(error) ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" Error State "), _cache[2] || (_cache[2] = createElementVNode("div", { class: "alert alert-error" }, [createElementVNode("span", { class: "text-sm" }, "Failed to load credit balance")], -1))], 2112)) : __props.variant === "compact" && creditBalance.value ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" Compact Variant (Badge) "), createElementVNode("div", _hoisted_2, [createElementVNode("div", { class: normalizeClass(["badge badge-lg gap-2", {
3991
+ "badge-success": totalAvailable.value > 0,
3992
+ "badge-warning": totalAvailable.value === 0
3993
+ }]) }, [_cache[3] || (_cache[3] = createElementVNode("svg", {
3994
+ xmlns: "http://www.w3.org/2000/svg",
3995
+ class: "h-4 w-4",
3996
+ fill: "none",
3997
+ viewBox: "0 0 24 24",
3998
+ stroke: "currentColor"
3999
+ }, [createElementVNode("path", {
4000
+ "stroke-linecap": "round",
4001
+ "stroke-linejoin": "round",
4002
+ "stroke-width": "2",
4003
+ d: "M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
4004
+ })], -1)), createElementVNode("span", _hoisted_3, toDisplayString(unref(formatStaffCreditValue)(totalAvailable.value.toString())) + " Credits", 1)], 2), __props.expandable ? (openBlock(), createElementBlock("button", {
4005
+ key: 0,
4006
+ class: "btn btn-ghost btn-xs",
4007
+ onClick: _cache[0] || (_cache[0] = ($event) => expanded.value = !expanded.value)
4008
+ }, toDisplayString(expanded.value ? "Hide" : "Details"), 1)) : createCommentVNode("v-if", true)])], 2112)) : creditBalance.value && (__props.variant === "full" || expanded.value) ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [createCommentVNode(" Expanded/Full Variant "), createElementVNode("div", _hoisted_4, [
4009
+ __props.showTitle ? (openBlock(), createElementBlock("h4", _hoisted_5, " Your Available Credits ")) : createCommentVNode("v-if", true),
4010
+ createElementVNode("div", _hoisted_6, [
4011
+ createElementVNode("div", _hoisted_7, [_cache[4] || (_cache[4] = createElementVNode("span", { class: "text-sm" }, "Monthly Balance:", -1)), createElementVNode("span", _hoisted_8, toDisplayString(unref(formatStaffCreditValue)(creditBalance.value.monthly)), 1)]),
4012
+ createElementVNode("div", _hoisted_9, [_cache[5] || (_cache[5] = createElementVNode("span", { class: "text-sm" }, "Rollover Balance:", -1)), createElementVNode("span", _hoisted_10, toDisplayString(unref(formatStaffCreditValue)(creditBalance.value.rollover)), 1)]),
4013
+ createElementVNode("div", { class: normalizeClass(["flex justify-between items-center p-3 rounded-lg", {
4014
+ "bg-success/20": totalAvailable.value > 0,
4015
+ "bg-warning/20": totalAvailable.value === 0
4016
+ }]) }, [_cache[6] || (_cache[6] = createElementVNode("span", { class: "font-medium" }, "Total Available:", -1)), createElementVNode("span", { class: normalizeClass(["text-xl font-bold", {
4017
+ "text-success": totalAvailable.value > 0,
4018
+ "text-warning": totalAvailable.value === 0
4019
+ }]) }, toDisplayString(unref(formatStaffCreditValue)(totalAvailable.value.toString())), 3)], 2)
4020
+ ]),
4021
+ createCommentVNode(" Warning if low balance "),
4022
+ totalAvailable.value === 0 ? (openBlock(), createElementBlock("div", _hoisted_11, [..._cache[7] || (_cache[7] = [createElementVNode("svg", {
4023
+ xmlns: "http://www.w3.org/2000/svg",
4024
+ class: "stroke-current shrink-0 h-6 w-6",
4025
+ fill: "none",
4026
+ viewBox: "0 0 24 24"
4027
+ }, [createElementVNode("path", {
4028
+ "stroke-linecap": "round",
4029
+ "stroke-linejoin": "round",
4030
+ "stroke-width": "2",
4031
+ d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
4032
+ })], -1), createElementVNode("div", null, [createElementVNode("h3", { class: "font-bold" }, "No Credits Available"), createElementVNode("p", { class: "text-sm" }, " Contact support to purchase more credits or wait for your monthly reset. ")], -1)])])) : createCommentVNode("v-if", true),
4033
+ createCommentVNode(" View History Link "),
4034
+ __props.showHistoryLink ? (openBlock(), createElementBlock("div", _hoisted_12, [createVNode(_component_router_link, {
4035
+ to: { name: "CreditTransactionHistory" },
4036
+ class: "link link-primary text-sm"
4037
+ }, {
4038
+ default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode(" View Credit History → ", -1)])]),
4039
+ _: 1
4040
+ })])) : createCommentVNode("v-if", true)
4041
+ ])], 2112)) : createCommentVNode("v-if", true)], 2);
4042
+ };
4043
+ }
4044
+ });
4045
+ var CreditBalanceWidget_default = /* @__PURE__ */ export_helper_default(_sfc_main$2, [["__scopeId", "data-v-703dc3c5"]]);
4046
+
4047
+ //#endregion
4048
+ //#region src/slices/support_ticket/shared/SupportTicketAttachments.vue
4049
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
4050
+ __name: "SupportTicketAttachments",
4051
+ setup(__props) {
4052
+ const supportTicketId = useRoute().params.id;
4053
+ const config = { title: "Support Ticket Attachments" };
4054
+ return (_ctx, _cache) => {
4055
+ return openBlock(), createBlock(FileManager_default, {
4056
+ "record-id": unref(supportTicketId),
4057
+ "record-type": "support_ticket",
4058
+ config
4059
+ }, null, 8, ["record-id"]);
4060
+ };
4061
+ }
4062
+ });
4063
+ var SupportTicketAttachments_default = _sfc_main$1;
4064
+
4065
+ //#endregion
4066
+ //#region src/slices/support_ticket/staff/staffSupportTicketFiltersMetadata.ts
4067
+ const adminSupportTicketFiltersSchemaWithMetadata = withMetadata(StaffSupportTicketFiltersSchema, "adminSupportTicketFilters", {
4068
+ type: {
4069
+ label: "Type",
4070
+ inputType: "select"
4071
+ },
4072
+ status: {
4073
+ label: "Status",
4074
+ inputType: "select"
4075
+ },
4076
+ approval_status: {
4077
+ label: "Approval Status",
4078
+ inputType: "select"
4079
+ },
4080
+ priority: {
4081
+ label: "Priority",
4082
+ inputType: "select"
4083
+ }
4084
+ });
4085
+
4086
+ //#endregion
4087
+ //#region src/slices/support_ticket/customer/composables/useSupportTicketPermissions.ts
4088
+ function useSupportTicketPermissions() {
4089
+ const canEditField = (field, status, isLocked) => {
4090
+ switch (status) {
4091
+ case "PENDING": return !isLocked;
4092
+ case "CANCELLED": return true;
4093
+ case "FOLLOWUP":
4094
+ case "IN_PROGRESS": return field === "priority";
4095
+ case "VERIFICATION": return false;
4096
+ default: return false;
4097
+ }
4098
+ };
4099
+ const canFullyEdit = (status, isLocked) => {
4100
+ return status === "PENDING" && !isLocked || status === "CANCELLED";
4101
+ };
4102
+ const getEditInfo = (status, isLocked) => {
4103
+ const canEditTitle = canEditField("title", status, isLocked);
4104
+ const canEditDescription = canEditField("description", status, isLocked);
4105
+ const canEditType = canEditField("type", status, isLocked);
4106
+ const canEditPriority = canEditField("priority", status, isLocked);
4107
+ const canSubmit = status !== "COMPLETED" && status !== "VERIFICATION";
4108
+ const showLimitation = !canFullyEdit(status, isLocked);
4109
+ let sectionTitle;
4110
+ let limitationMessage;
4111
+ let submitText;
4112
+ let submittingText;
4113
+ switch (status) {
4114
+ case "CANCELLED":
4115
+ sectionTitle = "Resubmit SupportTicket";
4116
+ limitationMessage = "";
4117
+ submitText = "Resubmit SupportTicket";
4118
+ submittingText = "Resubmitting...";
4119
+ break;
4120
+ case "FOLLOWUP":
4121
+ sectionTitle = "Limited Editing";
4122
+ limitationMessage = "This support ticket has been approved for development. Only priority can be adjusted.";
4123
+ submitText = "Update Priority";
4124
+ submittingText = "Updating Priority...";
4125
+ break;
4126
+ case "IN_PROGRESS":
4127
+ sectionTitle = "Limited Editing";
4128
+ limitationMessage = "Development is in progress. Only priority can be adjusted.";
4129
+ submitText = "Update Priority";
4130
+ submittingText = "Updating Priority...";
4131
+ break;
4132
+ case "VERIFICATION":
4133
+ sectionTitle = "Verify & Confirm";
4134
+ limitationMessage = "The work has been deployed. Please verify it meets your expectations and leave a comment below to confirm completion.";
4135
+ submitText = "Update SupportTicket";
4136
+ submittingText = "Updating SupportTicket...";
4137
+ break;
4138
+ case "PENDING":
4139
+ sectionTitle = "SupportTicket Details";
4140
+ limitationMessage = isLocked ? "This support ticket is locked for review." : "Editing is temporarily limited.";
4141
+ submitText = "Update SupportTicket";
4142
+ submittingText = "Updating SupportTicket...";
4143
+ break;
4144
+ default:
4145
+ sectionTitle = "SupportTicket Details";
4146
+ limitationMessage = "Editing is limited for this support ticket status.";
4147
+ submitText = "Update SupportTicket";
4148
+ submittingText = "Updating SupportTicket...";
4149
+ }
4150
+ return {
4151
+ sectionTitle,
4152
+ showLimitation,
4153
+ limitationMessage,
4154
+ canEditTitle,
4155
+ canEditDescription,
4156
+ canEditType,
4157
+ canEditPriority,
4158
+ canSubmit,
4159
+ submitText,
4160
+ submittingText
4161
+ };
4162
+ };
4163
+ const getSuccessMessage = (status) => {
4164
+ switch (status) {
4165
+ case "CANCELLED": return "SupportTicket resubmitted successfully! It will be reviewed again.";
4166
+ case "FOLLOWUP":
4167
+ case "IN_PROGRESS": return "Priority updated successfully!";
4168
+ default: return "SupportTicket updated successfully!";
4169
+ }
4170
+ };
4171
+ return {
4172
+ canEditField,
4173
+ canFullyEdit,
4174
+ getEditInfo,
4175
+ getSuccessMessage
4176
+ };
4177
+ }
4178
+
4179
+ //#endregion
4180
+ //#region src/slices/support_ticket/customer/composables/useSupportTicketStatus.ts
4181
+ function useSupportTicketStatus() {
4182
+ const getStatusInfo = (status, isLocked) => {
4183
+ switch (status) {
4184
+ case "FOLLOWUP": return {
4185
+ icon: "✅",
4186
+ label: "Approved - To Do",
4187
+ description: "Your support_ticket has been approved and is scheduled for development",
4188
+ alertClass: "alert-success",
4189
+ whatsNext: "Development will begin soon. You'll be notified of progress updates."
4190
+ };
4191
+ case "IN_PROGRESS": return {
4192
+ icon: "🔧",
4193
+ label: "In Development",
4194
+ description: "Development work is currently in progress",
4195
+ alertClass: "alert-warning",
4196
+ whatsNext: "Development is currently in progress. You'll be notified when completed."
4197
+ };
4198
+ case "VERIFICATION": return {
4199
+ icon: "🔍",
4200
+ label: "Verification",
4201
+ description: "The work has been deployed. Please verify it meets your expectations and leave a comment.",
4202
+ alertClass: "alert-warning",
4203
+ whatsNext: "Please verify the implementation and leave a comment to confirm completion. Once verified, our team will mark this ticket as complete."
4204
+ };
4205
+ case "COMPLETED": return {
4206
+ icon: "🎉",
4207
+ label: "Completed",
4208
+ description: "Your support ticket has been successfully implemented",
4209
+ alertClass: "alert-success",
4210
+ whatsNext: "Your support ticket has been completed! Thank you for your contribution."
4211
+ };
4212
+ case "CANCELLED": return {
4213
+ icon: "❌",
4214
+ label: "Cancelled",
4215
+ description: "This support ticket was not approved for development",
4216
+ alertClass: "alert-error",
4217
+ whatsNext: "You can modify and resubmit your support ticket with additional details."
4218
+ };
4219
+ case "PENDING": return {
4220
+ icon: "⏳",
4221
+ label: "Under Review",
4222
+ description: "Your support ticket is being reviewed by our team",
4223
+ alertClass: "alert-info",
4224
+ whatsNext: isLocked ? "Your support ticket is locked for review. Limited changes may be allowed." : "Our team is reviewing your support ticket. You can still make changes while it's under review."
4225
+ };
4226
+ default: return {
4227
+ icon: "ℹ️",
4228
+ label: status,
4229
+ description: "Status information",
4230
+ alertClass: "alert-info",
4231
+ whatsNext: "Check back for updates on your support ticket status."
4232
+ };
4233
+ }
4234
+ };
4235
+ const isEditable = (status, _isLocked) => {
4236
+ return status !== "COMPLETED" && status !== "VERIFICATION";
4237
+ };
4238
+ const isApproved = (status) => {
4239
+ return [
4240
+ "FOLLOWUP",
4241
+ "IN_PROGRESS",
4242
+ "VERIFICATION",
4243
+ "COMPLETED"
4244
+ ].includes(status);
4245
+ };
4246
+ const canResubmit = (status) => {
4247
+ return status === "CANCELLED";
4248
+ };
4249
+ return {
4250
+ getStatusInfo,
4251
+ isEditable,
4252
+ isApproved,
4253
+ canResubmit
4254
+ };
4255
+ }
4256
+
4257
+ //#endregion
4258
+ //#region src/middleware/skipVerifyEmailIfAlreadyVerified.ts
4259
+ /**
4260
+ * Redirects away from the verify-email page when the user is already logged in and verified.
4261
+ * Use as beforeEnter on the verify-email route.
4262
+ */
4263
+ const skipVerifyEmailIfAlreadyVerified = (to, _from, next) => {
4264
+ if (useUserSessionStore().currentSession?.user?.email_verified) next(getValidReturnUrl(to, "/"));
4265
+ else next();
4266
+ };
4267
+
4268
+ //#endregion
4269
+ //#region src/slices/auth/signupConsentsGuard.ts
4270
+ /**
4271
+ * Route guard for /auth/signup-consents.
4272
+ * Redirects to signup if there is no token in the query (e.g. direct URL, refresh).
4273
+ * The token is set when redirecting from Signup.vue after signupInitiate returns consents.
4274
+ */
4275
+ const signupConsentsGuard = (to, _from, next) => {
4276
+ const token = to.query.token;
4277
+ if (!token || token.trim() === "") {
4278
+ const returnUrl = to.query.returnUrl;
4279
+ next({
4280
+ path: "/auth/signup",
4281
+ query: returnUrl ? { returnUrl } : {}
4282
+ });
4283
+ } else next();
4284
+ };
4285
+
4286
+ //#endregion
4287
+ //#region src/slices/auth/authRoutes.ts
4288
+ const authPaths = {
4289
+ login: {
4290
+ path: "login",
4291
+ full_path: "/auth/login",
4292
+ name: "Login"
4293
+ },
4294
+ register: {
4295
+ path: "signup",
4296
+ full_path: "/auth/signup",
4297
+ name: "Signup"
4298
+ },
4299
+ forgot_password: {
4300
+ path: "forgot-password",
4301
+ full_path: "/auth/forgot-password",
4302
+ name: "Forgot Password"
4303
+ },
4304
+ reset_password: {
4305
+ path: "reset-password/:token",
4306
+ full_path: "/auth/reset-password",
4307
+ name: "Reset Password"
4308
+ },
4309
+ logout: {
4310
+ path: "logout",
4311
+ full_path: "/auth/logout",
4312
+ name: "Logout"
4313
+ },
4314
+ mfa_setup: {
4315
+ path: "mfa/setup",
4316
+ full_path: "/auth/mfa/setup",
4317
+ name: "MFA Setup"
4318
+ },
4319
+ mfa_verify: {
4320
+ path: "mfa/verify",
4321
+ full_path: "/auth/mfa/verify",
4322
+ name: "MFA Verify"
4323
+ },
4324
+ verify_email: {
4325
+ path: "verify-email",
4326
+ full_path: "/auth/verify-email",
4327
+ name: "Verify Email"
4328
+ },
4329
+ consent_required: {
4330
+ path: "consent-required",
4331
+ full_path: "/auth/consent-required",
4332
+ name: "Consent Required"
4333
+ },
4334
+ signup_consents: {
4335
+ path: "signup-consents",
4336
+ full_path: "/auth/signup-consents",
4337
+ name: "Signup Consents"
4338
+ }
4339
+ };
4340
+ const { allowUserSignup } = useEnv();
4341
+ const baseRoutes = [
4342
+ {
4343
+ path: authPaths.login.path,
4344
+ name: authPaths.login.name,
4345
+ component: () => import("./LoginForm-QFJ8NHww.js"),
4346
+ meta: {
4347
+ title: "Login",
4348
+ description: "Login page for Category 5 App"
4349
+ },
4350
+ beforeEnter: [userAlreadyLoggedIn]
4351
+ },
4352
+ {
4353
+ path: authPaths.forgot_password.path,
4354
+ name: authPaths.forgot_password.name,
4355
+ component: () => import("./ForgotPassword-tJVSg7PB.js"),
4356
+ meta: {
4357
+ title: "Forgot Password",
4358
+ description: "Forgot Password page for Category 5 App"
4359
+ },
4360
+ beforeEnter: [userAlreadyLoggedIn]
4361
+ },
4362
+ {
4363
+ path: authPaths.reset_password.path,
4364
+ name: authPaths.reset_password.name,
4365
+ component: () => import("./ResetPassword-D6to3G6a.js"),
4366
+ meta: {
4367
+ title: "Reset Password",
4368
+ description: "Reset Password page for Category 5 App"
4369
+ },
4370
+ beforeEnter: [userAlreadyLoggedIn]
4371
+ },
4372
+ {
4373
+ path: authPaths.logout.path,
4374
+ name: authPaths.logout.name,
4375
+ component: () => import("./Logout-CjDBff3W.js"),
4376
+ meta: {
4377
+ title: "Logout",
4378
+ description: "Logout page for Category 5 App"
4379
+ }
4380
+ },
4381
+ {
4382
+ path: authPaths.mfa_setup.path,
4383
+ name: authPaths.mfa_setup.name,
4384
+ component: () => import("./MfaSetup-BZcoxJx-.js"),
4385
+ meta: {
4386
+ title: "Set Up Two-Factor Authentication",
4387
+ description: "Enable MFA for your account"
4388
+ }
4389
+ },
4390
+ {
4391
+ path: authPaths.mfa_verify.path,
4392
+ name: authPaths.mfa_verify.name,
4393
+ component: () => import("./MfaVerify-Dy2aV5Gk.js"),
4394
+ meta: {
4395
+ title: "Verify Two-Factor Authentication",
4396
+ description: "Enter your MFA code to complete sign in"
4397
+ }
4398
+ },
4399
+ {
4400
+ path: authPaths.verify_email.path,
4401
+ name: authPaths.verify_email.name,
4402
+ component: () => import("./VerifyEmail-DMHczC9f.js"),
4403
+ meta: {
4404
+ title: "Verify Email",
4405
+ description: "Verify your email address"
4406
+ },
4407
+ beforeEnter: [skipVerifyEmailIfAlreadyVerified]
4408
+ },
4409
+ {
4410
+ path: "verify-email-required",
4411
+ redirect: (to) => ({
4412
+ path: "/auth/verify-email",
4413
+ query: to.query
4414
+ })
4415
+ },
4416
+ {
4417
+ path: authPaths.consent_required.path,
4418
+ name: authPaths.consent_required.name,
4419
+ component: () => import("./ConsentRequired-BPjsZoPY.js"),
4420
+ meta: {
4421
+ title: "Consent Required",
4422
+ description: "Accept updated terms to continue"
4423
+ }
4424
+ },
4425
+ {
4426
+ path: authPaths.signup_consents.path,
4427
+ name: authPaths.signup_consents.name,
4428
+ component: () => import("./SignupConsentFlow-X3kXuviv.js"),
4429
+ meta: {
4430
+ title: "Review and accept",
4431
+ description: "Review and accept terms to complete signup"
4432
+ },
4433
+ beforeEnter: [userAlreadyLoggedIn, signupConsentsGuard]
4434
+ }
4435
+ ];
4436
+ if (allowUserSignup) baseRoutes.splice(1, 0, {
4437
+ path: authPaths.register.path,
4438
+ name: authPaths.register.name,
4439
+ component: () => import("./Signup-XdImA1os.js"),
4440
+ meta: {
4441
+ title: "Register",
4442
+ description: "Register page for Category 5 App"
4443
+ },
4444
+ beforeEnter: [userAlreadyLoggedIn]
4445
+ });
4446
+ const authRoutes = baseRoutes;
4447
+
4448
+ //#endregion
4449
+ //#region src/slices/auth/features/login/SocialLoginButtons.vue
4450
+ const _hoisted_1 = { class: "social-login-buttons space-y-3" };
4451
+ const _sfc_main = /* @__PURE__ */ defineComponent({
4452
+ __name: "SocialLoginButtons",
4453
+ emits: ["login"],
4454
+ setup(__props) {
4455
+ return (_ctx, _cache) => {
4456
+ return openBlock(), createElementBlock("div", _hoisted_1, [
4457
+ createElementVNode("button", {
4458
+ type: "button",
4459
+ class: "btn btn-outline w-full",
4460
+ onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("login", "google"))
4461
+ }, [..._cache[3] || (_cache[3] = [createElementVNode("span", { class: "mr-2" }, "🔍", -1), createTextVNode(" Sign in with Google ", -1)])]),
4462
+ createElementVNode("button", {
4463
+ type: "button",
4464
+ class: "btn btn-outline w-full",
4465
+ onClick: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("login", "github"))
4466
+ }, [..._cache[4] || (_cache[4] = [createElementVNode("span", { class: "mr-2" }, "🐱", -1), createTextVNode(" Sign in with GitHub ", -1)])]),
4467
+ createElementVNode("button", {
4468
+ type: "button",
4469
+ class: "btn btn-outline w-full",
4470
+ onClick: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("login", "facebook"))
4471
+ }, [..._cache[5] || (_cache[5] = [createElementVNode("span", { class: "mr-2" }, "👤", -1), createTextVNode(" Sign in with Facebook ", -1)])])
4472
+ ]);
4473
+ };
4474
+ }
4475
+ });
4476
+ var SocialLoginButtons_default = _sfc_main;
4477
+
4478
+ //#endregion
4479
+ //#region src/slices/admin/adminRoutes.ts
4480
+ const adminRoutes = [{
4481
+ path: "/admin",
4482
+ name: "Admin",
4483
+ beforeEnter: [userIsSuperAdmin],
4484
+ meta: { sectionTabs: [
4485
+ {
4486
+ id: "create-user",
4487
+ label: "Create User",
4488
+ to: { name: "CreateUser" }
4489
+ },
4490
+ {
4491
+ id: "user-management",
4492
+ label: "User Management",
4493
+ to: { name: "UserManagement" }
4494
+ },
4495
+ {
4496
+ id: "support-staff",
4497
+ label: "Support Staff",
4498
+ to: { name: "SupportStaff" }
4499
+ },
4500
+ {
4501
+ id: "credit-management",
4502
+ label: "Credit Management",
4503
+ to: { name: "CreditManagement" }
4504
+ },
4505
+ {
4506
+ id: "support-ticket-maintenance",
4507
+ label: "Support Ticket Maintenance",
4508
+ to: { name: "SupportTicketMaintenance" }
4509
+ },
4510
+ {
4511
+ id: "signup-requirements",
4512
+ label: "Signup Requirements",
4513
+ to: { name: "SignupRequirements" }
4514
+ }
4515
+ ] },
4516
+ children: [
4517
+ {
4518
+ path: "create-user",
4519
+ name: "CreateUser",
4520
+ component: () => import("./CreateUserPage-C9uOeYDJ.js"),
4521
+ meta: {
4522
+ title: "Create User",
4523
+ description: "Create User page for Category 5 App",
4524
+ side_bar: {
4525
+ section: " Admin",
4526
+ visible_to: ["super_admin"]
4527
+ }
4528
+ }
4529
+ },
4530
+ {
4531
+ path: "support-staff",
4532
+ name: "SupportStaff",
4533
+ component: () => import("./SupportStaffPage-B69-kuvg.js"),
4534
+ meta: {
4535
+ title: "Support Staff",
4536
+ description: "Manage users who can triage support tickets",
4537
+ side_bar: {
4538
+ section: " Admin",
4539
+ visible_to: ["super_admin"]
4540
+ }
4541
+ }
4542
+ },
4543
+ {
4544
+ path: "users",
4545
+ name: "UserManagement",
4546
+ component: () => import("./UserListPage-Bmwg0an5.js"),
4547
+ meta: {
4548
+ title: "User Management",
4549
+ description: "View and manage all users",
4550
+ side_bar: {
4551
+ section: " Admin",
4552
+ visible_to: ["super_admin"]
4553
+ }
4554
+ }
4555
+ },
4556
+ {
4557
+ path: "users/:id/edit",
4558
+ name: "EditUser",
4559
+ component: () => import("./EditUserPage-C0K7EGjM.js"),
4560
+ meta: {
4561
+ title: "Edit User",
4562
+ description: "Edit user role",
4563
+ breadcrumbs: [
4564
+ {
4565
+ label: "Home",
4566
+ to: { name: "Home" }
4567
+ },
4568
+ {
4569
+ label: "User Management",
4570
+ to: { name: "UserManagement" }
4571
+ },
4572
+ {
4573
+ label: "Edit User",
4574
+ to: { name: "EditUser" }
4575
+ }
4576
+ ]
4577
+ }
4578
+ },
4579
+ {
4580
+ path: "credit-management",
4581
+ name: "CreditManagement",
4582
+ component: () => import("./CreditManagement-DXdIN-0d.js"),
4583
+ meta: {
4584
+ title: "Credit Management",
4585
+ description: "Manage customer credits and allocations",
4586
+ side_bar: {
4587
+ section: " Admin",
4588
+ visible_to: ["super_admin"]
4589
+ }
4590
+ }
4591
+ },
4592
+ {
4593
+ path: "support-ticket-maintenance",
4594
+ name: "SupportTicketMaintenance",
4595
+ component: () => import("./SupportTicketMaintenancePage-Bptja-xb.js"),
4596
+ meta: {
4597
+ title: "Support Ticket Maintenance",
4598
+ description: "Admin utilities for fixing support ticket data",
4599
+ side_bar: {
4600
+ section: " Admin",
4601
+ visible_to: ["super_admin"]
4602
+ }
4603
+ }
4604
+ },
4605
+ {
4606
+ path: "signup-requirements",
4607
+ name: "SignupRequirements",
4608
+ component: () => import("./SignupRequirementsPage-Cf-ElkEq.js"),
4609
+ meta: {
4610
+ title: "Signup Requirements",
4611
+ description: "Configure consent checkboxes shown on signup and consent-required flows",
4612
+ side_bar: {
4613
+ section: " Admin",
4614
+ visible_to: ["super_admin"]
4615
+ }
4616
+ }
4617
+ }
4618
+ ]
4619
+ }];
4620
+
4621
+ //#endregion
4622
+ //#region src/slices/customer/creditRoutes.ts
4623
+ const creditPaths = { credit_balance: {
4624
+ path: "",
4625
+ full_path: "/credits",
4626
+ name: "CreditBalanceDashboard"
4627
+ } };
4628
+ const creditRoutes = [{
4629
+ path: "/credits",
4630
+ name: "Credits",
4631
+ beforeEnter: [userAuthenticated, leadOrStaffOnly],
4632
+ meta: {},
4633
+ children: [{
4634
+ path: creditPaths.credit_balance.path,
4635
+ name: creditPaths.credit_balance.name,
4636
+ component: () => import("./CreditBalanceDashboard-0HiJE_OS.js"),
4637
+ meta: {
4638
+ title: "Credit Balance",
4639
+ description: "View your current credit balance and history.",
4640
+ side_bar: {
4641
+ section: "Support",
4642
+ visible_to: [
4643
+ "lead",
4644
+ "staff",
4645
+ "super_admin"
4646
+ ]
4647
+ }
4648
+ }
4649
+ }]
4650
+ }];
4651
+
4652
+ //#endregion
4653
+ export { timezones as A, UnverifiedEmailBanner_default as B, SupportTicketStatusBadge_default as C, TeamAttachmentsTab_default as D, teamFiltersSchemaWithMetadata as E, FieldDisplay_default as F, NotFound_default as G, Sidebar_default as H, InApp_default as I, InputModal_default as J, Navbar_default as K, Default_default as L, KeyValueEditor_default as M, FileManager_default as N, userRoutes as O, FieldGroup_default as P, Auth_default as R, customerSupportTicketRowSchemaWithMetadata as S, teamMemberFiltersSchemaWithMetadata as T, useBuildTag as U, useEmailVerificationGuard as V, RightSidebar_default as W, AppTabNavigation_default as X, AppHeader_default as Y, DragoncoreVue as Z, CompleteSupportTicketForm_default as _, authPaths as a, CustomerCreditBalance_default as b, useSupportTicketPermissions as c, CreditBalanceWidget_default as d, StaffSupportTicketList_default as f, ConvertToCustomerWorkflow_default as g, ConvertToInternalWorkflow_default as h, SocialLoginButtons_default as i, SummarySection_default as j, userAlreadyLoggedIn as k, adminSupportTicketFiltersSchemaWithMetadata as l, ReactivateInternalTaskWorkflow_default as m, creditRoutes as n, authRoutes as o, adminSupportTicketRowSchemaWithMetadata as p, LoginButton_default as q, adminRoutes as r, useSupportTicketStatus as s, creditPaths as t, SupportTicketAttachments_default as u, CancelInternalTaskWorkflow_default as v, SupportTicketTimeline_default as w, CustomerSupportTicketList_default as x, ApproveRejectActions_default as y, Admin_default as z };
4654
+ //# sourceMappingURL=src-DVe_0RO9.js.map