Files
matosbox/frontend/pages/achats/index.vue

151 lines
4.3 KiB
Vue

<template>
<main class="container">
<h1>{{ t('nav.achats') }}</h1>
<p v-if="message">{{ message }}</p>
<section class="card" style="margin-bottom: 16px;">
<h2>{{ t('achats.mode') }}</h2>
<div style="display: grid; gap: 8px;">
<label>
{{ t('achats.boutique') }}
<select v-model="boutique">
<option value="amazon">Amazon</option>
<option value="aliexpress">AliExpress</option>
<option value="generic">{{ t('achats.generic') }}</option>
</select>
</label>
<label>
{{ t('achats.format') }}
<select v-model="mode">
<option value="file">{{ t('achats.fileMode') }}</option>
<option value="json">{{ t('achats.jsonMode') }}</option>
</select>
</label>
<div v-if="mode === 'file'" style="display: grid; gap: 8px;">
<input type="file" @change="onFileChange" />
<button class="card" type="button" :disabled="uploading" @click="uploadFile">
{{ uploading ? t('form.saving') : t('actions.upload') }}
</button>
</div>
<div v-else style="display: grid; gap: 8px;">
<textarea
v-model="jsonText"
rows="8"
:placeholder="t('achats.jsonPlaceholder')"
/>
<button class="card" type="button" :disabled="uploading" @click="uploadJson">
{{ uploading ? t('form.saving') : t('actions.save') }}
</button>
</div>
</div>
</section>
<section v-if="result" class="card">
<h2>{{ t('achats.summary') }}</h2>
<p>{{ t('achats.imported') }}: {{ result.importes }}</p>
<p>{{ t('achats.created') }}: {{ result.crees }}</p>
<p>{{ t('achats.duplicates') }}: {{ result.doublons }}</p>
<div v-if="result.erreurs?.length" style="margin-top: 8px;">
<strong>{{ t('achats.errors') }}</strong>
<ul>
<li v-for="(err, idx) in result.erreurs" :key="idx">
<span v-if="err.ligne">#{{ err.ligne }} </span>{{ err.message }}
<span v-if="err.nom">({{ err.nom }})</span>
</li>
</ul>
</div>
</section>
</main>
</template>
<script setup lang="ts">
type ImportErreur = {
ligne?: number
message: string
nom?: string
}
type ImportResultat = {
importes: number
crees: number
doublons: number
erreurs: ImportErreur[]
}
const { apiBase, getErrorMessage } = useApi()
const { t } = useI18n()
const boutique = ref("amazon")
const mode = ref<"file" | "json">("file")
const uploading = ref(false)
const message = ref("")
const result = ref<ImportResultat | null>(null)
const file = ref<File | null>(null)
const jsonText = ref("")
const onFileChange = (event: Event) => {
const input = event.target as HTMLInputElement
file.value = input.files?.[0] ?? null
}
const uploadFile = async () => {
message.value = ""
result.value = null
if (!file.value) {
message.value = t("messages.noFiles")
return
}
uploading.value = true
try {
const formData = new FormData()
formData.append("boutique", boutique.value)
formData.append("fichier", file.value)
result.value = await $fetch(`${apiBase}/imports/achats`, {
method: "POST",
body: formData
})
message.value = t("messages.uploadDone")
} catch (err) {
message.value = getErrorMessage(err, t("messages.uploadError"))
} finally {
uploading.value = false
}
}
const uploadJson = async () => {
message.value = ""
result.value = null
if (!jsonText.value.trim()) {
message.value = t("errors.invalidJson")
return
}
uploading.value = true
try {
const parsed = JSON.parse(jsonText.value)
let payload = parsed
if (Array.isArray(parsed)) {
payload = { boutique: boutique.value, achats: parsed }
} else if (parsed && typeof parsed === "object") {
if (!parsed.boutique) {
payload = { ...parsed, boutique: boutique.value }
}
}
result.value = await $fetch(`${apiBase}/imports/achats`, {
method: "POST",
body: payload
})
message.value = t("messages.uploadDone")
} catch (err) {
if (err instanceof SyntaxError) {
message.value = t("errors.invalidJson")
} else {
message.value = getErrorMessage(err, t("messages.uploadError"))
}
} finally {
uploading.value = false
}
}
</script>