Skip to content

Commit

Permalink
refactor: use TextCopyToClipboard component
Browse files Browse the repository at this point in the history
  • Loading branch information
tonai committed Feb 20, 2025
1 parent aa97f27 commit d815925
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 189 deletions.
65 changes: 32 additions & 33 deletions assets/components/Utils/SnackbarMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,49 @@
import { fr } from "@codegouvfr/react-dsfr";
import Button from "@codegouvfr/react-dsfr/Button";
import { Slide, SlideProps, Snackbar } from "@mui/material";
import { FC, memo } from "react";
import { Slide, SlideProps, Snackbar, SnackbarCloseReason } from "@mui/material";
import { FC, memo, useEffect, useState } from "react";

import { useSnackbarStore } from "../../stores/SnackbarStore";
import Alert from "@codegouvfr/react-dsfr/Alert";

function SlideTransition(props: SlideProps) {
return <Slide {...props} direction="up" />;
}

const SnackbarMessage: FC = () => {
const message = useSnackbarStore((state) => state.message);
const messageUuid = useSnackbarStore((state) => state.messageUuid);
const clearMessage = useSnackbarStore((state) => state.clearMessage);
const [open, setOpen] = useState(false);

const handleClose = (_: React.SyntheticEvent | Event, reason?: string) => {
if (reason === "clickaway") {
return;
useEffect(() => {
if (message?.id) {
setOpen(true);
} else {
setOpen(false);
}
}, [message?.id]);

clearMessage();
};
function handleClose(_, reason: SnackbarCloseReason) {
if (reason !== "clickaway") {
close();
}
}

function close() {
setOpen(false);
}

return (
message && (
<Snackbar key={`${message}_${messageUuid}`} open={!!message} autoHideDuration={6000} onClose={handleClose} TransitionComponent={SlideTransition}>
<div
style={{
border: "solid 1.5px",
borderColor: fr.colors.decisions.border.actionHigh.blueFrance.default,
backgroundColor: fr.colors.decisions.background.contrast.grey.default,
}}
className={fr.cx("fr-grid-row", "fr-grid-row--middle", "fr-px-2v", "fr-py-1v")}
>
<span
style={{
color: fr.colors.decisions.text.actionHigh.blueFrance.default,
}}
>
{message}
</span>
&nbsp;
<Button priority="tertiary no outline" title="Fermer" iconId="ri-close-line" onClick={handleClose} />
</div>
</Snackbar>
)
<Snackbar key={message?.id ?? "empty"} autoHideDuration={6000} open={open} TransitionComponent={SlideTransition} onClose={handleClose}>
{message ? (
<Alert
closable
description={message.description}
isClosed={false}
severity={message.severity ?? "success"}
title={message.title}
onClose={close}
style={{ backgroundColor: "var(--background-default-grey)" }}
/>
) : undefined}
</Snackbar>
);
};

Expand Down
51 changes: 25 additions & 26 deletions assets/components/Utils/TextCopyToClipboard.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,42 @@
import { fr, type FrCxArg } from "@codegouvfr/react-dsfr";
import { fr } from "@codegouvfr/react-dsfr";
import Button from "@codegouvfr/react-dsfr/Button";
import { FC, memo } from "react";
import { FC, memo, ReactNode } from "react";
import { symToStr } from "tsafe/symToStr";
import { tss } from "tss-react";

import { useTranslation } from "../../i18n/i18n";
import { useSnackbarStore } from "../../stores/SnackbarStore";
import { useCopyToClipboard } from "@/hooks/useCopyToClipboard";
import { useTranslation } from "@/i18n";

type TextCopyToClipboardProps = {
label?: string;
text: string;
className?: FrCxArg;
title?: string;
className?: string;
successMessage?: string;
disabled?: boolean;
children?: ReactNode;
};

const TextCopyToClipboard: FC<TextCopyToClipboardProps> = ({ text, successMessage, className, disabled = false }) => {
const TextCopyToClipboard: FC<TextCopyToClipboardProps> = (props) => {
const { t: tCommon } = useTranslation("Common");
const { children, label, text, title = tCommon("copy_to_clipboard"), successMessage, className, disabled = false } = props;
const copy = useCopyToClipboard();
const { classes, cx } = useStyles({ disabled });

const setMessage = useSnackbarStore((state) => state.setMessage);

const copyToClipboard = async () => {
await navigator.clipboard.writeText(text);

setMessage(successMessage ?? tCommon("url_copied"));
};

const { classes, cx } = useStyles({
disabled,
});
function copyToClipboard() {
copy(text, successMessage);
}

return (
<div className={cx(classes.root, className)}>
{label && (
<span>
<strong>{label}</strong> :
</span>
)}
<span className={classes.textBox}>{text}</span>
<Button
iconId="ri-file-copy-2-line"
priority="tertiary no outline"
title={tCommon("copy_to_clipboard")}
onClick={copyToClipboard}
disabled={disabled}
/>
<Button iconId="ri-file-copy-line" priority="tertiary no outline" title={title} onClick={copyToClipboard} disabled={disabled} />
{children}
</div>
);
};
Expand All @@ -58,17 +56,18 @@ const useStyles = tss
position: "relative",
gap: fr.spacing("2v"),
flexWrap: "nowrap",
alignItems: "center",
},
textBox: {
display: "block",
width: "100%",
flex: "1",
borderRadius: "0.25rem 0.25rem 0 0",
color: disabled ? fr.colors.decisions.text.disabled.grey.default : fr.colors.decisions.text.default.grey.default,
userSelect: disabled ? "none" : "auto",
cursor: disabled ? "not-allowed" : "auto",
overflow: disabled ? "hidden" : "auto",
backgroundColor: disabled ? fr.colors.decisions.background.disabled.grey.default : fr.colors.decisions.background.alt.blueFrance.default,
padding: fr.spacing("2v"),
whiteSpace: "nowrap",
whiteSpace: "pre",
},
}));
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import OfferingStatusBadge from "../../../../../components/Utils/Badges/Offering
import Wait from "../../../../../components/Utils/Wait";
import RQKeys from "../../../../../modules/entrepot/RQKeys";
import { routes } from "../../../../../router/router";
import { useSnackbarStore } from "../../../../../stores/SnackbarStore";
import { offeringTypeDisplayName } from "../../../../../utils";
import api from "../../../../api";
import ServiceDesc from "./ServiceDesc";
import ListItem from "../ListItem";
import { useCopyToClipboard } from "@/hooks/useCopyToClipboard";
import { useSnackbarStore } from "@/stores/SnackbarStore";

