✏️ 메인 대시보드에 프로젝트 이름 수정 기능 추가
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- App.jsx dashboard에 프로젝트 이름 수정 기능 구현 - 프로젝트 선택 시 '✏️ 이름 수정' 버튼 표시 - 실제 API에서 프로젝트 목록 로드 (더미 데이터는 fallback) - 편집 폼: 파란색 박스로 명확하게 구분 - Enter 키로 빠른 저장 - 💾 저장 / ✕ 취소 버튼 - 저장 후 드롭다운 자동 갱신
This commit is contained in:
@@ -9,6 +9,7 @@ import SystemLogsPage from './pages/SystemLogsPage';
|
||||
import LogMonitoringPage from './pages/LogMonitoringPage';
|
||||
import ErrorBoundary from './components/ErrorBoundary';
|
||||
import errorLogger from './utils/errorLogger';
|
||||
import api from './api';
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
@@ -19,6 +20,48 @@ function App() {
|
||||
const [pageParams, setPageParams] = useState({});
|
||||
const [selectedProject, setSelectedProject] = useState(null);
|
||||
const [showUserMenu, setShowUserMenu] = useState(false);
|
||||
const [projects, setProjects] = useState([]);
|
||||
const [editingProject, setEditingProject] = useState(null);
|
||||
const [editedProjectName, setEditedProjectName] = useState('');
|
||||
|
||||
// 프로젝트 목록 로드
|
||||
const loadProjects = async () => {
|
||||
try {
|
||||
const response = await api.get('/projects');
|
||||
if (response.data && response.data.projects) {
|
||||
setProjects(response.data.projects);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('프로젝트 목록 로드 실패:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 프로젝트 이름 수정
|
||||
const updateProjectName = async (projectId) => {
|
||||
try {
|
||||
const response = await api.patch(`/dashboard/projects/${projectId}?job_name=${encodeURIComponent(editedProjectName)}`);
|
||||
|
||||
if (response.data.success) {
|
||||
// 프로젝트 목록 갱신
|
||||
await loadProjects();
|
||||
|
||||
// 선택된 프로젝트 업데이트
|
||||
if (selectedProject && selectedProject.id === projectId) {
|
||||
setSelectedProject({
|
||||
...selectedProject,
|
||||
project_name: editedProjectName
|
||||
});
|
||||
}
|
||||
|
||||
setEditingProject(null);
|
||||
setEditedProjectName('');
|
||||
alert('프로젝트 이름이 수정되었습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('프로젝트 이름 수정 실패:', error);
|
||||
alert('프로젝트 이름 수정에 실패했습니다.');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 저장된 토큰 확인
|
||||
@@ -28,6 +71,7 @@ function App() {
|
||||
if (token && userData) {
|
||||
setIsAuthenticated(true);
|
||||
setUser(JSON.parse(userData));
|
||||
loadProjects(); // 프로젝트 목록 로드
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
@@ -313,15 +357,43 @@ function App() {
|
||||
|
||||
{/* 프로젝트 선택 */}
|
||||
<div style={{ marginBottom: '32px' }}>
|
||||
<h2 style={{ fontSize: '18px', fontWeight: '600', color: '#2d3748', marginBottom: '12px' }}>
|
||||
📁 프로젝트 선택
|
||||
</h2>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
|
||||
<h2 style={{ fontSize: '18px', fontWeight: '600', color: '#2d3748', margin: 0 }}>
|
||||
📁 프로젝트 선택
|
||||
</h2>
|
||||
{selectedProject && (
|
||||
<button
|
||||
onClick={() => {
|
||||
setEditingProject(selectedProject);
|
||||
setEditedProjectName(selectedProject.project_name || '');
|
||||
}}
|
||||
style={{
|
||||
padding: '6px 12px',
|
||||
background: '#10b981',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '13px',
|
||||
fontWeight: '600',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px'
|
||||
}}
|
||||
title="프로젝트 이름 수정"
|
||||
>
|
||||
✏️ 이름 수정
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<select
|
||||
value={selectedProject?.official_project_code || ''}
|
||||
onChange={(e) => {
|
||||
const projectCode = e.target.value;
|
||||
if (projectCode) {
|
||||
setSelectedProject({
|
||||
const project = projects.find(p => p.official_project_code === projectCode);
|
||||
setSelectedProject(project || {
|
||||
official_project_code: projectCode,
|
||||
project_name: e.target.options[e.target.selectedIndex].text.split(' - ')[1]
|
||||
});
|
||||
@@ -339,10 +411,87 @@ function App() {
|
||||
}}
|
||||
>
|
||||
<option value="">프로젝트를 선택하세요</option>
|
||||
<option value="J24-001">J24-001 - 테스트 프로젝트 A</option>
|
||||
<option value="J24-002">J24-002 - 테스트 프로젝트 B</option>
|
||||
<option value="J24-003">J24-003 - 테스트 프로젝트 C</option>
|
||||
{projects.length > 0 ? (
|
||||
projects.map(project => (
|
||||
<option key={project.id || project.official_project_code} value={project.official_project_code}>
|
||||
{project.official_project_code} - {project.project_name || project.job_name}
|
||||
</option>
|
||||
))
|
||||
) : (
|
||||
<>
|
||||
<option value="J24-001">J24-001 - 테스트 프로젝트 A</option>
|
||||
<option value="J24-002">J24-002 - 테스트 프로젝트 B</option>
|
||||
<option value="J24-003">J24-003 - 테스트 프로젝트 C</option>
|
||||
</>
|
||||
)}
|
||||
</select>
|
||||
|
||||
{/* 프로젝트 이름 편집 폼 */}
|
||||
{editingProject && (
|
||||
<div style={{
|
||||
marginTop: '16px',
|
||||
background: '#eff6ff',
|
||||
border: '2px solid #3b82f6',
|
||||
borderRadius: '8px',
|
||||
padding: '16px'
|
||||
}}>
|
||||
<div style={{ marginBottom: '12px', fontWeight: '600', color: '#1e40af', fontSize: '14px' }}>
|
||||
프로젝트 이름 수정: {editingProject.official_project_code}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<input
|
||||
type="text"
|
||||
value={editedProjectName}
|
||||
onChange={(e) => setEditedProjectName(e.target.value)}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') updateProjectName(editingProject.id);
|
||||
}}
|
||||
placeholder="새 프로젝트 이름"
|
||||
autoFocus
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: '10px 12px',
|
||||
border: '2px solid #3b82f6',
|
||||
borderRadius: '6px',
|
||||
fontSize: '14px'
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
onClick={() => updateProjectName(editingProject.id)}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
background: '#10b981',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
fontWeight: '600'
|
||||
}}
|
||||
>
|
||||
💾 저장
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setEditingProject(null);
|
||||
setEditedProjectName('');
|
||||
}}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
background: '#ef4444',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
fontWeight: '600'
|
||||
}}
|
||||
>
|
||||
✕ 취소
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 핵심 기능 */}
|
||||
|
||||
Reference in New Issue
Block a user