refactor(tkeg): 대시보드 프로젝트 생성 기능 제거 (tkuser로 통합)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -427,71 +427,6 @@ async def get_quick_actions(
|
|||||||
raise HTTPException(status_code=500, detail=f"빠른 작업 조회 실패: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"빠른 작업 조회 실패: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/projects")
|
|
||||||
async def create_project(
|
|
||||||
official_project_code: str = Query(..., description="프로젝트 코드"),
|
|
||||||
project_name: str = Query(..., description="프로젝트 이름"),
|
|
||||||
client_name: str = Query(None, description="고객사명"),
|
|
||||||
current_user: dict = Depends(get_current_user),
|
|
||||||
db: Session = Depends(get_db)
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
새 프로젝트 생성
|
|
||||||
|
|
||||||
Args:
|
|
||||||
official_project_code: 프로젝트 코드 (예: J24-001)
|
|
||||||
project_name: 프로젝트 이름
|
|
||||||
client_name: 고객사명 (선택)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: 생성된 프로젝트 정보
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# 중복 확인
|
|
||||||
check_query = text("SELECT id FROM projects WHERE official_project_code = :code")
|
|
||||||
existing = db.execute(check_query, {"code": official_project_code}).fetchone()
|
|
||||||
|
|
||||||
if existing:
|
|
||||||
raise HTTPException(status_code=400, detail="이미 존재하는 프로젝트 코드입니다")
|
|
||||||
|
|
||||||
# 프로젝트 생성
|
|
||||||
insert_query = text("""
|
|
||||||
INSERT INTO projects (official_project_code, project_name, client_name, status)
|
|
||||||
VALUES (:code, :name, :client, 'active')
|
|
||||||
RETURNING *
|
|
||||||
""")
|
|
||||||
|
|
||||||
new_project = db.execute(insert_query, {
|
|
||||||
"code": official_project_code,
|
|
||||||
"name": project_name,
|
|
||||||
"client": client_name
|
|
||||||
}).fetchone()
|
|
||||||
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
# TODO: 활동 로그 기록 (추후 구현)
|
|
||||||
# ActivityLogger 사용법 확인 필요
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"message": "프로젝트가 생성되었습니다",
|
|
||||||
"project": {
|
|
||||||
"id": new_project.id,
|
|
||||||
"official_project_code": new_project.official_project_code,
|
|
||||||
"project_name": new_project.project_name,
|
|
||||||
"client_name": new_project.client_name,
|
|
||||||
"status": new_project.status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
except HTTPException:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
db.rollback()
|
|
||||||
logger.error(f"프로젝트 생성 실패: {str(e)}")
|
|
||||||
raise HTTPException(status_code=500, detail=f"프로젝트 생성 실패: {str(e)}")
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/projects")
|
@router.get("/projects")
|
||||||
async def get_projects(
|
async def get_projects(
|
||||||
current_user: dict = Depends(get_current_user),
|
current_user: dict = Depends(get_current_user),
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import Dialog from '@mui/material/Dialog';
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
|
||||||
import DialogActions from '@mui/material/DialogActions';
|
|
||||||
import TextField from '@mui/material/TextField';
|
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import api from '../../api';
|
|
||||||
|
|
||||||
export default function CreateProjectDialog({ open, onClose, onCreated }) {
|
|
||||||
const [code, setCode] = useState('');
|
|
||||||
const [name, setName] = useState('');
|
|
||||||
const [client, setClient] = useState('');
|
|
||||||
const [submitting, setSubmitting] = useState(false);
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
if (!code.trim() || !name.trim()) return;
|
|
||||||
setSubmitting(true);
|
|
||||||
try {
|
|
||||||
await api.post('/dashboard/projects', null, {
|
|
||||||
params: {
|
|
||||||
official_project_code: code.trim(),
|
|
||||||
project_name: name.trim(),
|
|
||||||
client_name: client.trim() || undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setCode('');
|
|
||||||
setName('');
|
|
||||||
setClient('');
|
|
||||||
onCreated?.();
|
|
||||||
onClose();
|
|
||||||
} catch (err) {
|
|
||||||
alert(err.response?.data?.detail || '프로젝트 생성 실패');
|
|
||||||
} finally {
|
|
||||||
setSubmitting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
|
|
||||||
<DialogTitle sx={{ fontWeight: 700 }}>프로젝트 생성</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, pt: 1 }}>
|
|
||||||
<TextField
|
|
||||||
label="프로젝트 코드"
|
|
||||||
placeholder="예: J24-001"
|
|
||||||
value={code}
|
|
||||||
onChange={e => setCode(e.target.value)}
|
|
||||||
size="small"
|
|
||||||
required
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
label="프로젝트명"
|
|
||||||
placeholder="예: 울산 SK에너지 확장"
|
|
||||||
value={name}
|
|
||||||
onChange={e => setName(e.target.value)}
|
|
||||||
size="small"
|
|
||||||
required
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
label="고객사 (선택)"
|
|
||||||
placeholder="예: Samsung Engineering"
|
|
||||||
value={client}
|
|
||||||
onChange={e => setClient(e.target.value)}
|
|
||||||
size="small"
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions sx={{ px: 3, pb: 2 }}>
|
|
||||||
<Button onClick={onClose} color="inherit">취소</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={handleSubmit}
|
|
||||||
disabled={submitting || !code.trim() || !name.trim()}
|
|
||||||
>
|
|
||||||
생성
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,6 @@ import ProjectSelectorBar from './ProjectSelectorBar';
|
|||||||
import MetricCards from './MetricCards';
|
import MetricCards from './MetricCards';
|
||||||
import QuickActionCards from './QuickActionCards';
|
import QuickActionCards from './QuickActionCards';
|
||||||
import AdminSection from './AdminSection';
|
import AdminSection from './AdminSection';
|
||||||
import CreateProjectDialog from './CreateProjectDialog';
|
|
||||||
import useDashboardData from './useDashboardData';
|
import useDashboardData from './useDashboardData';
|
||||||
|
|
||||||
export default function DashboardPage({
|
export default function DashboardPage({
|
||||||
@@ -22,7 +21,6 @@ export default function DashboardPage({
|
|||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
const [selectedProject, setSelectedProject] = useState(null);
|
const [selectedProject, setSelectedProject] = useState(null);
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
|
||||||
const { loading, getMetrics } = useDashboardData(user, projects, selectedProject);
|
const { loading, getMetrics } = useDashboardData(user, projects, selectedProject);
|
||||||
|
|
||||||
const roleLabelMap = {
|
const roleLabelMap = {
|
||||||
@@ -49,7 +47,6 @@ export default function DashboardPage({
|
|||||||
projects={projects}
|
projects={projects}
|
||||||
selectedProject={selectedProject}
|
selectedProject={selectedProject}
|
||||||
onSelectProject={setSelectedProject}
|
onSelectProject={setSelectedProject}
|
||||||
onCreateProject={() => setDialogOpen(true)}
|
|
||||||
inactiveProjects={inactiveProjects}
|
inactiveProjects={inactiveProjects}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -72,13 +69,6 @@ export default function DashboardPage({
|
|||||||
|
|
||||||
{/* Admin section */}
|
{/* Admin section */}
|
||||||
<AdminSection user={user} navigateToPage={navigateToPage} />
|
<AdminSection user={user} navigateToPage={navigateToPage} />
|
||||||
|
|
||||||
{/* Create project dialog */}
|
|
||||||
<CreateProjectDialog
|
|
||||||
open={dialogOpen}
|
|
||||||
onClose={() => setDialogOpen(false)}
|
|
||||||
onCreated={loadProjects}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,11 @@ import React from 'react';
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Autocomplete from '@mui/material/Autocomplete';
|
import Autocomplete from '@mui/material/Autocomplete';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
|
||||||
|
|
||||||
export default function ProjectSelectorBar({
|
export default function ProjectSelectorBar({
|
||||||
projects,
|
projects,
|
||||||
selectedProject,
|
selectedProject,
|
||||||
onSelectProject,
|
onSelectProject,
|
||||||
onCreateProject,
|
|
||||||
inactiveProjects,
|
inactiveProjects,
|
||||||
}) {
|
}) {
|
||||||
const activeProjects = projects.filter(p => {
|
const activeProjects = projects.filter(p => {
|
||||||
@@ -38,15 +35,6 @@ export default function ProjectSelectorBar({
|
|||||||
)}
|
)}
|
||||||
noOptionsText="프로젝트 없음"
|
noOptionsText="프로젝트 없음"
|
||||||
/>
|
/>
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
size="small"
|
|
||||||
startIcon={<AddIcon />}
|
|
||||||
onClick={onCreateProject}
|
|
||||||
sx={{ whiteSpace: 'nowrap' }}
|
|
||||||
>
|
|
||||||
프로젝트 생성
|
|
||||||
</Button>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user