Browse Source

Nc fix/integration bug fixes (#9150)

* fix(nc-gui): always show edit connection tab on clicking data source

* fix(nc-gui): show deleted workspace user info in connection list

* fix(nc-gui): show tooltip on hover deleted user details

* fix(nc-gui): some review changes

* fix(nc-gui): sync modal cleanup

* fix: supported docs label

* fix(nc-gui): pg icon issue in data source list

* fix(nc-gui): new integration page ui changes

* fix(nc-gui): handle upvote

* fix(nc-gui): add integration category icons

* fix(nc-gui): add request new integration in other category

* fix(nc-gui): focus request integration input on open

* fix(nc-gui): integration tab left spacing issue

* fix(nc-gui): integration tab list center aligned

* misc: minor changes

* fix(nc-gui): user should able to upvote on cliking tiles

* fix(nc-gui): add remaining integrations

* fix(nc-gui): add missing integration icons

* fix(nc-gui): trigger test connection on adding new connection from create source

* fix(nc-gui): integration list modal ui changes

* fix(nc-gui): remove integration type badge border

* fix(nc-gui): show colored integration icon on hover

* fix(nc-gui): integration upvote btn shadow issue

* fix(nc-gui): some pr review changes

* fix(nc-gui): move logic part in script

* chore(nc-gui): lint

---------

Co-authored-by: Raju Udava <86527202+dstala@users.noreply.github.com>
pull/9170/head
Ramesh Mane 5 months ago committed by GitHub
parent
commit
2a2a4c9cf0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 15
      packages/nc-gui/assets/nc-icons/bit-bucket.svg
  2. 12
      packages/nc-gui/assets/nc-icons/data-bricks.svg
  3. 7
      packages/nc-gui/assets/nc-icons/doller-sign.svg
  4. 3
      packages/nc-gui/assets/nc-icons/dropbox.svg
  5. 6
      packages/nc-gui/assets/nc-icons/globe.svg
  6. 14
      packages/nc-gui/assets/nc-icons/gmail.svg
  7. 3
      packages/nc-gui/assets/nc-icons/greenhouse.svg
  8. 3
      packages/nc-gui/assets/nc-icons/heart.svg
  9. 10
      packages/nc-gui/assets/nc-icons/intercom.svg
  10. 12
      packages/nc-gui/assets/nc-icons/lever.svg
  11. 10
      packages/nc-gui/assets/nc-icons/microsoft-dynamics-365.svg
  12. 10
      packages/nc-gui/assets/nc-icons/mssql-server.svg
  13. 16
      packages/nc-gui/assets/nc-icons/multi-file.svg
  14. 10
      packages/nc-gui/assets/nc-icons/oracle.svg
  15. 3
      packages/nc-gui/assets/nc-icons/pipedrive.svg
  16. 15
      packages/nc-gui/assets/nc-icons/postgresql.svg
  17. 11
      packages/nc-gui/assets/nc-icons/quickbooks.svg
  18. 5
      packages/nc-gui/assets/nc-icons/save.svg
  19. 17
      packages/nc-gui/assets/nc-icons/sql-server.svg
  20. 15
      packages/nc-gui/assets/nc-icons/telegram.svg
  21. 7
      packages/nc-gui/assets/nc-icons/view-gantt.svg
  22. 7
      packages/nc-gui/assets/nc-icons/zoho-crm.svg
  23. 12
      packages/nc-gui/components/api-client/Headers.vue
  24. 2
      packages/nc-gui/components/cell/attachment/Preview/Pdf.vue
  25. 39
      packages/nc-gui/components/dashboard/settings/DataSources.vue
  26. 2
      packages/nc-gui/components/dashboard/settings/Metadata.vue
  27. 9
      packages/nc-gui/components/dashboard/settings/UIAcl.vue
  28. 24
      packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue
  29. 25
      packages/nc-gui/components/dashboard/settings/data-sources/SupportedDocs.vue
  30. 3
      packages/nc-gui/components/project/AllTables.vue
  31. 4
      packages/nc-gui/components/smartsheet/details/Webhooks.vue
  32. 6
      packages/nc-gui/components/webhook/index.vue
  33. 839
      packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
  34. 533
      packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue
  35. 105
      packages/nc-gui/components/workspace/integrations/List.vue
  36. 25
      packages/nc-gui/components/workspace/integrations/SupportedDocs.vue
  37. 10
      packages/nc-gui/components/workspace/integrations/view.vue
  38. 2
      packages/nc-gui/composables/useUserSorts.ts
  39. 50
      packages/nc-gui/lang/en.json
  40. 75
      packages/nc-gui/lib/enums.ts
  41. 2
      packages/nc-gui/utils/baseCreateUtils.ts
  42. 48
      packages/nc-gui/utils/iconUtils.ts
  43. 485
      packages/nc-gui/utils/syncDataUtils.ts

15
packages/nc-gui/assets/nc-icons/bit-bucket.svg

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="30" viewBox="0 0 32 30" fill="none">
<g clip-path="url(#clip0_858_14572)">
<path d="M1.03852 0.562585C0.735629 0.558679 0.446516 0.688907 0.248732 0.918336C0.0509483 1.14777 -0.0352608 1.45291 0.0132305 1.75192L4.36557 28.1735C4.4775 28.8409 5.05249 29.3316 5.7292 29.3372H26.6091C27.1171 29.3438 27.5532 28.9774 27.6344 28.476L31.9868 1.75704C32.0353 1.45804 31.9491 1.15289 31.7513 0.923463C31.5535 0.694034 31.2644 0.563805 30.9615 0.567712L1.03852 0.562585ZM19.3655 19.6585H12.7011L10.8966 10.231H20.9803L19.3655 19.6585Z" fill="#2684FF"/>
<path d="M30.5762 10.2178H20.9615L19.348 19.6378H12.689L4.82617 28.9707C5.07539 29.1862 5.39313 29.306 5.72258 29.3087H26.591C27.0985 29.3153 27.5343 28.9492 27.6155 28.4482L30.5762 10.2178Z" fill="url(#paint0_linear_858_14572)"/>
</g>
<defs>
<linearGradient id="paint0_linear_858_14572" x1="2802.14" y1="274.018" x2="1691.48" y2="1851" gradientUnits="userSpaceOnUse">
<stop offset="0.18" stop-color="#0052CC"/>
<stop offset="1" stop-color="#2684FF"/>
</linearGradient>
<clipPath id="clip0_858_14572">
<rect width="32" height="28.875" fill="white" transform="translate(0 0.5625)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

12
packages/nc-gui/assets/nc-icons/data-bricks.svg

@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<g clip-path="url(#clip0_858_13877)">
<path d="M0.527344 18.7482V23.9059L16.0003 32L31.4732 23.9059V18.7482L26.0144 15.9624L31.4732 13.1012V7.98118V7.94353L31.4356 7.98118L16.0003 0L0.564991 7.94353H0.527344V13.1012L5.98617 15.9624L0.527344 18.7482Z" fill="#DB1905"/>
<path d="M0.527344 18.7478L16.0003 26.8419L31.4732 18.7478L26.0144 15.9619L16.0003 21.1949L5.98617 15.9619L0.527344 18.7478Z" fill="#FF5224"/>
<path d="M31.4732 7.98118L16.0003 16.0753L0.527344 7.98118L16.0003 0L31.4732 7.98118Z" fill="#FF5224"/>
</g>
<defs>
<clipPath id="clip0_858_13877">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 759 B

7
packages/nc-gui/assets/nc-icons/doller-sign.svg

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M8 0.666992V15.3337" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round"
stroke-linejoin="round" />
<path
d="M11.3333 3.33301H6.33333C5.71449 3.33301 5.121 3.57884 4.68342 4.01643C4.24583 4.45401 4 5.0475 4 5.66634C4 6.28518 4.24583 6.87867 4.68342 7.31626C5.121 7.75384 5.71449 7.99967 6.33333 7.99967H9.66667C10.2855 7.99967 10.879 8.24551 11.3166 8.68309C11.7542 9.12068 12 9.71417 12 10.333C12 10.9518 11.7542 11.5453 11.3166 11.9829C10.879 12.4205 10.2855 12.6663 9.66667 12.6663H4"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 742 B

3
packages/nc-gui/assets/nc-icons/dropbox.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="30" viewBox="0 0 32 30" fill="none">
<path d="M7.99997 1.37793L0 6.50998L7.99997 11.5666L15.9999 6.50998L7.99997 1.37793ZM23.9999 1.37793L15.9999 6.50998L23.9999 11.5666L31.9999 6.50998L23.9999 1.37793ZM0 16.6986L7.99997 21.8307L15.9999 16.6986L7.99997 11.5666L0 16.6986ZM23.9999 11.5666L15.9999 16.6986L23.9999 21.8307L31.9999 16.6986L23.9999 11.5666ZM7.99997 23.491L15.9999 28.6231L23.9999 23.491L15.9999 18.4345L7.99997 23.491Z" fill="#0062FF"/>
</svg>

After

Width:  |  Height:  |  Size: 516 B

6
packages/nc-gui/assets/nc-icons/globe.svg

@ -1,8 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="globe" clip-path="url(#clip0_184_5557)">
<path id="Vector" d="M8.00001 14.6666C11.6819 14.6666 14.6667 11.6819 14.6667 7.99998C14.6667 4.31808 11.6819 1.33331 8.00001 1.33331C4.31811 1.33331 1.33334 4.31808 1.33334 7.99998C1.33334 11.6819 4.31811 14.6666 8.00001 14.6666Z" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_2" d="M1.33334 8H14.6667" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_3" d="M8.00001 1.33331C9.66753 3.15888 10.6152 5.528 10.6667 7.99998C10.6152 10.472 9.66753 12.8411 8.00001 14.6666C6.33249 12.8411 5.38484 10.472 5.33334 7.99998C5.38484 5.528 6.33249 3.15888 8.00001 1.33331V1.33331Z" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector" d="M8.00001 14.6666C11.6819 14.6666 14.6667 11.6819 14.6667 7.99998C14.6667 4.31808 11.6819 1.33331 8.00001 1.33331C4.31811 1.33331 1.33334 4.31808 1.33334 7.99998C1.33334 11.6819 4.31811 14.6666 8.00001 14.6666Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_2" d="M1.33334 8H14.6667" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_3" d="M8.00001 1.33331C9.66753 3.15888 10.6152 5.528 10.6667 7.99998C10.6152 10.472 9.66753 12.8411 8.00001 14.6666C6.33249 12.8411 5.38484 10.472 5.33334 7.99998C5.38484 5.528 6.33249 3.15888 8.00001 1.33331V1.33331Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_184_5557">

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

14
packages/nc-gui/assets/nc-icons/gmail.svg

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="24" viewBox="0 0 32 24" fill="none">
<g clip-path="url(#clip0_858_13960)">
<path d="M2.18182 23.9998H7.27273V11.6362L0 6.18164V21.818C0 23.0253 0.978182 23.9998 2.18182 23.9998Z" fill="#4285F4"/>
<path d="M24.7275 23.9998H29.8184C31.0257 23.9998 32.0003 23.0216 32.0003 21.818V6.18164L24.7275 11.6362" fill="#34A853"/>
<path d="M24.7275 2.18215V11.6367L32.0003 6.18215V3.27306C32.0003 0.574876 28.9203 -0.963306 26.7639 0.654875" fill="#FBBC04"/>
<path d="M7.27246 11.6362V2.18164L15.9997 8.7271L24.727 2.18164V11.6362L15.9997 18.1816" fill="#EA4335"/>
<path d="M0 3.27306V6.18215L7.27273 11.6367V2.18215L5.23636 0.654875C3.07636 -0.963306 0 0.574876 0 3.27306Z" fill="#C5221F"/>
</g>
<defs>
<clipPath id="clip0_858_13960">
<rect width="32" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 895 B

3
packages/nc-gui/assets/nc-icons/greenhouse.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="32" viewBox="0 0 18 32" fill="none">
<path d="M9.26916 31.9355C5.0929 31.9966 1.68765 28.4146 1.76597 24.2757C1.79907 22.2849 2.61686 20.3876 4.04149 18.9967C5.46611 17.6057 7.38233 16.8334 9.37335 16.8479C13.3513 16.8587 16.6818 20.2726 16.7537 24.2541C16.8298 28.472 13.3527 32.0002 9.26916 31.9355ZM15.4092 24.4395C15.4344 21.0206 12.701 18.2297 9.30149 18.2038C5.90199 18.178 3.13123 20.9272 3.10249 24.3454C3.08486 25.9882 3.7203 27.5707 4.86913 28.7451C6.01796 29.9196 7.58613 30.5897 9.22892 30.6083C12.5911 30.6507 15.3841 27.8627 15.4092 24.4395ZM1.78106 9.08749C1.77443 8.27131 1.9293 7.46188 2.23677 6.70579C2.54424 5.94971 2.99822 5.26192 3.57259 4.68201C4.14696 4.1021 4.83036 3.64152 5.58346 3.32681C6.33655 3.01209 7.14446 2.84945 7.96066 2.84825C11.3523 2.84178 14.146 5.64272 14.151 9.05372C14.1568 12.5136 11.4069 15.3145 7.99515 15.3246C4.53314 15.3339 1.79112 12.5804 1.78106 9.08749ZM3.13986 9.04869C3.13368 9.68134 3.25217 10.309 3.48856 10.8959C3.72496 11.4827 4.07463 12.0173 4.51761 12.469C4.96059 12.9207 5.48821 13.2808 6.07034 13.5286C6.65247 13.7764 7.27771 13.9071 7.91037 13.9133C8.54302 13.9195 9.17069 13.801 9.75755 13.5646C10.3444 13.3282 10.879 12.9786 11.3307 12.5356C11.7824 12.0926 12.1425 11.565 12.3903 10.9828C12.6381 10.4007 12.7688 9.77547 12.775 9.14282C12.7923 6.44607 10.6581 4.24728 8.00378 4.22572C5.33577 4.20704 3.16069 6.36272 3.13986 9.04869ZM11.6167 1.26167C11.619 0.926071 11.7545 0.605124 11.9934 0.369437C12.1117 0.252736 12.2518 0.160479 12.4058 0.0979338C12.5598 0.0353886 12.7245 0.00377984 12.8907 0.00491219C13.0569 0.00604454 13.2212 0.0398959 13.3743 0.104533C13.5274 0.169171 13.6662 0.263329 13.7829 0.381631C13.8996 0.499933 13.9919 0.640063 14.0544 0.794019C14.117 0.947976 14.1486 1.11274 14.1475 1.27892C14.1493 1.44676 14.1175 1.61327 14.0541 1.76867C13.9907 1.92408 13.8968 2.06524 13.7781 2.18386C13.6593 2.30248 13.5181 2.39617 13.3626 2.45942C13.2071 2.52268 13.0406 2.55422 12.8727 2.5522C12.1614 2.54717 11.6167 1.9867 11.6167 1.26167Z" fill="#38B2A7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

3
packages/nc-gui/assets/nc-icons/heart.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M13.8931 3.07357C13.5526 2.73291 13.1483 2.46267 12.7033 2.2783C12.2584 2.09392 11.7814 1.99902 11.2998 1.99902C10.8181 1.99902 10.3412 2.09392 9.89618 2.2783C9.45121 2.46267 9.04692 2.73291 8.70642 3.07357L7.99975 3.78024L7.29309 3.07357C6.60529 2.38578 5.67244 1.99938 4.69975 1.99938C3.72706 1.99938 2.79422 2.38578 2.10642 3.07357C1.41863 3.76137 1.03223 4.69422 1.03223 5.66691C1.03223 6.6396 1.41863 7.57245 2.10642 8.26024L2.81309 8.96691L7.99975 14.1536L13.1864 8.96691L13.8931 8.26024C14.2337 7.91974 14.504 7.51545 14.6884 7.07048C14.8727 6.6255 14.9676 6.14857 14.9676 5.66691C14.9676 5.18525 14.8727 4.70831 14.6884 4.26334C14.504 3.81836 14.2337 3.41408 13.8931 3.07357V3.07357Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 900 B

10
packages/nc-gui/assets/nc-icons/intercom.svg

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<g clip-path="url(#clip0_858_14755)">
<path d="M27.7334 17.5935C27.7303 17.8744 27.6166 18.1428 27.4169 18.3403C27.2172 18.5379 26.9476 18.6487 26.6667 18.6487C26.3858 18.6487 26.1162 18.5379 25.9165 18.3403C25.7168 18.1428 25.603 17.8744 25.6 17.5935V8C25.603 7.7191 25.7168 7.45073 25.9165 7.25317C26.1162 7.05561 26.3858 6.9448 26.6667 6.9448C26.9476 6.9448 27.2172 7.05561 27.4169 7.25317C27.6166 7.45073 27.7303 7.7191 27.7334 8V17.5935ZM27.3611 24.2701C27.1963 24.4112 23.2449 27.727 16 27.727C8.75525 27.727 4.804 24.4111 4.63912 24.27C4.42437 24.0858 4.29155 23.8239 4.26986 23.5419C4.24818 23.2598 4.33941 22.9807 4.5235 22.7659C4.70719 22.5514 4.96845 22.4186 5.24996 22.3965C5.53148 22.3744 5.81025 22.4649 6.02513 22.6481C6.08775 22.7006 9.61625 25.5935 16.0001 25.5935C22.4641 25.5935 25.938 22.6798 25.9724 22.6504C26.4188 22.267 27.0934 22.3185 27.4765 22.766C27.6606 22.9808 27.7519 23.2599 27.7303 23.542C27.7087 23.824 27.5759 24.086 27.3611 24.2701ZM4.26662 8C4.26967 7.7191 4.38339 7.45073 4.5831 7.25317C4.78281 7.05561 5.05239 6.9448 5.33331 6.9448C5.61423 6.9448 5.88381 7.05561 6.08353 7.25317C6.28324 7.45073 6.39696 7.7191 6.4 8V17.5935C6.39696 17.8744 6.28324 18.1428 6.08353 18.3403C5.88381 18.5379 5.61423 18.6487 5.33331 18.6487C5.05239 18.6487 4.78281 18.5379 4.5831 18.3403C4.38339 18.1428 4.26967 17.8744 4.26662 17.5935V8ZM9.60013 5.86662C9.60441 5.58657 9.71867 5.31943 9.91824 5.1229C10.1178 4.92636 10.3867 4.81621 10.6667 4.81621C10.9468 4.81621 11.2157 4.92636 11.4153 5.1229C11.6148 5.31943 11.7291 5.58657 11.7334 5.86662V20.1168C11.7291 20.3968 11.6148 20.6639 11.4153 20.8605C11.2157 21.057 10.9468 21.1672 10.6667 21.1672C10.3867 21.1672 10.1178 21.057 9.91824 20.8605C9.71867 20.6639 9.60441 20.3968 9.60013 20.1168V5.86662ZM14.9334 5.32688C14.9334 5.1868 14.9609 5.04809 15.0145 4.91868C15.0681 4.78926 15.1467 4.67167 15.2457 4.57262C15.3448 4.47357 15.4624 4.39501 15.5918 4.34141C15.7212 4.28781 15.8599 4.26023 16 4.26025C16.1401 4.26022 16.2788 4.28778 16.4082 4.34137C16.5377 4.39496 16.6553 4.47353 16.7543 4.57258C16.8534 4.67163 16.932 4.78922 16.9856 4.91865C17.0392 5.04807 17.0668 5.18679 17.0667 5.32688V20.7935C17.0637 21.0744 16.95 21.3428 16.7503 21.5403C16.5506 21.7379 16.281 21.8487 16.0001 21.8487C15.7191 21.8487 15.4496 21.7379 15.2499 21.5403C15.0501 21.3428 14.9364 21.0744 14.9334 20.7935V5.32688ZM20.2668 5.86662C20.271 5.58657 20.3853 5.31943 20.5849 5.1229C20.7844 4.92636 21.0533 4.81621 21.3334 4.81621C21.6135 4.81621 21.8823 4.92636 22.0819 5.1229C22.2814 5.31943 22.3957 5.58657 22.4 5.86662V20.1168C22.3957 20.3968 22.2814 20.6639 22.0819 20.8605C21.8823 21.057 21.6135 21.1672 21.3334 21.1672C21.0533 21.1672 20.7844 21.057 20.5849 20.8605C20.3853 20.6639 20.271 20.3968 20.2668 20.1168V5.86662ZM28 0H4C1.79087 0 0 1.79087 0 4V28C0 30.209 1.79087 32 4 32H28C30.2091 32 32 30.209 32 28V4C32 1.79087 30.2091 0 28 0Z" fill="#1F8DED"/>
</g>
<defs>
<clipPath id="clip0_858_14755">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

12
packages/nc-gui/assets/nc-icons/lever.svg

@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<g clip-path="url(#clip0_858_14536)">
<path d="M31.6989 30.3151L25.6749 24.29C25.4846 24.1035 25.2288 23.999 24.9624 23.999C24.6959 23.999 24.4401 24.1035 24.2498 24.29L18.2258 30.3151C18.0821 30.452 17.9836 30.6295 17.9432 30.8238C17.9029 31.0182 17.9227 31.2202 18 31.4031C18.0773 31.5859 18.2084 31.7408 18.376 31.8473C18.5435 31.9538 18.7395 32.0067 18.9378 31.9991H30.9869C31.1841 32.004 31.3782 31.9494 31.544 31.8425C31.7097 31.7356 31.8395 31.5812 31.9163 31.3995C31.9931 31.2178 32.0135 31.0172 31.9747 30.8238C31.9359 30.6304 31.8398 30.4531 31.6989 30.3151Z" fill="#C3C6CC"/>
<path d="M31.8987 8.67475L30.0117 3.07272C29.9265 2.81938 29.7908 2.58598 29.6127 2.38672L0.228516 31.7699C0.400958 31.9242 0.626211 32.0062 0.857518 31.9989H9.20356C9.43682 32 9.66801 31.9551 9.88389 31.8667C10.0998 31.7784 10.2961 31.6483 10.4616 31.4839L31.4987 10.4468C31.7273 10.2202 31.8865 9.93309 31.9573 9.61914C32.0282 9.30519 32.0078 8.97752 31.8987 8.67475Z" fill="#E1E3E6"/>
<path d="M28.9266 1.98609L23.3246 0.100081C23.0218 -0.00908496 22.6941 -0.0294319 22.3802 0.0414374C22.0662 0.112307 21.7791 0.271439 21.5526 0.500082L0.514457 21.5372C0.183514 21.872 -0.00130248 22.3244 0.000454166 22.7952V31.1412C-0.00689207 31.3725 0.0751158 31.5978 0.229455 31.7702L29.6136 2.38712C29.4141 2.2084 29.1803 2.07197 28.9266 1.98609Z" fill="#C3C6CC"/>
</g>
<defs>
<clipPath id="clip0_858_14536">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

10
packages/nc-gui/assets/nc-icons/microsoft-dynamics-365.svg

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="32" viewBox="0 0 20 32" fill="none">
<g clip-path="url(#clip0_858_14322)">
<path d="M0 32L6.234 14.108L0 10.02V32ZM19.716 20.934V10.776L0 32L19.716 20.934ZM0 0V8.574L12.866 14.884L18.846 10.02L0 0Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_858_14322">
<rect width="20" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 413 B

10
packages/nc-gui/assets/nc-icons/mssql-server.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

16
packages/nc-gui/assets/nc-icons/multi-file.svg

@ -1,8 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.25 3H5.25C4.91848 3 4.60054 3.12643 4.36612 3.35147C4.1317 3.57652 4 3.88174 4 4.2V13.8C4 14.1183 4.1317 14.4235 4.36612 14.6485C4.60054 14.8736 4.91848 15 5.25 15H12.75C13.0815 15 13.3995 14.8736 13.6339 14.6485C13.8683 14.4235 14 14.1183 14 13.8V6.6L10.25 3Z" stroke="#FC3AC6" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 13H3.25C2.91848 13 2.60054 12.8736 2.36612 12.6485C2.1317 12.4235 2 12.1183 2 11.8V2.2C2 1.88174 2.1317 1.57652 2.36612 1.35147C2.60054 1.12643 2.91848 1 3.25 1H8.25L10.5 3" stroke="#FC3AC6" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6666 11.3333H6.33325" stroke="#FC3AC6" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6666 8.66669H6.33325" stroke="#FC3AC6" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.66659 6H6.99992H6.33325" stroke="#FC3AC6" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.3333 3.33331V7H13.4999" stroke="#FC3AC6" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M10.25 3H5.25C4.91848 3 4.60054 3.12643 4.36612 3.35147C4.1317 3.57652 4 3.88174 4 4.2V13.8C4 14.1183 4.1317 14.4235 4.36612 14.6485C4.60054 14.8736 4.91848 15 5.25 15H12.75C13.0815 15 13.3995 14.8736 13.6339 14.6485C13.8683 14.4235 14 14.1183 14 13.8V6.6L10.25 3Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 13H3.25C2.91848 13 2.60054 12.8736 2.36612 12.6485C2.1317 12.4235 2 12.1183 2 11.8V2.2C2 1.88174 2.1317 1.57652 2.36612 1.35147C2.60054 1.12643 2.91848 1 3.25 1H8.25L10.5 3" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6663 11.333H6.33301" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6663 8.66699H6.33301" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.66634 6H6.99967H6.33301" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.333 3.33301V6.99969H13.4997" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

10
packages/nc-gui/assets/nc-icons/oracle.svg

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="6" viewBox="0 0 32 6" fill="none">
<g clip-path="url(#clip0_858_13918)">
<path d="M13.3023 3.31593H15.2834L14.2359 1.63063L12.3133 4.67774H11.4383L13.7768 1.01751C13.8785 0.86962 14.0479 0.780273 14.2359 0.780273C14.4177 0.780273 14.5871 0.86654 14.6857 1.01135L17.0334 4.67774H16.1584L15.7456 3.99683H13.7398L13.3023 3.31593ZM22.3913 3.99683V0.817247H21.6488V4.30802C21.6488 4.40353 21.6857 4.49596 21.7566 4.56682C21.8275 4.63768 21.923 4.67774 22.0277 4.67774H25.4137L25.8512 3.99683H22.3913ZM10.1073 3.42685C10.8283 3.42685 11.4137 2.84454 11.4137 2.12359C11.4137 1.40264 10.8283 0.817247 10.1074 0.817247H6.85904V4.67774H7.60127V1.49814H10.0581C10.4031 1.49814 10.6804 1.77851 10.6804 2.12359C10.6804 2.46866 10.4031 2.74903 10.0581 2.74903L7.96483 2.74595L10.1813 4.67773H11.2596L9.76845 3.42685L10.1073 3.42685ZM2.28995 4.67774C1.22421 4.67774 0.359375 3.81505 0.359375 2.74903C0.359375 1.68301 1.22422 0.817247 2.28994 0.817247H4.53382C5.59984 0.817247 6.46407 1.68301 6.46407 2.74903C6.46407 3.81506 5.59984 4.67774 4.53382 4.67774H2.28995ZM4.48391 3.99683C5.17438 3.99683 5.73358 3.43917 5.73358 2.74903C5.73358 2.05889 5.17438 1.49814 4.48391 1.49814H2.33954C1.6494 1.49814 1.08987 2.05889 1.08987 2.74903C1.08987 3.43917 1.6494 3.99683 2.33954 3.99683H4.48391ZM18.5832 4.67774C17.5171 4.67774 16.6514 3.81506 16.6514 2.74903C16.6514 1.68301 17.5171 0.817247 18.5832 0.817247H21.2482L20.8138 1.49814H18.6325C17.9423 1.49814 17.3816 2.05889 17.3816 2.74903C17.3816 3.43917 17.9423 3.99683 18.6325 3.99683H21.3098L20.8724 4.67774H18.5832ZM27.6629 3.99683C27.0929 3.99683 26.6092 3.61479 26.4613 3.08794H29.6347L30.0722 2.40704H26.4613C26.6092 1.88327 27.0929 1.49814 27.6629 1.49814H29.8411L30.2817 0.817247H27.6136C26.5475 0.817247 25.6818 1.68301 25.6818 2.74903C25.6818 3.81506 26.5475 4.67774 27.6136 4.67774H29.9028L30.3403 3.99683H27.6629ZM30.6792 1.22702C30.679 1.18248 30.6877 1.13835 30.7047 1.09717C30.7217 1.05599 30.7466 1.01858 30.7781 0.987085C30.8096 0.955588 30.8471 0.930629 30.8882 0.913643C30.9294 0.896657 30.9735 0.887979 31.0181 0.888109C31.2091 0.888109 31.3601 1.03908 31.3601 1.22702C31.3601 1.41804 31.2091 1.56901 31.0181 1.56901C30.8301 1.56901 30.6792 1.41804 30.6792 1.22702ZM31.0181 1.66452C31.2584 1.66452 31.4525 1.47042 31.4525 1.2301C31.4525 0.98978 31.2584 0.795678 31.0181 0.795678C30.7808 0.795678 30.5867 0.98978 30.5867 1.2301C30.5867 1.47042 30.7808 1.66452 31.0181 1.66452ZM30.978 0.971296C31.0458 0.971296 31.0735 0.974376 31.1044 0.986695C31.1906 1.01443 31.1998 1.09145 31.1998 1.12226C31.1998 1.12842 31.1998 1.14383 31.1937 1.16231C31.1906 1.1808 31.1783 1.21777 31.1352 1.2455C31.129 1.24858 31.1259 1.25166 31.1136 1.25783L31.2245 1.45809H31.1167L31.0181 1.27323H30.9503V1.45809H30.8548V0.971296H30.978ZM31.0119 1.19313C31.0427 1.19005 31.0735 1.19005 31.092 1.16231C31.1012 1.14999 31.1044 1.13767 31.1044 1.11918C31.1044 1.09454 31.0889 1.07297 31.0674 1.06064C31.0458 1.0514 31.0242 1.0514 30.978 1.0514H30.9503V1.19313H31.0119Z" fill="#F80000"/>
</g>
<defs>
<clipPath id="clip0_858_13918">
<rect width="32" height="5.05263" fill="white" transform="translate(0 0.473633)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

3
packages/nc-gui/assets/nc-icons/pipedrive.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="32" viewBox="0 0 26 32" fill="none">
<path d="M15.4838 0C11.9283 0 9.85422 1.59544 8.87416 2.71225C8.7602 1.75499 8.12202 0.524216 5.68328 0.524216H0.327148V6.08547H2.51518C2.87986 6.08547 2.99382 6.19943 2.99382 6.5641V32H9.33V22.4501C9.33 22.1994 9.33 21.9487 9.30721 21.7436C10.2873 22.6553 12.179 23.9088 15.142 23.9088C21.3414 23.9088 25.6719 18.9858 25.6719 11.9658C25.7175 4.80912 21.5921 0 15.4838 0ZM14.1847 18.3704C10.7659 18.3704 9.21604 15.1111 9.21604 12.0798C9.21604 7.31624 11.8143 5.60684 14.2759 5.60684C17.2616 5.60684 19.2901 8.18234 19.2901 12.0342C19.2673 16.433 16.7146 18.3704 14.1847 18.3704Z" fill="#027737"/>
</svg>

After

Width:  |  Height:  |  Size: 702 B

15
packages/nc-gui/assets/nc-icons/postgresql.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

11
packages/nc-gui/assets/nc-icons/quickbooks.svg

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<g clip-path="url(#clip0_858_14700)">
<path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32Z" fill="#2CA01C"/>
<path d="M3.85701 15.9951C3.85829 19.6124 6.77541 22.5539 10.3927 22.5859H11.3181V20.1679H10.3927C8.15013 20.7798 5.83717 19.4576 5.22661 17.215C5.22149 17.1971 5.21765 17.1791 5.21253 17.1612C4.58405 14.8892 5.89989 12.5353 8.16421 11.8812H8.62629C9.20613 11.7545 9.80645 11.7545 10.3863 11.8812H12.634V24.7183C12.6225 26.0764 13.7143 27.1875 15.0724 27.2003V9.33915H10.4068C6.76389 9.35835 3.82757 12.3267 3.84677 15.9695V15.9708L3.85701 15.9951ZM21.617 9.34171H20.6916V11.8876H21.617C23.8545 11.2771 26.1623 12.5955 26.7729 14.8316C26.778 14.8483 26.7818 14.8662 26.7869 14.8828C27.4116 17.1471 26.0996 19.4921 23.8442 20.1449H23.3821C22.8023 20.2716 22.202 20.2716 21.6221 20.1449H19.3745V7.30907C19.386 5.95099 18.2941 4.83995 16.9361 4.82715V22.7139H21.6017C25.2765 22.6563 28.209 19.6316 28.1527 15.9567C28.0964 12.3625 25.1972 9.46203 21.6017 9.40571L21.617 9.34171Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_858_14700">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

5
packages/nc-gui/assets/nc-icons/save.svg

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M12.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V3.33333C2 2.97971 2.14048 2.64057 2.39052 2.39052C2.64057 2.14048 2.97971 2 3.33333 2H10.6667L14 5.33333V12.6667C14 13.0203 13.8595 13.3594 13.6095 13.6095C13.3594 13.8595 13.0203 14 12.6667 14Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.3337 14.0003V8.66699H4.66699V14.0003" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.66699 2V5.33333H10.0003" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 782 B

17
packages/nc-gui/assets/nc-icons/sql-server.svg

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<g clip-path="url(#clip0_858_34345)">
<path d="M24.2169 1.41601H4.13815C2.79078 1.41601 1.69727 2.51929 1.69727 3.8569V26.0006C1.69727 27.348 2.80055 28.4415 4.13815 28.4415H17.3677C17.2213 21.8609 19.4669 9.09015 24.2218 1.39648L24.2169 1.41601Z" fill="#0F80CC"/>
<path d="M23.4838 2.13379H4.13733C3.1805 2.13379 2.4043 2.91487 2.4043 3.86682V24.3947C6.78813 22.7105 13.3639 21.2606 17.9137 21.3289C19.3163 14.8069 21.1744 8.39246 23.4838 2.13379Z" fill="url(#paint0_linear_858_34345)"/>
<path d="M28.9026 0.693268C27.5259 -0.527175 25.8613 -0.0389977 24.2161 1.41577L23.4838 2.12851C20.6719 5.10639 18.1139 10.6326 17.2547 14.8553C17.5608 15.5241 17.8027 16.2204 17.9772 16.9349L18.0846 17.4231L18.192 17.921C18.192 17.921 18.1676 17.8234 18.0651 17.5305L17.9967 17.3352C17.983 17.299 17.9683 17.2632 17.9528 17.2278C17.7673 16.8031 17.2693 15.9 17.0399 15.5094C16.8623 16.0285 16.6946 16.551 16.5371 17.0765C17.1815 18.2579 17.572 20.2789 17.572 20.2789C17.572 20.2789 17.5378 20.1471 17.3767 19.6931C17.2303 19.2879 16.5175 18.0333 16.3516 17.7404C16.0587 18.8144 15.9464 19.5369 16.0489 19.7126C16.2442 20.0544 16.4394 20.689 16.6103 21.2943C16.8739 22.3678 17.089 23.4527 17.2547 24.5456L17.2791 24.8483C17.2285 26.0691 17.2529 27.2918 17.3523 28.5096C17.4793 30.0376 17.7185 31.3508 18.0211 32.0538L18.2262 31.9415C17.7868 30.5551 17.6013 28.7439 17.6794 26.6496C17.8014 23.4521 18.5337 19.5955 19.8957 15.5778C22.2048 9.46093 25.3877 4.58892 28.307 2.2652C25.6465 4.66703 22.0486 12.4388 20.9746 15.3142C19.7688 18.5361 18.9145 21.5628 18.397 24.4577C19.2855 21.7435 22.156 20.5718 22.156 20.5718C22.156 20.5718 23.5717 18.8339 25.212 16.3491L22.0632 17.1888L21.0478 17.6379C21.0478 17.6379 23.6352 16.0611 25.8661 15.3435C28.927 10.5252 32.2613 3.67603 28.9026 0.688386" fill="#003B57"/>
</g>
<defs>
<linearGradient id="paint0_linear_858_34345" x1="14.5648" y1="2.58779" x2="14.5648" y2="23.1596" gradientUnits="userSpaceOnUse">
<stop stop-color="#97D9F6"/>
<stop offset="0.92024" stop-color="#0F80CC"/>
<stop offset="1" stop-color="#0F80CC"/>
</linearGradient>
<clipPath id="clip0_858_34345">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

15
packages/nc-gui/assets/nc-icons/telegram.svg

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<g clip-path="url(#clip0_858_14191)">
<path d="M16 0C11.7575 0 7.685 1.68675 4.6875 4.68625C1.6875 7.68575 0 11.7583 0 16C0 20.2417 1.6875 24.3142 4.6875 27.3137C7.685 30.3132 11.7575 32 16 32C20.2425 32 24.315 30.3132 27.3125 27.3137C30.3125 24.3142 32 20.2417 32 16C32 11.7583 30.3125 7.68575 27.3125 4.68625C24.315 1.68675 20.2425 0 16 0Z" fill="url(#paint0_linear_858_14191)"/>
<path d="M7.24213 15.831C11.9071 13.799 15.0171 12.4593 16.5721 11.812C21.0171 9.96379 21.9396 9.64279 22.5421 9.63193C22.6746 9.62979 22.9696 9.66254 23.1621 9.81829C23.3221 9.94954 23.3671 10.127 23.3896 10.2515C23.4096 10.376 23.4371 10.6598 23.4146 10.8813C23.1746 13.4113 22.1321 19.5508 21.6021 22.3845C21.3796 23.5836 20.9371 23.9856 20.5096 24.0248C19.5796 24.1103 18.8746 23.4108 17.9746 22.821C16.5671 21.8978 15.7721 21.3233 14.4046 20.4225C12.8246 19.3815 13.8496 18.8093 14.7496 17.8743C14.9846 17.6295 19.0796 13.9058 19.1571 13.568C19.1671 13.5258 19.1771 13.3683 19.0821 13.2853C18.9896 13.202 18.8521 13.2305 18.7521 13.253C18.6096 13.285 16.3621 14.772 12.0021 17.7138C11.3646 18.1523 10.7871 18.366 10.2671 18.3548C9.69713 18.3425 8.59713 18.0318 7.77963 17.7663C6.77963 17.4405 5.98213 17.2683 6.05213 16.715C6.08713 16.427 6.48463 16.1323 7.24213 15.831Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_858_14191" x1="1600" y1="0" x2="1600" y2="3200" gradientUnits="userSpaceOnUse">
<stop stop-color="#2AABEE"/>
<stop offset="1" stop-color="#229ED9"/>
</linearGradient>
<clipPath id="clip0_858_14191">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

7
packages/nc-gui/assets/nc-icons/view-gantt.svg

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<rect x="6" y="7" width="9" height="2" rx="1" stroke="currentColor" stroke-width="1.33"/>
<rect x="9" y="12" width="6" height="2" rx="1" stroke="currentColor" stroke-width="1.33"/>
<path d="M3.5 4V6C3.5 7.10457 4.39543 8 5.5 8H6" stroke="currentColor" stroke-width="1.33" stroke-linecap="round"/>
<path d="M3.5 4V11C3.5 12.1046 4.39543 13 5.5 13H9" stroke="currentColor" stroke-width="1.33" stroke-linecap="round"/>
<rect x="1" y="2" width="10" height="2" rx="1" stroke="currentColor" stroke-width="1.33"/>
</svg>

After

Width:  |  Height:  |  Size: 619 B

7
packages/nc-gui/assets/nc-icons/zoho-crm.svg

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="12" viewBox="0 0 32 12" fill="none">
<path d="M14.2868 11.4619C14.043 11.4619 13.793 11.4119 13.5555 11.3056L8.48638 9.04298C7.58006 8.64295 7.17378 7.57412 7.57381 6.6678L9.83648 1.59868C10.2365 0.692366 11.3053 0.286086 12.2117 0.686115L17.2808 2.94878C18.1871 3.34881 18.5934 4.41764 18.1933 5.32396L15.9369 10.3931C15.6369 11.0681 14.9806 11.4619 14.2868 11.4619ZM13.9805 10.3556C14.3618 10.5243 14.8118 10.3493 14.9869 9.9743L17.2495 4.90517C17.4183 4.5239 17.2433 4.07386 16.8682 3.89885L11.7929 1.63618C11.4116 1.46742 10.9616 1.64244 10.7865 2.01746L8.52388 7.09284C8.35512 7.47411 8.53013 7.92415 8.90516 8.09916L13.9805 10.3556Z" fill="#089949"/>
<path d="M30.2005 11.4675H24.6439C23.6501 11.4675 22.8438 10.6612 22.8438 9.66734V4.11068C22.8438 3.11686 23.6501 2.31055 24.6439 2.31055H30.2005C31.1944 2.31055 32.0007 3.11686 32.0007 4.11068V9.66734C32.0007 10.6549 31.1944 11.4675 30.2005 11.4675ZM24.6439 3.34812C24.2251 3.34812 23.8813 3.6919 23.8813 4.11068V9.66734C23.8813 10.0861 24.2251 10.4299 24.6439 10.4299H30.2005C30.6193 10.4299 30.9631 10.0861 30.9631 9.66734V4.11068C30.9631 3.6919 30.6193 3.34812 30.2005 3.34812H24.6439Z" fill="#F9B21D"/>
<path d="M9.39834 5.13026L8.64829 6.80539C8.63579 6.82414 8.62953 6.84289 8.61703 6.85539L8.91081 8.65552C8.97956 9.06805 8.69829 9.46183 8.28576 9.52434L2.80411 10.4119C2.60409 10.4432 2.40408 10.3932 2.23532 10.2806C2.0728 10.1619 1.9603 9.98687 1.92904 9.78686L1.04148 4.3052C1.01023 4.10519 1.06023 3.90517 1.17274 3.73641C1.2915 3.5739 1.46651 3.46139 1.66652 3.43014L7.14818 2.54257C7.19193 2.53632 7.22943 2.53007 7.26694 2.53007C7.62946 2.53007 7.96074 2.79884 8.01699 3.16762L8.31076 4.98025L9.08582 3.24888L9.04206 3.00511C8.8858 2.02379 7.96074 1.35499 6.97941 1.5175L1.51026 2.40506C1.03523 2.48007 0.616448 2.73634 0.341427 3.12387C0.0601567 3.5114 -0.0523515 3.98643 0.022654 4.46772L0.910219 9.94937C0.985225 10.4244 1.24149 10.8432 1.63527 11.1245C1.94154 11.3495 2.31032 11.462 2.68535 11.462C2.77911 11.462 2.87911 11.4557 2.97912 11.437L8.46077 10.5494C9.44209 10.3931 10.1109 9.46808 9.94838 8.48676L9.39834 5.13026Z" fill="#E42527"/>
<path d="M15.9795 7.74926L16.7858 5.94288L16.5546 4.26776C16.5233 4.06774 16.5796 3.86773 16.7046 3.70522C16.8296 3.5427 17.0046 3.43645 17.2109 3.41145L22.7113 2.66139C22.7488 2.65514 22.78 2.65514 22.8175 2.65514C22.98 2.65514 23.1425 2.71139 23.28 2.8114C23.305 2.83015 23.3301 2.85515 23.3488 2.87391C23.5926 2.61764 23.9113 2.43637 24.2739 2.35512C24.1739 2.21761 24.0489 2.0926 23.9113 1.98634C23.5301 1.69257 23.055 1.57381 22.58 1.63631L17.0733 2.38637C16.5983 2.44887 16.1733 2.69889 15.8858 3.08017C15.592 3.46145 15.4732 3.93648 15.5357 4.41152L15.9795 7.74926Z" fill="#226DB4"/>
<path d="M25.3425 8.67402L24.6174 3.34863C24.2111 3.36113 23.8861 3.69866 23.8861 4.10494V5.66755L24.3111 8.81153C24.3424 9.01155 24.2861 9.21156 24.1611 9.37407C24.0361 9.53659 23.8611 9.64284 23.6548 9.66785L18.1544 10.4179C17.9544 10.4492 17.7544 10.3929 17.5919 10.2679C17.4294 10.1429 17.3231 9.96787 17.2981 9.7616L17.0419 7.89271L16.248 9.6991L16.2793 9.89911C16.3418 10.3741 16.5918 10.7992 16.9731 11.0867C17.2919 11.3305 17.6669 11.4555 18.0607 11.4555C18.1419 11.4555 18.2232 11.4492 18.3107 11.4367L23.8049 10.6929C24.2799 10.6304 24.7049 10.3804 24.9924 9.99912C25.28 9.62409 25.405 9.14906 25.3425 8.67402Z" fill="#226DB4"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

12
packages/nc-gui/components/api-client/Headers.vue

@ -3,12 +3,12 @@ const props = defineProps<{
modelValue: any[]
}>()
const emits = defineEmits(['update:modelValue'])
interface Option {
value: string
}
const emits = defineEmits(['update:modelValue'])
const vModel = useVModel(props, 'modelValue', emits)
const headerList = ref<Option[]>([
@ -151,12 +151,12 @@ const filterOption = (input: string, option: Option) => option.value.toUpperCase
}
.ant-select-selector {
@apply !rounded-l-lg !rounded-r-none !border-gray-200;
.ant-select-selection-search-input {
@apply !text-sm ;
.ant-select-selection-search-input {
@apply !text-sm;
}
.ant-select-selection-placeholder{
.ant-select-selection-placeholder {
@apply !text-gray-500;
}
}
}
</style>
</style>

2
packages/nc-gui/components/cell/attachment/Preview/Pdf.vue

@ -26,7 +26,7 @@ const openMethod = ref<'browser' | 'google' | undefined>()
<div class="flex items-center justify-center gap-2">
<NcButton class="!w-52" type="secondary" @click="openMethod = 'browser'">
<div class="flex items-center gap-1">
<GeneralIcon icon="globe" />
<GeneralIcon icon="globe" class="!text-gray-700" />
Open in browser
</div>
</NcButton>

39
packages/nc-gui/components/dashboard/settings/DataSources.vue

@ -339,10 +339,10 @@ const handleClickRow = (source: SourceType, tab?: string) => {
<div class="px-4 pt-4 pb-2 flex items-center justify-between gap-3">
<a-breadcrumb separator=">" class="flex-1 cursor-pointer font-weight-bold">
<a-breadcrumb-item @click="activeSource = null">
<a class="!no-underline">Data Sources</a>
<a class="!no-underline text-base">Data Sources</a>
</a-breadcrumb-item>
<a-breadcrumb-item v-if="activeSource">
<span class="capitalize">{{ activeSource.alias || 'Default Source' }}</span>
<span class="capitalize text-base">{{ activeSource.alias || 'Default Source' }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
@ -352,6 +352,20 @@ const handleClickRow = (source: SourceType, tab?: string) => {
</div>
<NcTabs v-model:activeKey="openedTab" class="nc-source-tab w-full h-[calc(100%_-_58px)] max-h-[calc(100%_-_58px)]">
<a-tab-pane v-if="!activeSource.is_meta && !activeSource.is_local" key="edit">
<template #tab>
<div class="tab" data-testid="nc-connection-tab">
<div>{{ $t('labels.connectionDetails') }}</div>
</div>
</template>
<div class="h-full">
<LazyDashboardSettingsDataSourcesEditBase
:source-id="activeSource.id"
@source-updated="loadBases(true)"
@close="activeSource = null"
/>
</div>
</a-tab-pane>
<a-tab-pane key="erd">
<template #tab>
<div class="tab" data-testid="nc-erd-tab">
@ -377,21 +391,6 @@ const handleClickRow = (source: SourceType, tab?: string) => {
<LazyDashboardSettingsBaseAudit :source-id="activeSource.id" />
</div>
</a-tab-pane>
<a-tab-pane v-if="!activeSource.is_meta && !activeSource.is_local" key="edit">
<template #tab>
<div class="tab" data-testid="nc-connection-tab">
<div>{{ $t('labels.connectionDetails') }}</div>
</div>
</template>
<div class="h-full">
<LazyDashboardSettingsDataSourcesEditBase
:source-id="activeSource.id"
@source-updated="loadBases(true)"
@close="activeSource = null"
/>
</div>
</a-tab-pane>
<a-tab-pane key="acl">
<template #tab>
<div class="tab" data-testid="nc-acl-tab">
@ -499,7 +498,7 @@ const handleClickRow = (source: SourceType, tab?: string) => {
:class="{
'!hidden': !source?.alias?.toLowerCase()?.includes(searchQuery.toLowerCase()),
}"
@click="activeSource = source"
@click="handleClickRow(source, 'edit')"
>
<div class="ds-table-col ds-table-enabled">
<div class="flex items-center gap-1" @click.stop>
@ -538,8 +537,8 @@ const handleClickRow = (source: SourceType, tab?: string) => {
</div>
<div class="ds-table-col ds-table-type">
<NcBadge rounded="lg" class="flex items-center gap-2 px-2 py-1 !h-7 truncate">
<GeneralBaseLogo :source-type="source.type" class="flex-none" />
<NcBadge rounded="lg" class="flex items-center gap-2 px-2 py-1 !h-7 truncate !border-transparent">
<GeneralBaseLogo :source-type="source.type" class="flex-none !w-4 !h-4" />
<NcTooltip placement="bottom" show-on-truncate-only class="text-sm truncate">
<template #title> {{ clientTypesMap[source.type]?.text || source.type }}</template>

2
packages/nc-gui/components/dashboard/settings/Metadata.vue

@ -128,7 +128,7 @@ const customRow = (record: Record<string, any>) => ({
<div v-else>
<!-- Tables metadata is in sync -->
<span>
<a-alert :message="$t('msg.info.tablesMetadataInSync')" type="success" show-icon class="!rounded-md"/>
<a-alert :message="$t('msg.info.tablesMetadataInSync')" type="success" show-icon class="!rounded-md" />
</span>
</div>
</div>

9
packages/nc-gui/components/dashboard/settings/UIAcl.vue

@ -153,9 +153,14 @@ const columns = [
<span> UI ACL : {{ base.title }} </span>
</NcTooltip>
<div class="flex flex-row items-center w-full mb-4 gap-2 justify-between">
<a-input v-model:value="searchInput" :placeholder="$t('placeholder.searchModels')" allow-clear class="nc-acl-search nc-input-border-on-value !w-[400px] nc-input-sm">
<a-input
v-model:value="searchInput"
:placeholder="$t('placeholder.searchModels')"
allow-clear
class="nc-acl-search nc-input-border-on-value !w-[400px] nc-input-sm"
>
<template #prefix>
<component :is="iconMap.search" class="text-gray-600"/>
<component :is="iconMap.search" class="text-gray-600" />
</template>
</a-input>
<div class="flex">

24
packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue

@ -353,8 +353,8 @@ const allowDataWrite = computed({
$e('c:source:data-write-toggle', { allowed: !v })
},
})
const changeIntegration = () => {
if (formState.value.fk_integration_id) {
const changeIntegration = (triggerTestConnection = false) => {
if (formState.value.fk_integration_id && selectedIntegration.value) {
formState.value.dataSource = {
connection: {
client: selectedIntegration.value.sub_type,
@ -365,6 +365,12 @@ const changeIntegration = () => {
} else {
onClientChange()
}
clearValidate()
if (triggerTestConnection) {
setTimeout(() => {
testConnection()
}, 300)
}
}
const handleAddNewConnection = () => {
@ -377,7 +383,7 @@ eventBus.on((event, payload) => {
until(() => selectedIntegration.value?.id === payload.id)
.toBeTruthy()
.then(() => {
changeIntegration()
changeIntegration(true)
})
}
})
@ -409,6 +415,10 @@ function handleAutoScroll(scroll: boolean, className: string) {
})
}
}
const filterIntegrationCategory = (c: IntegrationCategoryItemType) =>
[IntegrationCategoryType.DATABASE, IntegrationCategoryType.OTHERS].includes(c.value)
const filterIntegration = (c: IntegrationItemType) => c.categories.includes(IntegrationCategoryType.DATABASE)
</script>
<template>
@ -508,7 +518,7 @@ function handleAutoScroll(scroll: boolean, className: string) {
allow-clear
show-search
dropdown-match-select-width
@change="changeIntegration"
@change="changeIntegration()"
>
<a-select-option v-for="integration in integrations" :key="integration.id" :value="integration.id">
<div class="w-full flex gap-2 items-center" :data-testid="integration.title">
@ -713,7 +723,11 @@ function handleAutoScroll(scroll: boolean, className: string) {
</div>
</a-form>
<WorkspaceIntegrationsNewAvailableList is-modal />
<WorkspaceIntegrationsTab
is-modal
:filter-category="filterIntegrationCategory"
:filter-integration="filterIntegration"
/>
<WorkspaceIntegrationsEditOrAdd load-datasource-info :base-id="baseId" />
</div>
</div>

25
packages/nc-gui/components/dashboard/settings/data-sources/SupportedDocs.vue

@ -1,15 +1,15 @@
<script lang="ts" setup>
const supportedDocs = [
{
title: 'Configure a PostgreSQL Integration',
title: 'Integrations',
href: '',
},
{
title: 'Getting started with Integrations',
title: 'Create new connection',
href: '',
},
{
title: 'Troubleshoot database connection',
title: 'Add new data source',
href: '',
},
] as {
@ -24,13 +24,18 @@ const supportedDocs = [
<div class="text-sm text-gray-800 font-semibold">Supported Docs</div>
<div>
<div v-for="doc of supportedDocs" class="flex items-center gap-1">
<div class="h-7 w-7 flex items-center justify-center">
<GeneralIcon icon="bookOpen" class="flex-none w-4 h-4 text-gray-600"/>
</div>
<a :href="doc.href" target="_blank" rel="noopener noreferrer" class="!text-gray-700 text-sm !no-underline !hover:underline">
{{ doc.title }}
</a>
<div v-for="(doc, idx) of supportedDocs" :key="idx" class="flex items-center gap-1">
<div class="h-7 w-7 flex items-center justify-center">
<GeneralIcon icon="bookOpen" class="flex-none w-4 h-4 text-gray-500" />
</div>
<a
:href="doc.href"
target="_blank"
rel="noopener noreferrer"
class="!text-gray-600 text-sm !no-underline !hover:underline"
>
{{ doc.title }}
</a>
</div>
</div>
</div>

3
packages/nc-gui/components/project/AllTables.vue

@ -19,8 +19,6 @@ const { t } = useI18n()
const isImportModalOpen = ref(false)
const syncDataModalOpen = ref(false)
const defaultBase = computed(() => {
return openedProject.value?.sources?.[0]
})
@ -273,7 +271,6 @@ const onCreateBaseClick = () => {
</div>
<ProjectImportModal v-if="defaultBase" v-model:visible="isImportModalOpen" :source="defaultBase" />
<ProjectSyncDataModal v-if="defaultBase" v-model:open="syncDataModalOpen" />
<LazyDashboardSettingsDataSourcesCreateBase v-if="isNewBaseModalOpen" v-model:open="isNewBaseModalOpen" is-modal />
</div>
</template>

4
packages/nc-gui/components/smartsheet/details/Webhooks.vue

@ -248,8 +248,8 @@ const getHookTypeText = (hook: HookType) => {
type="secondary"
size="small"
class="!text-brand-500 !hover:text-brand-600"
@click="createWebhook"
data-testid="nc-new-webhook"
@click="createWebhook"
>
<div class="flex gap-2 items-center">
<GeneralIcon icon="plus" />
@ -303,7 +303,7 @@ const getHookTypeText = (hook: HookType) => {
</template>
<template v-if="column.key === 'action'">
<NcDropdown overlay-class-name="nc-webhook-item-action-dropdown">
<NcButton type="secondary" size="small" class="!w-8 !h-8" @click.stop data-testid="nc-webhook-item-action">
<NcButton type="secondary" size="small" class="!w-8 !h-8" data-testid="nc-webhook-item-action" @click.stop>
<component :is="iconMap.threeDotVertical" class="text-gray-700" />
</NcButton>
<template #overlay>

6
packages/nc-gui/components/webhook/index.vue

@ -612,11 +612,11 @@ onMounted(async () => {
</NcButton>
</NcTooltip>
<NcButton :loading="loading" type="primary" size="small" @click="saveHooks" data-testid="nc-save-webhook">
<NcButton :loading="loading" type="primary" size="small" data-testid="nc-save-webhook" @click="saveHooks">
{{ hook ? $t('labels.multiField.saveChanges') : $t('activity.createWebhook') }}
</NcButton>
<NcButton type="text" size="small" @click="modalVisible = false" data-testid="nc-close-webhook-modal">
<NcButton type="text" size="small" data-testid="nc-close-webhook-modal" @click="modalVisible = false">
<GeneralIcon icon="close" />
</NcButton>
</div>
@ -975,7 +975,7 @@ onMounted(async () => {
<div class="w-full flex flex-col gap-3">
<h2 class="text-sm text-gray-700 font-semibold !my-0">{{ $t('labels.supportDocs') }}</h2>
<div>
<div v-for="doc of supportedDocs" class="flex items-center gap-1">
<div v-for="(doc, idx) of supportedDocs" :key="idx" class="flex items-center gap-1">
<div class="h-7 w-7 flex items-center justify-center">
<GeneralIcon icon="bookOpen" class="flex-none w-4 h-4 text-gray-500" />
</div>

839
packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue

@ -0,0 +1,839 @@
<script lang="ts" setup>
import type { IntegrationType, UserType, WorkspaceUserType } from 'nocodb-sdk'
import dayjs from 'dayjs'
type SortFields = 'title' | 'sub_type' | 'created_at' | 'created_by' | 'source_count'
const {
integrations,
isLoadingIntegrations,
deleteConfirmText,
integrationPaginationData,
successConfirmModal,
searchQuery,
loadIntegrations,
deleteIntegration,
editIntegration,
duplicateIntegration,
getIntegration,
} = useIntegrationStore()
const { $api, $e } = useNuxtApp()
const { allCollaborators } = storeToRefs(useWorkspace())
const isDeleteIntegrationModalOpen = ref(false)
const toBeDeletedIntegration = ref<
| (IntegrationType & {
sources?: {
id: string
alias: string
project_title: string
base_id: string
}[]
})
| null
>(null)
const isLoadingGetLinkedSources = ref(false)
const tableWrapper = ref<HTMLDivElement>()
const titleHeaderCellRef = ref<HTMLDivElement>()
const orderBy = ref<Partial<Record<SortFields, 'asc' | 'desc' | undefined>>>({})
const localCollaborators = ref<User[] | UserType[]>([])
const { width } = useElementBounding(titleHeaderCellRef)
const collaboratorsMap = computed<Map<string, (WorkspaceUserType & { id: string }) | User | UserType>>(() => {
const map = new Map()
;(isEeUI ? allCollaborators.value : localCollaborators.value)?.forEach((coll) => {
if (coll?.id) {
map.set(coll.id, coll)
}
})
return map
})
const filteredIntegrations = computed(() =>
(integrations.value || []).sort((a, b) => {
if (orderBy.value.title) {
if (a.title && b.title) {
return orderBy.value.title === 'asc' ? (a.title < b.title ? -1 : 1) : a.title > b.title ? -1 : 1
}
} else if (orderBy.value.sub_type) {
if (a.sub_type && b.sub_type) {
return orderBy.value.sub_type === 'asc' ? (a.sub_type < b.sub_type ? -1 : 1) : a.sub_type > b.sub_type ? -1 : 1
}
} else if (orderBy.value.created_at) {
if (a?.created_at && b?.created_at) {
return orderBy.value.created_at === 'asc'
? dayjs(a.created_at).local().isBefore(dayjs(b.created_at).local())
? -1
: 1
: dayjs(a.created_at).local().isAfter(dayjs(b.created_at).local())
? -1
: 1
}
} else if (orderBy.value.created_by) {
if (a.created_by && b.created_by && collaboratorsMap.value.get(a.created_by) && collaboratorsMap.value.get(b.created_by)) {
return orderBy.value.created_by === 'asc'
? collaboratorsMap.value.get(a.created_by)?.email < collaboratorsMap.value.get(b.created_by)?.email
? -1
: 1
: collaboratorsMap.value.get(a.created_by)?.email > collaboratorsMap.value.get(b.created_by)?.email
? -1
: 1
}
} else if (orderBy.value.source_count) {
if (a.source_count !== undefined && b.source_count !== undefined) {
return orderBy.value.source_count === 'asc' ? a.source_count - b.source_count : b.source_count - a.source_count
}
}
return 0
}),
)
async function loadConnections(
page = integrationPaginationData.value.page,
limit = integrationPaginationData.value.pageSize,
updateCurrentPage = true,
) {
try {
if (updateCurrentPage) {
integrationPaginationData.value.page = 1
}
await loadIntegrations(undefined, undefined, updateCurrentPage ? 1 : page, limit)
} catch {}
}
const handleChangePage = async (page: number) => {
integrationPaginationData.value.page = page
await loadConnections(undefined, undefined, false)
}
const { onLeft, onRight, onUp, onDown } = usePaginationShortcuts({
paginationDataRef: integrationPaginationData,
changePage: handleChangePage,
isViewDataLoading: isLoadingIntegrations,
})
const openDeleteIntegration = async (source: IntegrationType) => {
isLoadingGetLinkedSources.value = true
$e('c:integration:delete')
deleteConfirmText.value = null
isDeleteIntegrationModalOpen.value = true
toBeDeletedIntegration.value = source
const connectionDetails = await getIntegration(source, {
includeSources: true,
})
toBeDeletedIntegration.value.sources = connectionDetails?.sources || []
isLoadingGetLinkedSources.value = false
}
const onDeleteConfirm = async () => {
await deleteIntegration(toBeDeletedIntegration.value, true)
}
const loadOrgUsers = async () => {
try {
const response: any = await $api.orgUsers.list()
if (!response?.list) return
localCollaborators.value = response.list as UserType[]
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
const updateOrderBy = (field: SortFields) => {
if (!integrations.value?.length) return
// Only single field sort supported, other old field sort config will reset
if (orderBy.value?.[field] === 'asc') {
orderBy.value = {
[field]: 'desc',
}
} else if (orderBy.value?.[field] === 'desc') {
orderBy.value = {
[field]: undefined,
}
} else {
orderBy.value = {
[field]: 'asc',
}
}
}
const handleSearchConnection = useDebounceFn(() => {
loadConnections()
}, 250)
const renderAltOrOptlKey = () => {
return isMac() ? '⌥' : 'ALT'
}
const isUserDeleted = (userId?: string) => {
if (!userId) return false
if (isEeUI) {
return !!collaboratorsMap.value.get(userId)?.deleted
} else {
return !collaboratorsMap.value.get(userId)?.email
}
}
const getUserNameByCreatedBy = (createdBy: string) => {
return (
collaboratorsMap.value.get(createdBy)?.display_name ||
collaboratorsMap.value.get(createdBy)?.email?.slice(0, collaboratorsMap.value.get(createdBy)?.email?.indexOf('@'))
)
}
useEventListener(tableWrapper, 'scroll', () => {
const stickyHeaderCell = tableWrapper.value?.querySelector('th.cell-title')
const nonStickyHeaderFirstCell = tableWrapper.value?.querySelector('th.cell-type')
if (!stickyHeaderCell?.getBoundingClientRect().right || !nonStickyHeaderFirstCell?.getBoundingClientRect().left) {
return
}
if (nonStickyHeaderFirstCell?.getBoundingClientRect().left < stickyHeaderCell?.getBoundingClientRect().right) {
tableWrapper.value?.classList.add('sticky-shadow')
} else {
tableWrapper.value?.classList.remove('sticky-shadow')
}
})
onMounted(async () => {
if (!isEeUI) {
await Promise.allSettled([!integrations.value.length && loadIntegrations(), loadOrgUsers()])
} else if (!integrations.value.length) {
await loadIntegrations()
}
})
// Keyboard shortcuts for pagination
onKeyStroke('ArrowLeft', onLeft)
onKeyStroke('ArrowRight', onRight)
onKeyStroke('ArrowUp', onUp)
onKeyStroke('ArrowDown', onDown)
</script>
<template>
<div class="h-full flex flex-col gap-6 nc-workspace-connections">
<div class="flex justify-between gap-12">
<div class="text-sm font-normal text-gray-600">
<div>
Manage connections for your integrations.
<a target="_blank" rel="noopener noreferrer"> Learn more </a>
</div>
</div>
<div class="flex items-center justify-end gap-3 mx-1">
<a-input
v-model:value="searchQuery"
type="text"
class="nc-search-integration-input !min-w-[300px] nc-input-sm flex-none"
:placeholder="`${$t('general.search')} ${$t('general.connections').toLowerCase()}`"
allow-clear
@input="handleSearchConnection"
>
<template #prefix>
<GeneralIcon icon="search" class="mr-2 h-4 w-4 text-gray-500" />
</template>
</a-input>
</div>
</div>
<div class="table-container relative flex-1">
<div
ref="tableWrapper"
class="nc-workspace-integration-table relative nc-scrollbar-thin !overflow-auto max-h-[calc(100%_-_40px)]"
:class="{
'h-full': filteredIntegrations?.length,
}"
>
<table class="!sticky top-0 z-5 w-full">
<thead>
<tr>
<th
class="cell-title !hover:bg-gray-100 select-none cursor-pointer"
:class="{
'cursor-not-allowed': !filteredIntegrations?.length,
'!text-gray-700': orderBy.title,
}"
@click="updateOrderBy('title')"
>
<div ref="titleHeaderCellRef" class="flex items-center gap-3">
<div>Name</div>
<GeneralIcon
v-if="orderBy.title"
icon="chevronDown"
class="flex-none"
:class="{
'transform rotate-180': orderBy?.title === 'asc',
}"
/>
<GeneralIcon v-else icon="chevronUpDown" class="flex-none" />
</div>
</th>
<th
class="cell-type !hover:bg-gray-100 select-none cursor-pointer"
:class="{
'cursor-not-allowed': !filteredIntegrations?.length,
'!text-gray-700': orderBy.sub_type,
}"
@click="updateOrderBy('sub_type')"
>
<div class="flex items-center gap-3">
<div>Type</div>
<GeneralIcon
v-if="orderBy.sub_type"
icon="chevronDown"
class="flex-none"
:class="{
'transform rotate-180': orderBy.sub_type === 'asc',
}"
/>
<GeneralIcon v-else icon="chevronUpDown" class="flex-none" />
</div>
</th>
<th
class="cell-created-date !hover:bg-gray-100 select-none cursor-pointer"
:class="{
'cursor-not-allowed': !filteredIntegrations?.length,
'!text-gray-700': orderBy.created_at,
}"
@click="updateOrderBy('created_at')"
>
<div class="flex items-center gap-3">
<div>Date added</div>
<GeneralIcon
v-if="orderBy.created_at"
icon="chevronDown"
class="flex-none"
:class="{
'transform rotate-180': orderBy.created_at === 'asc',
}"
/>
<GeneralIcon v-else icon="chevronUpDown" class="flex-none" />
</div>
</th>
<th
class="cell-added-by !hover:bg-gray-100 select-none cursor-pointer"
:class="{
'cursor-not-allowed': !filteredIntegrations?.length,
'!text-gray-700': orderBy.created_by,
}"
@click="updateOrderBy('created_by')"
>
<div class="flex items-center gap-3">
<div>Added by</div>
<GeneralIcon
v-if="orderBy.created_by"
icon="chevronDown"
class="flex-none"
:class="{
'transform rotate-180': orderBy.created_by === 'asc',
}"
/>
<GeneralIcon v-else icon="chevronUpDown" class="flex-none" />
</div>
</th>
<th
class="cell-usage !hover:bg-gray-100 select-none cursor-pointer"
:class="{
'cursor-not-allowed': !filteredIntegrations?.length,
}"
@click="updateOrderBy('source_count')"
>
<div class="flex items-center gap-3">
<div>Usage</div>
<GeneralIcon
v-if="orderBy?.source_count"
icon="chevronDown"
class="flex-none"
:class="{
'transform rotate-180': orderBy?.source_count === 'asc',
}"
/>
<GeneralIcon v-else icon="chevronUpDown" class="flex-none" />
</div>
</th>
<th class="cell-actions">
<div>Actions</div>
</th>
</tr>
</thead>
</table>
<template v-if="filteredIntegrations?.length">
<table class="h-full max-h-[calc(100%_-_55px)] w-full">
<tbody>
<tr v-for="integration of filteredIntegrations" :key="integration.id" @click="editIntegration(integration)">
<td class="cell-title">
<div
class="gap-3"
:style="{
maxWidth: `${width}px`,
}"
>
<NcTooltip placement="bottom" class="truncate" show-on-truncate-only>
<template #title> {{ integration.title }}</template>
{{ integration.title }}
</NcTooltip>
<span v-if="integration.is_private">
<NcBadge :border="false" class="text-primary !h-4.5 bg-brand-50 text-xs">{{
$t('general.private')
}}</NcBadge>
</span>
</div>
</td>
<td class="cell-type">
<div>
<NcBadge rounded="lg" class="flex items-center gap-2 px-2 py-1 !h-7 truncate !border-transparent">
<WorkspaceIntegrationsIcon
v-if="integration.sub_type"
:integration-type="integration.sub_type"
size="xs"
class="!p-0 !bg-transparent"
/>
<NcTooltip placement="bottom" show-on-truncate-only class="text-sm truncate">
<template #title> {{ clientTypesMap[integration?.sub_type]?.text || integration?.sub_type }}</template>
{{
integration.sub_type && clientTypesMap[integration.sub_type]
? clientTypesMap[integration.sub_type]?.text
: integration.sub_type
}}
</NcTooltip>
</NcBadge>
</div>
</td>
<td class="cell-created-date">
<div>
<NcTooltip placement="bottom" show-on-truncate-only>
<template #title> {{ dayjs(integration.created_at).local().format('DD MMM YYYY') }}</template>
{{ dayjs(integration.created_at).local().format('DD MMM YYYY') }}
</NcTooltip>
</div>
</td>
<td class="cell-added-by">
<div>
<NcTooltip :disabled="!isUserDeleted(integration.created_by)" class="w-full">
<template #title>
{{ `User not part of this ${isEeUI ? 'workspace' : 'organisation'} anymore` }}
</template>
<div
v-if="integration.created_by && collaboratorsMap.get(integration.created_by)?.email"
class="w-full flex gap-3 items-center"
>
<GeneralUserIcon
:email="collaboratorsMap.get(integration.created_by)?.email"
size="base"
class="flex-none"
:class="{
'!grayscale': isUserDeleted(integration.created_by),
}"
:style="
isUserDeleted(integration.created_by)
? {
filter: 'grayscale(100%) brightness(115%)',
}
: {}
"
/>
<div class="flex-1 flex flex-col max-w-[calc(100%_-_44px)]">
<div class="w-full flex gap-3">
<NcTooltip
class="text-sm !leading-5 capitalize font-semibold truncate"
:class="{
'text-gray-800': !isUserDeleted(integration.created_by),
'text-gray-500': isUserDeleted(integration.created_by),
}"
:disabled="isUserDeleted(integration.created_by)"
show-on-truncate-only
placement="bottom"
>
<template #title>
{{ getUserNameByCreatedBy(integration.created_by) }}
</template>
{{ getUserNameByCreatedBy(integration.created_by) }}
</NcTooltip>
</div>
<NcTooltip
class="text-xs !leading-4 truncate"
:class="{
'text-gray-600': !isUserDeleted(integration.created_by),
'text-gray-500': isUserDeleted(integration.created_by),
}"
:disabled="isUserDeleted(integration.created_by)"
show-on-truncate-only
placement="bottom"
>
<template #title>
{{ collaboratorsMap.get(integration.created_by)?.email }}
</template>
{{ collaboratorsMap.get(integration.created_by)?.email }}
</NcTooltip>
</div>
</div>
<div v-else class="w-full truncate text-gray-500">{{ integration.created_by }}</div>
</NcTooltip>
</div>
</td>
<td class="cell-usage">
<div>{{ integration?.source_count ?? 0 }}</div>
</td>
<td class="cell-actions" @click.stop>
<div class="justify-end">
<NcDropdown placement="bottomRight">
<NcButton size="small" type="secondary">
<GeneralIcon icon="threeDotVertical" />
</NcButton>
<template #overlay>
<NcMenu>
<NcMenuItem @click="editIntegration(integration)">
<GeneralIcon class="text-gray-800" icon="edit" />
<span>{{ $t('general.edit') }}</span>
</NcMenuItem>
<NcMenuItem @click="duplicateIntegration(integration)">
<GeneralIcon class="text-gray-800" icon="duplicate" />
<span>{{ $t('general.duplicate') }}</span>
</NcMenuItem>
<NcDivider />
<NcMenuItem class="!text-red-500 !hover:bg-red-50" @click="openDeleteIntegration(integration)">
<GeneralIcon icon="delete" />
{{ $t('general.delete') }}
</NcMenuItem>
</NcMenu>
</template>
</NcDropdown>
</div>
</td>
</tr>
</tbody>
</table>
</template>
</div>
<div
v-show="isLoadingIntegrations"
class="flex items-center justify-center absolute left-0 top-0 w-full h-full z-10 pb-10 pointer-events-none"
>
<div class="flex flex-col justify-center items-center gap-2">
<GeneralLoader size="xlarge" />
<span class="text-center">{{ $t('general.loading') }}</span>
</div>
</div>
<div
v-if="!isLoadingIntegrations && (!integrations?.length || !filteredIntegrations.length)"
class="flex-none integration-table-empty flex items-center justify-center py-8 px-6 h-full max-h-[calc(100%_-_94px)]"
>
<div
v-if="integrations?.length && !filteredIntegrations.length"
class="px-2 py-6 text-gray-500 flex flex-col items-center gap-6 text-center"
>
<img
src="~assets/img/placeholder/no-search-result-found.png"
class="!w-[164px] flex-none"
alt="No search results found"
/>
{{ $t('title.noResultsMatchedYourSearch') }}
</div>
<div v-else class="flex-none text-center flex flex-col items-center gap-3">
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" :description="$t('labels.noData')" class="!my-0" />
</div>
</div>
<div
v-if="integrationPaginationData.totalRows"
class="flex flex-row justify-center items-center bg-gray-50 min-h-10"
:class="{
'pointer-events-none': isLoadingIntegrations,
}"
>
<div class="flex justify-between items-center w-full px-6">
<div>&nbsp;</div>
<NcPagination
v-model:current="integrationPaginationData.page"
v-model:page-size="integrationPaginationData.pageSize"
:total="+integrationPaginationData.totalRows"
show-size-changer
:use-stored-page-size="false"
:prev-page-tooltip="`${renderAltOrOptlKey()}+←`"
:next-page-tooltip="`${renderAltOrOptlKey()}+→`"
:first-page-tooltip="`${renderAltOrOptlKey()}+↓`"
:last-page-tooltip="`${renderAltOrOptlKey()}+↑`"
@update:current="loadConnections(undefined, undefined, false)"
@update:page-size="loadConnections(integrationPaginationData.page, $event, false)"
/>
<div class="text-gray-500 text-xs">
{{ integrationPaginationData.totalRows }} {{ integrationPaginationData.totalRows === 1 ? 'record' : 'records' }}
</div>
</div>
</div>
</div>
<GeneralDeleteModal
v-model:visible="isDeleteIntegrationModalOpen"
:entity-name="$t('general.connection')"
:on-delete="onDeleteConfirm"
:delete-label="$t('general.delete')"
:show-default-delete-msg="!isLoadingGetLinkedSources && !toBeDeletedIntegration?.sources?.length"
>
<template #entity-preview>
<template v-if="isLoadingGetLinkedSources">
<div class="rounded-lg overflow-hidden">
<a-skeleton-input active class="h-9 !rounded-md !w-full"></a-skeleton-input>
</div>
<div class="rounded-lg overflow-hidden mt-2">
<a-skeleton-input active class="h-9 !rounded-md !w-full"></a-skeleton-input>
</div>
</template>
<div v-else-if="toBeDeletedIntegration" class="w-full flex flex-col text-gray-800">
<div class="flex flex-row items-center py-2 px-3.25 bg-gray-50 rounded-lg text-gray-700 mb-4">
<WorkspaceIntegrationsIcon
:integration-type="toBeDeletedIntegration.sub_type"
size="xs"
class="!p-0 !bg-transparent"
/>
<div
class="text-ellipsis overflow-hidden select-none w-full pl-3"
:style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
>
{{ toBeDeletedIntegration.title }}
</div>
</div>
<div v-if="toBeDeletedIntegration?.sources?.length" class="flex flex-col pb-2 text-small leading-[18px] text-gray-500">
<div class="mb-1">Following external data sources using this connection will also be removed</div>
<ul class="!list-disc ml-6 mb-0">
<li
v-for="(source, idx) of toBeDeletedIntegration.sources"
:key="idx"
class="marker:text-gray-500 !marker:(flex items-center !-mt-1)"
>
<div class="flex items-center gap-1">
<div class="flex items-center">
&nbsp;
<GeneralProjectIcon
type="database"
class="!grayscale min-w-4 flex-none -ml-1"
:style="{
filter: 'grayscale(100%) brightness(115%)',
}"
/>
</div>
<NcTooltip class="!truncate !max-w-[45%] flex-none" show-on-truncate-only>
<template #title>
{{ source.project_title }}
</template>
{{ source.project_title }}
</NcTooltip>
>
<GeneralBaseLogo
class="!grayscale min-w-4 flex-none"
:style="{
filter: 'grayscale(100%) brightness(115%)',
}"
/>
<NcTooltip class="truncate !max-w-[45%] capitalize" show-on-truncate-only>
<template #title>
{{ source.alias }}
</template>
{{ source.alias }}
</NcTooltip>
</div>
</li>
</ul>
<div class="mt-2">Do you want to proceed anyway?</div>
</div>
</div>
</template>
</GeneralDeleteModal>
<NcModal v-model:visible="successConfirmModal.isOpen" centered size="small" @keydown.esc="successConfirmModal.isOpen = false">
<div class="flex gap-4">
<div>
<GeneralIcon icon="circleCheckSolid" class="flex-none !text-green-700 mt-0.5 !h-6 !w-6" />
</div>
<div class="flex flex-col gap-3">
<div class="flex">
<h3 class="!m-0 text-base font-weight-700 flex-1">
{{ successConfirmModal.title }}
</h3>
<NcButton size="xsmall" type="text" @click="successConfirmModal.isOpen = false">
<GeneralIcon icon="close" class="text-gray-600" />
</NcButton>
</div>
<div class="text-sm text-gray-700">
{{ successConfirmModal.description }}
</div>
<!-- Todo: add link -->
<a target="_blank" rel="noopener noreferrer"> Learn more </a>
</div>
</div>
</NcModal>
</div>
</template>
<style lang="scss" scoped>
.source-card-link {
@apply !text-black !no-underline;
.nc-new-integration-type-title {
@apply text-sm font-weight-600 text-gray-600;
}
}
.source-card {
@apply flex items-center border-1 rounded-lg p-3 cursor-pointer hover:bg-gray-50;
width: 288px;
.name {
@apply ml-4 text-md font-semibold;
}
}
:deep(.ant-input-affix-wrapper.nc-search-integration-input) {
&:not(:has(.ant-input-clear-icon-hidden)):has(.ant-input-clear-icon) {
@apply border-[var(--ant-primary-5)];
}
}
.nc-new-integration-type-wrapper {
@apply flex flex-col gap-3;
}
.table-container {
@apply border-1 border-gray-200 rounded-lg overflow-hidden w-full;
.nc-workspace-integration-table {
&.sticky-shadow {
th,
td {
&.cell-title {
@apply border-r-1 border-gray-200;
}
}
}
&:not(.sticky-shadow) {
th,
td {
&.cell-title {
@apply border-r-1 border-transparent;
}
}
}
thead {
@apply w-full;
th {
@apply bg-gray-50 text-sm text-gray-500 font-weight-500;
&.cell-title {
@apply sticky left-0 z-4 bg-gray-50;
}
}
}
tbody {
@apply w-full;
tr {
@apply cursor-pointer;
td {
@apply text-sm text-gray-600;
&.cell-title {
@apply sticky left-0 z-4 bg-white !text-gray-800 font-semibold;
}
}
}
}
tr {
@apply h-[54px] flex border-b-1 border-gray-200 w-full;
&:hover td {
@apply !bg-gray-50;
}
&.selected td {
@apply !bg-gray-50;
}
th,
td {
@apply h-full;
& > div {
@apply px-6 h-full flex-1 flex items-center;
}
&.cell-title {
@apply flex-1 sticky left-0 z-5;
& > div {
@apply min-w-[250px];
}
}
&.cell-added-by {
@apply basis-[20%];
& > div {
@apply min-w-[250px];
}
}
&.cell-type {
@apply basis-[20%];
& > div {
@apply min-w-[178px];
}
}
&.cell-created-date {
@apply basis-[20%];
& > div {
@apply min-w-[158px];
}
}
&.cell-usage {
@apply w-[120px];
& > div {
@apply min-w-[118px];
}
}
&.cell-actions {
@apply w-[100px];
& > div {
@apply min-w-[98px];
}
}
}
}
}
}
.cell-header {
@apply text-xs font-semibold text-gray-500;
}
</style>

533
packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue

@ -0,0 +1,533 @@
<script lang="ts" setup>
import NcModal from '~/components/nc/Modal.vue'
/* eslint-disable @typescript-eslint/consistent-type-imports */
import { IntegrationCategoryType, type IntegrationItemType, SyncDataType } from '#imports'
const props = withDefaults(
defineProps<{
isModal?: boolean
filterCategory?: (c: IntegrationCategoryItemType) => boolean
filterIntegration?: (i: IntegrationItemType) => boolean
}>(),
{
isModal: false,
filterCategory: () => true,
filterIntegration: () => true,
},
)
const { isModal, filterCategory, filterIntegration } = props
const { $e } = useNuxtApp()
const { t } = useI18n()
const { syncDataUpvotes, updateSyncDataUpvotes } = useGlobal()
const { pageMode, IntegrationsPageMode, requestIntegration, addIntegration, saveIntegraitonRequest } = useIntegrationStore()
const activeCategory = ref<IntegrationCategoryItemType | null>(null)
const searchQuery = ref<string>('')
const requestNewIntegrationRef = ref<HTMLDivElement>()
const integrationListRef = ref<HTMLDivElement>()
const { width: integrationListContainerWidth } = useElementSize(integrationListRef)
const listWrapperMaxWidth = computed(() => {
if (integrationListContainerWidth.value <= 328 || integrationListContainerWidth.value < 624) {
return '328px'
}
if (integrationListContainerWidth.value < 920) {
return '576px'
}
if (integrationListContainerWidth.value < 1216) {
return '872px'
}
return '1168px'
})
const upvotesData = computed(() => {
return new Set(syncDataUpvotes.value)
})
const getIntegrationsByCategory = (category: IntegrationCategoryType, query: string) => {
return allIntegrations.filter((i) => {
return (
filterIntegration(i) && i.categories.includes(category) && t(i.title).toLowerCase().includes(query.trim().toLowerCase())
)
})
}
const integrationsMapByCategory = computed(() => {
return integrationCategories
.filter(filterCategory)
.filter((c) => (activeCategory.value ? c.value === activeCategory.value.value : true))
.reduce(
(acc, curr) => {
acc[curr.value] = {
title: curr.title,
list: getIntegrationsByCategory(curr.value, searchQuery.value),
}
return acc
},
{} as Record<
string,
{
title: string
list: IntegrationItemType[]
}
>,
)
})
const isAddNewIntegrationModalOpen = computed({
get: () => {
return pageMode.value === IntegrationsPageMode.LIST
},
set: (value: boolean) => {
if (value) {
pageMode.value = IntegrationsPageMode.LIST
} else {
pageMode.value = null
}
},
})
const handleUpvote = (syncDataType: SyncDataType) => {
if (upvotesData.value.has(syncDataType)) return
$e(`a:integration-request:${syncDataType}`)
updateSyncDataUpvotes([...syncDataUpvotes.value, syncDataType])
}
const handleAddIntegration = (category: IntegrationCategoryType, integration: IntegrationItemType) => {
if (!integration.isAvailable) {
handleUpvote(integration.value)
return
}
// currently we only support database integration category type
if (category !== IntegrationCategoryType.DATABASE) {
return
}
addIntegration(integration.value)
}
const handleSetRequestIntegrationRef = (node) => {
requestNewIntegrationRef.value = node as HTMLDivElement
return node
}
const handleOpenRequestIntegration = () => {
requestIntegration.value.isOpen = true
nextTick(() => {
requestNewIntegrationRef.value?.scrollIntoView?.({ behavior: 'smooth' })
requestNewIntegrationRef.value?.querySelector('textarea')?.focus?.()
requestNewIntegrationRef.value?.querySelector('textarea')?.select?.()
})
}
</script>
<template>
<component
:is="isModal ? NcModal : 'div'"
v-model:visible="isAddNewIntegrationModalOpen"
centered
size="large"
:class="{
'h-full': !isModal,
}"
wrap-class-name="nc-modal-available-integrations-list"
@keydown.esc="isAddNewIntegrationModalOpen = false"
>
<a-layout>
<!-- <a-layout-sider class="nc-integration-layout-sidebar"> -->
<!-- <div class="h-full flex flex-col gap-3"> -->
<!-- <div class="px-5 pt-3 text-sm text-gray-500 font-bold"> -->
<!-- {{ $t('title.categories') }} -->
<!-- </div> -->
<!-- <div class="px-3 pb-3 flex-1 flex flex-col gap-1 overflow-y-auto nc-scrollbar-thin"> -->
<!-- <div -->
<!-- class="nc-integration-category-item" -->
<!-- :class="{ -->
<!-- active: activeCategory === null, -->
<!-- }" -->
<!-- data-testid="all-integrations" -->
<!-- @click="activeCategory = null" -->
<!-- > -->
<!-- <div class="nc-integration-category-item-icon-wrapper bg-gray-200"> -->
<!-- <GeneralIcon icon="globe" class="stroke-transparent !text-gray-700" /> -->
<!-- </div> -->
<!-- <div class="nc-integration-category-item-content-wrapper"> -->
<!-- <div class="nc-integration-category-item-title">All Integrations</div> -->
<!-- &lt;!&ndash; <div class="nc-integration-category-item-subtitle">Content needed</div>&ndash;&gt; -->
<!-- </div> -->
<!-- </div> -->
<!-- <div -->
<!-- v-for="category of integrationCategories" -->
<!-- :key="category.value" -->
<!-- class="nc-integration-category-item" -->
<!-- :class="{ -->
<!-- active: activeCategory === category, -->
<!-- }" -->
<!-- :data-testid="category.value" -->
<!-- @click="activeCategory = category" -->
<!-- > -->
<!-- <div -->
<!-- class="nc-integration-category-item-icon-wrapper" -->
<!-- :style="{ -->
<!-- backgroundColor: category.iconBgColor, -->
<!-- }" -->
<!-- > -->
<!-- <component :is="category.icon" class="nc-integration-category-item-icon" :style="category.iconStyle" /> -->
<!-- </div> -->
<!-- <div class="nc-integration-category-item-content-wrapper"> -->
<!-- <div class="nc-integration-category-item-title">{{ $t(category.title) }}</div> -->
<!-- &lt;!&ndash; <div class="nc-integration-category-item-subtitle">{{ $t(category.subtitle) }}</div>&ndash;&gt; -->
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
<!-- </a-layout-sider> -->
<a-layout-content class="nc-integration-layout-content">
<div v-if="isModal" class="p-4 w-full flex items-center justify-between gap-3 border-b-1 border-gray-200">
<NcButton type="text" size="small" @click="isAddNewIntegrationModalOpen = false">
<GeneralIcon icon="arrowLeft" />
</NcButton>
<GeneralIcon icon="gitCommit" class="flex-none h-5 w-5" />
<div class="flex-1 text-base font-weight-700">New Connection</div>
<div class="flex items-center gap-3">
<NcButton size="small" type="text" @click="isAddNewIntegrationModalOpen = false">
<GeneralIcon icon="close" class="text-gray-600" />
</NcButton>
</div>
</div>
<div
class="w-full flex flex-col gap-6"
:class="{
'h-[calc(100%_-_66px)]': isModal,
'h-full': !isModal,
}"
>
<div class="px-6 pt-6">
<div
class="flex items-center justify-between flex-wrap gap-3 m-auto"
:style="{
maxWidth: listWrapperMaxWidth,
}"
>
<div class="text-sm font-normal text-gray-600">
<div>Connect integrations with NocoDB. <a target="_blank" rel="noopener noreferrer"> Learn more </a></div>
</div>
<a-input
v-model:value="searchQuery"
type="text"
class="nc-input-border-on-value nc-search-integration-input !min-w-[300px] !max-w-[400px] nc-input-sm flex-none"
placeholder="Search integration..."
allow-clear
>
<template #prefix>
<GeneralIcon icon="search" class="mr-2 h-4 w-4 text-gray-500" />
</template>
</a-input>
</div>
</div>
<div
ref="integrationListRef"
class="flex-1 px-6 pb-6 flex flex-col nc-workspace-settings-integrations-list overflow-y-auto nc-scrollbar-thin"
>
<div class="w-full flex justify-center">
<div
class="flex flex-col space-y-6 w-full"
:style="{
maxWidth: listWrapperMaxWidth,
}"
>
<template v-for="(category, key) in integrationsMapByCategory">
<div
v-if="category.list.length || key === IntegrationCategoryType.OTHERS"
:key="key"
class="integration-type-wrapper"
>
<div class="category-type-title">{{ $t(category.title) }}</div>
<div v-if="category.list.length" class="integration-type-list">
<NcTooltip
v-for="integration of category.list"
:key="integration.value"
:disabled="integration?.isAvailable"
placement="bottom"
>
<template #title>{{ $t('tooltip.comingSoonIntegration') }}</template>
<div
class="source-card"
:class="{
'is-available': integration?.isAvailable,
}"
@click="handleAddIntegration(key, integration)"
>
<div class="integration-icon-wrapper">
<component :is="integration.icon" class="integration-icon" :style="integration.iconStyle" />
</div>
<div class="name flex-1">{{ $t(integration.title) }}</div>
<div v-if="integration?.isAvailable" class="action-btn">+</div>
<div v-else class="">
<NcButton
type="secondary"
size="xs"
class="integration-upvote-btn !rounded-lg !px-1 !py-0"
:class="{
selected: upvotesData.has(integration.value),
}"
>
<div class="flex items-center gap-2">
<GeneralIcon icon="ncArrowUp" />
</div>
</NcButton>
</div>
</div>
</NcTooltip>
</div>
</div>
<div
v-if="key === IntegrationCategoryType.OTHERS"
:key="`${key}-request-integration`"
:ref="handleSetRequestIntegrationRef"
class="integration-type-wrapper !mt-4"
>
<div>
<div
class="source-card-request-integration"
:class="{
active: requestIntegration.isOpen,
}"
>
<div
v-if="!requestIntegration.isOpen"
class="source-card-item border-none"
@click="handleOpenRequestIntegration"
>
<div class="flex items-center justify-center rounded-lg w-[44px] h-[44px]">
<GeneralIcon icon="plusSquare" class="flex-none w-8 h-8 !text-brand-500" />
</div>
<div class="name">Request New Integration</div>
</div>
<div v-show="requestIntegration.isOpen" class="flex flex-col gap-4">
<div class="flex items-center justify-between gap-4">
<div class="text-base font-bold text-gray-800">Request Integration</div>
<NcButton size="xsmall" type="text" @click="requestIntegration.isOpen = false">
<GeneralIcon icon="close" class="text-gray-600" />
</NcButton>
</div>
<div class="flex flex-col gap-2">
<a-textarea
v-model:value="requestIntegration.msg"
class="!rounded-md !text-sm !min-h-[120px] max-h-[500px] nc-scrollbar-thin"
size="large"
hide-details
placeholder="Provide integration name and your use-case."
/>
</div>
<div class="flex items-center justify-end gap-3">
<NcButton size="small" type="secondary" @click="requestIntegration.isOpen = false">
{{ $t('general.cancel') }}
</NcButton>
<NcButton
:disabled="!requestIntegration.msg?.trim()"
:loading="requestIntegration.isLoading"
size="small"
@click="saveIntegraitonRequest(requestIntegration.msg)"
>
{{ $t('general.submit') }}
</NcButton>
</div>
</div>
</div>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</a-layout-content>
</a-layout>
</component>
</template>
<style lang="scss" scoped>
.nc-integration-layout-sidebar {
@apply !bg-white border-r-1 border-gray-200 !min-w-[260px] !max-w-[260px];
flex: 1 1 260px !important;
.nc-integration-category-item {
@apply flex gap-2 p-2 rounded-lg hover:bg-gray-100 cursor-pointer transition-all;
&.active {
@apply bg-gray-100;
}
.nc-integration-category-item-icon-wrapper {
@apply flex-none w-5 h-5 flex items-center justify-center rounded;
.nc-integration-category-item-icon {
@apply flex-none w-4 h-4;
}
}
.nc-integration-category-item-content-wrapper {
@apply flex-1 flex flex-col gap-1;
.nc-integration-category-item-title {
@apply text-sm text-gray-800 font-weight-500;
}
.nc-integration-category-item-subtitle {
@apply text-xs text-gray-500 font-weight-500;
}
}
}
}
.nc-integration-layout-content {
@apply !bg-white;
}
.source-card-request-integration {
@apply flex flex-col gap-4 border-1 rounded-xl p-3 w-[280px] overflow-hidden transition-all duration-300 max-w-[576px];
&.active {
@apply w-full;
}
&:not(.active) {
@apply cursor-pointer hover:bg-gray-50;
&:hover {
box-shadow: 0px 4px 8px -2px rgba(0, 0, 0, 0.08), 0px 2px 4px -2px rgba(0, 0, 0, 0.04);
}
}
.source-card-item {
@apply flex items-center gap-4;
.name {
@apply text-base font-semibold text-gray-800;
}
}
}
.source-card-link {
@apply !text-black !no-underline;
.nc-new-integration-type-title {
@apply text-sm font-weight-600 text-gray-600;
}
}
.nc-workspace-settings-integrations-list {
.integration-type-wrapper {
@apply flex flex-col gap-3;
.integration-type-list {
@apply flex gap-4 flex-wrap;
.source-card {
@apply flex items-center gap-4 border-1 border-gray-200 rounded-xl p-3 w-[280px] cursor-pointer;
.integration-icon-wrapper {
@apply flex-none h-[44px] w-[44px] rounded-lg flex items-center justify-center;
.integration-icon {
@apply flex-none stroke-transparent;
}
}
.name {
@apply text-base font-bold;
}
.action-btn {
@apply hidden text-2xl text-gray-500 w-7 h-7 text-center;
}
&.is-available {
&:hover {
@apply bg-gray-50;
box-shadow: 0px 4px 8px -2px rgba(0, 0, 0, 0.08), 0px 2px 4px -2px rgba(0, 0, 0, 0.04);
.action-btn {
@apply block;
}
}
// .integration-icon-wrapper {
// @apply bg-gray-100;
// }
.name {
@apply text-gray-800;
}
}
&:not(.is-available) {
&:not(:hover) {
.integration-icon-wrapper {
// @apply bg-gray-50;
.integration-icon {
@apply !grayscale;
filter: grayscale(100%) brightness(115%);
}
}
.name {
@apply text-gray-500;
}
}
&:hover {
.name {
@apply text-gray-800;
}
}
.integration-upvote-btn {
&.selected {
@apply shadow-selected !text-brand-500 !border-brand-500 !cursor-not-allowed pointer-events-none;
}
}
}
}
}
.category-type-title {
@apply text-sm text-gray-700 font-weight-700;
}
}
}
</style>
<style lang="scss">
.nc-modal-available-integrations-list {
.nc-modal {
@apply !p-0;
height: min(calc(100vh - 100px), 1024px);
max-height: min(calc(100vh - 100px), 1024px) !important;
}
.ant-modal-content {
overflow: hidden;
}
}
</style>

