@fluid-app/portal-sdk 0.1.286 → 0.1.288
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/AddressAutocompleteInput-C_H7gSJt.mjs +2214 -0
- package/dist/AddressAutocompleteInput-C_H7gSJt.mjs.map +1 -0
- package/dist/AddressAutocompleteInput-tZX9kPwb.cjs +2310 -0
- package/dist/AddressAutocompleteInput-tZX9kPwb.cjs.map +1 -0
- package/dist/ContactsScreen-BaNFVTVX.cjs +10 -0
- package/dist/ContactsScreen-CLLyfNru.mjs +3397 -0
- package/dist/ContactsScreen-CLLyfNru.mjs.map +1 -0
- package/dist/ContactsScreen-D8M6J3nC.cjs +3405 -0
- package/dist/ContactsScreen-D8M6J3nC.cjs.map +1 -0
- package/dist/ProfileScreen-BDvruZ90.mjs +1232 -0
- package/dist/ProfileScreen-BDvruZ90.mjs.map +1 -0
- package/dist/ProfileScreen-BH0A6Y3R.mjs +46 -0
- package/dist/ProfileScreen-CT5nLt3E.cjs +1244 -0
- package/dist/ProfileScreen-CT5nLt3E.cjs.map +1 -0
- package/dist/ProfileScreen-DrTa9Boy.cjs +48 -0
- package/dist/ShareablesScreen-BT3H1Omb.cjs +13896 -0
- package/dist/ShareablesScreen-BT3H1Omb.cjs.map +1 -0
- package/dist/ShareablesScreen-BrsJQqia.mjs +13880 -0
- package/dist/ShareablesScreen-BrsJQqia.mjs.map +1 -0
- package/dist/ShareablesScreen-IU9JBJzD.mjs +14 -0
- package/dist/ShareablesScreen-RxdJqjda.cjs +16 -0
- package/dist/ShopScreen-BLo9zSmm.mjs +45 -0
- package/dist/ShopScreen-BgiNBkIk.cjs +1295 -0
- package/dist/ShopScreen-BgiNBkIk.cjs.map +1 -0
- package/dist/ShopScreen-Bjo-5mdC.mjs +1282 -0
- package/dist/ShopScreen-Bjo-5mdC.mjs.map +1 -0
- package/dist/ShopScreen-BzaTY9cV.cjs +47 -0
- package/dist/SubscriptionsScreen-5IsYJnvc.cjs +5791 -0
- package/dist/SubscriptionsScreen-5IsYJnvc.cjs.map +1 -0
- package/dist/SubscriptionsScreen-B9_Cmmrc.mjs +5723 -0
- package/dist/SubscriptionsScreen-B9_Cmmrc.mjs.map +1 -0
- package/dist/SubscriptionsScreen-BpiWZf9J.cjs +11 -0
- package/dist/UpgradeScreen-DjCkknBC.cjs +6 -0
- package/dist/UpgradeScreen-Dqe70qIt.cjs +203 -0
- package/dist/UpgradeScreen-Dqe70qIt.cjs.map +1 -0
- package/dist/UpgradeScreen-WLq4bFhS.mjs +196 -0
- package/dist/UpgradeScreen-WLq4bFhS.mjs.map +1 -0
- package/dist/de-B2xuRzqB.mjs +125 -0
- package/dist/de-B2xuRzqB.mjs.map +1 -0
- package/dist/de-BMzN6pvJ.mjs +37 -0
- package/dist/de-BMzN6pvJ.mjs.map +1 -0
- package/dist/de-BP_BwSkX.mjs +24 -0
- package/dist/de-BP_BwSkX.mjs.map +1 -0
- package/dist/de-BcjRy6IX.mjs +74 -0
- package/dist/de-BcjRy6IX.mjs.map +1 -0
- package/dist/de-CA1BpazO.cjs +43 -0
- package/dist/de-CA1BpazO.cjs.map +1 -0
- package/dist/de-CQojBku3.cjs +131 -0
- package/dist/de-CQojBku3.cjs.map +1 -0
- package/dist/de-DLKXYoUC.cjs +80 -0
- package/dist/de-DLKXYoUC.cjs.map +1 -0
- package/dist/de-DLVI44iM.cjs +12 -0
- package/dist/de-DLVI44iM.cjs.map +1 -0
- package/dist/de-DZXu_Lma.mjs +6 -0
- package/dist/de-DZXu_Lma.mjs.map +1 -0
- package/dist/de-d5sXuDlc.cjs +12 -0
- package/dist/de-d5sXuDlc.cjs.map +1 -0
- package/dist/de-hW77cwjn.cjs +30 -0
- package/dist/de-hW77cwjn.cjs.map +1 -0
- package/dist/de-ndSZ5ykY.mjs +6 -0
- package/dist/de-ndSZ5ykY.mjs.map +1 -0
- package/dist/el-3T5wNvmF.cjs +12 -0
- package/dist/el-3T5wNvmF.cjs.map +1 -0
- package/dist/el-BVROkdqG.cjs +43 -0
- package/dist/el-BVROkdqG.cjs.map +1 -0
- package/dist/el-C1QZdoK1.cjs +131 -0
- package/dist/el-C1QZdoK1.cjs.map +1 -0
- package/dist/el-CT5RY-mx.mjs +125 -0
- package/dist/el-CT5RY-mx.mjs.map +1 -0
- package/dist/el-CWq1GFO2.mjs +37 -0
- package/dist/el-CWq1GFO2.mjs.map +1 -0
- package/dist/el-ChL3zQdm.mjs +6 -0
- package/dist/el-ChL3zQdm.mjs.map +1 -0
- package/dist/el-Dji5p2km.cjs +80 -0
- package/dist/el-Dji5p2km.cjs.map +1 -0
- package/dist/el-DtirnL7N.cjs +12 -0
- package/dist/el-DtirnL7N.cjs.map +1 -0
- package/dist/el-XW2NR7Vh.mjs +74 -0
- package/dist/el-XW2NR7Vh.mjs.map +1 -0
- package/dist/el-XwgbXcif.cjs +30 -0
- package/dist/el-XwgbXcif.cjs.map +1 -0
- package/dist/el-epEJkauy.mjs +6 -0
- package/dist/el-epEJkauy.mjs.map +1 -0
- package/dist/el-xbVPoQUL.mjs +24 -0
- package/dist/el-xbVPoQUL.mjs.map +1 -0
- package/dist/es-BSkjqnkF.cjs +12 -0
- package/dist/es-BSkjqnkF.cjs.map +1 -0
- package/dist/es-BX5djVHQ.cjs +30 -0
- package/dist/es-BX5djVHQ.cjs.map +1 -0
- package/dist/es-Bh1xngyE.mjs +6 -0
- package/dist/es-Bh1xngyE.mjs.map +1 -0
- package/dist/es-BjDZEneQ.cjs +80 -0
- package/dist/es-BjDZEneQ.cjs.map +1 -0
- package/dist/es-C2uchKja.cjs +43 -0
- package/dist/es-C2uchKja.cjs.map +1 -0
- package/dist/es-C2zfJQck.mjs +37 -0
- package/dist/es-C2zfJQck.mjs.map +1 -0
- package/dist/es-CWW1uLK3.mjs +6 -0
- package/dist/es-CWW1uLK3.mjs.map +1 -0
- package/dist/es-CyPr-juY.cjs +131 -0
- package/dist/es-CyPr-juY.cjs.map +1 -0
- package/dist/es-DYAui4cM.cjs +12 -0
- package/dist/es-DYAui4cM.cjs.map +1 -0
- package/dist/es-DjcSUa7j.mjs +125 -0
- package/dist/es-DjcSUa7j.mjs.map +1 -0
- package/dist/es-DnX0XFqD.mjs +74 -0
- package/dist/es-DnX0XFqD.mjs.map +1 -0
- package/dist/es-O3pQS-dk.mjs +24 -0
- package/dist/es-O3pQS-dk.mjs.map +1 -0
- package/dist/fr-7IjPHZxW.mjs +74 -0
- package/dist/fr-7IjPHZxW.mjs.map +1 -0
- package/dist/fr-BiJrmc6t.mjs +125 -0
- package/dist/fr-BiJrmc6t.mjs.map +1 -0
- package/dist/fr-BzVQUMC0.cjs +131 -0
- package/dist/fr-BzVQUMC0.cjs.map +1 -0
- package/dist/fr-C-2Pj7D2.cjs +43 -0
- package/dist/fr-C-2Pj7D2.cjs.map +1 -0
- package/dist/fr-CT47eWK_.mjs +6 -0
- package/dist/fr-CT47eWK_.mjs.map +1 -0
- package/dist/fr-DYML7Hb_.cjs +12 -0
- package/dist/fr-DYML7Hb_.cjs.map +1 -0
- package/dist/fr-DfMQoI8H.mjs +6 -0
- package/dist/fr-DfMQoI8H.mjs.map +1 -0
- package/dist/fr-MJsdTksK.mjs +24 -0
- package/dist/fr-MJsdTksK.mjs.map +1 -0
- package/dist/fr-XWBehwDw.cjs +30 -0
- package/dist/fr-XWBehwDw.cjs.map +1 -0
- package/dist/fr-_zhbWJHh.cjs +12 -0
- package/dist/fr-_zhbWJHh.cjs.map +1 -0
- package/dist/fr-jXtwm66y.mjs +37 -0
- package/dist/fr-jXtwm66y.mjs.map +1 -0
- package/dist/fr-wYZdZtt7.cjs +80 -0
- package/dist/fr-wYZdZtt7.cjs.map +1 -0
- package/dist/he-BNgtWUzu.cjs +43 -0
- package/dist/he-BNgtWUzu.cjs.map +1 -0
- package/dist/he-BcBh-9RB.cjs +12 -0
- package/dist/he-BcBh-9RB.cjs.map +1 -0
- package/dist/he-C4xGY_O5.cjs +30 -0
- package/dist/he-C4xGY_O5.cjs.map +1 -0
- package/dist/he-CBny3yj_.cjs +131 -0
- package/dist/he-CBny3yj_.cjs.map +1 -0
- package/dist/he-Cai6j3oW.mjs +6 -0
- package/dist/he-Cai6j3oW.mjs.map +1 -0
- package/dist/he-CsOIjabt.mjs +24 -0
- package/dist/he-CsOIjabt.mjs.map +1 -0
- package/dist/he-Cu_YCy1f.cjs +12 -0
- package/dist/he-Cu_YCy1f.cjs.map +1 -0
- package/dist/he-D-R8pe0L.mjs +125 -0
- package/dist/he-D-R8pe0L.mjs.map +1 -0
- package/dist/he-Dv0k0pX-.cjs +80 -0
- package/dist/he-Dv0k0pX-.cjs.map +1 -0
- package/dist/he-OAQhQxWm.mjs +74 -0
- package/dist/he-OAQhQxWm.mjs.map +1 -0
- package/dist/he-gcmGB1-C.mjs +6 -0
- package/dist/he-gcmGB1-C.mjs.map +1 -0
- package/dist/he-z0iIyfGu.mjs +37 -0
- package/dist/he-z0iIyfGu.mjs.map +1 -0
- package/dist/hu-BLBHZrAu.mjs +37 -0
- package/dist/hu-BLBHZrAu.mjs.map +1 -0
- package/dist/hu-BR0rOtJC.cjs +43 -0
- package/dist/hu-BR0rOtJC.cjs.map +1 -0
- package/dist/hu-BTBlTUaQ.cjs +131 -0
- package/dist/hu-BTBlTUaQ.cjs.map +1 -0
- package/dist/hu-BTvnBj9a.cjs +30 -0
- package/dist/hu-BTvnBj9a.cjs.map +1 -0
- package/dist/hu-CDvVhWXF.mjs +74 -0
- package/dist/hu-CDvVhWXF.mjs.map +1 -0
- package/dist/hu-CYFjRrQD.cjs +80 -0
- package/dist/hu-CYFjRrQD.cjs.map +1 -0
- package/dist/hu-Czc1FPg9.cjs +12 -0
- package/dist/hu-Czc1FPg9.cjs.map +1 -0
- package/dist/hu-D4fy_GxO.mjs +125 -0
- package/dist/hu-D4fy_GxO.mjs.map +1 -0
- package/dist/hu-DGyykLlw.mjs +24 -0
- package/dist/hu-DGyykLlw.mjs.map +1 -0
- package/dist/hu-DS-6XHh5.mjs +6 -0
- package/dist/hu-DS-6XHh5.mjs.map +1 -0
- package/dist/hu-DqI8hdAt.cjs +12 -0
- package/dist/hu-DqI8hdAt.cjs.map +1 -0
- package/dist/hu-pswRdooT.mjs +6 -0
- package/dist/hu-pswRdooT.mjs.map +1 -0
- package/dist/id-4tl2Uj7F.mjs +6 -0
- package/dist/id-4tl2Uj7F.mjs.map +1 -0
- package/dist/id-52itow18.cjs +43 -0
- package/dist/id-52itow18.cjs.map +1 -0
- package/dist/id-Bbvk8MDA.mjs +125 -0
- package/dist/id-Bbvk8MDA.mjs.map +1 -0
- package/dist/id-BxQgqmXd.cjs +131 -0
- package/dist/id-BxQgqmXd.cjs.map +1 -0
- package/dist/id-C4zO4qio.cjs +30 -0
- package/dist/id-C4zO4qio.cjs.map +1 -0
- package/dist/id-Cdurf_cc.mjs +24 -0
- package/dist/id-Cdurf_cc.mjs.map +1 -0
- package/dist/id-DGh6JGZ5.mjs +37 -0
- package/dist/id-DGh6JGZ5.mjs.map +1 -0
- package/dist/id-DP5zxg41.mjs +74 -0
- package/dist/id-DP5zxg41.mjs.map +1 -0
- package/dist/id-Dhws7LdN.mjs +6 -0
- package/dist/id-Dhws7LdN.mjs.map +1 -0
- package/dist/id-DqK8hygJ.cjs +12 -0
- package/dist/id-DqK8hygJ.cjs.map +1 -0
- package/dist/id-Vw9DQ3V1.cjs +12 -0
- package/dist/id-Vw9DQ3V1.cjs.map +1 -0
- package/dist/id-tlOQCFLJ.cjs +80 -0
- package/dist/id-tlOQCFLJ.cjs.map +1 -0
- package/dist/index.cjs +152 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +152 -61
- package/dist/index.mjs.map +1 -1
- package/dist/it--DHBKc26.cjs +12 -0
- package/dist/it--DHBKc26.cjs.map +1 -0
- package/dist/it-BODMi-FY.cjs +30 -0
- package/dist/it-BODMi-FY.cjs.map +1 -0
- package/dist/it-C34p5klu.mjs +24 -0
- package/dist/it-C34p5klu.mjs.map +1 -0
- package/dist/it-CPXF5aFi.cjs +80 -0
- package/dist/it-CPXF5aFi.cjs.map +1 -0
- package/dist/it-CbZjaErj.cjs +43 -0
- package/dist/it-CbZjaErj.cjs.map +1 -0
- package/dist/it-CfLSq25e.mjs +37 -0
- package/dist/it-CfLSq25e.mjs.map +1 -0
- package/dist/it-CpnrX6Qu.cjs +12 -0
- package/dist/it-CpnrX6Qu.cjs.map +1 -0
- package/dist/it-CqvzV23C.mjs +74 -0
- package/dist/it-CqvzV23C.mjs.map +1 -0
- package/dist/it-ELWZ1sxy.mjs +125 -0
- package/dist/it-ELWZ1sxy.mjs.map +1 -0
- package/dist/it-Hj3LCvz1.mjs +6 -0
- package/dist/it-Hj3LCvz1.mjs.map +1 -0
- package/dist/it-L0NVZqjU.cjs +131 -0
- package/dist/it-L0NVZqjU.cjs.map +1 -0
- package/dist/it-dsOgAn3r.mjs +6 -0
- package/dist/it-dsOgAn3r.mjs.map +1 -0
- package/dist/ja-B1DQXHgd.mjs +74 -0
- package/dist/ja-B1DQXHgd.mjs.map +1 -0
- package/dist/ja-B2NQ5sR-.cjs +80 -0
- package/dist/ja-B2NQ5sR-.cjs.map +1 -0
- package/dist/ja-BYGHlw8d.cjs +30 -0
- package/dist/ja-BYGHlw8d.cjs.map +1 -0
- package/dist/ja-BaU2QXo2.cjs +43 -0
- package/dist/ja-BaU2QXo2.cjs.map +1 -0
- package/dist/ja-BqySbP-W.mjs +6 -0
- package/dist/ja-BqySbP-W.mjs.map +1 -0
- package/dist/ja-C1QZfTGN.mjs +125 -0
- package/dist/ja-C1QZfTGN.mjs.map +1 -0
- package/dist/ja-C3C0k99E.cjs +12 -0
- package/dist/ja-C3C0k99E.cjs.map +1 -0
- package/dist/ja-Cft4ODrl.mjs +24 -0
- package/dist/ja-Cft4ODrl.mjs.map +1 -0
- package/dist/ja-CuLC8KzH.mjs +6 -0
- package/dist/ja-CuLC8KzH.mjs.map +1 -0
- package/dist/ja-D5fpLCDT.cjs +131 -0
- package/dist/ja-D5fpLCDT.cjs.map +1 -0
- package/dist/ja-JVcCjBJ9.cjs +12 -0
- package/dist/ja-JVcCjBJ9.cjs.map +1 -0
- package/dist/ja-kM5CUvdg.mjs +37 -0
- package/dist/ja-kM5CUvdg.mjs.map +1 -0
- package/dist/ko-BREsc9Rl.mjs +24 -0
- package/dist/ko-BREsc9Rl.mjs.map +1 -0
- package/dist/ko-BX4GXxaa.mjs +125 -0
- package/dist/ko-BX4GXxaa.mjs.map +1 -0
- package/dist/ko-C0Q33hJ9.cjs +80 -0
- package/dist/ko-C0Q33hJ9.cjs.map +1 -0
- package/dist/ko-Cdb4MyCC.mjs +6 -0
- package/dist/ko-Cdb4MyCC.mjs.map +1 -0
- package/dist/ko-D2TLG_3_.mjs +37 -0
- package/dist/ko-D2TLG_3_.mjs.map +1 -0
- package/dist/ko-D64no-WM.cjs +131 -0
- package/dist/ko-D64no-WM.cjs.map +1 -0
- package/dist/ko-DDBkvKT3.mjs +6 -0
- package/dist/ko-DDBkvKT3.mjs.map +1 -0
- package/dist/ko-DROYufGO.cjs +12 -0
- package/dist/ko-DROYufGO.cjs.map +1 -0
- package/dist/ko-HEegPkOi.cjs +30 -0
- package/dist/ko-HEegPkOi.cjs.map +1 -0
- package/dist/ko-PUpIJgsU.mjs +74 -0
- package/dist/ko-PUpIJgsU.mjs.map +1 -0
- package/dist/ko-WIfVodYY.cjs +12 -0
- package/dist/ko-WIfVodYY.cjs.map +1 -0
- package/dist/ko-vQjiRfrX.cjs +43 -0
- package/dist/ko-vQjiRfrX.cjs.map +1 -0
- package/dist/nl-BdRkpB-f.mjs +74 -0
- package/dist/nl-BdRkpB-f.mjs.map +1 -0
- package/dist/nl-Byk7MZjq.cjs +12 -0
- package/dist/nl-Byk7MZjq.cjs.map +1 -0
- package/dist/nl-C5U5y6gr.cjs +12 -0
- package/dist/nl-C5U5y6gr.cjs.map +1 -0
- package/dist/nl-C8T6RqHI.cjs +131 -0
- package/dist/nl-C8T6RqHI.cjs.map +1 -0
- package/dist/nl-CxaUAerZ.mjs +24 -0
- package/dist/nl-CxaUAerZ.mjs.map +1 -0
- package/dist/nl-D0dcnS-5.mjs +6 -0
- package/dist/nl-D0dcnS-5.mjs.map +1 -0
- package/dist/nl-D5y8p_At.cjs +80 -0
- package/dist/nl-D5y8p_At.cjs.map +1 -0
- package/dist/nl-DFUHK5ta.mjs +6 -0
- package/dist/nl-DFUHK5ta.mjs.map +1 -0
- package/dist/nl-DRxCBp_v.cjs +43 -0
- package/dist/nl-DRxCBp_v.cjs.map +1 -0
- package/dist/nl-Dww7O0AO.mjs +37 -0
- package/dist/nl-Dww7O0AO.mjs.map +1 -0
- package/dist/nl-ayPagHJ_.cjs +30 -0
- package/dist/nl-ayPagHJ_.cjs.map +1 -0
- package/dist/nl-mL8uL7cu.mjs +125 -0
- package/dist/nl-mL8uL7cu.mjs.map +1 -0
- package/dist/pl-AsC20yM9.cjs +43 -0
- package/dist/pl-AsC20yM9.cjs.map +1 -0
- package/dist/pl-Beb7Fiam.mjs +125 -0
- package/dist/pl-Beb7Fiam.mjs.map +1 -0
- package/dist/pl-BvfqoRfV.cjs +80 -0
- package/dist/pl-BvfqoRfV.cjs.map +1 -0
- package/dist/pl-COP9NGch.mjs +6 -0
- package/dist/pl-COP9NGch.mjs.map +1 -0
- package/dist/pl-CcIcOxXb.mjs +6 -0
- package/dist/pl-CcIcOxXb.mjs.map +1 -0
- package/dist/pl-CgKBvrJr.mjs +24 -0
- package/dist/pl-CgKBvrJr.mjs.map +1 -0
- package/dist/pl-DJKAFKNJ.cjs +12 -0
- package/dist/pl-DJKAFKNJ.cjs.map +1 -0
- package/dist/pl-DZZ1e6TM.cjs +12 -0
- package/dist/pl-DZZ1e6TM.cjs.map +1 -0
- package/dist/pl-DpVEpGL0.cjs +131 -0
- package/dist/pl-DpVEpGL0.cjs.map +1 -0
- package/dist/pl-LHrd1s6r.cjs +30 -0
- package/dist/pl-LHrd1s6r.cjs.map +1 -0
- package/dist/pl-PV9rlOz1.mjs +37 -0
- package/dist/pl-PV9rlOz1.mjs.map +1 -0
- package/dist/pl-XfyC_jQs.mjs +74 -0
- package/dist/pl-XfyC_jQs.mjs.map +1 -0
- package/dist/pt-B4hr_uJO.mjs +125 -0
- package/dist/pt-B4hr_uJO.mjs.map +1 -0
- package/dist/pt-BBFH5zDu.cjs +43 -0
- package/dist/pt-BBFH5zDu.cjs.map +1 -0
- package/dist/pt-BC56DXTY.mjs +37 -0
- package/dist/pt-BC56DXTY.mjs.map +1 -0
- package/dist/pt-BrnvgQRs.cjs +12 -0
- package/dist/pt-BrnvgQRs.cjs.map +1 -0
- package/dist/pt-CSvh02ed.mjs +6 -0
- package/dist/pt-CSvh02ed.mjs.map +1 -0
- package/dist/pt-CW6UZk1v.cjs +30 -0
- package/dist/pt-CW6UZk1v.cjs.map +1 -0
- package/dist/pt-CifIYahI.cjs +131 -0
- package/dist/pt-CifIYahI.cjs.map +1 -0
- package/dist/pt-CpQN7iPy.cjs +12 -0
- package/dist/pt-CpQN7iPy.cjs.map +1 -0
- package/dist/pt-DG1WQnTO.mjs +24 -0
- package/dist/pt-DG1WQnTO.mjs.map +1 -0
- package/dist/pt-DLFLkEpR.mjs +74 -0
- package/dist/pt-DLFLkEpR.mjs.map +1 -0
- package/dist/pt-D_Fxu0mz.mjs +6 -0
- package/dist/pt-D_Fxu0mz.mjs.map +1 -0
- package/dist/pt-Dx69fLab.cjs +80 -0
- package/dist/pt-Dx69fLab.cjs.map +1 -0
- package/dist/ro-B6ExafZC.cjs +131 -0
- package/dist/ro-B6ExafZC.cjs.map +1 -0
- package/dist/ro-BY4QQCns.cjs +30 -0
- package/dist/ro-BY4QQCns.cjs.map +1 -0
- package/dist/ro-CGOwHLvs.cjs +12 -0
- package/dist/ro-CGOwHLvs.cjs.map +1 -0
- package/dist/ro-CJJKGRs9.mjs +37 -0
- package/dist/ro-CJJKGRs9.mjs.map +1 -0
- package/dist/ro-Cu3cJnmT.mjs +74 -0
- package/dist/ro-Cu3cJnmT.mjs.map +1 -0
- package/dist/ro-D2WH48l8.mjs +6 -0
- package/dist/ro-D2WH48l8.mjs.map +1 -0
- package/dist/ro-DS3mARiW.cjs +43 -0
- package/dist/ro-DS3mARiW.cjs.map +1 -0
- package/dist/ro-DStH8jqQ.cjs +80 -0
- package/dist/ro-DStH8jqQ.cjs.map +1 -0
- package/dist/ro-Dg398PV0.mjs +24 -0
- package/dist/ro-Dg398PV0.mjs.map +1 -0
- package/dist/ro-DhGoI55Z.mjs +125 -0
- package/dist/ro-DhGoI55Z.mjs.map +1 -0
- package/dist/ro-dpJtFKiA.cjs +12 -0
- package/dist/ro-dpJtFKiA.cjs.map +1 -0
- package/dist/ro-lFxvLN7R.mjs +6 -0
- package/dist/ro-lFxvLN7R.mjs.map +1 -0
- package/dist/ru-9zdAxTCI.cjs +30 -0
- package/dist/ru-9zdAxTCI.cjs.map +1 -0
- package/dist/ru-BCP_P-Kw.mjs +6 -0
- package/dist/ru-BCP_P-Kw.mjs.map +1 -0
- package/dist/ru-BymfL4lY.mjs +125 -0
- package/dist/ru-BymfL4lY.mjs.map +1 -0
- package/dist/ru-C9sTu8KO.mjs +74 -0
- package/dist/ru-C9sTu8KO.mjs.map +1 -0
- package/dist/ru-CKfstS2M.cjs +43 -0
- package/dist/ru-CKfstS2M.cjs.map +1 -0
- package/dist/ru-CW4wBDfy.cjs +12 -0
- package/dist/ru-CW4wBDfy.cjs.map +1 -0
- package/dist/ru-CyoLQlWx.cjs +12 -0
- package/dist/ru-CyoLQlWx.cjs.map +1 -0
- package/dist/ru-DHNcAvHk.mjs +37 -0
- package/dist/ru-DHNcAvHk.mjs.map +1 -0
- package/dist/ru-DPs8Axnb.cjs +80 -0
- package/dist/ru-DPs8Axnb.cjs.map +1 -0
- package/dist/ru-JgTYyWIt.mjs +6 -0
- package/dist/ru-JgTYyWIt.mjs.map +1 -0
- package/dist/ru-fz-F75f4.cjs +131 -0
- package/dist/ru-fz-F75f4.cjs.map +1 -0
- package/dist/ru-k8yUinAx.mjs +24 -0
- package/dist/ru-k8yUinAx.mjs.map +1 -0
- package/dist/th-B83Fh-0G.cjs +12 -0
- package/dist/th-B83Fh-0G.cjs.map +1 -0
- package/dist/th-BVg0Jjgr.cjs +131 -0
- package/dist/th-BVg0Jjgr.cjs.map +1 -0
- package/dist/th-BYXBW0X2.cjs +43 -0
- package/dist/th-BYXBW0X2.cjs.map +1 -0
- package/dist/th-C7VrhPP5.cjs +12 -0
- package/dist/th-C7VrhPP5.cjs.map +1 -0
- package/dist/th-CZxqRIHs.mjs +6 -0
- package/dist/th-CZxqRIHs.mjs.map +1 -0
- package/dist/th-CmX6_2CM.mjs +74 -0
- package/dist/th-CmX6_2CM.mjs.map +1 -0
- package/dist/th-D8Prwe8n.cjs +30 -0
- package/dist/th-D8Prwe8n.cjs.map +1 -0
- package/dist/th-DkjnkI6P.mjs +6 -0
- package/dist/th-DkjnkI6P.mjs.map +1 -0
- package/dist/th-KyxBje7z.cjs +80 -0
- package/dist/th-KyxBje7z.cjs.map +1 -0
- package/dist/th-cad5Cap4.mjs +37 -0
- package/dist/th-cad5Cap4.mjs.map +1 -0
- package/dist/th-ge5FSv6T.mjs +125 -0
- package/dist/th-ge5FSv6T.mjs.map +1 -0
- package/dist/th-nYdqpaRn.mjs +24 -0
- package/dist/th-nYdqpaRn.mjs.map +1 -0
- package/dist/tl-B888TBzV.mjs +37 -0
- package/dist/tl-B888TBzV.mjs.map +1 -0
- package/dist/tl-Bua5-krl.mjs +24 -0
- package/dist/tl-Bua5-krl.mjs.map +1 -0
- package/dist/tl-CATDGPeB.cjs +12 -0
- package/dist/tl-CATDGPeB.cjs.map +1 -0
- package/dist/tl-CKjLSF_S.cjs +131 -0
- package/dist/tl-CKjLSF_S.cjs.map +1 -0
- package/dist/tl-CT5Bevzy.mjs +6 -0
- package/dist/tl-CT5Bevzy.mjs.map +1 -0
- package/dist/tl-CsRFJOIR.cjs +80 -0
- package/dist/tl-CsRFJOIR.cjs.map +1 -0
- package/dist/tl-D4iI7i02.cjs +30 -0
- package/dist/tl-D4iI7i02.cjs.map +1 -0
- package/dist/tl-DCKUBvuV.mjs +6 -0
- package/dist/tl-DCKUBvuV.mjs.map +1 -0
- package/dist/tl-DI0F6h6a.mjs +74 -0
- package/dist/tl-DI0F6h6a.mjs.map +1 -0
- package/dist/tl-DzIwhz3k.cjs +12 -0
- package/dist/tl-DzIwhz3k.cjs.map +1 -0
- package/dist/tl-icpkJ5RY.mjs +125 -0
- package/dist/tl-icpkJ5RY.mjs.map +1 -0
- package/dist/tl-lZlSu-sM.cjs +43 -0
- package/dist/tl-lZlSu-sM.cjs.map +1 -0
- package/dist/tr-9nLGvy6N.mjs +24 -0
- package/dist/tr-9nLGvy6N.mjs.map +1 -0
- package/dist/tr-B96yy4BZ.mjs +37 -0
- package/dist/tr-B96yy4BZ.mjs.map +1 -0
- package/dist/tr-BdEGICrX.cjs +80 -0
- package/dist/tr-BdEGICrX.cjs.map +1 -0
- package/dist/tr-Bsdv5dWu.cjs +12 -0
- package/dist/tr-Bsdv5dWu.cjs.map +1 -0
- package/dist/tr-Bu5HeGRu.cjs +30 -0
- package/dist/tr-Bu5HeGRu.cjs.map +1 -0
- package/dist/tr-COlHtN47.mjs +6 -0
- package/dist/tr-COlHtN47.mjs.map +1 -0
- package/dist/tr-CVK6oefd.cjs +12 -0
- package/dist/tr-CVK6oefd.cjs.map +1 -0
- package/dist/tr-CifdAA_w.cjs +43 -0
- package/dist/tr-CifdAA_w.cjs.map +1 -0
- package/dist/tr-D1KVbmGI.mjs +74 -0
- package/dist/tr-D1KVbmGI.mjs.map +1 -0
- package/dist/tr-DMnXKPnI.cjs +131 -0
- package/dist/tr-DMnXKPnI.cjs.map +1 -0
- package/dist/tr-DuFbZrRu.mjs +6 -0
- package/dist/tr-DuFbZrRu.mjs.map +1 -0
- package/dist/tr-QaTfqnit.mjs +125 -0
- package/dist/tr-QaTfqnit.mjs.map +1 -0
- package/dist/zh_CN-2FPc1yGp.mjs +6 -0
- package/dist/zh_CN-2FPc1yGp.mjs.map +1 -0
- package/dist/zh_CN-BJnhvkc0.mjs +37 -0
- package/dist/zh_CN-BJnhvkc0.mjs.map +1 -0
- package/dist/zh_CN-BT8zi4ct.mjs +125 -0
- package/dist/zh_CN-BT8zi4ct.mjs.map +1 -0
- package/dist/zh_CN-BUDga7b8.cjs +12 -0
- package/dist/zh_CN-BUDga7b8.cjs.map +1 -0
- package/dist/zh_CN-BWIy2qoV.cjs +43 -0
- package/dist/zh_CN-BWIy2qoV.cjs.map +1 -0
- package/dist/zh_CN-BrkBDK-m.cjs +131 -0
- package/dist/zh_CN-BrkBDK-m.cjs.map +1 -0
- package/dist/zh_CN-Bzo7sqbA.cjs +12 -0
- package/dist/zh_CN-Bzo7sqbA.cjs.map +1 -0
- package/dist/zh_CN-CEwzKvT8.mjs +74 -0
- package/dist/zh_CN-CEwzKvT8.mjs.map +1 -0
- package/dist/zh_CN-CLqN3qJp.mjs +24 -0
- package/dist/zh_CN-CLqN3qJp.mjs.map +1 -0
- package/dist/zh_CN-DZFGqn6D.cjs +30 -0
- package/dist/zh_CN-DZFGqn6D.cjs.map +1 -0
- package/dist/zh_CN-oOeDsP-B.mjs +6 -0
- package/dist/zh_CN-oOeDsP-B.mjs.map +1 -0
- package/dist/zh_CN-wE1DUfsY.cjs +80 -0
- package/dist/zh_CN-wE1DUfsY.cjs.map +1 -0
- package/dist/zh_TW-BeBQ0pV1.mjs +74 -0
- package/dist/zh_TW-BeBQ0pV1.mjs.map +1 -0
- package/dist/zh_TW-Bz5_kUpI.mjs +37 -0
- package/dist/zh_TW-Bz5_kUpI.mjs.map +1 -0
- package/dist/zh_TW-CZhSh3BU.cjs +80 -0
- package/dist/zh_TW-CZhSh3BU.cjs.map +1 -0
- package/dist/zh_TW-D-xI7JBs.mjs +6 -0
- package/dist/zh_TW-D-xI7JBs.mjs.map +1 -0
- package/dist/zh_TW-D86GUR9C.cjs +43 -0
- package/dist/zh_TW-D86GUR9C.cjs.map +1 -0
- package/dist/zh_TW-DPLNY3Sh.cjs +12 -0
- package/dist/zh_TW-DPLNY3Sh.cjs.map +1 -0
- package/dist/zh_TW-DQdWK1u9.cjs +12 -0
- package/dist/zh_TW-DQdWK1u9.cjs.map +1 -0
- package/dist/zh_TW-Dg6LeVzh.mjs +24 -0
- package/dist/zh_TW-Dg6LeVzh.mjs.map +1 -0
- package/dist/zh_TW-DoDLHILX.cjs +30 -0
- package/dist/zh_TW-DoDLHILX.cjs.map +1 -0
- package/dist/zh_TW-Dqcoo0F5.mjs +6 -0
- package/dist/zh_TW-Dqcoo0F5.mjs.map +1 -0
- package/dist/zh_TW-DzFlj-kU.cjs +131 -0
- package/dist/zh_TW-DzFlj-kU.cjs.map +1 -0
- package/dist/zh_TW-idE0Evam.mjs +125 -0
- package/dist/zh_TW-idE0Evam.mjs.map +1 -0
- package/package.json +16 -16
- package/dist/AddressAutocompleteInput-DiXfEThY.mjs +0 -2214
- package/dist/AddressAutocompleteInput-DiXfEThY.mjs.map +0 -1
- package/dist/AddressAutocompleteInput-DmsSlPVz.cjs +0 -2310
- package/dist/AddressAutocompleteInput-DmsSlPVz.cjs.map +0 -1
- package/dist/ContactsScreen-9SzNlWEb.cjs +0 -10
- package/dist/ContactsScreen-BIPKKITz.mjs +0 -3397
- package/dist/ContactsScreen-BIPKKITz.mjs.map +0 -1
- package/dist/ContactsScreen-Comv_f_d.cjs +0 -3405
- package/dist/ContactsScreen-Comv_f_d.cjs.map +0 -1
- package/dist/ProfileScreen-B4OYJdd1.cjs +0 -1244
- package/dist/ProfileScreen-B4OYJdd1.cjs.map +0 -1
- package/dist/ProfileScreen-Be3RVQ2L.mjs +0 -1232
- package/dist/ProfileScreen-Be3RVQ2L.mjs.map +0 -1
- package/dist/ProfileScreen-D9YYYFK-.mjs +0 -46
- package/dist/ProfileScreen-j7Eyy4tk.cjs +0 -48
- package/dist/ShareablesScreen-CPvylNL7.mjs +0 -13
- package/dist/ShareablesScreen-CQZBq1Hl.cjs +0 -13457
- package/dist/ShareablesScreen-CQZBq1Hl.cjs.map +0 -1
- package/dist/ShareablesScreen-DYOIQwK-.mjs +0 -13441
- package/dist/ShareablesScreen-DYOIQwK-.mjs.map +0 -1
- package/dist/ShareablesScreen-ljPcmGLg.cjs +0 -15
- package/dist/ShopScreen-BEKSgvCp.mjs +0 -45
- package/dist/ShopScreen-CU0ynA2v.mjs +0 -1282
- package/dist/ShopScreen-CU0ynA2v.mjs.map +0 -1
- package/dist/ShopScreen-Cr_XcWdY.cjs +0 -1295
- package/dist/ShopScreen-Cr_XcWdY.cjs.map +0 -1
- package/dist/ShopScreen-QjSfEvCZ.cjs +0 -47
- package/dist/SubscriptionsScreen-Ces1Pv8k.mjs +0 -5723
- package/dist/SubscriptionsScreen-Ces1Pv8k.mjs.map +0 -1
- package/dist/SubscriptionsScreen-Cu-WeJ7L.cjs +0 -11
- package/dist/SubscriptionsScreen-DMqKtptN.cjs +0 -5791
- package/dist/SubscriptionsScreen-DMqKtptN.cjs.map +0 -1
- package/dist/UpgradeScreen-B4CnkCjg.cjs +0 -203
- package/dist/UpgradeScreen-B4CnkCjg.cjs.map +0 -1
- package/dist/UpgradeScreen-C9TjaPoJ.mjs +0 -196
- package/dist/UpgradeScreen-C9TjaPoJ.mjs.map +0 -1
- package/dist/UpgradeScreen-jvHU1at7.cjs +0 -6
- package/dist/de-14i_YO8a.mjs +0 -125
- package/dist/de-14i_YO8a.mjs.map +0 -1
- package/dist/de-8f8CBqQz.mjs +0 -74
- package/dist/de-8f8CBqQz.mjs.map +0 -1
- package/dist/de-C2d-Wmdy.cjs +0 -80
- package/dist/de-C2d-Wmdy.cjs.map +0 -1
- package/dist/de-CjPctz3q.cjs +0 -43
- package/dist/de-CjPctz3q.cjs.map +0 -1
- package/dist/de-DBGj2OFs.cjs +0 -30
- package/dist/de-DBGj2OFs.cjs.map +0 -1
- package/dist/de-DCq1H3bn.mjs +0 -24
- package/dist/de-DCq1H3bn.mjs.map +0 -1
- package/dist/de-DbKC3CDA.mjs +0 -37
- package/dist/de-DbKC3CDA.mjs.map +0 -1
- package/dist/de-MxH9dP_P.cjs +0 -131
- package/dist/de-MxH9dP_P.cjs.map +0 -1
- package/dist/el-B0l-Q1qT.mjs +0 -37
- package/dist/el-B0l-Q1qT.mjs.map +0 -1
- package/dist/el-B8bnnoKc.mjs +0 -24
- package/dist/el-B8bnnoKc.mjs.map +0 -1
- package/dist/el-BGxr23AZ.mjs +0 -74
- package/dist/el-BGxr23AZ.mjs.map +0 -1
- package/dist/el-BKGArK2f.cjs +0 -80
- package/dist/el-BKGArK2f.cjs.map +0 -1
- package/dist/el-BdbemgXl.cjs +0 -43
- package/dist/el-BdbemgXl.cjs.map +0 -1
- package/dist/el-C_EpDLLk.mjs +0 -125
- package/dist/el-C_EpDLLk.mjs.map +0 -1
- package/dist/el-DJbG54c1.cjs +0 -30
- package/dist/el-DJbG54c1.cjs.map +0 -1
- package/dist/el-v1A4TFkT.cjs +0 -131
- package/dist/el-v1A4TFkT.cjs.map +0 -1
- package/dist/es-BMW1EA1c.cjs +0 -30
- package/dist/es-BMW1EA1c.cjs.map +0 -1
- package/dist/es-BaMIh5FA.mjs +0 -125
- package/dist/es-BaMIh5FA.mjs.map +0 -1
- package/dist/es-CT2hq6ZJ.mjs +0 -24
- package/dist/es-CT2hq6ZJ.mjs.map +0 -1
- package/dist/es-C_4clFHt.cjs +0 -80
- package/dist/es-C_4clFHt.cjs.map +0 -1
- package/dist/es-CiGfOfqA.cjs +0 -43
- package/dist/es-CiGfOfqA.cjs.map +0 -1
- package/dist/es-CtRMRF22.mjs +0 -37
- package/dist/es-CtRMRF22.mjs.map +0 -1
- package/dist/es-DwVnFc84.mjs +0 -74
- package/dist/es-DwVnFc84.mjs.map +0 -1
- package/dist/es-OY_6uXf5.cjs +0 -131
- package/dist/es-OY_6uXf5.cjs.map +0 -1
- package/dist/fr-6k1dLzPa.cjs +0 -30
- package/dist/fr-6k1dLzPa.cjs.map +0 -1
- package/dist/fr-BXkj4XVE.cjs +0 -43
- package/dist/fr-BXkj4XVE.cjs.map +0 -1
- package/dist/fr-BcnTVvSD.mjs +0 -24
- package/dist/fr-BcnTVvSD.mjs.map +0 -1
- package/dist/fr-C0Oj4xnv.mjs +0 -74
- package/dist/fr-C0Oj4xnv.mjs.map +0 -1
- package/dist/fr-Ce2tevwP.mjs +0 -125
- package/dist/fr-Ce2tevwP.mjs.map +0 -1
- package/dist/fr-DSG1yVZR.cjs +0 -80
- package/dist/fr-DSG1yVZR.cjs.map +0 -1
- package/dist/fr-Zh7yPXbQ.cjs +0 -131
- package/dist/fr-Zh7yPXbQ.cjs.map +0 -1
- package/dist/fr-kd7QtgdA.mjs +0 -37
- package/dist/fr-kd7QtgdA.mjs.map +0 -1
- package/dist/he-Alro4HT4.cjs +0 -30
- package/dist/he-Alro4HT4.cjs.map +0 -1
- package/dist/he-B4cg1J00.mjs +0 -74
- package/dist/he-B4cg1J00.mjs.map +0 -1
- package/dist/he-Bsinkol5.mjs +0 -125
- package/dist/he-Bsinkol5.mjs.map +0 -1
- package/dist/he-CTOKIYzG.cjs +0 -80
- package/dist/he-CTOKIYzG.cjs.map +0 -1
- package/dist/he-CdDmyq0k.cjs +0 -43
- package/dist/he-CdDmyq0k.cjs.map +0 -1
- package/dist/he-CuuLh6US.cjs +0 -131
- package/dist/he-CuuLh6US.cjs.map +0 -1
- package/dist/he-CvNcU3F-.mjs +0 -24
- package/dist/he-CvNcU3F-.mjs.map +0 -1
- package/dist/he-oOy2HM0e.mjs +0 -37
- package/dist/he-oOy2HM0e.mjs.map +0 -1
- package/dist/hu-B84oEeJZ.mjs +0 -125
- package/dist/hu-B84oEeJZ.mjs.map +0 -1
- package/dist/hu-BSPTfhWR.cjs +0 -43
- package/dist/hu-BSPTfhWR.cjs.map +0 -1
- package/dist/hu-CF_ikpEl.cjs +0 -80
- package/dist/hu-CF_ikpEl.cjs.map +0 -1
- package/dist/hu-CYlsbrG1.mjs +0 -24
- package/dist/hu-CYlsbrG1.mjs.map +0 -1
- package/dist/hu-D5x1eDHD.cjs +0 -30
- package/dist/hu-D5x1eDHD.cjs.map +0 -1
- package/dist/hu-Dv5s1IMB.mjs +0 -37
- package/dist/hu-Dv5s1IMB.mjs.map +0 -1
- package/dist/hu-IZ3Wz8XP.mjs +0 -74
- package/dist/hu-IZ3Wz8XP.mjs.map +0 -1
- package/dist/hu-Vw6dogZo.cjs +0 -131
- package/dist/hu-Vw6dogZo.cjs.map +0 -1
- package/dist/id-1z_sYwiV.cjs +0 -131
- package/dist/id-1z_sYwiV.cjs.map +0 -1
- package/dist/id-C30Gd0ig.mjs +0 -24
- package/dist/id-C30Gd0ig.mjs.map +0 -1
- package/dist/id-C4q84iQs.mjs +0 -37
- package/dist/id-C4q84iQs.mjs.map +0 -1
- package/dist/id-CKzznQLP.cjs +0 -80
- package/dist/id-CKzznQLP.cjs.map +0 -1
- package/dist/id-CXL-L1qX.mjs +0 -125
- package/dist/id-CXL-L1qX.mjs.map +0 -1
- package/dist/id-DI4Mmfx0.mjs +0 -74
- package/dist/id-DI4Mmfx0.mjs.map +0 -1
- package/dist/id-Duby505w.cjs +0 -30
- package/dist/id-Duby505w.cjs.map +0 -1
- package/dist/id-NeJHVkY8.cjs +0 -43
- package/dist/id-NeJHVkY8.cjs.map +0 -1
- package/dist/it-5pXu8eWE.cjs +0 -43
- package/dist/it-5pXu8eWE.cjs.map +0 -1
- package/dist/it-BSiMXbIw.cjs +0 -30
- package/dist/it-BSiMXbIw.cjs.map +0 -1
- package/dist/it-BbNDdOME.mjs +0 -37
- package/dist/it-BbNDdOME.mjs.map +0 -1
- package/dist/it-CQY4tm5S.cjs +0 -131
- package/dist/it-CQY4tm5S.cjs.map +0 -1
- package/dist/it-CTPG4nXE.mjs +0 -24
- package/dist/it-CTPG4nXE.mjs.map +0 -1
- package/dist/it-CnIs_n8L.mjs +0 -74
- package/dist/it-CnIs_n8L.mjs.map +0 -1
- package/dist/it-CwAg1PPt.mjs +0 -125
- package/dist/it-CwAg1PPt.mjs.map +0 -1
- package/dist/it-IEJYmwzw.cjs +0 -80
- package/dist/it-IEJYmwzw.cjs.map +0 -1
- package/dist/ja-B-YZ7YHO.mjs +0 -74
- package/dist/ja-B-YZ7YHO.mjs.map +0 -1
- package/dist/ja-BBOP6z7P.cjs +0 -131
- package/dist/ja-BBOP6z7P.cjs.map +0 -1
- package/dist/ja-CDnUJv_3.mjs +0 -37
- package/dist/ja-CDnUJv_3.mjs.map +0 -1
- package/dist/ja-D5845TL0.mjs +0 -24
- package/dist/ja-D5845TL0.mjs.map +0 -1
- package/dist/ja-DobSeN1K.cjs +0 -43
- package/dist/ja-DobSeN1K.cjs.map +0 -1
- package/dist/ja-X6HQT0aD.cjs +0 -80
- package/dist/ja-X6HQT0aD.cjs.map +0 -1
- package/dist/ja-lCgyxNWH.mjs +0 -125
- package/dist/ja-lCgyxNWH.mjs.map +0 -1
- package/dist/ja-pSbdkRSW.cjs +0 -30
- package/dist/ja-pSbdkRSW.cjs.map +0 -1
- package/dist/ko-B3Oa-b5c.mjs +0 -24
- package/dist/ko-B3Oa-b5c.mjs.map +0 -1
- package/dist/ko-B4GjXpnY.mjs +0 -125
- package/dist/ko-B4GjXpnY.mjs.map +0 -1
- package/dist/ko-BIi-F1dV.cjs +0 -131
- package/dist/ko-BIi-F1dV.cjs.map +0 -1
- package/dist/ko-BfySQ1ZI.mjs +0 -74
- package/dist/ko-BfySQ1ZI.mjs.map +0 -1
- package/dist/ko-C-DvBsfj.cjs +0 -80
- package/dist/ko-C-DvBsfj.cjs.map +0 -1
- package/dist/ko-CuJFyarb.cjs +0 -43
- package/dist/ko-CuJFyarb.cjs.map +0 -1
- package/dist/ko-ir7yEsoy.cjs +0 -30
- package/dist/ko-ir7yEsoy.cjs.map +0 -1
- package/dist/ko-o5PlsiO4.mjs +0 -37
- package/dist/ko-o5PlsiO4.mjs.map +0 -1
- package/dist/nl-B3aJ9t79.cjs +0 -30
- package/dist/nl-B3aJ9t79.cjs.map +0 -1
- package/dist/nl-B8i6Eran.mjs +0 -125
- package/dist/nl-B8i6Eran.mjs.map +0 -1
- package/dist/nl-Casz-6t5.mjs +0 -37
- package/dist/nl-Casz-6t5.mjs.map +0 -1
- package/dist/nl-D6QZWEm6.mjs +0 -74
- package/dist/nl-D6QZWEm6.mjs.map +0 -1
- package/dist/nl-DOW7O3Ls.cjs +0 -80
- package/dist/nl-DOW7O3Ls.cjs.map +0 -1
- package/dist/nl-DlW0bd6G.mjs +0 -24
- package/dist/nl-DlW0bd6G.mjs.map +0 -1
- package/dist/nl-L3QBBaKe.cjs +0 -131
- package/dist/nl-L3QBBaKe.cjs.map +0 -1
- package/dist/nl-UaKnUkiF.cjs +0 -43
- package/dist/nl-UaKnUkiF.cjs.map +0 -1
- package/dist/pl-BTpBOJRd.mjs +0 -74
- package/dist/pl-BTpBOJRd.mjs.map +0 -1
- package/dist/pl-Bc5PLKWA.cjs +0 -30
- package/dist/pl-Bc5PLKWA.cjs.map +0 -1
- package/dist/pl-BhYoyqAF.mjs +0 -125
- package/dist/pl-BhYoyqAF.mjs.map +0 -1
- package/dist/pl-CK-QOBHU.cjs +0 -43
- package/dist/pl-CK-QOBHU.cjs.map +0 -1
- package/dist/pl-CMFgAXzQ.mjs +0 -24
- package/dist/pl-CMFgAXzQ.mjs.map +0 -1
- package/dist/pl-CuCpTUw8.cjs +0 -80
- package/dist/pl-CuCpTUw8.cjs.map +0 -1
- package/dist/pl-Dhgn2yX2.cjs +0 -131
- package/dist/pl-Dhgn2yX2.cjs.map +0 -1
- package/dist/pl-QhF51ZKE.mjs +0 -37
- package/dist/pl-QhF51ZKE.mjs.map +0 -1
- package/dist/pt-BLHjWaSa.cjs +0 -43
- package/dist/pt-BLHjWaSa.cjs.map +0 -1
- package/dist/pt-BNcmRTXF.mjs +0 -125
- package/dist/pt-BNcmRTXF.mjs.map +0 -1
- package/dist/pt-Bk5lU5Bq.cjs +0 -30
- package/dist/pt-Bk5lU5Bq.cjs.map +0 -1
- package/dist/pt-Bo9r1x0D.cjs +0 -131
- package/dist/pt-Bo9r1x0D.cjs.map +0 -1
- package/dist/pt-CRWFsKEU.mjs +0 -37
- package/dist/pt-CRWFsKEU.mjs.map +0 -1
- package/dist/pt-DK2uIFjk.mjs +0 -24
- package/dist/pt-DK2uIFjk.mjs.map +0 -1
- package/dist/pt-DidvCPJO.mjs +0 -74
- package/dist/pt-DidvCPJO.mjs.map +0 -1
- package/dist/pt-gwAOG4ye.cjs +0 -80
- package/dist/pt-gwAOG4ye.cjs.map +0 -1
- package/dist/ro-0CezOHmT.cjs +0 -80
- package/dist/ro-0CezOHmT.cjs.map +0 -1
- package/dist/ro-B3zGlIxa.cjs +0 -30
- package/dist/ro-B3zGlIxa.cjs.map +0 -1
- package/dist/ro-D7ttD6wI.mjs +0 -24
- package/dist/ro-D7ttD6wI.mjs.map +0 -1
- package/dist/ro-DIuZ_x0Y.mjs +0 -125
- package/dist/ro-DIuZ_x0Y.mjs.map +0 -1
- package/dist/ro-DPmaLEta.cjs +0 -131
- package/dist/ro-DPmaLEta.cjs.map +0 -1
- package/dist/ro-DeurRKLx.mjs +0 -37
- package/dist/ro-DeurRKLx.mjs.map +0 -1
- package/dist/ro-nugOenVq.cjs +0 -43
- package/dist/ro-nugOenVq.cjs.map +0 -1
- package/dist/ro-uWPaYSks.mjs +0 -74
- package/dist/ro-uWPaYSks.mjs.map +0 -1
- package/dist/ru-AaRaNSOD.cjs +0 -80
- package/dist/ru-AaRaNSOD.cjs.map +0 -1
- package/dist/ru-B9QN1Xa3.cjs +0 -30
- package/dist/ru-B9QN1Xa3.cjs.map +0 -1
- package/dist/ru-C127BOjG.mjs +0 -125
- package/dist/ru-C127BOjG.mjs.map +0 -1
- package/dist/ru-C72sfdMa.mjs +0 -24
- package/dist/ru-C72sfdMa.mjs.map +0 -1
- package/dist/ru-CYExV0AV.cjs +0 -131
- package/dist/ru-CYExV0AV.cjs.map +0 -1
- package/dist/ru-D9P2Trsv.mjs +0 -74
- package/dist/ru-D9P2Trsv.mjs.map +0 -1
- package/dist/ru-DH4xf5h_.cjs +0 -43
- package/dist/ru-DH4xf5h_.cjs.map +0 -1
- package/dist/ru-DbmM_yGB.mjs +0 -37
- package/dist/ru-DbmM_yGB.mjs.map +0 -1
- package/dist/th-BFt4w61B.cjs +0 -30
- package/dist/th-BFt4w61B.cjs.map +0 -1
- package/dist/th-BG8R3dAP.cjs +0 -43
- package/dist/th-BG8R3dAP.cjs.map +0 -1
- package/dist/th-BJlArNGG.mjs +0 -24
- package/dist/th-BJlArNGG.mjs.map +0 -1
- package/dist/th-BavxQWOu.cjs +0 -131
- package/dist/th-BavxQWOu.cjs.map +0 -1
- package/dist/th-CGTuBtiC.mjs +0 -37
- package/dist/th-CGTuBtiC.mjs.map +0 -1
- package/dist/th-a1Jy5C_d.cjs +0 -80
- package/dist/th-a1Jy5C_d.cjs.map +0 -1
- package/dist/th-h90suc6n.mjs +0 -125
- package/dist/th-h90suc6n.mjs.map +0 -1
- package/dist/th-zwlwoiv6.mjs +0 -74
- package/dist/th-zwlwoiv6.mjs.map +0 -1
- package/dist/tl-BHwQDhWD.mjs +0 -125
- package/dist/tl-BHwQDhWD.mjs.map +0 -1
- package/dist/tl-BoIi6HWk.cjs +0 -131
- package/dist/tl-BoIi6HWk.cjs.map +0 -1
- package/dist/tl-BsGu5Py0.mjs +0 -24
- package/dist/tl-BsGu5Py0.mjs.map +0 -1
- package/dist/tl-ByorVCxH.cjs +0 -43
- package/dist/tl-ByorVCxH.cjs.map +0 -1
- package/dist/tl-C2azJUgn.mjs +0 -74
- package/dist/tl-C2azJUgn.mjs.map +0 -1
- package/dist/tl-Ce-0Cpm6.cjs +0 -30
- package/dist/tl-Ce-0Cpm6.cjs.map +0 -1
- package/dist/tl-DN6DtBpZ.cjs +0 -80
- package/dist/tl-DN6DtBpZ.cjs.map +0 -1
- package/dist/tl-DytTStSw.mjs +0 -37
- package/dist/tl-DytTStSw.mjs.map +0 -1
- package/dist/tr-5RnbIa4c.mjs +0 -24
- package/dist/tr-5RnbIa4c.mjs.map +0 -1
- package/dist/tr-8c97dqrL.cjs +0 -131
- package/dist/tr-8c97dqrL.cjs.map +0 -1
- package/dist/tr-BT6N_Wus.mjs +0 -74
- package/dist/tr-BT6N_Wus.mjs.map +0 -1
- package/dist/tr-BedGdI5d.cjs +0 -80
- package/dist/tr-BedGdI5d.cjs.map +0 -1
- package/dist/tr-BouBqgCB.mjs +0 -125
- package/dist/tr-BouBqgCB.mjs.map +0 -1
- package/dist/tr-D6eImzZD.mjs +0 -37
- package/dist/tr-D6eImzZD.mjs.map +0 -1
- package/dist/tr-DcRheecc.cjs +0 -43
- package/dist/tr-DcRheecc.cjs.map +0 -1
- package/dist/tr-De6vY4Jr.cjs +0 -30
- package/dist/tr-De6vY4Jr.cjs.map +0 -1
- package/dist/zh_CN-BQsZQZhr.mjs +0 -74
- package/dist/zh_CN-BQsZQZhr.mjs.map +0 -1
- package/dist/zh_CN-BokVyKnZ.cjs +0 -80
- package/dist/zh_CN-BokVyKnZ.cjs.map +0 -1
- package/dist/zh_CN-COIJjtl4.mjs +0 -37
- package/dist/zh_CN-COIJjtl4.mjs.map +0 -1
- package/dist/zh_CN-Cj5fwlCh.mjs +0 -24
- package/dist/zh_CN-Cj5fwlCh.mjs.map +0 -1
- package/dist/zh_CN-Cr8k96O_.cjs +0 -131
- package/dist/zh_CN-Cr8k96O_.cjs.map +0 -1
- package/dist/zh_CN-CtrM9Bt7.mjs +0 -125
- package/dist/zh_CN-CtrM9Bt7.mjs.map +0 -1
- package/dist/zh_CN-CubDPpC6.cjs +0 -43
- package/dist/zh_CN-CubDPpC6.cjs.map +0 -1
- package/dist/zh_CN-CyFIfnPm.cjs +0 -30
- package/dist/zh_CN-CyFIfnPm.cjs.map +0 -1
- package/dist/zh_TW-BQjHIF9g.cjs +0 -80
- package/dist/zh_TW-BQjHIF9g.cjs.map +0 -1
- package/dist/zh_TW-BrH-zluV.cjs +0 -43
- package/dist/zh_TW-BrH-zluV.cjs.map +0 -1
- package/dist/zh_TW-Cs0GkiU5.cjs +0 -30
- package/dist/zh_TW-Cs0GkiU5.cjs.map +0 -1
- package/dist/zh_TW-DB4Om9FX.mjs +0 -37
- package/dist/zh_TW-DB4Om9FX.mjs.map +0 -1
- package/dist/zh_TW-DdCGAeE-.mjs +0 -125
- package/dist/zh_TW-DdCGAeE-.mjs.map +0 -1
- package/dist/zh_TW-MAOS8yS2.mjs +0 -74
- package/dist/zh_TW-MAOS8yS2.mjs.map +0 -1
- package/dist/zh_TW-Q6KxkIVE.mjs +0 -24
- package/dist/zh_TW-Q6KxkIVE.mjs.map +0 -1
- package/dist/zh_TW-jgCKq0gP.cjs +0 -131
- package/dist/zh_TW-jgCKq0gP.cjs.map +0 -1
|
@@ -0,0 +1,3405 @@
|
|
|
1
|
+
const require_chunk = require("./chunk-9hOWP6kD.cjs");
|
|
2
|
+
const require_countries_api_context = require("./countries-api-context-C0C0K9gJ.cjs");
|
|
3
|
+
const require_PortalTenantClientProvider = require("./PortalTenantClientProvider-CVv-4rQ9.cjs");
|
|
4
|
+
const require_static_dict_adapter = require("./static-dict-adapter-D1ErNskC.cjs");
|
|
5
|
+
const require_src = require("./src-B0ut9PTw.cjs");
|
|
6
|
+
const require_parse_task_body = require("./parse-task-body-41D9d0mr.cjs");
|
|
7
|
+
const require_ScreenHeaderContext = require("./ScreenHeaderContext-CsfhnuJk.cjs");
|
|
8
|
+
const require_query_keys = require("./query-keys-D3lK70Ea.cjs");
|
|
9
|
+
const require_dist = require("./dist-DQ-PaPl7.cjs");
|
|
10
|
+
const require_dist$1 = require("./dist-PK-HEsOm.cjs");
|
|
11
|
+
let react = require("react");
|
|
12
|
+
react = require_chunk.__toESM(react);
|
|
13
|
+
let _tanstack_react_query = require("@tanstack/react-query");
|
|
14
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
15
|
+
let lucide_react = require("lucide-react");
|
|
16
|
+
let react_hook_form = require("react-hook-form");
|
|
17
|
+
let zod = require("zod");
|
|
18
|
+
//#region ../../platform/api-client-core/src/parse-api-errors.ts
|
|
19
|
+
/**
|
|
20
|
+
* Framework-agnostic API error parsing utilities.
|
|
21
|
+
*
|
|
22
|
+
* Extracts structured field-level errors from API responses and formats
|
|
23
|
+
* them into human-readable messages. Works with ApiError from this package
|
|
24
|
+
* as well as any error object that has `status`, `data`, and optional `message`.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Converts snake_case or camelCase field names to Title Case
|
|
28
|
+
*/
|
|
29
|
+
function formatFieldName(field) {
|
|
30
|
+
return field.replace(/_/g, " ").replace(/([A-Z])/g, " $1").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ").trim();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Type guard to check if an error looks like an API error
|
|
34
|
+
*/
|
|
35
|
+
function isApiLikeError(error) {
|
|
36
|
+
if (!error || typeof error !== "object") return false;
|
|
37
|
+
const err = error;
|
|
38
|
+
return typeof err.status === "number" && "data" in err && (typeof err.message === "string" || err.message === void 0);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Extracts field-level errors from API error data
|
|
42
|
+
*/
|
|
43
|
+
function extractFieldErrors(data) {
|
|
44
|
+
const errors = [];
|
|
45
|
+
if (!data || typeof data !== "object") return errors;
|
|
46
|
+
const errorObj = data;
|
|
47
|
+
for (const [key, value] of Object.entries(errorObj)) if (Array.isArray(value) && value.length > 0) errors.push({
|
|
48
|
+
field: formatFieldName(key),
|
|
49
|
+
messages: value.map((item) => typeof item === "string" ? item : JSON.stringify(item))
|
|
50
|
+
});
|
|
51
|
+
else if (typeof value === "string" && value.length > 0) errors.push({
|
|
52
|
+
field: formatFieldName(key),
|
|
53
|
+
messages: [value]
|
|
54
|
+
});
|
|
55
|
+
else if (value && typeof value === "object" && !Array.isArray(value)) extractFieldErrors(value).forEach((nestedError) => {
|
|
56
|
+
errors.push({
|
|
57
|
+
field: `${formatFieldName(key)} → ${nestedError.field}`,
|
|
58
|
+
messages: nestedError.messages
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
return errors;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Formats field errors into a readable description string
|
|
65
|
+
*/
|
|
66
|
+
function formatErrorDescription(errors) {
|
|
67
|
+
if (errors.length === 0) return "";
|
|
68
|
+
if (errors.length === 1) {
|
|
69
|
+
const err = errors[0];
|
|
70
|
+
if (!err) return "";
|
|
71
|
+
const message = err.messages[0] || "is invalid";
|
|
72
|
+
return `${err.field} ${message}`;
|
|
73
|
+
}
|
|
74
|
+
if (errors.length <= 3) return errors.map((e) => `${e.field} ${e.messages[0] || "is invalid"}`).join("\n");
|
|
75
|
+
const shown = errors.slice(0, 3).map((e) => `${e.field} ${e.messages[0] || "is invalid"}`).join("\n");
|
|
76
|
+
const remaining = errors.length - 3;
|
|
77
|
+
return `${shown}\n...and ${remaining} more ${remaining === 1 ? "error" : "errors"}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parses an error and returns a human-readable description string.
|
|
81
|
+
*
|
|
82
|
+
* Handles:
|
|
83
|
+
* - API-like errors with structured field-level data
|
|
84
|
+
* - API-like errors with a top-level message
|
|
85
|
+
* - Standard Error instances
|
|
86
|
+
* - Falls back to the provided fallback string
|
|
87
|
+
*
|
|
88
|
+
* @param error - The error to parse (ApiError, Error, or unknown)
|
|
89
|
+
* @param fallback - Optional fallback description if error cannot be parsed
|
|
90
|
+
* @returns A human-readable error description, or undefined if nothing could be extracted
|
|
91
|
+
*/
|
|
92
|
+
function parseApiErrors(error, fallback) {
|
|
93
|
+
if (isApiLikeError(error)) {
|
|
94
|
+
if (error.data) {
|
|
95
|
+
const fieldErrors = extractFieldErrors(error.data);
|
|
96
|
+
if (fieldErrors.length > 0) return formatErrorDescription(fieldErrors);
|
|
97
|
+
}
|
|
98
|
+
if (error.message) return error.message;
|
|
99
|
+
} else if (error instanceof Error) return error.message;
|
|
100
|
+
return fallback;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region ../../contacts/core/src/translation-api-context.ts
|
|
104
|
+
const { Provider: ContactsProvider, useTranslation } = require_static_dict_adapter.createTranslationContext("Contacts");
|
|
105
|
+
const ContactsTranslationProvider = ContactsProvider;
|
|
106
|
+
const useContactsTranslation = useTranslation;
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region ../../contacts/ui/src/screens/ContactCreateScreen.tsx
|
|
109
|
+
function ContactCreateScreen({ onNavigateToList, onSubmit, isPending, children }) {
|
|
110
|
+
const { t } = useContactsTranslation();
|
|
111
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
112
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_ScreenHeaderContext.ScreenHeaderActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
113
|
+
variant: "outline",
|
|
114
|
+
onClick: onNavigateToList,
|
|
115
|
+
disabled: isPending,
|
|
116
|
+
children: t("cancel")
|
|
117
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
118
|
+
onClick: onSubmit,
|
|
119
|
+
disabled: isPending,
|
|
120
|
+
children: isPending ? t("adding") : t("add_contact")
|
|
121
|
+
})] }),
|
|
122
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ScreenHeaderContext.ScreenHeaderBreadcrumbs, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Breadcrumb, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.BreadcrumbList, {
|
|
123
|
+
className: "text-lg",
|
|
124
|
+
children: [
|
|
125
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.BreadcrumbItem, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.BreadcrumbLink, {
|
|
126
|
+
href: "#",
|
|
127
|
+
onClick: (e) => {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
onNavigateToList();
|
|
130
|
+
},
|
|
131
|
+
children: t("breadcrumb")
|
|
132
|
+
}) }),
|
|
133
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.BreadcrumbSeparator, {}),
|
|
134
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.BreadcrumbItem, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.BreadcrumbPage, {
|
|
135
|
+
className: "font-semibold",
|
|
136
|
+
children: t("breadcrumb_new")
|
|
137
|
+
}) })
|
|
138
|
+
]
|
|
139
|
+
}) }) }),
|
|
140
|
+
children
|
|
141
|
+
] });
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region ../../contacts/core/src/contacts-api-context.ts
|
|
145
|
+
const ContactsApiContext = (0, react.createContext)(null);
|
|
146
|
+
const ContactsApiProvider = ContactsApiContext.Provider;
|
|
147
|
+
function useContactsDomainApi() {
|
|
148
|
+
const api = (0, react.useContext)(ContactsApiContext);
|
|
149
|
+
if (!api) throw new Error("useContactsDomainApi must be used within a ContactsApiProvider");
|
|
150
|
+
return api;
|
|
151
|
+
}
|
|
152
|
+
function useContactsCrud() {
|
|
153
|
+
return useContactsDomainApi().contacts;
|
|
154
|
+
}
|
|
155
|
+
function useNotesApi() {
|
|
156
|
+
return useContactsDomainApi().notes;
|
|
157
|
+
}
|
|
158
|
+
function useTasksApi() {
|
|
159
|
+
return useContactsDomainApi().tasks;
|
|
160
|
+
}
|
|
161
|
+
/** Returns GroupsApi if the provider supplies one, otherwise null. */
|
|
162
|
+
function useGroupsApi() {
|
|
163
|
+
return useContactsDomainApi().groups ?? null;
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region ../../contacts/core/src/query-keys.ts
|
|
167
|
+
const CONTACTS_QUERY_KEYS = {
|
|
168
|
+
all: (prefix) => [prefix],
|
|
169
|
+
list: (prefix) => [...CONTACTS_QUERY_KEYS.all(prefix), "list"],
|
|
170
|
+
detail: (prefix, id) => [
|
|
171
|
+
...CONTACTS_QUERY_KEYS.all(prefix),
|
|
172
|
+
"detail",
|
|
173
|
+
id
|
|
174
|
+
]
|
|
175
|
+
};
|
|
176
|
+
const contactsKeys = {
|
|
177
|
+
activities: (contactId) => [
|
|
178
|
+
"portal-contacts",
|
|
179
|
+
"activities",
|
|
180
|
+
contactId
|
|
181
|
+
],
|
|
182
|
+
tasks: (contactId) => [
|
|
183
|
+
"portal-contacts",
|
|
184
|
+
"tasks",
|
|
185
|
+
contactId
|
|
186
|
+
],
|
|
187
|
+
notes: (contactId) => [
|
|
188
|
+
"portal-contacts",
|
|
189
|
+
"notes",
|
|
190
|
+
contactId
|
|
191
|
+
],
|
|
192
|
+
orders: (contactId) => [
|
|
193
|
+
"rep-contacts",
|
|
194
|
+
"orders",
|
|
195
|
+
contactId
|
|
196
|
+
],
|
|
197
|
+
subscriptionOrders: (contactId) => [
|
|
198
|
+
"rep-contacts",
|
|
199
|
+
"subscription-orders",
|
|
200
|
+
contactId
|
|
201
|
+
],
|
|
202
|
+
groups: () => ["portal-contacts", "groups"]
|
|
203
|
+
};
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region ../../contacts/core/src/hooks/use-infinite-contacts.ts
|
|
206
|
+
function useInfiniteContacts(params) {
|
|
207
|
+
const api = useContactsCrud();
|
|
208
|
+
return (0, _tanstack_react_query.useInfiniteQuery)({
|
|
209
|
+
queryKey: [...CONTACTS_QUERY_KEYS.list("contacts"), params],
|
|
210
|
+
queryFn: ({ pageParam }) => api.listContacts({
|
|
211
|
+
...params,
|
|
212
|
+
page: pageParam
|
|
213
|
+
}),
|
|
214
|
+
getNextPageParam: (lastPage) => {
|
|
215
|
+
const currentPage = lastPage.meta.current_page;
|
|
216
|
+
if (currentPage == null) return void 0;
|
|
217
|
+
if (lastPage.meta.next_cursor) return currentPage + 1;
|
|
218
|
+
if (lastPage.meta.total_pages != null && currentPage < lastPage.meta.total_pages) return currentPage + 1;
|
|
219
|
+
},
|
|
220
|
+
initialPageParam: 1
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region ../../contacts/ui/src/portal/hooks/groups/use-groups.ts
|
|
225
|
+
function useGroups() {
|
|
226
|
+
const api = useGroupsApi();
|
|
227
|
+
return (0, _tanstack_react_query.useQuery)({
|
|
228
|
+
queryKey: contactsKeys.groups(),
|
|
229
|
+
queryFn: () => api.listGroups(),
|
|
230
|
+
enabled: !!api,
|
|
231
|
+
select: (data) => data.groups
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/utils.ts
|
|
236
|
+
function getDisplayName(contact) {
|
|
237
|
+
if (contact.full_name && contact.full_name.trim().length > 0) return contact.full_name;
|
|
238
|
+
const parts = [contact.first_name, contact.last_name].filter(Boolean);
|
|
239
|
+
if (parts.length > 0) return parts.join(" ");
|
|
240
|
+
if (contact.email) return contact.email;
|
|
241
|
+
return "Contact";
|
|
242
|
+
}
|
|
243
|
+
function getInitials(name) {
|
|
244
|
+
const parts = name.trim().split(/\s+/);
|
|
245
|
+
return ((parts[0]?.[0] ?? "") + (parts.length > 1 ? parts[parts.length - 1]?.[0] ?? "" : "")).toUpperCase() || "?";
|
|
246
|
+
}
|
|
247
|
+
/** Lowercase relative label derived from `contact.updated_at`.
|
|
248
|
+
* Returns null if the timestamp is missing or unparseable. Lowercase on
|
|
249
|
+
* purpose so callers can compose phrases like "Last updated 3 days ago"
|
|
250
|
+
* without an awkward "Last updated Today". */
|
|
251
|
+
function getRelativeUpdated(contact) {
|
|
252
|
+
const updated = typeof contact.updated_at === "string" ? contact.updated_at : null;
|
|
253
|
+
if (!updated) return null;
|
|
254
|
+
const date = new Date(updated);
|
|
255
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
256
|
+
const diffMs = Date.now() - date.getTime();
|
|
257
|
+
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
258
|
+
if (diffDays <= 0) return "today";
|
|
259
|
+
if (diffDays === 1) return "yesterday";
|
|
260
|
+
if (diffDays < 7) return `${diffDays} days ago`;
|
|
261
|
+
if (diffDays < 30) {
|
|
262
|
+
const weeks = Math.floor(diffDays / 7);
|
|
263
|
+
return weeks === 1 ? "1 week ago" : `${weeks} weeks ago`;
|
|
264
|
+
}
|
|
265
|
+
if (diffDays < 365) {
|
|
266
|
+
const months = Math.floor(diffDays / 30);
|
|
267
|
+
return months === 1 ? "1 month ago" : `${months} months ago`;
|
|
268
|
+
}
|
|
269
|
+
const years = Math.floor(diffDays / 365);
|
|
270
|
+
return years === 1 ? "1 year ago" : `${years} years ago`;
|
|
271
|
+
}
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/ContactsSidebarRow.tsx
|
|
274
|
+
function ContactsSidebarRow({ contact, isSelected, onSelect }) {
|
|
275
|
+
const name = getDisplayName(contact);
|
|
276
|
+
const id = String(contact.id);
|
|
277
|
+
const secondary = contact.email ?? contact.status?.trim() ?? null;
|
|
278
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
279
|
+
type: "button",
|
|
280
|
+
onClick: () => onSelect(id),
|
|
281
|
+
"aria-current": isSelected ? "true" : void 0,
|
|
282
|
+
className: require_src.cn("group flex w-full items-center gap-3.5 rounded-xl px-3 py-3 text-left transition-colors", isSelected ? "bg-muted" : "hover:bg-muted/60"),
|
|
283
|
+
children: [
|
|
284
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
285
|
+
className: require_src.cn("text-muted-foreground relative flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-xl text-base font-semibold transition-colors", isSelected ? "bg-background" : "bg-muted"),
|
|
286
|
+
children: contact.avatar_url ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
287
|
+
alt: name,
|
|
288
|
+
src: contact.avatar_url,
|
|
289
|
+
className: "size-12 object-cover"
|
|
290
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
291
|
+
"aria-hidden": "true",
|
|
292
|
+
children: getInitials(name)
|
|
293
|
+
})
|
|
294
|
+
}),
|
|
295
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
296
|
+
className: "min-w-0 flex-1",
|
|
297
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
298
|
+
className: "text-foreground block truncate text-sm font-semibold tracking-tight",
|
|
299
|
+
children: name
|
|
300
|
+
}), secondary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
301
|
+
className: "text-muted-foreground mt-0.5 block truncate text-sm",
|
|
302
|
+
children: secondary
|
|
303
|
+
})]
|
|
304
|
+
}),
|
|
305
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
|
|
306
|
+
className: require_src.cn("size-4 shrink-0 transition-all", isSelected ? "text-foreground" : "text-muted-foreground/60 opacity-0 group-hover:translate-x-0.5 group-hover:opacity-100"),
|
|
307
|
+
"aria-hidden": "true"
|
|
308
|
+
})
|
|
309
|
+
]
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/ContactsSidebar.tsx
|
|
314
|
+
const DEBOUNCE_MS = 200;
|
|
315
|
+
function useBuiltInFilters() {
|
|
316
|
+
const { t } = useContactsTranslation();
|
|
317
|
+
return (0, react.useMemo)(() => [
|
|
318
|
+
{
|
|
319
|
+
id: "all",
|
|
320
|
+
label: t("filter_all"),
|
|
321
|
+
value: { kind: "all" }
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
id: "status:lead",
|
|
325
|
+
label: t("filter_leads"),
|
|
326
|
+
value: {
|
|
327
|
+
kind: "status",
|
|
328
|
+
status: "lead"
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
id: "status:customer",
|
|
333
|
+
label: t("filter_customers"),
|
|
334
|
+
value: {
|
|
335
|
+
kind: "status",
|
|
336
|
+
status: "customer"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
], [t]);
|
|
340
|
+
}
|
|
341
|
+
function filterId(value) {
|
|
342
|
+
if (value.kind === "all") return "all";
|
|
343
|
+
if (value.kind === "status") return `status:${value.status}`;
|
|
344
|
+
return `group:${value.tag}`;
|
|
345
|
+
}
|
|
346
|
+
function ContactsSidebar({ selectedContactId, onSelect, onAdd }) {
|
|
347
|
+
const { t } = useContactsTranslation();
|
|
348
|
+
const builtInFilters = useBuiltInFilters();
|
|
349
|
+
const [searchInput, setSearchInput] = (0, react.useState)("");
|
|
350
|
+
const [debouncedSearch, setDebouncedSearch] = (0, react.useState)("");
|
|
351
|
+
const [filter, setFilter] = (0, react.useState)({ kind: "all" });
|
|
352
|
+
(0, react.useEffect)(() => {
|
|
353
|
+
const handle = window.setTimeout(() => {
|
|
354
|
+
setDebouncedSearch(searchInput.trim());
|
|
355
|
+
}, DEBOUNCE_MS);
|
|
356
|
+
return () => window.clearTimeout(handle);
|
|
357
|
+
}, [searchInput]);
|
|
358
|
+
const { data: groups = [] } = useGroups();
|
|
359
|
+
const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage, isError } = useInfiniteContacts((0, react.useMemo)(() => ({
|
|
360
|
+
search_query: debouncedSearch || void 0,
|
|
361
|
+
tags: filter.kind === "group" ? [filter.tag] : void 0,
|
|
362
|
+
sort_by: "full_name",
|
|
363
|
+
sort_direction: "asc",
|
|
364
|
+
per_page: 50
|
|
365
|
+
}), [debouncedSearch, filter]));
|
|
366
|
+
const contacts = (0, react.useMemo)(() => data?.pages.flatMap((page) => page.contacts ?? []) ?? [], [data]);
|
|
367
|
+
const visibleContacts = (0, react.useMemo)(() => {
|
|
368
|
+
if (filter.kind === "status") return contacts.filter((c) => c.status === filter.status);
|
|
369
|
+
return contacts;
|
|
370
|
+
}, [contacts, filter]);
|
|
371
|
+
const sentinelRef = (0, react.useRef)(null);
|
|
372
|
+
const hasAutoSelectedRef = (0, react.useRef)(false);
|
|
373
|
+
(0, react.useEffect)(() => {
|
|
374
|
+
if (hasAutoSelectedRef.current) return;
|
|
375
|
+
if (selectedContactId) {
|
|
376
|
+
hasAutoSelectedRef.current = true;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (isLoading || visibleContacts.length === 0) return;
|
|
380
|
+
if (!(typeof window === "undefined" || window.matchMedia("(min-width: 768px)").matches)) {
|
|
381
|
+
hasAutoSelectedRef.current = true;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const first = visibleContacts[0];
|
|
385
|
+
if (first) {
|
|
386
|
+
hasAutoSelectedRef.current = true;
|
|
387
|
+
onSelect(String(first.id));
|
|
388
|
+
}
|
|
389
|
+
}, [
|
|
390
|
+
selectedContactId,
|
|
391
|
+
isLoading,
|
|
392
|
+
visibleContacts,
|
|
393
|
+
onSelect
|
|
394
|
+
]);
|
|
395
|
+
(0, react.useEffect)(() => {
|
|
396
|
+
const sentinel = sentinelRef.current;
|
|
397
|
+
if (!sentinel || !hasNextPage) return;
|
|
398
|
+
const observer = new IntersectionObserver((entries) => {
|
|
399
|
+
if (entries[0]?.isIntersecting && !isFetchingNextPage) fetchNextPage();
|
|
400
|
+
});
|
|
401
|
+
observer.observe(sentinel);
|
|
402
|
+
return () => observer.disconnect();
|
|
403
|
+
}, [
|
|
404
|
+
hasNextPage,
|
|
405
|
+
isFetchingNextPage,
|
|
406
|
+
fetchNextPage
|
|
407
|
+
]);
|
|
408
|
+
const activeFilterId = filterId(filter);
|
|
409
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
410
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
411
|
+
className: "border-border/50 border-b px-6 py-3",
|
|
412
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
413
|
+
className: "flex items-center gap-3",
|
|
414
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
415
|
+
className: "bg-muted focus-within:ring-foreground/20 flex min-w-0 flex-1 items-center gap-2 rounded-lg px-3 py-1.5 transition focus-within:ring-2",
|
|
416
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Search, {
|
|
417
|
+
className: "text-muted-foreground size-3.5 shrink-0",
|
|
418
|
+
"aria-hidden": "true"
|
|
419
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
420
|
+
value: searchInput,
|
|
421
|
+
onChange: (e) => setSearchInput(e.target.value),
|
|
422
|
+
placeholder: t("search_placeholder"),
|
|
423
|
+
type: "search",
|
|
424
|
+
className: "placeholder:text-muted-foreground/80 h-auto flex-1 border-0 bg-transparent p-0 text-xs font-medium shadow-none focus-visible:ring-0",
|
|
425
|
+
"aria-label": t("search_placeholder")
|
|
426
|
+
})]
|
|
427
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
428
|
+
type: "button",
|
|
429
|
+
onClick: onAdd,
|
|
430
|
+
className: "bg-primary text-primary-foreground hover:bg-primary/90 inline-flex shrink-0 items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold transition-colors",
|
|
431
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, {
|
|
432
|
+
className: "size-3.5",
|
|
433
|
+
"aria-hidden": "true"
|
|
434
|
+
}), t("add")]
|
|
435
|
+
})]
|
|
436
|
+
})
|
|
437
|
+
}),
|
|
438
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterPills, {
|
|
439
|
+
activeId: activeFilterId,
|
|
440
|
+
builtInFilters,
|
|
441
|
+
groups: groups.map((g) => g.name),
|
|
442
|
+
onChange: setFilter
|
|
443
|
+
}),
|
|
444
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
445
|
+
className: "flex-1 overflow-y-auto px-3 pb-8",
|
|
446
|
+
children: isError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
447
|
+
className: "text-muted-foreground px-4 py-8 text-center text-sm",
|
|
448
|
+
children: t("error_loading_list")
|
|
449
|
+
}) : isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSkeleton, {}) : visibleContacts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
450
|
+
className: "space-y-2",
|
|
451
|
+
children: [hasNextPage || isFetchingNextPage ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
452
|
+
className: "text-muted-foreground px-4 py-8 text-center text-sm",
|
|
453
|
+
children: t("searching")
|
|
454
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
455
|
+
className: "text-muted-foreground px-4 py-8 text-center text-sm",
|
|
456
|
+
children: debouncedSearch ? t("no_contacts_search", { term: debouncedSearch }) : filter.kind === "all" ? t("no_contacts_yet") : t("no_contacts_filter")
|
|
457
|
+
}), hasNextPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
458
|
+
ref: sentinelRef,
|
|
459
|
+
"aria-hidden": "true",
|
|
460
|
+
className: "h-4"
|
|
461
|
+
})]
|
|
462
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
463
|
+
className: "space-y-1",
|
|
464
|
+
children: [
|
|
465
|
+
visibleContacts.map((contact) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactsSidebarRow, {
|
|
466
|
+
contact,
|
|
467
|
+
isSelected: String(contact.id) === selectedContactId,
|
|
468
|
+
onSelect
|
|
469
|
+
}, contact.id)),
|
|
470
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
471
|
+
ref: sentinelRef,
|
|
472
|
+
"aria-hidden": "true",
|
|
473
|
+
className: "h-4"
|
|
474
|
+
}),
|
|
475
|
+
isFetchingNextPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
476
|
+
className: "text-muted-foreground py-3 text-center text-xs",
|
|
477
|
+
children: t("loading_more")
|
|
478
|
+
})
|
|
479
|
+
]
|
|
480
|
+
})
|
|
481
|
+
})
|
|
482
|
+
] });
|
|
483
|
+
}
|
|
484
|
+
function FilterPills({ activeId, builtInFilters, groups, onChange }) {
|
|
485
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
486
|
+
className: "overflow-x-auto pt-4 pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
487
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
488
|
+
className: "flex gap-1.5 px-6",
|
|
489
|
+
children: (0, react.useMemo)(() => [...builtInFilters, ...groups.map((tag) => ({
|
|
490
|
+
id: `group:${tag}`,
|
|
491
|
+
label: tag,
|
|
492
|
+
value: {
|
|
493
|
+
kind: "group",
|
|
494
|
+
tag
|
|
495
|
+
}
|
|
496
|
+
}))], [builtInFilters, groups]).map(({ id, label, value }) => {
|
|
497
|
+
const isActive = id === activeId;
|
|
498
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
499
|
+
type: "button",
|
|
500
|
+
onClick: () => onChange(value),
|
|
501
|
+
"aria-pressed": isActive,
|
|
502
|
+
className: require_src.cn("shrink-0 rounded-full px-4 py-1.5 text-sm font-semibold capitalize transition-colors", isActive ? "bg-primary text-primary-foreground" : "border-border/50 bg-muted text-foreground hover:bg-muted/70 border"),
|
|
503
|
+
children: label
|
|
504
|
+
}, id);
|
|
505
|
+
})
|
|
506
|
+
})
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
function SidebarSkeleton() {
|
|
510
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
511
|
+
className: "space-y-1.5 px-1",
|
|
512
|
+
children: [
|
|
513
|
+
0,
|
|
514
|
+
1,
|
|
515
|
+
2,
|
|
516
|
+
3,
|
|
517
|
+
4
|
|
518
|
+
].map((i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
519
|
+
className: "flex items-center gap-3 rounded-xl px-3 py-3",
|
|
520
|
+
"aria-hidden": "true",
|
|
521
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-muted size-12 shrink-0 animate-pulse rounded-xl" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
522
|
+
className: "flex-1 space-y-2",
|
|
523
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-muted h-3.5 w-3/5 animate-pulse rounded" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-muted h-3 w-4/5 animate-pulse rounded" })]
|
|
524
|
+
})]
|
|
525
|
+
}, i))
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region ../../contacts/ui/src/shared/components/contacts/contactDetailsForm.tsx
|
|
530
|
+
const DEFAULT_COUNTRIES = [];
|
|
531
|
+
const statusOptions = [
|
|
532
|
+
{
|
|
533
|
+
name: "New",
|
|
534
|
+
value: "new"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
name: "Active",
|
|
538
|
+
value: "active"
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
name: "Inactive",
|
|
542
|
+
value: "inactive"
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
name: "Cold",
|
|
546
|
+
value: "cold"
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
name: "Lead",
|
|
550
|
+
value: "lead"
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
name: "Customer",
|
|
554
|
+
value: "customer"
|
|
555
|
+
}
|
|
556
|
+
];
|
|
557
|
+
const ContactDetailsForm = ({ className, countries = DEFAULT_COUNTRIES, renderAvatarPicker }) => {
|
|
558
|
+
const { control, watch, setValue } = (0, react_hook_form.useFormContext)();
|
|
559
|
+
const currentStatus = (0, react_hook_form.useWatch)({
|
|
560
|
+
control,
|
|
561
|
+
name: "status"
|
|
562
|
+
});
|
|
563
|
+
const avatarUrl = watch("avatar_url") ?? null;
|
|
564
|
+
const effectiveStatusOptions = (0, react.useMemo)(() => {
|
|
565
|
+
if (currentStatus && typeof currentStatus === "string" && !statusOptions.some((o) => o.value === currentStatus)) return [{
|
|
566
|
+
name: currentStatus.charAt(0).toUpperCase() + currentStatus.slice(1).replace(/_/g, " "),
|
|
567
|
+
value: currentStatus
|
|
568
|
+
}, ...statusOptions];
|
|
569
|
+
return statusOptions;
|
|
570
|
+
}, [currentStatus]);
|
|
571
|
+
const initials = [watch("first_name")?.[0] ?? "", watch("last_name")?.[0] ?? ""].filter(Boolean).join("").toUpperCase() || "?";
|
|
572
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
573
|
+
className: require_src.cn("space-y-6", className),
|
|
574
|
+
children: [renderAvatarPicker && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
575
|
+
className: "flex flex-col items-center gap-3",
|
|
576
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
577
|
+
className: "border-border bg-background relative h-20 w-20 shrink-0 overflow-hidden rounded-full border-2",
|
|
578
|
+
children: avatarUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
579
|
+
src: avatarUrl,
|
|
580
|
+
alt: "",
|
|
581
|
+
className: "h-full w-full object-cover"
|
|
582
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
583
|
+
className: "text-muted-foreground flex h-full w-full items-center justify-center text-lg font-semibold",
|
|
584
|
+
children: initials
|
|
585
|
+
})
|
|
586
|
+
}), renderAvatarPicker({
|
|
587
|
+
value: avatarUrl,
|
|
588
|
+
onChange: (url) => setValue("avatar_url", url ?? "", { shouldDirty: true })
|
|
589
|
+
})]
|
|
590
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
591
|
+
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
592
|
+
children: [
|
|
593
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
594
|
+
control,
|
|
595
|
+
name: "first_name",
|
|
596
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
597
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
598
|
+
className: "font-inter text-foreground font-medium",
|
|
599
|
+
children: "First Name"
|
|
600
|
+
}),
|
|
601
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
602
|
+
placeholder: "Enter first name",
|
|
603
|
+
...field,
|
|
604
|
+
value: field.value ?? "",
|
|
605
|
+
className: "ring-input"
|
|
606
|
+
}) }),
|
|
607
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
608
|
+
] })
|
|
609
|
+
}),
|
|
610
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
611
|
+
control,
|
|
612
|
+
name: "last_name",
|
|
613
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
614
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
615
|
+
className: "font-inter text-foreground font-medium",
|
|
616
|
+
children: "Last Name"
|
|
617
|
+
}),
|
|
618
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
619
|
+
placeholder: "Enter last name",
|
|
620
|
+
...field,
|
|
621
|
+
value: field.value ?? "",
|
|
622
|
+
className: "ring-input"
|
|
623
|
+
}) }),
|
|
624
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
625
|
+
] })
|
|
626
|
+
}),
|
|
627
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
628
|
+
control,
|
|
629
|
+
name: "email",
|
|
630
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
631
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
632
|
+
className: "font-inter text-foreground font-medium",
|
|
633
|
+
children: "Email"
|
|
634
|
+
}),
|
|
635
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
636
|
+
placeholder: "Enter email address",
|
|
637
|
+
type: "email",
|
|
638
|
+
...field,
|
|
639
|
+
value: field.value ?? "",
|
|
640
|
+
className: "ring-input"
|
|
641
|
+
}) }),
|
|
642
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
643
|
+
] })
|
|
644
|
+
}),
|
|
645
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
646
|
+
control,
|
|
647
|
+
name: "phone",
|
|
648
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
649
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
650
|
+
className: "font-inter text-foreground font-medium",
|
|
651
|
+
children: "Phone"
|
|
652
|
+
}),
|
|
653
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
654
|
+
placeholder: "Enter phone number",
|
|
655
|
+
...field,
|
|
656
|
+
value: field.value ?? "",
|
|
657
|
+
className: "ring-input"
|
|
658
|
+
}) }),
|
|
659
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
660
|
+
] })
|
|
661
|
+
}),
|
|
662
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
663
|
+
control,
|
|
664
|
+
name: "status",
|
|
665
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
666
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
667
|
+
className: "font-inter text-foreground font-medium",
|
|
668
|
+
children: "Status"
|
|
669
|
+
}),
|
|
670
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.Select, {
|
|
671
|
+
value: field.value ?? "",
|
|
672
|
+
onValueChange: field.onChange,
|
|
673
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SelectTrigger, {
|
|
674
|
+
className: "w-full",
|
|
675
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SelectValue, { placeholder: "Select status" })
|
|
676
|
+
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SelectContent, {
|
|
677
|
+
position: "popper",
|
|
678
|
+
sideOffset: 4,
|
|
679
|
+
children: effectiveStatusOptions.map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SelectItem, {
|
|
680
|
+
value: opt.value,
|
|
681
|
+
children: opt.name
|
|
682
|
+
}, opt.value))
|
|
683
|
+
})]
|
|
684
|
+
}),
|
|
685
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
686
|
+
] })
|
|
687
|
+
}),
|
|
688
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
689
|
+
control,
|
|
690
|
+
name: "address",
|
|
691
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, {
|
|
692
|
+
className: "lg:col-span-2",
|
|
693
|
+
children: [
|
|
694
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
695
|
+
className: "font-inter text-foreground font-medium",
|
|
696
|
+
children: "Full Address"
|
|
697
|
+
}),
|
|
698
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
699
|
+
placeholder: "Enter street address",
|
|
700
|
+
...field,
|
|
701
|
+
value: field.value ?? "",
|
|
702
|
+
className: "ring-input"
|
|
703
|
+
}) }),
|
|
704
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
705
|
+
]
|
|
706
|
+
})
|
|
707
|
+
}),
|
|
708
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
709
|
+
control,
|
|
710
|
+
name: "city",
|
|
711
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
712
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
713
|
+
className: "font-inter text-foreground font-medium",
|
|
714
|
+
children: "City"
|
|
715
|
+
}),
|
|
716
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
717
|
+
placeholder: "Enter city",
|
|
718
|
+
...field,
|
|
719
|
+
value: field.value ?? "",
|
|
720
|
+
className: "ring-input"
|
|
721
|
+
}) }),
|
|
722
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
723
|
+
] })
|
|
724
|
+
}),
|
|
725
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
726
|
+
control,
|
|
727
|
+
name: "state",
|
|
728
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
729
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
730
|
+
className: "font-inter text-foreground font-medium",
|
|
731
|
+
children: "State/Province"
|
|
732
|
+
}),
|
|
733
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
734
|
+
placeholder: "Enter state or province",
|
|
735
|
+
...field,
|
|
736
|
+
value: field.value ?? "",
|
|
737
|
+
className: "ring-input"
|
|
738
|
+
}) }),
|
|
739
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
740
|
+
] })
|
|
741
|
+
}),
|
|
742
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
743
|
+
control,
|
|
744
|
+
name: "postal_code",
|
|
745
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
746
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
747
|
+
className: "font-inter text-foreground font-medium",
|
|
748
|
+
children: "Postal Code"
|
|
749
|
+
}),
|
|
750
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
751
|
+
placeholder: "Enter postal code",
|
|
752
|
+
...field,
|
|
753
|
+
value: field.value ?? "",
|
|
754
|
+
className: "ring-input"
|
|
755
|
+
}) }),
|
|
756
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
757
|
+
] })
|
|
758
|
+
}),
|
|
759
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormField, {
|
|
760
|
+
control,
|
|
761
|
+
name: "country_code",
|
|
762
|
+
render: ({ field }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.FormItem, { children: [
|
|
763
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormLabel, {
|
|
764
|
+
className: "font-inter text-foreground font-medium",
|
|
765
|
+
children: "Country"
|
|
766
|
+
}),
|
|
767
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.Select, {
|
|
768
|
+
value: field.value ?? "",
|
|
769
|
+
onValueChange: field.onChange,
|
|
770
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SelectTrigger, {
|
|
771
|
+
className: "w-full",
|
|
772
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SelectValue, { placeholder: "Select country" })
|
|
773
|
+
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SelectContent, {
|
|
774
|
+
position: "popper",
|
|
775
|
+
sideOffset: 4,
|
|
776
|
+
children: countries.map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SelectItem, {
|
|
777
|
+
value: opt.value,
|
|
778
|
+
children: opt.name
|
|
779
|
+
}, opt.value))
|
|
780
|
+
})]
|
|
781
|
+
}),
|
|
782
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.FormMessage, {})
|
|
783
|
+
] })
|
|
784
|
+
})
|
|
785
|
+
]
|
|
786
|
+
})]
|
|
787
|
+
});
|
|
788
|
+
};
|
|
789
|
+
//#endregion
|
|
790
|
+
//#region ../../contacts/ui/src/shared/hooks/useContactDetail.ts
|
|
791
|
+
function useContactDetail(contactId, queryKeyPrefix = "contacts") {
|
|
792
|
+
const api = useContactsCrud();
|
|
793
|
+
return (0, _tanstack_react_query.useQuery)({
|
|
794
|
+
queryKey: CONTACTS_QUERY_KEYS.detail(queryKeyPrefix, contactId),
|
|
795
|
+
queryFn: () => api.getContact(contactId),
|
|
796
|
+
enabled: !!contactId
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
//#endregion
|
|
800
|
+
//#region ../../contacts/ui/src/shared/hooks/useUpdateContactMutation.ts
|
|
801
|
+
function useUpdateContactMutation(contactId, queryKeyPrefix = "contacts", options) {
|
|
802
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
803
|
+
const api = useContactsCrud();
|
|
804
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
805
|
+
mutationFn: ({ id, data }) => api.updateContact(id, data),
|
|
806
|
+
onSuccess: () => {
|
|
807
|
+
require_src.fluidToast({
|
|
808
|
+
title: "Contact updated successfully",
|
|
809
|
+
type: "success"
|
|
810
|
+
});
|
|
811
|
+
queryClient.invalidateQueries({ queryKey: CONTACTS_QUERY_KEYS.all(queryKeyPrefix) });
|
|
812
|
+
options?.onSuccess?.();
|
|
813
|
+
},
|
|
814
|
+
onError: (error) => {
|
|
815
|
+
require_src.fluidToast({
|
|
816
|
+
title: "Failed to save contact",
|
|
817
|
+
type: "error",
|
|
818
|
+
description: parseApiErrors(error)
|
|
819
|
+
});
|
|
820
|
+
options?.onError?.(error);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
//#endregion
|
|
825
|
+
//#region ../../contacts/ui/src/shared/hooks/useDeleteContactMutation.ts
|
|
826
|
+
function useDeleteContactMutation(queryKeyPrefix = "contacts", options) {
|
|
827
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
828
|
+
const api = useContactsCrud();
|
|
829
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
830
|
+
mutationFn: (contactId) => api.deleteContact(contactId),
|
|
831
|
+
onSuccess: () => {
|
|
832
|
+
require_src.fluidToast({
|
|
833
|
+
title: "Contact deleted successfully",
|
|
834
|
+
type: "success"
|
|
835
|
+
});
|
|
836
|
+
queryClient.invalidateQueries({ queryKey: CONTACTS_QUERY_KEYS.all(queryKeyPrefix) });
|
|
837
|
+
options?.onSuccess?.();
|
|
838
|
+
},
|
|
839
|
+
onError: (error) => {
|
|
840
|
+
require_src.fluidToast({
|
|
841
|
+
title: "Failed to delete contact",
|
|
842
|
+
type: "error",
|
|
843
|
+
description: parseApiErrors(error)
|
|
844
|
+
});
|
|
845
|
+
options?.onError?.(error);
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
//#endregion
|
|
850
|
+
//#region ../../contacts/ui/src/shared/schemas/contactFormSchema.ts
|
|
851
|
+
/**
|
|
852
|
+
* Form schema for creating a contact.
|
|
853
|
+
* Matches CompanyContactCreate / ContactCreate from the OpenAPI spec.
|
|
854
|
+
*
|
|
855
|
+
* @see CompanyContactCreate in company_contacts.d.ts
|
|
856
|
+
* @see ContactCreate in users_contacts.d.ts
|
|
857
|
+
*/
|
|
858
|
+
const createContactFormSchema = zod.z.object({
|
|
859
|
+
first_name: zod.z.string().min(1, { message: "First name is required" }),
|
|
860
|
+
last_name: zod.z.string().min(1, { message: "Last name is required" }),
|
|
861
|
+
status: zod.z.string().nullable().optional(),
|
|
862
|
+
email: zod.z.string().email().or(zod.z.literal("")).nullable().optional(),
|
|
863
|
+
phone: zod.z.string().nullable().optional(),
|
|
864
|
+
address: zod.z.string().nullable().optional(),
|
|
865
|
+
city: zod.z.string().nullable().optional(),
|
|
866
|
+
state: zod.z.string().nullable().optional(),
|
|
867
|
+
postal_code: zod.z.string().nullable().optional(),
|
|
868
|
+
country_code: zod.z.coerce.string().nullable().optional(),
|
|
869
|
+
language_code: zod.z.coerce.string().nullable().optional(),
|
|
870
|
+
affiliate: zod.z.record(zod.z.string(), zod.z.unknown()).nullable().optional(),
|
|
871
|
+
metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
872
|
+
});
|
|
873
|
+
/**
|
|
874
|
+
* Form schema for editing a contact.
|
|
875
|
+
* Same fields as create plus id. Uses .passthrough() so extra fields
|
|
876
|
+
* from the Contact read model (full_name, avatar_url, etc.) don't
|
|
877
|
+
* cause validation failures.
|
|
878
|
+
*
|
|
879
|
+
* @see CompanyContactUpdate in company_contacts.d.ts
|
|
880
|
+
*/
|
|
881
|
+
const editContactFormSchema = createContactFormSchema.passthrough();
|
|
882
|
+
//#endregion
|
|
883
|
+
//#region ../../contacts/ui/src/shared/hooks/useContactDetailPage.ts
|
|
884
|
+
const mutableKeys = Object.keys(createContactFormSchema.shape);
|
|
885
|
+
function useContactDetailPage(contactId, options) {
|
|
886
|
+
const queryKeyPrefix = options?.queryKeyPrefix ?? "contacts";
|
|
887
|
+
const { data, isLoading } = useContactDetail(contactId, queryKeyPrefix);
|
|
888
|
+
const { data: countries } = (0, _tanstack_react_query.useQuery)({
|
|
889
|
+
queryKey: ["countries"],
|
|
890
|
+
queryFn: options?.getCountries ?? (() => Promise.resolve([])),
|
|
891
|
+
enabled: !!options?.getCountries
|
|
892
|
+
});
|
|
893
|
+
const countryOptions = (0, react.useMemo)(() => [...countries?.map((c) => ({
|
|
894
|
+
name: c.name,
|
|
895
|
+
value: c.iso ?? c.id.toString()
|
|
896
|
+
})) ?? []].sort((a, b) => a.name.localeCompare(b.name)), [countries]);
|
|
897
|
+
const contact = data?.contact;
|
|
898
|
+
const methods = require_src.useZodForm(editContactFormSchema, {
|
|
899
|
+
values: (0, react.useMemo)(() => {
|
|
900
|
+
if (!contact) return void 0;
|
|
901
|
+
return {
|
|
902
|
+
...contact,
|
|
903
|
+
country_code: contact.country?.iso ?? contact.country_id?.toString() ?? null
|
|
904
|
+
};
|
|
905
|
+
}, [contact]),
|
|
906
|
+
mode: "onBlur"
|
|
907
|
+
});
|
|
908
|
+
const updateMutation = useUpdateContactMutation(contactId, queryKeyPrefix, { onSuccess: () => {
|
|
909
|
+
methods.reset(methods.getValues());
|
|
910
|
+
options?.onSaveSuccess?.();
|
|
911
|
+
} });
|
|
912
|
+
const deleteMutation = useDeleteContactMutation(queryKeyPrefix, { onSuccess: () => {
|
|
913
|
+
options?.onDeleteSuccess?.();
|
|
914
|
+
} });
|
|
915
|
+
const onSave = (0, react.useCallback)(() => {
|
|
916
|
+
methods.handleSubmit((formData) => {
|
|
917
|
+
const payload = {};
|
|
918
|
+
for (const key of mutableKeys) if (key in formData) payload[key] = formData[key];
|
|
919
|
+
updateMutation.mutate({
|
|
920
|
+
id: contactId,
|
|
921
|
+
data: payload
|
|
922
|
+
});
|
|
923
|
+
}, (errors) => {
|
|
924
|
+
require_src.fluidToast({
|
|
925
|
+
title: "Please fix the form errors before saving",
|
|
926
|
+
description: Object.entries(errors).map(([field, err]) => {
|
|
927
|
+
const msg = typeof err?.message === "string" ? err.message : "invalid";
|
|
928
|
+
return `${field.replace(/_/g, " ")}: ${msg}`;
|
|
929
|
+
}).join(", ") || void 0,
|
|
930
|
+
type: "error"
|
|
931
|
+
});
|
|
932
|
+
})();
|
|
933
|
+
}, [
|
|
934
|
+
methods,
|
|
935
|
+
updateMutation,
|
|
936
|
+
contactId
|
|
937
|
+
]);
|
|
938
|
+
const onDelete = (0, react.useCallback)(() => {
|
|
939
|
+
deleteMutation.mutate(contactId);
|
|
940
|
+
}, [deleteMutation, contactId]);
|
|
941
|
+
return {
|
|
942
|
+
contact,
|
|
943
|
+
isLoading,
|
|
944
|
+
methods,
|
|
945
|
+
countryOptions,
|
|
946
|
+
isDirty: methods.formState.isDirty,
|
|
947
|
+
isSubmitting: updateMutation.isPending,
|
|
948
|
+
isDeleting: deleteMutation.isPending,
|
|
949
|
+
onSave,
|
|
950
|
+
onDelete
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
//#endregion
|
|
954
|
+
//#region ../../contacts/ui/src/portal/hooks/contacts/use-contact-tasks.ts
|
|
955
|
+
function useContactTasks(contactId) {
|
|
956
|
+
const api = useTasksApi();
|
|
957
|
+
return (0, _tanstack_react_query.useQuery)({
|
|
958
|
+
queryKey: contactsKeys.tasks(contactId),
|
|
959
|
+
queryFn: () => api.listTasks(contactId),
|
|
960
|
+
enabled: !!contactId,
|
|
961
|
+
select: (data) => data.tasks
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
//#endregion
|
|
965
|
+
//#region ../../contacts/ui/src/portal/hooks/notes/use-contact-notes.ts
|
|
966
|
+
function useContactNotes(contactId) {
|
|
967
|
+
const api = useNotesApi();
|
|
968
|
+
return (0, _tanstack_react_query.useQuery)({
|
|
969
|
+
queryKey: contactsKeys.notes(contactId),
|
|
970
|
+
queryFn: () => api.listNotes(contactId),
|
|
971
|
+
enabled: !!contactId,
|
|
972
|
+
select: (data) => data.notes
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
//#endregion
|
|
976
|
+
//#region ../../contacts/ui/src/shared/components/contacts/statusBadge.tsx
|
|
977
|
+
const statusStyles = {
|
|
978
|
+
new: "border-[var(--status-new-border)] bg-[var(--status-new)] text-[var(--status-new-foreground)]",
|
|
979
|
+
active: "border-[var(--status-active-border)] bg-[var(--status-active)] text-[var(--status-active-foreground)]",
|
|
980
|
+
inactive: "border-border bg-muted text-muted-foreground",
|
|
981
|
+
lead: "border-[var(--status-lead-border)] bg-[var(--status-lead)] text-[var(--status-lead-foreground)]",
|
|
982
|
+
customer: "border-[var(--status-customer-border)] bg-[var(--status-customer)] text-[var(--status-customer-foreground)]",
|
|
983
|
+
success: "border-[var(--badge-success-border)] bg-[var(--badge-success)] text-[var(--badge-success-foreground)]",
|
|
984
|
+
warning: "border-[var(--badge-warning-border)] bg-[var(--badge-warning)] text-[var(--badge-warning-foreground)]",
|
|
985
|
+
danger: "border-[var(--badge-danger-border)] bg-[var(--badge-danger)] text-[var(--badge-danger-foreground)]",
|
|
986
|
+
info: "border-[var(--badge-info-border)] bg-[var(--badge-info)] text-[var(--badge-info-foreground)]",
|
|
987
|
+
neutral: "border-border bg-muted text-muted-foreground",
|
|
988
|
+
notice: "border-[var(--badge-notice-border)] bg-[var(--badge-notice)] text-[var(--badge-notice-foreground)]",
|
|
989
|
+
accent: "border-[var(--badge-accent-border)] bg-[var(--badge-accent)] text-[var(--badge-accent-foreground)]"
|
|
990
|
+
};
|
|
991
|
+
const defaultStyle = "border-border bg-muted text-muted-foreground";
|
|
992
|
+
function getStatusStyle(status) {
|
|
993
|
+
return statusStyles[status] ?? defaultStyle;
|
|
994
|
+
}
|
|
995
|
+
function StatusBadge({ status, label, className }) {
|
|
996
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
997
|
+
className: require_src.cn("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold capitalize", getStatusStyle(status), className),
|
|
998
|
+
children: label ?? status
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
//#endregion
|
|
1002
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/ContactDetailHero.tsx
|
|
1003
|
+
function ContactDetailHero({ contact, tasksCount, notesCount }) {
|
|
1004
|
+
const { t } = useContactsTranslation();
|
|
1005
|
+
const name = getDisplayName(contact);
|
|
1006
|
+
const status = contact.status?.trim();
|
|
1007
|
+
const lastUpdated = getRelativeUpdated(contact);
|
|
1008
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1009
|
+
className: "flex flex-col gap-6 md:flex-row md:items-center md:justify-between md:gap-8",
|
|
1010
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1011
|
+
className: "flex flex-col items-start gap-4 md:flex-row md:items-center md:gap-5",
|
|
1012
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1013
|
+
className: "bg-muted relative flex size-20 shrink-0 items-center justify-center overflow-hidden rounded-2xl md:size-24",
|
|
1014
|
+
children: contact.avatar_url ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
1015
|
+
alt: name,
|
|
1016
|
+
src: contact.avatar_url,
|
|
1017
|
+
className: "size-full object-cover"
|
|
1018
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1019
|
+
"aria-hidden": "true",
|
|
1020
|
+
className: "text-muted-foreground text-2xl font-bold tracking-tight md:text-3xl",
|
|
1021
|
+
children: getInitials(name)
|
|
1022
|
+
})
|
|
1023
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1024
|
+
className: "min-w-0 flex-1",
|
|
1025
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
1026
|
+
className: "text-foreground text-2xl leading-tight font-bold tracking-tight md:text-3xl",
|
|
1027
|
+
children: name
|
|
1028
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1029
|
+
className: "mt-2.5 flex flex-wrap items-center gap-2",
|
|
1030
|
+
children: [
|
|
1031
|
+
status && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusBadge, { status }),
|
|
1032
|
+
status && lastUpdated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1033
|
+
"aria-hidden": "true",
|
|
1034
|
+
className: "text-muted-foreground/40 text-sm",
|
|
1035
|
+
children: "·"
|
|
1036
|
+
}),
|
|
1037
|
+
lastUpdated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1038
|
+
className: "text-muted-foreground text-sm",
|
|
1039
|
+
children: t("last_updated", { date: lastUpdated })
|
|
1040
|
+
})
|
|
1041
|
+
]
|
|
1042
|
+
})]
|
|
1043
|
+
})]
|
|
1044
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1045
|
+
className: "flex shrink-0 items-center gap-6 md:gap-8",
|
|
1046
|
+
children: [
|
|
1047
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
1048
|
+
label: t("tab_tasks"),
|
|
1049
|
+
value: tasksCount,
|
|
1050
|
+
icon: lucide_react.ListTodo
|
|
1051
|
+
}),
|
|
1052
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1053
|
+
className: "bg-border h-10 w-px",
|
|
1054
|
+
"aria-hidden": "true"
|
|
1055
|
+
}),
|
|
1056
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
1057
|
+
label: t("tab_notes"),
|
|
1058
|
+
value: notesCount,
|
|
1059
|
+
icon: lucide_react.StickyNote
|
|
1060
|
+
})
|
|
1061
|
+
]
|
|
1062
|
+
})]
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
function Stat({ label, value, icon: Icon }) {
|
|
1066
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1067
|
+
className: "flex flex-col items-start gap-1",
|
|
1068
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1069
|
+
className: "text-muted-foreground flex items-center gap-1.5",
|
|
1070
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
1071
|
+
className: "size-3.5",
|
|
1072
|
+
"aria-hidden": true
|
|
1073
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1074
|
+
className: "text-xs font-medium",
|
|
1075
|
+
children: label
|
|
1076
|
+
})]
|
|
1077
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1078
|
+
className: "text-foreground text-2xl font-bold tracking-tight tabular-nums",
|
|
1079
|
+
children: value
|
|
1080
|
+
})]
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
//#endregion
|
|
1084
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/ContactInfoRow.tsx
|
|
1085
|
+
function buildAddressLine(contact) {
|
|
1086
|
+
const parts = [
|
|
1087
|
+
contact.address,
|
|
1088
|
+
contact.city,
|
|
1089
|
+
contact.state,
|
|
1090
|
+
contact.postal_code
|
|
1091
|
+
].filter((part) => Boolean(part?.trim()));
|
|
1092
|
+
if (parts.length === 0) return null;
|
|
1093
|
+
return parts.join(", ");
|
|
1094
|
+
}
|
|
1095
|
+
function ContactInfoRow({ contact, onEditEmpty }) {
|
|
1096
|
+
const { t } = useContactsTranslation();
|
|
1097
|
+
const address = buildAddressLine(contact);
|
|
1098
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1099
|
+
className: "border-border/50 divide-border/50 mt-6 divide-y overflow-hidden rounded-2xl border",
|
|
1100
|
+
children: [
|
|
1101
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
|
|
1102
|
+
icon: lucide_react.Mail,
|
|
1103
|
+
label: t("label_email"),
|
|
1104
|
+
value: contact.email,
|
|
1105
|
+
href: contact.email ? `mailto:${contact.email}` : void 0,
|
|
1106
|
+
onEditEmpty
|
|
1107
|
+
}),
|
|
1108
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
|
|
1109
|
+
icon: lucide_react.Phone,
|
|
1110
|
+
label: t("label_phone"),
|
|
1111
|
+
value: contact.phone,
|
|
1112
|
+
href: contact.phone ? `tel:${contact.phone}` : void 0,
|
|
1113
|
+
onEditEmpty
|
|
1114
|
+
}),
|
|
1115
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
|
|
1116
|
+
icon: lucide_react.MapPin,
|
|
1117
|
+
label: t("label_address"),
|
|
1118
|
+
value: address,
|
|
1119
|
+
href: address ? `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(address)}` : void 0,
|
|
1120
|
+
external: true,
|
|
1121
|
+
onEditEmpty
|
|
1122
|
+
})
|
|
1123
|
+
]
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
function InfoRow({ icon: Icon, label, value, href, external = false, onEditEmpty }) {
|
|
1127
|
+
const { t } = useContactsTranslation();
|
|
1128
|
+
const trimmed = value?.trim();
|
|
1129
|
+
const isEmpty = !trimmed;
|
|
1130
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
1131
|
+
const handleCopy = () => {
|
|
1132
|
+
if (!trimmed) return;
|
|
1133
|
+
if (typeof navigator === "undefined" || !navigator.clipboard) return;
|
|
1134
|
+
navigator.clipboard.writeText(trimmed).then(() => {
|
|
1135
|
+
setCopied(true);
|
|
1136
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
1137
|
+
}).catch(() => {});
|
|
1138
|
+
};
|
|
1139
|
+
if (isEmpty) {
|
|
1140
|
+
const addText = t("add_field", { field: label });
|
|
1141
|
+
if (!onEditEmpty) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1142
|
+
className: "flex items-center gap-3 px-4 py-2.5 md:px-5",
|
|
1143
|
+
children: [
|
|
1144
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1145
|
+
className: "border-border/50 text-muted-foreground/50 flex size-9 shrink-0 items-center justify-center rounded-full border-2 border-dashed",
|
|
1146
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
1147
|
+
className: "size-4",
|
|
1148
|
+
"aria-hidden": true
|
|
1149
|
+
})
|
|
1150
|
+
}),
|
|
1151
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1152
|
+
className: "min-w-0 flex-1",
|
|
1153
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1154
|
+
className: "text-muted-foreground text-xs font-medium",
|
|
1155
|
+
children: label
|
|
1156
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1157
|
+
className: "text-muted-foreground/70 truncate text-sm italic",
|
|
1158
|
+
children: addText
|
|
1159
|
+
})]
|
|
1160
|
+
}),
|
|
1161
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, {
|
|
1162
|
+
className: "text-muted-foreground/40 size-4 shrink-0",
|
|
1163
|
+
"aria-hidden": true
|
|
1164
|
+
})
|
|
1165
|
+
]
|
|
1166
|
+
});
|
|
1167
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1168
|
+
type: "button",
|
|
1169
|
+
onClick: onEditEmpty,
|
|
1170
|
+
"aria-label": addText,
|
|
1171
|
+
className: "hover:bg-muted/60 group flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors md:px-5",
|
|
1172
|
+
children: [
|
|
1173
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1174
|
+
className: "border-border/50 text-muted-foreground/50 group-hover:border-foreground/30 group-hover:text-muted-foreground flex size-9 shrink-0 items-center justify-center rounded-full border-2 border-dashed transition-colors",
|
|
1175
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
1176
|
+
className: "size-4",
|
|
1177
|
+
"aria-hidden": true
|
|
1178
|
+
})
|
|
1179
|
+
}),
|
|
1180
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1181
|
+
className: "min-w-0 flex-1",
|
|
1182
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1183
|
+
className: "text-muted-foreground text-xs font-medium",
|
|
1184
|
+
children: label
|
|
1185
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1186
|
+
className: "text-muted-foreground/70 group-hover:text-muted-foreground truncate text-sm italic transition-colors",
|
|
1187
|
+
children: addText
|
|
1188
|
+
})]
|
|
1189
|
+
}),
|
|
1190
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, {
|
|
1191
|
+
className: "text-muted-foreground/40 group-hover:text-foreground size-4 shrink-0 transition-colors",
|
|
1192
|
+
"aria-hidden": true
|
|
1193
|
+
})
|
|
1194
|
+
]
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1198
|
+
className: "hover:bg-muted/60 group flex items-center transition-colors",
|
|
1199
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
1200
|
+
href,
|
|
1201
|
+
...external ? {
|
|
1202
|
+
target: "_blank",
|
|
1203
|
+
rel: "noopener noreferrer"
|
|
1204
|
+
} : {},
|
|
1205
|
+
className: "flex min-w-0 flex-1 items-center gap-3 px-4 py-2.5 md:px-5",
|
|
1206
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1207
|
+
className: "bg-muted text-muted-foreground group-hover:bg-muted/70 group-hover:text-foreground flex size-9 shrink-0 items-center justify-center rounded-full transition-colors",
|
|
1208
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
1209
|
+
className: "size-4",
|
|
1210
|
+
"aria-hidden": true
|
|
1211
|
+
})
|
|
1212
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1213
|
+
className: "min-w-0 flex-1",
|
|
1214
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1215
|
+
className: "text-muted-foreground text-xs font-medium",
|
|
1216
|
+
children: label
|
|
1217
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1218
|
+
className: "text-foreground truncate text-sm font-semibold",
|
|
1219
|
+
children: trimmed
|
|
1220
|
+
})]
|
|
1221
|
+
})]
|
|
1222
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1223
|
+
type: "button",
|
|
1224
|
+
onClick: handleCopy,
|
|
1225
|
+
"aria-label": copied ? t("copied_field", { field: label }) : t("copy_field", { field: label }),
|
|
1226
|
+
className: "text-muted-foreground hover:bg-muted hover:text-foreground mr-3 flex size-8 shrink-0 items-center justify-center rounded-md transition-colors",
|
|
1227
|
+
children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {
|
|
1228
|
+
className: "text-primary size-3.5",
|
|
1229
|
+
"aria-hidden": true
|
|
1230
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Copy, {
|
|
1231
|
+
className: "size-3.5",
|
|
1232
|
+
"aria-hidden": true
|
|
1233
|
+
})
|
|
1234
|
+
})]
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
//#endregion
|
|
1238
|
+
//#region ../../contacts/core/src/iso-date.ts
|
|
1239
|
+
/**
|
|
1240
|
+
* Format a date as YYYY-MM-DD in the renderer's local timezone, optionally
|
|
1241
|
+
* shifted by `offsetDays`. Use this for due-date inputs where "today" must
|
|
1242
|
+
* resolve to the user's local calendar date — never `toISOString().slice(0, 10)`,
|
|
1243
|
+
* which returns the UTC date and silently rolls over a day for users east/west
|
|
1244
|
+
* of UTC at the wrong hours.
|
|
1245
|
+
*/
|
|
1246
|
+
function isoDate(offsetDays, now = /* @__PURE__ */ new Date()) {
|
|
1247
|
+
const d = new Date(now);
|
|
1248
|
+
d.setDate(d.getDate() + offsetDays);
|
|
1249
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Return a Date pinned to local midnight of the calendar day represented by
|
|
1253
|
+
* `input`. Use this whenever you need to compare two due dates by calendar day
|
|
1254
|
+
* (overdue / today / tomorrow / future).
|
|
1255
|
+
*
|
|
1256
|
+
* Why not just `new Date(input)`? `new Date("YYYY-MM-DD")` parses the string
|
|
1257
|
+
* as UTC midnight, which lands on the *previous* calendar day in any UTC−
|
|
1258
|
+
* timezone — a task due "May 1" then reads as April 30 for a user in the
|
|
1259
|
+
* Americas, classifying it as "Overdue" all day. Parse the date components
|
|
1260
|
+
* directly so the calendar-day intent of the string is preserved.
|
|
1261
|
+
*/
|
|
1262
|
+
function startOfLocalDay(input) {
|
|
1263
|
+
if (typeof input === "string") {
|
|
1264
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(input);
|
|
1265
|
+
if (match?.[1] && match[2] && match[3]) return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
|
1266
|
+
}
|
|
1267
|
+
const d = typeof input === "string" ? new Date(input) : input;
|
|
1268
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
1269
|
+
}
|
|
1270
|
+
//#endregion
|
|
1271
|
+
//#region ../../contacts/ui/src/portal/hooks/contacts/use-toggle-task-completion.ts
|
|
1272
|
+
function useToggleTaskCompletion(contactId) {
|
|
1273
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
1274
|
+
const api = useTasksApi();
|
|
1275
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
1276
|
+
mutationFn: ({ taskId, isCompleted }) => api.updateTask(taskId, contactId, { completed_at: isCompleted ? null : (/* @__PURE__ */ new Date()).toISOString() }),
|
|
1277
|
+
onSuccess: () => {
|
|
1278
|
+
queryClient.invalidateQueries({ queryKey: contactsKeys.tasks(contactId) });
|
|
1279
|
+
},
|
|
1280
|
+
onError: (error) => {
|
|
1281
|
+
require_src.fluidToast({
|
|
1282
|
+
title: "Failed to update task",
|
|
1283
|
+
type: "error",
|
|
1284
|
+
description: parseApiErrors(error)
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
//#endregion
|
|
1290
|
+
//#region ../../contacts/ui/src/portal/hooks/contacts/use-delete-contact-task.ts
|
|
1291
|
+
function useDeleteContactTask(contactId, options) {
|
|
1292
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
1293
|
+
const api = useTasksApi();
|
|
1294
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
1295
|
+
mutationFn: (taskId) => api.deleteTask(taskId, contactId),
|
|
1296
|
+
onSuccess: () => {
|
|
1297
|
+
require_src.fluidToast({
|
|
1298
|
+
title: "Task deleted",
|
|
1299
|
+
type: "success"
|
|
1300
|
+
});
|
|
1301
|
+
queryClient.invalidateQueries({ queryKey: contactsKeys.tasks(contactId) });
|
|
1302
|
+
options?.onSuccess?.();
|
|
1303
|
+
},
|
|
1304
|
+
onError: (error) => {
|
|
1305
|
+
require_src.fluidToast({
|
|
1306
|
+
title: "Failed to delete task",
|
|
1307
|
+
type: "error",
|
|
1308
|
+
description: parseApiErrors(error)
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
//#endregion
|
|
1314
|
+
//#region ../../contacts/ui/src/portal/hooks/contacts/use-update-contact-task.ts
|
|
1315
|
+
function useUpdateContactTask(contactId, options) {
|
|
1316
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
1317
|
+
const api = useTasksApi();
|
|
1318
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
1319
|
+
mutationFn: ({ taskId, input }) => api.updateTask(taskId, contactId, input),
|
|
1320
|
+
onSuccess: () => {
|
|
1321
|
+
require_src.fluidToast({
|
|
1322
|
+
title: "Task updated",
|
|
1323
|
+
type: "success"
|
|
1324
|
+
});
|
|
1325
|
+
queryClient.invalidateQueries({ queryKey: contactsKeys.tasks(contactId) });
|
|
1326
|
+
options?.onSuccess?.();
|
|
1327
|
+
},
|
|
1328
|
+
onError: (error) => {
|
|
1329
|
+
require_src.fluidToast({
|
|
1330
|
+
title: "Failed to update task",
|
|
1331
|
+
type: "error",
|
|
1332
|
+
description: parseApiErrors(error)
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
//#endregion
|
|
1338
|
+
//#region ../../contacts/ui/src/portal/utils/format-date.ts
|
|
1339
|
+
function formatDateForDisplay(dateStr) {
|
|
1340
|
+
return new Date(dateStr).toLocaleDateString("en-US", {
|
|
1341
|
+
month: "short",
|
|
1342
|
+
day: "numeric",
|
|
1343
|
+
year: "numeric"
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
//#endregion
|
|
1347
|
+
//#region ../../contacts/ui/src/portal/components/editor/note-task-editor.tsx
|
|
1348
|
+
function extractTitleAndBody(editor) {
|
|
1349
|
+
if (!editor) return {
|
|
1350
|
+
title: "",
|
|
1351
|
+
body: ""
|
|
1352
|
+
};
|
|
1353
|
+
const doc = editor.state.doc;
|
|
1354
|
+
let title = "";
|
|
1355
|
+
const bodyParts = [];
|
|
1356
|
+
doc.forEach((node) => {
|
|
1357
|
+
if (node.type.name === "heading" && !title) title = node.textContent.trim();
|
|
1358
|
+
else {
|
|
1359
|
+
const text = node.textContent.trim();
|
|
1360
|
+
if (text) bodyParts.push(text);
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
return {
|
|
1364
|
+
title,
|
|
1365
|
+
body: bodyParts.join("\n")
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
function extractBodyOnly(editor) {
|
|
1369
|
+
if (!editor) return "";
|
|
1370
|
+
const parts = [];
|
|
1371
|
+
editor.state.doc.forEach((node) => {
|
|
1372
|
+
const text = node.textContent.trim();
|
|
1373
|
+
if (text) parts.push(text);
|
|
1374
|
+
});
|
|
1375
|
+
return parts.join("\n");
|
|
1376
|
+
}
|
|
1377
|
+
function NoteTaskEditor({ titlePlaceholder = "New Note", bodyPlaceholder = "Start writing...", onChange, className, initialTitle, initialBody, showDueDate = false, initialDueDate, showTitle = true, editorClassName }) {
|
|
1378
|
+
const [dueDate, setDueDate] = (0, react.useState)(initialDueDate);
|
|
1379
|
+
const dueDateRef = (0, react.useRef)(initialDueDate);
|
|
1380
|
+
const dateInputRef = (0, react.useRef)(null);
|
|
1381
|
+
(0, react.useEffect)(() => {
|
|
1382
|
+
dueDateRef.current = dueDate;
|
|
1383
|
+
}, [dueDate]);
|
|
1384
|
+
const initialContent = showTitle ? initialTitle || initialBody ? `<h1>${initialTitle ?? ""}</h1>${initialBody ?? ""}` : void 0 : initialBody || void 0;
|
|
1385
|
+
const editor = require_dist.useEditor({
|
|
1386
|
+
extensions: [
|
|
1387
|
+
require_dist.StarterKit.configure({
|
|
1388
|
+
heading: showTitle ? false : { levels: [] },
|
|
1389
|
+
bulletList: {
|
|
1390
|
+
keepMarks: true,
|
|
1391
|
+
keepAttributes: false
|
|
1392
|
+
},
|
|
1393
|
+
orderedList: {
|
|
1394
|
+
keepMarks: true,
|
|
1395
|
+
keepAttributes: false
|
|
1396
|
+
}
|
|
1397
|
+
}),
|
|
1398
|
+
...showTitle ? [require_dist.Heading.configure({ levels: [1] })] : [],
|
|
1399
|
+
require_dist.Placeholder.configure({ placeholder: ({ node }) => {
|
|
1400
|
+
if (node.type.name === "heading") return titlePlaceholder;
|
|
1401
|
+
return bodyPlaceholder;
|
|
1402
|
+
} }),
|
|
1403
|
+
require_dist$1.TextAlign.configure({
|
|
1404
|
+
types: showTitle ? ["heading", "paragraph"] : ["paragraph"],
|
|
1405
|
+
alignments: [
|
|
1406
|
+
"left",
|
|
1407
|
+
"center",
|
|
1408
|
+
"right",
|
|
1409
|
+
"justify"
|
|
1410
|
+
]
|
|
1411
|
+
}),
|
|
1412
|
+
require_dist$1.Underline
|
|
1413
|
+
],
|
|
1414
|
+
content: initialContent ?? (showTitle ? {
|
|
1415
|
+
type: "doc",
|
|
1416
|
+
content: [{
|
|
1417
|
+
type: "heading",
|
|
1418
|
+
attrs: { level: 1 }
|
|
1419
|
+
}]
|
|
1420
|
+
} : void 0),
|
|
1421
|
+
onUpdate: ({ editor }) => {
|
|
1422
|
+
if (showTitle) {
|
|
1423
|
+
const { title, body } = extractTitleAndBody(editor);
|
|
1424
|
+
onChange?.({
|
|
1425
|
+
title,
|
|
1426
|
+
body,
|
|
1427
|
+
dueDate: dueDateRef.current
|
|
1428
|
+
});
|
|
1429
|
+
} else {
|
|
1430
|
+
const body = extractBodyOnly(editor);
|
|
1431
|
+
onChange?.({
|
|
1432
|
+
title: "",
|
|
1433
|
+
body,
|
|
1434
|
+
dueDate: dueDateRef.current
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
const fireOnChange = () => {
|
|
1440
|
+
if (!editor || !onChange) return;
|
|
1441
|
+
if (showTitle) {
|
|
1442
|
+
const { title, body } = extractTitleAndBody(editor);
|
|
1443
|
+
onChange({
|
|
1444
|
+
title,
|
|
1445
|
+
body,
|
|
1446
|
+
dueDate
|
|
1447
|
+
});
|
|
1448
|
+
} else onChange({
|
|
1449
|
+
title: "",
|
|
1450
|
+
body: extractBodyOnly(editor),
|
|
1451
|
+
dueDate
|
|
1452
|
+
});
|
|
1453
|
+
};
|
|
1454
|
+
(0, react.useEffect)(() => {
|
|
1455
|
+
fireOnChange();
|
|
1456
|
+
}, [editor]);
|
|
1457
|
+
(0, react.useEffect)(() => {
|
|
1458
|
+
fireOnChange();
|
|
1459
|
+
}, [dueDate]);
|
|
1460
|
+
const buttonBase = "flex h-7 w-7 items-center justify-center rounded text-xs transition-colors";
|
|
1461
|
+
const buttonActive = "bg-muted text-primary";
|
|
1462
|
+
const buttonInactive = "text-muted-foreground hover:bg-muted/50";
|
|
1463
|
+
const toolbarSeparator = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-border mx-1 h-5 w-px" });
|
|
1464
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1465
|
+
className: require_src.cn("border-border/50 flex flex-col overflow-hidden rounded-lg border", className),
|
|
1466
|
+
children: [
|
|
1467
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1468
|
+
className: "border-border/50 bg-muted/50 flex items-center gap-0.5 border-b px-2 py-1.5",
|
|
1469
|
+
children: [
|
|
1470
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1471
|
+
type: "button",
|
|
1472
|
+
onClick: () => editor?.chain().focus().toggleBold().run(),
|
|
1473
|
+
disabled: !editor?.can().chain().focus().toggleBold().run(),
|
|
1474
|
+
className: require_src.cn(buttonBase, "font-bold", editor?.isActive("bold") ? buttonActive : buttonInactive),
|
|
1475
|
+
title: "Bold",
|
|
1476
|
+
children: "B"
|
|
1477
|
+
}),
|
|
1478
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1479
|
+
type: "button",
|
|
1480
|
+
onClick: () => editor?.chain().focus().toggleItalic().run(),
|
|
1481
|
+
disabled: !editor?.can().chain().focus().toggleItalic().run(),
|
|
1482
|
+
className: require_src.cn(buttonBase, "italic", editor?.isActive("italic") ? buttonActive : buttonInactive),
|
|
1483
|
+
title: "Italic",
|
|
1484
|
+
children: "I"
|
|
1485
|
+
}),
|
|
1486
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1487
|
+
type: "button",
|
|
1488
|
+
onClick: () => editor?.chain().focus().toggleUnderline().run(),
|
|
1489
|
+
disabled: !editor?.can().chain().focus().toggleUnderline().run(),
|
|
1490
|
+
className: require_src.cn(buttonBase, "underline", editor?.isActive("underline") ? buttonActive : buttonInactive),
|
|
1491
|
+
title: "Underline",
|
|
1492
|
+
children: "U"
|
|
1493
|
+
}),
|
|
1494
|
+
toolbarSeparator,
|
|
1495
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1496
|
+
type: "button",
|
|
1497
|
+
onClick: () => editor?.chain().focus().setTextAlign("left").run(),
|
|
1498
|
+
className: require_src.cn(buttonBase, editor?.isActive({ textAlign: "left" }) ? buttonActive : buttonInactive),
|
|
1499
|
+
title: "Align left",
|
|
1500
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignLeft, { className: "h-3.5 w-3.5" })
|
|
1501
|
+
}),
|
|
1502
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1503
|
+
type: "button",
|
|
1504
|
+
onClick: () => editor?.chain().focus().setTextAlign("center").run(),
|
|
1505
|
+
className: require_src.cn(buttonBase, editor?.isActive({ textAlign: "center" }) ? buttonActive : buttonInactive),
|
|
1506
|
+
title: "Align center",
|
|
1507
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignCenter, { className: "h-3.5 w-3.5" })
|
|
1508
|
+
}),
|
|
1509
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1510
|
+
type: "button",
|
|
1511
|
+
onClick: () => editor?.chain().focus().setTextAlign("right").run(),
|
|
1512
|
+
className: require_src.cn(buttonBase, editor?.isActive({ textAlign: "right" }) ? buttonActive : buttonInactive),
|
|
1513
|
+
title: "Align right",
|
|
1514
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignRight, { className: "h-3.5 w-3.5" })
|
|
1515
|
+
}),
|
|
1516
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1517
|
+
type: "button",
|
|
1518
|
+
onClick: () => editor?.chain().focus().setTextAlign("justify").run(),
|
|
1519
|
+
className: require_src.cn(buttonBase, editor?.isActive({ textAlign: "justify" }) ? buttonActive : buttonInactive),
|
|
1520
|
+
title: "Justify",
|
|
1521
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignJustify, { className: "h-3.5 w-3.5" })
|
|
1522
|
+
}),
|
|
1523
|
+
toolbarSeparator,
|
|
1524
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1525
|
+
type: "button",
|
|
1526
|
+
onClick: () => editor?.chain().focus().toggleBulletList().run(),
|
|
1527
|
+
className: require_src.cn(buttonBase, editor?.isActive("bulletList") ? buttonActive : buttonInactive),
|
|
1528
|
+
title: "Bullet list",
|
|
1529
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.List, { className: "h-3.5 w-3.5" })
|
|
1530
|
+
}),
|
|
1531
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1532
|
+
type: "button",
|
|
1533
|
+
onClick: () => editor?.chain().focus().toggleOrderedList().run(),
|
|
1534
|
+
className: require_src.cn(buttonBase, editor?.isActive("orderedList") ? buttonActive : buttonInactive),
|
|
1535
|
+
title: "Ordered list",
|
|
1536
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ListOrdered, { className: "h-3.5 w-3.5" })
|
|
1537
|
+
}),
|
|
1538
|
+
showDueDate && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [toolbarSeparator, dueDate ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1539
|
+
className: "bg-primary/10 flex items-center gap-1 rounded px-2 py-1",
|
|
1540
|
+
children: [
|
|
1541
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Calendar, { className: "text-primary h-3.5 w-3.5" }),
|
|
1542
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1543
|
+
className: "text-primary text-xs font-medium",
|
|
1544
|
+
children: formatDateForDisplay(dueDate)
|
|
1545
|
+
}),
|
|
1546
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1547
|
+
type: "button",
|
|
1548
|
+
onClick: () => setDueDate(void 0),
|
|
1549
|
+
className: "text-primary/60 hover:text-primary ml-1",
|
|
1550
|
+
title: "Remove due date",
|
|
1551
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "h-3 w-3" })
|
|
1552
|
+
})
|
|
1553
|
+
]
|
|
1554
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1555
|
+
type: "button",
|
|
1556
|
+
onClick: () => dateInputRef.current?.showPicker(),
|
|
1557
|
+
className: "text-muted-foreground hover:bg-muted hover:text-foreground flex h-7 cursor-pointer items-center gap-1 rounded px-2 text-xs font-medium transition-colors",
|
|
1558
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Calendar, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Due date" })]
|
|
1559
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1560
|
+
ref: dateInputRef,
|
|
1561
|
+
type: "date",
|
|
1562
|
+
className: "invisible absolute h-0 w-0",
|
|
1563
|
+
onChange: (e) => {
|
|
1564
|
+
if (e.target.value) setDueDate((/* @__PURE__ */ new Date(e.target.value + "T00:00:00")).toISOString());
|
|
1565
|
+
}
|
|
1566
|
+
})] })] })
|
|
1567
|
+
]
|
|
1568
|
+
}),
|
|
1569
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dist.EditorContent, {
|
|
1570
|
+
editor,
|
|
1571
|
+
className: editorClassName ?? "min-h-[60vh] flex-1 overflow-y-auto p-4"
|
|
1572
|
+
}),
|
|
1573
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
|
|
1574
|
+
.ProseMirror {
|
|
1575
|
+
outline: none;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
.ProseMirror h1 {
|
|
1579
|
+
font-size: 1.5rem;
|
|
1580
|
+
font-weight: 600;
|
|
1581
|
+
line-height: 1.3;
|
|
1582
|
+
margin-bottom: 0.5rem;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
.ProseMirror h1.is-empty::before,
|
|
1586
|
+
.ProseMirror p.is-empty::before {
|
|
1587
|
+
color: var(--color-muted-foreground);
|
|
1588
|
+
content: attr(data-placeholder);
|
|
1589
|
+
float: left;
|
|
1590
|
+
height: 0;
|
|
1591
|
+
pointer-events: none;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
.ProseMirror ul {
|
|
1595
|
+
list-style-type: disc;
|
|
1596
|
+
padding-left: 1.5rem;
|
|
1597
|
+
margin: 0.5rem 0;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
.ProseMirror ol {
|
|
1601
|
+
list-style-type: decimal;
|
|
1602
|
+
padding-left: 1.5rem;
|
|
1603
|
+
margin: 0.5rem 0;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
.ProseMirror li {
|
|
1607
|
+
margin: 0.25rem 0;
|
|
1608
|
+
display: list-item;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
.ProseMirror li > p {
|
|
1612
|
+
margin: 0;
|
|
1613
|
+
}
|
|
1614
|
+
` })
|
|
1615
|
+
]
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
//#endregion
|
|
1619
|
+
//#region ../../contacts/ui/src/portal/components/editor/note-task-modal.tsx
|
|
1620
|
+
function NoteTaskModal({ open, onOpenChange, mode, type, initialTitle = "", initialBody = "", initialDueDate, showDueDate = false, onSave, isPending = false, isCompleted, onToggleComplete, isTogglePending = false }) {
|
|
1621
|
+
const [title, setTitle] = (0, react.useState)(initialTitle);
|
|
1622
|
+
const [body, setBody] = (0, react.useState)(initialBody);
|
|
1623
|
+
const dateInputRef = (0, react.useRef)(null);
|
|
1624
|
+
const [dueDate, setDueDate] = (0, react.useState)(initialDueDate);
|
|
1625
|
+
const typeLabel = type === "note" ? "Note" : "Task";
|
|
1626
|
+
const sheetTitle = mode === "edit" ? `Edit ${typeLabel}` : `New ${typeLabel}`;
|
|
1627
|
+
const requiresBody = type === "note";
|
|
1628
|
+
const canSave = !!title.trim() && (!requiresBody || !!body.trim()) && !isPending;
|
|
1629
|
+
const handleSave = () => {
|
|
1630
|
+
if (!canSave) return;
|
|
1631
|
+
onSave({
|
|
1632
|
+
title: title.trim(),
|
|
1633
|
+
body,
|
|
1634
|
+
dueDate
|
|
1635
|
+
});
|
|
1636
|
+
};
|
|
1637
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Sheet, {
|
|
1638
|
+
open,
|
|
1639
|
+
onOpenChange,
|
|
1640
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.SheetContent, {
|
|
1641
|
+
className: "flex w-full flex-col sm:max-w-lg",
|
|
1642
|
+
children: [
|
|
1643
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SheetHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.SheetTitle, { children: sheetTitle }) }),
|
|
1644
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1645
|
+
className: "flex min-h-0 flex-1 flex-col gap-3",
|
|
1646
|
+
children: [
|
|
1647
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Input, {
|
|
1648
|
+
placeholder: `${typeLabel} title`,
|
|
1649
|
+
value: title,
|
|
1650
|
+
onChange: (e) => setTitle(e.target.value),
|
|
1651
|
+
autoFocus: true
|
|
1652
|
+
}),
|
|
1653
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NoteTaskEditor, {
|
|
1654
|
+
showTitle: false,
|
|
1655
|
+
bodyPlaceholder: "Start writing...",
|
|
1656
|
+
initialBody,
|
|
1657
|
+
editorClassName: "min-h-[200px] flex-1 overflow-y-auto p-4",
|
|
1658
|
+
onChange: (content) => {
|
|
1659
|
+
setBody(content.body);
|
|
1660
|
+
}
|
|
1661
|
+
}),
|
|
1662
|
+
showDueDate && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1663
|
+
className: "flex items-center gap-2",
|
|
1664
|
+
children: dueDate ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1665
|
+
className: "bg-primary/10 inline-flex items-center gap-1.5 rounded-md px-2.5 py-1.5",
|
|
1666
|
+
children: [
|
|
1667
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Calendar, { className: "text-primary h-3.5 w-3.5" }),
|
|
1668
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1669
|
+
className: "text-primary text-sm font-medium",
|
|
1670
|
+
children: ["Due ", formatDateForDisplay(dueDate)]
|
|
1671
|
+
}),
|
|
1672
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1673
|
+
type: "button",
|
|
1674
|
+
onClick: () => setDueDate(void 0),
|
|
1675
|
+
className: "text-primary/60 hover:text-primary ml-1",
|
|
1676
|
+
title: "Remove due date",
|
|
1677
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "h-3 w-3" })
|
|
1678
|
+
})
|
|
1679
|
+
]
|
|
1680
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1681
|
+
className: "relative",
|
|
1682
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1683
|
+
type: "button",
|
|
1684
|
+
onClick: () => dateInputRef.current?.showPicker(),
|
|
1685
|
+
className: "text-muted-foreground hover:text-foreground hover:bg-muted inline-flex cursor-pointer items-center gap-1.5 rounded-md px-2.5 py-1.5 text-sm font-medium transition-colors",
|
|
1686
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Calendar, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Add due date" })]
|
|
1687
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1688
|
+
ref: dateInputRef,
|
|
1689
|
+
type: "date",
|
|
1690
|
+
className: "invisible absolute h-0 w-0",
|
|
1691
|
+
onChange: (e) => {
|
|
1692
|
+
if (e.target.value) setDueDate((/* @__PURE__ */ new Date(e.target.value + "T00:00:00")).toISOString());
|
|
1693
|
+
}
|
|
1694
|
+
})]
|
|
1695
|
+
})
|
|
1696
|
+
})
|
|
1697
|
+
]
|
|
1698
|
+
}),
|
|
1699
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.SheetFooter, {
|
|
1700
|
+
className: "flex-row justify-end gap-2 border-t pt-4",
|
|
1701
|
+
children: [
|
|
1702
|
+
mode === "edit" && onToggleComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
1703
|
+
variant: "outline",
|
|
1704
|
+
onClick: onToggleComplete,
|
|
1705
|
+
disabled: isTogglePending,
|
|
1706
|
+
className: "mr-auto",
|
|
1707
|
+
children: isCompleted ? "Mark Incomplete" : "Mark Complete"
|
|
1708
|
+
}),
|
|
1709
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
1710
|
+
variant: "outline",
|
|
1711
|
+
onClick: () => onOpenChange(false),
|
|
1712
|
+
children: "Cancel"
|
|
1713
|
+
}),
|
|
1714
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
1715
|
+
onClick: handleSave,
|
|
1716
|
+
disabled: !canSave,
|
|
1717
|
+
children: isPending ? "Saving..." : "Save"
|
|
1718
|
+
})
|
|
1719
|
+
]
|
|
1720
|
+
})
|
|
1721
|
+
]
|
|
1722
|
+
})
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
//#endregion
|
|
1726
|
+
//#region ../../contacts/ui/src/portal/components/tasks/task-list.tsx
|
|
1727
|
+
function formatDueDate$1(dateStr) {
|
|
1728
|
+
return new Date(dateStr).toLocaleDateString("en-US", {
|
|
1729
|
+
month: "short",
|
|
1730
|
+
day: "numeric",
|
|
1731
|
+
year: "numeric"
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
function formatRelativeTime(dateStr, t) {
|
|
1735
|
+
const date = new Date(dateStr);
|
|
1736
|
+
const diffMs = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
|
|
1737
|
+
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
1738
|
+
if (diffDays === 0) return date.toLocaleTimeString("en-US", {
|
|
1739
|
+
hour: "numeric",
|
|
1740
|
+
minute: "2-digit"
|
|
1741
|
+
});
|
|
1742
|
+
if (diffDays === 1) return t("task_yesterday");
|
|
1743
|
+
return diffDays === 1 ? t("task_days_ago_one") : t("task_days_ago_other", { count: diffDays });
|
|
1744
|
+
}
|
|
1745
|
+
function classifyDue(dueAt, isCompleted, t) {
|
|
1746
|
+
if (isCompleted) return {
|
|
1747
|
+
label: t("task_due", { date: formatDueDate$1(dueAt) }),
|
|
1748
|
+
tone: "done"
|
|
1749
|
+
};
|
|
1750
|
+
const due = startOfLocalDay(dueAt);
|
|
1751
|
+
const today = startOfLocalDay(/* @__PURE__ */ new Date());
|
|
1752
|
+
const tomorrow = new Date(today);
|
|
1753
|
+
tomorrow.setDate(today.getDate() + 1);
|
|
1754
|
+
const dueTime = due.getTime();
|
|
1755
|
+
if (dueTime < today.getTime()) return {
|
|
1756
|
+
label: t("task_overdue_due", { date: formatDueDate$1(dueAt) }),
|
|
1757
|
+
tone: "overdue"
|
|
1758
|
+
};
|
|
1759
|
+
if (dueTime === today.getTime()) return {
|
|
1760
|
+
label: t("quick_today"),
|
|
1761
|
+
tone: "today"
|
|
1762
|
+
};
|
|
1763
|
+
if (dueTime === tomorrow.getTime()) return {
|
|
1764
|
+
label: t("quick_tomorrow"),
|
|
1765
|
+
tone: "tomorrow"
|
|
1766
|
+
};
|
|
1767
|
+
return {
|
|
1768
|
+
label: formatDueDate$1(dueAt),
|
|
1769
|
+
tone: "future"
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
const DUE_TONE_CLASS = {
|
|
1773
|
+
overdue: "bg-destructive/10 text-destructive",
|
|
1774
|
+
today: "bg-primary/10 text-primary",
|
|
1775
|
+
tomorrow: "bg-primary/10 text-primary",
|
|
1776
|
+
future: "bg-muted text-muted-foreground",
|
|
1777
|
+
done: "bg-muted text-muted-foreground"
|
|
1778
|
+
};
|
|
1779
|
+
function TaskCard({ task, toggleCompletion, onEdit, onDeleteClick }) {
|
|
1780
|
+
const { t } = useContactsTranslation();
|
|
1781
|
+
const isCompleted = !!task.completed_at;
|
|
1782
|
+
const { title, body: bodyText } = require_parse_task_body.parseTaskBody(task.body ?? "");
|
|
1783
|
+
const timestamp = task.created_at ? formatRelativeTime(task.created_at, t) : "";
|
|
1784
|
+
const due = task.due_at ? classifyDue(task.due_at, isCompleted, t) : null;
|
|
1785
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1786
|
+
role: "button",
|
|
1787
|
+
tabIndex: 0,
|
|
1788
|
+
onClick: () => onEdit(task),
|
|
1789
|
+
onKeyDown: (e) => {
|
|
1790
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1791
|
+
e.preventDefault();
|
|
1792
|
+
onEdit(task);
|
|
1793
|
+
}
|
|
1794
|
+
},
|
|
1795
|
+
className: "group border-border/50 hover:border-foreground/20 hover:bg-muted/40 relative cursor-pointer rounded-xl border p-4 transition-all hover:shadow-sm",
|
|
1796
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1797
|
+
className: "flex items-start gap-3",
|
|
1798
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1799
|
+
type: "button",
|
|
1800
|
+
onClick: (e) => {
|
|
1801
|
+
e.stopPropagation();
|
|
1802
|
+
toggleCompletion.mutate({
|
|
1803
|
+
taskId: task.id,
|
|
1804
|
+
isCompleted
|
|
1805
|
+
});
|
|
1806
|
+
},
|
|
1807
|
+
disabled: toggleCompletion.isPending,
|
|
1808
|
+
className: "mt-0.5 shrink-0 transition-transform hover:scale-110 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1809
|
+
"aria-label": isCompleted ? t("mark_as_open") : t("mark_as_completed"),
|
|
1810
|
+
children: isCompleted ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CircleCheck, {
|
|
1811
|
+
className: "text-primary size-5",
|
|
1812
|
+
"aria-hidden": "true"
|
|
1813
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "border-muted-foreground/40 group-hover:border-primary size-5 rounded-full border-2 transition-colors" })
|
|
1814
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1815
|
+
className: "min-w-0 flex-1",
|
|
1816
|
+
children: [
|
|
1817
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
1818
|
+
className: require_src.cn("text-foreground line-clamp-2 text-sm leading-snug font-semibold", isCompleted && "text-muted-foreground line-through"),
|
|
1819
|
+
children: title
|
|
1820
|
+
}),
|
|
1821
|
+
bodyText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1822
|
+
className: require_src.cn("text-muted-foreground mt-1 line-clamp-2 text-xs leading-relaxed", isCompleted && "line-through"),
|
|
1823
|
+
children: bodyText
|
|
1824
|
+
}),
|
|
1825
|
+
(due || timestamp) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1826
|
+
className: "mt-2.5 flex flex-wrap items-center gap-1.5",
|
|
1827
|
+
children: [due && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1828
|
+
className: require_src.cn("inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium", DUE_TONE_CLASS[due.tone]),
|
|
1829
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Calendar, {
|
|
1830
|
+
className: "size-3",
|
|
1831
|
+
"aria-hidden": "true"
|
|
1832
|
+
}), due.label]
|
|
1833
|
+
}), timestamp && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1834
|
+
className: "text-muted-foreground/60 text-xs",
|
|
1835
|
+
children: timestamp
|
|
1836
|
+
})]
|
|
1837
|
+
})
|
|
1838
|
+
]
|
|
1839
|
+
})]
|
|
1840
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1841
|
+
className: "absolute top-3 right-3 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100",
|
|
1842
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuTrigger, {
|
|
1843
|
+
asChild: true,
|
|
1844
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
1845
|
+
variant: "ghost",
|
|
1846
|
+
size: "icon-xs",
|
|
1847
|
+
onClick: (e) => e.stopPropagation(),
|
|
1848
|
+
"aria-label": t("task_actions"),
|
|
1849
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.EllipsisVertical, { className: "size-4" })
|
|
1850
|
+
})
|
|
1851
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuContent, {
|
|
1852
|
+
align: "end",
|
|
1853
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuItem, {
|
|
1854
|
+
className: "text-destructive",
|
|
1855
|
+
onClick: (e) => {
|
|
1856
|
+
e.stopPropagation();
|
|
1857
|
+
onDeleteClick(task);
|
|
1858
|
+
},
|
|
1859
|
+
children: t("delete")
|
|
1860
|
+
})
|
|
1861
|
+
})] })
|
|
1862
|
+
})]
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
function TaskList({ tasks, isLoading, contactId }) {
|
|
1866
|
+
const { t } = useContactsTranslation();
|
|
1867
|
+
const [taskToDelete, setTaskToDelete] = (0, react.useState)(null);
|
|
1868
|
+
const [editingTask, setEditingTask] = (0, react.useState)(null);
|
|
1869
|
+
const toggleCompletion = useToggleTaskCompletion(contactId);
|
|
1870
|
+
const deleteTask = useDeleteContactTask(contactId, { onSuccess: () => setTaskToDelete(null) });
|
|
1871
|
+
const updateTask = useUpdateContactTask(contactId, { onSuccess: () => setEditingTask(null) });
|
|
1872
|
+
const handleSave = (data) => {
|
|
1873
|
+
if (!editingTask) return;
|
|
1874
|
+
const taskBody = data.body ? `${data.title}\n\n${data.body}` : data.title;
|
|
1875
|
+
updateTask.mutate({
|
|
1876
|
+
taskId: editingTask.id,
|
|
1877
|
+
input: {
|
|
1878
|
+
body: taskBody,
|
|
1879
|
+
due_at: data.dueDate ?? null
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
};
|
|
1883
|
+
const editingParsed = editingTask ? require_parse_task_body.parseTaskBody(editingTask.body ?? "") : null;
|
|
1884
|
+
const handleModalOpenChange = (open) => {
|
|
1885
|
+
if (!open) setEditingTask(null);
|
|
1886
|
+
};
|
|
1887
|
+
if (isLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1888
|
+
className: "flex flex-col gap-3",
|
|
1889
|
+
children: [
|
|
1890
|
+
1,
|
|
1891
|
+
2,
|
|
1892
|
+
3
|
|
1893
|
+
].map((i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "border-border/50 bg-muted/50 h-32 animate-pulse rounded-xl border" }, i))
|
|
1894
|
+
});
|
|
1895
|
+
if (tasks.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
1896
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1897
|
+
className: "space-y-4",
|
|
1898
|
+
children: [
|
|
1899
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1900
|
+
className: "flex flex-col gap-3",
|
|
1901
|
+
children: tasks.map((task) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskCard, {
|
|
1902
|
+
task,
|
|
1903
|
+
toggleCompletion,
|
|
1904
|
+
onEdit: setEditingTask,
|
|
1905
|
+
onDeleteClick: setTaskToDelete
|
|
1906
|
+
}, task.id))
|
|
1907
|
+
}),
|
|
1908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NoteTaskModal, {
|
|
1909
|
+
open: editingTask !== null,
|
|
1910
|
+
onOpenChange: handleModalOpenChange,
|
|
1911
|
+
mode: "edit",
|
|
1912
|
+
type: "task",
|
|
1913
|
+
initialTitle: editingParsed?.title ?? "",
|
|
1914
|
+
initialBody: editingParsed?.body ?? "",
|
|
1915
|
+
initialDueDate: editingTask?.due_at ?? void 0,
|
|
1916
|
+
showDueDate: true,
|
|
1917
|
+
onSave: handleSave,
|
|
1918
|
+
isPending: updateTask.isPending,
|
|
1919
|
+
isCompleted: editingTask ? !!editingTask.completed_at : void 0,
|
|
1920
|
+
onToggleComplete: editingTask ? () => toggleCompletion.mutate({
|
|
1921
|
+
taskId: editingTask.id,
|
|
1922
|
+
isCompleted: !!editingTask.completed_at
|
|
1923
|
+
}, { onSuccess: () => setEditingTask(null) }) : void 0,
|
|
1924
|
+
isTogglePending: toggleCompletion.isPending
|
|
1925
|
+
}, editingTask?.id ?? "edit"),
|
|
1926
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialog, {
|
|
1927
|
+
open: taskToDelete !== null,
|
|
1928
|
+
onOpenChange: (open) => {
|
|
1929
|
+
if (!open) setTaskToDelete(null);
|
|
1930
|
+
},
|
|
1931
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogTitle, { children: t("delete_task_title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogDescription, { children: t("delete_task_confirm") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogFooter, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogCancel, { children: t("cancel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogAction, {
|
|
1932
|
+
variant: "destructive",
|
|
1933
|
+
onClick: () => {
|
|
1934
|
+
if (taskToDelete) deleteTask.mutate(taskToDelete.id);
|
|
1935
|
+
},
|
|
1936
|
+
children: t("delete")
|
|
1937
|
+
})] })] })
|
|
1938
|
+
})
|
|
1939
|
+
]
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
//#endregion
|
|
1943
|
+
//#region ../../contacts/ui/src/portal/hooks/contacts/use-create-contact-task.ts
|
|
1944
|
+
function useCreateContactTask(contactId, options) {
|
|
1945
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
1946
|
+
const api = useTasksApi();
|
|
1947
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
1948
|
+
mutationFn: (input) => api.createTask(contactId, input),
|
|
1949
|
+
onSuccess: () => {
|
|
1950
|
+
require_src.fluidToast({
|
|
1951
|
+
title: "Task created",
|
|
1952
|
+
type: "success"
|
|
1953
|
+
});
|
|
1954
|
+
queryClient.invalidateQueries({ queryKey: contactsKeys.tasks(contactId) });
|
|
1955
|
+
options?.onSuccess?.();
|
|
1956
|
+
},
|
|
1957
|
+
onError: (error) => {
|
|
1958
|
+
require_src.fluidToast({
|
|
1959
|
+
title: "Failed to create task",
|
|
1960
|
+
type: "error",
|
|
1961
|
+
description: parseApiErrors(error)
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
//#endregion
|
|
1967
|
+
//#region ../../contacts/ui/src/portal/components/tasks/inline-task-composer.tsx
|
|
1968
|
+
const QUICK_DATES = [
|
|
1969
|
+
{
|
|
1970
|
+
key: "today",
|
|
1971
|
+
offsetDays: 0
|
|
1972
|
+
},
|
|
1973
|
+
{
|
|
1974
|
+
key: "tomorrow",
|
|
1975
|
+
offsetDays: 1
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
key: "next_week",
|
|
1979
|
+
offsetDays: 7
|
|
1980
|
+
}
|
|
1981
|
+
];
|
|
1982
|
+
function InlineTaskComposer({ contactId }) {
|
|
1983
|
+
const { t } = useContactsTranslation();
|
|
1984
|
+
const [isOpen, setIsOpen] = (0, react.useState)(false);
|
|
1985
|
+
const [body, setBody] = (0, react.useState)("");
|
|
1986
|
+
const [dueDate, setDueDate] = (0, react.useState)(null);
|
|
1987
|
+
const inputRef = (0, react.useRef)(null);
|
|
1988
|
+
const createTask = useCreateContactTask(contactId, { onSuccess: () => {
|
|
1989
|
+
setBody("");
|
|
1990
|
+
setDueDate(null);
|
|
1991
|
+
setIsOpen(false);
|
|
1992
|
+
} });
|
|
1993
|
+
(0, react.useEffect)(() => {
|
|
1994
|
+
if (isOpen) inputRef.current?.focus();
|
|
1995
|
+
}, [isOpen]);
|
|
1996
|
+
const quickDates = (0, react.useMemo)(() => QUICK_DATES.map((q) => ({
|
|
1997
|
+
key: q.key,
|
|
1998
|
+
iso: isoDate(q.offsetDays)
|
|
1999
|
+
})), []);
|
|
2000
|
+
const quickLabels = {
|
|
2001
|
+
today: t("quick_today"),
|
|
2002
|
+
tomorrow: t("quick_tomorrow"),
|
|
2003
|
+
next_week: t("quick_next_week")
|
|
2004
|
+
};
|
|
2005
|
+
const close = () => {
|
|
2006
|
+
setIsOpen(false);
|
|
2007
|
+
setBody("");
|
|
2008
|
+
setDueDate(null);
|
|
2009
|
+
};
|
|
2010
|
+
const submit = () => {
|
|
2011
|
+
const trimmed = body.trim();
|
|
2012
|
+
if (!trimmed || createTask.isPending) return;
|
|
2013
|
+
createTask.mutate({
|
|
2014
|
+
body: trimmed,
|
|
2015
|
+
due_at: dueDate
|
|
2016
|
+
});
|
|
2017
|
+
};
|
|
2018
|
+
const canSubmit = body.trim().length > 0 && !createTask.isPending;
|
|
2019
|
+
if (!isOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2020
|
+
type: "button",
|
|
2021
|
+
onClick: () => setIsOpen(true),
|
|
2022
|
+
className: "border-border/50 text-muted-foreground hover:border-foreground/30 hover:bg-muted/40 hover:text-foreground flex w-full items-center gap-3 rounded-xl border border-dashed px-5 py-4 text-sm font-medium transition-colors",
|
|
2023
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, {
|
|
2024
|
+
className: "size-4",
|
|
2025
|
+
"aria-hidden": "true"
|
|
2026
|
+
}), t("new_task")]
|
|
2027
|
+
});
|
|
2028
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2029
|
+
className: "border-border/50 focus-within:border-foreground/30 rounded-xl border p-3 transition-colors",
|
|
2030
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2031
|
+
className: "flex items-start gap-3",
|
|
2032
|
+
children: [
|
|
2033
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2034
|
+
className: "border-muted-foreground/50 mt-1.5 size-5 shrink-0 rounded-full border-2",
|
|
2035
|
+
"aria-hidden": "true"
|
|
2036
|
+
}),
|
|
2037
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2038
|
+
ref: inputRef,
|
|
2039
|
+
value: body,
|
|
2040
|
+
onChange: (e) => setBody(e.target.value),
|
|
2041
|
+
onKeyDown: (e) => {
|
|
2042
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
2043
|
+
e.preventDefault();
|
|
2044
|
+
submit();
|
|
2045
|
+
} else if (e.key === "Escape") {
|
|
2046
|
+
e.preventDefault();
|
|
2047
|
+
close();
|
|
2048
|
+
}
|
|
2049
|
+
},
|
|
2050
|
+
placeholder: t("task_describe_placeholder"),
|
|
2051
|
+
"aria-label": t("task_description_aria"),
|
|
2052
|
+
className: "placeholder:text-muted-foreground/80 text-foreground flex-1 border-0 bg-transparent text-sm font-medium outline-none"
|
|
2053
|
+
}),
|
|
2054
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2055
|
+
type: "button",
|
|
2056
|
+
onClick: close,
|
|
2057
|
+
"aria-label": t("task_discard_aria"),
|
|
2058
|
+
className: "text-muted-foreground hover:bg-muted hover:text-foreground -mt-0.5 -mr-0.5 flex size-7 shrink-0 items-center justify-center rounded-md transition-colors",
|
|
2059
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "size-4" })
|
|
2060
|
+
})
|
|
2061
|
+
]
|
|
2062
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2063
|
+
className: "mt-3 flex items-center gap-1.5 pl-8",
|
|
2064
|
+
children: [
|
|
2065
|
+
quickDates.map((q) => {
|
|
2066
|
+
const isActive = dueDate === q.iso;
|
|
2067
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2068
|
+
type: "button",
|
|
2069
|
+
onClick: () => setDueDate(isActive ? null : q.iso),
|
|
2070
|
+
"aria-pressed": isActive,
|
|
2071
|
+
className: require_src.cn("shrink-0 rounded-full px-3 py-1 text-xs font-medium transition-colors", isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/70"),
|
|
2072
|
+
children: quickLabels[q.key]
|
|
2073
|
+
}, q.key);
|
|
2074
|
+
}),
|
|
2075
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "ml-auto" }),
|
|
2076
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2077
|
+
type: "button",
|
|
2078
|
+
onClick: submit,
|
|
2079
|
+
disabled: !canSubmit,
|
|
2080
|
+
className: "bg-primary text-primary-foreground hover:bg-primary/90 disabled:bg-muted disabled:text-muted-foreground inline-flex shrink-0 items-center gap-1.5 rounded-full px-4 py-1.5 text-xs font-semibold transition-colors disabled:cursor-not-allowed",
|
|
2081
|
+
children: createTask.isPending ? t("adding") : t("add_task")
|
|
2082
|
+
})
|
|
2083
|
+
]
|
|
2084
|
+
})]
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
//#endregion
|
|
2088
|
+
//#region ../../contacts/ui/src/portal/hooks/notes/use-delete-contact-note.ts
|
|
2089
|
+
function useDeleteContactNote(contactId, options) {
|
|
2090
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
2091
|
+
const api = useNotesApi();
|
|
2092
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
2093
|
+
mutationFn: (noteId) => api.deleteNote(noteId, contactId),
|
|
2094
|
+
onSuccess: () => {
|
|
2095
|
+
require_src.fluidToast({
|
|
2096
|
+
title: "Note deleted",
|
|
2097
|
+
type: "success"
|
|
2098
|
+
});
|
|
2099
|
+
queryClient.invalidateQueries({ queryKey: contactsKeys.notes(contactId) });
|
|
2100
|
+
options?.onSuccess?.();
|
|
2101
|
+
},
|
|
2102
|
+
onError: (error) => {
|
|
2103
|
+
require_src.fluidToast({
|
|
2104
|
+
title: "Failed to delete note",
|
|
2105
|
+
type: "error",
|
|
2106
|
+
description: parseApiErrors(error)
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
//#endregion
|
|
2112
|
+
//#region ../../contacts/ui/src/portal/hooks/notes/use-update-contact-note.ts
|
|
2113
|
+
function useUpdateContactNote(contactId, options) {
|
|
2114
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
2115
|
+
const api = useNotesApi();
|
|
2116
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
2117
|
+
mutationFn: ({ noteId, input }) => api.updateNote(noteId, contactId, input),
|
|
2118
|
+
onSuccess: () => {
|
|
2119
|
+
require_src.fluidToast({
|
|
2120
|
+
title: "Note updated",
|
|
2121
|
+
type: "success"
|
|
2122
|
+
});
|
|
2123
|
+
queryClient.invalidateQueries({ queryKey: contactsKeys.notes(contactId) });
|
|
2124
|
+
options?.onSuccess?.();
|
|
2125
|
+
},
|
|
2126
|
+
onError: (error) => {
|
|
2127
|
+
require_src.fluidToast({
|
|
2128
|
+
title: "Failed to update note",
|
|
2129
|
+
type: "error",
|
|
2130
|
+
description: parseApiErrors(error)
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
//#endregion
|
|
2136
|
+
//#region ../../contacts/ui/src/portal/hooks/notes/use-create-contact-note.ts
|
|
2137
|
+
function useCreateContactNote(contactId, options) {
|
|
2138
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
2139
|
+
const api = useNotesApi();
|
|
2140
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
2141
|
+
mutationFn: (input) => api.createNote(contactId, input),
|
|
2142
|
+
onSuccess: () => {
|
|
2143
|
+
require_src.fluidToast({
|
|
2144
|
+
title: "Note created",
|
|
2145
|
+
type: "success"
|
|
2146
|
+
});
|
|
2147
|
+
queryClient.invalidateQueries({ queryKey: contactsKeys.notes(contactId) });
|
|
2148
|
+
options?.onSuccess?.();
|
|
2149
|
+
},
|
|
2150
|
+
onError: (error) => {
|
|
2151
|
+
require_src.fluidToast({
|
|
2152
|
+
title: "Failed to create note",
|
|
2153
|
+
type: "error",
|
|
2154
|
+
description: parseApiErrors(error)
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
//#endregion
|
|
2160
|
+
//#region ../../contacts/ui/src/portal/components/notes/inline-note-composer.tsx
|
|
2161
|
+
function detectMacPlatform() {
|
|
2162
|
+
if (typeof navigator === "undefined") return false;
|
|
2163
|
+
const platform = navigator.userAgentData?.platform ?? navigator.userAgent ?? "";
|
|
2164
|
+
return /Mac|iPhone|iPad/i.test(platform);
|
|
2165
|
+
}
|
|
2166
|
+
function InlineNoteComposer({ contactId }) {
|
|
2167
|
+
const { t } = useContactsTranslation();
|
|
2168
|
+
const [body, setBody] = (0, react.useState)("");
|
|
2169
|
+
const [isFocused, setIsFocused] = (0, react.useState)(false);
|
|
2170
|
+
const [isMac, setIsMac] = (0, react.useState)(false);
|
|
2171
|
+
const textareaRef = (0, react.useRef)(null);
|
|
2172
|
+
(0, react.useEffect)(() => {
|
|
2173
|
+
setIsMac(detectMacPlatform());
|
|
2174
|
+
}, []);
|
|
2175
|
+
const createNote = useCreateContactNote(contactId, { onSuccess: () => {
|
|
2176
|
+
setBody("");
|
|
2177
|
+
textareaRef.current?.blur();
|
|
2178
|
+
} });
|
|
2179
|
+
const submit = () => {
|
|
2180
|
+
const trimmed = body.trim();
|
|
2181
|
+
if (!trimmed || createNote.isPending) return;
|
|
2182
|
+
const firstLine = trimmed.split("\n")[0]?.slice(0, 120) ?? "Note";
|
|
2183
|
+
createNote.mutate({
|
|
2184
|
+
title: firstLine,
|
|
2185
|
+
body: trimmed
|
|
2186
|
+
});
|
|
2187
|
+
};
|
|
2188
|
+
const canSubmit = body.trim().length > 0 && !createNote.isPending;
|
|
2189
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2190
|
+
className: require_src.cn("border-border/50 focus-within:border-foreground/30 rounded-2xl border p-4 transition-colors", isFocused && "border-foreground/30"),
|
|
2191
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
2192
|
+
ref: textareaRef,
|
|
2193
|
+
value: body,
|
|
2194
|
+
onChange: (e) => setBody(e.target.value),
|
|
2195
|
+
onFocus: () => setIsFocused(true),
|
|
2196
|
+
onBlur: () => setIsFocused(false),
|
|
2197
|
+
onKeyDown: (e) => {
|
|
2198
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
2199
|
+
e.preventDefault();
|
|
2200
|
+
submit();
|
|
2201
|
+
}
|
|
2202
|
+
},
|
|
2203
|
+
placeholder: t("note_placeholder"),
|
|
2204
|
+
"aria-label": t("note_body_aria"),
|
|
2205
|
+
rows: 3,
|
|
2206
|
+
className: "placeholder:text-muted-foreground text-foreground min-h-[80px] w-full resize-none border-0 bg-transparent text-sm leading-relaxed outline-none"
|
|
2207
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2208
|
+
className: "mt-3 flex items-center justify-between gap-3",
|
|
2209
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2210
|
+
className: "text-muted-foreground text-xs",
|
|
2211
|
+
children: isMac ? t("tip_save_shortcut_mac") : t("tip_save_shortcut_other")
|
|
2212
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2213
|
+
type: "button",
|
|
2214
|
+
onClick: submit,
|
|
2215
|
+
disabled: !canSubmit,
|
|
2216
|
+
className: "bg-primary text-primary-foreground hover:bg-primary/90 disabled:bg-muted disabled:text-muted-foreground inline-flex shrink-0 items-center gap-1.5 rounded-full px-4 py-1.5 text-xs font-semibold transition-colors disabled:cursor-not-allowed",
|
|
2217
|
+
children: createNote.isPending ? t("adding") : t("add_note")
|
|
2218
|
+
})]
|
|
2219
|
+
})]
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
//#endregion
|
|
2223
|
+
//#region ../../contacts/ui/src/portal/components/notes/notes-list.tsx
|
|
2224
|
+
function formatDueDate(dateStr) {
|
|
2225
|
+
return new Date(dateStr).toLocaleDateString("en-US", {
|
|
2226
|
+
month: "short",
|
|
2227
|
+
day: "numeric",
|
|
2228
|
+
year: "numeric"
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
function formatRelativeNote(dateStr, t) {
|
|
2232
|
+
const date = new Date(dateStr);
|
|
2233
|
+
const diffMs = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
|
|
2234
|
+
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
2235
|
+
if (diffDays === 0) return date.toLocaleTimeString("en-US", {
|
|
2236
|
+
hour: "numeric",
|
|
2237
|
+
minute: "2-digit"
|
|
2238
|
+
});
|
|
2239
|
+
if (diffDays === 1) return t("task_yesterday");
|
|
2240
|
+
if (diffDays < 7) return t("task_days_ago_other", { count: diffDays });
|
|
2241
|
+
return formatDueDate(dateStr);
|
|
2242
|
+
}
|
|
2243
|
+
function NoteCard({ note, onEdit, onDelete }) {
|
|
2244
|
+
const { t } = useContactsTranslation();
|
|
2245
|
+
const assetCount = note.assets?.length ?? 0;
|
|
2246
|
+
const showTitle = !!note.title?.trim() && note.title.trim() !== note.body?.trim();
|
|
2247
|
+
const dueDateValue = note.due_at ?? note.due_date ?? null;
|
|
2248
|
+
const createdLabel = note.created_at ? formatRelativeNote(note.created_at, t) : null;
|
|
2249
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2250
|
+
role: "button",
|
|
2251
|
+
tabIndex: 0,
|
|
2252
|
+
onClick: () => onEdit(note),
|
|
2253
|
+
onKeyDown: (e) => {
|
|
2254
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
2255
|
+
e.preventDefault();
|
|
2256
|
+
onEdit(note);
|
|
2257
|
+
}
|
|
2258
|
+
},
|
|
2259
|
+
className: "group border-border/50 hover:border-foreground/20 hover:bg-muted/40 relative cursor-pointer rounded-xl border p-4 transition-all hover:shadow-sm",
|
|
2260
|
+
children: [
|
|
2261
|
+
showTitle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
2262
|
+
className: "text-foreground line-clamp-1 text-sm font-semibold",
|
|
2263
|
+
children: note.title
|
|
2264
|
+
}),
|
|
2265
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2266
|
+
className: require_src.cn("text-foreground line-clamp-4 text-sm leading-relaxed whitespace-pre-wrap", showTitle && "text-muted-foreground mt-1"),
|
|
2267
|
+
children: note.body
|
|
2268
|
+
}),
|
|
2269
|
+
(createdLabel || dueDateValue || assetCount > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2270
|
+
className: "mt-2.5 flex flex-wrap items-center gap-3",
|
|
2271
|
+
children: [
|
|
2272
|
+
createdLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2273
|
+
className: "text-muted-foreground/70 text-xs",
|
|
2274
|
+
children: createdLabel
|
|
2275
|
+
}),
|
|
2276
|
+
dueDateValue && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2277
|
+
className: "bg-primary/10 text-primary inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium",
|
|
2278
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Calendar, {
|
|
2279
|
+
className: "size-3",
|
|
2280
|
+
"aria-hidden": "true"
|
|
2281
|
+
}), t("task_due", { date: formatDueDate(dueDateValue) })]
|
|
2282
|
+
}),
|
|
2283
|
+
assetCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2284
|
+
className: "text-muted-foreground inline-flex items-center gap-1 text-xs",
|
|
2285
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Paperclip, {
|
|
2286
|
+
className: "size-3",
|
|
2287
|
+
"aria-hidden": "true"
|
|
2288
|
+
}), assetCount === 1 ? t("attachment_one") : t("attachment_other", { count: assetCount })]
|
|
2289
|
+
})
|
|
2290
|
+
]
|
|
2291
|
+
}),
|
|
2292
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2293
|
+
className: "absolute top-3 right-3 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100",
|
|
2294
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuTrigger, {
|
|
2295
|
+
asChild: true,
|
|
2296
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
2297
|
+
variant: "ghost",
|
|
2298
|
+
size: "icon-xs",
|
|
2299
|
+
onClick: (e) => e.stopPropagation(),
|
|
2300
|
+
"aria-label": t("note_actions"),
|
|
2301
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.EllipsisVertical, { className: "size-4" })
|
|
2302
|
+
})
|
|
2303
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuContent, {
|
|
2304
|
+
align: "end",
|
|
2305
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuItem, {
|
|
2306
|
+
className: "text-destructive",
|
|
2307
|
+
onClick: (e) => {
|
|
2308
|
+
e.stopPropagation();
|
|
2309
|
+
onDelete(note);
|
|
2310
|
+
},
|
|
2311
|
+
children: t("delete")
|
|
2312
|
+
})
|
|
2313
|
+
})] })
|
|
2314
|
+
})
|
|
2315
|
+
]
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
function NotesList({ notes, isLoading, contactId }) {
|
|
2319
|
+
const { t } = useContactsTranslation();
|
|
2320
|
+
const [noteToDelete, setNoteToDelete] = (0, react.useState)(null);
|
|
2321
|
+
const [editingNote, setEditingNote] = (0, react.useState)(null);
|
|
2322
|
+
const deleteNote = useDeleteContactNote(contactId, { onSuccess: () => setNoteToDelete(null) });
|
|
2323
|
+
const updateNote = useUpdateContactNote(contactId, { onSuccess: () => setEditingNote(null) });
|
|
2324
|
+
const handleEditSave = (data) => {
|
|
2325
|
+
if (!editingNote) return;
|
|
2326
|
+
updateNote.mutate({
|
|
2327
|
+
noteId: editingNote.id,
|
|
2328
|
+
input: {
|
|
2329
|
+
title: data.title,
|
|
2330
|
+
body: data.body
|
|
2331
|
+
}
|
|
2332
|
+
});
|
|
2333
|
+
};
|
|
2334
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2335
|
+
className: "space-y-4",
|
|
2336
|
+
children: [
|
|
2337
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineNoteComposer, { contactId }),
|
|
2338
|
+
isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2339
|
+
className: "flex flex-col gap-3",
|
|
2340
|
+
children: [
|
|
2341
|
+
1,
|
|
2342
|
+
2,
|
|
2343
|
+
3
|
|
2344
|
+
].map((i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "border-border/50 bg-muted/50 h-20 animate-pulse rounded-xl border" }, i))
|
|
2345
|
+
}) : notes.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2346
|
+
className: "flex flex-col gap-3",
|
|
2347
|
+
children: notes.map((note) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NoteCard, {
|
|
2348
|
+
note,
|
|
2349
|
+
onEdit: setEditingNote,
|
|
2350
|
+
onDelete: setNoteToDelete
|
|
2351
|
+
}, note.id))
|
|
2352
|
+
}),
|
|
2353
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NoteTaskModal, {
|
|
2354
|
+
open: editingNote !== null,
|
|
2355
|
+
onOpenChange: (open) => {
|
|
2356
|
+
if (!open) setEditingNote(null);
|
|
2357
|
+
},
|
|
2358
|
+
mode: "edit",
|
|
2359
|
+
type: "note",
|
|
2360
|
+
initialTitle: editingNote?.title ?? "",
|
|
2361
|
+
initialBody: editingNote?.body ?? "",
|
|
2362
|
+
onSave: handleEditSave,
|
|
2363
|
+
isPending: updateNote.isPending
|
|
2364
|
+
}, editingNote?.id ?? "edit"),
|
|
2365
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialog, {
|
|
2366
|
+
open: !!noteToDelete,
|
|
2367
|
+
onOpenChange: (open) => {
|
|
2368
|
+
if (!open) setNoteToDelete(null);
|
|
2369
|
+
},
|
|
2370
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogTitle, { children: t("delete_note_title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogDescription, { children: t("delete_note_irreversible") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogFooter, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogCancel, { children: t("cancel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogAction, {
|
|
2371
|
+
variant: "destructive",
|
|
2372
|
+
onClick: () => {
|
|
2373
|
+
if (noteToDelete) deleteNote.mutate(noteToDelete.id);
|
|
2374
|
+
},
|
|
2375
|
+
children: t("delete")
|
|
2376
|
+
})] })] })
|
|
2377
|
+
})
|
|
2378
|
+
]
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2381
|
+
//#endregion
|
|
2382
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/ContactDetailTabs.tsx
|
|
2383
|
+
const VISIBLE_TABS = ["Tasks", "Notes"];
|
|
2384
|
+
function ContactDetailTabs({ contactId, tasks, isLoadingTasks, notes, isLoadingNotes, activeTab, onTabChange }) {
|
|
2385
|
+
const { t } = useContactsTranslation();
|
|
2386
|
+
const tabLabels = {
|
|
2387
|
+
Tasks: t("tab_tasks"),
|
|
2388
|
+
Notes: t("tab_notes")
|
|
2389
|
+
};
|
|
2390
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2391
|
+
className: "mt-8",
|
|
2392
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2393
|
+
className: "border-border/50 flex items-center gap-1 border-b",
|
|
2394
|
+
children: VISIBLE_TABS.map((tab) => {
|
|
2395
|
+
const count = tab === "Tasks" ? tasks.length : notes.length;
|
|
2396
|
+
const isActive = activeTab === tab;
|
|
2397
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2398
|
+
type: "button",
|
|
2399
|
+
onClick: () => onTabChange(tab),
|
|
2400
|
+
className: require_src.cn("relative flex items-center gap-1.5 px-3 py-2.5 text-sm font-semibold transition-colors", isActive ? "text-foreground" : "text-muted-foreground hover:text-foreground"),
|
|
2401
|
+
children: [
|
|
2402
|
+
tabLabels[tab],
|
|
2403
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2404
|
+
className: require_src.cn("rounded-full px-1.5 text-xs leading-5 font-bold", isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"),
|
|
2405
|
+
children: count
|
|
2406
|
+
}),
|
|
2407
|
+
isActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2408
|
+
className: "bg-primary absolute inset-x-0 -bottom-px h-0.5",
|
|
2409
|
+
"aria-hidden": "true"
|
|
2410
|
+
})
|
|
2411
|
+
]
|
|
2412
|
+
}, tab);
|
|
2413
|
+
})
|
|
2414
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2415
|
+
className: "pt-6",
|
|
2416
|
+
children: [activeTab === "Tasks" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2417
|
+
className: "space-y-4",
|
|
2418
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineTaskComposer, { contactId }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskList, {
|
|
2419
|
+
tasks,
|
|
2420
|
+
isLoading: isLoadingTasks,
|
|
2421
|
+
contactId,
|
|
2422
|
+
hideHeader: true
|
|
2423
|
+
})]
|
|
2424
|
+
}), activeTab === "Notes" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NotesList, {
|
|
2425
|
+
notes,
|
|
2426
|
+
isLoading: isLoadingNotes,
|
|
2427
|
+
contactId
|
|
2428
|
+
})]
|
|
2429
|
+
})]
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
//#endregion
|
|
2433
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/ContactDetailPane.tsx
|
|
2434
|
+
function ContactDetailPane({ contactId, queryKeyPrefix, getCountries, onBack }) {
|
|
2435
|
+
const { t } = useContactsTranslation();
|
|
2436
|
+
const { contact, isLoading, methods, countryOptions, isDirty, isSubmitting, isDeleting, onSave, onDelete } = useContactDetailPage(contactId, {
|
|
2437
|
+
queryKeyPrefix,
|
|
2438
|
+
getCountries,
|
|
2439
|
+
onDeleteSuccess: onBack,
|
|
2440
|
+
onSaveSuccess: () => setEditProfileOpen(false)
|
|
2441
|
+
});
|
|
2442
|
+
const { data: tasks = [], isLoading: isLoadingTasks } = useContactTasks(contactId);
|
|
2443
|
+
const { data: notes = [], isLoading: isLoadingNotes } = useContactNotes(contactId);
|
|
2444
|
+
const [confirmDelete, setConfirmDelete] = (0, react.useState)(false);
|
|
2445
|
+
const [activeTab, setActiveTab] = (0, react.useState)("Tasks");
|
|
2446
|
+
const [editProfileOpen, setEditProfileOpen] = (0, react.useState)(false);
|
|
2447
|
+
const handleConfirmDelete = (0, react.useCallback)(() => {
|
|
2448
|
+
setConfirmDelete(false);
|
|
2449
|
+
onDelete();
|
|
2450
|
+
}, [onDelete]);
|
|
2451
|
+
if (isLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactDetailSkeleton, { onBack });
|
|
2452
|
+
if (!contact) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2453
|
+
className: "flex h-full flex-1 flex-col",
|
|
2454
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailTopBar, {
|
|
2455
|
+
onBack,
|
|
2456
|
+
phone: null,
|
|
2457
|
+
email: null,
|
|
2458
|
+
onDelete: null,
|
|
2459
|
+
onEditProfile: null
|
|
2460
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2461
|
+
className: "mx-auto flex max-w-md flex-col items-center px-6 py-16 text-center",
|
|
2462
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
2463
|
+
className: "text-foreground text-base font-semibold",
|
|
2464
|
+
children: t("error_loading")
|
|
2465
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2466
|
+
className: "text-muted-foreground mt-2 text-sm",
|
|
2467
|
+
children: t("error_loading_description")
|
|
2468
|
+
})]
|
|
2469
|
+
})]
|
|
2470
|
+
});
|
|
2471
|
+
const fullName = getDisplayName(contact);
|
|
2472
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_hook_form.FormProvider, {
|
|
2473
|
+
...methods,
|
|
2474
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2475
|
+
className: "flex h-full flex-1 flex-col",
|
|
2476
|
+
children: [
|
|
2477
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailTopBar, {
|
|
2478
|
+
onBack,
|
|
2479
|
+
phone: contact.phone ?? null,
|
|
2480
|
+
email: contact.email ?? null,
|
|
2481
|
+
onDelete: () => setConfirmDelete(true),
|
|
2482
|
+
onEditProfile: () => setEditProfileOpen(true)
|
|
2483
|
+
}),
|
|
2484
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2485
|
+
className: "flex-1 overflow-y-auto",
|
|
2486
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2487
|
+
className: "mx-auto w-full max-w-[900px] px-6 pt-8 pb-16 md:px-8 md:pt-10",
|
|
2488
|
+
children: [
|
|
2489
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactDetailHero, {
|
|
2490
|
+
contact,
|
|
2491
|
+
tasksCount: tasks.length,
|
|
2492
|
+
notesCount: notes.length
|
|
2493
|
+
}),
|
|
2494
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactInfoRow, {
|
|
2495
|
+
contact,
|
|
2496
|
+
onEditEmpty: () => setEditProfileOpen(true)
|
|
2497
|
+
}),
|
|
2498
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactDetailTabs, {
|
|
2499
|
+
contactId,
|
|
2500
|
+
tasks,
|
|
2501
|
+
isLoadingTasks,
|
|
2502
|
+
notes,
|
|
2503
|
+
isLoadingNotes,
|
|
2504
|
+
activeTab,
|
|
2505
|
+
onTabChange: setActiveTab
|
|
2506
|
+
})
|
|
2507
|
+
]
|
|
2508
|
+
})
|
|
2509
|
+
}),
|
|
2510
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Dialog, {
|
|
2511
|
+
open: editProfileOpen,
|
|
2512
|
+
onOpenChange: (open) => {
|
|
2513
|
+
if (!open && isSubmitting) return;
|
|
2514
|
+
setEditProfileOpen(open);
|
|
2515
|
+
},
|
|
2516
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DialogContent, {
|
|
2517
|
+
className: "max-h-[90vh] max-w-2xl overflow-y-auto",
|
|
2518
|
+
children: [
|
|
2519
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DialogHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DialogTitle, { children: t("edit_contact_name", { name: fullName }) }) }),
|
|
2520
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactDetailsForm, { countries: countryOptions }),
|
|
2521
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DialogFooter, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
2522
|
+
type: "button",
|
|
2523
|
+
variant: "secondary",
|
|
2524
|
+
onClick: () => setEditProfileOpen(false),
|
|
2525
|
+
disabled: isSubmitting,
|
|
2526
|
+
children: t("cancel")
|
|
2527
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.Button, {
|
|
2528
|
+
type: "button",
|
|
2529
|
+
onClick: onSave,
|
|
2530
|
+
disabled: !isDirty || isSubmitting,
|
|
2531
|
+
"aria-busy": isSubmitting,
|
|
2532
|
+
children: [isSubmitting && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Spinner, { className: "size-4" }), isSubmitting ? t("saving") : t("save_changes")]
|
|
2533
|
+
})] })
|
|
2534
|
+
]
|
|
2535
|
+
})
|
|
2536
|
+
}),
|
|
2537
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialog, {
|
|
2538
|
+
open: confirmDelete,
|
|
2539
|
+
onOpenChange: setConfirmDelete,
|
|
2540
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogTitle, { children: t("delete_name", { name: fullName }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogDescription, { children: t("delete_confirm_permanent") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.AlertDialogFooter, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogCancel, {
|
|
2541
|
+
disabled: isDeleting,
|
|
2542
|
+
children: t("cancel")
|
|
2543
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AlertDialogAction, {
|
|
2544
|
+
variant: "destructive",
|
|
2545
|
+
onClick: handleConfirmDelete,
|
|
2546
|
+
disabled: isDeleting,
|
|
2547
|
+
children: isDeleting ? t("deleting") : t("delete_contact")
|
|
2548
|
+
})] })] })
|
|
2549
|
+
})
|
|
2550
|
+
]
|
|
2551
|
+
})
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
function DetailTopBar({ onBack, phone, email, onDelete, onEditProfile }) {
|
|
2555
|
+
const { t } = useContactsTranslation();
|
|
2556
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2557
|
+
className: "border-border/50 bg-background/90 sticky top-0 z-10 flex items-center justify-between border-b px-4 py-3 backdrop-blur md:px-8",
|
|
2558
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2559
|
+
className: "flex items-center gap-3",
|
|
2560
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2561
|
+
type: "button",
|
|
2562
|
+
onClick: onBack,
|
|
2563
|
+
className: "text-muted-foreground hover:bg-muted hover:text-foreground flex size-8 items-center justify-center rounded-full transition-colors md:hidden",
|
|
2564
|
+
"aria-label": t("back_to_contacts"),
|
|
2565
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronLeft, { className: "size-4" })
|
|
2566
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2567
|
+
className: "text-muted-foreground hidden text-xs font-semibold tracking-widest uppercase md:inline",
|
|
2568
|
+
children: t("contact_label")
|
|
2569
|
+
})]
|
|
2570
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2571
|
+
className: "flex items-center gap-1",
|
|
2572
|
+
children: [
|
|
2573
|
+
onEditProfile && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2574
|
+
type: "button",
|
|
2575
|
+
onClick: onEditProfile,
|
|
2576
|
+
className: "text-muted-foreground hover:bg-muted hover:text-foreground flex size-8 items-center justify-center rounded-full transition-colors",
|
|
2577
|
+
"aria-label": t("edit_profile"),
|
|
2578
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Pencil, {
|
|
2579
|
+
className: "size-4",
|
|
2580
|
+
"aria-hidden": "true"
|
|
2581
|
+
})
|
|
2582
|
+
}),
|
|
2583
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionLink, {
|
|
2584
|
+
href: phone ? `tel:${phone}` : null,
|
|
2585
|
+
label: t("label_call"),
|
|
2586
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Phone, {
|
|
2587
|
+
className: "size-4",
|
|
2588
|
+
"aria-hidden": "true"
|
|
2589
|
+
})
|
|
2590
|
+
}),
|
|
2591
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionLink, {
|
|
2592
|
+
href: email ? `mailto:${email}` : null,
|
|
2593
|
+
label: t("label_email"),
|
|
2594
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Mail, {
|
|
2595
|
+
className: "size-4",
|
|
2596
|
+
"aria-hidden": "true"
|
|
2597
|
+
})
|
|
2598
|
+
}),
|
|
2599
|
+
onDelete && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2600
|
+
className: "bg-border mx-1 h-5 w-px",
|
|
2601
|
+
"aria-hidden": "true"
|
|
2602
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuTrigger, {
|
|
2603
|
+
asChild: true,
|
|
2604
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2605
|
+
type: "button",
|
|
2606
|
+
className: "text-muted-foreground hover:bg-muted hover:text-foreground flex size-8 items-center justify-center rounded-full transition-colors",
|
|
2607
|
+
"aria-label": t("more_actions"),
|
|
2608
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.MoreHorizontal, { className: "size-4" })
|
|
2609
|
+
})
|
|
2610
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuContent, {
|
|
2611
|
+
align: "end",
|
|
2612
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenuItem, {
|
|
2613
|
+
className: "text-destructive",
|
|
2614
|
+
onClick: onDelete,
|
|
2615
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Trash2, {
|
|
2616
|
+
className: "size-4",
|
|
2617
|
+
"aria-hidden": "true"
|
|
2618
|
+
}), t("delete_contact")]
|
|
2619
|
+
})
|
|
2620
|
+
})] })] })
|
|
2621
|
+
]
|
|
2622
|
+
})]
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
function ActionLink({ href, label, icon }) {
|
|
2626
|
+
const baseClass = "flex size-8 items-center justify-center rounded-full transition-colors";
|
|
2627
|
+
if (!href) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2628
|
+
"aria-label": `${label} (unavailable)`,
|
|
2629
|
+
className: require_src.cn(baseClass, "text-muted-foreground/40 cursor-default"),
|
|
2630
|
+
children: icon
|
|
2631
|
+
});
|
|
2632
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2633
|
+
href,
|
|
2634
|
+
"aria-label": label,
|
|
2635
|
+
className: require_src.cn(baseClass, "text-muted-foreground hover:bg-muted hover:text-foreground"),
|
|
2636
|
+
children: icon
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
function ContactDetailSkeleton({ onBack }) {
|
|
2640
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2641
|
+
className: "flex h-full flex-1 flex-col",
|
|
2642
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailTopBar, {
|
|
2643
|
+
onBack,
|
|
2644
|
+
phone: null,
|
|
2645
|
+
email: null,
|
|
2646
|
+
onDelete: null,
|
|
2647
|
+
onEditProfile: null
|
|
2648
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2649
|
+
className: "mx-auto w-full max-w-[900px] flex-1 space-y-6 px-6 pt-8 pb-16 md:px-8 md:pt-10",
|
|
2650
|
+
children: [
|
|
2651
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2652
|
+
className: "flex items-start gap-5",
|
|
2653
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-muted size-20 animate-pulse rounded-xl" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2654
|
+
className: "flex-1 space-y-3",
|
|
2655
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-muted h-7 w-48 animate-pulse rounded" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-muted h-4 w-24 animate-pulse rounded" })]
|
|
2656
|
+
})]
|
|
2657
|
+
}),
|
|
2658
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-muted h-24 animate-pulse rounded-xl" }),
|
|
2659
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "bg-muted h-64 animate-pulse rounded-xl" })
|
|
2660
|
+
]
|
|
2661
|
+
})]
|
|
2662
|
+
});
|
|
2663
|
+
}
|
|
2664
|
+
//#endregion
|
|
2665
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/ContactsEmptyState.tsx
|
|
2666
|
+
function ContactsEmptyState() {
|
|
2667
|
+
const { t } = useContactsTranslation();
|
|
2668
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2669
|
+
className: "hidden h-full flex-1 flex-col items-center justify-center px-8 text-center md:flex",
|
|
2670
|
+
children: [
|
|
2671
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2672
|
+
className: "bg-muted text-muted-foreground flex size-16 items-center justify-center rounded-xl",
|
|
2673
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Users, {
|
|
2674
|
+
className: "size-7",
|
|
2675
|
+
"aria-hidden": "true"
|
|
2676
|
+
})
|
|
2677
|
+
}),
|
|
2678
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
2679
|
+
className: "text-foreground mt-5 text-base font-semibold",
|
|
2680
|
+
children: t("empty_select_contact")
|
|
2681
|
+
}),
|
|
2682
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2683
|
+
className: "text-muted-foreground mt-1 max-w-xs text-sm",
|
|
2684
|
+
children: t("empty_select_description")
|
|
2685
|
+
})
|
|
2686
|
+
]
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
//#endregion
|
|
2690
|
+
//#region ../../contacts/ui/src/portal/components/contacts/rep-layout/RepContactsLayout.tsx
|
|
2691
|
+
function RepContactsLayout({ selectedContactId, onSelect, onAdd, queryKeyPrefix, getCountries }) {
|
|
2692
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2693
|
+
className: "bg-background flex h-full overflow-hidden",
|
|
2694
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
|
|
2695
|
+
className: require_src.cn("border-border/50 bg-background w-full shrink-0 overflow-hidden border-r md:flex md:w-[420px] md:flex-col", selectedContactId ? "hidden" : "flex flex-col"),
|
|
2696
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactsSidebar, {
|
|
2697
|
+
selectedContactId,
|
|
2698
|
+
onSelect,
|
|
2699
|
+
onAdd
|
|
2700
|
+
})
|
|
2701
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("main", {
|
|
2702
|
+
className: require_src.cn("bg-background relative flex-1 md:flex md:flex-col", selectedContactId ? "flex flex-col" : "hidden md:flex"),
|
|
2703
|
+
children: selectedContactId ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactDetailPane, {
|
|
2704
|
+
contactId: selectedContactId,
|
|
2705
|
+
queryKeyPrefix,
|
|
2706
|
+
getCountries,
|
|
2707
|
+
onBack: () => onSelect(null)
|
|
2708
|
+
}, selectedContactId) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactsEmptyState, {})
|
|
2709
|
+
})]
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
//#endregion
|
|
2713
|
+
//#region ../../contacts/ui/src/shared/hooks/useCreateContactMutation.ts
|
|
2714
|
+
function useCreateContactMutation(queryKeyPrefix = "contacts", options) {
|
|
2715
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
2716
|
+
const api = useContactsCrud();
|
|
2717
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
2718
|
+
mutationFn: (data) => api.createContact(data),
|
|
2719
|
+
onSuccess: (data) => {
|
|
2720
|
+
require_src.fluidToast({
|
|
2721
|
+
title: "Contact created successfully",
|
|
2722
|
+
type: "success"
|
|
2723
|
+
});
|
|
2724
|
+
queryClient.invalidateQueries({ queryKey: CONTACTS_QUERY_KEYS.all(queryKeyPrefix) });
|
|
2725
|
+
options?.onSuccess?.(data);
|
|
2726
|
+
},
|
|
2727
|
+
onError: (error) => {
|
|
2728
|
+
if (options?.onError) options.onError(error);
|
|
2729
|
+
else require_src.fluidToast({
|
|
2730
|
+
title: "Failed to create contact",
|
|
2731
|
+
type: "error",
|
|
2732
|
+
description: parseApiErrors(error)
|
|
2733
|
+
});
|
|
2734
|
+
}
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
//#endregion
|
|
2738
|
+
//#region ../../api-clients/portal-tenant-contacts/src/namespaces/portal_tenant_contacts.ts
|
|
2739
|
+
/**
|
|
2740
|
+
* List contacts with cursor pagination
|
|
2741
|
+
* Returns a paginated list of contacts belonging to the authenticated member.
|
|
2742
|
+
*
|
|
2743
|
+
* @param client - Fetch client instance
|
|
2744
|
+
* @param [params] - params
|
|
2745
|
+
*/
|
|
2746
|
+
async function contacts_list(client, params) {
|
|
2747
|
+
return client.get(`/api/contacts`, params);
|
|
2748
|
+
}
|
|
2749
|
+
/**
|
|
2750
|
+
* Create a new contact
|
|
2751
|
+
* Creates a new contact for the authenticated member.
|
|
2752
|
+
*
|
|
2753
|
+
* @param client - Fetch client instance
|
|
2754
|
+
* @param body - body
|
|
2755
|
+
*/
|
|
2756
|
+
async function contacts_create(client, body) {
|
|
2757
|
+
return client.post(`/api/contacts`, body);
|
|
2758
|
+
}
|
|
2759
|
+
/**
|
|
2760
|
+
* Get a specific contact
|
|
2761
|
+
* Returns a single contact by ID.
|
|
2762
|
+
*
|
|
2763
|
+
* @param client - Fetch client instance
|
|
2764
|
+
* @param id - id
|
|
2765
|
+
*/
|
|
2766
|
+
async function contacts_show(client, id) {
|
|
2767
|
+
return client.get(`/api/contacts/${id}`);
|
|
2768
|
+
}
|
|
2769
|
+
/**
|
|
2770
|
+
* Update a contact
|
|
2771
|
+
* Updates an existing contact's attributes.
|
|
2772
|
+
*
|
|
2773
|
+
* @param client - Fetch client instance
|
|
2774
|
+
* @param id - id
|
|
2775
|
+
* @param body - body
|
|
2776
|
+
*/
|
|
2777
|
+
async function contacts_update(client, id, body) {
|
|
2778
|
+
return client.patch(`/api/contacts/${id}`, body);
|
|
2779
|
+
}
|
|
2780
|
+
/**
|
|
2781
|
+
* Delete a contact
|
|
2782
|
+
* Soft-deletes a contact by ID.
|
|
2783
|
+
*
|
|
2784
|
+
* @param client - Fetch client instance
|
|
2785
|
+
* @param id - id
|
|
2786
|
+
*/
|
|
2787
|
+
async function contacts_destroy(client, id) {
|
|
2788
|
+
return client.delete(`/api/contacts/${id}`);
|
|
2789
|
+
}
|
|
2790
|
+
/**
|
|
2791
|
+
* Delete multiple contacts
|
|
2792
|
+
* Soft-deletes multiple contacts by their IDs.
|
|
2793
|
+
*
|
|
2794
|
+
* @param client - Fetch client instance
|
|
2795
|
+
* @param body - body
|
|
2796
|
+
*/
|
|
2797
|
+
async function contacts_bulk_destroy(client, body) {
|
|
2798
|
+
return client.delete(`/api/contacts/bulk`, { body });
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* List contact groups
|
|
2802
|
+
* Returns a paginated list of contact groups belonging to the authenticated member.
|
|
2803
|
+
*
|
|
2804
|
+
* @param client - Fetch client instance
|
|
2805
|
+
* @param [params] - params
|
|
2806
|
+
*/
|
|
2807
|
+
async function contacts_groups_list(client, params) {
|
|
2808
|
+
return client.get(`/api/contacts/groups`, params);
|
|
2809
|
+
}
|
|
2810
|
+
/**
|
|
2811
|
+
* Create a new group
|
|
2812
|
+
* Creates a new contact group for the authenticated member.
|
|
2813
|
+
*
|
|
2814
|
+
* @param client - Fetch client instance
|
|
2815
|
+
* @param body - body
|
|
2816
|
+
*/
|
|
2817
|
+
async function contacts_groups_create(client, body) {
|
|
2818
|
+
return client.post(`/api/contacts/groups`, body);
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Update a group
|
|
2822
|
+
* Updates an existing contact group.
|
|
2823
|
+
*
|
|
2824
|
+
* @param client - Fetch client instance
|
|
2825
|
+
* @param id - id
|
|
2826
|
+
* @param body - body
|
|
2827
|
+
*/
|
|
2828
|
+
async function contacts_groups_update(client, id, body) {
|
|
2829
|
+
return client.patch(`/api/contacts/groups/${id}`, body);
|
|
2830
|
+
}
|
|
2831
|
+
/**
|
|
2832
|
+
* Delete a group
|
|
2833
|
+
* Removes a contact group.
|
|
2834
|
+
*
|
|
2835
|
+
* @param client - Fetch client instance
|
|
2836
|
+
* @param id - id
|
|
2837
|
+
*/
|
|
2838
|
+
async function contacts_groups_destroy(client, id) {
|
|
2839
|
+
return client.delete(`/api/contacts/groups/${id}`);
|
|
2840
|
+
}
|
|
2841
|
+
/**
|
|
2842
|
+
* List notes for a contact
|
|
2843
|
+
* Returns notes attached to a contact.
|
|
2844
|
+
*
|
|
2845
|
+
* @param client - Fetch client instance
|
|
2846
|
+
* @param contact_id - contact_id
|
|
2847
|
+
* @param [params] - params
|
|
2848
|
+
*/
|
|
2849
|
+
async function contacts_notes_list(client, contact_id, params) {
|
|
2850
|
+
return client.get(`/api/contacts/${contact_id}/notes`, params);
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Create a note for a contact
|
|
2854
|
+
* Adds a new note to a contact.
|
|
2855
|
+
*
|
|
2856
|
+
* @param client - Fetch client instance
|
|
2857
|
+
* @param contact_id - contact_id
|
|
2858
|
+
* @param body - body
|
|
2859
|
+
*/
|
|
2860
|
+
async function contacts_notes_create(client, contact_id, body) {
|
|
2861
|
+
return client.post(`/api/contacts/${contact_id}/notes`, body);
|
|
2862
|
+
}
|
|
2863
|
+
/**
|
|
2864
|
+
* List tasks for a contact
|
|
2865
|
+
* Returns tasks assigned to a contact.
|
|
2866
|
+
*
|
|
2867
|
+
* @param client - Fetch client instance
|
|
2868
|
+
* @param contact_id - contact_id
|
|
2869
|
+
* @param [params] - params
|
|
2870
|
+
*/
|
|
2871
|
+
async function contacts_tasks_list(client, contact_id, params) {
|
|
2872
|
+
return client.get(`/api/contacts/${contact_id}/tasks`, params);
|
|
2873
|
+
}
|
|
2874
|
+
/**
|
|
2875
|
+
* Create a task for a contact
|
|
2876
|
+
* Adds a new task to a contact.
|
|
2877
|
+
*
|
|
2878
|
+
* @param client - Fetch client instance
|
|
2879
|
+
* @param contact_id - contact_id
|
|
2880
|
+
* @param body - body
|
|
2881
|
+
*/
|
|
2882
|
+
async function contacts_tasks_create(client, contact_id, body) {
|
|
2883
|
+
return client.post(`/api/contacts/${contact_id}/tasks`, body);
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Update a note
|
|
2887
|
+
* Updates an existing note on a contact.
|
|
2888
|
+
*
|
|
2889
|
+
* @param client - Fetch client instance
|
|
2890
|
+
* @param contact_id - contact_id
|
|
2891
|
+
* @param id - id
|
|
2892
|
+
* @param body - body
|
|
2893
|
+
*/
|
|
2894
|
+
async function contacts_notes_update(client, contact_id, id, body) {
|
|
2895
|
+
return client.patch(`/api/contacts/${contact_id}/notes/${id}`, body);
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Delete a note
|
|
2899
|
+
* Removes a note from a contact.
|
|
2900
|
+
*
|
|
2901
|
+
* @param client - Fetch client instance
|
|
2902
|
+
* @param contact_id - contact_id
|
|
2903
|
+
* @param id - id
|
|
2904
|
+
*/
|
|
2905
|
+
async function contacts_notes_destroy(client, contact_id, id) {
|
|
2906
|
+
return client.delete(`/api/contacts/${contact_id}/notes/${id}`);
|
|
2907
|
+
}
|
|
2908
|
+
/**
|
|
2909
|
+
* Update a task
|
|
2910
|
+
* Updates an existing task on a contact.
|
|
2911
|
+
*
|
|
2912
|
+
* @param client - Fetch client instance
|
|
2913
|
+
* @param contact_id - contact_id
|
|
2914
|
+
* @param id - id
|
|
2915
|
+
* @param body - body
|
|
2916
|
+
*/
|
|
2917
|
+
async function contacts_tasks_update(client, contact_id, id, body) {
|
|
2918
|
+
return client.patch(`/api/contacts/${contact_id}/tasks/${id}`, body);
|
|
2919
|
+
}
|
|
2920
|
+
/**
|
|
2921
|
+
* Delete a task
|
|
2922
|
+
* Removes a task from a contact.
|
|
2923
|
+
*
|
|
2924
|
+
* @param client - Fetch client instance
|
|
2925
|
+
* @param contact_id - contact_id
|
|
2926
|
+
* @param id - id
|
|
2927
|
+
*/
|
|
2928
|
+
async function contacts_tasks_destroy(client, contact_id, id) {
|
|
2929
|
+
return client.delete(`/api/contacts/${contact_id}/tasks/${id}`);
|
|
2930
|
+
}
|
|
2931
|
+
//#endregion
|
|
2932
|
+
//#region src/adapters/contacts-api-adapter.ts
|
|
2933
|
+
const EMPTY_META = {
|
|
2934
|
+
total_count: 0,
|
|
2935
|
+
total_pages: 0,
|
|
2936
|
+
current_page: 1,
|
|
2937
|
+
next_cursor: null
|
|
2938
|
+
};
|
|
2939
|
+
function paginationFromBff(meta, itemCount, currentPage, pageSize) {
|
|
2940
|
+
if (!meta) return EMPTY_META;
|
|
2941
|
+
const nextCursor = meta.pagination?.next_cursor ?? null;
|
|
2942
|
+
const hasNext = !!nextCursor;
|
|
2943
|
+
const totalCount = meta.total_count ?? (hasNext ? (currentPage + 1) * pageSize : (currentPage - 1) * pageSize + itemCount);
|
|
2944
|
+
return {
|
|
2945
|
+
total_count: totalCount,
|
|
2946
|
+
total_pages: Math.max(1, Math.ceil(totalCount / pageSize)),
|
|
2947
|
+
current_page: currentPage,
|
|
2948
|
+
next_cursor: nextCursor,
|
|
2949
|
+
request_id: meta.request_id ?? void 0,
|
|
2950
|
+
timestamp: meta.timestamp ?? void 0
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
/** BFF Contact → core Contact. Fills missing fields with safe defaults. */
|
|
2954
|
+
function mapContact(raw) {
|
|
2955
|
+
return {
|
|
2956
|
+
id: raw.id,
|
|
2957
|
+
full_name: `${raw.first_name ?? ""} ${raw.last_name ?? ""}`.trim(),
|
|
2958
|
+
first_name: raw.first_name ?? null,
|
|
2959
|
+
last_name: raw.last_name ?? null,
|
|
2960
|
+
email: raw.email ?? null,
|
|
2961
|
+
phone: raw.phone ?? null,
|
|
2962
|
+
status: raw.status ?? null,
|
|
2963
|
+
address: raw.address ?? null,
|
|
2964
|
+
city: raw.city ?? null,
|
|
2965
|
+
state: raw.state ?? null,
|
|
2966
|
+
postal_code: raw.postal_code ?? null,
|
|
2967
|
+
avatar_url: raw.avatar_url ?? null,
|
|
2968
|
+
country: raw.country ?? null,
|
|
2969
|
+
tags: raw.tags ?? [],
|
|
2970
|
+
metadata: {},
|
|
2971
|
+
created_at: raw.created_at,
|
|
2972
|
+
updated_at: raw.updated_at
|
|
2973
|
+
};
|
|
2974
|
+
}
|
|
2975
|
+
/** BFF Note → core ContactNote. */
|
|
2976
|
+
function mapNote(raw) {
|
|
2977
|
+
return {
|
|
2978
|
+
id: raw.id,
|
|
2979
|
+
title: raw.title ?? "",
|
|
2980
|
+
body: raw.body ?? "",
|
|
2981
|
+
note_type: null,
|
|
2982
|
+
due_date: null,
|
|
2983
|
+
due_time: null,
|
|
2984
|
+
pinned: null,
|
|
2985
|
+
created_at: raw.created_at,
|
|
2986
|
+
updated_at: raw.updated_at,
|
|
2987
|
+
contact_id: raw.contact_id ?? 0,
|
|
2988
|
+
assets: []
|
|
2989
|
+
};
|
|
2990
|
+
}
|
|
2991
|
+
/** BFF Task → core ContactTask. */
|
|
2992
|
+
function mapTask(raw) {
|
|
2993
|
+
return {
|
|
2994
|
+
id: raw.id,
|
|
2995
|
+
body: raw.title ?? "",
|
|
2996
|
+
due_at: raw.due_date ?? null,
|
|
2997
|
+
completed_at: raw.completed_at ?? (raw.completed ? (/* @__PURE__ */ new Date()).toISOString() : null),
|
|
2998
|
+
created_at: raw.created_at,
|
|
2999
|
+
contact_id: raw.contact_id ?? null
|
|
3000
|
+
};
|
|
3001
|
+
}
|
|
3002
|
+
/**
|
|
3003
|
+
* The contacts-ui sends offset pagination params (page, per_page, sort_by,
|
|
3004
|
+
* sort_direction, search, status). The BFF expects cursor pagination
|
|
3005
|
+
* (page[cursor], page[limit], search, tags). Translate between the two.
|
|
3006
|
+
*
|
|
3007
|
+
* `cursorCache` maps page numbers → cursor tokens so page-2+ requests can
|
|
3008
|
+
* resolve to the correct cursor. The cache is populated from BFF responses.
|
|
3009
|
+
*/
|
|
3010
|
+
function mapListParams(params, cursorCache) {
|
|
3011
|
+
if (!params) return void 0;
|
|
3012
|
+
const mapped = {};
|
|
3013
|
+
if (params.per_page || params.limit) mapped["page[limit]"] = params.per_page ?? params.limit;
|
|
3014
|
+
const page = params.page ?? 1;
|
|
3015
|
+
if (page > 1) {
|
|
3016
|
+
const cursor = cursorCache.get(page);
|
|
3017
|
+
if (cursor) mapped["page[cursor]"] = cursor;
|
|
3018
|
+
}
|
|
3019
|
+
if (params.search_query || params.search || params.query) mapped.search = params.search_query ?? params.search ?? params.query;
|
|
3020
|
+
if (params.tags) mapped.tags = params.tags;
|
|
3021
|
+
return mapped;
|
|
3022
|
+
}
|
|
3023
|
+
function createContactsAdapter(client) {
|
|
3024
|
+
const cursorCache = /* @__PURE__ */ new Map();
|
|
3025
|
+
return {
|
|
3026
|
+
getContact: async (id) => {
|
|
3027
|
+
return { contact: mapContact((await contacts_show(client, id)).contact) };
|
|
3028
|
+
},
|
|
3029
|
+
listContacts: async (params) => {
|
|
3030
|
+
const raw = params;
|
|
3031
|
+
const page = raw?.page ?? 1;
|
|
3032
|
+
const pageSize = raw?.per_page ?? 25;
|
|
3033
|
+
const body = await contacts_list(client, mapListParams(raw, cursorCache));
|
|
3034
|
+
const meta = body.meta;
|
|
3035
|
+
const nextCursor = (meta?.pagination)?.next_cursor;
|
|
3036
|
+
if (nextCursor) cursorCache.set(page + 1, nextCursor);
|
|
3037
|
+
const contacts = (body.contacts ?? []).map(mapContact);
|
|
3038
|
+
return {
|
|
3039
|
+
contacts,
|
|
3040
|
+
meta: paginationFromBff(meta, contacts.length, page, pageSize)
|
|
3041
|
+
};
|
|
3042
|
+
},
|
|
3043
|
+
createContact: async (data) => {
|
|
3044
|
+
return contacts_create(client, { contact: data });
|
|
3045
|
+
},
|
|
3046
|
+
updateContact: async (id, data) => {
|
|
3047
|
+
return contacts_update(client, id, { contact: data });
|
|
3048
|
+
},
|
|
3049
|
+
deleteContact: async (id) => {
|
|
3050
|
+
return contacts_destroy(client, id);
|
|
3051
|
+
},
|
|
3052
|
+
bulkDeleteContacts: async (ids) => {
|
|
3053
|
+
return contacts_bulk_destroy(client, { ids });
|
|
3054
|
+
}
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
3057
|
+
function createNotesAdapter(client) {
|
|
3058
|
+
return {
|
|
3059
|
+
listNotes: async (contactId) => {
|
|
3060
|
+
return { notes: ((await contacts_notes_list(client, contactId)).notes ?? []).map(mapNote) };
|
|
3061
|
+
},
|
|
3062
|
+
createNote: async (contactId, input) => {
|
|
3063
|
+
return contacts_notes_create(client, contactId, { note: input });
|
|
3064
|
+
},
|
|
3065
|
+
updateNote: async (noteId, contactId, input) => {
|
|
3066
|
+
return contacts_notes_update(client, contactId, noteId, { note: input });
|
|
3067
|
+
},
|
|
3068
|
+
deleteNote: async (noteId, contactId) => {
|
|
3069
|
+
return contacts_notes_destroy(client, contactId, noteId);
|
|
3070
|
+
}
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
function createTasksAdapter(client) {
|
|
3074
|
+
return {
|
|
3075
|
+
listTasks: async (contactId) => {
|
|
3076
|
+
return { tasks: ((await contacts_tasks_list(client, contactId)).tasks ?? []).map(mapTask) };
|
|
3077
|
+
},
|
|
3078
|
+
createTask: async (contactId, input) => {
|
|
3079
|
+
return contacts_tasks_create(client, contactId, { task: {
|
|
3080
|
+
title: input.body,
|
|
3081
|
+
due_date: input.due_at
|
|
3082
|
+
} });
|
|
3083
|
+
},
|
|
3084
|
+
updateTask: async (taskId, contactId, input) => {
|
|
3085
|
+
return contacts_tasks_update(client, contactId, taskId, { task: {
|
|
3086
|
+
title: input.body ?? void 0,
|
|
3087
|
+
completed: input.completed_at !== void 0 ? input.completed_at !== null : void 0,
|
|
3088
|
+
due_date: input.due_at
|
|
3089
|
+
} });
|
|
3090
|
+
},
|
|
3091
|
+
deleteTask: async (taskId, contactId) => {
|
|
3092
|
+
return contacts_tasks_destroy(client, contactId, taskId);
|
|
3093
|
+
}
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
3096
|
+
/** BFF Group → core ContactGroup. */
|
|
3097
|
+
function mapGroup(raw) {
|
|
3098
|
+
return {
|
|
3099
|
+
id: raw.id,
|
|
3100
|
+
name: raw.name ?? "",
|
|
3101
|
+
avatar: raw.avatar ?? null,
|
|
3102
|
+
avatar_background: raw.avatar_background ?? null,
|
|
3103
|
+
created_at: raw.created_at,
|
|
3104
|
+
updated_at: raw.updated_at
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
function createGroupsAdapter(client) {
|
|
3108
|
+
return {
|
|
3109
|
+
listGroups: async () => {
|
|
3110
|
+
return { groups: ((await contacts_groups_list(client)).groups ?? []).map(mapGroup) };
|
|
3111
|
+
},
|
|
3112
|
+
createGroup: async (input) => {
|
|
3113
|
+
return { group: mapGroup((await contacts_groups_create(client, { group: input })).group) };
|
|
3114
|
+
},
|
|
3115
|
+
updateGroup: async (groupId, input) => {
|
|
3116
|
+
return { group: mapGroup((await contacts_groups_update(client, groupId, { group: input })).group) };
|
|
3117
|
+
},
|
|
3118
|
+
deleteGroup: async (groupId) => {
|
|
3119
|
+
return contacts_groups_destroy(client, groupId);
|
|
3120
|
+
}
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
/**
|
|
3124
|
+
* Creates a composite ContactsDomainApi backed by the portal-tenant
|
|
3125
|
+
* Contacts BFF endpoints.
|
|
3126
|
+
*/
|
|
3127
|
+
function createPortalContactsDomainApiAdapter(client) {
|
|
3128
|
+
return {
|
|
3129
|
+
contacts: createContactsAdapter(client),
|
|
3130
|
+
notes: createNotesAdapter(client),
|
|
3131
|
+
tasks: createTasksAdapter(client),
|
|
3132
|
+
groups: createGroupsAdapter(client)
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
//#endregion
|
|
3136
|
+
//#region src/contacts/PortalContactsApiProvider.tsx
|
|
3137
|
+
function PortalContactsApiProvider({ children }) {
|
|
3138
|
+
const client = require_PortalTenantClientProvider.usePortalTenantClient();
|
|
3139
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactsApiProvider, {
|
|
3140
|
+
value: (0, react.useMemo)(() => createPortalContactsDomainApiAdapter(client), [client]),
|
|
3141
|
+
children
|
|
3142
|
+
});
|
|
3143
|
+
}
|
|
3144
|
+
//#endregion
|
|
3145
|
+
//#region ../../contacts/core/src/translation-adapter.ts
|
|
3146
|
+
const contactsDomain = require_static_dict_adapter.createDomainTranslations({
|
|
3147
|
+
fallback: {
|
|
3148
|
+
breadcrumb: "Contacts",
|
|
3149
|
+
breadcrumb_new: "New Contact",
|
|
3150
|
+
contact_label: "Contact",
|
|
3151
|
+
add: "Add",
|
|
3152
|
+
add_contact: "Add Contact",
|
|
3153
|
+
adding: "Adding...",
|
|
3154
|
+
update_contact: "Update Contact",
|
|
3155
|
+
saving: "Saving",
|
|
3156
|
+
cancel: "Cancel",
|
|
3157
|
+
save: "Save",
|
|
3158
|
+
delete: "Delete",
|
|
3159
|
+
delete_name: "Delete {{name}}?",
|
|
3160
|
+
delete_confirm_permanent: "This permanently removes the contact and all of their notes, tasks, and activity. This can't be undone.",
|
|
3161
|
+
delete_contact: "Delete contact",
|
|
3162
|
+
deleting: "Deleting...",
|
|
3163
|
+
search_placeholder: "Search contacts",
|
|
3164
|
+
error_loading: "Couldn't load contact",
|
|
3165
|
+
error_loading_description: "This contact may have been deleted, you may not have permission to view it, or there was a network error. Try selecting them again.",
|
|
3166
|
+
error_loading_list: "Couldn't load contacts. Pull down to retry.",
|
|
3167
|
+
error_generic: "Something went wrong. Please try again later.",
|
|
3168
|
+
loading: "Loading...",
|
|
3169
|
+
loading_more: "Loading more...",
|
|
3170
|
+
searching: "Searching...",
|
|
3171
|
+
people_count: "{{count}} people across your network",
|
|
3172
|
+
person_count: "{{count}} person across your network",
|
|
3173
|
+
no_contacts_search: "No contacts match \"{{term}}\".",
|
|
3174
|
+
no_contacts_yet: "No contacts yet.",
|
|
3175
|
+
no_contacts_filter: "No contacts match this filter.",
|
|
3176
|
+
empty_select_contact: "Select a contact",
|
|
3177
|
+
empty_select_description: "Pick someone from your list on the left to see their details, notes, and tasks.",
|
|
3178
|
+
tab_tasks: "Tasks",
|
|
3179
|
+
tab_notes: "Notes",
|
|
3180
|
+
edit_profile: "Edit profile",
|
|
3181
|
+
editing_name: "Editing {{name}}",
|
|
3182
|
+
profile_edit_description: "Update details below. Your contact card above is paused while you edit.",
|
|
3183
|
+
new_task: "New task",
|
|
3184
|
+
last_updated: "Last updated {{date}}",
|
|
3185
|
+
label_email: "Email",
|
|
3186
|
+
label_phone: "Phone",
|
|
3187
|
+
label_address: "Address",
|
|
3188
|
+
label_call: "Call",
|
|
3189
|
+
not_set: "Not set",
|
|
3190
|
+
more_actions: "More actions",
|
|
3191
|
+
back_to_contacts: "Back to contacts",
|
|
3192
|
+
back_to_tasks: "Back to Tasks",
|
|
3193
|
+
filter_all: "All",
|
|
3194
|
+
filter_leads: "Leads",
|
|
3195
|
+
filter_customers: "Customers",
|
|
3196
|
+
note_placeholder: "Leave a note about this contact…",
|
|
3197
|
+
note_body_aria: "Note body",
|
|
3198
|
+
add_note: "Add note",
|
|
3199
|
+
tip_save_shortcut_mac: "Tip: ⌘ + Enter to save",
|
|
3200
|
+
tip_save_shortcut_other: "Tip: Ctrl + Enter to save",
|
|
3201
|
+
task_describe_placeholder: "Describe the task…",
|
|
3202
|
+
task_description_aria: "Task description",
|
|
3203
|
+
task_discard_aria: "Discard task",
|
|
3204
|
+
add_task: "Add task",
|
|
3205
|
+
quick_today: "Today",
|
|
3206
|
+
quick_tomorrow: "Tomorrow",
|
|
3207
|
+
quick_next_week: "Next week",
|
|
3208
|
+
edit_contact_name: "Edit {{name}}",
|
|
3209
|
+
save_changes: "Save changes",
|
|
3210
|
+
add_field: "Add {{field}}",
|
|
3211
|
+
copy_field: "Copy {{field}}",
|
|
3212
|
+
copied_field: "Copied {{field}}",
|
|
3213
|
+
attachment_one: "1 attachment",
|
|
3214
|
+
attachment_other: "{{count}} attachments",
|
|
3215
|
+
note_actions: "Note actions",
|
|
3216
|
+
delete_note_title: "Delete note?",
|
|
3217
|
+
delete_note_irreversible: "This action cannot be undone.",
|
|
3218
|
+
task_yesterday: "Yesterday",
|
|
3219
|
+
task_days_ago_one: "1 day ago",
|
|
3220
|
+
task_days_ago_other: "{{count}} days ago",
|
|
3221
|
+
task_overdue_due: "Overdue · {{date}}",
|
|
3222
|
+
task_due: "Due {{date}}",
|
|
3223
|
+
mark_as_open: "Mark as open",
|
|
3224
|
+
mark_as_completed: "Mark as completed",
|
|
3225
|
+
task_actions: "Task actions",
|
|
3226
|
+
delete_task_title: "Delete task?",
|
|
3227
|
+
delete_task_confirm: "Are you sure you want to delete this task? This action cannot be undone."
|
|
3228
|
+
},
|
|
3229
|
+
loaders: {
|
|
3230
|
+
de: () => Promise.resolve().then(() => require("./de-CPL2Xpzo.cjs")).then((m) => m.default),
|
|
3231
|
+
el: () => Promise.resolve().then(() => require("./el-Q7NXrBp2.cjs")).then((m) => m.default),
|
|
3232
|
+
es: () => Promise.resolve().then(() => require("./es-ju_cXEC3.cjs")).then((m) => m.default),
|
|
3233
|
+
fr: () => Promise.resolve().then(() => require("./fr-Dvolparc.cjs")).then((m) => m.default),
|
|
3234
|
+
he: () => Promise.resolve().then(() => require("./he-Bbmjw5fF.cjs")).then((m) => m.default),
|
|
3235
|
+
hu: () => Promise.resolve().then(() => require("./hu-CDjT6dvP.cjs")).then((m) => m.default),
|
|
3236
|
+
id: () => Promise.resolve().then(() => require("./id-DYzJCnYl.cjs")).then((m) => m.default),
|
|
3237
|
+
it: () => Promise.resolve().then(() => require("./it-xV1fUEa8.cjs")).then((m) => m.default),
|
|
3238
|
+
ja: () => Promise.resolve().then(() => require("./ja-lkX4NFcg.cjs")).then((m) => m.default),
|
|
3239
|
+
ko: () => Promise.resolve().then(() => require("./ko-0v_pgldp.cjs")).then((m) => m.default),
|
|
3240
|
+
nl: () => Promise.resolve().then(() => require("./nl-BtDjIzc7.cjs")).then((m) => m.default),
|
|
3241
|
+
pl: () => Promise.resolve().then(() => require("./pl-BWEPmXSc.cjs")).then((m) => m.default),
|
|
3242
|
+
pt: () => Promise.resolve().then(() => require("./pt-C2PO_Mj1.cjs")).then((m) => m.default),
|
|
3243
|
+
ro: () => Promise.resolve().then(() => require("./ro-BQNdxJER.cjs")).then((m) => m.default),
|
|
3244
|
+
ru: () => Promise.resolve().then(() => require("./ru-BMlm0Iik.cjs")).then((m) => m.default),
|
|
3245
|
+
th: () => Promise.resolve().then(() => require("./th-Dh70cqMt.cjs")).then((m) => m.default),
|
|
3246
|
+
tl: () => Promise.resolve().then(() => require("./tl-Ba-YEt8X.cjs")).then((m) => m.default),
|
|
3247
|
+
tr: () => Promise.resolve().then(() => require("./tr-Dh9kUXz3.cjs")).then((m) => m.default),
|
|
3248
|
+
zh_CN: () => Promise.resolve().then(() => require("./zh_CN-W83ekXLJ.cjs")).then((m) => m.default),
|
|
3249
|
+
zh_TW: () => Promise.resolve().then(() => require("./zh_TW-CJVVqPeS.cjs")).then((m) => m.default)
|
|
3250
|
+
}
|
|
3251
|
+
});
|
|
3252
|
+
function createContactsTranslationAdapter(locale, dict) {
|
|
3253
|
+
return require_static_dict_adapter.createStaticDictAdapter(locale, dict);
|
|
3254
|
+
}
|
|
3255
|
+
//#endregion
|
|
3256
|
+
//#region src/providers/ContactsTranslationBridge.tsx
|
|
3257
|
+
function ContactsTranslationBridge({ children }) {
|
|
3258
|
+
const { locale } = require_static_dict_adapter.useActiveLocale();
|
|
3259
|
+
const dict = require_static_dict_adapter.useDomainDict(contactsDomain, locale);
|
|
3260
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactsTranslationProvider, {
|
|
3261
|
+
value: (0, react.useMemo)(() => createContactsTranslationAdapter(locale, dict), [locale, dict]),
|
|
3262
|
+
children
|
|
3263
|
+
});
|
|
3264
|
+
}
|
|
3265
|
+
//#endregion
|
|
3266
|
+
//#region src/screens/ContactsScreen.tsx
|
|
3267
|
+
const QUERY_KEY_PREFIX = "sdk-contacts";
|
|
3268
|
+
function ContactBrowseView({ selectedContactId, onSelect, onAdd }) {
|
|
3269
|
+
const countriesAdapter = require_countries_api_context.useCountriesApi();
|
|
3270
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RepContactsLayout, {
|
|
3271
|
+
selectedContactId,
|
|
3272
|
+
onSelect,
|
|
3273
|
+
onAdd,
|
|
3274
|
+
queryKeyPrefix: QUERY_KEY_PREFIX,
|
|
3275
|
+
getCountries: (0, react.useCallback)(async () => {
|
|
3276
|
+
return (await countriesAdapter.listCountries()).countries.map((c) => ({
|
|
3277
|
+
id: 0,
|
|
3278
|
+
name: c.name,
|
|
3279
|
+
iso: c.code
|
|
3280
|
+
}));
|
|
3281
|
+
}, [countriesAdapter])
|
|
3282
|
+
});
|
|
3283
|
+
}
|
|
3284
|
+
const CREATE_DEFAULT_VALUES = {
|
|
3285
|
+
first_name: "",
|
|
3286
|
+
last_name: "",
|
|
3287
|
+
email: "",
|
|
3288
|
+
phone: "",
|
|
3289
|
+
status: "new",
|
|
3290
|
+
address: "",
|
|
3291
|
+
city: "",
|
|
3292
|
+
state: "",
|
|
3293
|
+
postal_code: "",
|
|
3294
|
+
country_code: null,
|
|
3295
|
+
language_code: null,
|
|
3296
|
+
metadata: {},
|
|
3297
|
+
affiliate: {}
|
|
3298
|
+
};
|
|
3299
|
+
function ContactCreateView({ onNavigate, onCreateContact }) {
|
|
3300
|
+
const formRef = (0, react.useRef)(null);
|
|
3301
|
+
const countriesAdapter = require_countries_api_context.useCountriesApi();
|
|
3302
|
+
const { data: countriesResponse } = (0, _tanstack_react_query.useQuery)({
|
|
3303
|
+
queryKey: require_query_keys.storeKeys.countries(),
|
|
3304
|
+
queryFn: () => countriesAdapter.listCountries()
|
|
3305
|
+
});
|
|
3306
|
+
const countryOptions = (0, react.useMemo)(() => (countriesResponse?.countries ?? []).map((c) => ({
|
|
3307
|
+
name: c.name,
|
|
3308
|
+
value: c.code
|
|
3309
|
+
})).sort((a, b) => a.name.localeCompare(b.name)), [countriesResponse]);
|
|
3310
|
+
const methods = require_src.useZodForm(createContactFormSchema, { defaultValues: CREATE_DEFAULT_VALUES });
|
|
3311
|
+
const mutation = useCreateContactMutation(QUERY_KEY_PREFIX, { onSuccess: (data) => {
|
|
3312
|
+
onCreateContact?.();
|
|
3313
|
+
const newContactId = data?.contact?.id;
|
|
3314
|
+
if (newContactId) onNavigate({
|
|
3315
|
+
view: "browse",
|
|
3316
|
+
selectedContactId: String(newContactId)
|
|
3317
|
+
});
|
|
3318
|
+
else onNavigate({
|
|
3319
|
+
view: "browse",
|
|
3320
|
+
selectedContactId: null
|
|
3321
|
+
});
|
|
3322
|
+
} });
|
|
3323
|
+
const { mutate } = mutation;
|
|
3324
|
+
const onSubmit = (0, react.useMemo)(() => methods.handleSubmit((data) => {
|
|
3325
|
+
const { affiliate, ...contactFields } = data;
|
|
3326
|
+
const payload = {};
|
|
3327
|
+
for (const [key, value] of Object.entries(contactFields)) if (value !== "" && value !== null && value !== void 0) payload[key] = value;
|
|
3328
|
+
if (affiliate && Object.keys(affiliate).length > 0) payload.affiliate = affiliate;
|
|
3329
|
+
mutate(payload);
|
|
3330
|
+
}, () => {}), [methods, mutate]);
|
|
3331
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactCreateScreen, {
|
|
3332
|
+
onNavigateToList: (0, react.useCallback)(() => {
|
|
3333
|
+
onNavigate({
|
|
3334
|
+
view: "browse",
|
|
3335
|
+
selectedContactId: null
|
|
3336
|
+
});
|
|
3337
|
+
}, [onNavigate]),
|
|
3338
|
+
onSubmit: () => formRef.current?.requestSubmit(),
|
|
3339
|
+
isPending: mutation.isPending,
|
|
3340
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_hook_form.FormProvider, {
|
|
3341
|
+
...methods,
|
|
3342
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3343
|
+
className: "mx-auto max-w-7xl space-y-12 md:px-10 md:py-8",
|
|
3344
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("form", {
|
|
3345
|
+
ref: formRef,
|
|
3346
|
+
onSubmit,
|
|
3347
|
+
className: "mx-auto space-y-6 lg:max-w-2xl",
|
|
3348
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactDetailsForm, { countries: countryOptions })
|
|
3349
|
+
})
|
|
3350
|
+
})
|
|
3351
|
+
})
|
|
3352
|
+
});
|
|
3353
|
+
}
|
|
3354
|
+
function ContactsScreen({ onContactSelect, onCreateContact, background, textColor, accentColor, padding, borderRadius, defaultViewMode, ...divProps }) {
|
|
3355
|
+
const [nav, setNav] = (0, react.useState)({
|
|
3356
|
+
view: "browse",
|
|
3357
|
+
selectedContactId: null
|
|
3358
|
+
});
|
|
3359
|
+
const handleSelect = (0, react.useCallback)((id) => {
|
|
3360
|
+
setNav({
|
|
3361
|
+
view: "browse",
|
|
3362
|
+
selectedContactId: id
|
|
3363
|
+
});
|
|
3364
|
+
if (id) onContactSelect?.(id);
|
|
3365
|
+
}, [onContactSelect]);
|
|
3366
|
+
const handleAdd = (0, react.useCallback)(() => {
|
|
3367
|
+
setNav({ view: "new" });
|
|
3368
|
+
}, []);
|
|
3369
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactsTranslationBridge, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PortalContactsApiProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3370
|
+
...divProps,
|
|
3371
|
+
className: `h-full ${divProps.className ?? ""}`,
|
|
3372
|
+
children: [nav.view === "browse" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactBrowseView, {
|
|
3373
|
+
selectedContactId: nav.selectedContactId,
|
|
3374
|
+
onSelect: handleSelect,
|
|
3375
|
+
onAdd: handleAdd
|
|
3376
|
+
}), nav.view === "new" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactCreateView, {
|
|
3377
|
+
onNavigate: setNav,
|
|
3378
|
+
onCreateContact
|
|
3379
|
+
})]
|
|
3380
|
+
}) }) });
|
|
3381
|
+
}
|
|
3382
|
+
const contactsScreenPropertySchema = {
|
|
3383
|
+
widgetType: "ContactsScreen",
|
|
3384
|
+
displayName: "Contacts Screen",
|
|
3385
|
+
tabsConfig: [{
|
|
3386
|
+
id: "styling",
|
|
3387
|
+
label: "Styling"
|
|
3388
|
+
}],
|
|
3389
|
+
fields: []
|
|
3390
|
+
};
|
|
3391
|
+
//#endregion
|
|
3392
|
+
Object.defineProperty(exports, "ContactsScreen", {
|
|
3393
|
+
enumerable: true,
|
|
3394
|
+
get: function() {
|
|
3395
|
+
return ContactsScreen;
|
|
3396
|
+
}
|
|
3397
|
+
});
|
|
3398
|
+
Object.defineProperty(exports, "contactsScreenPropertySchema", {
|
|
3399
|
+
enumerable: true,
|
|
3400
|
+
get: function() {
|
|
3401
|
+
return contactsScreenPropertySchema;
|
|
3402
|
+
}
|
|
3403
|
+
});
|
|
3404
|
+
|
|
3405
|
+
//# sourceMappingURL=ContactsScreen-D8M6J3nC.cjs.map
|