Fix params/searchParams types for Next.js app router

This commit is contained in:
Enguerrand Ozano
2026-02-28 19:37:34 +01:00
parent fc881e5178
commit cdb50e0414
4 changed files with 608 additions and 21 deletions

View File

@@ -1,16 +1,33 @@
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import config from '../../../../payload.config'
import { importMap } from '../importMap'
import { RootPage, generatePageMetadata } from '@payloadcms/next/views';
import config from '../../../../payload.config';
import { importMap } from '../importMap';
type Args = {
params: Promise<{ segments: string[] }>
searchParams: Promise<{ [key: string]: string | string[] }>
export type PayloadRouteParams = {
segments?: string[];
};
export type PayloadRouteSearchParams = {
[key: string]: string | string[] | undefined;
};
type Props = {
params: PayloadRouteParams;
searchParams: PayloadRouteSearchParams;
};
export const generateMetadata = (props: Props) => {
return generatePageMetadata({
config,
params: Promise.resolve(props.params) as Promise<{ [key: string]: string | string[] }>,
searchParams: Promise.resolve(props.searchParams) as Promise<{ [key: string]: string | string[] }>,
});
};
export default function Page(props: Props) {
return RootPage({
config,
importMap,
params: Promise.resolve(props.params) as Promise<{ segments: string[] }>,
searchParams: Promise.resolve(props.searchParams) as Promise<{ [key: string]: string | string[] }>,
});
}
export const generateMetadata = ({ params, searchParams }: Args) =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) =>
RootPage({ config, importMap, params, searchParams })
export default Page

View File

