+page.svelte 6.33 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
2
3
4
5
6
7
8
<script>
	import { WEBUI_API_BASE_URL } from '$lib/constants';
	import { config, user } from '$lib/stores';
	import { goto } from '$app/navigation';
	import { onMount } from 'svelte';

	import toast from 'svelte-french-toast';

9
	import { updateUserRole, getUsers, deleteUserById } from '$lib/apis/users';
10
	import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
Timothy J. Baek's avatar
Timothy J. Baek committed
11

Timothy J. Baek's avatar
Timothy J. Baek committed
12
13
14
	let loaded = false;
	let users = [];

15
16
	let signUpEnabled = true;

Timothy J. Baek's avatar
Timothy J. Baek committed
17
18
19
20
21
	const updateRoleHandler = async (id, role) => {
		const res = await updateUserRole(localStorage.token, id, role).catch((error) => {
			toast.error(error);
			return null;
		});
Timothy J. Baek's avatar
Timothy J. Baek committed
22
23

		if (res) {
Timothy J. Baek's avatar
Timothy J. Baek committed
24
			users = await getUsers(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
25
26
27
		}
	};

28
29
30
31
32
33
34
35
36
37
	const deleteUserHandler = async (id) => {
		const res = await deleteUserById(localStorage.token, id).catch((error) => {
			toast.error(error);
			return null;
		});
		if (res) {
			users = await getUsers(localStorage.token);
		}
	};

38
39
40
41
	const toggleSignUpEnabled = async () => {
		signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
42
	onMount(async () => {
43
		if ($user?.role !== 'admin') {
Timothy J. Baek's avatar
Timothy J. Baek committed
44
45
			await goto('/');
		} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
46
			users = await getUsers(localStorage.token);
47
48

			signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
49
50
51
52
53
54
55
56
57
58
59
60
		}
		loaded = true;
	});
</script>

<div
	class=" bg-white dark:bg-gray-800 dark:text-gray-100 min-h-screen w-full flex justify-center font-mona"
>
	{#if loaded}
		<div class="w-full max-w-3xl px-10 md:px-16 min-h-screen flex flex-col">
			<div class="py-10 w-full">
				<div class=" flex flex-col justify-center">
61
62
63
64
					<div class=" flex justify-between items-center">
						<div class=" text-2xl font-semibold">Users ({users.length})</div>
						<div>
							<button
Timothy J. Baek's avatar
Timothy J. Baek committed
65
								class="flex items-center space-x-1 border border-gray-200 dark:border-gray-600 px-3 py-1 rounded-lg"
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
								type="button"
								on:click={() => {
									toggleSignUpEnabled();
								}}
							>
								{#if signUpEnabled}
									<svg
										xmlns="http://www.w3.org/2000/svg"
										viewBox="0 0 16 16"
										fill="currentColor"
										class="w-4 h-4"
									>
										<path
											d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
										/>
									</svg>

									<div class=" text-xs">
										New Sign Up <span class=" font-semibold">Enabled</span>
									</div>
								{:else}
									<svg
										xmlns="http://www.w3.org/2000/svg"
										viewBox="0 0 16 16"
										fill="currentColor"
										class="w-4 h-4"
									>
										<path
											fill-rule="evenodd"
											d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7A1.5 1.5 0 0 0 3 8.5v5A1.5 1.5 0 0 0 4.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 7V4.5A3.5 3.5 0 0 0 8 1Zm2 6V4.5a2 2 0 1 0-4 0V7h4Z"
											clip-rule="evenodd"
										/>
									</svg>

									<div class=" text-xs">
										New Sign Up <span class=" font-semibold">Disabled</span>
									</div>
								{/if}
							</button>
						</div>
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
					<div class=" text-gray-500 text-xs font-medium mt-1">
						Click on the user role cell in the table to change a user's role.
					</div>

					<hr class=" my-3 dark:border-gray-600" />

					<div class="scrollbar-hidden relative overflow-x-auto whitespace-nowrap">
						<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
							<thead
								class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"
							>
								<tr>
									<th scope="col" class="px-6 py-3"> Name </th>
									<th scope="col" class="px-6 py-3"> Email </th>
									<th scope="col" class="px-6 py-3"> Role </th>
122
									<th scope="col" class="px-6 py-3"> Action </th>
Timothy J. Baek's avatar
Timothy J. Baek committed
123
124
125
126
127
128
129
								</tr>
							</thead>
							<tbody>
								{#each users as user}
									<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
										<th
											scope="row"
130
											class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white w-fit"
Timothy J. Baek's avatar
Timothy J. Baek committed
131
132
133
134
135
										>
											<div class="flex flex-row">
												<img
													class=" rounded-full max-w-[30px] max-h-[30px] object-cover mr-4"
													src={user.profile_image_url}
136
													alt="user"
Timothy J. Baek's avatar
Timothy J. Baek committed
137
138
												/>

139
												<div class=" font-semibold self-center">{user.name}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
140
141
142
143
144
											</div>
										</th>
										<td class="px-6 py-4"> {user.email} </td>
										<td class="px-6 py-4">
											<button
145
												class="  dark:text-white underline"
Timothy J. Baek's avatar
Timothy J. Baek committed
146
147
												on:click={() => {
													if (user.role === 'user') {
Timothy J. Baek's avatar
Timothy J. Baek committed
148
														updateRoleHandler(user.id, 'admin');
Timothy J. Baek's avatar
Timothy J. Baek committed
149
													} else if (user.role === 'pending') {
Timothy J. Baek's avatar
Timothy J. Baek committed
150
														updateRoleHandler(user.id, 'user');
Timothy J. Baek's avatar
Timothy J. Baek committed
151
													} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
152
														updateRoleHandler(user.id, 'pending');
Timothy J. Baek's avatar
Timothy J. Baek committed
153
154
155
156
													}
												}}>{user.role}</button
											>
										</td>
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
										<td class="px-6 py-4 text-center flex justify-center">
											<button
												class="self-center w-fit text-sm p-1.5 border dark:border-gray-600 rounded-xl flex"
												on:click={async () => {
													deleteUserHandler(user.id);
												}}
											>
												<svg
													xmlns="http://www.w3.org/2000/svg"
													fill="none"
													viewBox="0 0 24 24"
													stroke-width="1.5"
													stroke="currentColor"
													class="w-4 h-4"
												>
													<path
														stroke-linecap="round"
														stroke-linejoin="round"
														d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
													/>
												</svg>
											</button>
										</td>
Timothy J. Baek's avatar
Timothy J. Baek committed
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
									</tr>
								{/each}
							</tbody>
						</table>
					</div>
				</div>
			</div>
		</div>
	{/if}
</div>

<style>
	.font-mona {
		font-family: 'Mona Sans';
	}

	.scrollbar-hidden::-webkit-scrollbar {
		display: none; /* for Chrome, Safari and Opera */
	}

	.scrollbar-hidden {
		-ms-overflow-style: none; /* IE and Edge */
		scrollbar-width: none; /* Firefox */
	}
</style>