Update glossary form with base components

* Create button layout to reuse across forms
* Create generic layout for forms to when create and edit items
pull/4789/head
Daniel Gayoso González 1 year ago
parent 3e827fe1ec
commit f67966af55
  1. 18
      assets/vue/components/basecomponents/BaseTextArea.vue
  2. 54
      assets/vue/components/basecomponents/BaseTextAreaWithVuelidate.vue
  3. 185
      assets/vue/components/glossary/GlossaryForm.vue
  4. 5
      assets/vue/components/layout/LayoutFormButtons.vue
  5. 12
      assets/vue/components/layout/LayoutFormGeneric.vue
  6. 5
      assets/vue/components/links/LinkForm.vue
  7. 25
      assets/vue/services/glossaryService.js
  8. 22
      assets/vue/views/glossary/GlossaryTermCreate.vue
  9. 25
      assets/vue/views/links/LinksCreate.vue

@ -9,11 +9,23 @@
type="text"
@update:model-value="$emit('update:modelValue', $event)"
/>
<label v-t="label" :class="{ 'p-error': isInvalid }" :for="id" />
<label
v-t="label"
:class="{ 'p-error': isInvalid }"
:for="id"
/>
</div>
<slot name="errors">
<small
v-if="isInvalid"
v-t="errorText"
class="p-error"
/>
</slot>
</div>
</template>
<script setup lang="ts">
<script setup>
import Textarea from "primevue/textarea"
const props = defineProps({
@ -41,7 +53,7 @@ const props = defineProps({
required: false,
default: false,
},
});
})
defineEmits(["update:modelValue"])
</script>

@ -0,0 +1,54 @@
<template>
<BaseTextArea
:id="id"
:label="labelWithRequiredIfNeeded"
:model-value="modelValue"
:is-invalid="vuelidateProperty.$error"
@update:model-value="emit('update:modelValue', $event)"
>
<template #errors>
<p
v-for="error in vuelidateProperty.$errors"
:key="error.$uid"
class="mt-1 text-error"
>
{{ error.$message }}
</p>
</template>
</BaseTextArea>
</template>
<script setup>
import BaseTextArea from "./BaseTextArea.vue"
import { computed } from "vue"
const props = defineProps({
id: {
type: String,
require: true,
default: "",
},
label: {
type: String,
required: true,
default: "",
},
modelValue: {
type: String,
required: true,
},
vuelidateProperty: {
type: Object,
required: true,
},
})
const emit = defineEmits(["update:modelValue"])
const labelWithRequiredIfNeeded = computed(() => {
if (Object.hasOwn(props.vuelidateProperty, "required")) {
return `* ${props.label}`
}
return props.label
})
</script>

