Skip to content
35 changes: 35 additions & 0 deletions src/components/ui/checkbox/checkbox.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import { Checkbox as CheckboxPrimitive } from 'bits-ui'
import Check from 'lucide-svelte/icons/check'
import Minus from 'lucide-svelte/icons/minus'
import { cn } from '$utils'

type $$Props = CheckboxPrimitive.Props
type $$Events = CheckboxPrimitive.Events

let className: $$Props['class'] = undefined
export let checked: NonNullable<$$Props['checked']> = false
export { className as class }
</script>

<CheckboxPrimitive.Root
class={cn(
'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',
className
)}
bind:checked
{...$$restProps}
on:click
>
<CheckboxPrimitive.Indicator
class={cn('flex h-4 w-4 items-center justify-center text-current')}
let:isChecked
let:isIndeterminate
>
{#if isChecked}
<Check class="h-3.5 w-3.5" />
{:else if isIndeterminate}
<Minus class="h-3.5 w-3.5" />
{/if}
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
7 changes: 7 additions & 0 deletions src/components/ui/checkbox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Root from './checkbox.svelte'

export {
Root,
//
Root as Checkbox
}
1 change: 1 addition & 0 deletions src/components/ui/theme-selector/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ThemeSelector } from './theme-selector.svelte'
58 changes: 58 additions & 0 deletions src/components/ui/theme-selector/theme-selector.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script lang="ts">
import Sun from 'lucide-svelte/icons/sun'
import Moon from 'lucide-svelte/icons/moon'
import Computer from 'lucide-svelte/icons/computer'
import { ModeWatcher, mode, setMode } from 'mode-watcher'

let currentMode: 'dark' | 'light' | 'system' = $mode || 'system'

$: {
setMode(currentMode)
}
</script>

<ModeWatcher />

<div class="inline-flex w-auto items-center rounded-full border border-primary">
<input
type="radio"
id="system"
name="mode"
class="hidden"
value={'system'}
bind:group={currentMode}
/>
<label for="system" class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full">
<Computer class="h-[1.2rem] w-[1.2rem]" />
</label>

<input
type="radio"
id="light"
name="mode"
class="hidden"
value={'light'}
bind:group={currentMode}
/>
<label for="light" class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full">
<Sun class="h-[1.2rem] w-[1.2rem]" />
</label>

<input
type="radio"
id="dark"
name="mode"
class="hidden"
value={'dark'}
bind:group={currentMode}
/>
<label for="dark" class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full">
<Moon class="h-[1.2rem] w-[1.2rem]" />
</label>
</div>

<style lang="css">
input[type='radio']:checked + label {
@apply border border-primary;
}
</style>
10 changes: 9 additions & 1 deletion src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { TOKEN_NAME, parseTokenToJwt } from 'services/auth/token'
import type { UserAuthCredentials } from 'services/user/user'
import { getUserAuthCredentials } from 'services/user/user-auth-service'

const PUBLIC_ROUTES = ['/', '/login', '/signup']
const PUBLIC_ROUTES = [
'/',
'/login',
'/signup',
'/forgot-password',
'/code-of-conduct',
'/privacy-policy',
'/terms-of-service'
]

