diff --git a/apps/web/AGENTS.md b/apps/web/AGENTS.md index 08f447d41..e99b55890 100644 --- a/apps/web/AGENTS.md +++ b/apps/web/AGENTS.md @@ -1,5 +1,3 @@ -[Next.js Docs Index]|root: ./.next-docs|STOP. What you remember about Next.js is WRONG for this project. Always search docs and read before any task.|If docs missing, run this command first: npx @next/codemod agents-md --output CLAUDE.md|01-app:{04-glossary.mdx}|01-app/01-getting-started:{01-installation.mdx,02-project-structure.mdx,03-layouts-and-pages.mdx,04-linking-and-navigating.mdx,05-server-and-client-components.mdx,06-fetching-data.mdx,07-mutating-data.mdx,08-caching.mdx,09-revalidating.mdx,10-error-handling.mdx,11-css.mdx,12-images.mdx,13-fonts.mdx,14-metadata-and-og-images.mdx,15-route-handlers.mdx,16-proxy.mdx,17-deploying.mdx,18-upgrading.mdx}|01-app/02-guides:{ai-agents.mdx,analytics.mdx,authentication.mdx,backend-for-frontend.mdx,caching-without-cache-components.mdx,ci-build-caching.mdx,content-security-policy.mdx,css-in-js.mdx,custom-server.mdx,data-security.mdx,debugging.mdx,draft-mode.mdx,environment-variables.mdx,forms.mdx,incremental-static-regeneration.mdx,instant-navigation.mdx,instrumentation.mdx,internationalization.mdx,json-ld.mdx,lazy-loading.mdx,local-development.mdx,mcp.mdx,mdx.mdx,memory-usage.mdx,migrating-to-cache-components.mdx,multi-tenant.mdx,multi-zones.mdx,open-telemetry.mdx,package-bundling.mdx,prefetching.mdx,preserving-ui-state.mdx,production-checklist.mdx,progressive-web-apps.mdx,public-static-pages.mdx,redirecting.mdx,sass.mdx,scripts.mdx,self-hosting.mdx,single-page-applications.mdx,static-exports.mdx,streaming.mdx,tailwind-v3-css.mdx,third-party-libraries.mdx,videos.mdx}|01-app/02-guides/migrating:{app-router-migration.mdx,from-create-react-app.mdx,from-vite.mdx}|01-app/02-guides/testing:{cypress.mdx,jest.mdx,playwright.mdx,vitest.mdx}|01-app/02-guides/upgrading:{codemods.mdx,version-14.mdx,version-15.mdx,version-16.mdx}|01-app/03-api-reference:{07-edge.mdx,08-turbopack.mdx}|01-app/03-api-reference/01-directives:{use-cache-private.mdx,use-cache-remote.mdx,use-cache.mdx,use-client.mdx,use-server.mdx}|01-app/03-api-reference/02-components:{font.mdx,form.mdx,image.mdx,link.mdx,script.mdx}|01-app/03-api-reference/03-file-conventions/01-metadata:{app-icons.mdx,manifest.mdx,opengraph-image.mdx,robots.mdx,sitemap.mdx}|01-app/03-api-reference/03-file-conventions/02-route-segment-config:{dynamicParams.mdx,instant.mdx,maxDuration.mdx,preferredRegion.mdx,runtime.mdx}|01-app/03-api-reference/03-file-conventions:{default.mdx,dynamic-routes.mdx,error.mdx,forbidden.mdx,instrumentation-client.mdx,instrumentation.mdx,intercepting-routes.mdx,layout.mdx,loading.mdx,mdx-components.mdx,not-found.mdx,page.mdx,parallel-routes.mdx,proxy.mdx,public-folder.mdx,route-groups.mdx,route.mdx,src-folder.mdx,template.mdx,unauthorized.mdx}|01-app/03-api-reference/04-functions:{after.mdx,cacheLife.mdx,cacheTag.mdx,catchError.mdx,connection.mdx,cookies.mdx,draft-mode.mdx,fetch.mdx,forbidden.mdx,generate-image-metadata.mdx,generate-metadata.mdx,generate-sitemaps.mdx,generate-static-params.mdx,generate-viewport.mdx,headers.mdx,image-response.mdx,next-request.mdx,next-response.mdx,not-found.mdx,permanentRedirect.mdx,redirect.mdx,refresh.mdx,revalidatePath.mdx,revalidateTag.mdx,unauthorized.mdx,unstable_cache.mdx,unstable_noStore.mdx,unstable_rethrow.mdx,updateTag.mdx,use-link-status.mdx,use-params.mdx,use-pathname.mdx,use-report-web-vitals.mdx,use-router.mdx,use-search-params.mdx,use-selected-layout-segment.mdx,use-selected-layout-segments.mdx,userAgent.mdx}|01-app/03-api-reference/05-config/01-next-config-js:{adapterPath.mdx,allowedDevOrigins.mdx,appDir.mdx,assetPrefix.mdx,authInterrupts.mdx,basePath.mdx,cacheComponents.mdx,cacheHandlers.mdx,cacheLife.mdx,compress.mdx,crossOrigin.mdx,cssChunking.mdx,deploymentId.mdx,devIndicators.mdx,distDir.mdx,env.mdx,expireTime.mdx,exportPathMap.mdx,generateBuildId.mdx,generateEtags.mdx,headers.mdx,htmlLimitedBots.mdx,httpAgentOptions.mdx,images.mdx,incrementalCacheHandlerPath.mdx,inlineCss.mdx,logging.mdx,mdxRs.mdx,onDemandEntries.mdx,optimizePackageImports.mdx,output.mdx,pageExtensions.mdx,poweredByHeader.mdx,productionBrowserSourceMaps.mdx,proxyClientMaxBodySize.mdx,reactCompiler.mdx,reactMaxHeadersLength.mdx,reactStrictMode.mdx,redirects.mdx,rewrites.mdx,sassOptions.mdx,serverActions.mdx,serverComponentsHmrCache.mdx,serverExternalPackages.mdx,staleTimes.mdx,staticGeneration.mdx,taint.mdx,trailingSlash.mdx,transpilePackages.mdx,turbopack.mdx,turbopackFileSystemCache.mdx,turbopackIgnoreIssue.mdx,typedRoutes.mdx,typescript.mdx,urlImports.mdx,useLightningcss.mdx,viewTransition.mdx,webVitalsAttribution.mdx,webpack.mdx}|01-app/03-api-reference/05-config:{02-typescript.mdx,03-eslint.mdx}|01-app/03-api-reference/06-cli:{create-next-app.mdx,next.mdx}|02-pages/01-getting-started:{01-installation.mdx,02-project-structure.mdx,04-images.mdx,05-fonts.mdx,06-css.mdx,11-deploying.mdx}|02-pages/02-guides:{analytics.mdx,authentication.mdx,babel.mdx,ci-build-caching.mdx,content-security-policy.mdx,css-in-js.mdx,custom-server.mdx,debugging.mdx,draft-mode.mdx,environment-variables.mdx,forms.mdx,incremental-static-regeneration.mdx,instrumentation.mdx,internationalization.mdx,lazy-loading.mdx,mdx.mdx,multi-zones.mdx,open-telemetry.mdx,package-bundling.mdx,post-css.mdx,preview-mode.mdx,production-checklist.mdx,redirecting.mdx,sass.mdx,scripts.mdx,self-hosting.mdx,static-exports.mdx,tailwind-v3-css.mdx,third-party-libraries.mdx}|02-pages/02-guides/migrating:{app-router-migration.mdx,from-create-react-app.mdx,from-vite.mdx}|02-pages/02-guides/testing:{cypress.mdx,jest.mdx,playwright.mdx,vitest.mdx}|02-pages/02-guides/upgrading:{codemods.mdx,version-10.mdx,version-11.mdx,version-12.mdx,version-13.mdx,version-14.mdx,version-9.mdx}|02-pages/03-building-your-application/01-routing:{01-pages-and-layouts.mdx,02-dynamic-routes.mdx,03-linking-and-navigating.mdx,05-custom-app.mdx,06-custom-document.mdx,07-api-routes.mdx,08-custom-error.mdx}|02-pages/03-building-your-application/02-rendering:{01-server-side-rendering.mdx,02-static-site-generation.mdx,04-automatic-static-optimization.mdx,05-client-side-rendering.mdx}|02-pages/03-building-your-application/03-data-fetching:{01-get-static-props.mdx,02-get-static-paths.mdx,03-forms-and-mutations.mdx,03-get-server-side-props.mdx,05-client-side.mdx}|02-pages/03-building-your-application/06-configuring:{12-error-handling.mdx}|02-pages/04-api-reference:{06-edge.mdx,08-turbopack.mdx}|02-pages/04-api-reference/01-components:{font.mdx,form.mdx,head.mdx,image-legacy.mdx,image.mdx,link.mdx,script.mdx}|02-pages/04-api-reference/02-file-conventions:{instrumentation.mdx,proxy.mdx,public-folder.mdx,src-folder.mdx}|02-pages/04-api-reference/03-functions:{get-initial-props.mdx,get-server-side-props.mdx,get-static-paths.mdx,get-static-props.mdx,next-request.mdx,next-response.mdx,use-params.mdx,use-report-web-vitals.mdx,use-router.mdx,use-search-params.mdx,userAgent.mdx}|02-pages/04-api-reference/04-config/01-next-config-js:{adapterPath.mdx,allowedDevOrigins.mdx,assetPrefix.mdx,basePath.mdx,bundlePagesRouterDependencies.mdx,compress.mdx,crossOrigin.mdx,deploymentId.mdx,devIndicators.mdx,distDir.mdx,env.mdx,exportPathMap.mdx,generateBuildId.mdx,generateEtags.mdx,headers.mdx,httpAgentOptions.mdx,images.mdx,logging.mdx,onDemandEntries.mdx,optimizePackageImports.mdx,output.mdx,pageExtensions.mdx,poweredByHeader.mdx,productionBrowserSourceMaps.mdx,proxyClientMaxBodySize.mdx,reactStrictMode.mdx,redirects.mdx,rewrites.mdx,serverExternalPackages.mdx,trailingSlash.mdx,transpilePackages.mdx,turbopack.mdx,typescript.mdx,urlImports.mdx,useLightningcss.mdx,webVitalsAttribution.mdx,webpack.mdx}|02-pages/04-api-reference/04-config:{01-typescript.mdx,02-eslint.mdx}|02-pages/04-api-reference/05-cli:{create-next-app.mdx,next.mdx}|03-architecture:{accessibility.mdx,fast-refresh.mdx,nextjs-compiler.mdx,supported-browsers.mdx}|04-community:{01-contribution-guide.mdx,02-rspack.mdx} - ## Internationalization The Next.js app uses `next-intl` for internationalization, with `en.json`, `ko.json`, and `zh.json` files living at `apps/web/messages`. When building user-facing components, make sure to include proper translation support. @@ -21,3 +19,11 @@ Canonical defaults (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for- ### Domain docs Single-context: one `CONTEXT.md` + `docs/adr/` at the repo root (created lazily by `/grill-with-docs`). See `docs/agents/domain.md`. + + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + +**Keep this block, including in commits.** It is part of the project's agent setup, maintained by `next dev` for every agent that works here. If it appears as an uncommitted change, that is intentional — commit it as-is. Do not remove it to clean up a diff; it will be regenerated. + diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 17a0831a7..6881b833a 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -25,6 +25,8 @@ const cspHeader = ` `; const nextConfig: NextConfig = { + cacheComponents: true, + partialPrefetching: true, reactCompiler: true, typedRoutes: true, transpilePackages: ["@parsertime/transactional"], diff --git a/apps/web/package.json b/apps/web/package.json index 58cf25be0..0d90aa7b1 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -135,7 +135,7 @@ "media-chrome": "^4.18.2", "motion": "^12.23.26", "nanoid": "^5.1.5", - "next": "16.2.9", + "next": "16.3.0-preview.5", "next-axiom": "^1.9.2", "next-intl": "^4.9.2", "next-seo": "^7.2.0", diff --git a/apps/web/src/app/(auth)/auth-error/layout.tsx b/apps/web/src/app/(auth)/auth-error/layout.tsx index d96e75ceb..327562839 100644 --- a/apps/web/src/app/(auth)/auth-error/layout.tsx +++ b/apps/web/src/app/(auth)/auth-error/layout.tsx @@ -1,9 +1,9 @@ import { Footer } from "@/components/footer"; +import { getMetadataTranslations } from "@/lib/metadata-i18n"; import type { Metadata } from "next"; -import { getTranslations } from "next-intl/server"; -export async function generateMetadata(): Promise { - const t = await getTranslations("authError.metadata"); +export function generateMetadata(): Metadata { + const t = getMetadataTranslations("authError.metadata"); return { title: t("title"), description: t("description") }; } diff --git a/apps/web/src/app/(auth)/auth-error/loading.tsx b/apps/web/src/app/(auth)/auth-error/loading.tsx new file mode 100644 index 000000000..55513b715 --- /dev/null +++ b/apps/web/src/app/(auth)/auth-error/loading.tsx @@ -0,0 +1,15 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export default function Loading() { + return ( +
+ + + +
+ + +
+
+ ); +} diff --git a/apps/web/src/app/(auth)/sign-in/loading.tsx b/apps/web/src/app/(auth)/sign-in/loading.tsx new file mode 100644 index 000000000..9edd166b1 --- /dev/null +++ b/apps/web/src/app/(auth)/sign-in/loading.tsx @@ -0,0 +1,41 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export default function Loading() { + return ( +
+
+
+ + +
+
+
+ + +
+
+
+
+ {["discord", "google", "github"].map((k) => ( + + ))} +
+
+ +
+
+
+ + +
+ + +
+
+
+
+ +
+
+ ); +} diff --git a/apps/web/src/app/(auth)/sign-in/page.tsx b/apps/web/src/app/(auth)/sign-in/page.tsx index c3ba4473a..b21ca7c2c 100644 --- a/apps/web/src/app/(auth)/sign-in/page.tsx +++ b/apps/web/src/app/(auth)/sign-in/page.tsx @@ -1,23 +1,15 @@ import { UserAuthForm } from "@/components/auth/user-auth-form"; import { Link } from "@/components/ui/link"; import { auth } from "@/lib/auth"; -import type { PagePropsWithLocale } from "@/types/next"; +import { defaultLocale } from "@/i18n/config"; +import { getMetadataTranslations } from "@/lib/metadata-i18n"; import type { Metadata, Route } from "next"; import { getTranslations } from "next-intl/server"; import Image from "next/image"; import { redirect } from "next/navigation"; -export async function generateMetadata( - props: PagePropsWithLocale<"/sign-in"> -): Promise { - const params = await props.params; - - const { locale } = params; - - const t = await getTranslations({ - locale, - namespace: "signInPage.metadataSignIn", - }); +export function generateMetadata(): Metadata { + const t = getMetadataTranslations("signInPage.metadataSignIn"); return { title: t("title"), @@ -35,7 +27,7 @@ export async function generateMetadata( height: 630, }, ], - locale, + locale: defaultLocale, }, }; } diff --git a/apps/web/src/app/(auth)/sign-up/loading.tsx b/apps/web/src/app/(auth)/sign-up/loading.tsx new file mode 100644 index 000000000..0299362d3 --- /dev/null +++ b/apps/web/src/app/(auth)/sign-up/loading.tsx @@ -0,0 +1,37 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export default function Loading() { + return ( +
+
+
+ + +
+
+
+ + +
+
+
+ {["a", "b", "c"].map((k) => ( + + ))} +
+ +
+
+ + +
+ +
+ +
+
+ +
+
+ ); +} diff --git a/apps/web/src/app/(auth)/sign-up/page.tsx b/apps/web/src/app/(auth)/sign-up/page.tsx index fc8794c25..8e38419ed 100644 --- a/apps/web/src/app/(auth)/sign-up/page.tsx +++ b/apps/web/src/app/(auth)/sign-up/page.tsx @@ -1,21 +1,13 @@ import { UserAuthForm } from "@/components/auth/user-auth-form"; import { Link } from "@/components/ui/link"; -import type { PagePropsWithLocale } from "@/types/next"; +import { defaultLocale } from "@/i18n/config"; +import { getMetadataTranslations } from "@/lib/metadata-i18n"; import type { Metadata } from "next"; import { getTranslations } from "next-intl/server"; import Image from "next/image"; -export async function generateMetadata( - props: PagePropsWithLocale<"/sign-up"> -): Promise { - const params = await props.params; - - const { locale } = params; - - const t = await getTranslations({ - locale, - namespace: "signInPage.metadataSignUp", - }); +export function generateMetadata(): Metadata { + const t = getMetadataTranslations("signInPage.metadataSignUp"); return { title: t("title"), @@ -33,7 +25,7 @@ export async function generateMetadata( height: 630, }, ], - locale, + locale: defaultLocale, }, }; } diff --git a/apps/web/src/app/(auth)/verify-request/layout.tsx b/apps/web/src/app/(auth)/verify-request/layout.tsx index e266d7fbf..933cd25d1 100644 --- a/apps/web/src/app/(auth)/verify-request/layout.tsx +++ b/apps/web/src/app/(auth)/verify-request/layout.tsx @@ -1,9 +1,9 @@ import { Footer } from "@/components/footer"; +import { getMetadataTranslations } from "@/lib/metadata-i18n"; import type { Metadata } from "next"; -import { getTranslations } from "next-intl/server"; -export async function generateMetadata(): Promise { - const t = await getTranslations("verifyRequest.metadata"); +export function generateMetadata(): Metadata { + const t = getMetadataTranslations("verifyRequest.metadata"); return { title: t("title"), description: t("description") }; } diff --git a/apps/web/src/app/(auth)/verify-request/loading.tsx b/apps/web/src/app/(auth)/verify-request/loading.tsx new file mode 100644 index 000000000..7b5ffc67e --- /dev/null +++ b/apps/web/src/app/(auth)/verify-request/loading.tsx @@ -0,0 +1,11 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export default function Loading() { + return ( +
+ + + +
+ ); +} diff --git a/apps/web/src/app/[team]/compare/loading.tsx b/apps/web/src/app/[team]/compare/loading.tsx new file mode 100644 index 000000000..62f49e3b6 --- /dev/null +++ b/apps/web/src/app/[team]/compare/loading.tsx @@ -0,0 +1,84 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +const FOUR = ["a", "b", "c", "d"]; +const SIX = ["a", "b", "c", "d", "e", "f"]; + +export default function Loading() { + return ( +
+
+ + +
+ +
+
+
+ + +
+
+ + + +
+
+
+ +
+
+
+
+ + +
+
+ + + +
+
+
+ +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ +
+
+ {SIX.map((k) => ( + + ))} +
+
+
+ {FOUR.map((k) => ( +
+
+ + + +
+
+ ))} +
+ +
+
+
+ ); +} diff --git a/apps/web/src/app/[team]/compare/page.tsx b/apps/web/src/app/[team]/compare/page.tsx index 9fe5102f3..6b48c8742 100644 --- a/apps/web/src/app/[team]/compare/page.tsx +++ b/apps/web/src/app/[team]/compare/page.tsx @@ -4,21 +4,15 @@ import { Effect } from "effect"; import { AppRuntime } from "@/data/runtime"; import { UserService } from "@/data/user"; import { auth } from "@/lib/auth"; +import { getMetadataTranslations } from "@/lib/metadata-i18n"; import prisma from "@/lib/prisma"; import type { PagePropsWithLocale } from "@/types/next"; import { $Enums } from "@/generated/prisma/browser"; import type { Metadata } from "next"; -import { getTranslations } from "next-intl/server"; import { notFound } from "next/navigation"; -export async function generateMetadata( - props: PagePropsWithLocale<"/[team]/compare"> -): Promise { - const params = await props.params; - const t = await getTranslations({ - locale: params.locale, - namespace: "comparePage.metadata", - }); +export function generateMetadata(): Metadata { + const t = getMetadataTranslations("comparePage.metadata"); return { title: t("title"), diff --git a/apps/web/src/app/[team]/map-groups/loading.tsx b/apps/web/src/app/[team]/map-groups/loading.tsx new file mode 100644 index 000000000..cf8af217b --- /dev/null +++ b/apps/web/src/app/[team]/map-groups/loading.tsx @@ -0,0 +1,43 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +const SIX = ["a", "b", "c", "d", "e", "f"]; + +export default function Loading() { + return ( +
+ + +
+
+
+ + +
+ +
+
+ {SIX.map((k) => ( +
+
+
+ + +
+ +
+
+ + +
+ + +
+ +
+
+ ))} +
+
+
+ ); +} diff --git a/apps/web/src/app/[team]/map-groups/page.tsx b/apps/web/src/app/[team]/map-groups/page.tsx index a8505c1c2..f93a8e2b0 100644 --- a/apps/web/src/app/[team]/map-groups/page.tsx +++ b/apps/web/src/app/[team]/map-groups/page.tsx @@ -4,21 +4,15 @@ import { Effect } from "effect"; import { AppRuntime } from "@/data/runtime"; import { UserService } from "@/data/user"; import { auth } from "@/lib/auth"; +import { getMetadataTranslations } from "@/lib/metadata-i18n"; import prisma from "@/lib/prisma"; import type { PagePropsWithLocale } from "@/types/next"; import { $Enums } from "@/generated/prisma/browser"; import type { Metadata } from "next"; -import { getTranslations } from "next-intl/server"; import { notFound } from "next/navigation"; -export async function generateMetadata( - props: PagePropsWithLocale<"/[team]/map-groups"> -): Promise { - const params = await props.params; - const t = await getTranslations({ - locale: params.locale, - namespace: "mapGroupsPage.metadata", - }); +export function generateMetadata(): Metadata { + const t = getMetadataTranslations("mapGroupsPage.metadata"); return { title: t("title"), diff --git a/apps/web/src/app/[team]/ops/loading.tsx b/apps/web/src/app/[team]/ops/loading.tsx new file mode 100644 index 000000000..b5e30046f --- /dev/null +++ b/apps/web/src/app/[team]/ops/loading.tsx @@ -0,0 +1,33 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +const ROWS = ["a", "b", "c", "d", "e"]; + +export default function Loading() { + return ( +
+
+
+ + +
+
+
+ +
+ +
+ {ROWS.map((k) => ( +
+
+ + +
+ +
+ ))} +
+
+
+
+ ); +} diff --git a/apps/web/src/app/[team]/ops/page.tsx b/apps/web/src/app/[team]/ops/page.tsx index a344d85be..88b7be6cf 100644 --- a/apps/web/src/app/[team]/ops/page.tsx +++ b/apps/web/src/app/[team]/ops/page.tsx @@ -5,6 +5,7 @@ import { AppRuntime } from "@/data/runtime"; import { UserService } from "@/data/user"; import { auth } from "@/lib/auth"; import { canManageTeam } from "@/lib/auth"; +import { getMetadataTranslations } from "@/lib/metadata-i18n"; import { listBlacklist, getBlacklistSuggestions, @@ -14,14 +15,8 @@ import type { Metadata } from "next"; import { getTranslations } from "next-intl/server"; import { notFound } from "next/navigation"; -export async function generateMetadata( - props: PagePropsWithLocale<"/[team]/ops"> -): Promise { - const params = await props.params; - const t = await getTranslations({ - locale: params.locale, - namespace: "teamOps", - }); +export function generateMetadata(): Metadata { + const t = getMetadataTranslations("teamOps"); return { title: t("title"), diff --git a/apps/web/src/app/[team]/scrim/[scrimId]/edit/loading.tsx b/apps/web/src/app/[team]/scrim/[scrimId]/edit/loading.tsx new file mode 100644 index 000000000..2a6f308de --- /dev/null +++ b/apps/web/src/app/[team]/scrim/[scrimId]/edit/loading.tsx @@ -0,0 +1,50 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export default function Loading() { + return ( +
+ +
+ +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ +
+ + +
+
+
+ + {["a", "b", "c"].map((k) => ( + + ))} +
+ +
+
+
+ + + + +
+
+
+ ); +} diff --git a/apps/web/src/app/[team]/scrim/[scrimId]/edit/page.tsx b/apps/web/src/app/[team]/scrim/[scrimId]/edit/page.tsx index 099b06bf1..202769a41 100644 --- a/apps/web/src/app/[team]/scrim/[scrimId]/edit/page.tsx +++ b/apps/web/src/app/[team]/scrim/[scrimId]/edit/page.tsx @@ -10,6 +10,7 @@ import { UserService } from "@/data/user"; import { auth } from "@/lib/auth"; import { scoutingTool } from "@/lib/flags"; import { resolveMapDataId } from "@/lib/map-data-resolver"; +import { getMetadataTranslations } from "@/lib/metadata-i18n"; import prisma from "@/lib/prisma"; import type { Metadata, Route } from "next"; import { getTranslations } from "next-intl/server"; @@ -18,7 +19,7 @@ export async function generateMetadata( props: PageProps<"/[team]/scrim/[scrimId]/edit"> ): Promise { const params = await props.params; - const t = await getTranslations("scrimPage.editMetadata"); + const t = getMetadataTranslations("scrimPage.editMetadata"); const scrim = await AppRuntime.runPromise( ScrimService.pipe( Effect.flatMap((svc) => svc.getScrim(parseInt(params.scrimId))) diff --git a/apps/web/src/app/[team]/scrim/[scrimId]/layout.tsx b/apps/web/src/app/[team]/scrim/[scrimId]/layout.tsx index b89711136..c6e4981dc 100644 --- a/apps/web/src/app/[team]/scrim/[scrimId]/layout.tsx +++ b/apps/web/src/app/[team]/scrim/[scrimId]/layout.tsx @@ -1,20 +1,27 @@ import { NoAuthCard } from "@/components/auth/no-auth"; import { SelectedPlayerProvider } from "@/components/map/player-switcher"; import { isAuthedToViewScrim } from "@/lib/auth"; +import { Suspense, type ReactNode } from "react"; -export default async function ScrimDashboardLayout( +export default function ScrimDashboardLayout( props: LayoutProps<"/[team]/scrim/[scrimId]"> ) { - const params = await props.params; - - const { children } = props; - - const id = parseInt(params.scrimId); - const isAuthed = await isAuthedToViewScrim(id); - - if (!isAuthed) { - return ; - } + return ( + + {props.children} + + ); +} +async function ScrimAuthGate({ + params, + children, +}: { + params: LayoutProps<"/[team]/scrim/[scrimId]">["params"]; + children: ReactNode; +}) { + const { scrimId } = await params; + const isAuthed = await isAuthedToViewScrim(parseInt(scrimId)); + if (!isAuthed) return ; return {children}; } diff --git a/apps/web/src/app/[team]/scrim/[scrimId]/map/[mapId]/layout.tsx b/apps/web/src/app/[team]/scrim/[scrimId]/map/[mapId]/layout.tsx index 93599c5f2..924b6f811 100644 --- a/apps/web/src/app/[team]/scrim/[scrimId]/map/[mapId]/layout.tsx +++ b/apps/web/src/app/[team]/scrim/[scrimId]/map/[mapId]/layout.tsx @@ -3,28 +3,33 @@ import { Footer } from "@/components/footer"; import { MobileBanner } from "@/components/map/mobile-banner"; import { SelectedPlayerProvider } from "@/components/map/player-switcher"; import { isAuthedToViewMap } from "@/lib/auth"; +import { Suspense, type ReactNode } from "react"; -export default async function MapDashboardLayout( +export default function MapDashboardLayout( props: LayoutProps<"/[team]/scrim/[scrimId]/map/[mapId]"> ) { - const params = await props.params; - - const { children } = props; - - const scrimId = parseInt(params.scrimId); - const mapId = parseInt(params.mapId); - - const isAuthed = await isAuthedToViewMap(scrimId, mapId); - - if (!isAuthed) { - return ; - } - + // The chrome (provider, banner, footer) is static so navigations to a map + // paint instantly; the authorization check and the page stream in behind it. return ( - {children} -