diff --git a/.gitignore b/.gitignore index 5ef6a52..7f69718 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +contactMessages.json +public/sitemap.xml diff --git a/Dockerfile b/Dockerfile index 9659d11..d6e8acd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,11 @@ WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . + +ENV PORT=5000 +ENV VERCEL_PROJECT_PRODUCTION_URL=http://194.59.214.243:5000 +ENV NEXT_PUBLIC_API_BASE_URL=http://194.59.214.243:5000/api + RUN npm run build # Production stage @@ -11,6 +16,8 @@ FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production ENV PORT=5000 +ENV VERCEL_PROJECT_PRODUCTION_URL=http://194.59.214.243:5000 +ENV NEXT_PUBLIC_API_BASE_URL=http://194.59.214.243:5000/api COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json diff --git a/contactMessages.json b/contactMessagess.json similarity index 100% rename from contactMessages.json rename to contactMessagess.json diff --git a/messages/en.d.json.ts b/messages/en.d.json.ts index 6db926b..932aec4 100644 --- a/messages/en.d.json.ts +++ b/messages/en.d.json.ts @@ -57,7 +57,7 @@ declare const messages: { "com_products_category_Ghermez_title": "Ghermez", "com_products_category_Semirom_title": "Semirom", "com_products_category_Zard_title": "Zard", - "com_products_category_types_count": "count Types", + "com_products_category_types_count": "{count} types", "com_products_category_show_all_CTA": "Show Products", "pages_products_content_title": "Brick Classification: Understanding the Variety and Applications", "pages_products_content_description": "Bricks, as one of humanity's oldest and most widely used building materials, have evolved significantly throughout history. Today, they are produced and supplied in an incredibly diverse range, allowing engineers and architects to choose the most suitable brick for any project and need, from traditional and historic structures to modern and advanced buildings. To better understand this diversity, we can classify bricks based on several key criteria, each with its own specific characteristics and applications. \n 1. Classification by Manufacturing Method: From Tradition to Advanced IndustryThe method by which bricks are produced directly impacts their appearance, texture, and properties. Hand-moulded/Stock Bricks: These bricks are produced by manually pressing clay into molds. The result is a brick with a relatively rough texture, uneven edges, and a more traditional appearance, where each piece feels unique. This type of brick is often used for restoration projects or for constructing buildings with a rustic or antique style. Machine-made Bricks: These bricks are produced using industrial machinery, resulting in much higher precision in dimensions and uniformity in appearance. Machine-made bricks themselves are divided into two main categories: Perforated/Hollow Bricks: These bricks have holes, offering several advantages: they reduce building weight, improve thermal and acoustic insulation, and enhance mortar adhesion. Solid Bricks: These bricks are without holes and possess very high compressive strength. They are ideal for load-bearing structures and walls requiring maximum strength. 2. Classification by Raw Material Type and Composition: From Clay to Concrete The material from which a brick is made determines its physical and chemical properties.", diff --git a/messages/en.json b/messages/en.json index 61878d1..2160264 100644 --- a/messages/en.json +++ b/messages/en.json @@ -54,7 +54,7 @@ "com_products_category_Ghermez_title": "Ghermez", "com_products_category_Semirom_title": "Semirom", "com_products_category_Zard_title": "Zard", - "com_products_category_types_count": "count Types", + "com_products_category_types_count": "{count} types", "com_products_category_show_all_CTA": "Show Products", "pages_products_content_title": "Brick Classification: Understanding the Variety and Applications", "pages_products_content_description": "Bricks, as one of humanity's oldest and most widely used building materials, have evolved significantly throughout history. Today, they are produced and supplied in an incredibly diverse range, allowing engineers and architects to choose the most suitable brick for any project and need, from traditional and historic structures to modern and advanced buildings. To better understand this diversity, we can classify bricks based on several key criteria, each with its own specific characteristics and applications. \n 1. Classification by Manufacturing Method: From Tradition to Advanced IndustryThe method by which bricks are produced directly impacts their appearance, texture, and properties. Hand-moulded/Stock Bricks: These bricks are produced by manually pressing clay into molds. The result is a brick with a relatively rough texture, uneven edges, and a more traditional appearance, where each piece feels unique. This type of brick is often used for restoration projects or for constructing buildings with a rustic or antique style. Machine-made Bricks: These bricks are produced using industrial machinery, resulting in much higher precision in dimensions and uniformity in appearance. Machine-made bricks themselves are divided into two main categories: Perforated/Hollow Bricks: These bricks have holes, offering several advantages: they reduce building weight, improve thermal and acoustic insulation, and enhance mortar adhesion. Solid Bricks: These bricks are without holes and possess very high compressive strength. They are ideal for load-bearing structures and walls requiring maximum strength. 2. Classification by Raw Material Type and Composition: From Clay to Concrete The material from which a brick is made determines its physical and chemical properties.", diff --git a/package-lock.json b/package-lock.json index f980f9b..1cc94a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "dotenv": "^16.5.0", "eslint": "^9", "eslint-config-next": "15.3.3", "eslint-config-prettier": "^10.1.5", @@ -32,7 +33,9 @@ "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.12", "tailwindcss": "^4", - "typescript": "^5" + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.8.3" } }, "node_modules/@alloc/quick-lru": { @@ -66,6 +69,28 @@ "integrity": "sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==", "dev": true }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -1281,6 +1306,30 @@ "tailwindcss": "4.1.8" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -1865,6 +1914,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1896,6 +1957,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2325,6 +2392,12 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2481,6 +2554,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -2493,6 +2575,18 @@ "node": ">=0.10.0" } }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2926,6 +3020,18 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2935,6 +3041,18 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -4082,15 +4200,15 @@ "dev": true }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsx-ast-utils": { @@ -4424,6 +4542,12 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -5881,16 +6005,61 @@ "typescript": ">=4.8.4" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/tslib": { @@ -6075,6 +6244,12 @@ "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6193,6 +6368,15 @@ "node": ">=18" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index f22724a..b1eb18d 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "build": "next build", "start": "next start", "lint": "next lint", - "sitemap": "next-sitemap" + "sitemap": "ts-node scripts/generate-sitemap.ts", + "postbuild": "npm run sitemap" }, "dependencies": { "axios": "^1.9.0", @@ -25,6 +26,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "dotenv": "^16.5.0", "eslint": "^9", "eslint-config-next": "15.3.3", "eslint-config-prettier": "^10.1.5", @@ -34,6 +36,8 @@ "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.12", "tailwindcss": "^4", - "typescript": "^5" + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.8.3" } } diff --git a/public/sitemap.xml b/public/sitemap.xml deleted file mode 100644 index 39fed00..0000000 --- a/public/sitemap.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/public/sitemaps.xml b/public/sitemaps.xml new file mode 100644 index 0000000..889a2a8 --- /dev/null +++ b/public/sitemaps.xml @@ -0,0 +1,311 @@ + + + + + http://localhost:3000/en/ + weekly + 0.8 + + + + + http://localhost:3000/fa/ + weekly + 0.8 + + + + + http://localhost:3000/en/about-us + weekly + 0.8 + + + + + http://localhost:3000/fa/about-us + weekly + 0.8 + + + + + http://localhost:3000/en/contact-us + weekly + 0.8 + + + + + http://localhost:3000/fa/contact-us + weekly + 0.8 + + + + + http://localhost:3000/en/blog + weekly + 0.8 + + + + + http://localhost:3000/fa/blog + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories + weekly + 0.8 + + + + + http://localhost:3000/en/projects + weekly + 0.8 + + + + + http://localhost:3000/fa/projects + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/1 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/1 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/2 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/2 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/3 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/3 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/40 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/40 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/1 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/1 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/2 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/2 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/3 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/3 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/40 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/40 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/1 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/1 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/2 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/2 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/3 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/3 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/40 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/40 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/1 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/1 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/2 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/2 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/3 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/3 + weekly + 0.8 + + + + + http://localhost:3000/en/product-categories/40 + weekly + 0.8 + + + + + http://localhost:3000/fa/product-categories/40 + weekly + 0.8 + + + \ No newline at end of file diff --git a/scripts/generate-sitemap.ts b/scripts/generate-sitemap.ts new file mode 100644 index 0000000..cde09f3 --- /dev/null +++ b/scripts/generate-sitemap.ts @@ -0,0 +1,36 @@ +// scripts/generate-sitemap.ts +const fs = require('fs'); +const path = require('path'); +const http = require('http'); +require('dotenv').config(); + +const API_PATH = '/api/sitemapBuilder'; +const DEST_PATH = path.join(process.cwd(), 'public', 'sitemap.xml'); + +function fetchSitemap(): Promise { + return new Promise((resolve, reject) => { + http + .get( + `${process.env.VERCEL_PROJECT_PRODUCTION_URL}${API_PATH}`, + (res: any) => { + let data = ''; + + res.on('data', (chunk: any) => (data += chunk)); + res.on('end', () => resolve(data)); + }, + ) + .on('error', reject); + }); +} + +(async () => { + try { + const xml = await fetchSitemap(); + + fs.writeFileSync(DEST_PATH, xml, 'utf8'); + console.log('✅ Sitemap generated at public/sitemap.xml'); + } catch (err) { + console.error('❌ Failed to generate sitemap:', err); + process.exit(1); + } +})(); diff --git a/src/app/[locale]/product-categories/[product_category_id]/page.tsx b/src/app/[locale]/product-categories/[product_category_id]/page.tsx index 8f1efb5..d294677 100644 --- a/src/app/[locale]/product-categories/[product_category_id]/page.tsx +++ b/src/app/[locale]/product-categories/[product_category_id]/page.tsx @@ -78,7 +78,7 @@ export default async function ProductPage({ productCategory={productCategory} className="mt-6" /> -
+

