Bổ sung chức năng cập nhật hình ảnh cho Profiles

This commit is contained in:
Truong Vo 2024-09-19 10:39:44 +07:00
parent 2941663e7b
commit 80bc1de1a3
5 changed files with 111 additions and 14 deletions

View File

@ -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);
}
}

View File

@ -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'

View File

@ -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"

View File

@ -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',

View File

@ -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 {
const resp = await axios.put(url, formData, header)
return handleResponse(resp)
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)
}