@@ -6,7 +6,7 @@ import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import { getBlogPost, getBlogPosts, getSiteConfig } from "@/lib/content";
type Props = { params: Promise<{ slug: string }> };
type Props = { params: { slug: string } };
// Corps des articles — FUTURE: champ rich text Payload CMS
const articleContenu: Record<string, string[]> = {
@@ -66,8 +66,10 @@ export async function generateStaticParams() {
return posts.map((p) => ({ slug: p.slug }));
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
export async function generateMetadata(
{ params }: Props
): Promise<Metadata> {
const { slug } = params;
const [post, config] = await Promise.all([getBlogPost(slug), getSiteConfig()]);
if (!post) return { title: "Article introuvable" };
return {
@@ -78,7 +80,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
}
export default async function BlogArticlePage({ params }: Props) {
const { slug } = await params;
const { slug } = params;
const post = await getBlogPost(slug);
if (!post) notFound();
@@ -91,7 +93,10 @@ export default async function BlogArticlePage({ params }: Props) {
<section className="bg-navy py-12 md:py-16">
<div className="max-w-3xl mx-auto px-4 sm:px-6">
<ScrollReveal direction="up">
<Link href="/blog" className="inline-flex items-center gap-1.5 text-white/50 hover:text-white text-sm mb-6 transition-colors">
<Link
href="/blog"
className="inline-flex items-center gap-1.5 text-white/50 hover:text-white text-sm mb-6 transition-colors"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>

4
package-lock.json generated
View File

@@ -28,6 +28,7 @@
"eslint": "^9",
"eslint-config-next": "^15.5.12",
"tailwindcss": "^4",
"tsx": "^4.21.0",
"typescript": "^5"
}
},
@@ -8319,6 +8320,7 @@
"resolved": "https://registry.npmjs.org/next/-/next-15.3.9.tgz",
"integrity": "sha512-bat50ogkh2esjfkbqmVocL5QunR9RGCSO2oQKFjKeDcEylIgw3JY6CMfGnzoVfXJ9SDLHI546sHmsk90D2ivwQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@next/env": "15.3.9",
"@swc/counter": "0.1.3",
@@ -9374,7 +9376,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -9692,7 +9693,6 @@
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.4.tgz",
"integrity": "sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==",
"license": "MIT",
"peer": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",

565
payload-types.ts Normal file
View File

@@ -0,0 +1,565 @@
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
/**
* Supported timezones in IANA format.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "supportedTimezones".
*/
export type SupportedTimezones =
| 'Pacific/Midway'
| 'Pacific/Niue'
| 'Pacific/Honolulu'
| 'Pacific/Rarotonga'
| 'America/Anchorage'
| 'Pacific/Gambier'
| 'America/Los_Angeles'
| 'America/Tijuana'
| 'America/Denver'
| 'America/Phoenix'
| 'America/Chicago'
| 'America/Guatemala'
| 'America/New_York'
| 'America/Bogota'
| 'America/Caracas'
| 'America/Santiago'
| 'America/Buenos_Aires'
| 'America/Sao_Paulo'
| 'Atlantic/South_Georgia'
| 'Atlantic/Azores'
| 'Atlantic/Cape_Verde'
| 'Europe/London'
| 'Europe/Berlin'
| 'Africa/Lagos'
| 'Europe/Athens'
| 'Africa/Cairo'
| 'Europe/Moscow'
| 'Asia/Riyadh'
| 'Asia/Dubai'
| 'Asia/Baku'
| 'Asia/Karachi'
| 'Asia/Tashkent'
| 'Asia/Calcutta'
| 'Asia/Dhaka'
| 'Asia/Almaty'
| 'Asia/Jakarta'
| 'Asia/Bangkok'
| 'Asia/Shanghai'
| 'Asia/Singapore'
| 'Asia/Tokyo'
| 'Asia/Seoul'
| 'Australia/Brisbane'
| 'Australia/Sydney'
| 'Pacific/Guam'
| 'Pacific/Noumea'
| 'Pacific/Auckland'
| 'Pacific/Fiji';
export interface Config {
auth: {
users: UserAuthOperations;
};
blocks: {};
collections: {
services: Service;
realisations: Realisation;
articles: Article;
testimonials: Testimonial;
faq: Faq;
media: Media;
'payload-kv': PayloadKv;
users: User;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsSelect: {
services: ServicesSelect<false> | ServicesSelect<true>;
realisations: RealisationsSelect<false> | RealisationsSelect<true>;
articles: ArticlesSelect<false> | ArticlesSelect<true>;
testimonials: TestimonialsSelect<false> | TestimonialsSelect<true>;
faq: FaqSelect<false> | FaqSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
users: UsersSelect<false> | UsersSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: number;
};
fallbackLocale: null;
globals: {};
globalsSelect: {};
locale: null;
widgets: {
collections: CollectionsWidget;
};
user: User;
jobs: {
tasks: unknown;
workflows: unknown;
};
}
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
password: string;
};
registerFirstUser: {
email: string;
password: string;
};
unlock: {
email: string;
password: string;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "services".
*/
export interface Service {
id: number;
/**
* Identifiant URL (ex: construction-maison)
*/
slug: string;
title: string;
shortDescription: string;
longDescription?: string | null;
/**
* ex: 🏠
*/
icon?: string | null;
keywords?:
| {
keyword?: string | null;
id?: string | null;
}[]
| null;
order?: number | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "realisations".
*/
export interface Realisation {
id: number;
title: string;
ville: string;
service?: (number | null) | Service;
description?: string | null;
image?: (number | null) | Media;
gallery?:
| {
image?: (number | null) | Media;
caption?: string | null;
id?: string | null;
}[]
| null;
featured?: boolean | null;
publishedAt?: string | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: number;
alt?: string | null;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
focalX?: number | null;
focalY?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "articles".
*/
export interface Article {
id: number;
title: string;
/**
* ex: combien-coute-construction-maison-nord
*/
slug: string;
excerpt?: string | null;
content?: {
root: {
type: string;
children: {
type: any;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
category?: ('construction' | 'renovation' | 'assainissement' | 'conseils') | null;
publishedAt?: string | null;
status?: ('draft' | 'published') | null;
seoTitle?: string | null;
seoDescription?: string | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "testimonials".
*/
export interface Testimonial {
id: number;
name: string;
ville?: string | null;
service?: (number | null) | Service;
text: string;
rating?: number | null;
featured?: boolean | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "faq".
*/
export interface Faq {
id: number;
question: string;
answer: string;
order?: number | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-kv".
*/
export interface PayloadKv {
id: number;
key: string;
data:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: number;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
sessions?:
| {
id: string;
createdAt?: string | null;
expiresAt: string;
}[]
| null;
password?: string | null;
collection: 'users';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: number;
document?:
| ({
relationTo: 'services';
value: number | Service;
} | null)
| ({
relationTo: 'realisations';
value: number | Realisation;
} | null)
| ({
relationTo: 'articles';
value: number | Article;
} | null)
| ({
relationTo: 'testimonials';
value: number | Testimonial;
} | null)
| ({
relationTo: 'faq';
value: number | Faq;
} | null)
| ({
relationTo: 'media';
value: number | Media;
} | null)
| ({
relationTo: 'users';
value: number | User;
} | null);
globalSlug?: string | null;
user: {
relationTo: 'users';
value: number | User;
};
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".
*/
export interface PayloadPreference {
id: number;
user: {
relationTo: 'users';
value: number | User;
};
key?: string | null;
value?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations".
*/
export interface PayloadMigration {
id: number;
name?: string | null;
batch?: number | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "services_select".
*/
export interface ServicesSelect<T extends boolean = true> {
slug?: T;
title?: T;
shortDescription?: T;
longDescription?: T;
icon?: T;
keywords?:
| T
| {
keyword?: T;
id?: T;
};
order?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "realisations_select".
*/
export interface RealisationsSelect<T extends boolean = true> {
title?: T;
ville?: T;
service?: T;
description?: T;
image?: T;
gallery?:
| T
| {
image?: T;
caption?: T;
id?: T;
};
featured?: T;
publishedAt?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "articles_select".
*/
export interface ArticlesSelect<T extends boolean = true> {
title?: T;
slug?: T;
excerpt?: T;
content?: T;
category?: T;
publishedAt?: T;
status?: T;
seoTitle?: T;
seoDescription?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "testimonials_select".
*/
export interface TestimonialsSelect<T extends boolean = true> {
name?: T;
ville?: T;
service?: T;
text?: T;
rating?: T;
featured?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "faq_select".
*/
export interface FaqSelect<T extends boolean = true> {
question?: T;
answer?: T;
order?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media_select".
*/
export interface MediaSelect<T extends boolean = true> {
alt?: T;
updatedAt?: T;
createdAt?: T;
url?: T;
thumbnailURL?: T;
filename?: T;
mimeType?: T;
filesize?: T;
width?: T;
height?: T;
focalX?: T;
focalY?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-kv_select".
*/
export interface PayloadKvSelect<T extends boolean = true> {
key?: T;
data?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select".
*/
export interface UsersSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
email?: T;
resetPasswordToken?: T;
resetPasswordExpiration?: T;
salt?: T;
hash?: T;
loginAttempts?: T;
lockUntil?: T;
sessions?:
| T
| {
id?: T;
createdAt?: T;
expiresAt?: T;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents_select".
*/
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
document?: T;
globalSlug?: T;
user?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences_select".
*/
export interface PayloadPreferencesSelect<T extends boolean = true> {
user?: T;
key?: T;
value?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations_select".
*/
export interface PayloadMigrationsSelect<T extends boolean = true> {
name?: T;
batch?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "collections_widget".
*/
export interface CollectionsWidget {
data?: {
[k: string]: unknown;
};
width: 'full';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".
*/
export interface Auth {
[k: string]: unknown;
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}