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

Create reusable CopyButton component #9498

Open
wants to merge 36 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e836004
refactor all copy functionalities
rajku-dev Dec 19, 2024
59b9bfb
Update src/Utils/utils.ts
rajku-dev Dec 19, 2024
029b390
create CopyButton component
rajku-dev Dec 19, 2024
56d858a
use CopyButton component across
rajku-dev Dec 19, 2024
ec05b19
disable serial copyBtn
rajku-dev Dec 19, 2024
2c58284
revert tooltip class
rajku-dev Dec 19, 2024
68da53b
use cva instead
rajku-dev Dec 19, 2024
ae2426c
Update src/components/Licenses/SBOMViewer.tsx
rajku-dev Dec 20, 2024
4f383e6
remove console logs
rajku-dev Dec 20, 2024
c14a04d
drop error handling
rajku-dev Dec 20, 2024
1588cff
Update src/components/Shifting/ShiftDetails.tsx
rajku-dev Dec 20, 2024
1ccce49
Update src/components/Assets/AssetWarrantyCard.tsx
rajku-dev Dec 20, 2024
ae494c0
fix lint
rajku-dev Dec 20, 2024
12a262a
Merge branch 'develop' into issue/9494/revamp-clipboard
rajku-dev Dec 20, 2024
d10c5be
Merge branch 'develop' into issue/9494/revamp-clipboard
nihal467 Dec 20, 2024
ba35911
Merge branch 'develop' into issue/9494/revamp-clipboard
rajku-dev Dec 21, 2024
bc9a831
add CopyButton in DoctorVideoSlideover
rajku-dev Dec 21, 2024
5f4dd02
Merge branch 'issue/9494/revamp-clipboard' of https://github.com/rajk…
rajku-dev Dec 21, 2024
98f484e
add disable logic
rajku-dev Dec 21, 2024
50edfb9
fix disable logic
rajku-dev Dec 21, 2024
2c7ab78
fix lint
rajku-dev Dec 21, 2024
d9ed226
format with prettier
rajku-dev Dec 21, 2024
f84ba5b
Move content handling logic to CopyButton component
rajku-dev Dec 21, 2024
6c99e4e
format with prettier
rajku-dev Dec 21, 2024
6183a19
format ShiftDetails
rajku-dev Dec 21, 2024
ce9c578
add translations
rajku-dev Dec 21, 2024
70c0cc4
edit CopyButton
rajku-dev Dec 21, 2024
90eea5a
Merge branch 'develop' into issue/9494/revamp-clipboard
rajku-dev Dec 21, 2024
6f48ef6
Update src/components/Common/CopyButton.tsx
rajku-dev Dec 22, 2024
6ba3268
Update src/components/Common/CopyButton.tsx
rajku-dev Dec 22, 2024
df97cd0
fix lint
rajku-dev Dec 22, 2024
fe561d7
Merge branch 'develop' into issue/9494/revamp-clipboard
rajku-dev Dec 23, 2024
1bb5cac
Handle conditional rendering of CopyButton in AssetWaarantyCard | Fi…
rajku-dev Dec 23, 2024
541aa66
Merge branch 'issue/9494/revamp-clipboard' of https://github.com/rajk…
rajku-dev Dec 23, 2024
92a17cd
Merge branch 'develop' into issue/9494/revamp-clipboard
rajku-dev Dec 23, 2024
9c0a815
Update src/components/Common/CopyButton.tsx
rajku-dev Dec 24, 2024
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
1 change: 1 addition & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@
"copied_to_clipboard": "Copied to clipboard",
"copilot_thinking": "Copilot is thinking...",
"copy_phone_number": "Copy Phone Number",
"copy_to_clipboard": "Copy to clipboard",
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
"could_not_autofill": "We could not autofill any fields from what you said",
"countries_travelled": "Countries travelled",
"covid_19_cat_gov": "Covid_19 Clinical Category as per Govt. of Kerala guideline (A/B/C)",
Expand Down
39 changes: 7 additions & 32 deletions src/components/Assets/AssetWarrantyCard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { t } from "i18next";
import { useEffect, useState } from "react";
import CopyToClipboard from "react-copy-to-clipboard";

