Skip to content

Commit

Permalink
more work on user table/modal/form
Browse files Browse the repository at this point in the history
  • Loading branch information
jbraswell committed Jul 6, 2024
1 parent 531d32d commit 682d0e5
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 111 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script lang="ts">
import { validator } from '@felte/validator-yup';
import { createForm } from 'felte';
import { Modal, Button, Helper, Input, Label, Select } from 'flowbite-svelte';
import type { SizeType } from 'flowbite-svelte';
import { Button, Helper, Input, Label, Select } from 'flowbite-svelte';
import * as yup from 'yup';
import { spinner } from '../stores/spinner';
Expand All @@ -11,22 +10,21 @@
import { translations } from '../stores/localization';
import { authenticatedUser } from '../stores/apiCredentials';
export let showModal: boolean;
export let selectedUserId: number;
export let usersById: Record<number, User> = {};
let size: SizeType = 'sm';
export let selectedUser: User;
export let users: User[];
let usersById = Object.fromEntries(users.map((user) => [user.id, user]));
let userItems = Object.values(usersById)
.map((user) => ({ value: user.id, name: user.displayName }))
.sort((a, b) => a.name.localeCompare(b.name));
const userTypeItems = [
{ value: 'deactivated', name: 'Deactivated' },
{ value: 'observer', name: 'Observer' },
{ value: 'serviceBodyAdmin', name: 'Service Body Administrator' }
];
const { form, data, errors, setInitialValues, reset } = createForm({
const { form, errors, setInitialValues, reset } = createForm({
initialValues: {
type: '',
ownerId: -1,
Expand Down Expand Up @@ -91,13 +89,17 @@
});
function populateForm() {
const user = usersById[selectedUserId];
const user = usersById[selectedUser.id];
// The only reason we use setInitialValues and reesethere instead of setData is to make development
// easier. It is super annoying that each time we save the file, hot module replacement causes the
// values in the form fields to be replaced when the UsersForm is refreshed.
let ownerId = user?.ownerId ?? -1;
if (ownerId === -1 && $authenticatedUser?.type === 'admin') {
ownerId = $authenticatedUser.id;
}
setInitialValues({
type: user?.type ?? '',
ownerId: user?.ownerId ? user.ownerId : -1,
ownerId: ownerId,
email: user?.email ?? '',
displayName: user?.displayName ?? '',
username: user?.username ?? '',
Expand All @@ -107,81 +109,76 @@
reset();
}
$: if (selectedUserId) {
$: if (selectedUser) {
populateForm();
}
</script>

<Modal bind:open={showModal} {size} autoclose>
<div class="p-2">
<p>User ID: {selectedUserId}</p>
<form use:form>
<div class="mb-6 grid gap-6 md:grid-cols-2">
<div>
<Label for="type" class="mb-2">{$translations.userTypeTitle}</Label>
<Select id="type" items={userTypeItems} name="type" disabled={selectedUserId === $authenticatedUser?.id} />
<Helper class="mt-2" color="red">
{#if $errors.type}
{$errors.type}
{/if}
</Helper>
</div>
<div>
<Label for="ownerId" class="mb-2">{$translations.ownerIdTitle}</Label>
<Select id="ownerId" items={userItems} name="ownerId" disabled={selectedUserId === $authenticatedUser?.id || $data.type === 'admin'} />
<Helper class="mt-2" color="red">
{#if $errors.ownerId}
{$errors.ownerId}
{/if}
</Helper>
</div>
</div>
<div class="mb-6">
<Label for="email" class="mb-2">{$translations.emailTitle}</Label>
<Input type="email" id="email" name="email" />
<Helper class="mt-2" color="red">
{#if $errors.email}
{$errors.email}
{/if}
</Helper>
</div>
<div class="mb-6">
<Label for="displayName" class="mb-2">{$translations.nameTitle}</Label>
<Input type="text" id="displayName" name="displayName" required />
<Helper class="mt-2" color="red">
{#if $errors.displayName}
{$errors.displayName}
{/if}
</Helper>
</div>
<div class="mb-6">
<Label for="description" class="mb-2">{$translations.descriptionTitle}</Label>
<Input type="text" id="description" name="description" />
<Helper class="mt-2" color="red">
{#if $errors.description}
{$errors.description}
{/if}
</Helper>
</div>
<div class="mb-6">
<Label for="username" class="mb-2">{$translations.usernameTitle}</Label>
<Input type="text" id="username" name="username" required />
<Helper class="mt-2" color="red">
{#if $errors.username}
{$errors.username}
{/if}
</Helper>
</div>
<div class="mb-6">
<Label for="password" class="mb-2">{$translations.passwordTitle}</Label>
<Input type="password" id="password" name="password" required />
<Helper class="mt-2" color="red">
{#if $errors.password}
{$errors.password}
{/if}
</Helper>
</div>
<Button type="submit">{$translations.applyChangesTitle}</Button>
</form>
<form use:form>
<div class="mb-6 grid gap-6 md:grid-cols-2">
<div>
<Label for="type" class="mb-2">{$translations.userTypeTitle}</Label>
<Select id="type" items={userTypeItems} name="type" disabled={selectedUser.id === $authenticatedUser?.id} />
<Helper class="mt-2" color="red">
{#if $errors.type}
{$errors.type}
{/if}
</Helper>
</div>
<div>
<Label for="ownerId" class="mb-2">{$translations.ownerIdTitle}</Label>
<Select id="ownerId" items={userItems} name="ownerId" disabled={$authenticatedUser?.type !== 'admin'} />
<Helper class="mt-2" color="red">
{#if $errors.ownerId}
{$errors.ownerId}
{/if}
</Helper>
</div>
</div>
<div class="mb-6">
<Label for="email" class="mb-2">{$translations.emailTitle}</Label>
<Input type="email" id="email" name="email" />
<Helper class="mt-2" color="red">
{#if $errors.email}
{$errors.email}
{/if}
</Helper>
</div>
<div class="mb-6">
<Label for="displayName" class="mb-2">{$translations.nameTitle}</Label>
<Input type="text" id="displayName" name="displayName" required />
<Helper class="mt-2" color="red">
{#if $errors.displayName}
{$errors.displayName}
{/if}
</Helper>
</div>
<div class="mb-6">
<Label for="description" class="mb-2">{$translations.descriptionTitle}</Label>
<Input type="text" id="description" name="description" />
<Helper class="mt-2" color="red">
{#if $errors.description}
{$errors.description}
{/if}
</Helper>
</div>
<div class="mb-6">
<Label for="username" class="mb-2">{$translations.usernameTitle}</Label>
<Input type="text" id="username" name="username" required />
<Helper class="mt-2" color="red">
{#if $errors.username}
{$errors.username}
{/if}
</Helper>
</div>
<div class="mb-6">
<Label for="password" class="mb-2">{$translations.passwordTitle}</Label>
<Input type="password" id="password" name="password" required />
<Helper class="mt-2" color="red">
{#if $errors.password}
{$errors.password}
{/if}
</Helper>
</div>
</Modal>
<Button type="submit" class="w-full">{$translations.applyChangesTitle}</Button>
</form>
16 changes: 16 additions & 0 deletions src/resources/js/components/UserModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
import { Modal } from 'flowbite-svelte';
import type { User } from 'bmlt-root-server-client';
import UserForm from './UserForm.svelte';
export let showModal: boolean;
export let selectedUser: User;
export let users: User[];
</script>

<Modal bind:open={showModal} size="sm" autoclose outsideclose>
<div class="p-2">
<UserForm {users} {selectedUser} />
</div>
</Modal>
47 changes: 19 additions & 28 deletions src/resources/js/routes/Users.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell, TableSearch } from 'flowbite-svelte';
import Nav from '../components/NavBar.svelte';
import UsersModal from '../components/UsersModal.svelte';
import UserModal from '../components/UserModal.svelte';
import { authenticatedUser } from '../stores/apiCredentials';
import { spinner } from '../stores/spinner';
Expand All @@ -10,71 +10,62 @@
import { onMount } from 'svelte';
import type { User } from 'bmlt-root-server-client';
let users: User[] = [];
let showModal = false;
let searchTerm = '';
let usersById: Record<number, User> = {};
let userItems = [{ value: -1, name: '', username: '' }];
let selectedUserId = -1;
let selectedUser: User;
async function getUsers(): Promise<void> {
try {
spinner.show();
const users = await RootServerApi.getUsers();
const _usersById: Record<number, User> = {};
for (const user of users) {
_usersById[user.id] = user;
if ($authenticatedUser?.type === 'admin') {
if (user.ownerId === null) {
user.ownerId = $authenticatedUser.id;
}
}
}
usersById = _usersById;
userItems = users.map((user) => ({ value: user.id, name: user.displayName, username: user.username })).sort((a, b) => a.name.localeCompare(b.name));
users = (await RootServerApi.getUsers()).filter((user) => user.id !== $authenticatedUser?.id).sort((a, b) => a.displayName.localeCompare(b.displayName));
spinner.hide();
} catch (error: any) {
RootServerApi.handleErrors(error);
}
}
function editUser(userId: number) {
selectedUserId = userId;
function editUser(user: User) {
selectedUser = user;
showModal = true;
}
function deleteUser(user: User) {
// TODO
selectedUser = user;
}
function closeModal() {
showModal = false;
}
onMount(getUsers);
$: filteredUsers = userItems.filter((user) => user.name.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1);
$: filteredUsers = users.filter((user) => user.displayName.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1);
</script>

<Nav />

<div class="mx-auto max-w-xl p-2">
<div class="mx-auto max-w-4xl p-2">
<h2 class="mb-4 text-center text-xl font-semibold dark:text-white">{$translations.usersTitle}</h2>

<TableSearch placeholder="Search by name" hoverable={true} bind:inputValue={searchTerm}>
<TableHead>
<TableHeadCell>Name</TableHeadCell>
<TableHeadCell>Username</TableHeadCell>
<TableHeadCell></TableHeadCell>
</TableHead>
<TableBody>
{#each filteredUsers as user}
<TableBodyRow>
<TableBodyCell>{user.name}</TableBodyCell>
<TableBodyCell>{user.username}</TableBodyCell>
<TableBodyCell>
<button on:click={() => editUser(user.value)} class="text-blue-500 hover:underline">Edit</button>
<TableBodyCell class="whitespace-normal">{user.displayName}</TableBodyCell>
<TableBodyCell class="text-right">
<button on:click={() => editUser(user)} class="mr-4 text-blue-700 dark:text-blue-500">Edit</button>
<button on:click={() => deleteUser(user)} class="text-blue-700 dark:text-blue-500">Delete</button>
</TableBodyCell>
</TableBodyRow>
{/each}
</TableBody>
</TableSearch>
</div>

<UsersModal bind:showModal {selectedUserId} {usersById} on:close={closeModal} />
<UserModal bind:showModal {users} {selectedUser} on:close={closeModal} />

0 comments on commit 682d0e5

Please sign in to comment.