Есть проблема. При попытке послать запрос на этот сервер происходит обрыв на preflight, он возвращает no-content. Однако по логам изменение проходит: API updateItem called for id=1 with data: {name: 'Laptop34'}
EditItemPage.jsx:23 Item updated: {message: 'Item updated'} и по серверу тоже: [2024-10-10T16:17:34.001Z] PUT /items/1 - Body: { name: 'Laptop34' }
[2024-10-10T16:17:35.020Z] GET /items - Body: {}
[2024-10-10T16:17:36.796Z] GET /items/1 - Body: {}. Почему? ItemForm.jsx import { useForm, useFieldArray } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import { useEffect } from 'react';
const schema = yup.object({
  name: yup.string().required('Name is required').max(50, 'Max length is 50'),
  category: yup.string().required('Category is required'),
  quantity: yup
    .number()
    .typeError('Quantity must be a number')
    .required('Quantity is required')
    .min(1, 'Quantity must be at least 1'),
  location: yup.string().notRequired(),
  supplier: yup.string().notRequired(),
  supplierLocation: yup.string().notRequired(),
  showPriceDetails: yup
    .string()
    .oneOf(['yes', 'no'], 'Please select an option')
    .required('Please select an option'),

  priceType: yup
    .string()
    .when('showPriceDetails', {
      is: 'yes',
      then: schema => schema.required('Price Type is required'),
      otherwise: schema => schema.notRequired(),
    }),

  price: yup
    .number()
    .transform((value, originalValue) => (originalValue === '' ? undefined : value))
    .when('showPriceDetails', {
      is: 'yes',
      then: schema =>
        schema
          .required('Price is required')
          .typeError('Price must be a number')
          .min(1, 'Price must be at least 1'),
      otherwise: schema => schema.notRequired(),
    }),

  endpoints: yup.array().of(
    yup.object({
      endpoint: yup.string().required('Endpoint is required'),
      status: yup.string().required('Status is required'),
    })
  ),
});
export const ItemForm = ({ defaultValues, onSubmit }) => {
  const {
    register,
    handleSubmit,
    watch,
    control,
    formState: { errors },
    reset,
  } = useForm({
    defaultValues,
    resolver: yupResolver(schema),
  });

  const { fields, append, remove } = useFieldArray({
    control,
    name: 'endpoints',
  });

  const showPriceDetails = watch('showPriceDetails', 'no') === 'yes';

  useEffect(() => {
    reset(defaultValues);
  }, [defaultValues, reset]);

  const handleFormSubmit = (data) => {
    // Создаем объект для хранения только измененных значений
    const changes = {};

    // Проверяем каждое поле на наличие изменений
    Object.keys(data).forEach((key) => {
      if (JSON.stringify(data[key]) !== JSON.stringify(defaultValues[key])) {
        changes[key] = data[key];
      }
    });

    // Отправляем только измененные данные
    onSubmit(changes);
  };

  return (
    <div className="form-container">
      <form onSubmit={handleSubmit(handleFormSubmit)} className="form-wrapper">
        {/* Сетка для имени, категории и количества */}
        <div className="form-grid grid-3">
          <div className="form-group">
            <label>Name:</label>
            <input
              {...register('name')}
              className={errors.name ? 'input-error' : ''}
            />
            {errors.name && <p className="error-message">{errors.name.message}</p>}
          </div>

          <div className="form-group">
            <label>Category:</label>
            <select
              {...register('category')}
              className={errors.category ? 'input-error' : ''}
            >
              <option value="">Select category</option>
              <option value="Electronics">Electronics</option>
              <option value="Furniture">Furniture</option>
              <option value="Clothing">Clothing</option>
              <option value="Food">Food</option>
              <option value="Toys">Toys</option>
              <option value="Books">Books</option>
              <option value="Sports">Sports</option>
            </select>
            {errors.category && (
              <p className="error-message">{errors.category.message}</p>
            )}
          </div>

          <div className="form-group">
            <label>Quantity:</label>
            <input
              type="number"
              {...register('quantity')}
              className={errors.quantity ? 'input-error' : ''}
            />
            {errors.quantity && (
              <p className="error-message">{errors.quantity.message}</p>
            )}
          </div>
        </div>

        {/* Сетка для поставщика и его локации */}
        <div className="form-grid grid-2">
          <div className="form-group">
            <label>Supplier (Optional):</label>
            <input
              {...register('supplier')}
              className={errors.supplier ? 'input-error' : ''}
            />
            {errors.supplier && (
              <p className="error-message">{errors.supplier.message}</p>
            )}
          </div>

          <div className="form-group">
            <label>Supplier Location (Optional):</label>
            <input
              {...register('supplierLocation')}
              className={errors.supplierLocation ? 'input-error' : ''}
            />
            {errors.supplierLocation && (
              <p className="error-message">{errors.supplierLocation.message}</p>
            )}
          </div>
        </div>

        {/* Сетка для ценовых данных */}
        <div className="form-group">
          <label>Show Price Details?</label>
          <div className="radio-group">
            <label>
              <input
                type="radio"
                value="yes"
                {...register('showPriceDetails')}
                defaultChecked={defaultValues?.showPriceDetails === 'yes'}
              />
              Yes
            </label>
            <label>
              <input
                type="radio"
                value="no"
                {...register('showPriceDetails')}
                defaultChecked={defaultValues?.showPriceDetails !== 'yes'}
              />
              No
            </label>
          </div>
          {errors.showPriceDetails && (
            <p className="error-message">{errors.showPriceDetails.message}</p>
          )}
        </div>

        {showPriceDetails && (
          <div className="form-grid grid-2">
            <div className="form-group">
              <label>Type:</label>
              <select
                {...register('priceType')}
                className={errors.priceType ? 'input-error' : ''}
              >
                <option value="">Select type</option>
                <option value="For Sale">For Sale</option>
                <option value="For Buying">For Buying</option>
              </select>
              {errors.priceType && (
                <p className="error-message">{errors.priceType.message}</p>
              )}
            </div>

            <div className="form-group">
              <label>Price:</label>
              <input
                type="number"
                {...register('price')}
                className={errors.price ? 'input-error' : ''}
                placeholder="Enter price"
              />
              {errors.price && (
                <p className="error-message">{errors.price.message}</p>
              )}
            </div>
          </div>
        )}

        {/* Сетка для Endpoints */}
        <div className="form-group full-width">
          <h3>Endpoints</h3>
          {fields.map((endpoint, index) => (
            <div key={endpoint.id} className="form-grid grid-2">
              <div className="form-group">
                <label>Endpoint {index + 1}:</label>
                <input
                  {...register(`endpoints.${index}.endpoint`)}
                  placeholder={`Endpoint ${index + 1}`}
                  className={errors.endpoints?.[index]?.endpoint ? 'input-error' : ''}
                />
                {errors.endpoints?.[index]?.endpoint && (
                  <p className="error-message">
                    {errors.endpoints[index].endpoint.message}
                  </p>
                )}
              </div>

              <div className="form-group">
                <label>Status:</label>
                <select
                  {...register(`endpoints.${index}.status`)}
                  className={errors.endpoints?.[index]?.status ? 'input-error' : ''}
                >
                  <option value="">Select status</option>
                  <option value="Delivering">Delivering</option>
                  <option value="Delivered">Delivered</option>
                  <option value="Pending">Pending</option>
                  <option value="Delayed">Delayed</option>
                  <option value="Canceled">Canceled</option>
                </select>
                {errors.endpoints?.[index]?.status && (
                  <p className="error-message">
                    {errors.endpoints[index].status.message}
                  </p>
                )}
              </div>

              <button
                type="button"
                className="remove-endpoint-button"
                onClick={() => remove(index)}
              >
                Remove Endpoint
              </button>
            </div>
          ))}
          <button
            type="button"
            onClick={() => append({ endpoint: '', status: '' })}
            className="add-endpoint-button"
          >
            Add Endpoint
          </button>
        </div>

        <div className="form-actions">
          <button type="submit" className="action-button">Save</button>
          <button type="button" className="action-button">Back to List</button>
        </div>
      </form>
    </div>
  );
}; api.js // api.js
import axios from 'axios';

