From 20d16e968cec6cb1f8a49daf4d8a40aae29786f4 Mon Sep 17 00:00:00 2001 From: Arjun Komath Date: Sat, 20 Sep 2025 11:30:44 +1000 Subject: [PATCH 1/2] Add page screenshot --- apps/web/package.json | 1 + apps/web/pages/pages/index.tsx | 138 +++++++++++++++++++-------------- apps/web/utils/capture.ts | 14 ++++ pnpm-lock.yaml | 35 +++++++++ 4 files changed, 130 insertions(+), 58 deletions(-) create mode 100644 apps/web/utils/capture.ts diff --git a/apps/web/package.json b/apps/web/package.json index f670558..6cbd6dc 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,6 +30,7 @@ "@types/validator": "^13.15.3", "@vercel/og": "^0.0.20", "canvas-confetti": "^1.9.3", + "capture-node": "^2.2.0", "chrono-node": "^2.7.6", "classnames": "^2.3.1", "easymde": "^2.18.0", diff --git a/apps/web/pages/pages/index.tsx b/apps/web/pages/pages/index.tsx index 0a3ee2c..6a48187 100644 --- a/apps/web/pages/pages/index.tsx +++ b/apps/web/pages/pages/index.tsx @@ -1,10 +1,10 @@ import { PageType, PageTypeToLabel } from "@changes-page/supabase/types/page"; import { PlusIcon, UserGroupIcon } from "@heroicons/react/solid"; import classNames from "classnames"; -import { InferGetServerSidePropsType } from "next"; +import type { InferGetServerSidePropsType } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; -import { useEffect, type JSX } from "react"; +import { type JSX, useEffect } from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { PrimaryRouterButton } from "../../components/core/buttons.component"; import { notifyError } from "../../components/core/toast.component"; @@ -13,6 +13,7 @@ import AuthLayout from "../../components/layout/auth-layout.component"; import Page from "../../components/layout/page.component"; import Changelog from "../../components/marketing/changelog"; import { ROUTES } from "../../data/routes.data"; +import { getPageScreenshotUrl } from "../../utils/capture"; import { getAppBaseURL } from "../../utils/helpers"; import { withSupabase } from "../../utils/supabase/withSupabase"; import { useUserData } from "../../utils/useUser"; @@ -33,9 +34,18 @@ export const getServerSideProps = withSupabase(async (_, { supabase }) => { ) .order("updated_at", { ascending: false }); + const screenshots = pages.map((page) => + getPageScreenshotUrl( + page.page_settings?.custom_domain + ? `https://${page.page_settings.custom_domain}` + : `https://${page.url_slug}.changes.page` + ) + ); + return { props: { pages: pages ?? [], + screenshots, error, }, }; @@ -43,6 +53,7 @@ export const getServerSideProps = withSupabase(async (_, { supabase }) => { export default function Pages({ pages, + screenshots, error, }: InferGetServerSidePropsType) { const { billingDetails, user } = useUserData(); @@ -80,7 +91,7 @@ export default function Pages({ message="Get started by creating your first page." buttonLink={ billingDetails?.has_active_subscription - ? `/pages/new` + ? "/pages/new" : `/api/billing/redirect-to-checkout?return_url=${getAppBaseURL()}/pages` } buttonLabel={ @@ -107,69 +118,79 @@ export default function Pages({ {pages.length ? (
    - {pages.map((page) => ( + {pages.map((page, idx) => (
  • -
    -
    - - {PageTypeToLabel[page.type]} - - {page.teams && page.user_id !== user?.id && ( -
    - - - Editor ({page.teams.name}) - -
    - )} +
    +
    + {`Screenshot
    -
    -

    - +
    + -

    - {page.description && ( -

    - {page.description} + {PageTypeToLabel[page.type]} + + {page.teams && page.user_id !== user?.id && ( +

    + + + Editor ({page.teams.name}) + +
    + )} +
    +
    +

    + +

    + {page.description && ( +

    + {page.description} +

    + )} +

    + {page.page_settings?.custom_domain + ? page.page_settings.custom_domain + : `${page.url_slug}.changes.page`}

    - )} -

    - {page.page_settings?.custom_domain - ? page.page_settings.custom_domain - : `${page.url_slug}.changes.page`} -

    +
    @@ -178,6 +199,7 @@ export default function Pages({ fill="currentColor" viewBox="0 0 20 20" > + Go to page = 8.10.0'} @@ -2488,6 +2497,9 @@ packages: crossws@0.3.5: resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + crypt@0.0.2: + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + css-b64-images@0.2.5: resolution: {integrity: sha512-TgQBEdP07adhrDfXvI5o6bHGukKBNMzp2Ngckc/6d09zpjD2gc1Hl3Ca1CKgb8FXjHi88+Phv2Uegs2kTL4zjg==} hasBin: true @@ -3335,6 +3347,9 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + is-buffer@2.0.5: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} engines: {node: '>=4'} @@ -3674,6 +3689,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + md5@2.3.0: + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + mdast-util-definitions@5.1.2: resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} @@ -7119,6 +7137,11 @@ snapshots: canvas-confetti@1.9.3: {} + capture-node@2.2.0: + dependencies: + md5: 2.3.0 + qs: 6.14.0 + ccount@2.0.1: {} chalk@2.4.2: @@ -7145,6 +7168,8 @@ snapshots: character-reference-invalid@2.0.1: {} + charenc@0.0.2: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -7258,6 +7283,8 @@ snapshots: dependencies: uncrypto: 0.1.3 + crypt@0.0.2: {} + css-b64-images@0.2.5: {} css-background-parser@0.1.0: {} @@ -8475,6 +8502,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-buffer@1.1.6: {} + is-buffer@2.0.5: {} is-bun-module@2.0.0: @@ -8766,6 +8795,12 @@ snapshots: math-intrinsics@1.1.0: {} + md5@2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + mdast-util-definitions@5.1.2: dependencies: '@types/mdast': 3.0.15 From 7a55b45f6ddc9e1303ea6986ff27241229f8465b Mon Sep 17 00:00:00 2001 From: Arjun Komath Date: Sat, 20 Sep 2025 11:42:01 +1000 Subject: [PATCH 2/2] Address CR --- apps/web/pages/pages/index.tsx | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/web/pages/pages/index.tsx b/apps/web/pages/pages/index.tsx index 6a48187..1ba6d18 100644 --- a/apps/web/pages/pages/index.tsx +++ b/apps/web/pages/pages/index.tsx @@ -35,11 +35,13 @@ export const getServerSideProps = withSupabase(async (_, { supabase }) => { .order("updated_at", { ascending: false }); const screenshots = pages.map((page) => - getPageScreenshotUrl( - page.page_settings?.custom_domain - ? `https://${page.page_settings.custom_domain}` - : `https://${page.url_slug}.changes.page` - ) + page + ? getPageScreenshotUrl( + page.page_settings?.custom_domain + ? `https://${page.page_settings.custom_domain}` + : `https://${page.url_slug}.changes.page` + ) + : null ); return { @@ -125,14 +127,16 @@ export default function Pages({ >
    -
    - {`Screenshot -
    + {screenshots[idx] ? ( +
    + {`Screenshot +
    + ) : null}