Задать вопрос
@Bazliter

Как загрузить файл в Next.js?

При клике на кнопку создать открывается модальное окно.
6769f0e7d8410425711377.png
В нем поля соответствуют таблице, вот вся схема БД:
6769f09b02a40580266683.png
Проблема у меня возникает с прикреплением файла (добавление задания не работает), мне надо исправить api и дописать course_task.tsx. Помогите пожалуйста, я просто не понимаю что вообще делать
Код страницы course_task.tsx:
"use client";

import React, { useEffect, useState } from "react";
import Image from "next/image";
import styles from "./course_task.module.css";
import Course_task_item from "./course_task_item";
import { useParams } from "next/navigation";

interface Assignment {
  assignment_id: number;
  title: string;
  description: string;
  max_grade: number;
  submissions_count: number;
  not_submitted_count: number;
  files: { file_name: string; file_path: string }[];
}

const Course_task: React.FC = () => {
  const { courseId } = useParams();
  const [assignments, setAssignments] = useState<Assignment[]>([]);
  const [error, setError] = useState<string | null>(null);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [newAssignment, setNewAssignment] = useState({
    title: "",
    description: "",
    max_grade: 0,
    files: [] as File[], // Состояние для файлов
  });

  useEffect(() => {
    if (!courseId) {
      setError("Идентификатор курса отсутствует.");
      return;
    }

    const fetchAssignments = async () => {
      try {
        setError(null);
        const response = await fetch(`/api/course/${courseId}/assignments`);
        if (!response.ok) {
          throw new Error("Ошибка при загрузке заданий");
        }
        const data = await response.json();
        setAssignments(data);
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      } catch (err) {
        setError("Не удалось загрузить задания.");
      }
    };

    fetchAssignments();
  }, [courseId]);

  const handleAddAssignment = async (e: React.FormEvent) => {
    e.preventDefault();
    
    const formData = new FormData();
    formData.append("title", newAssignment.title);
    formData.append("description", newAssignment.description);
    formData.append("max_grade", newAssignment.max_grade.toString());
    
    // Добавляем файлы в FormData
    newAssignment.files.forEach((file) => {
      formData.append("files", file);
    });

    try {
      const response = await fetch(`/api/course/${courseId}/assignments`, {
        method: "POST",
        body: formData,
      });
      
      if (!response.ok) {
        throw new Error("Ошибка при добавлении задания");
      }
      setIsModalOpen(false); // Закрываем модальное окно
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    } catch (err) {
      setError("Не удалось добавить задание.");
    }
  };

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files) {
      setNewAssignment({
        ...newAssignment,
        files: Array.from(e.target.files), // Добавляем выбранные файлы в состояние
      });
    }
  };

  return (
    <div className={styles.wrapper}>
      {error && <p className={styles.error}>{error}</p>}

      <a
        href="#"
        className={styles.btn_add}
        onClick={() => setIsModalOpen(true)}
      >
        <Image src="/assets/images/plus-white.svg" alt="+" width={16} height={16} />
        <p>Создать</p>
      </a>

      {assignments.map((assignment) => (
        <Course_task_item key={assignment.assignment_id} assignment={assignment} />
      ))}

      {/* Модальное окно */}
      {isModalOpen && (
        <div className={styles.modal}>
          <div className={styles.modal_content}>
            <h2>Добавить задание</h2>
            <form onSubmit={handleAddAssignment}>
              <label>
                Название:
                <input
                  type="text"
                  value={newAssignment.title}
                  onChange={(e) =>
                    setNewAssignment({ ...newAssignment, title: e.target.value })
                  }
                />
              </label>
              <label>
                Описание:
                <textarea
                  value={newAssignment.description}
                  onChange={(e) =>
                    setNewAssignment({ ...newAssignment, description: e.target.value })
                  }
                />
              </label>
              <label>
                Максимальная оценка:
                <input
                  type="number"
                  value={newAssignment.max_grade}
                  onChange={(e) =>
                    setNewAssignment({ ...newAssignment, max_grade: Number(e.target.value) })
                  }
                />
              </label>
              <label>
                Файлы:
                <input type="file" multiple onChange={handleFileChange} />
              </label>
              <button type="submit">Сохранить</button>
              <button type="button" onClick={() => setIsModalOpen(false)}>
                Закрыть
              </button>
            </form>
          </div>
        </div>
      )}
    </div>
  );
};

export default Course_task;

Схема бд на prisma:
model Assignment {
    assignment_id Int    @id @default(autoincrement())
    course_id     Int
    title         String
    description   String
    max_grade     Int

    Course      Course                     @relation(fields: [course_id], references: [course_id])
    Files       Assignment_Files_Mappings[]
    Submissions Submission[]
}

model Assignment_Files {
    file_id   Int    @id @default(autoincrement())
    file_name String
    file_path String
    file_type String

    Mappings Assignment_Files_Mappings[]
}

model Assignment_Files_Mappings {
    mapping_id    Int @id @default(autoincrement())
    assignment_id Int
    file_id       Int

    Assignment Assignment      @relation(fields: [assignment_id], references: [assignment_id])
    File       Assignment_Files @relation(fields: [file_id], references: [file_id])
}


api/course/[courseId]/assignments/route.ts:
import { NextResponse } from "next/server";
import { prisma } from "../../../../../../prisma/prisma-client";

export async function GET(request: Request, { params }: { params: { courseId: string } }) {
  const { courseId } = params;

  try {
    const assignments = await prisma.assignment.findMany({
      where: { course_id: Number(courseId) },
      include: {
        Files: {
          select: { File: { select: { file_name: true, file_path: true } } },
        },
        Submissions: true,
      },
    });

    const formattedAssignments = assignments.map((assignment) => ({
      assignment_id: assignment.assignment_id,
      title: assignment.title,
      description: assignment.description,
      max_grade: assignment.max_grade,
      submissions_count: assignment.Submissions.filter((sub) => sub.grade !== null).length,
      not_submitted_count: assignment.Submissions.filter((sub) => sub.grade === null).length,
      files: assignment.Files.map((file) => ({
        file_name: file.File.file_name,
        file_path: file.File.file_path,
      })),
    }));

    return NextResponse.json(formattedAssignments);
  } catch (error) {
    console.error(error);
    return NextResponse.json({ error: "Ошибка при загрузке заданий" }, { status: 500 });
  }
}
  • Вопрос задан
  • 17 просмотров
Подписаться 1 Средний Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы