├── .editorconfig ├── .env.example ├── .eslintrc.json ├── .github ├── .kodiak.toml ├── actions │ ├── cache-turbo │ │ └── action.yml │ ├── deploy │ │ └── action.yml │ ├── e2e │ │ └── action.yml │ ├── init │ │ └── action.yml │ ├── install │ │ └── action.yml │ ├── lint │ │ └── action.yml │ ├── release │ │ └── action.yml │ └── test │ │ └── action.yml ├── renovate.json └── workflows │ ├── pull.yml │ └── push.yml ├── .gitignore ├── .husky ├── pre-commit └── prepare-commit-msg ├── .lintstagedrc.mjs ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .prettierignore_staged ├── .prettierrc.mjs ├── .syncpackrc.mjs ├── .vscode ├── extensions.json └── settings.default.json ├── LICENSE ├── README.md ├── changelog.config.mjs ├── config └── release-branch-types │ ├── ci.cjs │ ├── feature.cjs │ ├── fix.cjs │ ├── index.cjs │ ├── refactor.cjs │ └── release.cjs ├── eslint.config.mjs ├── index.d.ts ├── node-modules-inspector.config.ts ├── package.json ├── packages ├── design-system │ ├── .storybook │ │ ├── font.css │ │ ├── main.ts │ │ ├── manager.ts │ │ └── preview.ts │ ├── jest.config.js │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── static │ │ │ └── fonts │ │ │ └── inter │ │ │ ├── inter-4.0.0-beta9g-var.woff2 │ │ │ ├── inter-var-full.woff2 │ │ │ ├── inter-var-italic.woff2 │ │ │ ├── inter-var-latin.woff2 │ │ │ └── inter-var.woff2 │ ├── scripts │ │ └── copy.sh │ ├── src │ │ ├── components │ │ │ ├── Anchor │ │ │ │ ├── Anchor.tsx │ │ │ │ └── index.ts │ │ │ ├── Callout │ │ │ │ ├── Callout.tsx │ │ │ │ └── index.ts │ │ │ ├── Icon │ │ │ │ ├── Icon.stories.tsx │ │ │ │ ├── Icon.test.tsx │ │ │ │ ├── Icon.tsx │ │ │ │ ├── Icon.types.ts │ │ │ │ └── index.ts │ │ │ ├── Section │ │ │ │ ├── Section.Content.tsx │ │ │ │ ├── Section.Header.Content.tsx │ │ │ │ ├── Section.Header.Title.tsx │ │ │ │ ├── Section.Header.tsx │ │ │ │ ├── Section.Hero.tsx │ │ │ │ ├── Section.Wrapper.tsx │ │ │ │ ├── Section.test.ts │ │ │ │ ├── Tags.tsx │ │ │ │ └── index.ts │ │ │ ├── SkipNav │ │ │ │ ├── SkipNav.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── ui │ │ │ └── .gitkeep │ │ └── utils │ │ │ └── cx.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsup.config.ts ├── jest-config │ ├── jest.config.js │ ├── jest.setup.js │ └── package.json ├── jest-presets │ ├── jest │ │ └── node │ │ │ └── jest-preset.js │ └── package.json ├── lighthouse-config │ ├── .lighthouserc.cjs │ └── package.json ├── next-config │ ├── next.config.mjs │ ├── package.json │ └── src │ │ ├── build-info.mjs │ │ ├── env.client.mjs │ │ ├── env.server.mjs │ │ └── security-headers.mjs ├── next-notion │ ├── README.md │ ├── package.json │ ├── scripts │ │ └── copy.sh │ ├── src │ │ ├── Notion.Blocks.tsx │ │ ├── Notion.Config.tsx │ │ ├── Notion.constants.ts │ │ ├── Notion.types.ts │ │ ├── Notion.utils.tsx │ │ ├── blocks │ │ │ ├── Column.tsx │ │ │ ├── Divider.tsx │ │ │ ├── Emoji.client.tsx │ │ │ ├── Emoji.server.tsx │ │ │ ├── Emoji.tsx │ │ │ ├── ListBulleted.tsx │ │ │ ├── ListColumn.tsx │ │ │ └── RichText.tsx │ │ ├── helper.ts │ │ ├── index.ts │ │ ├── queries │ │ │ ├── getBlockChildrenData.ts │ │ │ ├── getBlockChildrenDataParent.ts │ │ │ ├── getColumnData.ts │ │ │ ├── getDatabaseQuery.ts │ │ │ ├── getPageData.ts │ │ │ └── index.ts │ │ └── utils │ │ │ ├── getAwsImage.ts │ │ │ ├── getPropertyTypeData.ts │ │ │ ├── getSegmentInfo.ts │ │ │ ├── index.ts │ │ │ └── uuid.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── playwright-config │ ├── package.json │ └── playwright.config.ts ├── shared │ ├── package.json │ ├── scripts │ │ └── copy.sh │ ├── src │ │ ├── components │ │ │ ├── Analytics │ │ │ │ ├── Analytics.tsx │ │ │ │ ├── Fathom.tsx │ │ │ │ ├── Vercel.tsx │ │ │ │ └── index.ts │ │ │ ├── Navigation │ │ │ │ ├── Navigation.tsx │ │ │ │ └── index.ts │ │ │ └── Notion │ │ │ │ └── Blocks │ │ │ │ ├── Embed.Spotify.tsx │ │ │ │ ├── Embed.Twitter.tsx │ │ │ │ ├── Embed.tsx │ │ │ │ ├── Image.client.tsx │ │ │ │ ├── Image.tsx │ │ │ │ ├── Image.utils.ts │ │ │ │ ├── Video.YouTube.tsx │ │ │ │ └── Video.tsx │ │ ├── hooks │ │ │ ├── .gitkeep │ │ │ ├── index.ts │ │ │ ├── useSWRInfinitePages.ts │ │ │ ├── useThemeToggle.ts │ │ │ └── useThrottle.ts │ │ ├── index.ts │ │ ├── lib │ │ │ ├── constants.ts │ │ │ ├── fetcher.ts │ │ │ └── index.ts │ │ ├── notion │ │ │ └── utils │ │ │ │ ├── getDataFromCache.ts │ │ │ │ ├── getDatabaseQuery.ts │ │ │ │ ├── getDatabaseQueryByDateRange.ts │ │ │ │ ├── getMetadata.ts │ │ │ │ ├── getPageDataFromNotion.ts │ │ │ │ ├── getSegmentInfo.ts │ │ │ │ ├── getSlugPreview.ts │ │ │ │ └── index.ts │ │ ├── plaiceholder │ │ │ ├── getImage.ts │ │ │ └── index.ts │ │ └── redis │ │ │ ├── getCache.ts │ │ │ ├── getKey.ts │ │ │ ├── index.ts │ │ │ ├── redis.ts │ │ │ └── setCache.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── storybook-config │ ├── main.config.ts │ ├── manager.config.ts │ ├── package.json │ ├── preview-head.html │ ├── preview.config.ts │ ├── preview.css │ ├── public │ │ └── .gitkeep │ ├── themes.ts │ └── withTailwindTheme.decorator.tsx └── tailwind-config │ ├── hocus.plugin.js │ ├── package.json │ ├── postcss.config.js │ ├── radix.plugin.js │ ├── src │ ├── backgrounds.js │ ├── buttons.js │ ├── colors.js │ ├── index.js │ └── notion.js │ ├── styles │ ├── chrome.css │ ├── fonts.css │ ├── globals.css │ ├── radix-themes-tw.css │ └── safari.css │ └── tailwind.config.ts ├── patches ├── .gitkeep └── @semantic-release__commit-analyzer@13.0.0.patch ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── release.config.mjs ├── scripts └── clean.sh ├── sites ├── .gitignore ├── alexojerome.com │ └── .gitkeep ├── amidracula.com │ └── .gitkeep ├── ayemonthen.com │ └── .gitkeep ├── boohumbag.com │ └── .gitkeep ├── crimeimprov.com │ └── .gitkeep ├── dahriferks.com │ └── .gitkeep ├── fuckyoucandy.com │ └── .gitkeep ├── jerandky.com │ ├── next-env.d.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── fonts │ │ │ └── inter │ │ │ │ ├── inter-4.0.0-beta9g-var.woff2 │ │ │ │ ├── inter-var-full.woff2 │ │ │ │ ├── inter-var-italic.woff2 │ │ │ │ ├── inter-var-latin.woff2 │ │ │ │ └── inter-var.woff2 │ │ ├── humans.txt │ │ ├── images │ │ │ └── favicon │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── browserconfig.xml │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ ├── favicon.ico │ │ │ │ ├── mstile-150x150.png │ │ │ │ ├── safari-pinned-tab.svg │ │ │ │ └── site.webmanifest │ │ └── xml │ │ │ └── podcasts.xls │ ├── src │ │ ├── app │ │ │ ├── (notion) │ │ │ │ ├── _config │ │ │ │ │ ├── Episode.types.ts │ │ │ │ │ ├── Episode.utils.ts │ │ │ │ │ ├── Page.types.ts │ │ │ │ │ ├── Page.utils.ts │ │ │ │ │ ├── Person.types.ts │ │ │ │ │ ├── Person.utils.ts │ │ │ │ │ ├── Podcast.types.ts │ │ │ │ │ ├── Podcast.utils.ts │ │ │ │ │ ├── Venue.types.ts │ │ │ │ │ ├── Venue.utils.ts │ │ │ │ │ ├── config.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── temp │ │ │ │ │ │ └── generateMetadataCustom.ts │ │ │ │ └── podcasts │ │ │ │ │ └── [[...catchAll]] │ │ │ │ │ ├── _components │ │ │ │ │ ├── Episode.Slug.tsx │ │ │ │ │ ├── Image.tsx │ │ │ │ │ ├── Podcast.Episodes.tsx │ │ │ │ │ ├── Podcast.Listing.tsx │ │ │ │ │ └── Podcast.Slug.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── _errors │ │ │ │ └── 404.tsx │ │ │ ├── api │ │ │ │ └── rss │ │ │ │ │ └── podcasts │ │ │ │ │ ├── jer-and-ky-and-guest │ │ │ │ │ └── route.ts │ │ │ │ │ └── knockoffs │ │ │ │ │ └── route.ts │ │ │ ├── apple-icon.png │ │ │ ├── favicon.ico │ │ │ ├── icon.png │ │ │ ├── icon1.png │ │ │ ├── layout.tsx │ │ │ ├── not-found.tsx │ │ │ ├── page.tsx │ │ │ ├── robots.ts │ │ │ └── sitemap.ts │ │ ├── components │ │ │ ├── ErrorBoundary │ │ │ │ ├── ErrorBoundary.tsx │ │ │ │ └── index.ts │ │ │ ├── Footer │ │ │ │ ├── Footer.client.tsx │ │ │ │ ├── Footer.tsx │ │ │ │ └── index.ts │ │ │ ├── Notion │ │ │ │ ├── Notion.Config.ts │ │ │ │ ├── Notion.tsx │ │ │ │ └── index.ts │ │ │ ├── Providers │ │ │ │ ├── Providers.client.tsx │ │ │ │ ├── Providers.tsx │ │ │ │ ├── RouterEventProvider.Loading.client.tsx │ │ │ │ ├── RouterEventProvider.client.tsx │ │ │ │ ├── ThemeProvider.client.tsx │ │ │ │ └── index.ts │ │ │ └── Relations │ │ │ │ ├── Relations.Individual.tsx │ │ │ │ ├── Relations.Loading.tsx │ │ │ │ ├── Relations.tsx │ │ │ │ ├── Relations.utils.ts │ │ │ │ └── index.ts │ │ └── config │ │ │ └── .gitkeep │ ├── tsconfig.dev.json │ ├── tsconfig.json │ └── tsconfig.shared.json ├── jeromeand.com │ └── .gitkeep ├── jeromefitzgerald.com │ ├── .lighthouserc.cjs │ ├── drizzle.config.ts │ ├── jest.config.cjs │ ├── next-env.d.ts │ ├── next.config.ts │ ├── package.json │ ├── playwright.config.ts │ ├── postcss.config.mjs │ ├── public │ │ ├── fonts │ │ │ ├── geist │ │ │ │ ├── GeistMonoVF--1.0.0.woff2 │ │ │ │ └── GeistVF--1.0.0.woff2 │ │ │ └── inter │ │ │ │ ├── inter-4.0.0-beta9g-var.woff2 │ │ │ │ ├── inter-var-full.woff2 │ │ │ │ ├── inter-var-italic.woff2 │ │ │ │ ├── inter-var-latin.woff2 │ │ │ │ └── inter-var.woff2 │ │ ├── humans.txt │ │ ├── images │ │ │ ├── cursors │ │ │ │ └── pink │ │ │ │ │ ├── cursor-arrow.svg │ │ │ │ │ ├── cursor-close.svg │ │ │ │ │ ├── cursor-grab.svg │ │ │ │ │ ├── cursor-grabbing.svg │ │ │ │ │ └── cursor-pointer.svg │ │ │ ├── favicon │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── browserconfig.xml │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ ├── favicon.ico │ │ │ │ ├── mstile-150x150.png │ │ │ │ ├── safari-pinned-tab.svg │ │ │ │ └── site.webmanifest │ │ │ └── logos │ │ │ │ ├── arcade-comedy-theater.png │ │ │ │ ├── broadway-world--black.png │ │ │ │ ├── broadway-world--white.png │ │ │ │ ├── broadway-world.jpeg │ │ │ │ ├── broadway-world.png │ │ │ │ ├── pittsburgh-city-paper.png │ │ │ │ └── pittsburgh-magazine.png │ │ └── xml │ │ │ └── podcasts.xls │ ├── release.config.mjs │ ├── src │ │ ├── app │ │ │ ├── (pages) │ │ │ │ ├── about │ │ │ │ │ ├── _components │ │ │ │ │ │ ├── Section.Client.tsx │ │ │ │ │ │ └── Section.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── colophon │ │ │ │ │ └── page.tsx │ │ │ │ └── currently │ │ │ │ │ ├── listening-to │ │ │ │ │ ├── _components │ │ │ │ │ │ └── Music.client.tsx │ │ │ │ │ └── page.tsx │ │ │ │ │ └── reading │ │ │ │ │ ├── _components │ │ │ │ │ └── Book.client.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── (segments) │ │ │ │ ├── blog │ │ │ │ │ ├── [...key] │ │ │ │ │ │ ├── _components │ │ │ │ │ │ │ └── Blog.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── _components │ │ │ │ │ │ └── List.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── books │ │ │ │ │ ├── [key] │ │ │ │ │ │ ├── _components │ │ │ │ │ │ │ └── Book.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── _components │ │ │ │ │ │ └── List.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── events │ │ │ │ │ ├── [...key] │ │ │ │ │ │ ├── _components │ │ │ │ │ │ │ ├── Event.Data.List.tsx │ │ │ │ │ │ │ ├── Event.Slug.Header.Data.tsx │ │ │ │ │ │ │ └── Event.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── _components │ │ │ │ │ │ ├── List.Client.tsx │ │ │ │ │ │ ├── List.Deprecated.tsx │ │ │ │ │ │ ├── List.Temp.tsx │ │ │ │ │ │ └── List.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── pages │ │ │ │ │ └── page.tsx │ │ │ │ ├── podcasts │ │ │ │ │ ├── [key] │ │ │ │ │ │ ├── [key-episode] │ │ │ │ │ │ │ ├── _components │ │ │ │ │ │ │ │ └── Episode.tsx │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ │ ├── _components │ │ │ │ │ │ │ └── Podcast.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── _components │ │ │ │ │ │ └── List.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── shows │ │ │ │ │ ├── [key] │ │ │ │ │ │ ├── _components │ │ │ │ │ │ │ ├── Show.Slug.Header.Data.tsx │ │ │ │ │ │ │ └── Show.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── _components │ │ │ │ │ │ └── List.tsx │ │ │ │ │ └── page.tsx │ │ │ │ └── venues │ │ │ │ │ ├── [key] │ │ │ │ │ ├── _components │ │ │ │ │ │ └── Venue.tsx │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── _components │ │ │ │ │ └── List.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── _next │ │ │ │ ├── fonts.tsx │ │ │ │ ├── fonts │ │ │ │ │ ├── borogodo.tsx │ │ │ │ │ └── geist.tsx │ │ │ │ └── preload-resources.tsx │ │ │ ├── api │ │ │ │ └── v1 │ │ │ │ │ ├── music │ │ │ │ │ └── [slug] │ │ │ │ │ │ └── route.ts │ │ │ │ │ └── revalidate │ │ │ │ │ ├── actions.ts │ │ │ │ │ └── route.ts │ │ │ ├── apple-icon.png │ │ │ ├── favicon.ico │ │ │ ├── icon.png │ │ │ ├── icon1.png │ │ │ ├── layout.tsx │ │ │ ├── not-found.tsx │ │ │ ├── page.tsx │ │ │ ├── sitemap.ts │ │ │ ├── styles--globals.css │ │ │ └── styles--radix-themes-tw.css │ │ ├── components │ │ │ ├── Accordion │ │ │ │ ├── AccordionContent.tsx │ │ │ │ ├── AccordionItem.tsx │ │ │ │ ├── AccordionList.tsx │ │ │ │ ├── AccordionListItem.tsx │ │ │ │ ├── AccordionRoot.tsx │ │ │ │ ├── AccordionTrigger.tsx │ │ │ │ └── index.ts │ │ │ ├── Analytics │ │ │ │ ├── Analytics.tsx │ │ │ │ ├── Fathom.tsx │ │ │ │ ├── Vercel.tsx │ │ │ │ └── index.ts │ │ │ ├── Anchor │ │ │ │ ├── Anchor.tsx │ │ │ │ └── index.ts │ │ │ ├── Article │ │ │ │ ├── Article.Main.CTA.tsx │ │ │ │ └── Article.Main.tsx │ │ │ ├── Callout │ │ │ │ ├── Callout.tsx │ │ │ │ └── index.ts │ │ │ ├── Container │ │ │ │ ├── Container.Footer.Client.tsx │ │ │ │ ├── Container.Footer.tsx │ │ │ │ ├── Container.Gradient.tsx │ │ │ │ ├── Container.Main.tsx │ │ │ │ ├── Container.Navigation.tsx │ │ │ │ ├── Container.Section.tsx │ │ │ │ └── Container.Site.tsx │ │ │ ├── Credits │ │ │ │ ├── Credits.Header.tsx │ │ │ │ ├── Credits.Item.tsx │ │ │ │ ├── Credits.Items.tsx │ │ │ │ ├── Credits.Loading.tsx │ │ │ │ ├── Credits.tsx │ │ │ │ └── Credits.utils.ts │ │ │ ├── Currently │ │ │ │ ├── Currently.Event.tsx │ │ │ │ ├── Currently.Item.Wrapper.tsx │ │ │ │ ├── Currently.Item.tsx │ │ │ │ ├── Currently.Music.Client.tsx │ │ │ │ └── Currently.tsx │ │ │ ├── ErrorBoundary │ │ │ │ ├── ErrorBoundary.tsx │ │ │ │ └── index.ts │ │ │ ├── Header │ │ │ │ ├── Header.Full.tsx │ │ │ │ ├── Header.Sidebar.CTA.tsx │ │ │ │ └── Header.Sidebar.tsx │ │ │ ├── Icon │ │ │ │ ├── Icon.stories.tsx │ │ │ │ ├── Icon.test.tsx │ │ │ │ ├── Icon.tsx │ │ │ │ ├── Icon.types.ts │ │ │ │ └── index.ts │ │ │ ├── Image │ │ │ │ └── Image.Notion.tsx │ │ │ ├── List │ │ │ │ ├── LI.tsx │ │ │ │ ├── OL.tsx │ │ │ │ ├── UL.tsx │ │ │ │ └── index.ts │ │ │ ├── Navigation │ │ │ │ ├── Navigation.Button.Client.tsx │ │ │ │ ├── Navigation.Button.tsx │ │ │ │ ├── Navigation.Primary.tsx │ │ │ │ ├── Navigation.Secondary.tsx │ │ │ │ ├── Navigation.Separator.tsx │ │ │ │ ├── Navigation.Tertiary.tsx │ │ │ │ └── Navigation.tsx │ │ │ ├── Notion │ │ │ │ └── Blocks │ │ │ │ │ ├── Image.client.tsx │ │ │ │ │ ├── Image.tsx │ │ │ │ │ └── Image.utils.ts │ │ │ ├── Overlay │ │ │ │ └── Overlay.tsx │ │ │ ├── Providers │ │ │ │ ├── Providers.client.tsx │ │ │ │ ├── Providers.tsx │ │ │ │ ├── RouterEventProvider.Loading.client.tsx │ │ │ │ ├── RouterEventProvider.client.tsx │ │ │ │ ├── StoreInitEventsUpcoming.client.tsx │ │ │ │ ├── StoreProvider.client.tsx │ │ │ │ └── ThemeProvider.client.tsx │ │ │ ├── SkipNav │ │ │ │ ├── SkipNav.tsx │ │ │ │ └── index.ts │ │ │ └── Tags │ │ │ │ └── Tags.tsx │ │ ├── config │ │ │ ├── const.ts │ │ │ ├── next.config.env.client.mjs │ │ │ ├── next.config.env.server.mjs │ │ │ └── radix-themes-tw.css │ │ ├── data │ │ │ ├── bandcamps.ts │ │ │ ├── currently.tsx │ │ │ └── socials.tsx │ │ ├── e2e │ │ │ └── index.e2e.ts │ │ ├── hooks │ │ │ └── useSWRInfinitePages.ts │ │ ├── lib │ │ │ ├── constants.ts │ │ │ ├── drizzle │ │ │ │ ├── index.ts │ │ │ │ ├── init │ │ │ │ │ ├── migrate.ts │ │ │ │ │ ├── migrations │ │ │ │ │ │ ├── 0000_grey_hiroim.sql │ │ │ │ │ │ └── meta │ │ │ │ │ │ │ ├── 0000_snapshot.json │ │ │ │ │ │ │ └── _journal.json │ │ │ │ │ ├── seed.ts │ │ │ │ │ └── setup.ts │ │ │ │ ├── schemas │ │ │ │ │ ├── _notion │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-blocks │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-blogs │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-books │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-episodes │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-events │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-images │ │ │ │ │ │ ├── actions.ts │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-pages │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-podcasts │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-shows │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── cache-sites │ │ │ │ │ │ └── schemas.ts │ │ │ │ │ ├── cache-venues │ │ │ │ │ │ ├── queries.ts │ │ │ │ │ │ ├── schemas.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── helpers.ts │ │ │ │ │ ├── helpers.types.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── queries.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils │ │ │ │ │ ├── addItemToCache.ts │ │ │ │ │ ├── getImageKeySlug.ts │ │ │ │ │ ├── getKeyValue.ts │ │ │ │ │ ├── getKeyValues.ts │ │ │ │ │ └── index.ts │ │ │ ├── fetcher.ts │ │ │ ├── notion │ │ │ │ ├── Notion.Blocks.tsx │ │ │ │ ├── Notion.Component.tsx │ │ │ │ ├── Notion.utils.tsx │ │ │ │ ├── blocks │ │ │ │ │ ├── Column.tsx │ │ │ │ │ ├── Divider.tsx │ │ │ │ │ ├── Emoji.client.tsx │ │ │ │ │ ├── Emoji.server.tsx │ │ │ │ │ ├── Emoji.tsx │ │ │ │ │ ├── ListBulleted.tsx │ │ │ │ │ ├── ListColumn.tsx │ │ │ │ │ └── RichText.tsx │ │ │ │ ├── buildInitialCache.ts │ │ │ │ ├── config.default.ts │ │ │ │ ├── config.ts │ │ │ │ ├── getAwsImage.ts │ │ │ │ ├── getImageAlt.ts │ │ │ │ ├── index.ts │ │ │ │ └── notion.ts │ │ │ ├── plaiceholder │ │ │ │ ├── getImage.ts │ │ │ │ └── index.ts │ │ │ └── upstash │ │ │ │ ├── getCache.ts │ │ │ │ ├── getKey.ts │ │ │ │ ├── index.ts │ │ │ │ ├── redis.ts │ │ │ │ └── setCache.ts │ │ ├── store │ │ │ └── index.tsx │ │ └── utils │ │ │ ├── cx.ts │ │ │ ├── getBySegment.ts │ │ │ ├── getKey.ts │ │ │ ├── getKeySpotify.ts │ │ │ ├── getPlaceholder.ts │ │ │ ├── getPlaiceholder.ts │ │ │ ├── getTitleData.ts │ │ │ ├── isEmpty.ts │ │ │ ├── isObjectEmpty.ts │ │ │ ├── next │ │ │ └── getSegmentsForGenerateStaticParams.ts │ │ │ └── uuid.ts │ ├── tsconfig.json │ └── vercel.json ├── justinandjerome.com │ └── .gitkeep ├── nicegroupofpeople.com │ └── .gitkeep ├── nicerec.com │ └── .gitkeep ├── sarahfitzgerald.me │ └── .gitkeep ├── theeparodybros.com │ └── .gitkeep └── wkspizza.com │ └── .gitkeep ├── tsconfig.json ├── tsup.config.ts └── turbo.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [*.js] 16 | quote_type = single 17 | 18 | [{*.c,*.cc,*.h,*.hh,*.cpp,*.hpp,*.m,*.mm,*.mpp,*.js,*.java,*.go,*.rs,*.php,*.ng,*.jsx,*.ts,*.d,*.cs,*.swift}] 19 | curly_bracket_next_line = false 20 | spaces_around_operators = true 21 | spaces_around_brackets = outside 22 | # close enough to 1TB 23 | indent_brace_style = K&R 24 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | TURBO_TEAM= 2 | TURBO_TOKEN= 3 | TZ=UTC 4 | VERCEL_ENV= 5 | # @note(husky) 🚩 Feature Flags: Opt-out by setting to 0 6 | HUSKY__PRE_COMMIT__LINT_STAGED=1 7 | HUSKY__PREPARE_COMMITMSG__CONVENTIONAL=1 8 | # @note(next) 🔐 Required for: jeromefitzgerald.com 9 | GH_TOKEN= 10 | LHCI_GITHUB_APP_TOKEN= 11 | NEXT_PUBLIC__FATHOM_SITE_ID= 12 | NEXT_PUBLIC__SITE= 13 | NOTION_API_KEY= 14 | OCTOKIT_TOKEN= 15 | OG_API_KEY= 16 | PREVIEW_TOKEN= 17 | REVALIDATE_TOKEN= 18 | SPOTIFY_CLIENT_ID= 19 | SPOTIFY_CLIENT_SECRET= 20 | SPOTIFY_REFRESH_TOKEN= 21 | UPSTASH_REDIS_REST_URL= 22 | UPSTASH_REDIS_REST_TOKEN= 23 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.github/.kodiak.toml: -------------------------------------------------------------------------------- 1 | # .kodiak.toml 2 | # docs: https://kodiakhq.com/docs/config-reference 3 | 4 | version = 1 5 | 6 | [approve] 7 | #auto_approve_usernames = ["renovate"] 8 | 9 | [merge] 10 | automerge_label = [" 🥳️ LGTM", "🥳️ LGTM","🥳️ LGTM","LGTM"] 11 | blocking_labels = [" 🚧️ WIP", "🚧️ WIP","🚧️ WIP","WIP"] 12 | delete_branch_on_merge = true 13 | method = "squash" 14 | optimistic_updates = true 15 | prioritize_ready_to_merge = true 16 | require_automerge_label = true 17 | 18 | [merge.automerge_dependencies] 19 | #usernames=["renovate","renovate[bot]","renovate%5Bbot%5D"] 20 | #versions=["minor", "patch"] 21 | 22 | [merge.message] 23 | body = "pull_request_body" 24 | body_type = "markdown" 25 | cut_body_after = "" 26 | cut_body_and_text = true 27 | cut_body_before = "" 28 | include_pr_number = true 29 | strip_html_comments = true 30 | title = "pull_request_title" 31 | 32 | [update] 33 | always = true 34 | #ignored_usernames = ["renovate"] 35 | autoupdate_label = "🔄️ Conflict" 36 | -------------------------------------------------------------------------------- /.github/actions/init/action.yml: -------------------------------------------------------------------------------- 1 | # name: 'Init' 2 | name: '💽️ ' 3 | description: 'Localized Init' 4 | author: 'JeromeFitz' 5 | 6 | inputs: 7 | node-version: 8 | default: '22' 9 | description: '🔢 Node version' 10 | required: true 11 | 12 | runs: 13 | using: 'composite' 14 | steps: 15 | - name: '🔧 pnpm: Install' 16 | id: pnpm-setup 17 | uses: pnpm/action-setup@v4 18 | with: 19 | run_install: false 20 | 21 | - name: '💽️ Node ${{ inputs.node-version }}' 22 | id: node-setup 23 | uses: actions/setup-node@v4 24 | with: 25 | cache-dependency-path: pnpm-lock.yaml 26 | cache: 'pnpm' 27 | node-version: ${{ inputs.node-version }} 28 | -------------------------------------------------------------------------------- /.github/actions/install/action.yml: -------------------------------------------------------------------------------- 1 | # name: 'Install' 2 | name: '📦️ ' 3 | description: 'Localized Install' 4 | author: 'JeromeFitz' 5 | 6 | runs: 7 | using: 'composite' 8 | steps: 9 | - name: '📦️ Dependencies' 10 | id: dependencies 11 | shell: bash 12 | run: | 13 | pnpm install --frozen-lockfile 14 | -------------------------------------------------------------------------------- /.github/actions/lint/action.yml: -------------------------------------------------------------------------------- 1 | # name: 'Lint' 2 | name: '🚨️ ' 3 | description: 'Localized Lint' 4 | author: 'JeromeFitz' 5 | 6 | inputs: 7 | TURBO_TEAM: 8 | description: 'Provide Team ID for Turbo' 9 | required: true 10 | type: string 11 | TURBO_TOKEN: 12 | description: 'Provide Token for Turbo' 13 | required: true 14 | type: string 15 | 16 | runs: 17 | using: 'composite' 18 | steps: 19 | # @todo(gh-actions) Separate `main` from PR 20 | - name: '🎨 Format' 21 | id: format 22 | shell: bash 23 | env: 24 | TURBO_TEAM: ${{ inputs.TURBO_TEAM }} 25 | TURBO_TOKEN: ${{ inputs.TURBO_TOKEN }} 26 | run: | 27 | pnpm run format:prettier:check 28 | 29 | # @todo(gh-actions) Separate `main` from PR 30 | - name: '🚨️ Lint' 31 | id: lint 32 | shell: bash 33 | env: 34 | TURBO_TEAM: ${{ inputs.TURBO_TEAM }} 35 | TURBO_TOKEN: ${{ inputs.TURBO_TOKEN }} 36 | run: | 37 | pnpm turbo run lint --cache-dir=".cache-turbo" 38 | -------------------------------------------------------------------------------- /.github/actions/release/action.yml: -------------------------------------------------------------------------------- 1 | # name: 'Release' 2 | name: '🏷️ ' 3 | description: 'Localized Release' 4 | author: 'JeromeFitz' 5 | 6 | inputs: 7 | GH_TOKEN: 8 | descirption: 'Provide GitHub Token' 9 | required: true 10 | type: string 11 | 12 | runs: 13 | using: 'composite' 14 | steps: 15 | - name: '🏷️ Release' 16 | id: release 17 | shell: bash 18 | env: 19 | GH_TOKEN: ${{ inputs.GH_TOKEN }} 20 | run: | 21 | pnpm semantic-release 22 | -------------------------------------------------------------------------------- /.github/actions/test/action.yml: -------------------------------------------------------------------------------- 1 | # name: 'Test' 2 | name: '🧪 ' 3 | description: 'Localized Test' 4 | author: 'JeromeFitz' 5 | 6 | inputs: 7 | TURBO_TEAM: 8 | description: 'Provide Team ID for Turbo' 9 | required: true 10 | type: string 11 | TURBO_TOKEN: 12 | description: 'Provide Token for Turbo' 13 | required: true 14 | type: string 15 | WEBSITE: 16 | description: 'Provide Website for Lighthouse' 17 | required: true 18 | type: string 19 | default: 'jeromefitzgerald.com' 20 | 21 | runs: 22 | using: 'composite' 23 | steps: 24 | - name: '🃏 Jest: unit' 25 | id: test-jest 26 | shell: bash 27 | run: pnpm turbo run test:unit --filter="${{ inputs.WEBSITE }}" --cache-dir=".cache-turbo" 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # vercel 12 | .next 13 | .turbo 14 | 15 | # next-pwa 16 | /public/fallback-*.js 17 | /public/sw.js 18 | /public/workbox-*.js 19 | 20 | # production 21 | dist 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # local env files 33 | .env 34 | .env.* 35 | !.env.example 36 | # .env.build 37 | # .env.local 38 | # .env.development.local 39 | # .env.test.local 40 | # .env.production.local 41 | 42 | # vercel 43 | .vercel 44 | 45 | ## Custom 46 | .cache 47 | .cache-turbo 48 | .idea 49 | .lighthouseci 50 | .swc 51 | .tsbuildinfo 52 | build-info.json 53 | coverage 54 | e2e-report 55 | e2e-results 56 | out 57 | robots.txt 58 | sitemap.xml 59 | sitemap-*.xml 60 | storybook-static 61 | tsconfig.tsbuildinfo 62 | tsconfig.*.tsbuildinfo 63 | .vscode/** 64 | !.vscode/extensions.json 65 | !.vscode/settings.default.json 66 | 67 | ## ZZZ 68 | zzz 69 | 70 | # next 71 | sites/**/src/styles/output.css 72 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | [ -n "$CI" ] && exit 0 3 | 4 | FILE_ENV=.env 5 | if [ -f "$FILE_ENV" ]; then 6 | # shellcheck disable=SC2046 7 | [ ! -f "$FILE_ENV" ] || export $(grep -v '^#' $FILE_ENV | xargs) 8 | fi 9 | 10 | # @todo(husky) In POSIX sh, [[ ]] is undefined. 11 | # shellcheck disable=SC3010 12 | # shellcheck disable=SC2009 13 | if ps -o args= $PPID | grep -E -q ' --no-verify| -n | -n$' ; then 14 | exit 0 15 | fi 16 | 17 | if [ "$HUSKY" = "0" ]; then 18 | exit 0 19 | fi 20 | 21 | if [ ! "$HUSKY__PRE_COMMIT__LINT_STAGED" = "0" ]; then 22 | pnpm lint-staged 23 | pnpm run lint:packages 24 | fi 25 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | [ -n "$CI" ] && exit 0 3 | 4 | FILE_ENV=.env 5 | if [ -f "$FILE_ENV" ]; then 6 | # shellcheck disable=SC2046 7 | [ ! -f "$FILE_ENV" ] || export $(grep -v '^#' $FILE_ENV | xargs) 8 | fi 9 | 10 | if [ "$HUSKY" = "0" ]; then 11 | exit 0 12 | fi 13 | 14 | if [ ! "$HUSKY__PREPARE_COMMITMSG__CONVENTIONAL" = "0" ]; then 15 | exec < /dev/tty 16 | pnpm ccommit --hook || true 17 | fi 18 | -------------------------------------------------------------------------------- /.lintstagedrc.mjs: -------------------------------------------------------------------------------- 1 | import config from '@jeromefitz/lint-staged' 2 | 3 | export default config 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=false 2 | dedupe-peer-dependents=false 3 | ignore-dep-scripts=false 4 | enable-pre-post-scripts=true # next-sitemap requirement 5 | lockfile=true 6 | registry=https://registry.npmjs.org/ 7 | save-exact=true 8 | strict-peer-dependencies=false 9 | tag-version-prefix="" 10 | # https://pnpm.io/npmrc#public-hoist-pattern 11 | public-hoist-pattern[]=*ccommit* 12 | public-hoist-pattern[]=*eslint* 13 | public-hoist-pattern[]=*jest* 14 | public-hoist-pattern[]=*lint-staged* 15 | public-hoist-pattern[]=*playwright* 16 | public-hoist-pattern[]=*prettier* 17 | public-hoist-pattern[]=*semantic* 18 | public-hoist-pattern[]=*storybook* 19 | public-hoist-pattern[]=*testing-library* 20 | public-hoist-pattern[]=*types* 21 | # @note(pnpm) avoid if possible 22 | # shamefully-hoist=true 23 | 24 | # # # # # 25 | # # 26 | # # @hack(turbopack) for localized development (not ci) 27 | # # does not work yet, please keep using webpack 28 | # # 29 | # # -arch=arm64 --platform=darwin turbo 30 | # # reference: https://pnpm.io/pnpm-cli#differences-vs-npm 31 | # # 32 | # # npm_config_target_arch=arm64 33 | # # npm_config_target_platform=darwin 34 | # # 35 | # # pnpm install --config.target_arch=arm64 --config.target_platform=darwin --force 36 | # # 37 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | /.yarn 4 | .yarn 5 | **/_next/** 6 | **/.cache/** 7 | **/.cache-turbo/** 8 | **/.dist/** 9 | **/.lighthouseci/** 10 | **/.next/** 11 | **/.now/** 12 | **/.swc/** 13 | **/.types/** 14 | **/.vercel/** 15 | **/.vercel*/** 16 | **/bundles/** 17 | **/coverage/** 18 | **/dist/** 19 | **/e2e-report/** 20 | **/e2e-results/** 21 | **/out/** 22 | **/storybook-static/** 23 | 24 | **/deprecated/** 25 | **/fonts/** 26 | !/styles/fonts/** 27 | **/zzz--do-not-use/** 28 | **/zzz/** 29 | 30 | public/scripts/** 31 | next-env.d.ts 32 | 33 | # next-pwa 34 | /public/fallback-*.js 35 | /public/sw.js 36 | /public/workbox-*.js 37 | 38 | # transition 39 | todo/** 40 | 41 | # lock 42 | package-lock.json 43 | pnpm-lock.yaml 44 | yarn.lock 45 | .pnpm-store 46 | -------------------------------------------------------------------------------- /.prettierignore_staged: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | /.yarn 4 | .yarn 5 | **/_next/** 6 | **/.cache/** 7 | **/.cache-turbo/** 8 | **/.dist/** 9 | **/.lighthouseci/** 10 | **/.next/** 11 | **/.now/** 12 | **/.swc/** 13 | **/.types/** 14 | **/.vercel/** 15 | **/.vercel*/** 16 | **/bundles/** 17 | **/coverage/** 18 | **/dist/** 19 | **/e2e-report/** 20 | **/e2e-results/** 21 | **/out/** 22 | **/storybook-static/** 23 | 24 | **/deprecated/** 25 | **/fonts/** 26 | **/zzz--do-not-use/** 27 | **/zzz/** 28 | 29 | public/scripts/** 30 | next-env.d.ts 31 | 32 | # next-pwa 33 | /public/fallback-*.js 34 | /public/sw.js 35 | /public/workbox-*.js 36 | 37 | # transition 38 | todo/** 39 | 40 | # lock 41 | package-lock.json 42 | pnpm-lock.yaml 43 | yarn.lock 44 | .pnpm-store 45 | -------------------------------------------------------------------------------- /.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | import config from '@jeromefitz/prettier-config/tailwind' 2 | 3 | export default config 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "davidanson.vscode-markdownlint", 4 | "dbaeumer.vscode-eslint", 5 | "eamodio.gitlens", 6 | "editorconfig.editorconfig", 7 | "esbenp.prettier-vscode", 8 | "mgmcdermott.vscode-language-babel", 9 | "dangmai.workspace-default-settings" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.default.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2024 Nice Group of People, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /changelog.config.mjs: -------------------------------------------------------------------------------- 1 | import isCI from 'is-ci' 2 | 3 | if (!isCI) { 4 | const dotenv = await import('dotenv') 5 | dotenv.config({ path: './.env' }) 6 | } 7 | 8 | const isOverride = process.env.GIT_CZ__OVERRIDE_TEST || false 9 | 10 | const enabled = isOverride 11 | 12 | const _types = {} 13 | 14 | const commit = isOverride 15 | ? { 16 | after: { branchName: ' ', emoji: ' ', scope: ') ' }, 17 | before: { branchName: '', emoji: '', scope: '(' }, 18 | format: '{emoji}{scope}{branchName}{subject}', 19 | maxMessageLength: 64, 20 | minMessageLength: 3, 21 | questions: [ 22 | 'branchFlag', 23 | 'commitBreakingFlag', 24 | 'commitBreaking', 25 | // 'commitScopes', 26 | 'commitTypes', 27 | 'commitSubject', 28 | 'commitBodyFlag', 29 | 'commitBody', 30 | ], 31 | scopes: [], 32 | } 33 | : {} 34 | 35 | const branch = {} 36 | 37 | const types = isOverride ? _types : {} 38 | 39 | const changelog = { branch, commit, enabled, types } 40 | 41 | export default changelog 42 | -------------------------------------------------------------------------------- /config/release-branch-types/ci.cjs: -------------------------------------------------------------------------------- 1 | const ci = [] 2 | 3 | module.exports = ci 4 | -------------------------------------------------------------------------------- /config/release-branch-types/feature.cjs: -------------------------------------------------------------------------------- 1 | const feature = [] 2 | 3 | module.exports = feature 4 | -------------------------------------------------------------------------------- /config/release-branch-types/fix.cjs: -------------------------------------------------------------------------------- 1 | const fix = [] 2 | 3 | module.exports = fix 4 | -------------------------------------------------------------------------------- /config/release-branch-types/index.cjs: -------------------------------------------------------------------------------- 1 | const ci = require('./ci.cjs') 2 | const feature = require('./feature.cjs') 3 | const fix = require('./fix.cjs') 4 | const refactor = require('./refactor.cjs') 5 | const release = require('./release.cjs') 6 | 7 | /** 8 | * @todo To get a `prerelease` name these cannot be passed as RegEx. 9 | * With Node we can dynamically determine if it meets a RegEx 10 | * requirement and pass statically though (down the line). 11 | * 12 | * @todo We can "tell" what branch we are on prior to generation 13 | * and may be able to pass `feature/xyz` directly without 14 | * needing this set list to be the same for each branch in gitflow 15 | */ 16 | const releaseBranchTypes = { 17 | ci, 18 | feature, 19 | fix, 20 | release, 21 | refactor, 22 | } 23 | 24 | module.exports = releaseBranchTypes 25 | -------------------------------------------------------------------------------- /config/release-branch-types/refactor.cjs: -------------------------------------------------------------------------------- 1 | const refactor = [] 2 | 3 | module.exports = refactor 4 | -------------------------------------------------------------------------------- /config/release-branch-types/release.cjs: -------------------------------------------------------------------------------- 1 | const release = [] 2 | 3 | module.exports = release 4 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png' { 2 | const value: string 3 | export default value 4 | } 5 | 6 | declare module '*.svg' { 7 | const value: string 8 | export default value 9 | } 10 | 11 | type Format = 'png' | 'jpeg' 12 | type Theme = 'light' | 'dark' 13 | 14 | interface Image { 15 | src: string 16 | width: string 17 | height: string 18 | } 19 | -------------------------------------------------------------------------------- /node-modules-inspector.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'node-modules-inspector' 2 | 3 | export default defineConfig({ 4 | defaultFilters: { 5 | excludeDts: true, 6 | excludes: ['*eslint*', '*lint-staged*', '*prettier*', '*semantic*'], 7 | excludeWorkspace: true, 8 | }, 9 | defaultSettings: { 10 | moduleTypeSimple: true, 11 | }, 12 | 13 | publint: true, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/design-system/.storybook/font.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-display: swap; 3 | font-family: 'Inter'; 4 | font-style: normal; 5 | font-weight: 100 900; 6 | src: url('/public/static/fonts/inter/inter-4.0.0-beta9g-var.woff2') format('woff2'); 7 | } 8 | -------------------------------------------------------------------------------- /packages/design-system/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | export { default } from '@jeromefitz/storybook-config/main.config' 2 | -------------------------------------------------------------------------------- /packages/design-system/.storybook/manager.ts: -------------------------------------------------------------------------------- 1 | import '@jeromefitz/storybook-config/manager.config' 2 | -------------------------------------------------------------------------------- /packages/design-system/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import './font.css' 2 | 3 | import '@jeromefitz/tailwind-config/styles/globals.css' 4 | 5 | import '@radix-ui/themes/styles.css' 6 | 7 | export { default } from '@jeromefitz/storybook-config/preview.config' 8 | -------------------------------------------------------------------------------- /packages/design-system/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@jeromefitz/jest-config/jest.config.js') 2 | -------------------------------------------------------------------------------- /packages/design-system/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('node:path') 2 | 3 | const isCI = require('is-ci') 4 | !isCI && require('dotenv').config({ path: './.env' }) 5 | 6 | const buildInfoConfig = { 7 | owner: 'jeromefitz', 8 | repo: 'jeromefitzgerald.com', 9 | } 10 | 11 | const serverComponentsExternalPackages = [] 12 | const transpilePackages = [] 13 | 14 | module.exports = require('@jeromefitz/next-config')({ 15 | basePath: '', 16 | buildInfoConfig, 17 | pathDirName: path.join(__dirname), 18 | serverComponentsExternalPackages, 19 | transpilePackages, 20 | }) 21 | -------------------------------------------------------------------------------- /packages/design-system/postcss.config.js: -------------------------------------------------------------------------------- 1 | import configPostcss from '@jeromefitz/tailwind-config/postcss.config.js' 2 | 3 | export default configPostcss 4 | -------------------------------------------------------------------------------- /packages/design-system/public/static/fonts/inter/inter-4.0.0-beta9g-var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/packages/design-system/public/static/fonts/inter/inter-4.0.0-beta9g-var.woff2 -------------------------------------------------------------------------------- /packages/design-system/public/static/fonts/inter/inter-var-full.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/packages/design-system/public/static/fonts/inter/inter-var-full.woff2 -------------------------------------------------------------------------------- /packages/design-system/public/static/fonts/inter/inter-var-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/packages/design-system/public/static/fonts/inter/inter-var-italic.woff2 -------------------------------------------------------------------------------- /packages/design-system/public/static/fonts/inter/inter-var-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/packages/design-system/public/static/fonts/inter/inter-var-latin.woff2 -------------------------------------------------------------------------------- /packages/design-system/public/static/fonts/inter/inter-var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/packages/design-system/public/static/fonts/inter/inter-var.woff2 -------------------------------------------------------------------------------- /packages/design-system/scripts/copy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### 4 | # @note(build): NPM Publishes from `./dist` 5 | ### 6 | 7 | cp package.json ./dist/ 8 | # cp README.md ./dist/ 9 | # cp ../../LICENSE ./dist/ 10 | 11 | ### 12 | # @note(build): Replace `dist/index` w/ `index` 13 | ### 14 | 15 | if [[ "$OSTYPE" == "darwin"* ]]; then 16 | sed -i "" "s|dist/index|index|g" dist/package.json 17 | else 18 | sed -i -e "s|dist/index|index|g" dist/package.json 19 | fi 20 | 21 | ### 22 | # @custom(build) 23 | ### 24 | 25 | if [[ "$OSTYPE" == "darwin"* ]]; then 26 | if [[ "$OSTYPE" == "darwin"* ]]; then 27 | sed -i "" "s|dist/||g" dist/package.json 28 | else 29 | sed -i -e "s|dist/||g" dist/package.json 30 | fi 31 | else 32 | sed -i -e "s|dist/||g" dist/package.json 33 | fi 34 | 35 | if [[ "$OSTYPE" == "darwin"* ]]; then 36 | if [[ "$OSTYPE" == "darwin"* ]]; then 37 | sed -i "" "s|src/||g" dist/package.json 38 | else 39 | sed -i -e "s|src/||g" dist/package.json 40 | fi 41 | else 42 | sed -i -e "s|src/||g" dist/package.json 43 | fi 44 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Anchor/index.ts: -------------------------------------------------------------------------------- 1 | export { Anchor } from './Anchor' 2 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Callout/Callout.tsx: -------------------------------------------------------------------------------- 1 | import type { RootProps as CalloutRootProps } from '@radix-ui/themes/dist/esm/components/callout.js' 2 | import type { ReactNode } from 'react' 3 | 4 | import { 5 | Icon as CalloutIcon, 6 | Root as CalloutRoot, 7 | Text as CalloutText, 8 | } from '@radix-ui/themes/dist/esm/components/callout.js' 9 | 10 | import { cx } from '../../utils/cx' 11 | import { FileTextIcon } from '../Icon/index' 12 | 13 | interface AdditionalProps { 14 | children?: ReactNode 15 | className?: string 16 | classNameText?: string 17 | color?: string 18 | icon?: any 19 | } 20 | type CalloutRootPropsImpl = AdditionalProps & CalloutRootProps 21 | 22 | function CalloutImpl({ 23 | children = <>This page is in the process of being updated.>, 24 | className = '', 25 | classNameText = '', 26 | color = 'pink', 27 | icon: Icon = FileTextIcon, 28 | size = '2', 29 | variant = 'soft', 30 | }: CalloutRootPropsImpl) { 31 | return ( 32 | 38 | 39 | 40 | 41 | {children} 42 | 43 | ) 44 | } 45 | 46 | export { CalloutImpl as Callout } 47 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Callout/index.ts: -------------------------------------------------------------------------------- 1 | export { Callout } from './Callout' 2 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Icon/Icon.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { cx } from '../../utils/cx' 4 | import { MapIcon } from './Icon' 5 | 6 | const meta = { 7 | component: MapIcon, 8 | // @todo(eslint) storybook/no-title-property-in-meta 9 | title: 'Components/Icon', 10 | } satisfies Meta 11 | 12 | export default meta 13 | 14 | type Story = StoryObj 15 | 16 | export const Default: Story = { 17 | args: { 18 | className: cx(), 19 | }, 20 | argTypes: {}, 21 | } 22 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Icon/Icon.test.tsx: -------------------------------------------------------------------------------- 1 | import { expect } from '@jest/globals' 2 | import { composeStories } from '@storybook/react' 3 | import { render, screen } from '@testing-library/react' 4 | 5 | import * as stories from './Icon.stories' 6 | 7 | const IconStories = composeStories(stories) 8 | 9 | describe('Icon', () => { 10 | it('should contain a svg', () => { 11 | render() 12 | 13 | const icon = screen.getByRole('info', { hidden: true }) 14 | 15 | expect(icon).toBeInTheDocument() 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Icon/Icon.types.ts: -------------------------------------------------------------------------------- 1 | import { SVGAttributes } from 'react' 2 | 3 | /** 4 | * ref: https://github.com/radix-ui/icons/blob/master/packages/radix-icons/src/types.tsx 5 | */ 6 | interface IconProps extends SVGAttributes { 7 | children?: never 8 | color?: string 9 | label?: string 10 | style?: any 11 | } 12 | 13 | export type { IconProps } 14 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/Section.Content.tsx: -------------------------------------------------------------------------------- 1 | import { cx } from '../../utils/cx' 2 | 3 | function SectionContent({ children, className = '' }) { 4 | return ( 5 | 24 | 27 | {children} 28 | 29 | 30 | ) 31 | } 32 | 33 | export { SectionContent } 34 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/Section.Header.Content.tsx: -------------------------------------------------------------------------------- 1 | import { cx } from '../../utils/cx' 2 | 3 | function SectionHeaderContent({ children, className = '' }) { 4 | return ( 5 | 13 | {children} 14 | 15 | ) 16 | } 17 | 18 | export { SectionHeaderContent } 19 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/Section.Header.Title.tsx: -------------------------------------------------------------------------------- 1 | import { cx } from '../../utils/cx' 2 | 3 | function SectionHeaderTitle({ children, className = '', isTitle = false }) { 4 | return ( 5 | 18 | {children} 19 | 20 | ) 21 | } 22 | 23 | export { SectionHeaderTitle } 24 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/Section.Header.tsx: -------------------------------------------------------------------------------- 1 | import { cx } from '../../utils/cx' 2 | 3 | function SectionHeader({ children, className = '' }) { 4 | return ( 5 | <> 6 | 29 | {children} 30 | 31 | > 32 | ) 33 | } 34 | 35 | export { SectionHeader } 36 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/Section.Hero.tsx: -------------------------------------------------------------------------------- 1 | function SectionHero({ title }) { 2 | return ( 3 | 4 | {title} 5 | 6 | ) 7 | } 8 | 9 | export { SectionHero } 10 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/Section.Wrapper.tsx: -------------------------------------------------------------------------------- 1 | import { cx } from '../../utils/cx' 2 | 3 | function SectionWrapper({ children }) { 4 | return ( 5 | 6 | 16 | {children} 17 | 18 | 19 | ) 20 | } 21 | 22 | export { SectionWrapper } 23 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/Section.test.ts: -------------------------------------------------------------------------------- 1 | describe('Section', () => { 2 | it('placeholder: moved to @jeromefitz/ds', () => { 3 | expect(true).toBe(true) 4 | }) 5 | }) 6 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/Tags.tsx: -------------------------------------------------------------------------------- 1 | import { cx } from '../../utils/cx' 2 | 3 | function Tags({ className = '', classNameTag = '', tags }) { 4 | // console.dir(`> tags:`) 5 | // console.dir(tags) 6 | return ( 7 | 14 | {tags?.map((tag) => { 15 | const { color, id, name } = tag 16 | return ( 17 | 36 | {name} 37 | 38 | ) 39 | })} 40 | 41 | ) 42 | } 43 | 44 | export { Tags } 45 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Section/index.ts: -------------------------------------------------------------------------------- 1 | export { SectionContent } from './Section.Content' 2 | export { SectionHeader } from './Section.Header' 3 | export { SectionHeaderContent } from './Section.Header.Content' 4 | export { SectionHeaderTitle } from './Section.Header.Title' 5 | export { SectionHero } from './Section.Hero' 6 | export { SectionWrapper } from './Section.Wrapper' 7 | export { Tags } from './Tags' 8 | -------------------------------------------------------------------------------- /packages/design-system/src/components/SkipNav/index.ts: -------------------------------------------------------------------------------- 1 | export { SkipNavContent, SkipNavLink } from './SkipNav' 2 | -------------------------------------------------------------------------------- /packages/design-system/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { Anchor } from './Anchor/index' 2 | export { Callout } from './Callout/index' 3 | export { Icon } from './Icon/index' 4 | export { SkipNavContent, SkipNavLink } from './SkipNav/index' 5 | -------------------------------------------------------------------------------- /packages/design-system/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components/index' 2 | -------------------------------------------------------------------------------- /packages/design-system/src/ui/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/packages/design-system/src/ui/.gitkeep -------------------------------------------------------------------------------- /packages/design-system/src/utils/cx.ts: -------------------------------------------------------------------------------- 1 | import { ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | function cx(...inputs: ClassValue[]) { 5 | // return twMerge(clsx(inputs)) 6 | return clsx(inputs) 7 | } 8 | 9 | export { cx, twMerge } 10 | -------------------------------------------------------------------------------- /packages/design-system/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "outDir": "dist", 6 | "preserveSymlinks": true, 7 | // "rootDir": "src", 8 | 9 | "plugins": [ 10 | { 11 | "name": "next" 12 | } 13 | ], 14 | "strict": true, 15 | "strictNullChecks": true, 16 | "paths": { 17 | "@jeromefitz/next-config/*": ["../../packages/next-config/src/*"] 18 | } 19 | }, 20 | "include": ["src"], 21 | "exclude": ["node_modules"] 22 | } 23 | -------------------------------------------------------------------------------- /packages/design-system/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from 'tsup' 2 | 3 | import { defineConfig } from 'tsup' 4 | 5 | import { config as _config } from '../../tsup.config' 6 | 7 | const entry = ['src/**'] 8 | const config: Options = { 9 | ..._config, 10 | entry, 11 | tsconfig: './tsconfig.build.json', 12 | } 13 | 14 | export default defineConfig({ 15 | ...config, 16 | }) 17 | -------------------------------------------------------------------------------- /packages/jest-config/jest.config.js: -------------------------------------------------------------------------------- 1 | const jestNode = require('@jeromefitz/jest-presets/jest/node/jest-preset') 2 | const jestNext = require('next/jest') 3 | 4 | /** @type {import('jest').Config} */ 5 | const config = { 6 | coverageReporters: ['text', 'html'], 7 | // preset: '@jeromefitz/jest-presets/jest/node', 8 | setupFilesAfterEnv: ['@jeromefitz/jest-config/jest.setup.js'], 9 | testEnvironment: 'jest-environment-jsdom', 10 | watchman: false, 11 | ...jestNode, 12 | } 13 | 14 | const defineConfig = jestNext({ dir: './' }) 15 | module.exports = defineConfig(config) 16 | -------------------------------------------------------------------------------- /packages/jest-config/jest.setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @note(jest) 6.x !extend expect 3 | * @ref https://github.com/testing-library/jest-dom/releases/tag/v6.0.0 4 | */ 5 | // import '@testing-library/jest-dom/jest-globals' 6 | // import '@testing-library/jest-dom' 7 | -------------------------------------------------------------------------------- /packages/jest-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jeromefitz/jest-config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "devDependencies": { 6 | "@jeromefitz/jest-presets": "workspace:*", 7 | "@testing-library/dom": "10.4.0", 8 | "@testing-library/jest-dom": "6.6.3", 9 | "@testing-library/react": "16.3.0", 10 | "@testing-library/user-event": "14.6.1", 11 | "@types/jest": "29.5.14", 12 | "jest": "29.7.0", 13 | "jest-environment-jsdom": "29.7.0", 14 | "next": "15.3.3", 15 | "react": "19.1.0", 16 | "react-dom": "19.1.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/jest-presets/jest/node/jest-preset.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 3 | modulePathIgnorePatterns: [ 4 | '/test/__fixtures__', 5 | '/node_modules', 6 | '/dist', 7 | ], 8 | preset: 'ts-jest', 9 | roots: [''], 10 | transform: { 11 | '^.+\\.tsx?$': 'ts-jest', 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /packages/jest-presets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jeromefitz/jest-presets", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./jest/node/jest-preset.js", 6 | "devDependencies": { 7 | "jest": "29.7.0", 8 | "ts-jest": "29.3.4" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lighthouse-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jeromefitz/lighthouse-config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./.lighthouserc.cjs" 6 | } 7 | -------------------------------------------------------------------------------- /packages/next-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jeromefitz/next-config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./next.config.mjs", 6 | "devDependencies": { 7 | "@jeromefitz/prettier-config": "2.1.9", 8 | "@next/bundle-analyzer": "15.3.3", 9 | "@octokit/core": "7.0.2", 10 | "@plaiceholder/next": "3.0.0", 11 | "fast-json-stable-stringify": "2.1.0", 12 | "lodash": "4.17.21", 13 | "next": "15.3.3", 14 | "plaiceholder": "3.0.0", 15 | "react": "19.1.0", 16 | "react-dom": "19.1.0", 17 | "sharp": "0.34.1", 18 | "zod": "3.25.48" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/next-notion/README.md: -------------------------------------------------------------------------------- 1 | # next-notion 2 | 3 | README needs to be re-written. 4 | -------------------------------------------------------------------------------- /packages/next-notion/scripts/copy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### 4 | # @note(build): NPM Publishes from `./dist` 5 | ### 6 | 7 | cp package.json ./dist/ 8 | cp README.md ./dist/ 9 | cp ../../LICENSE ./dist/ 10 | 11 | ### 12 | # @note(build): Replace `dist/index` w/ `index` 13 | ### 14 | 15 | if [[ "$OSTYPE" == "darwin"* ]]; then 16 | sed -i "" "s|dist/index|index|g" dist/package.json 17 | else 18 | sed -i -e "s|dist/index|index|g" dist/package.json 19 | fi 20 | 21 | 22 | ### 23 | # @custom(build) 24 | ### 25 | 26 | if [[ "$OSTYPE" == "darwin"* ]]; then 27 | if [[ "$OSTYPE" == "darwin"* ]]; then 28 | sed -i "" "s|dist/||g" dist/package.json 29 | else 30 | sed -i -e "s|dist/||g" dist/package.json 31 | fi 32 | else 33 | sed -i -e "s|dist/||g" dist/package.json 34 | fi 35 | 36 | if [[ "$OSTYPE" == "darwin"* ]]; then 37 | if [[ "$OSTYPE" == "darwin"* ]]; then 38 | sed -i "" "s|src/||g" dist/package.json 39 | else 40 | sed -i -e "s|src/||g" dist/package.json 41 | fi 42 | else 43 | sed -i -e "s|src/||g" dist/package.json 44 | fi 45 | -------------------------------------------------------------------------------- /packages/next-notion/src/Notion.constants.ts: -------------------------------------------------------------------------------- 1 | import ms from 'ms' 2 | 3 | /** 4 | * @redis is in seconds not ms 5 | */ 6 | const getTimeInSeconds = (time: number) => (!time ? 0 : time / 1000) 7 | 8 | /** 9 | * @note in seconds 10 | * ...probably could be hard-coded 11 | */ 12 | const TIME = { 13 | DAY: getTimeInSeconds(ms('1d')), 14 | HOUR: getTimeInSeconds(ms('1h')), 15 | MINUTE: getTimeInSeconds(ms('1m')), 16 | MONTH: getTimeInSeconds(ms('30d')), 17 | YEAR: getTimeInSeconds(ms('1y')), 18 | } 19 | 20 | export { TIME } 21 | -------------------------------------------------------------------------------- /packages/next-notion/src/blocks/Column.tsx: -------------------------------------------------------------------------------- 1 | import type { 2 | BulletedListItemBlockObjectResponse, 3 | NumberedListItemBlockObjectResponse, 4 | } from '@notionhq/client/build/src/api-endpoints.js' 5 | 6 | import { forwardRef } from 'react' 7 | 8 | import { NotionBlocks as Blocks } from '../Notion.Blocks' 9 | import { getBlockKey } from '../Notion.utils' 10 | 11 | const Column = forwardRef(function Column(props: any, ref: any) { 12 | const { 13 | block, 14 | order, 15 | }: { 16 | block: BulletedListItemBlockObjectResponse | NumberedListItemBlockObjectResponse 17 | order: number 18 | } = props 19 | const key = getBlockKey(block.id, block.type, order) 20 | // const items = block[block.type][block.type] 21 | const items = block[block.type]?.results 22 | 23 | const Component = props?.as ?? 'div' 24 | const componentProps = { 25 | className: props?.className ?? undefined, 26 | } 27 | 28 | return ( 29 | 30 | {items.map((item, order) => { 31 | const blocksKey = `${key}--${order}` 32 | return 33 | })} 34 | 35 | ) 36 | }) 37 | 38 | export { Column } 39 | export default Column 40 | -------------------------------------------------------------------------------- /packages/next-notion/src/blocks/Divider.tsx: -------------------------------------------------------------------------------- 1 | import type { DividerBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints.js' 2 | 3 | import { forwardRef } from 'react' 4 | 5 | import { getBlockKey } from '../Notion.utils' 6 | 7 | const Divider = forwardRef(function Divider(props: any, ref: any) { 8 | const { block, order }: { block: DividerBlockObjectResponse; order: number } = 9 | props 10 | const key = getBlockKey(block.id, block.type, order) 11 | 12 | const Component = props?.as ?? 'p' 13 | const componentProps = { 14 | className: props?.className ?? undefined, 15 | } 16 | 17 | return 18 | }) 19 | 20 | export { Divider } 21 | export default Divider 22 | -------------------------------------------------------------------------------- /packages/next-notion/src/blocks/Emoji.tsx: -------------------------------------------------------------------------------- 1 | import { EmojiWrapper } from './Emoji.server' 2 | // import { lazy, Suspense } from 'react' 3 | 4 | // // @note(next) outside of page.tsx, need to ignore 5 | // // eslint-disable-next-line @typescript-eslint/ban-ts-comment 6 | // // @ts-ignore 7 | // const EmojiWrapper = lazy(() => import('./Emoji.client')) 8 | 9 | // function NotionEmoji({ id, text }) { 10 | // return ( 11 | // <> 12 | // {text}>}> 13 | // 14 | // 15 | // > 16 | // ) 17 | // } 18 | 19 | function NotionEmoji({ id, text }) { 20 | return 21 | } 22 | 23 | export { NotionEmoji } 24 | -------------------------------------------------------------------------------- /packages/next-notion/src/blocks/ListBulleted.tsx: -------------------------------------------------------------------------------- 1 | import type { 2 | BulletedListItemBlockObjectResponse, 3 | NumberedListItemBlockObjectResponse, 4 | } from '@notionhq/client/build/src/api-endpoints.js' 5 | 6 | import { forwardRef } from 'react' 7 | 8 | import { NotionBlocks as Blocks } from '../Notion.Blocks' 9 | import { getBlockKey } from '../Notion.utils' 10 | 11 | const ListBulleted = forwardRef(function ListBulleted(props: any, ref: any) { 12 | const { 13 | block, 14 | order, 15 | }: { 16 | block: BulletedListItemBlockObjectResponse | NumberedListItemBlockObjectResponse 17 | order: number 18 | } = props 19 | const key = getBlockKey(block.id, block.type, order) 20 | const items = block[block.type][block.type] 21 | 22 | const Component = props?.as ?? 'ul' 23 | const componentProps = { 24 | className: props?.className ?? undefined, 25 | } 26 | 27 | return ( 28 | 29 | {items.map((item, order) => { 30 | const blocksKey = `${key}--${order}` 31 | return 32 | })} 33 | 34 | ) 35 | }) 36 | 37 | export { ListBulleted } 38 | export default ListBulleted 39 | -------------------------------------------------------------------------------- /packages/next-notion/src/helper.ts: -------------------------------------------------------------------------------- 1 | import { envServer as env } from '@jeromefitz/next-config/env.server.mjs' 2 | 3 | import { Client } from '@notionhq/client' 4 | 5 | const notion = new Client({ 6 | auth: env.NOTION_API_KEY, 7 | }) 8 | 9 | export { notion } 10 | -------------------------------------------------------------------------------- /packages/next-notion/src/index.ts: -------------------------------------------------------------------------------- 1 | import { uuidConverter, uuidValidate } from './utils/uuid' 2 | 3 | const strings = [ 4 | '27360d9b5f274dc2ac19ad09837b6860', 5 | '27360d9b5f274dc2ac19ad09837b6860a', 6 | '27360d9b-5f27-4dc2-ac19-ad09837b6860', 7 | '27360d9b-5f27-4dc2-ac19-ad09837b6860a', 8 | ] 9 | 10 | function foo() { 11 | strings.map((str) => { 12 | console.dir(`--- ---`) 13 | console.dir(`${str}: `) 14 | const isValid = uuidValidate(str) 15 | console.dir(`- [${isValid ? 'x' : ' '}] uuidValidate`) 16 | if (!isValid) { 17 | const _str = uuidConverter(str) 18 | console.dir(`- [${_str ? 'x' : ' '}] ${_str}`) 19 | } else { 20 | console.dir(`- [${isValid ? 'x' : ' '}] ${str}`) 21 | } 22 | }) 23 | } 24 | 25 | foo() 26 | 27 | export default foo 28 | -------------------------------------------------------------------------------- /packages/next-notion/src/queries/getBlockChildrenData.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import { notion } from '../helper' 4 | 5 | /** 6 | * @todo(error-handling) 7 | */ 8 | async function getBlockChildrenData(block_id) { 9 | const response = await notion.blocks.children.list({ 10 | block_id, 11 | page_size: 50, 12 | }) 13 | return response 14 | } 15 | 16 | export { getBlockChildrenData } 17 | -------------------------------------------------------------------------------- /packages/next-notion/src/queries/getColumnData.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import { asyncForEach } from '@jeromefitz/utils' 4 | 5 | import _noop from 'lodash/noop.js' 6 | 7 | import { getBlockChildrenData } from './getBlockChildrenData' 8 | 9 | async function getColumnData(columnListData) { 10 | const columnResults: any = [] 11 | await asyncForEach(columnListData.results, async (columnData: any) => { 12 | const columnDataColumn = await getBlockChildrenData(columnData.id) 13 | const column = { 14 | ...columnData, 15 | columnDataColumn, 16 | } 17 | columnResults.push(column) 18 | }).catch(_noop) 19 | const columnList = { 20 | column_list: { 21 | ...columnListData, 22 | results: columnResults, 23 | }, 24 | } 25 | return columnList 26 | } 27 | 28 | export { getColumnData } 29 | -------------------------------------------------------------------------------- /packages/next-notion/src/queries/getPageData.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | // import { cache } from 'react' 4 | // import type { GetPageResponse } from '@notionhq/client/build/src/api-endpoints.js' 5 | // import type { PageObjectResponseShow } from '../../shows/[[...catchAll]]/Show.types' 6 | import { notion } from '../helper' 7 | 8 | type PageData = any 9 | 10 | async function getPageData(page_id) { 11 | // const getPageData = cache(async (page_id) => { 12 | // console.dir(`(2) page_id: ${page_id}`) 13 | // // if ((_startsWith(page_id), '/events') || page_id === undefined) return null 14 | // console.dir(`(3) page_id: ${page_id}`) 15 | // // return {} 16 | const response: PageData = await notion.pages.retrieve({ 17 | page_id, 18 | }) 19 | // console.dir(`(4) response: ${response?.id}`) 20 | // console.dir(response) 21 | return response 22 | } 23 | // ) 24 | 25 | export { getPageData } 26 | -------------------------------------------------------------------------------- /packages/next-notion/src/queries/index.ts: -------------------------------------------------------------------------------- 1 | export { getBlockChildrenData } from './getBlockChildrenData' 2 | export { getBlockChildrenDataParent } from './getBlockChildrenDataParent' 3 | export { getColumnData } from './getColumnData' 4 | export { getDatabaseQuery, getDatabaseQueryByDateRange } from './getDatabaseQuery' 5 | export { getPageData } from './getPageData' 6 | -------------------------------------------------------------------------------- /packages/next-notion/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | // export { isAwsImage, isAwsImageExpired, isImageExpired } from './getAwsImage' 2 | export { isAwsImage, isImageExpired } from './getAwsImage' 3 | export { getPropertyTypeData } from './getPropertyTypeData' 4 | export { getSegmentInfo } from './getSegmentInfo' 5 | -------------------------------------------------------------------------------- /packages/next-notion/src/utils/uuid.ts: -------------------------------------------------------------------------------- 1 | import { validate as uuidValidate } from 'uuid' 2 | 3 | const delimiter = '-' 4 | const indexes = [8, 13, 18, 23] 5 | 6 | function insertString(str: string, index: number, value: string) { 7 | return str.substr(0, index) + value + str.substr(index) 8 | } 9 | 10 | function setDelimeter(_uuid: string) { 11 | indexes.map((idx) => { 12 | _uuid = insertString(_uuid, idx, delimiter) 13 | }) 14 | return _uuid 15 | } 16 | 17 | function uuidConverter(_uuid: string) { 18 | if (uuidValidate(_uuid)) return _uuid 19 | if (_uuid.length === 32) return setDelimeter(_uuid) 20 | return null 21 | } 22 | 23 | export { uuidConverter, uuidValidate } 24 | -------------------------------------------------------------------------------- /packages/next-notion/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from 'tsup' 2 | 3 | import { defineConfig } from 'tsup' 4 | 5 | import { config as _config } from '../../tsup.config' 6 | 7 | const entry = ['src/**'] 8 | const config: Options = { 9 | ..._config, 10 | entry, 11 | format: ['esm'], 12 | } 13 | 14 | export default defineConfig({ 15 | ...config, 16 | }) 17 | -------------------------------------------------------------------------------- /packages/playwright-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jeromefitz/playwright-config", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "private": true, 6 | "main": "./playwright.config.ts", 7 | "devDependencies": { 8 | "@playwright/test": "1.52.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/shared/scripts/copy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### 4 | # @note(build): NPM Publishes from `./dist` 5 | ### 6 | 7 | cp package.json ./dist/ 8 | # cp README.md ./dist/ 9 | # cp ../../LICENSE ./dist/ 10 | 11 | ### 12 | # @note(build): Replace `dist/index` w/ `index` 13 | ### 14 | 15 | if [[ "$OSTYPE" == "darwin"* ]]; then 16 | sed -i "" "s|dist/index|index|g" dist/package.json 17 | else 18 | sed -i -e "s|dist/index|index|g" dist/package.json 19 | fi 20 | 21 | ### 22 | # @custom(build) 23 | ### 24 | 25 | if [[ "$OSTYPE" == "darwin"* ]]; then 26 | if [[ "$OSTYPE" == "darwin"* ]]; then 27 | sed -i "" "s|dist/||g" dist/package.json 28 | else 29 | sed -i -e "s|dist/||g" dist/package.json 30 | fi 31 | else 32 | sed -i -e "s|dist/||g" dist/package.json 33 | fi 34 | 35 | if [[ "$OSTYPE" == "darwin"* ]]; then 36 | if [[ "$OSTYPE" == "darwin"* ]]; then 37 | sed -i "" "s|src/||g" dist/package.json 38 | else 39 | sed -i -e "s|src/||g" dist/package.json 40 | fi 41 | else 42 | sed -i -e "s|src/||g" dist/package.json 43 | fi 44 | -------------------------------------------------------------------------------- /packages/shared/src/components/Analytics/Analytics.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from 'react' 2 | 3 | import { FathomAnalytics } from './Fathom' 4 | import { VercelAnalytics, VercelSpeedInsights } from './Vercel' 5 | 6 | function Analytics() { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | 13 | 14 | > 15 | ) 16 | } 17 | 18 | export { Analytics } 19 | -------------------------------------------------------------------------------- /packages/shared/src/components/Analytics/Fathom.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { envClient as env } from '@jeromefitz/next-config/env.client.mjs' 3 | 4 | import { load, trackPageview } from 'fathom-client' 5 | import { usePathname, useSearchParams } from 'next/navigation.js' 6 | import { useEffect } from 'react' 7 | 8 | function FathomAnalytics() { 9 | const pathname = usePathname() 10 | const searchParams = useSearchParams() 11 | useEffect(() => { 12 | if (env.IS_PRODUCTION) { 13 | load(env.NEXT_PUBLIC__FATHOM_SITE_ID, { 14 | honorDNT: true, 15 | includedDomains: [env.NEXT_PUBLIC__SITE], 16 | url: `https://cdn.usefathom.com/script.js`, 17 | }) 18 | } 19 | }, []) 20 | 21 | useEffect(() => { 22 | if (!pathname) return 23 | trackPageview({ 24 | referrer: document.referrer, 25 | url: pathname + searchParams.toString(), 26 | }) 27 | }, [pathname, searchParams]) 28 | 29 | return null 30 | } 31 | 32 | export { FathomAnalytics } 33 | -------------------------------------------------------------------------------- /packages/shared/src/components/Analytics/Vercel.tsx: -------------------------------------------------------------------------------- 1 | import { envClient as env } from '@jeromefitz/next-config/env.client.mjs' 2 | 3 | import { Analytics } from '@vercel/analytics/react' 4 | import { SpeedInsights } from '@vercel/speed-insights/next' 5 | 6 | function VercelAnalytics() { 7 | return env.IS_VERCEL ? : null 8 | } 9 | 10 | function VercelSpeedInsights() { 11 | return env.IS_VERCEL ? : null 12 | } 13 | 14 | export { VercelAnalytics, VercelSpeedInsights } 15 | -------------------------------------------------------------------------------- /packages/shared/src/components/Analytics/index.ts: -------------------------------------------------------------------------------- 1 | export { Analytics } from './Analytics' 2 | export { FathomAnalytics } from './Fathom' 3 | export { VercelAnalytics, VercelSpeedInsights } from './Vercel' 4 | -------------------------------------------------------------------------------- /packages/shared/src/components/Navigation/Navigation.tsx: -------------------------------------------------------------------------------- 1 | function Navigation() { 2 | return ( 3 | <> 4 | <>> 5 | > 6 | ) 7 | } 8 | 9 | export { Navigation } 10 | -------------------------------------------------------------------------------- /packages/shared/src/components/Navigation/index.ts: -------------------------------------------------------------------------------- 1 | export { Navigation } from './Navigation' 2 | -------------------------------------------------------------------------------- /packages/shared/src/components/Notion/Blocks/Embed.Spotify.tsx: -------------------------------------------------------------------------------- 1 | function EmbedSpotify({ id }) { 2 | return ( 3 | 4 | 19 | 20 | ) 21 | } 22 | 23 | export { EmbedSpotify } 24 | -------------------------------------------------------------------------------- /packages/shared/src/components/Notion/Blocks/Embed.Twitter.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Callout } from '@jeromefitz/ds/components/Callout/index' 4 | 5 | import { NotionEmoji as EmojiWrapper } from 'next-notion/blocks/Emoji' 6 | import { Tweet } from 'react-tweet' 7 | 8 | function EmbedTweet({ id }) { 9 | return ( 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | function EmbedTwitter({ block }) { 17 | const id = block.embed.url.split('/').slice(-1)[0] 18 | 19 | /** 20 | * @todo(notion) TextAnnotations 21 | */ 22 | const caption = block[block.type]?.caption 23 | ? block[block.type]?.caption[0]?.plain_text 24 | : null 25 | 26 | return ( 27 | <> 28 | 29 | {!!caption && ( 30 | 31 | 32 | {/* {caption} */} 33 | 34 | )} 35 | > 36 | ) 37 | } 38 | 39 | export { EmbedTweet, EmbedTwitter } 40 | -------------------------------------------------------------------------------- /packages/shared/src/components/Notion/Blocks/Embed.tsx: -------------------------------------------------------------------------------- 1 | import type { BlockObjectResponse } from '@notionhq/client/build/src/api-endpoints.js' 2 | 3 | import { EmbedTwitter } from './Embed.Twitter' 4 | 5 | // @todo(types) 6 | function EmbedImpl({ block }: { block: any | BlockObjectResponse }) { 7 | // console.dir(`EmbedImpl`) 8 | // console.dir(block) 9 | 10 | const isTwitter = block.embed.url.includes('twitter') 11 | if (isTwitter) { 12 | return 13 | } 14 | return null 15 | } 16 | 17 | export { EmbedImpl as Embed } 18 | export default EmbedImpl 19 | -------------------------------------------------------------------------------- /packages/shared/src/components/Notion/Blocks/Image.utils.ts: -------------------------------------------------------------------------------- 1 | import _filter from 'lodash/filter.js' 2 | import _startsWith from 'lodash/startsWith.js' 3 | 4 | const FIND_ALT = 'ALT: ' 5 | 6 | function getImageAlt(comments) { 7 | const comment = comments[0] 8 | const c = _filter(comments, (comment) => 9 | _startsWith(comment?.rich_text[0]?.plain_text, FIND_ALT), 10 | ) 11 | return !!c && c.length > 0 12 | ? c[0]?.rich_text[0]?.plain_text.slice(FIND_ALT.length) 13 | : comment 14 | ? comment?.rich_text[0]?.plain_text 15 | : '' 16 | } 17 | 18 | function getImageExpiration(block) { 19 | return block[block?.type]?.type === 'external' 20 | ? null 21 | : block[block?.type]?.file?.expiry_time 22 | } 23 | 24 | function getImageUrl(block) { 25 | return block[block.type].type === 'external' 26 | ? block[block?.type]?.external?.url 27 | : block[block?.type]?.file?.url 28 | } 29 | 30 | export { getImageAlt, getImageExpiration, getImageUrl } 31 | -------------------------------------------------------------------------------- /packages/shared/src/components/Notion/Blocks/Video.tsx: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import { Callout } from '@jeromefitz/ds/components/Callout/index' 4 | 5 | import type { VideoBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints.js' 6 | 7 | import { NotionEmoji as EmojiWrapper } from 'next-notion/blocks/Emoji' 8 | import { Fragment, Suspense } from 'react' 9 | 10 | import { VideoYouTube } from './Video.YouTube' 11 | 12 | // @todo(types) 13 | function VideoImpl({ block }: { block: any | VideoBlockObjectResponse }) { 14 | const url = block.video.external.url 15 | 16 | /** 17 | * @todo(notion) TextAnnotations 18 | */ 19 | const caption = block[block.type]?.caption 20 | ? block[block.type]?.caption[0]?.plain_text 21 | : null 22 | 23 | return ( 24 | }> 25 | <>> 26 | {/* Async out of next */} 27 | {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} 28 | {/* @ts-ignore */} 29 | 30 | {!!caption && ( 31 | 32 | 33 | {/* {caption} */} 34 | 35 | )} 36 | 37 | ) 38 | } 39 | 40 | export { VideoImpl as Video } 41 | export default VideoImpl 42 | -------------------------------------------------------------------------------- /packages/shared/src/hooks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/packages/shared/src/hooks/.gitkeep -------------------------------------------------------------------------------- /packages/shared/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useSWRInfinitePages } from './useSWRInfinitePages' 2 | export { useThemeToggle } from './useThemeToggle' 3 | export { useThrottle } from './useThrottle' 4 | -------------------------------------------------------------------------------- /packages/shared/src/hooks/useThemeToggle.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | 3 | function useThemeToggle({ setTheme, theme }) { 4 | return useCallback(() => { 5 | const newTheme = theme === 'dark' ? 'light' : 'dark' 6 | document.documentElement.style.setProperty('color-scheme', newTheme) 7 | setTheme(newTheme) 8 | }, [setTheme, theme]) 9 | } 10 | 11 | export { useThemeToggle } 12 | -------------------------------------------------------------------------------- /packages/shared/src/hooks/useThrottle.ts: -------------------------------------------------------------------------------- 1 | import _throttle from 'lodash/throttle' 2 | import { useCallback, useEffect, useRef } from 'react' 3 | 4 | function useThrottle(cb, delay) { 5 | const options = { leading: true, trailing: false } 6 | const cbRef = useRef(cb) 7 | useEffect(() => { 8 | cbRef.current = cb 9 | }) 10 | // @todo(eslint) react-hooks/exhaustive-deps 11 | return useCallback( 12 | _throttle((...args) => cbRef.current(...args), delay, options), 13 | [delay], 14 | ) 15 | } 16 | 17 | export { useThrottle } 18 | -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | getDatabaseQuery, 3 | getDatabaseQueryByDateRange, 4 | getDataFromCache, 5 | getMetadata, 6 | getPageDataFromNotion, 7 | getSegmentInfo, 8 | getSlugPreview, 9 | } from './notion/utils/index' 10 | // @todo(types) next-notion 11 | // export type { SegmentInfo } from './notion/utils/index' 12 | 13 | export { getImage } from './plaiceholder/getImage' 14 | 15 | export { getCache, getKey, setCache } from './redis/index' 16 | export type { RC } from './redis/index' 17 | -------------------------------------------------------------------------------- /packages/shared/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | import ms from 'ms' 2 | 3 | /** 4 | * @redis is in seconds not ms 5 | */ 6 | const getTimeInSeconds = (time: number) => (!time ? 0 : time / 1000) 7 | 8 | /** 9 | * @note in seconds 10 | * ...probably could be hard-coded 11 | */ 12 | const TIME = { 13 | DAY: getTimeInSeconds(ms('1d')), 14 | HOUR: getTimeInSeconds(ms('1h')), 15 | MINUTE: getTimeInSeconds(ms('1m')), 16 | MONTH: getTimeInSeconds(ms('30d')), 17 | YEAR: getTimeInSeconds(ms('1y')), 18 | } 19 | 20 | export { TIME } 21 | -------------------------------------------------------------------------------- /packages/shared/src/lib/fetcher.ts: -------------------------------------------------------------------------------- 1 | const fetcher = async function (requestInfo: RequestInfo): Promise { 2 | /** 3 | * @todo(error-handling) 4 | */ 5 | const response = await fetch(requestInfo) 6 | return response.json() 7 | } 8 | 9 | export { fetcher } 10 | -------------------------------------------------------------------------------- /packages/shared/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { TIME } from './constants' 2 | export { fetcher } from './fetcher' 3 | -------------------------------------------------------------------------------- /packages/shared/src/notion/utils/getDatabaseQueryByDateRange.ts: -------------------------------------------------------------------------------- 1 | import { getDatabaseQueryByDateRange as _getDatabaseQueryByDateRange } from 'next-notion/queries/index' 2 | // @todo(types) next-notion 3 | // import type { GetDatabaseQueryTypes } from 'next-notion/queries/index' 4 | import { cache } from 'react' 5 | 6 | const getDatabaseQueryByDateRange = cache( 7 | async ({ 8 | database_id, 9 | 10 | filterType = 'equals', 11 | segmentInfo, 12 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 13 | sortProperty, 14 | // }: GetDatabaseQueryTypes) => { 15 | }: any) => { 16 | return await _getDatabaseQueryByDateRange({ 17 | database_id, 18 | filterType, 19 | segmentInfo, 20 | }) 21 | }, 22 | ) 23 | 24 | export { getDatabaseQueryByDateRange } 25 | -------------------------------------------------------------------------------- /packages/shared/src/notion/utils/getPageDataFromNotion.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import { envClient as env } from '@jeromefitz/next-config/env.client.mjs' 4 | import { isObjectEmpty } from '@jeromefitz/utils' 5 | 6 | import { getPageData as _getPageData } from 'next-notion/queries/index' 7 | import { cache } from 'react' 8 | 9 | import { getCache, getKey, setCache } from '../../redis/index' 10 | 11 | /** 12 | * @todo(next) draft | revalidate 13 | */ 14 | const getPageDataFromNotion = cache(async (id) => { 15 | let data 16 | 17 | const prefix = `/notion/pages/${id}` 18 | const key: string = getKey(prefix) 19 | const dataFromCache = await getCache({ slug: key }) 20 | 21 | // console.dir(`> getCache: ${key}`) 22 | // console.dir(dataFromCache) 23 | 24 | const isCached = !!dataFromCache && !isObjectEmpty(dataFromCache) 25 | 26 | if (env.OVERRIDE_CACHE || !isCached) { 27 | const dataFromNotion = await _getPageData(id) 28 | // console.dir(`> dataFromNotion: ${id}`) 29 | // console.dir(dataFromNotion) 30 | 31 | if (!isObjectEmpty(dataFromNotion)) { 32 | // console.dir(`setCache: ${key}`) 33 | void setCache({ data: dataFromNotion, slug: key }) 34 | } 35 | 36 | data = dataFromNotion 37 | } else { 38 | // console.dir(`gotCache: ${key}`) 39 | data = dataFromCache 40 | } 41 | 42 | return data 43 | }) 44 | 45 | export { getPageDataFromNotion } 46 | -------------------------------------------------------------------------------- /packages/shared/src/notion/utils/getSegmentInfo.ts: -------------------------------------------------------------------------------- 1 | import { getSegmentInfo as _getSegmentInfo } from 'next-notion/utils/index' 2 | // @todo(types) next-notion 3 | // import type { SegmentInfo } from 'next-notion/utils/index' 4 | import { cache } from 'react' 5 | 6 | const getSegmentInfo: any = cache(({ SEGMENT, ...props }) => { 7 | return _getSegmentInfo({ SEGMENT, ...props }) 8 | }) 9 | 10 | export { getSegmentInfo } 11 | // export type { SegmentInfo } 12 | -------------------------------------------------------------------------------- /packages/shared/src/notion/utils/getSlugPreview.ts: -------------------------------------------------------------------------------- 1 | import { getPropertyTypeData } from 'next-notion/utils/index' 2 | 3 | function getSlugPreview(properties) { 4 | return getPropertyTypeData(properties, 'Slug.Preview') 5 | } 6 | 7 | export { getPropertyTypeData, getSlugPreview } 8 | -------------------------------------------------------------------------------- /packages/shared/src/notion/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { getDatabaseQuery } from './getDatabaseQuery' 2 | export { getDatabaseQueryByDateRange } from './getDatabaseQueryByDateRange' 3 | export { getDataFromCache } from './getDataFromCache' 4 | export { getMetadata } from './getMetadata' 5 | export { getPageDataFromNotion } from './getPageDataFromNotion' 6 | export { getSegmentInfo } from './getSegmentInfo' 7 | export { getSlugPreview } from './getSlugPreview' 8 | 9 | // @todo(types) next-notion 10 | // export type { SegmentInfo } from './getSegmentInfo' 11 | -------------------------------------------------------------------------------- /packages/shared/src/plaiceholder/getImage.ts: -------------------------------------------------------------------------------- 1 | import { getPlaiceholder } from 'plaiceholder' 2 | 3 | const getImage = async (src: string) => { 4 | // console.dir(`src: ${src}`) 5 | const buffer = await fetch(src).then(async (res) => 6 | Buffer.from(await res.arrayBuffer()), 7 | ) 8 | 9 | const { 10 | metadata: { height, width }, 11 | ...plaiceholder 12 | } = await getPlaiceholder(buffer, { size: 10 }) 13 | 14 | return { 15 | ...plaiceholder, 16 | img: { height, src, width }, 17 | } 18 | } 19 | 20 | export { getImage } 21 | 22 | /** 23 | * @note(plaiceholder) usage 24 | * ref: https://plaiceholder.co/docs/upgrading-to-3 25 | * 26 | */ 27 | // const { base64, img } = await getImage( 28 | // 'https://images.unsplash.com/photo-1621961458348-f013d219b50c?auto=format&fit=crop&w=2850&q=80' 29 | // ) 30 | -------------------------------------------------------------------------------- /packages/shared/src/plaiceholder/index.ts: -------------------------------------------------------------------------------- 1 | export { getImage } from './getImage' 2 | -------------------------------------------------------------------------------- /packages/shared/src/redis/getCache.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import type { RC } from './index' 4 | 5 | import redis, { getKey } from './index' 6 | 7 | async function getCache({ slug }: { slug: string }) { 8 | const key = getKey(slug) 9 | const cache = await redis.get(key) 10 | return cache 11 | } 12 | 13 | export { getCache } 14 | -------------------------------------------------------------------------------- /packages/shared/src/redis/getKey.ts: -------------------------------------------------------------------------------- 1 | // import { slug as _slug } from 'github-slugger' 2 | import { envClient as env } from '@jeromefitz/next-config/env.client.mjs' 3 | 4 | const KEY__PREFIX = env.NEXT_PUBLIC__SITE 5 | 6 | // const getKeysByJoin = ({ keyData, keyJoin = '/', keyPrefix }) => 7 | // `${keyPrefix}/${keyData.join(keyJoin)}`.toLowerCase() 8 | 9 | // const getKeysBySlugger = ({ keyData, keyPrefix }) => 10 | // `${keyPrefix}/${_slug(keyData)}`.toLowerCase() 11 | 12 | function getKey(slug: string) { 13 | const key = slug.includes(KEY__PREFIX) ? slug : `${KEY__PREFIX}${slug}` 14 | // console.dir(`> slug: ${slug}`) 15 | // console.dir(`> key: ${key}`) 16 | return key 17 | } 18 | 19 | export { getKey } 20 | -------------------------------------------------------------------------------- /packages/shared/src/redis/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ListBlockChildrenResponse, 3 | PageObjectResponse, 4 | } from '@notionhq/client/build/src/api-endpoints.js' 5 | 6 | interface RC { 7 | blocks: ListBlockChildrenResponse 8 | page: PageObjectResponse 9 | } 10 | 11 | import redis from './redis' 12 | 13 | export { getCache } from './getCache' 14 | export { getKey } from './getKey' 15 | export { setCache } from './setCache' 16 | 17 | export type { RC } 18 | export default redis 19 | -------------------------------------------------------------------------------- /packages/shared/src/redis/redis.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import https from 'node:https' 4 | 5 | import { envServer } from '@jeromefitz/next-config/env.server.mjs' 6 | 7 | import { Redis } from '@upstash/redis' 8 | 9 | const redis = new Redis({ 10 | agent: new https.Agent({ keepAlive: true }), 11 | retry: { 12 | backoff: (retryCount) => Math.exp(retryCount) * 50, 13 | retries: 5, 14 | }, 15 | token: envServer.UPSTASH_REDIS_REST_TOKEN, 16 | url: envServer.UPSTASH_REDIS_REST_URL, 17 | }) 18 | 19 | export default redis 20 | -------------------------------------------------------------------------------- /packages/shared/src/redis/setCache.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import stringify from 'fast-json-stable-stringify' 4 | 5 | import type { RC } from './index' 6 | 7 | import { TIME } from '../lib/constants' 8 | import redis, { getKey } from './index' 9 | 10 | // @todo(types) any 11 | function setCache({ data, slug }: { data: any | RC; slug: string }) { 12 | const key = getKey(slug) 13 | void redis.set(key, stringify(data), { 14 | ex: TIME.MONTH, 15 | }) 16 | return null 17 | } 18 | 19 | export { setCache } 20 | -------------------------------------------------------------------------------- /packages/shared/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | import { config as _config } from '../../tsup.config' 4 | 5 | const entry = ['src/**'] 6 | const config = { 7 | ..._config, 8 | entry, 9 | } 10 | 11 | export default defineConfig({ 12 | ...config, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/storybook-config/main.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path' 2 | 3 | // console.dir(`***`) 4 | // console.dir(resolve(__dirname, '../../sites/jeromefitzgerald.com/next.config.js')) 5 | 6 | /** @type { import('@storybook/nextjs').StorybookConfig } */ 7 | const config = { 8 | addons: [ 9 | '@storybook/addon-links', 10 | { 11 | name: '@storybook/addon-essentials', 12 | options: { 13 | actions: true, 14 | backgrounds: false, 15 | controls: true, 16 | docs: true, 17 | toolbars: true, 18 | viewport: true, 19 | }, 20 | }, 21 | '@storybook/addon-interactions', 22 | '@storybook/addon-a11y', 23 | // '@storybook/addon-styling', 24 | { 25 | name: '@storybook/addon-styling', 26 | options: { 27 | postCss: true, 28 | }, 29 | }, 30 | ], 31 | core: { 32 | disableTelemetry: true, 33 | enableCrashReports: true, 34 | }, 35 | docs: { 36 | autodocs: true, 37 | }, 38 | framework: { 39 | name: '@storybook/nextjs', 40 | options: {}, 41 | // options: { 42 | // // // nextConfigPath: resolve(__dirname, '../next.config.js'), 43 | // nextConfigPath: resolve(__dirname, '../next.config.js'), 44 | // }, 45 | }, 46 | staticDirs: [resolve(__dirname, 'public')], 47 | stories: ['../src/**/*.stories.{js,jsx,ts,tsx}'], 48 | } 49 | 50 | export default config 51 | -------------------------------------------------------------------------------- /packages/storybook-config/manager.config.ts: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/manager-api' 2 | 3 | import * as themes from './themes' 4 | 5 | addons.setConfig({ 6 | theme: themes.dark, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/storybook-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jeromefitz/storybook-config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "devDependencies": { 6 | "@jeromefitz/tailwind-config": "workspace:*", 7 | "@radix-ui/themes": "3.2.1", 8 | "@storybook/addon-a11y": "8.6.14", 9 | "@storybook/addon-essentials": "8.6.14", 10 | "@storybook/addon-interactions": "8.6.14", 11 | "@storybook/addon-links": "8.6.14", 12 | "@storybook/addon-styling": "1.3.7", 13 | "@storybook/addon-viewport": "8.6.14", 14 | "@storybook/blocks": "8.6.14", 15 | "@storybook/jest": "0.2.3", 16 | "@storybook/manager-api": "8.6.14", 17 | "@storybook/nextjs": "8.6.14", 18 | "@storybook/react": "8.6.14", 19 | "@storybook/testing-library": "0.2.2", 20 | "@storybook/theming": "8.6.14", 21 | "less": "4.3.0", 22 | "next": "15.3.3", 23 | "postcss": "8.5.4", 24 | "react": "19.1.0", 25 | "react-dom": "19.1.0", 26 | "storybook": "8.6.14", 27 | "webpack": "5.99.9" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/storybook-config/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 20 | 27 | -------------------------------------------------------------------------------- /packages/storybook-config/preview.config.ts: -------------------------------------------------------------------------------- 1 | // import { withThemeByClassName } from '@storybook/addon-styling' 2 | import { MINIMAL_VIEWPORTS as viewports } from '@storybook/addon-viewport' 3 | 4 | import * as themes from './themes' 5 | import { DEFAULT_THEME, withTailwindTheme } from './withTailwindTheme.decorator' 6 | 7 | import './preview.css' 8 | 9 | /** @type { import('@storybook/react').Preview } */ 10 | const config = { 11 | decorators: [withTailwindTheme], 12 | globalTypes: { 13 | theme: { 14 | defaultValue: DEFAULT_THEME, 15 | description: 'Global theme for components', 16 | name: 'Theme', 17 | toolbar: { 18 | // Change title based on selected value 19 | dynamicTitle: true, 20 | icon: 'paintbrush', 21 | // Array of plain string values or MenuItem shape (see below) 22 | items: [ 23 | { left: '🌞', title: 'Light', value: 'light' }, 24 | { left: '🌛', title: 'Dark', value: 'dark' }, 25 | ], 26 | }, 27 | }, 28 | }, 29 | parameters: { 30 | actions: { 31 | argTypesRegex: '^on[A-Z].*', 32 | }, 33 | controls: { 34 | matchers: { 35 | color: /(background|color)$/i, 36 | date: /Date$/, 37 | }, 38 | }, 39 | docs: { 40 | theme: themes.dark, 41 | }, 42 | nextjs: { 43 | appDirectory: true, 44 | }, 45 | viewport: { 46 | viewports, 47 | }, 48 | }, 49 | } 50 | 51 | export default config 52 | -------------------------------------------------------------------------------- /packages/storybook-config/preview.css: -------------------------------------------------------------------------------- 1 | body.loading * { 2 | transition: 'none !important'; 3 | } 4 | 5 | #__next { 6 | position: 'relative'; 7 | z-index: 0; 8 | } 9 | 10 | .light-theme [data-hide='dark'], 11 | .dark-theme [data-hide='light'] { 12 | display: 'none'; 13 | } 14 | -------------------------------------------------------------------------------- /packages/storybook-config/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/packages/storybook-config/public/.gitkeep -------------------------------------------------------------------------------- /packages/storybook-config/themes.ts: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming' 2 | 3 | const commonOptions = { 4 | brandTarget: '_blank', 5 | brandTitle: 'NGOP — Storybook', 6 | brandUrl: 'https://jeromefitzgerald.com/', 7 | } 8 | 9 | export const light = create({ 10 | base: 'light', 11 | ...commonOptions, 12 | }) 13 | 14 | export const dark = create({ 15 | base: 'dark', 16 | ...commonOptions, 17 | }) 18 | -------------------------------------------------------------------------------- /packages/tailwind-config/hocus.plugin.js: -------------------------------------------------------------------------------- 1 | import plugin from 'tailwindcss/plugin' 2 | 3 | /** 4 | * @note(tailwind) hover + focus = hocus 5 | */ 6 | const hocusPlugin = plugin(({ addVariant }) => { 7 | addVariant('hocus-visible', ['&:hover', '&:focus-visible']) 8 | addVariant('hocus-within', ['&:hover', '&:focus-within']) 9 | addVariant('hocus', ['&:hover', '&:focus']) 10 | 11 | addVariant('group-hocus-visible', [ 12 | ':merge(.group):hover &', 13 | ':merge(.group):focus-visible &', 14 | ]) 15 | addVariant('group-hocus-within', [ 16 | ':merge(.group):hover &', 17 | ':merge(.group):focus-within &', 18 | ]) 19 | addVariant('group-hocus', [':merge(.group):hover &', ':merge(.group):focus &']) 20 | 21 | addVariant('peer-hocus-visible', [ 22 | ':merge(.peer):hover ~ &', 23 | ':merge(.peer):focus-visible ~ &', 24 | ]) 25 | addVariant('peer-hocus-within', [ 26 | ':merge(.peer):hover ~ &', 27 | ':merge(.peer):focus-within ~ &', 28 | ]) 29 | addVariant('peer-hocus', [':merge(.peer):hover ~ &', ':merge(.peer):focus ~ &']) 30 | }) 31 | 32 | export default hocusPlugin 33 | -------------------------------------------------------------------------------- /packages/tailwind-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jeromefitz/tailwind-config", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "private": true, 6 | "devDependencies": { 7 | "@radix-ui/colors": "3.0.0", 8 | "@radix-ui/themes": "3.2.1", 9 | "@tailwindcss/aspect-ratio": "0.4.2", 10 | "@tailwindcss/forms": "0.5.10", 11 | "@tailwindcss/postcss": "4.1.8", 12 | "@tailwindcss/typography": "0.5.16", 13 | "autoprefixer": "10.4.21", 14 | "cssnano": "7.0.7", 15 | "lightningcss": "1.30.1", 16 | "postcss": "8.5.4", 17 | "radix-themes-tw": "1.1.0", 18 | "react": "19.1.0", 19 | "react-dom": "19.1.0", 20 | "sharp": "0.34.1", 21 | "tailwindcss": "4.1.8", 22 | "tailwindcss-radix": "4.0.2", 23 | "tw-animate-css": "1.3.3" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/tailwind-config/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /packages/tailwind-config/src/colors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @note(tailwind) each site may _not_ want _every_ color 3 | */ 4 | const isDev = process.env.NODE_ENV === 'development' 5 | const siteColors = [ 6 | 'black', 7 | // 'gray', 8 | // 'orange', 9 | 'pink', 10 | // 'purple', 11 | 'slate', 12 | 'white', 13 | // '', 14 | ] 15 | 16 | const _radixColors = [ 17 | 'tomato', 18 | 'red', 19 | 'crimson', 20 | 'pink', 21 | 'plum', 22 | 'purple', 23 | 'violet', 24 | 'indigo', 25 | 'blue', 26 | 'cyan', 27 | 'teal', 28 | 'green', 29 | 'grass', 30 | 'orange', 31 | 'brown', 32 | // bright 33 | 'sky', 34 | 'mint', 35 | 'lime', 36 | 'yellow', 37 | 'amber', 38 | // grays 39 | 'gray', 40 | 'mauve', 41 | 'slate', 42 | 'sage', 43 | 'olive', 44 | 'sand', 45 | // metals 46 | 'gold', 47 | 'bronze', 48 | // overlays 49 | 'black', 50 | 'white', 51 | ] 52 | const radixColors = isDev ? _radixColors : siteColors 53 | /** 54 | * @note(radix) These colors should have `black` text instead of `white` 55 | * ref: https://www.radix-ui.com/docs/colors/palette-composition/understanding-the-scale#steps-910-solid-backgrounds 56 | */ 57 | const foregroundTextBlack = ['sky', 'mint', 'lime', 'yellow', 'amber'] 58 | const excludes = ['white', 'black'] 59 | 60 | export default { excludes, foregroundTextBlack, radixColors } 61 | -------------------------------------------------------------------------------- /packages/tailwind-config/src/index.js: -------------------------------------------------------------------------------- 1 | import { backgrounds } from './backgrounds' 2 | import { buttonTypes, buttons } from './buttons' 3 | import { excludes, foregroundTextBlack, radixColors } from './colors' 4 | import { notion, notionColors } from './notion' 5 | 6 | export default { 7 | backgrounds, 8 | buttonTypes, 9 | buttons, 10 | excludes, 11 | foregroundTextBlack, 12 | notion, 13 | notionColors, 14 | radixColors, 15 | } 16 | -------------------------------------------------------------------------------- /packages/tailwind-config/styles/chrome.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Chrome has a bug with transitions on load since 2012! 3 | * 4 | * To prevent a "pop" of content, you have to disable all transitions until 5 | * the page is done loading. 6 | * 7 | * https://lab.laukstein.com/bug/input 8 | * https://twitter.com/timer150/status/1345217126680899584 9 | */ 10 | body.loading * { 11 | transition: none !important; 12 | } 13 | -------------------------------------------------------------------------------- /packages/tailwind-config/styles/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'NameSans'; 3 | font-weight: 1 1000; 4 | src: url('https://nice-fonts.s3.amazonaws.com/arrow-type/at--name-sans--variable--1.0.0.woff2'); 5 | } 6 | 7 | @font-face { 8 | font-family: 'NameSansMono'; 9 | font-weight: 1 1000; 10 | src: url('https://nice-fonts.s3.amazonaws.com/arrow-type/at--name-mono--variable-0.3.woff2'); 11 | } 12 | -------------------------------------------------------------------------------- /packages/tailwind-config/styles/safari.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Safari 15+ 3 | * 4 | * https://nextjs.org/docs/api-reference/next/image#known-browser-bugs 5 | */ 6 | @media not all and (min-resolution: 0.001dpcm) { 7 | img[loading='lazy'] { 8 | clip-path: inset(0.5px); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /patches/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/patches/.gitkeep -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'sites/*' 4 | enable-pre-post-scripts: true 5 | -------------------------------------------------------------------------------- /release.config.mjs: -------------------------------------------------------------------------------- 1 | import { getConfig } from '@jeromefitz/semantic' 2 | 3 | import isCI from 'is-ci' 4 | import _map from 'lodash/map.js' 5 | 6 | import releaseBranchTypes from './config/release-branch-types/index.cjs' 7 | 8 | if (!isCI) { 9 | const dotenv = await import('dotenv') 10 | dotenv.config({ path: './.env' }) 11 | } 12 | 13 | const branchTypes = _map( 14 | releaseBranchTypes, 15 | (releaseBranchType, releaseBranchTypeIndex) => { 16 | return _map(releaseBranchType, (branchType) => { 17 | return ( 18 | !!branchType && { 19 | name: `${releaseBranchTypeIndex}/${branchType}`, 20 | prerelease: branchType, 21 | } 22 | ) 23 | })[0] 24 | }, 25 | ).filter((branchType) => !!branchType) 26 | 27 | const branches = [ 28 | { name: 'main' }, 29 | { name: 'canary', prerelease: 'canary' }, 30 | { name: 'NICE-67', prerelease: 'NICE-67' }, 31 | { name: 'deps/semantic-release-24.x', prerelease: 'canary' }, 32 | ...branchTypes, 33 | ] 34 | 35 | const config = { 36 | branches, 37 | contributorsProhibitList: { 38 | email: [], 39 | login: ['BotJerome', 'JeromeFitz'], 40 | }, 41 | enableNpm: false, 42 | } 43 | 44 | // const _config = getConfig(config) 45 | 46 | const _config = config 47 | export { _config as config } 48 | const _getConfig = getConfig 49 | export { _getConfig as getConfig } 50 | -------------------------------------------------------------------------------- /sites/.gitignore: -------------------------------------------------------------------------------- 1 | # hide a lot of static bogus temp code 2 | **/_placeholder 3 | -------------------------------------------------------------------------------- /sites/alexojerome.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/alexojerome.com/.gitkeep -------------------------------------------------------------------------------- /sites/amidracula.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/amidracula.com/.gitkeep -------------------------------------------------------------------------------- /sites/ayemonthen.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/ayemonthen.com/.gitkeep -------------------------------------------------------------------------------- /sites/boohumbag.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/boohumbag.com/.gitkeep -------------------------------------------------------------------------------- /sites/crimeimprov.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/crimeimprov.com/.gitkeep -------------------------------------------------------------------------------- /sites/dahriferks.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/dahriferks.com/.gitkeep -------------------------------------------------------------------------------- /sites/fuckyoucandy.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/fuckyoucandy.com/.gitkeep -------------------------------------------------------------------------------- /sites/jerandky.com/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /sites/jerandky.com/next.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname, join } from 'node:path' 2 | import { fileURLToPath } from 'url' 3 | 4 | import nextConfig from '@jeromefitz/next-config/next.config.mjs' 5 | 6 | import dotenv from 'dotenv' 7 | import isCI from 'is-ci' 8 | if (!isCI) { 9 | dotenv.config({ patch: './.env' }) 10 | } 11 | 12 | const __filename = fileURLToPath(import.meta.url) 13 | const __dirname = dirname(__filename) 14 | 15 | const buildInfoConfig = { 16 | owner: 'jeromefitz', 17 | repo: 'jeromefitzgerald.com', 18 | } 19 | 20 | const serverComponentsExternalPackages = [ 21 | '@jeromefitz/notion', 22 | '@notionhq/client', 23 | // 'emoji-regex', 24 | // 'node-emoji', 25 | 'plaiceholder', 26 | ] 27 | /** 28 | * @note(tailwind) lol, if we do not transpile locally, 29 | * can we avoid the hack in app/design-system/page ? 30 | * also -- which one is the good one here, haha 31 | */ 32 | // const tp = ['@jeromefitz/ds', '@jeromefitz/shared', 'next-notion'] 33 | const tp = [] 34 | // const transpilePackages = isCI ? [] : [] 35 | const transpilePackages = tp 36 | // const transpilePackages = isCI ? [] : tp 37 | // const transpilePackages = isCI ? tp : [] 38 | 39 | const config = nextConfig({ 40 | basePath: '', 41 | buildInfoConfig: {}, 42 | pathDirName: join(__dirname), 43 | serverComponentsExternalPackages, 44 | transpilePackages, 45 | }) 46 | 47 | export default config 48 | -------------------------------------------------------------------------------- /sites/jerandky.com/postcss.config.js: -------------------------------------------------------------------------------- 1 | import configPostcss from '@jeromefitz/tailwind-config/postcss.config.js' 2 | 3 | export default configPostcss 4 | -------------------------------------------------------------------------------- /sites/jerandky.com/public/fonts/inter/inter-4.0.0-beta9g-var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/fonts/inter/inter-4.0.0-beta9g-var.woff2 -------------------------------------------------------------------------------- /sites/jerandky.com/public/fonts/inter/inter-var-full.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/fonts/inter/inter-var-full.woff2 -------------------------------------------------------------------------------- /sites/jerandky.com/public/fonts/inter/inter-var-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/fonts/inter/inter-var-italic.woff2 -------------------------------------------------------------------------------- /sites/jerandky.com/public/fonts/inter/inter-var-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/fonts/inter/inter-var-latin.woff2 -------------------------------------------------------------------------------- /sites/jerandky.com/public/fonts/inter/inter-var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/fonts/inter/inter-var.woff2 -------------------------------------------------------------------------------- /sites/jerandky.com/public/humans.txt: -------------------------------------------------------------------------------- 1 | . 2 | Jerome Fitzgerald 3 | Contact: j [at] jeromefitzgerald.com 4 | Website: jeromefitzgerald.com 5 | Twitter: @JeromeFitz 6 | . 7 | -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/images/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/images/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/images/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #f4f4f4 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/images/favicon/favicon.ico -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/public/images/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /sites/jerandky.com/public/images/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Jerome Fitzgerald (he/him)", 3 | "short_name": "Jerome", 4 | "icons": [ 5 | { 6 | "src": "/images/favicon/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/images/favicon/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png", 14 | "purpose": "any maskable" 15 | } 16 | ], 17 | "theme_color": "#0f0f0f", 18 | "background_color": "#0f0f0f", 19 | "display": "standalone" 20 | } 21 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/(notion)/_config/Page.types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CheckboxPropertyItemObjectResponse, 3 | FilesPropertyItemObjectResponse, 4 | FormulaPropertyItemObjectResponse, 5 | PageObjectResponse, 6 | RichTextPropertyItemObjectResponse, 7 | SelectPropertyItemObjectResponse, 8 | TitlePropertyItemObjectResponse, 9 | } from '@notionhq/client/build/src/api-endpoints' 10 | import type { Spread } from 'next-notion/Notion.types' 11 | 12 | type PageObjectResponsePage = Spread< 13 | [PageObjectResponse, { properties: PropertiesPage }] 14 | > 15 | interface PropertiesPage { 16 | ID: FormulaPropertyItemObjectResponse 17 | 'Is.Active': CheckboxPropertyItemObjectResponse 18 | 'Is.Indexed': CheckboxPropertyItemObjectResponse 19 | 'Is.Published': CheckboxPropertyItemObjectResponse 20 | 'Select.Test': SelectPropertyItemObjectResponse 21 | 'SEO.Description': RichTextPropertyItemObjectResponse 22 | 'SEO.Image': FilesPropertyItemObjectResponse 23 | 'SEO.Image.Description': RichTextPropertyItemObjectResponse 24 | 'SEO.Keywords': RichTextPropertyItemObjectResponse 25 | 'Slug.Preview': RichTextPropertyItemObjectResponse 26 | Title: TitlePropertyItemObjectResponse 27 | } 28 | 29 | export type { PageObjectResponsePage, PropertiesPage } 30 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/(notion)/_config/Person.utils.ts: -------------------------------------------------------------------------------- 1 | import { getPropertyTypeData } from 'next-notion/utils' 2 | 3 | import type { PropertiesPerson } from '../_config' 4 | 5 | function getPersonData(properties) { 6 | // if (!properties) return {} 7 | const data = { 8 | href: getPropertyTypeDataPerson(properties, 'Slug.Preview'), 9 | id: getPropertyTypeDataPerson(properties, 'ID'), 10 | isActive: getPropertyTypeDataPerson(properties, 'Is.Active'), 11 | isIndexed: getPropertyTypeDataPerson(properties, 'Is.Indexed'), 12 | isPublished: getPropertyTypeDataPerson(properties, 'Is.Published'), 13 | tags: [], 14 | title: getPropertyTypeDataPerson(properties, 'Title'), 15 | } 16 | return data 17 | } 18 | function getPropertyTypeDataPerson(properties, property: keyof PropertiesPerson) { 19 | return getPropertyTypeData(properties, property) 20 | } 21 | 22 | export { getPersonData, getPropertyTypeDataPerson } 23 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/(notion)/_config/Venue.types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | FormulaPropertyItemObjectResponse, 3 | NumberPropertyItemObjectResponse, 4 | PageObjectResponse, 5 | RelationPropertyItemObjectResponse, 6 | RichTextPropertyItemObjectResponse, 7 | TitlePropertyItemObjectResponse, 8 | } from '@notionhq/client/build/src/api-endpoints' 9 | import type { Spread } from 'next-notion/Notion.types' 10 | 11 | type PageObjectResponseVenue = Spread< 12 | [PageObjectResponse, { properties: PropertiesVenue }] 13 | > 14 | interface PropertiesVenue { 15 | 'Address.City': RichTextPropertyItemObjectResponse 16 | 'Address.Latitude': NumberPropertyItemObjectResponse 17 | 'Address.Longitude': NumberPropertyItemObjectResponse 18 | 'Address.Neighborhood': RichTextPropertyItemObjectResponse 19 | 'Address.PostalCode': NumberPropertyItemObjectResponse 20 | 'Address.State': RichTextPropertyItemObjectResponse 21 | 'Address.Street': RichTextPropertyItemObjectResponse 22 | // @todo(notion) deprecate field for Address.PostalCode 23 | 'Address.ZipCode': NumberPropertyItemObjectResponse 24 | ID: FormulaPropertyItemObjectResponse 25 | 'Relation.Events': RelationPropertyItemObjectResponse 26 | 'Slug.Preview': RichTextPropertyItemObjectResponse 27 | Title: TitlePropertyItemObjectResponse 28 | } 29 | 30 | export type { PageObjectResponseVenue, PropertiesVenue } 31 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/(notion)/_config/Venue.utils.ts: -------------------------------------------------------------------------------- 1 | import { getPropertyTypeData } from 'next-notion/utils' 2 | 3 | import type { PropertiesVenue } from '../_config' 4 | 5 | function getPropertyTypeDataVenue(properties, property: keyof PropertiesVenue) { 6 | return getPropertyTypeData(properties, property) 7 | } 8 | 9 | export { getPropertyTypeDataVenue } 10 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/(notion)/_config/config.ts: -------------------------------------------------------------------------------- 1 | import { envServer as env } from '@jeromefitz/next-config/env.server.mjs' 2 | 3 | const CONFIG = { 4 | EPISODES: { 5 | DATABASE_ID: env.NOTION__DATABASE__EPISODES ?? '', 6 | SEGMENT: 'episodes', 7 | }, 8 | 9 | PAGES: { 10 | DATABASE_ID: env.NOTION__DATABASE__PAGES ?? '', 11 | SEGMENT: 'pages', 12 | }, 13 | PEOPLE: { 14 | DATABASE_ID: env.NOTION__DATABASE__PEOPLE ?? '', 15 | SEGMENT: 'people', 16 | }, 17 | PODCASTS: { 18 | DATABASE_ID: env.NOTION__DATABASE__PODCASTS ?? '', 19 | SEGMENT: 'podcasts', 20 | }, 21 | VENUES: { 22 | DATABASE_ID: env.NOTION__DATABASE__VENUES ?? '', 23 | SEGMENT: 'venues', 24 | }, 25 | } 26 | 27 | export { CONFIG } 28 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/(notion)/_config/index.ts: -------------------------------------------------------------------------------- 1 | export { CONFIG } from './config' 2 | 3 | export type { PageObjectResponseEpisode, PropertiesEpisode } from './Episode.types' 4 | export { getEpisodeData, getPropertyTypeDataEpisode } from './Episode.utils' 5 | 6 | export type { PageObjectResponsePage, PropertiesPage } from './Page.types' 7 | export { getPageData, getPropertyTypeDataPage } from './Page.utils' 8 | 9 | export type { PageObjectResponsePerson, PropertiesPerson } from './Person.types' 10 | export { getPersonData, getPropertyTypeDataPerson } from './Person.utils' 11 | 12 | export type { PageObjectResponsePodcast, PropertiesPodcast } from './Podcast.types' 13 | export { getPodcastData, getPropertyTypeDataPodcast } from './Podcast.utils' 14 | 15 | export type { PageObjectResponseVenue, PropertiesVenue } from './Venue.types' 16 | export { getPropertyTypeDataVenue } from './Venue.utils' 17 | 18 | // @todo(types) way to do this for data passback and tell which one? 19 | // type PageObjectResponseCustom = 20 | // | PageObjectResponseEvent 21 | // | PageObjectResponsePage 22 | // | PageObjectResponsePerson 23 | // | PageObjectResponseShow 24 | // | PageObjectResponseVenue 25 | // export type { PageObjectResponseCustom } 26 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/(notion)/podcasts/[[...catchAll]]/_components/Podcast.Episodes.tsx: -------------------------------------------------------------------------------- 1 | import { Anchor } from '@jeromefitz/ds/components/Anchor' 2 | import { cx } from '@jeromefitz/ds/utils/cx' 3 | 4 | import { blocks } from '../../../../../components/Notion/Notion.Config' 5 | import { getPodcastData } from '../../../_config' 6 | 7 | function PodcastEpisodes({ properties }) { 8 | const { episodeSlugs, episodeTitles, ...props } = getPodcastData(properties) 9 | return ( 10 | <> 11 | 12 | Episodes 13 | 14 | 15 | {episodeSlugs.map((slug, i) => { 16 | const href = `${props.href}/${slug}` 17 | return ( 18 | 19 | {episodeTitles[i]} 20 | 21 | ) 22 | })} 23 | 24 | > 25 | ) 26 | } 27 | 28 | export { PodcastEpisodes } 29 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/src/app/apple-icon.png -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/src/app/favicon.ico -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/src/app/icon.png -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/src/app/icon1.png -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { FourOhFour } from './_errors/404' 2 | 3 | export default function NotFound() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { Anchor } from '@jeromefitz/ds/components/Anchor' 2 | import { 3 | SectionContent, 4 | SectionHeader, 5 | SectionHeaderContent, 6 | // SectionHero, 7 | SectionHeaderTitle, 8 | SectionWrapper, 9 | // Tags, 10 | } from '@jeromefitz/ds/components/Section' 11 | 12 | export default function Page() { 13 | return 14 | } 15 | 16 | function Slug() { 17 | const title = 'Jer & Ky BoyZ' 18 | const subtitle = 'MailShrimp' 19 | 20 | return ( 21 | <> 22 | 23 | 24 | {title} 25 | {subtitle} 26 | 27 | 28 | Website is under construction. 29 | 30 | Everything is available on RSS Feeds though through Apple, Spotify, and 31 | other podcast providers. 32 | 33 | 34 | Jer & Ky (& Guest) 35 | 36 | 37 | Knockoffs 38 | 39 | 40 | 41 | > 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/robots.ts: -------------------------------------------------------------------------------- 1 | import { MetadataRoute } from 'next' 2 | 3 | const host = 'https://jeromefitzgerald.com' 4 | 5 | function robots(): MetadataRoute.Robots { 6 | return { 7 | host, 8 | rules: { 9 | allow: ['/', '/api/v1/og/*'], 10 | disallow: ['/playground'], 11 | userAgent: '*', 12 | }, 13 | sitemap: `${host}/sitemap.xml`, 14 | } 15 | } 16 | 17 | export default robots 18 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import _orderBy from 'lodash/orderBy' 2 | import { MetadataRoute } from 'next' 3 | 4 | /** 5 | * @todo(next) dynamic 6 | */ 7 | const siteUrl = 'https://jeromefitzgerald.com' 8 | const lastModified = new Date() 9 | 10 | const root = [''] 11 | const podcasts = ['jer-and-ky-and-guest', 'knockoffs'] 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | const sitemapRoot = root.map((slug) => ({ 15 | lastModified, 16 | url: `${siteUrl}`, 17 | })) 18 | const sitemapPodcasts = podcasts.map((slug) => ({ 19 | lastModified, 20 | url: `${siteUrl}/podcasts/${slug}`, 21 | })) 22 | 23 | function sitemap(): MetadataRoute.Sitemap { 24 | return _orderBy( 25 | [ 26 | // { 27 | // url: `${siteUrl}`, 28 | // lastModified: new Date(), 29 | // }, 30 | ...sitemapRoot, 31 | ...sitemapPodcasts, 32 | ], 33 | ['url'], 34 | ['asc'], 35 | ) 36 | } 37 | 38 | export default sitemap 39 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/ErrorBoundary/index.ts: -------------------------------------------------------------------------------- 1 | export { ErrorBoundary } from './ErrorBoundary' 2 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Footer/Footer.client.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { MoonIcon, SunIcon } from '@jeromefitz/ds/components/Icon' 3 | import { useThemeToggle } from '@jeromefitz/shared/hooks/useThemeToggle' 4 | 5 | import { useTheme } from 'next-themes' 6 | import { useEffect, useState } from 'react' 7 | 8 | export const useLoaded = () => { 9 | const [loaded, setLoaded] = useState(false) 10 | useEffect(() => setLoaded(true), []) 11 | return loaded 12 | } 13 | 14 | function ThemeToggle() { 15 | const { resolvedTheme: theme, setTheme } = useTheme() 16 | const handleThemeToggle = useThemeToggle({ setTheme, theme }) 17 | const loaded = useLoaded() 18 | 19 | const Icon = theme === 'light' ? SunIcon : MoonIcon 20 | 21 | return ( 22 | <> 23 | handleThemeToggle()} 26 | > 27 | 28 | Toggle Theme 29 | 30 | {loaded && } 31 | 32 | 33 | 34 | > 35 | ) 36 | } 37 | 38 | export { ThemeToggle } 39 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Footer/index.ts: -------------------------------------------------------------------------------- 1 | export { Footer } from './Footer' 2 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Notion/Notion.tsx: -------------------------------------------------------------------------------- 1 | import { NotionBlocks } from 'next-notion/Notion.Blocks' 2 | 3 | import { blocks } from './Notion.Config' 4 | 5 | // @todo(types) 6 | function Notion({ data }: { data: any }) { 7 | return 8 | } 9 | 10 | export { Notion } 11 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Notion/index.ts: -------------------------------------------------------------------------------- 1 | export { Notion } from './Notion' 2 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Providers/Providers.tsx: -------------------------------------------------------------------------------- 1 | import { Providers as ProvidersClient } from './Providers.client' 2 | 3 | function Providers({ children }) { 4 | return {children} 5 | } 6 | 7 | export { Providers } 8 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Providers/RouterEventProvider.Loading.client.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { cx } from '@jeromefitz/ds/utils/cx' 3 | 4 | import { useNProgress } from '@tanem/react-nprogress' 5 | import { useEffect } from 'react' 6 | 7 | const Loading: React.FC<{ isRouteChanging: boolean }> = ({ isRouteChanging }) => { 8 | const { isFinished } = useNProgress({ 9 | isAnimating: isRouteChanging, 10 | minimum: 0.08, 11 | }) 12 | 13 | useEffect(() => { 14 | // eslint-disable-next-line @typescript-eslint/no-unused-expressions 15 | isFinished 16 | ? document.body.classList.remove('loading') 17 | : document.body.classList.add('loading') 18 | 19 | return () => { 20 | document.body.classList.remove('loading') 21 | } 22 | }, [isFinished]) 23 | 24 | return ( 25 | 38 | ) 39 | } 40 | 41 | export { Loading } 42 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Providers/RouterEventProvider.client.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import dynamic from 'next/dynamic' 3 | import { usePathname } from 'next/navigation' 4 | import { useEffect, useState } from 'react' 5 | 6 | // import { Loading } from './RouterEventProvider.Loading.client' 7 | const Loading = dynamic( 8 | async () => { 9 | const { Loading: Component } = await import( 10 | './RouterEventProvider.Loading.client' 11 | ) 12 | return { default: Component } 13 | }, 14 | { ssr: true }, 15 | ) 16 | 17 | function RouterEventProvider() { 18 | // @todo(types) 19 | const pathname: any = usePathname() 20 | 21 | const [isRouteChanging, isRouteChangingSet] = useState(false) 22 | const [pastRoute, pastRouteSet] = useState('') 23 | 24 | useEffect(() => { 25 | // eslint-disable-next-line @typescript-eslint/no-unused-expressions 26 | pastRoute !== pathname && isRouteChangingSet(true) 27 | // eslint-disable-next-line @typescript-eslint/no-unused-expressions 28 | pastRoute === pathname && isRouteChangingSet(false) 29 | pastRouteSet(pathname) 30 | }, [pathname, pastRoute]) 31 | 32 | return 33 | } 34 | 35 | export { RouterEventProvider } 36 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Providers/ThemeProvider.client.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { ThemeProvider as NextThemesProvider } from 'next-themes' 3 | 4 | // https://github.com/pacocoursey/next-themes/issues/152#issuecomment-1364280564 5 | export function ThemeProvider({ children }) { 6 | return ( 7 | 14 | {children} 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Providers/index.ts: -------------------------------------------------------------------------------- 1 | export { Providers } from './Providers' 2 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Relations/Relations.Loading.tsx: -------------------------------------------------------------------------------- 1 | function RelationLoading() { 2 | const random = Math.floor(Math.random() * (10 - 5 + 1)) + 5 3 | const colWidth = random === 12 ? 'w-full' : `w-${random}/12` 4 | return ( 5 | <> 6 | 7 | 10 | 11 | 12 | 13 | > 14 | ) 15 | } 16 | 17 | export { RelationLoading } 18 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Relations/Relations.utils.ts: -------------------------------------------------------------------------------- 1 | function getRelationTitle(str) { 2 | const title = str 3 | .split('.') 4 | .at(-1) 5 | .split(/(?=[A-Z])/) 6 | .join(' ') 7 | 8 | // console.dir(`title: ${title}`) 9 | 10 | return title 11 | } 12 | 13 | export { getRelationTitle } 14 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/components/Relations/index.ts: -------------------------------------------------------------------------------- 1 | export { Relations } from './Relations' 2 | export { RelationIndividual, RelationIndividuals } from './Relations.Individual' 3 | export { RelationLoading } from './Relations.Loading' 4 | export { getRelationTitle } from './Relations.utils' 5 | -------------------------------------------------------------------------------- /sites/jerandky.com/src/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jerandky.com/src/config/.gitkeep -------------------------------------------------------------------------------- /sites/jerandky.com/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.shared.json", 3 | "include": ["src", "index.d.ts", "next-env.d.ts"], 4 | "exclude": ["node_modules", "tests/*.test.ts"] 5 | } 6 | -------------------------------------------------------------------------------- /sites/jerandky.com/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.shared.json", 3 | "include": [ 4 | "./.next/types/**/*.ts", 5 | ".next/types/**/*.ts", 6 | "index.d.ts", 7 | "next-env.d.ts", 8 | "src" 9 | ], 10 | "exclude": ["node_modules", "tests/*.test.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /sites/jerandky.com/tsconfig.shared.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@jeromefitz/ds/*": ["../../packages/design-system/src/*"], 7 | "@jeromefitz/next-config/*": ["../../packages/next-config/src/*"], 8 | "@jeromefitz/shared/*": ["../../packages/shared/src/*"], 9 | "next-notion/*": ["../../packages/next-notion/src/*"], 10 | "~app/*": ["src/app/*"], 11 | "~components/*": ["src/components/*"], 12 | "~config/*": ["src/config/*"], 13 | "~containers/*": ["src/containers/*"], 14 | "~context/*": ["src/context/*"], 15 | "~custom/*": ["src/custom/*"], 16 | "~data/*": ["src/data/*"], 17 | "~hooks/*": ["src/hooks/*"], 18 | "~lib/*": ["src/lib/*"], 19 | "~pages/*": ["src/pages/*"], 20 | "~playground/*": ["src/playground/*"], 21 | "~routes/*": ["src/routes/*"], 22 | "~scripts/*": ["src/scripts/*"], 23 | "~store/*": ["src/store/*"], 24 | "~styles/*": ["src/styles/*"], 25 | "~svg/*": ["src/svg/*"], 26 | "~theme/*": ["src/theme/*"], 27 | "~ui/*": ["src/ui/*"], 28 | "~utils/*": ["src/utils/*"] 29 | }, 30 | "plugins": [ 31 | { 32 | "name": "next" 33 | } 34 | ], 35 | "preserveSymlinks": true, 36 | "skipLibCheck": true, 37 | "strictNullChecks": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sites/jeromeand.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromeand.com/.gitkeep -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/.lighthouserc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /* eslint-disable @typescript-eslint/no-require-imports */ 3 | const isCI = require('is-ci') 4 | 5 | let urlAdditional = undefined 6 | 7 | /** 8 | * @todo(turbo) 2.0.3 concern with way we pass `CI` in some cases 9 | */ 10 | if (!isCI) { 11 | urlAdditional = [ 12 | // '/currently/listening-to', 13 | // '/currently/reading', 14 | // '/events', 15 | // '/events/2023/07/15/jerome-and', 16 | // '/shows', 17 | // '/shows/alex-o-jerome', 18 | ] 19 | } 20 | 21 | module.exports = require('@jeromefitz/lighthouse-config')({ 22 | urlAdditional, 23 | website: 'jeromefitzgerald.com', 24 | }) 25 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { envServer } from '@jeromefitz/next-config/env.server.mjs' 2 | 3 | import type { Config } from 'drizzle-kit' 4 | 5 | export default { 6 | dbCredentials: { 7 | url: envServer.POSTGRES_URL!, 8 | }, 9 | dialect: 'postgresql', 10 | out: './src/lib/drizzle/init/migrations', 11 | schema: './src/lib/drizzle/schemas/index.ts', 12 | } satisfies Config 13 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/jest.config.cjs: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef, @typescript-eslint/no-require-imports 2 | module.exports = require('@jeromefitz/jest-config/jest.config.js') 3 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import configPlaywright from '@jeromefitz/playwright-config' 2 | 3 | export default configPlaywright({ 4 | basePath: '', 5 | port: 3000, 6 | }) 7 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | export default { 3 | plugins: { 4 | '@tailwindcss/postcss': {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/fonts/geist/GeistMonoVF--1.0.0.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/fonts/geist/GeistMonoVF--1.0.0.woff2 -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/fonts/geist/GeistVF--1.0.0.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/fonts/geist/GeistVF--1.0.0.woff2 -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/fonts/inter/inter-4.0.0-beta9g-var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/fonts/inter/inter-4.0.0-beta9g-var.woff2 -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/fonts/inter/inter-var-full.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/fonts/inter/inter-var-full.woff2 -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/fonts/inter/inter-var-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/fonts/inter/inter-var-italic.woff2 -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/fonts/inter/inter-var-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/fonts/inter/inter-var-latin.woff2 -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/fonts/inter/inter-var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/fonts/inter/inter-var.woff2 -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/humans.txt: -------------------------------------------------------------------------------- 1 | . 2 | Jerome Fitzgerald (he/him) 3 | Contact: j [at] jeromefitzgerald.com 4 | Website: jeromefitzgerald.com 5 | Bluesky: @jeromefitzgerald.com 6 | . 7 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/cursors/pink/cursor-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | cursor-close 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #f4f4f4 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/favicon/favicon.ico -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Jerome Fitzgerald (he/him)", 3 | "short_name": "Jerome", 4 | "icons": [ 5 | { 6 | "src": "/images/favicon/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/images/favicon/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png", 14 | "purpose": "any maskable" 15 | } 16 | ], 17 | "theme_color": "#030303", 18 | "background_color": "#030303", 19 | "display": "standalone" 20 | } 21 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/logos/arcade-comedy-theater.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/logos/arcade-comedy-theater.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/logos/broadway-world--black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/logos/broadway-world--black.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/logos/broadway-world--white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/logos/broadway-world--white.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/logos/broadway-world.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/logos/broadway-world.jpeg -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/logos/broadway-world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/logos/broadway-world.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/logos/pittsburgh-city-paper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/logos/pittsburgh-city-paper.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/public/images/logos/pittsburgh-magazine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/public/images/logos/pittsburgh-magazine.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/release.config.mjs: -------------------------------------------------------------------------------- 1 | import { getConfig } from '@jeromefitz/semantic' 2 | 3 | import isCI from 'is-ci' 4 | 5 | import { config as configDefault } from '../../release.config.mjs' 6 | 7 | if (!isCI) { 8 | const dotenv = await import('dotenv') 9 | dotenv.config({ path: '../../.env' }) 10 | } 11 | 12 | // const require = createRequire(import.meta.url) 13 | // const pkg = require('./package.json') 14 | // // const { name } = require('./package.json') 15 | // const { name } = pkg 16 | 17 | const configPassed = { 18 | ...configDefault, 19 | repositoryUrl: `https://github.com/JeromeFitz/websites`, 20 | tagFormat: 'website-v${version}', 21 | } 22 | 23 | const config = getConfig(configPassed) 24 | 25 | export default config 26 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/(pages)/about/_components/Section.Client.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@radix-ui/themes/dist/esm/components/box.js' 2 | import NextLink from 'next/link' 3 | 4 | function SectionClientLegend({ data }: { data: any }) { 5 | return ( 6 | <> 7 | {data.map((item: any, i: number) => { 8 | return ( 9 | 16 | {item.title} 17 | 18 | ) 19 | })} 20 | > 21 | ) 22 | } 23 | 24 | export { SectionClientLegend } 25 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/(pages)/currently/listening-to/page.tsx: -------------------------------------------------------------------------------- 1 | import { MusicClient } from './_components/Music.client' 2 | 3 | export default function Page() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/(segments)/blog/page.tsx: -------------------------------------------------------------------------------- 1 | import { getBlogs, segment } from '@/lib/drizzle/schemas/cache-blogs/queries' 2 | import { buildInitialCache } from '@/lib/notion/buildInitialCache' 3 | 4 | import { List } from './_components/List' 5 | 6 | // export const dynamic = 'force-dynamic' 7 | export const dynamic = 'force-static' 8 | 9 | export default async function Home() { 10 | const items = await getBlogs() 11 | await buildInitialCache({ segment }) 12 | 13 | return ( 14 | <> 15 | 16 | > 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/(segments)/pages/page.tsx: -------------------------------------------------------------------------------- 1 | import { segment } from '@/lib/drizzle/schemas/cache-pages/queries' 2 | import { buildInitialCache } from '@/lib/notion/buildInitialCache' 3 | 4 | // export const dynamic = 'force-dynamic' 5 | export const dynamic = 'force-static' 6 | 7 | export default async function Index() { 8 | await buildInitialCache({ segment }) 9 | 10 | return null 11 | } 12 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/(segments)/venues/[key]/_components/Venue.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | import { Heading } from '@radix-ui/themes/dist/esm/components/heading.js' 3 | import { Text } from '@radix-ui/themes/dist/esm/components/text.js' 4 | import { format } from 'date-fns' 5 | 6 | import type { Venue } from '@/lib/drizzle/schemas/cache-venues/types' 7 | 8 | import { ImageNotion } from '@/components/Image/Image.Notion' 9 | import { segment } from '@/lib/drizzle/schemas/cache-venues/queries' 10 | 11 | export function Venue({ item }: { item: Venue }) { 12 | return ( 13 | 24 | 25 | 26 | “{item.title}” 27 | 28 | 29 | {item.slugPreview} 30 | 31 | 32 | ISO: {format(item.datePublished, 'yyyy-MM-dd')} 33 | 34 | 35 | 36 | 37 | 38 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/(segments)/venues/page.tsx: -------------------------------------------------------------------------------- 1 | import { getVenues, segment } from '@/lib/drizzle/schemas/cache-venues/queries' 2 | import { buildInitialCache } from '@/lib/notion/buildInitialCache' 3 | 4 | import { List } from './_components/List' 5 | 6 | // export const dynamic = 'force-dynamic' 7 | export const dynamic = 'force-static' 8 | 9 | export default async function Index() { 10 | const items = await getVenues() 11 | await buildInitialCache({ segment }) 12 | 13 | // items.map((item) => { 14 | // console.dir(`item: ${item.id}: ${item.updated_at}`) 15 | // }) 16 | 17 | return ( 18 | <> 19 | 20 | > 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/_next/fonts.tsx: -------------------------------------------------------------------------------- 1 | import { cx } from '@/utils/cx' 2 | 3 | // import { fonts as borogodo } from './fonts/borogodo' 4 | import { fonts as geist } from './fonts/geist' 5 | 6 | 7 | const fonts = cx(geist) 8 | 9 | export { fonts } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/_next/fonts/borogodo.tsx: -------------------------------------------------------------------------------- 1 | import localFont from 'next/font/local' 2 | 3 | import { cx } from '@/utils/cx' 4 | 5 | const borogodo = localFont({ 6 | src: [ 7 | { 8 | path: 'https://cdn.jeromefitzgerald.com/fonts/borogodo/0.2/BorogodoV0.2-BlackS.woff2', 9 | style: 'normal', 10 | weight: '400', 11 | }, 12 | ], 13 | }) 14 | 15 | console.dir(`> borogodo:`) 16 | console.dir(borogodo) 17 | 18 | const fonts = cx(borogodo.className) 19 | 20 | export { fonts } 21 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/_next/fonts/geist.tsx: -------------------------------------------------------------------------------- 1 | import { Geist_Mono, Geist as Geist_Sans } from 'next/font/google' 2 | 3 | import { cx } from '@/utils/cx' 4 | const geistMono = Geist_Mono({ 5 | display: 'swap', 6 | subsets: ['latin'], 7 | variable: '--font-geist-mono', 8 | }) 9 | const geistSans = Geist_Sans({ 10 | display: 'swap', 11 | subsets: ['latin'], 12 | variable: '--font-geist-sans', 13 | }) 14 | 15 | const fonts = cx(geistSans.variable, geistMono.variable) 16 | 17 | export { fonts } 18 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/_next/preload-resources.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { preconnect, prefetchDNS } from 'react-dom' 4 | 5 | const preconnects = [ 6 | // https://web.dev/preconnect-and-dns-prefetch/#how-to-implement-rel=preconnect 7 | // 'https://jeromefitzgerald.com', 8 | 'https://cdn.jeromefitzgerald.com', 9 | 'https://cdn.jerandky.com', 10 | 'https://cdn.usefathom.com', 11 | 'https://vitals.vercel-insights.com', 12 | ] 13 | 14 | function PreloadResources() { 15 | preconnects.map((p) => { 16 | // console.dir(`> p: ${p}`) 17 | preconnect(p, { crossOrigin: 'anonymous' }) 18 | prefetchDNS(p) 19 | }) 20 | return null 21 | } 22 | 23 | export { PreloadResources } 24 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/api/v1/revalidate/actions.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | import { envServer as env } from '@jeromefitz/next-config/env.server.mjs' 3 | 4 | // eslint-disable-next-line @typescript-eslint/require-await 5 | export async function isValid(signature: null | string): Promise { 6 | return env.REVALIDATE_TOKEN === signature 7 | } 8 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/src/app/apple-icon.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/src/app/favicon.ico -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/src/app/icon.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/jeromefitzgerald.com/src/app/icon1.png -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | import { Heading } from '@radix-ui/themes/dist/esm/components/heading.js' 3 | import { Link } from '@radix-ui/themes/dist/esm/components/link.js' 4 | import { Text } from '@radix-ui/themes/dist/esm/components/text.js' 5 | import NextLink from 'next/link' 6 | 7 | export default function NotFound() { 8 | const item = { 9 | id: '404', 10 | subtitle: 'Not Found', 11 | title: '404', 12 | } 13 | 14 | return ( 15 | <> 16 | 27 | 28 | 29 | “{item.title}” 30 | 31 | 32 | {item.subtitle} 33 | 34 | 35 | Nothing so see here. 36 | 37 | Go Home 38 | 39 | 40 | 41 | > 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Accordion/AccordionContent.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | 'use client' 3 | import type { AccordionContentProps } from '@radix-ui/react-accordion' 4 | 5 | import * as Accordion from '@radix-ui/react-accordion' 6 | import { forwardRef } from 'react' 7 | 8 | import { cx } from '@/utils/cx' 9 | 10 | interface AccordionContentPropsImpl extends AccordionContentProps { 11 | children?: any 12 | className?: any 13 | } 14 | 15 | const AccordionContent = forwardRef( 16 | ({ children, className, ...props }: AccordionContentPropsImpl, forwardedRef) => ( 17 | 26 | {children} 27 | 28 | ), 29 | ) 30 | 31 | export { AccordionContent } 32 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Accordion/AccordionItem.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | 'use client' 3 | import type { AccordionItemProps } from '@radix-ui/react-accordion' 4 | 5 | import * as Accordion from '@radix-ui/react-accordion' 6 | import { forwardRef } from 'react' 7 | 8 | import { cx } from '@/utils/cx' 9 | 10 | const AccordionItem = forwardRef( 11 | // @todo(types) radix 12 | // @ts-ignore 13 | ({ children, className, ...props }: AccordionItemProps, forwardedRef) => ( 14 | 23 | {children} 24 | 25 | ), 26 | ) 27 | 28 | export { AccordionItem } 29 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Accordion/AccordionList.tsx: -------------------------------------------------------------------------------- 1 | function AccordionList({ children }: { children: React.ReactNode }) { 2 | return {children} 3 | } 4 | 5 | export { AccordionList } 6 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Accordion/AccordionRoot.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | 'use client' 3 | 4 | import { Accordion as AccordionRoot } from '@radix-ui/react-accordion' 5 | import { forwardRef } from 'react' 6 | 7 | interface AccordionRootPropsImpl { 8 | children?: any 9 | className?: any 10 | type?: 'multiple' | 'single' 11 | } 12 | 13 | const AccordionRootImpl = forwardRef( 14 | ( 15 | { children, className, type = 'single', ...props }: AccordionRootPropsImpl, 16 | forwardedRef, 17 | ) => ( 18 | 25 | {children} 26 | 27 | ), 28 | ) 29 | 30 | export { AccordionRootImpl as AccordionRoot } 31 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Accordion/AccordionTrigger.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | 'use client' 3 | import type { AccordionTriggerProps } from '@radix-ui/react-accordion' 4 | 5 | import * as Accordion from '@radix-ui/react-accordion' 6 | import { forwardRef } from 'react' 7 | 8 | import { ChevronDownIcon } from '@/components/Icon/index' 9 | import { cx } from '@/utils/cx' 10 | 11 | interface AccordionTriggerPropsImpl extends AccordionTriggerProps { 12 | children?: any 13 | className?: any 14 | } 15 | 16 | const AccordionTrigger = forwardRef( 17 | ({ children, className, ...props }: AccordionTriggerPropsImpl, forwardedRef) => ( 18 | // @ts-ignore 19 | 20 | 29 | {children} 30 | 34 | 35 | 36 | ), 37 | ) 38 | 39 | export { AccordionTrigger } 40 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Accordion/index.ts: -------------------------------------------------------------------------------- 1 | export { AccordionContent } from './AccordionContent' 2 | export { AccordionItem } from './AccordionItem' 3 | export { AccordionList } from './AccordionList' 4 | export { AccordionListItem } from './AccordionListItem' 5 | export { AccordionRoot } from './AccordionRoot' 6 | export { AccordionTrigger } from './AccordionTrigger' 7 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Analytics/Analytics.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from 'react' 2 | 3 | import { FathomAnalytics } from './Fathom' 4 | import { VercelAnalytics, VercelSpeedInsights } from './Vercel' 5 | 6 | function Analytics() { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | 13 | 14 | > 15 | ) 16 | } 17 | 18 | export { Analytics } 19 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Analytics/Fathom.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { envClient as env } from '@jeromefitz/next-config/env.client.mjs' 3 | 4 | import { load, trackPageview } from 'fathom-client' 5 | import { usePathname, useSearchParams } from 'next/navigation.js' 6 | import { useEffect } from 'react' 7 | 8 | function FathomAnalytics() { 9 | const pathname = usePathname() 10 | const searchParams = useSearchParams() 11 | useEffect(() => { 12 | if (env.IS_PRODUCTION) { 13 | load(env.NEXT_PUBLIC__FATHOM_SITE_ID, { 14 | honorDNT: true, 15 | includedDomains: [env.NEXT_PUBLIC__SITE], 16 | url: `https://cdn.usefathom.com/script.js`, 17 | }) 18 | } 19 | }, []) 20 | 21 | useEffect(() => { 22 | if (!pathname) return 23 | trackPageview({ 24 | referrer: document.referrer, 25 | url: pathname + searchParams.toString(), 26 | }) 27 | }, [pathname, searchParams]) 28 | 29 | return null 30 | } 31 | 32 | export { FathomAnalytics } 33 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Analytics/Vercel.tsx: -------------------------------------------------------------------------------- 1 | import { envClient as env } from '@jeromefitz/next-config/env.client.mjs' 2 | 3 | import { Analytics } from '@vercel/analytics/react' 4 | import { SpeedInsights } from '@vercel/speed-insights/next' 5 | 6 | function VercelAnalytics() { 7 | return env.IS_VERCEL ? : null 8 | } 9 | 10 | function VercelSpeedInsights() { 11 | return env.IS_VERCEL ? : null 12 | } 13 | 14 | export { VercelAnalytics, VercelSpeedInsights } 15 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Analytics/index.ts: -------------------------------------------------------------------------------- 1 | export { Analytics } from './Analytics' 2 | export { FathomAnalytics } from './Fathom' 3 | export { VercelAnalytics, VercelSpeedInsights } from './Vercel' 4 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Anchor/index.ts: -------------------------------------------------------------------------------- 1 | export { Anchor } from './Anchor' 2 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Article/Article.Main.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | 3 | /** 4 | * @todo(a11y) should this be article instead of main 5 | */ 6 | function ArticleMain({ children }: { children: React.ReactNode }) { 7 | return ( 8 | 24 | 25 | {children} 26 | 27 | 28 | ) 29 | } 30 | 31 | export { ArticleMain } 32 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Callout/Callout.tsx: -------------------------------------------------------------------------------- 1 | import type { RootProps as CalloutRootProps } from '@radix-ui/themes/dist/esm/components/callout.js' 2 | import type { ReactNode } from 'react' 3 | 4 | import { 5 | Icon as CalloutIcon, 6 | Root as CalloutRoot, 7 | Text as CalloutText, 8 | } from '@radix-ui/themes/dist/esm/components/callout.js' 9 | 10 | import { FileTextIcon } from '@/components/Icon/index' 11 | import { cx } from '@/utils/cx' 12 | 13 | interface AdditionalProps { 14 | children?: ReactNode 15 | className?: string 16 | classNameText?: string 17 | color?: string 18 | icon?: any 19 | } 20 | type CalloutRootPropsImpl = AdditionalProps & CalloutRootProps 21 | 22 | function CalloutImpl({ 23 | children = <>This page is in the process of being updated.>, 24 | className = '', 25 | classNameText = '', 26 | color = 'pink', 27 | icon: Icon = FileTextIcon, 28 | size = '2', 29 | variant = 'soft', 30 | }: CalloutRootPropsImpl) { 31 | return ( 32 | 38 | 39 | 40 | 41 | {children} 42 | 43 | ) 44 | } 45 | 46 | export { CalloutImpl as Callout } 47 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Callout/index.ts: -------------------------------------------------------------------------------- 1 | export { Callout } from './Callout' 2 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Container/Container.Gradient.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@radix-ui/themes/dist/esm/components/box.js' 2 | 3 | import { cx } from '@/utils/cx' 4 | 5 | function ContainerGradient() { 6 | return ( 7 | <> 8 | 19 | 31 | > 32 | ) 33 | } 34 | 35 | export { ContainerGradient } 36 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Container/Container.Main.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | 3 | import { cx } from '@/utils/cx' 4 | 5 | function ContainerContent({ 6 | children, 7 | className = '', 8 | }: { 9 | children: React.ReactNode 10 | className?: string 11 | }) { 12 | return ( 13 | 31 | {children} 32 | 33 | ) 34 | } 35 | 36 | function ContainerWithSidebar({ children }: { children: React.ReactNode }) { 37 | return ( 38 | 39 | {children} 40 | 41 | ) 42 | } 43 | 44 | export { ContainerContent, ContainerWithSidebar } 45 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Container/Container.Navigation.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@radix-ui/themes/dist/esm/components/box.js' 2 | 3 | import { Navigation } from '@/components/Navigation/Navigation' 4 | import { cx } from '@/utils/cx' 5 | 6 | function ContainerNavigation() { 7 | return ( 8 | 17 | 18 | 19 | 20 | 21 | ) 22 | } 23 | 24 | export { ContainerNavigation } 25 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Container/Container.Section.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@radix-ui/themes/dist/esm/components/box.js' 2 | 3 | import { cx } from '@/utils/cx' 4 | 5 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 6 | // @ts-ignore 7 | function ContainerSection({ children }: { children: React.ReactNode }) { 8 | return ( 9 | 17 | {children} 18 | 19 | ) 20 | } 21 | 22 | export { ContainerSection } 23 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Container/Container.Site.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | 3 | import { cx } from '@/utils/cx' 4 | 5 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 6 | // @ts-ignore 7 | function ContainerSite({ children }: { children: React.ReactNode }) { 8 | return ( 9 | 25 | {children} 26 | 27 | ) 28 | } 29 | 30 | export { ContainerSite } 31 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Credits/Credits.Header.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | import { Heading } from '@radix-ui/themes/dist/esm/components/heading.js' 3 | import { Text } from '@radix-ui/themes/dist/esm/components/text.js' 4 | 5 | import { cx } from '@/utils/cx' 6 | 7 | function CreditsHeader({ 8 | subtitle = 'Some of the wonderful people who make it happen.', 9 | title = 'Info', 10 | }) { 11 | return ( 12 | 30 | 35 | {title} 36 | 37 | 42 | {subtitle} 43 | 44 | 45 | ) 46 | } 47 | 48 | export { CreditsHeader } 49 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Credits/Credits.Item.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from '@radix-ui/themes/dist/esm/components/text.js' 2 | import { cache } from 'react' 3 | 4 | // import { getEventData } from '@/app/(notion)/_config/index' 5 | import { Anchor } from '@/components/Anchor/index' 6 | 7 | const CreditsItem = cache(({ item }: { item: any }) => { 8 | if (!item) return null 9 | const { properties } = item 10 | if (!properties) return null 11 | // const { href, isPublished, title } = getEventData(properties) 12 | 13 | if (item.isPublished) { 14 | return {item.title} 15 | } 16 | return {item.title} 17 | }) 18 | 19 | export { CreditsItem } 20 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Credits/Credits.Items.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from '@radix-ui/themes/dist/esm/components/text.js' 2 | 3 | import { LI } from '@/components/List/index' 4 | 5 | // import { CreditsItem } from './Credits.Item' 6 | 7 | /** 8 | * @todo(dynamic-credits) 9 | * title only? items are string[] 10 | * has data? items should be dynamically loaded via CreditsItem and have attributes 11 | */ 12 | const CreditsItems = ({ items }: { items: string[] }) => { 13 | return ( 14 | <> 15 | {items.map((item: string, i: number) => { 16 | return ( 17 | 18 | {item} 19 | {/* */} 20 | 21 | ) 22 | })} 23 | > 24 | ) 25 | } 26 | 27 | export { CreditsItems } 28 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Credits/Credits.Loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from '@radix-ui/themes/dist/esm/components/skeleton.js' 2 | import { Text } from '@radix-ui/themes/dist/esm/components/text.js' 3 | 4 | import { LI } from '@/components/List/index' 5 | 6 | function CreditsLoading({ size }: { size: number }) { 7 | return ( 8 | <> 9 | {Array(size) 10 | .fill(0) 11 | .map((_, i) => { 12 | return ( 13 | 14 | 15 | 16 | ) 17 | })} 18 | > 19 | ) 20 | } 21 | 22 | function Loading() { 23 | const random = 'ABCDEFGHIJKLMNOP' 24 | return ( 25 | 26 | 27 | {random} 28 | 29 | 30 | ) 31 | } 32 | 33 | export { CreditsLoading } 34 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Credits/Credits.utils.ts: -------------------------------------------------------------------------------- 1 | function getRelationTitle(str?: string) { 2 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 3 | // @ts-ignore 4 | const title = str 5 | .split('.') 6 | .at(-1) 7 | .split(/(?=[A-Z])/) 8 | .join(' ') 9 | 10 | return title 11 | } 12 | 13 | function getRollupTitle(str: string) { 14 | const title = str 15 | .replace('rollupPeople', '') 16 | .replace('rollupShows', '') 17 | .replace('Title', '') 18 | .replace('PrimaryCast', 'Cast') 19 | .replace('CastPast', 'Cast Emeritus') 20 | .replace('DirectorMusical', 'Musical Director') 21 | .replace('DirectorTechnical', 'Technical Director') 22 | 23 | return title 24 | } 25 | 26 | export { getRelationTitle, getRollupTitle } 27 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/ErrorBoundary/index.ts: -------------------------------------------------------------------------------- 1 | export { ErrorBoundary } from './ErrorBoundary' 2 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Icon/Icon.stories.tsx: -------------------------------------------------------------------------------- 1 | // import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | // import { cx } from '@/utils/cx' 4 | // import { MapIcon } from './Icon' 5 | 6 | // const meta = { 7 | // component: MapIcon, 8 | // // @todo(eslint) storybook/no-title-property-in-meta 9 | // title: 'Components/Icon', 10 | // } satisfies Meta 11 | 12 | // export default meta 13 | 14 | // type Story = StoryObj 15 | 16 | // export const Default: Story = { 17 | // args: { 18 | // className: cx(), 19 | // }, 20 | // argTypes: {}, 21 | // } 22 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Icon/Icon.test.tsx: -------------------------------------------------------------------------------- 1 | // import { expect } from '@jest/globals' 2 | // import { composeStories } from '@storybook/react' 3 | // import { render, screen } from '@testing-library/react' 4 | 5 | // import * as stories from './Icon.stories' 6 | 7 | // const IconStories = composeStories(stories) 8 | 9 | // describe('Icon', () => { 10 | // it('should contain a svg', () => { 11 | // render() 12 | 13 | // const icon = screen.getByRole('info', { hidden: true }) 14 | 15 | // expect(icon).toBeInTheDocument() 16 | // }) 17 | // }) 18 | 19 | describe('WIP', () => { 20 | it('placeholder: need _a_ test until you add more', () => { 21 | expect(true).toBe(true) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Icon/Icon.types.ts: -------------------------------------------------------------------------------- 1 | import { SVGAttributes } from 'react' 2 | 3 | /** 4 | * ref: https://github.com/radix-ui/icons/blob/master/packages/radix-icons/src/types.tsx 5 | */ 6 | interface IconProps extends SVGAttributes { 7 | children?: never 8 | color?: string 9 | label?: string 10 | style?: any 11 | } 12 | 13 | export type { IconProps } 14 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/List/LI.tsx: -------------------------------------------------------------------------------- 1 | import { CornerBottomLeftIcon } from '@radix-ui/react-icons' 2 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 3 | 4 | import { cx } from '@/utils/cx' 5 | 6 | function LI({ 7 | children, 8 | className = '', 9 | }: { 10 | children: React.ReactNode 11 | className?: string 12 | }) { 13 | return ( 14 | 21 | 22 | 23 | {children} 24 | 25 | 26 | ) 27 | } 28 | 29 | export { LI } 30 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/List/OL.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | 3 | function OL({ 4 | children, 5 | className = '', 6 | }: { 7 | children: React.ReactNode 8 | className?: string 9 | }) { 10 | return ( 11 | 12 | {children} 13 | 14 | ) 15 | } 16 | 17 | export { OL } 18 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/List/UL.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | 3 | function UL({ 4 | children, 5 | className = '', 6 | }: { 7 | children: React.ReactNode 8 | className?: string 9 | }) { 10 | return ( 11 | 12 | {children} 13 | 14 | ) 15 | } 16 | 17 | export { UL } 18 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/List/index.ts: -------------------------------------------------------------------------------- 1 | export { LI } from './LI' 2 | export { OL } from './OL' 3 | export { UL } from './UL' 4 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Navigation/Navigation.Button.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@radix-ui/themes/dist/esm/components/flex.js' 2 | 3 | import { cx } from '@/utils/cx' 4 | 5 | import { NavigationButtonClient } from './Navigation.Button.Client' 6 | 7 | function NavigationButton() { 8 | return ( 9 | 19 | 20 | 21 | ) 22 | } 23 | 24 | export { NavigationButton } 25 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Navigation/Navigation.Separator.tsx: -------------------------------------------------------------------------------- 1 | import { CaretRightIcon } from '@radix-ui/react-icons' 2 | 3 | import { cx } from '@/utils/cx' 4 | 5 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 6 | // @ts-ignore 7 | function NavigationSeparator({ className, isActive = true, order = 0 }) { 8 | if (!isActive) return null 9 | return ( 10 | 17 | 18 | 21 | 22 | 23 | ) 24 | } 25 | 26 | export { NavigationSeparator } 27 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Notion/Blocks/Image.utils.ts: -------------------------------------------------------------------------------- 1 | import _filter from 'lodash/filter.js' 2 | import _startsWith from 'lodash/startsWith.js' 3 | 4 | const FIND_ALT = 'ALT: ' 5 | 6 | function getImageAlt(comments: any) { 7 | const comment = comments[0] 8 | const c = _filter(comments, (comment) => 9 | _startsWith(comment?.rich_text[0]?.plain_text, FIND_ALT), 10 | ) 11 | return !!c && c.length > 0 12 | ? c[0]?.rich_text[0]?.plain_text.slice(FIND_ALT.length) 13 | : comment 14 | ? comment?.rich_text[0]?.plain_text 15 | : '' 16 | } 17 | 18 | function getImageExpiration(block: any) { 19 | return block[block?.type]?.type === 'external' 20 | ? null 21 | : block[block?.type]?.file?.expiry_time 22 | } 23 | 24 | function getImageUrl(block: any) { 25 | return block[block.type].type === 'external' 26 | ? block[block?.type]?.external?.url 27 | : block[block?.type]?.file?.url 28 | } 29 | 30 | export { getImageAlt, getImageExpiration, getImageUrl } 31 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Overlay/Overlay.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { useStore as _useStore, useShallow } from '@/store/index' 3 | import { cx } from '@/utils/cx' 4 | 5 | const useStore = () => { 6 | return _useStore( 7 | useShallow((store: { isOverlay: any }) => ({ 8 | isOverlay: store.isOverlay, 9 | })), 10 | ) 11 | } 12 | 13 | function Overlay() { 14 | const { isOverlay } = useStore() 15 | return ( 16 | 25 | ) 26 | } 27 | 28 | export { Overlay } 29 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Providers/Providers.client.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | function Providers({ children }: { children: React.ReactNode }) { 4 | return <>{children}> 5 | } 6 | 7 | export { Providers } 8 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Providers/StoreProvider.client.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { type PropsWithChildren } from 'react' 3 | 4 | import { Provider } from '@/store/index' 5 | 6 | const StoreProvider = ({ children }: PropsWithChildren) => { 7 | return {children} 8 | } 9 | 10 | export { StoreProvider } 11 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Providers/ThemeProvider.client.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { ThemeProvider as NextThemesProvider } from 'next-themes' 3 | 4 | // https://github.com/pacocoursey/next-themes/issues/152#issuecomment-1364280564 5 | export function ThemeProvider({ children }: { children: React.ReactNode }) { 6 | return ( 7 | 14 | {children} 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/SkipNav/index.ts: -------------------------------------------------------------------------------- 1 | export { SkipNavContent, SkipNavLink } from './SkipNav' 2 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/components/Tags/Tags.tsx: -------------------------------------------------------------------------------- 1 | import { cx } from '@/utils/cx' 2 | 3 | function Tags({ 4 | className = '', 5 | classNameTag = '', 6 | tags, 7 | }: { 8 | className?: string 9 | classNameTag?: string 10 | tags: any 11 | }) { 12 | // console.dir(`> tags:`) 13 | // console.dir(tags) 14 | return ( 15 | 22 | {tags?.map((tag: any) => { 23 | const { color, id, name } = tag 24 | return ( 25 | 44 | {name} 45 | 46 | ) 47 | })} 48 | 49 | ) 50 | } 51 | 52 | export { Tags } 53 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/config/const.ts: -------------------------------------------------------------------------------- 1 | const TZ = 'America/New_York' 2 | 3 | export { TZ } 4 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/data/bandcamps.ts: -------------------------------------------------------------------------------- 1 | const bandcamps = [ 2 | { 3 | album: 'The Control Center', 4 | artist: 'Buscrates', 5 | href: 'https://buscrates412.bandcamp.com/album/control-center', 6 | }, 7 | { 8 | album: 'Check Please', 9 | artist: 'Cam Chambers and Nice Rec', 10 | href: 'https://camchambers.bandcamp.com/album/check-please', 11 | }, 12 | { 13 | album: 'Shifty', 14 | artist: 'Else Collective', 15 | href: 'https://pjroduta.bandcamp.com/album/shifty', 16 | }, 17 | { 18 | album: 'Heat', 19 | artist: 'Flower Crown', 20 | href: 'https://flowercrownmusic.bandcamp.com/album/heat', 21 | }, 22 | { 23 | album: 'Made For The Soul', 24 | artist: 'FRH Golden x pvkvsv', 25 | href: 'https://frhgolden.bandcamp.com/album/made-for-the-soul', 26 | }, 27 | { 28 | album: 'Yinztroducing...', 29 | artist: 'Moemaw Naedon & C.Scott', 30 | href: 'https://soulslimerecords.bandcamp.com/album/yinztroducing', 31 | }, 32 | { 33 | album: 'Drink The Blue Sky', 34 | artist: 'Nice Rec', 35 | href: 'https://nicerec.bandcamp.com/album/drink-the-blue-sky', 36 | }, 37 | { 38 | album: 'Slowly', 39 | artist: 'Tory Silver', 40 | href: 'https://torysilvermusic.bandcamp.com/album/slowly', 41 | }, 42 | ] 43 | 44 | export { bandcamps } 45 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/e2e/index.e2e.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test' 2 | 3 | const basePath = '' 4 | 5 | test('has title', async ({ page }) => { 6 | await page.goto(basePath) 7 | 8 | await expect(page).toHaveTitle( 9 | 'Jerome Fitzgerald (he/him) | Actor. Comedian. Writer.', 10 | ) 11 | }) 12 | 13 | test('has heading', async ({ page }) => { 14 | await page.goto(basePath) 15 | 16 | /** 17 | * @note(playwright) 18 | * 19 | * - https://playwright.dev/docs/api/class-framelocator#frame-locator-first 20 | * - https://www.programsbuzz.com/article/playwright-select-first-or-last-element#first-method 21 | * 22 | */ 23 | const heading = page.getByRole('heading', { level: 1 }).first() 24 | await expect(heading).toContainText('Jerome Fitzgerald (he/him)') 25 | }) 26 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 2 | // @ts-ignore 3 | import ms from 'ms' 4 | 5 | /** 6 | * @redis is in seconds not ms 7 | */ 8 | const getTimeInSeconds = (time: number) => (!time ? 0 : time / 1000) 9 | 10 | /** 11 | * @note in seconds 12 | * ...probably could be hard-coded 13 | */ 14 | const TIME = { 15 | DAY: getTimeInSeconds(ms('1d')), 16 | HOUR: getTimeInSeconds(ms('1h')), 17 | MINUTE: getTimeInSeconds(ms('1m')), 18 | MONTH: getTimeInSeconds(ms('30d')), 19 | YEAR: getTimeInSeconds(ms('1y')), 20 | } 21 | 22 | export { TIME } 23 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/index.ts: -------------------------------------------------------------------------------- 1 | // import { neon } from '@neondatabase/serverless' 2 | import { config as dotenvConfig } from 'dotenv' 3 | // import { drizzle as _drizzle } from 'drizzle-orm/neon-http' 4 | import { drizzle as _drizzle } from 'drizzle-orm/postgres-js' 5 | import postgres from 'postgres' 6 | 7 | import * as schema from '@/lib/drizzle/schemas/index' 8 | 9 | dotenvConfig() 10 | 11 | if (!process.env.POSTGRES_URL) { 12 | throw new Error('POSTGRES_URL environment variable is not set') 13 | } 14 | 15 | /** 16 | * postgres 17 | */ 18 | const client = postgres(process.env.POSTGRES_URL!) 19 | const drizzle = _drizzle(client, { casing: 'snake_case', schema }) 20 | /** 21 | * neon 22 | */ 23 | // const client = neon(process.env.POSTGRES_URL!) 24 | // const drizzle = _drizzle(client, { casing: 'snake_case', schema }) 25 | 26 | export { client, drizzle } 27 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/init/migrate.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | import { config as dotenvConfig } from 'dotenv' 4 | import { migrate } from 'drizzle-orm/postgres-js/migrator' 5 | 6 | import { client, drizzle } from '../index' 7 | 8 | dotenvConfig() 9 | 10 | async function main() { 11 | await migrate(drizzle, { 12 | migrationsFolder: path.join(process.cwd(), '/src/lib/drizzle/init/migrations'), 13 | }) 14 | console.log(`🏁 Migrations complete`) 15 | await client.end() 16 | } 17 | 18 | void main() 19 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/init/migrations/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "postgresql", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "7", 8 | "when": 1744668252137, 9 | "tag": "0000_grey_hiroim", 10 | "breakpoints": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/init/seed.ts: -------------------------------------------------------------------------------- 1 | function main() { 2 | console.log('⏭️ Nothing to seed') 3 | process.exit() 4 | } 5 | 6 | void main() 7 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-blocks/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | import { createSelectSchema } from 'drizzle-zod' 3 | 4 | import { init } from '../helpers' 5 | 6 | const cacheBlocks = pgTable('cache_blocks', { 7 | ...init, 8 | }) 9 | 10 | export const selectSchemaBlocks = createSelectSchema(cacheBlocks) 11 | export { cacheBlocks } 12 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-blocks/types.ts: -------------------------------------------------------------------------------- 1 | // import type { ListBlockChildrenResponse } from '@notionhq/client/build/src/api-endpoints' 2 | 3 | import type { Cache } from '../helpers.types' 4 | 5 | export type CacheBlock = { 6 | results: any 7 | } 8 | 9 | export type Block = Cache & CacheBlock 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-blogs/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | import { createSelectSchema } from 'drizzle-zod' 3 | 4 | import { init } from '../helpers' 5 | 6 | const cacheBlogs = pgTable('cache_blogs', { 7 | ...init, 8 | }) 9 | 10 | export const selectSchemaBlogs = createSelectSchema(cacheBlogs) 11 | export { cacheBlogs } 12 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-books/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | 3 | import { init } from '../helpers' 4 | 5 | const cacheBooks = pgTable('cache_books', { 6 | ...init, 7 | }) 8 | 9 | export { cacheBooks } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-episodes/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | 3 | import { init } from '../helpers' 4 | 5 | const cacheEpisodes = pgTable('cache_episodes', { 6 | ...init, 7 | }) 8 | 9 | export { cacheEpisodes } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-events/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | 3 | import { init } from '../helpers' 4 | 5 | const cacheEvents = pgTable('cache_events', { 6 | ...init, 7 | }) 8 | 9 | export { cacheEvents } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-images/queries.ts: -------------------------------------------------------------------------------- 1 | import { envServer } from '@jeromefitz/next-config/env.server.mjs' 2 | 3 | import { eq, sql } from 'drizzle-orm' 4 | 5 | import { drizzle } from '@/lib/drizzle/index' 6 | 7 | import type { CacheImage } from './types' 8 | 9 | import { cacheImages } from './schemas' 10 | 11 | export const segment = 'images' 12 | 13 | export async function getImageKeyValue({ 14 | key, 15 | }: { 16 | key: string 17 | }): Promise { 18 | return await drizzle.execute( 19 | sql.raw(` 20 | SELECT 21 | id, 22 | key, 23 | value, 24 | slug, 25 | src, 26 | width AS "width", 27 | height AS "height", 28 | blur_data_url AS "blurDataUrl", 29 | inserted_at AS "insertedAt", 30 | updated_at AS "updatedAt" 31 | FROM 32 | cache_images 33 | WHERE 34 | site_id = ${envServer.POSTGRES_SITE_ID} 35 | AND 36 | key = '${key}'`), 37 | ) 38 | } 39 | 40 | export async function getImageKeyValue2({ 41 | key, 42 | }: { 43 | key: string 44 | }): Promise { 45 | return await drizzle.select().from(cacheImages).where(eq(cacheImages.key, key)) 46 | } 47 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-images/schemas.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable perfectionist/sort-objects */ 2 | import { pgTable, text } from 'drizzle-orm/pg-core' 3 | 4 | import { init } from '../helpers' 5 | 6 | const cacheImages = pgTable('cache_images', { 7 | ...init, 8 | blurDataUrl: text('blur_data_url').notNull(), 9 | slug: text().notNull(), 10 | src: text().notNull(), 11 | width: text().notNull(), 12 | height: text().notNull(), 13 | }) 14 | 15 | export { cacheImages } 16 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-images/types.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable perfectionist/sort-object-types */ 2 | import type { Cache } from '../helpers.types' 3 | 4 | type CacheImageValue = { 5 | key: string 6 | blurDataUrl: string 7 | src: string 8 | slug: string 9 | width: string 10 | height: string 11 | } 12 | 13 | export type CacheInit2 = { 14 | id: number 15 | key: string 16 | value: CacheImageValue[] 17 | } 18 | 19 | export type CacheImageCustom = { 20 | blurDataUrl: string 21 | slug: string 22 | src: string 23 | width: string 24 | height: string 25 | } 26 | 27 | export type CacheImage = Cache & CacheImageCustom 28 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-pages/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | 3 | import { init } from '../helpers' 4 | 5 | const cachePages = pgTable('cache_pages', { 6 | ...init, 7 | }) 8 | 9 | export { cachePages } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-podcasts/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | 3 | import { init } from '../helpers' 4 | 5 | const cachePodcasts = pgTable('cache_podcasts', { 6 | ...init, 7 | }) 8 | 9 | export { cachePodcasts } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-shows/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | 3 | import { init } from '../helpers' 4 | 5 | const cacheShows = pgTable('cache_shows', { 6 | ...init, 7 | }) 8 | 9 | export { cacheShows } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-sites/schemas.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable perfectionist/sort-objects */ 2 | import { integer, jsonb, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core' 3 | 4 | const cacheSites = pgTable('cache_sites', { 5 | id: integer().primaryKey().generatedAlwaysAsIdentity(), 6 | uuid: uuid().defaultRandom(), 7 | key: text().notNull(), 8 | value: jsonb().notNull(), 9 | insertedAt: timestamp().notNull().defaultNow(), 10 | updatedAt: timestamp().notNull().defaultNow(), 11 | }) 12 | 13 | export { cacheSites } 14 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/cache-venues/schemas.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from 'drizzle-orm/pg-core' 2 | 3 | import { init } from '../helpers' 4 | 5 | const cacheVenues = pgTable('cache_venues', { 6 | ...init, 7 | }) 8 | 9 | export { cacheVenues } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/helpers.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable perfectionist/sort-objects */ 2 | import { integer, jsonb, text, timestamp, uuid } from 'drizzle-orm/pg-core' 3 | 4 | import { cacheSites } from './cache-sites/schemas' 5 | 6 | const initKeyValueTable = { 7 | id: integer().primaryKey().generatedAlwaysAsIdentity().notNull(), 8 | uuid: uuid().defaultRandom().notNull(), 9 | key: text().notNull(), 10 | value: jsonb().notNull().$type(), 11 | } 12 | 13 | const timestamps = { 14 | insertedAt: timestamp('inserted_at').notNull().defaultNow(), 15 | updatedAt: timestamp('updated_at').notNull().defaultNow(), 16 | } 17 | 18 | const relations = { 19 | siteId: integer('site_id') 20 | .references(() => cacheSites.id) 21 | .notNull(), 22 | } 23 | 24 | export const init = { 25 | ...initKeyValueTable, 26 | ...timestamps, 27 | ...relations, 28 | } 29 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/helpers.types.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable perfectionist/sort-object-types */ 2 | 3 | import type { ListBlockChildrenResponse } from '@notionhq/client/build/src/api-endpoints' 4 | 5 | export type CacheInit = { 6 | id: number 7 | uuid: string 8 | siteId: number 9 | key: string 10 | value: ListBlockChildrenResponse[] 11 | // value: any 12 | } 13 | 14 | export type CacheTimestamps = { 15 | insertedAt: Date 16 | updatedAt: Date 17 | } 18 | 19 | export type Cache = CacheInit & CacheTimestamps 20 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/index.ts: -------------------------------------------------------------------------------- 1 | export { cacheBlocks } from './cache-blocks/schemas' 2 | export { cacheBlogs } from './cache-blogs/schemas' 3 | export { cacheBooks } from './cache-books/schemas' 4 | export { cacheEpisodes } from './cache-episodes/schemas' 5 | export { cacheEvents } from './cache-events/schemas' 6 | export { cacheImages } from './cache-images/schemas' 7 | export { cachePages } from './cache-pages/schemas' 8 | export { cachePodcasts } from './cache-podcasts/schemas' 9 | export { cacheShows } from './cache-shows/schemas' 10 | export { cacheSites } from './cache-sites/schemas' 11 | export { cacheVenues } from './cache-venues/schemas' 12 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/queries.ts: -------------------------------------------------------------------------------- 1 | export { getBlogsWithLimit } from '@/lib/drizzle/schemas/cache-blogs/queries' 2 | export { getBooksWithLimit } from '@/lib/drizzle/schemas/cache-books/queries' 3 | export { getEventsWithLimit } from '@/lib/drizzle/schemas/cache-events/queries' 4 | export { getPagesWithLimit } from '@/lib/drizzle/schemas/cache-pages/queries' 5 | export { getPodcastsWithLimit } from '@/lib/drizzle/schemas/cache-podcasts/queries' 6 | export { getShowsWithLimit } from '@/lib/drizzle/schemas/cache-shows/queries' 7 | export { getVenuesWithLimit } from '@/lib/drizzle/schemas/cache-venues/queries' 8 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/schemas/types.ts: -------------------------------------------------------------------------------- 1 | export type { NotionSeoImage } from '@/lib/drizzle/schemas/_notion/types' 2 | export type { Blog } from '@/lib/drizzle/schemas/cache-blogs/types' 3 | export type { Book } from '@/lib/drizzle/schemas/cache-books/types' 4 | export type { Episode } from '@/lib/drizzle/schemas/cache-episodes/types' 5 | export type { Event } from '@/lib/drizzle/schemas/cache-events/types' 6 | export type { Podcast } from '@/lib/drizzle/schemas/cache-podcasts/types' 7 | export type { Show } from '@/lib/drizzle/schemas/cache-shows/types' 8 | export type { Venue } from '@/lib/drizzle/schemas/cache-venues/types' 9 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/types.ts: -------------------------------------------------------------------------------- 1 | export interface SegmentsArray { 2 | key: string | string[] 3 | } 4 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/utils/getImageKeySlug.ts: -------------------------------------------------------------------------------- 1 | import { slug as _slug } from 'github-slugger' 2 | import { isHttpsUri } from 'valid-url' 3 | 4 | export function getImageKeySlug(imageUrl: string) { 5 | let key = '', 6 | slug = '' 7 | if (isHttpsUri(imageUrl)) { 8 | slug = _slug(imageUrl.includes('?') ? imageUrl.split('?')[0] : imageUrl) 9 | key = `/image/${slug}`.toLowerCase() 10 | } 11 | return { key, slug } 12 | } 13 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/utils/getKeyValue.ts: -------------------------------------------------------------------------------- 1 | import { sql } from 'drizzle-orm' 2 | 3 | import type { Segment } from '@/utils/getBySegment' 4 | 5 | import { drizzle } from '@/lib/drizzle/index' 6 | import { getBySegment } from '@/utils/getBySegment' 7 | 8 | async function getKeyValue({ key, segment }: { key: string; segment: Segment }) { 9 | return await drizzle.execute( 10 | sql.raw( 11 | `SELECT id, key, value, inserted_at, updated_at FROM ${getBySegment[segment].drizzleDatabaseString} WHERE key = '${key}'`, 12 | ), 13 | ) 14 | } 15 | 16 | export { getKeyValue } 17 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/utils/getKeyValues.ts: -------------------------------------------------------------------------------- 1 | import { sql } from 'drizzle-orm' 2 | 3 | import type { Segment } from '@/utils/getBySegment' 4 | 5 | import { drizzle } from '@/lib/drizzle/index' 6 | import { getBySegment } from '@/utils/getBySegment' 7 | 8 | async function getKeyValues({ segment }: { segment: Segment }) { 9 | return await drizzle.execute( 10 | sql.raw( 11 | `SELECT id, key, value, inserted_at, updated_at FROM ${getBySegment[segment].drizzleDatabaseString}`, 12 | ), 13 | ) 14 | } 15 | 16 | export { getKeyValues } 17 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/drizzle/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { addItemToCache, overrideItemToCache } from './addItemToCache' 2 | export { getKeyValue } from './getKeyValue' 3 | export { getKeyValues } from './getKeyValues' 4 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/fetcher.ts: -------------------------------------------------------------------------------- 1 | const fetcher = async function (requestInfo: RequestInfo): Promise { 2 | /** 3 | * @todo(error-handling) 4 | */ 5 | const response = await fetch(requestInfo) 6 | return response.json() 7 | } 8 | 9 | export { fetcher } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/Notion.Component.tsx: -------------------------------------------------------------------------------- 1 | import { blocks } from '@/lib/notion/config' 2 | import { NotionBlocks } from '@/lib/notion/Notion.Blocks' 3 | 4 | function Notion({ data }: { data: any }) { 5 | return 6 | } 7 | 8 | export { Notion } 9 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/blocks/Column.tsx: -------------------------------------------------------------------------------- 1 | import type { 2 | BulletedListItemBlockObjectResponse, 3 | NumberedListItemBlockObjectResponse, 4 | } from '@notionhq/client/build/src/api-endpoints.js' 5 | 6 | import { forwardRef } from 'react' 7 | 8 | import { NotionBlocks as Blocks } from '../Notion.Blocks' 9 | import { getBlockKey } from '../Notion.utils' 10 | 11 | const Column = forwardRef(function Column(props: any, ref: any) { 12 | const { 13 | block, 14 | order, 15 | }: { 16 | block: 17 | | any 18 | | BulletedListItemBlockObjectResponse 19 | | NumberedListItemBlockObjectResponse 20 | order: number 21 | } = props 22 | const key = getBlockKey(block.id, block.type, order) 23 | // const items = block[block.type][block.type] 24 | const items = block[block.type]?.results 25 | 26 | const Component = props?.as ?? 'div' 27 | const componentProps = { 28 | className: props?.className ?? undefined, 29 | } 30 | 31 | return ( 32 | 33 | {items.map((item: any, order: number) => { 34 | const blocksKey = `${key}--${order}` 35 | return 36 | })} 37 | 38 | ) 39 | }) 40 | 41 | export { Column } 42 | export default Column 43 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/blocks/Divider.tsx: -------------------------------------------------------------------------------- 1 | import type { DividerBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints.js' 2 | 3 | import { forwardRef } from 'react' 4 | 5 | import { getBlockKey } from '../Notion.utils' 6 | 7 | const Divider = forwardRef(function Divider(props: any, ref: any) { 8 | const { block, order }: { block: DividerBlockObjectResponse; order: number } = 9 | props 10 | const key = getBlockKey(block.id, block.type, order) 11 | 12 | const Component = props?.as ?? 'p' 13 | const componentProps = { 14 | className: props?.className ?? undefined, 15 | } 16 | 17 | return 18 | }) 19 | 20 | export { Divider } 21 | export default Divider 22 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/blocks/Emoji.tsx: -------------------------------------------------------------------------------- 1 | import { EmojiWrapper } from './Emoji.server' 2 | // import { lazy, Suspense } from 'react' 3 | 4 | // // @note(next) outside of page.tsx, need to ignore 5 | // // eslint-disable-next-line @typescript-eslint/ban-ts-comment 6 | // // @ts-ignore 7 | // const EmojiWrapper = lazy(() => import('./Emoji.client')) 8 | 9 | // function NotionEmoji({ id, text }) { 10 | // return ( 11 | // <> 12 | // {text}>}> 13 | // 14 | // 15 | // > 16 | // ) 17 | // } 18 | 19 | function NotionEmoji({ id, text }: { id: string; text: string }) { 20 | return 21 | } 22 | 23 | export { NotionEmoji } 24 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/blocks/ListBulleted.tsx: -------------------------------------------------------------------------------- 1 | import type { 2 | BulletedListItemBlockObjectResponse, 3 | NumberedListItemBlockObjectResponse, 4 | } from '@notionhq/client/build/src/api-endpoints.js' 5 | 6 | import { forwardRef } from 'react' 7 | 8 | import { NotionBlocks as Blocks } from '../Notion.Blocks' 9 | import { getBlockKey } from '../Notion.utils' 10 | 11 | const ListBulleted = forwardRef(function ListBulleted(props: any, ref: any) { 12 | const { 13 | block, 14 | order, 15 | }: { 16 | block: 17 | | any 18 | | BulletedListItemBlockObjectResponse 19 | | NumberedListItemBlockObjectResponse 20 | order: number 21 | } = props 22 | const key = getBlockKey(block.id, block.type, order) 23 | const items = block[block.type][block.type] 24 | 25 | const Component = props?.as ?? 'ul' 26 | const componentProps = { 27 | className: props?.className ?? undefined, 28 | } 29 | 30 | return ( 31 | 32 | {items.map((item: any, order: number) => { 33 | const blocksKey = `${key}--${order}` 34 | return 35 | })} 36 | 37 | ) 38 | }) 39 | 40 | export { ListBulleted } 41 | export default ListBulleted 42 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/getAwsImage.ts: -------------------------------------------------------------------------------- 1 | import { TZDate } from '@date-fns/tz' 2 | import { format, isAfter, parseISO } from 'date-fns' 3 | 4 | const TZ_UTC = 'UTC' 5 | const URL_AWS = 'amazonaws.com' 6 | 7 | function isAwsImage(imageUrl: string) { 8 | return imageUrl?.includes(URL_AWS) || false 9 | } 10 | 11 | /** 12 | * @note(notion) 13 | * - Check if AWS Image: Y: Continue; No: Skip 14 | * - Check if `expiry_time` set: Y: Continue; No: Force 15 | * - Check if has valid Expiration Time for AWS Notion 16 | */ 17 | function isImageExpired(image: any) { 18 | if (!isAwsImage(image?.src)) { 19 | return false 20 | } 21 | let expiry_time = image?.expiry_time 22 | if (expiry_time === null || expiry_time === undefined) { 23 | return true 24 | } 25 | 26 | const timestamp = new Date() 27 | let utc: any = new TZDate(timestamp, TZ_UTC) 28 | const formatConfig = `yyyy-MM-dd'T'HH:mm:ss.ms'Z'` 29 | utc = format(utc, formatConfig) 30 | expiry_time = parseISO(image?.expiry_time) 31 | const isExpired = isAfter(parseISO(utc), expiry_time) 32 | 33 | return isExpired 34 | } 35 | 36 | export { isAwsImage, isImageExpired } 37 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/getImageAlt.ts: -------------------------------------------------------------------------------- 1 | import _filter from 'lodash/filter.js' 2 | import _startsWith from 'lodash/startsWith.js' 3 | 4 | const FIND_ALT = 'ALT: ' 5 | 6 | function getImageAlt(comments: any) { 7 | const comment = comments[0] 8 | const c = _filter(comments, (comment: any) => 9 | _startsWith(comment?.rich_text[0]?.plain_text, FIND_ALT), 10 | ) 11 | return !!c && c.length > 0 12 | ? c[0]?.rich_text[0]?.plain_text.slice(FIND_ALT.length) 13 | : comment 14 | ? comment?.rich_text[0]?.plain_text 15 | : '' 16 | } 17 | 18 | export { getImageAlt } 19 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/index.ts: -------------------------------------------------------------------------------- 1 | export { notion } from './notion' 2 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/notion/notion.ts: -------------------------------------------------------------------------------- 1 | import { envServer as env } from '@jeromefitz/next-config/env.server.mjs' 2 | 3 | import { Client } from '@notionhq/client' 4 | 5 | const notion = new Client({ 6 | auth: env.NOTION_API_KEY, 7 | }) 8 | 9 | export { notion } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/plaiceholder/getImage.ts: -------------------------------------------------------------------------------- 1 | import { getPlaiceholder } from 'plaiceholder' 2 | 3 | const getImage = async (src: string) => { 4 | // console.dir(`src: ${src}`) 5 | const buffer = await fetch(src).then(async (res) => 6 | Buffer.from(await res.arrayBuffer()), 7 | ) 8 | 9 | const { 10 | metadata: { height, width }, 11 | ...plaiceholder 12 | } = await getPlaiceholder(buffer, { size: 10 }) 13 | 14 | return { 15 | ...plaiceholder, 16 | img: { height, src, width }, 17 | } 18 | } 19 | 20 | export { getImage } 21 | 22 | /** 23 | * @note(plaiceholder) usage 24 | * ref: https://plaiceholder.co/docs/upgrading-to-3 25 | * 26 | */ 27 | // const { base64, img } = await getImage( 28 | // 'https://images.unsplash.com/photo-1621961458348-f013d219b50c?auto=format&fit=crop&w=2850&q=80' 29 | // ) 30 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/plaiceholder/index.ts: -------------------------------------------------------------------------------- 1 | export { getImage } from './getImage' 2 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/upstash/getCache.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import type { RC } from './index' 4 | 5 | import { getKey, redis } from './index' 6 | 7 | async function getCache({ slug }: { slug: string }) { 8 | const key = getKey(slug) 9 | const cache = await redis.get(key) 10 | return cache 11 | } 12 | 13 | export { getCache } 14 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/upstash/getKey.ts: -------------------------------------------------------------------------------- 1 | import { envClient as env } from '@jeromefitz/next-config/env.client.mjs' 2 | 3 | const KEY__PREFIX = env.NEXT_PUBLIC__SITE 4 | 5 | function getKey(slug: string) { 6 | const key = slug.includes(KEY__PREFIX) ? slug : `${KEY__PREFIX}${slug}` 7 | // console.dir(`> slug: ${slug}`) 8 | // console.dir(`> key: ${key}`) 9 | return key 10 | } 11 | 12 | export { getKey } 13 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/upstash/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ListBlockChildrenResponse, 3 | PageObjectResponse, 4 | } from '@notionhq/client/build/src/api-endpoints.js' 5 | 6 | interface RC { 7 | blocks: ListBlockChildrenResponse 8 | page: PageObjectResponse 9 | } 10 | 11 | export { getCache } from './getCache' 12 | export { getKey } from './getKey' 13 | export { redis } from './redis' 14 | export { setCache } from './setCache' 15 | 16 | export type { RC } 17 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/upstash/redis.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import https from 'node:https' 4 | 5 | import { envServer } from '@jeromefitz/next-config/env.server.mjs' 6 | 7 | import { Redis } from '@upstash/redis' 8 | 9 | const redis = new Redis({ 10 | agent: new https.Agent({ keepAlive: true }), 11 | retry: { 12 | backoff: (retryCount) => Math.exp(retryCount) * 50, 13 | retries: 5, 14 | }, 15 | token: envServer.UPSTASH_REDIS_REST_TOKEN, 16 | url: envServer.UPSTASH_REDIS_REST_URL, 17 | }) 18 | 19 | export { redis } 20 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/lib/upstash/setCache.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import stringify from 'fast-json-stable-stringify' 4 | 5 | import { TIME } from '@/lib/constants' 6 | 7 | import type { RC } from './index' 8 | 9 | import { getKey, redis } from './index' 10 | 11 | function setCache({ data, slug }: { data: any | RC; slug: string }) { 12 | const key = getKey(slug) 13 | void redis.set(key, stringify(data), { 14 | ex: TIME.MONTH, 15 | }) 16 | return null 17 | } 18 | 19 | export { setCache } 20 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/cx.ts: -------------------------------------------------------------------------------- 1 | import { ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | function cx(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | // return clsx(inputs) 7 | } 8 | 9 | export { cx, twMerge } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/getKey.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @todo(types) string | string[] 3 | * 4 | * getKey => key 5 | * getKeyForGenerateStaticParams => return 6 | * 7 | */ 8 | export function getKey(segment: string, key: any) { 9 | if (segment === 'pages') { 10 | return `/${key}` 11 | } 12 | return `/${segment}/${Object.prototype.toString.call(key) === '[object Array]' ? key.join('/') : key}` 13 | } 14 | 15 | /** 16 | * @note (ಠ_ಠ) 17 | * 18 | * [[...key]] – Y to passing "segment" 19 | * [...key] || [key] - N to passing "segment" 20 | * 21 | */ 22 | // @todo(types) string[] | string 23 | export function getKeyForGenerateStaticParams(segment: string, key: string): any { 24 | const _key = key.replace(`/${segment}/`, '') 25 | return _key.includes('/') ? _key.split('/') : _key 26 | } 27 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/getKeySpotify.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Spotify API 3 | * 4 | * @todo(spotify) 5 | * 6 | * recently-played cursor is: after|before 7 | * - its history is 50 records 8 | * - no need to cycle through cursors indefinetly 9 | * - just request limit of 50 10 | * 11 | * top-artists|tracks cursor is: offset 12 | * - works well with pageIndex 13 | */ 14 | const getKeySpotify = (pageIndex: number, { limit, time_range, type, url }: any) => { 15 | const offset = pageIndex === 0 ? 0 : 10 * pageIndex 16 | const key = [ 17 | `${url}/${type}?limit=${limit}&offset=${offset}&time_range=${time_range}`, 18 | ] 19 | return key 20 | } 21 | 22 | const INIT = { 23 | limit: 10, 24 | offset: 0, 25 | time_range: 'medium_term', 26 | type: 'top-artists', 27 | url: '/api/v1/music', 28 | } 29 | 30 | export { getKeySpotify, INIT } 31 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/getPlaiceholder.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | import type { GetPlaiceholderOptions } from 'plaiceholder' 3 | 4 | import { getPlaiceholder as _getPlaiceholder } from 'plaiceholder' 5 | 6 | export async function getPlaiceholder(filepath: string) { 7 | try { 8 | const buffer = await fetch(filepath).then(async (res) => 9 | Buffer.from(await res.arrayBuffer()), 10 | ) 11 | 12 | const options: GetPlaiceholderOptions = { 13 | brightness: 1, 14 | lightness: 5, 15 | saturation: 1, 16 | size: 18, 17 | } 18 | 19 | const { 20 | base64, 21 | metadata: { height, width }, 22 | } = await _getPlaiceholder(buffer, options) 23 | 24 | return { 25 | blurDataURL: base64, 26 | height, 27 | src: filepath, 28 | width, 29 | } 30 | } catch { 31 | return { 32 | blurDataURL: 33 | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAIAAABPmPnhAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA1UlEQVR4nFWOwUrDQABEZ3az2e3awC6VpI3NHxS00KOXtpdKSiSo6bkKBW+evfgNlebuRezJP9EP6K94qJiK4LsNMzAP/I8gAcRJ//GpvsgXYJMPkPC+U17drZ+3m83reJz/FT+jLM2qavn5sasuyyTOJtPqt9bGAGjZdjG/qdd12nHvL2/Xi3sczoy1JI+i6HQwnA3Pi3m+/9qvbh+gtQYgVUDSO3fcPfFJ2ku73ruzwQham1ApIYWQsmWNixPrHNjYkBAUKgxFIAmqMLBRW0jJRpbkNwFLHj/O9IP8AAAAAElFTkSuQmCC', 34 | src: filepath, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/getTitleData.ts: -------------------------------------------------------------------------------- 1 | function getTitleData({ data, type }: { data: any; type: string }) { 2 | const typeData = data[type] 3 | const dataReturn = typeData[0]?.plain_text ?? '' 4 | return dataReturn 5 | } 6 | 7 | export { getTitleData } 8 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/isEmpty.ts: -------------------------------------------------------------------------------- 1 | export function isEmpty(data: any[]) { 2 | return !Array.isArray(data) || !data.length 3 | } 4 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/isObjectEmpty.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks whether an object is empty 3 | * @param {} metadata 4 | */ 5 | const isObjectEmpty = (metadata: any | any[]) => { 6 | return 0 === Object.entries(metadata).length && metadata.constructor === Object 7 | } 8 | 9 | export { isObjectEmpty } 10 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/next/getSegmentsForGenerateStaticParams.ts: -------------------------------------------------------------------------------- 1 | import { envClient } from '@jeromefitz/next-config/env.client.mjs' 2 | 3 | import type { SegmentsArray } from '@/lib/drizzle/types' 4 | import type { Segment } from '@/utils/getBySegment' 5 | 6 | import { getBySegment } from '@/utils/getBySegment' 7 | import { getKeyForGenerateStaticParams } from '@/utils/getKey' 8 | 9 | export async function getSegmentsForGenerateStaticParams(segment: Segment) { 10 | if (envClient.IS_DEV) { 11 | return [] 12 | } 13 | const segments: SegmentsArray[] = [] 14 | const items: any = await getBySegment[segment].getItems({ 15 | limit: getBySegment[segment].limit, 16 | }) 17 | 18 | items.map((item: any) => { 19 | segments.push({ key: getKeyForGenerateStaticParams(segment, item.key) }) 20 | }) 21 | 22 | console.info(`> generateStaticParams (${segment}: ${segments.length})`) 23 | 24 | return segments 25 | } 26 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/src/utils/uuid.ts: -------------------------------------------------------------------------------- 1 | import { validate as uuidValidate } from 'uuid' 2 | 3 | const delimiter = '-' 4 | const indexes = [8, 13, 18, 23] 5 | 6 | function insertString(str: string, index: number, value: string) { 7 | return str.substr(0, index) + value + str.substr(index) 8 | } 9 | 10 | function setDelimeter(_uuid: string) { 11 | indexes.map((idx) => { 12 | _uuid = insertString(_uuid, idx, delimiter) 13 | }) 14 | return _uuid 15 | } 16 | 17 | function uuidConverter(_uuid: string) { 18 | if (uuidValidate(_uuid)) return _uuid 19 | if (_uuid.length === 32) return setDelimeter(_uuid) 20 | return null 21 | } 22 | 23 | export { uuidConverter, uuidValidate } 24 | -------------------------------------------------------------------------------- /sites/jeromefitzgerald.com/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "baseUrl": ".", 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@jeromefitz/next-config/env.client.mjs": [ 24 | "./src/config/next.config.env.client.mjs" 25 | ], 26 | "@jeromefitz/next-config/env.server.mjs": [ 27 | "./src/config/next.config.env.server.mjs" 28 | ], 29 | // "@jeromefitz/next-config/*": ["../../packages/next-config/src/*"], 30 | "radix-themes-tw": ["./src/config/radix-themes-tw.css"], 31 | "@/*": ["./src/*"] 32 | } 33 | }, 34 | "include": ["**/*.ts", "**/*.tsx", "next-env.d.ts", ".next/types/**/*.ts"], 35 | "exclude": ["node_modules", "zzz/**"] 36 | } 37 | -------------------------------------------------------------------------------- /sites/justinandjerome.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/justinandjerome.com/.gitkeep -------------------------------------------------------------------------------- /sites/nicegroupofpeople.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/nicegroupofpeople.com/.gitkeep -------------------------------------------------------------------------------- /sites/nicerec.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/nicerec.com/.gitkeep -------------------------------------------------------------------------------- /sites/sarahfitzgerald.me/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/sarahfitzgerald.me/.gitkeep -------------------------------------------------------------------------------- /sites/theeparodybros.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/theeparodybros.com/.gitkeep -------------------------------------------------------------------------------- /sites/wkspizza.com/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeromeFitz/websites/d4f1db1cfd7688eb468bcb8aeb707fd719844569/sites/wkspizza.com/.gitkeep -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from 'tsup' 2 | 3 | import { defineConfig } from 'tsup' 4 | 5 | const config: Options = { 6 | clean: true, 7 | dts: true, 8 | format: ['esm'], 9 | minify: true, 10 | onSuccess: 'pnpm copy', 11 | outDir: 'dist', 12 | silent: true, 13 | sourcemap: false, 14 | splitting: false, 15 | target: ['node22'], 16 | treeshake: false, 17 | } 18 | 19 | export { config } 20 | export default defineConfig({ ...config }) 21 | --------------------------------------------------------------------------------
{title}
12 | Episodes 13 |
Website is under construction.
30 | Everything is available on RSS Feeds though through Apple, Spotify, and 31 | other podcast providers. 32 |
34 | Jer & Ky (& Guest) 35 |
37 | Knockoffs 38 |