[FWA-3] completed update Profile

This commit is contained in:
2024-06-11 12:28:02 +00:00
parent 0ea7ba1a39
commit 3fed0650c9
17 changed files with 246 additions and 105 deletions

View File

@ -2,3 +2,4 @@ export const POST_LOGIN = '/api/auth/login'
export const POST_LOGOUT = '/api/auth/logout'
export const POST_REFRESH = '/api/auth/refresh'
export const GET_USER_PROFILE = '/api/user/me'
export const PUT_UPDATE_USER_PROFILE = '/api/user/update-profile'

View File

@ -1,6 +1,10 @@
import { protocol } from './index'
import { GET_USER_PROFILE } from './url'
import { GET_USER_PROFILE, PUT_UPDATE_USER_PROFILE } from './url'
export const getProfile = () => {
return protocol.get(GET_USER_PROFILE, {})
}
export const putUpdateProfile = (payload) => {
return protocol.put(PUT_UPDATE_USER_PROFILE, payload)
}

View 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>
)}
/>
)
}

View File

@ -0,0 +1 @@
export { default } from './TextInput'

View File

@ -7,12 +7,7 @@ export default function useAuth(setAuth) {
const navigate = useNavigate()
const clickLogIn = async (username, password, cbFormReset) => {
const loginData = {
username: username,
password: password,
}
const resp = await postLogin(loginData)
const resp = await postLogin({ username, password })
if (resp.status === 200) {
const token = resp.data || {}

View File

@ -6,7 +6,13 @@
"logout": "Đăng xuất",
"dashboard": "Bảng điều khiển",
"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": {
"CREATED_USER": "Username already registered!",

View File

@ -1,13 +1,15 @@
import { useSiteContext } from '@context/SiteContext'
import useLanguage from '@hooks/useLanguage'
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 { Show, onMount } from 'solid-js'
import { onMount } from 'solid-js'
import * as yup from 'yup'
import './login.scss'
import Logo from '@assets/logo.svg'
import TextInput from '@components/common/TextInput'
import useAuth from '@hooks/useAuth'
import useToast from '@hooks/useToast'
@ -66,79 +68,18 @@ export default function Login() {
<div class="card-body">
<h1 class="card-title">{language.ui.login}</h1>
<form autoComplete="off" onSubmit={submit}>
<Field
mode="input"
<TextInput
name="username"
placeholder="Username"
icon={IconUser}
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
mode="input"
<TextInput
name="password"
type="password"
placeholder="Password"
icon={IconKey}
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">
<button type="submit" class="btn btn-primary">

View File

@ -1,3 +0,0 @@
export default function Profile() {
return <>Profile</>
}

View File

@ -0,0 +1,116 @@
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,
},
{ silentValidation: false },
)
})
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)
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>
)
}

View File

@ -0,0 +1 @@
export { default } from './Profile'

View File

@ -47,4 +47,13 @@ export class Helpers {
? JSON.parse(AES.decrypt(hash, SECRET_KEY).toString(enc.Utf8))
: defaultValue
}
static clearObject = (object) => {
for (var propName in object) {
if (object[propName] === null || object[propName] === undefined) {
delete object[propName]
}
}
return object
}
}