Add user frontend support
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user