Skip to content

Commit

Permalink
feat: page listes des parcellaires stockés
Browse files Browse the repository at this point in the history
Signed-off-by: Maud Royer <hello@maudroyer.fr>
  • Loading branch information
jillro committed May 10, 2024
1 parent 8d16ab0 commit ca73699
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 16 deletions.
35 changes: 35 additions & 0 deletions src/components/versions/FullStorageModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup>
import Modal from "@/components/Modal.vue"
defineEmits(['close'])
</script>

<template>
<Modal @close="$emit('close')" v-bind="$attrs" icon="fr-icon-warning-fill">
<template #title>Espace de stockage complet</template>

<p>
Vous avez atteint la limite de stockage de votre navigateur.
</p>
<p>
Pour télécharger de nouveaux parcellaires, veuillez en supprimer
parmi la liste des parcellaires téléchargés.
</p>


<template #footer>
<ul class="fr-btns-group fr-btns-group--inline">
<li>
<router-link to="/exploitations/stockage/" class="fr-btn fr-btn--primary">Voir la liste</router-link>
</li>
<li>
<button class="fr-btn fr-btn--tertiary" @click="$emit('close')">Annuler</button>
</li>
</ul>
</template>
</Modal>
</template>

<style scoped>
</style>
7 changes: 4 additions & 3 deletions src/pages/certification/exploitations/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ import { monthYearDateFormat, dateFormat } from '@/components/dates.js'
import { searchOperators } from '@/cartobio-api.js'
import { useUserStore } from '@/stores/user.js'
import { useLocalStorage, useOnline } from "@vueuse/core"
import { useOnline } from "@vueuse/core"
import { useOperatorStore } from "@/stores/operator.js"
const props = defineProps({
search: {
Expand All @@ -177,17 +178,17 @@ const props = defineProps({
const userStore = useUserStore()
const operatorStore = useOperatorStore()
const router = useRouter()
const route = useRoute()
const isOnline = useOnline()
const operatorStorage = useLocalStorage('operators', {})
const { startPage, user } = storeToRefs(userStore)
const error = ref('')
const isSearching = ref(false)
const searchResults = ref([])
const operators = computed(() => isOnline.value ? searchResults.value : Object.entries(operatorStorage.value).map(
const operators = computed(() => isOnline.value ? searchResults.value : Object.entries(operatorStore.storage).map(
([, [operator]]) => operator
))
Expand Down
11 changes: 10 additions & 1 deletion src/pages/exploitations/[numeroBio]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ meta:
v-else
type="button"
class="fr-btn fr-btn--tertiary-no-outline fr-icon-download-line"
@click.stop.prevent="operatorStore.downloadRecord(record.record_id)"
@click.stop.prevent="tryDownloadRecord(record.record_id)"
>Télécharger en hors-ligne</button>
</td>
<td>
Expand Down Expand Up @@ -173,6 +173,7 @@ meta:
@close="editVersionModal = null"
/>
<DeleteVersionModal v-if="deleteRecordModal" :record="operatorStore.records.find(record => record.record_id === deleteRecordModal)" @close="deleteRecordModal = null" />
<FullStorageModal v-if="fullStorageModal" @close="fullStorageModal = false" />
</Teleport>
</main>
</template>
Expand All @@ -187,6 +188,7 @@ import ActionDropdown from "@/components/ActionDropdown.vue"
import EditVersionModal from "@/components/versions/EditVersionModal.vue"
import DeleteVersionModal from "@/components/versions/DeleteVersionModal.vue"
import NewVersionModal from "@/components/versions/NewVersionModal.vue"
import FullStorageModal from "@/components/versions/FullStorageModal.vue"
import OperatorSetupFlow from '@/components/OperatorSetup/Flow.vue'
import ActionFromScratch from '@/components/OperatorSetup/Actions/FromScratch.vue'
import ActionFromSource from '@/components/OperatorSetup/Actions/FromSource.vue'
Expand All @@ -209,6 +211,7 @@ const isOnline = useOnline()
const newVersionModal = ref(false)
const deleteRecordModal = ref(null)
const editVersionModal = ref(null)
const fullStorageModal = ref(false)
const operatorSetupActions = [
...(permissions.isOc ? [{ id: 'operator', selector: markRaw(ActionByOperator) }] : []),
Expand Down Expand Up @@ -248,6 +251,12 @@ async function duplicateVersion(recordId) {
toast.success('La version a été dupliquée')
}
async function tryDownloadRecord(recordId) {
if (!await operatorStore.downloadRecord(recordId)) {
fullStorageModal.value = true
}
}
</script>
<style scoped>
Expand Down
78 changes: 78 additions & 0 deletions src/pages/exploitations/stockage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script setup>
import { useOperatorStore } from "@/stores/operator.js"
import { computed } from "vue"
import { usePermissions } from "@/stores/permissions.js"
const operatorStore = useOperatorStore()
const permissions = usePermissions()
const storedRecords = computed(() => {
return Array.from((function* records() {
for (const [, [operator, records]] of Object.entries(operatorStore.storage)) {
for (const record of records) {
if (localStorage.getItem(`record-${record.record_id}`)) {
yield {
operator,
record
}
}
}
}
})())
})
</script>

<template>
<main class="fr-container fr-pb-2w">
<nav role="navigation" class="fr-breadcrumb fr-mb-2w">
<ol class="fr-breadcrumb__list">
<li><router-link class="fr-breadcrumb__link" :to="permissions.startPage">Exploitations</router-link></li>
<li><a class="fr-breadcrumb__link" aria-current="page">Parcellaires téléchargés</a></li>
</ol>
</nav>
<h1 class="fr-h3">Parcellaires téléchargés</h1>
<div class="fr-table fr-table--bordered">
<table>
<thead>
<tr>
<th>Nom de l'exploitation</th>
<th>Nom de version</th>
<th>Date d'audit</th>
<th></th>
</tr>
</thead>
<tbody>
<tr
v-for="{operator, record} in storedRecords"
:key="record.record_id"
@click="$router.push(`/exploitations/${operator.numeroBio}/${record.record_id}/`)"
>
<td>{{ operator.nom }}</td>
<td>{{ record.version_name }}</td>
<td>{{ record.audit_date }}</td>
<td>
<button
type="button"
class="fr-btn fr-btn--tertiary-no-outline fr-icon-close-circle-fill"
@click.stop.prevent="operatorStore.clearDownloadRecord(record.record_id)"
>Supprimer des téléchargements</button>
</td>
</tr>
</tbody>
</table>
</div>
</main>
</template>

<style scoped>
.fr-table table {
width: 100%;
display: table;
& tbody tr:hover,
& tbody button:hover {
cursor: pointer;
background-color: var(--background-alt-blue-france-hover);
}
}
</style>
75 changes: 63 additions & 12 deletions src/stores/operator.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,22 @@ import { useLocalStorage } from "@vueuse/core"
/**
* @typedef {import('@vue/reactivity').Ref} Ref
* @typedef {import('@vue/reactivity').UnwrapRef} UnwrapRef
* @typedef {import('@vue/reactivity').ComputedRef} ComputedRef
* @typedef {import('@vueuse/shared').RemovableRef} RemovableRef
*/

function date(record) {
return new Date(record.certification_date_debut || record.audit_date || record.created_at)
}

export const useOperatorStore = defineStore('operator', () => {
const operatorsStorage = useLocalStorage('operators', {})

/**
* @type {RemovableRef<{
* [numeroBio: string]: [import('@agencebio/cartobio-types').AgenceBioNormalizedOperator, import('@agencebio/cartobio-types').NormalizedRecordSummary[]]
* }>}
*/
const storage = useLocalStorage('operators', {})

/**
* @typedef {import('@agencebio/cartobio-types').AgenceBioNormalizedOperator}
Expand Down Expand Up @@ -51,8 +59,8 @@ export const useOperatorStore = defineStore('operator', () => {
* @return {Promise<void>}
*/
async function ready (numeroBio) {
if (!navigator.onLine && operatorsStorage.value[numeroBio]) {
const [operatorData, recordsData] = operatorsStorage.value[numeroBio]
if (!navigator.onLine && storage.value[numeroBio]) {
const [operatorData, recordsData] = storage.value[numeroBio]
operator.value = operatorData
records.value = recordsData
return
Expand All @@ -67,30 +75,65 @@ export const useOperatorStore = defineStore('operator', () => {
apiClient.get(`/v2/operator/${numeroBio}/records`).then(({ data: r }) => {
records.value = r.sort((recordA, recordB) => date(recordB) - date(recordA)).map(record => ({
...record,
storedOffline: localStorage.getItem(`record-${record.record_id}`)
storedOffline: !!localStorage.getItem(`record-${record.record_id}`)
}))
})
}

async function downloadOperator() {
operatorsStorage.value[operator.value.numeroBio] = [operator.value, records.value]
storage.value[operator.value.numeroBio] = [operator.value, records.value]
}

async function clearDownloadOperator() {
delete operatorsStorage.value[operator.value.numeroBio]
delete storage.value[operator.value.numeroBio]
}

/**
* Download a record for offline use.
* Resolves true if the record was successfully downloaded, false if the storage is full.
* @param recordId
* @return {Promise<boolean>}
*/
async function downloadRecord (recordId) {
const record = await getRecord(recordId)
localStorage.setItem(`record-${record.record_id}`, JSON.stringify(record))
records.value.find(r => r.record_id === record.record_id).storedOffline = true
await downloadOperator()
try {
const record = await getRecord(recordId)
localStorage.setItem(`record-${record.record_id}`, JSON.stringify(record))
if (records.value.find(r => r.record_id === record.record_id)) {
records.value.find(r => r.record_id === record.record_id).storedOffline = true
}
await downloadOperator()
const [, recordsSummaryStorage] = storage.value[record.numerobio]
recordsSummaryStorage.find(r => r.record_id === recordId).storedOffline = true
} catch (e) {
if (e instanceof DOMException &&
// everything except Firefox
(e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === "QuotaExceededError" ||
// Firefox
e.name === "NS_ERROR_DOM_QUOTA_REACHED")) {
return false
}

throw e
}

return true

}

async function clearDownloadRecord (recordId) {
const numeroBio = JSON.parse(localStorage.getItem(`record-${recordId}`)).numerobio
localStorage.removeItem(`record-${recordId}`)
records.value.find(r => r.record_id === recordId).storedOffline = false
if (records.value.every(r => !r.storedOffline)) {
if (records.value?.find(r => r.record_id === recordId)) {
records.value.find(r => r.record_id === recordId).storedOffline = false
}
const [, recordsSummaryStorage] = storage.value[numeroBio]
recordsSummaryStorage.find(r => r.record_id === recordId).storedOffline = false
if (recordsSummaryStorage.every(r => localStorage.getItem(`record-${r.record_id}`) === null)) {
await clearDownloadOperator()
}
}
Expand All @@ -108,8 +151,16 @@ export const useOperatorStore = defineStore('operator', () => {
}
})

// Update storedOffline properties when storage changes
watch(storage, () => {
for (let i = 0; i < records.value?.length; i++) {
records.value[i].storedOffline = !!localStorage.getItem(`record-${records.value[i].record_id}`)
}
})

return {
// ref
storage,
operator,
records,
// computed
Expand Down
1 change: 1 addition & 0 deletions src/stores/record.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const useRecordStore = defineStore('record', () => {
const sets = useFeaturesSetsStore()

const initialState = {
numerobio: null,
record_id: null,
version_name: null,
certification_date_debut: null,
Expand Down

0 comments on commit ca73699

Please sign in to comment.