Import Vector lab project

This commit is contained in:
root
2026-06-06 18:31:55 +00:00
commit fcc22063ed
105 changed files with 31639 additions and 0 deletions
@@ -0,0 +1,212 @@
import React, { useState } from "react";
import Step1Child from "./Step1Child";
import Step2Health from "./Step2Health";
import Step2bPsychotype from "./Step2bPsychotype";
import Step3Circumstances from "./Step3Circumstances";
import Step4Environment from "./Step4Environment";
import "./CaseForm.css";
const INITIAL_DATA = {
has_diagnosis: false,
diagnosis_type: [],
cant_swim: false,
terrain: [],
has_dog: false,
extra_resources: []
};
const CaseForm = ({ isMobile = false }) => {
const [currentStep, setCurrentStep] = useState(1);
const [formData, setFormData] = useState(INITIAL_DATA);
const [errors, setErrors] = useState([]);
const [submitting, setSubmitting] = useState(false);
// Мобильная: 3 шага, Десктоп: 5 шагов
const totalSteps = isMobile ? 3 : 5;
const updateFormData = (updates) => {
setFormData(prev => ({ ...prev, ...updates }));
};
const validateStep = (step) => {
const newErrors = [];
if (step === 1) {
if (!formData.age_years || formData.age_years < 0 || formData.age_years > 120) {
newErrors.push("Укажите корректный возраст");
}
if (!formData.gender) {
newErrors.push("Выберите пол");
}
}
// Для десктопа: Шаг 4 = Обстоятельства
if (step === 4 && !isMobile) {
if (!formData.reason) {
newErrors.push("Укажите причину потери");
}
if (!formData.lost_time) {
newErrors.push("Укажите дату и время потери");
}
if (!formData.last_known_place) {
newErrors.push("Укажите последнее известное место");
}
}
// Для десктопа: Шаг 5 = Среда и местность
if (step === 5 && !isMobile) {
if (!formData.terrain || formData.terrain.length === 0) {
newErrors.push("Выберите хотя бы один тип местности");
}
if (!formData.tnp_lat || !formData.tnp_lon) {
newErrors.push("Укажите GPS координаты точки потери");
}
}
// Для мобильной: Шаг 3 = Обстоятельства (с GPS)
if (step === 3 && isMobile) {
if (!formData.reason) {
newErrors.push("Укажите причину потери");
}
if (!formData.tnp_lat || !formData.tnp_lon) {
newErrors.push("Укажите GPS координаты ТНП");
}
}
setErrors(newErrors);
return newErrors.length === 0;
};
const handleNext = () => {
if (validateStep(currentStep)) {
setCurrentStep(prev => Math.min(prev + 1, totalSteps));
}
};
const handleBack = () => {
setCurrentStep(prev => Math.max(prev - 1, 1));
};
const handleSubmit = async () => {
if (!validateStep(currentStep)) return;
setSubmitting(true);
setErrors([]);
try {
const payload = {
...formData,
clothes_description: [
formData.clothes_upper,
formData.clothes_lower,
formData.clothes_shoes
].filter(Boolean).join(", ")
};
// Создаем карточку
const response = await fetch("/api/v1/cases", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || "Ошибка при создании карточки");
}
const result = await response.json();
console.log("Case created:", result);
// Перенаправляем на страницу анализа с ID карточки
window.location.href = `/analysis/${result.id}`;
} catch (err) {
setErrors([err.message || "Ошибка при отправке данных"]);
} finally {
setSubmitting(false);
}
};
const renderStep = () => {
// Мобильная версия: 3 шага
// 1 = Ребёнок, 2 = Здоровье, 3 = Обстоятельства
if (isMobile) {
switch (currentStep) {
case 1: return <Step1Child data={formData} updateData={updateFormData} />;
case 2: return <Step2Health data={formData} updateData={updateFormData} />;
case 3: return <Step3Circumstances data={formData} updateData={updateFormData} />;
default: return null;
}
}
// Десктопная версия: 5 шагов
// 1 = Ребёнок, 2 = Здоровье, 3 = Психотип, 4 = Обстоятельства, 5 = Среда
switch (currentStep) {
case 1: return <Step1Child data={formData} updateData={updateFormData} />;
case 2: return <Step2Health data={formData} updateData={updateFormData} />;
case 3: return <Step2bPsychotype data={formData} updateData={updateFormData} />;
case 4: return <Step3Circumstances data={formData} updateData={updateFormData} />;
case 5: return <Step4Environment data={formData} updateData={updateFormData} />;
default: return null;
}
};
return (
<div className="case-form">
<div className="form-header">
<h1>Карточка поиска</h1>
<div className="step-indicator">
Шаг {currentStep} из {totalSteps}
</div>
</div>
{errors.length > 0 && (
<div className="error-banner">
{errors.map((err, idx) => (
<div key={idx}>{err}</div>
))}
</div>
)}
<div className="form-content">
{renderStep()}
</div>
<div className="form-navigation">
{currentStep > 1 && (
<button
type="button"
className="btn-secondary"
onClick={handleBack}
disabled={submitting}
>
Назад
</button>
)}
{currentStep < totalSteps ? (
<button
type="button"
className="btn-primary"
onClick={handleNext}
disabled={submitting}
>
Далее
</button>
) : (
<button
type="button"
className="btn-primary submit-btn"
onClick={handleSubmit}
disabled={submitting}
>
{submitting ? "Создание карточки..." : "Создать карточку"}
</button>
)}
</div>
</div>
);
};
export default CaseForm;