Skip to content

Commit

Permalink
feat: edition hors-ligne et synchronisation
Browse files Browse the repository at this point in the history
Signed-off-by: Maud Royer <hello@maudroyer.fr>
  • Loading branch information
jillro committed Jun 3, 2024
1 parent 94d18f0 commit 42dc155
Show file tree
Hide file tree
Showing 20 changed files with 613 additions and 133 deletions.
38 changes: 38 additions & 0 deletions src/components/Conflict.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup>
import { useCartoBioStorage } from "@/stores/storage.js"
import VersionConflictModal from "@/components/versions/VersionConflictModal.vue"
import { ref } from "vue"
const storage = useCartoBioStorage()
const modalRecordId = ref(null)
</script>

<template>
<div class="fr-alert fr-alert--error" role="alert">
<div class="fr-alert__title">Conflit entre versions</div>
<p>
Les exploitations suivantes ont été modifiées pendant que vous étiez hors-ligne. Cliquez sur l'exploitation pour
résoudre le conflit.
</p>

<ul class="fr-text--bold">
<li v-for="conflict in storage.conflicts" :key="conflict">
<a href="#" @click="modalRecordId = conflict">
{{ storage.operators[storage.records[conflict].numerobio].operator.nom }}
</a>
</li>
</ul>
</div>

<Teleport to="body">
<VersionConflictModal
v-if="modalRecordId"
:record-id="modalRecordId"
@close="modalRecordId = null"
/>
</Teleport>
</template>

<style scoped>
</style>
7 changes: 3 additions & 4 deletions src/components/Features/FeatureGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,9 @@
class="fr-hidden fr-unhidden-sm fr-unhidden-md fr-unhidden-lg fr-unhidden-xl"
:class="{'fr-btn': true, 'fr-btn--tertiary-no-outline': true, 'fr-icon-edit-line': true }"
@click="toggleEditForm(feature.id)" aria-label="Modifier"
:disabled="!isOnline"
/>
<ActionDropdown with-icons :disabled="!isOnline">
<ActionDropdown with-icons>
<li v-if="permissions.canChangeGeometry && isOnline">
<router-link :to="`/exploitations/${operatorStore.operator.numeroBio}/${recordStore.record.record_id}/modifier/${feature.id}`" type="button" class="fr-btn fr-btn--tertiary-no-outline fr-icon-geometry fr-text--sm">
Modifier le contour
Expand All @@ -108,7 +107,7 @@
<button
type="button"
@click.prevent="toggleDeleteForm(feature.id)"
:disabled="!permissions.canDeleteFeature || !isOnline"
:disabled="!permissions.canDeleteFeature"
class="fr-btn fr-btn--tertiary-no-outline fr-icon-delete-line btn--error fr-text--sm"
>
Supprimer la parcelle
Expand Down Expand Up @@ -195,7 +194,7 @@ watch(selectedIds, (selectedIds, prevSelectedIds) => {
open.value = true
setTimeout(() => {
document.querySelector(`tr#parcelle-${newItems[0]}`)?.scrollIntoView({ behavior: 'smooth', block: 'center' })
document?.querySelector(`tr#parcelle-${newItems[0]}`)?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}, 200)
}
})
Expand Down
4 changes: 1 addition & 3 deletions src/components/Features/MassActionsSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<button
class="fr-btn fr-btn--sm fr-btn--secondary fr-btn--icon-right fr-icon-arrow-down-s-line menu-button"
@click.stop.prevent="toggle"
:disabled="!isOnline"
>
{{ label }}
</button>
Expand All @@ -26,7 +25,7 @@

