avant codex
@@ -8,4 +8,4 @@ RUN npm run build
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
EXPOSE 8061
|
||||
|
||||
41
frontend/public/icons/moon/first_quarter.svg
Normal file
@@ -0,0 +1,41 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Lune - Premier quartier">
|
||||
<title>Lune - Premier quartier</title>
|
||||
<defs>
|
||||
<linearGradient id="nightSkyFQ" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0F2027"/>
|
||||
<stop offset="100%" stop-color="#203A43"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="moonLightFQ" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#F5F3E7"/>
|
||||
<stop offset="100%" stop-color="#D9D6C8"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Night sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#nightSkyFQ)"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<g fill="#FFFFFF" opacity="0.8">
|
||||
<circle cx="24" cy="28" r="2"/>
|
||||
<circle cx="98" cy="22" r="1.8"/>
|
||||
<circle cx="110" cy="46" r="1.5"/>
|
||||
<circle cx="18" cy="60" r="1.5"/>
|
||||
<circle cx="90" cy="78" r="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Moon base (dark disc) -->
|
||||
<circle cx="64" cy="64" r="36" fill="#2C3E50"/>
|
||||
|
||||
<!-- Illuminated half (right side) -->
|
||||
<path d="M64 28
|
||||
A36 36 0 0 1 64 100
|
||||
L64 28 Z"
|
||||
fill="url(#moonLightFQ)"/>
|
||||
|
||||
<!-- Subtle craters on lit side -->
|
||||
<g fill="#CFCBB8" opacity="0.5">
|
||||
<circle cx="74" cy="52" r="4"/>
|
||||
<circle cx="82" cy="70" r="3"/>
|
||||
<circle cx="70" cy="78" r="2.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
39
frontend/public/icons/moon/full_moon.svg
Normal file
@@ -0,0 +1,39 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Lune - Pleine lune">
|
||||
<title>Pleine lune</title>
|
||||
<defs>
|
||||
<linearGradient id="nightSkyFM" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0B1D2A"/>
|
||||
<stop offset="100%" stop-color="#1C2F3F"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="moonFull" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#F8F6EC"/>
|
||||
<stop offset="70%" stop-color="#E2DECF"/>
|
||||
<stop offset="100%" stop-color="#CFCBB8"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Night sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#nightSkyFM)"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<g fill="#FFFFFF" opacity="0.85">
|
||||
<circle cx="22" cy="24" r="2"/>
|
||||
<circle cx="104" cy="20" r="1.8"/>
|
||||
<circle cx="112" cy="48" r="1.5"/>
|
||||
<circle cx="18" cy="68" r="1.6"/>
|
||||
<circle cx="96" cy="86" r="2"/>
|
||||
<circle cx="40" cy="18" r="1.4"/>
|
||||
</g>
|
||||
|
||||
<!-- Full moon -->
|
||||
<circle cx="64" cy="64" r="38" fill="url(#moonFull)"/>
|
||||
|
||||
<!-- Craters -->
|
||||
<g fill="#BEB9A6" opacity="0.6">
|
||||
<circle cx="52" cy="50" r="6"/>
|
||||
<circle cx="76" cy="60" r="5"/>
|
||||
<circle cx="68" cy="82" r="4"/>
|
||||
<circle cx="48" cy="72" r="3.5"/>
|
||||
<circle cx="84" cy="44" r="3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
41
frontend/public/icons/moon/last_quarter.svg
Normal file
@@ -0,0 +1,41 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Lune - Dernier quartier">
|
||||
<title>Lune - Dernier quartier</title>
|
||||
<defs>
|
||||
<linearGradient id="nightSkyLQ" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0F2027"/>
|
||||
<stop offset="100%" stop-color="#203A43"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="moonLightLQ" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#F5F3E7"/>
|
||||
<stop offset="100%" stop-color="#D9D6C8"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Night sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#nightSkyLQ)"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<g fill="#FFFFFF" opacity="0.8">
|
||||
<circle cx="24" cy="28" r="2"/>
|
||||
<circle cx="98" cy="22" r="1.8"/>
|
||||
<circle cx="110" cy="46" r="1.5"/>
|
||||
<circle cx="18" cy="60" r="1.5"/>
|
||||
<circle cx="90" cy="78" r="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Moon base (dark disc) -->
|
||||
<circle cx="64" cy="64" r="36" fill="#2C3E50"/>
|
||||
|
||||
<!-- Illuminated half (left side) -->
|
||||
<path d="M64 28
|
||||
A36 36 0 0 0 64 100
|
||||
L64 28 Z"
|
||||
fill="url(#moonLightLQ)"/>
|
||||
|
||||
<!-- Subtle craters on lit side -->
|
||||
<g fill="#CFCBB8" opacity="0.5">
|
||||
<circle cx="54" cy="52" r="4"/>
|
||||
<circle cx="46" cy="70" r="3"/>
|
||||
<circle cx="58" cy="78" r="2.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
37
frontend/public/icons/moon/new_moon.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Lune - Nouvelle lune">
|
||||
<title>Lune - Nouvelle lune</title>
|
||||
<defs>
|
||||
<linearGradient id="nightSkyNM" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#081821"/>
|
||||
<stop offset="100%" stop-color="#132A35"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="haloNM" cx="50%" cy="50%" r="60%">
|
||||
<stop offset="0%" stop-color="#FFFFFF" stop-opacity="0.18"/>
|
||||
<stop offset="70%" stop-color="#FFFFFF" stop-opacity="0.06"/>
|
||||
<stop offset="100%" stop-color="#FFFFFF" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Night sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#nightSkyNM)"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<g fill="#FFFFFF" opacity="0.85">
|
||||
<circle cx="22" cy="26" r="2"/>
|
||||
<circle cx="46" cy="18" r="1.4"/>
|
||||
<circle cx="104" cy="22" r="1.8"/>
|
||||
<circle cx="112" cy="48" r="1.5"/>
|
||||
<circle cx="18" cy="70" r="1.6"/>
|
||||
<circle cx="94" cy="88" r="2"/>
|
||||
<circle cx="34" cy="46" r="1.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Very dark moon disc -->
|
||||
<circle cx="64" cy="64" r="38" fill="#0B0F14"/>
|
||||
|
||||
<!-- Subtle rim/earthshine hint -->
|
||||
<circle cx="64" cy="64" r="40" fill="none" stroke="#FFFFFF" stroke-width="2" opacity="0.12"/>
|
||||
|
||||
<!-- Soft halo -->
|
||||
<circle cx="64" cy="64" r="46" fill="url(#haloNM)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
43
frontend/public/icons/moon/waning_crescent.svg
Normal file
@@ -0,0 +1,43 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Lune - Dernier croissant">
|
||||
<title>Dernier croissant</title>
|
||||
<defs>
|
||||
<linearGradient id="nightSkyWC" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0B1D2A"/>
|
||||
<stop offset="100%" stop-color="#1C2F3F"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="moonLightWC" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#F6F4E8"/>
|
||||
<stop offset="100%" stop-color="#D9D6C8"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Night sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#nightSkyWC)"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<g fill="#FFFFFF" opacity="0.85">
|
||||
<circle cx="24" cy="26" r="2"/>
|
||||
<circle cx="46" cy="18" r="1.4"/>
|
||||
<circle cx="104" cy="22" r="1.8"/>
|
||||
<circle cx="112" cy="48" r="1.5"/>
|
||||
<circle cx="18" cy="70" r="1.6"/>
|
||||
<circle cx="94" cy="88" r="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Dark moon base -->
|
||||
<circle cx="64" cy="64" r="36" fill="#2C3E50"/>
|
||||
|
||||
<!-- Illuminated crescent (left side, waning) -->
|
||||
<path d="
|
||||
M64 28
|
||||
A36 36 0 0 0 64 100
|
||||
A24 36 0 0 1 64 28
|
||||
Z"
|
||||
fill="url(#moonLightWC)"/>
|
||||
|
||||
<!-- Subtle craters on lit crescent -->
|
||||
<g fill="#CFCBB8" opacity="0.5">
|
||||
<circle cx="52" cy="56" r="3.5"/>
|
||||
<circle cx="54" cy="72" r="2.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
44
frontend/public/icons/moon/waning_gibbous.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Lune - Gibbeuse décroissante">
|
||||
<title>Waning Gibbous</title>
|
||||
<defs>
|
||||
<linearGradient id="nightSkyWG" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0B1D2A"/>
|
||||
<stop offset="100%" stop-color="#1C2F3F"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="moonLightWG" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#F6F4E8"/>
|
||||
<stop offset="100%" stop-color="#D9D6C8"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Night sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#nightSkyWG)"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<g fill="#FFFFFF" opacity="0.85">
|
||||
<circle cx="22" cy="24" r="2"/>
|
||||
<circle cx="44" cy="18" r="1.4"/>
|
||||
<circle cx="104" cy="22" r="1.8"/>
|
||||
<circle cx="112" cy="46" r="1.5"/>
|
||||
<circle cx="18" cy="70" r="1.6"/>
|
||||
<circle cx="94" cy="88" r="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Dark moon base -->
|
||||
<circle cx="64" cy="64" r="36" fill="#2C3E50"/>
|
||||
|
||||
<!-- Illuminated gibbous (mostly lit, shadow on right) -->
|
||||
<path d="
|
||||
M64 28
|
||||
A36 36 0 1 0 64 100
|
||||
A26 36 0 1 1 64 28
|
||||
Z"
|
||||
fill="url(#moonLightWG)"/>
|
||||
|
||||
<!-- Subtle craters -->
|
||||
<g fill="#CFCBB8" opacity="0.5">
|
||||
<circle cx="54" cy="50" r="5"/>
|
||||
<circle cx="68" cy="70" r="4"/>
|
||||
<circle cx="52" cy="78" r="3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
43
frontend/public/icons/moon/waxing_crescent.svg
Normal file
@@ -0,0 +1,43 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Lune - Premier croissant">
|
||||
<title>Waxing Crescent</title>
|
||||
<defs>
|
||||
<linearGradient id="nightSkyWCX" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0B1D2A"/>
|
||||
<stop offset="100%" stop-color="#1C2F3F"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="moonLightWCX" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#F6F4E8"/>
|
||||
<stop offset="100%" stop-color="#D9D6C8"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Night sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#nightSkyWCX)"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<g fill="#FFFFFF" opacity="0.85">
|
||||
<circle cx="22" cy="24" r="2"/>
|
||||
<circle cx="46" cy="18" r="1.4"/>
|
||||
<circle cx="104" cy="22" r="1.8"/>
|
||||
<circle cx="112" cy="48" r="1.5"/>
|
||||
<circle cx="18" cy="70" r="1.6"/>
|
||||
<circle cx="94" cy="88" r="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Dark moon base -->
|
||||
<circle cx="64" cy="64" r="36" fill="#2C3E50"/>
|
||||
|
||||
<!-- Illuminated crescent (right side, waxing) -->
|
||||
<path d="
|
||||
M64 28
|
||||
A36 36 0 0 1 64 100
|
||||
A24 36 0 0 0 64 28
|
||||
Z"
|
||||
fill="url(#moonLightWCX)"/>
|
||||
|
||||
<!-- Subtle craters on lit crescent -->
|
||||
<g fill="#CFCBB8" opacity="0.5">
|
||||
<circle cx="74" cy="56" r="3.5"/>
|
||||
<circle cx="72" cy="72" r="2.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
44
frontend/public/icons/moon/waxing_gibbous.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Lune - Gibbeuse croissante">
|
||||
<title>Waxing Gibbous</title>
|
||||
<defs>
|
||||
<linearGradient id="nightSkyWXG" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0B1D2A"/>
|
||||
<stop offset="100%" stop-color="#1C2F3F"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="moonLightWXG" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#F6F4E8"/>
|
||||
<stop offset="100%" stop-color="#D9D6C8"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Night sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#nightSkyWXG)"/>
|
||||
|
||||
<!-- Stars -->
|
||||
<g fill="#FFFFFF" opacity="0.85">
|
||||
<circle cx="22" cy="24" r="2"/>
|
||||
<circle cx="44" cy="18" r="1.4"/>
|
||||
<circle cx="104" cy="22" r="1.8"/>
|
||||
<circle cx="112" cy="46" r="1.5"/>
|
||||
<circle cx="18" cy="70" r="1.6"/>
|
||||
<circle cx="94" cy="88" r="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Dark moon base -->
|
||||
<circle cx="64" cy="64" r="36" fill="#2C3E50"/>
|
||||
|
||||
<!-- Illuminated gibbous (mostly lit, shadow on left) -->
|
||||
<path d="
|
||||
M64 28
|
||||
A36 36 0 1 1 64 100
|
||||
A26 36 0 1 0 64 28
|
||||
Z"
|
||||
fill="url(#moonLightWXG)"/>
|
||||
|
||||
<!-- Subtle craters -->
|
||||
<g fill="#CFCBB8" opacity="0.5">
|
||||
<circle cx="74" cy="50" r="5"/>
|
||||
<circle cx="62" cy="70" r="4"/>
|
||||
<circle cx="76" cy="78" r="3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
47
frontend/public/icons/weather/0.svg
Normal file
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Soleil - ciel clair">
|
||||
<title>Soleil - ciel clair</title>
|
||||
<defs>
|
||||
<radialGradient id="sunCore" cx="50%" cy="50%" r="60%">
|
||||
<stop offset="0%" stop-color="#FFE68A"/>
|
||||
<stop offset="55%" stop-color="#FFC93C"/>
|
||||
<stop offset="100%" stop-color="#FFB300"/>
|
||||
</radialGradient>
|
||||
<filter id="softGlow" x="-30%" y="-30%" width="160%" height="160%">
|
||||
<feGaussianBlur stdDeviation="2.2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Clear sky background -->
|
||||
<rect x="0" y="0" width="128" height="128" rx="20" fill="#6EC6FF"/>
|
||||
|
||||
<!-- Subtle sky highlight -->
|
||||
<path d="M0,20 C30,6 72,6 128,26 L128,0 L0,0 Z" fill="#8ED7FF" opacity="0.65"/>
|
||||
|
||||
<!-- Sun -->
|
||||
<g transform="translate(64 58)" filter="url(#softGlow)">
|
||||
<!-- Rays -->
|
||||
<g stroke="#FFD25A" stroke-width="6" stroke-linecap="round" opacity="0.95">
|
||||
<line x1="0" y1="-44" x2="0" y2="-60"/>
|
||||
<line x1="0" y1="44" x2="0" y2="60"/>
|
||||
<line x1="-44" y1="0" x2="-60" y2="0"/>
|
||||
<line x1="44" y1="0" x2="60" y2="0"/>
|
||||
|
||||
<line x1="31" y1="-31" x2="44" y2="-44"/>
|
||||
<line x1="-31" y1="-31" x2="-44" y2="-44"/>
|
||||
<line x1="31" y1="31" x2="44" y2="44"/>
|
||||
<line x1="-31" y1="31" x2="-44" y2="44"/>
|
||||
</g>
|
||||
|
||||
<!-- Core -->
|
||||
<circle r="26" fill="url(#sunCore)"/>
|
||||
<circle r="30" fill="#FFD45C" opacity="0.18"/>
|
||||
</g>
|
||||
|
||||
<!-- Horizon hint -->
|
||||
<path d="M0,104 C26,96 46,94 64,94 C82,94 102,96 128,104 L128,128 L0,128 Z" fill="#49B36B"/>
|
||||
<path d="M0,110 C26,102 46,100 64,100 C82,100 102,102 128,110 L128,128 L0,128 Z" fill="#3AA65E" opacity="0.8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
44
frontend/public/icons/weather/1.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Principalement clair">
|
||||
<title>Principalement clair</title>
|
||||
<defs>
|
||||
<radialGradient id="sunGrad" cx="50%" cy="50%" r="60%">
|
||||
<stop offset="0%" stop-color="#FFE68A"/>
|
||||
<stop offset="60%" stop-color="#FFC93C"/>
|
||||
<stop offset="100%" stop-color="#FFB300"/>
|
||||
</radialGradient>
|
||||
<filter id="glow" x="-30%" y="-30%" width="160%" height="160%">
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Sky background -->
|
||||
<rect width="128" height="128" rx="20" fill="#6EC6FF"/>
|
||||
|
||||
<!-- Sun (partially visible) -->
|
||||
<g transform="translate(48 48)" filter="url(#glow)">
|
||||
<g stroke="#FFD25A" stroke-width="5" stroke-linecap="round">
|
||||
<line x1="0" y1="-34" x2="0" y2="-48"/>
|
||||
<line x1="0" y1="34" x2="0" y2="48"/>
|
||||
<line x1="-34" y1="0" x2="-48" y2="0"/>
|
||||
<line x1="34" y1="0" x2="48" y2="0"/>
|
||||
<line x1="24" y1="-24" x2="34" y2="-34"/>
|
||||
<line x1="-24" y1="-24" x2="-34" y2="-34"/>
|
||||
<line x1="24" y1="24" x2="34" y2="34"/>
|
||||
<line x1="-24" y1="24" x2="-34" y2="34"/>
|
||||
</g>
|
||||
<circle r="22" fill="url(#sunGrad)"/>
|
||||
</g>
|
||||
|
||||
<!-- Small cloud (mostly clear) -->
|
||||
<g transform="translate(70 72)">
|
||||
<ellipse cx="0" cy="10" rx="26" ry="14" fill="#FFFFFF"/>
|
||||
<circle cx="-14" cy="6" r="10" fill="#FFFFFF"/>
|
||||
<circle cx="2" cy="2" r="12" fill="#FFFFFF"/>
|
||||
<circle cx="18" cy="8" r="9" fill="#FFFFFF"/>
|
||||
<ellipse cx="0" cy="14" rx="26" ry="8" fill="#E6F3FF" opacity="0.6"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
50
frontend/public/icons/weather/2.svg
Normal file
@@ -0,0 +1,50 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Partiellement nuageux">
|
||||
<title>Partiellement nuageux</title>
|
||||
<defs>
|
||||
<radialGradient id="sunGradPN" cx="50%" cy="50%" r="60%">
|
||||
<stop offset="0%" stop-color="#FFE68A"/>
|
||||
<stop offset="60%" stop-color="#FFC93C"/>
|
||||
<stop offset="100%" stop-color="#FFB300"/>
|
||||
</radialGradient>
|
||||
<filter id="sunGlowPN" x="-40%" y="-40%" width="180%" height="180%">
|
||||
<feGaussianBlur stdDeviation="2.5"/>
|
||||
</filter>
|
||||
<linearGradient id="cloudShadePN" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#FFFFFF"/>
|
||||
<stop offset="100%" stop-color="#E6EEF7"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="#7EC8FF"/>
|
||||
|
||||
<!-- Sun behind clouds -->
|
||||
<g transform="translate(44 44)">
|
||||
<circle r="24" fill="url(#sunGradPN)" filter="url(#sunGlowPN)"/>
|
||||
<g stroke="#FFD25A" stroke-width="4" stroke-linecap="round" opacity="0.9">
|
||||
<line x1="0" y1="-36" x2="0" y2="-50"/>
|
||||
<line x1="0" y1="36" x2="0" y2="50"/>
|
||||
<line x1="-36" y1="0" x2="-50" y2="0"/>
|
||||
<line x1="36" y1="0" x2="50" y2="0"/>
|
||||
<line x1="26" y1="-26" x2="38" y2="-38"/>
|
||||
<line x1="-26" y1="-26" x2="-38" y2="-38"/>
|
||||
<line x1="26" y1="26" x2="38" y2="38"/>
|
||||
<line x1="-26" y1="26" x2="-38" y2="38"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Main cloud -->
|
||||
<g transform="translate(70 78)">
|
||||
<ellipse cx="0" cy="12" rx="34" ry="18" fill="url(#cloudShadePN)"/>
|
||||
<circle cx="-20" cy="8" r="14" fill="url(#cloudShadePN)"/>
|
||||
<circle cx="6" cy="4" r="18" fill="url(#cloudShadePN)"/>
|
||||
<circle cx="26" cy="10" r="12" fill="url(#cloudShadePN)"/>
|
||||
</g>
|
||||
|
||||
<!-- Secondary smaller cloud -->
|
||||
<g transform="translate(40 86)" opacity="0.9">
|
||||
<ellipse cx="0" cy="8" rx="20" ry="12" fill="#F4F8FC"/>
|
||||
<circle cx="-10" cy="6" r="8" fill="#F4F8FC"/>
|
||||
<circle cx="8" cy="4" r="10" fill="#F4F8FC"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
36
frontend/public/icons/weather/3.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Couvert">
|
||||
<title>Couvert</title>
|
||||
<defs>
|
||||
<linearGradient id="skyOvercast" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#C9D3DD"/>
|
||||
<stop offset="100%" stop-color="#9FAAB5"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudMain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#F1F4F7"/>
|
||||
<stop offset="100%" stop-color="#D5DBE1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudShadow" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#E2E7EC"/>
|
||||
<stop offset="100%" stop-color="#C3CBD3"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Grey sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyOvercast)"/>
|
||||
|
||||
<!-- Large cloud layer -->
|
||||
<g transform="translate(64 74)">
|
||||
<ellipse cx="0" cy="16" rx="46" ry="24" fill="url(#cloudShadow)"/>
|
||||
<circle cx="-28" cy="10" r="18" fill="url(#cloudShadow)"/>
|
||||
<circle cx="0" cy="2" r="22" fill="url(#cloudShadow)"/>
|
||||
<circle cx="30" cy="12" r="16" fill="url(#cloudShadow)"/>
|
||||
</g>
|
||||
|
||||
<!-- Upper cloud layer -->
|
||||
<g transform="translate(64 56)">
|
||||
<ellipse cx="0" cy="14" rx="40" ry="20" fill="url(#cloudMain)"/>
|
||||
<circle cx="-22" cy="10" r="16" fill="url(#cloudMain)"/>
|
||||
<circle cx="6" cy="4" r="20" fill="url(#cloudMain)"/>
|
||||
<circle cx="26" cy="12" r="14" fill="url(#cloudMain)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
34
frontend/public/icons/weather/45.svg
Normal file
@@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Brouillard">
|
||||
<title>Brouillard</title>
|
||||
<defs>
|
||||
<linearGradient id="fogSky" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#DCE3E9"/>
|
||||
<stop offset="100%" stop-color="#B8C2CC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="fogCloud" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#F4F7FA"/>
|
||||
<stop offset="100%" stop-color="#D9E0E6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky background -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#fogSky)"/>
|
||||
|
||||
<!-- Diffuse sun (very muted) -->
|
||||
<circle cx="40" cy="40" r="18" fill="#FFFFFF" opacity="0.25"/>
|
||||
|
||||
<!-- Main cloud mass -->
|
||||
<g transform="translate(64 62)">
|
||||
<ellipse cx="0" cy="12" rx="42" ry="20" fill="url(#fogCloud)" opacity="0.9"/>
|
||||
<circle cx="-22" cy="8" r="14" fill="url(#fogCloud)" opacity="0.9"/>
|
||||
<circle cx="8" cy="4" r="18" fill="url(#fogCloud)" opacity="0.9"/>
|
||||
<circle cx="28" cy="10" r="12" fill="url(#fogCloud)" opacity="0.9"/>
|
||||
</g>
|
||||
|
||||
<!-- Fog horizontal layers -->
|
||||
<g stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" opacity="0.7">
|
||||
<line x1="24" y1="84" x2="104" y2="84"/>
|
||||
<line x1="20" y1="96" x2="108" y2="96"/>
|
||||
<line x1="28" y1="108" x2="100" y2="108"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
56
frontend/public/icons/weather/48.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Brouillard givrant">
|
||||
<title>Brouillard givrant</title>
|
||||
<defs>
|
||||
<linearGradient id="ffSky" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#D7E6F5"/>
|
||||
<stop offset="100%" stop-color="#A9B9C9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ffCloud" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#F7FBFF"/>
|
||||
<stop offset="100%" stop-color="#D7E3EE"/>
|
||||
</linearGradient>
|
||||
<filter id="ffSoft" x="-30%" y="-30%" width="160%" height="160%">
|
||||
<feGaussianBlur stdDeviation="1.6"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Cold sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#ffSky)"/>
|
||||
|
||||
<!-- Muted sun halo -->
|
||||
<circle cx="40" cy="38" r="18" fill="#FFFFFF" opacity="0.22" filter="url(#ffSoft)"/>
|
||||
|
||||
<!-- Cloud mass -->
|
||||
<g transform="translate(64 60)">
|
||||
<ellipse cx="0" cy="12" rx="44" ry="22" fill="url(#ffCloud)" opacity="0.92"/>
|
||||
<circle cx="-24" cy="8" r="15" fill="url(#ffCloud)" opacity="0.92"/>
|
||||
<circle cx="8" cy="4" r="19" fill="url(#ffCloud)" opacity="0.92"/>
|
||||
<circle cx="30" cy="10" r="13" fill="url(#ffCloud)" opacity="0.92"/>
|
||||
</g>
|
||||
|
||||
<!-- Fog layers -->
|
||||
<g stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" opacity="0.68">
|
||||
<line x1="22" y1="82" x2="106" y2="82"/>
|
||||
<line x1="18" y1="94" x2="110" y2="94"/>
|
||||
<line x1="26" y1="106" x2="102" y2="106"/>
|
||||
</g>
|
||||
|
||||
<!-- Snowflake / ice marker -->
|
||||
<g transform="translate(96 96)">
|
||||
<circle cx="0" cy="0" r="14" fill="#FFFFFF" opacity="0.22"/>
|
||||
<g stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" opacity="0.9">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-7" y1="-7" x2="7" y2="7"/>
|
||||
<line x1="-7" y1="7" x2="7" y2="-7"/>
|
||||
<line x1="0" y1="-9" x2="3" y2="-6"/>
|
||||
<line x1="0" y1="-9" x2="-3" y2="-6"/>
|
||||
<line x1="0" y1="9" x2="3" y2="6"/>
|
||||
<line x1="0" y1="9" x2="-3" y2="6"/>
|
||||
<line x1="-9" y1="0" x2="-6" y2="3"/>
|
||||
<line x1="-9" y1="0" x2="-6" y2="-3"/>
|
||||
<line x1="9" y1="0" x2="6" y2="3"/>
|
||||
<line x1="9" y1="0" x2="6" y2="-3"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
32
frontend/public/icons/weather/51.svg
Normal file
@@ -0,0 +1,32 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Bruine légère">
|
||||
<title>Bruine légère</title>
|
||||
<defs>
|
||||
<linearGradient id="drizzleSky" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#9EC9F5"/>
|
||||
<stop offset="100%" stop-color="#6FA8DC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="drizzleCloud" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#F2F5F8"/>
|
||||
<stop offset="100%" stop-color="#D6DEE6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#drizzleSky)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 58)">
|
||||
<ellipse cx="0" cy="14" rx="40" ry="20" fill="url(#drizzleCloud)"/>
|
||||
<circle cx="-22" cy="10" r="15" fill="url(#drizzleCloud)"/>
|
||||
<circle cx="8" cy="4" r="18" fill="url(#drizzleCloud)"/>
|
||||
<circle cx="28" cy="12" r="13" fill="url(#drizzleCloud)"/>
|
||||
</g>
|
||||
|
||||
<!-- Light drizzle (fine drops) -->
|
||||
<g stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" opacity="0.8">
|
||||
<line x1="40" y1="88" x2="40" y2="96"/>
|
||||
<line x1="56" y1="92" x2="56" y2="100"/>
|
||||
<line x1="72" y1="88" x2="72" y2="96"/>
|
||||
<line x1="88" y1="92" x2="88" y2="100"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
33
frontend/public/icons/weather/53.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Bruine modérée">
|
||||
<title>Bruine modérée</title>
|
||||
<defs>
|
||||
<linearGradient id="skyDrizzleMod" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#8FBDEB"/>
|
||||
<stop offset="100%" stop-color="#5F95C9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudDrizzleMod" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#EEF2F6"/>
|
||||
<stop offset="100%" stop-color="#CBD5DF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyDrizzleMod)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 56)">
|
||||
<ellipse cx="0" cy="16" rx="42" ry="22" fill="url(#cloudDrizzleMod)"/>
|
||||
<circle cx="-24" cy="10" r="16" fill="url(#cloudDrizzleMod)"/>
|
||||
<circle cx="10" cy="4" r="20" fill="url(#cloudDrizzleMod)"/>
|
||||
<circle cx="30" cy="12" r="14" fill="url(#cloudDrizzleMod)"/>
|
||||
</g>
|
||||
|
||||
<!-- Moderate drizzle (more frequent drops) -->
|
||||
<g stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" opacity="0.9">
|
||||
<line x1="34" y1="86" x2="34" y2="100"/>
|
||||
<line x1="50" y1="92" x2="50" y2="106"/>
|
||||
<line x1="66" y1="86" x2="66" y2="100"/>
|
||||
<line x1="82" y1="92" x2="82" y2="106"/>
|
||||
<line x1="98" y1="86" x2="98" y2="100"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
35
frontend/public/icons/weather/55.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Bruine dense">
|
||||
<title>Bruine dense</title>
|
||||
<defs>
|
||||
<linearGradient id="skyDrizzleDense" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#7EA9D6"/>
|
||||
<stop offset="100%" stop-color="#4F7FAF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudDrizzleDense" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#E8EDF2"/>
|
||||
<stop offset="100%" stop-color="#B9C6D3"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Darker sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyDrizzleDense)"/>
|
||||
|
||||
<!-- Thick cloud -->
|
||||
<g transform="translate(64 54)">
|
||||
<ellipse cx="0" cy="18" rx="44" ry="24" fill="url(#cloudDrizzleDense)"/>
|
||||
<circle cx="-26" cy="12" r="18" fill="url(#cloudDrizzleDense)"/>
|
||||
<circle cx="12" cy="4" r="22" fill="url(#cloudDrizzleDense)"/>
|
||||
<circle cx="34" cy="14" r="16" fill="url(#cloudDrizzleDense)"/>
|
||||
</g>
|
||||
|
||||
<!-- Dense drizzle (many close drops) -->
|
||||
<g stroke="#FFFFFF" stroke-width="4.5" stroke-linecap="round" opacity="0.95">
|
||||
<line x1="26" y1="84" x2="26" y2="104"/>
|
||||
<line x1="38" y1="90" x2="38" y2="110"/>
|
||||
<line x1="50" y1="84" x2="50" y2="104"/>
|
||||
<line x1="62" y1="90" x2="62" y2="110"/>
|
||||
<line x1="74" y1="84" x2="74" y2="104"/>
|
||||
<line x1="86" y1="90" x2="86" y2="110"/>
|
||||
<line x1="98" y1="84" x2="98" y2="104"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
44
frontend/public/icons/weather/56.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Bruine verglaçante">
|
||||
<title>Bruine verglaçante</title>
|
||||
<defs>
|
||||
<linearGradient id="skyFreezingDrizzle" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#9FD0F7"/>
|
||||
<stop offset="100%" stop-color="#6E9FC8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudFreezingDrizzle" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#F4F9FF"/>
|
||||
<stop offset="100%" stop-color="#D2DEE9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Cold sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyFreezingDrizzle)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 54)">
|
||||
<ellipse cx="0" cy="18" rx="44" ry="24" fill="url(#cloudFreezingDrizzle)"/>
|
||||
<circle cx="-26" cy="12" r="18" fill="url(#cloudFreezingDrizzle)"/>
|
||||
<circle cx="12" cy="4" r="22" fill="url(#cloudFreezingDrizzle)"/>
|
||||
<circle cx="34" cy="14" r="16" fill="url(#cloudFreezingDrizzle)"/>
|
||||
</g>
|
||||
|
||||
<!-- Freezing drizzle (thin icy drops) -->
|
||||
<g stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" opacity="0.95">
|
||||
<line x1="34" y1="86" x2="34" y2="102"/>
|
||||
<line x1="50" y1="92" x2="50" y2="108"/>
|
||||
<line x1="66" y1="86" x2="66" y2="102"/>
|
||||
<line x1="82" y1="92" x2="82" y2="108"/>
|
||||
<line x1="98" y1="86" x2="98" y2="102"/>
|
||||
</g>
|
||||
|
||||
<!-- Ice indicator (snowflake accent) -->
|
||||
<g transform="translate(98 100)">
|
||||
<circle cx="0" cy="0" r="12" fill="#FFFFFF" opacity="0.25"/>
|
||||
<g stroke="#FFFFFF" stroke-width="2.5" stroke-linecap="round">
|
||||
<line x1="0" y1="-7" x2="0" y2="7"/>
|
||||
<line x1="-7" y1="0" x2="7" y2="0"/>
|
||||
<line x1="-5" y1="-5" x2="5" y2="5"/>
|
||||
<line x1="-5" y1="5" x2="5" y2="-5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
47
frontend/public/icons/weather/57.svg
Normal file
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Bruine verglaçante dense">
|
||||
<title>Bruine verglaçante dense</title>
|
||||
<defs>
|
||||
<linearGradient id="skyFZD" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#7FB4E3"/>
|
||||
<stop offset="100%" stop-color="#4E7EA8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudFZD" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#EEF6FF"/>
|
||||
<stop offset="100%" stop-color="#C7D6E6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Cold dense sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyFZD)"/>
|
||||
|
||||
<!-- Thick cloud -->
|
||||
<g transform="translate(64 52)">
|
||||
<ellipse cx="0" cy="20" rx="46" ry="26" fill="url(#cloudFZD)"/>
|
||||
<circle cx="-28" cy="14" r="20" fill="url(#cloudFZD)"/>
|
||||
<circle cx="14" cy="6" r="24" fill="url(#cloudFZD)"/>
|
||||
<circle cx="36" cy="16" r="18" fill="url(#cloudFZD)"/>
|
||||
</g>
|
||||
|
||||
<!-- Dense freezing drizzle -->
|
||||
<g stroke="#FFFFFF" stroke-width="4.5" stroke-linecap="round" opacity="0.95">
|
||||
<line x1="24" y1="84" x2="24" y2="110"/>
|
||||
<line x1="36" y1="90" x2="36" y2="116"/>
|
||||
<line x1="48" y1="84" x2="48" y2="110"/>
|
||||
<line x1="60" y1="90" x2="60" y2="116"/>
|
||||
<line x1="72" y1="84" x2="72" y2="110"/>
|
||||
<line x1="84" y1="90" x2="84" y2="116"/>
|
||||
<line x1="96" y1="84" x2="96" y2="110"/>
|
||||
<line x1="108" y1="90" x2="108" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Ice symbol -->
|
||||
<g transform="translate(100 98)">
|
||||
<circle cx="0" cy="0" r="14" fill="#FFFFFF" opacity="0.25"/>
|
||||
<g stroke="#FFFFFF" stroke-width="3" stroke-linecap="round">
|
||||
<line x1="0" y1="-8" x2="0" y2="8"/>
|
||||
<line x1="-8" y1="0" x2="8" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
35
frontend/public/icons/weather/61.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Pluie légère">
|
||||
<title>Pluie légère</title>
|
||||
<defs>
|
||||
<linearGradient id="skyLightRain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#8FBCEB"/>
|
||||
<stop offset="100%" stop-color="#5F8FC2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudLightRain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#F1F4F8"/>
|
||||
<stop offset="100%" stop-color="#D3DAE2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="dropLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#BFE4FF"/>
|
||||
<stop offset="100%" stop-color="#5DA9E9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyLightRain)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 54)">
|
||||
<ellipse cx="0" cy="18" rx="42" ry="22" fill="url(#cloudLightRain)"/>
|
||||
<circle cx="-24" cy="12" r="16" fill="url(#cloudLightRain)"/>
|
||||
<circle cx="10" cy="4" r="20" fill="url(#cloudLightRain)"/>
|
||||
<circle cx="30" cy="14" r="14" fill="url(#cloudLightRain)"/>
|
||||
</g>
|
||||
|
||||
<!-- Light rain drops -->
|
||||
<g fill="url(#dropLight)">
|
||||
<path d="M40 86 C40 82 44 78 44 78 C44 78 48 82 48 86 A4 4 0 1 1 40 86 Z"/>
|
||||
<path d="M60 92 C60 88 64 84 64 84 C64 84 68 88 68 92 A4 4 0 1 1 60 92 Z"/>
|
||||
<path d="M80 86 C80 82 84 78 84 78 C84 78 88 82 88 86 A4 4 0 1 1 80 86 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
36
frontend/public/icons/weather/63.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Pluie modérée">
|
||||
<title>Pluie modérée</title>
|
||||
<defs>
|
||||
<linearGradient id="skyModerateRain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#7EA9D6"/>
|
||||
<stop offset="100%" stop-color="#4F7FAF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudModerateRain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#E8EDF2"/>
|
||||
<stop offset="100%" stop-color="#B9C6D3"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="dropModerate" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#CFE9FF"/>
|
||||
<stop offset="100%" stop-color="#3D8EDB"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyModerateRain)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 52)">
|
||||
<ellipse cx="0" cy="20" rx="44" ry="24" fill="url(#cloudModerateRain)"/>
|
||||
<circle cx="-26" cy="14" r="18" fill="url(#cloudModerateRain)"/>
|
||||
<circle cx="12" cy="6" r="22" fill="url(#cloudModerateRain)"/>
|
||||
<circle cx="34" cy="16" r="16" fill="url(#cloudModerateRain)"/>
|
||||
</g>
|
||||
|
||||
<!-- Moderate rain drops -->
|
||||
<g fill="url(#dropModerate)">
|
||||
<path d="M30 84 C30 78 36 72 36 72 C36 72 42 78 42 84 A6 6 0 1 1 30 84 Z"/>
|
||||
<path d="M50 94 C50 88 56 82 56 82 C56 82 62 88 62 94 A6 6 0 1 1 50 94 Z"/>
|
||||
<path d="M70 84 C70 78 76 72 76 72 C76 72 82 78 82 84 A6 6 0 1 1 70 84 Z"/>
|
||||
<path d="M90 94 C90 88 96 82 96 82 C96 82 102 88 102 94 A6 6 0 1 1 90 94 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
36
frontend/public/icons/weather/65.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Pluie forte">
|
||||
<title>Pluie forte</title>
|
||||
<defs>
|
||||
<linearGradient id="skyHeavyRain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#5F8FC2"/>
|
||||
<stop offset="100%" stop-color="#2F5E8F"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudHeavyRain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#DDE3EA"/>
|
||||
<stop offset="100%" stop-color="#AEBBC8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="dropHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#BFE4FF"/>
|
||||
<stop offset="100%" stop-color="#1E6FBF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Dark sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyHeavyRain)"/>
|
||||
|
||||
<!-- Large dense cloud -->
|
||||
<g transform="translate(64 50)">
|
||||
<ellipse cx="0" cy="22" rx="46" ry="26" fill="url(#cloudHeavyRain)"/>
|
||||
<circle cx="-28" cy="16" r="20" fill="url(#cloudHeavyRain)"/>
|
||||
<circle cx="14" cy="6" r="24" fill="url(#cloudHeavyRain)"/>
|
||||
<circle cx="38" cy="18" r="18" fill="url(#cloudHeavyRain)"/>
|
||||
</g>
|
||||
|
||||
<!-- Heavy rain drops -->
|
||||
<g fill="url(#dropHeavy)">
|
||||
<path d="M22 84 C22 74 32 64 32 64 C32 64 42 74 42 84 A10 10 0 1 1 22 84 Z"/>
|
||||
<path d="M44 98 C44 88 54 78 54 78 C54 78 64 88 64 98 A10 10 0 1 1 44 98 Z"/>
|
||||
<path d="M66 84 C66 74 76 64 76 64 C76 64 86 74 86 84 A10 10 0 1 1 66 84 Z"/>
|
||||
<path d="M88 98 C88 88 98 78 98 78 C98 78 108 88 108 98 A10 10 0 1 1 88 98 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
45
frontend/public/icons/weather/66.svg
Normal file
@@ -0,0 +1,45 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Pluie verglaçante légère">
|
||||
<title>Pluie verglaçante légère</title>
|
||||
<defs>
|
||||
<linearGradient id="skyFreezingRainLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#9FD0F7"/>
|
||||
<stop offset="100%" stop-color="#6E9FC8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudFreezingRainLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#F4F9FF"/>
|
||||
<stop offset="100%" stop-color="#D2DEE9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="dropIceLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#E6F7FF"/>
|
||||
<stop offset="100%" stop-color="#4FA3E3"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Cold sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyFreezingRainLight)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 54)">
|
||||
<ellipse cx="0" cy="18" rx="42" ry="22" fill="url(#cloudFreezingRainLight)"/>
|
||||
<circle cx="-24" cy="12" r="16" fill="url(#cloudFreezingRainLight)"/>
|
||||
<circle cx="10" cy="4" r="20" fill="url(#cloudFreezingRainLight)"/>
|
||||
<circle cx="30" cy="14" r="14" fill="url(#cloudFreezingRainLight)"/>
|
||||
</g>
|
||||
|
||||
<!-- Light freezing rain drops -->
|
||||
<g fill="url(#dropIceLight)">
|
||||
<path d="M42 88 C42 82 48 76 48 76 C48 76 54 82 54 88 A6 6 0 1 1 42 88 Z"/>
|
||||
<path d="M70 94 C70 88 76 82 76 82 C76 82 82 88 82 94 A6 6 0 1 1 70 94 Z"/>
|
||||
</g>
|
||||
|
||||
<!-- Ice indicator -->
|
||||
<g transform="translate(98 100)">
|
||||
<circle cx="0" cy="0" r="12" fill="#FFFFFF" opacity="0.25"/>
|
||||
<g stroke="#FFFFFF" stroke-width="2.5" stroke-linecap="round">
|
||||
<line x1="0" y1="-7" x2="0" y2="7"/>
|
||||
<line x1="-7" y1="0" x2="7" y2="0"/>
|
||||
<line x1="-5" y1="-5" x2="5" y2="5"/>
|
||||
<line x1="-5" y1="5" x2="5" y2="-5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
47
frontend/public/icons/weather/67.svg
Normal file
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Pluie verglaçante forte">
|
||||
<title>Pluie verglaçante forte</title>
|
||||
<defs>
|
||||
<linearGradient id="skyFZRHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#6FA8D6"/>
|
||||
<stop offset="100%" stop-color="#2E5E8C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudFZRHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#EAF2F9"/>
|
||||
<stop offset="100%" stop-color="#B7C6D6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="dropFZRHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#E6F7FF"/>
|
||||
<stop offset="100%" stop-color="#1E78C8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Cold dark sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyFZRHeavy)"/>
|
||||
|
||||
<!-- Large dense cloud -->
|
||||
<g transform="translate(64 50)">
|
||||
<ellipse cx="0" cy="22" rx="46" ry="26" fill="url(#cloudFZRHeavy)"/>
|
||||
<circle cx="-28" cy="16" r="20" fill="url(#cloudFZRHeavy)"/>
|
||||
<circle cx="14" cy="6" r="24" fill="url(#cloudFZRHeavy)"/>
|
||||
<circle cx="38" cy="18" r="18" fill="url(#cloudFZRHeavy)"/>
|
||||
</g>
|
||||
|
||||
<!-- Heavy freezing rain drops -->
|
||||
<g fill="url(#dropFZRHeavy)">
|
||||
<path d="M20 84 C20 72 34 60 34 60 C34 60 48 72 48 84 A12 12 0 1 1 20 84 Z"/>
|
||||
<path d="M44 100 C44 88 58 76 58 76 C58 76 72 88 72 100 A12 12 0 1 1 44 100 Z"/>
|
||||
<path d="M68 84 C68 72 82 60 82 60 C82 60 96 72 96 84 A12 12 0 1 1 68 84 Z"/>
|
||||
<path d="M92 100 C92 88 106 76 106 76 C106 76 120 88 120 100 A12 12 0 1 1 92 100 Z"/>
|
||||
</g>
|
||||
|
||||
<!-- Ice symbol -->
|
||||
<g transform="translate(96 92)">
|
||||
<circle cx="0" cy="0" r="14" fill="#FFFFFF" opacity="0.25"/>
|
||||
<g stroke="#FFFFFF" stroke-width="3" stroke-linecap="round">
|
||||
<line x1="0" y1="-8" x2="0" y2="8"/>
|
||||
<line x1="-8" y1="0" x2="8" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
49
frontend/public/icons/weather/71.svg
Normal file
@@ -0,0 +1,49 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Chute de neige faible">
|
||||
<title>Chute de neige faible</title>
|
||||
<defs>
|
||||
<linearGradient id="skyLightSnow" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#BFD9F2"/>
|
||||
<stop offset="100%" stop-color="#8FB3D1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudLightSnow" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#F5F8FB"/>
|
||||
<stop offset="100%" stop-color="#D8E0E8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyLightSnow)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 54)">
|
||||
<ellipse cx="0" cy="18" rx="42" ry="22" fill="url(#cloudLightSnow)"/>
|
||||
<circle cx="-24" cy="12" r="16" fill="url(#cloudLightSnow)"/>
|
||||
<circle cx="10" cy="4" r="20" fill="url(#cloudLightSnow)"/>
|
||||
<circle cx="30" cy="14" r="14" fill="url(#cloudLightSnow)"/>
|
||||
</g>
|
||||
|
||||
<!-- Light snowflakes -->
|
||||
<g stroke="#FFFFFF" stroke-width="2.5" stroke-linecap="round">
|
||||
<!-- Flake 1 -->
|
||||
<g transform="translate(44 92)">
|
||||
<line x1="0" y1="-6" x2="0" y2="6"/>
|
||||
<line x1="-6" y1="0" x2="6" y2="0"/>
|
||||
<line x1="-4" y1="-4" x2="4" y2="4"/>
|
||||
<line x1="-4" y1="4" x2="4" y2="-4"/>
|
||||
</g>
|
||||
<!-- Flake 2 -->
|
||||
<g transform="translate(64 100)">
|
||||
<line x1="0" y1="-6" x2="0" y2="6"/>
|
||||
<line x1="-6" y1="0" x2="6" y2="0"/>
|
||||
<line x1="-4" y1="-4" x2="4" y2="4"/>
|
||||
<line x1="-4" y1="4" x2="4" y2="-4"/>
|
||||
</g>
|
||||
<!-- Flake 3 -->
|
||||
<g transform="translate(84 92)">
|
||||
<line x1="0" y1="-6" x2="0" y2="6"/>
|
||||
<line x1="-6" y1="0" x2="6" y2="0"/>
|
||||
<line x1="-4" y1="-4" x2="4" y2="4"/>
|
||||
<line x1="-4" y1="4" x2="4" y2="-4"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
56
frontend/public/icons/weather/73.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Chute de neige modérée">
|
||||
<title>Chute de neige modérée</title>
|
||||
<defs>
|
||||
<linearGradient id="skySnowMod" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#AFCDE8"/>
|
||||
<stop offset="100%" stop-color="#6F9BC2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudSnowMod" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#EEF3F8"/>
|
||||
<stop offset="100%" stop-color="#C9D5E0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skySnowMod)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 52)">
|
||||
<ellipse cx="0" cy="20" rx="44" ry="24" fill="url(#cloudSnowMod)"/>
|
||||
<circle cx="-26" cy="14" r="18" fill="url(#cloudSnowMod)"/>
|
||||
<circle cx="12" cy="6" r="22" fill="url(#cloudSnowMod)"/>
|
||||
<circle cx="34" cy="16" r="16" fill="url(#cloudSnowMod)"/>
|
||||
</g>
|
||||
|
||||
<!-- Moderate snowflakes -->
|
||||
<g stroke="#FFFFFF" stroke-width="3" stroke-linecap="round">
|
||||
<!-- Flake 1 -->
|
||||
<g transform="translate(32 88)">
|
||||
<line x1="0" y1="-7" x2="0" y2="7"/>
|
||||
<line x1="-7" y1="0" x2="7" y2="0"/>
|
||||
<line x1="-5" y1="-5" x2="5" y2="5"/>
|
||||
<line x1="-5" y1="5" x2="5" y2="-5"/>
|
||||
</g>
|
||||
<!-- Flake 2 -->
|
||||
<g transform="translate(52 98)">
|
||||
<line x1="0" y1="-7" x2="0" y2="7"/>
|
||||
<line x1="-7" y1="0" x2="7" y2="0"/>
|
||||
<line x1="-5" y1="-5" x2="5" y2="5"/>
|
||||
<line x1="-5" y1="5" x2="5" y2="-5"/>
|
||||
</g>
|
||||
<!-- Flake 3 -->
|
||||
<g transform="translate(72 88)">
|
||||
<line x1="0" y1="-7" x2="0" y2="7"/>
|
||||
<line x1="-7" y1="0" x2="7" y2="0"/>
|
||||
<line x1="-5" y1="-5" x2="5" y2="5"/>
|
||||
<line x1="-5" y1="5" x2="5" y2="-5"/>
|
||||
</g>
|
||||
<!-- Flake 4 -->
|
||||
<g transform="translate(92 98)">
|
||||
<line x1="0" y1="-7" x2="0" y2="7"/>
|
||||
<line x1="-7" y1="0" x2="7" y2="0"/>
|
||||
<line x1="-5" y1="-5" x2="5" y2="5"/>
|
||||
<line x1="-5" y1="5" x2="5" y2="-5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
63
frontend/public/icons/weather/75.svg
Normal file
@@ -0,0 +1,63 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Chute de neige forte">
|
||||
<title>Chute de neige forte</title>
|
||||
<defs>
|
||||
<linearGradient id="skySnowHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#8FB3D1"/>
|
||||
<stop offset="100%" stop-color="#4E7EA8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudSnowHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#E6EDF4"/>
|
||||
<stop offset="100%" stop-color="#B7C6D6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Darker snowy sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skySnowHeavy)"/>
|
||||
|
||||
<!-- Large dense cloud -->
|
||||
<g transform="translate(64 50)">
|
||||
<ellipse cx="0" cy="22" rx="46" ry="26" fill="url(#cloudSnowHeavy)"/>
|
||||
<circle cx="-28" cy="16" r="20" fill="url(#cloudSnowHeavy)"/>
|
||||
<circle cx="14" cy="6" r="24" fill="url(#cloudSnowHeavy)"/>
|
||||
<circle cx="38" cy="18" r="18" fill="url(#cloudSnowHeavy)"/>
|
||||
</g>
|
||||
|
||||
<!-- Heavy snowflakes -->
|
||||
<g stroke="#FFFFFF" stroke-width="3.5" stroke-linecap="round">
|
||||
<!-- Flake 1 -->
|
||||
<g transform="translate(24 86)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
<!-- Flake 2 -->
|
||||
<g transform="translate(44 100)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
<!-- Flake 3 -->
|
||||
<g transform="translate(64 86)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
<!-- Flake 4 -->
|
||||
<g transform="translate(84 100)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
<!-- Flake 5 -->
|
||||
<g transform="translate(104 86)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
35
frontend/public/icons/weather/77.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Neige en grains">
|
||||
<title>Neige en grains</title>
|
||||
<defs>
|
||||
<linearGradient id="skySnowGrain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#AFCDE8"/>
|
||||
<stop offset="100%" stop-color="#6F9BC2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudSnowGrain" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#EEF3F8"/>
|
||||
<stop offset="100%" stop-color="#C9D5E0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skySnowGrain)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 52)">
|
||||
<ellipse cx="0" cy="20" rx="44" ry="24" fill="url(#cloudSnowGrain)"/>
|
||||
<circle cx="-26" cy="14" r="18" fill="url(#cloudSnowGrain)"/>
|
||||
<circle cx="12" cy="6" r="22" fill="url(#cloudSnowGrain)"/>
|
||||
<circle cx="34" cy="16" r="16" fill="url(#cloudSnowGrain)"/>
|
||||
</g>
|
||||
|
||||
<!-- Snow grains (small pellets) -->
|
||||
<g fill="#FFFFFF">
|
||||
<circle cx="28" cy="88" r="3"/>
|
||||
<circle cx="40" cy="96" r="3"/>
|
||||
<circle cx="52" cy="88" r="3"/>
|
||||
<circle cx="64" cy="100" r="3"/>
|
||||
<circle cx="76" cy="88" r="3"/>
|
||||
<circle cx="88" cy="96" r="3"/>
|
||||
<circle cx="100" cy="88" r="3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
35
frontend/public/icons/weather/80.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Averse de pluie légère">
|
||||
<title>Averse de pluie légère</title>
|
||||
<defs>
|
||||
<linearGradient id="skyShowerLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#7EA9D6"/>
|
||||
<stop offset="100%" stop-color="#3E6E9E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudShowerLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#E8EDF2"/>
|
||||
<stop offset="100%" stop-color="#B9C6D3"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="dropShowerLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#CFE9FF"/>
|
||||
<stop offset="100%" stop-color="#2F86D6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyShowerLight)"/>
|
||||
|
||||
<!-- Darker cloud (shower type) -->
|
||||
<g transform="translate(64 50)">
|
||||
<ellipse cx="0" cy="22" rx="46" ry="26" fill="url(#cloudShowerLight)"/>
|
||||
<circle cx="-28" cy="16" r="20" fill="url(#cloudShowerLight)"/>
|
||||
<circle cx="14" cy="6" r="24" fill="url(#cloudShowerLight)"/>
|
||||
<circle cx="38" cy="18" r="18" fill="url(#cloudShowerLight)"/>
|
||||
</g>
|
||||
|
||||
<!-- Light shower drops (slanted to show burst effect) -->
|
||||
<g fill="url(#dropShowerLight)" transform="rotate(-15 64 90)">
|
||||
<path d="M38 88 C38 82 44 76 44 76 C44 76 50 82 50 88 A6 6 0 1 1 38 88 Z"/>
|
||||
<path d="M62 96 C62 90 68 84 68 84 C68 84 74 90 74 96 A6 6 0 1 1 62 96 Z"/>
|
||||
<path d="M86 88 C86 82 92 76 92 76 C92 76 98 82 98 88 A6 6 0 1 1 86 88 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
35
frontend/public/icons/weather/81.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Averse de pluie modérée">
|
||||
<title>Averse de pluie modérée</title>
|
||||
<defs>
|
||||
<linearGradient id="skyShowerModerate" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#6F9BC2"/>
|
||||
<stop offset="100%" stop-color="#2E5E8C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudShowerModerate" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#DDE3EA"/>
|
||||
<stop offset="100%" stop-color="#AEBBC8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="dropShowerModerate" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#CFE9FF"/>
|
||||
<stop offset="100%" stop-color="#1E6FBF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Dark sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyShowerModerate)"/>
|
||||
|
||||
<!-- Dense cloud -->
|
||||
<g transform="translate(64 48)">
|
||||
<ellipse cx="0" cy="24" rx="48" ry="28" fill="url(#cloudShowerModerate)"/>
|
||||
<circle cx="-30" cy="18" r="22" fill="url(#cloudShowerModerate)"/>
|
||||
<circle cx="16" cy="8" r="26" fill="url(#cloudShowerModerate)"/>
|
||||
<circle cx="42" cy="20" r="20" fill="url(#cloudShowerModerate)"/>
|
||||
</g>
|
||||
|
||||
<!-- Moderate shower drops (slanted, more numerous) -->
|
||||
<g fill="url(#dropShowerModerate)" transform="rotate(-18 64 92)">
|
||||
<path d="M26 86 C26 76 38 64 38 64 C38 64 50 76 50 86 A10 10 0 1 1 26 86 Z"/>
|
||||
<path d="M52 100 C52 90 64 78 64 78 C64 78 76 90 76 100 A10 10 0 1 1 52 100 Z"/>
|
||||
<path d="M78 86 C78 76 90 64 90 64 C90 64 102 76 102 86 A10 10 0 1 1 78 86 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
35
frontend/public/icons/weather/82.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Averse de pluie violente">
|
||||
<title>Averse de pluie violente</title>
|
||||
<defs>
|
||||
<linearGradient id="skyStormShower" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#3E6E9E"/>
|
||||
<stop offset="100%" stop-color="#1E3F63"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudStormShower" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#C9D3DD"/>
|
||||
<stop offset="100%" stop-color="#8FA0B2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="dropStorm" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#BFE4FF"/>
|
||||
<stop offset="100%" stop-color="#0F5EA8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Dark storm sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyStormShower)"/>
|
||||
|
||||
<!-- Massive storm cloud -->
|
||||
<g transform="translate(64 46)">
|
||||
<ellipse cx="0" cy="26" rx="50" ry="30" fill="url(#cloudStormShower)"/>
|
||||
<circle cx="-32" cy="20" r="24" fill="url(#cloudStormShower)"/>
|
||||
<circle cx="18" cy="8" r="28" fill="url(#cloudStormShower)"/>
|
||||
<circle cx="46" cy="22" r="22" fill="url(#cloudStormShower)"/>
|
||||
</g>
|
||||
|
||||
<!-- Violent shower drops (large, angled, numerous) -->
|
||||
<g fill="url(#dropStorm)" transform="rotate(-22 64 92)">
|
||||
<path d="M16 84 C16 70 34 52 34 52 C34 52 52 70 52 84 A14 14 0 1 1 16 84 Z"/>
|
||||
<path d="M44 104 C44 90 62 72 62 72 C62 72 80 90 80 104 A14 14 0 1 1 44 104 Z"/>
|
||||
<path d="M72 84 C72 70 90 52 90 52 C90 52 108 70 108 84 A14 14 0 1 1 72 84 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
49
frontend/public/icons/weather/85.svg
Normal file
@@ -0,0 +1,49 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Averse de neige légère">
|
||||
<title>Averse de neige légère</title>
|
||||
<defs>
|
||||
<linearGradient id="skySnowShowerLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#AFCDE8"/>
|
||||
<stop offset="100%" stop-color="#6F9BC2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudSnowShowerLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#EEF3F8"/>
|
||||
<stop offset="100%" stop-color="#C9D5E0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skySnowShowerLight)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 50)">
|
||||
<ellipse cx="0" cy="22" rx="46" ry="26" fill="url(#cloudSnowShowerLight)"/>
|
||||
<circle cx="-28" cy="16" r="20" fill="url(#cloudSnowShowerLight)"/>
|
||||
<circle cx="14" cy="6" r="24" fill="url(#cloudSnowShowerLight)"/>
|
||||
<circle cx="38" cy="18" r="18" fill="url(#cloudSnowShowerLight)"/>
|
||||
</g>
|
||||
|
||||
<!-- Light snow shower (slightly angled flakes) -->
|
||||
<g stroke="#FFFFFF" stroke-width="2.5" stroke-linecap="round" transform="rotate(-15 64 92)">
|
||||
<!-- Flake 1 -->
|
||||
<g transform="translate(44 92)">
|
||||
<line x1="0" y1="-6" x2="0" y2="6"/>
|
||||
<line x1="-6" y1="0" x2="6" y2="0"/>
|
||||
<line x1="-4" y1="-4" x2="4" y2="4"/>
|
||||
<line x1="-4" y1="4" x2="4" y2="-4"/>
|
||||
</g>
|
||||
<!-- Flake 2 -->
|
||||
<g transform="translate(64 100)">
|
||||
<line x1="0" y1="-6" x2="0" y2="6"/>
|
||||
<line x1="-6" y1="0" x2="6" y2="0"/>
|
||||
<line x1="-4" y1="-4" x2="4" y2="4"/>
|
||||
<line x1="-4" y1="4" x2="4" y2="-4"/>
|
||||
</g>
|
||||
<!-- Flake 3 -->
|
||||
<g transform="translate(84 92)">
|
||||
<line x1="0" y1="-6" x2="0" y2="6"/>
|
||||
<line x1="-6" y1="0" x2="6" y2="0"/>
|
||||
<line x1="-4" y1="-4" x2="4" y2="4"/>
|
||||
<line x1="-4" y1="4" x2="4" y2="-4"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
56
frontend/public/icons/weather/86.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Averse de neige forte">
|
||||
<title>Averse de neige forte</title>
|
||||
<defs>
|
||||
<linearGradient id="skySnowShowerHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#7EA9D6"/>
|
||||
<stop offset="100%" stop-color="#3E6E9E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudSnowShowerHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#E6EDF4"/>
|
||||
<stop offset="100%" stop-color="#B7C6D6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Dark snowy sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skySnowShowerHeavy)"/>
|
||||
|
||||
<!-- Massive cloud -->
|
||||
<g transform="translate(64 46)">
|
||||
<ellipse cx="0" cy="26" rx="50" ry="30" fill="url(#cloudSnowShowerHeavy)"/>
|
||||
<circle cx="-32" cy="20" r="24" fill="url(#cloudSnowShowerHeavy)"/>
|
||||
<circle cx="18" cy="8" r="28" fill="url(#cloudSnowShowerHeavy)"/>
|
||||
<circle cx="46" cy="22" r="22" fill="url(#cloudSnowShowerHeavy)"/>
|
||||
</g>
|
||||
|
||||
<!-- Heavy snow shower (angled, larger flakes) -->
|
||||
<g stroke="#FFFFFF" stroke-width="3.5" stroke-linecap="round" transform="rotate(-18 64 92)">
|
||||
<!-- Flake 1 -->
|
||||
<g transform="translate(28 88)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
<!-- Flake 2 -->
|
||||
<g transform="translate(52 104)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
<!-- Flake 3 -->
|
||||
<g transform="translate(76 88)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
<!-- Flake 4 -->
|
||||
<g transform="translate(100 104)">
|
||||
<line x1="0" y1="-9" x2="0" y2="9"/>
|
||||
<line x1="-9" y1="0" x2="9" y2="0"/>
|
||||
<line x1="-6" y1="-6" x2="6" y2="6"/>
|
||||
<line x1="-6" y1="6" x2="6" y2="-6"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
42
frontend/public/icons/weather/95.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Orage léger">
|
||||
<title>Orage léger</title>
|
||||
<defs>
|
||||
<linearGradient id="skyLightStorm" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#6F8FB0"/>
|
||||
<stop offset="100%" stop-color="#2E4F6E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudLightStorm" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#DDE3EA"/>
|
||||
<stop offset="100%" stop-color="#AAB7C4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="lightningLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#FFF7A8"/>
|
||||
<stop offset="100%" stop-color="#FFC400"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="rainLightStorm" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#CFE9FF"/>
|
||||
<stop offset="100%" stop-color="#2F86D6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Storm sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyLightStorm)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 48)">
|
||||
<ellipse cx="0" cy="24" rx="48" ry="28" fill="url(#cloudLightStorm)"/>
|
||||
<circle cx="-30" cy="18" r="22" fill="url(#cloudLightStorm)"/>
|
||||
<circle cx="16" cy="8" r="26" fill="url(#cloudLightStorm)"/>
|
||||
<circle cx="42" cy="20" r="20" fill="url(#cloudLightStorm)"/>
|
||||
</g>
|
||||
|
||||
<!-- Lightning bolt (small) -->
|
||||
<polygon points="66,70 54,92 66,92 58,112 82,82 68,82"
|
||||
fill="url(#lightningLight)"/>
|
||||
|
||||
<!-- Light rain -->
|
||||
<g fill="url(#rainLightStorm)" opacity="0.9">
|
||||
<path d="M34 94 C34 88 40 82 40 82 C40 82 46 88 46 94 A6 6 0 1 1 34 94 Z"/>
|
||||
<path d="M86 98 C86 92 92 86 92 86 C92 86 98 92 98 98 A6 6 0 1 1 86 98 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
40
frontend/public/icons/weather/96.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Orage avec grêle légère">
|
||||
<title>Orage avec grêle légère</title>
|
||||
<defs>
|
||||
<linearGradient id="skyStormHailLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#4E7EA8"/>
|
||||
<stop offset="100%" stop-color="#1E3F63"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudStormHailLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#D1DAE3"/>
|
||||
<stop offset="100%" stop-color="#98A8B8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="boltHailLight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#FFF7A8"/>
|
||||
<stop offset="100%" stop-color="#FFB300"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Storm sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyStormHailLight)"/>
|
||||
|
||||
<!-- Cloud -->
|
||||
<g transform="translate(64 46)">
|
||||
<ellipse cx="0" cy="26" rx="50" ry="30" fill="url(#cloudStormHailLight)"/>
|
||||
<circle cx="-32" cy="20" r="24" fill="url(#cloudStormHailLight)"/>
|
||||
<circle cx="18" cy="8" r="28" fill="url(#cloudStormHailLight)"/>
|
||||
<circle cx="46" cy="22" r="22" fill="url(#cloudStormHailLight)"/>
|
||||
</g>
|
||||
|
||||
<!-- Lightning bolt -->
|
||||
<polygon points="68,66 52,96 66,96 56,120 90,84 72,84"
|
||||
fill="url(#boltHailLight)"/>
|
||||
|
||||
<!-- Light hail (small pellets) -->
|
||||
<g fill="#FFFFFF" opacity="0.95">
|
||||
<circle cx="34" cy="92" r="4"/>
|
||||
<circle cx="52" cy="104" r="4"/>
|
||||
<circle cx="70" cy="92" r="4"/>
|
||||
<circle cx="88" cy="104" r="4"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
42
frontend/public/icons/weather/99.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Orage avec grêle forte">
|
||||
<title>Orage avec grêle forte</title>
|
||||
<defs>
|
||||
<linearGradient id="skyStormHailHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#3E6E9E"/>
|
||||
<stop offset="100%" stop-color="#162F47"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="cloudStormHailHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#C9D3DD"/>
|
||||
<stop offset="100%" stop-color="#7F93A6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="boltHailHeavy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#FFF7A8"/>
|
||||
<stop offset="100%" stop-color="#FF9F00"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Dark storm sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyStormHailHeavy)"/>
|
||||
|
||||
<!-- Massive cloud -->
|
||||
<g transform="translate(64 44)">
|
||||
<ellipse cx="0" cy="28" rx="52" ry="32" fill="url(#cloudStormHailHeavy)"/>
|
||||
<circle cx="-34" cy="22" r="26" fill="url(#cloudStormHailHeavy)"/>
|
||||
<circle cx="20" cy="10" r="30" fill="url(#cloudStormHailHeavy)"/>
|
||||
<circle cx="48" cy="24" r="24" fill="url(#cloudStormHailHeavy)"/>
|
||||
</g>
|
||||
|
||||
<!-- Large lightning bolt -->
|
||||
<polygon points="70,60 50,100 66,100 54,126 96,82 74,82"
|
||||
fill="url(#boltHailHeavy)"/>
|
||||
|
||||
<!-- Heavy hail (larger pellets, more numerous) -->
|
||||
<g fill="#FFFFFF" opacity="0.95">
|
||||
<circle cx="24" cy="92" r="6"/>
|
||||
<circle cx="40" cy="108" r="6"/>
|
||||
<circle cx="58" cy="92" r="6"/>
|
||||
<circle cx="76" cy="108" r="6"/>
|
||||
<circle cx="94" cy="92" r="6"/>
|
||||
<circle cx="110" cy="108" r="6"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
46
frontend/public/icons/weather/risque_canicule.svg
Normal file
@@ -0,0 +1,46 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Risque de canicule">
|
||||
<title>Risque de canicule</title>
|
||||
<defs>
|
||||
<linearGradient id="skyHeatRisk" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#FFD27A"/>
|
||||
<stop offset="100%" stop-color="#FF8C42"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="sunHeat" cx="50%" cy="50%" r="60%">
|
||||
<stop offset="0%" stop-color="#FFF2A6"/>
|
||||
<stop offset="60%" stop-color="#FFC93C"/>
|
||||
<stop offset="100%" stop-color="#FF9F00"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Hot sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyHeatRisk)"/>
|
||||
|
||||
<!-- Intense sun -->
|
||||
<g transform="translate(64 48)">
|
||||
<circle r="26" fill="url(#sunHeat)"/>
|
||||
<g stroke="#FFB300" stroke-width="6" stroke-linecap="round">
|
||||
<line x1="0" y1="-44" x2="0" y2="-60"/>
|
||||
<line x1="0" y1="44" x2="0" y2="60"/>
|
||||
<line x1="-44" y1="0" x2="-60" y2="0"/>
|
||||
<line x1="44" y1="0" x2="60" y2="0"/>
|
||||
<line x1="31" y1="-31" x2="44" y2="-44"/>
|
||||
<line x1="-31" y1="-31" x2="-44" y2="-44"/>
|
||||
<line x1="31" y1="31" x2="44" y2="44"/>
|
||||
<line x1="-31" y1="31" x2="-44" y2="44"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Thermometer (high temperature) -->
|
||||
<g transform="translate(96 68)">
|
||||
<rect x="-6" y="-28" width="12" height="40" rx="6" fill="#FFFFFF" stroke="#E53935" stroke-width="3"/>
|
||||
<circle cx="0" cy="20" r="14" fill="#E53935"/>
|
||||
<rect x="-2" y="-8" width="4" height="20" fill="#E53935"/>
|
||||
</g>
|
||||
|
||||
<!-- Heat waves -->
|
||||
<g stroke="#FF6F00" stroke-width="4" stroke-linecap="round" opacity="0.8">
|
||||
<path d="M24 96 C28 88 20 84 24 76"/>
|
||||
<path d="M44 102 C48 94 40 90 44 82"/>
|
||||
<path d="M64 96 C68 88 60 84 64 76"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
35
frontend/public/icons/weather/risque_gèle.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Risque de gel">
|
||||
<title>Risque de gel</title>
|
||||
<defs>
|
||||
<linearGradient id="skyFrostRisk" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#BFE4FF"/>
|
||||
<stop offset="100%" stop-color="#7FAFD6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="groundFrost" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#EAF4FF"/>
|
||||
<stop offset="100%" stop-color="#C8DDF0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Cold sky -->
|
||||
<rect width="128" height="128" rx="20" fill="url(#skyFrostRisk)"/>
|
||||
|
||||
<!-- Ground frost layer -->
|
||||
<rect x="0" y="88" width="128" height="40" fill="url(#groundFrost)"/>
|
||||
|
||||
<!-- Thermometer (cold) -->
|
||||
<g transform="translate(40 40)">
|
||||
<rect x="10" y="0" width="12" height="46" rx="6" fill="#FFFFFF" stroke="#5FA8E3" stroke-width="3"/>
|
||||
<circle cx="16" cy="56" r="14" fill="#5FA8E3"/>
|
||||
<rect x="14" y="24" width="4" height="20" fill="#5FA8E3"/>
|
||||
</g>
|
||||
|
||||
<!-- Snowflake warning symbol -->
|
||||
<g transform="translate(88 54)" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round">
|
||||
<circle cx="0" cy="0" r="18" fill="#FFFFFF" opacity="0.2"/>
|
||||
<line x1="0" y1="-10" x2="0" y2="10"/>
|
||||
<line x1="-10" y1="0" x2="10" y2="0"/>
|
||||
<line x1="-7" y1="-7" x2="7" y2="7"/>
|
||||
<line x1="-7" y1="7" x2="7" y2="-7"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,9 +1,20 @@
|
||||
/// <reference types="../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { ref } from 'vue';
|
||||
import { RouterView } from 'vue-router';
|
||||
import { RouterLink, RouterView } from 'vue-router';
|
||||
import AppHeader from '@/components/AppHeader.vue';
|
||||
import AppDrawer from '@/components/AppDrawer.vue';
|
||||
const drawerOpen = ref(false);
|
||||
const links = [
|
||||
{ to: '/', label: 'Dashboard', icon: '🏠' },
|
||||
{ to: '/jardins', label: 'Jardins', icon: '🪴' },
|
||||
{ to: '/plantes', label: 'Plantes', icon: '🌱' },
|
||||
{ to: '/outils', label: 'Outils', icon: '🔧' },
|
||||
{ to: '/plantations', label: 'Plantations', icon: '🥕' },
|
||||
{ to: '/taches', label: 'Tâches', icon: '✅' },
|
||||
{ to: '/planning', label: 'Planning', icon: '📆' },
|
||||
{ to: '/calendrier', label: 'Calendrier', icon: '🌙' },
|
||||
{ to: '/reglages', label: 'Réglages', icon: '⚙️' },
|
||||
];
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
@@ -12,9 +23,11 @@ let __VLS_directives;
|
||||
// @ts-ignore
|
||||
const __VLS_0 = __VLS_asFunctionalComponent(AppHeader, new AppHeader({
|
||||
...{ 'onToggleDrawer': {} },
|
||||
...{ class: "lg:hidden" },
|
||||
}));
|
||||
const __VLS_1 = __VLS_0({
|
||||
...{ 'onToggleDrawer': {} },
|
||||
...{ class: "lg:hidden" },
|
||||
}, ...__VLS_functionalComponentArgsRest(__VLS_0));
|
||||
let __VLS_3;
|
||||
let __VLS_4;
|
||||
@@ -44,24 +57,133 @@ const __VLS_13 = {
|
||||
}
|
||||
};
|
||||
var __VLS_9;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.main, __VLS_intrinsicElements.main)({
|
||||
...{ class: "pt-14 min-h-screen" },
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "lg:flex" },
|
||||
});
|
||||
const __VLS_14 = {}.RouterView;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.aside, __VLS_intrinsicElements.aside)({
|
||||
...{ class: "hidden lg:flex lg:flex-col lg:fixed lg:inset-y-0 lg:w-60 bg-bg-hard border-r border-bg-soft z-30" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "px-5 pt-6 pb-4 border-b border-bg-soft" },
|
||||
});
|
||||
const __VLS_14 = {}.RouterLink;
|
||||
/** @type {[typeof __VLS_components.RouterLink, typeof __VLS_components.RouterLink, ]} */ ;
|
||||
// @ts-ignore
|
||||
const __VLS_15 = __VLS_asFunctionalComponent(__VLS_14, new __VLS_14({
|
||||
to: "/",
|
||||
...{ class: "text-green font-bold text-xl tracking-wide flex items-center gap-2" },
|
||||
}));
|
||||
const __VLS_16 = __VLS_15({
|
||||
to: "/",
|
||||
...{ class: "text-green font-bold text-xl tracking-wide flex items-center gap-2" },
|
||||
}, ...__VLS_functionalComponentArgsRest(__VLS_15));
|
||||
__VLS_17.slots.default;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
var __VLS_17;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.nav, __VLS_intrinsicElements.nav)({
|
||||
...{ class: "flex-1 py-4 px-3 flex flex-col gap-0.5 overflow-y-auto" },
|
||||
});
|
||||
for (const [l] of __VLS_getVForSourceType((__VLS_ctx.links))) {
|
||||
const __VLS_18 = {}.RouterLink;
|
||||
/** @type {[typeof __VLS_components.RouterLink, typeof __VLS_components.RouterLink, ]} */ ;
|
||||
// @ts-ignore
|
||||
const __VLS_19 = __VLS_asFunctionalComponent(__VLS_18, new __VLS_18({
|
||||
key: (l.to),
|
||||
to: (l.to),
|
||||
...{ class: "flex items-center gap-3 text-text-muted hover:text-text py-2 px-3 rounded-lg text-sm transition-colors group" },
|
||||
activeClass: "bg-bg-soft text-green font-medium",
|
||||
}));
|
||||
const __VLS_20 = __VLS_19({
|
||||
key: (l.to),
|
||||
to: (l.to),
|
||||
...{ class: "flex items-center gap-3 text-text-muted hover:text-text py-2 px-3 rounded-lg text-sm transition-colors group" },
|
||||
activeClass: "bg-bg-soft text-green font-medium",
|
||||
}, ...__VLS_functionalComponentArgsRest(__VLS_19));
|
||||
__VLS_21.slots.default;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-base leading-none" },
|
||||
});
|
||||
(l.icon);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(l.label);
|
||||
var __VLS_21;
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "px-4 py-4 border-t border-bg-soft text-text-muted text-xs" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.main, __VLS_intrinsicElements.main)({
|
||||
...{ class: "pt-14 lg:pt-0 lg:pl-60 min-h-screen w-full" },
|
||||
});
|
||||
const __VLS_22 = {}.RouterView;
|
||||
/** @type {[typeof __VLS_components.RouterView, ]} */ ;
|
||||
// @ts-ignore
|
||||
const __VLS_15 = __VLS_asFunctionalComponent(__VLS_14, new __VLS_14({}));
|
||||
const __VLS_16 = __VLS_15({}, ...__VLS_functionalComponentArgsRest(__VLS_15));
|
||||
const __VLS_23 = __VLS_asFunctionalComponent(__VLS_22, new __VLS_22({}));
|
||||
const __VLS_24 = __VLS_23({}, ...__VLS_functionalComponentArgsRest(__VLS_23));
|
||||
/** @type {__VLS_StyleScopedClasses['lg:hidden']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hidden']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:flex-col']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:fixed']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:inset-y-0']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:w-60']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-r']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['z-30']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-5']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['pt-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['pb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-b']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['tracking-wide']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-col']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-0.5']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['overflow-y-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-colors']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['group']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-base']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['leading-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-t']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['pt-14']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:pt-0']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:pl-60']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['min-h-screen']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
RouterLink: RouterLink,
|
||||
RouterView: RouterView,
|
||||
AppHeader: AppHeader,
|
||||
AppDrawer: AppDrawer,
|
||||
drawerOpen: drawerOpen,
|
||||
links: links,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
5
frontend/src/api/lunar.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import client from './client';
|
||||
export const lunarApi = {
|
||||
getMonth: (month) => client.get('/api/lunar', { params: { month } }).then(r => r.data),
|
||||
getDictons: (mois) => client.get('/api/dictons', { params: { mois } }).then(r => r.data),
|
||||
};
|
||||
27
frontend/src/api/lunar.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import client from './client'
|
||||
|
||||
export interface LunarDay {
|
||||
date: string
|
||||
phase: string
|
||||
illumination: number
|
||||
croissante_decroissante: string
|
||||
montante_descendante: string
|
||||
signe: string
|
||||
type_jour: string
|
||||
perigee: boolean
|
||||
apogee: boolean
|
||||
noeud_lunaire: boolean
|
||||
}
|
||||
|
||||
export interface Dicton {
|
||||
id: number
|
||||
mois: number
|
||||
jour?: number
|
||||
texte: string
|
||||
region?: string
|
||||
}
|
||||
|
||||
export const lunarApi = {
|
||||
getMonth: (month: string) => client.get<LunarDay[]>('/api/lunar', { params: { month } }).then(r => r.data),
|
||||
getDictons: (mois: number) => client.get<Dicton[]>('/api/dictons', { params: { mois } }).then(r => r.data),
|
||||
}
|
||||
4
frontend/src/api/meteo.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import client from './client';
|
||||
export const meteoApi = {
|
||||
getForecast: (days = 14) => client.get('/api/meteo', { params: { days } }).then(r => r.data),
|
||||
};
|
||||
16
frontend/src/api/meteo.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import client from './client'
|
||||
|
||||
export interface MeteoDay {
|
||||
date: string
|
||||
t_max?: number
|
||||
t_min?: number
|
||||
pluie_mm: number
|
||||
vent_kmh: number
|
||||
code: number
|
||||
label: string
|
||||
icone: string
|
||||
}
|
||||
|
||||
export const meteoApi = {
|
||||
getForecast: (days = 14) => client.get<{ days: MeteoDay[] }>('/api/meteo', { params: { days } }).then(r => r.data),
|
||||
}
|
||||
8
frontend/src/api/plants.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import client from './client';
|
||||
export const plantsApi = {
|
||||
list: (categorie) => client.get('/api/plants', { params: categorie ? { categorie } : {} }).then(r => r.data),
|
||||
get: (id) => client.get(`/api/plants/${id}`).then(r => r.data),
|
||||
create: (p) => client.post('/api/plants', p).then(r => r.data),
|
||||
update: (id, p) => client.put(`/api/plants/${id}`, p).then(r => r.data),
|
||||
delete: (id) => client.delete(`/api/plants/${id}`),
|
||||
};
|
||||
33
frontend/src/api/plants.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import client from './client'
|
||||
|
||||
export interface Plant {
|
||||
id?: number
|
||||
nom_commun: string
|
||||
nom_botanique?: string
|
||||
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
|
||||
}
|
||||
|
||||
export const plantsApi = {
|
||||
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}`),
|
||||
}
|
||||
6
frontend/src/api/recoltes.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import client from './client';
|
||||
export const recoltesApi = {
|
||||
list: (plantingId) => client.get(`/api/plantings/${plantingId}/recoltes`).then(r => r.data),
|
||||
create: (plantingId, data) => client.post(`/api/plantings/${plantingId}/recoltes`, data).then(r => r.data),
|
||||
delete: (id) => client.delete(`/api/recoltes/${id}`),
|
||||
};
|
||||
21
frontend/src/api/recoltes.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import client from './client'
|
||||
|
||||
export interface Recolte {
|
||||
id?: number
|
||||
plantation_id?: number
|
||||
quantite: number
|
||||
unite: string // kg|g|unites|litres|bottes
|
||||
date_recolte: string
|
||||
notes?: string
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export const recoltesApi = {
|
||||
list: (plantingId: number) =>
|
||||
client.get<Recolte[]>(`/api/plantings/${plantingId}/recoltes`).then(r => r.data),
|
||||
|
||||
create: (plantingId: number, data: Omit<Recolte, 'id' | 'plantation_id' | 'created_at'>) =>
|
||||
client.post<Recolte>(`/api/plantings/${plantingId}/recoltes`, data).then(r => r.data),
|
||||
|
||||
delete: (id: number) => client.delete(`/api/recoltes/${id}`),
|
||||
}
|
||||
8
frontend/src/api/tools.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import client from './client';
|
||||
export const toolsApi = {
|
||||
list: () => client.get('/api/tools').then(r => r.data),
|
||||
get: (id) => client.get(`/api/tools/${id}`).then(r => r.data),
|
||||
create: (t) => client.post('/api/tools', t).then(r => r.data),
|
||||
update: (id, t) => client.put(`/api/tools/${id}`, t).then(r => r.data),
|
||||
delete: (id) => client.delete(`/api/tools/${id}`),
|
||||
};
|
||||
17
frontend/src/api/tools.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import client from './client'
|
||||
|
||||
export interface Tool {
|
||||
id?: number
|
||||
nom: string
|
||||
description?: string
|
||||
categorie?: string
|
||||
photo_url?: string
|
||||
}
|
||||
|
||||
export const toolsApi = {
|
||||
list: () => client.get<Tool[]>('/api/tools').then(r => r.data),
|
||||
get: (id: number) => client.get<Tool>(`/api/tools/${id}`).then(r => r.data),
|
||||
create: (t: Partial<Tool>) => client.post<Tool>('/api/tools', t).then(r => r.data),
|
||||
update: (id: number, t: Partial<Tool>) => client.put<Tool>(`/api/tools/${id}`, t).then(r => r.data),
|
||||
delete: (id: number) => client.delete(`/api/tools/${id}`),
|
||||
}
|
||||
@@ -4,11 +4,12 @@ const __VLS_emit = defineEmits(['close']);
|
||||
const links = [
|
||||
{ to: '/', label: 'Dashboard' },
|
||||
{ to: '/jardins', label: 'Jardins' },
|
||||
{ to: '/varietes', label: 'Variétés' },
|
||||
{ to: '/plantes', label: 'Plantes' },
|
||||
{ to: '/outils', label: 'Outils' },
|
||||
{ to: '/plantations', label: 'Plantations' },
|
||||
{ to: '/taches', label: 'Tâches' },
|
||||
{ to: '/planning', label: 'Planning' },
|
||||
{ to: '/lunaire', label: 'Calendrier lunaire' },
|
||||
{ to: '/calendrier', label: 'Calendrier' },
|
||||
{ to: '/reglages', label: 'Réglages' },
|
||||
];
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
|
||||
@@ -1,28 +1,11 @@
|
||||
<template>
|
||||
<header class="fixed top-0 left-0 right-0 z-50 bg-bg-hard border-b border-bg-soft h-14 flex items-center px-4 gap-4">
|
||||
<button class="md:hidden text-text-muted hover:text-text text-xl leading-none" @click="$emit('toggle-drawer')">☰</button>
|
||||
<button class="text-text-muted hover:text-text text-xl leading-none" @click="$emit('toggle-drawer')">☰</button>
|
||||
<RouterLink to="/" class="text-green font-bold text-lg tracking-wide">🌿 Jardin</RouterLink>
|
||||
<nav class="hidden md:flex gap-5 ml-4">
|
||||
<RouterLink
|
||||
v-for="l in links" :key="l.to" :to="l.to"
|
||||
class="text-text-muted hover:text-text transition-colors text-sm"
|
||||
active-class="text-green font-semibold"
|
||||
>{{ l.label }}</RouterLink>
|
||||
</nav>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
defineEmits(['toggle-drawer'])
|
||||
const links = [
|
||||
{ to: '/', label: 'Dashboard' },
|
||||
{ to: '/jardins', label: 'Jardins' },
|
||||
{ to: '/varietes', label: 'Variétés' },
|
||||
{ to: '/plantations', label: 'Plantations' },
|
||||
{ to: '/taches', label: 'Tâches' },
|
||||
{ to: '/planning', label: 'Planning' },
|
||||
{ to: '/lunaire', label: 'Lunaire' },
|
||||
{ to: '/reglages', label: 'Réglages' },
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { RouterLink } from 'vue-router';
|
||||
const __VLS_emit = defineEmits(['toggle-drawer']);
|
||||
const links = [
|
||||
{ to: '/', label: 'Dashboard' },
|
||||
{ to: '/jardins', label: 'Jardins' },
|
||||
{ to: '/varietes', label: 'Variétés' },
|
||||
{ to: '/plantations', label: 'Plantations' },
|
||||
{ to: '/taches', label: 'Tâches' },
|
||||
{ to: '/planning', label: 'Planning' },
|
||||
{ to: '/lunaire', label: 'Lunaire' },
|
||||
{ to: '/reglages', label: 'Réglages' },
|
||||
];
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
@@ -22,7 +12,7 @@ __VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElement
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.$emit('toggle-drawer');
|
||||
} },
|
||||
...{ class: "md:hidden text-text-muted hover:text-text text-xl leading-none" },
|
||||
...{ class: "text-text-muted hover:text-text text-xl leading-none" },
|
||||
});
|
||||
const __VLS_0 = {}.RouterLink;
|
||||
/** @type {[typeof __VLS_components.RouterLink, typeof __VLS_components.RouterLink, ]} */ ;
|
||||
@@ -37,29 +27,6 @@ const __VLS_2 = __VLS_1({
|
||||
}, ...__VLS_functionalComponentArgsRest(__VLS_1));
|
||||
__VLS_3.slots.default;
|
||||
var __VLS_3;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.nav, __VLS_intrinsicElements.nav)({
|
||||
...{ class: "hidden md:flex gap-5 ml-4" },
|
||||
});
|
||||
for (const [l] of __VLS_getVForSourceType((__VLS_ctx.links))) {
|
||||
const __VLS_4 = {}.RouterLink;
|
||||
/** @type {[typeof __VLS_components.RouterLink, typeof __VLS_components.RouterLink, ]} */ ;
|
||||
// @ts-ignore
|
||||
const __VLS_5 = __VLS_asFunctionalComponent(__VLS_4, new __VLS_4({
|
||||
key: (l.to),
|
||||
to: (l.to),
|
||||
...{ class: "text-text-muted hover:text-text transition-colors text-sm" },
|
||||
activeClass: "text-green font-semibold",
|
||||
}));
|
||||
const __VLS_6 = __VLS_5({
|
||||
key: (l.to),
|
||||
to: (l.to),
|
||||
...{ class: "text-text-muted hover:text-text transition-colors text-sm" },
|
||||
activeClass: "text-green font-semibold",
|
||||
}, ...__VLS_functionalComponentArgsRest(__VLS_5));
|
||||
__VLS_7.slots.default;
|
||||
(l.label);
|
||||
var __VLS_7;
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['fixed']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['top-0']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['left-0']} */ ;
|
||||
@@ -73,7 +40,6 @@ for (const [l] of __VLS_getVForSourceType((__VLS_ctx.links))) {
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['md:hidden']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xl']} */ ;
|
||||
@@ -82,20 +48,11 @@ for (const [l] of __VLS_getVForSourceType((__VLS_ctx.links))) {
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['tracking-wide']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hidden']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['md:flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-5']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['ml-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-colors']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
RouterLink: RouterLink,
|
||||
links: links,
|
||||
};
|
||||
},
|
||||
emits: {},
|
||||
|
||||
@@ -5,11 +5,15 @@ export default createRouter({
|
||||
{ path: '/', component: () => import('@/views/DashboardView.vue') },
|
||||
{ path: '/jardins', component: () => import('@/views/JardinsView.vue') },
|
||||
{ path: '/jardins/:id', component: () => import('@/views/JardinDetailView.vue') },
|
||||
{ path: '/varietes', component: () => import('@/views/VarietesView.vue') },
|
||||
{ path: '/plantes', component: () => import('@/views/PlantesView.vue') },
|
||||
{ path: '/outils', component: () => import('@/views/OutilsView.vue') },
|
||||
{ path: '/plantations', component: () => import('@/views/PlantationsView.vue') },
|
||||
{ path: '/planning', component: () => import('@/views/PlanningView.vue') },
|
||||
{ path: '/taches', component: () => import('@/views/TachesView.vue') },
|
||||
{ path: '/lunaire', component: () => import('@/views/LunaireView.vue') },
|
||||
{ path: '/calendrier', component: () => import('@/views/CalendrierView.vue') },
|
||||
{ path: '/reglages', component: () => import('@/views/ReglagesView.vue') },
|
||||
// Redirect des anciens liens
|
||||
{ path: '/varietes', redirect: '/plantes' },
|
||||
{ path: '/lunaire', redirect: '/calendrier' },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -18,10 +18,17 @@ export const useGardensStore = defineStore('gardens', () => {
|
||||
return created
|
||||
}
|
||||
|
||||
async function update(id: number, g: Partial<Garden>) {
|
||||
const updated = await gardensApi.update(id, g)
|
||||
const idx = gardens.value.findIndex(x => x.id === id)
|
||||
if (idx !== -1) gardens.value[idx] = updated
|
||||
return updated
|
||||
}
|
||||
|
||||
async function remove(id: number) {
|
||||
await gardensApi.delete(id)
|
||||
gardens.value = gardens.value.filter(g => g.id !== id)
|
||||
}
|
||||
|
||||
return { gardens, loading, fetchAll, create, remove }
|
||||
return { gardens, loading, fetchAll, create, update, remove }
|
||||
})
|
||||
|
||||
@@ -18,10 +18,17 @@ export const usePlantingsStore = defineStore('plantings', () => {
|
||||
return created
|
||||
}
|
||||
|
||||
async function update(id: number, p: Partial<Planting>) {
|
||||
const updated = await plantingsApi.update(id, p)
|
||||
const idx = plantings.value.findIndex(x => x.id === id)
|
||||
if (idx !== -1) plantings.value[idx] = updated
|
||||
return updated
|
||||
}
|
||||
|
||||
async function remove(id: number) {
|
||||
await plantingsApi.delete(id)
|
||||
plantings.value = plantings.value.filter(p => p.id !== id)
|
||||
}
|
||||
|
||||
return { plantings, loading, fetchAll, create, remove }
|
||||
return { plantings, loading, fetchAll, create, update, remove }
|
||||
})
|
||||
|
||||
26
frontend/src/stores/plants.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { plantsApi } from '@/api/plants';
|
||||
export const usePlantsStore = defineStore('plants', () => {
|
||||
const plants = ref([]);
|
||||
const loading = ref(false);
|
||||
async function fetchAll(categorie) {
|
||||
loading.value = true;
|
||||
try {
|
||||
plants.value = await plantsApi.list(categorie);
|
||||
}
|
||||
finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
async function create(p) {
|
||||
const created = await plantsApi.create(p);
|
||||
plants.value.push(created);
|
||||
return created;
|
||||
}
|
||||
async function remove(id) {
|
||||
await plantsApi.delete(id);
|
||||
plants.value = plants.value.filter(p => p.id !== id);
|
||||
}
|
||||
return { plants, loading, fetchAll, create, remove };
|
||||
});
|
||||
27
frontend/src/stores/plants.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { plantsApi, type Plant } from '@/api/plants'
|
||||
|
||||
export const usePlantsStore = defineStore('plants', () => {
|
||||
const plants = ref<Plant[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
async function fetchAll(categorie?: string) {
|
||||
loading.value = true
|
||||
try { plants.value = await plantsApi.list(categorie) }
|
||||
finally { loading.value = false }
|
||||
}
|
||||
|
||||
async function create(p: Partial<Plant>) {
|
||||
const created = await plantsApi.create(p)
|
||||
plants.value.push(created)
|
||||
return created
|
||||
}
|
||||
|
||||
async function remove(id: number) {
|
||||
await plantsApi.delete(id)
|
||||
plants.value = plants.value.filter(p => p.id !== id)
|
||||
}
|
||||
|
||||
return { plants, loading, fetchAll, create, remove }
|
||||
})
|
||||
@@ -18,6 +18,13 @@ export const useTasksStore = defineStore('tasks', () => {
|
||||
return created
|
||||
}
|
||||
|
||||
async function update(id: number, data: Partial<Task>) {
|
||||
const updated = await tasksApi.update(id, data)
|
||||
const idx = tasks.value.findIndex(t => t.id === id)
|
||||
if (idx !== -1) tasks.value[idx] = updated
|
||||
return updated
|
||||
}
|
||||
|
||||
async function updateStatut(id: number, statut: string) {
|
||||
const t = tasks.value.find(t => t.id === id)
|
||||
if (!t) return
|
||||
@@ -30,5 +37,5 @@ export const useTasksStore = defineStore('tasks', () => {
|
||||
tasks.value = tasks.value.filter(t => t.id !== id)
|
||||
}
|
||||
|
||||
return { tasks, loading, fetchAll, create, updateStatut, remove }
|
||||
return { tasks, loading, fetchAll, create, update, updateStatut, remove }
|
||||
})
|
||||
|
||||
26
frontend/src/stores/tools.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { toolsApi } from '@/api/tools';
|
||||
export const useToolsStore = defineStore('tools', () => {
|
||||
const tools = ref([]);
|
||||
const loading = ref(false);
|
||||
async function fetchAll() {
|
||||
loading.value = true;
|
||||
try {
|
||||
tools.value = await toolsApi.list();
|
||||
}
|
||||
finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
async function create(t) {
|
||||
const created = await toolsApi.create(t);
|
||||
tools.value.push(created);
|
||||
return created;
|
||||
}
|
||||
async function remove(id) {
|
||||
await toolsApi.delete(id);
|
||||
tools.value = tools.value.filter(t => t.id !== id);
|
||||
}
|
||||
return { tools, loading, fetchAll, create, remove };
|
||||
});
|
||||
34
frontend/src/stores/tools.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { toolsApi, type Tool } from '@/api/tools'
|
||||
|
||||
export const useToolsStore = defineStore('tools', () => {
|
||||
const tools = ref<Tool[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
async function fetchAll() {
|
||||
loading.value = true
|
||||
try { tools.value = await toolsApi.list() }
|
||||
finally { loading.value = false }
|
||||
}
|
||||
|
||||
async function create(t: Partial<Tool>) {
|
||||
const created = await toolsApi.create(t)
|
||||
tools.value.push(created)
|
||||
return created
|
||||
}
|
||||
|
||||
async function update(id: number, t: Partial<Tool>) {
|
||||
const updated = await toolsApi.update(id, t)
|
||||
const idx = tools.value.findIndex(x => x.id === id)
|
||||
if (idx !== -1) tools.value[idx] = updated
|
||||
return updated
|
||||
}
|
||||
|
||||
async function remove(id: number) {
|
||||
await toolsApi.delete(id)
|
||||
tools.value = tools.value.filter(t => t.id !== id)
|
||||
}
|
||||
|
||||
return { tools, loading, fetchAll, create, update, remove }
|
||||
})
|
||||
@@ -2,96 +2,100 @@
|
||||
<div class="p-4 max-w-4xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold text-green">📷 Bibliothèque</h1>
|
||||
<button
|
||||
@click="showIdentify = true"
|
||||
class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90"
|
||||
>
|
||||
<button @click="showIdentify = true"
|
||||
class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90">
|
||||
Identifier une plante
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Filtres -->
|
||||
<div class="flex gap-2 mb-4 flex-wrap">
|
||||
<button
|
||||
v-for="f in filters"
|
||||
:key="f.val"
|
||||
@click="activeFilter = f.val"
|
||||
:class="[
|
||||
'px-3 py-1 rounded-full text-xs font-medium transition-colors',
|
||||
activeFilter === f.val
|
||||
? 'bg-green text-bg'
|
||||
: 'bg-bg-soft text-text-muted hover:text-text',
|
||||
]"
|
||||
>
|
||||
<button v-for="f in filters" :key="f.val" @click="activeFilter = f.val"
|
||||
:class="['px-3 py-1 rounded-full text-xs font-medium transition-colors',
|
||||
activeFilter === f.val ? 'bg-green text-bg' : 'bg-bg-soft text-text-muted hover:text-text']">
|
||||
{{ f.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Grille -->
|
||||
<div v-if="loading" class="text-text-muted text-sm">Chargement...</div>
|
||||
<div v-else-if="!filtered.length" class="text-text-muted text-sm py-4">
|
||||
Aucune photo.
|
||||
</div>
|
||||
<div v-else-if="!filtered.length" class="text-text-muted text-sm py-4">Aucune photo.</div>
|
||||
<div v-else class="grid grid-cols-3 md:grid-cols-4 gap-2">
|
||||
<div
|
||||
v-for="m in filtered"
|
||||
:key="m.id"
|
||||
<div v-for="m in filtered" :key="m.id"
|
||||
class="aspect-square rounded-lg overflow-hidden bg-bg-hard relative group cursor-pointer"
|
||||
@click="lightbox = m"
|
||||
>
|
||||
<img
|
||||
:src="m.thumbnail_url || m.url"
|
||||
:alt="m.titre || ''"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
<div
|
||||
v-if="m.identified_common"
|
||||
class="absolute bottom-0 left-0 right-0 bg-black/70 text-xs text-green px-1 py-0.5 truncate"
|
||||
>
|
||||
@click="openLightbox(m)">
|
||||
<img :src="m.thumbnail_url || m.url" :alt="m.titre || ''" class="w-full h-full object-cover" />
|
||||
<div v-if="m.identified_common"
|
||||
class="absolute bottom-0 left-0 right-0 bg-black/70 text-xs text-green px-1 py-0.5 truncate">
|
||||
{{ m.identified_common }}
|
||||
</div>
|
||||
<div class="absolute top-1 left-1 bg-black/60 text-text-muted text-xs px-1 rounded">
|
||||
{{ labelFor(m.entity_type) }}
|
||||
</div>
|
||||
<button @click.stop="deleteMedia(m)"
|
||||
class="hidden group-hover:flex absolute top-1 right-1 bg-red/80 text-white text-xs rounded px-1">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lightbox -->
|
||||
<div
|
||||
v-if="lightbox"
|
||||
class="fixed inset-0 bg-black/80 z-50 flex items-center justify-center p-4"
|
||||
@click.self="lightbox = null"
|
||||
>
|
||||
<div class="max-w-lg w-full">
|
||||
<img :src="lightbox.url" class="w-full rounded-xl" />
|
||||
<div
|
||||
v-if="lightbox.identified_species"
|
||||
class="text-center mt-3 text-text-muted text-sm"
|
||||
>
|
||||
<div class="text-green font-semibold text-base">
|
||||
{{ lightbox.identified_common }}
|
||||
<div v-if="lightbox" class="fixed inset-0 bg-black/80 z-50 flex items-center justify-center p-4" @click.self="lightbox = null">
|
||||
<div class="max-w-lg w-full bg-bg-hard rounded-xl overflow-hidden border border-bg-soft">
|
||||
<img :src="lightbox.url" class="w-full" />
|
||||
<div class="p-4">
|
||||
<!-- Infos identification -->
|
||||
<div v-if="lightbox.identified_species" class="text-center mb-3">
|
||||
<div class="text-green font-semibold text-base">{{ lightbox.identified_common }}</div>
|
||||
<div class="italic text-text-muted text-sm">{{ lightbox.identified_species }}</div>
|
||||
<div class="text-xs text-text-muted mt-1">
|
||||
Confiance : {{ Math.round((lightbox.identified_confidence || 0) * 100) }}% — via {{ lightbox.identified_source }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="italic">{{ lightbox.identified_species }}</div>
|
||||
<div class="text-xs mt-1">
|
||||
Confiance : {{ Math.round((lightbox.identified_confidence || 0) * 100) }}% —
|
||||
via {{ lightbox.identified_source }}
|
||||
<!-- Lien actuel -->
|
||||
<div class="text-xs text-text-muted mb-3 text-center">
|
||||
{{ labelFor(lightbox.entity_type) }}
|
||||
<span v-if="lightbox.entity_type === 'plante' && plantName(lightbox.entity_id)">
|
||||
: <span class="text-green font-medium">{{ plantName(lightbox.entity_id) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- Actions -->
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
<button @click="startLink(lightbox!)"
|
||||
class="flex-1 bg-blue/20 text-blue hover:bg-blue/30 px-3 py-2 rounded-lg text-xs font-medium transition-colors">
|
||||
🔗 Associer à une plante
|
||||
</button>
|
||||
<button @click="deleteMedia(lightbox!); lightbox = null"
|
||||
class="bg-red/20 text-red hover:bg-red/30 px-3 py-2 rounded-lg text-xs font-medium transition-colors">
|
||||
🗑 Supprimer
|
||||
</button>
|
||||
</div>
|
||||
<button class="mt-3 w-full text-text-muted hover:text-text text-sm" @click="lightbox = null">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal associer à une plante -->
|
||||
<div v-if="linkMedia" class="fixed inset-0 bg-black/70 z-50 flex items-center justify-center p-4" @click.self="linkMedia = null">
|
||||
<div class="bg-bg-hard rounded-xl p-6 w-full max-w-sm border border-bg-soft">
|
||||
<h3 class="text-text font-bold mb-4">Associer à une plante</h3>
|
||||
<select v-model="linkPlantId"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green mb-4">
|
||||
<option :value="null">-- Choisir une plante --</option>
|
||||
<option v-for="p in plantsStore.plants" :key="p.id" :value="p.id">
|
||||
{{ p.nom_commun }}{{ p.variete ? ' — ' + p.variete : '' }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button @click="linkMedia = null" class="px-4 py-2 text-text-muted hover:text-text text-sm">Annuler</button>
|
||||
<button @click="confirmLink" :disabled="!linkPlantId"
|
||||
class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90 disabled:opacity-40">
|
||||
Associer
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="mt-4 w-full text-text-muted hover:text-text text-sm"
|
||||
@click="lightbox = null"
|
||||
>
|
||||
Fermer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal identification -->
|
||||
<PhotoIdentifyModal
|
||||
v-if="showIdentify"
|
||||
@close="showIdentify = false"
|
||||
@identified="onIdentified"
|
||||
/>
|
||||
<PhotoIdentifyModal v-if="showIdentify" @close="showIdentify = false" @identified="onIdentified" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -99,18 +103,13 @@
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import axios from 'axios'
|
||||
import PhotoIdentifyModal from '@/components/PhotoIdentifyModal.vue'
|
||||
import { usePlantsStore } from '@/stores/plants'
|
||||
|
||||
interface Media {
|
||||
id: number
|
||||
entity_type: string
|
||||
entity_id: number
|
||||
url: string
|
||||
thumbnail_url?: string
|
||||
titre?: string
|
||||
identified_species?: string
|
||||
identified_common?: string
|
||||
identified_confidence?: number
|
||||
identified_source?: string
|
||||
id: number; entity_type: string; entity_id: number
|
||||
url: string; thumbnail_url?: string; titre?: string
|
||||
identified_species?: string; identified_common?: string
|
||||
identified_confidence?: number; identified_source?: string
|
||||
}
|
||||
|
||||
const medias = ref<Media[]>([])
|
||||
@@ -118,6 +117,10 @@ const loading = ref(false)
|
||||
const lightbox = ref<Media | null>(null)
|
||||
const showIdentify = ref(false)
|
||||
const activeFilter = ref('')
|
||||
const linkMedia = ref<Media | null>(null)
|
||||
const linkPlantId = ref<number | null>(null)
|
||||
|
||||
const plantsStore = usePlantsStore()
|
||||
|
||||
const filters = [
|
||||
{ val: '', label: 'Toutes' },
|
||||
@@ -125,24 +128,53 @@ const filters = [
|
||||
{ val: 'jardin', label: '🏡 Jardins' },
|
||||
{ val: 'plantation', label: '🥕 Plantations' },
|
||||
{ val: 'outil', label: '🔧 Outils' },
|
||||
{ val: 'bibliotheque', label: '📷 Sans lien' },
|
||||
]
|
||||
|
||||
const filtered = computed(() =>
|
||||
activeFilter.value
|
||||
? medias.value.filter((m) => m.entity_type === activeFilter.value)
|
||||
: medias.value,
|
||||
activeFilter.value ? medias.value.filter(m => m.entity_type === activeFilter.value) : medias.value
|
||||
)
|
||||
|
||||
function labelFor(type: string) {
|
||||
const map: Record<string, string> = {
|
||||
plante: '🌱',
|
||||
jardin: '🏡',
|
||||
plantation: '🥕',
|
||||
outil: '🔧',
|
||||
plante: '🌱 Plante', jardin: '🏡 Jardin',
|
||||
plantation: '🥕 Plantation', outil: '🔧 Outil', bibliotheque: '📷'
|
||||
}
|
||||
return map[type] ?? '📷'
|
||||
}
|
||||
|
||||
function plantName(id: number) {
|
||||
return plantsStore.plants.find(p => p.id === id)?.nom_commun ?? ''
|
||||
}
|
||||
|
||||
function openLightbox(m: Media) { lightbox.value = m }
|
||||
|
||||
function startLink(m: Media) {
|
||||
linkMedia.value = m
|
||||
linkPlantId.value = m.entity_type === 'plante' ? m.entity_id : null
|
||||
}
|
||||
|
||||
async function confirmLink() {
|
||||
if (!linkMedia.value || !linkPlantId.value) return
|
||||
await axios.patch(`/api/media/${linkMedia.value.id}`, {
|
||||
entity_type: 'plante', entity_id: linkPlantId.value,
|
||||
})
|
||||
const m = medias.value.find(x => x.id === linkMedia.value!.id)
|
||||
if (m) { m.entity_type = 'plante'; m.entity_id = linkPlantId.value! }
|
||||
if (lightbox.value?.id === linkMedia.value.id) {
|
||||
lightbox.value = { ...lightbox.value, entity_type: 'plante', entity_id: linkPlantId.value! }
|
||||
}
|
||||
linkMedia.value = null
|
||||
linkPlantId.value = null
|
||||
}
|
||||
|
||||
async function deleteMedia(m: Media) {
|
||||
if (!confirm('Supprimer cette photo ?')) return
|
||||
await axios.delete(`/api/media/${m.id}`)
|
||||
medias.value = medias.value.filter(x => x.id !== m.id)
|
||||
if (lightbox.value?.id === m.id) lightbox.value = null
|
||||
}
|
||||
|
||||
async function fetchAll() {
|
||||
loading.value = true
|
||||
try {
|
||||
@@ -153,9 +185,10 @@ async function fetchAll() {
|
||||
}
|
||||
}
|
||||
|
||||
function onIdentified() {
|
||||
fetchAll()
|
||||
}
|
||||
function onIdentified() { fetchAll() }
|
||||
|
||||
onMounted(fetchAll)
|
||||
onMounted(() => {
|
||||
fetchAll()
|
||||
plantsStore.fetchAll()
|
||||
})
|
||||
</script>
|
||||
|
||||
247
frontend/src/views/CalendrierView.vue
Normal file
@@ -0,0 +1,247 @@
|
||||
<template>
|
||||
<div class="p-4 max-w-4xl mx-auto">
|
||||
<h1 class="text-2xl font-bold text-blue mb-4">📅 Calendrier</h1>
|
||||
|
||||
<!-- Onglets -->
|
||||
<div class="flex gap-1 mb-6 bg-bg-soft rounded-lg p-1 w-fit">
|
||||
<button v-for="tab in tabs" :key="tab.id" @click="activeTab = tab.id"
|
||||
:class="['px-4 py-2 rounded-md text-sm font-medium transition-colors',
|
||||
activeTab === tab.id ? 'bg-blue text-bg' : 'text-text-muted hover:text-text']">
|
||||
{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Sélecteur mois (onglets lunaire/dictons) -->
|
||||
<div v-if="activeTab === 'lunaire' || activeTab === 'dictons'" class="flex items-center gap-3 mb-4">
|
||||
<button @click="prevMonth" class="text-text-muted hover:text-text text-lg">◀</button>
|
||||
<span class="text-text font-semibold">{{ monthLabel }}</span>
|
||||
<button @click="nextMonth" class="text-text-muted hover:text-text text-lg">▶</button>
|
||||
</div>
|
||||
|
||||
<!-- === LUNAIRE === -->
|
||||
<div v-if="activeTab === 'lunaire'">
|
||||
<div v-if="loadingLunar" class="text-text-muted text-sm py-4">Calcul en cours (skyfield)...</div>
|
||||
<div v-else-if="errorLunar" class="bg-red/10 border border-red rounded-lg p-4 text-red text-sm">
|
||||
{{ errorLunar }}
|
||||
</div>
|
||||
<div v-else-if="!lunarDays.length" class="text-text-muted text-sm py-4">Aucune donnée lunaire.</div>
|
||||
<div v-else>
|
||||
<!-- Grille calendrier -->
|
||||
<div class="grid grid-cols-7 gap-1 mb-2">
|
||||
<div v-for="d in ['Lun','Mar','Mer','Jeu','Ven','Sam','Dim']" :key="d"
|
||||
class="text-center text-text-muted text-xs py-1">{{ d }}</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-7 gap-1">
|
||||
<div v-for="_ in firstDayOffset" :key="'empty-'+_" class="h-16"></div>
|
||||
<div v-for="day in lunarDays" :key="day.date"
|
||||
@click="selectedDay = day"
|
||||
:class="['h-16 bg-bg-soft rounded-lg p-1 cursor-pointer hover:border hover:border-blue transition-colors flex flex-col items-center justify-center gap-0.5',
|
||||
selectedDay?.date === day.date ? 'border border-blue' : 'border border-transparent']">
|
||||
<span class="text-text-muted text-xs">{{ new Date(day.date+'T12:00:00').getDate() }}</span>
|
||||
<img :src="moonIcon(day.illumination, day.croissante_decroissante)" class="w-6 h-6 opacity-90" alt="phase" />
|
||||
<span class="text-xs leading-none" :class="typeColor(day.type_jour)">{{ typeEmoji(day.type_jour) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Détail jour sélectionné -->
|
||||
<div v-if="selectedDay" class="mt-4 bg-bg-soft rounded-xl p-4 border border-bg-hard">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<img :src="moonIcon(selectedDay.illumination, selectedDay.croissante_decroissante)" class="w-10 h-10" alt="phase" />
|
||||
<div>
|
||||
<div class="text-text font-bold">{{ formatDate(selectedDay.date) }}</div>
|
||||
<div class="text-text-muted text-sm">{{ selectedDay.phase || 'Pas de phase particulière' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 text-sm">
|
||||
<div class="bg-bg rounded-lg p-2">
|
||||
<div class="text-text-muted text-xs mb-1">Illumination</div>
|
||||
<div class="text-text">{{ selectedDay.illumination }}%</div>
|
||||
</div>
|
||||
<div class="bg-bg rounded-lg p-2">
|
||||
<div class="text-text-muted text-xs mb-1">Tendance</div>
|
||||
<div class="text-text">{{ selectedDay.croissante_decroissante }}</div>
|
||||
</div>
|
||||
<div class="bg-bg rounded-lg p-2">
|
||||
<div class="text-text-muted text-xs mb-1">Lune</div>
|
||||
<div class="text-text">{{ selectedDay.montante_descendante }}</div>
|
||||
</div>
|
||||
<div class="bg-bg rounded-lg p-2">
|
||||
<div class="text-text-muted text-xs mb-1">Signe</div>
|
||||
<div class="text-text">{{ selectedDay.signe }}</div>
|
||||
</div>
|
||||
<div class="bg-bg rounded-lg p-2 col-span-2">
|
||||
<div class="text-text-muted text-xs mb-1">Type de jour</div>
|
||||
<div :class="['font-semibold', typeColor(selectedDay.type_jour)]">
|
||||
{{ typeEmoji(selectedDay.type_jour) }} {{ selectedDay.type_jour }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedDay.perigee" class="mt-2 text-xs text-orange bg-orange/10 rounded px-2 py-1">⚡ Périgée (lune proche)</div>
|
||||
<div v-if="selectedDay.apogee" class="mt-2 text-xs text-blue bg-blue/10 rounded px-2 py-1">🌌 Apogée (lune éloignée)</div>
|
||||
<div v-if="selectedDay.noeud_lunaire" class="mt-2 text-xs text-yellow bg-yellow/10 rounded px-2 py-1">✦ Nœud lunaire</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- === MÉTÉO === -->
|
||||
<div v-if="activeTab === 'meteo'">
|
||||
<div v-if="loadingMeteo" class="text-text-muted text-sm py-4">Chargement météo...</div>
|
||||
<div v-else-if="!meteoData.length" class="text-text-muted text-sm py-4">Données météo non disponibles.</div>
|
||||
<div v-else class="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||
<div v-for="day in meteoData" :key="day.date"
|
||||
class="bg-bg-soft rounded-xl p-3 border border-bg-hard flex flex-col items-center gap-1">
|
||||
<div class="text-text-muted text-xs">{{ formatDate(day.date) }}</div>
|
||||
<img :src="weatherIcon(day.code)" class="w-10 h-10" :alt="day.label" />
|
||||
<div class="text-text text-xs font-medium text-center">{{ day.label }}</div>
|
||||
<div class="flex gap-2 text-xs mt-1">
|
||||
<span class="text-orange">↑{{ day.t_max?.toFixed(0) }}°</span>
|
||||
<span class="text-blue">↓{{ day.t_min?.toFixed(0) }}°</span>
|
||||
</div>
|
||||
<div v-if="day.pluie_mm > 0" class="text-xs text-blue">💧 {{ day.pluie_mm }}mm</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- === TÂCHES === -->
|
||||
<div v-if="activeTab === 'taches'">
|
||||
<p class="text-text-muted text-sm py-4">Les tâches planifiées s'afficheront ici.</p>
|
||||
</div>
|
||||
|
||||
<!-- === DICTONS === -->
|
||||
<div v-if="activeTab === 'dictons'">
|
||||
<div v-if="!dictons.length" class="text-text-muted text-sm py-4">Aucun dicton pour ce mois.</div>
|
||||
<div v-for="d in dictons" :key="d.id" class="bg-bg-soft rounded-lg p-4 mb-2 border border-bg-hard">
|
||||
<p class="text-text italic text-sm">"{{ d.texte }}"</p>
|
||||
<p v-if="d.region" class="text-text-muted text-xs mt-1">— {{ d.region }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { lunarApi, type LunarDay, type Dicton } from '@/api/lunar'
|
||||
import { meteoApi, type MeteoDay } from '@/api/meteo'
|
||||
|
||||
const activeTab = ref('lunaire')
|
||||
const tabs = [
|
||||
{ id: 'lunaire', label: '🌙 Lunaire' },
|
||||
{ id: 'meteo', label: '☀️ Météo' },
|
||||
{ id: 'taches', label: '✅ Tâches' },
|
||||
{ id: 'dictons', label: '📜 Dictons' },
|
||||
]
|
||||
|
||||
const now = new Date()
|
||||
const currentYear = ref(now.getFullYear())
|
||||
const currentMonth = ref(now.getMonth() + 1)
|
||||
|
||||
const monthLabel = computed(() => {
|
||||
const d = new Date(currentYear.value, currentMonth.value - 1, 1)
|
||||
return d.toLocaleDateString('fr-FR', { month: 'long', year: 'numeric' })
|
||||
})
|
||||
|
||||
const monthStr = computed(() => `${currentYear.value}-${String(currentMonth.value).padStart(2, '0')}`)
|
||||
|
||||
function prevMonth() {
|
||||
if (currentMonth.value === 1) { currentMonth.value = 12; currentYear.value-- }
|
||||
else currentMonth.value--
|
||||
}
|
||||
function nextMonth() {
|
||||
if (currentMonth.value === 12) { currentMonth.value = 1; currentYear.value++ }
|
||||
else currentMonth.value++
|
||||
}
|
||||
|
||||
// Lunaire
|
||||
const lunarDays = ref<LunarDay[]>([])
|
||||
const loadingLunar = ref(false)
|
||||
const errorLunar = ref('')
|
||||
const selectedDay = ref<LunarDay | null>(null)
|
||||
|
||||
const firstDayOffset = computed(() => {
|
||||
if (!lunarDays.value.length) return 0
|
||||
const d = new Date(lunarDays.value[0].date + 'T12:00:00')
|
||||
return (d.getDay() + 6) % 7 // Lundi=0
|
||||
})
|
||||
|
||||
async function loadLunar() {
|
||||
loadingLunar.value = true; errorLunar.value = ''; selectedDay.value = null
|
||||
try {
|
||||
lunarDays.value = await lunarApi.getMonth(monthStr.value)
|
||||
} catch (e: unknown) {
|
||||
const err = e as { response?: { data?: { detail?: string } } }
|
||||
errorLunar.value = err?.response?.data?.detail || 'Erreur lors du chargement du calendrier lunaire.'
|
||||
} finally {
|
||||
loadingLunar.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Météo
|
||||
const meteoData = ref<MeteoDay[]>([])
|
||||
const loadingMeteo = ref(false)
|
||||
|
||||
async function loadMeteo() {
|
||||
loadingMeteo.value = true
|
||||
try {
|
||||
const res = await meteoApi.getForecast(14)
|
||||
meteoData.value = res.days || []
|
||||
} catch { meteoData.value = [] }
|
||||
finally { loadingMeteo.value = false }
|
||||
}
|
||||
|
||||
// Dictons
|
||||
const dictons = ref<Dicton[]>([])
|
||||
async function loadDictons() {
|
||||
try { dictons.value = await lunarApi.getDictons(currentMonth.value) }
|
||||
catch { dictons.value = [] }
|
||||
}
|
||||
|
||||
// Helpers affichage
|
||||
function moonIcon(illum: number, tendance: string): string {
|
||||
const i = illum / 100
|
||||
let name: string
|
||||
if (i < 0.05) name = 'new_moon'
|
||||
else if (tendance === 'Croissante') {
|
||||
if (i < 0.35) name = 'waxing_crescent'
|
||||
else if (i < 0.65) name = 'first_quarter'
|
||||
else if (i < 0.95) name = 'waxing_gibbous'
|
||||
else name = 'full_moon'
|
||||
} else {
|
||||
if (i > 0.95) name = 'full_moon'
|
||||
else if (i > 0.65) name = 'waning_gibbous'
|
||||
else if (i > 0.35) name = 'last_quarter'
|
||||
else name = 'waning_crescent'
|
||||
}
|
||||
return `/icons/moon/${name}.svg`
|
||||
}
|
||||
|
||||
function weatherIcon(code: number): string {
|
||||
// WMO code → fichier SVG disponible
|
||||
const available = [0, 1, 2, 3, 45, 48, 51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 71, 73, 75, 77, 80, 81, 82, 85, 86, 95, 96, 99]
|
||||
const closest = available.reduce((prev, curr) =>
|
||||
Math.abs(curr - code) < Math.abs(prev - code) ? curr : prev
|
||||
)
|
||||
return `/icons/weather/${closest}.svg`
|
||||
}
|
||||
function typeEmoji(type: string): string {
|
||||
return ({ Racine: '🌱', Feuille: '🌿', Fleur: '🌸', Fruit: '🍅' } as Record<string, string>)[type] || '●'
|
||||
}
|
||||
function typeColor(type: string): string {
|
||||
return ({ Racine: 'text-yellow', Feuille: 'text-green', Fleur: 'text-orange', Fruit: 'text-red' } as Record<string, string>)[type] || 'text-text-muted'
|
||||
}
|
||||
function formatDate(dateStr: string): string {
|
||||
return new Date(dateStr + 'T12:00:00').toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })
|
||||
}
|
||||
|
||||
watch(monthStr, () => {
|
||||
if (activeTab.value === 'lunaire') loadLunar()
|
||||
if (activeTab.value === 'dictons') loadDictons()
|
||||
})
|
||||
|
||||
watch(activeTab, (tab) => {
|
||||
if (tab === 'lunaire' && !lunarDays.value.length) loadLunar()
|
||||
if (tab === 'meteo' && !meteoData.value.length) loadMeteo()
|
||||
if (tab === 'dictons' && !dictons.value.length) loadDictons()
|
||||
})
|
||||
|
||||
onMounted(() => { loadLunar(); loadMeteo() })
|
||||
</script>
|
||||
635
frontend/src/views/CalendrierView.vue.js
Normal file
@@ -0,0 +1,635 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { lunarApi } from '@/api/lunar';
|
||||
import { meteoApi } from '@/api/meteo';
|
||||
const activeTab = ref('lunaire');
|
||||
const tabs = [
|
||||
{ id: 'lunaire', label: '🌙 Lunaire' },
|
||||
{ id: 'meteo', label: '☀️ Météo' },
|
||||
{ id: 'taches', label: '✅ Tâches' },
|
||||
{ id: 'dictons', label: '📜 Dictons' },
|
||||
];
|
||||
const now = new Date();
|
||||
const currentYear = ref(now.getFullYear());
|
||||
const currentMonth = ref(now.getMonth() + 1);
|
||||
const monthLabel = computed(() => {
|
||||
const d = new Date(currentYear.value, currentMonth.value - 1, 1);
|
||||
return d.toLocaleDateString('fr-FR', { month: 'long', year: 'numeric' });
|
||||
});
|
||||
const monthStr = computed(() => `${currentYear.value}-${String(currentMonth.value).padStart(2, '0')}`);
|
||||
function prevMonth() {
|
||||
if (currentMonth.value === 1) {
|
||||
currentMonth.value = 12;
|
||||
currentYear.value--;
|
||||
}
|
||||
else
|
||||
currentMonth.value--;
|
||||
}
|
||||
function nextMonth() {
|
||||
if (currentMonth.value === 12) {
|
||||
currentMonth.value = 1;
|
||||
currentYear.value++;
|
||||
}
|
||||
else
|
||||
currentMonth.value++;
|
||||
}
|
||||
// Lunaire
|
||||
const lunarDays = ref([]);
|
||||
const loadingLunar = ref(false);
|
||||
const errorLunar = ref('');
|
||||
const selectedDay = ref(null);
|
||||
const firstDayOffset = computed(() => {
|
||||
if (!lunarDays.value.length)
|
||||
return 0;
|
||||
const d = new Date(lunarDays.value[0].date + 'T12:00:00');
|
||||
return (d.getDay() + 6) % 7; // Lundi=0
|
||||
});
|
||||
async function loadLunar() {
|
||||
loadingLunar.value = true;
|
||||
errorLunar.value = '';
|
||||
selectedDay.value = null;
|
||||
try {
|
||||
lunarDays.value = await lunarApi.getMonth(monthStr.value);
|
||||
}
|
||||
catch (e) {
|
||||
const err = e;
|
||||
errorLunar.value = err?.response?.data?.detail || 'Erreur lors du chargement du calendrier lunaire.';
|
||||
}
|
||||
finally {
|
||||
loadingLunar.value = false;
|
||||
}
|
||||
}
|
||||
// Météo
|
||||
const meteoData = ref([]);
|
||||
const loadingMeteo = ref(false);
|
||||
async function loadMeteo() {
|
||||
loadingMeteo.value = true;
|
||||
try {
|
||||
const res = await meteoApi.getForecast(14);
|
||||
meteoData.value = res.days || [];
|
||||
}
|
||||
catch {
|
||||
meteoData.value = [];
|
||||
}
|
||||
finally {
|
||||
loadingMeteo.value = false;
|
||||
}
|
||||
}
|
||||
// Dictons
|
||||
const dictons = ref([]);
|
||||
async function loadDictons() {
|
||||
try {
|
||||
dictons.value = await lunarApi.getDictons(currentMonth.value);
|
||||
}
|
||||
catch {
|
||||
dictons.value = [];
|
||||
}
|
||||
}
|
||||
// Helpers affichage
|
||||
function moonEmoji(illum, tendance) {
|
||||
const i = illum / 100;
|
||||
if (i < 0.05)
|
||||
return '🌑';
|
||||
if (tendance === 'Croissante') {
|
||||
if (i < 0.25)
|
||||
return '🌒';
|
||||
if (i < 0.5)
|
||||
return '🌓';
|
||||
if (i < 0.75)
|
||||
return '🌔';
|
||||
return '🌕';
|
||||
}
|
||||
else {
|
||||
if (i > 0.75)
|
||||
return '🌕';
|
||||
if (i > 0.5)
|
||||
return '🌖';
|
||||
if (i > 0.25)
|
||||
return '🌗';
|
||||
return '🌘';
|
||||
}
|
||||
}
|
||||
function typeEmoji(type) {
|
||||
return { Racine: '🌱', Feuille: '🌿', Fleur: '🌸', Fruit: '🍅' }[type] || '●';
|
||||
}
|
||||
function typeColor(type) {
|
||||
return { Racine: 'text-yellow', Feuille: 'text-green', Fleur: 'text-orange', Fruit: 'text-red' }[type] || 'text-text-muted';
|
||||
}
|
||||
function formatDate(dateStr) {
|
||||
return new Date(dateStr + 'T12:00:00').toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' });
|
||||
}
|
||||
watch(monthStr, () => {
|
||||
if (activeTab.value === 'lunaire')
|
||||
loadLunar();
|
||||
if (activeTab.value === 'dictons')
|
||||
loadDictons();
|
||||
});
|
||||
watch(activeTab, (tab) => {
|
||||
if (tab === 'lunaire' && !lunarDays.value.length)
|
||||
loadLunar();
|
||||
if (tab === 'meteo' && !meteoData.value.length)
|
||||
loadMeteo();
|
||||
if (tab === 'dictons' && !dictons.value.length)
|
||||
loadDictons();
|
||||
});
|
||||
onMounted(() => { loadLunar(); loadMeteo(); });
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4 max-w-4xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-blue mb-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-1 mb-6 bg-bg-soft rounded-lg p-1 w-fit" },
|
||||
});
|
||||
for (const [tab] of __VLS_getVForSourceType((__VLS_ctx.tabs))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.activeTab = tab.id;
|
||||
} },
|
||||
key: (tab.id),
|
||||
...{ class: (['px-4 py-2 rounded-md text-sm font-medium transition-colors',
|
||||
__VLS_ctx.activeTab === tab.id ? 'bg-blue text-bg' : 'text-text-muted hover:text-text']) },
|
||||
});
|
||||
(tab.label);
|
||||
}
|
||||
if (__VLS_ctx.activeTab === 'lunaire' || __VLS_ctx.activeTab === 'dictons') {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center gap-3 mb-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (__VLS_ctx.prevMonth) },
|
||||
...{ class: "text-text-muted hover:text-text text-lg" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text font-semibold" },
|
||||
});
|
||||
(__VLS_ctx.monthLabel);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (__VLS_ctx.nextMonth) },
|
||||
...{ class: "text-text-muted hover:text-text text-lg" },
|
||||
});
|
||||
}
|
||||
if (__VLS_ctx.activeTab === 'lunaire') {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
if (__VLS_ctx.loadingLunar) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm py-4" },
|
||||
});
|
||||
}
|
||||
else if (__VLS_ctx.errorLunar) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-red/10 border border-red rounded-lg p-4 text-red text-sm" },
|
||||
});
|
||||
(__VLS_ctx.errorLunar);
|
||||
}
|
||||
else if (!__VLS_ctx.lunarDays.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm py-4" },
|
||||
});
|
||||
}
|
||||
else {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-7 gap-1 mb-2" },
|
||||
});
|
||||
for (const [d] of __VLS_getVForSourceType((['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim']))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (d),
|
||||
...{ class: "text-center text-text-muted text-xs py-1" },
|
||||
});
|
||||
(d);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-7 gap-1" },
|
||||
});
|
||||
for (const [_] of __VLS_getVForSourceType((__VLS_ctx.firstDayOffset))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: ('empty-' + _),
|
||||
...{ class: "h-16" },
|
||||
});
|
||||
}
|
||||
for (const [day] of __VLS_getVForSourceType((__VLS_ctx.lunarDays))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.activeTab === 'lunaire'))
|
||||
return;
|
||||
if (!!(__VLS_ctx.loadingLunar))
|
||||
return;
|
||||
if (!!(__VLS_ctx.errorLunar))
|
||||
return;
|
||||
if (!!(!__VLS_ctx.lunarDays.length))
|
||||
return;
|
||||
__VLS_ctx.selectedDay = day;
|
||||
} },
|
||||
key: (day.date),
|
||||
...{ class: (['h-16 bg-bg-soft rounded-lg p-1 cursor-pointer hover:border hover:border-blue transition-colors flex flex-col items-center justify-center gap-0.5',
|
||||
__VLS_ctx.selectedDay?.date === day.date ? 'border border-blue' : 'border border-transparent']) },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text-muted text-xs" },
|
||||
});
|
||||
(new Date(day.date + 'T12:00:00').getDate());
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-base leading-none" },
|
||||
});
|
||||
(__VLS_ctx.moonEmoji(day.illumination, day.croissante_decroissante));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-xs leading-none" },
|
||||
...{ class: (__VLS_ctx.typeColor(day.type_jour)) },
|
||||
});
|
||||
(__VLS_ctx.typeEmoji(day.type_jour));
|
||||
}
|
||||
if (__VLS_ctx.selectedDay) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "mt-4 bg-bg-soft rounded-xl p-4 border border-bg-hard" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center gap-3 mb-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-3xl" },
|
||||
});
|
||||
(__VLS_ctx.moonEmoji(__VLS_ctx.selectedDay.illumination, __VLS_ctx.selectedDay.croissante_decroissante));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text font-bold" },
|
||||
});
|
||||
(__VLS_ctx.formatDate(__VLS_ctx.selectedDay.date));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
(__VLS_ctx.selectedDay.phase || 'Pas de phase particulière');
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-2 gap-2 text-sm" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-bg rounded-lg p-2" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text" },
|
||||
});
|
||||
(__VLS_ctx.selectedDay.illumination);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-bg rounded-lg p-2" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text" },
|
||||
});
|
||||
(__VLS_ctx.selectedDay.croissante_decroissante);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-bg rounded-lg p-2" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text" },
|
||||
});
|
||||
(__VLS_ctx.selectedDay.montante_descendante);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-bg rounded-lg p-2" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text" },
|
||||
});
|
||||
(__VLS_ctx.selectedDay.signe);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-bg rounded-lg p-2 col-span-2" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: (['font-semibold', __VLS_ctx.typeColor(__VLS_ctx.selectedDay.type_jour)]) },
|
||||
});
|
||||
(__VLS_ctx.typeEmoji(__VLS_ctx.selectedDay.type_jour));
|
||||
(__VLS_ctx.selectedDay.type_jour);
|
||||
if (__VLS_ctx.selectedDay.perigee) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "mt-2 text-xs text-orange bg-orange/10 rounded px-2 py-1" },
|
||||
});
|
||||
}
|
||||
if (__VLS_ctx.selectedDay.apogee) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "mt-2 text-xs text-blue bg-blue/10 rounded px-2 py-1" },
|
||||
});
|
||||
}
|
||||
if (__VLS_ctx.selectedDay.noeud_lunaire) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "mt-2 text-xs text-yellow bg-yellow/10 rounded px-2 py-1" },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__VLS_ctx.activeTab === 'meteo') {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
if (__VLS_ctx.loadingMeteo) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm py-4" },
|
||||
});
|
||||
}
|
||||
else if (!__VLS_ctx.meteoData.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm py-4" },
|
||||
});
|
||||
}
|
||||
else {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-2 sm:grid-cols-4 gap-3" },
|
||||
});
|
||||
for (const [day] of __VLS_getVForSourceType((__VLS_ctx.meteoData))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (day.date),
|
||||
...{ class: "bg-bg-soft rounded-xl p-3 border border-bg-hard flex flex-col items-center gap-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs" },
|
||||
});
|
||||
(__VLS_ctx.formatDate(day.date));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-3xl" },
|
||||
});
|
||||
(day.icone);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text text-xs font-medium text-center" },
|
||||
});
|
||||
(day.label);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 text-xs mt-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-orange" },
|
||||
});
|
||||
(day.t_max?.toFixed(0));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-blue" },
|
||||
});
|
||||
(day.t_min?.toFixed(0));
|
||||
if (day.pluie_mm > 0) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-xs text-blue" },
|
||||
});
|
||||
(day.pluie_mm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__VLS_ctx.activeTab === 'taches') {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.p, __VLS_intrinsicElements.p)({
|
||||
...{ class: "text-text-muted text-sm py-4" },
|
||||
});
|
||||
}
|
||||
if (__VLS_ctx.activeTab === 'dictons') {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
if (!__VLS_ctx.dictons.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm py-4" },
|
||||
});
|
||||
}
|
||||
for (const [d] of __VLS_getVForSourceType((__VLS_ctx.dictons))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (d.id),
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 mb-2 border border-bg-hard" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.p, __VLS_intrinsicElements.p)({
|
||||
...{ class: "text-text italic text-sm" },
|
||||
});
|
||||
(d.texte);
|
||||
if (d.region) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.p, __VLS_intrinsicElements.p)({
|
||||
...{ class: "text-text-muted text-xs mt-1" },
|
||||
});
|
||||
(d.region);
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-4xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-blue']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-fit']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-red/10']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-7']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-7']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['h-16']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-base']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['leading-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['leading-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-3xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['col-span-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-orange']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-orange/10']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-blue']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-blue/10']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-yellow']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-yellow/10']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['sm:grid-cols-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-col']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-3xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-medium']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-orange']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-blue']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-blue']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['italic']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-1']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
activeTab: activeTab,
|
||||
tabs: tabs,
|
||||
monthLabel: monthLabel,
|
||||
prevMonth: prevMonth,
|
||||
nextMonth: nextMonth,
|
||||
lunarDays: lunarDays,
|
||||
loadingLunar: loadingLunar,
|
||||
errorLunar: errorLunar,
|
||||
selectedDay: selectedDay,
|
||||
firstDayOffset: firstDayOffset,
|
||||
meteoData: meteoData,
|
||||
loadingMeteo: loadingMeteo,
|
||||
dictons: dictons,
|
||||
moonEmoji: moonEmoji,
|
||||
typeEmoji: typeEmoji,
|
||||
typeColor: typeColor,
|
||||
formatDate: formatDate,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
; /* PartiallyEnd: #4569/main.vue */
|
||||
@@ -23,6 +23,22 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mb-6">
|
||||
<h2 class="text-text-muted text-xs uppercase tracking-widest mb-3">Météo (3 jours)</h2>
|
||||
<div class="flex gap-2 overflow-x-auto">
|
||||
<div v-for="day in meteo3j" :key="day.date"
|
||||
class="bg-bg-soft rounded-xl p-3 border border-bg-hard flex flex-col items-center gap-1 min-w-[90px]">
|
||||
<div class="text-text-muted text-xs">{{ formatDate(day.date) }}</div>
|
||||
<div class="text-2xl">{{ day.icone }}</div>
|
||||
<div class="text-xs text-center text-text-muted">{{ day.label }}</div>
|
||||
<div class="flex gap-1 text-xs">
|
||||
<span class="text-orange">↑{{ day.t_max?.toFixed(0) }}°</span>
|
||||
<span class="text-blue">↓{{ day.t_min?.toFixed(0) }}°</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-text-muted text-xs uppercase tracking-widest mb-3">Jardins</h2>
|
||||
<div v-if="gardensStore.loading" class="text-text-muted text-sm">Chargement...</div>
|
||||
@@ -40,15 +56,26 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useGardensStore } from '@/stores/gardens'
|
||||
import { useTasksStore } from '@/stores/tasks'
|
||||
import { meteoApi, type MeteoDay } from '@/api/meteo'
|
||||
|
||||
const router = useRouter()
|
||||
const gardensStore = useGardensStore()
|
||||
const tasksStore = useTasksStore()
|
||||
const pendingTasks = computed(() => tasksStore.tasks.filter(t => t.statut === 'a_faire').slice(0, 5))
|
||||
|
||||
onMounted(() => { gardensStore.fetchAll(); tasksStore.fetchAll() })
|
||||
const meteo3j = ref<MeteoDay[]>([])
|
||||
|
||||
function formatDate(s: string) {
|
||||
return new Date(s + 'T12:00:00').toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
gardensStore.fetchAll()
|
||||
tasksStore.fetchAll()
|
||||
try { const r = await meteoApi.getForecast(3); meteo3j.value = r.days.slice(0, 3) } catch {}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,22 +1,232 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useGardensStore } from '@/stores/gardens';
|
||||
import { useTasksStore } from '@/stores/tasks';
|
||||
import { meteoApi } from '@/api/meteo';
|
||||
const router = useRouter();
|
||||
const gardensStore = useGardensStore();
|
||||
const tasksStore = useTasksStore();
|
||||
const pendingTasks = computed(() => tasksStore.tasks.filter(t => t.statut === 'a_faire').slice(0, 5));
|
||||
const meteo3j = ref([]);
|
||||
function formatDate(s) {
|
||||
return new Date(s + 'T12:00:00').toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' });
|
||||
}
|
||||
onMounted(async () => {
|
||||
gardensStore.fetchAll();
|
||||
tasksStore.fetchAll();
|
||||
try {
|
||||
const r = await meteoApi.getForecast(3);
|
||||
meteo3j.value = r.days.slice(0, 3);
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-2xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
...{ class: "text-2xl font-bold text-green mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.section, __VLS_intrinsicElements.section)({
|
||||
...{ class: "mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h2, __VLS_intrinsicElements.h2)({
|
||||
...{ class: "text-text-muted text-xs uppercase tracking-widest mb-3" },
|
||||
});
|
||||
if (!__VLS_ctx.pendingTasks.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm py-2" },
|
||||
});
|
||||
}
|
||||
for (const [t] of __VLS_getVForSourceType((__VLS_ctx.pendingTasks))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (t.id),
|
||||
...{ class: "bg-bg-soft rounded-lg p-3 mb-2 flex items-center gap-3 border border-bg-hard" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: ({
|
||||
'text-red': t.priorite === 'haute',
|
||||
'text-yellow': t.priorite === 'normale',
|
||||
'text-text-muted': t.priorite === 'basse'
|
||||
}) },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text text-sm flex-1" },
|
||||
});
|
||||
(t.titre);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.tasksStore.updateStatut(t.id, 'fait');
|
||||
} },
|
||||
...{ class: "text-xs text-green hover:underline px-2" },
|
||||
});
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.section, __VLS_intrinsicElements.section)({
|
||||
...{ class: "mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h2, __VLS_intrinsicElements.h2)({
|
||||
...{ class: "text-text-muted text-xs uppercase tracking-widest mb-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 overflow-x-auto" },
|
||||
});
|
||||
for (const [day] of __VLS_getVForSourceType((__VLS_ctx.meteo3j))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (day.date),
|
||||
...{ class: "bg-bg-soft rounded-xl p-3 border border-bg-hard flex flex-col items-center gap-1 min-w-[90px]" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs" },
|
||||
});
|
||||
(__VLS_ctx.formatDate(day.date));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-2xl" },
|
||||
});
|
||||
(day.icone);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-xs text-center text-text-muted" },
|
||||
});
|
||||
(day.label);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-1 text-xs" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-orange" },
|
||||
});
|
||||
(day.t_max?.toFixed(0));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-blue" },
|
||||
});
|
||||
(day.t_min?.toFixed(0));
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.section, __VLS_intrinsicElements.section)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h2, __VLS_intrinsicElements.h2)({
|
||||
...{ class: "text-text-muted text-xs uppercase tracking-widest mb-3" },
|
||||
});
|
||||
if (__VLS_ctx.gardensStore.loading) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
}
|
||||
for (const [g] of __VLS_getVForSourceType((__VLS_ctx.gardensStore.gardens))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.router.push(`/jardins/${g.id}`);
|
||||
} },
|
||||
key: (g.id),
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 mb-2 border border-bg-hard cursor-pointer hover:border-green transition-colors" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text font-medium" },
|
||||
});
|
||||
(g.nom);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "ml-2 text-xs text-text-muted px-2 py-0.5 bg-bg rounded" },
|
||||
});
|
||||
(g.type);
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['uppercase']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['tracking-widest']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:underline']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['uppercase']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['tracking-widest']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['overflow-x-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-col']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['min-w-[90px]']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-orange']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-blue']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['uppercase']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['tracking-widest']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['cursor-pointer']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-colors']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-medium']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['ml-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-0.5']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
return {
|
||||
router: router,
|
||||
gardensStore: gardensStore,
|
||||
tasksStore: tasksStore,
|
||||
pendingTasks: pendingTasks,
|
||||
meteo3j: meteo3j,
|
||||
formatDate: formatDate,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
|
||||
@@ -1,22 +1,136 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { gardensApi } from '@/api/gardens';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const garden = ref(null);
|
||||
const cells = ref([]);
|
||||
const displayCells = computed(() => {
|
||||
if (!garden.value)
|
||||
return [];
|
||||
const map = new Map(cells.value.map(c => [`${c.row}-${c.col}`, c]));
|
||||
const result = [];
|
||||
for (let row = 0; row < garden.value.grille_hauteur; row++) {
|
||||
for (let col = 0; col < garden.value.grille_largeur; col++) {
|
||||
result.push(map.get(`${row}-${col}`) ?? {
|
||||
col, row,
|
||||
libelle: `${String.fromCharCode(65 + row)}${col + 1}`,
|
||||
etat: 'libre',
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
onMounted(async () => {
|
||||
const id = Number(route.params.id);
|
||||
garden.value = await gardensApi.get(id);
|
||||
cells.value = await gardensApi.cells(id);
|
||||
});
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-3xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.router.back();
|
||||
} },
|
||||
...{ class: "text-text-muted text-sm mb-4 hover:text-text" },
|
||||
});
|
||||
if (__VLS_ctx.garden) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green mb-1" },
|
||||
});
|
||||
(__VLS_ctx.garden.nom);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.p, __VLS_intrinsicElements.p)({
|
||||
...{ class: "text-text-muted text-sm mb-6" },
|
||||
});
|
||||
(__VLS_ctx.garden.type);
|
||||
(__VLS_ctx.garden.exposition ?? 'exposition non définie');
|
||||
if (__VLS_ctx.garden.sol_type) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(__VLS_ctx.garden.sol_type);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h2, __VLS_intrinsicElements.h2)({
|
||||
...{ class: "text-text-muted text-xs uppercase tracking-widest mb-3" },
|
||||
});
|
||||
(__VLS_ctx.garden.grille_largeur);
|
||||
(__VLS_ctx.garden.grille_hauteur);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "overflow-x-auto pb-2" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid gap-1 w-max" },
|
||||
...{ style: (`grid-template-columns: repeat(${__VLS_ctx.garden.grille_largeur}, 52px)`) },
|
||||
});
|
||||
for (const [cell] of __VLS_getVForSourceType((__VLS_ctx.displayCells))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (`${cell.row}-${cell.col}`),
|
||||
...{ class: "w-[52px] h-[52px] bg-bg-soft border border-bg-hard rounded-md flex items-center justify-center text-xs text-text-muted cursor-pointer hover:border-green transition-colors select-none" },
|
||||
...{ class: ({ 'border-orange/60 bg-orange/10 text-orange': cell.etat === 'occupe' }) },
|
||||
title: (cell.libelle),
|
||||
});
|
||||
(cell.libelle);
|
||||
}
|
||||
}
|
||||
else {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-3xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['uppercase']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['tracking-widest']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['overflow-x-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['pb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-max']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-[52px]']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['h-[52px]']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-md']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['cursor-pointer']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-colors']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['select-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
return {
|
||||
router: router,
|
||||
garden: garden,
|
||||
displayCells: displayCells,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
|
||||
@@ -1,66 +1,100 @@
|
||||
<template>
|
||||
<div class="p-4 max-w-2xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold text-green">Jardins</h1>
|
||||
<button
|
||||
class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90 transition-opacity"
|
||||
@click="showForm = !showForm"
|
||||
>+ Nouveau</button>
|
||||
<h1 class="text-2xl font-bold text-green">🪴 Jardins</h1>
|
||||
<button class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90"
|
||||
@click="openCreate">+ Nouveau</button>
|
||||
</div>
|
||||
|
||||
<form v-if="showForm" class="bg-bg-soft rounded-lg p-4 mb-6 border border-green/30" @submit.prevent="submit">
|
||||
<div class="grid gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Nom *</label>
|
||||
<input v-model="form.nom" required
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Type</label>
|
||||
<select v-model="form.type" class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm">
|
||||
<option value="plein_air">Plein air</option>
|
||||
<option value="serre">Serre</option>
|
||||
<option value="tunnel">Tunnel</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Largeur grille</label>
|
||||
<input v-model.number="form.grille_largeur" type="number" min="1" max="20"
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Hauteur grille</label>
|
||||
<input v-model.number="form.grille_hauteur" type="number" min="1" max="20"
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 mt-4">
|
||||
<button type="submit" class="bg-green text-bg px-4 py-2 rounded text-sm font-semibold">Créer</button>
|
||||
<button type="button" class="text-text-muted text-sm px-4 py-2 hover:text-text" @click="showForm = false">Annuler</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div v-if="store.loading" class="text-text-muted text-sm">Chargement...</div>
|
||||
<div
|
||||
v-for="g in store.gardens" :key="g.id"
|
||||
class="bg-bg-soft rounded-lg p-4 mb-3 border border-bg-hard flex items-center gap-3 cursor-pointer hover:border-green transition-colors group"
|
||||
@click="router.push(`/jardins/${g.id}`)"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div v-for="g in store.gardens" :key="g.id"
|
||||
class="bg-bg-soft rounded-lg p-4 mb-3 border border-bg-hard flex items-center gap-3 group">
|
||||
<div class="flex-1 cursor-pointer" @click="router.push(`/jardins/${g.id}`)">
|
||||
<div class="text-text font-medium group-hover:text-green transition-colors">{{ g.nom }}</div>
|
||||
<div class="text-text-muted text-xs mt-1">{{ g.type }} · {{ g.grille_largeur }}×{{ g.grille_hauteur }} cases</div>
|
||||
<div class="text-text-muted text-xs mt-1">
|
||||
{{ typeLabel(g.type) }} · {{ g.grille_largeur }}×{{ g.grille_hauteur }} cases
|
||||
<span v-if="g.exposition"> · {{ g.exposition }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="text-text-muted hover:text-red text-sm px-2 py-1 rounded hover:bg-bg transition-colors"
|
||||
@click.stop="store.remove(g.id!)"
|
||||
>✕</button>
|
||||
<button @click="startEdit(g)" class="text-yellow text-xs hover:underline px-2">Édit.</button>
|
||||
<button @click="store.remove(g.id!)" class="text-text-muted hover:text-red text-sm px-2">✕</button>
|
||||
</div>
|
||||
|
||||
<div v-if="!store.loading && !store.gardens.length" class="text-text-muted text-sm text-center py-8">
|
||||
Aucun jardin. Créez-en un !
|
||||
</div>
|
||||
|
||||
<!-- Modal création / édition -->
|
||||
<div v-if="showForm" class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" @click.self="closeForm">
|
||||
<div class="bg-bg-hard rounded-xl p-6 w-full max-w-md border border-bg-soft max-h-[90vh] overflow-y-auto">
|
||||
<h2 class="text-text font-bold text-lg mb-4">{{ editId ? 'Modifier le jardin' : 'Nouveau jardin' }}</h2>
|
||||
<form @submit.prevent="submit" class="grid gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Nom *</label>
|
||||
<input v-model="form.nom" required
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Description</label>
|
||||
<textarea v-model="form.description" rows="2"
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none resize-none" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Type</label>
|
||||
<select v-model="form.type" class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm">
|
||||
<option value="plein_air">Plein air</option>
|
||||
<option value="serre">Serre</option>
|
||||
<option value="tunnel">Tunnel</option>
|
||||
<option value="bac">Bac / Pot</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Largeur grille</label>
|
||||
<input v-model.number="form.grille_largeur" type="number" min="1" max="30"
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Hauteur grille</label>
|
||||
<input v-model.number="form.grille_hauteur" type="number" min="1" max="30"
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Exposition</label>
|
||||
<select v-model="form.exposition" class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm">
|
||||
<option value="">—</option>
|
||||
<option value="Nord">Nord</option>
|
||||
<option value="Est">Est</option>
|
||||
<option value="Sud">Sud</option>
|
||||
<option value="Ouest">Ouest</option>
|
||||
<option value="Sud-Est">Sud-Est</option>
|
||||
<option value="Sud-Ouest">Sud-Ouest</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Type de sol</label>
|
||||
<select v-model="form.sol_type" class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm">
|
||||
<option value="">—</option>
|
||||
<option value="argileux">Argileux</option>
|
||||
<option value="limoneux">Limoneux</option>
|
||||
<option value="sableux">Sableux</option>
|
||||
<option value="calcaire">Calcaire</option>
|
||||
<option value="humifère">Humifère</option>
|
||||
<option value="mixte">Mixte</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 mt-2">
|
||||
<button type="submit" class="bg-green text-bg px-4 py-2 rounded text-sm font-semibold">
|
||||
{{ editId ? 'Enregistrer' : 'Créer' }}
|
||||
</button>
|
||||
<button type="button" class="text-text-muted text-sm px-4 py-2 hover:text-text" @click="closeForm">Annuler</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -68,17 +102,49 @@
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useGardensStore } from '@/stores/gardens'
|
||||
import type { Garden } from '@/api/gardens'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useGardensStore()
|
||||
const showForm = ref(false)
|
||||
const form = reactive({ nom: '', type: 'plein_air', grille_largeur: 6, grille_hauteur: 4 })
|
||||
const editId = ref<number | null>(null)
|
||||
|
||||
onMounted(() => store.fetchAll())
|
||||
const form = reactive({
|
||||
nom: '', description: '', type: 'plein_air',
|
||||
grille_largeur: 6, grille_hauteur: 4,
|
||||
exposition: '', sol_type: '',
|
||||
})
|
||||
|
||||
function typeLabel(t: string) {
|
||||
return ({ plein_air: 'Plein air', serre: 'Serre', tunnel: 'Tunnel', bac: 'Bac/Pot' } as Record<string, string>)[t] ?? t
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
editId.value = null
|
||||
Object.assign(form, { nom: '', description: '', type: 'plein_air', grille_largeur: 6, grille_hauteur: 4, exposition: '', sol_type: '' })
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
function startEdit(g: Garden) {
|
||||
editId.value = g.id!
|
||||
Object.assign(form, {
|
||||
nom: g.nom, description: g.description || '',
|
||||
type: g.type, grille_largeur: g.grille_largeur, grille_hauteur: g.grille_hauteur,
|
||||
exposition: g.exposition || '', sol_type: g.sol_type || '',
|
||||
})
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
function closeForm() { showForm.value = false; editId.value = null }
|
||||
|
||||
async function submit() {
|
||||
await store.create({ ...form })
|
||||
showForm.value = false
|
||||
Object.assign(form, { nom: '', type: 'plein_air', grille_largeur: 6, grille_hauteur: 4 })
|
||||
if (editId.value) {
|
||||
await store.update(editId.value, { ...form })
|
||||
} else {
|
||||
await store.create({ ...form })
|
||||
}
|
||||
closeForm()
|
||||
}
|
||||
|
||||
onMounted(() => store.fetchAll())
|
||||
</script>
|
||||
|
||||
@@ -1,22 +1,294 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useGardensStore } from '@/stores/gardens';
|
||||
const router = useRouter();
|
||||
const store = useGardensStore();
|
||||
const showForm = ref(false);
|
||||
const form = reactive({ nom: '', type: 'plein_air', grille_largeur: 6, grille_hauteur: 4 });
|
||||
onMounted(() => store.fetchAll());
|
||||
async function submit() {
|
||||
await store.create({ ...form });
|
||||
showForm.value = false;
|
||||
Object.assign(form, { nom: '', type: 'plein_air', grille_largeur: 6, grille_hauteur: 4 });
|
||||
}
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-2xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center justify-between mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.showForm = !__VLS_ctx.showForm;
|
||||
} },
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90 transition-opacity" },
|
||||
});
|
||||
if (__VLS_ctx.showForm) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.form, __VLS_intrinsicElements.form)({
|
||||
...{ onSubmit: (__VLS_ctx.submit) },
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 mb-6 border border-green/30" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
required: true,
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" },
|
||||
});
|
||||
(__VLS_ctx.form.nom);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.form.type),
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "plein_air",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "serre",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "tunnel",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-2 gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "number",
|
||||
min: "1",
|
||||
max: "20",
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" },
|
||||
});
|
||||
(__VLS_ctx.form.grille_largeur);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "number",
|
||||
min: "1",
|
||||
max: "20",
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" },
|
||||
});
|
||||
(__VLS_ctx.form.grille_hauteur);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 mt-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
type: "submit",
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded text-sm font-semibold" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showForm))
|
||||
return;
|
||||
__VLS_ctx.showForm = false;
|
||||
} },
|
||||
type: "button",
|
||||
...{ class: "text-text-muted text-sm px-4 py-2 hover:text-text" },
|
||||
});
|
||||
}
|
||||
if (__VLS_ctx.store.loading) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
}
|
||||
for (const [g] of __VLS_getVForSourceType((__VLS_ctx.store.gardens))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.router.push(`/jardins/${g.id}`);
|
||||
} },
|
||||
key: (g.id),
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 mb-3 border border-bg-hard flex items-center gap-3 cursor-pointer hover:border-green transition-colors group" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text font-medium group-hover:text-green transition-colors" },
|
||||
});
|
||||
(g.nom);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mt-1" },
|
||||
});
|
||||
(g.type);
|
||||
(g.grille_largeur);
|
||||
(g.grille_hauteur);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.store.remove(g.id);
|
||||
} },
|
||||
...{ class: "text-text-muted hover:text-red text-sm px-2 py-1 rounded hover:bg-bg transition-colors" },
|
||||
});
|
||||
}
|
||||
if (!__VLS_ctx.store.loading && !__VLS_ctx.store.gardens.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm text-center py-8" },
|
||||
});
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-between']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-opacity']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-green/30']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['cursor-pointer']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-colors']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['group']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-medium']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['group-hover:text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-colors']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-colors']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-8']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
return {
|
||||
router: router,
|
||||
store: store,
|
||||
showForm: showForm,
|
||||
form: form,
|
||||
submit: submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
|
||||
@@ -4,15 +4,23 @@ const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-2xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
...{ class: "text-2xl font-bold text-green mb-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.p, __VLS_intrinsicElements.p)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
|
||||
88
frontend/src/views/OutilsView.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="p-4 max-w-4xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold text-yellow">🔧 Outils</h1>
|
||||
<button @click="showForm = true" class="bg-yellow text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90">+ Ajouter</button>
|
||||
</div>
|
||||
|
||||
<div v-if="toolsStore.loading" class="text-text-muted text-sm">Chargement...</div>
|
||||
<div v-else-if="!toolsStore.tools.length" class="text-text-muted text-sm py-4">Aucun outil enregistré.</div>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
<div v-for="t in toolsStore.tools" :key="t.id"
|
||||
class="bg-bg-soft rounded-lg p-4 border border-bg-hard flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-text font-semibold">{{ t.nom }}</span>
|
||||
<div class="flex gap-2">
|
||||
<button @click="startEdit(t)" class="text-yellow text-xs hover:underline">Édit.</button>
|
||||
<button @click="removeTool(t.id!)" class="text-red text-xs hover:underline">Suppr.</button>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="t.categorie" class="text-xs text-yellow bg-yellow/10 rounded-full px-2 py-0.5 w-fit">{{ t.categorie }}</span>
|
||||
<p v-if="t.description" class="text-text-muted text-xs">{{ t.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showForm" class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" @click.self="closeForm">
|
||||
<div class="bg-bg-hard rounded-xl p-6 w-full max-w-sm border border-bg-soft">
|
||||
<h2 class="text-text font-bold text-lg mb-4">{{ editId ? 'Modifier l\'outil' : 'Nouvel outil' }}</h2>
|
||||
<form @submit.prevent="submitTool" class="flex flex-col gap-3">
|
||||
<input v-model="form.nom" placeholder="Nom de l'outil *" required
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-yellow" />
|
||||
<select v-model="form.categorie"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-yellow">
|
||||
<option value="">Catégorie</option>
|
||||
<option value="beche">Bêche</option>
|
||||
<option value="fourche">Fourche</option>
|
||||
<option value="griffe">Griffe/Grelinette</option>
|
||||
<option value="arrosage">Arrosage</option>
|
||||
<option value="taille">Taille</option>
|
||||
<option value="autre">Autre</option>
|
||||
</select>
|
||||
<textarea v-model="form.description" placeholder="Description..."
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-yellow resize-none h-16" />
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button type="button" @click="closeForm" class="px-4 py-2 text-text-muted hover:text-text text-sm">Annuler</button>
|
||||
<button type="submit" class="bg-yellow text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90">
|
||||
{{ editId ? 'Enregistrer' : 'Créer' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { useToolsStore } from '@/stores/tools'
|
||||
import type { Tool } from '@/api/tools'
|
||||
|
||||
const toolsStore = useToolsStore()
|
||||
const showForm = ref(false)
|
||||
const editId = ref<number | null>(null)
|
||||
const form = reactive({ nom: '', categorie: '', description: '' })
|
||||
|
||||
function startEdit(t: Tool) {
|
||||
editId.value = t.id!
|
||||
Object.assign(form, { nom: t.nom, categorie: t.categorie || '', description: t.description || '' })
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
function closeForm() { showForm.value = false; editId.value = null }
|
||||
|
||||
async function submitTool() {
|
||||
if (editId.value) {
|
||||
await toolsStore.update(editId.value, { ...form })
|
||||
} else {
|
||||
await toolsStore.create({ ...form })
|
||||
}
|
||||
Object.assign(form, { nom: '', categorie: '', description: '' })
|
||||
closeForm()
|
||||
}
|
||||
|
||||
async function removeTool(id: number) {
|
||||
if (confirm('Supprimer cet outil ?')) await toolsStore.remove(id)
|
||||
}
|
||||
|
||||
onMounted(() => toolsStore.fetchAll())
|
||||
</script>
|
||||
295
frontend/src/views/OutilsView.vue.js
Normal file
@@ -0,0 +1,295 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { useToolsStore } from '@/stores/tools';
|
||||
const toolsStore = useToolsStore();
|
||||
const showForm = ref(false);
|
||||
const form = reactive({ nom: '', categorie: '', description: '' });
|
||||
async function submitTool() {
|
||||
await toolsStore.create({ ...form });
|
||||
Object.assign(form, { nom: '', categorie: '', description: '' });
|
||||
showForm.value = false;
|
||||
}
|
||||
async function removeTool(id) {
|
||||
if (confirm('Supprimer cet outil ?'))
|
||||
await toolsStore.remove(id);
|
||||
}
|
||||
onMounted(() => toolsStore.fetchAll());
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4 max-w-4xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center justify-between mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-yellow" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.showForm = true;
|
||||
} },
|
||||
...{ class: "bg-yellow text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90" },
|
||||
});
|
||||
if (__VLS_ctx.toolsStore.loading) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
}
|
||||
else if (!__VLS_ctx.toolsStore.tools.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm py-4" },
|
||||
});
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3" },
|
||||
});
|
||||
for (const [t] of __VLS_getVForSourceType((__VLS_ctx.toolsStore.tools))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (t.id),
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 border border-bg-hard flex flex-col gap-2" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center justify-between" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text font-semibold" },
|
||||
});
|
||||
(t.nom);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.removeTool(t.id);
|
||||
} },
|
||||
...{ class: "text-red text-xs hover:underline" },
|
||||
});
|
||||
if (t.categorie) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-xs text-yellow bg-yellow/10 rounded-full px-2 py-0.5 w-fit" },
|
||||
});
|
||||
(t.categorie);
|
||||
}
|
||||
if (t.description) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.p, __VLS_intrinsicElements.p)({
|
||||
...{ class: "text-text-muted text-xs" },
|
||||
});
|
||||
(t.description);
|
||||
}
|
||||
}
|
||||
if (__VLS_ctx.showForm) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showForm))
|
||||
return;
|
||||
__VLS_ctx.showForm = false;
|
||||
} },
|
||||
...{ class: "fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-bg-hard rounded-xl p-6 w-full max-w-sm border border-bg-soft" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h2, __VLS_intrinsicElements.h2)({
|
||||
...{ class: "text-text font-bold text-lg mb-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.form, __VLS_intrinsicElements.form)({
|
||||
...{ onSubmit: (__VLS_ctx.submitTool) },
|
||||
...{ class: "flex flex-col gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
placeholder: "Nom de l'outil *",
|
||||
required: true,
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-yellow" },
|
||||
});
|
||||
(__VLS_ctx.form.nom);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.form.categorie),
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-yellow" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "beche",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "fourche",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "griffe",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "arrosage",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "taille",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "autre",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.textarea)({
|
||||
value: (__VLS_ctx.form.description),
|
||||
placeholder: "Description...",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-yellow resize-none h-16" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 justify-end" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showForm))
|
||||
return;
|
||||
__VLS_ctx.showForm = false;
|
||||
} },
|
||||
type: "button",
|
||||
...{ class: "px-4 py-2 text-text-muted hover:text-text text-sm" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
type: "submit",
|
||||
...{ class: "bg-yellow text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90" },
|
||||
});
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-4xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-between']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-yellow']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-yellow']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['sm:grid-cols-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['lg:grid-cols-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-col']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-between']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:underline']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-yellow']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-yellow/10']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-0.5']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-fit']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['fixed']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['inset-0']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-black/60']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['z-50']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-col']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-yellow']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-yellow']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-yellow']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['resize-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['h-16']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-end']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-yellow']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
toolsStore: toolsStore,
|
||||
showForm: showForm,
|
||||
form: form,
|
||||
submitTool: submitTool,
|
||||
removeTool: removeTool,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
; /* PartiallyEnd: #4569/main.vue */
|
||||
@@ -1,6 +1,141 @@
|
||||
<template>
|
||||
<div class="p-4 max-w-2xl mx-auto">
|
||||
<h1 class="text-2xl font-bold text-green mb-4">Planning</h1>
|
||||
<p class="text-text-muted text-sm">Vue calendrier — prochaine étape.</p>
|
||||
<div class="p-4 max-w-3xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold text-green">📆 Planning</h1>
|
||||
<!-- Navigateur semaine -->
|
||||
<div class="flex items-center gap-3">
|
||||
<button @click="prevWeek" class="text-text-muted hover:text-text text-lg">◀</button>
|
||||
<span class="text-text text-sm font-medium">{{ weekLabel }}</span>
|
||||
<button @click="nextWeek" class="text-text-muted hover:text-text text-lg">▶</button>
|
||||
<button @click="goToday" class="text-xs text-green border border-green/30 rounded px-2 py-0.5 hover:bg-green/10">Auj.</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grille semaine -->
|
||||
<div class="grid grid-cols-7 gap-1 mb-2">
|
||||
<div v-for="d in weekDays" :key="d.iso"
|
||||
:class="['text-center text-xs py-1 rounded',
|
||||
d.isToday ? 'text-green font-bold' : 'text-text-muted']">
|
||||
<div>{{ d.dayShort }}</div>
|
||||
<div :class="['text-sm font-semibold mt-0.5', d.isToday ? 'bg-green text-bg rounded-full w-6 h-6 flex items-center justify-center mx-auto' : '']">
|
||||
{{ d.dayNum }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tâches par jour -->
|
||||
<div class="grid grid-cols-7 gap-1">
|
||||
<div v-for="d in weekDays" :key="d.iso"
|
||||
:class="['min-h-24 rounded-lg p-1 border transition-colors',
|
||||
d.isToday ? 'border-green/40 bg-green/5' : 'border-bg-hard bg-bg-soft']">
|
||||
<div v-for="t in tasksByDay[d.iso] || []" :key="t.id"
|
||||
:class="['text-xs rounded px-1 py-0.5 mb-0.5 cursor-pointer hover:opacity-80 truncate',
|
||||
priorityClass(t.priorite)]"
|
||||
:title="t.titre">
|
||||
{{ t.titre }}
|
||||
</div>
|
||||
<!-- Zone drop-cible (vide) -->
|
||||
<div v-if="!(tasksByDay[d.iso]?.length)" class="text-text-muted text-xs text-center pt-2 opacity-40">—</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tâches sans date -->
|
||||
<div class="mt-6">
|
||||
<h2 class="text-text-muted text-xs uppercase tracking-widest mb-2">Sans date</h2>
|
||||
<div v-if="!unscheduled.length" class="text-text-muted text-xs pl-2">Toutes les tâches ont une échéance.</div>
|
||||
<div v-for="t in unscheduled" :key="t.id"
|
||||
class="bg-bg-soft rounded-lg p-2 mb-1 border border-bg-hard flex items-center gap-2">
|
||||
<span :class="['text-xs w-2 h-2 rounded-full flex-shrink-0', dotClass(t.priorite)]"></span>
|
||||
<span class="text-text text-sm flex-1 truncate">{{ t.titre }}</span>
|
||||
<span :class="['text-xs px-1.5 py-0.5 rounded', statutClass(t.statut)]">{{ t.statut }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useTasksStore } from '@/stores/tasks'
|
||||
|
||||
const store = useTasksStore()
|
||||
|
||||
const today = new Date()
|
||||
const weekStart = ref(getMonday(today))
|
||||
|
||||
function getMonday(d: Date) {
|
||||
const day = d.getDay()
|
||||
const diff = (day === 0 ? -6 : 1 - day)
|
||||
const m = new Date(d)
|
||||
m.setDate(d.getDate() + diff)
|
||||
m.setHours(0, 0, 0, 0)
|
||||
return m
|
||||
}
|
||||
|
||||
function toIso(d: Date) {
|
||||
return d.toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
const weekDays = computed(() => {
|
||||
const todayIso = toIso(today)
|
||||
return Array.from({ length: 7 }, (_, i) => {
|
||||
const d = new Date(weekStart.value)
|
||||
d.setDate(d.getDate() + i)
|
||||
return {
|
||||
iso: toIso(d),
|
||||
dayShort: d.toLocaleDateString('fr-FR', { weekday: 'short' }),
|
||||
dayNum: d.getDate(),
|
||||
isToday: toIso(d) === todayIso,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const weekLabel = computed(() => {
|
||||
const start = weekDays.value[0]
|
||||
const end = weekDays.value[6]
|
||||
const s = new Date(start.iso + 'T12:00:00')
|
||||
const e = new Date(end.iso + 'T12:00:00')
|
||||
return `${s.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })} – ${e.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: 'numeric' })}`
|
||||
})
|
||||
|
||||
const tasksByDay = computed(() => {
|
||||
const map: Record<string, typeof store.tasks> = {}
|
||||
for (const t of store.tasks) {
|
||||
if (!t.echeance) continue
|
||||
const key = t.echeance.slice(0, 10)
|
||||
if (!map[key]) map[key] = []
|
||||
map[key].push(t)
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
const unscheduled = computed(() => store.tasks.filter(t => !t.echeance && t.statut !== 'fait'))
|
||||
|
||||
function prevWeek() {
|
||||
const d = new Date(weekStart.value)
|
||||
d.setDate(d.getDate() - 7)
|
||||
weekStart.value = d
|
||||
}
|
||||
function nextWeek() {
|
||||
const d = new Date(weekStart.value)
|
||||
d.setDate(d.getDate() + 7)
|
||||
weekStart.value = d
|
||||
}
|
||||
function goToday() { weekStart.value = getMonday(today) }
|
||||
|
||||
const priorityClass = (p: string) => ({
|
||||
haute: 'bg-red/20 text-red',
|
||||
normale: 'bg-yellow/20 text-yellow',
|
||||
basse: 'bg-bg-hard text-text-muted',
|
||||
}[p] || 'bg-bg-hard text-text-muted')
|
||||
|
||||
const dotClass = (p: string) => ({
|
||||
haute: 'bg-red', normale: 'bg-yellow', basse: 'bg-text-muted',
|
||||
}[p] || 'bg-text-muted')
|
||||
|
||||
const statutClass = (s: string) => ({
|
||||
a_faire: 'bg-blue/20 text-blue', en_cours: 'bg-green/20 text-green',
|
||||
fait: 'bg-text-muted/20 text-text-muted',
|
||||
}[s] || '')
|
||||
|
||||
onMounted(() => store.fetchAll())
|
||||
</script>
|
||||
|
||||
@@ -4,15 +4,23 @@ const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-2xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
...{ class: "text-2xl font-bold text-green mb-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.p, __VLS_intrinsicElements.p)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
|
||||
@@ -1,35 +1,258 @@
|
||||
<template>
|
||||
<div class="p-4 max-w-2xl mx-auto">
|
||||
<h1 class="text-2xl font-bold text-green mb-6">Plantations</h1>
|
||||
<div class="p-4 max-w-3xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold text-green">🌱 Plantations</h1>
|
||||
<button @click="showCreate = true"
|
||||
class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90">
|
||||
+ Nouvelle
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Filtres statut -->
|
||||
<div class="flex gap-2 mb-4 flex-wrap">
|
||||
<button v-for="s in statuts" :key="s.val" @click="filterStatut = s.val"
|
||||
:class="['px-3 py-1 rounded-full text-xs font-medium transition-colors',
|
||||
filterStatut === s.val ? 'bg-blue text-bg' : 'bg-bg-soft text-text-muted hover:text-text']">
|
||||
{{ s.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="store.loading" class="text-text-muted text-sm">Chargement...</div>
|
||||
<div v-for="p in store.plantings" :key="p.id"
|
||||
class="bg-bg-soft rounded-lg p-4 mb-3 border border-bg-hard">
|
||||
<div class="flex items-start gap-3">
|
||||
<div v-else-if="!filtered.length" class="text-text-muted text-sm text-center py-8">
|
||||
Aucune plantation enregistrée.
|
||||
</div>
|
||||
|
||||
<div v-for="p in filtered" :key="p.id"
|
||||
class="bg-bg-soft rounded-xl mb-3 border border-bg-hard overflow-hidden">
|
||||
<!-- En-tête plantation -->
|
||||
<div class="p-4 flex items-start gap-3">
|
||||
<div class="flex-1">
|
||||
<div class="text-text font-medium">Plantation #{{ p.id }}</div>
|
||||
<div class="text-text-muted text-xs mt-1">
|
||||
Jardin {{ p.garden_id }} · Variété {{ p.variety_id }} · {{ p.quantite }} plant(s)
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span class="text-text font-semibold">{{ plantName(p.variety_id) }}</span>
|
||||
<span class="text-text-muted text-xs">— {{ gardenName(p.garden_id) }}</span>
|
||||
<span :class="['text-xs px-2 py-0.5 rounded-full font-medium', statutClass(p.statut)]">{{ p.statut }}</span>
|
||||
</div>
|
||||
<div class="text-text-muted text-xs mt-1 flex gap-3 flex-wrap">
|
||||
<span>{{ p.quantite }} plant(s)</span>
|
||||
<span v-if="p.date_plantation">🌱 {{ fmtDate(p.date_plantation) }}</span>
|
||||
<span v-if="p.notes">📝 {{ p.notes }}</span>
|
||||
</div>
|
||||
<span class="inline-block mt-2 text-xs px-2 py-0.5 rounded" :class="{
|
||||
'bg-blue/20 text-blue': p.statut === 'prevu',
|
||||
'bg-green/20 text-green': p.statut === 'en_cours',
|
||||
'bg-text-muted/20 text-text-muted': p.statut === 'termine',
|
||||
'bg-red/20 text-red': p.statut === 'echoue',
|
||||
}">{{ p.statut }}</span>
|
||||
</div>
|
||||
<button class="text-text-muted hover:text-red text-sm" @click="store.remove(p.id!)">✕</button>
|
||||
<div class="flex gap-2 items-center">
|
||||
<button @click="toggleRecoltes(p.id!)"
|
||||
:class="['text-xs px-2 py-1 rounded transition-colors',
|
||||
openRecoltes === p.id ? 'bg-aqua/20 text-aqua' : 'bg-bg-hard text-text-muted hover:text-aqua']">
|
||||
🍅 Récoltes
|
||||
</button>
|
||||
<button @click="startEdit(p)" class="text-yellow text-xs hover:underline">Édit.</button>
|
||||
<button @click="store.remove(p.id!)" class="text-text-muted hover:text-red text-sm ml-1">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section récoltes (dépliable) -->
|
||||
<div v-if="openRecoltes === p.id" class="border-t border-bg-hard px-4 py-3 bg-bg/50">
|
||||
<div v-if="loadingRecoltes" class="text-text-muted text-xs py-2">Chargement...</div>
|
||||
<div v-else>
|
||||
<div v-if="!recoltesList.length" class="text-text-muted text-xs mb-2">Aucune récolte enregistrée.</div>
|
||||
<div v-for="r in recoltesList" :key="r.id"
|
||||
class="flex items-center gap-3 text-sm py-1 border-b border-bg-hard last:border-0">
|
||||
<span class="text-aqua font-mono">{{ r.quantite }} {{ r.unite }}</span>
|
||||
<span class="text-text-muted text-xs">{{ fmtDate(r.date_recolte) }}</span>
|
||||
<span v-if="r.notes" class="text-text-muted text-xs flex-1 truncate">{{ r.notes }}</span>
|
||||
<button @click="deleteRecolte(r.id!, p.id!)" class="text-text-muted hover:text-red text-xs ml-auto">✕</button>
|
||||
</div>
|
||||
|
||||
<!-- Formulaire ajout récolte -->
|
||||
<form @submit.prevent="addRecolte(p.id!)" class="flex gap-2 mt-3 flex-wrap items-end">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Quantité *</label>
|
||||
<input v-model.number="rForm.quantite" type="number" step="0.1" min="0" required
|
||||
class="bg-bg border border-bg-hard rounded px-2 py-1 text-text text-xs w-20 focus:border-aqua outline-none" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Unité</label>
|
||||
<select v-model="rForm.unite"
|
||||
class="bg-bg border border-bg-hard rounded px-2 py-1 text-text text-xs focus:border-aqua outline-none">
|
||||
<option>kg</option><option>g</option><option>unites</option><option>litres</option><option>bottes</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Date *</label>
|
||||
<input v-model="rForm.date_recolte" type="date" required
|
||||
class="bg-bg border border-bg-hard rounded px-2 py-1 text-text text-xs focus:border-aqua outline-none" />
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="bg-aqua text-bg px-3 py-1 rounded text-xs font-semibold hover:opacity-90 self-end">
|
||||
+ Ajouter
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!store.loading && !store.plantings.length" class="text-text-muted text-sm text-center py-8">
|
||||
Aucune plantation enregistrée.
|
||||
|
||||
<!-- Modal création / édition plantation -->
|
||||
<div v-if="showCreate" class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4"
|
||||
@click.self="closeCreate">
|
||||
<div class="bg-bg-hard rounded-xl p-6 w-full max-w-md border border-bg-soft">
|
||||
<h2 class="text-text font-bold text-lg mb-4">{{ editId ? 'Modifier la plantation' : 'Nouvelle plantation' }}</h2>
|
||||
<form @submit.prevent="createPlanting" class="flex flex-col gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Jardin *</label>
|
||||
<select v-model.number="cForm.garden_id" required
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green">
|
||||
<option value="">Choisir un jardin</option>
|
||||
<option v-for="g in gardensStore.gardens" :key="g.id" :value="g.id">{{ g.nom }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Plante *</label>
|
||||
<select v-model.number="cForm.variety_id" required
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green">
|
||||
<option value="">Choisir une plante</option>
|
||||
<option v-for="p in plantsStore.plants" :key="p.id" :value="p.id">
|
||||
{{ p.nom_commun }}{{ p.variete ? ' — ' + p.variete : '' }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Quantité</label>
|
||||
<input v-model.number="cForm.quantite" type="number" min="1"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Date plantation</label>
|
||||
<input v-model="cForm.date_plantation" type="date"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Statut</label>
|
||||
<select v-model="cForm.statut"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green">
|
||||
<option value="prevu">Prévu</option>
|
||||
<option value="en_cours">En cours</option>
|
||||
<option value="termine">Terminé</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea v-model="cForm.notes" placeholder="Notes..."
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green resize-none h-16" />
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button type="button" @click="closeCreate" class="px-4 py-2 text-text-muted hover:text-text text-sm">Annuler</button>
|
||||
<button type="submit" class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90">
|
||||
{{ editId ? 'Enregistrer' : 'Créer' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { usePlantingsStore } from '@/stores/plantings'
|
||||
import { useGardensStore } from '@/stores/gardens'
|
||||
import { usePlantsStore } from '@/stores/plants'
|
||||
import { recoltesApi, type Recolte } from '@/api/recoltes'
|
||||
|
||||
const store = usePlantingsStore()
|
||||
onMounted(() => store.fetchAll())
|
||||
const gardensStore = useGardensStore()
|
||||
const plantsStore = usePlantsStore()
|
||||
|
||||
const showCreate = ref(false)
|
||||
const editId = ref<number | null>(null)
|
||||
const filterStatut = ref('')
|
||||
const openRecoltes = ref<number | null>(null)
|
||||
const recoltesList = ref<Recolte[]>([])
|
||||
const loadingRecoltes = ref(false)
|
||||
|
||||
const statuts = [
|
||||
{ val: '', label: 'Toutes' },
|
||||
{ val: 'prevu', label: '📋 Prévu' },
|
||||
{ val: 'en_cours', label: '🌿 En cours' },
|
||||
{ val: 'termine', label: '✅ Terminé' },
|
||||
{ val: 'echoue', label: '❌ Échoué' },
|
||||
]
|
||||
|
||||
const cForm = reactive({
|
||||
garden_id: 0, variety_id: 0, quantite: 1,
|
||||
date_plantation: '', statut: 'prevu', notes: ''
|
||||
})
|
||||
|
||||
const rForm = reactive({
|
||||
quantite: 1, unite: 'kg', date_recolte: new Date().toISOString().slice(0, 10)
|
||||
})
|
||||
|
||||
const filtered = computed(() =>
|
||||
filterStatut.value ? store.plantings.filter(p => p.statut === filterStatut.value) : store.plantings
|
||||
)
|
||||
|
||||
function plantName(id: number) {
|
||||
const p = plantsStore.plants.find(x => x.id === id)
|
||||
return p ? (p.variete ? `${p.nom_commun} (${p.variete})` : p.nom_commun) : `Plante #${id}`
|
||||
}
|
||||
|
||||
function gardenName(id: number) {
|
||||
return gardensStore.gardens.find(g => g.id === id)?.nom ?? `Jardin #${id}`
|
||||
}
|
||||
|
||||
function fmtDate(s: string) {
|
||||
return new Date(s + (s.length === 10 ? 'T12:00:00' : '')).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: 'numeric' })
|
||||
}
|
||||
|
||||
const statutClass = (s: string) => ({
|
||||
prevu: 'bg-blue/20 text-blue',
|
||||
en_cours: 'bg-green/20 text-green',
|
||||
termine: 'bg-text-muted/20 text-text-muted',
|
||||
echoue: 'bg-red/20 text-red',
|
||||
}[s] || 'bg-bg text-text-muted')
|
||||
|
||||
async function toggleRecoltes(id: number) {
|
||||
if (openRecoltes.value === id) { openRecoltes.value = null; return }
|
||||
openRecoltes.value = id
|
||||
loadingRecoltes.value = true
|
||||
try { recoltesList.value = await recoltesApi.list(id) } catch { recoltesList.value = [] }
|
||||
finally { loadingRecoltes.value = false }
|
||||
}
|
||||
|
||||
async function addRecolte(plantingId: number) {
|
||||
const created = await recoltesApi.create(plantingId, { ...rForm })
|
||||
recoltesList.value.push(created)
|
||||
Object.assign(rForm, { quantite: 1, unite: 'kg', date_recolte: new Date().toISOString().slice(0, 10) })
|
||||
}
|
||||
|
||||
async function deleteRecolte(id: number, plantingId: number) {
|
||||
if (!confirm('Supprimer cette récolte ?')) return
|
||||
await recoltesApi.delete(id)
|
||||
recoltesList.value = recoltesList.value.filter(r => r.id !== id)
|
||||
}
|
||||
|
||||
function startEdit(p: typeof store.plantings[0]) {
|
||||
editId.value = p.id!
|
||||
Object.assign(cForm, {
|
||||
garden_id: p.garden_id, variety_id: p.variety_id,
|
||||
quantite: p.quantite, date_plantation: p.date_plantation?.slice(0, 10) || '',
|
||||
statut: p.statut, notes: p.notes || '',
|
||||
})
|
||||
showCreate.value = true
|
||||
}
|
||||
|
||||
function closeCreate() { showCreate.value = false; editId.value = null }
|
||||
|
||||
async function createPlanting() {
|
||||
if (editId.value) {
|
||||
await store.update(editId.value, { ...cForm })
|
||||
} else {
|
||||
await store.create({ ...cForm })
|
||||
}
|
||||
closeCreate()
|
||||
Object.assign(cForm, { garden_id: 0, variety_id: 0, quantite: 1, date_plantation: '', statut: 'prevu', notes: '' })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
store.fetchAll()
|
||||
gardensStore.fetchAll()
|
||||
plantsStore.fetchAll()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,22 +1,704 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { usePlantingsStore } from '@/stores/plantings';
|
||||
import { useGardensStore } from '@/stores/gardens';
|
||||
import { usePlantsStore } from '@/stores/plants';
|
||||
import { recoltesApi } from '@/api/recoltes';
|
||||
const store = usePlantingsStore();
|
||||
const gardensStore = useGardensStore();
|
||||
const plantsStore = usePlantsStore();
|
||||
const showCreate = ref(false);
|
||||
const filterStatut = ref('');
|
||||
const openRecoltes = ref(null);
|
||||
const recoltesList = ref([]);
|
||||
const loadingRecoltes = ref(false);
|
||||
const statuts = [
|
||||
{ val: '', label: 'Toutes' },
|
||||
{ val: 'prevu', label: '📋 Prévu' },
|
||||
{ val: 'en_cours', label: '🌿 En cours' },
|
||||
{ val: 'termine', label: '✅ Terminé' },
|
||||
{ val: 'echoue', label: '❌ Échoué' },
|
||||
];
|
||||
const cForm = reactive({
|
||||
garden_id: 0, variety_id: 0, quantite: 1,
|
||||
date_plantation: '', statut: 'prevu', notes: ''
|
||||
});
|
||||
const rForm = reactive({
|
||||
quantite: 1, unite: 'kg', date_recolte: new Date().toISOString().slice(0, 10)
|
||||
});
|
||||
const filtered = computed(() => filterStatut.value ? store.plantings.filter(p => p.statut === filterStatut.value) : store.plantings);
|
||||
function plantName(id) {
|
||||
const p = plantsStore.plants.find(x => x.id === id);
|
||||
return p ? (p.variete ? `${p.nom_commun} (${p.variete})` : p.nom_commun) : `Plante #${id}`;
|
||||
}
|
||||
function gardenName(id) {
|
||||
return gardensStore.gardens.find(g => g.id === id)?.nom ?? `Jardin #${id}`;
|
||||
}
|
||||
function fmtDate(s) {
|
||||
return new Date(s + (s.length === 10 ? 'T12:00:00' : '')).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: 'numeric' });
|
||||
}
|
||||
const statutClass = (s) => ({
|
||||
prevu: 'bg-blue/20 text-blue',
|
||||
en_cours: 'bg-green/20 text-green',
|
||||
termine: 'bg-text-muted/20 text-text-muted',
|
||||
echoue: 'bg-red/20 text-red',
|
||||
}[s] || 'bg-bg text-text-muted');
|
||||
async function toggleRecoltes(id) {
|
||||
if (openRecoltes.value === id) {
|
||||
openRecoltes.value = null;
|
||||
return;
|
||||
}
|
||||
openRecoltes.value = id;
|
||||
loadingRecoltes.value = true;
|
||||
try {
|
||||
recoltesList.value = await recoltesApi.list(id);
|
||||
}
|
||||
catch {
|
||||
recoltesList.value = [];
|
||||
}
|
||||
finally {
|
||||
loadingRecoltes.value = false;
|
||||
}
|
||||
}
|
||||
async function addRecolte(plantingId) {
|
||||
const created = await recoltesApi.create(plantingId, { ...rForm });
|
||||
recoltesList.value.push(created);
|
||||
Object.assign(rForm, { quantite: 1, unite: 'kg', date_recolte: new Date().toISOString().slice(0, 10) });
|
||||
}
|
||||
async function deleteRecolte(id, plantingId) {
|
||||
if (!confirm('Supprimer cette récolte ?'))
|
||||
return;
|
||||
await recoltesApi.delete(id);
|
||||
recoltesList.value = recoltesList.value.filter(r => r.id !== id);
|
||||
}
|
||||
async function createPlanting() {
|
||||
await store.create({ ...cForm });
|
||||
showCreate.value = false;
|
||||
Object.assign(cForm, { garden_id: 0, variety_id: 0, quantite: 1, date_plantation: '', statut: 'prevu', notes: '' });
|
||||
}
|
||||
onMounted(() => {
|
||||
store.fetchAll();
|
||||
gardensStore.fetchAll();
|
||||
plantsStore.fetchAll();
|
||||
});
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-3xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center justify-between mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.showCreate = true;
|
||||
} },
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 mb-4 flex-wrap" },
|
||||
});
|
||||
for (const [s] of __VLS_getVForSourceType((__VLS_ctx.statuts))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.filterStatut = s.val;
|
||||
} },
|
||||
key: (s.val),
|
||||
...{ class: (['px-3 py-1 rounded-full text-xs font-medium transition-colors',
|
||||
__VLS_ctx.filterStatut === s.val ? 'bg-blue text-bg' : 'bg-bg-soft text-text-muted hover:text-text']) },
|
||||
});
|
||||
(s.label);
|
||||
}
|
||||
if (__VLS_ctx.store.loading) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
}
|
||||
else if (!__VLS_ctx.filtered.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm text-center py-8" },
|
||||
});
|
||||
}
|
||||
for (const [p] of __VLS_getVForSourceType((__VLS_ctx.filtered))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (p.id),
|
||||
...{ class: "bg-bg-soft rounded-xl mb-3 border border-bg-hard overflow-hidden" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4 flex items-start gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center gap-2 flex-wrap" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text font-semibold" },
|
||||
});
|
||||
(__VLS_ctx.plantName(p.variety_id));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text-muted text-xs" },
|
||||
});
|
||||
(__VLS_ctx.gardenName(p.garden_id));
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: (['text-xs px-2 py-0.5 rounded-full font-medium', __VLS_ctx.statutClass(p.statut)]) },
|
||||
});
|
||||
(p.statut);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mt-1 flex gap-3 flex-wrap" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(p.quantite);
|
||||
if (p.date_plantation) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(__VLS_ctx.fmtDate(p.date_plantation));
|
||||
}
|
||||
if (p.notes) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(p.notes);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 items-center" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.toggleRecoltes(p.id);
|
||||
} },
|
||||
...{ class: (['text-xs px-2 py-1 rounded transition-colors',
|
||||
__VLS_ctx.openRecoltes === p.id ? 'bg-aqua/20 text-aqua' : 'bg-bg-hard text-text-muted hover:text-aqua']) },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.store.remove(p.id);
|
||||
} },
|
||||
...{ class: "text-text-muted hover:text-red text-sm ml-1" },
|
||||
});
|
||||
if (__VLS_ctx.openRecoltes === p.id) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "border-t border-bg-hard px-4 py-3 bg-bg/50" },
|
||||
});
|
||||
if (__VLS_ctx.loadingRecoltes) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs py-2" },
|
||||
});
|
||||
}
|
||||
else {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
if (!__VLS_ctx.recoltesList.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mb-2" },
|
||||
});
|
||||
}
|
||||
for (const [r] of __VLS_getVForSourceType((__VLS_ctx.recoltesList))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (r.id),
|
||||
...{ class: "flex items-center gap-3 text-sm py-1 border-b border-bg-hard last:border-0" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-aqua font-mono" },
|
||||
});
|
||||
(r.quantite);
|
||||
(r.unite);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text-muted text-xs" },
|
||||
});
|
||||
(__VLS_ctx.fmtDate(r.date_recolte));
|
||||
if (r.notes) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text-muted text-xs flex-1 truncate" },
|
||||
});
|
||||
(r.notes);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.openRecoltes === p.id))
|
||||
return;
|
||||
if (!!(__VLS_ctx.loadingRecoltes))
|
||||
return;
|
||||
__VLS_ctx.deleteRecolte(r.id, p.id);
|
||||
} },
|
||||
...{ class: "text-text-muted hover:text-red text-xs ml-auto" },
|
||||
});
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.form, __VLS_intrinsicElements.form)({
|
||||
...{ onSubmit: (...[$event]) => {
|
||||
if (!(__VLS_ctx.openRecoltes === p.id))
|
||||
return;
|
||||
if (!!(__VLS_ctx.loadingRecoltes))
|
||||
return;
|
||||
__VLS_ctx.addRecolte(p.id);
|
||||
} },
|
||||
...{ class: "flex gap-2 mt-3 flex-wrap items-end" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "number",
|
||||
step: "0.1",
|
||||
min: "0",
|
||||
required: true,
|
||||
...{ class: "bg-bg border border-bg-hard rounded px-2 py-1 text-text text-xs w-20 focus:border-aqua outline-none" },
|
||||
});
|
||||
(__VLS_ctx.rForm.quantite);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.rForm.unite),
|
||||
...{ class: "bg-bg border border-bg-hard rounded px-2 py-1 text-text text-xs focus:border-aqua outline-none" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "date",
|
||||
required: true,
|
||||
...{ class: "bg-bg border border-bg-hard rounded px-2 py-1 text-text text-xs focus:border-aqua outline-none" },
|
||||
});
|
||||
(__VLS_ctx.rForm.date_recolte);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
type: "submit",
|
||||
...{ class: "bg-aqua text-bg px-3 py-1 rounded text-xs font-semibold hover:opacity-90 self-end" },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__VLS_ctx.showCreate) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showCreate))
|
||||
return;
|
||||
__VLS_ctx.showCreate = false;
|
||||
} },
|
||||
...{ class: "fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-bg-hard rounded-xl p-6 w-full max-w-md border border-bg-soft" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h2, __VLS_intrinsicElements.h2)({
|
||||
...{ class: "text-text font-bold text-lg mb-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.form, __VLS_intrinsicElements.form)({
|
||||
...{ onSubmit: (__VLS_ctx.createPlanting) },
|
||||
...{ class: "flex flex-col gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.cForm.garden_id),
|
||||
required: true,
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "",
|
||||
});
|
||||
for (const [g] of __VLS_getVForSourceType((__VLS_ctx.gardensStore.gardens))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
key: (g.id),
|
||||
value: (g.id),
|
||||
});
|
||||
(g.nom);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.cForm.variety_id),
|
||||
required: true,
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "",
|
||||
});
|
||||
for (const [p] of __VLS_getVForSourceType((__VLS_ctx.plantsStore.plants))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
key: (p.id),
|
||||
value: (p.id),
|
||||
});
|
||||
(p.nom_commun);
|
||||
(p.variete ? ' — ' + p.variete : '');
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-2 gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "number",
|
||||
min: "1",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
(__VLS_ctx.cForm.quantite);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "date",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
(__VLS_ctx.cForm.date_plantation);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.cForm.statut),
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "prevu",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "en_cours",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "termine",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.textarea)({
|
||||
value: (__VLS_ctx.cForm.notes),
|
||||
placeholder: "Notes...",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green resize-none h-16" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 justify-end" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showCreate))
|
||||
return;
|
||||
__VLS_ctx.showCreate = false;
|
||||
} },
|
||||
type: "button",
|
||||
...{ class: "px-4 py-2 text-text-muted hover:text-text text-sm" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
type: "submit",
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90" },
|
||||
});
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-3xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-between']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-wrap']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-8']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['overflow-hidden']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-start']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-wrap']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-wrap']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['ml-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-t']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg/50']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-b']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['last:border-0']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-aqua']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-mono']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['truncate']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['ml-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-wrap']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-end']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-20']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-aqua']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-aqua']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-aqua']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-aqua']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['self-end']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['fixed']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['inset-0']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-black/60']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['z-50']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-md']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-col']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['resize-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['h-16']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-end']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
return {
|
||||
store: store,
|
||||
gardensStore: gardensStore,
|
||||
plantsStore: plantsStore,
|
||||
showCreate: showCreate,
|
||||
filterStatut: filterStatut,
|
||||
openRecoltes: openRecoltes,
|
||||
recoltesList: recoltesList,
|
||||
loadingRecoltes: loadingRecoltes,
|
||||
statuts: statuts,
|
||||
cForm: cForm,
|
||||
rForm: rForm,
|
||||
filtered: filtered,
|
||||
plantName: plantName,
|
||||
gardenName: gardenName,
|
||||
fmtDate: fmtDate,
|
||||
statutClass: statutClass,
|
||||
toggleRecoltes: toggleRecoltes,
|
||||
addRecolte: addRecolte,
|
||||
deleteRecolte: deleteRecolte,
|
||||
createPlanting: createPlanting,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
|
||||
361
frontend/src/views/PlantesView.vue
Normal file
@@ -0,0 +1,361 @@
|
||||
<template>
|
||||
<div class="p-4 max-w-4xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold text-green">🌱 Plantes</h1>
|
||||
<button @click="showForm = true" class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90">+ Ajouter</button>
|
||||
</div>
|
||||
|
||||
<!-- Filtres catégorie -->
|
||||
<div class="flex gap-2 mb-4 flex-wrap">
|
||||
<button v-for="cat in categories" :key="cat.val"
|
||||
@click="selectedCat = cat.val"
|
||||
:class="['px-3 py-1 rounded-full text-xs font-medium transition-colors',
|
||||
selectedCat === cat.val ? 'bg-green text-bg' : 'bg-bg-soft text-text-muted hover:text-text']">
|
||||
{{ cat.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Liste -->
|
||||
<div v-if="plantsStore.loading" class="text-text-muted text-sm">Chargement...</div>
|
||||
<div v-else-if="!filteredPlants.length" class="text-text-muted text-sm py-4">Aucune plante.</div>
|
||||
<div v-for="p in filteredPlants" :key="p.id"
|
||||
class="bg-bg-soft rounded-lg mb-2 border border-bg-hard overflow-hidden">
|
||||
<!-- En-tête cliquable -->
|
||||
<div class="p-4 flex items-start justify-between gap-4 cursor-pointer"
|
||||
@click="toggleDetail(p.id!)">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-1 flex-wrap">
|
||||
<span class="text-text font-semibold">{{ p.nom_commun }}</span>
|
||||
<span v-if="p.variete" class="text-text-muted text-xs">— {{ p.variete }}</span>
|
||||
<span v-if="p.categorie" :class="['text-xs px-2 py-0.5 rounded-full font-medium', catClass(p.categorie)]">{{ catLabel(p.categorie) }}</span>
|
||||
</div>
|
||||
<div class="text-text-muted text-xs flex gap-3 flex-wrap">
|
||||
<span v-if="p.famille">🌿 {{ p.famille }}</span>
|
||||
<span v-if="p.espacement_cm">↔ {{ p.espacement_cm }}cm</span>
|
||||
<span v-if="p.besoin_eau">💧 {{ p.besoin_eau }}</span>
|
||||
<span v-if="p.plantation_mois">🌱 Plantation: mois {{ p.plantation_mois }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<span class="text-text-muted text-xs">{{ openId === p.id ? '▲' : '▼' }}</span>
|
||||
<button @click.stop="startEdit(p)" class="text-yellow text-xs hover:underline">Édit.</button>
|
||||
<button @click.stop="removePlant(p.id!)" class="text-red text-xs hover:underline">Suppr.</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Panneau détail -->
|
||||
<div v-if="openId === p.id" class="border-t border-bg-hard px-4 pb-4 pt-3">
|
||||
<!-- Notes -->
|
||||
<p v-if="p.notes" class="text-text-muted text-sm mb-3 italic">{{ p.notes }}</p>
|
||||
|
||||
<!-- Galerie photos -->
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<span class="text-text-muted text-xs font-medium uppercase tracking-wide">Photos</span>
|
||||
<button @click="openUpload(p)" class="text-green text-xs hover:underline">+ Ajouter une photo</button>
|
||||
</div>
|
||||
|
||||
<div v-if="loadingPhotos" class="text-text-muted text-xs">Chargement...</div>
|
||||
<div v-else-if="!plantPhotos.length" class="text-text-muted text-xs mb-3">Aucune photo pour cette plante.</div>
|
||||
<div v-else class="grid grid-cols-4 gap-2 mb-3">
|
||||
<div v-for="m in plantPhotos" :key="m.id"
|
||||
class="aspect-square rounded overflow-hidden bg-bg-hard relative group cursor-pointer"
|
||||
@click="lightbox = m">
|
||||
<img :src="m.thumbnail_url || m.url" class="w-full h-full object-cover" />
|
||||
<div v-if="m.identified_common"
|
||||
class="absolute bottom-0 left-0 right-0 bg-black/70 text-xs text-green px-1 py-0.5 truncate">
|
||||
{{ m.identified_common }}
|
||||
</div>
|
||||
<button @click.stop="deletePhoto(m)" class="hidden group-hover:flex absolute top-1 right-1 bg-red/80 text-white text-xs rounded px-1">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lier une photo existante de la bibliothèque -->
|
||||
<button @click="openLinkPhoto(p)" class="text-blue text-xs hover:underline">
|
||||
🔗 Lier une photo existante de la bibliothèque
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal formulaire création / édition -->
|
||||
<div v-if="showForm || editPlant" class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" @click.self="closeForm">
|
||||
<div class="bg-bg-hard rounded-xl p-6 w-full max-w-md border border-bg-soft max-h-[90vh] overflow-y-auto">
|
||||
<h2 class="text-text font-bold text-lg mb-4">{{ editPlant ? 'Modifier la plante' : 'Nouvelle plante' }}</h2>
|
||||
<form @submit.prevent="submitPlant" class="flex flex-col gap-3">
|
||||
<input v-model="form.nom_commun" placeholder="Nom commun *" required
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" />
|
||||
<input v-model="form.nom_botanique" placeholder="Nom botanique"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" />
|
||||
<input v-model="form.variete" placeholder="Variété"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" />
|
||||
<input v-model="form.famille" placeholder="Famille botanique"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" />
|
||||
<select v-model="form.categorie"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green">
|
||||
<option value="">Catégorie</option>
|
||||
<option value="potager">Potager</option>
|
||||
<option value="fleur">Fleur</option>
|
||||
<option value="arbre">Arbre</option>
|
||||
<option value="arbuste">Arbuste</option>
|
||||
<option value="adventice">Adventice (mauvaise herbe)</option>
|
||||
</select>
|
||||
<select v-model="form.type_plante"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green">
|
||||
<option value="">Type</option>
|
||||
<option value="legume">Légume</option>
|
||||
<option value="fruit">Fruit</option>
|
||||
<option value="aromatique">Aromatique</option>
|
||||
<option value="fleur">Fleur</option>
|
||||
<option value="adventice">Adventice</option>
|
||||
</select>
|
||||
<select v-model="form.besoin_eau"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green">
|
||||
<option value="">Besoin en eau</option>
|
||||
<option value="faible">Faible</option>
|
||||
<option value="moyen">Moyen</option>
|
||||
<option value="élevé">Élevé</option>
|
||||
</select>
|
||||
<select v-model="form.besoin_soleil"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green">
|
||||
<option value="">Ensoleillement</option>
|
||||
<option value="ombre">Ombre</option>
|
||||
<option value="mi-ombre">Mi-ombre</option>
|
||||
<option value="plein soleil">Plein soleil</option>
|
||||
</select>
|
||||
<div class="flex gap-2">
|
||||
<input v-model.number="form.espacement_cm" type="number" placeholder="Espacement (cm)"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm flex-1 outline-none focus:border-green" />
|
||||
<input v-model.number="form.temp_min_c" type="number" placeholder="T° min (°C)"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm flex-1 outline-none focus:border-green" />
|
||||
</div>
|
||||
<input v-model="form.plantation_mois" placeholder="Mois plantation (ex: 3,4,5)"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" />
|
||||
<input v-model="form.recolte_mois" placeholder="Mois récolte (ex: 7,8,9)"
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" />
|
||||
<textarea v-model="form.notes" placeholder="Notes..."
|
||||
class="bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green resize-none h-20" />
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button type="button" @click="closeForm"
|
||||
class="px-4 py-2 text-text-muted hover:text-text text-sm">Annuler</button>
|
||||
<button type="submit"
|
||||
class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90">
|
||||
{{ editPlant ? 'Enregistrer' : 'Créer' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal upload photo pour une plante -->
|
||||
<div v-if="uploadTarget" class="fixed inset-0 bg-black/70 z-50 flex items-center justify-center p-4" @click.self="uploadTarget = null">
|
||||
<div class="bg-bg-hard rounded-xl p-6 w-full max-w-sm border border-bg-soft">
|
||||
<h3 class="text-text font-bold mb-4">Photo pour "{{ uploadTarget.nom_commun }}"</h3>
|
||||
<label class="block border-2 border-dashed border-bg-soft rounded-lg p-6 text-center cursor-pointer hover:border-green transition-colors">
|
||||
<input type="file" accept="image/*" class="hidden" @change="uploadPhoto" />
|
||||
<div class="text-text-muted text-sm">📷 Choisir une image</div>
|
||||
</label>
|
||||
<button @click="uploadTarget = null" class="mt-3 w-full text-text-muted hover:text-text text-sm">Annuler</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal lier photo existante -->
|
||||
<div v-if="linkTarget" class="fixed inset-0 bg-black/70 z-50 flex items-center justify-center p-4" @click.self="linkTarget = null">
|
||||
<div class="bg-bg-hard rounded-xl p-6 w-full max-w-2xl border border-bg-soft max-h-[80vh] flex flex-col">
|
||||
<h3 class="text-text font-bold mb-3">Lier une photo à "{{ linkTarget.nom_commun }}"</h3>
|
||||
<p class="text-text-muted text-xs mb-3">Sélectionne une photo de la bibliothèque (non liée à une plante)</p>
|
||||
<div v-if="!unlinkPhotos.length" class="text-text-muted text-sm py-4 text-center">Aucune photo disponible.</div>
|
||||
<div v-else class="grid grid-cols-4 gap-2 overflow-y-auto flex-1">
|
||||
<div v-for="m in unlinkPhotos" :key="m.id"
|
||||
class="aspect-square rounded overflow-hidden bg-bg-hard relative cursor-pointer group border-2 transition-colors"
|
||||
:class="selectedLinkPhoto === m.id ? 'border-green' : 'border-transparent'"
|
||||
@click="selectedLinkPhoto = m.id">
|
||||
<img :src="m.thumbnail_url || m.url" class="w-full h-full object-cover" />
|
||||
<div v-if="m.identified_common" class="absolute bottom-0 left-0 right-0 bg-black/70 text-xs text-green px-1 py-0.5 truncate">{{ m.identified_common }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 justify-end mt-3">
|
||||
<button @click="linkTarget = null" class="px-4 py-2 text-text-muted hover:text-text text-sm">Annuler</button>
|
||||
<button @click="confirmLink" :disabled="!selectedLinkPhoto"
|
||||
class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90 disabled:opacity-40">
|
||||
Lier la photo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lightbox -->
|
||||
<div v-if="lightbox" class="fixed inset-0 bg-black/80 z-50 flex items-center justify-center p-4" @click.self="lightbox = null">
|
||||
<div class="max-w-lg w-full">
|
||||
<img :src="lightbox.url" class="w-full rounded-xl" />
|
||||
<div v-if="lightbox.identified_species" class="text-center mt-3 text-text-muted text-sm">
|
||||
<div class="text-green font-semibold text-base">{{ lightbox.identified_common }}</div>
|
||||
<div class="italic">{{ lightbox.identified_species }}</div>
|
||||
<div class="text-xs mt-1">Confiance : {{ Math.round((lightbox.identified_confidence || 0) * 100) }}% — via {{ lightbox.identified_source }}</div>
|
||||
</div>
|
||||
<button class="mt-4 w-full text-text-muted hover:text-text text-sm" @click="lightbox = null">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
import { usePlantsStore } from '@/stores/plants'
|
||||
import type { Plant } from '@/api/plants'
|
||||
|
||||
const plantsStore = usePlantsStore()
|
||||
const showForm = ref(false)
|
||||
const editPlant = ref<Plant | null>(null)
|
||||
const selectedCat = ref('')
|
||||
const openId = ref<number | null>(null)
|
||||
const plantPhotos = ref<Media[]>([])
|
||||
const loadingPhotos = ref(false)
|
||||
const uploadTarget = ref<Plant | null>(null)
|
||||
const linkTarget = ref<Plant | null>(null)
|
||||
const unlinkPhotos = ref<Media[]>([])
|
||||
const selectedLinkPhoto = ref<number | null>(null)
|
||||
const lightbox = ref<Media | null>(null)
|
||||
|
||||
interface Media {
|
||||
id: number; entity_type: string; entity_id: number
|
||||
url: string; thumbnail_url?: string; titre?: string
|
||||
identified_species?: string; identified_common?: string
|
||||
identified_confidence?: number; identified_source?: string
|
||||
}
|
||||
|
||||
const categories = [
|
||||
{ val: '', label: 'Toutes' },
|
||||
{ val: 'potager', label: '🥕 Potager' },
|
||||
{ val: 'fleur', label: '🌸 Fleur' },
|
||||
{ val: 'arbre', label: '🌳 Arbre' },
|
||||
{ val: 'arbuste', label: '🌿 Arbuste' },
|
||||
{ val: 'adventice', label: '🌾 Adventices' },
|
||||
]
|
||||
|
||||
const form = reactive({
|
||||
nom_commun: '', nom_botanique: '', variete: '', famille: '',
|
||||
categorie: '', type_plante: '', besoin_eau: '', besoin_soleil: '',
|
||||
espacement_cm: undefined as number | undefined,
|
||||
temp_min_c: undefined as number | undefined,
|
||||
plantation_mois: '', recolte_mois: '', notes: '',
|
||||
})
|
||||
|
||||
const filteredPlants = computed(() =>
|
||||
selectedCat.value ? plantsStore.plants.filter(p => p.categorie === selectedCat.value) : plantsStore.plants
|
||||
)
|
||||
|
||||
const catClass = (cat: string) => ({
|
||||
potager: 'bg-green/20 text-green',
|
||||
fleur: 'bg-orange/20 text-orange',
|
||||
arbre: 'bg-blue/20 text-blue',
|
||||
arbuste: 'bg-yellow/20 text-yellow',
|
||||
adventice: 'bg-red/20 text-red',
|
||||
}[cat] || 'bg-bg text-text-muted')
|
||||
|
||||
const catLabel = (cat: string) => ({
|
||||
potager: '🥕 Potager', fleur: '🌸 Fleur', arbre: '🌳 Arbre',
|
||||
arbuste: '🌿 Arbuste', adventice: '🌾 Adventice',
|
||||
}[cat] || cat)
|
||||
|
||||
async function toggleDetail(id: number) {
|
||||
if (openId.value === id) { openId.value = null; return }
|
||||
openId.value = id
|
||||
await fetchPhotos(id)
|
||||
}
|
||||
|
||||
async function fetchPhotos(plantId: number) {
|
||||
loadingPhotos.value = true
|
||||
try {
|
||||
const { data } = await axios.get<Media[]>('/api/media', {
|
||||
params: { entity_type: 'plante', entity_id: plantId }
|
||||
})
|
||||
plantPhotos.value = data
|
||||
} finally {
|
||||
loadingPhotos.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function startEdit(p: Plant) {
|
||||
editPlant.value = p
|
||||
Object.assign(form, {
|
||||
nom_commun: p.nom_commun || '', nom_botanique: (p as any).nom_botanique || '',
|
||||
variete: p.variete || '', famille: p.famille || '',
|
||||
categorie: p.categorie || '', type_plante: p.type_plante || '',
|
||||
besoin_eau: p.besoin_eau || '', besoin_soleil: p.besoin_soleil || '',
|
||||
espacement_cm: p.espacement_cm, temp_min_c: p.temp_min_c,
|
||||
plantation_mois: p.plantation_mois || '', recolte_mois: p.recolte_mois || '',
|
||||
notes: p.notes || '',
|
||||
})
|
||||
}
|
||||
|
||||
function closeForm() {
|
||||
showForm.value = false
|
||||
editPlant.value = null
|
||||
Object.assign(form, {
|
||||
nom_commun: '', nom_botanique: '', variete: '', famille: '', categorie: '',
|
||||
type_plante: '', besoin_eau: '', besoin_soleil: '',
|
||||
espacement_cm: undefined, temp_min_c: undefined,
|
||||
plantation_mois: '', recolte_mois: '', notes: '',
|
||||
})
|
||||
}
|
||||
|
||||
async function submitPlant() {
|
||||
if (editPlant.value) {
|
||||
await axios.put(`/api/plants/${editPlant.value.id}`, { ...form })
|
||||
await plantsStore.fetchAll()
|
||||
} else {
|
||||
await plantsStore.create({ ...form })
|
||||
}
|
||||
closeForm()
|
||||
}
|
||||
|
||||
async function removePlant(id: number) {
|
||||
if (confirm('Supprimer cette plante ?')) {
|
||||
await plantsStore.remove(id)
|
||||
if (openId.value === id) openId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function openUpload(p: Plant) { uploadTarget.value = p }
|
||||
|
||||
async function uploadPhoto(e: Event) {
|
||||
const file = (e.target as HTMLInputElement).files?.[0]
|
||||
if (!file || !uploadTarget.value) return
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
const { data: uploaded } = await axios.post('/api/upload', fd)
|
||||
await axios.post('/api/media', {
|
||||
entity_type: 'plante', entity_id: uploadTarget.value.id,
|
||||
url: uploaded.url, thumbnail_url: uploaded.thumbnail_url,
|
||||
})
|
||||
uploadTarget.value = null
|
||||
if (openId.value) await fetchPhotos(openId.value)
|
||||
}
|
||||
|
||||
async function deletePhoto(m: Media) {
|
||||
if (!confirm('Supprimer cette photo ?')) return
|
||||
await axios.delete(`/api/media/${m.id}`)
|
||||
if (openId.value) await fetchPhotos(openId.value)
|
||||
}
|
||||
|
||||
async function openLinkPhoto(p: Plant) {
|
||||
linkTarget.value = p
|
||||
selectedLinkPhoto.value = null
|
||||
const { data } = await axios.get<Media[]>('/api/media/all')
|
||||
// Photos non liées à une plante (bibliothèque ou autres)
|
||||
unlinkPhotos.value = data.filter(m => m.entity_type !== 'plante')
|
||||
}
|
||||
|
||||
async function confirmLink() {
|
||||
if (!selectedLinkPhoto.value || !linkTarget.value) return
|
||||
await axios.patch(`/api/media/${selectedLinkPhoto.value}`, {
|
||||
entity_type: 'plante', entity_id: linkTarget.value.id,
|
||||
})
|
||||
const pid = linkTarget.value.id
|
||||
linkTarget.value = null
|
||||
selectedLinkPhoto.value = null
|
||||
if (openId.value === pid) await fetchPhotos(pid)
|
||||
}
|
||||
|
||||
onMounted(() => plantsStore.fetchAll())
|
||||
</script>
|
||||
446
frontend/src/views/PlantesView.vue.js
Normal file
@@ -0,0 +1,446 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { usePlantsStore } from '@/stores/plants';
|
||||
const plantsStore = usePlantsStore();
|
||||
const showForm = ref(false);
|
||||
const selectedCat = ref('');
|
||||
const categories = [
|
||||
{ val: '', label: 'Toutes' },
|
||||
{ val: 'potager', label: '🥕 Potager' },
|
||||
{ val: 'fleur', label: '🌸 Fleur' },
|
||||
{ val: 'arbre', label: '🌳 Arbre' },
|
||||
{ val: 'arbuste', label: '🌿 Arbuste' },
|
||||
];
|
||||
const form = reactive({
|
||||
nom_commun: '', variete: '', famille: '', categorie: '', type_plante: '',
|
||||
espacement_cm: undefined,
|
||||
temp_min_c: undefined,
|
||||
notes: '',
|
||||
});
|
||||
const filteredPlants = computed(() => selectedCat.value ? plantsStore.plants.filter(p => p.categorie === selectedCat.value) : plantsStore.plants);
|
||||
const catClass = (cat) => ({
|
||||
potager: 'bg-green/20 text-green',
|
||||
fleur: 'bg-orange/20 text-orange',
|
||||
arbre: 'bg-blue/20 text-blue',
|
||||
arbuste: 'bg-yellow/20 text-yellow',
|
||||
}[cat] || 'bg-bg text-text-muted');
|
||||
async function submitPlant() {
|
||||
await plantsStore.create({ ...form });
|
||||
Object.assign(form, { nom_commun: '', variete: '', famille: '', categorie: '', type_plante: '', espacement_cm: undefined, temp_min_c: undefined, notes: '' });
|
||||
showForm.value = false;
|
||||
}
|
||||
async function removePlant(id) {
|
||||
if (confirm('Supprimer cette plante ?'))
|
||||
await plantsStore.remove(id);
|
||||
}
|
||||
onMounted(() => plantsStore.fetchAll());
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4 max-w-4xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center justify-between mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.showForm = true;
|
||||
} },
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 mb-4 flex-wrap" },
|
||||
});
|
||||
for (const [cat] of __VLS_getVForSourceType((__VLS_ctx.categories))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.selectedCat = cat.val;
|
||||
} },
|
||||
key: (cat.val),
|
||||
...{ class: (['px-3 py-1 rounded-full text-xs font-medium transition-colors',
|
||||
__VLS_ctx.selectedCat === cat.val ? 'bg-green text-bg' : 'bg-bg-soft text-text-muted hover:text-text']) },
|
||||
});
|
||||
(cat.label);
|
||||
}
|
||||
if (__VLS_ctx.plantsStore.loading) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
}
|
||||
else if (!__VLS_ctx.filteredPlants.length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm py-4" },
|
||||
});
|
||||
}
|
||||
for (const [p] of __VLS_getVForSourceType((__VLS_ctx.filteredPlants))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (p.id),
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 mb-2 border border-bg-hard flex items-start justify-between gap-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center gap-2 mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text font-semibold" },
|
||||
});
|
||||
(p.nom_commun);
|
||||
if (p.variete) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text-muted text-xs" },
|
||||
});
|
||||
(p.variete);
|
||||
}
|
||||
if (p.categorie) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: (['text-xs px-2 py-0.5 rounded-full font-medium', __VLS_ctx.catClass(p.categorie)]) },
|
||||
});
|
||||
(p.categorie);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs flex gap-3 flex-wrap" },
|
||||
});
|
||||
if (p.famille) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(p.famille);
|
||||
}
|
||||
if (p.espacement_cm) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(p.espacement_cm);
|
||||
}
|
||||
if (p.besoin_eau) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(p.besoin_eau);
|
||||
}
|
||||
if (p.plantation_mois) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({});
|
||||
(p.plantation_mois);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.removePlant(p.id);
|
||||
} },
|
||||
...{ class: "text-red text-xs hover:underline shrink-0" },
|
||||
});
|
||||
}
|
||||
if (__VLS_ctx.showForm) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showForm))
|
||||
return;
|
||||
__VLS_ctx.showForm = false;
|
||||
} },
|
||||
...{ class: "fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "bg-bg-hard rounded-xl p-6 w-full max-w-md border border-bg-soft" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h2, __VLS_intrinsicElements.h2)({
|
||||
...{ class: "text-text font-bold text-lg mb-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.form, __VLS_intrinsicElements.form)({
|
||||
...{ onSubmit: (__VLS_ctx.submitPlant) },
|
||||
...{ class: "flex flex-col gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
placeholder: "Nom commun *",
|
||||
required: true,
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
(__VLS_ctx.form.nom_commun);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
placeholder: "Variété",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
(__VLS_ctx.form.variete);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
placeholder: "Famille botanique",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
(__VLS_ctx.form.famille);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.form.categorie),
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "potager",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "fleur",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "arbre",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "arbuste",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.form.type_plante),
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "legume",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "fruit",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "aromatique",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "fleur",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "number",
|
||||
placeholder: "Espacement (cm)",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm flex-1 outline-none focus:border-green" },
|
||||
});
|
||||
(__VLS_ctx.form.espacement_cm);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "number",
|
||||
placeholder: "T° min (°C)",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm flex-1 outline-none focus:border-green" },
|
||||
});
|
||||
(__VLS_ctx.form.temp_min_c);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.textarea)({
|
||||
value: (__VLS_ctx.form.notes),
|
||||
placeholder: "Notes...",
|
||||
...{ class: "bg-bg border border-bg-soft rounded-lg px-3 py-2 text-text text-sm w-full outline-none focus:border-green resize-none h-20" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 justify-end" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showForm))
|
||||
return;
|
||||
__VLS_ctx.showForm = false;
|
||||
} },
|
||||
type: "button",
|
||||
...{ class: "px-4 py-2 text-text-muted hover:text-text text-sm" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
type: "submit",
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90" },
|
||||
});
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-4xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-between']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-wrap']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-start']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-between']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-wrap']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:underline']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['shrink-0']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['fixed']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['inset-0']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-black/60']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['z-50']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-md']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-col']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['resize-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['h-20']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-end']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
plantsStore: plantsStore,
|
||||
showForm: showForm,
|
||||
selectedCat: selectedCat,
|
||||
categories: categories,
|
||||
form: form,
|
||||
filteredPlants: filteredPlants,
|
||||
catClass: catClass,
|
||||
submitPlant: submitPlant,
|
||||
removePlant: removePlant,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
; /* PartiallyEnd: #4569/main.vue */
|
||||
@@ -4,15 +4,23 @@ const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-2xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
...{ class: "text-2xl font-bold text-green mb-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.p, __VLS_intrinsicElements.p)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
|
||||
@@ -1,42 +1,11 @@
|
||||
<template>
|
||||
<div class="p-4 max-w-2xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold text-green">Tâches</h1>
|
||||
<button
|
||||
class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90"
|
||||
@click="showForm = !showForm"
|
||||
>+ Nouvelle</button>
|
||||
<h1 class="text-2xl font-bold text-green">✅ Tâches</h1>
|
||||
<button class="bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90"
|
||||
@click="openCreate">+ Nouvelle</button>
|
||||
</div>
|
||||
|
||||
<form v-if="showForm" class="bg-bg-soft rounded-lg p-4 mb-6 border border-green/30" @submit.prevent="submit">
|
||||
<div class="grid gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Titre *</label>
|
||||
<input v-model="form.titre" required
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Priorité</label>
|
||||
<select v-model="form.priorite" class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm">
|
||||
<option value="basse">Basse</option>
|
||||
<option value="normale">Normale</option>
|
||||
<option value="haute">Haute</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Échéance</label>
|
||||
<input v-model="form.echeance" type="date"
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 mt-4">
|
||||
<button type="submit" class="bg-green text-bg px-4 py-2 rounded text-sm font-semibold">Créer</button>
|
||||
<button type="button" class="text-text-muted text-sm px-4 py-2 hover:text-text" @click="showForm = false">Annuler</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div v-for="[groupe, label] in groupes" :key="groupe" class="mb-6">
|
||||
<h2 class="text-text-muted text-xs uppercase tracking-widest mb-2">{{ label }}</h2>
|
||||
<div v-if="!byStatut(groupe).length" class="text-text-muted text-xs pl-2 mb-2">—</div>
|
||||
@@ -47,26 +16,80 @@
|
||||
'text-yellow': t.priorite === 'normale',
|
||||
'text-text-muted': t.priorite === 'basse'
|
||||
}">●</span>
|
||||
<span class="text-text text-sm flex-1">{{ t.titre }}</span>
|
||||
<div class="flex gap-1 items-center">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="text-text text-sm">{{ t.titre }}</div>
|
||||
<div v-if="t.echeance" class="text-text-muted text-xs">📅 {{ fmtDate(t.echeance) }}</div>
|
||||
</div>
|
||||
<div class="flex gap-1 items-center shrink-0">
|
||||
<button v-if="t.statut === 'a_faire'" class="text-xs text-blue hover:underline"
|
||||
@click="store.updateStatut(t.id!, 'en_cours')">→ En cours</button>
|
||||
<button v-if="t.statut === 'en_cours'" class="text-xs text-green hover:underline"
|
||||
@click="store.updateStatut(t.id!, 'fait')">✓ Fait</button>
|
||||
<button class="text-xs text-text-muted hover:text-red ml-2" @click="store.remove(t.id!)">✕</button>
|
||||
<button @click="startEdit(t)" class="text-xs text-yellow hover:underline ml-2">Édit.</button>
|
||||
<button class="text-xs text-text-muted hover:text-red ml-1" @click="store.remove(t.id!)">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal création / édition -->
|
||||
<div v-if="showForm" class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4" @click.self="closeForm">
|
||||
<div class="bg-bg-hard rounded-xl p-6 w-full max-w-md border border-bg-soft">
|
||||
<h2 class="text-text font-bold text-lg mb-4">{{ editId ? 'Modifier la tâche' : 'Nouvelle tâche' }}</h2>
|
||||
<form @submit.prevent="submit" class="grid gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Titre *</label>
|
||||
<input v-model="form.titre" required
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Description</label>
|
||||
<textarea v-model="form.description" rows="2"
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none resize-none" />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Priorité</label>
|
||||
<select v-model="form.priorite" class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm">
|
||||
<option value="basse">Basse</option>
|
||||
<option value="normale">Normale</option>
|
||||
<option value="haute">Haute</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Statut</label>
|
||||
<select v-model="form.statut" class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm">
|
||||
<option value="a_faire">À faire</option>
|
||||
<option value="en_cours">En cours</option>
|
||||
<option value="fait">Terminé</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-text-muted text-xs block mb-1">Échéance</label>
|
||||
<input v-model="form.echeance" type="date"
|
||||
class="w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" />
|
||||
</div>
|
||||
<div class="flex gap-2 mt-2">
|
||||
<button type="submit" class="bg-green text-bg px-4 py-2 rounded text-sm font-semibold">
|
||||
{{ editId ? 'Enregistrer' : 'Créer' }}
|
||||
</button>
|
||||
<button type="button" class="text-text-muted text-sm px-4 py-2 hover:text-text" @click="closeForm">Annuler</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { useTasksStore } from '@/stores/tasks'
|
||||
import type { Task } from '@/api/tasks'
|
||||
|
||||
const store = useTasksStore()
|
||||
const showForm = ref(false)
|
||||
const form = reactive({ titre: '', priorite: 'normale', statut: 'a_faire', echeance: '' })
|
||||
const editId = ref<number | null>(null)
|
||||
const form = reactive({ titre: '', description: '', priorite: 'normale', statut: 'a_faire', echeance: '' })
|
||||
|
||||
const groupes: [string, string][] = [
|
||||
['a_faire', 'À faire'],
|
||||
@@ -76,11 +99,36 @@ const groupes: [string, string][] = [
|
||||
|
||||
const byStatut = (s: string) => store.tasks.filter(t => t.statut === s)
|
||||
|
||||
function fmtDate(s: string) {
|
||||
return new Date(s + 'T12:00:00').toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
editId.value = null
|
||||
Object.assign(form, { titre: '', description: '', priorite: 'normale', statut: 'a_faire', echeance: '' })
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
function startEdit(t: Task) {
|
||||
editId.value = t.id!
|
||||
Object.assign(form, {
|
||||
titre: t.titre, description: (t as any).description || '',
|
||||
priorite: t.priorite, statut: t.statut,
|
||||
echeance: t.echeance ? t.echeance.slice(0, 10) : '',
|
||||
})
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
function closeForm() { showForm.value = false; editId.value = null }
|
||||
|
||||
onMounted(() => store.fetchAll())
|
||||
|
||||
async function submit() {
|
||||
await store.create({ ...form })
|
||||
showForm.value = false
|
||||
Object.assign(form, { titre: '', priorite: 'normale', statut: 'a_faire', echeance: '' })
|
||||
if (editId.value) {
|
||||
await store.update(editId.value, { ...form })
|
||||
} else {
|
||||
await store.create({ ...form })
|
||||
}
|
||||
closeForm()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,22 +1,297 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { useTasksStore } from '@/stores/tasks';
|
||||
const store = useTasksStore();
|
||||
const showForm = ref(false);
|
||||
const form = reactive({ titre: '', priorite: 'normale', statut: 'a_faire', echeance: '' });
|
||||
const groupes = [
|
||||
['a_faire', 'À faire'],
|
||||
['en_cours', 'En cours'],
|
||||
['fait', 'Terminé'],
|
||||
];
|
||||
const byStatut = (s) => store.tasks.filter(t => t.statut === s);
|
||||
onMounted(() => store.fetchAll());
|
||||
async function submit() {
|
||||
await store.create({ ...form });
|
||||
showForm.value = false;
|
||||
Object.assign(form, { titre: '', priorite: 'normale', statut: 'a_faire', echeance: '' });
|
||||
}
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-2xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center justify-between mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.showForm = !__VLS_ctx.showForm;
|
||||
} },
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90" },
|
||||
});
|
||||
if (__VLS_ctx.showForm) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.form, __VLS_intrinsicElements.form)({
|
||||
...{ onSubmit: (__VLS_ctx.submit) },
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 mb-6 border border-green/30" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
required: true,
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" },
|
||||
});
|
||||
(__VLS_ctx.form.titre);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-2 gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.form.priorite),
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "basse",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "normale",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "haute",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
type: "date",
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" },
|
||||
});
|
||||
(__VLS_ctx.form.echeance);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 mt-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
type: "submit",
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded text-sm font-semibold" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showForm))
|
||||
return;
|
||||
__VLS_ctx.showForm = false;
|
||||
} },
|
||||
type: "button",
|
||||
...{ class: "text-text-muted text-sm px-4 py-2 hover:text-text" },
|
||||
});
|
||||
}
|
||||
for (const [[groupe, label]] of __VLS_getVForSourceType((__VLS_ctx.groupes))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (groupe),
|
||||
...{ class: "mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h2, __VLS_intrinsicElements.h2)({
|
||||
...{ class: "text-text-muted text-xs uppercase tracking-widest mb-2" },
|
||||
});
|
||||
(label);
|
||||
if (!__VLS_ctx.byStatut(groupe).length) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs pl-2 mb-2" },
|
||||
});
|
||||
}
|
||||
for (const [t] of __VLS_getVForSourceType((__VLS_ctx.byStatut(groupe)))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (t.id),
|
||||
...{ class: "bg-bg-soft rounded-lg p-3 mb-2 flex items-center gap-3 border border-bg-hard" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: ({
|
||||
'text-red': t.priorite === 'haute',
|
||||
'text-yellow': t.priorite === 'normale',
|
||||
'text-text-muted': t.priorite === 'basse'
|
||||
}) },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text text-sm flex-1" },
|
||||
});
|
||||
(t.titre);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-1 items-center" },
|
||||
});
|
||||
if (t.statut === 'a_faire') {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(t.statut === 'a_faire'))
|
||||
return;
|
||||
__VLS_ctx.store.updateStatut(t.id, 'en_cours');
|
||||
} },
|
||||
...{ class: "text-xs text-blue hover:underline" },
|
||||
});
|
||||
}
|
||||
if (t.statut === 'en_cours') {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(t.statut === 'en_cours'))
|
||||
return;
|
||||
__VLS_ctx.store.updateStatut(t.id, 'fait');
|
||||
} },
|
||||
...{ class: "text-xs text-green hover:underline" },
|
||||
});
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.store.remove(t.id);
|
||||
} },
|
||||
...{ class: "text-xs text-text-muted hover:text-red ml-2" },
|
||||
});
|
||||
}
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-between']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-green/30']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['uppercase']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['tracking-widest']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['pl-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-blue']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:underline']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:underline']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['ml-2']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
return {
|
||||
store: store,
|
||||
showForm: showForm,
|
||||
form: form,
|
||||
groupes: groupes,
|
||||
byStatut: byStatut,
|
||||
submit: submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
|
||||
@@ -1,22 +1,322 @@
|
||||
/// <reference types="../../node_modules/.vue-global-types/vue_3.5_0_0_0.d.ts" />
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { useVarietiesStore } from '@/stores/varieties';
|
||||
const store = useVarietiesStore();
|
||||
const showForm = ref(false);
|
||||
const form = reactive({ nom_commun: '', variete: '', famille: '', besoin_eau: '' });
|
||||
onMounted(() => store.fetchAll());
|
||||
async function submit() {
|
||||
await store.create({ ...form });
|
||||
showForm.value = false;
|
||||
Object.assign(form, { nom_commun: '', variete: '', famille: '', besoin_eau: '' });
|
||||
}
|
||||
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
||||
const __VLS_ctx = {};
|
||||
let __VLS_components;
|
||||
let __VLS_directives;
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "p-4" },
|
||||
...{ class: "p-4 max-w-2xl mx-auto" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex items-center justify-between mb-6" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.h1, __VLS_intrinsicElements.h1)({
|
||||
...{ class: "text-2xl font-bold text-green" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.showForm = !__VLS_ctx.showForm;
|
||||
} },
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded-lg text-sm font-semibold hover:opacity-90" },
|
||||
});
|
||||
if (__VLS_ctx.showForm) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.form, __VLS_intrinsicElements.form)({
|
||||
...{ onSubmit: (__VLS_ctx.submit) },
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 mb-6 border border-green/30" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
required: true,
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" },
|
||||
});
|
||||
(__VLS_ctx.form.nom_commun);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "grid grid-cols-2 gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" },
|
||||
});
|
||||
(__VLS_ctx.form.variete);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.input)({
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm focus:border-green outline-none" },
|
||||
});
|
||||
(__VLS_ctx.form.famille);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.label, __VLS_intrinsicElements.label)({
|
||||
...{ class: "text-text-muted text-xs block mb-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.select, __VLS_intrinsicElements.select)({
|
||||
value: (__VLS_ctx.form.besoin_eau),
|
||||
...{ class: "w-full bg-bg border border-bg-hard rounded px-3 py-2 text-text text-sm" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "faible",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "moyen",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.option, __VLS_intrinsicElements.option)({
|
||||
value: "fort",
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 mt-4" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
type: "submit",
|
||||
...{ class: "bg-green text-bg px-4 py-2 rounded text-sm font-semibold" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
if (!(__VLS_ctx.showForm))
|
||||
return;
|
||||
__VLS_ctx.showForm = false;
|
||||
} },
|
||||
type: "button",
|
||||
...{ class: "text-text-muted text-sm px-4 py-2 hover:text-text" },
|
||||
});
|
||||
}
|
||||
if (__VLS_ctx.store.loading) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-sm" },
|
||||
});
|
||||
}
|
||||
for (const [v] of __VLS_getVForSourceType((__VLS_ctx.store.varieties))) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
key: (v.id),
|
||||
...{ class: "bg-bg-soft rounded-lg p-4 mb-2 border border-bg-hard flex items-start gap-3" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex-1" },
|
||||
});
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text font-medium" },
|
||||
});
|
||||
(v.nom_commun);
|
||||
if (v.variete) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-text-muted text-xs" },
|
||||
});
|
||||
(v.variete);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "text-text-muted text-xs mt-1" },
|
||||
});
|
||||
(v.famille);
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.div, __VLS_intrinsicElements.div)({
|
||||
...{ class: "flex gap-2 mt-2 flex-wrap" },
|
||||
});
|
||||
if (v.besoin_eau) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-xs px-2 py-0.5 bg-bg rounded text-blue" },
|
||||
});
|
||||
(v.besoin_eau);
|
||||
}
|
||||
if (v.espacement_cm) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-xs px-2 py-0.5 bg-bg rounded text-text-muted" },
|
||||
});
|
||||
(v.espacement_cm);
|
||||
}
|
||||
if (v.plantation_mois) {
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.span, __VLS_intrinsicElements.span)({
|
||||
...{ class: "text-xs px-2 py-0.5 bg-bg rounded text-green" },
|
||||
});
|
||||
(v.plantation_mois);
|
||||
}
|
||||
__VLS_asFunctionalElement(__VLS_intrinsicElements.button, __VLS_intrinsicElements.button)({
|
||||
...{ onClick: (...[$event]) => {
|
||||
__VLS_ctx.store.remove(v.id);
|
||||
} },
|
||||
...{ class: "text-text-muted hover:text-red text-sm px-2 py-1 rounded hover:bg-bg transition-colors" },
|
||||
});
|
||||
}
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['max-w-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mx-auto']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-center']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['justify-between']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-2xl']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-bold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:opacity-90']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-6']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-green/30']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['grid-cols-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['focus:border-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['outline-none']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['block']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['w-full']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-semibold']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg-soft']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded-lg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['p-4']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mb-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['border-bg-hard']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['items-start']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-3']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['font-medium']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['gap-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['mt-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['flex-wrap']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-0.5']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-blue']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-0.5']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-xs']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-0.5']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-green']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-text-muted']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:text-red']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['text-sm']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['px-2']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['py-1']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['rounded']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['hover:bg-bg']} */ ;
|
||||
/** @type {__VLS_StyleScopedClasses['transition-colors']} */ ;
|
||||
var __VLS_dollars;
|
||||
const __VLS_self = (await import('vue')).defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
return {
|
||||
store: store,
|
||||
showForm: showForm,
|
||||
form: form,
|
||||
submit: submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default (await import('vue')).defineComponent({
|
||||
|
||||
@@ -9,8 +9,8 @@ export default defineConfig({
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': 'http://localhost:8000',
|
||||
'/uploads': 'http://localhost:8000',
|
||||
'/api': 'http://localhost:8060',
|
||||
'/uploads': 'http://localhost:8060',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||