Skip to content

Commit

Permalink
feat: edit user information
Browse files Browse the repository at this point in the history
  • Loading branch information
johanohly committed Sep 9, 2024
1 parent e75a7bb commit 5f29f22
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script lang="ts">
import SuperDebug, {
defaults,
type Infer,
superForm,
} from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';
import { editUserSchema } from '$lib/zod/user';
import * as Form from '$lib/components/ui/form';
import * as Select from '$lib/components/ui/select';
import { Input } from '$lib/components/ui/input';
import { toTitleCase } from '$lib/utils';
import { toast } from 'svelte-sonner';
import type { User } from '$lib/db';
let { user }: { user: User } = $props();
const form = superForm(
defaults<Infer<typeof editUserSchema>>(user, zod(editUserSchema)),
{
validators: zod(editUserSchema),
onUpdated({ form }) {
if (form.message) {
if (form.message.type === 'success') {
toast.success(form.message.text);
return void location.reload();
}
toast.error(form.message.text);
}
},
},
);
const { form: formData, enhance } = form;
</script>

<form method="POST" action="/edit-user" use:enhance>
<SuperDebug data={$formData} stringTruncate={20} />
<Form.Field {form} name="username">
<Form.Control let:attrs>
<Form.Label>Username</Form.Label>
<Input bind:value={$formData.username} {...attrs} />
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field {form} name="displayName">
<Form.Control let:attrs>
<Form.Label>Display Name</Form.Label>
<Input bind:value={$formData.displayName} {...attrs} />
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field {form} name="unit">
<Form.Control let:attrs>
<Form.Label>Unit of measurement</Form.Label>
<Select.Root
selected={{
label: toTitleCase($formData.unit),
value: $formData.unit,
}}
onSelectedChange={(v) => {
v && ($formData.unit = v.value);
}}
>
<Select.Trigger {...attrs}>
<Select.Value placeholder="Select a unit" />
</Select.Trigger>
<Select.Content>
<Select.Item value="metric" label="Metric" />
<Select.Item value="imperial" label="Imperial" />
</Select.Content>
</Select.Root>
<input type="hidden" value={$formData.unit} name={attrs.name} />
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Button class="mt-1">Save</Form.Button>
</form>
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script lang="ts">
import { PageHeader } from '.';
import { PageHeader } from '../index';
import { toTitleCase } from '$lib/utils';
import type { User } from 'lucia';
import { Button } from '$lib/components/ui/form';
import EditUserForm from './EditUserForm.svelte';
let { user }: { user: User } = $props();
</script>
Expand All @@ -21,4 +22,5 @@
<Button variant="destructive">Log out</Button>
</form>
</div>
<EditUserForm {user} />
</PageHeader>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as GeneralPage } from './GeneralPage.svelte';
2 changes: 1 addition & 1 deletion src/lib/components/modals/settings/pages/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { default as AppearancePage } from './AppearancePage.svelte';
export { default as PageHeader } from './PageHeader.svelte';
export { default as UsersPage } from './users-page/UsersPage.svelte';
export { default as GeneralPage } from './GeneralPage.svelte';
export { default as GeneralPage } from './general-page/GeneralPage.svelte';
export { default as ImportPage } from './ImportPage.svelte';
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
);
const { form: formData, enhance, submitting } = form;
$inspect(formData);
</script>

<Modal bind:open>
Expand Down
14 changes: 2 additions & 12 deletions src/lib/zod/auth.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
import { z } from 'zod';
import { userSchema } from '$lib/zod/user';

export const signUpSchema = z.object({
username: z
.string()
.min(3, { message: 'Username must be at least 3 characters long' })
.max(20, { message: 'Username must be at most 20 characters long' })
.regex(/^[a-zA-Z0-9_]+$/, {
message: 'Username can only contain letters, numbers, and underscores',
}),
password: z.string().min(8),
displayName: z.string().min(3),
unit: z.enum(['imperial', 'metric']).default('metric'),
});
export const signUpSchema = userSchema.omit({ role: true });

export const signInSchema = z.object({
username: z.string(),
Expand Down
17 changes: 14 additions & 3 deletions src/lib/zod/user.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { z } from 'zod';
import { signUpSchema as addUserPrimitive } from '$lib/zod/auth';

const addUserExtras = z.object({
export const userSchema = z.object({
username: z
.string()
.min(3, { message: 'Username must be at least 3 characters long' })
.max(20, { message: 'Username must be at most 20 characters long' })
.regex(/^[a-zA-Z0-9_]+$/, {
message: 'Username can only contain letters, numbers, and underscores',
}),
password: z.string().min(8),
displayName: z.string().min(3),
unit: z.enum(['imperial', 'metric']).default('metric'),
role: z.enum(['user', 'admin']).default('user'),
});

export const addUserSchema = addUserPrimitive.merge(addUserExtras);
export const editUserSchema = userSchema.omit({ password: true, role: true });

export const addUserSchema = userSchema;
57 changes: 57 additions & 0 deletions src/routes/(actions)/edit-user/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { zod } from 'sveltekit-superforms/adapters';
import type { RequestHandler } from './$types';
import { actionResult, setError, superValidate } from 'sveltekit-superforms';
import { editUserSchema } from '$lib/zod/user';
import { usernameExists } from '$lib/server/utils/auth';
import { db } from '$lib/db';

export const POST: RequestHandler = async ({ locals, request }) => {
const form = await superValidate(request, zod(editUserSchema));
if (!form.valid) return actionResult('failure', { form });

const user = locals.user;
if (!user) {
return actionResult('error', 'You must be logged in to create users.', 401);
}

const { username, displayName, unit } = form.data;

const updatedFields: Record<string, any> = {};

if (username !== user.username) {
updatedFields.username = username;
}
if (displayName !== user.displayName) {
updatedFields.displayName = displayName;
}
if (unit !== user.unit) {
updatedFields.unit = unit;
}

if (Object.keys(updatedFields).length === 0) {
form.message = { type: 'error', text: 'No changes made' };
return actionResult('success', { form });
}

if (updatedFields.username) {
const exists = await usernameExists(updatedFields.username);
if (exists) {
setError(form, 'username', 'Username already exists');
return actionResult('failure', { form });
}
}

const resp = await db
.updateTable('user')
.set(updatedFields)
.where('id', '=', user.id)
.executeTakeFirst();
if (!resp.numUpdatedRows) {
form.message = { type: 'error', text: 'Failed to edit your information' };
return actionResult('failure', { form });
}

form.message = { type: 'success', text: 'Information updated' };

return actionResult('success', { form });
};

0 comments on commit 5f29f22

Please sign in to comment.