{productCategory.title} Products

diff --git a/src/app/api/product_categories/[id]/products/data.ts b/src/app/api/product_categories/[id]/products/data.ts index e83f9be..7b76d24 100644 --- a/src/app/api/product_categories/[id]/products/data.ts +++ b/src/app/api/product_categories/[id]/products/data.ts @@ -1,5 +1,5 @@ -import { productCategories } from '../../data'; import type { IProductCardProps } from '@/components/pages/products/productCard/productCard.d'; +import { productCategoriesData } from '../../data'; // Helper to generate 50 products for a category export function generateProductsForCategory( @@ -18,7 +18,7 @@ export function generateProductsForCategory( // Map of categoryId to products export const productsByCategory: Record = Object.fromEntries( - productCategories.map((cat) => [ + productCategoriesData.map((cat) => [ cat.id, generateProductsForCategory(cat.id, cat.title), ]), diff --git a/src/app/api/product_categories/[id]/products/route.ts b/src/app/api/product_categories/[id]/products/route.ts index ac1704d..dac8508 100644 --- a/src/app/api/product_categories/[id]/products/route.ts +++ b/src/app/api/product_categories/[id]/products/route.ts @@ -1,14 +1,14 @@ import { NextResponse, NextRequest } from 'next/server'; import { cookies } from 'next/headers'; -import { productCategories } from '../../data'; import { generateProductsForCategory } from './data'; +import { productCategoriesData } from '../../data'; export async function GET(request: NextRequest, context: any) { const cookieStore = await cookies(); const lang = cookieStore.get('locale')?.value || 'en'; const params = await context.params; - const category = productCategories.find((cat) => cat.id === params.id); + const category = productCategoriesData.find((cat) => cat.id === params.id); if (!category) { return NextResponse.json({ error: 'Not found' }, { status: 404 }); diff --git a/src/app/api/product_categories/[id]/route.ts b/src/app/api/product_categories/[id]/route.ts index f005af1..a82936e 100644 --- a/src/app/api/product_categories/[id]/route.ts +++ b/src/app/api/product_categories/[id]/route.ts @@ -2,7 +2,7 @@ import { NextResponse, NextRequest } from 'next/server'; import { cookies } from 'next/headers'; -import { productCategories } from '../data'; +import { productCategoriesData } from '../data'; interface IContext { params: { @@ -20,7 +20,7 @@ export async function GET(request: NextRequest, context: any) { const { id } = params; - let category = productCategories.find((cat) => cat.id === id); + let category = productCategoriesData.find((cat) => cat.id === id); if (!category) { return NextResponse.json({ error: 'Not found' }, { status: 404 }); diff --git a/src/app/api/product_categories/data.ts b/src/app/api/product_categories/data.ts index dbdddff..83c0378 100644 --- a/src/app/api/product_categories/data.ts +++ b/src/app/api/product_categories/data.ts @@ -1,4 +1,4 @@ -export const productCategories = [ +export const productCategoriesData = [ { id: '1', title: 'Ghazaghi', diff --git a/src/app/api/product_categories/route.ts b/src/app/api/product_categories/route.ts index 7652ca3..3a39120 100644 --- a/src/app/api/product_categories/route.ts +++ b/src/app/api/product_categories/route.ts @@ -1,63 +1,36 @@ import { NextResponse } from 'next/server'; import { cookies } from 'next/headers'; -import { productCategories } from './data'; +import { APIProductCategoryEntity } from '@/models/api'; +import { productCategoriesData } from './data'; export async function GET(request: Request) { - let data = productCategories; + try { + let data = productCategoriesData as APIProductCategoryEntity[]; - const cookieStore = await cookies(); - const lang = (await cookieStore).get('lang')?.value || 'fa'; + const cookieStore = await cookies(); + const lang = (await cookieStore).get('locale')?.value || 'en'; + console.log('!!!!!!!!!!!!!!!!!!!!'); + console.log(lang); - if (lang === 'fa') { - data = productCategories.map( - ({ - id, - title, - fa_title, - icon, - typesCount, - priceTypeTitle, - fa_priceTypeTitle, - meta_description, - meta_keywords, - meta_title, - slug, - bestForTitle, - fa_bestForTitle, - description, - fa_description, - }) => { - if (lang === 'fa') { - return { - id, - icon, - typesCount, - meta_description, - meta_keywords, - meta_title, - slug, - title: fa_title || title, - priceTypeTitle: fa_priceTypeTitle || priceTypeTitle, - bestForTitle: fa_bestForTitle || bestForTitle, - description: fa_description || description, - }; - } - return { - id, - icon, - typesCount, - title, - priceTypeTitle, - bestForTitle, - meta_description, - meta_keywords, - meta_title, - slug, - description, - }; - }, + if (lang === 'fa') { + data = productCategoriesData.map((productCategory) => ({ + ...productCategory, + title: (lang && productCategory.fa_title) || productCategory.title, + priceTypeTitle: + productCategory.fa_priceTypeTitle || productCategory.priceTypeTitle, + bestForTitle: + productCategory.fa_bestForTitle || productCategory.bestForTitle, + description: + productCategory.fa_description || productCategory.description, + })); + } + + return NextResponse.json({ data }); + } catch (error) { + console.error('Error fetching product categories:', error); + return NextResponse.json( + { error: 'Failed to fetch product categories' }, + { status: 500 }, ); } - - return NextResponse.json({ data: productCategories }); } diff --git a/src/app/api/sitemapBuilder/route.ts b/src/app/api/sitemapBuilder/route.ts new file mode 100644 index 0000000..f83f744 --- /dev/null +++ b/src/app/api/sitemapBuilder/route.ts @@ -0,0 +1,56 @@ +'use server'; + +// app/(api)/sitemap/route.ts +import { NextResponse } from 'next/server'; +import { productCategoriesData } from '../product_categories/data'; +import { host } from '@/config'; +import { projects } from '../projects/data'; + +const pages = [ + '/', + '/about-us', + '/contact-us', + '/blog', + '/product-categories', + '/projects', +]; + +const locales = ['en', 'fa']; + +export async function GET() { + try { + pages.push( + ...productCategoriesData.map( + (productCategory) => `/product-categories/${productCategory.id}`, + ), + ); + pages.push(...projects.map((project) => `/projects/${project.id}`)); + + const urls = pages.flatMap((page) => + locales.map((locale) => { + const loc = `/${locale}`; + return ` + + ${host}${loc}${page} + weekly + 0.8 + + `; + }), + ); + + const xml = ` + + ${urls.join('\n')} + `; + + return new NextResponse(xml, { + headers: { + 'Content-Type': 'application/xml', + }, + }); + } catch (err) { + console.error('Sitemap generation failed:', err); + return new NextResponse('Internal Server Errorsssssss', { status: 500 }); + } +} diff --git a/src/components/layout/header/index.tsx b/src/components/layout/header/index.tsx index 2917b3d..e6a74c8 100644 --- a/src/components/layout/header/index.tsx +++ b/src/components/layout/header/index.tsx @@ -5,15 +5,20 @@ import Navbar from '../navbar'; import Button, { IconButton } from '@/components/uikit/button'; import { Icon } from '@/components/uikit/icons'; import { useTranslations } from 'next-intl'; +import routeFactory from '@/assets/constants/routeFactory'; +import { useParams } from 'next/navigation'; export default function Header() { const t = useTranslations(); + const params = useParams(); + const locale = params.locale as string; const bannerFooter = [ t('com_home_Banner_link1'), t('com_home_Banner_link2'), t('com_home_Banner_link3'), ]; + const routes = routeFactory(t, locale); return (
- +
diff --git a/src/components/pages/products/productList/index.tsx b/src/components/pages/products/productList/index.tsx index c0741af..36dd034 100644 --- a/src/components/pages/products/productList/index.tsx +++ b/src/components/pages/products/productList/index.tsx @@ -19,6 +19,10 @@ export default function ProductList({ .then((res) => { setProducts(res.data); setPagination(res.pagination); + + document + .getElementById('productList') + ?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }) .catch((error) => { console.error('Failed to fetch products:', error); diff --git a/src/components/pages/project/ProjectGrid.tsx b/src/components/pages/project/ProjectGrid.tsx index 05f7e6a..936d8a8 100644 --- a/src/components/pages/project/ProjectGrid.tsx +++ b/src/components/pages/project/ProjectGrid.tsx @@ -3,7 +3,11 @@ import Link from 'next/link'; import { IProjectCardProps } from './project'; import { getProjects } from '@/services/projects'; -export default async function ProjectsGrid({ projects }) { +export default async function ProjectsGrid({ + projects, +}: { + projects: IProjectCardProps[]; +}) { return (
{projects.map((project, index) => ( diff --git a/src/models/api.d.ts b/src/models/api.d.ts new file mode 100644 index 0000000..fe99e98 --- /dev/null +++ b/src/models/api.d.ts @@ -0,0 +1,13 @@ +export interface APIProductCategoryEntity { + id: string; + title: string; + fa_title?: string; + icon: string; + typesCount: number; + priceTypeTitle: string; + fa_priceTypeTitle?: string; + bestForTitle: string; + fa_bestForTitle?: string; + fa_description?: string; + description?: string; +} diff --git a/src/services/projects.ts b/src/services/projects.ts index c7594c8..3c33c7a 100644 --- a/src/services/projects.ts +++ b/src/services/projects.ts @@ -1,8 +1,6 @@ 'use server'; -import { IProjectCardProps } from '@/components/pages/project/project'; import api from '@/lib/axios'; -import { IResponse } from '@/models/response'; import { cookies } from 'next/headers'; export async function getProjects() { @@ -12,7 +10,6 @@ export async function getProjects() { headers: { Cookie: cookiesStore.toString(), }, - }) .then(({ data }) => Promise.resolve(data.data)) .catch((error) => Promise.reject(error));