import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon";

import { AssetData } from "@/components/Assets/AssetTypes";
import CopyButton from "@/components/Common/CopyButton";

import { formatDate } from "@/Utils/utils";

Expand All @@ -19,17 +16,6 @@ export default function AssetWarrantyCard(props: { asset: AssetData }) {
Vendor: asset.vendor_name,
};

const [isCopied, setIsCopied] = useState(false);

useEffect(() => {
if (isCopied) {
const timeout = setTimeout(() => {
setIsCopied(false);
}, 2000);
return () => clearTimeout(timeout);
}
}, [isCopied]);

return (
<div className="warranty-card relative z-10 flex h-full w-full flex-col overflow-hidden p-6 text-white transition-all hover:scale-[1.01] hover:from-primary-600 hover:to-primary-700 rounded-xl xl:w-96">
<div className="mb-3 text-right text-lg font-bold italic">
Expand All @@ -44,23 +30,12 @@ export default function AssetWarrantyCard(props: { asset: AssetData }) {
</div>
<div className="flex items-center gap-2 font-semibold">
{details[key as keyof typeof details] || "--"}
{key === "Serial Number" && (
<button className="tooltip tooltip-bottom">
<CopyToClipboard
text={details[key as keyof typeof details] || "--"}
onCopy={() => setIsCopied(true)}
>
{isCopied ? (
<span className="text-sm text-white">
{t("copied_to_clipboard")}
</span>
) : (
<CareIcon icon="l-copy" className="text-lg" />
)}
</CopyToClipboard>
<span className="tooltip-text">Copy to clipboard</span>
</button>
)}
{key === "Serial Number" &&
details[key as keyof typeof details] && (
<CopyButton
content={details[key as keyof typeof details]}
/>
)}
</div>
</div>
))}
Expand Down
62 changes: 62 additions & 0 deletions src/components/Common/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { VariantProps } from "class-variance-authority";
import { useState } from "react";
import * as React from "react";
import { useTranslation } from "react-i18next";

import CareIcon from "@/CAREUI/icons/CareIcon";

import { Button, buttonVariants } from "@/components/ui/button";
import { TooltipComponent, TooltipProvider } from "@/components/ui/tooltip";

import { copyToClipboard } from "@/Utils/utils";
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved

export interface CopyButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
children?: React.ReactNode;
content: string | undefined;
tooltipContent?: string;
}
rithviknishad marked this conversation as resolved.
Show resolved Hide resolved

const CopyButton = ({
content,
tooltipContent,
children,
size,
}: CopyButtonProps) => {
const [isCopied, setIsCopied] = useState(false);
const { t } = useTranslation();

if (!content) return null;

return (
<TooltipProvider>
<TooltipComponent
content={
isCopied
? t("copied_to_clipboard")
: (tooltipContent ?? t("copy_to_clipboard"))
}
rajku-dev marked this conversation as resolved.
Show resolved Hide resolved
>
<Button
variant="link"
size={size}
onClick={() => {
copyToClipboard(content);
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2500);
}}
>
{children || (
<CareIcon
icon={isCopied ? "l-check" : "l-copy"}
className="text-lg"
/>
)}
</Button>
</TooltipComponent>
</TooltipProvider>
);
};

export default CopyButton;
124 changes: 54 additions & 70 deletions src/components/Facility/DoctorVideoSlideover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon";
import SlideOver from "@/CAREUI/interactive/SlideOver";
import Switch from "@/CAREUI/interactive/Switch";

