feat: SWG 가스켓 전체 구성 정보 표시 개선
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
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로 완전한 구성 표시 - 외부링/필러/내부링/추가구성 모든 정보 포함 - 구매수량 계산 모달에서 정확한 재질 정보 확인 가능
This commit is contained in:
359
frontend/src/pages/JobRegistrationPage.jsx
Normal file
359
frontend/src/pages/JobRegistrationPage.jsx
Normal file
@@ -0,0 +1,359 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user