Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Last refactorings in Create card #97

Merged
merged 8 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions frontend/src/feature/cards/components/CardDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<v-card-text>
<h4 class="pa-2 ma-2">{{ card.question }}</h4>
<v-container class="pa-2 mt-2">
<v-form @submit.prevent @keydown.enter="highlightCorrectAnswers" class="pa-2 mt-2">
<v-text-field v-if="card.type === CardType.SIMPLEQA" v-model="providedAnswer"
density="compact" label="Your Answer"
:class="{'correct': answerShown && isCorrect(providedAnswer),
Expand All @@ -25,17 +25,15 @@
'error': selected.includes(option) && answerShown && !isCorrect(option),
'selected': !answerShown && selected.includes(option)}"/>
</v-list>
</v-container>
<v-row class="mt-3 pa-3 d-flex justify-space-around">
<v-btn @click="highlightCorrectAnswers" :disabled="!selected && !providedAnswer"
prepend-icon="mdi-check-circle" color="green" variant="outlined">
Check Answer
</v-btn>
</v-row>
</v-form>
</v-card-text>

<v-container>
<v-row class="pa-3 d-flex justify-space-around">
<v-btn @click="highlightCorrectAnswers()" :disabled="!selected && !providedAnswer"
prepend-icon="mdi-check-circle" color="green" variant="outlined">
Check Answer
</v-btn>
</v-row>
</v-container>

<v-card-actions class="pa-2 ma-0">
<v-spacer/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<base-card-page>
<v-card-title class="text-center text-h4">Edit card of type {{ translateType(card.type) }}</v-card-title>
<v-card-title class="text-center text-h4">{{ mode }} card of type {{ translateType(card.type) }}</v-card-title>
<v-card-subtitle class="text-center">Submit Changes with Enter or Save button</v-card-subtitle>

<v-card-text>
Expand All @@ -26,7 +26,7 @@

<v-card-actions class="pa-2 ma-0">
<v-spacer/>
<save-mdi-button :disabled="!formValid" :click-handler="() => emit('update', newCard)"/>
<save-mdi-button :disabled="!formValid" :click-handler="emitAction"/>
<cancel-mdi-button tooltip-text="Reset content"
:click-handler="resetNewCard"/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Expand All @@ -50,15 +50,21 @@ import BaseCardPage from "@/shared/pages/BaseCardPage.vue";

const props = defineProps<({
card: Card,
mode: 'Edit' | 'Add',
})>();

const emit = defineEmits<({
'close': [val: boolean],
'post': [val: Card],
'update': [val: Card],
})>();

const newCard = ref<Card>(clone(props.card));
const formValid = ref(false);

const emitAction = () => props.mode === 'Edit'
? emit('update', newCard.value)
: emit('post', newCard.value);

const resetNewCard = () => newCard.value = clone(props.card);
</script>
1 change: 1 addition & 0 deletions frontend/src/feature/cards/components/RadioButtonCol.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<div class="d-flex flex-wrap justify-sm-space-between ma-n3">
<v-radio-group v-for="index in Array.from({ length: groupSize }, (_, ind) => ind)" :key="index"
:model-value="model"
:rules="[v => v != undefined || 'Choose!']"
@update:model-value="val => model = val!">
<v-radio :value="index" class="ml-n4 mt-n2 mb-n2" label="correct" color="green"/>
</v-radio-group>
Expand Down
35 changes: 8 additions & 27 deletions frontend/src/feature/cards/model/card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,33 +42,14 @@ export const clone = (card: Card) : Card => {
};
};

