Merge pull request 'feature/profile' (#3) from feature/profile into main
Reviewed-on: sam/fuware#3
This commit is contained in:
commit
59efe83d76
@ -1,16 +1,16 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from sqlalchemy import DateTime
|
from sqlalchemy import DateTime
|
||||||
from sqlalchemy.orm import declarative_base, Mapped, mapped_column
|
from sqlalchemy.orm import declarative_base, Mapped, mapped_column, QueryPropertyDescriptor
|
||||||
from text_unidecode import unidecode
|
from text_unidecode import unidecode
|
||||||
|
|
||||||
from backend.db.db_setup import SessionLocal
|
from backend.db.db_setup import SessionLocal
|
||||||
|
|
||||||
Model = declarative_base()
|
Model = declarative_base()
|
||||||
Model.query = SessionLocal.query_property()
|
|
||||||
|
|
||||||
class SqlAlchemyBase(Model):
|
class SqlAlchemyBase(Model):
|
||||||
__abstract__ = True
|
__abstract__ = True
|
||||||
|
query: QueryPropertyDescriptor = SessionLocal.query_property()
|
||||||
|
|
||||||
created_at: Mapped[datetime | None] = mapped_column(DateTime, default=datetime.utcnow(), index=True)
|
created_at: Mapped[datetime | None] = mapped_column(DateTime, default=datetime.utcnow(), index=True)
|
||||||
updated_at: Mapped[datetime | None] = mapped_column(DateTime, default=datetime.utcnow(), onupdate=datetime.utcnow())
|
updated_at: Mapped[datetime | None] = mapped_column(DateTime, default=datetime.utcnow(), onupdate=datetime.utcnow())
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from backend.core.config import get_app_settings
|
from backend.core.config import get_app_settings
|
||||||
from backend.core.security.security import hash_password
|
from backend.core.security.security import hash_password
|
||||||
from backend.db.models import User
|
from backend.db.models import User
|
||||||
from backend.schemas import UserCreate
|
from backend.schemas import UserCreate, UserProfile
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class RepositoryUsers:
|
|||||||
return self.user.query.filter_by(username=username).first()
|
return self.user.query.filter_by(username=username).first()
|
||||||
|
|
||||||
def get_by_id(self, user_id: str):
|
def get_by_id(self, user_id: str):
|
||||||
return self.user.query.filter_by(id=UUID(user_id)).first()
|
return self.user.query.filter_by(id=UUID(str(user_id))).one()
|
||||||
|
|
||||||
def create(self, db: Session, user: UserCreate | UserSeeds):
|
def create(self, db: Session, user: UserCreate | UserSeeds):
|
||||||
try:
|
try:
|
||||||
@ -34,3 +34,18 @@ class RepositoryUsers:
|
|||||||
|
|
||||||
db.refresh(db_user)
|
db.refresh(db_user)
|
||||||
return db_user
|
return db_user
|
||||||
|
|
||||||
|
def update(self, db: Session, user: UserProfile, user_id: str):
|
||||||
|
db_user = self.get_by_id(user_id)
|
||||||
|
if not db_user:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
user.update_password()
|
||||||
|
self.user.query.where(User.id == user_id).update(user.dict(exclude_unset=True, exclude_none=True))
|
||||||
|
db.commit()
|
||||||
|
except Exception:
|
||||||
|
db.rollback()
|
||||||
|
raise
|
||||||
|
|
||||||
|
db.refresh(db_user)
|
||||||
|
return db_user
|
||||||
|
@ -9,7 +9,7 @@ from backend.core.config import get_app_settings
|
|||||||
from backend.core.dependencies.dependencies import get_current_user
|
from backend.core.dependencies.dependencies import get_current_user
|
||||||
from backend.core import MessageCode
|
from backend.core import MessageCode
|
||||||
from backend.db.db_setup import generate_session
|
from backend.db.db_setup import generate_session
|
||||||
from backend.schemas import ReturnValue, UserRequest, LoginResponse, UserCreate, PrivateUser
|
from backend.schemas import ReturnValue, UserRequest, LoginResponse, PrivateUser
|
||||||
from backend.services.user import UserService
|
from backend.services.user import UserService
|
||||||
|
|
||||||
|
|
||||||
@ -29,14 +29,6 @@ async def get_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
|
|||||||
return {'access_token': token, 'token_type': 'bearer'}
|
return {'access_token': token, 'token_type': 'bearer'}
|
||||||
|
|
||||||
|
|
||||||
@auth_router.put('/register')
|
|
||||||
def register_user(user: UserCreate, db: db_dependency) -> ReturnValue[Any]:
|
|
||||||
db_user = user_service.get_by_username(username=user.username)
|
|
||||||
if db_user:
|
|
||||||
raise HTTPException(status_code=400, detail=MessageCode.CREATED_USER)
|
|
||||||
user_service.create(db=db, user=user)
|
|
||||||
return ReturnValue(status=200, data="created")
|
|
||||||
|
|
||||||
@auth_router.post('/login', response_model=ReturnValue[LoginResponse])
|
@auth_router.post('/login', response_model=ReturnValue[LoginResponse])
|
||||||
def user_login(user: UserRequest, response: Response) -> ReturnValue[Any]:
|
def user_login(user: UserRequest, response: Response) -> ReturnValue[Any]:
|
||||||
db_user = user_service.check_exist(user=user)
|
db_user = user_service.check_exist(user=user)
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from backend.core.config import get_app_settings
|
from backend.core.config import get_app_settings
|
||||||
from backend.core.dependencies import is_logged_in
|
from backend.core.dependencies import is_logged_in
|
||||||
|
from backend.core.message_code import MessageCode
|
||||||
from backend.db.db_setup import generate_session
|
from backend.db.db_setup import generate_session
|
||||||
from backend.schemas.common import ReturnValue
|
from backend.schemas.common import ReturnValue
|
||||||
from backend.schemas.user import ProfileResponse
|
from backend.schemas.user import ProfileResponse, UserCreate, UserProfile
|
||||||
from backend.services.user import UserService
|
from backend.services.user import UserService
|
||||||
|
|
||||||
|
|
||||||
@ -16,6 +17,19 @@ settings = get_app_settings()
|
|||||||
db_dependency = Annotated[Session, Depends(generate_session)]
|
db_dependency = Annotated[Session, Depends(generate_session)]
|
||||||
current_user_token = Annotated[ProfileResponse, Depends(is_logged_in)]
|
current_user_token = Annotated[ProfileResponse, Depends(is_logged_in)]
|
||||||
|
|
||||||
|
@public_router.put('/register')
|
||||||
|
def register_user(user: UserCreate, db: db_dependency) -> ReturnValue[Any]:
|
||||||
|
db_user = user_service.get_by_username(username=user.username)
|
||||||
|
if db_user:
|
||||||
|
raise HTTPException(status_code=400, detail=MessageCode.CREATED_USER)
|
||||||
|
user_service.create(db=db, user=user)
|
||||||
|
return ReturnValue(status=200, data="created")
|
||||||
|
|
||||||
@public_router.get("/me", response_model=ReturnValue[ProfileResponse])
|
@public_router.get("/me", response_model=ReturnValue[ProfileResponse])
|
||||||
def get_user(current_user: current_user_token) -> ReturnValue[Any]:
|
def get_user(current_user: current_user_token) -> ReturnValue[Any]:
|
||||||
return ReturnValue(status=200, data=current_user)
|
return ReturnValue(status=200, data=current_user)
|
||||||
|
|
||||||
|
@public_router.put("/update-profile", response_model=ReturnValue[ProfileResponse])
|
||||||
|
def update_user(user: UserProfile, current_user: current_user_token, db: db_dependency) -> ReturnValue[Any]:
|
||||||
|
db_user = user_service.update(db=db, user=user, user_id=current_user.id)
|
||||||
|
return ReturnValue(status=200, data=db_user)
|
||||||
|
@ -3,6 +3,7 @@ from uuid import UUID
|
|||||||
from pydantic import ConfigDict
|
from pydantic import ConfigDict
|
||||||
from fastapi import Form
|
from fastapi import Form
|
||||||
|
|
||||||
|
from backend.core.security.security import hash_password
|
||||||
from backend.schemas.main_model import MainModel
|
from backend.schemas.main_model import MainModel
|
||||||
|
|
||||||
class UserBase(MainModel):
|
class UserBase(MainModel):
|
||||||
@ -14,6 +15,16 @@ class UserRequest(UserBase):
|
|||||||
class UserCreate(UserRequest):
|
class UserCreate(UserRequest):
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
|
class UserProfile(MainModel):
|
||||||
|
name: str | None = None
|
||||||
|
password: str | None = None
|
||||||
|
is_admin: bool | None = None
|
||||||
|
is_lock: bool | None = None
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
def update_password(self):
|
||||||
|
self.password = (None if self.password is None else hash_password(self.password))
|
||||||
|
|
||||||
class UserSeeds(UserCreate):
|
class UserSeeds(UserCreate):
|
||||||
is_admin: bool
|
is_admin: bool
|
||||||
is_lock: bool
|
is_lock: bool
|
||||||
|
@ -10,12 +10,20 @@ class UserService(BaseService):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.repos = RepositoryUsers()
|
self.repos = RepositoryUsers()
|
||||||
|
|
||||||
def get_all(self, skip: int = 0, limit: int = 100):
|
def generate_token(self, user_id: str):
|
||||||
return self.repos.get_all(skip=skip, limit=limit)
|
access_token = create_access_token(data={"sub": str(user_id)})
|
||||||
|
refresh_token = create_refresh_token(data={"sub": str(user_id)})
|
||||||
|
return access_token, refresh_token
|
||||||
|
|
||||||
def get_by_username(self, username: str):
|
def get_by_username(self, username: str):
|
||||||
return self.repos.get_by_username(username)
|
return self.repos.get_by_username(username)
|
||||||
|
|
||||||
|
def get_access_token(self, user_id: str):
|
||||||
|
return create_access_token(data={"sub": str(user_id)})
|
||||||
|
|
||||||
|
def get_all(self, skip: int = 0, limit: int = 100):
|
||||||
|
return self.repos.get_all(skip=skip, limit=limit)
|
||||||
|
|
||||||
def get_by_id(self, user_id: str):
|
def get_by_id(self, user_id: str):
|
||||||
return self.repos.get_by_id(user_id)
|
return self.repos.get_by_id(user_id)
|
||||||
|
|
||||||
@ -23,6 +31,7 @@ class UserService(BaseService):
|
|||||||
return self.repos.create(db=db, user=user)
|
return self.repos.create(db=db, user=user)
|
||||||
|
|
||||||
def check_exist(self, user: UserRequest):
|
def check_exist(self, user: UserRequest):
|
||||||
|
print(f"user: {user}")
|
||||||
db_user = self.get_by_username(username=user.username)
|
db_user = self.get_by_username(username=user.username)
|
||||||
if not db_user:
|
if not db_user:
|
||||||
return False
|
return False
|
||||||
@ -30,10 +39,5 @@ class UserService(BaseService):
|
|||||||
return False
|
return False
|
||||||
return db_user
|
return db_user
|
||||||
|
|
||||||
def generate_token(self, user_id: str):
|
def update(self, db: Session, user: UserCreate, user_id: str):
|
||||||
access_token = create_access_token(data={"sub": str(user_id)})
|
return self.repos.update(db=db, user=user, user_id=user_id)
|
||||||
refresh_token = create_refresh_token(data={"sub": str(user_id)})
|
|
||||||
return access_token, refresh_token
|
|
||||||
|
|
||||||
def get_access_token(self, user_id: str):
|
|
||||||
return create_access_token(data={"sub": str(user_id)})
|
|
||||||
|
@ -2,3 +2,4 @@ export const POST_LOGIN = '/api/auth/login'
|
|||||||
export const POST_LOGOUT = '/api/auth/logout'
|
export const POST_LOGOUT = '/api/auth/logout'
|
||||||
export const POST_REFRESH = '/api/auth/refresh'
|
export const POST_REFRESH = '/api/auth/refresh'
|
||||||
export const GET_USER_PROFILE = '/api/user/me'
|
export const GET_USER_PROFILE = '/api/user/me'
|
||||||
|
export const PUT_UPDATE_USER_PROFILE = '/api/user/update-profile'
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { protocol } from './index'
|
import { protocol } from './index'
|
||||||
import { GET_USER_PROFILE } from './url'
|
import { GET_USER_PROFILE, PUT_UPDATE_USER_PROFILE } from './url'
|
||||||
|
|
||||||
export const getProfile = () => {
|
export const getProfile = () => {
|
||||||
return protocol.get(GET_USER_PROFILE, {})
|
return protocol.get(GET_USER_PROFILE, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const putUpdateProfile = (payload) => {
|
||||||
|
return protocol.put(PUT_UPDATE_USER_PROFILE, payload)
|
||||||
|
}
|
||||||
|
39
frontend/src/components/common/TextInput/TextInput.jsx
Normal file
39
frontend/src/components/common/TextInput/TextInput.jsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Field } from 'solid-form-handler'
|
||||||
|
import { Show, splitProps } from 'solid-js'
|
||||||
|
import { Dynamic } from 'solid-js/web'
|
||||||
|
|
||||||
|
export default function Finput(props) {
|
||||||
|
const [local, rest] = splitProps(props, ['label', 'icon'])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Field
|
||||||
|
{...props}
|
||||||
|
mode="input"
|
||||||
|
render={(field) => (
|
||||||
|
<div class="form-control w-full [&:not(:last-child)]:pb-3">
|
||||||
|
<Show when={local.label}>
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text">{local.label}</span>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<label
|
||||||
|
class="input input-bordered flex items-center gap-2 w-full"
|
||||||
|
classList={{ 'input-error': field.helpers.error }}
|
||||||
|
>
|
||||||
|
<Show when={local.icon}>
|
||||||
|
<Dynamic component={local.icon} size={18} />
|
||||||
|
</Show>
|
||||||
|
<input {...rest} class="grow w-full" {...field.props} />
|
||||||
|
</label>
|
||||||
|
<Show when={field.helpers.error}>
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text-alt text-red-600">
|
||||||
|
{field.helpers.errorMessage}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
1
frontend/src/components/common/TextInput/index.js
Normal file
1
frontend/src/components/common/TextInput/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './TextInput'
|
@ -7,12 +7,7 @@ export default function useAuth(setAuth) {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const clickLogIn = async (username, password, cbFormReset) => {
|
const clickLogIn = async (username, password, cbFormReset) => {
|
||||||
const loginData = {
|
const resp = await postLogin({ username, password })
|
||||||
username: username,
|
|
||||||
password: password,
|
|
||||||
}
|
|
||||||
|
|
||||||
const resp = await postLogin(loginData)
|
|
||||||
|
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const token = resp.data || {}
|
const token = resp.data || {}
|
||||||
|
@ -6,7 +6,13 @@
|
|||||||
"logout": "Đăng xuất",
|
"logout": "Đăng xuất",
|
||||||
"dashboard": "Bảng điều khiển",
|
"dashboard": "Bảng điều khiển",
|
||||||
"profile": "Hồ sơ",
|
"profile": "Hồ sơ",
|
||||||
"houses": "Kho"
|
"changeInfo": "Đổi thông tin",
|
||||||
|
"save": "Lưu",
|
||||||
|
"clear": "Xóa",
|
||||||
|
"houses": "Kho",
|
||||||
|
"displayName": "Display Name",
|
||||||
|
"newPassword": "New Password",
|
||||||
|
"confirmNewPassword": "Confirm New Password"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"CREATED_USER": "Username already registered!",
|
"CREATED_USER": "Username already registered!",
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { useSiteContext } from '@context/SiteContext'
|
import { useSiteContext } from '@context/SiteContext'
|
||||||
import useLanguage from '@hooks/useLanguage'
|
import useLanguage from '@hooks/useLanguage'
|
||||||
import { useNavigate } from '@solidjs/router'
|
import { useNavigate } from '@solidjs/router'
|
||||||
import { Field, useFormHandler } from 'solid-form-handler'
|
import { IconKey, IconUser } from '@tabler/icons-solidjs'
|
||||||
|
import { useFormHandler } from 'solid-form-handler'
|
||||||
import { yupSchema } from 'solid-form-handler/yup'
|
import { yupSchema } from 'solid-form-handler/yup'
|
||||||
import { Show, onMount } from 'solid-js'
|
import { onMount } from 'solid-js'
|
||||||
import * as yup from 'yup'
|
import * as yup from 'yup'
|
||||||
import './login.scss'
|
import './login.scss'
|
||||||
|
|
||||||
import Logo from '@assets/logo.svg'
|
import Logo from '@assets/logo.svg'
|
||||||
|
import TextInput from '@components/common/TextInput'
|
||||||
import useAuth from '@hooks/useAuth'
|
import useAuth from '@hooks/useAuth'
|
||||||
import useToast from '@hooks/useToast'
|
import useToast from '@hooks/useToast'
|
||||||
|
|
||||||
@ -66,79 +68,18 @@ export default function Login() {
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h1 class="card-title">{language.ui.login}</h1>
|
<h1 class="card-title">{language.ui.login}</h1>
|
||||||
<form autoComplete="off" onSubmit={submit}>
|
<form autoComplete="off" onSubmit={submit}>
|
||||||
<Field
|
<TextInput
|
||||||
mode="input"
|
|
||||||
name="username"
|
name="username"
|
||||||
|
placeholder="Username"
|
||||||
|
icon={IconUser}
|
||||||
formHandler={formHandler}
|
formHandler={formHandler}
|
||||||
render={(field) => (
|
|
||||||
<label class="form-control w-full pb-5">
|
|
||||||
<label
|
|
||||||
class="input input-bordered flex items-center gap-2 w-full"
|
|
||||||
classList={{ 'input-error': field.helpers.error }}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="currentColor"
|
|
||||||
class="w-4 h-4 opacity-70"
|
|
||||||
>
|
|
||||||
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
|
|
||||||
</svg>
|
|
||||||
<input
|
|
||||||
id="username"
|
|
||||||
type="text"
|
|
||||||
class="grow w-full"
|
|
||||||
placeholder="Username"
|
|
||||||
{...field.props}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Show when={field.helpers.error}>
|
|
||||||
<div class="label">
|
|
||||||
<span class="label-text-alt text-red-600">
|
|
||||||
{field.helpers.errorMessage}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
<Field
|
<TextInput
|
||||||
mode="input"
|
|
||||||
name="password"
|
name="password"
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
icon={IconKey}
|
||||||
formHandler={formHandler}
|
formHandler={formHandler}
|
||||||
render={(field) => (
|
|
||||||
<label class="form-control w-full">
|
|
||||||
<label
|
|
||||||
class="input input-bordered flex items-center gap-2 w-full"
|
|
||||||
classList={{ 'input-error': field.helpers.error }}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="currentColor"
|
|
||||||
class="w-4 h-4 opacity-70"
|
|
||||||
>
|
|
||||||
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
|
|
||||||
</svg>
|
|
||||||
<input
|
|
||||||
id="password"
|
|
||||||
type="password"
|
|
||||||
class="grow w-full"
|
|
||||||
placeholder="Password"
|
|
||||||
{...field.props}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Show when={field.helpers.error}>
|
|
||||||
<div class="label">
|
|
||||||
<span class="label-text-alt text-red-600">
|
|
||||||
{field.helpers.errorMessage}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
<div class="card-actions justify-end mt-5">
|
<div class="card-actions justify-end mt-5">
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export default function Profile() {
|
|
||||||
return <>Profile</>
|
|
||||||
}
|
|
115
frontend/src/pages/Profile/Profile.jsx
Normal file
115
frontend/src/pages/Profile/Profile.jsx
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import { putUpdateProfile } from '@api/user'
|
||||||
|
import TextInput from '@components/common/TextInput'
|
||||||
|
import { useSiteContext } from '@context/SiteContext'
|
||||||
|
import useLanguage from '@hooks/useLanguage'
|
||||||
|
import useToast from '@hooks/useToast'
|
||||||
|
import { IconKey, IconLetterN, IconUserCircle } from '@tabler/icons-solidjs'
|
||||||
|
import { Helpers } from '@utils/helper'
|
||||||
|
import { useFormHandler } from 'solid-form-handler'
|
||||||
|
import { yupSchema } from 'solid-form-handler/yup'
|
||||||
|
import { createEffect } from 'solid-js'
|
||||||
|
import * as yup from 'yup'
|
||||||
|
|
||||||
|
const profileSchema = yup.object({
|
||||||
|
name: yup.string().required('Name is required'),
|
||||||
|
password: yup.string().nullable().optional(),
|
||||||
|
'confirm-password': yup.string().when('password', {
|
||||||
|
is: (val) => !!(val && val.length > 0),
|
||||||
|
then: (schema) =>
|
||||||
|
schema.oneOf([yup.ref('password'), null], 'Passwords must match'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const language = useLanguage()
|
||||||
|
const notify = useToast()
|
||||||
|
|
||||||
|
export default function Profile() {
|
||||||
|
const {
|
||||||
|
store: { userInfo },
|
||||||
|
setUser,
|
||||||
|
} = useSiteContext()
|
||||||
|
const formHandler = useFormHandler(yupSchema(profileSchema))
|
||||||
|
const { formData } = formHandler
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
formHandler.fillForm({
|
||||||
|
name: userInfo?.name,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const submit = async (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
await formHandler.validateForm()
|
||||||
|
try {
|
||||||
|
const { name, password } = formData()
|
||||||
|
const clearObj = Helpers.clearObject({
|
||||||
|
name: name || null,
|
||||||
|
password: password || null,
|
||||||
|
})
|
||||||
|
const resp = await putUpdateProfile(clearObj)
|
||||||
|
|
||||||
|
if (resp.status === 200) {
|
||||||
|
setUser(resp.data)
|
||||||
|
formHandler.setFieldValue('password', '')
|
||||||
|
formHandler.setFieldValue('confirm-password', '')
|
||||||
|
notify.success({
|
||||||
|
title: 'Update profile success!',
|
||||||
|
description: 'Your profile has been updated!',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notify.error({
|
||||||
|
title: 'Update profile fail!',
|
||||||
|
description: error?.data
|
||||||
|
? language.message[error.data]
|
||||||
|
: 'Your username or password input is wrong!',
|
||||||
|
closable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="profile">
|
||||||
|
<div class="divider divider-accent text-xl">
|
||||||
|
<span class="text-green-400">
|
||||||
|
<IconUserCircle size={30} />
|
||||||
|
</span>
|
||||||
|
{language.ui.profile}
|
||||||
|
</div>
|
||||||
|
<div class="card w-full bg-base-100 shadow-lg border border-gray-200">
|
||||||
|
<div class="card-body">
|
||||||
|
<form autoComplete="off" onSubmit={submit}>
|
||||||
|
<p class="card-title">{language.ui.changeInfo}</p>
|
||||||
|
<div class="form-content py-5">
|
||||||
|
<TextInput
|
||||||
|
icon={IconLetterN}
|
||||||
|
name="name"
|
||||||
|
placeholder={language.ui.displayName}
|
||||||
|
formHandler={formHandler}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
icon={IconKey}
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
placeholder={language.ui.newPassword}
|
||||||
|
formHandler={formHandler}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
icon={IconKey}
|
||||||
|
name="confirm-password"
|
||||||
|
type="password"
|
||||||
|
placeholder={language.ui.confirmNewPassword}
|
||||||
|
formHandler={formHandler}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
{language.ui.save}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
frontend/src/pages/Profile/index.js
Normal file
1
frontend/src/pages/Profile/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './Profile'
|
@ -47,4 +47,13 @@ export class Helpers {
|
|||||||
? JSON.parse(AES.decrypt(hash, SECRET_KEY).toString(enc.Utf8))
|
? JSON.parse(AES.decrypt(hash, SECRET_KEY).toString(enc.Utf8))
|
||||||
: defaultValue
|
: defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static clearObject = (object) => {
|
||||||
|
for (var propName in object) {
|
||||||
|
if (object[propName] === null || object[propName] === undefined) {
|
||||||
|
delete object[propName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return object
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user