Agrégateur de produits québécois — www.fabri-ka.com
Python 38.4%
HTML 30.3%
TypeScript 17.2%
CSS 11%
JavaScript 3.2%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2// pages/Inscrire.tsx — « Inscrire mon atelier » : auto-inscription des3// artisans/fabricants québécois. Le dépôt part en quarantaine de validation4// (statut pending) — rien n'est publié sans validation manuelle.5import { useEffect, useState } from 'react'6import {7 fetchFacets,8 setPageMeta,9 submitInscription,10 type InscriptionPayload,11} from '../api'1213const FIELD: React.CSSProperties = { display: 'grid', gap: 4 }1415export default function Inscrire() {16 const [cats, setCats] = useState<{ key: string; label: string }[]>([])17 const [form, setForm] = useState<InscriptionPayload>({18 name: '',19 metier: '',20 description: '',21 address: '',22 website: '',23 email: '',24 phone: '',25 entreprise_url: '',26 })27 const [socials, setSocials] = useState('')28 const [photos, setPhotos] = useState('')29 const [sending, setSending] = useState(false)30 const [done, setDone] = useState<string | null>(null)31 const [error, setError] = useState<string | null>(null)3233 useEffect(() => {34 setPageMeta(35 'Inscrire mon atelier — Fabri-Ka',36 'Artisans et fabricants québécois : inscrivez votre atelier sur ' +37 'Fabri-Ka. Chaque fiche est validée manuellement avant publication.'38 )39 fetchFacets()40 .then((f) => setCats(f.all_categories ?? []))41 .catch(() => {})42 }, [])4344 function set<K extends keyof InscriptionPayload>(45 key: K,46 value: InscriptionPayload[K]47 ) {48 setForm((f) => ({ ...f, [key]: value }))49 }5051 async function onSubmit(e: React.FormEvent) {52 e.preventDefault()53 setError(null)54 setSending(true)55 try {56 const clean = (s: string) =>57 s58 .split(/[\n,]/)59 .map((x) => x.trim())60 .filter(Boolean)61 const res = await submitInscription({62 ...form,63 socials: clean(socials),64 photos: clean(photos),65 })66 setDone(67 res.message ??68 'Merci ! Votre atelier est en attente de validation avant publication.'69 )70 } catch (err) {71 setError(err instanceof Error ? err.message : 'Erreur inattendue')72 } finally {73 setSending(false)74 }75 }7677 if (done) {78 return (79 <div className="page inscrire-page">80 <span className="kicker">Inscrire mon atelier</span>81 <h1>Demande reçue !</h1>82 <p className="contact-lede">{done}</p>83 <p className="contact-note">84 Notre équipe vérifie chaque atelier (fabrication québécoise,85 coordonnées, site) avant de publier la fiche. Vous serez joint aux86 coordonnées fournies si un détail manque.87 </p>88 </div>89 )90 }9192 return (93 <div className="page inscrire-page">94 <span className="kicker">Artisans & fabricants québécois</span>95 <h1>Inscrire mon atelier</h1>96 <p className="contact-lede">97 Vous fabriquez au Québec ? Inscrivez votre atelier : après une{' '}98 <b>validation manuelle</b> (fabrication québécoise et coordonnées99 vérifiables), votre fiche apparaîtra dans le répertoire — et si votre100 boutique en ligne expose un catalogue public, vos produits seront101 ajoutés automatiquement.102 </p>103104 <form105 className="inscrire-form"106 onSubmit={onSubmit}107 style={{ display: 'grid', gap: 16, maxWidth: 640 }}108 >109 <label style={FIELD}>110 <b>Nom de l’atelier / entreprise *</b>111 <input112 required113 minLength={2}114 maxLength={120}115 value={form.name}116 onChange={(e) => set('name', e.target.value)}117 placeholder="Ex. : Atelier Boréal"118 />119 </label>120 <label style={FIELD}>121 <b>Métier / spécialité *</b>122 <select123 required124 value={form.metier}125 onChange={(e) => set('metier', e.target.value)}126 >127 <option value="">Choisir…</option>128 {cats.map((c) => (129 <option key={c.key} value={c.key}>130 {c.label}131 </option>132 ))}133 </select>134 </label>135 <label style={FIELD}>136 <b>Description *</b>137 <textarea138 required139 minLength={20}140 maxLength={4000}141 rows={5}142 value={form.description}143 onChange={(e) => set('description', e.target.value)}144 placeholder="Ce que vous fabriquez, où, comment… (min. 20 caractères)"145 />146 </label>147 <label style={FIELD}>148 <b>Adresse de l’atelier</b>149 <input150 maxLength={300}151 value={form.address}152 onChange={(e) => set('address', e.target.value)}153 placeholder="123 rue Principale, Ville (Québec) G0X 0X0"154 />155 </label>156 <label style={FIELD}>157 <b>Site web / boutique en ligne</b>158 <input159 type="url"160 maxLength={200}161 value={form.website}162 onChange={(e) => set('website', e.target.value)}163 placeholder="https://…"164 />165 </label>166 <div167 style={{168 display: 'grid',169 gap: 16,170 gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))',171 }}172 >173 <label style={FIELD}>174 <b>Courriel *</b>175 <input176 type="email"177 maxLength={200}178 value={form.email}179 onChange={(e) => set('email', e.target.value)}180 placeholder="vous@atelier.ca"181 />182 </label>183 <label style={FIELD}>184 <b>Téléphone</b>185 <input186 type="tel"187 maxLength={40}188 value={form.phone}189 onChange={(e) => set('phone', e.target.value)}190 placeholder="418 555-0199"191 />192 </label>193 </div>194 <label style={FIELD}>195 <b>Réseaux sociaux</b>196 <textarea197 rows={2}198 value={socials}199 onChange={(e) => setSocials(e.target.value)}200 placeholder={'https://instagram.com/…\nhttps://facebook.com/…'}201 />202 <small>Une URL par ligne (Instagram, Facebook, TikTok…)</small>203 </label>204 <label style={FIELD}>205 <b>Photos (liens)</b>206 <textarea207 rows={2}208 value={photos}209 onChange={(e) => setPhotos(e.target.value)}210 placeholder="https://…/photo-atelier.jpg"211 />212 <small>213 Liens vers des photos de l’atelier ou des produits (une URL214 par ligne)215 </small>216 </label>217 {/* honeypot anti-robot : invisible, doit rester vide */}218 <label219 aria-hidden="true"220 style={{ position: 'absolute', left: '-9999px', height: 0, overflow: 'hidden' }}221 tabIndex={-1}222 >223 Ne pas remplir224 <input225 type="text"226 autoComplete="off"227 tabIndex={-1}228 value={form.entreprise_url}229 onChange={(e) => set('entreprise_url', e.target.value)}230 />231 </label>232 {error && (233 <p role="alert" style={{ color: 'var(--danger, #b3261e)' }}>234 {error}235 </p>236 )}237 <button type="submit" className="btn-primary" disabled={sending}>238 {sending ? 'Envoi…' : 'Soumettre mon atelier'}239 </button>240 <p className="contact-note">241 En soumettant, vous confirmez que les produits sont fabriqués (ou242 conçus) au Québec. Un courriel ou un téléphone est requis pour la243 vérification. Aucune fiche n’est publiée sans validation244 manuelle.245 </p>246 </form>247 </div>248 )249}250