SPB Git

spb/trouve-ka Public

Trouve-KA — moteur de recherche web indépendant, Québec-first. Crawler distribué, index OpenSearch, ranking bilingue, galerie d'images. En prod : www.trouve-ka.com

Python 76.8% TypeScript 15.7% SQL 3.9% Shell 1.4% CSS 1.3% Dockerfile 0.7%
3.0 KB · 108 lines tsx
Raw Blame History
1/**2 * Trouve-KA — formulaire client de soumission d'URL3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 */67"use client";89import { useState, type FormEvent } from "react";10import { Button } from "@/components/ui/button";11import { Input } from "@/components/ui/input";12import type { SubmitResponse } from "@/lib/types";1314type FormState =15  | { kind: "idle" }16  | { kind: "loading" }17  | { kind: "success"; message: string }18  | { kind: "error"; message: string };1920function isValidHttpUrl(value: string): boolean {21  try {22    const url = new URL(value);23    return url.protocol === "http:" || url.protocol === "https:";24  } catch {25    return false;26  }27}2829export function SubmitForm() {30  const [url, setUrl] = useState("");31  const [state, setState] = useState<FormState>({ kind: "idle" });3233  async function onSubmit(event: FormEvent) {34    event.preventDefault();35    const value = url.trim();3637    if (!isValidHttpUrl(value)) {38      setState({39        kind: "error",40        message:41          "Entrez une URL valide commençant par http:// ou https:// (ex. https://exemple.quebec).",42      });43      return;44    }4546    setState({ kind: "loading" });47    try {48      const res = await fetch("/api/submit", {49        method: "POST",50        headers: { "Content-Type": "application/json" },51        body: JSON.stringify({ url: value }),52      });53      if (!res.ok) throw new Error(`Erreur ${res.status}`);54      const data = (await res.json()) as SubmitResponse;55      if (data.accepted) {56        setState({57          kind: "success",58          message:59            data.message ||60            "Merci! Le site a été ajouté à la file du robot d'indexation.",61        });62        setUrl("");63      } else {64        setState({65          kind: "error",66          message: data.message || "Cette URL n'a pas été acceptée.",67        });68      }69    } catch {70      setState({71        kind: "error",72        message:73          "Impossible de joindre le moteur pour le moment. Réessayez sous peu.",74      });75    }76  }7778  return (79    <form onSubmit={onSubmit} className="mt-6">80      <label htmlFor="url-soumission" className="klabel block">81        Adresse du site (URL)82      </label>83      <div className="mt-2 flex flex-col gap-2 sm:flex-row">84        <Input85          id="url-soumission"86          type="url"87          inputMode="url"88          placeholder="https://exemple.quebec"89          value={url}90          onChange={(event) => setUrl(event.target.value)}91          required92          className="flex-1"93        />94        <Button type="submit" disabled={state.kind === "loading"}>95          {state.kind === "loading" ? "Envoi…" : "Soumettre"}96        </Button>97      </div>98      <p aria-live="polite" className="mt-3 min-h-5 text-sm">99        {state.kind === "success" ? (100          <span className="font-medium text-green">{state.message}</span>101        ) : state.kind === "error" ? (102          <span className="font-medium text-danger">{state.message}</span>103        ) : null}104      </p>105    </form>106  );107}108