export const handle: Handle = async ({ event, resolve }) => {
const { locals, cookies, url } = event
Expand Down
52 changes: 52 additions & 0 deletions src/routes/(auth)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script lang="ts">
import Check from 'lucide-svelte/icons/check'
import { ThemeSelector } from '$components/ui/theme-selector'
</script>

<div class="flex h-screen">
<div class="flex w-1/2 flex-col bg-secondary text-secondary-foreground">
<!-- Top Section -->
<div class="flex-none p-6">
<div class="text-2xl">
<span>⌘ Tiny-TMS</span>
</div>
</div>

<!-- Middle Section -->
<div
class="flex flex-grow flex-col items-start justify-center space-y-4 p-6 text-2xl font-semibold"
>
<div class="flex items-center">
<span class="mr-2.5 rounded-full bg-primary p-1.5">
<Check strokeWidth={1} class="stroke-primary-foreground" />
</span>
<span>Efficiently translate your content</span>
</div>
<div class="flex items-center">
<span class="mr-2.5 rounded-full bg-primary p-1.5">
<Check strokeWidth={1} class="stroke-primary-foreground" />
</span>
<span>Open source and self hosting</span>
</div>
<div class="flex items-center">
<span class="mr-2.5 rounded-full bg-primary p-1.5">
<Check strokeWidth={1} class="stroke-primary-foreground" />
</span>
<span>Seamless AI-Autocompletion integrations</span>
</div>
</div>

<!-- Bottom Section -->
<div class="flex-none p-6">
<div>
<p>A Translation Management System to cover the needs of most people.</p>
<p>Not for everyone, but for most.</p>
</div>
</div>
<div class="p-6">
<ThemeSelector />
</div>
</div>

<div class="my-auto w-1/2"><slot></slot></div>
</div>
1 change: 1 addition & 0 deletions src/routes/(auth)/forgot-password/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Forgot Password
59 changes: 38 additions & 21 deletions src/routes/(auth)/login/login-form.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script lang="ts">
import * as Form from '$components/ui/form'
import { Input } from '$components/ui/input'
import * as Card from '$components/ui/card'
import { Checkbox } from '$components/ui/checkbox'
import { Label } from '$components/ui/label'
import { type LoginFormSchema, loginSchema } from './schema'
import { type Infer, type SuperValidated, superForm } from 'sveltekit-superforms'
import { zodClient } from 'sveltekit-superforms/adapters'
Expand All @@ -26,36 +27,52 @@
})

const { form: formData, enhance } = form
$formData.email = 'test@test.com'
$formData.password = 'passwordAÖOGDÖADG'
</script>

<form method="POST" use:enhance>
<Card.Root class="m-auto w-[350px]">
<Card.Header>
<Card.Title>Log In</Card.Title>
<Card.Description>Log In to tiny-tms and start doing you translations</Card.Description>
</Card.Header>
<Card.Content>
<div class="flex items-center justify-center">
<div class="w-full max-w-2xl">
<div class="text-center">
<h2 class="mt-6 text-center text-3xl font-extrabold">Sign in to your account</h2>
<p class="mt-2 text-center text-sm">
Or <a href="/signup" class="font-medium underline">sign up for a new account</a>
</p>
</div>
<form method="POST" use:enhance>
<Form.Field {form} name="email">
<Form.Control let:attrs>
<Form.Label>E-Mail</Form.Label>
<Input {...attrs} bind:value={$formData.email} />
<Form.Label>Email</Form.Label>
<Input {...attrs} placeholder="m@example.com" bind:value={$formData.email} />
</Form.Control>
<Form.Description>This is your e-mail.</Form.Description>
<Form.FieldErrors />
</Form.Field>
<Form.Field {form} name="password">
<Form.Control let:attrs>
<Form.Label>Password</Form.Label>
<Input {...attrs} type="password" bind:value={$formData.password} />
<Input
{...attrs}
type="password"
placeholder="enter password"
bind:value={$formData.password}
/>
</Form.Control>
<Form.Description>This is your password</Form.Description>
<Form.FieldErrors />
</Form.Field>
</Card.Content>
<Card.Footer class="flex justify-end">
<Form.Button>Log In</Form.Button>
</Card.Footer>
</Card.Root>
</form>
<div class="mb-5 mt-7 flex items-center justify-between">
<div class="flex items-center">
<Checkbox id="stay-logged-in" />
<Label for="stay-logged-in" class="ml-2 text-sm">Stay logged in</Label>
</div>
<div class="text-sm">
<a href="/forgot-password" class="font-medium underline">Forgot your password?</a>
</div>
</div>
<Form.Button class="w-full">Log In</Form.Button>
</form>
<div class="mt-5 text-sm">
Check out our <a href="/code-of-conduct" class="font-medium underline">Code of Conduct</a>
and
<a href="privacy-policy" class="font-medium underline">Privacy Policy</a>
page.
</div>
</div>
</div>
6 changes: 3 additions & 3 deletions src/routes/(auth)/signup/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ export const signupSchema = z
.email({ message: 'Please enter a valid email address' }),
password: z
.string({ required_error: 'Password is required' })
.min(8, { message: 'Password must be at least 8 characters' })
.trim(),
confirmPassword: z.string().trim()
.min(8, { message: 'Password must be at least 8 characters' }),
confirmPassword: z.string(),
termsOfService: z.literal(true, { required_error: 'You must agree to the terms of service' })
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
Expand Down
72 changes: 48 additions & 24 deletions src/routes/(auth)/signup/signup-form.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import * as Form from '$components/ui/form'
import { Input } from '$components/ui/input'
import * as Card from '$components/ui/card'
import { Checkbox } from '$components/ui/checkbox'
import { type SignupFormSchema, signupSchema } from './schema'
import { type Infer, type SuperValidated, superForm } from 'sveltekit-superforms'
import { zodClient } from 'sveltekit-superforms/adapters'
Expand All @@ -26,45 +26,69 @@
})