@ -1,64 +1,55 @@
<template>
<div>
<form @submit.prevent="submitGlossaryForm" name="glossary" id="glossary">
<div class="field">
<div class="p-float-label">
<input
v-model="formData.name"
id="glossary_title"
name="name"
type="text"
class="p-inputtext p-component p-filled"
/>
<label for="glossary_title">
<span class="form_required">*</span>
Term
</label>
</div>
</div>
<div class="field">
<div class="p-float-label">
<textarea v-model="formData.description" id="description" name="description"></textarea>
<label for="description">
<span class="form_required">*</span>
Term definition
</label>
</div>
</div>
<div class="field 2">
<div class="8">
<label for="glossary_SubmitGlossary" class="h-4"> </label>
<button class="btn btn--primary" name="SubmitGlossary" type="submit" id="glossary_SubmitGlossary">
<em class="mdi mdi-plus"></em> Save term
</button>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<span class="form_required">*</span>
<small>Required field</small>
</div>
</div>
<input name="_qf__glossary" type="hidden" value="" id="glossary__qf__glossary" />
<input name="sec_token" type="hidden" value="1e7d47c276bfdfe308a79e1b71d58089" id="glossary_sec_token" />
</form>
</div>
<form class="mt-6 flex flex-col gap-2">
<BaseInputTextWithVuelidate
id="term-name"
v-model="formData.name"
:vuelidate-property="v$.name"
:label="t('Term')"
/>
<BaseTextAreaWithVuelidate
id="term-description"
v-model="formData.description"
:label="t('Description')"
:vuelidate-property="v$.description"
/>
<LayoutFormButtons>
<BaseButton
:label="t('Back')"
type="black"
icon="back"
@click="emit('backPressed')"
/>
<BaseButton
:label="t('Save term')"
type="success"
icon="send"
@click="submitGlossaryForm"
/>
</LayoutFormButtons>
</form>
</template>
<script setup>
import axios from "axios"
import { ENTRYPOINT } from "../../config/entrypoint"
import { useRoute, useRouter } from "vue-router"
import { useI18n } from "vue-i18n"
import { ref, onMounted } from "vue"
import { onMounted, reactive, ref } from "vue"
import { RESOURCE_LINK_PUBLISHED } from "../resource_links/visibility"
import LayoutFormButtons from "../layout/LayoutFormButtons.vue"
import BaseButton from "../basecomponents/BaseButton.vue"
import BaseInputTextWithVuelidate from "../basecomponents/BaseInputTextWithVuelidate.vue"
import { required } from "@vuelidate/validators"
import useVuelidate from "@vuelidate/core"
import BaseTextAreaWithVuelidate from "../basecomponents/BaseTextAreaWithVuelidate.vue"
import { useNotification } from "../../composables/notification"
import glossaryService from "../../services/glossaryService"
import { useCidReq } from "../../composables/cidReq"
const route = useRoute()
const router = useRouter()
const { t } = useI18n()
const notification = useNotification()
const { sid, cid } = useCidReq()
const props = defineProps({
termId: {
@ -67,82 +58,80 @@ const props = defineProps({
},
})
const emit = defineEmits(["backPressed"])
const parentResourceNodeId = ref(Number(route.params.node))
const resourceLinkList = ref(
JSON.stringify([
{
sid: route.query.sid,
cid: route.query.cid,
sid,
cid,
visibility: RESOURCE_LINK_PUBLISHED, // visible by default
},
])
)
const formData = ref({
const formData = reactive({
name: "",
description: "",
})
const rules = {
name: { required },
description: { required },
}
const v$ = useVuelidate(rules, formData)
onMounted(() => {
fetchTerm()
})
const fetchTerm = () => {
if (props.termId) {
axios
.get(ENTRYPOINT + "glossaries/" + props.termId)
.then((response) => {
const glossary = response.data
formData.value.name = glossary.name
formData.value.description = glossary.description
})
.catch((error) => {
console.error("Error fetching link:", error)
})
const fetchTerm = async () => {
if (props.termId === null) {
return
}
try {
const glossary = await glossaryService.getGlossaryTerm(props.termId)
formData.name = glossary.name
formData.description = glossary.description
} catch (error) {
console.error("Error glossary term:", error)
notification.showErrorNotification(t("Could not fetch glossary term"))
}
}
const submitGlossaryForm = () => {
const submitGlossaryForm = async () => {
v$.value.$touch()
if (v$.value.$invalid) {
return
}
const postData = {
name: formData.value.name,
description: formData.value.description,
name: formData.name,
description: formData.description,
parentResourceNodeId: parentResourceNodeId.value,
resourceLinkList: resourceLinkList.value,
sid: route.query.sid,
cid: route.query.cid,
}
if (props.termId) {
const endpoint = `${ENTRYPOINT}glossaries/${props.termId}`
axios
.put(endpoint, postData)
.then((response) => {
console.log("Glossary updated:", response.data)
router.push({
name: "GlossaryList",
query: route.query,
})
})
.catch((error) => {
console.error("Error updating Glossary:", error)
})
} else {
const endpoint = `${ENTRYPOINT}glossaries`
axios
.post(endpoint, postData)
.then((response) => {
console.log("Glossary created:", response.data)
router.push({
name: "GlossaryList",
query: route.query,
})
})
.catch((error) => {
console.error("Error creating Glossary:", error)
})
try {
if (props.linkId) {
await glossaryService.updateGlossaryTerm(props.linkId, postData)
} else {
await glossaryService.createGlossaryTerm(postData)
}
notification.showSuccessNotification(t("Glossary term saved"))
await router.push({
name: "GlossaryList",
query: route.query,
})
} catch (error) {
console.error("Error updating link:", error)
notification.showErrorNotification(t("Could not create glossary term"))
}
}
</script>

@ -0,0 +1,5 @@
<template>
<div class="flex gap-4">
<slot></slot>
</div>
</template>

@ -0,0 +1,12 @@
<template>
<div>
<h2 class="text-h3 font-small text-gray-800 mb-4">
<slot name="header"></slot>
</h2>
<hr class="m-0 mb-4"/>
<slot></slot>
</div>
</template>
<script setup>
</script>

@ -43,7 +43,7 @@
option-value="value"
/>
<div class="flex gap-4">
<LayoutFormButtons>
<BaseButton
:label="t('Back')"
type="black"
@ -56,7 +56,7 @@
icon="send"
@click="submitForm"
/>
</div>
</LayoutFormButtons>
</form>
</template>
@ -75,6 +75,7 @@ import BaseCheckbox from "../basecomponents/BaseCheckbox.vue";
import BaseTextArea from "../basecomponents/BaseTextArea.vue";
import BaseSelect from "../basecomponents/BaseSelect.vue";
import {useNotification} from "../../composables/notification";
import LayoutFormButtons from "../layout/LayoutFormButtons.vue";
const notification = useNotification()
const {t} = useI18n()

@ -10,6 +10,31 @@ export default {
return response.data
},
/**
* @param {String|Number} termId
*/
getGlossaryTerm: async (termId) => {
const response = await axios.get(ENTRYPOINT + `glossaries/${termId}`)
return response.data
},
/**
* @param {Object} data
*/
createGlossaryTerm: async (data) => {
const response = await axios.post(ENTRYPOINT + `glossaries`, data)
return response.data
},
/**
* @param {String|Number} termId
* @param {Object} data
*/
updateGlossaryTerm: async (termId, data) => {
const response = await axios.put(ENTRYPOINT + `glossaries/${termId}`, data)
return response.data
},
/**
* @param {Object} data
*/

@ -1,22 +1,24 @@
<template>
<div>
<div class="mb-4">
<button class="btn btn--secondary" @click="goBack">Back</button>
</div>
<h1 class="text-h3 font-small text-gray-800 mb-4">
Add new glossary term
<hr />
</h1>
<GlossaryForm />
</div>
<LayoutFormGeneric>
<template #header>
<BaseIcon icon="plus" />
{{ t("Add new glossary term") }}
</template>
<GlossaryForm @back-pressed="goBack" />
</LayoutFormGeneric>
</template>
<script setup>
import GlossaryForm from "../../components/glossary/GlossaryForm.vue"
import { useRouter, useRoute } from "vue-router"
import BaseIcon from "../../components/basecomponents/BaseIcon.vue"
import { useI18n } from "vue-i18n"
import LayoutFormGeneric from "../../components/layout/LayoutFormGeneric.vue"
const router = useRouter()
const route = useRoute()
const { t } = useI18n()
const goBack = () => {
router.push({

@ -1,23 +1,22 @@
<template>
<div>
<h2 class="text-h3 font-small text-gray-800 mb-4">
<BaseIcon icon="link-add"/>
{{ t('Add a link') }}
</h2>
<hr class="m-0 mb-4"/>
<LinkForm
@back-pressed="goBack"
/>
</div>
<LayoutFormGeneric>
<template #header>
<BaseIcon icon="link-add" />
{{ t("Add a link") }}
</template>
<LinkForm @back-pressed="goBack" />
</LayoutFormGeneric>
</template>
<script setup>
import LinkForm from "../../components/links/LinkForm.vue"
import { useRouter, useRoute } from "vue-router"
import {useI18n} from "vue-i18n";
import { useRoute, useRouter } from "vue-router"
import { useI18n } from "vue-i18n"
import BaseIcon from "../../components/basecomponents/BaseIcon.vue"
import LayoutFormGeneric from "../../components/layout/LayoutFormGeneric.vue"
const {t} = useI18n()
const { t } = useI18n()
const router = useRouter()
const route = useRoute()

Loading…
Cancel
Save