Skip to content
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

Merged
merged 2 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
614 changes: 614 additions & 0 deletions apps/webapp/src/app/(Magic-Link)/magic-link/page.tsx

Large diffs are not rendered by default.

195 changes: 114 additions & 81 deletions apps/webapp/src/components/Connection/ConnectionTable.tsx
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";
Expand All @@ -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);
}

Comment on lines 43 to +44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

  if (error) {
-   console.log("error connections..");
+   return (
+     <div className="text-destructive">
+       Failed to load connections. Please try again later.
+     </div>
+   );
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log("error connections..");
}
const linkedConnections = (filter: string) => connections?.filter((connection) => connection.status == filter);
}
if (error) {
return (
<div className="text-destructive">
Failed to load connections. Please try again later.
</div>
);
}

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!,
}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove non-null assertion operator.

The non-null assertion (!) should be replaced with proper type checking to ensure type safety.

-   connectionToken: connection.connection_token!,
+   connectionToken: connection.connection_token ?? '',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
connectionToken: connection.connection_token!,
connectionToken: connection.connection_token ?? '',
🧰 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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:

  • The uniqueLink parameter is used directly in the URL without encoding, which could lead to issues with special characters
  • The redirectIngressUri from config is also used without encoding
  • The magic link store shows uniqueLink is initialized as "https://" which suggests it's a full URL that definitely needs encoding

Recommended fixes:

  • Use encodeURIComponent() for both URL parameters:
link = `${config.WEBAPP_URL}/magic-link?uniqueLink=${encodeURIComponent(uniqueLink)}&redirectIngressUri=${encodeURIComponent(config.REDIRECT_WEBHOOK_INGRESS)}`;
🔗 Analysis chain

Verify magic link URL construction across environments.

The link generation logic looks correct, but please ensure:

  1. The uniqueLink parameter is properly encoded
  2. The redirectIngressUri is properly validated for self-hosted environments

Let me check for URL encoding and validation by searching for related utility functions or validation logic.

🏁 Scripts executed

The 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">
Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<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>
<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"
>
<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>
🧰 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>
</>
)
}
);
}
92 changes: 54 additions & 38 deletions apps/webapp/src/components/Connection/CopyLinkInput.tsx
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
Copy link
Contributor

Choose a reason for hiding this comment

The 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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();


const handleCopy = async () => {
Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (err) {
console.error('Failed to copy: ', err);
console.error("Failed to copy: ", err);
}
} catch (err) {
toast.error("Failed to copy magic link to clipboard", {
action: {
label: "Close",
onClick: () => console.log("Close"),
},
});
console.error("Clipboard write failed:", err);
}


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
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<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>
<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"
/>
</svg>
🧰 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>
}
)}
</>
);
};
Expand Down
Loading
Loading