const API_BASE_URL = 'http://localhost:5000'; // Укажите полный URL

export const getItems = () => axios.get(`${API_BASE_URL}/items`);

export const createItem = (data) => {
  console.log('API createItem called with data:', data); // Логирование
  return axios.post(`${API_BASE_URL}/items`, data);
};

export const updateItem = (id, data) => {
  console.log(`API updateItem called for id=${id} with data:`, data); // Логирование
  return axios.put(`${API_BASE_URL}/items/${id}`, data);
}; CreateItem.jsx import { useNavigate } from 'react-router-dom';
import { createItem } from '../api';
import { ItemForm } from '../components/ItemForm';

export const CreateItemPage = () => {
  const navigate = useNavigate();

  const handleSubmit = (data) => {
    console.log('Creating item with data:', data); // Логирование данных на клиенте
    createItem(data)
      .then((response) => {
        console.log('Item created:', response.data); // Логирование ответа сервера
        navigate('/');
      })
      .catch((error) => {
        console.error('Error creating item:', error); // Логирование ошибок
      });
  };

  return (
    <div>
      <h1>Create Item</h1>
      <ItemForm
        onSubmit={handleSubmit}
        defaultValues={{
          name: '',
          category: '',
          quantity: 1,
          location: '',
          supplier: '',
          supplierLocation: '',
          showPriceDetails: 'no',
          priceType: '',
          price: '',
          endpoints: []
        }}
      />
      <button onClick={() => navigate('/')}>Back to List</button>
    </div>
  );
}; Edit Item.jsx  import { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { getItems, updateItem } from '../api';
import { ItemForm } from '../components/ItemForm';

export const EditItemPage = () => {
  const { id } = useParams();
  const navigate = useNavigate();
  const [item, setItem] = useState(null);

  useEffect(() => {
    getItems().then((response) => {
      const foundItem = response.data.find((item) => item.id === parseInt(id));
      setItem(foundItem);
    }).catch((error) => {
      console.error('Error fetching items:', error);
    });
  }, [id]);

  const handleSubmit = (data) => {
    updateItem(id, data)
      .then((response) => {
        console.log('Item updated:', response.data); // Log the response after updating
        navigate('/'); // Redirect to the item list
      })
      .catch((error) => {
        console.error('Error updating item:', error); // Handle errors during update
      });
  };

  // Render loading state while fetching the item
  if (!item) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>Edit Item</h1>
      <ItemForm
        onSubmit={handleSubmit}
        defaultValues={item} // Use the fetched item as default values
      />
      <button onClick={() => navigate('/')}>Back to List</button>
    </div>
  );
};
Index.js import express from "express";
import cors from "cors";
import {warehouseItems} from "./Items.js"; // Импортируем массив данных

