From 240d5bd0fcd56fd0c7a2de73c0e64629e5eb6797 Mon Sep 17 00:00:00 2001 From: Nico Schett Date: Tue, 21 May 2024 18:15:50 +0200 Subject: [PATCH 1/5] save --- cache.patch | 27 ++++++++ my-gatsby-site/src/pages/mdx.tsx | 14 +++- my-gatsby-site/src/pages/wholesale.tsx | 2 +- .../gatsby-plugin-jaen/gatsby/gatsby-node.ts | 9 +-- packages/gatsby-plugin-jaen/package.json | 2 +- .../src/components/shared/Link/Link.tsx | 2 +- .../src/pages/cms/index.tsx | 2 +- .../src/pages/cms/pages/index.tsx | 2 - .../src/slices/jaen-frame.tsx | 8 +-- packages/jaen-fields-mdx/package.json | 2 +- .../src/MdxField/components/Editor.tsx | 7 +- packages/jaen/package.json | 2 +- packages/jaen/src/contexts/auth.tsx | 20 ++++-- packages/jaen/src/contexts/cms-management.tsx | 68 ++++--------------- packages/jaen/src/contexts/page.tsx | 20 +++++- packages/jaen/src/hooks/use-page.ts | 49 ++++++++++--- packages/jaen/src/utils/diff-objects.ts | 42 ++++++++++++ 17 files changed, 187 insertions(+), 91 deletions(-) create mode 100644 cache.patch create mode 100644 packages/jaen/src/utils/diff-objects.ts diff --git a/cache.patch b/cache.patch new file mode 100644 index 00000000..73b6b78c --- /dev/null +++ b/cache.patch @@ -0,0 +1,27 @@ +diff --git a/node_modules/gatsby/cache-dir/create-content-digest-browser-shim.js b/node_modules/gatsby/cache-dir/create-content-digest-browser-shim.js +index edc673f..cbd390c 100644 +--- a/node_modules/gatsby/cache-dir/create-content-digest-browser-shim.js ++++ b/node_modules/gatsby/cache-dir/create-content-digest-browser-shim.js +@@ -1 +1 @@ +-exports.createContentDigest = () => `` ++export const createContentDigest = () => `` +diff --git a/node_modules/gatsby/cache-dir/public-page-renderer.js b/node_modules/gatsby/cache-dir/public-page-renderer.js +index 6a91ada..e2ee03a 100644 +--- a/node_modules/gatsby/cache-dir/public-page-renderer.js ++++ b/node_modules/gatsby/cache-dir/public-page-renderer.js +@@ -1,9 +1,11 @@ + const preferDefault = m => (m && m.default) || m + ++let renderer = () => null; ++ + if (process.env.BUILD_STAGE === `develop`) { +- module.exports = preferDefault(require(`./public-page-renderer-dev`)) ++ renderer = preferDefault(require(`./public-page-renderer-dev`)) + } else if (process.env.BUILD_STAGE === `build-javascript`) { +- module.exports = preferDefault(require(`./public-page-renderer-prod`)) +-} else { +- module.exports = () => null ++ renderer = preferDefault(require(`./public-page-renderer-prod`)) + } ++ ++export default renderer; diff --git a/my-gatsby-site/src/pages/mdx.tsx b/my-gatsby-site/src/pages/mdx.tsx index 041289ad..0258f494 100644 --- a/my-gatsby-site/src/pages/mdx.tsx +++ b/my-gatsby-site/src/pages/mdx.tsx @@ -1,8 +1,9 @@ import {Field, PageConfig, PageProps} from '@atsnek/jaen' import {Box} from '@chakra-ui/react' -import {MdxField} from '@atsnek/jaen-fields-mdx' +import {MdxField, UncontrolledMdxField} from '@atsnek/jaen-fields-mdx' import {Link} from 'gatsby-plugin-jaen' import {usePage} from '@atsnek/jaen' +import {useState} from 'react' const QASMPlayground: React.FC<{ playground?: boolean @@ -26,6 +27,17 @@ const Page: React.FC = ({location, pageContext}) => { console.log('page:', page) + const [value, setValue] = useState() + + return ( + + ) + return ( <> ( return } - return + return } return diff --git a/packages/gatsby-plugin-jaen/src/pages/cms/index.tsx b/packages/gatsby-plugin-jaen/src/pages/cms/index.tsx index e0c2c2eb..1fc04e40 100644 --- a/packages/gatsby-plugin-jaen/src/pages/cms/index.tsx +++ b/packages/gatsby-plugin-jaen/src/pages/cms/index.tsx @@ -33,7 +33,7 @@ const DashboardPage: React.FC = () => { return ( diff --git a/packages/gatsby-plugin-jaen/src/pages/cms/pages/index.tsx b/packages/gatsby-plugin-jaen/src/pages/cms/pages/index.tsx index 067e3caf..20809eaf 100644 --- a/packages/gatsby-plugin-jaen/src/pages/cms/pages/index.tsx +++ b/packages/gatsby-plugin-jaen/src/pages/cms/pages/index.tsx @@ -201,8 +201,6 @@ const PagesPage: React.FC = () => { }, parentPages, onSubmit: data => { - alert('submit') - console.log('manager.updatePage', manager.updatePage) manager.updatePage(currentPage.id, { slug: data.slug, template: data.template, diff --git a/packages/gatsby-plugin-jaen/src/slices/jaen-frame.tsx b/packages/gatsby-plugin-jaen/src/slices/jaen-frame.tsx index d2ce0656..2b12ce6b 100644 --- a/packages/gatsby-plugin-jaen/src/slices/jaen-frame.tsx +++ b/packages/gatsby-plugin-jaen/src/slices/jaen-frame.tsx @@ -244,16 +244,16 @@ const Slice: React.FC = props => { logo: }, user: { - user: auth.user + user: auth.user?.profile ? { username: auth.user.profile.preferred_username?.replace( `@${auth.user.profile['urn:zitadel:iam:user:resourceowner:primary_domain']}`, '' ) || auth.user.profile.sub, - firstName: auth.user?.profile?.given_name, - lastName: auth.user?.profile?.family_name, - avatarURL: auth.user?.profile?.picture + firstName: auth.user.profile.given_name, + lastName: auth.user.profile.family_name, + avatarURL: auth.user.profile.picture } : { username: 'Guest' diff --git a/packages/jaen-fields-mdx/package.json b/packages/jaen-fields-mdx/package.json index 5799ae7c..678821b6 100644 --- a/packages/jaen-fields-mdx/package.json +++ b/packages/jaen-fields-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@atsnek/jaen-fields-mdx", - "version": "1.0.0-rc.18", + "version": "1.0.0-rc.19", "description": "Markdown field for Jaen", "type": "module", "source": "src/index.ts", diff --git a/packages/jaen-fields-mdx/src/MdxField/components/Editor.tsx b/packages/jaen-fields-mdx/src/MdxField/components/Editor.tsx index 740f0eea..cd20a661 100644 --- a/packages/jaen-fields-mdx/src/MdxField/components/Editor.tsx +++ b/packages/jaen-fields-mdx/src/MdxField/components/Editor.tsx @@ -8,7 +8,7 @@ import CodeMirror, { EditorView } from '@uiw/react-codemirror' -import React, {useCallback, useEffect, useMemo, useState} from 'react' +import React, {useCallback, useEffect, useMemo} from 'react' import {ErrorBoundary} from 'react-error-boundary' import {statistics, Statistics} from 'vfile-statistics' @@ -30,16 +30,13 @@ const MemoizedCodeMirror = React.memo(props => { export interface EditorProps extends BaseEditorProps {} export const Editor: React.FC = props => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [defaultValue, _] = useState(props.mdast) - const [state, setConfig] = useMdx( { gfm: true, frontmatter: true, math: true, directive: true, - mdast: defaultValue + mdast: props.mdast }, false, props.components diff --git a/packages/jaen/package.json b/packages/jaen/package.json index 6fa1b721..166ae93b 100644 --- a/packages/jaen/package.json +++ b/packages/jaen/package.json @@ -1,6 +1,6 @@ { "name": "@atsnek/jaen", - "version": "1.0.0-rc.74", + "version": "1.0.0-rc.83", "description": "The Webapps framework for creating scalable and dynamic applications with ease.", "type": "module", "source": "src/index.ts", diff --git a/packages/jaen/src/contexts/auth.tsx b/packages/jaen/src/contexts/auth.tsx index a29b9d34..154cf5d2 100644 --- a/packages/jaen/src/contexts/auth.tsx +++ b/packages/jaen/src/contexts/auth.tsx @@ -29,11 +29,11 @@ export const useAuth = () => { const data = await response.json() - const userRoles = data.result.map((grant: any) => { + const userRoles = (data.result?.map((grant: any) => { return (grant.roles || []).map((role: any) => { return `${grant.projectId}:${role}` }) - }) + }) || []) as string[][] const projectScopedRoles = userRoles.flat() @@ -136,6 +136,8 @@ export const withAuthSecurity = < const pageConfigAuth = props.pageContext.pageConfig?.auth const auth = useAuth() + console.log('auth', auth) + const loadingText = useMemo(() => { switch (auth.activeNavigator) { case 'signinRedirect': @@ -151,8 +153,6 @@ export const withAuthSecurity = < } }, [auth.activeNavigator]) - const isAuthRequired = pageConfigAuth?.isRequired - if (loadingText) { return (
@@ -183,8 +183,16 @@ export const withAuthSecurity = < ) } - if (isAuthRequired) { - const roles = pageConfigAuth?.roles + if (pageConfigAuth?.isRequired) { + let roles = pageConfigAuth?.roles + + if (pageConfigAuth.isAdminRequired) { + if (!roles) { + roles = ['jaen:admin'] + } else { + roles.push('jaen:admin') + } + } if (roles) { const hasRoles = checkUserRoles(auth.user, roles) diff --git a/packages/jaen/src/contexts/cms-management.tsx b/packages/jaen/src/contexts/cms-management.tsx index 0af8415f..09021374 100644 --- a/packages/jaen/src/contexts/cms-management.tsx +++ b/packages/jaen/src/contexts/cms-management.tsx @@ -22,6 +22,7 @@ import {useSiteMetadataContext} from './site-metadata' import {uploadFile} from '../utils/open-storage-gateway' import {useNotificationsContext} from './notifications' import {sqJaen} from '../clients/jaen/src' +// import {diffObjects} from '../utils/diff-objects' // Errors @@ -213,6 +214,12 @@ export const CMSManagementProvider = withRedux( return 5 } + console.log( + 'parentPage.childPagesOrder', + parentPage.childPagesOrder, + parentPage + ) + const aIndex = parentPage.childPagesOrder?.indexOf(a.id) const bIndex = parentPage.childPagesOrder?.indexOf(b.id) @@ -286,56 +293,9 @@ export const CMSManagementProvider = withRedux( pageId: string, updatedPage: Partial ): void => { - console.log('updatedPage', updatedPage, 'pageId', pageId) - const page = pagesDict[pageId] - function deepObjectDiff( - obj1: Partial, - obj2: Partial - ) { - function compareObjects( - o1: {[x: string]: any}, - o2: {[x: string]: any; hasOwnProperty: (arg0: string) => any} - ) { - for (const key in o2) { - if (o2.hasOwnProperty(key)) { - if ( - o1[key] && - typeof o1[key] === 'object' && - o2[key] && - typeof o2[key] === 'object' - ) { - if (!compareObjects(o1[key], o2[key])) { - return false - } - } else if (o1[key] !== o2[key]) { - return false - } - } - } - return true - } - - const diff: Partial = {} - - for (const key in obj2) { - if ( - !obj1.hasOwnProperty(key) || - (typeof obj2[key] === 'object' && - !compareObjects(obj1[key], obj2[key])) || - (typeof obj2[key] !== 'object' && obj1[key] !== obj2[key]) - ) { - diff[key] = obj2[key] - } - } - - return diff - } - - updatedPage = deepObjectDiff(page || {}, updatedPage) - - console.log('updatedPage', updatedPage, 'page', page) + // updatedPage = diffObjects>(page || {}, updatedPage) // check if slug is unique when updating slug const slug = updatedPage.slug || 'new-page' @@ -350,15 +310,17 @@ export const CMSManagementProvider = withRedux( throw new DuplicateSlugError(slug) } + const fromId = + updatedPage?.parentPage?.id !== undefined && + page?.parentPage?.id !== updatedPage?.parentPage?.id + ? page?.parentPage?.id + : undefined + dispatch( pageActions.page_updateOrCreate({ id: pageId, ...updatedPage, - fromId: - // Only set the fromId if the parentPage has changed - updatedPage?.parentPage?.id || updatedPage?.parentPage === null - ? page?.parentPage?.id - : undefined + fromId: fromId }) ) } diff --git a/packages/jaen/src/contexts/page.tsx b/packages/jaen/src/contexts/page.tsx index 0f434d28..9aee3424 100644 --- a/packages/jaen/src/contexts/page.tsx +++ b/packages/jaen/src/contexts/page.tsx @@ -5,6 +5,7 @@ import {RootState, store} from '../redux' import {JaenPage} from '../types' import {deepmergeArrayIdMerge} from '../utils/deepmerge' import {useDynamicPaths} from '../hooks/use-dynamic-paths' +import {usePage} from '../hooks/use-page' export interface PageProviderProps { jaenPage: { @@ -73,6 +74,11 @@ export interface UsePageIndexProps { * Include excluded pages in the index. */ includeExcluded?: boolean + + /** + * Sort the pages based on the `sortOrder` field. + */ + sortByPageOrder?: boolean } export const useJaenPageIndex = ( @@ -83,8 +89,6 @@ export const useJaenPageIndex = ( } => { const {jaenPage, jaenPages} = usePageContext() - console.log('jaenPage', jaenPage, 'jaenPages', jaenPages) - const paths = useDynamicPaths({ staticPages: (jaenPages || []) as any }) @@ -111,6 +115,8 @@ export const useJaenPageIndex = ( return jaenPage.id }, [jaenPage, jaenPages, props, paths]) + const {childPagesOrder = []} = usePage({id: id}) + const staticChildren = useMemo(() => { let children: Array<{id: string} & Partial> = [] @@ -185,6 +191,16 @@ export const useJaenPageIndex = ( return !c.excludedFromIndex && !c.deleted }) + if (props?.sortByPageOrder) { + // sort based on childPagesOrder + mergedChildren = mergedChildren.sort((a, b) => { + const aIndex = childPagesOrder.indexOf(a.id) + const bIndex = childPagesOrder.indexOf(b.id) + + return aIndex - bIndex + }) + } + if (props) { const {filter, sort} = props diff --git a/packages/jaen/src/hooks/use-page.ts b/packages/jaen/src/hooks/use-page.ts index e5f684a5..16560076 100644 --- a/packages/jaen/src/hooks/use-page.ts +++ b/packages/jaen/src/hooks/use-page.ts @@ -4,36 +4,69 @@ import {useEffect, useMemo, useState} from 'react' import {usePageContext} from '../contexts/page' import {RootState, store} from '../redux' import {JaenPage} from '../types' +import {useDynamicPaths} from './use-dynamic-paths' -export interface UsePageProps {} +export interface UsePageProps { + id?: string + path?: string +} + +export const usePage = (props: UsePageProps): JaenPage => { + const {jaenPage, jaenPages} = usePageContext() + + const paths = useDynamicPaths({ + staticPages: (jaenPages || []) as any + }) + + const id = useMemo(() => { + if (props?.id) { + return props.id + } else if (props?.path) { + if (jaenPages == null) { + throw new Error('Unable to resolve page by path. No pages provided.') + } + + const path = props?.path -export const usePage = (_: UsePageProps): JaenPage => { - const {jaenPage} = usePageContext() + const newId = paths[path]?.jaenPageId + + if (!newId) { + throw new Error(`Could not resolve page by path: ${path}`) + } + + return newId + } + + return jaenPage.id + }, [jaenPage, jaenPages, props, paths]) const [dynamicPage, setDynamicPage] = useState | undefined>( undefined ) useEffect(() => { - const dynamicPage = store.getState().page.pages.nodes[jaenPage.id] + const dynamicPage = store.getState().page.pages.nodes[id] setDynamicPage(dynamicPage) const unsubscribe = store.subscribe(() => { const state = store.getState() as RootState - const dynamicPage = state.page.pages.nodes[jaenPage.id] + const dynamicPage = state.page.pages.nodes[id] setDynamicPage(dynamicPage) }) return () => { unsubscribe() } - }, [jaenPage]) + }, [id]) const page = useMemo(() => { - return deepmerge(jaenPage, dynamicPage || {}) - }, [jaenPage, dynamicPage]) + const staticPage = + jaenPage.id === id ? jaenPage : jaenPages?.find(page => page.id === id) + + return deepmerge(staticPage || {}, dynamicPage || {}) + }, [id, dynamicPage]) return page } diff --git a/packages/jaen/src/utils/diff-objects.ts b/packages/jaen/src/utils/diff-objects.ts new file mode 100644 index 00000000..657977a7 --- /dev/null +++ b/packages/jaen/src/utils/diff-objects.ts @@ -0,0 +1,42 @@ +type Diff = { + [K in keyof T]?: T[K] extends object ? Diff : T[K] +} + +export function diffObjects(obj1: T, obj2: T): Diff { + const diff: Partial = {} + + const keys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]) as Set< + keyof T + > + + keys.forEach(key => { + const value1 = obj1[key] + const value2 = obj2[key] + + console.log(key, value1, value2) + + if (value2 !== null && typeof value2 === 'object') { + diff[key] = diffObjects(value1 || {}, value2) as any + } else if ( + !value1 || + !value2 || + typeof value1 !== 'object' || + typeof value2 !== 'object' + ) { + console.log(obj2, value2) + + // Check if obj2 contains the key + if (obj2.hasOwnProperty(key)) { + console.log('key not in obj2', key, value1, value2) + + // Check if the value is different + if (value1 !== value2) { + console.log('assign', value1, value2) + diff[key] = value2 + } + } + } + }) + + return diff +} From 0ee604b55e22df101b3b54a0104a812800c2b380 Mon Sep 17 00:00:00 2001 From: Nico Schett Date: Wed, 22 May 2024 11:09:42 +0200 Subject: [PATCH 2/5] feat: add notification popup --- packages/gatsby-plugin-jaen/components.json | 2 +- packages/gatsby-plugin-jaen/package.json | 21 +- .../src/components/Popup.tsx | 93 +++ .../src/components/ui/calendar.tsx | 64 ++ .../src/components/ui/card.tsx | 79 +++ .../src/components/ui/checkbox.tsx | 26 + .../src/components/ui/dialog.tsx | 119 ++++ .../src/components/ui/form.tsx | 175 +++++ .../src/components/ui/input.tsx | 25 + .../src/components/ui/label.tsx | 24 + .../src/components/ui/popover.tsx | 29 + .../src/components/ui/textarea.tsx | 24 + .../src/components/ui/toast.tsx | 126 ++++ .../src/components/ui/toaster.tsx | 33 + .../src/components/ui/toggle.tsx | 43 ++ .../src/components/ui/use-toast.ts | 189 ++++++ .../src/gatsby/wrap-root-element.tsx | 9 +- .../hooks/use-notification-popup-widget.ts | 12 + .../src/pages/cms/notification/popup.tsx | 501 ++++++++++++++ .../gatsby-plugin-jaen/tailwind.config.js | 2 +- yarn.lock | 638 +++++++++++++++++- 21 files changed, 2207 insertions(+), 27 deletions(-) create mode 100644 packages/gatsby-plugin-jaen/src/components/Popup.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/calendar.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/card.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/checkbox.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/dialog.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/form.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/input.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/label.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/popover.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/textarea.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/toast.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/toaster.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/toggle.tsx create mode 100644 packages/gatsby-plugin-jaen/src/components/ui/use-toast.ts create mode 100644 packages/gatsby-plugin-jaen/src/hooks/use-notification-popup-widget.ts create mode 100644 packages/gatsby-plugin-jaen/src/pages/cms/notification/popup.tsx diff --git a/packages/gatsby-plugin-jaen/components.json b/packages/gatsby-plugin-jaen/components.json index 4a4616c5..776ca83e 100644 --- a/packages/gatsby-plugin-jaen/components.json +++ b/packages/gatsby-plugin-jaen/components.json @@ -12,6 +12,6 @@ }, "aliases": { "components": "components", - "utils": "../../lib/utils" + "utils": "utils" } } diff --git a/packages/gatsby-plugin-jaen/package.json b/packages/gatsby-plugin-jaen/package.json index ed7666a6..b66147eb 100644 --- a/packages/gatsby-plugin-jaen/package.json +++ b/packages/gatsby-plugin-jaen/package.json @@ -1,6 +1,6 @@ { "name": "gatsby-plugin-jaen", - "version": "1.0.0-rc.99", + "version": "1.0.0-rc.100", "main": "index.js", "types": "./src/index.ts", "files": [ @@ -25,14 +25,26 @@ "@chakra-ui/react": "^2.8.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", + "@hookform/resolvers": "^3.4.2", "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-toast": "^1.1.5", + "@radix-ui/react-toggle": "^1.0.3", "@sentry/gatsby": "^7.106.0", + "@tiptap/extension-link": "^2.4.0", + "@tiptap/extension-underline": "^2.4.0", + "@tiptap/pm": "^2.4.0", + "@tiptap/react": "^2.4.0", + "@tiptap/starter-kit": "^2.4.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "date-fns": "^3.6.0", "framer-motion": "^10.13.0", "gatsby-plugin-google-gtag": "^5.12.0", "gatsby-plugin-image": "^3.13.1", @@ -47,9 +59,10 @@ "lucide-react": "^0.358.0", "react-beautiful-dnd": "^13.1.1", "react-complex-tree": "^2.2.0", + "react-day-picker": "^8.10.1", "react-dropzone": "^14.2.3", "react-filerobot-image-editor": "^4.5.1", - "react-hook-form": "^7.45.2", + "react-hook-form": "^7.51.5", "react-konva": "^18.2.10", "react-photo-view": "^1.2.3", "react-zoom-pan-pinch": "^3.1.0", @@ -58,9 +71,11 @@ "styled-components": "^6.0.5", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", - "use-debounce": "^9.0.4" + "use-debounce": "^9.0.4", + "zod": "^3.23.8" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.13", "@types/lodash.throttle": "^4.1.7", "@types/react-beautiful-dnd": "^13.1.8", "gatsby": "^5.11.0", diff --git a/packages/gatsby-plugin-jaen/src/components/Popup.tsx b/packages/gatsby-plugin-jaen/src/components/Popup.tsx new file mode 100644 index 00000000..3d13103d --- /dev/null +++ b/packages/gatsby-plugin-jaen/src/components/Popup.tsx @@ -0,0 +1,93 @@ +import React, {useEffect} from 'react' + +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger +} from './ui/dialog' +import {Button} from './ui/button' +import {useNotificationPopupWidget} from '../hooks/use-notification-popup-widget' + +export const Popup: React.FC<{}> = () => { + const [{data}] = useNotificationPopupWidget() + + const [consent, setConsent] = React.useState(true) + + useEffect(() => { + // The local storage contains the notification popup data so we can check if the user has already seen the notification + // When the data is missing or the data is outdated we show the notification + const localStorageData = localStorage.getItem('notification-popup') + + if (!localStorageData) { + setConsent(false) + } + + // String comparison to check if the data is outdated + else if (data && localStorageData !== JSON.stringify(data)) { + setConsent(false) + } else { + setConsent(true) + } + }, [data]) + + const handleConsent = () => { + localStorage.setItem('notification-popup', JSON.stringify(data)) + setConsent(true) + } + + useEffect(() => { + // Skip if the user has already seen the notification + if (consent) { + return + } + + const popup = document.getElementById('notification-popup') + if (popup) { + // Check if the notification is enabled + const isEnabled = data?.isEnabled ?? false + const from = data?.from ? new Date(data?.from) : null + const to = data?.to ? new Date(data?.to) : null + + if (isEnabled) { + const now = new Date() + + let isInTimeRange = true + if (from && to) { + isInTimeRange = now >= from && now <= to + } else if (from) { + isInTimeRange = now >= from + } else if (to) { + isInTimeRange = now <= to + } + + if (isInTimeRange) { + popup.click() + } + } + } + }, [consent, data]) + + return ( + + + + + {data?.title} + +

+ + + + + + +
+
+ ) +} diff --git a/packages/gatsby-plugin-jaen/src/components/ui/calendar.tsx b/packages/gatsby-plugin-jaen/src/components/ui/calendar.tsx new file mode 100644 index 00000000..602098aa --- /dev/null +++ b/packages/gatsby-plugin-jaen/src/components/ui/calendar.tsx @@ -0,0 +1,64 @@ +import * as React from 'react' +import {ChevronLeft, ChevronRight} from 'lucide-react' +import {DayPicker} from 'react-day-picker' + +import {cn} from '../../lib/utils' +import {buttonVariants} from './button' + +export type CalendarProps = React.ComponentProps + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + , + IconRight: ({...props}) => + }} + {...props} + /> + ) +} +Calendar.displayName = 'Calendar' + +export {Calendar} diff --git a/packages/gatsby-plugin-jaen/src/components/ui/card.tsx b/packages/gatsby-plugin-jaen/src/components/ui/card.tsx new file mode 100644 index 00000000..5ff854d6 --- /dev/null +++ b/packages/gatsby-plugin-jaen/src/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from 'react' + +import {cn} from '../../lib/utils' + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +
+)) +Card.displayName = 'Card' + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +
+)) +CardHeader.displayName = 'CardHeader' + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +

