From decbc0fb28818bcb3d150993a9e0e082693eb00f Mon Sep 17 00:00:00 2001 From: cryptohardcore <133330042+cryptohardcore@users.noreply.github.com> Date: Mon, 1 Jun 2026 21:17:40 +0700 Subject: [PATCH] Add Konstantin attribution to README --- README.md | 4 + next.config.ts | 2 + package.json | 5 +- src/app/(frontend)/page.tsx | 156 ++++++++++++++------ src/app/(frontend)/styles.css | 268 ++++++++++++++++++---------------- src/payload.config.ts | 5 +- 6 files changed, 264 insertions(+), 176 deletions(-) diff --git a/README.md b/README.md index a58e16b..0daf744 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ pnpm run deploy This will spin up Wrangler in `production` mode, run any created migrations, build the app and then deploy the bundle up to Cloudflare. +If you move these steps into your CI pipeline, make sure the deploy step activates the new Worker version. For an immediate production release, use `pnpm run deploy` or `npx wrangler deploy`. Do not use `npx wrangler versions upload` by itself for production deploys: that command only uploads an inactive Worker version, so live traffic will keep serving the previous homepage until the uploaded version is deployed with `wrangler versions deploy` or through the Cloudflare dashboard. + That's it! You can if you wish move these steps into your CI pipeline as well. ## Enabling logs @@ -117,3 +119,5 @@ This also applies to your own code, in the case of importing a lot of libraries ## Questions If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions). + +by Konstantin diff --git a/next.config.ts b/next.config.ts index 8dd8bfe..d6b9fd3 100644 --- a/next.config.ts +++ b/next.config.ts @@ -13,6 +13,8 @@ const nextConfig = { }, ], }, + output: 'standalone', + // Packages with Cloudflare Workers (workerd) specific code // Read more: https://opennext.js.org/cloudflare/howtos/workerd serverExternalPackages: ['jose', 'pg-cloudflare'], diff --git a/package.json b/package.json index 1a1dc48..55bf209 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "MIT", "type": "module", "scripts": { - "build": "cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=8000\" next build", + "build": "cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=8000\" next build && npm run build:worker", "deploy": "pnpm run deploy:database && pnpm run deploy:app", "deploy:app": "opennextjs-cloudflare build --env=$CLOUDFLARE_ENV && opennextjs-cloudflare deploy --env=$CLOUDFLARE_ENV", "deploy:database": "cross-env NODE_ENV=production PAYLOAD_SECRET=ignore payload migrate && wrangler d1 execute D1 --command 'PRAGMA optimize' --env=$CLOUDFLARE_ENV --remote", @@ -22,7 +22,8 @@ "start": "cross-env NODE_OPTIONS=--no-deprecation next start", "test": "pnpm run test:int && pnpm run test:e2e", "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm\" playwright test --config=playwright.config.ts", - "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts" + "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts", + "build:worker": "opennextjs-cloudflare build --skipNextBuild" }, "dependencies": { "@opennextjs/cloudflare": "^1.11.0", diff --git a/src/app/(frontend)/page.tsx b/src/app/(frontend)/page.tsx index d0e8d64..88b5f78 100644 --- a/src/app/(frontend)/page.tsx +++ b/src/app/(frontend)/page.tsx @@ -1,59 +1,119 @@ -import { headers as getHeaders } from 'next/headers.js' -import Image from 'next/image' -import { getPayload } from 'payload' import React from 'react' -import { fileURLToPath } from 'url' -import config from '@/payload.config' import './styles.css' -export default async function HomePage() { - const headers = await getHeaders() - const payloadConfig = await config - const payload = await getPayload({ config: payloadConfig }) - const { user } = await payload.auth({ headers }) +export const dynamic = 'force-dynamic' +export const revalidate = 0 - const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}` +const events = [ + { date: 'May 30', title: 'Old Town Night Market', location: 'Phuket Town' }, + { date: 'Jun 2', title: 'Sunset Beach Yoga', location: 'Kata Beach' }, + { date: 'Jun 5', title: 'Andaman Seafood Festival', location: 'Rawai Pier' }, +] +const featuredArticles = [ + '7 hidden beaches locals still love in low season', + 'How to island-hop from Phuket in one weekend', + 'Best family-friendly cafés with ocean views', +] + +export default function HomePage() { return ( -
-
- - - Payload Logo - - {!user &&

Welcome to your new project.

} - {user &&

Welcome back, {user.email}

} -
- - Go to admin panel - - - Documentation - +
+
+
+

