diff --git a/README.md b/README.md index c9d2de0e..745110ce 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ This readme file is used mostly to document project dependencies and conventions - reactflow - js-file-download - use-debounce + - qrcode.react - html-to-image - @tanstack/react-table - @uiw/react-codemirror diff --git a/rsconcept/frontend/package-lock.json b/rsconcept/frontend/package-lock.json index e47b08bd..9b260c91 100644 --- a/rsconcept/frontend/package-lock.json +++ b/rsconcept/frontend/package-lock.json @@ -17,6 +17,7 @@ "clsx": "^2.1.1", "html-to-image": "^1.11.11", "js-file-download": "^0.4.12", + "qrcode.react": "^4.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-error-boundary": "^4.1.2", @@ -8666,6 +8667,15 @@ ], "license": "MIT" }, + "node_modules/qrcode.react": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz", + "integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", diff --git a/rsconcept/frontend/package.json b/rsconcept/frontend/package.json index e894aa38..2e9f60fa 100644 --- a/rsconcept/frontend/package.json +++ b/rsconcept/frontend/package.json @@ -21,6 +21,7 @@ "clsx": "^2.1.1", "html-to-image": "^1.11.11", "js-file-download": "^0.4.12", + "qrcode.react": "^4.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-error-boundary": "^4.1.2", diff --git a/rsconcept/frontend/src/components/Icons.tsx b/rsconcept/frontend/src/components/Icons.tsx index 0d095018..d6f114ee 100644 --- a/rsconcept/frontend/src/components/Icons.tsx +++ b/rsconcept/frontend/src/components/Icons.tsx @@ -20,6 +20,7 @@ export { TbEye as IconShow } from 'react-icons/tb'; export { TbEyeX as IconHide } from 'react-icons/tb'; export { BiShareAlt as IconShare } from 'react-icons/bi'; export { LuFilter as IconFilter } from 'react-icons/lu'; +export { LuQrCode as IconQR } from 'react-icons/lu'; export { LuFilterX as IconFilterReset } from 'react-icons/lu'; export {BiDownArrowCircle as IconOpenList } from 'react-icons/bi'; export { LuTriangleAlert as IconAlert } from 'react-icons/lu'; diff --git a/rsconcept/frontend/src/dialogs/DlgShowQR.tsx b/rsconcept/frontend/src/dialogs/DlgShowQR.tsx new file mode 100644 index 00000000..8a1b16ac --- /dev/null +++ b/rsconcept/frontend/src/dialogs/DlgShowQR.tsx @@ -0,0 +1,26 @@ +'use client'; + +import clsx from 'clsx'; +import { QRCodeSVG } from 'qrcode.react'; + +import Modal, { ModalProps } from '@/components/ui/Modal'; + +interface DlgShowQRProps extends Pick { + target: string; +} + +function DlgShowQR({ hideWindow, target }: DlgShowQRProps) { + return ( + +
+ +
+
+ ); +} + +export default DlgShowQR; diff --git a/rsconcept/frontend/src/pages/RSFormPage/MenuRSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/MenuRSTabs.tsx index d6c52bb2..eaefed7a 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/MenuRSTabs.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/MenuRSTabs.tsx @@ -19,6 +19,7 @@ import { IconNewVersion, IconOSS, IconOwner, + IconQR, IconReader, IconReplace, IconShare, @@ -85,6 +86,11 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) { controller.share(); } + function handleShowQR() { + schemaMenu.hide(); + controller.showQR(); + } + function handleCreateVersion() { schemaMenu.hide(); controller.createVersion(); @@ -155,10 +161,16 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) { onClick={handleShare} disabled={controller.schema?.access_policy !== AccessPolicy.PUBLIC} /> + } + onClick={handleShowQR} + /> {user ? ( } + icon={} disabled={model.isArchive} onClick={handleClone} /> diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx index 30909dea..4c3ad8d6 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx @@ -21,6 +21,7 @@ import DlgEditVersions from '@/dialogs/DlgEditVersions'; import DlgEditWordForms from '@/dialogs/DlgEditWordForms'; import DlgInlineSynthesis from '@/dialogs/DlgInlineSynthesis'; import DlgRenameCst from '@/dialogs/DlgRenameCst'; +import DlgShowQR from '@/dialogs/DlgShowQR'; import DlgShowTypeGraph from '@/dialogs/DlgShowTypeGraph'; import DlgSubstituteCst from '@/dialogs/DlgSubstituteCst'; import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm'; @@ -108,6 +109,7 @@ export interface IRSEditContext extends ILibraryItemEditor { substitute: () => void; showTypeGraph: () => void; + showQR: () => void; } const RSEditContext = createContext(null); @@ -172,6 +174,7 @@ export const RSEditState = ({ const [showEditVersions, setShowEditVersions] = useState(false); const [showInlineSynthesis, setShowInlineSynthesis] = useState(false); const [showTypeGraph, setShowTypeGraph] = useState(false); + const [showQR, setShowQR] = useState(false); const [createInitialData, setCreateInitialData] = useState(); const [showCreateCst, setShowCreateCst] = useState(false); @@ -627,6 +630,11 @@ export const RSEditState = ({ [model] ); + function generateQR(): string { + const currentRef = window.location.href; + return currentRef.includes('?') ? currentRef + '&qr' : currentRef + '?qr'; + } + return ( setShowTypeGraph(true) + showTypeGraph: () => setShowTypeGraph(true), + showQR: () => setShowQR(true) }} > {model.schema ? ( <> + {showQR ? setShowQR(false)} target={generateQR()} /> : null} {showUpload ? setShowUpload(false)} /> : null} {showClone ? (