-
Notifications
You must be signed in to change notification settings - Fork 195
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
Integrate magic link into webapp via /magic-link Route #722
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,14 +1,17 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'use client' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"use client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { columns } from "./columns" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { DataTable } from "../shared/data-table" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { columns } from "./columns"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { DataTable } from "../shared/data-table"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Card, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CardContent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CardHeader, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CardTitle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from "@/components/ui/card" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "../ui/dialog"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogContent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogDescription, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogFooter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogHeader, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogTitle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogTrigger, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from "../ui/dialog"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Button } from "../ui/button"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { PlusCircledIcon } from "@radix-ui/react-icons"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import CopyLinkInput from "./CopyLinkInput"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -19,49 +22,47 @@ import AddConnectionButton from "./AddConnectionButton"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import config from "@/lib/config"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import useMagicLinkStore from "@/state/magicLinkStore"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import useOrganisationStore from "@/state/organisationStore"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { usePostHog } from 'posthog-js/react' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { usePostHog } from "posthog-js/react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import useProjectStore from "@/state/projectStore"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default function ConnectionTable() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const {idProject} = useProjectStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { idProject } = useProjectStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { data: connections, isLoading, error } = useConnections(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [isGenerated, setIsGenerated] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const posthog = usePostHog() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const {uniqueLink} = useMagicLinkStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const {nameOrg} = useOrganisationStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const posthog = usePostHog(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(isLoading){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DataTableLoading data={[]} columns={columns}/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { uniqueLink } = useMagicLinkStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { nameOrg } = useOrganisationStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (isLoading) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return <DataTableLoading data={[]} columns={columns} />; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.log("error connections.."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const linkedConnections = (filter: string) => connections?.filter((connection) => connection.status == filter); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const linkedConnections = (filter: string) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
connections?.filter((connection) => connection.status == filter); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const ts = connections?.map((connection) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
organisation: nameOrg, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
organisation: nameOrg, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
app: connection.provider_slug, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
vertical: connection.vertical, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
category: connection.token_type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
vertical: connection.vertical, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
category: connection.token_type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
status: connection.status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
linkedUser: connection.id_linked_user, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
date: connection.created_at, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
connectionToken: connection.connection_token! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
linkedUser: connection.id_linked_user, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
date: connection.created_at, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
connectionToken: connection.connection_token!, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Remove non-null assertion operator. The non-null assertion ( - connectionToken: connection.connection_token!,
+ connectionToken: connection.connection_token ?? '', 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome[error] 56-56: Forbidden non-null assertion. (lint/style/noNonNullAssertion) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let link: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(config.DISTRIBUTION == 'selfhost' && config.REDIRECT_WEBHOOK_INGRESS) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
link = `${config.MAGIC_LINK_DOMAIN}/?uniqueLink=${uniqueLink}&redirectIngressUri=${config.REDIRECT_WEBHOOK_INGRESS}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}else{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
link = `${config.MAGIC_LINK_DOMAIN}/?uniqueLink=${uniqueLink}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (config.DISTRIBUTION == "selfhost" && config.REDIRECT_WEBHOOK_INGRESS) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
link = `${config.WEBAPP_URL}/magic-link?uniqueLink=${uniqueLink}&redirectIngressUri=${config.REDIRECT_WEBHOOK_INGRESS}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
link = `${config.WEBAPP_URL}/magic-link?uniqueLink=${uniqueLink}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+60
to
64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification URL parameters are not properly encoded in magic link generation The link generation logic needs improvement for security and reliability:
Recommended fixes:
link = `${config.WEBAPP_URL}/magic-link?uniqueLink=${encodeURIComponent(uniqueLink)}&redirectIngressUri=${encodeURIComponent(config.REDIRECT_WEBHOOK_INGRESS)}`; 🔗 Analysis chainVerify magic link URL construction across environments. The link generation logic looks correct, but please ensure:
Let me check for URL encoding and validation by searching for related utility functions or validation logic. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check for proper URL encoding and validation in the codebase
rg -A 2 "uniqueLink.*="
rg -A 2 "REDIRECT_WEBHOOK_INGRESS"
Length of output: 3999 Script: #!/bin/bash
# Search for URL encoding/validation utilities
rg -A 2 "encodeURI|encodeURIComponent"
# Check magic link store implementation
ast-grep --pattern 'const useMagicLinkStore = $$$'
Length of output: 14886 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="hidden h-full flex-1 flex-col space-y-8 md:flex"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -71,68 +72,100 @@ export default function ConnectionTable() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardTitle className="text-lg font-medium">Linked</CardTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CardHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="text-2xl font-bold">{linkedConnections("valid")?.length}</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="text-2xl font-bold"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{linkedConnections("valid")?.length} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CardContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Card> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Card> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardTitle className="text-lg font-medium">Incomplete Link</CardTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardTitle className="text-lg font-medium"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Incomplete Link | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CardTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CardHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="text-2xl font-bold">{linkedConnections("1")?.length}</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="text-2xl font-bold"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{linkedConnections("1")?.length} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CardContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Card> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Card> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardTitle className="text-lg font-medium">Relink Needed</CardTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardTitle className="text-lg font-medium"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Relink Needed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CardTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CardHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CardContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="text-2xl font-bold">{linkedConnections("2")?.length}</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="text-2xl font-bold"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{linkedConnections("2")?.length} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CardContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Card> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{isGenerated ? <Dialog open={isGenerated} onOpenChange={setIsGenerated}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogTrigger asChild> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button variant="outline" className="" onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
posthog?.capture("add_new_connection_button_clicked", { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
id_project: idProject, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mode: config.DISTRIBUTION | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<PlusCircledIcon className="mr-2 h-4 w-4" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Add New Connection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogTrigger> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogContent className="sm:w-[450px]"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogTitle>Share this magic link with your customers</DialogTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Once they finish the oAuth flow, a new connection would be enabled. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="grid gap-4 py-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="grid grid-cols-4 items-center gap-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CopyLinkInput /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button variant="outline" size="sm" className="h-7 gap-1" type="submit" onClick={() => window.open(link, '_blank')}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="mr-2">Open Link</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 2C2.44772 2 2 2.44772 2 3V12C2 12.5523 2.44772 13 3 13H12C12.5523 13 13 12.5523 13 12V8.5C13 8.22386 12.7761 8 12.5 8C12.2239 8 12 8.22386 12 8.5V12H3V3L6.5 3C6.77614 3 7 2.77614 7 2.5C7 2.22386 6.77614 2 6.5 2H3ZM12.8536 2.14645C12.9015 2.19439 12.9377 2.24964 12.9621 2.30861C12.9861 2.36669 12.9996 2.4303 13 2.497L13 2.5V2.50049V5.5C13 5.77614 12.7761 6 12.5 6C12.2239 6 12 5.77614 12 5.5V3.70711L6.85355 8.85355C6.65829 9.04882 6.34171 9.04882 6.14645 8.85355C5.95118 8.65829 5.95118 8.34171 6.14645 8.14645L11.2929 3H9.5C9.22386 3 9 2.77614 9 2.5C9 2.22386 9.22386 2 9.5 2H12.4999H12.5C12.5678 2 12.6324 2.01349 12.6914 2.03794C12.7504 2.06234 12.8056 2.09851 12.8536 2.14645Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{isGenerated ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Dialog open={isGenerated} onOpenChange={setIsGenerated}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogTrigger asChild> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant="outline" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
posthog?.capture("add_new_connection_button_clicked", { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
id_project: idProject, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mode: config.DISTRIBUTION, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<PlusCircledIcon className="mr-2 h-4 w-4" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Add New Connection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Dialog> : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogTrigger> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogContent className="sm:w-[450px]"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Share this magic link with your customers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Once they finish the oAuth flow, a new connection would be | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
enabled. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="grid gap-4 py-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="grid grid-cols-4 items-center gap-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CopyLinkInput /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant="outline" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="h-7 gap-1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type="submit" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => window.open(link, "_blank")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="mr-2">Open Link</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<svg | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
width="15" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
height="15" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
viewBox="0 0 15 15" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fill="none" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<path | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
d="M3 2C2.44772 2 2 2.44772 2 3V12C2 12.5523 2.44772 13 3 13H12C12.5523 13 13 12.5523 13 12V8.5C13 8.22386 12.7761 8 12.5 8C12.2239 8 12 8.22386 12 8.5V12H3V3L6.5 3C6.77614 3 7 2.77614 7 2.5C7 2.22386 6.77614 2 6.5 2H3ZM12.8536 2.14645C12.9015 2.19439 12.9377 2.24964 12.9621 2.30861C12.9861 2.36669 12.9996 2.4303 13 2.497L13 2.5V2.50049V5.5C13 5.77614 12.7761 6 12.5 6C12.2239 6 12 5.77614 12 5.5V3.70711L6.85355 8.85355C6.65829 9.04882 6.34171 9.04882 6.14645 8.85355C5.95118 8.65829 5.95118 8.34171 6.14645 8.14645L11.2929 3H9.5C9.22386 3 9 2.77614 9 2.5C9 2.22386 9.22386 2 9.5 2H12.4999H12.5C12.5678 2 12.6324 2.01349 12.6914 2.03794C12.7504 2.06234 12.8056 2.09851 12.8536 2.14645Z" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fill="currentColor" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fillRule="evenodd" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
clipRule="evenodd" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
></path> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</svg> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+146
to
+159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add accessibility attributes to SVG icon. The SVG icon needs proper accessibility attributes for screen readers. <svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
+ role="img"
+ aria-label="Open in new tab"
> 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome[error] 146-152: Alternative text title element cannot be empty For accessibility purposes, SVGs should have an alternative text, provided via title element. If the svg element has role="img", you should add the aria-label or aria-labelledby attribute. (lint/a11y/noSvgWithoutTitle) [error] 153-158: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing. Unsafe fix: Use a SelfClosingElement instead (lint/style/useSelfClosingElements) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Dialog> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<AddConnectionButton setIsGenerated={setIsGenerated} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Suspense> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ts && <DataTable data={ts} columns={columns}/>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Suspense> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Suspense>{ts && <DataTable data={ts} columns={columns} />}</Suspense> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,23 +1,23 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'use client' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"use client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Button } from '@/components/ui/button'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Input } from '@/components/ui/input'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import useMagicLinkStore from '@/state/magicLinkStore'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import config from '@/lib/config'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { LoadingSpinner } from './LoadingSpinner'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { toast } from 'sonner'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Button } from "@/components/ui/button"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Input } from "@/components/ui/input"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import useMagicLinkStore from "@/state/magicLinkStore"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import config from "@/lib/config"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useState } from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { LoadingSpinner } from "./LoadingSpinner"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { toast } from "sonner"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const CopyLinkInput = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [copied, setCopied] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const {uniqueLink} = useMagicLinkStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { uniqueLink } = useMagicLinkStore(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let link: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(config.DISTRIBUTION == 'selfhost' && config.REDIRECT_WEBHOOK_INGRESS) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
link = `${config.MAGIC_LINK_DOMAIN}/?uniqueLink=${uniqueLink}&redirectIngressUri=${config.REDIRECT_WEBHOOK_INGRESS}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}else{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
link = `${config.MAGIC_LINK_DOMAIN}/?uniqueLink=${uniqueLink}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (config.DISTRIBUTION == "selfhost" && config.REDIRECT_WEBHOOK_INGRESS) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
link = `${config.WEBAPP_URL}/magic-link?uniqueLink=${uniqueLink}&redirectIngressUri=${config.REDIRECT_WEBHOOK_INGRESS}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
link = `${config.WEBAPP_URL}/magic-link?uniqueLink=${uniqueLink}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+17
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance link construction with URL API and validation The current link construction could be improved for better security and reliability. Consider this safer implementation: - if (config.DISTRIBUTION == "selfhost" && config.REDIRECT_WEBHOOK_INGRESS) {
- link = `${config.WEBAPP_URL}/magic-link?uniqueLink=${uniqueLink}&redirectIngressUri=${config.REDIRECT_WEBHOOK_INGRESS}`;
- } else {
- link = `${config.WEBAPP_URL}/magic-link?uniqueLink=${uniqueLink}`;
- }
+ const url = new URL('/magic-link', config.WEBAPP_URL);
+ url.searchParams.set('uniqueLink', uniqueLink);
+ if (config.DISTRIBUTION === "selfhost" && config.REDIRECT_WEBHOOK_INGRESS) {
+ url.searchParams.set('redirectIngressUri', config.REDIRECT_WEBHOOK_INGRESS);
+ }
+ link = url.toString(); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const handleCopy = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -28,40 +28,56 @@ const CopyLinkInput = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
label: "Close", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick: () => console.log("Close"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setCopied(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.error('Failed to copy: ', err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.error("Failed to copy: ", err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
34
to
36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Improve error handling in copy function The error case should provide feedback to the user via toast notification. Consider this enhancement: } catch (err) {
- console.error("Failed to copy: ", err);
+ toast.error("Failed to copy magic link to clipboard", {
+ action: {
+ label: "Close",
+ onClick: () => console.log("Close"),
+ },
+ });
+ console.error("Clipboard write failed:", err);
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{uniqueLink !== 'https://' ? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Input | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
defaultValue={link} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
readOnly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="col-span-3 flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={handleCopy} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant="outline" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className=" text-white rounded-md" // Adjust the styling as needed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{copied ? 'Copied!' : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className='mr-1'>Copy</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.6788 2.95419C10.0435 2.53694 9.18829 2.54594 8.51194 3.00541C8.35757 3.11027 8.1921 3.27257 7.7651 3.69957L7.14638 4.31829C6.95112 4.51355 6.63454 4.51355 6.43928 4.31829C6.24401 4.12303 6.24401 3.80645 6.43928 3.61119L7.058 2.99247C7.0725 2.97797 7.08679 2.96366 7.1009 2.94955C7.47044 2.57991 7.70691 2.34336 7.95001 2.17822C8.94398 1.50299 10.2377 1.46813 11.2277 2.11832C11.4692 2.27689 11.7002 2.508 12.0515 2.85942C12.0662 2.8741 12.081 2.88898 12.0961 2.90408C12.1112 2.91917 12.1261 2.93405 12.1408 2.94871C12.4922 3.30001 12.7233 3.53102 12.8819 3.77248C13.5321 4.76252 13.4972 6.05623 12.822 7.0502C12.6568 7.2933 12.4203 7.52976 12.0507 7.89929C12.0366 7.9134 12.0222 7.92771 12.0077 7.94221L11.389 8.56093C11.1938 8.7562 10.8772 8.7562 10.6819 8.56093C10.4867 8.36567 10.4867 8.04909 10.6819 7.85383L11.3006 7.23511C11.7276 6.80811 11.8899 6.64264 11.9948 6.48827C12.4543 5.81192 12.4633 4.95675 12.046 4.32141C11.9513 4.17714 11.8009 4.02307 11.389 3.61119C10.9771 3.1993 10.8231 3.04893 10.6788 2.95419ZM4.31796 6.43961C4.51322 6.63487 4.51322 6.95146 4.31796 7.14672L3.69924 7.76544C3.27224 8.19244 3.10993 8.35791 3.00507 8.51227C2.54561 9.18863 2.53661 10.0438 2.95385 10.6791C3.0486 10.8234 3.19896 10.9775 3.61085 11.3894C4.02274 11.8012 4.17681 11.9516 4.32107 12.0464C4.95642 12.4636 5.81158 12.4546 6.48794 11.9951C6.6423 11.8903 6.80777 11.728 7.23477 11.301L7.85349 10.6823C8.04875 10.487 8.36533 10.487 8.5606 10.6823C8.75586 10.8775 8.75586 11.1941 8.5606 11.3894L7.94188 12.0081C7.92738 12.0226 7.91307 12.0369 7.89897 12.051C7.52943 12.4206 7.29296 12.6572 7.04986 12.8223C6.05589 13.4976 4.76219 13.5324 3.77214 12.8822C3.53068 12.7237 3.29967 12.4925 2.94837 12.1411C2.93371 12.1264 2.91883 12.1116 2.90374 12.0965C2.88865 12.0814 2.87377 12.0665 2.8591 12.0518C2.50766 11.7005 2.27656 11.4695 2.11799 11.2281C1.4678 10.238 1.50265 8.94432 2.17788 7.95035C2.34303 7.70724 2.57957 7.47077 2.94922 7.10124C2.96333 7.08713 2.97763 7.07283 2.99213 7.05833L3.61085 6.43961C3.80611 6.24435 4.12269 6.24435 4.31796 6.43961Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className='flex flex-row items-center justify-center'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<LoadingSpinner className='mr-2 h-4 w-4 animate-spin'/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{uniqueLink !== "https://" ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Input | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+41
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Enhance loading state feedback The loading state could be more informative to users. Consider adding a loading message: ) : (
<div className="flex flex-row items-center justify-center">
<LoadingSpinner className="mr-2 h-4 w-4 animate-spin" />
+ <span className="text-sm text-muted-foreground">Generating magic link...</span>
</div> Also applies to: 76-80 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
defaultValue={link} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
readOnly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="col-span-3 flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={handleCopy} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant="outline" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className=" text-white rounded-md" // Adjust the styling as needed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{copied ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"Copied!" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="mr-1">Copy</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<svg | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
width="15" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
height="15" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
viewBox="0 0 15 15" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fill="none" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<path | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
d="M10.6788 2.95419C10.0435 2.53694 9.18829 2.54594 8.51194 3.00541C8.35757 3.11027 8.1921 3.27257 7.7651 3.69957L7.14638 4.31829C6.95112 4.51355 6.63454 4.51355 6.43928 4.31829C6.24401 4.12303 6.24401 3.80645 6.43928 3.61119L7.058 2.99247C7.0725 2.97797 7.08679 2.96366 7.1009 2.94955C7.47044 2.57991 7.70691 2.34336 7.95001 2.17822C8.94398 1.50299 10.2377 1.46813 11.2277 2.11832C11.4692 2.27689 11.7002 2.508 12.0515 2.85942C12.0662 2.8741 12.081 2.88898 12.0961 2.90408C12.1112 2.91917 12.1261 2.93405 12.1408 2.94871C12.4922 3.30001 12.7233 3.53102 12.8819 3.77248C13.5321 4.76252 13.4972 6.05623 12.822 7.0502C12.6568 7.2933 12.4203 7.52976 12.0507 7.89929C12.0366 7.9134 12.0222 7.92771 12.0077 7.94221L11.389 8.56093C11.1938 8.7562 10.8772 8.7562 10.6819 8.56093C10.4867 8.36567 10.4867 8.04909 10.6819 7.85383L11.3006 7.23511C11.7276 6.80811 11.8899 6.64264 11.9948 6.48827C12.4543 5.81192 12.4633 4.95675 12.046 4.32141C11.9513 4.17714 11.8009 4.02307 11.389 3.61119C10.9771 3.1993 10.8231 3.04893 10.6788 2.95419ZM4.31796 6.43961C4.51322 6.63487 4.51322 6.95146 4.31796 7.14672L3.69924 7.76544C3.27224 8.19244 3.10993 8.35791 3.00507 8.51227C2.54561 9.18863 2.53661 10.0438 2.95385 10.6791C3.0486 10.8234 3.19896 10.9775 3.61085 11.3894C4.02274 11.8012 4.17681 11.9516 4.32107 12.0464C4.95642 12.4636 5.81158 12.4546 6.48794 11.9951C6.6423 11.8903 6.80777 11.728 7.23477 11.301L7.85349 10.6823C8.04875 10.487 8.36533 10.487 8.5606 10.6823C8.75586 10.8775 8.75586 11.1941 8.5606 11.3894L7.94188 12.0081C7.92738 12.0226 7.91307 12.0369 7.89897 12.051C7.52943 12.4206 7.29296 12.6572 7.04986 12.8223C6.05589 13.4976 4.76219 13.5324 3.77214 12.8822C3.53068 12.7237 3.29967 12.4925 2.94837 12.1411C2.93371 12.1264 2.91883 12.1116 2.90374 12.0965C2.88865 12.0814 2.87377 12.0665 2.8591 12.0518C2.50766 11.7005 2.27656 11.4695 2.11799 11.2281C1.4678 10.238 1.50265 8.94432 2.17788 7.95035C2.34303 7.70724 2.57957 7.47077 2.94922 7.10124C2.96333 7.08713 2.97763 7.07283 2.99213 7.05833L3.61085 6.43961C3.80611 6.24435 4.12269 6.24435 4.31796 6.43961Z" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fill="currentColor" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fillRule="evenodd" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
clipRule="evenodd" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
></path> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</svg> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+58
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Address SVG accessibility issues The copy icon SVG lacks proper accessibility attributes. Add proper accessibility attributes to the SVG: <svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
+ role="img"
+ aria-label="Copy link"
>
- <path
- d="M10.6788 2.95419C10.0435 2.53694 9.18829 2.54594 8.51194 3.00541C8.35757 3.11027 8.1921 3.27257 7.7651 3.69957L7.14638 4.31829C6.95112 4.51355 6.63454 4.51355 6.43928 4.31829C6.24401 4.12303 6.24401 3.80645 6.43928 3.61119L7.058 2.99247C7.0725 2.97797 7.08679 2.96366 7.1009 2.94955C7.47044 2.57991 7.70691 2.34336 7.95001 2.17822C8.94398 1.50299 10.2377 1.46813 11.2277 2.11832C11.4692 2.27689 11.7002 2.508 12.0515 2.85942C12.0662 2.8741 12.081 2.88898 12.0961 2.90408C12.1112 2.91917 12.1261 2.93405 12.1408 2.94871C12.4922 3.30001 12.7233 3.53102 12.8819 3.77248C13.5321 4.76252 13.4972 6.05623 12.822 7.0502C12.6568 7.2933 12.4203 7.52976 12.0507 7.89929C12.0366 7.9134 12.0222 7.92771 12.0077 7.94221L11.389 8.56093C11.1938 8.7562 10.8772 8.7562 10.6819 8.56093C10.4867 8.36567 10.4867 8.04909 10.6819 7.85383L11.3006 7.23511C11.7276 6.80811 11.8899 6.64264 11.9948 6.48827C12.4543 5.81192 12.4633 4.95675 12.046 4.32141C11.9513 4.17714 11.8009 4.02307 11.389 3.61119C10.9771 3.1993 10.8231 3.04893 10.6788 2.95419ZM4.31796 6.43961C4.51322 6.63487 4.51322 6.95146 4.31796 7.14672L3.69924 7.76544C3.27224 8.19244 3.10993 8.35791 3.00507 8.51227C2.54561 9.18863 2.53661 10.0438 2.95385 10.6791C3.0486 10.8234 3.19896 10.9775 3.61085 11.3894C4.02274 11.8012 4.17681 11.9516 4.32107 12.0464C4.95642 12.4636 5.81158 12.4546 6.48794 11.9951C6.6423 11.8903 6.80777 11.728 7.23477 11.301L7.85349 10.6823C8.04875 10.487 8.36533 10.487 8.5606 10.6823C8.75586 10.8775 8.75586 11.1941 8.5606 11.3894L7.94188 12.0081C7.92738 12.0226 7.91307 12.0369 7.89897 12.051C7.52943 12.4206 7.29296 12.6572 7.04986 12.8223C6.05589 13.4976 4.76219 13.5324 3.77214 12.8822C3.53068 12.7237 3.29967 12.4925 2.94837 12.1411C2.93371 12.1264 2.91883 12.1116 2.90374 12.0965C2.88865 12.0814 2.87377 12.0665 2.8591 12.0518C2.50766 11.7005 2.27656 11.4695 2.11799 11.2281C1.4678 10.238 1.50265 8.94432 2.17788 7.95035C2.34303 7.70724 2.57957 7.47077 2.94922 7.10124C2.96333 7.08713 2.97763 7.07283 2.99213 7.05833L3.61085 6.43961C3.80611 6.24435 4.12269 6.24435 4.31796 6.43961Z"
- fill="currentColor"
- fillRule="evenodd"
- clipRule="evenodd"
- ></path>
+ <path
+ d="M10.6788 2.95419C10.0435 2.53694 9.18829 2.54594 8.51194 3.00541C8.35757 3.11027 8.1921 3.27257 7.7651 3.69957L7.14638 4.31829C6.95112 4.51355 6.63454 4.51355 6.43928 4.31829C6.24401 4.12303 6.24401 3.80645 6.43928 3.61119L7.058 2.99247C7.0725 2.97797 7.08679 2.96366 7.1009 2.94955C7.47044 2.57991 7.70691 2.34336 7.95001 2.17822C8.94398 1.50299 10.2377 1.46813 11.2277 2.11832C11.4692 2.27689 11.7002 2.508 12.0515 2.85942C12.0662 2.8741 12.081 2.88898 12.0961 2.90408C12.1112 2.91917 12.1261 2.93405 12.1408 2.94871C12.4922 3.30001 12.7233 3.53102 12.8819 3.77248C13.5321 4.76252 13.4972 6.05623 12.822 7.0502C12.6568 7.2933 12.4203 7.52976 12.0507 7.89929C12.0366 7.9134 12.0222 7.92771 12.0077 7.94221L11.389 8.56093C11.1938 8.7562 10.8772 8.7562 10.6819 8.56093C10.4867 8.36567 10.4867 8.04909 10.6819 7.85383L11.3006 7.23511C11.7276 6.80811 11.8899 6.64264 11.9948 6.48827C12.4543 5.81192 12.4633 4.95675 12.046 4.32141C11.9513 4.17714 11.8009 4.02307 11.389 3.61119C10.9771 3.1993 10.8231 3.04893 10.6788 2.95419ZM4.31796 6.43961C4.51322 6.63487 4.51322 6.95146 4.31796 7.14672L3.69924 7.76544C3.27224 8.19244 3.10993 8.35791 3.00507 8.51227C2.54561 9.18863 2.53661 10.0438 2.95385 10.6791C3.0486 10.8234 3.19896 10.9775 3.61085 11.3894C4.02274 11.8012 4.17681 11.9516 4.32107 12.0464C4.95642 12.4636 5.81158 12.4546 6.48794 11.9951C6.6423 11.8903 6.80777 11.728 7.23477 11.301L7.85349 10.6823C8.04875 10.487 8.36533 10.487 8.5606 10.6823C8.75586 10.8775 8.75586 11.1941 8.5606 11.3894L7.94188 12.0081C7.92738 12.0226 7.91307 12.0369 7.89897 12.051C7.52943 12.4206 7.29296 12.6572 7.04986 12.8223C6.05589 13.4976 4.76219 13.5324 3.77214 12.8822C3.53068 12.7237 3.29967 12.4925 2.94837 12.1411C2.93371 12.1264 2.91883 12.1116 2.90374 12.0965C2.88865 12.0814 2.87377 12.0665 2.8591 12.0518C2.50766 11.7005 2.27656 11.4695 2.11799 11.2281C1.4678 10.238 1.50265 8.94432 2.17788 7.95035C2.34303 7.70724 2.57957 7.47077 2.94922 7.10124C2.96333 7.08713 2.97763 7.07283 2.99213 7.05833L3.61085 6.43961C3.80611 6.24435 4.12269 6.24435 4.31796 6.43961Z"
+ fill="currentColor"
+ fillRule="evenodd"
+ clipRule="evenodd"
+ /> 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome[error] 58-64: Alternative text title element cannot be empty For accessibility purposes, SVGs should have an alternative text, provided via title element. If the svg element has role="img", you should add the aria-label or aria-labelledby attribute. (lint/a11y/noSvgWithoutTitle) [error] 65-70: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing. Unsafe fix: Use a SelfClosingElement instead (lint/style/useSelfClosingElements) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex flex-row items-center justify-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<LoadingSpinner className="mr-2 h-4 w-4 animate-spin" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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.
Improve error handling for connection failures.
The current error handling only logs to console, which is insufficient for production. Consider displaying an error message to users and implementing proper error recovery.
📝 Committable suggestion