Add user frontend support
This commit is contained in:
@@ -1,4 +1,9 @@
|
|||||||
import { env } from '$env/dynamic/public';
|
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';
|
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
|
// Get subscription notifications with pagination
|
||||||
export async function fetchSubscriptionNotifications(
|
export async function fetchSubscriptionNotifications(
|
||||||
subscriptionId: string,
|
subscriptionId: string,
|
||||||
|
|||||||
@@ -35,6 +35,8 @@
|
|||||||
'token',
|
'token',
|
||||||
JSON.stringify({ value: data.access_token, expiresAt: expirationTime })
|
JSON.stringify({ value: data.access_token, expiresAt: expirationTime })
|
||||||
);
|
);
|
||||||
|
localStorage.setItem('username', loginUsername);
|
||||||
|
|
||||||
goto('/').then(() => location.reload());
|
goto('/').then(() => location.reload());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
loginError = 'Network error - ' + err;
|
loginError = 'Network error - ' + err;
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { fetchUserSettings, updateSetting } from '$lib/api';
|
import { fetchUserSettings, updateSetting, fetchUsers, updateUser, deleteUser } from '$lib/api';
|
||||||
import type { Settings } from '$lib/api';
|
import type { Settings, User } from '$lib/api';
|
||||||
import CodeMirror from 'svelte-codemirror-editor';
|
import CodeMirror from 'svelte-codemirror-editor';
|
||||||
|
|
||||||
let settings: Settings | null = $state(null);
|
let settings: Settings | null = $state(null);
|
||||||
|
let users: User[] = $state([]);
|
||||||
|
let currentUser: string | null = $state(localStorage.getItem('username'));
|
||||||
let isLoading = $state(false);
|
let isLoading = $state(false);
|
||||||
let error: string | null = $state(null);
|
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) {
|
async function saveSetting(setting: Settings) {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
error = null;
|
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(() => {
|
onMount(() => {
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
loadUsers();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -80,6 +121,65 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</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}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user