<script setup>
import { ref } from 'vue'
import { onClickOutside, useOnline } from '@vueuse/core'
import { onClickOutside } from '@vueuse/core'
import ActionDropdown from "@/components/ActionDropdown.vue"
defineProps({
Expand All @@ -42,7 +41,6 @@ defineProps({
const emit = defineEmits(['submit'])
const isOnline = useOnline()
const isMenuOpen = ref(false)
const isModalOpen = ref(false)
const openerElement = ref(null)
Expand Down
17 changes: 13 additions & 4 deletions src/components/Features/SingleItemCertificationBodyForm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { flushPromises, mount } from "@vue/test-utils"
import axios from 'axios'
import { useRecordStore } from "@/stores/record.js"
import { usePermissions } from "@/stores/permissions.js"
import { useCartoBioStorage } from "@/stores/storage.js"
import {
ANNOTATIONS,
AnnotationTags,
AUDITOR_RULES,
CERTIFICATION_BODY_DECISION,
LEVEL_AB,
LEVEL_C1,
Expand All @@ -22,14 +22,16 @@ import TableComponent from "../record/Table.vue"
const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false })
const recordStore = useRecordStore(pinia)
const permissions = usePermissions(pinia)
const storage = useCartoBioStorage(pinia)

describe("SingleItemCertificationBodyForm", () => {
let wrapper

beforeEach(async () => {
recordStore.reset()
recordStore.$reset()
recordStore.update(record)
permissions.isOc = true
storage.online = true

const AsyncComponent = defineComponent({
components: { TableComponent },
Expand Down Expand Up @@ -101,11 +103,13 @@ describe("SingleItemCertificationBodyForm", () => {
await form.find('#downgraded_state').setValue(CERTIFICATION_BODY_DECISION.ACCEPTED)

// click and assess server update
axios.__createMock.put.mockResolvedValueOnce({ data: record })
axios.__createMock.patch.mockResolvedValueOnce({ data: record })
await form.find('.fr-modal__footer button.fr-btn').trigger('click')

await flushPromises()
expect(wrapper.findComponent(EditForm).exists()).toEqual(false)
expect(axios.__createMock.put.mock.lastCall).toMatchObject([
expect(axios.__createMock.patch).toHaveBeenCalled()
expect(axios.__createMock.patch.mock.lastCall).toMatchObject([
'/v2/audits/054f0d70-c3da-448f-823e-81fcf7c2bf6e/parcelles/2',
{
properties: {
Expand All @@ -127,6 +131,11 @@ describe("SingleItemCertificationBodyForm", () => {
}
]
}
},
{
headers: {
"If-Unmodified-Since": expect.any(String)
}
}
])
})
Expand Down
12 changes: 8 additions & 4 deletions src/components/Features/Table.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DeletionReasonsCode, GROUPE_COMMUNE } from "@/components/Features/index
import { useFeaturesStore } from "@/stores/features.js"
import { usePermissions } from "@/stores/permissions.js"
import { useRecordStore } from "@/stores/record.js"
import { useCartoBioStorage } from "@/stores/storage.js"

import record from './__fixtures__/record-with-features.json' assert { type: 'json' }
import Modal from "@/components/Modal.vue"
Expand All @@ -16,14 +17,16 @@ import EditForm from "@/components/Features/SingleItemOperatorForm.vue"
import TableComponent from "../record/Table.vue"

const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false })
const storageStore = useCartoBioStorage(pinia)
const recordStore = useRecordStore(pinia)
const featuresStore = useFeaturesStore(pinia)
const permissions = usePermissions(pinia)

describe("Features Table", () => {
beforeEach(() => {
recordStore.reset()
recordStore.$reset()
recordStore.update(record)
storageStore.online = true
})

afterEach(() => {
Expand Down Expand Up @@ -113,7 +116,7 @@ describe("Features Table", () => {
await table.find('tr#parcelle-2 td').trigger('click')
await flushPromises()

axios.__createMock.put.mockResolvedValueOnce({ data: record })
axios.__createMock.patch.mockResolvedValueOnce({ data: record })

// this throws if the modal form does not exist
// it catches the Component reference even if it has been Teleport-ed in the <body>
Expand All @@ -123,8 +126,9 @@ describe("Features Table", () => {
//submit form
await form.find('.fr-modal__footer button.fr-btn').trigger('click')

await flushPromises()
// modal is down, and the table should be updated
expect(axios.__createMock.put).toHaveBeenCalled()
expect(axios.__createMock.patch).toHaveBeenCalled()
expect(wrapper.findComponent(EditForm).exists()).toEqual(false)
})

Expand Down Expand Up @@ -182,7 +186,7 @@ describe("Features Table", () => {
expect(modal.exists()).toEqual(false)

// now, we change a field and we should not be able to close it
axios.__createMock.put.mockResolvedValueOnce({ data: record })
axios.__createMock.patch.mockResolvedValueOnce({ data: record })

table.find('tr.parcelle td').trigger('click')
await flushPromises()
Expand Down
17 changes: 6 additions & 11 deletions src/components/record/CertificationSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import { usePermissions } from "@/stores/permissions.js"
import { useRecordStore } from "@/stores/record.js"
import CertificationModal from "@/components/record/modals/CertificationModal.vue"
import SaveAuditModal from "@/components/record/modals/SaveAuditModal.vue"
import { useOnline } from "@vueuse/core"
const isOnline = useOnline()
const recordStore = useRecordStore()
const operatorStore = useOperatorStore()
const featuresSets = useFeaturesSetsStore()
Expand All @@ -23,23 +21,23 @@ const showCertificationModal = ref(false)
const canEndAudit = computed(() => permissions.isOc && recordStore.hasFeatures && !featuresSets.hasRequiredSets)
async function handleSaveAudit ({ patch }) {
await recordStore.updateInfo({
function handleSaveAudit ({ patch }) {
recordStore.updateInfo({
...patch,
certification_state: CERTIFICATION_STATE.AUDITED
})
showSaveAuditModal.value = false
}
async function handleSendAudit () {
await recordStore.updateInfo({
function handleSendAudit () {
recordStore.updateInfo({
certification_state: CERTIFICATION_STATE.PENDING_CERTIFICATION
})
}
async function handleCertify ({ patch }) {
await recordStore.updateInfo({
function handleCertify ({ patch }) {
recordStore.updateInfo({
...patch,
certification_state: CERTIFICATION_STATE.CERTIFIED
})
Expand Down Expand Up @@ -81,7 +79,6 @@ async function handleCertify ({ patch }) {
v-if="permissions.canSaveAudit"
class="fr-btn"
@click="showSaveAuditModal = true"
:disabled="!isOnline"
>Terminer l'audit</button>
<span v-else>L'auditeur doit maintenant terminer l'audit.</span>
</div>
Expand All @@ -93,7 +90,6 @@ async function handleCertify ({ patch }) {
v-if="permissions.canSendAudit"
class="fr-btn"
@click="handleSendAudit"
:disabled="!isOnline"
>Soumettre pour certification</button>
<span v-else>L'auditeur doit maintenant soumettre l'audit pour certification.</span>
</div>
Expand All @@ -104,7 +100,6 @@ async function handleCertify ({ patch }) {
<button
v-if="permissions.canCertify"
class="fr-btn" @click="showCertificationModal = true"
:disabled="!isOnline"
>Certifier le parcellaire</button>
<span v-else>Le chargé de certification doit maintenant certifier le parcellaire.</span>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/record/Header.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe("RecordHeader", () => {
afterEach(() => {
operatorStore.$reset()
userStore.$reset()
recordStore.reset()
recordStore.$reset()
})

it('should display store informations', () => {
Expand Down
1 change: 0 additions & 1 deletion src/components/record/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
v-if="!disableActions"
class="fr-btn fr-btn--tertiary-no-outline fr-icon fr-icon-edit-line edit-version-info"
@click="showEditVersionModal = true"
:disabled="!isOnline"
>
Modifier la version
</button>
Expand Down
7 changes: 2 additions & 5 deletions src/components/record/modals/CultureTypeModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<form id="mass-edit-form" @submit.prevent="emit('submit', { ids: selectedIds, patch })">
<div class="fr-input-group">
<CultureSelector :cultures="patch.cultures" @change="$cultures => patch.cultures = $cultures" ref="autofocusedElement" />
<CultureSelector :cultures="patch.cultures" @change="$cultures => patch.cultures = $cultures" />
</div>
</form>

Expand All @@ -27,8 +27,7 @@
</template>

<script setup>
import { reactive, ref } from 'vue'
import { useFocus } from '@vueuse/core'
import { reactive } from 'vue'
import { storeToRefs } from 'pinia'
import { useFeaturesStore } from '@/stores/features.js'
Expand All @@ -40,8 +39,6 @@ const emit = defineEmits(['submit'])
const store = useFeaturesStore()
const { selectedIds } = storeToRefs(store)
const autofocusedElement = ref()
useFocus(autofocusedElement, { initialValue: true })
const patch = reactive({
cultures: [ { CPF: '' }]
Expand Down
4 changes: 2 additions & 2 deletions src/components/versions/EditVersionModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const patch = reactive({
certification_date_fin: record.certification_date_fin
})
async function save() {
await recordStore.updateInfo({
function save() {
recordStore.updateInfo({
version_name: patch.version_name,
...(patch.audit_date && { audit_date: patch.audit_date }),
...(patch.certification_date_fin && { certification_date_fin: patch.certification_date_fin })
Expand Down
54 changes: 54 additions & 0 deletions src/components/versions/VersionConflictModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup>
import Modal from "@/components/Modal.vue"
import { useCartoBioStorage } from "@/stores/storage.js"
const emit = defineEmits(['close'])
const props = defineProps({
recordId: {
type: String,
required: true
}
})
const storage = useCartoBioStorage()
async function duplicate() {
await storage.resolveConflict(props.recordId, true)
emit('close')
}
async function merge() {
await storage.resolveConflict(props.recordId, false)
emit('close')
}
</script>

<template>
<Modal @close="$emit('close')" v-bind="$attrs" icon="fr-icon-warning-fill">
<template #title>Conflit entre versions</template>
<p>
Il semblerait qu’une autre personne ait effectué des modifications sur un parcellaire pendant que vous le modifiiez hors-ligne :
</p>
<p>
Version : <b>{{ storage.records[recordId].version_name }}</b> de l’exploitation <b>{{ storage.operators[storage.records[recordId].numerobio].nom }}</b><br/>
</p>
<p>
Souhaitez-vous tout de même appliquer les changements faits hors-ligne sur cette version ?
</p>

<template #footer>
<ul class="fr-btns-group fr-btns-group--inline">
<li>
<button class="fr-btn" @click="duplicate">Créer une nouvelle version</button>
</li>
<li>
<button class="fr-btn fr-btn--tertiary" @click="merge">Appliquer les changements</button>
</li>
</ul>
</template>
</Modal>
</template>

<style scoped>
</style>
Loading

0 comments on commit 42dc155

Please sign in to comment.