import CopyButton from "@/components/Common/CopyButton";
import Loading from "@/components/Common/Loading";
import { SkillObjectModel } from "@/components/Users/models";
import { UserAssignedModel } from "@/components/Users/models";
Expand All @@ -18,7 +19,6 @@ import routes from "@/Utils/request/api";
import useTanStackQueryInstead from "@/Utils/request/useQuery";
import {
classNames,
copyToClipboard,
formatName,
isUserOnline,
relativeTime,
Expand Down Expand Up @@ -185,7 +185,11 @@ function UserListItem({ user }: { user: UserAnnotatedWithGroup }) {
e.stopPropagation();
if (!user.alt_phone_number) return;
const phoneNumber = user.alt_phone_number?.replace(/\D+/g, "");
const message = `${courtesyTitle(user)} ${formatName(user)}, I have a query regarding a patient.\n\nPatient Link: ${window.location.href}`;
const message = `${courtesyTitle(user)} ${formatName(
user,
)}, I have a query regarding a patient.\n\nPatient Link: ${
window.location.href
}`;
const encodedMessage = encodeURIComponent(message);
const whatsappAppURL = `whatsapp://send?phone=${phoneNumber}&text=${encodedMessage}`;
const whatsappWebURL = `https://wa.me/${phoneNumber}?text=${encodedMessage}`;
Expand Down Expand Up @@ -241,7 +245,6 @@ function UserListItem({ user }: { user: UserAnnotatedWithGroup }) {
}

const { t } = useTranslation();
const [copied, setCopied] = useState(false);

return (
<div
Expand All @@ -252,78 +255,59 @@ function UserListItem({ user }: { user: UserAnnotatedWithGroup }) {
: "pointer-events-none cursor-not-allowed bg-secondary-400",
)}
>
<a className="flex" onClick={connectOnWhatsApp}>
<div className="flex flex-none items-center justify-center sm:h-6 sm:w-6 md:h-10 md:w-10">
{
// Show online icon based on last_login
user.last_login && isUserOnline(user) ? (
<>
<CareIcon icon={icon} className="text-xl text-green-600" />
<span
className="relative top-2 h-3 w-3 rounded-full bg-primary-500"
aria-label="Online"
/>
</>
) : (
<CareIcon icon={icon} className="text-2xl text-secondary-600" />
)
}
<div className="flex flex-none items-center justify-center sm:h-6 sm:w-6 md:h-10 md:w-10">
{
// Show online icon based on last_login
user.last_login && isUserOnline(user) ? (
<>
<CareIcon icon={icon} className="text-xl text-green-600" />
<span
className="relative top-2 h-3 w-3 rounded-full bg-primary-500"
aria-label="Online"
/>
</>
) : (
<CareIcon icon={icon} className="text-2xl text-secondary-600" />
)
}
</div>
<div className="ml-4 flex flex-auto flex-col gap-1">
<div className="flex justify-between gap-2 text-sm text-secondary-700">
<span>
<strong>{formatName(user)}</strong>
</span>
<DoctorConnectButtons
user={user}
connectOnWhatsApp={connectOnWhatsApp}
/>
</div>
<div className="ml-4 flex flex-auto flex-col gap-1">
<div className="flex justify-between gap-2 text-sm text-secondary-700">
<span>
<strong>{formatName(user)}</strong>
</span>
<DoctorConnectButtons
user={user}
connectOnWhatsApp={connectOnWhatsApp}
/>
</div>
{!!user.skills.length && (
<div className="mt-1 text-sm leading-5 text-secondary-900">
<div className="flex flex-wrap gap-2">
{user.skills?.map((skill: SkillObjectModel) => (
<span
key={skill.id}
className="flex items-center gap-2 rounded-full border-secondary-300 bg-secondary-200 px-3 text-xs text-secondary-900"
>
<p className="py-1.5">{skill.name}</p>
</span>
))}
</div>
</div>
)}
<div className="flex justify-between gap-2 text-sm text-secondary-500">
<div className="flex items-center gap-1">
<a
role="button"
href="#"
onClick={(e) => {
e.stopPropagation();
copyToClipboard(user?.alt_phone_number || "");
setCopied(true);
setTimeout(() => setCopied(false), 2500);
}}
>
<span className="tooltip" id="copy-phoneicon">
<span className="tooltip-text tooltip-top">
{t("copy_phone_number")}
</span>
<CareIcon
icon={copied ? "l-check" : "l-clipboard"}
id="copy-icon"
className="h-5 w-5"
/>
{!!user.skills.length && (
<div className="mt-1 text-sm leading-5 text-secondary-900">
<div className="flex flex-wrap gap-2">
{user.skills?.map((skill: SkillObjectModel) => (
<span
key={skill.id}
className="flex items-center gap-2 rounded-full border-secondary-300 bg-secondary-200 px-3 text-xs text-secondary-900"
>
<p className="py-1.5">{skill.name}</p>
</span>
</a>
<span>{user.alt_phone_number}</span>
</div>
<div className="text-sm text-secondary-500">
{user.last_login && <span>{relativeTime(user.last_login)}</span>}
))}
</div>
</div>
)}
<div className="flex justify-between gap-2 text-sm text-secondary-500">
<div className="flex items-center gap-1">
<CopyButton
content={user.alt_phone_number}
tooltipContent={t("copy_phone_number")}
/>
<span>{user.alt_phone_number}</span>
</div>
<div className="text-sm text-secondary-500">
{user.last_login && <span>{relativeTime(user.last_login)}</span>}
</div>
</div>
</a>
</div>
</div>
);
}
Expand Down
27 changes: 8 additions & 19 deletions src/components/Licenses/SBOMViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import dayjs from "dayjs";
import React, { useState } from "react";
import { CopyToClipboard } from "react-copy-to-clipboard";