+)) +CardTitle.displayName = 'CardTitle' + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +

+)) +CardDescription.displayName = 'CardDescription' + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +

+)) +CardContent.displayName = 'CardContent' + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +
+)) +CardFooter.displayName = 'CardFooter' + +export {Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent} diff --git a/packages/gatsby-plugin-jaen/src/components/ui/checkbox.tsx b/packages/gatsby-plugin-jaen/src/components/ui/checkbox.tsx new file mode 100644 index 00000000..a4dbef42 --- /dev/null +++ b/packages/gatsby-plugin-jaen/src/components/ui/checkbox.tsx @@ -0,0 +1,26 @@ +import * as React from 'react' +import * as CheckboxPrimitive from '@radix-ui/react-checkbox' +import {Check} from 'lucide-react' + +import {cn} from '../../lib/utils' + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export {Checkbox} diff --git a/packages/gatsby-plugin-jaen/src/components/ui/dialog.tsx b/packages/gatsby-plugin-jaen/src/components/ui/dialog.tsx new file mode 100644 index 00000000..574af07e --- /dev/null +++ b/packages/gatsby-plugin-jaen/src/components/ui/dialog.tsx @@ -0,0 +1,119 @@ +import * as React from 'react' +import * as DialogPrimitive from '@radix-ui/react-dialog' +import {X} from 'lucide-react' + +import {cn} from '../../lib/utils' + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, children, ...props}, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = 'DialogHeader' + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = 'DialogFooter' + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription +} diff --git a/packages/gatsby-plugin-jaen/src/components/ui/form.tsx b/packages/gatsby-plugin-jaen/src/components/ui/form.tsx new file mode 100644 index 00000000..132fe18d --- /dev/null +++ b/packages/gatsby-plugin-jaen/src/components/ui/form.tsx @@ -0,0 +1,175 @@ +import * as React from 'react' +import * as LabelPrimitive from '@radix-ui/react-label' +import {Slot} from '@radix-ui/react-slot' +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext +} from 'react-hook-form' + +import {cn} from '../../lib/utils' +import {Label} from '../ui/label' + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath +> = { + name: TName +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath +>({ + ...props +}: ControllerProps) => { + return ( + + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const {getFieldState, formState} = useFormContext() + + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error('useFormField should be used within ') + } + + const {id} = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => { + const id = React.useId() + + return ( + +
+ + ) +}) +FormItem.displayName = 'FormItem' + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => { + const {error, formItemId} = useFormField() + + return ( +