Compare commits

..
Author SHA1 Message Date
Fahim b189ca3f81 Fix img to webp 2025-05-10 18:57:02 +07:00
Fahim 02b40701f9 fix: text color 2025-05-08 23:21:51 +07:00
Rasyid 6e78b49731 feat: integrate payload cms to landing page (#25)
* feat: integrate payload cms to landing page

* feat: add payload configuration and update TypeScript target

* fix: update TypeScript paths for payload configuration

* refactor: update config import paths to use local payload.config

* fix: update quotation marks for testimonials to use HTML entities

* fix: include withPayload in Next.js plugins for proper configuration
2025-05-08 22:11:57 +07:00
Rasyid 3aa552afd6 feat: landing page migration (#24)
* chore: cleanup nextjs project

* feat(shadcn-ui): initialize shadcn ui libs

* feat: update landing app with shadcnI integration

* feat: add path mapping for shadcn-ui atoms in tsconfig

* feat: remove unused tw-animate-css import

* feat: migrate header component

* feat: migrate hero component

* feat: migrate features component

* feat: migrate community component

* feat: migrate learning resources component

* feat: migrate testimonials component

* feat: migrate call to action component

* feat: migrate footer component

* feat: migrate simple theme switcher component

* feat: update dependencies in package.json and package-lock.json

* chore: add tailwind-animate-css package

* feat: initialize index file to export atoms

* chore: remove pnpm, accidentally added out of habit
2025-04-27 09:31:24 +07:00
33 changed files with 5608 additions and 247 deletions
+4 -1
View File
@@ -1 +1,4 @@
VITE_API_URL=https://api.example.com VITE_API_URL=
CMS_SECRET=
CMS_POSTGRES_URL=
+2 -4
View File
@@ -2,6 +2,7 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const { composePlugins, withNx } = require('@nx/next'); const { composePlugins, withNx } = require('@nx/next');
const { withPayload } = require('@payloadcms/next/withPayload');
/** /**
* @type {import('@nx/next/plugins/with-nx').WithNxOptions} * @type {import('@nx/next/plugins/with-nx').WithNxOptions}
@@ -14,9 +15,6 @@ const nextConfig = {
}, },
}; };
const plugins = [ const plugins = [withNx, withPayload];
// Add more Next.js plugins to this list if needed.
withNx,
];
module.exports = composePlugins(...plugins)(nextConfig); module.exports = composePlugins(...plugins)(nextConfig);
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

@@ -41,7 +41,8 @@ export function CallToAction() {
<div className="flex flex-col sm:flex-row gap-4 justify-center"> <div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button <Button
size="lg" size="lg"
className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90" className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90
font-bold dark:text-black hover:text-black dark:hover:text-white cursor-pointer"
> >
Gabung Discord Gabung Discord
</Button> </Button>
@@ -188,7 +188,8 @@ export default function Footer() {
placeholder="Email Anda" placeholder="Email Anda"
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50" className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
/> />
<button className="inline-flex items-center justify-center rounded-md bg-primary px-3 py-1 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"> <button className="inline-flex items-center justify-center rounded-md bg-primary px-3 py-1 text-sm shadow hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50
font-bold text-white dark:text-black hover:text-black dark:hover:text-white cursor-pointer">
Daftar Daftar
</button> </button>
</div> </div>
@@ -40,7 +40,7 @@ export function Header() {
alt="IMPHNEN Logo" alt="IMPHNEN Logo"
width={64} width={64}
height={64} height={64}
className="object-cover" className="object-cover cursor-pointer"
/> />
</div> </div>
</div> </div>
@@ -82,7 +82,7 @@ export function Header() {
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<SimpleThemeToggle /> <SimpleThemeToggle />
<Button className="hidden md:flex bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 transition-all duration-300"> <Button className="hidden md:flex bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 transition-all duration-300 font-bold text-white dark:text-black hover:text-black dark:hover:text-white cursor-pointer">
Gabung Discord Gabung Discord
</Button> </Button>
@@ -134,7 +134,7 @@ export function Header() {
> >
Testimoni Testimoni
</Link> </Link>
<Button className="mt-4 bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90"> <Button className="mt-4 bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 font-bold text-white dark:text-black hover:text-black dark:hover:text-white cursor-pointer">
Gabung Discord Gabung Discord
</Button> </Button>
</nav> </nav>
@@ -79,7 +79,8 @@ export function Hero() {
<a href="https://facebook.com"> <a href="https://facebook.com">
<Button <Button
size="lg" size="lg"
className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 transition-all duration-300" className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 transition-all duration-300
font-bold text-white dark:text-black hover:text-black dark:hover:text-white cursor-pointer"
> >
Mulai Belajar Mulai Belajar
</Button> </Button>
@@ -170,7 +170,8 @@ export function LearningResources() {
menengah. menengah.
</p> </p>
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
<Button className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90"> <Button className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90
font-bold text-white dark:text-black hover:text-black dark:hover:text-white cursor-pointer">
Mulai Kursus Mulai Kursus
</Button> </Button>
<Button variant="outline">Lihat Silabus</Button> <Button variant="outline">Lihat Silabus</Button>
@@ -29,7 +29,7 @@ export function SimpleThemeToggle() {
variant="outline" variant="outline"
size="icon" size="icon"
onClick={toggleTheme} onClick={toggleTheme}
className="focus-visible:ring-0" className="focus-visible:ring-0 cursor-pointer"
> >
{theme === 'dark' ? ( {theme === 'dark' ? (
<SunIcon className="h-[1.2rem] w-[1.2rem]" /> <SunIcon className="h-[1.2rem] w-[1.2rem]" />
@@ -15,21 +15,21 @@ export function Testimonials() {
'Saya yang tadinya tidak mengerti apa-apa tentang programming, sekarang bisa membuat website sendiri dengan mudah. Terima kasih IMPHNEN!', 'Saya yang tadinya tidak mengerti apa-apa tentang programming, sekarang bisa membuat website sendiri dengan mudah. Terima kasih IMPHNEN!',
name: 'Budi Santoso', name: 'Budi Santoso',
role: 'Web Developer Pemula', role: 'Web Developer Pemula',
avatar: '/placeholder.svg?height=64&width=64', avatar: '/Budi Santoso.webp',
}, },
{ {
quote: quote:
'Komunitas yang sangat supportif! Setiap pertanyaan selalu dijawab dengan cepat dan jelas. Diskusi programnya mudah dipahami.', 'Komunitas yang sangat supportif! Setiap pertanyaan selalu dijawab dengan cepat dan jelas. Diskusi programnya mudah dipahami.',
name: 'Anita Ratna', name: 'Anita Ratna',
role: 'Mobile App Developer', role: 'Mobile App Developer',
avatar: '/placeholder.svg?height=64&width=64', avatar: '/Anita Ratna.webp',
}, },
{ {
quote: quote:
'Server Discord IMPHNEN adalah tempat belajar terbaik untuk programmer pemula seperti saya. Materinya lengkap dan komunitasnya sangat membantu!', 'Server Discord IMPHNEN adalah tempat belajar terbaik untuk programmer pemula seperti saya. Materinya lengkap dan komunitasnya sangat membantu!',
name: 'Dedi Permana', name: 'Dedi Permana',
role: 'Data Scientist', role: 'Data Scientist',
avatar: '/placeholder.svg?height=64&width=64', avatar: '/Dedi Permana.webp',
}, },
]; ];
@@ -101,7 +101,7 @@ export function Testimonials() {
<div className="relative z-10"> <div className="relative z-10">
<p className="mb-6 text-muted-foreground italic"> <p className="mb-6 text-muted-foreground italic">
"{testimonial.quote}" &ldquo;{testimonial.quote}&rdquo;
</p> </p>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
@@ -1,6 +1,6 @@
import { type Metadata } from 'next'; import { type Metadata } from 'next';
import { Inter } from 'next/font/google'; import { Inter } from 'next/font/google';
import '../styles/globals.css'; import '../../styles/globals.css';
import { ThemeProvider } from './_components/theme-provider'; import { ThemeProvider } from './_components/theme-provider';
const inter = Inter({ subsets: ['latin'] }); const inter = Inter({ subsets: ['latin'] });
@@ -0,0 +1,27 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next';
import { NotFoundPage, 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 const generateMetadata = ({
params,
searchParams,
}: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams });
const NotFound = ({ params, searchParams }: Args) =>
NotFoundPage({ config, params, searchParams, importMap });
export default NotFound;
@@ -0,0 +1,27 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next';
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 const generateMetadata = ({
params,
searchParams,
}: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams });
const Page = ({ params, searchParams }: Args) =>
RootPage({ config, params, searchParams, importMap });
export default Page;
@@ -0,0 +1 @@
export const importMap = {};
@@ -0,0 +1,19 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import '@payloadcms/next/css';
import {
REST_DELETE,
REST_GET,
REST_OPTIONS,
REST_PATCH,
REST_POST,
REST_PUT,
} from '@payloadcms/next/routes';
import config from '../../../../payload.config';
export const GET = REST_GET(config);
export const POST = REST_POST(config);
export const DELETE = REST_DELETE(config);
export const PATCH = REST_PATCH(config);
export const PUT = REST_PUT(config);
export const OPTIONS = REST_OPTIONS(config);
@@ -0,0 +1,7 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import '@payloadcms/next/css';
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes';
import config from '../../../../payload.config';
export const GET = GRAPHQL_PLAYGROUND_GET(config);
@@ -0,0 +1,8 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes';
import config from '../../../../payload.config';
export const POST = GRAPHQL_POST(config);
export const OPTIONS = REST_OPTIONS(config);
+35
View File
@@ -0,0 +1,35 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import '@payloadcms/next/css';
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts';
import type { ServerFunctionClient } from 'payload';
import React from 'react';
import config from '../../payload.config';
import { importMap } from './admin/importMap.js';
import './custom.scss';
type Args = {
children: React.ReactNode;
};
const serverFunction: ServerFunctionClient = async function (args) {
'use server';
return handleServerFunctions({
...args,
config,
importMap,
});
};
const Layout = ({ children }: Args) => (
<RootLayout
config={config}
importMap={importMap}
serverFunction={serverFunction}
>
{children}
</RootLayout>
);
export default Layout;
+16
View File
@@ -0,0 +1,16 @@
import type { CollectionConfig } from 'payload';
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
upload: true,
};
+13
View File
@@ -0,0 +1,13 @@
import type { CollectionConfig } from 'payload';
export const Users: CollectionConfig = {
slug: 'users',
admin: {
useAsTitle: 'email',
},
auth: true,
fields: [
// Email added by default
// Add more fields as needed
],
};
+4
View File
@@ -0,0 +1,4 @@
import { getPayload } from 'payload';
import config from '../payload.config';
export const payload = await getPayload({ config });
+285
View File
@@ -0,0 +1,285 @@
/* 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: {
users: User;
media: Media;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsSelect: {
users: UsersSelect<false> | UsersSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: number;
};
globals: {};
globalsSelect: {};
locale: null;
user: User & {
collection: 'users';
};
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` "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;
password?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: number;
alt: string;
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` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: number;
document?:
| ({
relationTo: 'users';
value: number | User;
} | null)
| ({
relationTo: 'media';
value: number | Media;
} | 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` "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;
}
/**
* 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-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` "auth".
*/
export interface Auth {
[k: string]: unknown;
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}
+40
View File
@@ -0,0 +1,40 @@
import { postgresAdapter } from '@payloadcms/db-postgres';
import { lexicalEditor } from '@payloadcms/richtext-lexical';
import path from 'path';
import { buildConfig } from 'payload';
import sharp from 'sharp';
import { fileURLToPath } from 'url';
import { Media } from './collections/Media';
import { Users } from './collections/Users';
const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
export default buildConfig({
admin: {
user: Users.slug,
importMap: {
importMapFile: path.resolve(
__dirname,
'app',
'(payload)',
'admin',
'importMap.js'
),
},
},
collections: [Users, Media],
editor: lexicalEditor(),
secret: process.env.CMS_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
db: postgresAdapter({
pool: {
connectionString: process.env.CMS_POSTGRES_URL || '',
},
}),
sharp,
plugins: [],
});
+1
View File
@@ -6,6 +6,7 @@
"noEmit": true, "noEmit": true,
"emitDeclarationOnly": false, "emitDeclarationOnly": false,
"esModuleInterop": true, "esModuleInterop": true,
"target": "ES2022",
"module": "esnext", "module": "esnext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
+5094 -229
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -18,6 +18,8 @@
"landing:dev": "nx serve landing", "landing:dev": "nx serve landing",
"landing:build": "nx build landing", "landing:build": "nx build landing",
"landing:prod": "cd ./dist/apps/landing && npx next start", "landing:prod": "cd ./dist/apps/landing && npx next start",
"landing:generate:importmap": "PAYLOAD_CONFIG_PATH=apps/landing/src/payload.config.ts payload generate:importmap",
"landing:generate:types": "PAYLOAD_CONFIG_PATH=apps/landing/src/payload.config.ts payload generate:types",
"ui:test": "nx test ui", "ui:test": "nx test ui",
"ui:build": "nx build ui", "ui:build": "nx build ui",
"ui:storybook": "nx storybook ui" "ui:storybook": "nx storybook ui"
@@ -26,6 +28,9 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^6.0.0", "@ant-design/icons": "^6.0.0",
"@hookform/resolvers": "^5.0.1", "@hookform/resolvers": "^5.0.1",
"@payloadcms/db-postgres": "^3.37.0",
"@payloadcms/next": "^3.37.0",
"@payloadcms/richtext-lexical": "^3.37.0",
"@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-slot": "^1.2.0",
"@tanstack/react-query": "^5.74.4", "@tanstack/react-query": "^5.74.4",
"@tanstack/react-store": "^0.7.0", "@tanstack/react-store": "^0.7.0",
@@ -34,14 +39,17 @@
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"framer-motion": "^12.9.2", "framer-motion": "^12.9.2",
"graphql": "^16.11.0",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"next": "~15.2.4", "next": "~15.2.4",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"payload": "^3.37.0",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-hook-form": "^7.55.0", "react-hook-form": "^7.55.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-router-dom": "^7.3.0", "react-router-dom": "^7.3.0",
"sharp": "^0.34.1",
"sonner": "^2.0.3", "sonner": "^2.0.3",
"tailwind-merge": "^3.0.2", "tailwind-merge": "^3.0.2",
"zod": "^3.24.2" "zod": "^3.24.2"