From f1005609d1e1aaba2b9af7a74dee7f3a16795908 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Mon, 26 May 2025 23:09:33 +0200 Subject: [PATCH] feat: add joyride --- apps/nextjs/package.json | 1 + .../app/[locale]/manage/help/tours/page.tsx | 38 ++++++ .../nextjs/src/app/[locale]/manage/layout.tsx | 6 + packages/translation/src/lang/en.json | 14 ++- pnpm-lock.yaml | 111 ++++++++++++++++++ 5 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 apps/nextjs/src/app/[locale]/manage/help/tours/page.tsx diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index ca8bd011a..b46063512 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -83,6 +83,7 @@ "react": "19.2.3", "react-dom": "19.2.3", "react-error-boundary": "^6.0.0", + "react-joyride": "^2.9.3", "react-simple-code-editor": "^0.14.1", "sass": "^1.96.0", "superjson": "2.2.6", diff --git a/apps/nextjs/src/app/[locale]/manage/help/tours/page.tsx b/apps/nextjs/src/app/[locale]/manage/help/tours/page.tsx new file mode 100644 index 000000000..84def9b23 --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/help/tours/page.tsx @@ -0,0 +1,38 @@ +import { auth } from "@homarr/auth/next"; +import { getScopedI18n } from "@homarr/translation/server"; +import { createMetaTitle } from "~/metadata"; +import { ManageContainer } from "~/components/manage/manage-container"; +import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; +import { Title, SimpleGrid, Card } from "@mantine/core"; + +export async function generateMetadata() { + const session = await auth(); + if (!session?.user || !session.user.permissions.includes("admin")) { + return {}; + } + + const t = await getScopedI18n("management"); + + return { + title: createMetaTitle(t("metaTitle")), + }; +} + +export default async function ToursPage() { + const t = await getScopedI18n("tours"); + return ( + + + {t("page.title")} + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/apps/nextjs/src/app/[locale]/manage/layout.tsx b/apps/nextjs/src/app/[locale]/manage/layout.tsx index 32013221c..3ef31c032 100644 --- a/apps/nextjs/src/app/[locale]/manage/layout.tsx +++ b/apps/nextjs/src/app/[locale]/manage/layout.tsx @@ -23,6 +23,7 @@ import { IconUserFilled, IconUsers, IconUsersGroup, + IconMotorbikeFilled } from "@tabler/icons-react"; import { auth } from "@homarr/auth/next"; @@ -158,6 +159,11 @@ export default async function ManageLayout({ children }: PropsWithChildren) { label: t("items.help.label"), icon: IconHelpSquareRoundedFilled, items: [ + { + label: t("items.help.items.tours"), + icon: IconMotorbikeFilled, + href: "/manage/help/tours" + }, { label: t("items.help.items.documentation"), icon: IconBook2, diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 517ecfea1..4a74aed4b 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -3015,7 +3015,8 @@ "documentation": "Documentation", "submitIssue": "Submit an issue", "discord": "Community Discord", - "sourceCode": "Source Code" + "sourceCode": "Source Code", + "tours": "Interactive tours" } }, "about": "About" @@ -3977,6 +3978,12 @@ "label": "Edit" } }, + "help": { + "label": "Help", + "tours": { + "label": "Interactive tours" + } + }, "users": { "label": "Users", "create": { @@ -4531,5 +4538,10 @@ "error": "Error" } } + }, + "tours": { + "page": { + "title": "Interactive tours" + } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f774c9aaa..51305f816 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -313,6 +313,9 @@ importers: react-error-boundary: specifier: ^6.0.0 version: 6.0.0(react@19.2.3) + react-joyride: + specifier: ^2.9.3 + version: 2.9.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-simple-code-editor: specifier: ^0.14.1 version: 0.14.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -3269,6 +3272,12 @@ packages: '@formatjs/intl-localematcher@0.5.5': resolution: {integrity: sha512-t5tOGMgZ/i5+ALl2/offNqAQq/lfUnKLEw0mXQI4N4bqpedhrSE+fyKLpwnd22sK0dif6AV+ufQcTsKShB9J1g==} + '@gilbarbara/deep-equal@0.1.2': + resolution: {integrity: sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==} + + '@gilbarbara/deep-equal@0.3.1': + resolution: {integrity: sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw==} + '@gitbeaker/core@43.8.0': resolution: {integrity: sha512-H+LfKuf4dExBinb79c+CXViRBvTVQNf5BYLNSizm2SiqdED5JruhKX88payefleY0szp7G/mySlFSXPyGRH1dQ==} engines: {node: '>=18.20.0'} @@ -6185,6 +6194,9 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} + deep-diff@1.0.2: + resolution: {integrity: sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -7586,6 +7598,12 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} + is-lite@0.8.2: + resolution: {integrity: sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==} + + is-lite@1.2.1: + resolution: {integrity: sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw==} + is-lower-case@1.1.3: resolution: {integrity: sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==} @@ -8991,6 +9009,10 @@ packages: po-parser@2.0.0: resolution: {integrity: sha512-SZvoKi3PoI/hHa2V9je9CW7Xgxl4dvO74cvaa6tWShIHT51FkPxje6pt0gTJznJrU67ix91nDaQp2hUxkOYhKA==} + popper.js@1.16.1: + resolution: {integrity: sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==} + deprecated: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1 + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -9282,6 +9304,12 @@ packages: peerDependencies: react: '>=16.13.1' + react-floater@0.7.9: + resolution: {integrity: sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==} + peerDependencies: + react: 15 - 18 + react-dom: 15 - 18 + react-immutable-proptypes@2.2.0: resolution: {integrity: sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==} peerDependencies: @@ -9294,6 +9322,12 @@ packages: react: '>= 16.6' react-dom: '>= 16.6' + react-innertext@1.1.5: + resolution: {integrity: sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==} + peerDependencies: + '@types/react': '>=0.0.0 <=99' + react: '>=0.0.0 <=99' + react-inspector@6.0.2: resolution: {integrity: sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==} peerDependencies: @@ -9302,6 +9336,12 @@ packages: react-is@19.2.3: resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==} + react-joyride@2.9.3: + resolution: {integrity: sha512-1+Mg34XK5zaqJ63eeBhqdbk7dlGCFp36FXwsEvgpjqrtyywX2C6h9vr3jgxP0bGHCw8Ilsp/nRDzNVq6HJ3rNw==} + peerDependencies: + react: 15 - 18 + react-dom: 15 - 18 + react-markdown@10.1.0: resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: @@ -9685,6 +9725,12 @@ packages: resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} + scroll@3.0.1: + resolution: {integrity: sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==} + + scrollparent@2.1.0: + resolution: {integrity: sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA==} + secure-json-parse@4.0.0: resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==} @@ -10309,6 +10355,12 @@ packages: resolution: {integrity: sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==} engines: {node: '>= 0.4'} + tree-changes@0.11.3: + resolution: {integrity: sha512-r14mvDZ6tqz8PRQmlFKjhUVngu4VZ9d92ON3tp0EGpFBE6PAHOq8Bx8m8ahbNoGE3uI/npjYcJiqVydyOiYXag==} + + tree-changes@0.9.3: + resolution: {integrity: sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -12004,6 +12056,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@gilbarbara/deep-equal@0.1.2': {} + + '@gilbarbara/deep-equal@0.3.1': {} + '@gitbeaker/core@43.8.0': dependencies: '@gitbeaker/requester-utils': 43.8.0 @@ -15336,6 +15392,8 @@ snapshots: dependencies: mimic-response: 3.1.0 + deep-diff@1.0.2: {} + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -17034,6 +17092,10 @@ snapshots: is-interactive@1.0.0: {} + is-lite@0.8.2: {} + + is-lite@1.2.1: {} + is-lower-case@1.1.3: dependencies: lower-case: 1.1.4 @@ -18534,6 +18596,8 @@ snapshots: po-parser@2.0.0: {} + popper.js@1.16.1: {} + possible-typed-array-names@1.0.0: {} postcss-js@4.0.1(postcss@8.5.6): @@ -18887,6 +18951,16 @@ snapshots: '@babel/runtime': 7.28.4 react: 19.2.3 + react-floater@0.7.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + deepmerge: 4.3.1 + is-lite: 0.8.2 + popper.js: 1.16.1 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + tree-changes: 0.9.3 + react-immutable-proptypes@2.2.0(immutable@3.8.2): dependencies: immutable: 3.8.2 @@ -18898,12 +18972,35 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) + react-innertext@1.1.5(@types/react@19.2.7)(react@19.2.3): + dependencies: + '@types/react': 19.2.7 + react: 19.2.3 + react-inspector@6.0.2(react@19.2.3): dependencies: react: 19.2.3 react-is@19.2.3: {} + react-joyride@2.9.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@gilbarbara/deep-equal': 0.3.1 + deep-diff: 1.0.2 + deepmerge: 4.3.1 + is-lite: 1.2.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-floater: 0.7.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-innertext: 1.1.5(@types/react@19.2.7)(react@19.2.3) + react-is: 19.2.3 + scroll: 3.0.1 + scrollparent: 2.1.0 + tree-changes: 0.11.3 + type-fest: 4.41.0 + transitivePeerDependencies: + - '@types/react' + react-markdown@10.1.0(@types/react@19.2.7)(react@19.2.3): dependencies: '@types/hast': 3.0.4 @@ -19391,6 +19488,10 @@ snapshots: ajv-formats: 2.1.1(ajv@8.17.1) ajv-keywords: 5.1.0(ajv@8.17.1) + scroll@3.0.1: {} + + scrollparent@2.1.0: {} + secure-json-parse@4.0.0: {} semantic-release@25.0.2(typescript@5.9.3): @@ -20201,6 +20302,16 @@ snapshots: traverse@0.6.8: {} + tree-changes@0.11.3: + dependencies: + '@gilbarbara/deep-equal': 0.3.1 + is-lite: 1.2.1 + + tree-changes@0.9.3: + dependencies: + '@gilbarbara/deep-equal': 0.1.2 + is-lite: 0.8.2 + tree-kill@1.2.2: {} tree-sitter-json@0.24.8(tree-sitter@0.21.1):