Skip to content

Commit c246f2b

Browse files
(feat) auth redesign (#74)
* Added basic auth layout Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * Updated design of Login page Added placeholder pages Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * Redesigned Register page Added terms of service placeholder page Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * Fixed error inside autogenerated checkbox component Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * Fixed bug regarding checkbox in signup page Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * Improved design Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * Finally managed to get basic checked css to work Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * Improved design of checks inside auth layout Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * Cleaned up public routes Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> * minor changes from review Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com> --------- Signed-off-by: Benjamin Strasser <bp.strasser@gmail.com>
1 parent 89be1a5 commit c246f2b

File tree

14 files changed

+259
-71
lines changed

14 files changed

+259
-71
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script lang="ts">
2+
import { Checkbox as CheckboxPrimitive } from 'bits-ui'
3+
import Check from 'lucide-svelte/icons/check'
4+
import Minus from 'lucide-svelte/icons/minus'
5+
import { cn } from '$utils'
6+
7+
type $$Props = CheckboxPrimitive.Props
8+
type $$Events = CheckboxPrimitive.Events
9+
10+
let className: $$Props['class'] = undefined
11+
export let checked: NonNullable<$$Props['checked']> = false
12+
export { className as class }
13+
</script>
14+
15+
<CheckboxPrimitive.Root
16+
class={cn(
17+
'peer box-content h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[disabled=true]:opacity-50',
18+
className
19+
)}
20+
bind:checked
21+
{...$$restProps}
22+
on:click
23+
>
24+
<CheckboxPrimitive.Indicator
25+
class={cn('flex h-4 w-4 items-center justify-center text-current')}
26+
let:isChecked
27+
let:isIndeterminate
28+
>
29+
{#if isChecked}
30+
<Check class="h-3.5 w-3.5" />
31+
{:else if isIndeterminate}
32+
<Minus class="h-3.5 w-3.5" />
33+
{/if}
34+
</CheckboxPrimitive.Indicator>
35+
</CheckboxPrimitive.Root>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Root from './checkbox.svelte'
2+
3+
export {
4+
Root,
5+
//
6+
Root as Checkbox
7+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as ThemeSelector } from './theme-selector.svelte'
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<script lang="ts">
2+
import Sun from 'lucide-svelte/icons/sun'
3+
import Moon from 'lucide-svelte/icons/moon'
4+
import Computer from 'lucide-svelte/icons/computer'
5+
import { ModeWatcher, mode, setMode } from 'mode-watcher'
6+
7+
let currentMode: 'dark' | 'light' | 'system' = $mode || 'system'
8+
9+
$: {
10+
setMode(currentMode)
11+
}
12+
</script>
13+
14+
<ModeWatcher />
15+
16+
<div class="inline-flex w-auto items-center rounded-full border border-primary">
17+
<input
18+
type="radio"
19+
id="system"
20+
name="mode"
21+
class="hidden"
22+
value={'system'}
23+
bind:group={currentMode}
24+
/>
25+
<label for="system" class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full">
26+
<Computer class="h-[1.2rem] w-[1.2rem]" />
27+
</label>
28+
29+
<input
30+
type="radio"
31+
id="light"
32+
name="mode"
33+
class="hidden"
34+
value={'light'}
35+
bind:group={currentMode}
36+
/>
37+
<label for="light" class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full">
38+
<Sun class="h-[1.2rem] w-[1.2rem]" />
39+
</label>
40+
41+
<input
42+
type="radio"
43+
id="dark"
44+
name="mode"
45+
class="hidden"
46+
value={'dark'}
47+
bind:group={currentMode}
48+
/>
49+
<label for="dark" class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full">
50+
<Moon class="h-[1.2rem] w-[1.2rem]" />
51+
</label>
52+
</div>
53+
54+
<style lang="css">
55+
input[type='radio']:checked + label {
56+
@apply border border-primary;
57+
}
58+
</style>

src/hooks.server.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ import { TOKEN_NAME, parseTokenToJwt } from 'services/auth/token'
44
import type { UserAuthCredentials } from 'services/user/user'
55
import { getUserAuthCredentials } from 'services/user/user-auth-service'
66

7-
const PUBLIC_ROUTES = ['/', '/login', '/signup']
7+
const PUBLIC_ROUTES = [
8+
'/',
9+
'/login',
10+
'/signup',
11+
'/forgot-password',
12+
'/code-of-conduct',
13+
'/privacy-policy',
14+
'/terms-of-service'
15+
]
816

917
export const handle: Handle = async ({ event, resolve }) => {
1018
const { locals, cookies, url } = event

src/routes/(auth)/+layout.svelte

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<script lang="ts">
2+
import Check from 'lucide-svelte/icons/check'
3+
import { ThemeSelector } from '$components/ui/theme-selector'
4+
</script>
5+
6+
<div class="flex h-screen">
7+
<div class="flex w-1/2 flex-col bg-secondary text-secondary-foreground">
8+
<!-- Top Section -->
9+
<div class="flex-none p-6">
10+
<div class="text-2xl">
11+
<span>⌘ Tiny-TMS</span>
12+
</div>
13+
</div>
14+
15+
<!-- Middle Section -->
16+
<div
17+
class="flex flex-grow flex-col items-start justify-center space-y-4 p-6 text-2xl font-semibold"
18+
>
19+
<div class="flex items-center">
20+
<span class="mr-2.5 rounded-full bg-primary p-1.5">
21+
<Check strokeWidth={1} class="stroke-primary-foreground" />
22+
</span>
23+
<span>Efficiently translate your content</span>
24+
</div>
25+
<div class="flex items-center">
26+
<span class="mr-2.5 rounded-full bg-primary p-1.5">
27+
<Check strokeWidth={1} class="stroke-primary-foreground" />
28+
</span>
29+
<span>Open source and self hosting</span>
30+
</div>
31+
<div class="flex items-center">
32+
<span class="mr-2.5 rounded-full bg-primary p-1.5">
33+
<Check strokeWidth={1} class="stroke-primary-foreground" />
34+
</span>
35+
<span>Seamless AI-Autocompletion integrations</span>
36+
</div>
37+
</div>
38+
39+
<!-- Bottom Section -->
40+
<div class="flex-none p-6">
41+
<div>
42+
<p>A Translation Management System to cover the needs of most people.</p>
43+
<p>Not for everyone, but for most.</p>
44+
</div>
45+
</div>
46+
<div class="p-6">
47+
<ThemeSelector />
48+
</div>
49+
</div>
50+
51+
<div class="my-auto w-1/2"><slot></slot></div>
52+
</div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Forgot Password
Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts">
22
import * as Form from '$components/ui/form'
33
import { Input } from '$components/ui/input'
4-
import * as Card from '$components/ui/card'
4+
import { Checkbox } from '$components/ui/checkbox'
5+
import { Label } from '$components/ui/label'
56
import { type LoginFormSchema, loginSchema } from './schema'
67
import { type Infer, type SuperValidated, superForm } from 'sveltekit-superforms'
78
import { zodClient } from 'sveltekit-superforms/adapters'
@@ -26,36 +27,52 @@
2627
})
2728
2829
const { form: formData, enhance } = form
29-
$formData.email = 'test@test.com'
30-
$formData.password = 'passwordAÖOGDÖADG'
3130
</script>
3231

33-
<form method="POST" use:enhance>
34-
<Card.Root class="m-auto w-[350px]">
35-
<Card.Header>
36-
<Card.Title>Log In</Card.Title>
37-
<Card.Description>Log In to tiny-tms and start doing you translations</Card.Description>
38-
</Card.Header>
39-
<Card.Content>
32+
<div class="flex items-center justify-center">
33+
<div class="w-full max-w-2xl">
34+
<div class="text-center">
35+
<h2 class="mt-6 text-center text-3xl font-extrabold">Sign in to your account</h2>
36+
<p class="mt-2 text-center text-sm">
37+
Or <a href="/signup" class="font-medium underline">sign up for a new account</a>
38+
</p>
39+
</div>
40+
<form method="POST" use:enhance>
4041
<Form.Field {form} name="email">
4142
<Form.Control let:attrs>
42-
<Form.Label>E-Mail</Form.Label>
43-
<Input {...attrs} bind:value={$formData.email} />
43+
<Form.Label>Email</Form.Label>
44+
<Input {...attrs} placeholder="m@example.com" bind:value={$formData.email} />
4445
</Form.Control>
45-
<Form.Description>This is your e-mail.</Form.Description>
4646
<Form.FieldErrors />
4747
</Form.Field>
4848
<Form.Field {form} name="password">
4949
<Form.Control let:attrs>
5050
<Form.Label>Password</Form.Label>
51-
<Input {...attrs} type="password" bind:value={$formData.password} />
51+
<Input
52+
{...attrs}
53+
type="password"
54+
placeholder="enter password"
55+
bind:value={$formData.password}
56+
/>
5257
</Form.Control>
53-
<Form.Description>This is your password</Form.Description>
5458
<Form.FieldErrors />
5559
</Form.Field>
56-
</Card.Content>
57-
<Card.Footer class="flex justify-end">
58-
<Form.Button>Log In</Form.Button>
59-
</Card.Footer>
60-
</Card.Root>
61-
</form>
60+
<div class="mb-5 mt-7 flex items-center justify-between">
61+
<div class="flex items-center">
62+
<Checkbox id="stay-logged-in" />
63+
<Label for="stay-logged-in" class="ml-2 text-sm">Stay logged in</Label>
64+
</div>
65+
<div class="text-sm">
66+
<a href="/forgot-password" class="font-medium underline">Forgot your password?</a>
67+
</div>
68+
</div>
69+
<Form.Button class="w-full">Log In</Form.Button>
70+
</form>
71+
<div class="mt-5 text-sm">
72+
Check out our <a href="/code-of-conduct" class="font-medium underline">Code of Conduct</a>
73+
and
74+
<a href="privacy-policy" class="font-medium underline">Privacy Policy</a>
75+
page.
76+
</div>
77+
</div>
78+
</div>

src/routes/(auth)/signup/schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ export const signupSchema = z
77
.email({ message: 'Please enter a valid email address' }),
88
password: z
99
.string({ required_error: 'Password is required' })
10-
.min(8, { message: 'Password must be at least 8 characters' })
11-
.trim(),
12-
confirmPassword: z.string().trim()
10+
.min(8, { message: 'Password must be at least 8 characters' }),
11+
confirmPassword: z.string(),
12+
termsOfService: z.literal(true, { required_error: 'You must agree to the terms of service' })
1313
})
1414
.refine((data) => data.password === data.confirmPassword, {
1515
message: "Passwords don't match",
Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import * as Form from '$components/ui/form'
33
import { Input } from '$components/ui/input'
4-
import * as Card from '$components/ui/card'
4+
import { Checkbox } from '$components/ui/checkbox'
55
import { type SignupFormSchema, signupSchema } from './schema'
66
import { type Infer, type SuperValidated, superForm } from 'sveltekit-superforms'
77
import { zodClient } from 'sveltekit-superforms/adapters'
@@ -26,45 +26,69 @@
2626
})
2727
2828
const { form: formData, enhance } = form
29-
$formData.email = 'test@test.com'
30-
$formData.password = 'passwordAÖOGDÖADG'
31-
$formData.confirmPassword = 'passwordAÖOGDÖADG'
3229
</script>
3330

