Skip to content

Commit

Permalink
Last refactorings in Create card (#97)
Browse files Browse the repository at this point in the history
* bump versions & switch native image build to paketo (finally !!)

* refactor errorService and useApi and add cardService tests

* refactor last issues for create card UI

centralize Spring Test profile declaration.

* slight impro

* some ui improvements
  • Loading branch information
wisskirchenj authored Apr 9, 2024
1 parent e737afe commit 635e8b4
Show file tree
Hide file tree
Showing 14 changed files with 99 additions and 106 deletions.
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

0 comments on commit 635e8b4

Please sign in to comment.