EditGroupModal.svelte 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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. controls: true,
  32. file_upload: true,
  33. delete: true,
  34. edit: true,
  35. temporary: true
  36. },
  37. features: {
  38. web_search: true,
  39. image_generation: true,
  40. code_interpreter: true
  41. }
  42. };
  43. export let userIds = [];
  44. const submitHandler = async () => {
  45. loading = true;
  46. const group = {
  47. name,
  48. description,
  49. permissions,
  50. user_ids: userIds
  51. };
  52. await onSubmit(group);
  53. loading = false;
  54. show = false;
  55. };
  56. const init = () => {
  57. if (group) {
  58. name = group.name;
  59. description = group.description;
  60. permissions = group?.permissions ?? {};
  61. userIds = group?.user_ids ?? [];
  62. }
  63. };
  64. $: if (show) {
  65. init();
  66. }
  67. onMount(() => {
  68. console.log(tabs);
  69. selectedTab = tabs[0];
  70. init();
  71. });
  72. </script>
  73. <Modal size="md" bind:show>
  74. <div>
  75. <div class=" flex justify-between dark:text-gray-100 px-5 pt-4 mb-1.5">
  76. <div class=" text-lg font-medium self-center font-primary">
  77. {#if custom}
  78. {#if edit}
  79. {$i18n.t('Edit User Group')}
  80. {:else}
  81. {$i18n.t('Add User Group')}
  82. {/if}
  83. {:else}
  84. {$i18n.t('Edit Default Permissions')}
  85. {/if}
  86. </div>
  87. <button
  88. class="self-center"
  89. on:click={() => {
  90. show = false;
  91. }}
  92. >
  93. <svg
  94. xmlns="http://www.w3.org/2000/svg"
  95. viewBox="0 0 20 20"
  96. fill="currentColor"
  97. class="w-5 h-5"
  98. >
  99. <path
  100. 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"
  101. />
  102. </svg>
  103. </button>
  104. </div>
  105. <div class="flex flex-col md:flex-row w-full px-4 pb-4 md:space-x-4 dark:text-gray-200">
  106. <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
  107. <form
  108. class="flex flex-col w-full"
  109. on:submit={(e) => {
  110. e.preventDefault();
  111. submitHandler();
  112. }}
  113. >
  114. <div class="flex flex-col lg:flex-row w-full h-full pb-2 lg:space-x-4">
  115. <div
  116. id="admin-settings-tabs-container"
  117. 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"
  118. >
  119. {#if tabs.includes('general')}
  120. <button
  121. class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
  122. 'general'
  123. ? ''
  124. : ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  125. on:click={() => {
  126. selectedTab = 'general';
  127. }}
  128. type="button"
  129. >
  130. <div class=" self-center mr-2">
  131. <svg
  132. xmlns="http://www.w3.org/2000/svg"
  133. viewBox="0 0 16 16"
  134. fill="currentColor"
  135. class="w-4 h-4"
  136. >
  137. <path
  138. fill-rule="evenodd"
  139. 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"
  140. clip-rule="evenodd"
  141. />
  142. </svg>
  143. </div>
  144. <div class=" self-center">{$i18n.t('General')}</div>
  145. </button>
  146. {/if}
  147. {#if tabs.includes('permissions')}
  148. <button
  149. class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
  150. 'permissions'
  151. ? ''
  152. : ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  153. on:click={() => {
  154. selectedTab = 'permissions';
  155. }}
  156. type="button"
  157. >
  158. <div class=" self-center mr-2">
  159. <WrenchSolid />
  160. </div>
  161. <div class=" self-center">{$i18n.t('Permissions')}</div>
  162. </button>
  163. {/if}
  164. {#if tabs.includes('users')}
  165. <button
  166. class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
  167. 'users'
  168. ? ''
  169. : ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  170. on:click={() => {
  171. selectedTab = 'users';
  172. }}
  173. type="button"
  174. >
  175. <div class=" self-center mr-2">
  176. <UserPlusSolid />
  177. </div>
  178. <div class=" self-center">{$i18n.t('Users')} ({userIds.length})</div>
  179. </button>
  180. {/if}
  181. </div>
  182. <div
  183. class="flex-1 mt-1 lg:mt-1 lg:h-[22rem] lg:max-h-[22rem] overflow-y-auto scrollbar-hidden"
  184. >
  185. {#if selectedTab == 'general'}
  186. <Display bind:name bind:description />
  187. {:else if selectedTab == 'permissions'}
  188. <Permissions bind:permissions />
  189. {:else if selectedTab == 'users'}
  190. <Users bind:userIds {users} />
  191. {/if}
  192. </div>
  193. </div>
  194. <!-- <div
  195. class=" tabs flex flex-row overflow-x-auto gap-2.5 text-sm font-medium border-b border-b-gray-800 scrollbar-hidden"
  196. >
  197. {#if tabs.includes('display')}
  198. <button
  199. class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
  200. 'display'
  201. ? ' dark:border-white'
  202. : 'border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  203. on:click={() => {
  204. selectedTab = 'display';
  205. }}
  206. type="button"
  207. >
  208. {$i18n.t('Display')}
  209. </button>
  210. {/if}
  211. {#if tabs.includes('permissions')}
  212. <button
  213. class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
  214. 'permissions'
  215. ? ' dark:border-white'
  216. : 'border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  217. on:click={() => {
  218. selectedTab = 'permissions';
  219. }}
  220. type="button"
  221. >
  222. {$i18n.t('Permissions')}
  223. </button>
  224. {/if}
  225. {#if tabs.includes('users')}
  226. <button
  227. class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
  228. 'users'
  229. ? ' dark:border-white'
  230. : ' border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
  231. on:click={() => {
  232. selectedTab = 'users';
  233. }}
  234. type="button"
  235. >
  236. {$i18n.t('Users')} ({userIds.length})
  237. </button>
  238. {/if}
  239. </div> -->
  240. <div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
  241. {#if edit}
  242. <button
  243. 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"
  244. type="button"
  245. on:click={() => {
  246. onDelete();
  247. show = false;
  248. }}
  249. >
  250. {$i18n.t('Delete')}
  251. </button>
  252. {/if}
  253. <button
  254. 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
  255. ? ' cursor-not-allowed'
  256. : ''}"
  257. type="submit"
  258. disabled={loading}
  259. >
  260. {$i18n.t('Save')}
  261. {#if loading}
  262. <div class="ml-2 self-center">
  263. <svg
  264. class=" w-4 h-4"
  265. viewBox="0 0 24 24"
  266. fill="currentColor"
  267. xmlns="http://www.w3.org/2000/svg"
  268. ><style>
  269. .spinner_ajPY {
  270. transform-origin: center;
  271. animation: spinner_AtaB 0.75s infinite linear;
  272. }
  273. @keyframes spinner_AtaB {
  274. 100% {
  275. transform: rotate(360deg);
  276. }
  277. }
  278. </style><path
  279. 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"
  280. opacity=".25"
  281. /><path
  282. 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"
  283. class="spinner_ajPY"
  284. /></svg
  285. >
  286. </div>
  287. {/if}
  288. </button>
  289. </div>
  290. </form>
  291. </div>
  292. </div>
  293. </div>
  294. </Modal>