diff --git a/geneit_app/package-lock.json b/geneit_app/package-lock.json index ef5ee26..e179448 100644 --- a/geneit_app/package-lock.json +++ b/geneit_app/package-lock.json @@ -33,6 +33,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-easy-crop": "^5.0.7", + "react-qr-code": "^2.0.14", "react-router-dom": "^6.23.1", "react-zoom-pan-pinch": "^3.4.4", "svg2pdf.js": "^2.2.3", @@ -3483,6 +3484,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" + }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -3533,6 +3539,24 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/react-qr-code": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.14.tgz", + "integrity": "sha512-xvAUqmXzFzf7X6aQAAKb6T02YYk9grBBFeqpp1MiVhUAKG3Rg9+hFiOKRYg4+rWc2MiXNxkri0ulAJgS12xh7Q==", + "dependencies": { + "prop-types": "^15.8.1", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "*", + "react-native-svg": "*" + }, + "peerDependenciesMeta": { + "react-native-svg": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", diff --git a/geneit_app/package.json b/geneit_app/package.json index 4259423..2730de4 100644 --- a/geneit_app/package.json +++ b/geneit_app/package.json @@ -29,6 +29,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-easy-crop": "^5.0.7", + "react-qr-code": "^2.0.14", "react-router-dom": "^6.23.1", "react-zoom-pan-pinch": "^3.4.4", "svg2pdf.js": "^2.2.3", diff --git a/geneit_app/src/api/accommodations/AccommodationsCalendarURLApi.tsx b/geneit_app/src/api/accommodations/AccommodationsCalendarURLApi.tsx index 7a1d27d..b898904 100644 --- a/geneit_app/src/api/accommodations/AccommodationsCalendarURLApi.tsx +++ b/geneit_app/src/api/accommodations/AccommodationsCalendarURLApi.tsx @@ -33,4 +33,11 @@ export class AccommodationsCalendarURLApi { }) ).data; } + + /** + * Get accommodation calendar URL route + */ + static CalendarURL(c: AccommodationCalendarURL): string { + return `${APIClient.backendURL()}/acccommodations_calendar/${c.token}`; + } } diff --git a/geneit_app/src/dialogs/accommodations/InstallCalendarDialog.tsx b/geneit_app/src/dialogs/accommodations/InstallCalendarDialog.tsx new file mode 100644 index 0000000..bddd61d --- /dev/null +++ b/geneit_app/src/dialogs/accommodations/InstallCalendarDialog.tsx @@ -0,0 +1,76 @@ +import { + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + Button, + Typography, + FormControl, + IconButton, + InputAdornment, + InputLabel, + OutlinedInput, +} from "@mui/material"; +import { + AccommodationCalendarURL, + AccommodationsCalendarURLApi, +} from "../../api/accommodations/AccommodationsCalendarURLApi"; +import { VisibilityOff, Visibility } from "@mui/icons-material"; +import ContentCopyIcon from "@mui/icons-material/ContentCopy"; +import { CopyToClipboard } from "../../widgets/CopyToClipboard"; +import QRCode from "react-qr-code"; + +export function InstallCalendarDialog(p: { + cal?: AccommodationCalendarURL; + onClose: () => void; +}): React.ReactElement { + if (!p.cal) return <>; + + return ( + + Installation du calendrier + + + + Afin d'installer le calendrier {p.cal.name} sur votre + appareil, veuillez utiliser l'URL suivante : + +
+ + URL + + + + + + + + } + label="Password" + /> +
+ +
+
+
+
+ + + +
+ ); +} diff --git a/geneit_app/src/hooks/context_providers/accommodations/InstallCalendarDialogProvider.tsx b/geneit_app/src/hooks/context_providers/accommodations/InstallCalendarDialogProvider.tsx new file mode 100644 index 0000000..61d05cd --- /dev/null +++ b/geneit_app/src/hooks/context_providers/accommodations/InstallCalendarDialogProvider.tsx @@ -0,0 +1,44 @@ +import React, { PropsWithChildren } from "react"; +import { AccommodationCalendarURL } from "../../../api/accommodations/AccommodationsCalendarURLApi"; +import { InstallCalendarDialog } from "../../../dialogs/accommodations/InstallCalendarDialog"; + +type DialogContext = (cal: AccommodationCalendarURL) => Promise; + +const DialogContextK = React.createContext(null); + +export function InstallCalendarDialogProvider( + p: PropsWithChildren +): React.ReactElement { + const [cal, setCal] = React.useState(); + + const cb = React.useRef void)>(null); + + const handleClose = () => { + setCal(undefined); + + if (cb.current !== null) cb.current(); + cb.current = null; + }; + + const hook: DialogContext = (c) => { + setCal(c); + + return new Promise((res) => { + cb.current = res; + }); + }; + + return ( + <> + + {p.children} + + + {cal && } + + ); +} + +export function useInstallCalendarDialog(): DialogContext { + return React.useContext(DialogContextK)!; +} diff --git a/geneit_app/src/routes/family/accommodations/AccommodationsSettingsRoute.tsx b/geneit_app/src/routes/family/accommodations/AccommodationsSettingsRoute.tsx index dfb4495..534c51d 100644 --- a/geneit_app/src/routes/family/accommodations/AccommodationsSettingsRoute.tsx +++ b/geneit_app/src/routes/family/accommodations/AccommodationsSettingsRoute.tsx @@ -24,6 +24,8 @@ import { TimeWidget } from "../../../widgets/TimeWidget"; import { useAccommodations } from "../../../widgets/accommodations/BaseAccommodationsRoute"; import { useCreateAccommodationCalendarURL } from "../../../hooks/context_providers/accommodations/CreateAccommodationCalendarURLDialogProvider"; import { AccommodationsCalendarURLApi } from "../../../api/accommodations/AccommodationsCalendarURLApi"; +import { useInstallCalendarDialog } from "../../../hooks/context_providers/accommodations/InstallCalendarDialogProvider"; +import { InstallCalendarDialog } from "../../../dialogs/accommodations/InstallCalendarDialog"; export function AccommodationsSettingsRoute(): React.ReactElement { return ( @@ -224,6 +226,7 @@ function AccommodationsCalURLsCard(): React.ReactElement { const family = useFamily(); const createCalendarURLDialog = useCreateAccommodationCalendarURL(); + const calendarURLDialog = useInstallCalendarDialog(); const createCalendarURL = async () => { try { @@ -241,8 +244,8 @@ function AccommodationsCalURLsCard(): React.ReactElement { setSuccess("Le calendrier a été créé avec succès !"); // TODO : reload URLS list - // TODO : show QrCode dialog - console.log(cal); + + calendarURLDialog(cal); } catch (e) { console.error("Failed to create new accommodation calendar URL!", e); setError(`Échec de la création du calendrier! ${e}`); diff --git a/geneit_app/src/widgets/CopyToClipboard.tsx b/geneit_app/src/widgets/CopyToClipboard.tsx new file mode 100644 index 0000000..72ea019 --- /dev/null +++ b/geneit_app/src/widgets/CopyToClipboard.tsx @@ -0,0 +1,30 @@ +import { ButtonBase } from "@mui/material"; +import { PropsWithChildren } from "react"; +import { useSnackbar } from "../hooks/context_providers/SnackbarProvider"; + +export function CopyToClipboard( + p: PropsWithChildren<{ content: string }> +): React.ReactElement { + const snackbar = useSnackbar(); + + const copy = () => { + navigator.clipboard.writeText(p.content); + snackbar(`${p.content} copied to clipboard.`); + }; + + return ( + + {p.children} + + ); +} diff --git a/geneit_app/src/widgets/accommodations/BaseAccommodationsRoute.tsx b/geneit_app/src/widgets/accommodations/BaseAccommodationsRoute.tsx index 9516652..5e6b24b 100644 --- a/geneit_app/src/widgets/accommodations/BaseAccommodationsRoute.tsx +++ b/geneit_app/src/widgets/accommodations/BaseAccommodationsRoute.tsx @@ -8,6 +8,7 @@ import { CreateAccommodationCalendarURLDialogProvider } from "../../hooks/contex import { UpdateAccommodationDialogProvider } from "../../hooks/context_providers/accommodations/UpdateAccommodationDialogProvider"; import { AsyncWidget } from "../AsyncWidget"; import { useFamily } from "../BaseFamilyRoute"; +import { InstallCalendarDialogProvider } from "../../hooks/context_providers/accommodations/InstallCalendarDialogProvider"; interface AccommodationsContext { accommodations: AccommodationsList; @@ -63,7 +64,9 @@ export function BaseAccommodationsRoute(): React.ReactElement { > - + + +