export const generateNewCard = (cardType: CardType) : Card =>{
const card = {} as Card;

// shared fields of all types of card
card.tags = [];
card.title = '';
card.question = '';

switch (cardType) {
case CardType.SIMPLEQA:
card.type = CardType.SIMPLEQA;
card.answer = '';
break;
case CardType.MULTIPLE_CHOICE:
card.type = CardType.MULTIPLE_CHOICE;
card.correctOptions = [];
card.options = [''];
break;
case CardType.SINGLE_CHOICE:
card.type = CardType.SINGLE_CHOICE;
card.options = [''];
card.correctOption = 0;
break;
default:
break;
}
return card;
export const emptyCard = (cardType: CardType) : Card =>{
return {
type: cardType,
question: '',
tags: [],
options: [],
correctOptions: [],
};
};

export const translateType = (type: CardType) : string => {
Expand Down
101 changes: 55 additions & 46 deletions frontend/src/feature/cards/pages/CardsPage.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
<template>
<v-container :hidden="displayDetails || displayEdit">
<v-container :hidden="uiState.display !== 'cards'">
<v-card class="pa-2 ma-2 mx-auto d-flex flex-column justify-space-between" color="secondary" fill-height>
<v-card-title v-text="`Cards in ${categoryName}`" class="text-center text-h4"/>
<div class="d-flex flex-row justify-space-between gc-12">
<v-select
v-model="cardType"
label="Select type of new card"
:items="[CardType.SIMPLEQA, CardType.MULTIPLE_CHOICE, CardType.SINGLE_CHOICE]"
></v-select>
<add-mdi-button tooltip-text="create card" :click-handler="addCard"/>
</div>

<v-row class="mr-0">
<v-col md="7"/>
<v-col cols="12" md="3">
<v-select v-if="uiState.selectActive" v-model="cardType" label="Select type of new card"
:items="[CardType.SIMPLEQA, CardType.MULTIPLE_CHOICE, CardType.SINGLE_CHOICE]"/>
</v-col>
<v-col md="1"/>
<v-col cols="12" md="1" class="mb-4">
<add-mdi-button tooltip-text="Create new card" :click-handler="addButtonClicked"/>
</v-col>
</v-row>

<v-form @submit.prevent="filter.set = filter.input">
<v-text-field clearable @click:clear="filter.input=''" v-model="filter.input"
Expand All @@ -20,74 +22,81 @@
</v-card>
</v-container>

<card-details v-if="displayDetails" :card="card" @close="displayDetails = false" @edit="toggleEdit"
@delete="deleteCard"/>
<card-edit v-if="displayEdit" :card="card" @close="toggleEdit" @update="updateCard"/>
<card-details v-if="uiState.display === 'details'" :card="card"
@close="uiState.display = 'cards'" @edit="editForm" @delete="deleteCard"/>
<card-form v-if="uiState.display === 'form'" :mode="uiState.formMode" :card="card"
@close="closeForm" @update="updateCard" @post="addCard"/>
</template>

<script setup lang="ts">
import {ref, shallowRef} from "vue";
import {Card, CardType, generateNewCard} from "@/feature/cards/model/card.ts";
import useCardsService from "@/feature/cards/composables/useCardsService.ts";
import {useRoute} from "vue-router";
import CardDetails from "@/feature/cards/components/CardDetails.vue";
import CardItemScroller from "@/feature/cards/components/CardItemScroller.vue";
import {useRoute} from "vue-router";
import CardEdit from "@/feature/cards/components/CardEdit.vue";
import AddMdiButton from "@/shared/buttons/AddMdiButton.vue";
import CardForm from "@/feature/cards/components/CardForm.vue";
import useCardsService from "@/feature/cards/composables/useCardsService.ts";
import {Card, CardType, emptyCard} from "@/feature/cards/model/card.ts";

const props = defineProps<({
categoryId: string
})>();

const categoryName = useRoute().query.name;
const filter = ref({input: "", set: ""});
const displayDetails = shallowRef(false);
const displayEdit = shallowRef(false);
const uiState = ref<{
display: 'cards' | 'form' | 'details', formMode: 'Edit' | 'Add', selectActive: boolean
}>({
display: 'cards',
formMode: 'Edit',
selectActive: false
});
const toggleReload = shallowRef(false);
const displayCreate = ref(false);
const card = ref<Card>({} as Card);
const cardType = ref<CardType>({} as CardType);

cardType.value = CardType.SIMPLEQA;
const cardType = ref<CardType>(CardType.SIMPLEQA);

const openCard = async (id: string) => {
displayDetails.value = true;
uiState.value.display = 'details';
card.value = await useCardsService().getCardById(id, props.categoryId);
};

const addCard = async () => {
card.value = generateNewCard(cardType.value);
displayCreate.value = true;
displayEdit.value = true;
const addButtonClicked = () => {
// If no card type selection shown, show it, else open add form with empty card of selected type
if (uiState.value.selectActive) {
card.value = emptyCard(cardType.value);
uiState.value.formMode = 'Add';
uiState.value.display = 'form';
}
uiState.value.selectActive = !uiState.value.selectActive;
};

const addCard = async (newCard: Card) => {
await useCardsService().postNewCard(props.categoryId, newCard);
reloadAndShowCards();
};

const updateCard = async (newCard: Card) => {
newCard.tags = newCard.tags.filter(tag => !!tag);
if (displayCreate.value) {
await useCardsService().postNewCard(props.categoryId, newCard);
card.value = {} as Card;
} else {
card.value = await useCardsService().putCard(props.categoryId, newCard);
}
toggleReload.value = !toggleReload.value;
toggleEdit();
card.value = await useCardsService().putCard(props.categoryId, newCard);
reloadAndShowCards();
};

const deleteCard = async (curCard: Card) => {
await useCardsService().deleteCard(props.categoryId, curCard);
reloadAndShowCards();
};

const reloadAndShowCards = () => {
toggleReload.value = !toggleReload.value;
displayEdit.value = false;
displayDetails.value = false;
uiState.value.display = 'cards';
};

const toggleEdit = () => {
if (!displayCreate.value) {
displayDetails.value = !displayDetails.value;
displayEdit.value = !displayEdit.value;
} else {
displayCreate.value = false;
displayEdit.value = false;
}
const editForm = () => {
uiState.value.formMode = 'Edit';
uiState.value.display = 'form';
};

const closeForm = () => { // switch display to details for edit mode, cards for add mode
uiState.value.display = uiState.value.formMode === 'Edit' ? 'details' : 'cards';
};
</script>
12 changes: 5 additions & 7 deletions frontend/src/feature/dataio/pages/DataExport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@
<v-card-item>
<v-img src="@/assets/folder.jpg" aspect-ratio="2.25" class="mx-auto"/>
<v-label class="float-right text-sm-subtitle-2 text-grey">Photo by&nbsp;<a
href="https://unsplash.com/@qwitka?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Maksym Kaharlytskyi</a>&nbsp;on&nbsp;<a
href="https://unsplash.com/@qwitka?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Maksym
Kaharlytskyi</a>&nbsp;on&nbsp;<a
href="https://unsplash.com/photos/file-cabinet-Q9y3LRuuxmg?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
</v-label>
</v-card-item>

<v-card-text class="mt-5 mb-5">
<v-row justify="center">
<v-form cols="12" sm="6" md="4">
<input type="submit" hidden/><!-- Required for the form to submit on enter -->
<v-btn color="black" border @click="exportData" variant="text">
Start Export
</v-btn>
</v-form>
<v-btn color="black" border @click="exportData" variant="text">
Start Export
</v-btn>
</v-row>
</v-card-text>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.web.servlet.MockMvc;

Expand All @@ -35,7 +34,6 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(classes = TestMongoConfiguration.class)
@TestPropertySource(properties = {"spring.profiles.active=test"})
@AutoConfigureMockMvc
@DisabledInAotMode
class CardControllerIT {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;

import java.io.IOException;
import java.util.Arrays;
Expand All @@ -47,7 +46,6 @@
@DataMongoTest
@EnableMongoAuditing
@ContextConfiguration(classes = TestMongoConfiguration.class)
@TestPropertySource(properties = {"spring.profiles.active=test"})
class CardServiceIT {

private static final String PATH = "/json/cards.json";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.web.servlet.MockMvc;

Expand All @@ -29,7 +28,6 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(classes = TestMongoConfiguration.class)
@TestPropertySource(properties = {"spring.profiles.active=test"})
@AutoConfigureMockMvc
@DisabledInAotMode
class CategoryControllerIT {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;

import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -32,7 +31,6 @@

@DataMongoTest
@ContextConfiguration(classes = TestMongoConfiguration.class)
@TestPropertySource(properties = {"spring.profiles.active=test"})
class CategoryServiceIT {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.test.context.TestPropertySource;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(classes = TestMongoConfiguration.class)
@TestPropertySource(properties = {"spring.profiles.active=test"})
class ExampleDataInitializerIT {

// needed since otherwise test tries to connect to Authorization server on AppContext creation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;

import java.util.Set;
Expand All @@ -38,7 +37,6 @@

@WebMvcTest({RedirectController.class, CategoryController.class,
AuthenticationResolver.class, ObservabilityConfiguration.class})
@TestPropertySource(properties = {"spring.profiles.active=test"})
class RedirectControllerIT {

// needed since otherwise test tries to connect to Authorization server on Spring security context creation
Expand Down
Loading