-
Notifications
You must be signed in to change notification settings - Fork 0
Fix: corrige les trois points de review de la landing (markup, snippet iframe, échec de copie) #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Zoubeir23
merged 6 commits into
main
from
Fix/065-corrige-les-points-de-review-de-la-landing
Aug 2, 2026
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
687dc63
065-corrige-le-markup-des-listes-dossier-et-securite
Zoubeir23 b96b158
065-normalise-l-url-du-snippet-et-retire-frameborder
Zoubeir23 2d878c2
065-ajoute-le-libelle-d-echec-de-copie
Zoubeir23 7899e75
065-affiche-explicitement-l-echec-de-copie-du-snippet
Zoubeir23 7b895bf
065-fiabilise-l-url-publique-du-snippet-d-integration
Zoubeir23 e184b07
065-sort-les-tables-d-etat-du-bouton-de-copie
Zoubeir23 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,51 +1,83 @@ | ||
| "use client"; | ||
|
|
||
| import { useEffect, useState } from "react"; | ||
| import { Copy, Check } from "lucide-react"; | ||
| import { Copy, Check, TriangleAlert } from "lucide-react"; | ||
|
|
||
| type CopyStatus = "idle" | "copied" | "failed"; | ||
|
|
||
| /** Durée d'affichage du retour visuel avant retour à l'état initial. */ | ||
| const FEEDBACK_DURATION_MS = 2500; | ||
|
|
||
| const STATUS_ICONS: Record<CopyStatus, React.ReactNode> = { | ||
| idle: <Copy className="w-3.5 h-3.5" />, | ||
| copied: <Check className="w-3.5 h-3.5 text-primary" />, | ||
| failed: <TriangleAlert className="w-3.5 h-3.5" />, | ||
| }; | ||
|
|
||
| const STATUS_CLASSNAMES: Record<CopyStatus, string> = { | ||
| idle: "border-border text-muted-foreground hover:border-primary/40 hover:text-foreground", | ||
| copied: "border-border text-muted-foreground hover:border-primary/40 hover:text-foreground", | ||
| failed: "border-destructive/40 text-destructive", | ||
| }; | ||
|
|
||
| interface CopyEmbedCodeButtonProps { | ||
| code: string; | ||
| copyLabel: string; | ||
| copiedLabel: string; | ||
| copyFailedLabel: string; | ||
| } | ||
|
|
||
| /** | ||
| * Bouton de copie du snippet d'intégration du widget. Le libellé revient à son | ||
| * état initial après deux secondes, et le timer est nettoyé au démontage. | ||
| * Bouton de copie du snippet d'intégration du widget. | ||
| * | ||
| * Le presse-papiers est indisponible hors contexte sécurisé ou si l'utilisateur | ||
| * refuse la permission : l'échec est alors affiché explicitement, avec une | ||
| * consigne de sélection manuelle, plutôt que de laisser le clic sans effet. | ||
| */ | ||
| export function CopyEmbedCodeButton({ code, copyLabel, copiedLabel }: CopyEmbedCodeButtonProps) { | ||
| const [hasCopied, setHasCopied] = useState(false); | ||
| export function CopyEmbedCodeButton({ | ||
| code, | ||
| copyLabel, | ||
| copiedLabel, | ||
| copyFailedLabel, | ||
| }: CopyEmbedCodeButtonProps) { | ||
| const [copyStatus, setCopyStatus] = useState<CopyStatus>("idle"); | ||
|
|
||
| useEffect(() => { | ||
| if (!hasCopied) return; | ||
| if (copyStatus === "idle") return; | ||
|
|
||
| const resetTimeout = setTimeout(() => setHasCopied(false), 2000); | ||
| const resetTimeout = setTimeout(() => setCopyStatus("idle"), FEEDBACK_DURATION_MS); | ||
| return () => clearTimeout(resetTimeout); | ||
| }, [hasCopied]); | ||
| }, [copyStatus]); | ||
|
|
||
| const copyEmbedCode = async () => { | ||
| if (!navigator.clipboard) { | ||
| setCopyStatus("failed"); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| await navigator.clipboard.writeText(code); | ||
| setHasCopied(true); | ||
| setCopyStatus("copied"); | ||
| } catch { | ||
| // Clipboard indisponible (contexte non sécurisé ou permission refusée) : | ||
| // on laisse le code visible à l'écran pour une sélection manuelle. | ||
| setHasCopied(false); | ||
| setCopyStatus("failed"); | ||
| } | ||
| }; | ||
|
|
||
| const statusLabels: Record<CopyStatus, string> = { | ||
| idle: copyLabel, | ||
| copied: copiedLabel, | ||
| failed: copyFailedLabel, | ||
| }; | ||
|
|
||
| return ( | ||
| <button | ||
| type="button" | ||
| onClick={copyEmbedCode} | ||
| className="inline-flex items-center gap-2 rounded-xl border border-border bg-background px-4 py-2.5 font-mono text-[11px] uppercase tracking-[0.16em] text-muted-foreground hover:border-primary/40 hover:text-foreground transition-colors" | ||
| aria-live="polite" | ||
| className={`inline-flex items-center gap-2 rounded-xl border bg-background px-4 py-2.5 font-mono text-[11px] uppercase tracking-[0.16em] transition-colors ${STATUS_CLASSNAMES[copyStatus]}`} | ||
| > | ||
| {hasCopied ? ( | ||
| <Check className="w-3.5 h-3.5 text-primary" /> | ||
| ) : ( | ||
| <Copy className="w-3.5 h-3.5" /> | ||
| )} | ||
| {hasCopied ? copiedLabel : copyLabel} | ||
| {STATUS_ICONS[copyStatus]} | ||
| {statusLabels[copyStatus]} | ||
| </button> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: Zoubeir23/DocFlowAI
Length of output: 26356
Validez
NEXT_PUBLIC_APP_URLavec Zod avant de générer le snippet.buildEmbedCodeaccepte toute valeur deprocess.env.NEXT_PUBLIC_APP_URL, ce qui peut produire unsrcvide, invalide ou mal échappé. Ajoutez un schémalib/validations.ts, autorisez uniquement https et une URL propre, et passez cette valeur validée àbuildEmbedCode.🤖 Prompt for AI Agents
Source: Coding guidelines