Add user frontend support

This commit is contained in:
Sami Abuzakuk
2025-11-01 22:21:06 +01:00
parent 2f9f75ee3c
commit e913eb34f2
3 changed files with 147 additions and 2 deletions

View File

@@ -1,4 +1,9 @@
import { env } from '$env/dynamic/public';
export interface User {
id: number;
username: string;
password: string | null;
}
export const API_URL = env.PUBLIC_API_URL || 'http://localhost:8000';
@@ -288,6 +293,44 @@ export async function deleteSubscription(subscriptionId: number): Promise<void>
}
}
// Fetch all users
export async function fetchUsers(): Promise<User[]> {
const response = await fetch(`${API_URL}/users`, {
headers: authHeaders()
});
if (!response.ok) {
throw new Error('Failed to fetch users');
}
return response.json();
}
// Update a user
export async function updateUser(
userId: number,
username: string,
password: string
): Promise<void> {
const response = await fetch(`${API_URL}/users/${userId}`, {
method: 'PUT',
headers: authHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify({ username, password })
});
if (!response.ok) {
throw new Error('Failed to update user');
}
}
// Delete a user
export async function deleteUser(userId: number): Promise<void> {
const response = await fetch(`${API_URL}/users/${userId}`, {
method: 'DELETE',
headers: authHeaders()
});
if (!response.ok) {
throw new Error('Failed to delete user');
}
}
// Get subscription notifications with pagination
export async function fetchSubscriptionNotifications(
subscriptionId: string,

View File

@@ -35,6 +35,8 @@
'token',
JSON.stringify({ value: data.access_token, expiresAt: expirationTime })
);
localStorage.setItem('username', loginUsername);
goto('/').then(() => location.reload());
} catch (err) {
loginError = 'Network error - ' + err;

View File

@@ -1,10 +1,12 @@
<script lang="ts">
import { onMount } from 'svelte';
import { fetchUserSettings, updateSetting } from '$lib/api';
import type { Settings } from '$lib/api';
import { fetchUserSettings, updateSetting, fetchUsers, updateUser, deleteUser } from '$lib/api';
import type { Settings, User } from '$lib/api';
import CodeMirror from 'svelte-codemirror-editor';
let settings: Settings | null = $state(null);
let users: User[] = $state([]);
let currentUser: string | null = $state(localStorage.getItem('username'));
let isLoading = $state(false);
let error: string | null = $state(null);
@@ -21,6 +23,18 @@
}
}
async function loadUsers() {
isLoading = true;
error = null;
try {
users = await fetchUsers();
} catch (err) {
error = 'Failed to load users - ' + err;
} finally {
isLoading = false;
}
}
async function saveSetting(setting: Settings) {
isLoading = true;
error = null;
@@ -34,8 +48,35 @@
}
}
async function handleUpdateUser(user: User, username: string, password: string) {
isLoading = true;
error = null;
try {
await updateUser(user.id, username, password);
loadUsers(); // Refresh users after update
} catch (err) {
error = 'Failed to update user - ' + err;
} finally {
isLoading = false;
}
}
async function handleDeleteUser(userId: number) {
isLoading = true;
error = null;
try {
await deleteUser(userId);
loadUsers(); // Refresh users after deletion
} catch (err) {
error = 'Failed to delete user - ' + err;
} finally {
isLoading = false;
}
}
onMount(() => {
loadSettings();
loadUsers();
});
</script>
@@ -80,6 +121,65 @@
</button>
</div>
</div>
<h2 class="text-xl font-bold mt-8">User Management</h2>
{#if isLoading}
<p>Loading users...</p>
{:else if error}
<p class="text-red-500">{$error}</p>
{:else}
<table
class="table-auto w-full mt-4 border-collapse border border-gray-300 shadow-lg rounded-lg"
>
<thead>
<tr class="bg-gray-100">
<th class="px-4 py-2 border border-gray-300 text-left font-semibold">Users</th>
<th class="px-4 py-2 border border-gray-300 text-center font-semibold">Actions</th>
</tr>
</thead>
<tbody>
{#each users as user (user.id)}
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2 border-gray-300">
{#if user.username === currentUser}
<input
type="text"
placeholder="New Username"
class="px-2 py-1 border rounded"
bind:value={user.username}
/>
<input
type="password"
placeholder="New Password"
class="px-2 py-1 border rounded"
bind:value={user.password}
/>
{:else}
{user.username}
{/if}
</td>
<td class="border px-4 py-2 border-gray-300 text-center">
{#if user.username === currentUser}
<button
class="ml-2 px-2 py-1 bg-yellow-500 text-white rounded hover:bg-yellow-600"
onclick={() => handleUpdateUser(user, user.username, user.password)}
>
Update
</button>
{:else}
<button
class="ml-2 px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600"
onclick={() => handleDeleteUser(user.id)}
>
Delete
</button>
{/if}
</td>
</tr>
{/each}
</tbody>
</table>
{/if}
{/if}
</div>