@dragonmastery/dragoncore-vue 0.0.30 → 0.0.31
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.
- package/dist/{ConsentFlowStep-DsVhXa91.js → ConsentRequired-Bhkxu0rv.js} +119 -11
- package/dist/ConsentRequired-Bhkxu0rv.js.map +1 -0
- package/dist/{CreateTeamForm-5V_ks5Ie.js → CreateTeamForm-BHgTTHAk.js} +2 -2
- package/dist/{CreateTeamForm-B7MsOsiV.js → CreateTeamForm-bpLbK18g.js} +2 -2
- package/dist/{CreateTeamForm-B7MsOsiV.js.map → CreateTeamForm-bpLbK18g.js.map} +1 -1
- package/dist/{CreateUserPage-DNi45YF4.js → CreateUserPage-C9uOeYDJ.js} +1 -1
- package/dist/{CreateUserPage-1WiLNGr_.js → CreateUserPage-CqKcY7_X.js} +1 -1
- package/dist/{CreateUserPage-1WiLNGr_.js.map → CreateUserPage-CqKcY7_X.js.map} +1 -1
- package/dist/{CreditBalanceDashboard-CoIEyZWh.js → CreditBalanceDashboard-BEsOr0Rw.js} +2 -2
- package/dist/{CreditBalanceDashboard-CoIEyZWh.js.map → CreditBalanceDashboard-BEsOr0Rw.js.map} +1 -1
- package/dist/{CreditBalanceDashboard-D_TsFlTp.js → CreditBalanceDashboard-DZQqekKa.js} +4 -4
- package/dist/{CreditManagement-CdkqQM7F.js → CreditManagement-ByFH6IHV.js} +4 -4
- package/dist/{CreditManagement-CCyU_yja.js → CreditManagement-DiVSMbWZ.js} +2 -2
- package/dist/{CreditManagement-CCyU_yja.js.map → CreditManagement-DiVSMbWZ.js.map} +1 -1
- package/dist/{CreditTransactionHistory-UPg9uDNy.js → CreditTransactionHistory-mcacl2xG.js} +2 -2
- package/dist/{CreditTransactionHistory-UPg9uDNy.js.map → CreditTransactionHistory-mcacl2xG.js.map} +1 -1
- package/dist/{CustomerCreateSupportTicketForm-CiTTUqtW.js → CustomerCreateSupportTicketForm-CroUyX15.js} +3 -3
- package/dist/{CustomerCreateSupportTicketForm-DFH1JtlA.js → CustomerCreateSupportTicketForm-IefplMnK.js} +2 -2
- package/dist/{CustomerCreateSupportTicketForm-DFH1JtlA.js.map → CustomerCreateSupportTicketForm-IefplMnK.js.map} +1 -1
- package/dist/{CustomerSupportTicketDetailPage-DAwiE2t6.js → CustomerSupportTicketDetailPage-Dengb4Zx.js} +2 -2
- package/dist/{CustomerSupportTicketDetailPage-DAwiE2t6.js.map → CustomerSupportTicketDetailPage-Dengb4Zx.js.map} +1 -1
- package/dist/{CustomerSupportTicketList-CKZl8jxx.js → CustomerSupportTicketList-CpOaBgNr.js} +30 -30
- package/dist/{CustomerSupportTicketSuccess-BJO2xsQR.js → CustomerSupportTicketSuccess-BPUwEn4h.js} +3 -3
- package/dist/{CustomerSupportTicketSuccess-BEhFZgtn.js → CustomerSupportTicketSuccess-RQskseXP.js} +2 -2
- package/dist/{CustomerSupportTicketSuccess-BEhFZgtn.js.map → CustomerSupportTicketSuccess-RQskseXP.js.map} +1 -1
- package/dist/{DefaultReferralTeamPage-D3UIrIZK.js → DefaultReferralTeamPage-Be7-RI3s.js} +1 -1
- package/dist/{DefaultReferralTeamPage-D3UIrIZK.js.map → DefaultReferralTeamPage-Be7-RI3s.js.map} +1 -1
- package/dist/{EditTeamForm-Bf4rSgQg.js → EditTeamForm-KX2cNn24.js} +2 -2
- package/dist/{EditTeamForm-C1_-p3lZ.js → EditTeamForm-RBO99ocN.js} +2 -2
- package/dist/{EditTeamForm-C1_-p3lZ.js.map → EditTeamForm-RBO99ocN.js.map} +1 -1
- package/dist/{EditUserPage-CQgp-08o.js → EditUserPage-AcRFhChk.js} +1 -1
- package/dist/{EditUserPage-CwsO8naT.js → EditUserPage-DGuV8pzp.js} +1 -1
- package/dist/{EditUserPage-CwsO8naT.js.map → EditUserPage-DGuV8pzp.js.map} +1 -1
- package/dist/{ForgotPassword-Dd-E3_o1.js → ForgotPassword-DZ-d8rWX.js} +2 -2
- package/dist/{ForgotPassword-Dd-E3_o1.js.map → ForgotPassword-DZ-d8rWX.js.map} +1 -1
- package/dist/{ForgotPassword-BhmO5Lfd.js → ForgotPassword-LnjRUIY7.js} +1 -1
- package/dist/{LoginForm-CFADKiln.js → LoginForm-D-gfB2hS.js} +1 -1
- package/dist/{LoginForm-CSMHsZrq.js → LoginForm-DEgTB9RF.js} +2 -2
- package/dist/{LoginForm-CSMHsZrq.js.map → LoginForm-DEgTB9RF.js.map} +1 -1
- package/dist/{Logout-Bdktl4NZ.js → Logout-Bb2xe5BM.js} +2 -2
- package/dist/{Logout-Bdktl4NZ.js.map → Logout-Bb2xe5BM.js.map} +1 -1
- package/dist/{Logout-CmKJK14I.js → Logout-Ctq-a1GS.js} +1 -1
- package/dist/{MfaSetup-BtfQXhLn.js → MfaSetup-6E6apXWC.js} +2 -2
- package/dist/{MfaSetup-Bjc3v0hs.js → MfaSetup-m080C6iX.js} +3 -3
- package/dist/{MfaSetup-Bjc3v0hs.js.map → MfaSetup-m080C6iX.js.map} +1 -1
- package/dist/{MfaVerify-SUfSRf4m.js → MfaVerify-C95WB9v2.js} +2 -2
- package/dist/{MfaVerify-uJlPz8xg.js → MfaVerify-D8Ch-8hN.js} +3 -3
- package/dist/{MfaVerify-uJlPz8xg.js.map → MfaVerify-D8Ch-8hN.js.map} +1 -1
- package/dist/{ResetPassword-Dz2N3pH4.js → ResetPassword-9DFz-Qt3.js} +1 -1
- package/dist/{ResetPassword-CyizBRob.js → ResetPassword-DflPd8Qg.js} +2 -2
- package/dist/{ResetPassword-CyizBRob.js.map → ResetPassword-DflPd8Qg.js.map} +1 -1
- package/dist/{SavedFiltersPage-Cz01ZeHx.js → SavedFiltersPage-ey8wOr0T.js} +31 -31
- package/dist/{SavedFiltersPage-Cz01ZeHx.js.map → SavedFiltersPage-ey8wOr0T.js.map} +1 -1
- package/dist/Signup-C052ykf5.js +9 -0
- package/dist/{Signup-CkhRQErA.js → Signup-KuiKHB4h.js} +22 -30
- package/dist/Signup-KuiKHB4h.js.map +1 -0
- package/dist/{SignupRequirementsPage-33z--rhH.js → SignupRequirementsPage-CwnsnQKb.js} +1 -1
- package/dist/{SignupRequirementsPage-33z--rhH.js.map → SignupRequirementsPage-CwnsnQKb.js.map} +1 -1
- package/dist/{StaffCreateSupportTicketForm-BtR-Aowv.js → StaffCreateSupportTicketForm-75Bo0jdz.js} +2 -2
- package/dist/{StaffCreateSupportTicketForm-BtR-Aowv.js.map → StaffCreateSupportTicketForm-75Bo0jdz.js.map} +1 -1
- package/dist/{StaffCreateSupportTicketForm-D7ctCaXe.js → StaffCreateSupportTicketForm-DYEddYii.js} +3 -3
- package/dist/{StaffSupportTicketDetailPage-LqnNfU34.js → StaffSupportTicketDetailPage-6VyPNdw7.js} +2 -2
- package/dist/{StaffSupportTicketDetailPage-LqnNfU34.js.map → StaffSupportTicketDetailPage-6VyPNdw7.js.map} +1 -1
- package/dist/{StaffSupportTicketList-GyzlONKe.js → StaffSupportTicketList-CxV6u2gF.js} +30 -30
- package/dist/{StaffSupportTicketSuccess-B3N-RMoT.js → StaffSupportTicketSuccess-BYoBXx1i.js} +2 -2
- package/dist/{StaffSupportTicketSuccess-B3N-RMoT.js.map → StaffSupportTicketSuccess-BYoBXx1i.js.map} +1 -1
- package/dist/{StaffSupportTicketSuccess-DvonYilY.js → StaffSupportTicketSuccess-FfnJXc_k.js} +3 -3
- package/dist/{SupportStaffPage-geoITTqt.js → SupportStaffPage-CLxWU628.js} +1 -1
- package/dist/{SupportStaffPage-geoITTqt.js.map → SupportStaffPage-CLxWU628.js.map} +1 -1
- package/dist/{SupportTicketMaintenancePage-CEKi8xQB.js → SupportTicketMaintenancePage-B07avInx.js} +1 -1
- package/dist/{SupportTicketMaintenancePage-CEKi8xQB.js.map → SupportTicketMaintenancePage-B07avInx.js.map} +1 -1
- package/dist/{TeamAttachmentsTab-ChP4DaUP.js → TeamAttachmentsTab-D0DOmdnr.js} +30 -30
- package/dist/{TeamList-_SsqJicG.js → TeamList-DXQj_Omo.js} +2 -2
- package/dist/{TeamList-_SsqJicG.js.map → TeamList-DXQj_Omo.js.map} +1 -1
- package/dist/{TeamList-cp8Pa2xg.js → TeamList-hmP44hfw.js} +2 -2
- package/dist/{TeamParent-BUnqP-dr.js → TeamParent-6JhqsYaO.js} +2 -2
- package/dist/{TeamParent-BUnqP-dr.js.map → TeamParent-6JhqsYaO.js.map} +1 -1
- package/dist/{TeamParent-BseZ6Zoi.js → TeamParent-BI9ItLoY.js} +2 -2
- package/dist/{TimelineNoteInput-BBZv3X4p.js → TimelineNoteInput-P3ycD18j.js} +1 -1
- package/dist/{TimelineNoteInput-BBZv3X4p.js.map → TimelineNoteInput-P3ycD18j.js.map} +1 -1
- package/dist/UserListPage-9jhUu3TH.js +5 -0
- package/dist/{UserListPage-CDMSZpXK.js → UserListPage-DelzxCID.js} +1 -1
- package/dist/{UserListPage-CDMSZpXK.js.map → UserListPage-DelzxCID.js.map} +1 -1
- package/dist/{VerifyEmail-CWUhRA1o.js → VerifyEmail-Cy2s4yP0.js} +3 -3
- package/dist/{VerifyEmail-CWUhRA1o.js.map → VerifyEmail-Cy2s4yP0.js.map} +1 -1
- package/dist/{VerifyEmail-CLDngljq.js → VerifyEmail-DBKMZJMx.js} +2 -2
- package/dist/{ViewTeam-ttqX2In8.js → ViewTeam-ByZdYEJG.js} +2 -2
- package/dist/{ViewTeam-ttqX2In8.js.map → ViewTeam-ByZdYEJG.js.map} +1 -1
- package/dist/{ViewTeam-rLNxVgS2.js → ViewTeam-DEOe7wqT.js} +2 -2
- package/dist/{customerSupportTicketRoutes-C-DKBy5g.js → customerSupportTicketRoutes-DvfXGaSC.js} +6 -6
- package/dist/{customerSupportTicketRoutes-C-DKBy5g.js.map → customerSupportTicketRoutes-DvfXGaSC.js.map} +1 -1
- package/dist/index.d.ts +889 -894
- package/dist/index.js +31 -32
- package/dist/{mfaSchema-C6PatIbY.js → mfaSchema-Ukqzdyck.js} +1 -1
- package/dist/{mfaSchema-C6PatIbY.js.map → mfaSchema-Ukqzdyck.js.map} +1 -1
- package/dist/{saved_filter-C2N9l_a9.js → saved_filter-erjEgsdK.js} +2 -2
- package/dist/{saved_filter-C2N9l_a9.js.map → saved_filter-erjEgsdK.js.map} +1 -1
- package/dist/{src-C8B9TJiH.js → src-DSF_hIBe.js} +23 -56
- package/dist/src-DSF_hIBe.js.map +1 -0
- package/dist/{staffSupportTicketRoutes-CyMecWpC.js → staffSupportTicketRoutes-73ceKhL-.js} +6 -6
- package/dist/{staffSupportTicketRoutes-CyMecWpC.js.map → staffSupportTicketRoutes-73ceKhL-.js.map} +1 -1
- package/dist/{teamRoutes-CFDsHPkd.js → teamRoutes-CSBq1DNq.js} +7 -7
- package/dist/{teamRoutes-CFDsHPkd.js.map → teamRoutes-CSBq1DNq.js.map} +1 -1
- package/dist/{useEmailVerificationChannel-QuMSgzzM.js → useEmailVerificationChannel-C76Gnyi1.js} +2 -2
- package/dist/{useEmailVerificationChannel-QuMSgzzM.js.map → useEmailVerificationChannel-C76Gnyi1.js.map} +1 -1
- package/dist/{useReturnUrl-B5V3SJf5.js → useReturnUrl-DnezAxBA.js} +2 -10
- package/dist/{useReturnUrl-B5V3SJf5.js.map → useReturnUrl-DnezAxBA.js.map} +1 -1
- package/package.json +2 -2
- package/dist/ConsentFlowStep-DsVhXa91.js.map +0 -1
- package/dist/ConsentRequired-B3eLxJgx.js +0 -114
- package/dist/ConsentRequired-B3eLxJgx.js.map +0 -1
- package/dist/Signup-CkhRQErA.js.map +0 -1
- package/dist/Signup-cOvXCtJj.js +0 -9
- package/dist/SignupConsentFlow-CKMFsnf5.js +0 -223
- package/dist/SignupConsentFlow-CKMFsnf5.js.map +0 -1
- package/dist/UserListPage-BABli3QG.js +0 -5
- package/dist/signupConsentStorage-pWSoHuhO.js +0 -35
- package/dist/signupConsentStorage-pWSoHuhO.js.map +0 -1
- package/dist/src-C8B9TJiH.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,15 +3,15 @@ import { i as setRefreshTokenHandler, n as createRefreshTokenHandler, r as getRe
|
|
|
3
3
|
import "./useQueryCache-alzaRWEb.js";
|
|
4
4
|
import { t as useMutation } from "./useMutation-BLNuJoYl.js";
|
|
5
5
|
import { t as useQuery } from "./useQuery-BzUGEOj0.js";
|
|
6
|
-
import { A as userAlreadyLoggedIn, B as Admin_default, C as customerSupportTicketRowSchemaWithMetadata, D as teamFiltersSchemaWithMetadata, E as teamMemberFiltersSchemaWithMetadata, F as FieldGroup_default, G as RightSidebar_default, H as useEmailVerificationGuard, I as FieldDisplay_default, J as LoginButton_default, K as NotFound_default, L as InApp_default, M as SummarySection_default, N as KeyValueEditor_default, O as TeamAttachmentsTab_default, P as FileManager_default, Q as DragoncoreVue, R as Default_default, S as CustomerSupportTicketList_default, T as SupportTicketTimeline_default, U as Sidebar_default, V as UnverifiedEmailBanner_default, W as useBuildTag, X as AppHeader_default, Y as InputModal_default, Z as AppTabNavigation_default, _ as ConvertToCustomerWorkflow_default, a as authPaths, b as ApproveRejectActions_default, c as useSupportTicketStatus, d as SupportTicketAttachments_default, f as CreditBalanceWidget_default, g as ConvertToInternalWorkflow_default, h as ReactivateInternalTaskWorkflow_default, i as SocialLoginButtons_default, j as timezones, k as userRoutes, l as useSupportTicketPermissions, m as adminSupportTicketRowSchemaWithMetadata, n as creditRoutes, o as authRoutes, p as StaffSupportTicketList_default, q as Navbar_default, r as adminRoutes, s as getAuthRoutes, t as creditPaths, u as adminSupportTicketFiltersSchemaWithMetadata, v as CompleteSupportTicketForm_default, w as SupportTicketStatusBadge_default, x as CustomerCreditBalance_default, y as CancelInternalTaskWorkflow_default, z as Auth_default } from "./src-
|
|
6
|
+
import { A as userAlreadyLoggedIn, B as Admin_default, C as customerSupportTicketRowSchemaWithMetadata, D as teamFiltersSchemaWithMetadata, E as teamMemberFiltersSchemaWithMetadata, F as FieldGroup_default, G as RightSidebar_default, H as useEmailVerificationGuard, I as FieldDisplay_default, J as LoginButton_default, K as NotFound_default, L as InApp_default, M as SummarySection_default, N as KeyValueEditor_default, O as TeamAttachmentsTab_default, P as FileManager_default, Q as DragoncoreVue, R as Default_default, S as CustomerSupportTicketList_default, T as SupportTicketTimeline_default, U as Sidebar_default, V as UnverifiedEmailBanner_default, W as useBuildTag, X as AppHeader_default, Y as InputModal_default, Z as AppTabNavigation_default, _ as ConvertToCustomerWorkflow_default, a as authPaths, b as ApproveRejectActions_default, c as useSupportTicketStatus, d as SupportTicketAttachments_default, f as CreditBalanceWidget_default, g as ConvertToInternalWorkflow_default, h as ReactivateInternalTaskWorkflow_default, i as SocialLoginButtons_default, j as timezones, k as userRoutes, l as useSupportTicketPermissions, m as adminSupportTicketRowSchemaWithMetadata, n as creditRoutes, o as authRoutes, p as StaffSupportTicketList_default, q as Navbar_default, r as adminRoutes, s as getAuthRoutes, t as creditPaths, u as adminSupportTicketFiltersSchemaWithMetadata, v as CompleteSupportTicketForm_default, w as SupportTicketStatusBadge_default, x as CustomerCreditBalance_default, y as CancelInternalTaskWorkflow_default, z as Auth_default } from "./src-DSF_hIBe.js";
|
|
7
7
|
import { t as AppLink_default } from "./AppLink-FcNGKgvG.js";
|
|
8
|
-
import { a as useSavedFilters, c as buildQueryWithFilters, d as serializeFiltersToQueryParams, f as PINNED_PRESETS_KEY, g as BaseModal_default, h as MS, i as savedFilterRoutes, l as deserializeFiltersFromQueryParams, m as usePinnedPresets, n as ManagePresetsModal_default, o as createLastUsedPresetGuard, p as useInjectedPinnedPresets, r as SaveFilterModal_default, s as getLastUsedPresetRedirect, t as SavedFilterPresets_default, u as extractFiltersFromQuery } from "./saved_filter-
|
|
8
|
+
import { a as useSavedFilters, c as buildQueryWithFilters, d as serializeFiltersToQueryParams, f as PINNED_PRESETS_KEY, g as BaseModal_default, h as MS, i as savedFilterRoutes, l as deserializeFiltersFromQueryParams, m as usePinnedPresets, n as ManagePresetsModal_default, o as createLastUsedPresetGuard, p as useInjectedPinnedPresets, r as SaveFilterModal_default, s as getLastUsedPresetRedirect, t as SavedFilterPresets_default, u as extractFiltersFromQuery } from "./saved_filter-erjEgsdK.js";
|
|
9
9
|
import { t as ConfirmDialog_default } from "./ConfirmDialog-DjthOYU6.js";
|
|
10
10
|
import { n as ImageModal_default, t as InlineAttachments_default } from "./InlineAttachments-DAn_QknY.js";
|
|
11
11
|
import { t as TeamMembersTab_default } from "./TeamMembersTab-BigqpBDH.js";
|
|
12
|
-
import { a as
|
|
12
|
+
import { a as withReturnUrl, i as withBackLink, n as getBackLinkFromRoute, r as getValidReturnUrl } from "./useReturnUrl-DnezAxBA.js";
|
|
13
13
|
import { n as ThemePref_default, t as Appearance_default } from "./Appearance-shr0Aql0.js";
|
|
14
|
-
import {
|
|
14
|
+
import { n as signupSchemaWithMetadata, r as SIGNUP_EXTENSIBILITY_KEYS, t as Signup_default } from "./Signup-KuiKHB4h.js";
|
|
15
15
|
import { n as useBreadcrumbs, t as BREADCRUMB_KEY } from "./useBreadcrumbs-CPWXm0hm.js";
|
|
16
16
|
import { t as ExternalLinkIcon_default } from "./ExternalLinkIcon-BKVV5Gjm.js";
|
|
17
17
|
import { t as FieldsetSection_default } from "./FieldsetSection-Br_sygWW.js";
|
|
@@ -24,46 +24,45 @@ import { n as userProfileSchemaWithMetadata, t as UserProfilePage_default } from
|
|
|
24
24
|
import { n as changePasswordSchemaWithMetadata, t as ChangePasswordPage_default } from "./ChangePasswordPage-Dy8lFUcI.js";
|
|
25
25
|
import { n as teamUpdateSchemaWithMetadata, t as teamCreateSchemaWithMetadata } from "./teamMetadata-NTjPt89L.js";
|
|
26
26
|
import { n as teamMemberRoutes, t as teamMemberPaths } from "./team_memberRoutes-BgjY9Kwq.js";
|
|
27
|
-
import { n as teamRoutes, t as teamPaths } from "./teamRoutes-
|
|
28
|
-
import { t as CreateTeamForm_default } from "./CreateTeamForm-
|
|
29
|
-
import { t as EditTeamForm_default } from "./EditTeamForm-
|
|
27
|
+
import { n as teamRoutes, t as teamPaths } from "./teamRoutes-CSBq1DNq.js";
|
|
28
|
+
import { t as CreateTeamForm_default } from "./CreateTeamForm-bpLbK18g.js";
|
|
29
|
+
import { t as EditTeamForm_default } from "./EditTeamForm-RBO99ocN.js";
|
|
30
30
|
import { n as recordVersionRowSchemaWithMetadata, t as TeamHistoryTab_default } from "./TeamHistoryTab-CxzA4u_G.js";
|
|
31
|
-
import { n as teamRowSchemaWithMetadata, t as TeamList_default } from "./TeamList-
|
|
31
|
+
import { n as teamRowSchemaWithMetadata, t as TeamList_default } from "./TeamList-DXQj_Omo.js";
|
|
32
32
|
import { a as EditNoteModal_default, i as noteCreateMetadata, n as NoteList_default, o as noteUpdateMetadata, r as noteRowSchemaWithMetadata, t as TeamNotesTab_default } from "./TeamNotesTab-Cego-QT3.js";
|
|
33
|
-
import { t as TeamParent_default } from "./TeamParent-
|
|
34
|
-
import { t as ViewTeam_default } from "./ViewTeam-
|
|
33
|
+
import { t as TeamParent_default } from "./TeamParent-6JhqsYaO.js";
|
|
34
|
+
import { t as ViewTeam_default } from "./ViewTeam-ByZdYEJG.js";
|
|
35
35
|
import { n as teamMemberUpdateSchemaWithMetadata, t as teamMemberCreateSchemaWithMetadata } from "./teamMemberMetadata-C4urCwBU.js";
|
|
36
36
|
import { t as CreateTeamMemberForm_default } from "./CreateTeamMemberForm-DeUyXnVa.js";
|
|
37
37
|
import { t as EditTeamMemberForm_default } from "./EditTeamMemberForm-D9cofrUM.js";
|
|
38
38
|
import { n as teamMemberRowSchemaWithMetadata, t as TeamMemberList_default } from "./TeamMemberList-BYUANoBg.js";
|
|
39
39
|
import { t as TeamMemberParent_default } from "./TeamMemberParent-DmYcHU3n.js";
|
|
40
40
|
import { t as ViewTeamMember_default } from "./ViewTeamMember-DqWZ3F_h.js";
|
|
41
|
-
import { n as customerSupportTicketRoutes, t as customerSupportPaths } from "./customerSupportTicketRoutes-
|
|
42
|
-
import { n as staffSupportTicketRoutes, t as staffSupportPaths } from "./staffSupportTicketRoutes-
|
|
41
|
+
import { n as customerSupportTicketRoutes, t as customerSupportPaths } from "./customerSupportTicketRoutes-DvfXGaSC.js";
|
|
42
|
+
import { n as staffSupportTicketRoutes, t as staffSupportPaths } from "./staffSupportTicketRoutes-73ceKhL-.js";
|
|
43
43
|
import { a as SupportTicketTypeBadge_default, c as formatStaffCreditValue, i as SupportTicketApprovalBadge_default, n as TimelineItem_default, o as SupportTicketPriorityBadge_default, r as formatTicketDate, s as formatCustomerCreditValue, t as TimelineSystemEvent_default } from "./TimelineSystemEvent-D5fkhkZT.js";
|
|
44
|
-
import { n as customerSupportTicketCreateSchemaWithMetadata, r as customerSupportTicketUpdateSchemaWithMetadata, t as CustomerCreateSupportTicketForm_default } from "./CustomerCreateSupportTicketForm-
|
|
44
|
+
import { n as customerSupportTicketCreateSchemaWithMetadata, r as customerSupportTicketUpdateSchemaWithMetadata, t as CustomerCreateSupportTicketForm_default } from "./CustomerCreateSupportTicketForm-IefplMnK.js";
|
|
45
45
|
import { t as formatTicketDisplayId } from "./displayIdFormatter-Ca4Al9iB.js";
|
|
46
46
|
import { t as CustomerSupportTicketParent_default } from "./CustomerSupportTicketParent-rl4Ym8oa.js";
|
|
47
|
-
import { t as CustomerSupportTicketSuccess_default } from "./CustomerSupportTicketSuccess-
|
|
48
|
-
import { n as adminSupportTicketCreateSchemaWithMetadata, r as adminSupportTicketUpdateSchemaWithMetadata, t as StaffCreateSupportTicketForm_default } from "./StaffCreateSupportTicketForm-
|
|
47
|
+
import { t as CustomerSupportTicketSuccess_default } from "./CustomerSupportTicketSuccess-RQskseXP.js";
|
|
48
|
+
import { n as adminSupportTicketCreateSchemaWithMetadata, r as adminSupportTicketUpdateSchemaWithMetadata, t as StaffCreateSupportTicketForm_default } from "./StaffCreateSupportTicketForm-75Bo0jdz.js";
|
|
49
49
|
import { t as SupportTicketDevLifecycleBadge_default } from "./SupportTicketDevLifecycleBadge-D8-Cv1Np.js";
|
|
50
50
|
import { t as StaffSupportTicketParent_default } from "./StaffSupportTicketParent-DPvdLUii.js";
|
|
51
|
-
import { t as StaffSupportTicketSuccess_default } from "./StaffSupportTicketSuccess-
|
|
52
|
-
import { n as loginSchemaWithMetadata, t as LoginForm_default } from "./LoginForm-
|
|
53
|
-
import "./useEmailVerificationChannel-
|
|
54
|
-
import { n as
|
|
55
|
-
import { n as
|
|
56
|
-
import {
|
|
57
|
-
import { t as
|
|
58
|
-
import {
|
|
59
|
-
import { t as
|
|
60
|
-
import { t as
|
|
61
|
-
import { t as
|
|
62
|
-
import { n as
|
|
63
|
-
import { n as
|
|
64
|
-
import { n as
|
|
65
|
-
import {
|
|
66
|
-
import { t as
|
|
67
|
-
import { a as addCreditsSchemaWithMetadata, i as AddCredits_default, n as SetMonthlyAllocation_default, o as setMonthlyAllocationSchemaWithMetadata, r as ResetMonthlyBalance_default, t as CreditManagement_default } from "./CreditManagement-CCyU_yja.js";
|
|
51
|
+
import { t as StaffSupportTicketSuccess_default } from "./StaffSupportTicketSuccess-BYoBXx1i.js";
|
|
52
|
+
import { n as loginSchemaWithMetadata, t as LoginForm_default } from "./LoginForm-DEgTB9RF.js";
|
|
53
|
+
import "./useEmailVerificationChannel-C76Gnyi1.js";
|
|
54
|
+
import { n as forgotPasswordSchemaWithMetadata, t as ForgotPassword_default } from "./ForgotPassword-DZ-d8rWX.js";
|
|
55
|
+
import { n as resetPasswordSchemaWithMetadata, t as ResetPassword_default } from "./ResetPassword-DflPd8Qg.js";
|
|
56
|
+
import { t as Logout_default } from "./Logout-Bb2xe5BM.js";
|
|
57
|
+
import { n as mfaVerifySchemaWithMetadata, t as mfaSetupConfirmSchemaWithMetadata } from "./mfaSchema-Ukqzdyck.js";
|
|
58
|
+
import { t as MfaSetup_default } from "./MfaSetup-m080C6iX.js";
|
|
59
|
+
import { t as MfaVerify_default } from "./MfaVerify-D8Ch-8hN.js";
|
|
60
|
+
import { t as VerifyEmail_default } from "./VerifyEmail-Cy2s4yP0.js";
|
|
61
|
+
import { n as userRowSchemaWithMetadata, r as UserTypeBadge_default, t as UserListPage_default } from "./UserListPage-DelzxCID.js";
|
|
62
|
+
import { n as createUserSchemaWithMetadata, t as CreateUserPage_default } from "./CreateUserPage-CqKcY7_X.js";
|
|
63
|
+
import { n as updateUserSchemaWithMetadata, t as EditUserPage_default } from "./EditUserPage-DGuV8pzp.js";
|
|
64
|
+
import { i as CreditBalanceOverview_default, n as CreditTransactionTypeBadge_default, r as creditTransactionRowSchemaWithMetadata, t as CreditTransactionHistory_default } from "./CreditTransactionHistory-mcacl2xG.js";
|
|
65
|
+
import { t as CreditBalanceDashboard_default } from "./CreditBalanceDashboard-BEsOr0Rw.js";
|
|
66
|
+
import { a as addCreditsSchemaWithMetadata, i as AddCredits_default, n as SetMonthlyAllocation_default, o as setMonthlyAllocationSchemaWithMetadata, r as ResetMonthlyBalance_default, t as CreditManagement_default } from "./CreditManagement-DiVSMbWZ.js";
|
|
68
67
|
|
|
69
68
|
export { AddCredits_default as AddCredits, Admin_default as AdminLayout, AppHeader_default as AppHeader, AppLink_default as AppLink, AppTabNavigation_default as AppTabNavigation, Appearance_default as AppearancePage, ApproveRejectActions_default as ApproveRejectActions, Auth_default as AuthLayout, BATCH_MODE, BREADCRUMB_KEY, BaseModal_default as BaseModal, CancelInternalTaskWorkflow_default as CancelInternalTaskWorkflow, ChangePasswordPage_default as ChangePasswordPage, CompleteSupportTicketForm_default as CompleteSupportTicketForm, ConfirmDialog_default as ConfirmDialog, ConvertToCustomerWorkflow_default as ConvertToCustomerWorkflow, ConvertToInternalWorkflow_default as ConvertToInternalWorkflow, CreateTeamForm_default as CreateTeamForm, CreateTeamMemberForm_default as CreateTeamMemberForm, CreateUserPage_default as CreateUserPage, CreditBalanceDashboard_default as CreditBalanceDashboard, CreditBalanceOverview_default as CreditBalanceOverview, CreditBalanceWidget_default as CreditBalanceWidget, CreditManagement_default as CreditManagement, CreditTransactionHistory_default as CreditTransactionHistory, CreditTransactionTypeBadge_default as CreditTransactionTypeBadge, CustomerCreateSupportTicketForm_default as CustomerCreateSupportTicketForm, CustomerCreditBalance_default as CustomerCreditBalance, CustomerSupportTicketList_default as CustomerSupportTicketList, CustomerSupportTicketParent_default as CustomerSupportTicketParent, CustomerSupportTicketSuccess_default as CustomerSupportTicketSuccess, Default_default as DefaultLayout, DragoncoreVue, EditNoteModal_default as EditNoteModal, EditTeamForm_default as EditTeamForm, EditTeamMemberForm_default as EditTeamMemberForm, EditUserPage_default as EditUserPage, EnhancedRefreshTokenHandler, ExternalLinkIcon_default as ExternalLinkIcon, FieldDisplay_default as FieldDisplay, FieldGroup_default as FieldGroup, FieldsetSection_default as FieldsetSection, FileManager_default as FileManager, ForgotPassword_default as ForgotPassword, ImageModal_default as ImageModal, InApp_default as InAppLayout, InlineAttachments_default as InlineAttachments, InputModal_default as InputModal, KeyValueEditor_default as KeyValueEditor, LOG_LEVEL, LoadingErrorStates_default as LoadingErrorStates, Logger, LoginButton_default as LoginButton, LoginForm_default as LoginForm, Logout_default as Logout, MS, ManagePresetsModal_default as ManagePresetsModal, MfaSetup_default as MfaSetup, MfaVerify_default as MfaVerify, Navbar_default as Navbar, NotFound_default as NotFound, NoteList_default as NoteList, PINNED_PRESETS_KEY, ReactivateInternalTaskWorkflow_default as ReactivateInternalTaskWorkflow, RecordChangesSummary_default as RecordChangesSummary, RecordVersionList_default as RecordVersionList, RecordVersionViewer_default as RecordVersionViewer, ResetMonthlyBalance_default as ResetMonthlyBalance, ResetPassword_default as ResetPassword, RightSidebar_default as RightSidebar, SIGNUP_EXTENSIBILITY_KEYS, SaveFilterModal_default as SaveFilterModal, SavedFilterPresets_default as SavedFilterPresets, SetMonthlyAllocation_default as SetMonthlyAllocation, Sidebar_default as Sidebar, Signup_default as Signup, SocialLoginButtons_default as SocialLoginButtons, StaffCreateSupportTicketForm_default as StaffCreateSupportTicketForm, StaffSupportTicketList_default as StaffSupportTicketList, StaffSupportTicketParent_default as StaffSupportTicketParent, StaffSupportTicketSuccess_default as StaffSupportTicketSuccess, SummarySection_default as SummarySection, SupportTicketApprovalBadge_default as SupportTicketApprovalBadge, SupportTicketAttachments_default as SupportTicketAttachments, SupportTicketDevLifecycleBadge_default as SupportTicketDevLifecycleBadge, SupportTicketPriorityBadge_default as SupportTicketPriorityBadge, SupportTicketStatusBadge_default as SupportTicketStatusBadge, SupportTicketTimeline_default as SupportTicketTimeline, SupportTicketTypeBadge_default as SupportTicketTypeBadge, TeamAttachmentsTab_default as TeamAttachmentsTab, TeamHistoryTab_default as TeamHistoryTab, TeamList_default as TeamList, TeamMemberList_default as TeamMemberList, TeamMemberParent_default as TeamMemberParent, TeamMembersTab_default as TeamMembersTab, TeamNotesTab_default as TeamNotesTab, TeamParent_default as TeamParent, ThemePref_default as ThemePref, TimelineItem_default as TimelineItem, TimelineSystemEvent_default as TimelineSystemEvent, UnverifiedEmailBanner_default as UnverifiedEmailBanner, UserListPage_default as UserListPage, UserProfilePage_default as UserProfilePage, UserTypeBadge_default as UserTypeBadge, VerifyEmail_default as VerifyEmail, ViewTeam_default as ViewTeam, ViewTeamMember_default as ViewTeamMember, ZiniaContainer_default as ZiniaContainer, addCreditsSchemaWithMetadata, adminRoutes, adminSupportTicketCreateSchemaWithMetadata, adminSupportTicketFiltersSchemaWithMetadata, adminSupportTicketRowSchemaWithMetadata, adminSupportTicketUpdateSchemaWithMetadata, authPaths, authRoutes, buildQueryWithFilters, changePasswordSchemaWithMetadata, createAppBatch, createAuthenticatedGuard, createLastUsedPresetGuard, createLeadOrStaffOnlyGuard, createRefreshTokenHandler, createStaffOnlyGuard, createSuperAdminOnlyGuard, createUserAuthorizedGuard, createUserSchemaWithMetadata, creditPaths, creditRoutes, creditTransactionRowSchemaWithMetadata, customerSupportPaths, customerSupportTicketCreateSchemaWithMetadata, customerSupportTicketRoutes, customerSupportTicketRowSchemaWithMetadata, customerSupportTicketUpdateSchemaWithMetadata, deserializeFiltersFromQueryParams, executeWithAuth, extractFiltersFromQuery, extractRpcErrorMessage, forgotPasswordSchemaWithMetadata, formatCustomerCreditValue, formatStaffCreditValue, formatSystemTimestamp, formatTicketDate, formatTicketDisplayId, formatToISODate, formatUserDate, getAuthRoutes, getBackLinkFromRoute, getLastUsedPresetRedirect, getRefreshTokenHandler, getTimezoneOffsetString, getValidReturnUrl, leadOrStaffOnly, logIfEnabled, logger, loginSchemaWithMetadata, mfaSetupConfirmSchemaWithMetadata, mfaVerifySchemaWithMetadata, noteCreateMetadata, noteRowSchemaWithMetadata, noteUpdateMetadata, recordVersionRowSchemaWithMetadata, resetPasswordSchemaWithMetadata, savedFilterRoutes, serializeFiltersToQueryParams, setMonthlyAllocationSchemaWithMetadata, setRefreshTokenHandler, setRouter, signupSchemaWithMetadata, staffOnly, staffSupportPaths, staffSupportTicketRoutes, teamCreateSchemaWithMetadata, teamFiltersSchemaWithMetadata, teamMemberCreateSchemaWithMetadata, teamMemberFiltersSchemaWithMetadata, teamMemberPaths, teamMemberRoutes, teamMemberRowSchemaWithMetadata, teamMemberUpdateSchemaWithMetadata, teamPaths, teamRoutes, teamRowSchemaWithMetadata, teamUpdateSchemaWithMetadata, timezones, toStringWithLocalTimeZoneOffSet, updateUserSchemaWithMetadata, useBreadcrumbs, useBuildTag, useEmailVerificationGuard, useEnv, useInjectedPinnedPresets, useMutation, usePinnedPresets, useQuery, useSavedFilters, useSupportTicketPermissions, useSupportTicketStatus, useUserSessionStore, userAlreadyLoggedIn, userAuthenticated, userIsSuperAdmin, userProfileSchemaWithMetadata, userRoutes, userRowSchemaWithMetadata, withBackLink, withReturnUrl };
|
|
@@ -27,4 +27,4 @@ const mfaSetupConfirmSchemaWithMetadata = withMetadata(mfaSetupConfirmSchema, "m
|
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
29
|
export { mfaVerifySchemaWithMetadata as n, mfaSetupConfirmSchemaWithMetadata as t };
|
|
30
|
-
//# sourceMappingURL=mfaSchema-
|
|
30
|
+
//# sourceMappingURL=mfaSchema-Ukqzdyck.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mfaSchema-
|
|
1
|
+
{"version":3,"file":"mfaSchema-Ukqzdyck.js","names":[],"sources":["../src/slices/auth/features/mfa/mfaSchema.ts"],"sourcesContent":["import { withMetadata } from '@dragonmastery/zinia-forms-core';\nimport { z } from 'zod';\n\n/** 6-digit TOTP code from authenticator app */\nexport const mfaCodeSchema = z\n .string()\n .length(6, 'Enter the 6-digit code from your authenticator app')\n .regex(/^\\d{6}$/, 'Code must be 6 digits');\n\nexport const mfaVerifySchema = z.object({\n code: mfaCodeSchema,\n});\n\nexport const mfaSetupConfirmSchema = z.object({\n code: mfaCodeSchema,\n});\n\nexport type MfaVerifyForm = z.infer<typeof mfaVerifySchema>;\nexport type MfaSetupConfirmForm = z.infer<typeof mfaSetupConfirmSchema>;\n\nexport const mfaVerifySchemaWithMetadata = withMetadata(mfaVerifySchema, 'mfaVerifySchema', {\n code: {\n inputType: 'text',\n placeholder: '000000',\n helpText: 'Enter the 6-digit code from your authenticator app',\n autocomplete: 'one-time-code',\n className: 'mfa-code-field',\n autofocus: true,\n maxlength: 6,\n },\n});\n\nexport const mfaSetupConfirmSchemaWithMetadata = withMetadata(\n mfaSetupConfirmSchema,\n 'mfaSetupConfirmSchema',\n {\n code: {\n inputType: 'text',\n placeholder: '000000',\n helpText: 'Enter the 6-digit code to verify setup',\n autocomplete: 'one-time-code',\n className: 'mfa-code-field',\n autofocus: true,\n maxlength: 6,\n },\n },\n);\n"],"mappings":";;;;;AAIA,MAAa,gBAAgB,EAC1B,QAAQ,CACR,OAAO,GAAG,qDAAqD,CAC/D,MAAM,WAAW,wBAAwB;AAE5C,MAAa,kBAAkB,EAAE,OAAO,EACtC,MAAM,eACP,CAAC;AAEF,MAAa,wBAAwB,EAAE,OAAO,EAC5C,MAAM,eACP,CAAC;AAKF,MAAa,8BAA8B,aAAa,iBAAiB,mBAAmB,EAC1F,MAAM;CACJ,WAAW;CACX,aAAa;CACb,UAAU;CACV,cAAc;CACd,WAAW;CACX,WAAW;CACX,WAAW;CACZ,EACF,CAAC;AAEF,MAAa,oCAAoC,aAC/C,uBACA,yBACA,EACE,MAAM;CACJ,WAAW;CACX,aAAa;CACb,UAAU;CACV,cAAc;CACd,WAAW;CACX,WAAW;CACX,WAAW;CACZ,EACF,CACF"}
|
|
@@ -543,7 +543,7 @@ function useSavedFilters(config) {
|
|
|
543
543
|
const savedFilterRoutes = [{
|
|
544
544
|
path: "/saved-filters",
|
|
545
545
|
name: "SavedFilters",
|
|
546
|
-
component: () => import("./SavedFiltersPage-
|
|
546
|
+
component: () => import("./SavedFiltersPage-ey8wOr0T.js"),
|
|
547
547
|
beforeEnter: [userAuthenticated],
|
|
548
548
|
meta: {
|
|
549
549
|
title: "Saved Filters",
|
|
@@ -1207,4 +1207,4 @@ var SavedFilterPresets_default = _sfc_main;
|
|
|
1207
1207
|
|
|
1208
1208
|
//#endregion
|
|
1209
1209
|
export { useSavedFilters as a, buildQueryWithFilters as c, serializeFiltersToQueryParams as d, PINNED_PRESETS_KEY as f, BaseModal_default as g, MS as h, savedFilterRoutes as i, deserializeFiltersFromQueryParams as l, usePinnedPresets as m, ManagePresetsModal_default as n, createLastUsedPresetGuard as o, useInjectedPinnedPresets as p, SaveFilterModal_default as r, getLastUsedPresetRedirect as s, SavedFilterPresets_default as t, extractFiltersFromQuery as u };
|
|
1210
|
-
//# sourceMappingURL=saved_filter-
|
|
1210
|
+
//# sourceMappingURL=saved_filter-erjEgsdK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saved_filter-C2N9l_a9.js","names":["DEFAULT_OPERATORS: Record<string, string>","config: FilterConfiguration","operator: string","params: Record<string, string>","filters: FilterQueryParams","base: Record<string, string | string[]>","LAST_USED_PREFIX","getLastUsedKey","savedFilterRoutes: Array<ExtendedRouteRecordRaw>"],"sources":["../src/components/BaseModal.vue","../src/constants/time.ts","../src/slices/saved_filter/usePinnedPresets.ts","../src/utils/filterUrlParser.ts","../src/slices/saved_filter/lastUsedPresetGuard.ts","../src/slices/saved_filter/useSavedFilters.ts","../src/slices/saved_filter/savedFilterRoutes.ts","../src/slices/saved_filter/SaveFilterModal.vue","../src/slices/saved_filter/ManagePresetsModal.vue","../src/slices/saved_filter/PresetsModal.vue","../src/slices/saved_filter/SavedFilterPresets.vue"],"sourcesContent":["<template>\n <dialog ref=\"modal\" class=\"modal\">\n <div class=\"modal-box max-w-md w-full max-h-[90vh] flex flex-col p-0\">\n <!-- Header with title and close button - fixed, not scrollable -->\n <div\n class=\"flex justify-between items-center p-6 pb-4 border-b border-base-300 flex-shrink-0\"\n >\n <h3 class=\"font-bold text-lg\">\n <slot name=\"title\">{{ title }}</slot>\n </h3>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n @click.prevent=\"handleClose\"\n aria-label=\"Close modal\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-6 w-6\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n <!-- Content slot - scrollable area -->\n <div class=\"modal-content flex-1 overflow-y-auto p-6\">\n <slot></slot>\n </div>\n </div>\n <form method=\"dialog\" class=\"modal-backdrop\">\n <button type=\"button\" @click.prevent=\"handleClose\">close</button>\n </form>\n </dialog>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch } from 'vue';\n\ninterface Props {\n isOpen: boolean;\n title?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: '',\n});\n\nconst emit = defineEmits<{\n close: [];\n}>();\n\nconst modal = ref<HTMLDialogElement>();\n\nconst handleClose = () => {\n emit('close');\n};\n\n// Watch for modal open/close\nwatch(\n () => props.isOpen,\n (isOpen) => {\n if (isOpen && modal.value) {\n modal.value.showModal();\n } else if (!isOpen && modal.value) {\n modal.value.close();\n }\n },\n { immediate: true },\n);\n</script>\n","/**\n * Time duration constants in milliseconds.\n * Use for staleTime, cache TTL, etc.\n *\n * @example\n * staleTime: 30 * MS.ONE_SECOND\n * staleTime: 5 * MS.ONE_MINUTE\n * staleTime: 24 * MS.ONE_HOUR\n */\nexport const MS = {\n ONE_SECOND: 1000,\n ONE_MINUTE: 60 * 1000,\n ONE_HOUR: 60 * 60 * 1000,\n} as const;\n","/**\n * Composable for pinning presets to favorites (home page + sidebar).\n * Uses API with 24h cache. Max 5 pinned presets.\n *\n * Call usePinnedPresets() once in the layout (InAppLayout) and provide it.\n * Children use useInjectedPinnedPresets() to avoid duplicate fetches.\n */\n\nimport type { SavedFilterReadDto } from '@dragonmastery/dragoncore-shared';\nimport type { InjectionKey } from 'vue';\nimport { inject } from 'vue';\nimport { MS } from '../../constants/time';\nimport { useMutation } from '../../composables/useMutation';\nimport { useQuery } from '../../composables/useQuery';\nimport { computed } from 'vue';\n\nconst CACHE_KEY = 'pinned-presets';\nconst MAX_PINNED = 5;\n\nexport type PinnedPreset = Pick<\n SavedFilterReadDto,\n 'id' | 'name' | 'context' | 'route_path' | 'filters'\n>;\n\nexport function usePinnedPresets() {\n const { data: pinnedData, refetch } = useQuery(\n (api) => api.savedFilters.listPinnedPresets(),\n { cacheKey: CACHE_KEY, staleTime: 24 * MS.ONE_HOUR },\n );\n\n const { mutate: addPin } = useMutation(\n (api, id: string) => api.savedFilters.addPinnedPreset(id),\n { invalidate: CACHE_KEY },\n );\n\n const { mutate: removePin } = useMutation(\n (api, id: string) => api.savedFilters.removePinnedPreset(id),\n { invalidate: CACHE_KEY },\n );\n\n const pinned = computed(() => pinnedData.value ?? []);\n const pinnedIds = computed(() => new Set(pinned.value.map((p) => p.id)));\n const canPinMore = computed(() => pinned.value.length < MAX_PINNED);\n\n function isPinned(presetId: string): boolean {\n return pinnedIds.value.has(presetId);\n }\n\n async function pinPreset(preset: SavedFilterReadDto): Promise<boolean> {\n if (pinned.value.length >= MAX_PINNED) return false;\n if (pinnedIds.value.has(preset.id)) return true;\n try {\n await addPin(preset.id);\n await refetch();\n return true;\n } catch {\n return false;\n }\n }\n\n async function unpinPreset(presetId: string): Promise<void> {\n try {\n await removePin(presetId);\n await refetch();\n } catch {\n // Error handled by mutation\n }\n }\n\n async function togglePin(preset: SavedFilterReadDto): Promise<boolean> {\n if (isPinned(preset.id)) {\n await unpinPreset(preset.id);\n return false;\n }\n return pinPreset(preset);\n }\n\n function getPresetLink(preset: PinnedPreset): {\n path: string;\n query: Record<string, string | string[]>;\n } {\n return {\n path: preset.route_path,\n query: preset.filters ?? {},\n };\n }\n\n function updatePinnedPreset(\n presetId: string,\n updates: Partial<PinnedPreset>,\n ): void {\n // API is source of truth. Call refetchPinned when a preset is renamed\n // to refresh the pinned list with updated names.\n void presetId;\n void updates;\n }\n\n return {\n pinned,\n pinnedIds,\n canPinMore,\n isPinned,\n pinPreset,\n unpinPreset,\n togglePin,\n getPresetLink,\n updatePinnedPreset,\n refetchPinned: refetch,\n maxPinned: MAX_PINNED,\n };\n}\n\nexport const PINNED_PRESETS_KEY = Symbol('pinnedPresets') as InjectionKey<\n ReturnType<typeof usePinnedPresets>\n>;\n\n/** Use pinned presets from the layout provider. Call this in child components (Sidebar, AppHome, etc.). */\nexport function useInjectedPinnedPresets() {\n const injected = inject(PINNED_PRESETS_KEY);\n if (!injected) {\n throw new Error(\n 'useInjectedPinnedPresets must be used within a component tree that provides pinned presets (e.g. InAppLayout).',\n );\n }\n return injected;\n}\n","/**\n * Filter URL Parser - Query string serialization/deserialization\n * Uses compact DrizzleORM-style format: flt[field]=value or flt[field][op]=value\n */\n\nimport { OPERATORS } from '@dragonmastery/dragoncore-shared';\n\n/**\n * Default operators for each field type when not specified in URL\n */\nconst DEFAULT_OPERATORS: Record<string, string> = {\n string: OPERATORS.CONTAINS,\n number: OPERATORS.EQUALS,\n date: OPERATORS.EQUALS,\n boolean: OPERATORS.EQUALS,\n enum: OPERATORS.EQUALS,\n};\n\n/**\n * Numeric types that should be converted to numbers\n */\nconst NUMERIC_TYPES = ['number', 'money', 'percentage'];\n\n/**\n * Check if operator requires array values\n */\nfunction requiresArrayValues(operator: string): boolean {\n return (\n operator === OPERATORS.IS_ONE_OF ||\n operator === OPERATORS.IS_NOT_ONE_OF ||\n operator === OPERATORS.BETWEEN\n );\n}\n\n/**\n * Check if operator is a null check (requires no value)\n */\nfunction isNullCheckOperator(operator: string): boolean {\n return operator === OPERATORS.IS_EMPTY || operator === OPERATORS.IS_NOT_EMPTY;\n}\n\n/**\n * Field registry metadata for type inference\n */\nexport interface FieldRegistryMetadata {\n [fieldName: string]: {\n type: 'string' | 'number' | 'date' | 'boolean' | 'enum' | 'money' | 'percentage';\n filterable?: boolean;\n };\n}\n\n/**\n * Filter value object structure\n */\nexport interface FilterValueObject {\n operator: string;\n value?: any;\n values?: any[];\n caseSensitive?: boolean;\n}\n\n/**\n * Filter configuration - flat object mapping field names to filter objects\n */\nexport type FilterConfiguration = Record<string, FilterValueObject>;\n\n/**\n * Deserialize filter configuration from URL query parameters object\n * Parses compact DrizzleORM-style format: flt[field]=value or flt[field][op]=value\n *\n * @param params - URL query parameters object (from Vue Router route.query)\n * @param registry - Field registry metadata for type inference\n * @returns Filter configuration object\n *\n * @example\n * // URL: ?flt[department][eq]=Engineering&flt[id]=4&flt[role][in]=[\"admin\",\"user\"]\n * const filters = deserializeFiltersFromQueryParams(route.query, fieldRegistry);\n * // Result: {\n * // department: { operator: 'eq', value: 'Engineering' },\n * // id: { operator: 'eq', value: 4 },\n * // role: { operator: 'in', values: ['admin', 'user'] }\n * // }\n */\nexport function deserializeFiltersFromQueryParams(\n params: Record<string, string | string[]>,\n registry: FieldRegistryMetadata,\n): FilterConfiguration {\n const config: FilterConfiguration = {};\n const fieldMap = new Map<string, Partial<FilterValueObject>>();\n\n // Parse query params\n for (const [key, value] of Object.entries(params)) {\n // Match flt[field] or flt[field][op]\n const matchDefault = key.match(/^flt\\[([^\\]]+)\\]$/);\n const matchWithOp = key.match(/^flt\\[([^\\]]+)\\]\\[([^\\]]+)\\]$/);\n\n if (!matchDefault && !matchWithOp) continue;\n\n const fieldName = matchDefault ? matchDefault[1] : matchWithOp ? matchWithOp[1] : null;\n const operatorShorthand = matchWithOp ? matchWithOp[2] : null;\n const stringValue = Array.isArray(value) ? value[0] : value;\n\n if (!fieldName) continue;\n\n const fieldMeta = registry[fieldName];\n if (!fieldMeta) continue; // Skip unknown fields\n\n const fieldType = fieldMeta.type;\n const defaultOp = DEFAULT_OPERATORS[fieldType] || OPERATORS.EQUALS;\n\n // Determine operator\n let operator: string;\n if (operatorShorthand) {\n operator = operatorShorthand;\n } else {\n operator = defaultOp;\n }\n\n if (!fieldMap.has(fieldName)) {\n fieldMap.set(fieldName, { operator });\n }\n\n const filter = fieldMap.get(fieldName)!;\n filter.operator = operator;\n\n // Parse value based on operator type\n if (isNullCheckOperator(operator)) {\n // Null checks don't have values\n // Value is already set, nothing more to do\n } else if (requiresArrayValues(operator)) {\n // Array operators: parse JSON array (handles commas/quotes in values)\n if (!stringValue || stringValue.trim() === '') {\n fieldMap.delete(fieldName);\n continue;\n }\n\n try {\n const parsed = JSON.parse(stringValue);\n if (Array.isArray(parsed) && parsed.length > 0) {\n // Convert to appropriate types based on field type\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.values = parsed.map((v) => Number(v));\n } else {\n filter.values = parsed;\n }\n } else {\n fieldMap.delete(fieldName);\n continue;\n }\n } catch (e) {\n // Invalid JSON, try legacy comma-separated format for backward compatibility\n const values = stringValue\n .split(',')\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n if (values.length === 0) {\n fieldMap.delete(fieldName);\n continue;\n }\n\n if (operator === OPERATORS.BETWEEN && values.length === 2) {\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.values = [Number(values[0]!), Number(values[1]!)];\n } else if (fieldType === 'date') {\n filter.values = [values[0]!, values[1]!];\n } else {\n filter.values = values;\n }\n } else if (operator === OPERATORS.BETWEEN) {\n fieldMap.delete(fieldName);\n continue;\n } else {\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.values = values.map((v) => Number(v));\n } else {\n filter.values = values;\n }\n }\n }\n } else {\n // Single value operators\n // Special case: if operator is in/notIn but we got a single value, convert to array\n if (\n (operator === OPERATORS.IS_ONE_OF || operator === OPERATORS.IS_NOT_ONE_OF) &&\n stringValue\n ) {\n // Convert single value to array for array operators\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.values = [Number(stringValue)];\n } else {\n filter.values = [stringValue];\n }\n } else if (stringValue) {\n // Regular single value operator\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.value = Number(stringValue);\n } else if (fieldType === 'boolean') {\n filter.value = stringValue === 'true' || stringValue === '1';\n } else {\n filter.value = stringValue;\n }\n\n // String fields: always case-insensitive (contains is default)\n if (fieldType === 'string' && operator === OPERATORS.CONTAINS) {\n filter.caseSensitive = false;\n }\n }\n }\n }\n\n // Convert to FilterConfiguration\n for (const [fieldName, filter] of fieldMap.entries()) {\n if (filter.operator) {\n config[fieldName] = filter as FilterValueObject;\n }\n }\n\n return config;\n}\n\n/**\n * Serialize filter configuration to URL query parameters object\n * Uses compact DrizzleORM-style format: flt[field][op]=value\n *\n * @param config - Filter configuration object\n * @param registry - Field registry metadata for type inference\n * @returns URL query parameters object (can be used with Vue Router)\n *\n * @example\n * const filters = {\n * department: { operator: 'eq', value: 'Engineering' },\n * role: { operator: 'in', values: ['admin', 'user'] }\n * };\n * const params = serializeFiltersToQueryParams(filters, fieldRegistry);\n * // Result: {\n * // 'flt[department][eq]': 'Engineering',\n * // 'flt[role][in]': '[\"admin\",\"user\"]'\n * // }\n */\nexport function serializeFiltersToQueryParams(\n config: FilterConfiguration,\n registry: FieldRegistryMetadata,\n): Record<string, string> {\n const params: Record<string, string> = {};\n\n for (const [fieldName, filter] of Object.entries(config)) {\n const fieldMeta = registry[fieldName];\n if (!fieldMeta) continue;\n\n const operator = filter.operator;\n\n if (isNullCheckOperator(operator)) {\n // Null checks don't need values, just the operator\n params[`flt[${fieldName}][${operator}]`] = '';\n } else if (requiresArrayValues(operator)) {\n // Array operators: use JSON encoding (handles commas/quotes)\n if (filter.values && Array.isArray(filter.values)) {\n params[`flt[${fieldName}][${operator}]`] = JSON.stringify(filter.values);\n }\n } else {\n // Single value: include operator explicitly\n params[`flt[${fieldName}][${operator}]`] = String(filter.value);\n }\n }\n\n return params;\n}\n\n/** Raw filter params format used by useSavedFilters (flt[key]=value) */\nexport type FilterQueryParams = Record<string, string | string[]>;\n\n/**\n * Extract filter query params from route query.\n * Handles Vue Router's LocationQuery type which can have null values.\n * Only extracts flt[...] params (actual filters).\n *\n * @example\n * // URL: ?flt[type][in]=[\"sales\",\"escalation\"]&flt[phase][ne]=Post+Sale\n * // Returns: { 'flt[type][in]': '[\"sales\",\"escalation\"]', 'flt[phase][ne]': 'Post+Sale' }\n */\nexport function extractFiltersFromQuery(query: Record<string, unknown>): FilterQueryParams {\n const filters: FilterQueryParams = {};\n\n Object.keys(query).forEach((key) => {\n if (key.startsWith('flt[')) {\n const value = query[key];\n if (value !== null && value !== undefined) {\n if (Array.isArray(value)) {\n const filtered = value.filter((v): v is string => v !== null && v !== undefined);\n if (filtered.length > 0) {\n filters[key] = filtered;\n }\n } else {\n filters[key] = String(value);\n }\n }\n }\n });\n\n return filters;\n}\n\n/**\n * Build a new query object with filter params replaced (not merged).\n * Strips all flt[...] params from the current query, then adds the new filters.\n * Preserves non-filter params (sort, pagination, etc).\n */\nexport function buildQueryWithFilters(\n currentQuery: Record<string, unknown>,\n filters: FilterQueryParams,\n): Record<string, string | string[]> {\n const base: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(currentQuery)) {\n if (!key.startsWith('flt[') && value !== null && value !== undefined) {\n base[key] = Array.isArray(value)\n ? (value.filter((v): v is string => v != null) as string[])\n : String(value);\n }\n }\n return { ...base, ...filters };\n}\n","/**\n * Router guard helper: redirect to last-used preset filters when navigating with no URL filters.\n * Uses localStorage (no API call) - filters are stored when user applies a preset.\n * Prevents double fetch by applying preset before the list component mounts.\n */\n\nimport { buildQueryWithFilters, extractFiltersFromQuery } from '../../utils/filterUrlParser';\n\nconst LAST_USED_PREFIX = 'lastUsedPreset:';\nconst LAST_USED_FILTERS_PREFIX = 'lastUsedPresetFilters:';\n\nfunction getLastUsedKey(context: string): string {\n return `${LAST_USED_PREFIX}${context}`;\n}\n\nfunction getLastUsedFiltersKey(context: string): string {\n return `${LAST_USED_FILTERS_PREFIX}${context}`;\n}\n\nexport function getLastUsedPresetFilters(\n context: string,\n): Record<string, string | string[]> | null {\n try {\n const stored = localStorage.getItem(getLastUsedFiltersKey(context));\n if (!stored) return null;\n const parsed = JSON.parse(stored) as Record<string, string | string[]>;\n return typeof parsed === 'object' && parsed !== null ? parsed : null;\n } catch {\n return null;\n }\n}\n\nexport function setLastUsedPresetFilters(\n context: string,\n filters: Record<string, string | string[]> | null,\n): void {\n try {\n if (filters && Object.keys(filters).length > 0) {\n localStorage.setItem(getLastUsedFiltersKey(context), JSON.stringify(filters));\n } else {\n localStorage.removeItem(getLastUsedFiltersKey(context));\n }\n } catch {\n // Ignore\n }\n}\n\n/**\n * If navigating with no filters and we have a last-used preset, returns the query to redirect to.\n * Otherwise returns null (no redirect needed).\n */\nexport function getLastUsedPresetRedirect(\n context: string,\n currentQuery: Record<string, unknown>,\n): Record<string, string | string[]> | null {\n const filters = extractFiltersFromQuery(currentQuery);\n if (Object.keys(filters).length > 0) return null;\n\n const lastUsedId = localStorage.getItem(getLastUsedKey(context));\n if (!lastUsedId) return null;\n\n const lastUsedFilters = getLastUsedPresetFilters(context);\n if (!lastUsedFilters || Object.keys(lastUsedFilters).length === 0) return null;\n\n return buildQueryWithFilters(currentQuery, lastUsedFilters);\n}\n\n/**\n * Creates a Vue Router beforeEnter guard that redirects to last-used preset when navigating with no filters.\n * Use on list routes that have SavedFilterPresets.\n */\nexport function createLastUsedPresetGuard(\n context: string,\n routeName: string,\n) {\n return (to: { name?: unknown; query: Record<string, unknown> }) => {\n if (to.name !== routeName) return undefined;\n const redirectQuery = getLastUsedPresetRedirect(context, to.query);\n if (!redirectQuery) return undefined;\n return { name: routeName, query: redirectQuery };\n };\n}\n","/**\n * Composable for saved filter presets - save, apply, and manage named filter configurations.\n * Works with any data table that provides getFiltersFromTable / applyFiltersToTable adapters.\n */\n\nimport type { DragoncoreApi } from '@dragonmastery/dragoncore-shared';\nimport type { SavedFilterReadDto } from '@dragonmastery/dragoncore-shared';\nimport { MS } from '../../constants/time';\nimport { useMutation } from '../../composables/useMutation';\nimport { useQuery } from '../../composables/useQuery';\nimport { computed, onMounted, ref, watch } from 'vue';\nimport { setLastUsedPresetFilters } from './lastUsedPresetGuard';\n\nconst LAST_USED_PREFIX = 'lastUsedPreset:';\n\nexport type SavedFiltersConfig = {\n /** Unique context per table type (e.g. \"tracker\", \"followup\") */\n context: string;\n /** Route path for the table (e.g. \"/trackers\", \"/followups\") */\n routePath: string;\n /** Read current filters from the table/URL */\n getFiltersFromTable: () => Record<string, string | string[]>;\n /** Apply filters to the table/URL */\n applyFiltersToTable: (filters: Record<string, string | string[]>) => void;\n /** Called when user explicitly clears all filters - clear last-used preset */\n onClearFilters?: () => void;\n};\n\nfunction getLastUsedKey(context: string): string {\n return `${LAST_USED_PREFIX}${context}`;\n}\n\nfunction getLastUsedPresetId(context: string): string | null {\n try {\n return localStorage.getItem(getLastUsedKey(context));\n } catch {\n return null;\n }\n}\n\nfunction setLastUsedPresetId(context: string, presetId: string | null): void {\n try {\n if (presetId) {\n localStorage.setItem(getLastUsedKey(context), presetId);\n } else {\n localStorage.removeItem(getLastUsedKey(context));\n }\n } catch {\n // Ignore\n }\n}\n\nexport function useSavedFilters<TApi extends DragoncoreApi = DragoncoreApi>(\n config: SavedFiltersConfig,\n) {\n const {\n context,\n routePath,\n getFiltersFromTable,\n applyFiltersToTable,\n onClearFilters,\n } = config;\n\n const cacheKey = `saved-filters:${context}`;\n const hasAppliedLastUsed = ref(false);\n const lastUsedPresetIdRef = ref<string | null>(getLastUsedPresetId(context));\n\n const { data: presets, loading: presetsLoading, refetch: refetchPresets } = useQuery<\n TApi,\n SavedFilterReadDto[]\n >((api) => api.savedFilters.listSavedFilters(context), {\n cacheKey,\n staleTime: 24 * MS.ONE_HOUR,\n });\n\n const { mutate: createPreset, loading: creating } = useMutation<\n TApi,\n { name: string; context: string; route_path: string; filters: Record<string, string | string[]>; sort_by?: string; sort_direction?: 'asc' | 'desc' },\n SavedFilterReadDto\n >(\n (api, input) => api.savedFilters.createSavedFilter(input),\n { invalidate: /^saved-filters:/ },\n );\n\n const { mutate: updatePreset, loading: updating } = useMutation<\n TApi,\n { id: string; name?: string; route_path?: string; filters?: Record<string, string | string[]>; sort_by?: string; sort_direction?: 'asc' | 'desc' },\n SavedFilterReadDto | null\n >(\n (api, input) => api.savedFilters.updateSavedFilter(input),\n { invalidate: /^saved-filters:|^pinned-presets/ },\n );\n\n const { mutate: deletePreset, loading: deleting } = useMutation<TApi, string, boolean>(\n (api, id) => api.savedFilters.deleteSavedFilter(id),\n { invalidate: /^saved-filters:/ },\n );\n\n function applyPreset(preset: SavedFilterReadDto): void {\n applyFiltersToTable(preset.filters);\n setLastUsedPresetId(context, preset.id);\n setLastUsedPresetFilters(context, preset.filters);\n lastUsedPresetIdRef.value = preset.id;\n }\n\n function clearPreset(): void {\n applyFiltersToTable({});\n setLastUsedPresetId(context, null);\n setLastUsedPresetFilters(context, null);\n lastUsedPresetIdRef.value = null;\n }\n\n function clearLastUsed(): void {\n setLastUsedPresetId(context, null);\n setLastUsedPresetFilters(context, null);\n lastUsedPresetIdRef.value = null;\n onClearFilters?.();\n }\n\n async function saveCurrentFilters(name: string): Promise<SavedFilterReadDto | null> {\n const filters = getFiltersFromTable();\n if (Object.keys(filters).length === 0) {\n return null;\n }\n try {\n const result = await createPreset({\n name,\n context,\n route_path: routePath,\n filters,\n });\n if (result) {\n await refetchPresets();\n setLastUsedPresetId(context, result.id);\n setLastUsedPresetFilters(context, result.filters);\n lastUsedPresetIdRef.value = result.id;\n }\n return result ?? null;\n } catch {\n return null;\n }\n }\n\n async function updatePresetFilters(presetId: string, name?: string): Promise<SavedFilterReadDto | null> {\n const filters = getFiltersFromTable();\n try {\n const result = await updatePreset({\n id: presetId,\n ...(name !== undefined && { name }),\n filters,\n });\n return result ?? null;\n } catch {\n return null;\n }\n }\n\n async function renamePreset(presetId: string, name: string): Promise<SavedFilterReadDto | null> {\n try {\n const result = await updatePreset({ id: presetId, name });\n if (result) {\n await refetchPresets();\n }\n return result ?? null;\n } catch {\n return null;\n }\n }\n\n async function removePreset(presetId: string): Promise<boolean> {\n try {\n await deletePreset(presetId);\n await refetchPresets();\n return true;\n } catch {\n return false;\n }\n }\n\n // On mount: if no filters in URL, apply last-used preset when presets load.\n // Note: For routes with lastUsedPresetGuard, the guard redirects before mount so this is often a no-op.\n onMounted(() => {\n const filters = getFiltersFromTable();\n if (Object.keys(filters).length > 0) {\n hasAppliedLastUsed.value = true;\n return;\n }\n const lastUsedId = getLastUsedPresetId(context);\n if (!lastUsedId) {\n hasAppliedLastUsed.value = true;\n return;\n }\n const unwatch = watch(\n () => presets.value,\n (list) => {\n if (!list || hasAppliedLastUsed.value) return;\n const preset = list.find((p) => p.id === lastUsedId);\n if (preset) {\n applyPreset(preset);\n }\n hasAppliedLastUsed.value = true;\n unwatch();\n },\n { immediate: true },\n );\n });\n\n const activePreset = computed(() => {\n const id = lastUsedPresetIdRef.value;\n if (!id) return null;\n return (presets.value ?? []).find((p) => p.id === id) ?? null;\n });\n\n return {\n presets: computed(() => presets.value ?? []),\n presetsLoading,\n creating,\n updating,\n deleting,\n applyPreset,\n clearPreset,\n clearLastUsed,\n activePreset,\n saveCurrentFilters,\n updatePresetFilters,\n renamePreset,\n removePreset,\n };\n}\n","import { userAuthenticated } from '../../middleware/userAuthorized';\nimport type { ExtendedRouteRecordRaw } from '../../types/ExtendedRoute';\n\nexport const savedFilterRoutes: Array<ExtendedRouteRecordRaw> = [\n {\n path: '/saved-filters',\n name: 'SavedFilters',\n component: () => import('./SavedFiltersPage.vue'),\n beforeEnter: [userAuthenticated],\n meta: {\n title: 'Saved Filters',\n description: 'Manage your saved filter presets and favorites',\n side_bar: {\n section: 'Saved Filters',\n visible_to: ['consumer', 'lead', 'staff', 'super_admin'],\n },\n },\n },\n];\n","<template>\n <BaseModal :is-open=\"isOpen\" title=\"Save Filter Preset\" @close=\"close\">\n <form @submit.prevent=\"handleSubmit\" class=\"space-y-4\">\n <div class=\"form-control\">\n <label class=\"label\">\n <span class=\"label-text\">Preset name</span>\n </label>\n <input\n v-model=\"name\"\n type=\"text\"\n placeholder=\"e.g. My Sales Escalations\"\n class=\"input input-bordered w-full\"\n maxlength=\"100\"\n />\n <label v-if=\"error\" class=\"label\">\n <span class=\"label-text-alt text-error\">{{ error }}</span>\n </label>\n </div>\n <div class=\"flex justify-end gap-2\">\n <button type=\"button\" class=\"btn btn-ghost\" @click=\"close\">\n Cancel\n </button>\n <button type=\"submit\" class=\"btn btn-primary\" :disabled=\"saving || !name.trim()\">\n {{ saving ? 'Saving...' : 'Save' }}\n </button>\n </div>\n </form>\n </BaseModal>\n</template>\n\n<script setup lang=\"ts\">\nimport BaseModal from '../../components/BaseModal.vue';\nimport { ref, watch } from 'vue';\n\nconst props = defineProps<{\n isOpen: boolean;\n saving?: boolean;\n}>();\n\nconst emit = defineEmits<{\n close: [];\n save: [name: string];\n}>();\n\nconst name = ref('');\nconst error = ref('');\n\nfunction close() {\n emit('close');\n}\n\nfunction handleSubmit() {\n const trimmed = name.value.trim();\n if (!trimmed) {\n error.value = 'Name is required';\n return;\n }\n if (trimmed.length > 100) {\n error.value = 'Name must be 100 characters or less';\n return;\n }\n error.value = '';\n emit('save', trimmed);\n}\n\nwatch(() => props.isOpen, (open) => {\n if (open) {\n name.value = '';\n error.value = '';\n }\n});\n</script>\n","<template>\n <BaseModal :is-open=\"isOpen\" title=\"Manage Filter Presets\" @close=\"close\">\n <div class=\"space-y-4\">\n <p v-if=\"presets.length === 0 && !loading\" class=\"text-base-content/70 text-sm\">\n No saved presets yet. Set filters, then use \"Save current filters\" in the Presets menu.\n </p>\n <div v-else-if=\"loading\" class=\"flex justify-center py-4\">\n <span class=\"loading loading-spinner loading-md\" />\n </div>\n <ul v-else class=\"space-y-2\">\n <li\n v-for=\"preset in presets\"\n :key=\"preset.id\"\n class=\"flex items-center gap-2 p-2 rounded-lg bg-base-200\"\n >\n <template v-if=\"editingId === preset.id\">\n <input\n ref=\"renameInputRef\"\n v-model=\"editingName\"\n type=\"text\"\n class=\"input input-sm input-bordered flex-1 min-w-0\"\n maxlength=\"100\"\n placeholder=\"Preset name\"\n @keydown.enter=\"saveRename(preset.id)\"\n @keydown.escape=\"cancelRename\"\n />\n <button\n type=\"button\"\n class=\"btn btn-sm btn-primary shrink-0\"\n :disabled=\"renaming || !editingName.trim()\"\n @click=\"saveRename(preset.id)\"\n >\n {{ renaming ? '...' : 'Save' }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost shrink-0\"\n :disabled=\"renaming\"\n @click=\"cancelRename\"\n >\n Cancel\n </button>\n </template>\n <template v-else>\n <span class=\"font-medium truncate flex-1 min-w-0\">\n {{ preset.name }}\n <span class=\"text-base-content/60 text-xs font-normal ml-1\">\n ({{ Object.keys(preset.filters || {}).length }})\n </span>\n </span>\n <div class=\"flex gap-1 shrink-0\">\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n title=\"Rename\"\n @click=\"startRename(preset)\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost text-error\"\n title=\"Delete\"\n :disabled=\"deleting\"\n @click=\"handleDelete(preset)\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" 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\" />\n </svg>\n </button>\n </div>\n </template>\n </li>\n </ul>\n <div class=\"flex justify-end pt-2\">\n <button type=\"button\" class=\"btn btn-ghost\" @click=\"close\">\n Close\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<script setup lang=\"ts\">\nimport BaseModal from '../../components/BaseModal.vue';\nimport type { SavedFilterReadDto } from '@dragonmastery/dragoncore-shared';\nimport { nextTick, ref, watch } from 'vue';\n\nconst props = defineProps<{\n isOpen: boolean;\n presets: SavedFilterReadDto[];\n loading?: boolean;\n deleting?: boolean;\n renaming?: boolean;\n renamePreset: (presetId: string, name: string) => Promise<SavedFilterReadDto | null>;\n}>();\n\nconst emit = defineEmits<{\n close: [];\n delete: [preset: SavedFilterReadDto];\n}>();\n\nconst editingId = ref<string | null>(null);\nconst editingName = ref('');\nconst renameInputRef = ref<HTMLInputElement | null>(null);\n\nfunction close() {\n emit('close');\n}\n\nfunction startRename(preset: SavedFilterReadDto) {\n editingId.value = preset.id;\n editingName.value = preset.name;\n nextTick(() => renameInputRef.value?.focus());\n}\n\nfunction cancelRename() {\n editingId.value = null;\n editingName.value = '';\n}\n\nasync function saveRename(presetId: string) {\n const name = editingName.value.trim();\n if (!name) return;\n const result = await props.renamePreset(presetId, name);\n if (result) {\n cancelRename();\n }\n}\n\nfunction handleDelete(preset: SavedFilterReadDto) {\n emit('delete', preset);\n}\n\nwatch(() => props.isOpen, (open) => {\n if (!open) {\n cancelRename();\n }\n});\n</script>\n","<template>\n <BaseModal :is-open=\"isOpen\" title=\"Filter Presets\" @close=\"close\">\n <div class=\"space-y-4\">\n <!-- Preset list: apply (click name), rename, delete -->\n <div v-if=\"presets.length > 0\">\n <p class=\"text-xs font-medium text-base-content/60 uppercase tracking-wide mb-2\">\n Apply preset\n </p>\n <ul class=\"space-y-2\">\n <li\n v-for=\"preset in presets\"\n :key=\"preset.id\"\n class=\"flex items-center gap-2 p-2 rounded-lg bg-base-200\"\n >\n <template v-if=\"editingId === preset.id\">\n <input\n ref=\"renameInputRef\"\n v-model=\"editingName\"\n type=\"text\"\n class=\"input input-sm input-bordered flex-1 min-w-0\"\n maxlength=\"100\"\n placeholder=\"Preset name\"\n @keydown.enter=\"saveRename(preset.id)\"\n @keydown.escape=\"cancelRename\"\n />\n <button\n type=\"button\"\n class=\"btn btn-sm btn-primary shrink-0\"\n :disabled=\"renaming || !editingName.trim()\"\n @click=\"saveRename(preset.id)\"\n >\n {{ renaming ? '...' : 'Save' }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost shrink-0\"\n :disabled=\"renaming\"\n @click=\"cancelRename\"\n >\n Cancel\n </button>\n </template>\n <template v-else>\n <button\n type=\"button\"\n class=\"flex-1 min-w-0 text-left font-medium truncate rounded px-2 py-1 -mx-2 -my-1 hover:bg-base-300\"\n :class=\"{ 'bg-primary/10': preset.id === activePreset?.id }\"\n @click=\"applyAndClose(preset)\"\n >\n {{ preset.name }}\n <span class=\"text-base-content/60 text-xs font-normal ml-1\">\n ({{ Object.keys(preset.filters || {}).length }})\n </span>\n </button>\n <div class=\"flex gap-1 shrink-0\">\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n :title=\"isPinned(preset.id) ? 'Unpin from favorites' : (canPinMore ? 'Pin to favorites (home + sidebar)' : `Maximum ${maxPinned} pinned`)\"\n :disabled=\"!canPinMore && !isPinned(preset.id)\"\n @click.stop=\"handleTogglePin(preset)\"\n >\n <svg\n class=\"w-4 h-4\"\n :class=\"{ 'text-primary': isPinned(preset.id) }\"\n :fill=\"isPinned(preset.id) ? 'currentColor' : 'none'\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n title=\"Rename\"\n @click.stop=\"startRename(preset)\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost text-error\"\n title=\"Delete\"\n :disabled=\"deleting\"\n @click.stop=\"handleDelete(preset)\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" 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\" />\n </svg>\n </button>\n </div>\n </template>\n </li>\n </ul>\n </div>\n\n <!-- Empty state -->\n <p\n v-else-if=\"!loading\"\n class=\"text-base-content/70 text-sm\"\n >\n No presets yet. Set filters, then save below.\n </p>\n\n <div v-if=\"loading\" class=\"flex justify-center py-2\">\n <span class=\"loading loading-spinner loading-md\" />\n </div>\n\n <div class=\"divider my-2\" />\n\n <!-- Save current filters: inline form when saving -->\n <div v-if=\"showSaveForm\" class=\"space-y-2\">\n <label class=\"label py-0\">\n <span class=\"label-text\">Preset name</span>\n </label>\n <div class=\"flex gap-2\">\n <input\n ref=\"saveInputRef\"\n v-model=\"saveName\"\n type=\"text\"\n placeholder=\"e.g. My Sales Escalations\"\n class=\"input input-sm input-bordered flex-1\"\n maxlength=\"100\"\n @keydown.enter=\"submitSave\"\n @keydown.escape=\"cancelSave\"\n />\n <button\n type=\"button\"\n class=\"btn btn-sm btn-primary\"\n :disabled=\"creating || !saveName.trim()\"\n @click=\"submitSave\"\n >\n {{ creating ? 'Saving...' : 'Save' }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n :disabled=\"creating\"\n @click=\"cancelSave\"\n >\n Cancel\n </button>\n </div>\n </div>\n <button\n v-else\n type=\"button\"\n class=\"btn btn-ghost btn-sm btn-block justify-start gap-2\"\n :disabled=\"!hasFilters || creating || !canSaveMore\"\n :title=\"!canSaveMore ? `Maximum ${MAX_PRESETS_PER_CONTEXT} presets per page. Delete one to save a new preset.` : undefined\"\n @click=\"startSave\"\n >\n <svg class=\"w-4 h-4 shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8v8H4V4h8m0 0l4 4-4 4\" />\n </svg>\n Save current filters\n <span v-if=\"!canSaveMore\" class=\"text-base-content/60 text-xs\">(max {{ MAX_PRESETS_PER_CONTEXT }})</span>\n </button>\n\n <div class=\"flex justify-end pt-2\">\n <button type=\"button\" class=\"btn btn-ghost\" @click=\"close\">\n Close\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<script setup lang=\"ts\">\nimport BaseModal from '../../components/BaseModal.vue';\nimport {\n MAX_PRESETS_PER_CONTEXT,\n type SavedFilterReadDto,\n} from '@dragonmastery/dragoncore-shared';\nimport { computed, nextTick, ref, watch } from 'vue';\n\nconst props = defineProps<{\n isOpen: boolean;\n presets: SavedFilterReadDto[];\n loading?: boolean;\n creating?: boolean;\n deleting?: boolean;\n renaming?: boolean;\n hasFilters: boolean;\n activePreset: SavedFilterReadDto | null;\n applyPreset: (preset: SavedFilterReadDto) => void;\n saveCurrentFilters: (name: string) => Promise<SavedFilterReadDto | null>;\n renamePreset: (presetId: string, name: string) => Promise<SavedFilterReadDto | null>;\n isPinned: (presetId: string) => boolean;\n togglePin: (preset: SavedFilterReadDto) => boolean | Promise<boolean>;\n canPinMore: boolean;\n maxPinned?: number;\n}>();\n\nconst maxPinned = computed(() => props.maxPinned ?? 5);\n\nfunction handleTogglePin(preset: SavedFilterReadDto) {\n props.togglePin(preset);\n}\n\nconst emit = defineEmits<{\n close: [];\n delete: [preset: SavedFilterReadDto];\n rename: [preset: SavedFilterReadDto];\n}>();\n\nconst canSaveMore = computed(\n () => props.presets.length < MAX_PRESETS_PER_CONTEXT,\n);\n\nconst editingId = ref<string | null>(null);\nconst editingName = ref('');\nconst renameInputRef = ref<HTMLInputElement | null>(null);\n\nconst showSaveForm = ref(false);\nconst saveName = ref('');\nconst saveInputRef = ref<HTMLInputElement | null>(null);\n\nfunction close() {\n emit('close');\n}\n\nfunction applyAndClose(preset: SavedFilterReadDto) {\n props.applyPreset(preset);\n close();\n}\n\nfunction startRename(preset: SavedFilterReadDto) {\n editingId.value = preset.id;\n editingName.value = preset.name;\n nextTick(() => renameInputRef.value?.focus());\n}\n\nfunction cancelRename() {\n editingId.value = null;\n editingName.value = '';\n}\n\nasync function saveRename(presetId: string) {\n const name = editingName.value.trim();\n if (!name) return;\n const result = await props.renamePreset(presetId, name);\n if (result) {\n emit('rename', result);\n cancelRename();\n }\n}\n\nfunction handleDelete(preset: SavedFilterReadDto) {\n emit('delete', preset);\n}\n\nfunction startSave() {\n if (props.hasFilters && canSaveMore.value) {\n showSaveForm.value = true;\n saveName.value = '';\n nextTick(() => saveInputRef.value?.focus());\n }\n}\n\nfunction cancelSave() {\n showSaveForm.value = false;\n saveName.value = '';\n}\n\nasync function submitSave() {\n const name = saveName.value.trim();\n if (!name) return;\n const result = await props.saveCurrentFilters(name);\n if (result) {\n cancelSave();\n close();\n }\n}\n\nwatch(() => props.isOpen, (open) => {\n if (!open) {\n cancelRename();\n cancelSave();\n }\n});\n</script>\n","<template>\n <div class=\"flex items-center gap-2 flex-wrap\">\n <!-- Active preset chip: shows when a preset is applied, with clear (×) -->\n <div\n v-if=\"props.activePreset\"\n class=\"badge badge-primary badge-lg gap-1 pr-1\"\n >\n <span class=\"truncate max-w-[8rem] sm:max-w-[12rem]\">{{ props.activePreset.name }}</span>\n <button\n type=\"button\"\n class=\"btn btn-ghost btn-xs btn-circle\"\n aria-label=\"Clear preset\"\n @click=\"handleClearPreset\"\n >\n <svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Presets button: opens modal with everything -->\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n :disabled=\"props.presetsLoading\"\n :title=\"props.presets.length ? 'Apply, save, or manage presets' : 'No presets saved'\"\n @click=\"showPresetsModal = true\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z\"\n />\n </svg>\n Presets\n </button>\n\n <!-- Delete confirmation modal -->\n <ConfirmDialog\n v-model=\"showDeleteConfirm\"\n title=\"Delete preset?\"\n confirm-text=\"Delete\"\n cancel-text=\"Cancel\"\n processing-text=\"Deleting...\"\n confirm-button-class=\"btn-error\"\n :is-processing=\"props.deleting\"\n @confirm=\"confirmDelete\"\n @cancel=\"presetToDelete = null\"\n >\n <template #message>\n <p>\n Are you sure you want to delete \"{{ presetToDelete?.name }}\"?\n This cannot be undone.\n </p>\n </template>\n </ConfirmDialog>\n\n <!-- Single Presets modal: apply, save, rename, delete -->\n <PresetsModal\n :is-open=\"showPresetsModal\"\n :presets=\"props.presets\"\n :loading=\"props.presetsLoading\"\n :creating=\"props.creating\"\n :deleting=\"props.deleting\"\n :renaming=\"props.renaming\"\n :has-filters=\"props.hasFilters\"\n :active-preset=\"props.activePreset\"\n :apply-preset=\"props.applyPreset\"\n :save-current-filters=\"props.saveCurrentFilters\"\n :rename-preset=\"props.renamePreset\"\n :is-pinned=\"pinnedPresets.isPinned\"\n :toggle-pin=\"pinnedPresets.togglePin\"\n :can-pin-more=\"pinnedPresets.canPinMore.value\"\n :max-pinned=\"pinnedPresets.maxPinned\"\n @close=\"showPresetsModal = false\"\n @delete=\"handleDelete\"\n @rename=\"handleRename\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport type { SavedFilterReadDto } from '@dragonmastery/dragoncore-shared';\nimport { ref } from 'vue';\nimport ConfirmDialog from '../../components/ConfirmDialog.vue';\nimport PresetsModal from './PresetsModal.vue';\nimport { useInjectedPinnedPresets } from './usePinnedPresets';\n\nconst pinnedPresets = useInjectedPinnedPresets();\n\nconst props = defineProps<{\n presets: SavedFilterReadDto[];\n presetsLoading: boolean;\n creating: boolean;\n deleting: boolean;\n renaming: boolean;\n hasFilters: boolean;\n activePreset: SavedFilterReadDto | null;\n applyPreset: (preset: SavedFilterReadDto) => void;\n clearPreset: () => void;\n saveCurrentFilters: (name: string) => Promise<SavedFilterReadDto | null>;\n renamePreset: (presetId: string, name: string) => Promise<SavedFilterReadDto | null>;\n removePreset: (presetId: string) => Promise<boolean>;\n}>();\n\nconst showPresetsModal = ref(false);\nconst showDeleteConfirm = ref(false);\nconst presetToDelete = ref<{ id: string; name: string } | null>(null);\n\nfunction handleClearPreset() {\n props.clearPreset();\n}\n\nfunction handleDelete(preset: { id: string; name: string }) {\n presetToDelete.value = preset;\n showDeleteConfirm.value = true;\n}\n\nasync function confirmDelete() {\n if (!presetToDelete.value) return;\n pinnedPresets.unpinPreset(presetToDelete.value.id);\n await props.removePreset(presetToDelete.value.id);\n presetToDelete.value = null;\n showDeleteConfirm.value = false;\n}\n\nasync function handleRename(preset: { id: string; name: string }) {\n pinnedPresets.updatePinnedPreset(preset.id, { name: preset.name });\n await pinnedPresets.refetchPinned?.();\n}\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"saved_filter-erjEgsdK.js","names":["DEFAULT_OPERATORS: Record<string, string>","config: FilterConfiguration","operator: string","params: Record<string, string>","filters: FilterQueryParams","base: Record<string, string | string[]>","LAST_USED_PREFIX","getLastUsedKey","savedFilterRoutes: Array<ExtendedRouteRecordRaw>"],"sources":["../src/components/BaseModal.vue","../src/constants/time.ts","../src/slices/saved_filter/usePinnedPresets.ts","../src/utils/filterUrlParser.ts","../src/slices/saved_filter/lastUsedPresetGuard.ts","../src/slices/saved_filter/useSavedFilters.ts","../src/slices/saved_filter/savedFilterRoutes.ts","../src/slices/saved_filter/SaveFilterModal.vue","../src/slices/saved_filter/ManagePresetsModal.vue","../src/slices/saved_filter/PresetsModal.vue","../src/slices/saved_filter/SavedFilterPresets.vue"],"sourcesContent":["<template>\n <dialog ref=\"modal\" class=\"modal\">\n <div class=\"modal-box max-w-md w-full max-h-[90vh] flex flex-col p-0\">\n <!-- Header with title and close button - fixed, not scrollable -->\n <div\n class=\"flex justify-between items-center p-6 pb-4 border-b border-base-300 flex-shrink-0\"\n >\n <h3 class=\"font-bold text-lg\">\n <slot name=\"title\">{{ title }}</slot>\n </h3>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n @click.prevent=\"handleClose\"\n aria-label=\"Close modal\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-6 w-6\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n <!-- Content slot - scrollable area -->\n <div class=\"modal-content flex-1 overflow-y-auto p-6\">\n <slot></slot>\n </div>\n </div>\n <form method=\"dialog\" class=\"modal-backdrop\">\n <button type=\"button\" @click.prevent=\"handleClose\">close</button>\n </form>\n </dialog>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch } from 'vue';\n\ninterface Props {\n isOpen: boolean;\n title?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: '',\n});\n\nconst emit = defineEmits<{\n close: [];\n}>();\n\nconst modal = ref<HTMLDialogElement>();\n\nconst handleClose = () => {\n emit('close');\n};\n\n// Watch for modal open/close\nwatch(\n () => props.isOpen,\n (isOpen) => {\n if (isOpen && modal.value) {\n modal.value.showModal();\n } else if (!isOpen && modal.value) {\n modal.value.close();\n }\n },\n { immediate: true },\n);\n</script>\n","/**\n * Time duration constants in milliseconds.\n * Use for staleTime, cache TTL, etc.\n *\n * @example\n * staleTime: 30 * MS.ONE_SECOND\n * staleTime: 5 * MS.ONE_MINUTE\n * staleTime: 24 * MS.ONE_HOUR\n */\nexport const MS = {\n ONE_SECOND: 1000,\n ONE_MINUTE: 60 * 1000,\n ONE_HOUR: 60 * 60 * 1000,\n} as const;\n","/**\n * Composable for pinning presets to favorites (home page + sidebar).\n * Uses API with 24h cache. Max 5 pinned presets.\n *\n * Call usePinnedPresets() once in the layout (InAppLayout) and provide it.\n * Children use useInjectedPinnedPresets() to avoid duplicate fetches.\n */\n\nimport type { SavedFilterReadDto } from '@dragonmastery/dragoncore-shared';\nimport type { InjectionKey } from 'vue';\nimport { inject } from 'vue';\nimport { MS } from '../../constants/time';\nimport { useMutation } from '../../composables/useMutation';\nimport { useQuery } from '../../composables/useQuery';\nimport { computed } from 'vue';\n\nconst CACHE_KEY = 'pinned-presets';\nconst MAX_PINNED = 5;\n\nexport type PinnedPreset = Pick<\n SavedFilterReadDto,\n 'id' | 'name' | 'context' | 'route_path' | 'filters'\n>;\n\nexport function usePinnedPresets() {\n const { data: pinnedData, refetch } = useQuery(\n (api) => api.savedFilters.listPinnedPresets(),\n { cacheKey: CACHE_KEY, staleTime: 24 * MS.ONE_HOUR },\n );\n\n const { mutate: addPin } = useMutation(\n (api, id: string) => api.savedFilters.addPinnedPreset(id),\n { invalidate: CACHE_KEY },\n );\n\n const { mutate: removePin } = useMutation(\n (api, id: string) => api.savedFilters.removePinnedPreset(id),\n { invalidate: CACHE_KEY },\n );\n\n const pinned = computed(() => pinnedData.value ?? []);\n const pinnedIds = computed(() => new Set(pinned.value.map((p) => p.id)));\n const canPinMore = computed(() => pinned.value.length < MAX_PINNED);\n\n function isPinned(presetId: string): boolean {\n return pinnedIds.value.has(presetId);\n }\n\n async function pinPreset(preset: SavedFilterReadDto): Promise<boolean> {\n if (pinned.value.length >= MAX_PINNED) return false;\n if (pinnedIds.value.has(preset.id)) return true;\n try {\n await addPin(preset.id);\n await refetch();\n return true;\n } catch {\n return false;\n }\n }\n\n async function unpinPreset(presetId: string): Promise<void> {\n try {\n await removePin(presetId);\n await refetch();\n } catch {\n // Error handled by mutation\n }\n }\n\n async function togglePin(preset: SavedFilterReadDto): Promise<boolean> {\n if (isPinned(preset.id)) {\n await unpinPreset(preset.id);\n return false;\n }\n return pinPreset(preset);\n }\n\n function getPresetLink(preset: PinnedPreset): {\n path: string;\n query: Record<string, string | string[]>;\n } {\n return {\n path: preset.route_path,\n query: preset.filters ?? {},\n };\n }\n\n function updatePinnedPreset(\n presetId: string,\n updates: Partial<PinnedPreset>,\n ): void {\n // API is source of truth. Call refetchPinned when a preset is renamed\n // to refresh the pinned list with updated names.\n void presetId;\n void updates;\n }\n\n return {\n pinned,\n pinnedIds,\n canPinMore,\n isPinned,\n pinPreset,\n unpinPreset,\n togglePin,\n getPresetLink,\n updatePinnedPreset,\n refetchPinned: refetch,\n maxPinned: MAX_PINNED,\n };\n}\n\nexport const PINNED_PRESETS_KEY = Symbol('pinnedPresets') as InjectionKey<\n ReturnType<typeof usePinnedPresets>\n>;\n\n/** Use pinned presets from the layout provider. Call this in child components (Sidebar, AppHome, etc.). */\nexport function useInjectedPinnedPresets() {\n const injected = inject(PINNED_PRESETS_KEY);\n if (!injected) {\n throw new Error(\n 'useInjectedPinnedPresets must be used within a component tree that provides pinned presets (e.g. InAppLayout).',\n );\n }\n return injected;\n}\n","/**\n * Filter URL Parser - Query string serialization/deserialization\n * Uses compact DrizzleORM-style format: flt[field]=value or flt[field][op]=value\n */\n\nimport { OPERATORS } from '@dragonmastery/dragoncore-shared';\n\n/**\n * Default operators for each field type when not specified in URL\n */\nconst DEFAULT_OPERATORS: Record<string, string> = {\n string: OPERATORS.CONTAINS,\n number: OPERATORS.EQUALS,\n date: OPERATORS.EQUALS,\n boolean: OPERATORS.EQUALS,\n enum: OPERATORS.EQUALS,\n};\n\n/**\n * Numeric types that should be converted to numbers\n */\nconst NUMERIC_TYPES = ['number', 'money', 'percentage'];\n\n/**\n * Check if operator requires array values\n */\nfunction requiresArrayValues(operator: string): boolean {\n return (\n operator === OPERATORS.IS_ONE_OF ||\n operator === OPERATORS.IS_NOT_ONE_OF ||\n operator === OPERATORS.BETWEEN\n );\n}\n\n/**\n * Check if operator is a null check (requires no value)\n */\nfunction isNullCheckOperator(operator: string): boolean {\n return operator === OPERATORS.IS_EMPTY || operator === OPERATORS.IS_NOT_EMPTY;\n}\n\n/**\n * Field registry metadata for type inference\n */\nexport interface FieldRegistryMetadata {\n [fieldName: string]: {\n type: 'string' | 'number' | 'date' | 'boolean' | 'enum' | 'money' | 'percentage';\n filterable?: boolean;\n };\n}\n\n/**\n * Filter value object structure\n */\nexport interface FilterValueObject {\n operator: string;\n value?: any;\n values?: any[];\n caseSensitive?: boolean;\n}\n\n/**\n * Filter configuration - flat object mapping field names to filter objects\n */\nexport type FilterConfiguration = Record<string, FilterValueObject>;\n\n/**\n * Deserialize filter configuration from URL query parameters object\n * Parses compact DrizzleORM-style format: flt[field]=value or flt[field][op]=value\n *\n * @param params - URL query parameters object (from Vue Router route.query)\n * @param registry - Field registry metadata for type inference\n * @returns Filter configuration object\n *\n * @example\n * // URL: ?flt[department][eq]=Engineering&flt[id]=4&flt[role][in]=[\"admin\",\"user\"]\n * const filters = deserializeFiltersFromQueryParams(route.query, fieldRegistry);\n * // Result: {\n * // department: { operator: 'eq', value: 'Engineering' },\n * // id: { operator: 'eq', value: 4 },\n * // role: { operator: 'in', values: ['admin', 'user'] }\n * // }\n */\nexport function deserializeFiltersFromQueryParams(\n params: Record<string, string | string[]>,\n registry: FieldRegistryMetadata,\n): FilterConfiguration {\n const config: FilterConfiguration = {};\n const fieldMap = new Map<string, Partial<FilterValueObject>>();\n\n // Parse query params\n for (const [key, value] of Object.entries(params)) {\n // Match flt[field] or flt[field][op]\n const matchDefault = key.match(/^flt\\[([^\\]]+)\\]$/);\n const matchWithOp = key.match(/^flt\\[([^\\]]+)\\]\\[([^\\]]+)\\]$/);\n\n if (!matchDefault && !matchWithOp) continue;\n\n const fieldName = matchDefault ? matchDefault[1] : matchWithOp ? matchWithOp[1] : null;\n const operatorShorthand = matchWithOp ? matchWithOp[2] : null;\n const stringValue = Array.isArray(value) ? value[0] : value;\n\n if (!fieldName) continue;\n\n const fieldMeta = registry[fieldName];\n if (!fieldMeta) continue; // Skip unknown fields\n\n const fieldType = fieldMeta.type;\n const defaultOp = DEFAULT_OPERATORS[fieldType] || OPERATORS.EQUALS;\n\n // Determine operator\n let operator: string;\n if (operatorShorthand) {\n operator = operatorShorthand;\n } else {\n operator = defaultOp;\n }\n\n if (!fieldMap.has(fieldName)) {\n fieldMap.set(fieldName, { operator });\n }\n\n const filter = fieldMap.get(fieldName)!;\n filter.operator = operator;\n\n // Parse value based on operator type\n if (isNullCheckOperator(operator)) {\n // Null checks don't have values\n // Value is already set, nothing more to do\n } else if (requiresArrayValues(operator)) {\n // Array operators: parse JSON array (handles commas/quotes in values)\n if (!stringValue || stringValue.trim() === '') {\n fieldMap.delete(fieldName);\n continue;\n }\n\n try {\n const parsed = JSON.parse(stringValue);\n if (Array.isArray(parsed) && parsed.length > 0) {\n // Convert to appropriate types based on field type\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.values = parsed.map((v) => Number(v));\n } else {\n filter.values = parsed;\n }\n } else {\n fieldMap.delete(fieldName);\n continue;\n }\n } catch (e) {\n // Invalid JSON, try legacy comma-separated format for backward compatibility\n const values = stringValue\n .split(',')\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n if (values.length === 0) {\n fieldMap.delete(fieldName);\n continue;\n }\n\n if (operator === OPERATORS.BETWEEN && values.length === 2) {\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.values = [Number(values[0]!), Number(values[1]!)];\n } else if (fieldType === 'date') {\n filter.values = [values[0]!, values[1]!];\n } else {\n filter.values = values;\n }\n } else if (operator === OPERATORS.BETWEEN) {\n fieldMap.delete(fieldName);\n continue;\n } else {\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.values = values.map((v) => Number(v));\n } else {\n filter.values = values;\n }\n }\n }\n } else {\n // Single value operators\n // Special case: if operator is in/notIn but we got a single value, convert to array\n if (\n (operator === OPERATORS.IS_ONE_OF || operator === OPERATORS.IS_NOT_ONE_OF) &&\n stringValue\n ) {\n // Convert single value to array for array operators\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.values = [Number(stringValue)];\n } else {\n filter.values = [stringValue];\n }\n } else if (stringValue) {\n // Regular single value operator\n if (NUMERIC_TYPES.includes(fieldType)) {\n filter.value = Number(stringValue);\n } else if (fieldType === 'boolean') {\n filter.value = stringValue === 'true' || stringValue === '1';\n } else {\n filter.value = stringValue;\n }\n\n // String fields: always case-insensitive (contains is default)\n if (fieldType === 'string' && operator === OPERATORS.CONTAINS) {\n filter.caseSensitive = false;\n }\n }\n }\n }\n\n // Convert to FilterConfiguration\n for (const [fieldName, filter] of fieldMap.entries()) {\n if (filter.operator) {\n config[fieldName] = filter as FilterValueObject;\n }\n }\n\n return config;\n}\n\n/**\n * Serialize filter configuration to URL query parameters object\n * Uses compact DrizzleORM-style format: flt[field][op]=value\n *\n * @param config - Filter configuration object\n * @param registry - Field registry metadata for type inference\n * @returns URL query parameters object (can be used with Vue Router)\n *\n * @example\n * const filters = {\n * department: { operator: 'eq', value: 'Engineering' },\n * role: { operator: 'in', values: ['admin', 'user'] }\n * };\n * const params = serializeFiltersToQueryParams(filters, fieldRegistry);\n * // Result: {\n * // 'flt[department][eq]': 'Engineering',\n * // 'flt[role][in]': '[\"admin\",\"user\"]'\n * // }\n */\nexport function serializeFiltersToQueryParams(\n config: FilterConfiguration,\n registry: FieldRegistryMetadata,\n): Record<string, string> {\n const params: Record<string, string> = {};\n\n for (const [fieldName, filter] of Object.entries(config)) {\n const fieldMeta = registry[fieldName];\n if (!fieldMeta) continue;\n\n const operator = filter.operator;\n\n if (isNullCheckOperator(operator)) {\n // Null checks don't need values, just the operator\n params[`flt[${fieldName}][${operator}]`] = '';\n } else if (requiresArrayValues(operator)) {\n // Array operators: use JSON encoding (handles commas/quotes)\n if (filter.values && Array.isArray(filter.values)) {\n params[`flt[${fieldName}][${operator}]`] = JSON.stringify(filter.values);\n }\n } else {\n // Single value: include operator explicitly\n params[`flt[${fieldName}][${operator}]`] = String(filter.value);\n }\n }\n\n return params;\n}\n\n/** Raw filter params format used by useSavedFilters (flt[key]=value) */\nexport type FilterQueryParams = Record<string, string | string[]>;\n\n/**\n * Extract filter query params from route query.\n * Handles Vue Router's LocationQuery type which can have null values.\n * Only extracts flt[...] params (actual filters).\n *\n * @example\n * // URL: ?flt[type][in]=[\"sales\",\"escalation\"]&flt[phase][ne]=Post+Sale\n * // Returns: { 'flt[type][in]': '[\"sales\",\"escalation\"]', 'flt[phase][ne]': 'Post+Sale' }\n */\nexport function extractFiltersFromQuery(query: Record<string, unknown>): FilterQueryParams {\n const filters: FilterQueryParams = {};\n\n Object.keys(query).forEach((key) => {\n if (key.startsWith('flt[')) {\n const value = query[key];\n if (value !== null && value !== undefined) {\n if (Array.isArray(value)) {\n const filtered = value.filter((v): v is string => v !== null && v !== undefined);\n if (filtered.length > 0) {\n filters[key] = filtered;\n }\n } else {\n filters[key] = String(value);\n }\n }\n }\n });\n\n return filters;\n}\n\n/**\n * Build a new query object with filter params replaced (not merged).\n * Strips all flt[...] params from the current query, then adds the new filters.\n * Preserves non-filter params (sort, pagination, etc).\n */\nexport function buildQueryWithFilters(\n currentQuery: Record<string, unknown>,\n filters: FilterQueryParams,\n): Record<string, string | string[]> {\n const base: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(currentQuery)) {\n if (!key.startsWith('flt[') && value !== null && value !== undefined) {\n base[key] = Array.isArray(value)\n ? (value.filter((v): v is string => v != null) as string[])\n : String(value);\n }\n }\n return { ...base, ...filters };\n}\n","/**\n * Router guard helper: redirect to last-used preset filters when navigating with no URL filters.\n * Uses localStorage (no API call) - filters are stored when user applies a preset.\n * Prevents double fetch by applying preset before the list component mounts.\n */\n\nimport { buildQueryWithFilters, extractFiltersFromQuery } from '../../utils/filterUrlParser';\n\nconst LAST_USED_PREFIX = 'lastUsedPreset:';\nconst LAST_USED_FILTERS_PREFIX = 'lastUsedPresetFilters:';\n\nfunction getLastUsedKey(context: string): string {\n return `${LAST_USED_PREFIX}${context}`;\n}\n\nfunction getLastUsedFiltersKey(context: string): string {\n return `${LAST_USED_FILTERS_PREFIX}${context}`;\n}\n\nexport function getLastUsedPresetFilters(\n context: string,\n): Record<string, string | string[]> | null {\n try {\n const stored = localStorage.getItem(getLastUsedFiltersKey(context));\n if (!stored) return null;\n const parsed = JSON.parse(stored) as Record<string, string | string[]>;\n return typeof parsed === 'object' && parsed !== null ? parsed : null;\n } catch {\n return null;\n }\n}\n\nexport function setLastUsedPresetFilters(\n context: string,\n filters: Record<string, string | string[]> | null,\n): void {\n try {\n if (filters && Object.keys(filters).length > 0) {\n localStorage.setItem(getLastUsedFiltersKey(context), JSON.stringify(filters));\n } else {\n localStorage.removeItem(getLastUsedFiltersKey(context));\n }\n } catch {\n // Ignore\n }\n}\n\n/**\n * If navigating with no filters and we have a last-used preset, returns the query to redirect to.\n * Otherwise returns null (no redirect needed).\n */\nexport function getLastUsedPresetRedirect(\n context: string,\n currentQuery: Record<string, unknown>,\n): Record<string, string | string[]> | null {\n const filters = extractFiltersFromQuery(currentQuery);\n if (Object.keys(filters).length > 0) return null;\n\n const lastUsedId = localStorage.getItem(getLastUsedKey(context));\n if (!lastUsedId) return null;\n\n const lastUsedFilters = getLastUsedPresetFilters(context);\n if (!lastUsedFilters || Object.keys(lastUsedFilters).length === 0) return null;\n\n return buildQueryWithFilters(currentQuery, lastUsedFilters);\n}\n\n/**\n * Creates a Vue Router beforeEnter guard that redirects to last-used preset when navigating with no filters.\n * Use on list routes that have SavedFilterPresets.\n */\nexport function createLastUsedPresetGuard(\n context: string,\n routeName: string,\n) {\n return (to: { name?: unknown; query: Record<string, unknown> }) => {\n if (to.name !== routeName) return undefined;\n const redirectQuery = getLastUsedPresetRedirect(context, to.query);\n if (!redirectQuery) return undefined;\n return { name: routeName, query: redirectQuery };\n };\n}\n","/**\n * Composable for saved filter presets - save, apply, and manage named filter configurations.\n * Works with any data table that provides getFiltersFromTable / applyFiltersToTable adapters.\n */\n\nimport type { DragoncoreApi } from '@dragonmastery/dragoncore-shared';\nimport type { SavedFilterReadDto } from '@dragonmastery/dragoncore-shared';\nimport { MS } from '../../constants/time';\nimport { useMutation } from '../../composables/useMutation';\nimport { useQuery } from '../../composables/useQuery';\nimport { computed, onMounted, ref, watch } from 'vue';\nimport { setLastUsedPresetFilters } from './lastUsedPresetGuard';\n\nconst LAST_USED_PREFIX = 'lastUsedPreset:';\n\nexport type SavedFiltersConfig = {\n /** Unique context per table type (e.g. \"tracker\", \"followup\") */\n context: string;\n /** Route path for the table (e.g. \"/trackers\", \"/followups\") */\n routePath: string;\n /** Read current filters from the table/URL */\n getFiltersFromTable: () => Record<string, string | string[]>;\n /** Apply filters to the table/URL */\n applyFiltersToTable: (filters: Record<string, string | string[]>) => void;\n /** Called when user explicitly clears all filters - clear last-used preset */\n onClearFilters?: () => void;\n};\n\nfunction getLastUsedKey(context: string): string {\n return `${LAST_USED_PREFIX}${context}`;\n}\n\nfunction getLastUsedPresetId(context: string): string | null {\n try {\n return localStorage.getItem(getLastUsedKey(context));\n } catch {\n return null;\n }\n}\n\nfunction setLastUsedPresetId(context: string, presetId: string | null): void {\n try {\n if (presetId) {\n localStorage.setItem(getLastUsedKey(context), presetId);\n } else {\n localStorage.removeItem(getLastUsedKey(context));\n }\n } catch {\n // Ignore\n }\n}\n\nexport function useSavedFilters<TApi extends DragoncoreApi = DragoncoreApi>(\n config: SavedFiltersConfig,\n) {\n const {\n context,\n routePath,\n getFiltersFromTable,\n applyFiltersToTable,\n onClearFilters,\n } = config;\n\n const cacheKey = `saved-filters:${context}`;\n const hasAppliedLastUsed = ref(false);\n const lastUsedPresetIdRef = ref<string | null>(getLastUsedPresetId(context));\n\n const { data: presets, loading: presetsLoading, refetch: refetchPresets } = useQuery<\n TApi,\n SavedFilterReadDto[]\n >((api) => api.savedFilters.listSavedFilters(context), {\n cacheKey,\n staleTime: 24 * MS.ONE_HOUR,\n });\n\n const { mutate: createPreset, loading: creating } = useMutation<\n TApi,\n { name: string; context: string; route_path: string; filters: Record<string, string | string[]>; sort_by?: string; sort_direction?: 'asc' | 'desc' },\n SavedFilterReadDto\n >(\n (api, input) => api.savedFilters.createSavedFilter(input),\n { invalidate: /^saved-filters:/ },\n );\n\n const { mutate: updatePreset, loading: updating } = useMutation<\n TApi,\n { id: string; name?: string; route_path?: string; filters?: Record<string, string | string[]>; sort_by?: string; sort_direction?: 'asc' | 'desc' },\n SavedFilterReadDto | null\n >(\n (api, input) => api.savedFilters.updateSavedFilter(input),\n { invalidate: /^saved-filters:|^pinned-presets/ },\n );\n\n const { mutate: deletePreset, loading: deleting } = useMutation<TApi, string, boolean>(\n (api, id) => api.savedFilters.deleteSavedFilter(id),\n { invalidate: /^saved-filters:/ },\n );\n\n function applyPreset(preset: SavedFilterReadDto): void {\n applyFiltersToTable(preset.filters);\n setLastUsedPresetId(context, preset.id);\n setLastUsedPresetFilters(context, preset.filters);\n lastUsedPresetIdRef.value = preset.id;\n }\n\n function clearPreset(): void {\n applyFiltersToTable({});\n setLastUsedPresetId(context, null);\n setLastUsedPresetFilters(context, null);\n lastUsedPresetIdRef.value = null;\n }\n\n function clearLastUsed(): void {\n setLastUsedPresetId(context, null);\n setLastUsedPresetFilters(context, null);\n lastUsedPresetIdRef.value = null;\n onClearFilters?.();\n }\n\n async function saveCurrentFilters(name: string): Promise<SavedFilterReadDto | null> {\n const filters = getFiltersFromTable();\n if (Object.keys(filters).length === 0) {\n return null;\n }\n try {\n const result = await createPreset({\n name,\n context,\n route_path: routePath,\n filters,\n });\n if (result) {\n await refetchPresets();\n setLastUsedPresetId(context, result.id);\n setLastUsedPresetFilters(context, result.filters);\n lastUsedPresetIdRef.value = result.id;\n }\n return result ?? null;\n } catch {\n return null;\n }\n }\n\n async function updatePresetFilters(presetId: string, name?: string): Promise<SavedFilterReadDto | null> {\n const filters = getFiltersFromTable();\n try {\n const result = await updatePreset({\n id: presetId,\n ...(name !== undefined && { name }),\n filters,\n });\n return result ?? null;\n } catch {\n return null;\n }\n }\n\n async function renamePreset(presetId: string, name: string): Promise<SavedFilterReadDto | null> {\n try {\n const result = await updatePreset({ id: presetId, name });\n if (result) {\n await refetchPresets();\n }\n return result ?? null;\n } catch {\n return null;\n }\n }\n\n async function removePreset(presetId: string): Promise<boolean> {\n try {\n await deletePreset(presetId);\n await refetchPresets();\n return true;\n } catch {\n return false;\n }\n }\n\n // On mount: if no filters in URL, apply last-used preset when presets load.\n // Note: For routes with lastUsedPresetGuard, the guard redirects before mount so this is often a no-op.\n onMounted(() => {\n const filters = getFiltersFromTable();\n if (Object.keys(filters).length > 0) {\n hasAppliedLastUsed.value = true;\n return;\n }\n const lastUsedId = getLastUsedPresetId(context);\n if (!lastUsedId) {\n hasAppliedLastUsed.value = true;\n return;\n }\n const unwatch = watch(\n () => presets.value,\n (list) => {\n if (!list || hasAppliedLastUsed.value) return;\n const preset = list.find((p) => p.id === lastUsedId);\n if (preset) {\n applyPreset(preset);\n }\n hasAppliedLastUsed.value = true;\n unwatch();\n },\n { immediate: true },\n );\n });\n\n const activePreset = computed(() => {\n const id = lastUsedPresetIdRef.value;\n if (!id) return null;\n return (presets.value ?? []).find((p) => p.id === id) ?? null;\n });\n\n return {\n presets: computed(() => presets.value ?? []),\n presetsLoading,\n creating,\n updating,\n deleting,\n applyPreset,\n clearPreset,\n clearLastUsed,\n activePreset,\n saveCurrentFilters,\n updatePresetFilters,\n renamePreset,\n removePreset,\n };\n}\n","import { userAuthenticated } from '../../middleware/userAuthorized';\nimport type { ExtendedRouteRecordRaw } from '../../types/ExtendedRoute';\n\nexport const savedFilterRoutes: Array<ExtendedRouteRecordRaw> = [\n {\n path: '/saved-filters',\n name: 'SavedFilters',\n component: () => import('./SavedFiltersPage.vue'),\n beforeEnter: [userAuthenticated],\n meta: {\n title: 'Saved Filters',\n description: 'Manage your saved filter presets and favorites',\n side_bar: {\n section: 'Saved Filters',\n visible_to: ['consumer', 'lead', 'staff', 'super_admin'],\n },\n },\n },\n];\n","<template>\n <BaseModal :is-open=\"isOpen\" title=\"Save Filter Preset\" @close=\"close\">\n <form @submit.prevent=\"handleSubmit\" class=\"space-y-4\">\n <div class=\"form-control\">\n <label class=\"label\">\n <span class=\"label-text\">Preset name</span>\n </label>\n <input\n v-model=\"name\"\n type=\"text\"\n placeholder=\"e.g. My Sales Escalations\"\n class=\"input input-bordered w-full\"\n maxlength=\"100\"\n />\n <label v-if=\"error\" class=\"label\">\n <span class=\"label-text-alt text-error\">{{ error }}</span>\n </label>\n </div>\n <div class=\"flex justify-end gap-2\">\n <button type=\"button\" class=\"btn btn-ghost\" @click=\"close\">\n Cancel\n </button>\n <button type=\"submit\" class=\"btn btn-primary\" :disabled=\"saving || !name.trim()\">\n {{ saving ? 'Saving...' : 'Save' }}\n </button>\n </div>\n </form>\n </BaseModal>\n</template>\n\n<script setup lang=\"ts\">\nimport BaseModal from '../../components/BaseModal.vue';\nimport { ref, watch } from 'vue';\n\nconst props = defineProps<{\n isOpen: boolean;\n saving?: boolean;\n}>();\n\nconst emit = defineEmits<{\n close: [];\n save: [name: string];\n}>();\n\nconst name = ref('');\nconst error = ref('');\n\nfunction close() {\n emit('close');\n}\n\nfunction handleSubmit() {\n const trimmed = name.value.trim();\n if (!trimmed) {\n error.value = 'Name is required';\n return;\n }\n if (trimmed.length > 100) {\n error.value = 'Name must be 100 characters or less';\n return;\n }\n error.value = '';\n emit('save', trimmed);\n}\n\nwatch(() => props.isOpen, (open) => {\n if (open) {\n name.value = '';\n error.value = '';\n }\n});\n</script>\n","<template>\n <BaseModal :is-open=\"isOpen\" title=\"Manage Filter Presets\" @close=\"close\">\n <div class=\"space-y-4\">\n <p v-if=\"presets.length === 0 && !loading\" class=\"text-base-content/70 text-sm\">\n No saved presets yet. Set filters, then use \"Save current filters\" in the Presets menu.\n </p>\n <div v-else-if=\"loading\" class=\"flex justify-center py-4\">\n <span class=\"loading loading-spinner loading-md\" />\n </div>\n <ul v-else class=\"space-y-2\">\n <li\n v-for=\"preset in presets\"\n :key=\"preset.id\"\n class=\"flex items-center gap-2 p-2 rounded-lg bg-base-200\"\n >\n <template v-if=\"editingId === preset.id\">\n <input\n ref=\"renameInputRef\"\n v-model=\"editingName\"\n type=\"text\"\n class=\"input input-sm input-bordered flex-1 min-w-0\"\n maxlength=\"100\"\n placeholder=\"Preset name\"\n @keydown.enter=\"saveRename(preset.id)\"\n @keydown.escape=\"cancelRename\"\n />\n <button\n type=\"button\"\n class=\"btn btn-sm btn-primary shrink-0\"\n :disabled=\"renaming || !editingName.trim()\"\n @click=\"saveRename(preset.id)\"\n >\n {{ renaming ? '...' : 'Save' }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost shrink-0\"\n :disabled=\"renaming\"\n @click=\"cancelRename\"\n >\n Cancel\n </button>\n </template>\n <template v-else>\n <span class=\"font-medium truncate flex-1 min-w-0\">\n {{ preset.name }}\n <span class=\"text-base-content/60 text-xs font-normal ml-1\">\n ({{ Object.keys(preset.filters || {}).length }})\n </span>\n </span>\n <div class=\"flex gap-1 shrink-0\">\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n title=\"Rename\"\n @click=\"startRename(preset)\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost text-error\"\n title=\"Delete\"\n :disabled=\"deleting\"\n @click=\"handleDelete(preset)\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" 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\" />\n </svg>\n </button>\n </div>\n </template>\n </li>\n </ul>\n <div class=\"flex justify-end pt-2\">\n <button type=\"button\" class=\"btn btn-ghost\" @click=\"close\">\n Close\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<script setup lang=\"ts\">\nimport BaseModal from '../../components/BaseModal.vue';\nimport type { SavedFilterReadDto } from '@dragonmastery/dragoncore-shared';\nimport { nextTick, ref, watch } from 'vue';\n\nconst props = defineProps<{\n isOpen: boolean;\n presets: SavedFilterReadDto[];\n loading?: boolean;\n deleting?: boolean;\n renaming?: boolean;\n renamePreset: (presetId: string, name: string) => Promise<SavedFilterReadDto | null>;\n}>();\n\nconst emit = defineEmits<{\n close: [];\n delete: [preset: SavedFilterReadDto];\n}>();\n\nconst editingId = ref<string | null>(null);\nconst editingName = ref('');\nconst renameInputRef = ref<HTMLInputElement | null>(null);\n\nfunction close() {\n emit('close');\n}\n\nfunction startRename(preset: SavedFilterReadDto) {\n editingId.value = preset.id;\n editingName.value = preset.name;\n nextTick(() => renameInputRef.value?.focus());\n}\n\nfunction cancelRename() {\n editingId.value = null;\n editingName.value = '';\n}\n\nasync function saveRename(presetId: string) {\n const name = editingName.value.trim();\n if (!name) return;\n const result = await props.renamePreset(presetId, name);\n if (result) {\n cancelRename();\n }\n}\n\nfunction handleDelete(preset: SavedFilterReadDto) {\n emit('delete', preset);\n}\n\nwatch(() => props.isOpen, (open) => {\n if (!open) {\n cancelRename();\n }\n});\n</script>\n","<template>\n <BaseModal :is-open=\"isOpen\" title=\"Filter Presets\" @close=\"close\">\n <div class=\"space-y-4\">\n <!-- Preset list: apply (click name), rename, delete -->\n <div v-if=\"presets.length > 0\">\n <p class=\"text-xs font-medium text-base-content/60 uppercase tracking-wide mb-2\">\n Apply preset\n </p>\n <ul class=\"space-y-2\">\n <li\n v-for=\"preset in presets\"\n :key=\"preset.id\"\n class=\"flex items-center gap-2 p-2 rounded-lg bg-base-200\"\n >\n <template v-if=\"editingId === preset.id\">\n <input\n ref=\"renameInputRef\"\n v-model=\"editingName\"\n type=\"text\"\n class=\"input input-sm input-bordered flex-1 min-w-0\"\n maxlength=\"100\"\n placeholder=\"Preset name\"\n @keydown.enter=\"saveRename(preset.id)\"\n @keydown.escape=\"cancelRename\"\n />\n <button\n type=\"button\"\n class=\"btn btn-sm btn-primary shrink-0\"\n :disabled=\"renaming || !editingName.trim()\"\n @click=\"saveRename(preset.id)\"\n >\n {{ renaming ? '...' : 'Save' }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost shrink-0\"\n :disabled=\"renaming\"\n @click=\"cancelRename\"\n >\n Cancel\n </button>\n </template>\n <template v-else>\n <button\n type=\"button\"\n class=\"flex-1 min-w-0 text-left font-medium truncate rounded px-2 py-1 -mx-2 -my-1 hover:bg-base-300\"\n :class=\"{ 'bg-primary/10': preset.id === activePreset?.id }\"\n @click=\"applyAndClose(preset)\"\n >\n {{ preset.name }}\n <span class=\"text-base-content/60 text-xs font-normal ml-1\">\n ({{ Object.keys(preset.filters || {}).length }})\n </span>\n </button>\n <div class=\"flex gap-1 shrink-0\">\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n :title=\"isPinned(preset.id) ? 'Unpin from favorites' : (canPinMore ? 'Pin to favorites (home + sidebar)' : `Maximum ${maxPinned} pinned`)\"\n :disabled=\"!canPinMore && !isPinned(preset.id)\"\n @click.stop=\"handleTogglePin(preset)\"\n >\n <svg\n class=\"w-4 h-4\"\n :class=\"{ 'text-primary': isPinned(preset.id) }\"\n :fill=\"isPinned(preset.id) ? 'currentColor' : 'none'\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n title=\"Rename\"\n @click.stop=\"startRename(preset)\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost text-error\"\n title=\"Delete\"\n :disabled=\"deleting\"\n @click.stop=\"handleDelete(preset)\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" 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\" />\n </svg>\n </button>\n </div>\n </template>\n </li>\n </ul>\n </div>\n\n <!-- Empty state -->\n <p\n v-else-if=\"!loading\"\n class=\"text-base-content/70 text-sm\"\n >\n No presets yet. Set filters, then save below.\n </p>\n\n <div v-if=\"loading\" class=\"flex justify-center py-2\">\n <span class=\"loading loading-spinner loading-md\" />\n </div>\n\n <div class=\"divider my-2\" />\n\n <!-- Save current filters: inline form when saving -->\n <div v-if=\"showSaveForm\" class=\"space-y-2\">\n <label class=\"label py-0\">\n <span class=\"label-text\">Preset name</span>\n </label>\n <div class=\"flex gap-2\">\n <input\n ref=\"saveInputRef\"\n v-model=\"saveName\"\n type=\"text\"\n placeholder=\"e.g. My Sales Escalations\"\n class=\"input input-sm input-bordered flex-1\"\n maxlength=\"100\"\n @keydown.enter=\"submitSave\"\n @keydown.escape=\"cancelSave\"\n />\n <button\n type=\"button\"\n class=\"btn btn-sm btn-primary\"\n :disabled=\"creating || !saveName.trim()\"\n @click=\"submitSave\"\n >\n {{ creating ? 'Saving...' : 'Save' }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n :disabled=\"creating\"\n @click=\"cancelSave\"\n >\n Cancel\n </button>\n </div>\n </div>\n <button\n v-else\n type=\"button\"\n class=\"btn btn-ghost btn-sm btn-block justify-start gap-2\"\n :disabled=\"!hasFilters || creating || !canSaveMore\"\n :title=\"!canSaveMore ? `Maximum ${MAX_PRESETS_PER_CONTEXT} presets per page. Delete one to save a new preset.` : undefined\"\n @click=\"startSave\"\n >\n <svg class=\"w-4 h-4 shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8v8H4V4h8m0 0l4 4-4 4\" />\n </svg>\n Save current filters\n <span v-if=\"!canSaveMore\" class=\"text-base-content/60 text-xs\">(max {{ MAX_PRESETS_PER_CONTEXT }})</span>\n </button>\n\n <div class=\"flex justify-end pt-2\">\n <button type=\"button\" class=\"btn btn-ghost\" @click=\"close\">\n Close\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<script setup lang=\"ts\">\nimport BaseModal from '../../components/BaseModal.vue';\nimport {\n MAX_PRESETS_PER_CONTEXT,\n type SavedFilterReadDto,\n} from '@dragonmastery/dragoncore-shared';\nimport { computed, nextTick, ref, watch } from 'vue';\n\nconst props = defineProps<{\n isOpen: boolean;\n presets: SavedFilterReadDto[];\n loading?: boolean;\n creating?: boolean;\n deleting?: boolean;\n renaming?: boolean;\n hasFilters: boolean;\n activePreset: SavedFilterReadDto | null;\n applyPreset: (preset: SavedFilterReadDto) => void;\n saveCurrentFilters: (name: string) => Promise<SavedFilterReadDto | null>;\n renamePreset: (presetId: string, name: string) => Promise<SavedFilterReadDto | null>;\n isPinned: (presetId: string) => boolean;\n togglePin: (preset: SavedFilterReadDto) => boolean | Promise<boolean>;\n canPinMore: boolean;\n maxPinned?: number;\n}>();\n\nconst maxPinned = computed(() => props.maxPinned ?? 5);\n\nfunction handleTogglePin(preset: SavedFilterReadDto) {\n props.togglePin(preset);\n}\n\nconst emit = defineEmits<{\n close: [];\n delete: [preset: SavedFilterReadDto];\n rename: [preset: SavedFilterReadDto];\n}>();\n\nconst canSaveMore = computed(\n () => props.presets.length < MAX_PRESETS_PER_CONTEXT,\n);\n\nconst editingId = ref<string | null>(null);\nconst editingName = ref('');\nconst renameInputRef = ref<HTMLInputElement | null>(null);\n\nconst showSaveForm = ref(false);\nconst saveName = ref('');\nconst saveInputRef = ref<HTMLInputElement | null>(null);\n\nfunction close() {\n emit('close');\n}\n\nfunction applyAndClose(preset: SavedFilterReadDto) {\n props.applyPreset(preset);\n close();\n}\n\nfunction startRename(preset: SavedFilterReadDto) {\n editingId.value = preset.id;\n editingName.value = preset.name;\n nextTick(() => renameInputRef.value?.focus());\n}\n\nfunction cancelRename() {\n editingId.value = null;\n editingName.value = '';\n}\n\nasync function saveRename(presetId: string) {\n const name = editingName.value.trim();\n if (!name) return;\n const result = await props.renamePreset(presetId, name);\n if (result) {\n emit('rename', result);\n cancelRename();\n }\n}\n\nfunction handleDelete(preset: SavedFilterReadDto) {\n emit('delete', preset);\n}\n\nfunction startSave() {\n if (props.hasFilters && canSaveMore.value) {\n showSaveForm.value = true;\n saveName.value = '';\n nextTick(() => saveInputRef.value?.focus());\n }\n}\n\nfunction cancelSave() {\n showSaveForm.value = false;\n saveName.value = '';\n}\n\nasync function submitSave() {\n const name = saveName.value.trim();\n if (!name) return;\n const result = await props.saveCurrentFilters(name);\n if (result) {\n cancelSave();\n close();\n }\n}\n\nwatch(() => props.isOpen, (open) => {\n if (!open) {\n cancelRename();\n cancelSave();\n }\n});\n</script>\n","<template>\n <div class=\"flex items-center gap-2 flex-wrap\">\n <!-- Active preset chip: shows when a preset is applied, with clear (×) -->\n <div\n v-if=\"props.activePreset\"\n class=\"badge badge-primary badge-lg gap-1 pr-1\"\n >\n <span class=\"truncate max-w-[8rem] sm:max-w-[12rem]\">{{ props.activePreset.name }}</span>\n <button\n type=\"button\"\n class=\"btn btn-ghost btn-xs btn-circle\"\n aria-label=\"Clear preset\"\n @click=\"handleClearPreset\"\n >\n <svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Presets button: opens modal with everything -->\n <button\n type=\"button\"\n class=\"btn btn-sm btn-ghost\"\n :disabled=\"props.presetsLoading\"\n :title=\"props.presets.length ? 'Apply, save, or manage presets' : 'No presets saved'\"\n @click=\"showPresetsModal = true\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z\"\n />\n </svg>\n Presets\n </button>\n\n <!-- Delete confirmation modal -->\n <ConfirmDialog\n v-model=\"showDeleteConfirm\"\n title=\"Delete preset?\"\n confirm-text=\"Delete\"\n cancel-text=\"Cancel\"\n processing-text=\"Deleting...\"\n confirm-button-class=\"btn-error\"\n :is-processing=\"props.deleting\"\n @confirm=\"confirmDelete\"\n @cancel=\"presetToDelete = null\"\n >\n <template #message>\n <p>\n Are you sure you want to delete \"{{ presetToDelete?.name }}\"?\n This cannot be undone.\n </p>\n </template>\n </ConfirmDialog>\n\n <!-- Single Presets modal: apply, save, rename, delete -->\n <PresetsModal\n :is-open=\"showPresetsModal\"\n :presets=\"props.presets\"\n :loading=\"props.presetsLoading\"\n :creating=\"props.creating\"\n :deleting=\"props.deleting\"\n :renaming=\"props.renaming\"\n :has-filters=\"props.hasFilters\"\n :active-preset=\"props.activePreset\"\n :apply-preset=\"props.applyPreset\"\n :save-current-filters=\"props.saveCurrentFilters\"\n :rename-preset=\"props.renamePreset\"\n :is-pinned=\"pinnedPresets.isPinned\"\n :toggle-pin=\"pinnedPresets.togglePin\"\n :can-pin-more=\"pinnedPresets.canPinMore.value\"\n :max-pinned=\"pinnedPresets.maxPinned\"\n @close=\"showPresetsModal = false\"\n @delete=\"handleDelete\"\n @rename=\"handleRename\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport type { SavedFilterReadDto } from '@dragonmastery/dragoncore-shared';\nimport { ref } from 'vue';\nimport ConfirmDialog from '../../components/ConfirmDialog.vue';\nimport PresetsModal from './PresetsModal.vue';\nimport { useInjectedPinnedPresets } from './usePinnedPresets';\n\nconst pinnedPresets = useInjectedPinnedPresets();\n\nconst props = defineProps<{\n presets: SavedFilterReadDto[];\n presetsLoading: boolean;\n creating: boolean;\n deleting: boolean;\n renaming: boolean;\n hasFilters: boolean;\n activePreset: SavedFilterReadDto | null;\n applyPreset: (preset: SavedFilterReadDto) => void;\n clearPreset: () => void;\n saveCurrentFilters: (name: string) => Promise<SavedFilterReadDto | null>;\n renamePreset: (presetId: string, name: string) => Promise<SavedFilterReadDto | null>;\n removePreset: (presetId: string) => Promise<boolean>;\n}>();\n\nconst showPresetsModal = ref(false);\nconst showDeleteConfirm = ref(false);\nconst presetToDelete = ref<{ id: string; name: string } | null>(null);\n\nfunction handleClearPreset() {\n props.clearPreset();\n}\n\nfunction handleDelete(preset: { id: string; name: string }) {\n presetToDelete.value = preset;\n showDeleteConfirm.value = true;\n}\n\nasync function confirmDelete() {\n if (!presetToDelete.value) return;\n pinnedPresets.unpinPreset(presetToDelete.value.id);\n await props.removePreset(presetToDelete.value.id);\n presetToDelete.value = null;\n showDeleteConfirm.value = false;\n}\n\nasync function handleRename(preset: { id: string; name: string }) {\n pinnedPresets.updatePinnedPreset(preset.id, { name: preset.name });\n await pinnedPresets.refetchPinned?.();\n}\n</script>\n"],"mappings":"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"}
|