diff --git a/backend/app/routers/dashboard.py b/backend/app/routers/dashboard.py index 8eafd86..d44ebb1 100644 --- a/backend/app/routers/dashboard.py +++ b/backend/app/routers/dashboard.py @@ -427,6 +427,78 @@ async def get_quick_actions( 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() + + # 활동 로그 기록 + ActivityLogger.log_activity( + db=db, + user_id=current_user.get('user_id'), + action="CREATE_PROJECT", + target_type="PROJECT", + target_id=new_project.id, + details=f"프로젝트 생성: {official_project_code} - {project_name}" + ) + + 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") async def get_projects( current_user: dict = Depends(get_current_user), @@ -442,10 +514,12 @@ async def get_projects( query = text(""" SELECT id, - job_no, official_project_code, - job_name, - project_type, + project_name, + client_name, + design_project_code, + design_project_name, + status, created_at, updated_at FROM projects @@ -458,11 +532,13 @@ async def get_projects( for row in results: projects.append({ "id": row.id, - "job_no": row.job_no, "official_project_code": row.official_project_code, - "job_name": row.job_name, - "project_name": row.job_name, # 호환성을 위해 추가 - "project_type": row.project_type, + "project_name": row.project_name, + "job_name": row.project_name, # 호환성을 위해 추가 + "client_name": row.client_name, + "design_project_code": row.design_project_code, + "design_project_name": row.design_project_name, + "status": row.status, "created_at": row.created_at.isoformat() if row.created_at else None, "updated_at": row.updated_at.isoformat() if row.updated_at else None }) @@ -506,14 +582,14 @@ async def update_project_name( # 프로젝트 이름 업데이트 update_query = text(""" UPDATE projects - SET job_name = :job_name, + SET project_name = :project_name, updated_at = CURRENT_TIMESTAMP WHERE id = :project_id RETURNING * """) updated = db.execute(update_query, { - "job_name": job_name, + "project_name": job_name, "project_id": project_id }).fetchone() @@ -534,9 +610,9 @@ async def update_project_name( "message": "프로젝트 이름이 수정되었습니다", "project": { "id": updated.id, - "job_no": updated.job_no, - "job_name": updated.job_name, - "official_project_code": updated.official_project_code + "official_project_code": updated.official_project_code, + "project_name": updated.project_name, + "job_name": updated.project_name # 호환성 } } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d35d1ec..219b4d7 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -23,16 +23,50 @@ function App() { const [projects, setProjects] = useState([]); const [editingProject, setEditingProject] = useState(null); const [editedProjectName, setEditedProjectName] = useState(''); + const [showCreateProject, setShowCreateProject] = useState(false); + const [newProjectCode, setNewProjectCode] = useState(''); + const [newProjectName, setNewProjectName] = useState(''); + const [newClientName, setNewClientName] = useState(''); // 프로젝트 목록 로드 const loadProjects = async () => { try { - const response = await api.get('/projects'); + const response = await api.get('/dashboard/projects'); if (response.data && response.data.projects) { setProjects(response.data.projects); } } catch (error) { console.error('프로젝트 목록 로드 실패:', error); + // API 실패 시 에러를 무시하고 더미 데이터 사용 + } + }; + + // 프로젝트 생성 + const createProject = async () => { + if (!newProjectCode || !newProjectName) { + alert('프로젝트 코드와 이름을 입력해주세요.'); + return; + } + + try { + const response = await api.post(`/dashboard/projects?official_project_code=${encodeURIComponent(newProjectCode)}&project_name=${encodeURIComponent(newProjectName)}&client_name=${encodeURIComponent(newClientName)}`); + + if (response.data.success) { + // 프로젝트 목록 갱신 + await loadProjects(); + + // 폼 초기화 + setShowCreateProject(false); + setNewProjectCode(''); + setNewProjectName(''); + setNewClientName(''); + + alert('프로젝트가 생성되었습니다.'); + } + } catch (error) { + console.error('프로젝트 생성 실패:', error); + const errorMsg = error.response?.data?.detail || '프로젝트 생성에 실패했습니다.'; + alert(errorMsg); } }; @@ -355,21 +389,18 @@ function App() { {/* 메인 콘텐츠 */}