feat: profile image upload in accounts page
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Users, Plus, Pencil, Trash2, X, Search, AlertTriangle } from 'lucide-react';
|
import { Users, Plus, Pencil, Trash2, X, Search, AlertTriangle, Camera, Loader2 } from 'lucide-react';
|
||||||
import { useAuthStore } from '@/stores';
|
import { useAuthStore } from '@/stores';
|
||||||
import employeeService from '@/services/employeeService';
|
import employeeService from '@/services/employeeService';
|
||||||
import type { Employee, RoleCode, EmpStatus } from '@/types';
|
import type { Employee, RoleCode, EmpStatus } from '@/types';
|
||||||
@@ -273,9 +273,13 @@ export default function AccountsPage() {
|
|||||||
<tr key={emp.empId} className="hover:bg-gray-50/30 transition-colors">
|
<tr key={emp.empId} className="hover:bg-gray-50/30 transition-colors">
|
||||||
<td className="px-4 sm:px-6 py-3">
|
<td className="px-4 sm:px-6 py-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
{emp.profileImage ? (
|
||||||
|
<img src={emp.profileImage} alt={emp.name} className="w-8 h-8 rounded-full object-cover border border-border flex-shrink-0" />
|
||||||
|
) : (
|
||||||
<div className={`w-8 h-8 rounded-full ${getInitialColor(emp.name, idx)} flex items-center justify-center text-xs font-medium text-white flex-shrink-0`}>
|
<div className={`w-8 h-8 rounded-full ${getInitialColor(emp.name, idx)} flex items-center justify-center text-xs font-medium text-white flex-shrink-0`}>
|
||||||
{emp.name[0]}
|
{emp.name[0]}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<span className="font-medium text-text-main">{emp.name}</span>
|
<span className="font-medium text-text-main">{emp.name}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -495,6 +499,46 @@ export default function AccountsPage() {
|
|||||||
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-3">추가 정보</p>
|
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-3">추가 정보</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Profile Image */}
|
||||||
|
<div>
|
||||||
|
<label className={labelClass}>프로필 사진</label>
|
||||||
|
<div className="flex items-center gap-3 mt-1">
|
||||||
|
<div className="w-14 h-14 rounded-full bg-surface-container flex items-center justify-center overflow-hidden border border-border flex-shrink-0">
|
||||||
|
{targetEmployee.profileImage ? (
|
||||||
|
<img src={targetEmployee.profileImage} alt="" className="w-full h-full object-cover" />
|
||||||
|
) : (
|
||||||
|
<span className="text-lg font-bold text-text-muted">{targetEmployee.name[0]}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<label className="flex items-center gap-1.5 px-3 py-2 rounded-xl border border-border text-xs font-medium text-text-muted hover:bg-gray-50 cursor-pointer transition-colors">
|
||||||
|
<Camera className="w-3.5 h-3.5" /> 업로드
|
||||||
|
<input type="file" accept="image/*" className="hidden" onChange={async (e) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('file', file);
|
||||||
|
try {
|
||||||
|
const uploadRes = await fetch('/api/v1/files/upload', { method: 'POST', body: form });
|
||||||
|
const uploadData = await uploadRes.json();
|
||||||
|
if (uploadData.success) {
|
||||||
|
await employeeService.updateProfileImage(targetEmployee.empId, uploadData.data);
|
||||||
|
await fetchEmployees();
|
||||||
|
const emp = (await employeeService.getOne(targetEmployee.empId)).data;
|
||||||
|
setTargetEmployee(emp);
|
||||||
|
}
|
||||||
|
} catch { alert('업로드 실패'); }
|
||||||
|
}} />
|
||||||
|
</label>
|
||||||
|
{targetEmployee.profileImage && (
|
||||||
|
<button type="button" onClick={async () => {
|
||||||
|
await employeeService.updateProfileImage(targetEmployee.empId, '');
|
||||||
|
setTargetEmployee({ ...targetEmployee, profileImage: '' });
|
||||||
|
await fetchEmployees();
|
||||||
|
}} className="text-xs text-status-critical-text hover:underline">삭제</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className={labelClass}>생년월일</label>
|
<label className={labelClass}>생년월일</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user