EditGroupModal.svelte 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <script lang="ts">
  2. import { toast } from 'svelte-sonner';
  3. import { getContext, onMount } from 'svelte';
  4. const i18n = getContext('i18n');
  5. import Modal from '$lib/components/common/Modal.svelte';
  6. import Display from './Display.svelte';
  7. import Permissions from './Permissions.svelte';
  8. import Users from './Users.svelte';
  9. import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
  10. import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
  11. export let onSubmit: Function = () => {};
  12. export let onDelete: Function = () => {};
  13. export let show = false;
  14. export let edit = false;
  15. export let users = [];
  16. export let group = null;
  17. export let custom = true;
  18. export let tabs = ['general', 'permissions', 'users'];
  19. let selectedTab = 'general';
  20. let loading = false;
  21. export let name = '';
  22. export let description = '';
  23. export let permissions = {
  24. workspace: {
  25. models: false,
  26. knowledge: false,
  27. prompts: false,
  28. tools: false
  29. },
  30. chat: {
  31. file_upload: true,
  32. delete: true,
  33. edit: true,
  34. temporary: true
  35. }
  36. };
  37. export let userIds = [];
  38. const submitHandler = async () => {
  39. loading = true;
  40. const group = {
  41. name,
  42. description,
  43. permissions,
  44. user_ids: userIds
  45. };
  46. await onSubmit(group);
  47. loading = false;
  48. show = false;
  49. };
  50. const init = () => {
  51. if (group) {
  52. name = group.name;
  53. description = group.description;
  54. permissions = group?.permissions ?? {
  55. workspace: {
  56. models: false,
  57. knowledge: false,
  58. prompts: false,
  59. tools: false
  60. },
  61. chat: {
  62. file_upload: true,
  63. delete: true,
  64. edit: true,
  65. temporary: true
  66. }
  67. };
  68. userIds = group?.user_ids ?? [];
  69. }
  70. };
  71. $: if (show) {
  72. init();
  73. }
  74. onMount(() => {
  75. console.log(tabs);
  76. selectedTab = tabs[0];
  77. init();
  78. });
  79. </script>
  80. <Modal size="md" bind:show>
  81. <div>
  82. <div class=" flex justify-between dark:text-gray-100 px-5 pt-4 mb-1.5">
  83. <div class=" text-lg font-medium self-center font-primary">
  84. {#if custom}
  85. {#if edit}
  86. {$i18n.t('Edit User Group')}
  87. {:else}
  88. {$i18n.t('Add User Group')}
  89. {/if}
  90. {:else}
  91. {$i18n.t('Edit Default Permissions')}
  92. {/if}
  93. </div>
  94. <button
  95. class="self-center"
  96. on:click={() => {
  97. show = false;
  98. }}
  99. >
  100. <svg
  101. xmlns="http://www.w3.org/2000/svg"
  102. viewBox="0 0 20 20"
  103. fill="currentColor"
  104. class="w-5 h-5"
  105. >
  106. <path
  107. d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
  108. />
  109. </svg>
  110. </button>
  111. </div>
  112. <div class="flex flex-col md:flex-row w-full px-4 pb-4 md:space-x-4 dark:text-gray-200">
  113. <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
  114. <form
  115. class="flex flex-col w-full"
  116. on:submit={(e) => {
  117. e.preventDefault();
  118. submitHandler();
  119. }}
  120. >
  121. <div class="flex flex-col lg:flex-row w-full h-full pb-2 lg:space-x-4">
  122. <div
  123. id="admin-settings-tabs-container"
  124. class="tabs flex flex-row overflow-x-auto gap-2.5 max-w-full lg:gap-1 lg:flex-col lg:flex-none lg:w-40 dark:text-gray-200 text-sm font-medium text-left scrollbar-none"
  125. >
  126. {#if tabs.includes('general')}
  127. <button
  128. class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
  129. 'general'
  130. ? ''
  131. : ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  132. on:click={() => {
  133. selectedTab = 'general';
  134. }}
  135. type="button"
  136. >
  137. <div class=" self-center mr-2">
  138. <svg
  139. xmlns="http://www.w3.org/2000/svg"
  140. viewBox="0 0 16 16"
  141. fill="currentColor"
  142. class="w-4 h-4"
  143. >
  144. <path
  145. fill-rule="evenodd"
  146. d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
  147. clip-rule="evenodd"
  148. />
  149. </svg>
  150. </div>
  151. <div class=" self-center">{$i18n.t('General')}</div>
  152. </button>
  153. {/if}
  154. {#if tabs.includes('permissions')}
  155. <button
  156. class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
  157. 'permissions'
  158. ? ''
  159. : ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  160. on:click={() => {
  161. selectedTab = 'permissions';
  162. }}
  163. type="button"
  164. >
  165. <div class=" self-center mr-2">
  166. <WrenchSolid />
  167. </div>
  168. <div class=" self-center">{$i18n.t('Permissions')}</div>
  169. </button>
  170. {/if}
  171. {#if tabs.includes('users')}
  172. <button
  173. class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
  174. 'users'
  175. ? ''
  176. : ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  177. on:click={() => {
  178. selectedTab = 'users';
  179. }}
  180. type="button"
  181. >
  182. <div class=" self-center mr-2">
  183. <UserPlusSolid />
  184. </div>
  185. <div class=" self-center">{$i18n.t('Users')} ({userIds.length})</div>
  186. </button>
  187. {/if}
  188. </div>
  189. <div
  190. class="flex-1 mt-1 lg:mt-1 lg:h-[22rem] lg:max-h-[22rem] overflow-y-auto scrollbar-hidden"
  191. >
  192. {#if selectedTab == 'general'}
  193. <Display bind:name bind:description />
  194. {:else if selectedTab == 'permissions'}
  195. <Permissions bind:permissions />
  196. {:else if selectedTab == 'users'}
  197. <Users bind:userIds {users} />
  198. {/if}
  199. </div>
  200. </div>
  201. <!-- <div
  202. class=" tabs flex flex-row overflow-x-auto gap-2.5 text-sm font-medium border-b border-b-gray-800 scrollbar-hidden"
  203. >
  204. {#if tabs.includes('display')}
  205. <button
  206. class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
  207. 'display'
  208. ? ' dark:border-white'
  209. : 'border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  210. on:click={() => {
  211. selectedTab = 'display';
  212. }}
  213. type="button"
  214. >
  215. {$i18n.t('Display')}
  216. </button>
  217. {/if}
  218. {#if tabs.includes('permissions')}
  219. <button
  220. class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
  221. 'permissions'
  222. ? ' dark:border-white'
  223. : 'border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  224. on:click={() => {
  225. selectedTab = 'permissions';
  226. }}
  227. type="button"
  228. >
  229. {$i18n.t('Permissions')}
  230. </button>
  231. {/if}
  232. {#if tabs.includes('users')}
  233. <button
  234. class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
  235. 'users'
  236. ? ' dark:border-white'
  237. : ' border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  238. on:click={() => {
  239. selectedTab = 'users';
  240. }}
  241. type="button"
  242. >
  243. {$i18n.t('Users')} ({userIds.length})
  244. </button>
  245. {/if}
  246. </div> -->
  247. <div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
  248. {#if edit}
  249. <button
  250. class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-gray-900 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
  251. type="button"
  252. on:click={() => {
  253. onDelete();
  254. show = false;
  255. }}
  256. >
  257. {$i18n.t('Delete')}
  258. </button>
  259. {/if}
  260. <button
  261. class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
  262. ? ' cursor-not-allowed'
  263. : ''}"
  264. type="submit"
  265. disabled={loading}
  266. >
  267. {$i18n.t('Save')}
  268. {#if loading}
  269. <div class="ml-2 self-center">
  270. <svg
  271. class=" w-4 h-4"
  272. viewBox="0 0 24 24"
  273. fill="currentColor"
  274. xmlns="http://www.w3.org/2000/svg"
  275. ><style>
  276. .spinner_ajPY {
  277. transform-origin: center;
  278. animation: spinner_AtaB 0.75s infinite linear;
  279. }
  280. @keyframes spinner_AtaB {
  281. 100% {
  282. transform: rotate(360deg);
  283. }
  284. }
  285. </style><path
  286. d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
  287. opacity=".25"
  288. /><path
  289. d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
  290. class="spinner_ajPY"
  291. /></svg
  292. >
  293. </div>
  294. {/if}
  295. </button>
  296. </div>
  297. </form>
  298. </div>
  299. </div>
  300. </div>
  301. </Modal>