Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
johannesbrandenburger committed May 14, 2024
2 parents 7e37320 + 5dac795 commit a8fe4f1
Show file tree
Hide file tree
Showing 11 changed files with 462 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,16 @@ public void setRangeLow(String rangeLow) {
public void setRangeHigh(String rangeHigh) {
this.rangeHigh = rangeHigh;
}

public FeedbackQuestionType getType() {
return this.type;
}

public String getRangeLow() {
return this.rangeLow;
}

public String getRangeHigh() {
return this.rangeHigh;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ public Integer checkAnswer(List<String> answer) {
public void setType(QuizQuestionType type) {
this.type = type;
}

public QuizQuestionType getType() {
return this.type;
}
}

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions frontend/lib/components/choose/choose_course.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:frontend/components/layout/sliver_layout.dart';
import 'package:frontend/global.dart';
import 'package:frontend/models/course.dart';
import 'package:frontend/theme/assets.dart';
import 'package:frontend/components/elements/course/JoinCourseDialog.dart';
Expand Down Expand Up @@ -66,6 +67,10 @@ class _ChooseCourseState extends State<ChooseCourse> {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
side: widget.courses[index].isOwner ? BorderSide(
color: colors.primary,
width: 2.0,
) : BorderSide.none,
),
elevation: 1.0,
surfaceTintColor: Colors.white,
Expand Down
9 changes: 5 additions & 4 deletions frontend/lib/components/choose/choose_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,11 @@ class _ChooseFormState extends State<ChooseForm> {
),
overflow: TextOverflow.ellipsis,
),
IconButton(
icon: Icon(Icons.info_outline, color: colors.onSurface),
onPressed: _showInfoDialog,
),
if (widget.course.isOwner)
IconButton(
icon: Icon(Icons.info_outline, color: colors.onSurface),
onPressed: _showInfoDialog,
),
],
)),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"use client"

import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Label } from "@radix-ui/react-dropdown-menu";
import { useRouter } from 'next/navigation'
import { useState, useEffect } from "react";
import { toast } from "sonner";
import { CircleArrowLeft, Loader2 } from 'lucide-react';
import { hasValidJwtToken } from "@/lib/utils";
import * as React from "react"
import { Course, FeedbackForm, QuizForm } from "@/lib/models";
import { changeCourseOfForm, fetchFeedbackForm, listCourses } from "@/lib/requests";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"