type ServicesListItemProps = {
service: Service;
Expand All @@ -25,8 +26,8 @@ type ServicesListItemProps = {
};
const ServicesListItem: FC<ServicesListItemProps> = ({ service, datasheetName, datastoreId }) => {
const queryClient = useQueryClient();

const setMessage = useSnackbarStore((state) => state.setMessage);
const copy = useCopyToClipboard();

const unpublishServiceConfirmModal = createModal({
id: `unpublish-service-confirm-modal-${service._id}`,
Expand Down Expand Up @@ -73,9 +74,7 @@ const ServicesListItem: FC<ServicesListItemProps> = ({ service, datasheetName, d
if (!service.share_url) {
setMessage("URL de diffusion indisponible");
} else {
await navigator.clipboard.writeText(service.share_url);

setMessage("URL copiée");
copy(service.share_url);
}
},
},
Expand Down
83 changes: 21 additions & 62 deletions assets/entrepot/pages/users/keys/UserKeysListTab/UserKeyLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { PermissionWithOfferingsDetailsResponseDto } from "../../../../../@types
import { fr } from "@codegouvfr/react-dsfr";
import Button from "@codegouvfr/react-dsfr/Button";
import { useTranslation } from "../../../../../i18n/i18n";
import { useSnackbarStore } from "../../../../../stores/SnackbarStore";
import TextCopyToClipboard from "@/components/Utils/TextCopyToClipboard";

type UserKeyLinkProps = {
permissionId: string;
Expand All @@ -17,8 +17,6 @@ type UserKeyLinkProps = {
const UserKeyLink: FC<UserKeyLinkProps> = ({ permissionId, hash, offeringId }) => {
const { t: tCommon } = useTranslation("Common");

const setMessage = useSnackbarStore((state) => state.setMessage);

const { data: permission } = useQuery<PermissionWithOfferingsDetailsResponseDto>({
queryKey: RQKeys.get_permission(permissionId),
queryFn: ({ signal }) => api.user.getPermission(permissionId, { signal }),
Expand All @@ -38,54 +36,26 @@ const UserKeyLink: FC<UserKeyLinkProps> = ({ permissionId, hash, offeringId }) =
return (
<div className={fr.cx("fr-mb-3v")}>
{wmtsCapabilitiesUrl && (
<>
<span className={fr.cx("fr-hint-text")}>WMTS</span>
<div className={fr.cx("fr-grid-row", "fr-grid-row--middle", "fr-mb-1v")}>
<input className={fr.cx("fr-input", "fr-col-6")} type="text" value={wmtsCapabilitiesUrl} readOnly />
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("copy")}
priority={"tertiary no outline"}
iconId={"ri-file-copy-2-line"}
onClick={async () => {
await navigator.clipboard.writeText(wmtsCapabilitiesUrl);
setMessage(tCommon("url_copied"));
}}
/>
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("new_window")}
priority="tertiary no outline"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: wmtsCapabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
</div>
</>
<TextCopyToClipboard className={fr.cx("fr-mb-4v")} label="WMTS" text={wmtsCapabilitiesUrl} successMessage={tCommon("url_copied")}>
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("new_window")}
priority="tertiary no outline"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: wmtsCapabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
</TextCopyToClipboard>
)}
{tmsCapabilitiesUrl && (
<>
<span className={fr.cx("fr-hint-text")}>TMS</span>
<div className={fr.cx("fr-grid-row", "fr-grid-row--middle")}>
<input className={fr.cx("fr-input", "fr-col-6")} type="text" value={tmsCapabilitiesUrl} readOnly />
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("copy")}
priority={"tertiary no outline"}
iconId={"ri-file-copy-2-line"}
onClick={async () => {
await navigator.clipboard.writeText(tmsCapabilitiesUrl);
setMessage(tCommon("url_copied"));
}}
/>
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("new_window")}
priority="tertiary no outline"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: tmsCapabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
</div>
</>
<TextCopyToClipboard label="TMS" text={tmsCapabilitiesUrl} successMessage={tCommon("url_copied")}>
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("new_window")}
priority="tertiary no outline"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: tmsCapabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
</TextCopyToClipboard>
)}
</div>
);
Expand All @@ -102,26 +72,15 @@ const UserKeyLink: FC<UserKeyLinkProps> = ({ permissionId, hash, offeringId }) =
const capabilitiesUrl = `${rootUrl}?service=${service}&version=${version}&request=GetCapabilities&apikey=${hash}`;

return (
<div className={fr.cx("fr-grid-row", "fr-grid-row--middle", "fr-mt-2v")}>
<input className={fr.cx("fr-input", "fr-col-6")} type="text" value={capabilitiesUrl} readOnly />
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("copy")}
priority={"tertiary no outline"}
iconId={"ri-file-copy-2-line"}
onClick={async () => {
await navigator.clipboard.writeText(capabilitiesUrl);
setMessage(tCommon("url_copied"));
}}
/>
<TextCopyToClipboard className={fr.cx("fr-mb-4v")} text={capabilitiesUrl} successMessage={tCommon("url_copied")}>
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("new_window")}
priority="tertiary no outline"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: capabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
</div>
</TextCopyToClipboard>
);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { Translations } from "../../../../../i18n/types";
const { i18n } = declareComponentKeys<
| "no_keys"
| "no_permission_warning"
| { K: "hash_value"; P: { value?: string }; R: string }
| "hash_value"
| "unavailable"
| "hash_value_copied"
| "services"
| "no_services"
Expand All @@ -19,7 +20,8 @@ export type I18n = typeof i18n;
export const UserKeysListTabFrTranslations: Translations<"fr">["UserKeysListTab"] = {
no_keys: "Vous n'avez aucune clé d’accès",
no_permission_warning: "Vous n'avez aucune permission, il n'est pas possible d’ajouter une clé",
hash_value: ({ value }) => `Valeur du hash : ${value ?? "indisponible"}`,
hash_value: "Valeur du hash",
unavailable: "Indisponible",
hash_value_copied: "Valeur du hash copiée",
services: "Services accessibles",
no_services: "Cette clé n'a accès à aucun service",
Expand All @@ -32,7 +34,8 @@ export const UserKeysListTabFrTranslations: Translations<"fr">["UserKeysListTab"
export const UserKeysListTabEnTranslations: Translations<"en">["UserKeysListTab"] = {
no_keys: "You don't have any access keys",
no_permission_warning: "You have no permissions, it is not possible to add a key",
hash_value: ({ value }) => `Hash value : ${value}`,
hash_value: "Hash value",
unavailable: "Unavailable",
hash_value_copied: "Hash value copied",
services: "Accessible services",
no_services: "This key does not have access to any services",
Expand Down
Loading

0 comments on commit d815925

Please sign in to comment.