@dasidev/dasi-ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/README.md +346 -0
  2. package/bin/dasi-cli.cjs +184 -0
  3. package/dist/date-selector-test-BlukYeWl.js +91 -0
  4. package/dist/favicon.ico +0 -0
  5. package/dist/html2canvas.esm-CKxSAI8P.js +4886 -0
  6. package/dist/img/brand/ic_pln.svg +12 -0
  7. package/dist/img/brand/mapp_power_logo.svg +21 -0
  8. package/dist/img/common/pltu_ulumbu_flores_ntt.jpeg +0 -0
  9. package/dist/index-BQSA2aPs.js +126556 -0
  10. package/dist/index.es-DQWt-PZn.js +5769 -0
  11. package/dist/index.es.js +11 -0
  12. package/dist/index.umd.js +8564 -0
  13. package/dist/informasi-gudang-BmoEy2RL.js +164 -0
  14. package/dist/informasi-gudang-DXfS46Nh.js +50 -0
  15. package/dist/purify.es-C-9oolON.js +546 -0
  16. package/dist/scripts/pdf.worker.min.js +29 -0
  17. package/dist/scripts/pdf.worker.min.mjs +29 -0
  18. package/dist/scripts/pdf.worker.mjs +57722 -0
  19. package/dist/scripts/pdf.worker.mjs.map +1 -0
  20. package/dist/style.css +1 -0
  21. package/dist/test-schema-JFghGc0_.js +8 -0
  22. package/dist/test-schema-uusFsJe4.js +438 -0
  23. package/dist/types-l0sNRNKZ.js +1 -0
  24. package/package.json +178 -0
  25. package/src/App.vue +18 -0
  26. package/src/__tests__/index.test.ts +9 -0
  27. package/src/api/api.ts +117 -0
  28. package/src/assets/app-selector.svg +3 -0
  29. package/src/assets/dasi.png +0 -0
  30. package/src/assets/foto_ss.svg +21 -0
  31. package/src/assets/icons/circle-blue.svg +4 -0
  32. package/src/assets/icons/circle-gray.svg +15 -0
  33. package/src/assets/icons/circle-green.svg +4 -0
  34. package/src/assets/icons/circle-orange.svg +4 -0
  35. package/src/assets/icons/circle-purple.svg +4 -0
  36. package/src/assets/icons/circle-red.svg +15 -0
  37. package/src/assets/icons/harbor.svg +12 -0
  38. package/src/assets/icons/ic-box-red.svg +8 -0
  39. package/src/assets/icons/ic-chevron-right.svg +1 -0
  40. package/src/assets/icons/ic-loading.svg +9 -0
  41. package/src/assets/icons/ic-reset.svg +16 -0
  42. package/src/assets/icons/ic-sailing.svg +5 -0
  43. package/src/assets/icons/icon-app-selector.svg +3 -0
  44. package/src/assets/icons/icon-browser-check.svg +4 -0
  45. package/src/assets/icons/icon-calendar.svg +3 -0
  46. package/src/assets/icons/icon-chart-bar.svg +3 -0
  47. package/src/assets/icons/icon-chart-doc.svg +16 -0
  48. package/src/assets/icons/icon-chart-line.svg +10 -0
  49. package/src/assets/icons/icon-chart-mix.svg +15 -0
  50. package/src/assets/icons/icon-chart-pie.svg +11 -0
  51. package/src/assets/icons/icon-continue.svg +12 -0
  52. package/src/assets/icons/icon-dashboard-2.svg +17 -0
  53. package/src/assets/icons/icon-dashboard.svg +3 -0
  54. package/src/assets/icons/icon-data-kelistrikan.svg +19 -0
  55. package/src/assets/icons/icon-data-sentral.svg +11 -0
  56. package/src/assets/icons/icon-database.svg +5 -0
  57. package/src/assets/icons/icon-desktop.svg +3 -0
  58. package/src/assets/icons/icon-download.svg +13 -0
  59. package/src/assets/icons/icon-energi-primer.svg +12 -0
  60. package/src/assets/icons/icon-faba-apk2.svg +11 -0
  61. package/src/assets/icons/icon-faba.svg +11 -0
  62. package/src/assets/icons/icon-factory.svg +14 -0
  63. package/src/assets/icons/icon-globe-doc.svg +19 -0
  64. package/src/assets/icons/icon-ikk.svg +10 -0
  65. package/src/assets/icons/icon-kbb.svg +13 -0
  66. package/src/assets/icons/icon-kos.svg +16 -0
  67. package/src/assets/icons/icon-kpi-bod.svg +15 -0
  68. package/src/assets/icons/icon-kss.svg +14 -0
  69. package/src/assets/icons/icon-map.svg +12 -0
  70. package/src/assets/icons/icon-monitoring-harian.svg +13 -0
  71. package/src/assets/icons/icon-notification.svg +4 -0
  72. package/src/assets/icons/icon-overview.svg +17 -0
  73. package/src/assets/icons/icon-pltu.svg +13 -0
  74. package/src/assets/icons/icon-sebaran-sentral.svg +12 -0
  75. package/src/assets/icons/icon-select-data-kelistrikan.svg +19 -0
  76. package/src/assets/icons/icon-select-data-sentral.svg +11 -0
  77. package/src/assets/icons/icon-select-energi-primer.svg +12 -0
  78. package/src/assets/icons/icon-select-faba-apk2.svg +11 -0
  79. package/src/assets/icons/icon-select-ikk.svg +10 -0
  80. package/src/assets/icons/icon-select-kbb.svg +13 -0
  81. package/src/assets/icons/icon-select-kos.svg +16 -0
  82. package/src/assets/icons/icon-select-kpi-bod.svg +15 -0
  83. package/src/assets/icons/icon-select-kss.svg +14 -0
  84. package/src/assets/icons/icon-select-monitoring-harian.svg +13 -0
  85. package/src/assets/icons/icon-select-overview.svg +17 -0
  86. package/src/assets/icons/icon-select-sebaran-sentral.svg +12 -0
  87. package/src/assets/icons/icon-sentral-white.svg +13 -0
  88. package/src/assets/icons/icon-shipping.svg +5 -0
  89. package/src/assets/icons/icon-sort.svg +5 -0
  90. package/src/assets/icons/icon-tree-box.svg +14 -0
  91. package/src/assets/icons/icon-warehouse.svg +12 -0
  92. package/src/assets/icons/pin-green.svg +3 -0
  93. package/src/assets/icons/pin-orange.svg +3 -0
  94. package/src/assets/icons/pin-purple.svg +3 -0
  95. package/src/assets/icons/ship.svg +3 -0
  96. package/src/assets/icons/shipment/icon-antri.svg +15 -0
  97. package/src/assets/icons/shipment/icon-bongkar.svg +4 -0
  98. package/src/assets/icons/shipment/icon-invoice.svg +6 -0
  99. package/src/assets/icons/shipment/icon-loading.svg +8 -0
  100. package/src/assets/icons/shipment/icon-pembayaran.svg +13 -0
  101. package/src/assets/icons/shipment/icon-pengiriman.svg +4 -0
  102. package/src/assets/icons/shipment/icon-sailing.svg +4 -0
  103. package/src/assets/icons/shipment/icon-shipment-completed.svg +6 -0
  104. package/src/assets/icons/shipment/icon-shipment-in-progress.svg +6 -0
  105. package/src/assets/icons/shipment/icon-shipment-over-sla.svg +6 -0
  106. package/src/assets/icons/shipment/icon-spt.svg +4 -0
  107. package/src/assets/icons/shipment/icon-total-shipment.svg +4 -0
  108. package/src/assets/icons/upload_doc_icon.svg +42 -0
  109. package/src/assets/icons/upload_icon_blue.svg +14 -0
  110. package/src/assets/login-bg-day-min.jpg +0 -0
  111. package/src/assets/login-bg-night-min.jpg +0 -0
  112. package/src/assets/login-bg.jpg +0 -0
  113. package/src/assets/login-day.png +0 -0
  114. package/src/assets/login-night.png +0 -0
  115. package/src/assets/lucide-circle-plus-blue.svg +1 -0
  116. package/src/assets/pdf-logo.svg +11 -0
  117. package/src/assets/pemasok-card-bg.svg +6 -0
  118. package/src/assets/success_animation.gif +0 -0
  119. package/src/assets/success_animation.mp4 +0 -0
  120. package/src/assets/success_animation.webm +0 -0
  121. package/src/components/button/BtnAddOutline.vue +14 -0
  122. package/src/components/button/BtnCircle.vue +10 -0
  123. package/src/components/button/BtnOutline.vue +15 -0
  124. package/src/components/button/BtnPrimary.vue +25 -0
  125. package/src/components/button/BtnSecondary.vue +26 -0
  126. package/src/components/detail/AccountDetailTimeline.vue +144 -0
  127. package/src/components/detail/ApprovalInfo.vue +288 -0
  128. package/src/components/detail/DCI2.vue +164 -0
  129. package/src/components/detail/DetailContentHeader.vue +83 -0
  130. package/src/components/detail/DetailContentItem.vue +186 -0
  131. package/src/components/detail/DetailContentItems.vue +388 -0
  132. package/src/components/detail/DetailContentLoading.vue +12 -0
  133. package/src/components/detail/DetailContentTablet.vue +10 -0
  134. package/src/components/detail/DetailSheet.vue +294 -0
  135. package/src/components/detail/DetailTimeline.vue +191 -0
  136. package/src/components/detail/DocApprovalDialog.vue +29 -0
  137. package/src/components/detail/DocViewerContent.vue +991 -0
  138. package/src/components/dialog/ConfirmDialog.vue +96 -0
  139. package/src/components/dialog/DialogBase.vue +53 -0
  140. package/src/components/dialog/DialogSelect.vue +212 -0
  141. package/src/components/dialog/ErrorDialog.vue +63 -0
  142. package/src/components/dialog/FormDialog.vue +141 -0
  143. package/src/components/dialog/FormInputerDialog.vue +91 -0
  144. package/src/components/dialog/InfoDialog.vue +74 -0
  145. package/src/components/dialog/SuccessDialog.vue +51 -0
  146. package/src/components/examples/TestSchemaExample.vue +288 -0
  147. package/src/components/forms/auth/LoginForm.vue +806 -0
  148. package/src/components/forms/auth/PwdScore.vue +68 -0
  149. package/src/components/helper/ApiTester.vue +153 -0
  150. package/src/components/helper/ChangePwd.vue +150 -0
  151. package/src/components/helper/CheckboxElement.vue +43 -0
  152. package/src/components/helper/ConfigSwitcher.vue +54 -0
  153. package/src/components/helper/Copyright.vue +10 -0
  154. package/src/components/helper/ErrorScreen.vue +40 -0
  155. package/src/components/helper/LucideIcon.vue +27 -0
  156. package/src/components/helper/PdfViewer.vue +103 -0
  157. package/src/components/helper/PinInputer.vue +205 -0
  158. package/src/components/helper/PrivacyPolicy.vue +122 -0
  159. package/src/components/layout/PageActivityHeader.vue +48 -0
  160. package/src/components/layout/PageHeader.vue +70 -0
  161. package/src/components/loadings/LoadingDialog.vue +29 -0
  162. package/src/components/loadings/LoadingDialogSpin.vue +25 -0
  163. package/src/components/loadings/LoadingIndicator.vue +38 -0
  164. package/src/components/loadings/LoadingScreen.vue +23 -0
  165. package/src/components/notif/Notif.vue +103 -0
  166. package/src/components/notif/NotifItem.vue +41 -0
  167. package/src/components/pages/Header.vue +431 -0
  168. package/src/components/pages/Leftbar.vue +417 -0
  169. package/src/components/pages/PageActivity.vue +108 -0
  170. package/src/components/pages/PageActivityContent.vue +597 -0
  171. package/src/components/pages/PageContentTable.vue +589 -0
  172. package/src/components/pages/PageTab.vue +84 -0
  173. package/src/components/selector/BaseSelector.vue +1136 -0
  174. package/src/components/selector/ConfigDataSelector.vue +136 -0
  175. package/src/components/settings/SettingsItem.vue +38 -0
  176. package/src/components/tab/TabView.vue +11 -0
  177. package/src/components/tab/TabViewItem.vue +18 -0
  178. package/src/components/tab/TabViewItemBar.vue +9 -0
  179. package/src/components/tables/CellHover.vue +65 -0
  180. package/src/components/tables/DashboardDataTable.vue +707 -0
  181. package/src/components/tables/DataStatusTag.vue +52 -0
  182. package/src/components/tables/DataTable.vue +156 -0
  183. package/src/components/tables/DataTableAccordion.vue +249 -0
  184. package/src/components/tables/DataTableActionRow.vue +64 -0
  185. package/src/components/tables/DataTableCell.vue +272 -0
  186. package/src/components/tables/DataTableHeader.vue +60 -0
  187. package/src/components/tables/DataTableRow.vue +213 -0
  188. package/src/components/tables/ExpandedTable.vue +259 -0
  189. package/src/components/tables/PageTable.vue +73 -0
  190. package/src/components/tables/Pagination.vue +98 -0
  191. package/src/components/tables/dropdown/BaseDropdownTable.vue +140 -0
  192. package/src/components/tables/dropdown/DropdownTableActivity.vue +33 -0
  193. package/src/components/tables/dropdown/DropdownTableAsset.vue +30 -0
  194. package/src/components/tables/dropdown/DropdownTableConfig.vue +30 -0
  195. package/src/components/tables/dropdown/DropdownTableDataKonektor.vue +31 -0
  196. package/src/components/tables/dropdown/DropdownTableDataLabel.vue +30 -0
  197. package/src/components/tables/dropdown/DropdownTableDataSchema.vue +31 -0
  198. package/src/components/tables/dropdown/DropdownTableFabaPemanfaat.vue +30 -0
  199. package/src/components/tables/dropdown/DropdownTableGroup.vue +36 -0
  200. package/src/components/tables/dropdown/DropdownTableHalaman.vue +33 -0
  201. package/src/components/tables/dropdown/DropdownTableLevel.vue +66 -0
  202. package/src/components/tables/dropdown/DropdownTableOrganization.vue +47 -0
  203. package/src/components/tables/dropdown/DropdownTablePengelola.vue +28 -0
  204. package/src/components/tables/dropdown/DropdownTableQueryLayer.vue +29 -0
  205. package/src/components/tables/dropdown/DropdownTableSentral.vue +33 -0
  206. package/src/components/tables/dropdown/DropdownTableWarehouse.vue +30 -0
  207. package/src/components/tables/dropdown/TableDropdown.vue +52 -0
  208. package/src/components/ui/accordion/Accordion.vue +19 -0
  209. package/src/components/ui/accordion/AccordionContent.vue +24 -0
  210. package/src/components/ui/accordion/AccordionItem.vue +24 -0
  211. package/src/components/ui/accordion/AccordionTrigger.vue +42 -0
  212. package/src/components/ui/accordion/index.ts +4 -0
  213. package/src/components/ui/alert-dialog/AlertDialog.vue +14 -0
  214. package/src/components/ui/alert-dialog/AlertDialogAction.vue +20 -0
  215. package/src/components/ui/alert-dialog/AlertDialogCancel.vue +20 -0
  216. package/src/components/ui/alert-dialog/AlertDialogContent.vue +42 -0
  217. package/src/components/ui/alert-dialog/AlertDialogDescription.vue +25 -0
  218. package/src/components/ui/alert-dialog/AlertDialogFooter.vue +21 -0
  219. package/src/components/ui/alert-dialog/AlertDialogHeader.vue +16 -0
  220. package/src/components/ui/alert-dialog/AlertDialogTitle.vue +22 -0
  221. package/src/components/ui/alert-dialog/AlertDialogTrigger.vue +11 -0
  222. package/src/components/ui/alert-dialog/index.ts +9 -0
  223. package/src/components/ui/avatar/Avatar.vue +24 -0
  224. package/src/components/ui/avatar/AvatarFallback.vue +11 -0
  225. package/src/components/ui/avatar/AvatarImage.vue +9 -0
  226. package/src/components/ui/avatar/UsersAvatar.vue +28 -0
  227. package/src/components/ui/avatar/index.ts +24 -0
  228. package/src/components/ui/button/Button.vue +27 -0
  229. package/src/components/ui/button/index.ts +34 -0
  230. package/src/components/ui/calendar/Calendar.vue +325 -0
  231. package/src/components/ui/calendar/index.ts +22 -0
  232. package/src/components/ui/checkbox/Checkbox.vue +33 -0
  233. package/src/components/ui/checkbox/index.ts +1 -0
  234. package/src/components/ui/command/Command.vue +30 -0
  235. package/src/components/ui/command/CommandDialog.vue +21 -0
  236. package/src/components/ui/command/CommandEmpty.vue +20 -0
  237. package/src/components/ui/command/CommandGroup.vue +29 -0
  238. package/src/components/ui/command/CommandInput.vue +33 -0
  239. package/src/components/ui/command/CommandItem.vue +26 -0
  240. package/src/components/ui/command/CommandList.vue +27 -0
  241. package/src/components/ui/command/CommandSeparator.vue +23 -0
  242. package/src/components/ui/command/CommandShortcut.vue +14 -0
  243. package/src/components/ui/command/index.ts +9 -0
  244. package/src/components/ui/context-menu/ContextMenu.vue +15 -0
  245. package/src/components/ui/context-menu/ContextMenuCheckboxItem.vue +40 -0
  246. package/src/components/ui/context-menu/ContextMenuContent.vue +36 -0
  247. package/src/components/ui/context-menu/ContextMenuGroup.vue +11 -0
  248. package/src/components/ui/context-menu/ContextMenuItem.vue +34 -0
  249. package/src/components/ui/context-menu/ContextMenuLabel.vue +25 -0
  250. package/src/components/ui/context-menu/ContextMenuPortal.vue +11 -0
  251. package/src/components/ui/context-menu/ContextMenuRadioGroup.vue +19 -0
  252. package/src/components/ui/context-menu/ContextMenuRadioItem.vue +40 -0
  253. package/src/components/ui/context-menu/ContextMenuSeparator.vue +20 -0
  254. package/src/components/ui/context-menu/ContextMenuShortcut.vue +14 -0
  255. package/src/components/ui/context-menu/ContextMenuSub.vue +19 -0
  256. package/src/components/ui/context-menu/ContextMenuSubContent.vue +35 -0
  257. package/src/components/ui/context-menu/ContextMenuSubTrigger.vue +34 -0
  258. package/src/components/ui/context-menu/ContextMenuTrigger.vue +13 -0
  259. package/src/components/ui/context-menu/index.ts +14 -0
  260. package/src/components/ui/datetime/DatetimeRangeComponent.vue +52 -0
  261. package/src/components/ui/dialog/Dialog.vue +14 -0
  262. package/src/components/ui/dialog/DialogClose.vue +11 -0
  263. package/src/components/ui/dialog/DialogContent.vue +53 -0
  264. package/src/components/ui/dialog/DialogDescription.vue +24 -0
  265. package/src/components/ui/dialog/DialogFooter.vue +19 -0
  266. package/src/components/ui/dialog/DialogHeader.vue +16 -0
  267. package/src/components/ui/dialog/DialogScrollContent.vue +59 -0
  268. package/src/components/ui/dialog/DialogTitle.vue +29 -0
  269. package/src/components/ui/dialog/DialogTrigger.vue +11 -0
  270. package/src/components/ui/dialog/index.ts +9 -0
  271. package/src/components/ui/dropdown-menu/DropdownMenu.vue +14 -0
  272. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +40 -0
  273. package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +38 -0
  274. package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +11 -0
  275. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +28 -0
  276. package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +24 -0
  277. package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +19 -0
  278. package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +41 -0
  279. package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +22 -0
  280. package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +14 -0
  281. package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  282. package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +30 -0
  283. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +33 -0
  284. package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +13 -0
  285. package/src/components/ui/dropdown-menu/index.ts +16 -0
  286. package/src/components/ui/form/FormControl.vue +16 -0
  287. package/src/components/ui/form/FormDescription.vue +20 -0
  288. package/src/components/ui/form/FormItem.vue +25 -0
  289. package/src/components/ui/form/FormLabel.vue +23 -0
  290. package/src/components/ui/form/FormMessage.vue +16 -0
  291. package/src/components/ui/form/index.ts +6 -0
  292. package/src/components/ui/form/useFormField.ts +30 -0
  293. package/src/components/ui/hover-card/HoverCard.vue +14 -0
  294. package/src/components/ui/hover-card/HoverCardContent.vue +41 -0
  295. package/src/components/ui/hover-card/HoverCardTrigger.vue +11 -0
  296. package/src/components/ui/hover-card/index.ts +3 -0
  297. package/src/components/ui/input/Input.vue +24 -0
  298. package/src/components/ui/input/index.ts +1 -0
  299. package/src/components/ui/label/Label.vue +27 -0
  300. package/src/components/ui/label/index.ts +1 -0
  301. package/src/components/ui/pagination/PaginationEllipsis.vue +22 -0
  302. package/src/components/ui/pagination/PaginationFirst.vue +29 -0
  303. package/src/components/ui/pagination/PaginationLast.vue +29 -0
  304. package/src/components/ui/pagination/PaginationNext.vue +29 -0
  305. package/src/components/ui/pagination/PaginationPrev.vue +29 -0
  306. package/src/components/ui/pagination/index.ts +10 -0
  307. package/src/components/ui/pin-input/PinInput.vue +23 -0
  308. package/src/components/ui/pin-input/PinInputGroup.vue +18 -0
  309. package/src/components/ui/pin-input/PinInputInput.vue +18 -0
  310. package/src/components/ui/pin-input/PinInputSeparator.vue +15 -0
  311. package/src/components/ui/pin-input/index.ts +4 -0
  312. package/src/components/ui/popover/Popover.vue +15 -0
  313. package/src/components/ui/popover/PopoverContent.vue +48 -0
  314. package/src/components/ui/popover/PopoverTrigger.vue +11 -0
  315. package/src/components/ui/popover/index.ts +4 -0
  316. package/src/components/ui/preview/PreviewPdf.vue +118 -0
  317. package/src/components/ui/progress/ProgressCircle.vue +27 -0
  318. package/src/components/ui/progress/SemiCircularProgressBar.vue +83 -0
  319. package/src/components/ui/progress/TotalCalories.vue +31 -0
  320. package/src/components/ui/radio-group/RadioGroup.vue +25 -0
  321. package/src/components/ui/radio-group/RadioGroupItem.vue +37 -0
  322. package/src/components/ui/radio-group/index.ts +2 -0
  323. package/src/components/ui/scroll-area/ScrollArea.vue +29 -0
  324. package/src/components/ui/scroll-area/ScrollBar.vue +30 -0
  325. package/src/components/ui/scroll-area/index.ts +2 -0
  326. package/src/components/ui/select/Select.vue +15 -0
  327. package/src/components/ui/select/SelectContent.vue +52 -0
  328. package/src/components/ui/select/SelectGroup.vue +19 -0
  329. package/src/components/ui/select/SelectInline.vue +84 -0
  330. package/src/components/ui/select/SelectItem.vue +44 -0
  331. package/src/components/ui/select/SelectItemText.vue +11 -0
  332. package/src/components/ui/select/SelectLabel.vue +13 -0
  333. package/src/components/ui/select/SelectScrollDownButton.vue +24 -0
  334. package/src/components/ui/select/SelectScrollUpButton.vue +24 -0
  335. package/src/components/ui/select/SelectSeparator.vue +17 -0
  336. package/src/components/ui/select/SelectTrigger.vue +31 -0
  337. package/src/components/ui/select/SelectTriggerCustom.vue +23 -0
  338. package/src/components/ui/select/SelectValue.vue +11 -0
  339. package/src/components/ui/select/index.ts +12 -0
  340. package/src/components/ui/separator/Separator.vue +20 -0
  341. package/src/components/ui/separator/index.ts +1 -0
  342. package/src/components/ui/sheet/Sheet.vue +14 -0
  343. package/src/components/ui/sheet/SheetClose.vue +11 -0
  344. package/src/components/ui/sheet/SheetContent.vue +48 -0
  345. package/src/components/ui/sheet/SheetDescription.vue +22 -0
  346. package/src/components/ui/sheet/SheetFooter.vue +19 -0
  347. package/src/components/ui/sheet/SheetHeader.vue +16 -0
  348. package/src/components/ui/sheet/SheetTitle.vue +22 -0
  349. package/src/components/ui/sheet/SheetTrigger.vue +11 -0
  350. package/src/components/ui/sheet/index.ts +31 -0
  351. package/src/components/ui/skeleton/Skeleton.vue +28 -0
  352. package/src/components/ui/skeleton/index.ts +1 -0
  353. package/src/components/ui/sonner/Sonner.vue +22 -0
  354. package/src/components/ui/sonner/index.ts +1 -0
  355. package/src/components/ui/star/StarRating.vue +19 -0
  356. package/src/components/ui/switch/Switch.vue +37 -0
  357. package/src/components/ui/switch/index.ts +1 -0
  358. package/src/components/ui/table/Table.vue +16 -0
  359. package/src/components/ui/table/TableBody.vue +14 -0
  360. package/src/components/ui/table/TableCaption.vue +14 -0
  361. package/src/components/ui/table/TableCell.vue +21 -0
  362. package/src/components/ui/table/TableEmpty.vue +37 -0
  363. package/src/components/ui/table/TableFooter.vue +14 -0
  364. package/src/components/ui/table/TableHead.vue +14 -0
  365. package/src/components/ui/table/TableHeader.vue +14 -0
  366. package/src/components/ui/table/TableRow.vue +14 -0
  367. package/src/components/ui/table/index.ts +8 -0
  368. package/src/components/ui/tabs/Tabs.vue +15 -0
  369. package/src/components/ui/tabs/TabsContent.vue +22 -0
  370. package/src/components/ui/tabs/TabsList.vue +25 -0
  371. package/src/components/ui/tabs/TabsTrigger.vue +27 -0
  372. package/src/components/ui/tabs/index.ts +4 -0
  373. package/src/components/ui/tags-input/TagsInput.vue +22 -0
  374. package/src/components/ui/tags-input/TagsInputInput.vue +19 -0
  375. package/src/components/ui/tags-input/TagsInputItem.vue +22 -0
  376. package/src/components/ui/tags-input/TagsInputItemDelete.vue +24 -0
  377. package/src/components/ui/tags-input/TagsInputItemText.vue +19 -0
  378. package/src/components/ui/tags-input/index.ts +5 -0
  379. package/src/components/ui/textarea/Textarea.vue +24 -0
  380. package/src/components/ui/textarea/index.ts +1 -0
  381. package/src/components/ui/tooltip/Tooltip.vue +14 -0
  382. package/src/components/ui/tooltip/TooltipContent.vue +31 -0
  383. package/src/components/ui/tooltip/TooltipProvider.vue +11 -0
  384. package/src/components/ui/tooltip/TooltipTrigger.vue +11 -0
  385. package/src/components/ui/tooltip/index.ts +4 -0
  386. package/src/composables/useAppConfig.ts +332 -0
  387. package/src/composables/useDarkMode.ts +71 -0
  388. package/src/config/app.config.ts +318 -0
  389. package/src/config/examples/ecommerce.config.ts +132 -0
  390. package/src/config/examples/generic.config.ts +132 -0
  391. package/src/config/menu.config.ts +149 -0
  392. package/src/config/my-app.config.ts +134 -0
  393. package/src/config/test-config.ts +32 -0
  394. package/src/config/theme.config.ts +250 -0
  395. package/src/docs/index.ts +21 -0
  396. package/src/docs.scss +403 -0
  397. package/src/index.d.ts +5 -0
  398. package/src/index.ts +20 -0
  399. package/src/layouts/AuthLayout.vue +68 -0
  400. package/src/layouts/DefaultLayout.vue +119 -0
  401. package/src/layouts/DocsLayout.vue +681 -0
  402. package/src/layouts/FormGlobal.vue +50 -0
  403. package/src/layouts/GlobalDialog.vue +122 -0
  404. package/src/layouts/RakorConfirmDialog.vue +95 -0
  405. package/src/layouts/SettingsLayout.vue +115 -0
  406. package/src/lib/constants.ts +2 -0
  407. package/src/lib/detail.utils.ts +213 -0
  408. package/src/lib/form.utils.ts +1009 -0
  409. package/src/lib/page.flow.utils.ts +81 -0
  410. package/src/lib/page.utils.ts +865 -0
  411. package/src/lib/performance.utils.ts +302 -0
  412. package/src/lib/tablerow.utils.ts +51 -0
  413. package/src/lib/utils.ts +643 -0
  414. package/src/main.scss +717 -0
  415. package/src/main.ts +74 -0
  416. package/src/menu.ts +78 -0
  417. package/src/nestedlist_color.scss +161 -0
  418. package/src/router/index.ts +92 -0
  419. package/src/stores/auth.ts +117 -0
  420. package/src/stores/counter.ts +12 -0
  421. package/src/stores/dialog.ts +168 -0
  422. package/src/stores/form.ts +103 -0
  423. package/src/stores/tabs.ts +52 -0
  424. package/src/tw.scss +419 -0
  425. package/src/types/form.types.ts +348 -0
  426. package/src/types/types.ts +7 -0
  427. package/src/utils/config.utils.ts +149 -0
  428. package/src/views/NotFound.vue +30 -0
  429. package/src/views/PageActivity.vue +15 -0
  430. package/src/views/auth/LoginView.vue +7 -0
  431. package/src/views/auth/OauthCallback.vue +101 -0
  432. package/src/views/dashboard/index.vue +16 -0
  433. package/src/views/settings/AccountSettingsView.vue +70 -0
  434. package/src/views/settings/AuditLogsSettingsView.vue +116 -0
  435. package/src/views/settings/DeviceSettingsView.vue +70 -0
  436. package/src/views/settings/MainSettingsView.vue +12 -0
  437. package/src/views/settings/ProfileSettingsView.vue +104 -0
  438. package/src/vueform/config/informasi-gudang.ts +47 -0
  439. package/src/vueform/config/test-schema.ts +8 -0
  440. package/src/vueform/config/types.ts +768 -0
  441. package/src/vueform/customization/classes.js +46 -0
  442. package/src/vueform/customization/tailwind.classes.js +2117 -0
  443. package/src/vueform/elements/ConfigDataSelectorElement.vue +50 -0
  444. package/src/vueform/elements/DateSelectorElement.vue +323 -0
  445. package/src/vueform/elements/SelectorElement.vue +153 -0
  446. package/src/vueform/schemas/date-selector-test.ts +103 -0
  447. package/src/vueform/schemas/informasi-gudang.ts +160 -0
  448. package/src/vueform/schemas/test-schema.ts +483 -0
  449. package/src/vueform.config.js +77 -0
  450. package/src/vueform.validator.ts +77 -0
