Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- H/F/I/O SS304/GRAPHITE/CS/CS 패턴에서 4개 구성요소 모두 표시 - 기존 SS304 + GRAPHITE → SS304/GRAPHITE/CS/CS로 완전한 구성 표시 - 외부링/필러/내부링/추가구성 모든 정보 포함 - 구매수량 계산 모달에서 정확한 재질 정보 확인 가능
359 lines
12 KiB
JavaScript
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; |