Bổ sung chức năng cập nhật hình ảnh cho Profiles
This commit is contained in:
parent
2941663e7b
commit
80bc1de1a3
|
|
@ -3,12 +3,14 @@
|
|||
namespace Modules\Admin\app\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Services\JiraService;
|
||||
use App\Traits\AnalyzeData;
|
||||
use App\Traits\HasFilterRequest;
|
||||
use App\Traits\HasOrderByRequest;
|
||||
use App\Traits\HasSearchRequest;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Admin\app\Models\Sprint;
|
||||
use Modules\Admin\app\Models\UserCriteria;
|
||||
|
||||
|
|
@ -72,4 +74,23 @@ class ProfileController extends Controller
|
|||
});
|
||||
return array_values($filteredProjects) ? array_values($filteredProjects)[0] : array_values($filteredProjects);
|
||||
}
|
||||
|
||||
public function updateProfilesData(Request $request)
|
||||
{
|
||||
$userInfo = auth('admins')->user();
|
||||
$request->validate([
|
||||
'file' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048', // Chỉ chấp nhận file ảnh dưới 2MB
|
||||
]);
|
||||
|
||||
$user = User::findOrFail($userInfo->id);
|
||||
|
||||
if ($user->avatar) {
|
||||
Storage::disk('public')->delete($user->avatar);
|
||||
}
|
||||
|
||||
$path = $request->file('file')->store('avatars', 'public');
|
||||
$user->avatar = $path;
|
||||
$user->save();
|
||||
return AbstractController::ResultSuccess($path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,3 +73,4 @@ export const createTestCase = API_URL + 'v1/admin/criterias/test-cases'
|
|||
|
||||
//Profile
|
||||
export const getProfilesData = API_URL + 'v1/admin/criterias/profiles-data'
|
||||
export const updateProfilesData = API_URL + 'v1/admin/criterias/profiles-data/update'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { logout } from '@/rtk/dispatches/auth'
|
|||
import { get, post } from '@/rtk/helpers/apiService'
|
||||
import { requirementsPassword } from '@/rtk/helpers/variables'
|
||||
import { useAppDispatch, useAppSelector } from '@/rtk/hooks'
|
||||
import { getUser } from '@/rtk/localStorage'
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
|
|
@ -130,7 +131,6 @@ const Navbar = ({
|
|||
}: TProps) => {
|
||||
const user = useAppSelector((state) => state.authentication.user)
|
||||
const [active, setActive] = useState<string>('')
|
||||
|
||||
//state from dashboard
|
||||
const location = useLocation()
|
||||
useEffect(() => {
|
||||
|
|
@ -151,6 +151,8 @@ const Navbar = ({
|
|||
confirm_password: '',
|
||||
})
|
||||
const [countSpam, setCountSpam] = useState(0)
|
||||
const [avatar, setAvatar] = useState(user.user.avatar)
|
||||
|
||||
const navigate = useNavigate()
|
||||
const dispatch = useAppDispatch()
|
||||
const passwordRegex =
|
||||
|
|
@ -161,6 +163,10 @@ const Navbar = ({
|
|||
getInitialValueInEffect: true,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setAvatar(user.user.avatar)
|
||||
}, [user])
|
||||
|
||||
// const links = data.map((item) => {
|
||||
// if (
|
||||
// user?.user?.permission !== 'admin' &&
|
||||
|
|
@ -362,7 +368,11 @@ const Navbar = ({
|
|||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={user.user.avatarUrl || ''}
|
||||
src={
|
||||
avatar !== ''
|
||||
? import.meta.env.VITE_BACKEND_URL + 'storage/' + avatar
|
||||
: ''
|
||||
}
|
||||
alt="User Avatar"
|
||||
size={60}
|
||||
radius="xl"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { getProfilesData } from '@/api/Admin'
|
||||
import { getProfilesData, updateProfilesData } from '@/api/Admin'
|
||||
import { changePassword } from '@/api/Auth'
|
||||
import PasswordRequirementInput from '@/components/PasswordRequirementInput/PasswordRequirementInput'
|
||||
import { logout } from '@/rtk/dispatches/auth'
|
||||
import { get, post } from '@/rtk/helpers/apiService'
|
||||
import { get, post, postImage } from '@/rtk/helpers/apiService'
|
||||
import { requirementsPassword } from '@/rtk/helpers/variables'
|
||||
import { useAppDispatch, useAppSelector } from '@/rtk/hooks'
|
||||
import { getUser } from '@/rtk/localStorage'
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
|
|
@ -21,6 +22,7 @@ import { IconCornerDownRight, IconPasswordUser } from '@tabler/icons-react'
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import classes from './Profile.module.css'
|
||||
import { success } from '@/rtk/slices/auth'
|
||||
|
||||
interface TableRow {
|
||||
// Add properties for each column in the table
|
||||
|
|
@ -70,22 +72,65 @@ const CriteriaTable = ({ data }: { data: TableRow[] }) => (
|
|||
const isCompactMenu = false
|
||||
const Profile = () => {
|
||||
const user = useAppSelector((state) => state.authentication.user)
|
||||
const userData = getUser()
|
||||
|
||||
const [opened, setOpened] = useState(false)
|
||||
const [dataChange, setDataChange] = useState({
|
||||
password: '',
|
||||
new_password: '',
|
||||
confirm_password: '',
|
||||
})
|
||||
const [avatar, setAvatar] = useState(user.user.avatar)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [dataProfile, setDataProfile] = useState<any>([])
|
||||
|
||||
// const [projects, setProjects] = useState(projectsData)
|
||||
const [expandedProjects, setExpandedProjects] = useState<ExpandedProjects>({})
|
||||
const [expandedSprints, setExpandedSprints] = useState<ExpandedSprints>({})
|
||||
|
||||
const [countSpam, setCountSpam] = useState(0)
|
||||
|
||||
const [selectedAvatar, setSelectedAvatar] = useState<string | null>(null)
|
||||
const navigate = useNavigate()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const updateAvatar = async (file: File) => {
|
||||
try {
|
||||
const res = await postImage(updateProfilesData, file, 'post')
|
||||
if (res.status) {
|
||||
return res.data
|
||||
}
|
||||
} catch (error: any) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message ?? error,
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
const handleAvatarChange = (event: any) => {
|
||||
const file = event.target.files[0]
|
||||
if (file) {
|
||||
setSelectedAvatar(URL.createObjectURL(file)) // Hiển thị ảnh mới đã chọn tạm thời
|
||||
const fetchData = async () => {
|
||||
const result = await updateAvatar(file)
|
||||
notifications.show({
|
||||
title: 'Success',
|
||||
message: 'Update avatar success',
|
||||
color: 'green',
|
||||
})
|
||||
if (userData) {
|
||||
let userObj = JSON.parse(userData)
|
||||
userObj.user.avatar = result
|
||||
let updatedUserData = JSON.stringify(userObj)
|
||||
localStorage.setItem('user', updatedUserData)
|
||||
dispatch(success({ user: userObj }))
|
||||
}
|
||||
//Update avatar
|
||||
setAvatar(result ?? [])
|
||||
}
|
||||
fetchData()
|
||||
}
|
||||
}
|
||||
const passwordRegex =
|
||||
/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
|
||||
|
||||
|
|
@ -222,16 +267,31 @@ const Profile = () => {
|
|||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
style={{ display: 'none' }}
|
||||
id="avatar-input"
|
||||
onChange={handleAvatarChange}
|
||||
/>
|
||||
<Avatar
|
||||
src={null || ''}
|
||||
src={
|
||||
selectedAvatar
|
||||
? selectedAvatar
|
||||
: avatar !== ''
|
||||
? import.meta.env.VITE_BACKEND_URL + 'storage/' + avatar
|
||||
: ''
|
||||
}
|
||||
alt="User Avatar"
|
||||
size={150}
|
||||
// radius="xl"
|
||||
mb={5}
|
||||
mt={5}
|
||||
onClick={() => {
|
||||
console.log('update avatar')
|
||||
}}
|
||||
onClick={() =>
|
||||
(
|
||||
document.getElementById('avatar-input') as HTMLElement
|
||||
).click()
|
||||
}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
// border: '2px solid black',
|
||||
|
|
|
|||
|
|
@ -105,13 +105,18 @@ export const deleteApi = async (url: any) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const postImage = async (url: string, body: any) => {
|
||||
export const postImage = async (url: string, body: any, method: any) => {
|
||||
const header = await getFormDataHeader()
|
||||
const formData = new FormData()
|
||||
formData.append('file', body)
|
||||
try {
|
||||
if (method === 'post') {
|
||||
const resp = await axios.post(url, formData, header)
|
||||
return handleResponse(resp)
|
||||
} else {
|
||||
const resp = await axios.put(url, formData, header)
|
||||
return handleResponse(resp)
|
||||
}
|
||||
} catch (err: any) {
|
||||
throw handleResponse(err.response)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue