@@ -27,6 +27,9 @@ const emit = defineEmits<{
2727 submitEdit: [body : ArticleUpdateBody ]
2828}>()
2929
30+ /** Limite titre Vinted : espaces comptés comme un caractère. */
31+ const VINTED_TITLE_MAX_CHARS = 100
32+
3033const title = ref (' ' )
3134const description = ref (' ' )
3235const pokemonName = ref (' ' )
@@ -45,6 +48,9 @@ const isGraded = ref(false)
4548const gradedGraderValueId = ref (' ' )
4649const gradedGradeValueId = ref (' ' )
4750const gradedCertNumber = ref (' ' )
51+ /** Édition : cocher pour enregistrer l’article comme non publié sur le canal (suivi GoupixDex uniquement). */
52+ const clearVintedPublication = ref (false )
53+ const clearEbayPublication = ref (false )
4854const purchasePrice = ref (' ' )
4955const sellPrice = ref (' ' )
5056const toast = useToast ()
@@ -83,6 +89,23 @@ const canPublishEbay = computed(
8389 && svcSettings .value ?.ebay_listing_config_complete === true
8490)
8591
92+ const titleLenVinted = computed (() => title .value .trim ().length )
93+
94+ function titleWithinVintedLimit(): boolean {
95+ return titleLenVinted .value <= VINTED_TITLE_MAX_CHARS
96+ }
97+
98+ function assignTitleFromExternal(raw : string ) {
99+ title .value = raw .trim ().slice (0 , VINTED_TITLE_MAX_CHARS )
100+ }
101+
102+ const titleFieldDescription = computed (() => {
103+ const n = titleLenVinted .value
104+ const max = VINTED_TITLE_MAX_CHARS
105+ const base = ` ${n } / ${max } caractères (limite Vinted, espaces inclus) `
106+ return n > max ? ` ${base } — raccourcissez avant enregistrement ` : base
107+ })
108+
86109onMounted (async () => {
87110 try {
88111 svcSettings .value = await getSettings ()
@@ -139,6 +162,8 @@ watch(
139162 ? a .graded_grade_value_id
140163 : ' '
141164 gradedCertNumber .value = a .graded_cert_number ?? ' '
165+ clearVintedPublication .value = false
166+ clearEbayPublication .value = false
142167 },
143168 { immediate: true }
144169)
@@ -175,7 +200,7 @@ function applyScanPrefill(scan: {
175200 ocr: Record <string , unknown >
176201 pricing: { cardmarket_eur: number | null , tcgplayer_usd: number | null }
177202}) {
178- title . value = scan .listing_preview .title
203+ assignTitleFromExternal ( scan .listing_preview .title )
179204 description .value = scan .listing_preview .description
180205 const o = scan .ocr
181206 const en = typeof o .pokemon_name_english === ' string' ? o .pokemon_name_english : ' '
@@ -214,7 +239,7 @@ async function applyEbayPrefill(p: {
214239 imageUrl? : string | null
215240}) {
216241 if (p .title ) {
217- title . value = p .title
242+ assignTitleFromExternal ( p .title )
218243 }
219244 if (p .description ) {
220245 description .value = p .description
@@ -262,7 +287,7 @@ async function applyWardrobeSlot(p: WardrobeSlotPrefill) {
262287 wardrobeVintedListed .value = p .wardrobeVintedListed
263288 wardrobeVintedPublishedAtIso .value = p .vintedPublishedAtIso
264289 wardrobeImportSoldPrice .value = p .importSoldPrice
265- title . value = p .title
290+ assignTitleFromExternal ( p .title )
266291 description .value = p .description
267292 purchasePrice .value = p .purchasePrice || ' 0'
268293 sellPrice .value = p .sellPrice
@@ -306,6 +331,9 @@ defineExpose({
306331})
307332
308333function buildCreateFormData(): FormData {
334+ if (! titleWithinVintedLimit ()) {
335+ throw new Error (' ARTICLE_TITLE_TOO_LONG' )
336+ }
309337 const fd = new FormData ()
310338 fd .append (' title' , title .value .trim ())
311339 fd .append (' description' , description .value )
@@ -369,6 +397,22 @@ function imageSrc(url: string) {
369397}
370398
371399function submit() {
400+ if (! title .value .trim ()) {
401+ toast .add ({
402+ title: ' Titre requis' ,
403+ description: ' Indiquez un titre pour l’article.' ,
404+ color: ' error'
405+ })
406+ return
407+ }
408+ if (! titleWithinVintedLimit ()) {
409+ toast .add ({
410+ title: ' Titre trop long' ,
411+ description: ` Vinted : maximum ${VINTED_TITLE_MAX_CHARS } caractères (espaces inclus). Actuellement : ${title .value .trim ().length }. ` ,
412+ color: ' error'
413+ })
414+ return
415+ }
372416 if (isGraded .value ) {
373417 if (! gradedGraderValueId .value || ! gradedGradeValueId .value ) {
374418 toast .add ({
@@ -383,7 +427,7 @@ function submit() {
383427 emit (' submitCreate' , buildCreateFormData ())
384428 return
385429 }
386- emit ( ' submitEdit ' , {
430+ const editBody : ArticleUpdateBody = {
387431 title: title .value .trim (),
388432 description: description .value ,
389433 pokemon_name: pokemonName .value .trim () || null ,
@@ -400,15 +444,30 @@ function submit() {
400444 sell_price: sellPrice .value .trim ()
401445 ? Number (sellPrice .value .replace (' ,' , ' .' ))
402446 : null
403- })
447+ }
448+ if (clearVintedPublication .value ) {
449+ editBody .clear_vinted_publication = true
450+ }
451+ if (clearEbayPublication .value ) {
452+ editBody .clear_ebay_publication = true
453+ }
454+ emit (' submitEdit' , editBody )
404455}
405456 </script >
406457
407458<template >
408459 <div class =" space-y-6" >
409460 <div class =" grid gap-4 sm:grid-cols-2" >
410- <UFormField label =" Titre" required >
411- <UInput v-model =" title" class =" w-full" />
461+ <UFormField
462+ label =" Titre"
463+ required
464+ :description =" titleFieldDescription"
465+ >
466+ <UInput
467+ v-model =" title"
468+ class =" w-full"
469+ :maxlength =" mode === 'create' ? VINTED_TITLE_MAX_CHARS : undefined"
470+ />
412471 </UFormField >
413472 <UFormField v-if =" !isGraded" label =" État" >
414473 <USelect
@@ -628,6 +687,34 @@ function submit() {
628687 </div >
629688 </div >
630689
690+ <div
691+ v-if ="
692+ mode === 'edit'
693+ && article
694+ && !article.is_sold
695+ && ((article.published_on_vinted ?? false) || (article.published_on_ebay ?? false))
696+ "
697+ class =" rounded-lg border border-default p-4 space-y-3"
698+ >
699+ <p class =" text-sm font-medium text-highlighted" >
700+ Statut de publication dans GoupixDex
701+ </p >
702+ <p class =" text-xs text-muted leading-relaxed" >
703+ Cochez pour indiquer que l’article n’est plus « en ligne » dans l’app. Cela ne retire pas l’annonce sur Vinted ou eBay :
704+ supprimez-la sur le site si besoin. Utile pour relancer une publication depuis GoupixDex.
705+ </p >
706+ <UCheckbox
707+ v-if =" article.published_on_vinted ?? false"
708+ v-model =" clearVintedPublication"
709+ label =" Ne plus marquer comme publié sur Vinted"
710+ />
711+ <UCheckbox
712+ v-if =" article.published_on_ebay ?? false"
713+ v-model =" clearEbayPublication"
714+ label =" Ne plus marquer comme publié sur eBay"
715+ />
716+ </div >
717+
631718 <UButton
632719 v-if =" showSubmitButton"
633720 color =" primary"
0 commit comments