@@ -0,0 +1,1136 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * BaseSelector - Unified Selector Component
4
+ *
5
+ * This component consolidates all selector variants (BaseSelectorSingle, BaseSelectorSingleAvatar, etc.)
6
+ * into a single, highly configurable component. It supports both single and multiple selection modes,
7
+ * avatar display, custom field mapping, and comprehensive API integration.
8
+ *
9
+ * Key Features:
10
+ * - Single & Multiple Selection: Flexible selection modes
11
+ * - Avatar Display: User avatars with fallback support
12
+ * - Auto-fill Organization: Automatic organization selection
13
+ * - Custom Field Mapping: Flexible field configuration
14
+ * - Debounced Search: 300ms search debouncing for performance
15
+ * - Pagination Support: Built-in pagination with navigation
16
+ * - Error Handling: Comprehensive error handling with user feedback
17
+ * - Loading States: Visual feedback during API calls
18
+ * - Keyboard Navigation: Full keyboard accessibility
19
+ *
20
+ * @example
21
+ * // Basic single selection
22
+ * <BaseSelector
23
+ * v-model="selectedUserId"
24
+ * endpoint="/api/users"
25
+ * placeholder="Select a user..."
26
+ * />
27
+ *
28
+ * @example
29
+ * // Multiple selection with avatars
30
+ * <BaseSelector
31
+ * v-model="selectedUsers"
32
+ * endpoint="/api/users"
33
+ * :multiple="true"
34
+ * :show-avatar="true"
35
+ * :return-object="true"
36
+ * />
37
+ *
38
+ * @example
39
+ * // Organization selector with auto-fill
40
+ * <BaseSelector
41
+ * v-model="selectedOrg"
42
+ * endpoint="/api/organizations"
43
+ * :show-avatar="true"
44
+ * :auto-fill-org="true"
45
+ * access-level="admin"
46
+ * />
47
+ */
48
+
49
+ import api from "@/api/api";
50
+ import DialogSelect, { type PageMeta } from "@/components/dialog/DialogSelect.vue";
51
+ import BtnCircle from "@/components/button/BtnCircle.vue";
52
+ import { ChevronRight, LucideX } from "lucide-vue-next";
53
+ import { onMounted, watch, ref, computed, onUnmounted } from "vue";
54
+ import { cn } from "@/lib/utils";
55
+ import { useDebounceSearch, useMemoryManagement } from "@/lib/performance.utils";
56
+ import Avatar from "@/components/ui/avatar/Avatar.vue";
57
+ import AvatarFallback from "@/components/ui/avatar/AvatarFallback.vue";
58
+ import AvatarImage from "@/components/ui/avatar/AvatarImage.vue";
59
+ import CheckboxElement from "@/components/helper/CheckboxElement.vue";
60
+ import { toast } from "vue-sonner";
61
+
62
+ /**
63
+ * Interface for items returned from the API
64
+ * Represents a selectable item with optional avatar and access level information
65
+ */
66
+ interface SelectItem {
67
+ id: string | number;
68
+ name: string;
69
+ description?: string;
70
+ avatar?: string;
71
+ isChecked?: boolean;
72
+ accessLevel?: {
73
+ code: string;
74
+ name: string;
75
+ };
76
+ [key: string]: any; // Allow additional fields for flexibility
77
+ }
78
+
79
+ /**
80
+ * Props interface for BaseSelector component
81
+ * Consolidates all functionality from previous selector variants
82
+ */
83
+ interface BaseSelectorProps {
84
+ // Core functionality (from BaseSelectorSingle.vue)
85
+ /** API endpoint for data fetching */
86
+ endpoint: string;
87
+ /** Placeholder text when no item is selected */
88
+ placeholder?: string;
89
+ /** Title for the selection dialog */
90
+ title?: string;
91
+ /** Custom name field for display (supports dot notation like 'user.name') */
92
+ nameField?: string;
93
+ /** Custom function to format the name field */
94
+ nameFieldFunc?: Function;
95
+ /** Custom description field for display (supports dot notation) */
96
+ descriptionField?: string;
97
+ /** Custom function to format the description field */
98
+ descriptionFieldFunc?: Function;
99
+ /** Disable automatic data loading on mount */
100
+ disableAutoLoad?: boolean;
101
+ /** Disable the selector */
102
+ disabled?: boolean;
103
+ /** Columns to search in (comma-separated) */
104
+ searchColumns?: string;
105
+
106
+ // Avatar functionality (from BaseSelectorSingleAvatar.vue)
107
+ /** Show avatar display functionality */
108
+ showAvatar?: boolean;
109
+ /** Auto-fill with user organization */
110
+ autoFillOrg?: boolean;
111
+ /** Required access level for auto-fill */
112
+ accessLevel?: string;
113
+
114
+ // Return value configuration
115
+ /** If true, returns full object; if false, returns only ID */
116
+ returnObject?: boolean;
117
+
118
+ // Future extensibility
119
+ /** Enable multiple selection mode */
120
+ multiple?: boolean;
121
+
122
+ // Styling
123
+ /** Additional CSS classes */
124
+ class?: string;
125
+ /** Show the trigger element (for embedded usage) */
126
+ showTrigger?: boolean;
127
+
128
+ // Dynamic Parameters
129
+ /** Static query parameters to append to endpoint */
130
+ params?: Record<string, any>;
131
+ /** Condition parameters formatted as key:value,key2:value2 */
132
+ condition?: Record<string, any>;
133
+ /** Order by field for sorting */
134
+ orderBy?: string;
135
+ /** Sort direction (asc or desc) */
136
+ sort?: "asc" | "desc";
137
+ }
138
+
139
+ // ===== REACTIVE STATE =====
140
+ /** Array of items fetched from the API */
141
+ const data = ref<SelectItem[]>([]);
142
+ /** Currently selected item (single selection mode) */
143
+ const selected = ref<SelectItem | null>(null);
144
+ /** Array of selected items (multiple selection mode) */
145
+ const selectedMultiple = ref<SelectItem[]>([]);
146
+ /** Array of checked items for multiple selection (temporary selections in dialog) */
147
+ const checkeds = ref<SelectItem[]>([]);
148
+ /** Array of checked IDs for multiple selection */
149
+ const checkedIds = ref<(string | number)[]>([]);
150
+ /** Two-way binding model for the component value */
151
+ const model = defineModel<string | number | (string | number)[] | SelectItem[] | null>();
152
+ /** Event emitter for change events */
153
+ const emits = defineEmits(["change"]);
154
+
155
+ /** Component props with default values */
156
+ const props = withDefaults(defineProps<BaseSelectorProps>(), {
157
+ placeholder: "Pilih item...",
158
+ searchColumns: "name",
159
+ showAvatar: false,
160
+ returnObject: false,
161
+ multiple: false,
162
+ disabled: false,
163
+ disableAutoLoad: false,
164
+ autoFillOrg: false,
165
+ nameField: "",
166
+ showTrigger: true,
167
+ params: () => ({}),
168
+ condition: () => ({}),
169
+ orderBy: undefined,
170
+ sort: "asc"
171
+ });
172
+
173
+ // ===== INTERNAL STATE =====
174
+ /** Current search query */
175
+ const q = ref("");
176
+ /** Pagination metadata for API requests */
177
+ const pageMeta = ref<PageMeta>({ total: 0, limit: 20, page: 1 });
178
+ /** Loading state indicator */
179
+ const loading = ref(true);
180
+ /** Dialog open/close state */
181
+ const dialogOpen = ref(false);
182
+ /** Error message for display */
183
+ const error = ref<string | null>(null);
184
+
185
+ // ===== PERFORMANCE OPTIMIZATIONS =====
186
+ /** Memory management utilities for cleanup */
187
+ const { addCleanup, cleanup } = useMemoryManagement();
188
+ /** Debounced search function to prevent excessive API calls (300ms timeout as per requirement 8.1) */
189
+ const debouncedSearch = useDebounceSearch((query: string) => {
190
+ q.value = query;
191
+ pageMeta.value.page = 1; // Reset to first page on new search
192
+ getData();
193
+ }, 300);
194
+
195
+ // ===== COMPUTED PROPERTIES =====
196
+ /** Development mode flag for debugging */
197
+ const IS_DEV = (import.meta as any)?.env?.DEV === true;
198
+ /**
199
+ * Computed property for avatar background color based on endpoint
200
+ * Provides visual distinction between different data types
201
+ */
202
+ const avatarColorClass = computed(() => {
203
+ const endpointPath = props.endpoint.split("?")[0];
204
+ // More robust endpoint matching for avatar colors
205
+ if (endpointPath === "/account" || endpointPath.includes("/account")) {
206
+ return "bg-green-200";
207
+ }
208
+ if (endpointPath === "/organization" || endpointPath.includes("/organization")) {
209
+ return "bg-blue-200";
210
+ }
211
+ // Default color for other endpoints
212
+ return "bg-gray-200";
213
+ });
214
+
215
+ /**
216
+ * Computed property for avatar text color based on endpoint
217
+ * Ensures proper contrast with background colors
218
+ */
219
+ const avatarTextColorClass = computed(() => {
220
+ const endpointPath = props.endpoint.split("?")[0];
221
+ // More robust endpoint matching for avatar text colors
222
+ if (endpointPath === "/account" || endpointPath.includes("/account")) {
223
+ return "text-green-700";
224
+ }
225
+ if (endpointPath === "/organization" || endpointPath.includes("/organization")) {
226
+ return "text-blue-700";
227
+ }
228
+ // Default text color for other endpoints
229
+ return "text-gray-700";
230
+ });
231
+
232
+ /**
233
+ * Enhanced display name resolver with comprehensive field parsing
234
+ * Supports dot notation, template fields, custom functions, and fallbacks
235
+ *
236
+ * @param object - The object to extract the display name from
237
+ * @returns Formatted display name string
238
+ */
239
+ function getEnhancedDisplayName(object: any): string {
240
+ if (!object) return "";
241
+
242
+ // Priority 1: Use nameFieldFunc if available
243
+ if (props.nameFieldFunc) {
244
+ try {
245
+ const result = props.nameFieldFunc(object);
246
+ if (result && String(result).trim() !== "" && String(result) !== "undefined") {
247
+ return String(result);
248
+ }
249
+ } catch (error) {
250
+ if (IS_DEV) console.warn("[BaseSelector] nameFieldFunc error:", error);
251
+ }
252
+ }
253
+
254
+ // Priority 2: Use nameField with advanced parsing
255
+ if (props.nameField && props.nameField.trim() !== "") {
256
+ try {
257
+ // Handle template fields like "{user.name} - {user.role}"
258
+ const templateRegex = /\{([a-zA-Z0-9_\.]+)\}/g;
259
+ if (templateRegex.test(props.nameField)) {
260
+ return props.nameField.replace(templateRegex, (match, fieldPath) => {
261
+ const value = getNestedValue(object, fieldPath);
262
+ return value !== null && value !== undefined ? String(value) : "";
263
+ });
264
+ }
265
+
266
+ // Handle dot notation like "user.profile.name"
267
+ if (props.nameField.includes(".")) {
268
+ const value = getNestedValue(object, props.nameField);
269
+ if (value !== null && value !== undefined && String(value).trim() !== "") {
270
+ return String(value);
271
+ }
272
+ }
273
+
274
+ // Handle simple field name
275
+ const value = object[props.nameField];
276
+ if (value !== null && value !== undefined && String(value).trim() !== "") {
277
+ return String(value);
278
+ }
279
+ } catch (error) {
280
+ if (IS_DEV) console.warn("[BaseSelector] nameField parsing error:", error);
281
+ }
282
+ }
283
+
284
+ // Priority 3: Common fallback fields
285
+ const fallbackFields = ["name", "title", "label", "display", "text"];
286
+ for (const field of fallbackFields) {
287
+ const value = object[field];
288
+ if (value !== null && value !== undefined && String(value).trim() !== "") {
289
+ return String(value);
290
+ }
291
+ }
292
+
293
+ // Priority 4: ID as last resort
294
+ if (object.id !== null && object.id !== undefined) {
295
+ return String(object.id);
296
+ }
297
+
298
+ return "";
299
+ }
300
+
301
+ /**
302
+ * Enhanced nested value getter with robust error handling
303
+ * Supports deep object traversal with dot notation
304
+ *
305
+ * @param obj - Object to traverse
306
+ * @param path - Dot notation path like "user.profile.name"
307
+ * @returns The nested value or null if not found
308
+ */
309
+ function getNestedValue(obj: any, path: string): any {
310
+ if (!obj || !path) return null;
311
+
312
+ try {
313
+ const keys = path.split(".");
314
+ let current = obj;
315
+
316
+ for (const key of keys) {
317
+ if (current === null || current === undefined) return null;
318
+ if (typeof current !== "object") return null;
319
+ current = current[key];
320
+ }
321
+
322
+ return current;
323
+ } catch (error) {
324
+ if (IS_DEV) console.warn("[BaseSelector] getNestedValue error:", error);
325
+ return null;
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Computed property for display text in the selector trigger
331
+ * Handles both single and multiple selection display logic with enhanced parsing
332
+ */
333
+ const displayText = computed(() => {
334
+ // Multiple selection display logic
335
+ if (props.multiple && selectedMultiple.value.length > 0) {
336
+ if (selectedMultiple.value.length === 1) {
337
+ const displayName = getEnhancedDisplayName(selectedMultiple.value[0]);
338
+ return displayName || props.placeholder;
339
+ }
340
+ return `${selectedMultiple.value.length} items selected`;
341
+ }
342
+
343
+ // Single selection display logic
344
+ if (selected.value) {
345
+ const displayName = getEnhancedDisplayName(selected.value);
346
+ return displayName || props.placeholder;
347
+ }
348
+
349
+ return props.placeholder;
350
+ });
351
+
352
+ /**
353
+ * Helper function to check if an item is currently selected
354
+ * Works for both single and multiple selection modes
355
+ */
356
+ const isItemSelected = (item: SelectItem) => {
357
+ if (props.multiple) {
358
+ // For multiple selection, check if item is in the checkedIds array
359
+ return checkedIds.value.includes(item.id);
360
+ }
361
+ return selected.value?.id === item.id;
362
+ };
363
+
364
+ /**
365
+ * Select all checkbox status
366
+ * Returns true if all visible items are selected, false if none, 'indeterminate' if some
367
+ */
368
+ const checkedAllStatus = ref<boolean | "indeterminate">(false);
369
+
370
+ // ===== CORE FUNCTIONS =====
371
+ /**
372
+ * Handles item selection for both single and multiple modes
373
+ * Emits appropriate change events based on returnObject prop
374
+ *
375
+ * @param option - The selected item
376
+ */
377
+ function selectItem(option: SelectItem) {
378
+ if (props.multiple) {
379
+ // Multiple selection mode - toggle selection using checkedIds pattern
380
+ const checkedIdx = checkedIds.value.indexOf(option.id);
381
+ if (checkedIdx >= 0) {
382
+ // Remove if already selected (toggle off)
383
+ checkeds.value.splice(checkedIdx, 1);
384
+ checkedIds.value.splice(checkedIdx, 1);
385
+ } else {
386
+ // Add if not selected (toggle on)
387
+ checkeds.value.push(option);
388
+ checkedIds.value.push(option.id);
389
+ }
390
+
391
+ // Update the selectedMultiple for display purposes
392
+ selectedMultiple.value = [...checkeds.value];
393
+
394
+ // Set model value based on returnObject prop
395
+ if (props.returnObject) {
396
+ model.value = selectedMultiple.value;
397
+ } else {
398
+ model.value = selectedMultiple.value.map(item => item.id);
399
+ }
400
+
401
+ if (IS_DEV) {
402
+ console.log("[BaseSelector] selectItem - returnObject:", props.returnObject, "model.value:", model.value);
403
+ }
404
+
405
+ // Refresh checkbox states
406
+ refreshCheckeds();
407
+
408
+ // Emit change event with proper format based on returnObject prop
409
+ setTimeout(() => {
410
+ if (props.returnObject) {
411
+ emits(
412
+ "change",
413
+ selectedMultiple.value.map(item => item.id),
414
+ selectedMultiple.value
415
+ );
416
+ } else {
417
+ emits(
418
+ "change",
419
+ selectedMultiple.value.map(item => item.id)
420
+ );
421
+ }
422
+ });
423
+ } else {
424
+ // Single selection mode
425
+ selected.value = option;
426
+
427
+ if (props.returnObject) {
428
+ model.value = option.id;
429
+ // Emit the full object for returnObject mode (like BaseSelectorSingleAvatar)
430
+ setTimeout(() => {
431
+ emits("change", option.id, option);
432
+ });
433
+ } else {
434
+ model.value = option.id;
435
+ // Emit only the ID for non-returnObject mode (like BaseSelectorSingle)
436
+ setTimeout(() => {
437
+ emits("change", option.id);
438
+ });
439
+ }
440
+
441
+ // Clear search and close dialog after single selection (only for single selection mode)
442
+ q.value = "";
443
+ if (!props.multiple) {
444
+ dialogOpen.value = false;
445
+ }
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Clears the current selection
451
+ * Handles both single and multiple selection modes
452
+ */
453
+ function clearItem() {
454
+ if (props.disabled) return;
455
+
456
+ if (props.multiple) {
457
+ selectedMultiple.value = [];
458
+ checkeds.value = [];
459
+ checkedIds.value = [];
460
+ model.value = [];
461
+ } else {
462
+ selected.value = null;
463
+ model.value = null;
464
+ }
465
+ q.value = ""; // Clear search query
466
+
467
+ // Emit change event for clearing with proper format
468
+ setTimeout(() => {
469
+ if (props.multiple) {
470
+ if (props.returnObject) {
471
+ emits("change", [], []);
472
+ } else {
473
+ emits("change", []);
474
+ }
475
+ } else {
476
+ if (props.returnObject) {
477
+ emits("change", null, null);
478
+ } else {
479
+ emits("change", null);
480
+ }
481
+ }
482
+ });
483
+ }
484
+
485
+ /**
486
+ * Selects all visible items in multiple selection mode
487
+ */
488
+ function selectAll() {
489
+ if (!props.multiple || props.disabled) return;
490
+
491
+ if (!checkedAllStatus.value || checkedAllStatus.value === "indeterminate") {
492
+ // Select all visible items
493
+ const newSelections = data.value.filter(item => !checkedIds.value.includes(item.id));
494
+
495
+ // Add to checkeds and checkedIds
496
+ checkeds.value = [...checkeds.value, ...newSelections];
497
+ checkedIds.value = [...checkedIds.value, ...newSelections.map(item => item.id)];
498
+
499
+ // Update selectedMultiple for display
500
+ selectedMultiple.value = [...checkeds.value];
501
+
502
+ // Set model value based on returnObject prop
503
+ if (props.returnObject) {
504
+ model.value = selectedMultiple.value;
505
+ } else {
506
+ model.value = selectedMultiple.value.map(item => item.id);
507
+ }
508
+ } else {
509
+ // Deselect all
510
+ checkeds.value = [];
511
+ checkedIds.value = [];
512
+ selectedMultiple.value = [];
513
+ model.value = [];
514
+ }
515
+
516
+ // Refresh checkbox states
517
+ refreshCheckeds();
518
+
519
+ // Emit change event
520
+ setTimeout(() => {
521
+ if (props.returnObject) {
522
+ emits(
523
+ "change",
524
+ selectedMultiple.value.map(item => item.id),
525
+ selectedMultiple.value
526
+ );
527
+ } else {
528
+ emits(
529
+ "change",
530
+ selectedMultiple.value.map(item => item.id)
531
+ );
532
+ }
533
+ });
534
+ }
535
+
536
+ /**
537
+ * Clears all selections in multiple selection mode
538
+ */
539
+ function clearAllSelections() {
540
+ if (!props.multiple || props.disabled) return;
541
+
542
+ checkeds.value = [];
543
+ checkedIds.value = [];
544
+ selectedMultiple.value = [];
545
+ model.value = [];
546
+
547
+ // Refresh checkbox states
548
+ refreshCheckeds();
549
+
550
+ // Emit change event
551
+ setTimeout(() => {
552
+ if (props.returnObject) {
553
+ emits("change", [], []);
554
+ } else {
555
+ emits("change", []);
556
+ }
557
+ });
558
+ }
559
+
560
+ /**
561
+ * Refreshes the isChecked property on data items based on checkedIds
562
+ * This is essential for proper checkbox state display
563
+ */
564
+ function refreshCheckeds() {
565
+ if (!props.multiple) return;
566
+
567
+ let checkedAll = true;
568
+ let notCheckedAll = true;
569
+
570
+ data.value.forEach((row: SelectItem) => {
571
+ row.isChecked = checkedIds.value && checkedIds.value.includes(row.id);
572
+ if (row.isChecked) notCheckedAll = false;
573
+ else checkedAll = false;
574
+ });
575
+
576
+ // Update checkedAllStatus
577
+ if (checkedAll === notCheckedAll) {
578
+ checkedAllStatus.value = "indeterminate";
579
+ } else {
580
+ checkedAllStatus.value = checkedAll;
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Finishes selection and closes dialog in multiple selection mode
586
+ */
587
+ function finishSelection() {
588
+ if (!props.multiple) return;
589
+
590
+ // Update selectedMultiple with current checkeds
591
+ selectedMultiple.value = [...checkeds.value];
592
+
593
+ // Set model value based on returnObject prop
594
+ if (props.returnObject) {
595
+ model.value = selectedMultiple.value;
596
+ } else {
597
+ model.value = selectedMultiple.value.map(item => item.id);
598
+ }
599
+
600
+ // Close the dialog
601
+ dialogOpen.value = false;
602
+
603
+ // Emit final change event with current selections
604
+ setTimeout(() => {
605
+ if (props.returnObject) {
606
+ emits(
607
+ "change",
608
+ selectedMultiple.value.map(item => item.id),
609
+ selectedMultiple.value
610
+ );
611
+ } else {
612
+ emits(
613
+ "change",
614
+ selectedMultiple.value.map(item => item.id)
615
+ );
616
+ }
617
+ });
618
+ }
619
+
620
+ /**
621
+ * Handles pagination navigation
622
+ * Supports first, previous, next, and last page navigation
623
+ *
624
+ * @param type - Navigation type ('first', 'prev', 'next', 'last')
625
+ */
626
+ function navigate(type: string) {
627
+ const totalPages = Math.ceil(pageMeta.value.total / pageMeta.value.limit);
628
+
629
+ switch (type) {
630
+ case "first":
631
+ if (pageMeta.value.page !== 1) {
632
+ pageMeta.value.page = 1;
633
+ } else {
634
+ return; // Already on first page, no need to reload
635
+ }
636
+ break;
637
+ case "prev":
638
+ if (pageMeta.value.page > 1) {
639
+ pageMeta.value.page -= 1;
640
+ } else {
641
+ return; // Already on first page
642
+ }
643
+ break;
644
+ case "next":
645
+ if (pageMeta.value.page < totalPages) {
646
+ pageMeta.value.page += 1;
647
+ } else {
648
+ return; // Already on last page
649
+ }
650
+ break;
651
+ case "last":
652
+ if (pageMeta.value.page !== totalPages && totalPages > 0) {
653
+ pageMeta.value.page = totalPages;
654
+ } else {
655
+ return; // Already on last page or no data
656
+ }
657
+ break;
658
+ default:
659
+ console.warn("Invalid navigation type:", type);
660
+ return;
661
+ }
662
+ getData(); // Fetch data for the new page
663
+ }
664
+
665
+ /**
666
+ * Fetches data from the API endpoint
667
+ * Handles pagination, search, and error states
668
+ * Uses the standardized API response format: { data: [], totalData: number }
669
+ */
670
+ function getData() {
671
+ loading.value = true;
672
+ error.value = null;
673
+
674
+ // Check if endpoint is defined
675
+ if (!props.endpoint) {
676
+ console.error("BaseSelector: endpoint prop is required but not provided");
677
+ error.value = "No endpoint configured";
678
+ loading.value = false;
679
+ return;
680
+ }
681
+
682
+ // Build the API endpoint URL with query parameters
683
+ const endpoint = props.endpoint.indexOf("?") > 0 ? `${props.endpoint}&` : `${props.endpoint}?`;
684
+
685
+ // Add search columns if not already present in endpoint
686
+ const searchColumns = endpoint.includes("searchColumns") ? "" : `&searchColumns=${props.searchColumns}`;
687
+
688
+ // Build dynamic parameters
689
+ const buildParams = () => {
690
+ let paramString = "";
691
+
692
+ // Add static params
693
+ if (props.params && Object.keys(props.params).length > 0) {
694
+ const paramEntries = Object.entries(props.params)
695
+ .filter(([_, value]) => value !== null && value !== undefined && value !== "")
696
+ .map(([key, value]) => `${key}=${encodeURIComponent(value)}`);
697
+ if (paramEntries.length > 0) {
698
+ paramString += `&${paramEntries.join("&")}`;
699
+ }
700
+ }
701
+
702
+ // Add condition params
703
+ if (props.condition && Object.keys(props.condition).length > 0) {
704
+ const conditionEntries = Object.entries(props.condition)
705
+ .filter(([_, value]) => value !== null && value !== undefined && value !== "")
706
+ .map(([key, value]) => `${key}:${encodeURIComponent(value)}`);
707
+ if (conditionEntries.length > 0) {
708
+ paramString += `&condition=${conditionEntries.join(",")}`;
709
+ }
710
+ }
711
+
712
+ // Add orderBy parameter
713
+ if (props.orderBy) {
714
+ paramString += `&orderBy=${encodeURIComponent(props.orderBy)}`;
715
+ }
716
+
717
+ // Add sort parameter
718
+ if (props.sort) {
719
+ paramString += `&sort=${encodeURIComponent(props.sort)}`;
720
+ }
721
+
722
+ return paramString;
723
+ };
724
+
725
+ // Construct the full URI with pagination, search, and dynamic parameters
726
+ const uri = `${endpoint}limit=${pageMeta.value.limit}&page=${pageMeta.value.page}&search=${q.value}${searchColumns}${buildParams()}`;
727
+
728
+ api
729
+ .get(uri)
730
+ .then(r => {
731
+ // Handle standardized API response format
732
+ pageMeta.value.total = r.data.totalData || 0;
733
+ data.value = r.data.data || [];
734
+ error.value = null;
735
+
736
+ // Refresh checkbox states after data is loaded
737
+ if (props.multiple) {
738
+ refreshCheckeds();
739
+ }
740
+ })
741
+ .catch(err => {
742
+ console.error("BaseSelector getData error:", err);
743
+ // Reset data on error
744
+ pageMeta.value.total = 0;
745
+ data.value = [];
746
+ error.value = err.response?.data?.message || "Failed to load data";
747
+
748
+ // Show error toast for user feedback (except for auth errors)
749
+ if (err.response?.status !== 401 && error.value) {
750
+ toast.error(error.value);
751
+ }
752
+ })
753
+ .finally(() => {
754
+ loading.value = false;
755
+ });
756
+ }
757
+
758
+ /**
759
+ * Handles search input with debouncing
760
+ * Uses the optimized debounced search function to prevent excessive API calls
761
+ *
762
+ * @param query - Search query string
763
+ */
764
+ function searchItem(query: string) {
765
+ debouncedSearch(query);
766
+ }
767
+
768
+ function parsedDescription(object: SelectItem) {
769
+ if (!object || !props.descriptionField) return object?.description || "";
770
+
771
+ try {
772
+ const regex = /\{([a-zA-Z0-9_]+)\}/g;
773
+
774
+ if (regex.test(props.descriptionField)) {
775
+ return props.descriptionField.replace(regex, (_: any, fieldName: any) => {
776
+ if (fieldName.includes(".")) {
777
+ const fields = fieldName.split(".");
778
+ let value: any = object;
779
+ for (const field of fields) {
780
+ if (value && typeof value === "object") {
781
+ value = value[field];
782
+ } else {
783
+ return "";
784
+ }
785
+ }
786
+ return value || "";
787
+ }
788
+ return (object as any)[fieldName] || "";
789
+ });
790
+ }
791
+
792
+ if (props.descriptionField?.includes(".")) {
793
+ const fields = props.descriptionField.split(".");
794
+ let value: any = object;
795
+ for (const field of fields) {
796
+ if (value && typeof value === "object") {
797
+ value = value[field];
798
+ } else {
799
+ return "";
800
+ }
801
+ }
802
+ return value || "";
803
+ }
804
+
805
+ return (object as any)[props.descriptionField] || "";
806
+ } catch (error) {
807
+ console.warn("Error parsing description field:", error);
808
+ return object?.description || "";
809
+ }
810
+ }
811
+
812
+ function parsedField(object: SelectItem) {
813
+ return getEnhancedDisplayName(object);
814
+ }
815
+
816
+ function triggerClicked() {
817
+ if (props.disabled) return;
818
+
819
+ // Initialize checked state for multiple selection
820
+ if (props.multiple) {
821
+ checkeds.value = [...selectedMultiple.value];
822
+ checkedIds.value = selectedMultiple.value.map(item => item.id);
823
+ }
824
+
825
+ // Clear any pending search timeout (handled by debounced search utility)
826
+ q.value = "";
827
+ pageMeta.value.page = 1; // Reset page to 1 when opening dialog
828
+ error.value = null; // Clear any previous errors
829
+ dialogOpen.value = true;
830
+
831
+ if (props.disableAutoLoad) {
832
+ getData(); // Load fresh data with empty search
833
+ } else {
834
+ getData(); // Always refresh data when opening dialog
835
+ }
836
+ }
837
+
838
+ watch(model, (newVal, oldVal) => {
839
+ // Avoid infinite loops
840
+ if (newVal === oldVal) return;
841
+ if (!model.value && !newVal) return;
842
+
843
+ if (props.multiple) {
844
+ // Handle multiple selection mode
845
+ if (Array.isArray(newVal)) {
846
+ // Check if it's an array of IDs or array of objects
847
+ if (newVal.length > 0 && typeof newVal[0] === "object") {
848
+ // Array of objects (returnObject = true)
849
+ selectedMultiple.value = newVal as SelectItem[];
850
+ checkeds.value = [...selectedMultiple.value];
851
+ checkedIds.value = selectedMultiple.value.map(item => item.id);
852
+ } else {
853
+ // Array of IDs (returnObject = false) - need to fetch objects for display
854
+ checkedIds.value = newVal as (string | number)[];
855
+ // Keep selectedMultiple as is for display purposes, will be updated on data fetch
856
+ }
857
+ } else if (!newVal) {
858
+ selectedMultiple.value = [];
859
+ checkeds.value = [];
860
+ checkedIds.value = [];
861
+ }
862
+ } else {
863
+ // Handle single selection mode
864
+ if (newVal && typeof newVal === "string" && model.value === newVal) return;
865
+
866
+ if (newVal && typeof newVal === "object" && (newVal as any).length > 0) {
867
+ selected.value = (newVal as any)[0] as SelectItem;
868
+ model.value = selected.value ? selected.value.id : null;
869
+ } else if (!newVal) {
870
+ selected.value = null;
871
+ model.value = null;
872
+ }
873
+ }
874
+
875
+ // Emit change event with proper delay to avoid race conditions
876
+ setTimeout(() => {
877
+ if (props.multiple) {
878
+ if (props.returnObject) {
879
+ emits(
880
+ "change",
881
+ selectedMultiple.value.map(item => item.id),
882
+ selectedMultiple.value
883
+ );
884
+ } else {
885
+ emits(
886
+ "change",
887
+ selectedMultiple.value.map(item => item.id)
888
+ );
889
+ }
890
+ } else {
891
+ if (typeof model.value === "string" || typeof model.value === "number" || model.value === null) {
892
+ if (props.returnObject) {
893
+ emits("change", model.value, selected.value);
894
+ } else {
895
+ emits("change", model.value);
896
+ }
897
+ }
898
+ }
899
+ }, 0);
900
+ });
901
+
902
+ // Watch dialog open state to ensure search is reset
903
+ watch(dialogOpen, newValue => {
904
+ if (newValue) {
905
+ // Ensure search query is reset when dialog opens
906
+ q.value = "";
907
+ pageMeta.value.page = 1;
908
+ }
909
+ });
910
+
911
+ onMounted(() => {
912
+ setTimeout(() => {
913
+ if (model.value) {
914
+ if ((typeof model.value === "string" || typeof model.value === "number") && (!selected.value || (selected.value && selected.value.id !== model.value))) {
915
+ const detailUri = props.endpoint.split("?")[0];
916
+ api
917
+ .get(`${detailUri}/${model.value}`)
918
+ .then(r => {
919
+ selected.value = r.data.data as SelectItem;
920
+ model.value = selected.value ? selected.value.id : null;
921
+ })
922
+ .catch(err => {
923
+ console.error("Failed to load selected item details:", err);
924
+ // Don't show toast for this error as it might be expected in some cases
925
+ });
926
+ return;
927
+ }
928
+
929
+ if (typeof model.value === "object" && (model.value as any).length > 0) {
930
+ selected.value = (model.value as any)[0] as SelectItem;
931
+ model.value = selected.value ? selected.value.id : null;
932
+ }
933
+ }
934
+ }, 0);
935
+
936
+ // Auto fill organization functionality (from BaseSelectorSingleAvatar)
937
+ if (props.autoFillOrg) {
938
+ api
939
+ .get("/organization/my")
940
+ .then(r => {
941
+ // Enhanced accessLevel filtering and validation
942
+ const orgData = r.data.data;
943
+ if (!props.accessLevel) {
944
+ // No access level restriction, auto-fill with organization
945
+ selectItem(orgData);
946
+ } else if (orgData.accessLevel && props.accessLevel === orgData.accessLevel.code) {
947
+ // Access level matches, auto-fill with organization
948
+ selectItem(orgData);
949
+ } else {
950
+ // Access level doesn't match, don't auto-fill but don't show error
951
+ console.info("Organization access level does not match required level:", {
952
+ required: props.accessLevel,
953
+ actual: orgData.accessLevel?.code
954
+ });
955
+ }
956
+ })
957
+ .catch(err => {
958
+ console.error("Auto fill organization failed:", err);
959
+ // Only show error toast if it's not a 401/403 (auth) error
960
+ if (err.response?.status !== 401 && err.response?.status !== 403) {
961
+ toast.error("Get my organization failed!");
962
+ }
963
+ });
964
+ }
965
+
966
+ if (!props.disableAutoLoad) getData();
967
+ });
968
+
969
+ // Cleanup on component unmount (performance optimization)
970
+ onUnmounted(() => {
971
+ cleanup();
972
+ });
973
+ </script>
974
+
975
+ <template>
976
+ <div class="relative w-full items-center" :class="{ '!cursor-default': props.disabled }" v-if="props.showTrigger !== false">
977
+ <div
978
+ :class="[
979
+ cn(
980
+ 'form-input-group group flex min-h-0 w-full flex-1 cursor-pointer border-solid transition-input duration-200 form-radius-input-lg form-color-input form-bg-input form-shadow-input form-border-width-input form-border-color-input hover:form-color-input-hover hover:form-bg-input-hover hover:form-border-color-input-hover hover:form-shadow-input-hover focused:form-color-input-focus focused:form-bg-input-focus focused:form-border-color-input-focus focused:form-ring focused:form-shadow-input-focus focused-hover:form-shadow-input-hover',
981
+ props.class,
982
+ { '!cursor-default !bg-gray-100 form-bg-disabled': props.disabled }
983
+ )
984
+ ]"
985
+ v-on:click="triggerClicked">
986
+ <div
987
+ class="w-full border-0 bg-transparent form-text-lg form-radius-input-lg form-color-input form-autofill-default form-p-input-lg group-hover:form-color-input-hover with-floating:form-p-input-floating-lg"
988
+ :class="{ '': selected }">
989
+ <div class="text-left" v-if="(multiple && selectedMultiple.length > 0) || (!multiple && selected && model)">
990
+ <!-- Avatar display when showAvatar is true -->
991
+ <template v-if="showAvatar && !multiple">
992
+ <Avatar class="absolute left-2 top-1/2 -mt-3.5 size-7" :class="avatarColorClass">
993
+ <AvatarImage v-if="selected?.avatar" :src="selected.avatar" />
994
+ <AvatarFallback class="text-sm font-medium" :class="avatarTextColorClass">
995
+ {{ selected?.name?.substring(0, 1).toUpperCase() || "?" }}
996
+ </AvatarFallback>
997
+ </Avatar>
998
+ <div class="my-auto w-full pl-8 pr-7">
999
+ <div class="overflow-hidden text-ellipsis whitespace-nowrap font-medium">
1000
+ {{ displayText }}
1001
+ </div>
1002
+ </div>
1003
+ </template>
1004
+ <!-- Multiple selection display with avatar -->
1005
+ <template v-else-if="showAvatar && multiple">
1006
+ <div class="flex items-center gap-1 pl-2 pr-7">
1007
+ <div class="flex -space-x-1" v-if="selectedMultiple.length <= 3">
1008
+ <Avatar v-for="item in selectedMultiple.slice(0, 3)" :key="item.id" class="size-6 border-2 border-white" :class="avatarColorClass">
1009
+ <AvatarImage v-if="item.avatar" :src="item.avatar" />
1010
+ <AvatarFallback class="text-xs font-medium" :class="avatarTextColorClass">
1011
+ {{ item.name.substring(0, 1).toUpperCase() }}
1012
+ </AvatarFallback>
1013
+ </Avatar>
1014
+ </div>
1015
+ <Avatar v-else class="size-6" :class="avatarColorClass">
1016
+ <AvatarFallback class="text-xs font-medium" :class="avatarTextColorClass">
1017
+ {{ selectedMultiple.length }}
1018
+ </AvatarFallback>
1019
+ </Avatar>
1020
+ <div class="ml-2 overflow-hidden text-ellipsis whitespace-nowrap font-medium">
1021
+ {{ displayText }}
1022
+ </div>
1023
+ </div>
1024
+ </template>
1025
+ <!-- Regular display when showAvatar is false -->
1026
+ <template v-else>
1027
+ <div class="max-w-full pr-10">
1028
+ <div class="group relative w-full overflow-hidden text-ellipsis whitespace-nowrap font-medium">
1029
+ <div v-if="props.nameFieldFunc && !multiple && selected" class="overflow-hidden text-ellipsis whitespace-nowrap" v-html="props.nameFieldFunc(selected)"></div>
1030
+ <template v-else>
1031
+ {{ displayText }}
1032
+ <div class="absolute left-0 top-full hidden rounded-sm bg-black/75 px-1.5 pb-0.5 pt-0.5 text-[11px] font-normal leading-snug text-white group-hover:block">
1033
+ {{ displayText }}
1034
+ </div>
1035
+ </template>
1036
+ </div>
1037
+ </div>
1038
+ </template>
1039
+ </div>
1040
+ <div class="text-left text-gray-400 dark:text-gray-500" v-else>
1041
+ {{ props.placeholder }}
1042
+ </div>
1043
+ </div>
1044
+ </div>
1045
+ <span class="absolute inset-y-0 end-0 flex items-center justify-center px-2 text-slate-400 dark:text-slate-500" v-if="!props.disabled">
1046
+ <BtnCircle
1047
+ class="mr-1 h-6 w-6 cursor-pointer bg-slate-100 hover:bg-slate-200 dark:bg-slate-700 dark:hover:bg-slate-600"
1048
+ :class="{ '!size-6': showAvatar }"
1049
+ v-on:click.stop="clearItem"
1050
+ v-if="(multiple && selectedMultiple.length > 0) || (!multiple && selected && model)">
1051
+ <LucideX :size="14"></LucideX>
1052
+ </BtnCircle>
1053
+ <ChevronRight v-else />
1054
+ </span>
1055
+ </div>
1056
+
1057
+ <DialogSelect
1058
+ :options="data"
1059
+ @select="selectItem"
1060
+ @search="searchItem"
1061
+ @select-all="selectAll"
1062
+ @clear-selections="clearAllSelections"
1063
+ @finish-selection="finishSelection"
1064
+ :loading="loading"
1065
+ :page-meta="pageMeta"
1066
+ @navigate="navigate"
1067
+ :show-trigger="false"
1068
+ v-model:open="dialogOpen"
1069
+ option-attribute="name"
1070
+ value-attribute="id"
1071
+ description-attribute="description"
1072
+ :display-search="true"
1073
+ :display-pagination="true"
1074
+ :select-all-loading="false"
1075
+ :mutiple="multiple"
1076
+ :selected-count="checkeds.length"
1077
+ :checked-all-status="checkedAllStatus">
1078
+ <template #option="{ option }">
1079
+ <!-- Avatar option display when showAvatar is true -->
1080
+ <div
1081
+ v-if="showAvatar"
1082
+ class="flex flex-row items-start gap-3 rounded-md px-6 py-2"
1083
+ :class="{
1084
+ 'bg-slate-200 dark:bg-dark_bg3': option.isChecked,
1085
+ 'bg-blue-50': multiple && option.isChecked
1086
+ }">
1087
+ <Avatar class="size-9" :class="avatarColorClass">
1088
+ <AvatarImage v-if="option.avatar" :src="option.avatar" />
1089
+ <AvatarFallback class="text-sm font-semibold" :class="avatarTextColorClass">
1090
+ {{ option.name.substring(0, 1).toUpperCase() }}
1091
+ </AvatarFallback>
1092
+ </Avatar>
1093
+ <div class="my-auto flex-1">
1094
+ <div class="break-words font-semibold leading-[1.25]">{{ parsedField(option) || option.name || option.id }}</div>
1095
+ <div class="text-[13px] font-normal text-gray-500 dark:text-gray-400" v-if="props.condition?.levels">
1096
+ {{ option.accessLevel.name }}
1097
+ </div>
1098
+ </div>
1099
+ <!-- Checkbox for multiple selection -->
1100
+ <div v-if="multiple" class="flex items-center" @click.stop="selectItem(option)">
1101
+ <CheckboxElement :checked="option.isChecked" class="mt-1.5" />
1102
+ </div>
1103
+ </div>
1104
+ <!-- Regular option display when showAvatar is false -->
1105
+ <div
1106
+ v-else
1107
+ class="flex items-center rounded-md px-6 py-2"
1108
+ :class="{
1109
+ 'bg-slate-200 dark:bg-dark_bg3': option.isChecked,
1110
+ 'bg-blue-50': multiple && option.isChecked
1111
+ }">
1112
+ <div class="my-auto flex w-full flex-1 flex-col">
1113
+ <div class="font-semibold">
1114
+ <div v-if="props.nameFieldFunc" v-html="props.nameFieldFunc(option)"></div>
1115
+ <template v-else>
1116
+ {{ parsedField(option) || option.name || option.id }}
1117
+ </template>
1118
+ </div>
1119
+ <div class="-mt-[2px] w-full text-sm font-normal text-gray-500 dark:text-gray-400">
1120
+ <div v-if="props.descriptionFieldFunc" v-html="props.descriptionFieldFunc(option)"></div>
1121
+ <template v-else-if="parsedDescription(option)">
1122
+ {{ parsedDescription(option) }}
1123
+ </template>
1124
+ </div>
1125
+ </div>
1126
+ <!-- Checkbox for multiple selection -->
1127
+ <div v-if="multiple" class="ml-2 flex items-center" @click.stop="selectItem(option)">
1128
+ <CheckboxElement :checked="option.isChecked" class="mt-1.5" />
1129
+ </div>
1130
+ </div>
1131
+ </template>
1132
+ <template #title>
1133
+ {{ props.title }}
1134
+ </template>
1135
+ </DialogSelect>
1136
+ </template>