Skip to content
Snippets Groups Projects
Commit 1e6d488d authored by Peter J's avatar Peter J
Browse files

test variants

parent 1f556b1c
No related branches found
No related tags found
No related merge requests found
Showing
with 461 additions and 72 deletions
package cz.cvut.fel.pro.etmt.config;
import cz.cvut.fel.pro.etmt.model.library.Category;
import cz.cvut.fel.pro.etmt.model.library.Item;
import cz.cvut.fel.pro.etmt.model.library.Question;
import cz.cvut.fel.pro.etmt.model.library.TestTemplate;
import cz.cvut.fel.pro.etmt.payload.library.CategoryPayload;
import cz.cvut.fel.pro.etmt.payload.library.ItemPayload;
import cz.cvut.fel.pro.etmt.payload.library.QuestionPayload;
import cz.cvut.fel.pro.etmt.payload.library.TestTemplatePayload;
import cz.cvut.fel.pro.etmt.model.library.*;
import cz.cvut.fel.pro.etmt.payload.library.*;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
......@@ -28,10 +22,12 @@ public class MapperConfig {
modelMapper.typeMap(QuestionPayload.class, Item.class).setConverter(converterWithDestinationSupplier(Question::new));
modelMapper.typeMap(CategoryPayload.class, Item.class).setConverter(converterWithDestinationSupplier(Category::new));
modelMapper.typeMap(TestTemplatePayload.class, Item.class).setConverter(converterWithDestinationSupplier(TestTemplate::new));
modelMapper.typeMap(TestVariantPayload.class, Item.class).setConverter(converterWithDestinationSupplier(TestVariant::new));
modelMapper.typeMap(Question.class, ItemPayload.class).setConverter(converterWithDestinationSupplier(QuestionPayload::new));
modelMapper.typeMap(Category.class, ItemPayload.class).setConverter(converterWithDestinationSupplier(CategoryPayload::new));
modelMapper.typeMap(TestTemplate.class, ItemPayload.class).setConverter(converterWithDestinationSupplier(TestTemplatePayload::new));
modelMapper.typeMap(TestVariant.class, ItemPayload.class).setConverter(converterWithDestinationSupplier(TestVariantPayload::new));
return modelMapper;
}
......
package cz.cvut.fel.pro.etmt.controller;
import cz.cvut.fel.pro.etmt.model.library.Category;
import cz.cvut.fel.pro.etmt.model.library.Question;
import cz.cvut.fel.pro.etmt.model.library.TestTemplate;
import cz.cvut.fel.pro.etmt.model.library.TestVariant;
import cz.cvut.fel.pro.etmt.payload.library.QuestionPayload;
import cz.cvut.fel.pro.etmt.payload.library.TestVariantPayload;
import cz.cvut.fel.pro.etmt.service.ItemService;
import cz.cvut.fel.pro.etmt.service.TestVariantService;
import javassist.NotFoundException;
import lombok.AllArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@RestController
@RestControllerAdvice
@AllArgsConstructor
@RequestMapping("/tests")
public class TestVariantController {
private ModelMapper modelMapper;
private TestVariantService testVariantService;
private ItemService itemService;
@GetMapping("/{id}")
public ResponseEntity<TestVariantPayload> getQuestionById(@PathVariable final String id) {
var optionalItem = itemService.getItemById(id, TestVariant.class);
if (optionalItem.isEmpty()) {
return ResponseEntity.notFound().build();
}
var item = optionalItem.get();
var payload = modelMapper.map(item, TestVariantPayload.class);
return ResponseEntity.ok().body(payload);
}
@GetMapping("/generate")
public ResponseEntity<List<TestVariant>> generate(@RequestParam final String testTemplateId) throws Exception {
var optionalTemplate = itemService.getItemById(testTemplateId, TestTemplate.class);
if (optionalTemplate.isEmpty()) {
throw new NotFoundException("can't find test template for given id");
}
var template = (TestTemplate) optionalTemplate.get();
var category = itemService.addItem(Category.builder()
.title(String.format("Test variants - %s", template.getTitle()))
.parentId(template.getParentId())
.build(), Category.class);
var generatedQuestions = testVariantService.generateCases(template);
var variants = new ArrayList<TestVariant>();
for (var i = 0; i < generatedQuestions.length; i++) {
var combo = generatedQuestions[i];
var testVariant = new TestVariant();
testVariant.setQuestions(
Stream.of(combo)
.filter(o -> o instanceof Question)
.map(o -> (Question) o)
.collect(Collectors.toList())
);
testVariant.setParentId(category.getId());
testVariant.setTitle(String.format("Variant %d", i + 1));
testVariant.setLeaf(true);
testVariant = itemService.addItem(testVariant, TestVariant.class);
variants.add(testVariant);
}
return ResponseEntity.ok().body(variants);
}
}
package cz.cvut.fel.pro.etmt.model.library;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.ArrayList;
import java.util.List;
@Document("item")
......@@ -13,6 +16,14 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true)
public class TestVariant extends Item {
private List<String> solution;
@DBRef
private List<Question> questions = new ArrayList<>();
private List<String> solution = new ArrayList<>();
@Builder
public TestVariant(String id, String parentId, String title, String key) {
super(id, parentId, title, key, true);
}
}
......@@ -15,7 +15,8 @@ import javax.validation.constraints.NotNull;
@JsonSubTypes({
@JsonSubTypes.Type(value = CategoryPayload.class, name = "category"),
@JsonSubTypes.Type(value = QuestionPayload.class, name = "question"),
@JsonSubTypes.Type(value = TestTemplatePayload.class, name = "template")
@JsonSubTypes.Type(value = TestTemplatePayload.class, name = "template"),
@JsonSubTypes.Type(value = TestVariantPayload.class, name = "variant")
})
public abstract class ItemPayload {
......
package cz.cvut.fel.pro.etmt.payload.library;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.cvut.fel.pro.etmt.model.library.Question;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class TestVariantPayload extends ItemPayload {
@JsonProperty("isLeaf")
private boolean isLeaf = true;
private List<Question> questions;
private List<String> solution;
}
......@@ -3,6 +3,7 @@ package cz.cvut.fel.pro.etmt.service;
import cz.cvut.fel.pro.etmt.model.User;
import cz.cvut.fel.pro.etmt.model.library.Category;
import cz.cvut.fel.pro.etmt.model.library.Item;
import cz.cvut.fel.pro.etmt.model.library.Question;
import cz.cvut.fel.pro.etmt.repository.ItemRepository;
import javassist.NotFoundException;
import lombok.AllArgsConstructor;
......@@ -11,7 +12,7 @@ import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
import javax.xml.catalog.Catalog;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
......@@ -46,6 +47,21 @@ public class ItemService {
return itemRepository.findById(id).map(clazz::cast);
}
public List<Question> getQuestionsWithGivenPointsFromSubtree(@NotNull final Category root, @NotNull final Integer points) {
List<Question> questions = new ArrayList<>();
for (var child : root.getChildren()) {
if (child instanceof Question) {
var question = (Question) child;
if (question.getPoints().equals(points)) {
questions.add((Question)child);
}
} else if (child instanceof Category) {
questions.addAll(getQuestionsWithGivenPointsFromSubtree((Category)child, points));
}
}
return questions;
}
/**
* Adds an item to user's library
*
......
package cz.cvut.fel.pro.etmt.service;
import com.github.jesg.dither.Dither;
import cz.cvut.fel.pro.etmt.model.library.Category;
import cz.cvut.fel.pro.etmt.model.library.Question;
import cz.cvut.fel.pro.etmt.model.library.TestTemplate;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.sql.Array;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
@Slf4j
public class DitherService {
public class TestVariantService {
private ItemService itemService;
private void log2DArray(Object[][] array) {
log.info(Arrays.deepToString(array)
......@@ -16,33 +26,61 @@ public class DitherService {
.replaceAll("[\\[\\]]", " "));
}
public void generateCases() {
Object[][] questions = Dither.aetg(2, new Object[][]{
public Object[][] generateCases(final TestTemplate testTemplate) throws Exception {
var root = itemService.getItemById(testTemplate.getParentId(), Category.class).get();
var topics = testTemplate.getTopics();
var equivalenceClasses = new ArrayList<List<Question>>();
for (var topic : topics) {
var questions = itemService.getQuestionsWithGivenPointsFromSubtree(root, topic.getPoints());
var partitions = partition(questions, topic.getQuestionCount());
equivalenceClasses.addAll(partitions);
}
var aetgInput = equivalenceClasses.stream()
.map(Collection::toArray)
.toArray(Object[][]::new);
Object[][] generatedQuestions = Dither.aetg(2, aetgInput);
log2DArray(generatedQuestions);
return generatedQuestions;
}
public void testGenerate() {
Object[][] r1 = Dither.aetg(2, new Object[][]{
new Object[]{"3b_q1", "3b_q2", "3b_q3"},
new Object[]{"3b_q4", "3b_q5", "3b_q6"},
new Object[]{"5b_q1", "5b_q2"},
new Object[]{"8b_q1", "8b_q2"}});
log2DArray(r1);
Object[][] r2 = Dither.aetg(2, new Object[][]{
new Object[]{"3b_q1", "3b_q2"},
new Object[]{"3b_q4", "3b_q5", "3b_q6"},
new Object[]{"5b_q1", "5b_q2"},
new Object[]{"8b_q1", "8b_q2"}});
log2DArray(questions);
log2DArray(r2);
Object[][] results3 = Dither.aetg(2, 0, new Object[][]{
Object[][] r3 = Dither.aetg(2, 0, new Object[][]{
new Object[]{"3b_q1", "3b_q2"},
new Object[]{"3b_q4", "3b_q5", "3b_q6"},
new Object[]{"5b_q1", "5b_q2"},
new Object[]{"8b_q1", "8b_q2"}},
new Integer[][]{
},
questions);
log2DArray(results3);
r2);
log2DArray(r3);
Object[][] results4 = Dither.aetg(2, 0, new Object[][]{
Object[][] r4 = Dither.aetg(2, 0, new Object[][]{
new Object[]{"3b_q1", "3b_q2"},
new Object[]{"3b_q4", "3b_q5", "3b_q6"},
new Object[]{"5b_q1", "5b_q2"},
new Object[]{"8b_q1", "8b_q2"}},
new Integer[][]{
},
results3);
log2DArray(results4);
r3);
log2DArray(r4);
// 3-way IPOG
......@@ -81,4 +119,24 @@ public class DitherService {
log2DArray(resultsAetg);*/
}
static <T> Collection<List<T>> partition(List<T> inputList, int partitionCount) throws Exception {
var avgPartSize = inputList.size() / partitionCount;
if (avgPartSize < 2) {
throw new Exception("partitions can't be smaller than size 2");
}
var remainder = inputList.size() % avgPartSize;
var partitions = new ArrayList<List<T>>();
for (var i = 0; i < partitionCount; i++) {
var partitionSize = avgPartSize;
if (i + 1 == partitionCount) {
partitionSize += remainder;
}
partitions.add(inputList.subList(i, partitionSize));
}
return partitions;
}
}
package cz.cvut.fel.pro.etmt.service;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class TestVariantServiceTest {
@InjectMocks
private TestVariantService testSubject;
@Test
public void testGenerate() {
testSubject.testGenerate();
}
}
import { Variant } from '@testing-library/react';
import { API_BASE_URL } from '../Constants';
import { Category, Nullable, Question, SignInResponse, Template, UserCredentialsPayload } from './Common';
......@@ -115,6 +116,20 @@ export const apiGetTemplate = (id: string, token: Nullable<string>) => {
}, token);
}
export const apiGetVariants = (templateId: string, token: Nullable<string>) => {
return apiRequest<Array<Variant>>({
url: API_BASE_URL + "/tests/generate?testTemplateId=" + templateId,
method: RequestMethod.GET,
}, token)
}
export const apiGetVariant = (id: string, token: Nullable<string>) => {
return apiRequest<Variant>({
url: API_BASE_URL + "/tests/" + id,
method: RequestMethod.GET
}, token);
}
export const apiGetLibrary = (token: Nullable<string>) => {
return apiRequest<Array<any>>({
url: API_BASE_URL + "/library",
......
......@@ -38,6 +38,10 @@ export interface Template extends Item {
topics: Array<Topic>
}
export interface Variant extends Item {
questions: Array<Question>
}
export interface Topic {
questionCount: number,
points: number
......
......@@ -44,7 +44,7 @@ const AddTemplateModal: FunctionComponent<ItemModalProps> = (props: ItemModalPro
.then(() => {
notification.success({
message: SUCCESS_MSG,
description: 'Category added!'
description: 'Template added!'
});
props.modalState.setIsModalVisible(false);
props.onModalClose();
......@@ -113,17 +113,17 @@ const AddTemplateModal: FunctionComponent<ItemModalProps> = (props: ItemModalPro
<Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
<Form.Item
{...restField}
name={[name, 'first']}
name={[name, 'questionCount']}
rules={[{ required: true, message: 'Missing question count' }]}
>
<Input placeholder="1" />
<Input placeholder="Questions count" />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'last']}
name={[name, 'points']}
rules={[{ required: true, message: 'Missing points' }]}
>
<Input placeholder="3" />
<Input placeholder="Points" />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</Space>
......
import { Button, Modal, notification } from "antd";
import { FunctionComponent, useEffect, useState } from "react";
import { apiGetVariants } from "../../common/APIUtils";
import { ERR_MSG, SUCCESS_MSG } from "../../common/Common";
import { useAuth } from "../auth/Auth";
import { ItemModalProps } from "../dashboard/Library";
const GenerateModal: FunctionComponent<ItemModalProps> = (props: ItemModalProps) => {
const auth = useAuth();
const [confirmLoading, setConfirmLoading] = useState(false);
const onGenerate = () => {
console.log("generate tests under: ", props.selectedItem);
setConfirmLoading(true);
setTimeout(
() => {
apiGetVariants(props.selectedItem.id, auth.user?.token)
.then(() => {
notification.success({
message: SUCCESS_MSG,
description: 'Variants generated!'
});
props.modalState.setIsModalVisible(false);
props.onModalClose();
}).catch(error => {
console.error(error);
notification.error({
message: ERR_MSG,
description: 'There was an error while generating test variants... Please try again.'
})
}).finally(() => setConfirmLoading(false))
},
500
);
}
return (
<Modal
title={"Generate test variants from " + props.selectedItem.title}
visible={props.modalState.isModalVisible}
confirmLoading={confirmLoading}
footer={[
<Button type="primary" loading={confirmLoading} onClick={onGenerate}>
Generate
</Button>
]}
>
</Modal>
);
}
export default GenerateModal;
import { DeleteOutlined, EditOutlined, FileOutlined, FileUnknownOutlined, FolderOutlined, FrownFilled, FrownOutlined, MehOutlined, PlusCircleOutlined, PropertySafetyFilled, QuestionCircleOutlined, SmileOutlined } from "@ant-design/icons";
import { Button, Card, Divider, Modal, Skeleton, Tooltip, Typography } from "antd";
import Meta from "antd/lib/card/Meta";
import { Content } from "antd/lib/layout/layout";
import Title from "antd/lib/skeleton/Title";
import Tree, { DataNode } from "antd/lib/tree";
import DirectoryTree from "antd/lib/tree/DirectoryTree";
import { DeleteOutlined, EditOutlined, FileOutlined, FileTextOutlined, FileUnknownOutlined, FolderOutlined, QuestionCircleOutlined } from "@ant-design/icons";
import { Button, Card, Divider, Table, Tooltip } from "antd";
import Tree from "antd/lib/tree";
import { FunctionComponent, useEffect, useState } from "react";
import { isJSDocReturnTag } from "typescript";
import { apiGetLibrary, apiGetQuestion, apiGetTemplate, apiPostQuestion } from "../../common/APIUtils";
import { apiGetLibrary, apiGetQuestion, apiGetTemplate, apiGetVariant } from "../../common/APIUtils";
import { Topic } from "../../common/Common";
import { useAuth } from "../auth/Auth";
import AddQuestionModal from "../questions/AddQuestion";
import AddCategoryModal from "./AddCategory";
import AddTemplateModal from "./AddTemplateModal";
import GenerateModal from "./GenerateModal";
import RemoveItemModal from "./RemoveItemModal";
export enum ItemType {
Category = "category",
Question = "question",
Template = "template"
Template = "template",
Variant = "variant"
}
export const getIcon = (type: ItemType) => {
......@@ -28,6 +26,8 @@ export const getIcon = (type: ItemType) => {
return <FileOutlined />
case ItemType.Category:
return <FolderOutlined />
case ItemType.Variant:
return <FileTextOutlined />
default:
return <FileUnknownOutlined />
}
......@@ -56,9 +56,21 @@ const ItemInfo: FunctionComponent<ItemInfoProps> = (props: ItemInfoProps) => {
const [isAddCategoryModalVisible, setIsAddCategoryModalVisible] = useState<boolean>(false);
const [isAddTemplateModalVisible, setIsAddTemplateModalVisible] = useState<boolean>(false);
const [isRemoveItemModalVisible, setIsRemoveItemModalVisible] = useState<boolean>(false);
const [isGenerateModalVisible, setIsGenerateModalVisible] = useState<boolean>(false);
const [fetchedItem, setFetchedItem] = useState<any | null>(null);
var actions = [
<Tooltip title="Edit">
<EditOutlined key="edit" />
</Tooltip>,
<Tooltip title="Delete">
<DeleteOutlined key="delete" onClick={() => setIsRemoveItemModalVisible(true)} />
</Tooltip>,
];
var content = null;
// on selectedItem state update
useEffect(() => {
if (props.selectedItem === null) {
......@@ -68,6 +80,15 @@ const ItemInfo: FunctionComponent<ItemInfoProps> = (props: ItemInfoProps) => {
console.log("selectedItem: ", props.selectedItem);
setFetchedItem(null);
actions = [
<Tooltip title="Edit">
<EditOutlined key="edit" />
</Tooltip>,
<Tooltip title="Delete">
<DeleteOutlined key="delete" onClick={() => setIsRemoveItemModalVisible(true)} />
</Tooltip>,
];
switch (props.selectedItem.type) {
case ItemType.Category:
break;
......@@ -89,57 +110,138 @@ const ItemInfo: FunctionComponent<ItemInfoProps> = (props: ItemInfoProps) => {
apiGetTemplate(props.selectedItem.id, auth.user.token)
.then(response => {
console.log("fetched template: ", response);
setFetchedItem(response);
}).catch(error => {
console.error("error fetching template: ", error);
}).finally(() => setIsLoading(false));
}, 500);
break;
case ItemType.Variant:
setIsLoading(true);
setTimeout(() => {
apiGetVariant(props.selectedItem.id, auth.user.token)
.then(response => {
console.log("fetched variant: ", response);
setFetchedItem(response);
}).catch(error => {
console.error("error fetching variant: ", error);
}).finally(() => setIsLoading(false));
}, 500);
break;
default:
console.error("unknown item type: ", props.selectedItem.type);
break;
}
}, [props.selectedItem]);
const actions = (props.selectedItem.type === "category") ?
[
<Tooltip title="Edit">
<EditOutlined key="edit" />
</Tooltip>,
<Tooltip title="Delete">
<DeleteOutlined key="delete" onClick={() => setIsRemoveItemModalVisible(true)} />
</Tooltip>,
<Tooltip title="Add subcategory">
<FolderOutlined key="addSubcategory" onClick={() => setIsAddCategoryModalVisible(true)} />
</Tooltip>,
<Tooltip title="Add question">
<QuestionCircleOutlined key="addQuestion" onClick={() => setIsAddQuestionModalVisible(true)} />
</Tooltip>,
<Tooltip title="Add template">
<FileOutlined key="addTemplate" onClick={() => setIsAddTemplateModalVisible(true)} />
</Tooltip>
] :
[
<Tooltip title="Edit">
<EditOutlined key="edit" />
</Tooltip>,
<Tooltip title="Delete">
<DeleteOutlined key="delete" onClick={() => setIsRemoveItemModalVisible(true)} />
</Tooltip>,
];
switch (props.selectedItem.type) {
case ItemType.Category:
actions = [
<Tooltip title="Edit">
<EditOutlined key="edit" />
</Tooltip>,
<Tooltip title="Delete">
<DeleteOutlined key="delete" onClick={() => setIsRemoveItemModalVisible(true)} />
</Tooltip>,
<Tooltip title="Add subcategory">
<FolderOutlined key="addSubcategory" onClick={() => setIsAddCategoryModalVisible(true)} />
</Tooltip>,
<Tooltip title="Add question">
<QuestionCircleOutlined key="addQuestion" onClick={() => setIsAddQuestionModalVisible(true)} />
</Tooltip>,
<Tooltip title="Add template">
<FileOutlined key="addTemplate" onClick={() => setIsAddTemplateModalVisible(true)} />
</Tooltip>
];
content = <div>
<h4>{fetchedItem?.title} ({fetchedItem?.type})</h4>
</div>;
break;
case ItemType.Template:
actions = [
<Tooltip title="Edit">
<EditOutlined key="edit" />
</Tooltip>,
<Tooltip title="Delete">
<DeleteOutlined key="delete" onClick={() => setIsRemoveItemModalVisible(true)} />
</Tooltip>,
<Tooltip title="Generate test variants">
<FileTextOutlined key="generate" onClick={() => setIsGenerateModalVisible(true)} />
</Tooltip>,
];
content = <div>
<h4>{fetchedItem?.title} ({fetchedItem?.type})</h4>
<Table columns={[
{
title: 'Topic',
dataIndex: 'topic',
key: 'topic'
},
{
title: 'Question count',
dataIndex: 'questionCount',
key: 'questionCount',
},
{
title: 'Points',
dataIndex: 'points',
key: 'points',
}
]} dataSource={fetchedItem?.topics.map((item: Topic) => {
var n = fetchedItem?.topics.indexOf(item) + 1;
return {
key: n,
topic: "Topic " + n,
questionCount: item.questionCount,
points: item.points
}
}) as Array<any>} />
</div>;
break;
case ItemType.Variant:
actions = [
<Tooltip title="Edit">
<EditOutlined key="edit" />
</Tooltip>,
<Tooltip title="Delete">
<DeleteOutlined key="delete" onClick={() => setIsRemoveItemModalVisible(true)} />
</Tooltip>,
];
content = <div>
<h4>{fetchedItem?.title} ({fetchedItem?.type})</h4>
</div>;
break;
default:
actions = [
<Tooltip title="Edit">
<EditOutlined key="edit" />
</Tooltip>,
<Tooltip title="Delete">
<DeleteOutlined key="delete" onClick={() => setIsRemoveItemModalVisible(true)} />
</Tooltip>,
];
content = <div>
<h4>{fetchedItem?.title} ({fetchedItem?.type})</h4>
</div>;
break;
}
return (
<Card
style={{ marginTop: 16 }}
actions={actions}
>
<Skeleton loading={isLoading}>
<div>
<h4>{fetchedItem?.title}</h4>
<p>
{fetchedItem?.text}
</p>
</div>
</Skeleton>
{content}
<AddQuestionModal
modalState={
{
......@@ -173,6 +275,17 @@ const ItemInfo: FunctionComponent<ItemInfoProps> = (props: ItemInfoProps) => {
onModalClose={props.onContentUpdate}
/>
<GenerateModal
modalState={
{
isModalVisible: isGenerateModalVisible,
setIsModalVisible: setIsGenerateModalVisible
}
}
selectedItem={props.selectedItem}
onModalClose={props.onContentUpdate}
/>
<RemoveItemModal
modalState={
{
......@@ -238,7 +351,6 @@ const Library: FunctionComponent<LibraryProps> = () => {
<Tree
defaultExpandAll
showIcon={true}
/* showLine={true} */
onSelect={onSelect}
treeData={treeData}
/>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment