Files
TK-BOM-Project/frontend/src/pages/JobRegistrationPage.jsx
Hyungi Ahn 4f8e395f87
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
feat: SWG 가스켓 전체 구성 정보 표시 개선
- H/F/I/O SS304/GRAPHITE/CS/CS 패턴에서 4개 구성요소 모두 표시
- 기존 SS304 + GRAPHITE → SS304/GRAPHITE/CS/CS로 완전한 구성 표시
- 외부링/필러/내부링/추가구성 모든 정보 포함
- 구매수량 계산 모달에서 정확한 재질 정보 확인 가능
2025-08-30 14:23:01 +09:00

359 lines
12 KiB
JavaScript

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { api } from '../api';
import './JobRegistrationPage.css';
const JobRegistrationPage = () => {
const navigate = useNavigate();
const [formData, setFormData] = useState({
jobNo: '',
projectName: '',
clientName: '',
location: '',
contractDate: '',
deliveryDate: '',
deliveryMethod: '',
description: '',
projectType: '냉동기',
status: 'PLANNING'
});
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState({});
const [projectTypes, setProjectTypes] = useState([
{ value: '냉동기', label: '냉동기' },
{ value: 'BOG', label: 'BOG' },
{ value: '다이아프람', label: '다이아프람' },
{ value: '드라이어', label: '드라이어' }
]);
const [newProjectType, setNewProjectType] = useState('');
const [showAddProjectType, setShowAddProjectType] = useState(false);
const statusOptions = [
{ value: 'PLANNING', label: '계획' },
{ value: 'DESIGN', label: '설계' },
{ value: 'PROCUREMENT', label: '조달' },
{ value: 'CONSTRUCTION', label: '시공' },
{ value: 'COMPLETED', label: '완료' }
];
const handleInputChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
// 입력 시 에러 제거
if (errors[name]) {
setErrors(prev => ({
...prev,
[name]: ''
}));
}
};
const addProjectType = () => {
if (newProjectType.trim() && !projectTypes.find(type => type.value === newProjectType.trim())) {
const newType = { value: newProjectType.trim(), label: newProjectType.trim() };
setProjectTypes(prev => [...prev, newType]);
setFormData(prev => ({ ...prev, projectType: newProjectType.trim() }));
setNewProjectType('');
setShowAddProjectType(false);
}
};
const removeProjectType = (valueToRemove) => {
if (projectTypes.length > 1) { // 최소 1개는 유지
setProjectTypes(prev => prev.filter(type => type.value !== valueToRemove));
if (formData.projectType === valueToRemove) {
setFormData(prev => ({ ...prev, projectType: projectTypes[0].value }));
}
}
};
const validateForm = () => {
const newErrors = {};
if (!formData.jobNo.trim()) {
newErrors.jobNo = 'Job No.는 필수 입력 항목입니다.';
}
if (!formData.projectName.trim()) {
newErrors.projectName = '프로젝트명은 필수 입력 항목입니다.';
}
if (!formData.clientName.trim()) {
newErrors.clientName = '고객사명은 필수 입력 항목입니다.';
}
if (formData.contractDate && formData.deliveryDate && new Date(formData.contractDate) > new Date(formData.deliveryDate)) {
newErrors.deliveryDate = '납기일은 수주일 이후여야 합니다.';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) {
return;
}
setLoading(true);
try {
// Job 생성 API 호출
const response = await api.post('/jobs', {
job_no: formData.jobNo,
job_name: formData.projectName,
client_name: formData.clientName,
project_site: formData.location || null,
contract_date: formData.contractDate || null,
delivery_date: formData.deliveryDate || null,
delivery_terms: formData.deliveryMethod || null,
description: formData.description || null,
project_type: formData.projectType,
status: formData.status
});
if (response.data.success) {
alert('프로젝트가 성공적으로 등록되었습니다!');
navigate('/project-selection');
} else {
alert('등록에 실패했습니다: ' + response.data.message);
}
} catch (error) {
console.error('Job 등록 오류:', error);
if (error.response?.data?.detail) {
alert('등록 실패: ' + error.response.data.detail);
} else {
alert('등록 중 오류가 발생했습니다.');
}
} finally {
setLoading(false);
}
};
return (
<div className="job-registration-page">
<div className="job-registration-container">
<header className="page-header">
<button
className="back-button"
onClick={() => navigate('/')}
>
메인으로 돌아가기
</button>
<h1>프로젝트 기본정보 등록</h1>
<p>새로운 프로젝트의 Job No. 기본 정보를 입력해주세요</p>
</header>
<form className="registration-form" onSubmit={handleSubmit}>
<div className="form-grid">
<div className="form-group">
<label htmlFor="jobNo" className="required">Job No.</label>
<input
type="text"
id="jobNo"
name="jobNo"
value={formData.jobNo}
onChange={handleInputChange}
placeholder="예: TK-2025-001"
className={errors.jobNo ? 'error' : ''}
/>
{errors.jobNo && <span className="error-message">{errors.jobNo}</span>}
</div>
<div className="form-group">
<label htmlFor="projectName" className="required">프로젝트명</label>
<input
type="text"
id="projectName"
name="projectName"
value={formData.projectName}
onChange={handleInputChange}
placeholder="프로젝트명을 입력하세요"
className={errors.projectName ? 'error' : ''}
/>
{errors.projectName && <span className="error-message">{errors.projectName}</span>}
</div>
<div className="form-group">
<label htmlFor="clientName" className="required">고객사명</label>
<input
type="text"
id="clientName"
name="clientName"
value={formData.clientName}
onChange={handleInputChange}
placeholder="고객사명을 입력하세요"
className={errors.clientName ? 'error' : ''}
/>
{errors.clientName && <span className="error-message">{errors.clientName}</span>}
</div>
<div className="form-group">
<label htmlFor="location">프로젝트 위치</label>
<input
type="text"
id="location"
name="location"
value={formData.location}
onChange={handleInputChange}
placeholder="예: 울산광역시 남구"
/>
</div>
<div className="form-group">
<label htmlFor="projectType">프로젝트 유형</label>
<div className="project-type-container">
<select
id="projectType"
name="projectType"
value={formData.projectType}
onChange={handleInputChange}
>
{projectTypes.map(type => (
<option key={type.value} value={type.value}>
{type.label}
</option>
))}
</select>
<div className="project-type-actions">
<button
type="button"
className="add-type-btn"
onClick={() => setShowAddProjectType(true)}
title="프로젝트 유형 추가"
>
+
</button>
{projectTypes.length > 1 && (
<button
type="button"
className="remove-type-btn"
onClick={() => removeProjectType(formData.projectType)}
title="현재 선택된 유형 삭제"
>
-
</button>
)}
</div>
</div>
{showAddProjectType && (
<div className="add-project-type-form">
<input
type="text"
value={newProjectType}
onChange={(e) => setNewProjectType(e.target.value)}
placeholder="새 프로젝트 유형 입력"
onKeyPress={(e) => e.key === 'Enter' && addProjectType()}
/>
<button type="button" onClick={addProjectType}>추가</button>
<button type="button" onClick={() => setShowAddProjectType(false)}>취소</button>
</div>
)}
</div>
<div className="form-group">
<label htmlFor="status">프로젝트 상태</label>
<select
id="status"
name="status"
value={formData.status}
onChange={handleInputChange}
>
{statusOptions.map(status => (
<option key={status.value} value={status.value}>
{status.label}
</option>
))}
</select>
</div>
<div className="form-group">
<label htmlFor="contractDate">수주일</label>
<input
type="date"
id="contractDate"
name="contractDate"
value={formData.contractDate}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="deliveryDate">납기일</label>
<input
type="date"
id="deliveryDate"
name="deliveryDate"
value={formData.deliveryDate}
onChange={handleInputChange}
className={errors.deliveryDate ? 'error' : ''}
/>
{errors.deliveryDate && <span className="error-message">{errors.deliveryDate}</span>}
</div>
<div className="form-group">
<label htmlFor="deliveryMethod">납품 방법</label>
<select
id="deliveryMethod"
name="deliveryMethod"
value={formData.deliveryMethod}
onChange={handleInputChange}
>
<option value="">납품 방법 선택</option>
<option value="FOB">FOB (Free On Board)</option>
<option value="CIF">CIF (Cost, Insurance and Freight)</option>
<option value="EXW">EXW (Ex Works)</option>
<option value="DDP">DDP (Delivered Duty Paid)</option>
<option value="직접납품">직접납품</option>
<option value="택배">택배</option>
<option value="기타">기타</option>
</select>
</div>
<div className="form-group full-width">
<label htmlFor="description">프로젝트 설명</label>
<textarea
id="description"
name="description"
value={formData.description}
onChange={handleInputChange}
placeholder="프로젝트에 대한 상세 설명을 입력하세요"
rows="4"
/>
</div>
</div>
<div className="form-actions">
<button
type="button"
className="cancel-button"
onClick={() => navigate('/')}
>
취소
</button>
<button
type="submit"
className="submit-button"
disabled={loading}
>
{loading ? '등록 중...' : '프로젝트 등록'}
</button>
</div>
</form>
</div>
</div>
);
};
export default JobRegistrationPage;