const app = express();

// Настройка CORS (разрешает все источники)
app.use(cors());

// Парсинг JSON тела запроса
app.use(express.json());

// Middleware для логирования всех запросов
app.use((req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.path} - Body:`, req.body);
  next();
});

app.get("/items", (req, res) => {
  res.json(warehouseItems);
});

app.post("/items", (req, res) => {
  const newItem = { id: warehouseItems.length + 1, ...req.body };
  warehouseItems.push(newItem);
  setTimeout(() => { // Задержка 1 секунду для визуализации запроса в DevTools
    res.status(201).json(newItem);
  }, 1000);
});

app.put("/items/:id", (req, res) => {
  const id = parseInt(req.params.id);
  const updatedFields = req.body;
  const itemIndex = warehouseItems.findIndex(item => item.id === id);

  if (itemIndex === -1) {
    return res.status(404).json({ message: "Item not found" });
  }

  warehouseItems[itemIndex] = { ...warehouseItems[itemIndex], ...updatedFields };
  setTimeout(() => { // Задержка 1 секунду для визуализации запроса в DevTools
    res.json({ message: "Item updated" });
  }, 1000);
});

const PORT = 5000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
Items.js export const warehouseItems = [
    {
      id: 1,
      name: "Laptop",
      category: "Electronics",
      quantity: 5,
      location: "Warehouse A",
      supplier: "TechCorp",
      supplierLocation: "New York",
      price: 1200,
      showPriceDetails: 'no',
      endpoints: [
        { endpoint: "Endpoint 1", status: "Pending" },
        { endpoint: "Endpoint 2", status: "Delivered" },
      ],
    },
  ];
  
   
  У меня vite, вот конфиг import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})