const { form: formData, enhance } = form
$formData.email = 'test@test.com'
$formData.password = 'passwordAÖOGDÖADG'
$formData.confirmPassword = 'passwordAÖOGDÖADG'
</script>

<form method="POST" use:enhance>
<Card.Root class="m-auto w-[350px]">
<Card.Header>
<Card.Title>Sign Up to tiny-tms</Card.Title>
<Card.Description>Register to tiny-tms and start doing you translations</Card.Description>
</Card.Header>
<Card.Content>
<div class="flex items-center justify-center">
<div class="w-full max-w-2xl">
<div class="text-center">
<h2 class="mt-6 text-center text-3xl font-extrabold">Register</h2>
<p class="mt-2 text-center text-sm">
Enter you information to create an user account for this Tiny-TMS instance
</p>
<p class="text-center text-sm">
or <a href="/login" class="font-medium underline">sign in to an existing account</a>
</p>
</div>
<form method="POST" use:enhance>
<Form.Field {form} name="email">
<Form.Control let:attrs>
<Form.Label>E-Mail</Form.Label>
<Input {...attrs} bind:value={$formData.email} />
<Form.Label>Email</Form.Label>
<Input {...attrs} placeholder="m@example.com" bind:value={$formData.email} />
</Form.Control>
<Form.Description>This is your e-mail.</Form.Description>
<Form.FieldErrors />
</Form.Field>
<Form.Field {form} name="password">
<Form.Control let:attrs>
<Form.Label>Password</Form.Label>
<Input {...attrs} type="password" bind:value={$formData.password} />
<Input
{...attrs}
placeholder="enter password"
type="password"
bind:value={$formData.password}
/>
</Form.Control>
<Form.Description>This is your password</Form.Description>
<Form.FieldErrors />
</Form.Field>
<Form.Field {form} name="confirmPassword">
<Form.Control let:attrs>
<Form.Label>Confirm Password</Form.Label>
<Input {...attrs} type="password" bind:value={$formData.confirmPassword} />
<Input
{...attrs}
placeholder="enter password again"
type="password"
bind:value={$formData.confirmPassword}
/>
</Form.Control>
<Form.Description>This is your password</Form.Description>
<Form.FieldErrors />
</Form.Field>
</Card.Content>
<Card.Footer class="flex justify-end">
<Form.Button>Sign Up</Form.Button>
</Card.Footer>
</Card.Root>
</form>

<Form.Field {form} name="termsOfService">
<Form.Control let:attrs>
<div class="mb-5 mt-7 flex items-center justify-between">
<div class="flex items-center">
<Checkbox {...attrs} bind:checked={$formData.termsOfService} />
<Form.Label class="ml-2 text-sm">
I agree with the <a href="/terms-of-service" class="font-medium underline">
Terms of Service
</a>
</Form.Label>
<input name={attrs.name} value={$formData.termsOfService} hidden />
</div>
</div>
</Form.Control>
</Form.Field>

<Form.Button class="w-full">Sign Up</Form.Button>
</form>
</div>
</div>
1 change: 1 addition & 0 deletions src/routes/(terms-policy)/code-of-conduct/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Code of Conduct
1 change: 1 addition & 0 deletions src/routes/(terms-policy)/privacy-policy/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Privacy Policy
1 change: 1 addition & 0 deletions src/routes/(terms-policy)/terms-of-service/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Terms of Service
Loading