✏️ 메인 대시보드에 프로젝트 이름 수정 기능 추가
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 LogMonitoringPage from './pages/LogMonitoringPage';
|
||||||
import ErrorBoundary from './components/ErrorBoundary';
|
import ErrorBoundary from './components/ErrorBoundary';
|
||||||
import errorLogger from './utils/errorLogger';
|
import errorLogger from './utils/errorLogger';
|
||||||
|
import api from './api';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@@ -19,6 +20,48 @@ function App() {
|
|||||||
const [pageParams, setPageParams] = useState({});
|
const [pageParams, setPageParams] = useState({});
|
||||||
const [selectedProject, setSelectedProject] = useState(null);
|
const [selectedProject, setSelectedProject] = useState(null);
|
||||||
const [showUserMenu, setShowUserMenu] = useState(false);
|
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(() => {
|
useEffect(() => {
|
||||||
// 저장된 토큰 확인
|
// 저장된 토큰 확인
|
||||||
@@ -28,6 +71,7 @@ function App() {
|
|||||||
if (token && userData) {
|
if (token && userData) {
|
||||||
setIsAuthenticated(true);
|
setIsAuthenticated(true);
|
||||||
setUser(JSON.parse(userData));
|
setUser(JSON.parse(userData));
|
||||||
|
loadProjects(); // 프로젝트 목록 로드
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@@ -313,15 +357,43 @@ function App() {
|
|||||||
|
|
||||||
{/* 프로젝트 선택 */}
|
{/* 프로젝트 선택 */}
|
||||||
<div style={{ marginBottom: '32px' }}>
|
<div style={{ marginBottom: '32px' }}>
|
||||||
<h2 style={{ fontSize: '18px', fontWeight: '600', color: '#2d3748', marginBottom: '12px' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
|
||||||
📁 프로젝트 선택
|
<h2 style={{ fontSize: '18px', fontWeight: '600', color: '#2d3748', margin: 0 }}>
|
||||||
</h2>
|
📁 프로젝트 선택
|
||||||
|
</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
|
<select
|
||||||
value={selectedProject?.official_project_code || ''}
|
value={selectedProject?.official_project_code || ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const projectCode = e.target.value;
|
const projectCode = e.target.value;
|
||||||
if (projectCode) {
|
if (projectCode) {
|
||||||
setSelectedProject({
|
const project = projects.find(p => p.official_project_code === projectCode);
|
||||||
|
setSelectedProject(project || {
|
||||||
official_project_code: projectCode,
|
official_project_code: projectCode,
|
||||||
project_name: e.target.options[e.target.selectedIndex].text.split(' - ')[1]
|
project_name: e.target.options[e.target.selectedIndex].text.split(' - ')[1]
|
||||||
});
|
});
|
||||||
@@ -339,10 +411,87 @@ function App() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="">프로젝트를 선택하세요</option>
|
<option value="">프로젝트를 선택하세요</option>
|
||||||
<option value="J24-001">J24-001 - 테스트 프로젝트 A</option>
|
{projects.length > 0 ? (
|
||||||
<option value="J24-002">J24-002 - 테스트 프로젝트 B</option>
|
projects.map(project => (
|
||||||
<option value="J24-003">J24-003 - 테스트 프로젝트 C</option>
|
<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>
|
</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>
|
</div>
|
||||||
|
|
||||||
{/* 핵심 기능 */}
|
{/* 핵심 기능 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user