diff --git a/e2e/settings/updating-settings.spec.ts b/e2e/settings/updating-settings.spec.ts
index 8fedcf8..769c159 100644
--- a/e2e/settings/updating-settings.spec.ts
+++ b/e2e/settings/updating-settings.spec.ts
@@ -8,12 +8,12 @@ test.describe('update user settings', () => {
// Arrange
await settingsPage.goto()
// Act
- await settingsPage.editName('John Doe')
- await settingsPage.submit()
+ await settingsPage.edit('name', 'John Doe')
+ await settingsPage.submit('name')
await page.reload()
// Assert
await expect(page).toHaveURL('/settings/profile')
- await expect(page.getByPlaceholder('Nombre')).toHaveValue('John Doe')
+ await expect(page.getByLabel('name')).toHaveValue('John Doe')
})
})
diff --git a/e2e/tests/pages/settings.page.ts b/e2e/tests/pages/settings.page.ts
index c2d711e..84cc8cb 100644
--- a/e2e/tests/pages/settings.page.ts
+++ b/e2e/tests/pages/settings.page.ts
@@ -11,12 +11,16 @@ class SettingsPage {
await this.page.goto('/settings/profile')
}
- async editName(name: string) {
- await this.page.getByPlaceholder('Nombre').fill(name)
+ async edit(label: string, name: string) {
+ await this.page.getByLabel(label).fill(name)
}
- async submit() {
- await this.page.getByRole('button', { name: 'Enviar' }).click()
+ async submit(label: string) {
+ await this.page
+ .getByRole('region')
+ .filter({ has: this.page.getByLabel(label) })
+ .getByRole('button', { name: 'Guardar' })
+ .click()
}
async restore() {
diff --git a/src/app/settings/layout.tsx b/src/app/settings/layout.tsx
index 1d3fb23..a725d01 100644
--- a/src/app/settings/layout.tsx
+++ b/src/app/settings/layout.tsx
@@ -1,39 +1,25 @@
'use client'
-import { Card, CardBody, CardHeader, Tab, Tabs } from '@nextui-org/react'
-import NextLink from 'next/link'
import { usePathname } from 'next/navigation'
import { ReactNode } from 'react'
+import SettingsTemplate from '@/components/settings-template/settings-template'
+
+const TABS = [
+ {
+ target: 'profile',
+ title: 'Perfil',
+ },
+]
+
export default function Layout({ children }: { children: ReactNode }) {
const pathname = usePathname()
return (
<>
-
-
- Ajustes de usuario
-
-
-
-
-
- {children}
-
-
+
+ {children}
+
>
)
}
diff --git a/src/app/settings/profile/page.tsx b/src/app/settings/profile/page.tsx
index 6f3e9de..d0d5920 100644
--- a/src/app/settings/profile/page.tsx
+++ b/src/app/settings/profile/page.tsx
@@ -1,6 +1,7 @@
import { redirect } from 'next/navigation'
-import EditProfileForm from '@/components/edit-profile-form/edit-profile-form'
+import SettingsForm from '@/components/settings-form/settings-form'
+import SettingsFormInputText from '@/components/settings-form/settings-form-input-text'
import UserResponse from '@/core/user/dto/responses/user.response'
import { findUser } from '@/core/user/infrastructure/actions/find-user'
import { auth } from '@/lib/auth/auth'
@@ -16,7 +17,31 @@ export default async function Page() {
return (
<>
-
+
+
+ Introduce tu nombre completo o con el que más se te conoce.
+
+
+
+
+ Esta es la dirección de acceso a la plataforma.
+
+
+
>
)
}
diff --git a/src/core/user/application/update-setting.use-case.ts b/src/core/user/application/update-setting.use-case.ts
new file mode 100644
index 0000000..c7e9e2b
--- /dev/null
+++ b/src/core/user/application/update-setting.use-case.ts
@@ -0,0 +1,46 @@
+import { err, ok, Result } from 'neverthrow'
+
+import NotFoundError from '@/core/common/domain/errors/application/not-found-error'
+import ApplicationError from '@/core/common/domain/errors/application-error'
+import DomainError from '@/core/common/domain/errors/domain-error'
+import Email from '@/core/common/domain/value-objects/email'
+import FullName from '@/core/common/domain/value-objects/fullname'
+import User from '@/core/user/domain/model/user.entity'
+import Users from '@/core/user/domain/services/users.repository'
+import UpdateSettingRequest from '@/core/user/dto/requests/update-setting.request'
+
+export default class UpdateSettingUseCase {
+ constructor(private readonly users: Users) {}
+
+ async with(
+ command: UpdateSettingRequest,
+ ): Promise> {
+ return Email.create(command.email)
+ .asyncAndThen((email) => this.users.findByEmail(email))
+ .andThen((user) => this.updateUser(user, command))
+ .andThen((user) => this.users.save(user))
+ }
+
+ private updateUser(user: User, command: UpdateSettingRequest) {
+ switch (command.field) {
+ case 'name': {
+ return this.updateFullName(command, user)
+ }
+ default: {
+ return err(
+ new ApplicationError(
+ `Field ${command.field} does not exists in User entity`,
+ ),
+ )
+ }
+ }
+ }
+
+ private updateFullName(command: UpdateSettingRequest, user: User) {
+ return FullName.create(command.value).andThen((fullName) => {
+ user.name = fullName
+
+ return ok(user)
+ })
+ }
+}
diff --git a/src/core/user/dto/requests/update-setting.request.ts b/src/core/user/dto/requests/update-setting.request.ts
new file mode 100644
index 0000000..6a6c573
--- /dev/null
+++ b/src/core/user/dto/requests/update-setting.request.ts
@@ -0,0 +1,13 @@
+import { DeepReadonly } from 'ts-essentials'
+
+type UpdateSettingRequest = DeepReadonly<{
+ email: string
+ field: string
+ value: string
+}>
+
+const UpdateSettingRequest = {
+ with: (properties: UpdateSettingRequest): UpdateSettingRequest => properties,
+}
+
+export default UpdateSettingRequest
diff --git a/src/core/user/infrastructure/actions/update-setting.ts b/src/core/user/infrastructure/actions/update-setting.ts
new file mode 100644
index 0000000..1488ebc
--- /dev/null
+++ b/src/core/user/infrastructure/actions/update-setting.ts
@@ -0,0 +1,75 @@
+'use server'
+
+import { revalidateTag } from 'next/cache'
+import { z } from 'zod'
+
+import UpdateSettingRequest from '@/core/user/dto/requests/update-setting.request'
+import me from '@/core/user/infrastructure/actions/me'
+import container from '@/lib/container'
+import FormResponse from '@/lib/zod/form-response'
+
+export type UpdatableFields = 'name' | 'email'
+
+interface UpdateSettingForm {
+ field: UpdatableFields
+ value: string
+}
+
+const UpdateSettingMap = {
+ name: z.object({
+ field: z.string(),
+ value: z.string().min(1).max(64),
+ }),
+}
+
+type UpdateSettingMapKeys = keyof typeof UpdateSettingMap
+
+export async function updateSetting(
+ previousState: FormResponse,
+ formData: FormData,
+): Promise> {
+ const user = await me()
+ const { field } = previousState.data
+
+ if (!user) {
+ return FormResponse.custom(
+ [field],
+ 'Error en la sesión del usuario',
+ previousState.data,
+ )
+ }
+
+ const { email } = user
+
+ if (!(field in UpdateSettingMap)) {
+ return FormResponse.custom(
+ [field],
+ 'El campo no es válido',
+ previousState.data,
+ )
+ }
+
+ const result = UpdateSettingMap[field as UpdateSettingMapKeys].safeParse(
+ Object.fromEntries(formData),
+ )
+
+ if (!result.success) {
+ return FormResponse.withError(
+ result.error,
+ previousState.data,
+ )
+ }
+
+ const { value } = result.data
+
+ await container.updateSetting.with(
+ UpdateSettingRequest.with({ email, field, value }),
+ )
+
+ revalidateTag(`role-for-${email}`)
+
+ return FormResponse.success(
+ result.data as UpdateSettingForm,
+ 'Perfil actualizado.',
+ )
+}
diff --git a/src/lib/container/container.ts b/src/lib/container/container.ts
index 54d7ef6..99ff032 100644
--- a/src/lib/container/container.ts
+++ b/src/lib/container/container.ts
@@ -1,6 +1,6 @@
import CreateBookUseCase from '@/core/book/application/create-book.use-case'
import EditBookUseCase from '@/core/book/application/edit-book.use-case'
-import { LoanBookUseCase } from '@/core/book/application/loan-book-use.case'
+import LoanBookUseCase from '@/core/book/application/loan-book-use.case'
import ReturnBookUseCase from '@/core/book/application/return-book.use-case'
import FindAllBooksQuery from '@/core/book/infrastructure/queries/find-all-books.query'
import FindBookQuery from '@/core/book/infrastructure/queries/find-book.query'
@@ -11,6 +11,7 @@ import GetHistoricalLoansQuery from '@/core/loan/infrastructure/queries/get-hist
import LoansPrisma from '@/core/loan/infrastructure/services/loans-prisma.repository'
import EnableUserUseCase from '@/core/user/application/enable-user.use-case'
import FindUserUseCase from '@/core/user/application/find-user.use-case'
+import UpdateSettingUseCase from '@/core/user/application/update-setting.use-case'
import UpdateUserUseCase from '@/core/user/application/update-user.use-case'
import FindAllUsersQuery from '@/core/user/infrastructure/queries/find-all-users.query'
import UsersPrisma from '@/core/user/infrastructure/services/users-prisma.repository'
@@ -35,6 +36,7 @@ const Container = {
getHistoricalLoans: new GetHistoricalLoansQuery(prisma),
loanBook: new LoanBookUseCase(books, loanBookService),
returnBook: new ReturnBookUseCase(books, returnBookService),
+ updateSetting: new UpdateSettingUseCase(users),
updateUser: new UpdateUserUseCase(users),
}
},