34-
<form method="POST" use:enhance>
35-
<Card.Root class="m-auto w-[350px]">
36-
<Card.Header>
37-
<Card.Title>Sign Up to tiny-tms</Card.Title>
38-
<Card.Description>Register to tiny-tms and start doing you translations</Card.Description>
39-
</Card.Header>
40-
<Card.Content>
31+
<div class="flex items-center justify-center">
32+
<div class="w-full max-w-2xl">
33+
<div class="text-center">
34+
<h2 class="mt-6 text-center text-3xl font-extrabold">Register</h2>
35+
<p class="mt-2 text-center text-sm">
36+
Enter you information to create an user account for this Tiny-TMS instance
37+
</p>
38+
<p class="text-center text-sm">
39+
or <a href="/login" class="font-medium underline">sign in to an existing account</a>
40+
</p>
41+
</div>
42+
<form method="POST" use:enhance>
4143
<Form.Field {form} name="email">
4244
<Form.Control let:attrs>
43-
<Form.Label>E-Mail</Form.Label>
44-
<Input {...attrs} bind:value={$formData.email} />
45+
<Form.Label>Email</Form.Label>
46+
<Input {...attrs} placeholder="m@example.com" bind:value={$formData.email} />
4547
</Form.Control>
46-
<Form.Description>This is your e-mail.</Form.Description>
4748
<Form.FieldErrors />
4849
</Form.Field>
4950
<Form.Field {form} name="password">
5051
<Form.Control let:attrs>
5152
<Form.Label>Password</Form.Label>
52-
<Input {...attrs} type="password" bind:value={$formData.password} />
53+
<Input
54+
{...attrs}
55+
placeholder="enter password"
56+
type="password"
57+
bind:value={$formData.password}
58+
/>
5359
</Form.Control>
54-
<Form.Description>This is your password</Form.Description>
5560
<Form.FieldErrors />
5661
</Form.Field>
5762
<Form.Field {form} name="confirmPassword">
5863
<Form.Control let:attrs>
5964
<Form.Label>Confirm Password</Form.Label>
60-
<Input {...attrs} type="password" bind:value={$formData.confirmPassword} />
65+
<Input
66+
{...attrs}
67+
placeholder="enter password again"
68+
type="password"
69+
bind:value={$formData.confirmPassword}
70+
/>
6171
</Form.Control>
62-
<Form.Description>This is your password</Form.Description>
6372
<Form.FieldErrors />
6473
</Form.Field>
65-
</Card.Content>
66-
<Card.Footer class="flex justify-end">
67-
<Form.Button>Sign Up</Form.Button>
68-
</Card.Footer>
69-
</Card.Root>
70-
</form>
74+
75+
<Form.Field {form} name="termsOfService">
76+
<Form.Control let:attrs>
77+
<div class="mb-5 mt-7 flex items-center justify-between">
78+
<div class="flex items-center">
79+
<Checkbox {...attrs} bind:checked={$formData.termsOfService} />
80+
<Form.Label class="ml-2 text-sm">
81+
I agree with the <a href="/terms-of-service" class="font-medium underline">
82+
Terms of Service
83+
</a>
84+
</Form.Label>
85+
<input name={attrs.name} value={$formData.termsOfService} hidden />
86+
</div>
87+
</div>
88+
</Form.Control>
89+
</Form.Field>
90+
91+
<Form.Button class="w-full">Sign Up</Form.Button>
92+
</form>
93+
</div>
94+
</div>

0 commit comments

Comments
 (0)