diff --git a/src/atlas_frontend/src/canisters/atlasSpace/api.ts b/src/atlas_frontend/src/canisters/atlasSpace/api.ts
index b1859cf..37be8eb 100644
--- a/src/atlas_frontend/src/canisters/atlasSpace/api.ts
+++ b/src/atlas_frontend/src/canisters/atlasSpace/api.ts
@@ -1,6 +1,7 @@
import type { ActorSubclass } from "@dfinity/agent";
import type {
_SERVICE,
+ AnswerFormat,
ClosedTask,
State,
Submission,
@@ -22,6 +23,7 @@ interface CreateSubtaskArg {
title: string;
description: string;
allow_resubmit: boolean;
+ answer_format: AnswerFormat
}
interface GetAtlasSpaceArgs {
@@ -93,6 +95,7 @@ export const createNewTask = async ({
task_title: arg.title,
task_description: arg.description,
allow_resubmit: arg.allow_resubmit,
+ answer_format: arg.answer_format
},
}));
diff --git a/src/atlas_frontend/src/components/Submissions/index.tsx b/src/atlas_frontend/src/components/Submissions/index.tsx
index 6bfe213..44bcf45 100644
--- a/src/atlas_frontend/src/components/Submissions/index.tsx
+++ b/src/atlas_frontend/src/components/Submissions/index.tsx
@@ -133,7 +133,7 @@ const Submissions = () => {
-
+
)}
+ {"List" in submission.submissionData.submission && (
+
+ {submission.submissionData.submission.List.items.map((item: string, idx: number) => (
+ -
+ {item}
+
+ ))}
+
+ )}
+
{submissionState === "Rejected" &&
submission.submissionData.rejection_reason[0] &&
submission.submissionData.rejection_reason[0].trim().length > 0 && (
diff --git a/src/atlas_frontend/src/components/Task/tasks/GenericTask.tsx b/src/atlas_frontend/src/components/Task/tasks/GenericTask.tsx
index b150eda..d8c6077 100644
--- a/src/atlas_frontend/src/components/Task/tasks/GenericTask.tsx
+++ b/src/atlas_frontend/src/components/Task/tasks/GenericTask.tsx
@@ -1,11 +1,11 @@
-import React from "react";
+import React, { useMemo } from "react";
import { useState } from "react";
import type {
_SERVICE,
TaskType,
} from "../../../../../declarations/atlas_space/atlas_space.did";
import Button from "../../Shared/Button";
-import { useForm, type SubmitHandler } from "react-hook-form";
+import { useFieldArray, useForm, type SubmitHandler } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import {
@@ -32,21 +32,13 @@ interface GenericTaskProps {
disabled?: boolean;
}
-interface GenericTaskFormInput {
+interface TextFormData {
taskSubmission: string;
}
-const maxDescriptionLength = 500;
-
-const schema = yup.object({
- taskSubmission: yup
- .string()
- .max(maxDescriptionLength)
- .trim()
- .min(2)
- .required()
- .label("Task submission"),
-});
+interface ListFormData {
+ items: { value: string }[];
+}
const GenericTask = ({
genericTask,
@@ -58,22 +50,49 @@ const GenericTask = ({
disabled = false,
}: GenericTaskProps) => {
const dispatch = useDispatch();
- const { user } = useAuth();
+ const { user, connect } = useAuth();
const [openSubmission, setSubmission] = useState(false);
- const { register, handleSubmit} = useForm({
- resolver: yupResolver(schema),
+ const authAtlasSpace = useAuthAtlasSpaceActor(spacePrincipal);
+ const answerFormatKey = "TitleAndDescription" in genericTask.task_content
+ ? (Object.keys(genericTask.task_content.TitleAndDescription.answer_format)[0] as
+ | "Small"
+ | "Paragraph"
+ | "Long"
+ | "List")
+ : null;
+
+ const maxTextLength = useMemo(() => {
+ switch (answerFormatKey) {
+ case "Small":
+ return 254;
+ case "Paragraph":
+ return 600;
+ case "Long":
+ return 2500;
+ default:
+ return 254;
+ }
+ }, [answerFormatKey]);
+
+ const textSchema = yup.object({
+ taskSubmission: yup
+ .string()
+ .trim()
+ .min(2)
+ .max(maxTextLength)
+ .required("Field is required"),
+ });
+
+ const textForm = useForm({
+ resolver: yupResolver(textSchema),
defaultValues: {
- taskSubmission: "",
+ taskSubmission: ""
},
});
- const { connect } = useAuth();
- const authAtlasSpace = useAuthAtlasSpaceActor(spacePrincipal);
- const handleSubmitResponse: SubmitHandler = async ({
- taskSubmission,
- }) => {
+ const onSubmitText: SubmitHandler = async ({ taskSubmission }) => {
if (!authAtlasSpace || !unAuthAtlasSpace) return;
-
+
await runWithLoading(async () => {
const call = submitSubtaskSubmission({
authAtlasSpace,
@@ -88,7 +107,61 @@ const GenericTask = ({
});
setSubmission(false);
- getSpaceTasks({
+ await getSpaceTasks({
+ spaceId: spacePrincipal.toString(),
+ unAuthAtlasSpace,
+ dispatch,
+ });
+ }, dispatch, () => setSubmission(false));
+ };
+
+ const listSchema = yup.object({
+ items: yup
+ .array()
+ .of(
+ yup.object({
+ value: yup
+ .string()
+ .trim()
+ .max(254, "Item too long")
+ .required("Item cannot be empty"),
+ })
+ )
+ .max(25, "Max 25 items allowed")
+ .required(),
+});
+
+const listForm = useForm({
+ resolver: yupResolver(listSchema),
+ defaultValues: {
+ items: [{ value: "" }]
+ },
+});
+
+ const { fields, append, remove } = useFieldArray({
+ control: listForm.control,
+ name: "items",
+ });
+
+ const onSubmitList = async () => {
+ if (!authAtlasSpace || !unAuthAtlasSpace) return;
+
+ const items = listForm.getValues().items.map(item => item.value.trim());
+ await runWithLoading(async () => {
+ const call = submitSubtaskSubmission({
+ authAtlasSpace,
+ taskId: BigInt(taskId),
+ subtaskId: BigInt(subtaskId),
+ submission: { List: { items } },
+ });
+ await toast.promise(call, {
+ loading: "Submitting list...",
+ success: "Submitted response.",
+ error: getErrorWithInfoToast("Failed to submit response."),
+ });
+
+ setSubmission(false);
+ await getSpaceTasks({
spaceId: spacePrincipal.toString(),
unAuthAtlasSpace,
dispatch,
@@ -112,10 +185,8 @@ const GenericTask = ({
);
const rawState = Object.keys(submissionData?.state || {})[0] ?? null;
-
const validStates = ["Rejected", "WaitingForReview", "Accepted"] as const;
type SubmissionState = typeof validStates[number];
-
const submissionState = validStates.includes(rawState as SubmissionState)
? (rawState as SubmissionState)
: null;
@@ -161,19 +232,54 @@ const GenericTask = ({
)}
{canSubmit && openSubmission && !disabled && (
-
- )}
+ <>
+ {answerFormatKey !== "List" ? (
+
+ ) : (
+
+ )}
+ >
+ )}
+
{canSubmit && !openSubmission && !disabled && (
|