feat: refactor product components and add sitemap support
- Updated product components to use new IPageParams interface for better type safety. - Added next-sitemap configuration and generated sitemap.xml and robots.txt files. - Removed deprecated ProductCard component and its associated types. - Enhanced internationalization support across various pages.
This commit is contained in:
+9
-15
@@ -1,25 +1,19 @@
|
|||||||
# Use official Node.js image as the base
|
# Build stage
|
||||||
FROM node:20-alpine AS builder
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
# Copy the rest of the app
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build the Next.js app
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
# Set environment variables (optional)
|
FROM node:20-alpine AS runner
|
||||||
|
WORKDIR /app
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=5000
|
||||||
# Expose port
|
COPY --from=builder /app/.next ./.next
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|
||||||
# Start the Next.js app
|
|
||||||
CMD ["npm", "start"]
|
CMD ["npm", "start"]
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
module.exports = {
|
||||||
|
siteUrl: process.env.SITE_URL || 'http://localhost:3000',
|
||||||
|
generateRobotsTxt: true,
|
||||||
|
changefreq: 'weekly',
|
||||||
|
priority: 0.7,
|
||||||
|
sitemapSize: 7000,
|
||||||
|
// Enable i18n support
|
||||||
|
i18n: {
|
||||||
|
locales: ['en', 'fa'],
|
||||||
|
defaultLocale: 'en',
|
||||||
|
},
|
||||||
|
// Optionally, exclude certain paths (like API routes)
|
||||||
|
exclude: ['/api/*', '/_next/*'],
|
||||||
|
// If you want to include additional sitemaps (for subdirectories, etc.)
|
||||||
|
// additionalSitemaps: [
|
||||||
|
// `${process.env.SITE_URL || 'http://localhost:5000'}/custom-sitemap.xml`,
|
||||||
|
// ],
|
||||||
|
};
|
||||||
Generated
+40
@@ -28,6 +28,7 @@
|
|||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-prettier": "^5.4.1",
|
"eslint-plugin-prettier": "^5.4.1",
|
||||||
|
"next-sitemap": "^4.2.3",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.12",
|
"prettier-plugin-tailwindcss": "^0.6.12",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
@@ -59,6 +60,12 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@corex/deepmerge": {
|
||||||
|
"version": "4.0.43",
|
||||||
|
"resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-4.0.43.tgz",
|
||||||
|
"integrity": "sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@emnapi/core": {
|
"node_modules/@emnapi/core": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz",
|
||||||
@@ -4654,6 +4661,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/next-sitemap": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/iamvishnusankar/next-sitemap.git"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@corex/deepmerge": "^4.0.43",
|
||||||
|
"@next/env": "^13.4.3",
|
||||||
|
"fast-glob": "^3.2.12",
|
||||||
|
"minimist": "^1.2.8"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"next-sitemap": "bin/next-sitemap.mjs",
|
||||||
|
"next-sitemap-cjs": "bin/next-sitemap.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"next": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/next-sitemap/node_modules/@next/env": {
|
||||||
|
"version": "13.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.11.tgz",
|
||||||
|
"integrity": "sha512-fbb2C7HChgM7CemdCY+y3N1n8pcTKdqtQLbC7/EQtPdLvlMUT9JX/dBYl8MMZAtYG4uVMyPFHXckb68q/NRwqg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/next/node_modules/postcss": {
|
"node_modules/next/node_modules/postcss": {
|
||||||
"version": "8.4.31",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
|
|||||||
+3
-1
@@ -6,7 +6,8 @@
|
|||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"sitemap": "next-sitemap"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-prettier": "^5.4.1",
|
"eslint-plugin-prettier": "^5.4.1",
|
||||||
|
"next-sitemap": "^4.2.3",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.12",
|
"prettier-plugin-tailwindcss": "^0.6.12",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# *
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
# Host
|
||||||
|
Host: http://localhost:3000
|
||||||
|
|
||||||
|
# Sitemaps
|
||||||
|
Sitemap: http://localhost:3000/sitemap.xml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
</sitemapindex>
|
||||||
@@ -1,21 +1,23 @@
|
|||||||
import '@/app/globals.css';
|
import '@/app/globals.css';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { NextIntlClientProvider } from 'next-intl';
|
import { NextIntlClientProvider } from 'next-intl';
|
||||||
import type { ILayoutLocaleParams } from '@/models/layout.d';
|
import type { IPageParams } from '@/models/layout.d';
|
||||||
|
|
||||||
// export const metadata: Metadata = {
|
// export const metadata: Metadata = {
|
||||||
// title: 'Create Next App',
|
// title: 'Create Next App',
|
||||||
// description: 'Generated by create next app',
|
// description: 'Generated by create next app',
|
||||||
// };
|
// };
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function ErrorLayout({
|
||||||
children,
|
children,
|
||||||
params,
|
params,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
params: ILayoutLocaleParams;
|
params: IPageParams;
|
||||||
}>) {
|
}>) {
|
||||||
const locale = await params.locale;
|
const paramsData = await params;
|
||||||
|
const locale = paramsData.locale;
|
||||||
|
const fontClass = locale === 'fa' ? 'font-fa' : 'font-en';
|
||||||
let messages;
|
let messages;
|
||||||
try {
|
try {
|
||||||
messages = (await import(`../../../messages/${locale}.json`)).default;
|
messages = (await import(`../../../messages/${locale}.json`)).default;
|
||||||
@@ -25,7 +27,7 @@ export default async function RootLayout({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang={locale} dir={locale === 'fa' ? 'rtl' : 'ltr'}>
|
<html lang={locale} dir={locale === 'fa' ? 'rtl' : 'ltr'}>
|
||||||
<body className="min-h-svh w-full overflow-x-hidden">
|
<body className={`min-h-svh w-full overflow-x-hidden ${fontClass}`}>
|
||||||
<NextIntlClientProvider locale={locale} messages={messages}>
|
<NextIntlClientProvider locale={locale} messages={messages}>
|
||||||
{children}
|
{children}
|
||||||
</NextIntlClientProvider>
|
</NextIntlClientProvider>
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import routeFactory from '@/assets/constants/routeFactory';
|
import routeFactory from '@/assets/constants/routeFactory';
|
||||||
import images from '@/assets/images/images';
|
import images from '@/assets/images/images';
|
||||||
import Button from '@/components/uikit/button';
|
import Button from '@/components/uikit/button';
|
||||||
import { ILayoutLocaleParams } from '@/models/layout';
|
import { IPageParams } from '@/models/layout';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
export default async function NotFound({
|
export default async function NotFound({ params }: { params: IPageParams }) {
|
||||||
params,
|
const paramsData = await params;
|
||||||
}: {
|
const t = await getTranslations({ locale: paramsData.locale });
|
||||||
params: ILayoutLocaleParams;
|
const routes = routeFactory(t, paramsData.locale);
|
||||||
}) {
|
|
||||||
const t = await getTranslations({ locale: params.locale });
|
|
||||||
const routes = routeFactory(t, params.locale);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto flex h-svh flex-col items-center justify-center gap-6">
|
<div className="container mx-auto flex h-svh flex-col items-center justify-center gap-6">
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import images from '@/assets/images/images';
|
import images from '@/assets/images/images';
|
||||||
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||||
import AboutTopInfo from '@/components/pages/about/TopInfo';
|
import AboutTopInfo from '@/components/pages/about/TopInfo';
|
||||||
import { ILayoutLocaleParams } from '@/models/layout';
|
import { IPageParams } from '@/models/layout';
|
||||||
import { useTranslations } from 'next-intl';
|
|
||||||
import { setRequestLocale } from 'next-intl/server';
|
import { setRequestLocale } from 'next-intl/server';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
export default async function AboutUs({
|
export default async function AboutUs({
|
||||||
params,
|
params,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
params: ILayoutLocaleParams;
|
params: IPageParams;
|
||||||
}>) {
|
}>) {
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import images from '@/assets/images/images';
|
import images from '@/assets/images/images';
|
||||||
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||||
import PostsGrid from '@/components/pages/blog/PostsGrid';
|
import PostsGrid from '@/components/pages/blog/PostsGrid';
|
||||||
import { ILayoutLocaleParams } from '@/models/layout';
|
import { IPageParams } from '@/models/layout';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
export default async function Blog({
|
export default async function Blog({ params }: { params: IPageParams }) {
|
||||||
params,
|
const paramsData = await params;
|
||||||
}: {
|
const t = await getTranslations({ locale: paramsData.locale });
|
||||||
params: ILayoutLocaleParams;
|
|
||||||
}) {
|
|
||||||
const t = await getTranslations({ locale: params.locale });
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-col items-center justify-between">
|
<main className="flex flex-col items-center justify-between">
|
||||||
|
|||||||
@@ -5,14 +5,12 @@ import ContactForm from '@/components/pages/contact/ContactMeForm';
|
|||||||
import TouchUs from '@/components/pages/contact/TouchUs';
|
import TouchUs from '@/components/pages/contact/TouchUs';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
import { ILayoutLocaleParams } from '@/models/layout';
|
import { IPageParams } from '@/models/layout';
|
||||||
|
|
||||||
export default async function Contact({
|
export default async function Contact({ params }: { params: IPageParams }) {
|
||||||
params,
|
const { locale } = await params;
|
||||||
}: {
|
|
||||||
params: ILayoutLocaleParams;
|
const t = await getTranslations({ locale });
|
||||||
}) {
|
|
||||||
const t = await getTranslations({ locale: params.locale });
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-col items-center justify-between">
|
<main className="flex flex-col items-center justify-between">
|
||||||
|
|||||||
@@ -2,24 +2,18 @@ import Footer from '@/components/layout/footer/Footer';
|
|||||||
import '@/app/globals.css';
|
import '@/app/globals.css';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
// import { cookies } from 'next/headers';
|
|
||||||
import { NextIntlClientProvider } from 'next-intl';
|
import { NextIntlClientProvider } from 'next-intl';
|
||||||
import type { ILayoutLocaleParams } from '@/models/layout.d';
|
import type { IPageParams } from '@/models/layout.d';
|
||||||
import { cookies } from 'next/headers';
|
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
params,
|
params,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
params: ILayoutLocaleParams;
|
params: IPageParams;
|
||||||
}>) {
|
}>) {
|
||||||
const paramsData = await params;
|
const paramsData = await params;
|
||||||
const locale = paramsData.locale;
|
const locale = paramsData.locale;
|
||||||
// const cookieStore = await cookies();
|
|
||||||
|
|
||||||
// // Set locale cookie on the server
|
|
||||||
// cookieStore.set('locale', locale, { path: '/', httpOnly: false });
|
|
||||||
|
|
||||||
const fontClass = locale === 'fa' ? 'font-fa' : 'font-en';
|
const fontClass = locale === 'fa' ? 'font-fa' : 'font-en';
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,13 @@ import Header from '@/components/layout/header';
|
|||||||
import AboutTopInfo from '@/components/pages/about/TopInfo';
|
import AboutTopInfo from '@/components/pages/about/TopInfo';
|
||||||
import HomeStorySection from '@/components/pages/home/HomeStorySection';
|
import HomeStorySection from '@/components/pages/home/HomeStorySection';
|
||||||
import HomePageProducts from '@/components/pages/home/Products';
|
import HomePageProducts from '@/components/pages/home/Products';
|
||||||
import { ILayoutLocaleParams } from '@/models/layout';
|
import type { IPageParams } from '@/models/layout.d';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
interface IPageParams {
|
export default async function Home({ params }: { params: IPageParams }) {
|
||||||
params: ILayoutLocaleParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function Home({ params }: IPageParams) {
|
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
|
|
||||||
const t = await getTranslations({ locale });
|
await getTranslations({ locale });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-col items-center justify-between">
|
<main className="flex flex-col items-center justify-between">
|
||||||
|
|||||||
@@ -3,22 +3,20 @@ import images from '@/assets/images/images';
|
|||||||
import Breadcrumb from '@/components/layout/breadcrumb';
|
import Breadcrumb from '@/components/layout/breadcrumb';
|
||||||
import { IBreadcrumbItem } from '@/components/layout/breadcrumb/breadcrumb';
|
import { IBreadcrumbItem } from '@/components/layout/breadcrumb/breadcrumb';
|
||||||
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||||
import Pagination from '@/components/layout/pagination';
|
|
||||||
import ProductCategoryCardInProducts from '@/components/pages/productCategories/ProductCategoryCardInProducts';
|
import ProductCategoryCardInProducts from '@/components/pages/productCategories/ProductCategoryCardInProducts';
|
||||||
import ProductCard from '@/components/pages/products/ProductCard';
|
|
||||||
import ProductList from '@/components/pages/products/productList';
|
import ProductList from '@/components/pages/products/productList';
|
||||||
import { ILayoutLocaleParams } from '@/models/layout';
|
import { IPageParams } from '@/models/layout';
|
||||||
import { getProductCategoryById, getProducts } from '@/services/products';
|
import { getProductCategoryById, getProducts } from '@/services/products';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
interface IParams extends ILayoutLocaleParams {
|
interface IParams {
|
||||||
product_category_id: string;
|
product_category_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function ProductPage({
|
export default async function ProductPage({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
params: Promise<IParams>;
|
params: IPageParams<IParams>;
|
||||||
}) {
|
}) {
|
||||||
const { product_category_id: productCategoryId, locale } = await params;
|
const { product_category_id: productCategoryId, locale } = await params;
|
||||||
const t = await getTranslations({ locale });
|
const t = await getTranslations({ locale });
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ import images from '@/assets/images/images';
|
|||||||
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||||
import ProductsCategoryCard from '@/components/pages/productCategories/ProductCategoryCard';
|
import ProductsCategoryCard from '@/components/pages/productCategories/ProductCategoryCard';
|
||||||
import type { IProductCategoryCardProps } from '@/components/pages/productCategories/productCategoryCard.d';
|
import type { IProductCategoryCardProps } from '@/components/pages/productCategories/productCategoryCard.d';
|
||||||
import { ILayoutLocaleParams } from '@/models/layout';
|
import { IPageParams } from '@/models/layout';
|
||||||
import { getProductCategories } from '@/services/products';
|
import { getProductCategories } from '@/services/products';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
export default async function ProductsPage({
|
export default async function ProductsPage({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
params: ILayoutLocaleParams;
|
params: IPageParams;
|
||||||
}) {
|
}) {
|
||||||
const awaitedParams = await params;
|
const { locale } = await params;
|
||||||
const t = await getTranslations({ locale: awaitedParams.locale });
|
const t = await getTranslations({ locale });
|
||||||
const productCategories: IProductCategoryCardProps[] =
|
const productCategories: IProductCategoryCardProps[] =
|
||||||
await getProductCategories();
|
await getProductCategories();
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import images from '@/assets/images/images';
|
import images from '@/assets/images/images';
|
||||||
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||||
import ProjectsGrid from '@/components/pages/project/ProjectGrid';
|
import ProjectsGrid from '@/components/pages/project/ProjectGrid';
|
||||||
import { ILayoutLocaleParams } from '@/models/layout';
|
import { IPageParams } from '@/models/layout';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
export default async function Projects({
|
export default async function Projects({ params }: { params: IPageParams }) {
|
||||||
params,
|
const { locale } = await params;
|
||||||
}: {
|
const t = await getTranslations({ locale });
|
||||||
params: ILayoutLocaleParams;
|
|
||||||
}) {
|
|
||||||
const t = await getTranslations({ locale: params.locale });
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-col items-center justify-between">
|
<main className="flex flex-col items-center justify-between">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { productCategories } from '../../data';
|
import { productCategories } from '../../data';
|
||||||
import type { IProductCardProps } from '@/components/pages/products/productCard.d';
|
import type { IProductCardProps } from '@/components/pages/products/productCard/productCard.d';
|
||||||
|
|
||||||
// Helper to generate 50 products for a category
|
// Helper to generate 50 products for a category
|
||||||
export function generateProductsForCategory(
|
export function generateProductsForCategory(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export async function GET(request: NextRequest, context: any) {
|
|||||||
const cookieStore = await cookies();
|
const cookieStore = await cookies();
|
||||||
const lang = cookieStore.get('locale')?.value || 'en';
|
const lang = cookieStore.get('locale')?.value || 'en';
|
||||||
|
|
||||||
const params = context.params;
|
const params = await context.params;
|
||||||
const category = productCategories.find((cat) => cat.id === params.id);
|
const category = productCategories.find((cat) => cat.id === params.id);
|
||||||
|
|
||||||
if (!category) {
|
if (!category) {
|
||||||
|
|||||||
@@ -4,14 +4,19 @@ import { NextResponse, NextRequest } from 'next/server';
|
|||||||
import { cookies } from 'next/headers';
|
import { cookies } from 'next/headers';
|
||||||
import { productCategories } from '../data';
|
import { productCategories } from '../data';
|
||||||
|
|
||||||
export async function GET(
|
interface IContext {
|
||||||
request: NextRequest,
|
params: {
|
||||||
context: { params: { id: string } },
|
id: string;
|
||||||
) {
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IParams {}
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest, context: any) {
|
||||||
const cookieStore = await cookies();
|
const cookieStore = await cookies();
|
||||||
const lang = cookieStore.get('locale')?.value || 'en';
|
const lang = cookieStore.get('locale')?.value || 'en';
|
||||||
|
|
||||||
const params = await context.params;
|
const params = (await context).params;
|
||||||
|
|
||||||
const { id } = params;
|
const { id } = params;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { writeFile, readFile } from 'fs/promises';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const filePath = path.resolve(process.cwd(), 'contactMessages.json');
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
const { first_name, last_name, phone, email, message } = body;
|
||||||
|
|
||||||
|
if (!first_name || !last_name || !phone || !email || !message) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'مقدار تمامی فیلدها را پر کنید' },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let messages = [];
|
||||||
|
try {
|
||||||
|
const fileData = await readFile(filePath, 'utf-8');
|
||||||
|
messages = JSON.parse(fileData);
|
||||||
|
} catch {
|
||||||
|
messages = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const newMessage = {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
message,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
messages.push(newMessage);
|
||||||
|
|
||||||
|
// Write updated messages back to file
|
||||||
|
await writeFile(filePath, JSON.stringify(messages, null, 2), 'utf-8');
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Your message has been received.',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error, message: 'مشکلی پیش آمده.' },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import ProductCard from '../ProductCard';
|
|
||||||
import Pagination from '@/components/layout/pagination';
|
import Pagination from '@/components/layout/pagination';
|
||||||
import { getProducts } from '@/services/products';
|
import { getProducts } from '@/services/products';
|
||||||
import { IProductListProps } from './productList';
|
import { IProductListProps } from './productList';
|
||||||
|
import ProductCard from '../productCard';
|
||||||
|
|
||||||
export default function ProductList({
|
export default function ProductList({
|
||||||
initialProducts,
|
initialProducts,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IPagination } from '@/models/response';
|
import { IPagination } from '@/models/response';
|
||||||
import { IProductCardProps } from '../productCard';
|
import { IProductCardProps } from '../productCard/productCard.d';
|
||||||
|
|
||||||
export interface IProductListProps {
|
export interface IProductListProps {
|
||||||
initialProducts: IProductCardProps[];
|
initialProducts: IProductCardProps[];
|
||||||
|
|||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
export interface IPageParams<T = {}> extends Promise<ILayoutLocaleParams & T> {}
|
||||||
export interface ILayoutLocaleParams {
|
export interface ILayoutLocaleParams {
|
||||||
locale: TLocales;
|
locale: TLocales;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import type { IProductCategoryCardProps } from '@/components/pages/productCategories/productCategoryCard.d';
|
import type { IProductCategoryCardProps } from '@/components/pages/productCategories/productCategoryCard.d';
|
||||||
import type { IProductCardProps } from '@/components/pages/products/productCard.d';
|
import type { IProductCardProps } from '@/components/pages/products/productCard/productCard.d';
|
||||||
import api from '@/lib/axios';
|
import api from '@/lib/axios';
|
||||||
import { IResponse } from '@/models/response';
|
import { IResponse } from '@/models/response';
|
||||||
import { cookies } from 'next/headers';
|
import { cookies } from 'next/headers';
|
||||||
|
|||||||
Reference in New Issue
Block a user