105
packages/nc-gui/components/workspace/integrations/List.vue

@ -20,7 +20,7 @@ const {
const { $api, $e } = useNuxtApp()
const { collaborators } = storeToRefs(useWorkspace())
const { allCollaborators } = storeToRefs(useWorkspace())
const isDeleteIntegrationModalOpen = ref(false)
const toBeDeletedIntegration = ref<
@ -50,7 +50,7 @@ const { width } = useElementBounding(titleHeaderCellRef)
const collaboratorsMap = computed<Map<string, (WorkspaceUserType & { id: string }) | User | UserType>>(() => {
const map = new Map()
;(isEeUI ? collaborators.value : localCollaborators.value)?.forEach((coll) => {
;(isEeUI ? allCollaborators.value : localCollaborators.value)?.forEach((coll) => {
if (coll?.id) {
map.set(coll.id, coll)
}
@ -181,6 +181,16 @@ const renderAltOrOptlKey = () => {
return isMac() ? '⌥' : 'ALT'
}
const isUserDeleted = (userId?: string) => {
if (!userId) return false
if (isEeUI) {
return !!collaboratorsMap.value.get(userId)?.deleted
} else {
return !collaboratorsMap.value.get(userId)?.email
}
}
useEventListener(tableWrapper, 'scroll', () => {
const stickyHeaderCell = tableWrapper.value?.querySelector('th.cell-title')
const nonStickyHeaderFirstCell = tableWrapper.value?.querySelector('th.cell-type')
@ -410,47 +420,76 @@ onKeyStroke('ArrowDown', onDown)
</td>
<td class="cell-added-by">
<div>
<div
v-if="integration.created_by && collaboratorsMap.get(integration.created_by)?.email"
class="w-full flex gap-3 items-center"
>
<GeneralUserIcon
:email="collaboratorsMap.get(integration.created_by)?.email"
size="base"
class="flex-none"
/>
<div class="flex-1 flex flex-col max-w-[calc(100%_-_44px)]">
<div class="w-full flex gap-3">
<NcTooltip
class="text-sm !leading-5 text-gray-800 capitalize font-semibold truncate"
show-on-truncate-only
placement="bottom"
>
<template #title>
<NcTooltip :disabled="!isUserDeleted(integration.created_by)" class="w-full">
<template #title>
{{ `User not part of this ${isEeUI ? 'workspace' : 'organisation'} anymore` }}
</template>
<div
v-if="integration.created_by && collaboratorsMap.get(integration.created_by)?.email"
class="w-full flex gap-3 items-center"
>
<GeneralUserIcon
:email="collaboratorsMap.get(integration.created_by)?.email"
size="base"
class="flex-none"
:class="{
'!grayscale': isUserDeleted(integration.created_by),
}"
:style="
isUserDeleted(integration.created_by)
? {
filter: 'grayscale(100%) brightness(115%)',
}
: {}
"
/>
<div class="flex-1 flex flex-col max-w-[calc(100%_-_44px)]">
<div class="w-full flex gap-3">
<NcTooltip
class="text-sm !leading-5 capitalize font-semibold truncate"
:class="{
'text-gray-800': !isUserDeleted(integration.created_by),
'text-gray-500': isUserDeleted(integration.created_by),
}"
:disabled="isUserDeleted(integration.created_by)"
show-on-truncate-only
placement="bottom"
>
<template #title>
{{
collaboratorsMap.get(integration.created_by)?.display_name ||
collaboratorsMap
.get(integration.created_by)
?.email?.slice(0, collaboratorsMap.get(integration.created_by)?.email.indexOf('@'))
}}
</template>
{{
collaboratorsMap.get(integration.created_by)?.display_name ||
collaboratorsMap
.get(integration.created_by)
?.email?.slice(0, collaboratorsMap.get(integration.created_by)?.email.indexOf('@'))
}}
</NcTooltip>
</div>
<NcTooltip
class="text-xs !leading-4 truncate"
:class="{
'text-gray-600': !isUserDeleted(integration.created_by),
'text-gray-500': isUserDeleted(integration.created_by),
}"
:disabled="isUserDeleted(integration.created_by)"
show-on-truncate-only
placement="bottom"
>
<template #title>
{{ collaboratorsMap.get(integration.created_by)?.email }}
</template>
{{
collaboratorsMap.get(integration.created_by)?.display_name ||
collaboratorsMap
.get(integration.created_by)
?.email?.slice(0, collaboratorsMap.get(integration.created_by)?.email.indexOf('@'))
}}
{{ collaboratorsMap.get(integration.created_by)?.email }}
</NcTooltip>
</div>
<NcTooltip class="text-xs !leading-4 text-gray-600 truncate" show-on-truncate-only placement="bottom">
<template #title>
{{ collaboratorsMap.get(integration.created_by)?.email }}
</template>
{{ collaboratorsMap.get(integration.created_by)?.email }}
</NcTooltip>
</div>
</div>
<template v-else>{{ integration.created_by }} </template>
<div v-else class="w-full truncate text-gray-500">{{ integration.created_by }} </div>
</NcTooltip>
</div>
</td>
<td class="cell-usage">

25
packages/nc-gui/components/workspace/integrations/SupportedDocs.vue

@ -1,15 +1,15 @@
<script lang="ts" setup>
const supportedDocs = [
{
title: 'Configure a PostgreSQL Integration',
title: 'Integrations',
href: '',
},
{
title: 'Getting started with Integrations',
title: 'Create new connection',
href: '',
},
{
title: 'Troubleshoot database connection',
title: 'Add new data source',
href: '',
},
] as {
@ -24,13 +24,18 @@ const supportedDocs = [
<div class="text-sm text-gray-800 font-semibold">Supported Docs</div>
<div>
<div v-for="doc of supportedDocs" class="flex items-center gap-1">
<div class="h-7 w-7 flex items-center justify-center">
<GeneralIcon icon="bookOpen" class="flex-none w-4 h-4 text-gray-600"/>
</div>
<a :href="doc.href" target="_blank" rel="noopener noreferrer" class="!text-gray-700 text-sm !no-underline !hover:underline">
{{ doc.title }}
</a>
<div v-for="(doc, idx) of supportedDocs" :key="idx" class="flex items-center gap-1">
<div class="h-7 w-7 flex items-center justify-center">
<GeneralIcon icon="bookOpen" class="flex-none w-4 h-4 text-gray-600" />
</div>
<a
:href="doc.href"
target="_blank"
rel="noopener noreferrer"
class="!text-gray-700 text-sm !no-underline !hover:underline"
>
{{ doc.title }}
</a>
</div>
</div>
</div>

10
packages/nc-gui/components/workspace/integrations/view.vue

@ -36,7 +36,7 @@ onMounted(() => {
until(() => currentWorkspace.value?.id)
.toMatch((v) => !!v)
.then(async () => {
await Promise.all([loadCollaborators({} as any, currentWorkspace.value!.id), loadIntegrations()])
await Promise.all([loadCollaborators({ includeDeleted: true }, currentWorkspace.value!.id), loadIntegrations()])
})
})
@ -55,7 +55,7 @@ onBeforeMount(() => {
<NcTabs v-model:activeKey="activeViewTab">
<template #leftExtra>
<div class="w-6"></div>
<div class="w-3"></div>
</template>
<template v-if="isUIAllowed('workspaceIntegrations')">
<a-tab-pane key="integrations" class="w-full">
@ -65,8 +65,8 @@ onBeforeMount(() => {
{{ $t('general.integrations') }}
</div>
</template>
<div class="h-[calc(100vh-92px)] p-6">
<WorkspaceIntegrationsNewAvailableList />
<div class="h-[calc(100vh-92px)]">
<WorkspaceIntegrationsTab />
</div>
</a-tab-pane>
</template>
@ -89,7 +89,7 @@ onBeforeMount(() => {
</div>
</template>
<div class="h-[calc(100vh-92px)] p-6">
<WorkspaceIntegrationsList />
<WorkspaceIntegrationsConnectionsTab />
</div>
</a-tab-pane>
</template>

2
packages/nc-gui/composables/useUserSorts.ts

@ -148,7 +148,7 @@ export function useUserSorts(roleType: 'Workspace' | 'Org' | 'Project' | 'Organi
return b[sortsConfig.field] - a[sortsConfig.field]
}
}
case 'webhook-operation-type':{
case 'webhook-operation-type': {
if (sortsConfig.direction === 'asc') {
return `${a?.event} ${a?.operation}`?.localeCompare(`${b?.event} ${b?.operation}`)
} else {

50
packages/nc-gui/lang/en.json

@ -369,7 +369,49 @@
"trello": "Trello",
"typeform": "Typeform",
"workday": "Workday",
"zendesk": "Zendesk"
"zendesk": "Zendesk",
"mysql": "MySQL",
"postgreSQL": "PostgreSQL",
"sqlServer": "SQL Server",
"dataBricks": "DataBricks",
"mssqlServer": "MSSQL Server",
"oracle": "Oracle",
"telegram": "Telegram",
"whatsapp": "Whatsapp",
"gmail": "Gmail",
"pipedrive": "Pipedrive",
"microsoftDynamics365": "Microsoft Dynamics 365",
"zohoCrm": "Zoho CRM",
"greenhouse": "Greenhouse",
"lever": "Lever",
"bitbucket": "BitBucket",
"quickbooks": "Quickbooks",
"intercom": "Intercom",
"dropbox": "Dropbox"
},
"integrationCategories" :{
"allIntegrations": "All Integrations",
"allIntegrationsSubtitle": "",
"databaseSubtitle": "Seamlessly connect and manage your databases with NocoDB.",
"communication": "Communication",
"communicationSubtitle": "Get notified on changes and streamline team communication with NocoDB.",
"projectManagement": "Project Management",
"projectManagementSubtitle": "Enhance project workflows and task management with NocoDB.",
"crm": "CRM",
"crmSubtitle": "Optimize customer relationship management through NocoDB integrations.",
"marketing": "Marketing",
"marketingSubtitle": "Boost your marketing efforts with NocoDB's powerful integrations.",
"ats": "ATS",
"atsSubtitle": "Streamline your applicant tracking system with NocoDB.",
"development": "Development",
"developmentSubtitle": "Accelerate development processes with NocoDB integrations.",
"finance": "Finance",
"financeSubtitle": "Simplify financial operations and data management with NocoDB.",
"ticketing": "Ticketing",
"ticketingSubtitle": "Manage and track support tickets efficiently with NocoDB.",
"storageSubtitle": "Integrate and organize your storage solutions seamlessly with NocoDB.",
"others": "Others",
"othersSubtitle": "Discover additional versatile integrations to enhance your NocoDB experience."
}
},
"datatype": {
@ -558,7 +600,8 @@
"looksLikeThisStackIsEmpty": "Looks like this stack does not have any records",
"fromScratch": "From scratch",
"fromFileAndExternalSources": "From files & external sources",
"directlyInRealTime": "Directly in real time"
"directlyInRealTime": "Directly in real time",
"categories": "Categories"
},
"labels": {
"defaultView": "Default view",
@ -1219,7 +1262,8 @@
"preFillFormInfo": "To get a prefilled link, make sure you’ve filled the necessary fields in the form view builder.",
"surveyFormInfo": "Form mode with one field per page",
"useFieldEditMenuToConfigFieldType": "Use field edit menu for type conversions after file is imported",
"roleInheritedFromWorkspace": "Role inherited from workspace"
"roleInheritedFromWorkspace": "Role inherited from workspace",
"comingSoonIntegration": "Coming soon! Click to upvote for the integration you need in NocoDB."
},
"placeholder": {
"selectSlackChannels": "Select Slack channels",

75
packages/nc-gui/lib/enums.ts

@ -178,29 +178,72 @@ export enum IntegrationStoreEvents {
INTEGRATION_ADD = 'integration-add',
}
// Move this to nocodb-sdk
export enum SyncDataType {
// APPLE_NUMBERS = 'apple-numbers',
ASANA = 'asana',
BOX = 'box',
GITHUB = 'github',
GITLAB = 'gitlab',
GOOGLE_CALENDAR = 'google-calendar',
GOOGLE_DRIVE = 'google-drive',
GOOGLE_SHEETS = 'google-sheets',
HUBSPOT = 'hubspot',
JIRA = 'jira',
MAILCHIMP = 'mailchimp',
// Database
SNOWFLAKE = 'snowflake',
MICROSOFT_ACCESS = 'microsoft-access',
MICROSOFT_EXCEL = 'microsoft-excel',
TABLEAU = 'tableau',
ORACLE = 'oracle',
// Communication
SLACK = 'slack',
DISCORD = 'discord',
TWILLO = 'twillo',
MICROSOFT_OUTLOOK = 'microsoft-outlook',
MICROSOFT_TEAMS = 'microsoft-teams',
TELEGRAM = 'telegram',
GMAIL = 'gmail',
WHATSAPP = 'whatsapp',
// Project Management
ASANA = 'asana',
JIRA = 'jira',
MIRO = 'miro',
TRELLO = 'trello',
// CRM
SALESFORCE = 'salesforce',
SNOWFLAKE = 'snowflake',
STRIPE = 'stripe',
PIPEDRIVE = 'pipedrive',
MICROSOFT_DYNAMICS_365 = 'microsoft-dynamics-365',
ZOHO_CRM = 'zoho-crm',
// Marketing
HUBSPOT = 'hubspot',
MAILCHIMP = 'mailchimp',
SURVEYMONKEY = 'surveymonkey',
TABLEAU = 'tableau',
TRELLO = 'trello',
TYPEFORM = 'typeform',
// ATS
WORKDAY = 'workday',
GREENHOUSE = 'greenhouse',
LEVER = 'lever',
// Development
GITHUB = 'github',
GITLAB = 'gitlab',
BITBUCKET = 'bitbucket',
// Finance
STRIPE = 'stripe',
QUICKBOOKS = 'quickbooks',
// Ticketing
INTERCOM = 'intercom',
ZENDESK = 'zendesk',
// Storage
BOX = 'box',
GOOGLE_DRIVE = 'google-drive',
DROPBOX = 'dropbox',
// Others
APPLE_NUMBERS = 'apple-numbers',
GOOGLE_CALENDAR = 'google-calendar',
MICROSOFT_EXCEL = 'microsoft-excel',
GOOGLE_SHEETS = 'google-sheets',
}
export enum IntegrationCategoryType {
DATABASE = 'database',
COMMUNICATION = 'communication',
PROJECT_MANAGEMENT = 'project-management',
CRM = 'crm',
MARKETING = 'marketing',
ATS = 'ats',
DEVELOPMENT = 'development',
FINANCE = 'finance',
TICKETING = 'ticketing',
STORAGE = 'storage',
OTHERS = 'others',
}

2
packages/nc-gui/utils/baseCreateUtils.ts

@ -1,4 +1,4 @@
import { SSLUsage, type BoolType } from 'nocodb-sdk'
import { type BoolType, SSLUsage } from 'nocodb-sdk'
import { ClientType } from '~/lib/enums'
// todo: move to noco-sdk

48
packages/nc-gui/utils/iconUtils.ts

@ -250,6 +250,32 @@ import NcMattermost from '~icons/nc-icons/mattermost'
import NcTwilio from '~icons/nc-icons/twilio'
import NcWhatsapp from '~icons/nc-icons/whatsapp'
// View icons
import NcViewGantt from '~icons/nc-icons/view-gantt'
import NcDollerSign from '~icons/nc-icons/doller-sign'
import NcMultiFile from '~icons/nc-icons/multi-file'
import NcHeart from '~icons/nc-icons/heart'
import NcSave from '~icons/nc-icons/save'
import NcMySql from '~icons/logos/mysql-icon'
import NcPostgreSql from '~icons/nc-icons/postgresql'
import NcSqlServer from '~icons/nc-icons/sql-server'
import NcDataBricks from '~icons/nc-icons/data-bricks'
import NcMssqlServer from '~icons/nc-icons/mssql-server'
import NcOracle from '~icons/nc-icons/oracle'
import NcGmail from '~icons/nc-icons/gmail'
import NcTelegram from '~icons/nc-icons/telegram'
import NcMicrosoftDynamics365 from '~icons/nc-icons/microsoft-dynamics-365'
import NcPipedrive from '~icons/nc-icons/pipedrive'
import NcZohoCrm from '~icons/nc-icons/zoho-crm'
import NcGreenhouse from '~icons/nc-icons/greenhouse'
import NcLever from '~icons/nc-icons/lever'
import NcBitBucket from '~icons/nc-icons/bit-bucket'
import NcQuickbooks from '~icons/nc-icons/quickbooks'
import NcIntercom from '~icons/nc-icons/intercom'
import NcDropbox from '~icons/nc-icons/dropbox'
// keep it for reference
// todo: remove it after all icons are migrated
/* export const iconMapOld = {
@ -725,6 +751,28 @@ export const iconMap = {
mattermost: NcMattermost,
twilio: NcTwilio,
whatsapp: NcWhatsapp,
viewGannt: NcViewGantt,
dollerSign: NcDollerSign,
multiFile: NcMultiFile,
heart: NcHeart,
ncSave: NcSave,
mysql: NcMySql,
postgreSql: NcPostgreSql,
sqlServer: NcSqlServer,
dataBricks: NcDataBricks,
mssqlServer: NcMssqlServer,
oracle: NcOracle,
gmail: NcGmail,
telegram: NcTelegram,
microsoftDynamics365: NcMicrosoftDynamics365,
pipedrive: NcPipedrive,
zohoCrm: NcZohoCrm,
greenhouse: NcGreenhouse,
lever: NcLever,
bitBucket: NcBitBucket,
quickbooks: NcQuickbooks,
intercom: NcIntercom,
dropbox: NcDropbox,
}
export const getMdiIcon = (type: string): any => {

485
packages/nc-gui/utils/syncDataUtils.ts

@ -1,38 +1,461 @@
import type { FunctionalComponent, SVGAttributes } from 'nuxt/dist/app/compat/capi'
import { SyncDataType } from '~/lib/enums'
export const syncDataTypes = [
// { title: 'objects.syncData.appleNumbers', value: SyncDataType.APPLE_NUMBERS, icon: iconMap.appleSolid },
{ title: 'objects.syncData.asana', value: SyncDataType.ASANA, icon: iconMap.asana },
{ title: 'objects.syncData.box', value: SyncDataType.BOX, icon: iconMap.box },
{ title: 'objects.syncData.github', value: SyncDataType.GITHUB, icon: iconMap.githubSolid },
{ title: 'objects.syncData.gitlab', value: SyncDataType.GITLAB, icon: iconMap.gitlab },
{ title: 'objects.syncData.googleCalendar', value: SyncDataType.GOOGLE_CALENDAR, icon: iconMap.googleCalendar },
{ title: 'objects.syncData.googleDrive', value: SyncDataType.GOOGLE_DRIVE, icon: iconMap.googleDrive },
{ title: 'objects.syncData.googleSheets', value: SyncDataType.GOOGLE_SHEETS, icon: iconMap.googleSheet },
{ title: 'objects.syncData.hubspot', value: SyncDataType.HUBSPOT, icon: iconMap.hubspot },
{ title: 'objects.syncData.jira', value: SyncDataType.JIRA, icon: iconMap.jira },
{ title: 'objects.syncData.mailchimp', value: SyncDataType.MAILCHIMP, icon: iconMap.mailchimp },
{ title: 'objects.syncData.microsoftAccess', value: SyncDataType.MICROSOFT_ACCESS, icon: iconMap.microsoftAccess },
{ title: 'objects.syncData.microsoftExcel', value: SyncDataType.MICROSOFT_EXCEL, icon: iconMap.microsoftExcel },
{ title: 'objects.syncData.microsoftOutlook', value: SyncDataType.MICROSOFT_OUTLOOK, icon: iconMap.microsoftOutlook },
{ title: 'objects.syncData.miro', value: SyncDataType.MIRO, icon: iconMap.miro },
{ title: 'objects.syncData.salesforce', value: SyncDataType.SALESFORCE, icon: iconMap.salesforce },
{ title: 'objects.syncData.snowflake', value: SyncDataType.SNOWFLAKE, icon: iconMap.snowflake },
{ title: 'objects.syncData.stripe', value: SyncDataType.STRIPE, icon: iconMap.stripe },
{ title: 'objects.syncData.surveyMonkey', value: SyncDataType.SURVEYMONKEY, icon: iconMap.surveyMonkey },
{ title: 'objects.syncData.tableau', value: SyncDataType.TABLEAU, icon: iconMap.tableau },
{ title: 'objects.syncData.trello', value: SyncDataType.TRELLO, icon: iconMap.trello },
{ title: 'objects.syncData.typeform', value: SyncDataType.TYPEFORM, icon: iconMap.typeform },
{ title: 'objects.syncData.workday', value: SyncDataType.WORKDAY, icon: iconMap.workday },
{ title: 'objects.syncData.zendesk', value: SyncDataType.ZENDESK, icon: iconMap.zendesk },
] as {
import type { CSSProperties, FunctionalComponent, SVGAttributes } from 'nuxt/dist/app/compat/capi'
import { ClientType, IntegrationCategoryType, SyncDataType } from '~/lib/enums'
export interface IntegrationItemType {
title: string
icon: FunctionalComponent<SVGAttributes, {}, any, {}>
value: SyncDataType | ClientType
categories: IntegrationCategoryType[]
isAvailable?: boolean
iconStyle?: CSSProperties
}
export interface IntegrationCategoryItemType {
title: string
subtitle: string
value: IntegrationCategoryType
icon: FunctionalComponent<SVGAttributes, {}, any, {}>
iconBgColor?: string
iconStyle?: CSSProperties
}
export const integrationCategories: IntegrationCategoryItemType[] = [
{
title: 'labels.database',
subtitle: 'objects.integrationCategories.databaseSubtitle',
value: IntegrationCategoryType.DATABASE,
icon: iconMap.database,
iconBgColor: '#D4F7E0',
iconStyle: {
color: '#17803D',
},
},
{
title: 'objects.integrationCategories.communication',
subtitle: 'objects.integrationCategories.communicationSubtitle',
value: IntegrationCategoryType.COMMUNICATION,
icon: iconMap.messageCircle,
iconBgColor: '#FFF0F7',
iconStyle: {
color: '#801044',
},
},
{
title: 'objects.integrationCategories.projectManagement',
subtitle: 'objects.integrationCategories.projectManagementSubtitle',
value: IntegrationCategoryType.PROJECT_MANAGEMENT,
icon: iconMap.viewGannt,
iconBgColor: '#FFF0D1',
iconStyle: {
color: '#977223',
},
},
{
title: 'objects.integrationCategories.crm',
subtitle: 'objects.integrationCategories.crmSubtitle',
value: IntegrationCategoryType.CRM,
icon: iconMap.users,
iconBgColor: '#D7F2FF',
iconStyle: {
color: '#207399',
},
},
{
title: 'objects.integrationCategories.marketing',
subtitle: 'objects.integrationCategories.marketingSubtitle',
value: IntegrationCategoryType.MARKETING,
icon: iconMap.heart,
iconBgColor: '#FED8F4',
iconStyle: {
color: '#972377',
},
},
{
title: 'objects.integrationCategories.ats',
subtitle: 'objects.integrationCategories.atsSubtitle',
value: IntegrationCategoryType.ATS,
icon: iconMap.multiFile,
iconBgColor: '#FEE6D6',
iconStyle: {
color: '#C86827',
},
},
{
title: 'objects.integrationCategories.development',
subtitle: 'objects.integrationCategories.developmentSubtitle',
value: IntegrationCategoryType.DEVELOPMENT,
icon: iconMap.code,
iconBgColor: '#E5D4F5',
iconStyle: {
color: '#4B177B',
},
},
{
title: 'objects.integrationCategories.finance',
subtitle: 'objects.integrationCategories.financeSubtitle',
value: IntegrationCategoryType.FINANCE,
icon: iconMap.dollerSign,
iconBgColor: '#D4F7E0',
iconStyle: {
color: '#17803D',
},
},
{
title: 'objects.integrationCategories.ticketing',
subtitle: 'objects.integrationCategories.ticketingSubtitle',
value: IntegrationCategoryType.TICKETING,
icon: iconMap.globe,
iconBgColor: '#FFF0D1',
iconStyle: {
color: '#977223',
},
},
{
title: 'labels.storage',
subtitle: 'objects.integrationCategories.storageSubtitle',
value: IntegrationCategoryType.STORAGE,
icon: iconMap.ncSave,
iconBgColor: '#E7E7E9',
iconStyle: {
color: '#374151',
},
},
{
title: 'objects.integrationCategories.others',
subtitle: 'objects.integrationCategories.othersSubtitle',
value: IntegrationCategoryType.OTHERS,
icon: iconMap.plusSquare,
iconBgColor: 'white',
iconStyle: {
color: '#374151',
},
},
]
export const allIntegrations: IntegrationItemType[] = [
// Database
{
title: 'objects.syncData.mysql',
value: ClientType.MYSQL,
icon: iconMap.mysql,
categories: [IntegrationCategoryType.DATABASE],
isAvailable: true,
iconStyle: {
width: '32px',
height: '32px',
},
},
{
title: 'objects.syncData.postgreSQL',
value: ClientType.PG,
icon: iconMap.postgreSql,
categories: [IntegrationCategoryType.DATABASE],
isAvailable: true,
},
{
title: 'objects.syncData.snowflake',
value: ClientType.SNOWFLAKE,
icon: iconMap.snowflake,
categories: [IntegrationCategoryType.DATABASE],
},
{
title: 'objects.syncData.dataBricks',
value: ClientType.DATABRICKS,
icon: iconMap.dataBricks,
categories: [IntegrationCategoryType.DATABASE],
},
{
title: 'objects.syncData.microsoftAccess',
value: SyncDataType.MICROSOFT_ACCESS,
icon: iconMap.microsoftAccess,
categories: [IntegrationCategoryType.DATABASE],
},
{
title: 'objects.syncData.mssqlServer',
value: ClientType.MSSQL,
icon: iconMap.mssqlServer,
categories: [IntegrationCategoryType.DATABASE],
},
{
title: 'objects.syncData.oracle',
value: SyncDataType.ORACLE,
icon: iconMap.oracle,
categories: [IntegrationCategoryType.DATABASE],
},
{
title: 'objects.syncData.tableau',
value: SyncDataType.TABLEAU,
icon: iconMap.tableau,
categories: [IntegrationCategoryType.DATABASE],
},
// Communication
{
title: 'general.slack',
value: SyncDataType.SLACK,
icon: iconMap.slack,
categories: [IntegrationCategoryType.COMMUNICATION],
iconStyle: {
width: '32px',
height: '32px',
},
},
{
title: 'general.discord',
value: SyncDataType.DISCORD,
icon: iconMap.ncDiscord,
categories: [IntegrationCategoryType.COMMUNICATION],
iconStyle: {
width: '32px',
height: '32px',
},
},
{
title: 'general.twilio',
value: SyncDataType.TWILLO,
icon: iconMap.twilio,
categories: [IntegrationCategoryType.COMMUNICATION],
iconStyle: {
width: '32px',
height: '32px',
},
},
{
title: 'objects.syncData.microsoftOutlook',
value: SyncDataType.MICROSOFT_OUTLOOK,
icon: iconMap.microsoftOutlook,
categories: [IntegrationCategoryType.COMMUNICATION],
},
{
title: 'general.microsoftTeams',
value: SyncDataType.MICROSOFT_TEAMS,
icon: iconMap.microsoftTeams,
categories: [IntegrationCategoryType.COMMUNICATION],
iconStyle: {
width: '32px',
height: '32px',
},
},
{
title: 'objects.syncData.gmail',
value: SyncDataType.GMAIL,
icon: iconMap.gmail,
categories: [IntegrationCategoryType.COMMUNICATION],
},
{
title: 'objects.syncData.telegram',
value: SyncDataType.TELEGRAM,
icon: iconMap.telegram,
categories: [IntegrationCategoryType.COMMUNICATION],
},
{
title: 'objects.syncData.whatsapp',
value: SyncDataType.WHATSAPP,
icon: iconMap.whatsapp,
categories: [IntegrationCategoryType.COMMUNICATION],
iconStyle: {
width: '32px',
height: '32px',
},
},
// Project Management
{
title: 'objects.syncData.asana',
value: SyncDataType.ASANA,
icon: iconMap.asana,
categories: [IntegrationCategoryType.PROJECT_MANAGEMENT],
},
{
title: 'objects.syncData.jira',
value: SyncDataType.JIRA,
icon: iconMap.jira,
categories: [IntegrationCategoryType.PROJECT_MANAGEMENT, IntegrationCategoryType.TICKETING],
},
{
title: 'objects.syncData.miro',
value: SyncDataType.MIRO,
icon: iconMap.miro,
categories: [IntegrationCategoryType.PROJECT_MANAGEMENT],
},
{
title: 'objects.syncData.trello',
value: SyncDataType.TRELLO,
icon: iconMap.trello,
categories: [IntegrationCategoryType.PROJECT_MANAGEMENT],
},
// CRM
{
title: 'objects.syncData.microsoftDynamics365',
value: SyncDataType.MICROSOFT_DYNAMICS_365,
icon: iconMap.microsoftDynamics365,
categories: [IntegrationCategoryType.CRM],
},
{
title: 'objects.syncData.pipedrive',
value: SyncDataType.PIPEDRIVE,
icon: iconMap.pipedrive,
categories: [IntegrationCategoryType.CRM],
},
{
title: 'objects.syncData.salesforce',
value: SyncDataType.SALESFORCE,
icon: iconMap.salesforce,
categories: [IntegrationCategoryType.CRM],
},
{
title: 'objects.syncData.zohoCrm',
value: SyncDataType.ZOHO_CRM,
icon: iconMap.zohoCrm,
categories: [IntegrationCategoryType.CRM],
},
// Marketing
{
title: 'objects.syncData.hubspot',
value: SyncDataType.HUBSPOT,
icon: iconMap.hubspot,
categories: [IntegrationCategoryType.MARKETING],
},
{
title: 'objects.syncData.mailchimp',
value: SyncDataType.MAILCHIMP,
icon: iconMap.mailchimp,
categories: [IntegrationCategoryType.MARKETING],
},
{
title: 'objects.syncData.surveyMonkey',
value: SyncDataType.SURVEYMONKEY,
icon: iconMap.surveyMonkey,
categories: [IntegrationCategoryType.MARKETING],
},
{
title: 'objects.syncData.typeform',
value: SyncDataType.TYPEFORM,
icon: iconMap.typeform,
categories: [IntegrationCategoryType.MARKETING],
},
// ATS
{
title: 'objects.syncData.workday',
value: SyncDataType.WORKDAY,
icon: iconMap.workday,
categories: [IntegrationCategoryType.ATS],
},
{
title: 'objects.syncData.greenhouse',
value: SyncDataType.GREENHOUSE,
icon: iconMap.greenhouse,
categories: [IntegrationCategoryType.ATS],
},
{
title: 'objects.syncData.lever',
value: SyncDataType.LEVER,
icon: iconMap.lever,
categories: [IntegrationCategoryType.ATS],
},
// Development
{
title: 'objects.syncData.bitbucket',
value: SyncDataType.BITBUCKET,
icon: iconMap.bitBucket,
categories: [IntegrationCategoryType.DEVELOPMENT],
},
{
title: 'objects.syncData.github',
value: SyncDataType.GITHUB,
icon: iconMap.githubSolid,
categories: [IntegrationCategoryType.DEVELOPMENT],
},
{
title: 'objects.syncData.gitlab',
value: SyncDataType.GITLAB,
icon: iconMap.gitlab,
categories: [IntegrationCategoryType.DEVELOPMENT],
},
// Finance
{
title: 'objects.syncData.stripe',
value: SyncDataType.STRIPE,
icon: iconMap.stripe,
categories: [IntegrationCategoryType.FINANCE],
},
{
title: 'objects.syncData.quickbooks',
value: SyncDataType.QUICKBOOKS,
icon: iconMap.quickbooks,
categories: [IntegrationCategoryType.FINANCE],
},
// Ticketing
{
title: 'objects.syncData.intercom',
value: SyncDataType.INTERCOM,
icon: iconMap.intercom,
categories: [IntegrationCategoryType.TICKETING],
},
{
title: 'objects.syncData.zendesk',
value: SyncDataType.ZENDESK,
icon: iconMap.zendesk,
categories: [IntegrationCategoryType.TICKETING],
},
// Storage
{ title: 'objects.syncData.box', value: SyncDataType.BOX, icon: iconMap.box, categories: [IntegrationCategoryType.STORAGE] },
{
title: 'objects.syncData.dropbox',
value: SyncDataType.DROPBOX,
icon: iconMap.dropbox,
categories: [IntegrationCategoryType.STORAGE],
},
{
title: 'objects.syncData.googleDrive',
value: SyncDataType.GOOGLE_DRIVE,
icon: iconMap.googleDrive,
categories: [IntegrationCategoryType.STORAGE],
},
// Others
{
title: 'objects.syncData.appleNumbers',
value: SyncDataType.APPLE_NUMBERS,
icon: iconMap.appleSolid,
categories: [IntegrationCategoryType.OTHERS],
},
{
title: 'objects.syncData.googleCalendar',
value: SyncDataType.GOOGLE_CALENDAR,
icon: iconMap.googleCalendar,
categories: [IntegrationCategoryType.OTHERS],
},
{
title: 'objects.syncData.microsoftExcel',
value: SyncDataType.MICROSOFT_EXCEL,
icon: iconMap.microsoftExcel,
categories: [IntegrationCategoryType.OTHERS],
},
{
title: 'objects.syncData.googleSheets',
value: SyncDataType.GOOGLE_SHEETS,
icon: iconMap.googleSheet,
categories: [IntegrationCategoryType.OTHERS],
},
]
export const syncDataTypes = [] as {
title: string
icon: FunctionalComponent<SVGAttributes, {}, any, {}>
value: SyncDataType
}[]
export const syncDataTypesMap = syncDataTypes.reduce((acc, curr) => {
export const syncDataTypesMap = allIntegrations.reduce((acc, curr) => {
acc[curr.value] = curr
return acc
}, {})
}, {} as Record<string, IntegrationItemType>)

Loading…
Cancel
Save