feat(plantes): API plants.ts — Plant + PlantVariety + endpoints varieties
Remplace Plant (variete/boutique/tags inline) par Plant + PlantVariety séparés. Ajoute temp_germination, temps_levee_j, varieties[]. Ajoute CRUD variétés dans plantsApi. Corrige PlantesView et TachesView pour lire boutique/variete via varieties?.[0]. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,29 +1,12 @@
|
||||
// frontend/src/api/plants.ts
|
||||
import client from './client'
|
||||
|
||||
export interface Plant {
|
||||
export interface PlantVariety {
|
||||
id?: number
|
||||
nom_commun: string
|
||||
nom_botanique?: string
|
||||
plant_id?: number
|
||||
variete?: string
|
||||
famille?: string
|
||||
categorie?: string // potager|fleur|arbre|arbuste
|
||||
tags?: string
|
||||
type_plante?: string
|
||||
besoin_eau?: string
|
||||
besoin_soleil?: string
|
||||
espacement_cm?: number
|
||||
temp_min_c?: number
|
||||
hauteur_cm?: number
|
||||
plantation_mois?: string
|
||||
recolte_mois?: string
|
||||
semis_interieur_mois?: string
|
||||
semis_exterieur_mois?: string
|
||||
maladies_courantes?: string
|
||||
astuces_culture?: string
|
||||
url_reference?: string
|
||||
notes?: string
|
||||
associations_favorables?: string[]
|
||||
associations_defavorables?: string[]
|
||||
notes_variete?: string
|
||||
boutique_nom?: string
|
||||
boutique_url?: string
|
||||
prix_achat?: number
|
||||
@@ -32,10 +15,49 @@ export interface Plant {
|
||||
dluo?: string
|
||||
}
|
||||
|
||||
export interface Plant {
|
||||
id?: number
|
||||
nom_commun: string
|
||||
nom_botanique?: string
|
||||
famille?: string
|
||||
categorie?: string // potager|fleur|arbre|arbuste
|
||||
type_plante?: string
|
||||
besoin_eau?: string
|
||||
besoin_soleil?: string
|
||||
espacement_cm?: number
|
||||
temp_min_c?: number
|
||||
hauteur_cm?: number
|
||||
temp_germination?: string // ex: "8-10°C"
|
||||
temps_levee_j?: string // ex: "15-20 jours"
|
||||
plantation_mois?: string
|
||||
recolte_mois?: string
|
||||
semis_interieur_mois?: string
|
||||
semis_exterieur_mois?: string
|
||||
repiquage_mois?: string
|
||||
profondeur_semis_cm?: number
|
||||
duree_culture_j?: number
|
||||
sol_conseille?: string
|
||||
maladies_courantes?: string
|
||||
astuces_culture?: string
|
||||
url_reference?: string
|
||||
notes?: string
|
||||
associations_favorables?: string[]
|
||||
associations_defavorables?: string[]
|
||||
varieties?: PlantVariety[]
|
||||
}
|
||||
|
||||
export const plantsApi = {
|
||||
list: (categorie?: string) => client.get<Plant[]>('/api/plants', { params: categorie ? { categorie } : {} }).then(r => r.data),
|
||||
list: (categorie?: string) =>
|
||||
client.get<Plant[]>('/api/plants', { params: categorie ? { categorie } : {} }).then(r => r.data),
|
||||
get: (id: number) => client.get<Plant>(`/api/plants/${id}`).then(r => r.data),
|
||||
create: (p: Partial<Plant>) => client.post<Plant>('/api/plants', p).then(r => r.data),
|
||||
update: (id: number, p: Partial<Plant>) => client.put<Plant>(`/api/plants/${id}`, p).then(r => r.data),
|
||||
delete: (id: number) => client.delete(`/api/plants/${id}`),
|
||||
// Variétés
|
||||
createVariety: (plantId: number, v: Partial<PlantVariety>) =>
|
||||
client.post<PlantVariety>(`/api/plants/${plantId}/varieties`, v).then(r => r.data),
|
||||
updateVariety: (plantId: number, vid: number, v: Partial<PlantVariety>) =>
|
||||
client.put<PlantVariety>(`/api/plants/${plantId}/varieties/${vid}`, v).then(r => r.data),
|
||||
deleteVariety: (plantId: number, vid: number) =>
|
||||
client.delete(`/api/plants/${plantId}/varieties/${vid}`),
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="detailPlant.variete" class="text-yellow font-bold uppercase tracking-widest text-sm">{{ detailPlant.variete }}</p>
|
||||
<p v-if="detailPlant.varieties?.[0]?.variete" class="text-yellow font-bold uppercase tracking-widest text-sm">{{ detailPlant.varieties?.[0]?.variete }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="closeDetail" class="text-text-muted hover:text-red transition-colors text-2xl ml-4">✕</button>
|
||||
@@ -137,34 +137,34 @@
|
||||
</div>
|
||||
|
||||
<!-- Boutique -->
|
||||
<div v-if="detailPlant.boutique_nom || detailPlant.prix_achat || detailPlant.poids || detailPlant.dluo" class="space-y-2">
|
||||
<div v-if="detailPlant.varieties?.[0]?.boutique_nom || detailPlant.varieties?.[0]?.prix_achat || detailPlant.varieties?.[0]?.poids || detailPlant.varieties?.[0]?.dluo" class="space-y-2">
|
||||
<h3 class="text-[10px] font-black text-text-muted uppercase tracking-widest">🛒 Approvisionnement</h3>
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
<div v-if="detailPlant.boutique_nom" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<div v-if="detailPlant.varieties?.[0]?.boutique_nom" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<span class="text-[9px] font-black text-text-muted uppercase block mb-0.5">Enseigne</span>
|
||||
<span class="text-text text-sm font-bold">{{ detailPlant.boutique_nom }}</span>
|
||||
<span class="text-text text-sm font-bold">{{ detailPlant.varieties?.[0]?.boutique_nom }}</span>
|
||||
</div>
|
||||
<div v-if="detailPlant.prix_achat" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<div v-if="detailPlant.varieties?.[0]?.prix_achat" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<span class="text-[9px] font-black text-text-muted uppercase block mb-0.5">Prix</span>
|
||||
<span class="text-yellow font-bold">{{ detailPlant.prix_achat.toFixed(2) }} €</span>
|
||||
<span class="text-yellow font-bold">{{ detailPlant.varieties?.[0]?.prix_achat?.toFixed(2) }} €</span>
|
||||
</div>
|
||||
<div v-if="detailPlant.date_achat" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<div v-if="detailPlant.varieties?.[0]?.date_achat" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<span class="text-[9px] font-black text-text-muted uppercase block mb-0.5">Date d'achat</span>
|
||||
<span class="text-text text-sm">{{ detailPlant.date_achat }}</span>
|
||||
<span class="text-text text-sm">{{ detailPlant.varieties?.[0]?.date_achat }}</span>
|
||||
</div>
|
||||
<div v-if="detailPlant.poids" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<div v-if="detailPlant.varieties?.[0]?.poids" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<span class="text-[9px] font-black text-text-muted uppercase block mb-0.5">Poids / Qté</span>
|
||||
<span class="text-text text-sm font-bold">{{ detailPlant.poids }}</span>
|
||||
<span class="text-text text-sm font-bold">{{ detailPlant.varieties?.[0]?.poids }}</span>
|
||||
</div>
|
||||
<div v-if="detailPlant.dluo" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<div v-if="detailPlant.varieties?.[0]?.dluo" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<span class="text-[9px] font-black text-text-muted uppercase block mb-0.5">DLUO</span>
|
||||
<span :class="['text-sm font-bold', isDluoExpired(detailPlant.dluo) ? 'text-red' : 'text-green']">
|
||||
{{ detailPlant.dluo }}{{ isDluoExpired(detailPlant.dluo) ? ' ⚠️' : '' }}
|
||||
<span :class="['text-sm font-bold', isDluoExpired(detailPlant.varieties?.[0]?.dluo ?? '') ? 'text-red' : 'text-green']">
|
||||
{{ detailPlant.varieties?.[0]?.dluo }}{{ isDluoExpired(detailPlant.varieties?.[0]?.dluo ?? '') ? ' ⚠️' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="detailPlant.boutique_url" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<div v-if="detailPlant.varieties?.[0]?.boutique_url" class="bg-bg/30 p-3 rounded-xl border border-bg-soft">
|
||||
<span class="text-[9px] font-black text-text-muted uppercase block mb-0.5">Lien</span>
|
||||
<a :href="detailPlant.boutique_url" target="_blank" rel="noopener"
|
||||
<a :href="detailPlant.varieties?.[0]?.boutique_url" target="_blank" rel="noopener"
|
||||
class="text-blue text-xs hover:underline truncate block">🔗 Voir le produit</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -572,7 +572,7 @@ const filteredPlants = computed(() => {
|
||||
result = result.filter(p => {
|
||||
const group = plantGroups.value.get((p.nom_commun || '').toLowerCase()) || []
|
||||
return group.some(v =>
|
||||
v.nom_commun?.toLowerCase().includes(q) || v.variete?.toLowerCase().includes(q)
|
||||
v.nom_commun?.toLowerCase().includes(q) || v.varieties?.[0]?.variete?.toLowerCase().includes(q)
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -684,15 +684,16 @@ function startEdit(p: Plant) {
|
||||
|
||||
closeDetail()
|
||||
editPlant.value = p
|
||||
const v0 = p.varieties?.[0]
|
||||
Object.assign(form, {
|
||||
nom_commun: p.nom_commun || '', variete: p.variete || '', famille: p.famille || '',
|
||||
nom_commun: p.nom_commun || '', variete: v0?.variete || '', famille: p.famille || '',
|
||||
categorie: p.categorie || 'potager', besoin_eau: p.besoin_eau || 'moyen', besoin_soleil: p.besoin_soleil || 'plein soleil',
|
||||
plantation_mois: p.plantation_mois || '', notes: p.notes || '',
|
||||
associations_favorables: [...(withAssoc.associations_favorables ?? [])],
|
||||
associations_defavorables: [...(withAssoc.associations_defavorables ?? [])],
|
||||
boutique_nom: p.boutique_nom || '', boutique_url: p.boutique_url || '',
|
||||
prix_achat: p.prix_achat ?? null, date_achat: p.date_achat || '',
|
||||
poids: p.poids || '', dluo: p.dluo || '',
|
||||
boutique_nom: v0?.boutique_nom || '', boutique_url: v0?.boutique_url || '',
|
||||
prix_achat: v0?.prix_achat ?? null, date_achat: v0?.date_achat || '',
|
||||
poids: v0?.poids || '', dluo: v0?.dluo || '',
|
||||
})
|
||||
assocFilter.value = ''
|
||||
showAssocModal.value = false
|
||||
|
||||
@@ -416,7 +416,7 @@ const plantingsByGarden = computed(() => {
|
||||
function plantingLabel(p: Planting): string {
|
||||
const plant = plantsStore.plants.find(pl => pl.id === p.variety_id)
|
||||
const nom = plant
|
||||
? [plant.nom_commun, plant.variete].filter(Boolean).join(' — ')
|
||||
? [plant.nom_commun, plant.varieties?.[0]?.variete].filter(Boolean).join(' — ')
|
||||
: `Variété #${p.variety_id}`
|
||||
const date = p.date_plantation ? ` (${fmtDate(p.date_plantation)})` : ''
|
||||
return `${nom}${date}`
|
||||
|
||||
Reference in New Issue
Block a user