export default function ChangeCoursePage({ params }: { params: { courseId: string, formId: string } }) {

const router = useRouter();
const [loading, setLoading] = useState(true);
const [form, setForm] = useState<FeedbackForm>({
id: "",
name: "",
key: "",
type: "Feedback",
description: "",
questions: [],
lastModified: new Date(),
});
const [courses, setCourses] = useState<Course[]>([]);
useEffect(() => {
const loadData = async () => {
setLoading(true);
let courses = await listCourses();
let form = await fetchFeedbackForm(params.courseId, params.formId);
console.log(form);
setCourses(courses);
form && setForm(form);
setLoading(false);
};
loadData();
}, [params.courseId, params.formId]);

useEffect(() => {
hasValidJwtToken().then((isValid) => {
if (!isValid) router.push("/");
});
}, [router]);

return (
<div className="flex flex-col items-center justify-center h-max m-4">

{loading && (
<Loader2 className="w-6 h-6 animate-spin" />
)}

{!loading && (
<>
<div className="flex justify-between w-full">
<Button
variant="secondary"
className="mb-4 self-start text-sm"
onClick={() => {
router.push(`/courses/${params.courseId}/feedbackform/${params.formId}`);
}}
><CircleArrowLeft /></Button>
<div className="flex items-center">

</div>

</div>
<h1 className="text-2xl mb-4 font-bold">
Form: {form.name}
</h1>
<Card className="w-full">
<CardContent>
<Label
className="mt-4 mb-2"
>Select the course in which this form should be used:</Label>
<Select
defaultValue={params.courseId}
onValueChange={async (value) => {
let result = await changeCourseOfForm(params.courseId, params.formId, value);
if (result) {
toast.success("Course changed successfully");
router.push(`/courses/${value}/feedbackform/${params.formId}/change-course`);
}
}}
>
<SelectTrigger>
<SelectValue>{courses.find(c => c.id === params.courseId)?.name}</SelectValue>
</SelectTrigger>
<SelectContent>
{courses.map((course) => (
<SelectItem key={course.id} value={course.id}>
{course.name}
</SelectItem>
))}
</SelectContent>
</Select>
</CardContent>
</Card>
</>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ export default function FeedbackFormPage({ params }: { params: { courseId: strin
}}
><CircleArrowLeft /></Button>
<div className="flex items-center">
<Button
className="mb-4 self-end ml-4"
variant="outline"
onClick={async () => {
router.push(`/courses/${params.courseId}/feedbackform/${params.formId}/change-course`);
}}
>Change Course</Button>
<Button
className="mb-4 self-end ml-4"
variant="outline"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"use client"

import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Label } from "@radix-ui/react-dropdown-menu";
import { useRouter } from 'next/navigation'
import { useState, useEffect } from "react";
import { toast } from "sonner";
import { CircleArrowLeft, Loader2 } from 'lucide-react';
import { hasValidJwtToken } from "@/lib/utils";
import * as React from "react"
import { Course, QuizForm } from "@/lib/models";
import { changeCourseOfForm, fetchQuizForm, listCourses } from "@/lib/requests";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"


export default function ChangeCoursePage({ params }: { params: { courseId: string, formId: string } }) {

const router = useRouter();
const [loading, setLoading] = useState(true);
const [form, setForm] = useState<QuizForm>({
id: "",
name: "",
key: "",
type: "Quiz",
description: "",
questions: [],
lastModified: new Date(),
});
const [courses, setCourses] = useState<Course[]>([]);
useEffect(() => {
const loadData = async () => {
setLoading(true);
let courses = await listCourses();
let form = await fetchQuizForm(params.courseId, params.formId);
console.log(form);
setCourses(courses);
form && setForm(form);
setLoading(false);
};
loadData();
}, [params.courseId, params.formId]);

useEffect(() => {
hasValidJwtToken().then((isValid) => {
if (!isValid) router.push("/");
});
}, [router]);

return (
<div className="flex flex-col items-center justify-center h-max m-4">

{loading && (
<Loader2 className="w-6 h-6 animate-spin" />
)}

{!loading && (
<>
<div className="flex justify-between w-full">
<Button
variant="secondary"
className="mb-4 self-start text-sm"
onClick={() => {
router.push(`/courses/${params.courseId}/quizform/${params.formId}`);
}}
><CircleArrowLeft /></Button>
<div className="flex items-center">

</div>

</div>
<h1 className="text-2xl mb-4 font-bold">
Form: {form.name}
</h1>
<Card className="w-full">
<CardContent>
<Label
className="mt-4 mb-2"
>Select the course in which this form should be used:</Label>
<Select
defaultValue={params.courseId}
onValueChange={async (value) => {
let result = await changeCourseOfForm(params.courseId, params.formId, value);
if (result) {
toast.success("Course changed successfully");
router.push(`/courses/${value}/quizform/${params.formId}/change-course`);
}
}}
>
<SelectTrigger>
<SelectValue>{courses.find(c => c.id === params.courseId)?.name}</SelectValue>
</SelectTrigger>
<SelectContent>
{courses.map((course) => (
<SelectItem key={course.id} value={course.id}>
{course.name}
</SelectItem>
))}
</SelectContent>
</Select>
</CardContent>
</Card>
</>
)}
</div>
);
}
23 changes: 15 additions & 8 deletions inputtool/app/courses/[courseId]/quizform/[formId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default function QuizFormPage({ params }: { params: { courseId: string, f
} else {
toast.error("Failed to save.");
}
}
}

const result = await updateQuizForm(params.courseId, params.formId, quizform.name, quizform.description);
if (result) {
Expand Down Expand Up @@ -193,6 +193,13 @@ export default function QuizFormPage({ params }: { params: { courseId: string, f
}}
><CircleArrowLeft /></Button>
<div className="flex items-center">
<Button
className="mb-4 self-end ml-4"
variant="outline"
onClick={async () => {
router.push(`/courses/${params.courseId}/quizform/${params.formId}/change-course`);
}}
>Change Course</Button>
<Button
className="mb-4 self-end ml-4"
variant="outline"
Expand Down Expand Up @@ -321,25 +328,25 @@ export default function QuizFormPage({ params }: { params: { courseId: string, f
>
<TableCell>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
{question?.type === "YES_NO" && <ToggleLeft size={16}/>}
{question?.type === "MULTIPLE_CHOICE" && <ListTodo size={16}/>}
{question?.type === "SINGLE_CHOICE" && <SquareCheckBig size={16}/>}
{question?.type === "FULLTEXT" && <TextCursorInput size={16}/>}
{question?.type === "YES_NO" && <ToggleLeft size={16} />}
{question?.type === "MULTIPLE_CHOICE" && <ListTodo size={16} />}
{question?.type === "SINGLE_CHOICE" && <SquareCheckBig size={16} />}
{question?.type === "FULLTEXT" && <TextCursorInput size={16} />}
{question?.type}
</div>
</TableCell>
<TableCell className="font-medium">{question.name}</TableCell>
<TableCell>{question.description}</TableCell>
<TableCell>{question.options?.join(", ")}</TableCell>
{ (question?.type === "MULTIPLE_CHOICE" || question?.type === "SINGLE_CHOICE") && (
{(question?.type === "MULTIPLE_CHOICE" || question?.type === "SINGLE_CHOICE") && (
<TableCell>{question.correctAnswers?.map((a) => {
// find in options ["0"] -> "Option 1"
let option = question.options?.find((o, i) => i.toString() === a);
return option ? `"${option}"` : a;
}).join(", ") || "-"}</TableCell>
) || (
<TableCell>{question.correctAnswers?.map((a) => `"${a}"`).join(", ")|| "-"}</TableCell>
)}
<TableCell>{question.correctAnswers?.map((a) => `"${a}"`).join(", ") || "-"}</TableCell>
)}
<TableCell className="flex gap-2 flex-col">
<Button
className="flex flex-col h-8 w-8"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,13 @@ export default function QuizQuestionPage({ params }: { params: { courseId: strin
<span>Single Choice</span>
</div>
</SelectItem>
<SelectItem value="FULLTEXT">
{/* NOT YET SUPPORTED */}
{/* <SelectItem value="FULLTEXT">
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
<TextCursorInput size={iconSize} />
<span>Fulltext</span>
</div>
</SelectItem>
</SelectItem> */}
</SelectContent>
</Select>
<div className="mt-2 flex gap-4 items-center">
Expand Down
14 changes: 14 additions & 0 deletions inputtool/lib/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,17 @@ export async function deleteQuizQuestion(courseId: string, formId: string, quest
return true;
}

export async function changeCourseOfForm(courseId: string, formId: string, newCourseId: string): Promise<boolean> {
const BACKEND_URL = await getBackendUrl();
const jwtToken = localStorage.getItem("jwtToken");
let changeCourseResponse = await fetch(`${BACKEND_URL}/maint/course/${courseId}/form/${formId}/change-course`, {
method: "PUT",
headers: { "AUTHORIZATION": "Bearer " + jwtToken, "Content-Type": "application/json" },
body: JSON.stringify({ newCourseId })
});
if (changeCourseResponse.status !== 200) {
toast.error(`Failed to change course of form. Please try again. Status: ${changeCourseResponse.status}`);
return false;
}
return true;
}

0 comments on commit a8fe4f1

Please sign in to comment.