import Card from "@/CAREUI/display/Card";
import CareIcon from "@/CAREUI/icons/CareIcon";

import CopyButton from "@/components/Common/CopyButton";
import beBomData from "@/components/Licenses/be-sbom.json";
import feBomData from "@/components/Licenses/fe-sbom.json";
import licenseUrls from "@/components/Licenses/licenseUrls.json";
Expand All @@ -13,6 +13,7 @@ const getLicenseUrl = (licenseId: string | undefined): string | null => {
if (!licenseId) return null;
return licenseUrls[licenseId as keyof typeof licenseUrls] || null;
};

interface CycloneDXExternalRef {
url?: string;
type?: string;
Expand Down Expand Up @@ -64,15 +65,9 @@ interface CycloneDXBOM {
}

const BOMDisplay: React.FC = () => {
const [copyStatus, setCopyStatus] = useState(false);
const [showExternalRefs, setShowExternalRefs] = useState<number | null>(null);
const [activeTab, setActiveTab] = useState<string>("bom");

const handleCopy = () => {
setCopyStatus(true);
setTimeout(() => setCopyStatus(false), 2000);
};

const bomData = (activeTab === "bom" ? feBomData : beBomData) as CycloneDXBOM;

return (
Expand Down Expand Up @@ -182,19 +177,13 @@ const BOMDisplay: React.FC = () => {
))}
</div>
<div className="mt-4">
<CopyToClipboard
text={JSON.stringify(bomData, null, 2)}
onCopy={handleCopy}
<CopyButton
content={JSON.stringify(bomData, null, 2)}
tooltipContent="Copy BOM JSON to clipboard"
variant="primary"
>
<button className="text-md hover:bg-primary-dark w-full rounded-md bg-primary px-4 py-2 text-white transition-all duration-300 focus:outline-none md:w-auto">
Copy BOM JSON
</button>
</CopyToClipboard>
{copyStatus && (
<span className="mt-2 block text-sm text-gray-600">
Copied to clipboard!
</span>
)}
Copy BOM JSON
</CopyButton>
</div>
</Card>
</div>
Expand Down
Loading
Loading