ToDoPhuket.com

+

Your island guide to Phuket

-
-
-

Update this page by editing

- - app/(frontend)/page.tsx - -
+ + + +
+
+

Discover what's happening today in Phuket

+

+ Daily local updates, upcoming events, travel ideas, and property opportunities—all in one + place. +

+
+ +
+
+

Latest News

+
    +
  • + Airport Express ferries expand service + Faster transfers between Patong, Kata, and airport terminals. +
  • +
  • + Monsoon safety updates released + Lifeguard zones and real-time beach flag reporting now live. +
  • +
  • + Weekend street food route announced + New evening route connects three popular night markets. +
  • +
+
+ +
+

Upcoming Events

+
    + {events.map((event) => ( +
  • + {event.date} +
    + {event.title} +

    {event.location}

    +
    +
  • + ))} +
+
+ +
+

Featured Articles

+
    + {featuredArticles.map((article) => ( +
  • {article}
  • + ))} +
+
+ +
+

Real Estate Search

+
+ + + + + + + + + + +
+
+
+
+ +
+

© 2026 ToDoPhuket.com · Phuket, Thailand

+

News · Events · Guides · Real Estate

+
) } diff --git a/src/app/(frontend)/styles.css b/src/app/(frontend)/styles.css index d1fb941..0abada3 100644 --- a/src/app/(frontend)/styles.css +++ b/src/app/(frontend)/styles.css @@ -1,164 +1,184 @@ :root { - --font-mono: 'Roboto Mono', monospace; + --bg: #f5f8fc; + --text: #17324d; + --card: #ffffff; + --accent: #0b6ecb; + --accent-soft: #d8ebff; } * { box-sizing: border-box; } -html { - font-size: 18px; - line-height: 32px; - - background: rgb(0, 0, 0); - -webkit-font-smoothing: antialiased; +body { + margin: 0; + font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif; + background: radial-gradient(circle at top right, #d8ebff, var(--bg) 45%); + color: var(--text); } -html, -body, -#app { - height: 100%; +h1, +h2, +h3, +p { + margin: 0; } -body { - font-family: system-ui; - font-size: 18px; - line-height: 32px; +.todophuket-page { + max-width: 1160px; + margin: 0 auto; + padding: 2rem; + display: grid; + gap: 1.5rem; +} - margin: 0; - color: rgb(1000, 1000, 1000); +.site-header, +.site-footer, +.card, +.hero { + background: var(--card); + border-radius: 1rem; + box-shadow: 0 8px 28px rgba(20, 40, 70, 0.08); +} - @media (max-width: 1024px) { - font-size: 15px; - line-height: 24px; - } +.site-header { + padding: 1.25rem 1.5rem; + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; } -img { - max-width: 100%; - height: auto; - display: block; +.eyebrow { + color: var(--accent); + font-size: 0.85rem; + font-weight: 700; + letter-spacing: 0.03em; + text-transform: uppercase; } -h1 { - margin: 40px 0; - font-size: 64px; - line-height: 70px; - font-weight: bold; +.site-header h1 { + font-size: 1.7rem; +} - @media (max-width: 1024px) { - margin: 24px 0; - font-size: 42px; - line-height: 42px; - } +nav { + display: flex; + gap: 0.8rem; + flex-wrap: wrap; +} - @media (max-width: 768px) { - font-size: 38px; - line-height: 38px; - } +nav a { + color: var(--text); + text-decoration: none; + background: var(--accent-soft); + padding: 0.45rem 0.7rem; + border-radius: 999px; + font-size: 0.9rem; + font-weight: 600; +} - @media (max-width: 400px) { - font-size: 32px; - line-height: 32px; - } +.hero { + padding: 1.5rem; } -p { - margin: 24px 0; +.hero h2 { + font-size: 1.5rem; + margin-bottom: 0.5rem; +} - @media (max-width: 1024px) { - margin: calc(var(--base) * 0.75) 0; - } +.hero p { + color: #375570; } -a { - color: currentColor; +.grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1rem; +} - &:focus { - opacity: 0.8; - outline: none; - } +.card { + padding: 1.2rem; +} - &:active { - opacity: 0.7; - outline: none; - } +.card h3 { + margin-bottom: 0.8rem; } -svg { - vertical-align: middle; +.news ul, +.articles ul, +.events ul { + margin: 0; + padding-left: 1rem; + display: grid; + gap: 0.65rem; } -.home { +.events li { display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - height: 100vh; - padding: 45px; - max-width: 1024px; - margin: 0 auto; - overflow: hidden; + gap: 0.8rem; + align-items: flex-start; + list-style: none; + padding: 0.35rem 0; +} - @media (max-width: 400px) { - padding: 24px; - } +.event-date { + background: var(--accent-soft); + color: var(--accent); + padding: 0.15rem 0.45rem; + border-radius: 0.4rem; + font-size: 0.8rem; + font-weight: 700; +} - .content { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - flex-grow: 1; +.events p { + color: #4a6680; + font-size: 0.9rem; +} - h1 { - text-align: center; - } - } +.real-estate form { + display: grid; + gap: 0.45rem; +} + +.real-estate label { + font-size: 0.88rem; + font-weight: 600; +} + +.real-estate input, +.real-estate select, +.real-estate button { + border: 1px solid #c7d8ea; + border-radius: 0.5rem; + font: inherit; + padding: 0.55rem 0.65rem; +} + +.real-estate button { + margin-top: 0.4rem; + border: 0; + background: var(--accent); + color: #fff; + font-weight: 700; + cursor: pointer; +} + +.site-footer { + padding: 1rem 1.5rem; + display: flex; + justify-content: space-between; + color: #40607c; + font-size: 0.92rem; +} - .links { - display: flex; - align-items: center; - gap: 12px; - - a { - text-decoration: none; - padding: 0.25rem 0.5rem; - border-radius: 4px; - } - - .admin { - color: rgb(0, 0, 0); - background: rgb(1000, 1000, 1000); - border: 1px solid rgb(0, 0, 0); - } - - .docs { - color: rgb(1000, 1000, 1000); - background: rgb(0, 0, 0); - border: 1px solid rgb(1000, 1000, 1000); - } +@media (max-width: 900px) { + .grid { + grid-template-columns: 1fr; } - .footer { - display: flex; - align-items: center; - gap: 8px; - - @media (max-width: 1024px) { - flex-direction: column; - gap: 6px; - } - - p { - margin: 0; - } - - .codeLink { - text-decoration: none; - padding: 0 0.5rem; - background: rgb(60, 60, 60); - border-radius: 4px; - } + .site-header, + .site-footer { + flex-direction: column; + align-items: flex-start; } } diff --git a/src/payload.config.ts b/src/payload.config.ts index da3d5fe..67c191a 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -17,6 +17,7 @@ const realpath = (value: string) => (fs.existsSync(value) ? fs.realpathSync(valu const isCLI = process.argv.some((value) => realpath(value).endsWith(path.join('payload', 'bin.js'))) const isProduction = process.env.NODE_ENV === 'production' +const isNextBuild = process.env.NEXT_PHASE === 'phase-production-build' const createLog = (level: string, fn: typeof console.log) => (objOrMsg: object | string, msg?: string) => { @@ -39,7 +40,7 @@ const cloudflareLogger = { } as any // Use PayloadLogger type when it's exported const cloudflare = - isCLI || !isProduction + isCLI || !isProduction || isNextBuild ? await getCloudflareContextFromWrangler() : await getCloudflareContext({ async: true }) @@ -72,7 +73,7 @@ function getCloudflareContextFromWrangler(): Promise { ({ getPlatformProxy }) => getPlatformProxy({ environment: process.env.CLOUDFLARE_ENV, - remoteBindings: isProduction, + remoteBindings: isProduction && !isNextBuild, } satisfies GetPlatformProxyOptions), ) }