Skip to content

Commit

Permalink
up
Browse files Browse the repository at this point in the history
  • Loading branch information
HugoRCD committed Feb 19, 2025
1 parent 7fff92d commit ee7313a
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 173 deletions.
3 changes: 3 additions & 0 deletions app/layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const links = [[{
}, {
label: 'Notifications',
to: '/settings/notifications'
}, {
label: 'Security',
to: '/settings/security'
}]
}], [{
label: 'Feedback',
Expand Down
4 changes: 4 additions & 0 deletions app/pages/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const links = [[{
label: 'Notifications',
icon: 'i-lucide-bell',
to: '/settings/notifications'
}, {
label: 'Security',
icon: 'i-lucide-shield',
to: '/settings/security'
}], [{
label: 'Documentation',
icon: 'i-lucide-book-open',
Expand Down
271 changes: 107 additions & 164 deletions app/pages/settings/index.vue
Original file line number Diff line number Diff line change
@@ -1,41 +1,28 @@
<script setup lang="ts">
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
import type { FormError } from '#ui/types'
const fileRef = ref<HTMLInputElement>()
const schema = z.object({
const profileSchema = z.object({
name: z.string().min(2, 'Too short'),
email: z.string().email('Invalid email'),
password_current: z.string().min(8, 'Must be at least 8 characters'),
password_new: z.string().min(8, 'Must be at least 8 characters'),
username: z.string().min(2, 'Too short'),
avatar: z.string().optional(),
bio: z.string().optional()
})
type Schema = z.output<typeof schema>
type ProfileSchema = z.output<typeof profileSchema>
const state = reactive<Partial<Schema>>({
const profile = reactive<Partial<ProfileSchema>>({
name: 'Benjamin Canac',
email: '[email protected]',
username: 'benjamincanac',
avatar: undefined,
bio: undefined,
password_current: undefined,
password_new: undefined
bio: undefined
})
const validate = (state: Schema): FormError[] => {
const errors = []
if (state.password_current === state.password_new)
errors.push({ name: 'password_new', message: 'Passwords must be different' })
return errors
}
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) {
async function onSubmit(event: FormSubmitEvent<ProfileSchema>) {
toast.add({
title: 'Success',
description: 'Your settings have been updated.',
Expand All @@ -52,7 +39,7 @@ function onFileChange(e: Event) {
return
}
state.avatar = URL.createObjectURL(input.files[0])
profile.avatar = URL.createObjectURL(input.files[0])
}
function onFileClick() {
Expand All @@ -62,157 +49,113 @@ function onFileClick() {

<template>
<div class="space-y-4">
<div class="flex justify-between items-center gap-4">
<UFormField label="Theme" description="Customize the look and feel of your dashboard." />
<UColorModeSelect />
</div>
<USeparator />
<div class="flex justify-between items-center gap-4">
<UFormField label="Profile" description="This information will be displayed publicly so be careful what you share." />
<UButton
form="settings"
label="Save changes"
color="neutral"
type="submit"
/>
</div>
<USeparator />
<UForm
id="settings"
:schema="schema"
:state="state"
:validate="validate"
class="space-y-4"
@submit="onSubmit"
>
<UFormField
name="name"
label="Name"
description="Will appear on receipts, invoices, and other communication."
required
class="flex justify-between items-start gap-4"
>
<UInput
v-model="state.name"
autocomplete="off"
size="md"
/>
</UFormField>
<USeparator />
<UFormField
name="email"
label="Email"
description="Used to sign in, for email receipts and product updates."
required
class="flex justify-between items-start gap-4"
>
<UInput
v-model="state.email"
type="email"
autocomplete="off"
size="md"
/>
</UFormField>
<USeparator />
<UFormField
name="username"
label="Username"
description="Your unique username for logging in and your profile URL."
required
class="flex justify-between items-start gap-4"
>
<UInput
v-model="state.username"
type="username"
autocomplete="off"
size="md"
<UPageCard>
<div class="flex justify-between items-center gap-4">
<UFormField label="Theme" description="Customize the look and feel of your dashboard." />
<UColorModeSelect />
</div>
</UPageCard>
<UPageCard>
<div class="flex justify-between items-center">
<UFormField label="Profile" description="This information will be displayed publicly so be careful what you share." />
<UButton
form="settings"
label="Save changes"
color="neutral"
type="submit"
/>
</UFormField>
</div>
<USeparator />
<UFormField
name="avatar"
label="Avatar"
description="JPG, GIF or PNG. 1MB Max."
class="flex justify-between items-center gap-4"
<UForm
id="settings"
:schema="profileSchema"
:state="profile"
class="space-y-4"
@submit="onSubmit"
>
<div class="flex flex-wrap items-center gap-3">
<UAvatar
:src="state.avatar"
:alt="state.name"
size="lg"
<UFormField
name="name"
label="Name"
description="Will appear on receipts, invoices, and other communication."
required
class="flex justify-between items-start gap-4"
>
<UInput
v-model="profile.name"
autocomplete="off"
/>
<UButton
label="Choose"
color="neutral"
size="md"
@click="onFileClick"
</UFormField>
<USeparator />
<UFormField
name="email"
label="Email"
description="Used to sign in, for email receipts and product updates."
required
class="flex justify-between items-start gap-4"
>
<UInput
v-model="profile.email"
type="email"
autocomplete="off"
/>
<input
ref="fileRef"
type="file"
class="hidden"
accept=".jpg, .jpeg, .png, .gif"
@change="onFileChange"
>
</div>
</UFormField>
<USeparator />
<UFormField
name="bio"
label="Bio"
description="Brief description for your profile. URLs are hyperlinked."
class="flex justify-between items-start gap-4"
:ui="{ container: 'w-full' }"
>
<UTextarea
v-model="state.bio"
:rows="5"
autoresize
class="w-full"
size="md"
/>
</UFormField>
<USeparator />
<UFormField
name="password"
label="Password"
description="Confirm your current password before setting a new one."
class="flex justify-between items-start gap-4"
>
<div class="flex flex-col gap-2">
<UFormField name="password_current">
<UInput
id="password"
v-model="state.password_current"
type="password"
placeholder="Current password"
size="md"
</UFormField>
<USeparator />
<UFormField
name="username"
label="Username"
description="Your unique username for logging in and your profile URL."
required
class="flex justify-between items-start gap-4"
>
<UInput
v-model="profile.username"
type="username"
autocomplete="off"
/>
</UFormField>
<USeparator />
<UFormField
name="avatar"
label="Avatar"
description="JPG, GIF or PNG. 1MB Max."
class="flex justify-between items-center gap-4"
>
<div class="flex flex-wrap items-center gap-3">
<UAvatar
:src="profile.avatar"
:alt="profile.name"
size="lg"
/>
</UFormField>
<UFormField name="password_new">
<UInput
id="password_new"
v-model="state.password_new"
type="password"
placeholder="New password"
size="md"
<UButton
label="Choose"
color="neutral"
@click="onFileClick"
/>
</UFormField>
</div>
</UFormField>
<USeparator />
<UFormField
name="Account"
label="Account"
description="No longer want to use our service? You can delete your account here. This action is not reversible. All information related to this account will be deleted permanently."
class="flex flex-col items-end gap-4"
>
<UButton
label="Delete account"
color="error"
size="md"
/>
</UFormField>
</UForm>
<input
ref="fileRef"
type="file"
class="hidden"
accept=".jpg, .jpeg, .png, .gif"
@change="onFileChange"
>
</div>
</UFormField>
<USeparator />
<UFormField
name="bio"
label="Bio"
description="Brief description for your profile. URLs are hyperlinked."
class="flex justify-between items-start gap-4"
:ui="{ container: 'w-full' }"
>
<UTextarea
v-model="profile.bio"
:rows="5"
autoresize
class="w-full"
/>
</UFormField>
</UForm>
</UPageCard>
</div>
</template>
9 changes: 3 additions & 6 deletions app/pages/settings/members.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,18 @@ const filteredMembers = computed(() => {
/>
</div>
</div>
<UCard
:ui="{ header: 'p-4 sm:px-6', body: 'p-0' }"
class="w-full sm:max-w-xl"
>
<UPageCard class="w-full">
<template #header>
<UInput
v-model="q"
icon="i-lucide-search"
placeholder="Search members"
autofocus
class="w-full"
/>
</template>

<!-- ~/components/settings/MembersList.vue -->
<SettingsMembersList :members="filteredMembers" />
</UCard>
</UPageCard>
</div>
</template>
4 changes: 2 additions & 2 deletions app/pages/settings/notifications.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async function onChange() {
{{ section.description }}
</span>
</div>
<UCard :ui="{ body: 'divide-y divide-(--ui-border)' }">
<UPageCard :ui="{ container: 'divide-y divide-(--ui-border)' }">
<UFormField
v-for="field in section.fields"
:key="field.name"
Expand All @@ -72,7 +72,7 @@ async function onChange() {
@update:model-value="onChange"
/>
</UFormField>
</UCard>
</UPageCard>
</div>
</div>
</template>
Loading

0 comments on commit ee7313a

Please sign in to comment.