diff --git a/package-lock.json b/package-lock.json index d40263e..ecb28ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,14 +8,21 @@ "name": "antalya-web", "version": "0.1.0", "dependencies": { + "axios": "^1.13.2", + "framer-motion": "^12.23.24", "next": "16.0.3", "react": "19.2.0", - "react-dom": "19.2.0" + "react-dom": "19.2.0", + "react-google-recaptcha": "^3.1.0", + "selenium-webdriver": "^4.38.0", + "sitemap": "^9.0.0", + "xml2js": "^0.6.2" }, "devDependencies": { "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "@types/react-google-recaptcha": "^2.1.9", "eslint": "^9", "eslint-config-next": "16.0.3", "typescript": "^5" @@ -261,6 +268,12 @@ "node": ">=6.9.0" } }, + "node_modules/@bazel/runfiles": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.5.0.tgz", + "integrity": "sha512-RzahvqTkfpY2jsDxo8YItPX+/iZ6hbiikw1YhE0bA9EKBR5Og8Pa6FHn9PO9M0zaXRVsr0GFQLKbB/0rzy9SzA==", + "license": "Apache-2.0" + }, "node_modules/@emnapi/core": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", @@ -1263,7 +1276,6 @@ "version": "20.19.25", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -1289,6 +1301,25 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-google-recaptcha": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@types/react-google-recaptcha/-/react-google-recaptcha-2.1.9.tgz", + "integrity": "sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.47.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz", @@ -1915,6 +1946,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2109,6 +2146,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2135,6 +2178,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2243,7 +2297,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2343,6 +2396,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2357,6 +2422,12 @@ "dev": true, "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2501,6 +2572,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -2528,7 +2608,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2626,7 +2705,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2636,7 +2714,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2674,7 +2751,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2687,7 +2763,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3302,6 +3377,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3318,11 +3413,53 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/framer-motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz", + "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.23", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3383,7 +3520,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3408,7 +3544,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3496,7 +3631,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3568,7 +3702,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3581,7 +3714,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -3597,7 +3729,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3623,6 +3754,15 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3633,6 +3773,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -3660,6 +3806,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4126,7 +4278,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -4205,6 +4356,18 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4249,6 +4412,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4276,7 +4448,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -4299,7 +4470,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4329,6 +4499,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4352,6 +4543,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/motion-dom": { + "version": "12.23.23", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4463,7 +4669,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4650,6 +4855,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4757,11 +4968,16 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -4769,6 +4985,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4809,6 +5031,19 @@ "node": ">=0.10.0" } }, + "node_modules/react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "license": "MIT", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-dom": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", @@ -4821,11 +5056,44 @@ "react": "^19.2.0" } }, + "node_modules/react-google-recaptcha": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz", + "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.5.0", + "react-async-script": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT" }, "node_modules/reflect.getprototypeof": { @@ -4968,6 +5236,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -5003,12 +5277,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sax": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz", + "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", + "license": "BlueOak-1.0.0" + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, + "node_modules/selenium-webdriver": { + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.38.0.tgz", + "integrity": "sha512-5/UXXFSQmn7FGQkbcpAqvfhzflUdMWtT7QqpEgkFD6Q6rDucxB5EUfzgjmr6JbUj30QodcW3mDXehzoeS/Vy5w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/SeleniumHQ" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/selenium" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@bazel/runfiles": "^6.3.1", + "jszip": "^3.10.1", + "tmp": "^0.2.5", + "ws": "^8.18.3" + }, + "engines": { + "node": ">= 20.0.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5068,6 +5373,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/sharp": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", @@ -5225,6 +5536,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sitemap": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.0.tgz", + "integrity": "sha512-J/SU27FJ+I52TcDLKZzPRRVQUMj0Pp1i/HLb2lrkU+hrMLM+qdeRjdacrNxnSW48Waa3UcEOGOdX1+0Lob7TgA==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/sitemap/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5255,6 +5600,15 @@ "node": ">= 0.4" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -5488,6 +5842,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5698,7 +6061,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/unrs-resolver": { @@ -5777,6 +6139,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5892,6 +6260,49 @@ "node": ">=0.10.0" } }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index f472a2d..07160a6 100644 --- a/package.json +++ b/package.json @@ -6,17 +6,25 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint" + "lint": "eslint", + "sitemap": "node scripts/generate-sitemap.cjs" }, "dependencies": { + "axios": "^1.13.2", + "framer-motion": "^12.23.24", "next": "16.0.3", "react": "19.2.0", - "react-dom": "19.2.0" + "react-dom": "19.2.0", + "react-google-recaptcha": "^3.1.0", + "selenium-webdriver": "^4.38.0", + "sitemap": "^9.0.0", + "xml2js": "^0.6.2" }, "devDependencies": { "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "@types/react-google-recaptcha": "^2.1.9", "eslint": "^9", "eslint-config-next": "16.0.3", "typescript": "^5" diff --git a/public/images/about-testimonial-bg.jpg b/public/images/about-testimonial-bg.jpg new file mode 100644 index 0000000..d4fedd7 Binary files /dev/null and b/public/images/about-testimonial-bg.jpg differ diff --git a/public/images/footer-bg.png b/public/images/footer-bg.png new file mode 100644 index 0000000..a78229d Binary files /dev/null and b/public/images/footer-bg.png differ diff --git a/public/images/section-bg.jpg b/public/images/section-bg.jpg new file mode 100644 index 0000000..d4fedd7 Binary files /dev/null and b/public/images/section-bg.jpg differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..3bf2014 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,6 @@ +# Allow all search engines to crawl all pages +User-agent: * +Disallow: + +# Sitemap +Sitemap: http://localhost:3000/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..5b4d207 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1 @@ +http://localhost:3000/daily1.0http://localhost:3000/about/monthly0.5http://localhost:3000/menu/weekly0.6http://localhost:3000/gallery/weekly0.6http://localhost:3000/contact/weekly0.6http://localhost:3000/blog/weekly0.6http://localhost:3000/blog/the-art-of-turkish-tea/weekly0.6http://localhost:3000/blog/secrets-of-charcoal-grilling/weekly0.6http://localhost:3000/blog/a-taste-of-sweet-legacy/weekly0.6 \ No newline at end of file diff --git a/scripts/generate-sitemap.cjs b/scripts/generate-sitemap.cjs new file mode 100644 index 0000000..afd8673 --- /dev/null +++ b/scripts/generate-sitemap.cjs @@ -0,0 +1,66 @@ +const fs = require("fs"); +const path = require("path"); +const { SitemapStream, streamToPromise } = require("sitemap"); +const { pathToFileURL } = require("url"); + +const hostname = "http://localhost:3000"; // localhost for development +const addTrailingSlash = true; // ✅ Set this true if your Next.js uses trailingSlash: true + +// Utility to format URLs based on config +const shouldAddSlash = (url) => { + if (url === "/") return false; + if (/\.[a-z0-9]{2,6}(\?.*)?$/i.test(url)) return false; + return true; +}; + +const formatUrl = (url) => { + if (!url.startsWith("/")) url = "/" + url; + if (addTrailingSlash && shouldAddSlash(url) && !url.endsWith("/")) { + return url + "/"; + } + if (!addTrailingSlash && url.endsWith("/") && url !== "/") { + return url.slice(0, -1); + } + return url; +}; + +// ✅ Static pages +const staticLinks = [ + { url: '/', changefreq: 'daily', priority: 1.0 }, + { url: '/about/', changefreq: 'monthly', priority: 0.5 }, + { url: '/menu/', changefreq: 'weekly', priority: 0.6 }, + { url: '/gallery/', changefreq: 'weekly', priority: 0.6 }, + { url: '/contact/', changefreq: 'weekly', priority: 0.6 }, + { url: '/blog/', changefreq: 'weekly', priority: 0.6 }, +]; + +// ✅ Dynamic blog posts (only URLs, no extra metadata needed beyond static format) +const blogPosts = [ + { slug: '/blog/the-art-of-turkish-tea' }, + { slug: '/blog/secrets-of-charcoal-grilling/' }, + { slug: '/blog/a-taste-of-sweet-legacy/' }, +]; + +const blogLinks = blogPosts.map(p => ({ url: p.slug, changefreq: 'weekly', priority: 0.6 })); + +const allLinks = [...staticLinks, ...blogLinks].map(l => ({ ...l, url: formatUrl(l.url) })); + +async function generateSitemap() { + try { + const sitemap = new SitemapStream({ hostname }); + const writeStream = fs.createWriteStream(path.resolve(__dirname, "../public/sitemap.xml")); + sitemap.pipe(writeStream); + console.log('📦 Writing URLs to sitemap:'); + allLinks.forEach(link => { + console.log(' -', hostname + link.url); + sitemap.write(link); + }); + sitemap.end(); + await streamToPromise(sitemap); + console.log('✅ sitemap.xml created successfully!'); + } catch (error) { + console.error('❌ Error creating sitemap.xml:', error); + } +} + +generateSitemap(); diff --git a/scripts/image_alt_issues.csv b/scripts/image_alt_issues.csv new file mode 100644 index 0000000..eed7e97 --- /dev/null +++ b/scripts/image_alt_issues.csv @@ -0,0 +1,10 @@ +Page URL,Image Src,Alt Text,Issue Type +"http://localhost:3000/","","Antalya Restaurant","Duplicate Alt (2 times)" +"http://localhost:3000/about/","","Antalya Restaurant","Duplicate Alt (3 times)" +"http://localhost:3000/menu/","","Antalya Restaurant","Duplicate Alt (3 times)" +"http://localhost:3000/gallery/","","Antalya Restaurant","Duplicate Alt (3 times)" +"http://localhost:3000/contact/","","Antalya Restaurant","Duplicate Alt (3 times)" +"http://localhost:3000/blog/","","Antalya Restaurant","Duplicate Alt (3 times)" +"http://localhost:3000/blog/the-art-of-turkish-tea/","","Antalya Restaurant","Duplicate Alt (3 times)" +"http://localhost:3000/blog/secrets-of-charcoal-grilling/","","Antalya Restaurant","Duplicate Alt (3 times)" +"http://localhost:3000/blog/a-taste-of-sweet-legacy/","","Antalya Restaurant","Duplicate Alt (3 times)" diff --git a/scripts/seo-test-selenium.cjs b/scripts/seo-test-selenium.cjs new file mode 100644 index 0000000..dd1c342 --- /dev/null +++ b/scripts/seo-test-selenium.cjs @@ -0,0 +1,244 @@ +const { Builder, By } = require("selenium-webdriver"); +const chrome = require("selenium-webdriver/chrome"); +const axios = require("axios"); +const xml2js = require("xml2js"); +const fs = require("fs"); +const path = require("path"); + +// CSV file for Image Alt issues +const csvPath = path.join(__dirname, "image_alt_issues.csv"); +fs.writeFileSync(csvPath, "Page URL,Image Src,Alt Text,Issue Type\n", "utf8"); + +// ========================== +// 1️⃣ Fetch URLs from sitemap.xml +// ========================== +async function getUrlsFromSitemap(sitemapUrl) { + try { + const res = await axios.get(sitemapUrl); + const parsed = await xml2js.parseStringPromise(res.data); + return parsed.urlset.url.map((u) => u.loc[0]); + } catch (err) { + console.error("❌ Failed to load sitemap:", err.message); + return []; + } +} + +// ========================== +// 2️⃣ Check HTTP Status +// ========================== +async function checkLinkStatus(url) { + try { + const res = await axios.get(url, { + timeout: 10000, + validateStatus: () => true, + }); + if (res.status === 200 && res.data.toLowerCase().includes("page not found")) { + return "Soft 404"; + } + return res.status; + } catch (err) { + return err.response ? err.response.status : "❌ No Response"; + } +} + +// ========================== +// 3️⃣ Main SEO + Accessibility + Image Alt Audit +// ========================== +async function checkSEO(url, siteDomain) { + const options = new chrome.Options(); + options.addArguments("--headless", "--no-sandbox", "--disable-gpu"); + const driver = await new Builder() + .forBrowser("chrome") + .setChromeOptions(options) + .build(); + + try { + const pageStatus = await checkLinkStatus(url); + if (pageStatus === 404 || pageStatus === "Soft 404") { + console.log(`\n🚫 ${url} → ❌ Page not found (${pageStatus})`); + return; + } + + await driver.get(url); + const pageSource = await driver.getPageSource(); + + // Basic SEO Elements + const title = await driver.getTitle(); + const descElem = await driver.findElements(By.css('meta[name="description"]')); + const canonicalElem = await driver.findElements(By.css('link[rel="canonical"]')); + const robotsElem = await driver.findElements(By.css('meta[name="robots"]')); + const viewportElem = await driver.findElements(By.css('meta[name="viewport"]')); + const charset = await driver.findElements(By.css('meta[charset]')); + const htmlTag = await driver.findElement(By.css("html")); + const langAttr = await htmlTag.getAttribute("lang").catch(() => ""); + const h1Tags = await driver.findElements(By.css("h1")); + const h2Tags = await driver.findElements(By.css("h2")); + + // Meta Description + let descContent = descElem.length > 0 ? await descElem[0].getAttribute("content") : ""; + const descLength = descContent.length; + const descStatus = + descLength === 0 + ? "❌ Missing" + : descLength < 50 + ? `⚠️ Too short (${descLength})` + : descLength > 160 + ? `⚠️ Too long (${descLength})` + : "✅ Perfect"; + + // Title length check + const titleLength = title.length; + const titleStatus = + titleLength === 0 + ? "❌ Missing" + : titleLength < 30 + ? `⚠️ Too short (${titleLength})` + : titleLength > 65 + ? `⚠️ Too long (${titleLength})` + : "✅ Perfect"; + + // Canonical + const canonicalURL = canonicalElem.length > 0 ? await canonicalElem[0].getAttribute("href") : "❌ Missing"; + + // 🖼️ Image Accessibility Audit + const imgs = await driver.findElements(By.css("img")); + let missingAlt = 0; + let emptyAlt = 0; + let duplicateAlt = []; + const altTextMap = new Map(); + + for (const img of imgs) { + const src = await img.getAttribute("src"); + const alt = (await img.getAttribute("alt"))?.trim() ?? null; + + if (alt === null) { + missingAlt++; + fs.appendFileSync(csvPath, `"${url}","${src}","","Missing Alt"\n`, "utf8"); + continue; + } + + if (alt === "") { + emptyAlt++; + fs.appendFileSync(csvPath, `"${url}","${src}","(empty)","Empty Alt"\n`, "utf8"); + } + + if (altTextMap.has(alt)) { + altTextMap.set(alt, altTextMap.get(alt) + 1); + } else { + altTextMap.set(alt, 1); + } + } + + for (const [altText, count] of altTextMap.entries()) { + if (altText && count > 1) { + duplicateAlt.push({ altText, count }); + fs.appendFileSync( + csvPath, + `"${url}","","${altText}","Duplicate Alt (${count} times)"\n`, + "utf8" + ); + } + } + + // Detect tracking & schema tags + const hasGTM = pageSource.includes("googletagmanager.com/gtm.js"); + const hasClarity = pageSource.includes("clarity.ms/tag"); + const hasFBPixel = pageSource.includes("fbevents.js") || pageSource.includes("fbq("); + const hasAnalytics = pageSource.includes("www.googletagmanager.com/gtag/js"); + + const ogTags = await driver.findElements(By.css("meta[property^='og:']")); + const twitterTags = await driver.findElements(By.css("meta[name^='twitter:']")); + const schemaScripts = await driver.findElements(By.css('script[type="application/ld+json"]')); + + // Links check + const anchorTags = await driver.findElements(By.css("a[href]")); + const brokenLinks = []; + for (const a of anchorTags) { + const href = await a.getAttribute("href"); + if (!href || href.startsWith("#") || href.startsWith("mailto:")) continue; + + const fullUrl = href.startsWith("http") + ? href + : `${siteDomain}${href.startsWith("/") ? href : `/${href}`}`; + + if (fullUrl.includes(siteDomain)) { + const status = await checkLinkStatus(fullUrl); + if (status === 404 || status === "Soft 404" || status === "❌ No Response") { + brokenLinks.push({ link: fullUrl, status }); + } + } + } + + // Lazy loading check + const mediaElems = await driver.findElements(By.css("img, video, iframe")); + const lazyLoadCount = await Promise.all( + mediaElems.map(async (el) => { + const loading = await el.getAttribute("loading"); + return loading === "lazy"; + }) + ); + const lazyLoaded = lazyLoadCount.filter((v) => v).length; + + // Console Summary + console.log(`\n🔍 Checking: ${url}`); + console.log("-------------------------------------------"); + console.log("Title:", titleStatus); + console.log("Meta Description:", descStatus); + console.log("Canonical URL:", canonicalURL); + console.log("Meta Robots:", robotsElem.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Viewport:", viewportElem.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Charset:", charset.length > 0 ? "✅ Found" : "❌ Missing"); + console.log("HTML lang:", langAttr ? `✅ ${langAttr}` : "⚠️ Missing"); + console.log("H1 Tags:", h1Tags.length > 0 ? `✅ ${h1Tags.length}` : "❌ Missing"); + console.log("H2 Tags:", h2Tags.length > 0 ? `ℹ️ ${h2Tags.length}` : "⚠️ None"); + console.log("Images:", imgs.length); + console.log("Missing Alt:", missingAlt > 0 ? `❌ ${missingAlt}` : "✅ None"); + console.log("Empty Alt:", emptyAlt > 0 ? `⚠️ ${emptyAlt}` : "✅ None"); + console.log("Duplicate Alt:", duplicateAlt.length > 0 ? `⚠️ ${duplicateAlt.length}` : "✅ None"); + console.log("Lazy Loaded Images:", lazyLoaded > 0 ? `✅ ${lazyLoaded}` : "⚠️ None"); + console.log("Open Graph Tags:", ogTags.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Twitter Tags:", twitterTags.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Schema Markup:", schemaScripts.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Google Analytics:", hasAnalytics ? "✅ Found" : "⚠️ Missing"); + console.log("GTM:", hasGTM ? "✅ Found" : "⚠️ Missing"); + console.log("Clarity:", hasClarity ? "✅ Found" : "⚠️ Missing"); + console.log("Facebook Pixel:", hasFBPixel ? "✅ Found" : "⚠️ Missing"); + + if (brokenLinks.length > 0) { + console.log("\n❌ Broken Links:"); + brokenLinks.forEach((b) => console.log(` → ${b.link} [${b.status}]`)); + } else { + console.log("✅ No broken links found."); + } + } catch (err) { + console.error(`❌ Error on ${url}:`, err.message); + } finally { + await driver.quit(); + } +} + +// ========================== +// 4️⃣ Run Full Site Audit +// ========================== +(async () => { + const sitemapUrl = "http://localhost:3000/sitemap.xml"; + const siteDomain = "http://localhost:3000"; + + console.log("📄 Fetching URLs from sitemap..."); + const urls = await getUrlsFromSitemap(sitemapUrl); + + if (urls.length === 0) { + console.error("❌ No URLs found in sitemap."); + return; + } + + console.log(`✅ Found ${urls.length} URLs in sitemap.`); + console.log("🚀 Starting Full SEO + Accessibility + Broken Link Audit..."); + + for (const url of urls) { + await checkSEO(url, siteDomain); + } + + console.log("\n✅ Full SEO Audit Completed!"); + console.log(`📁 CSV Report: ${csvPath}`); +})(); diff --git a/src/app/about/about.module.css b/src/app/about/about.module.css new file mode 100644 index 0000000..dfa8fa8 --- /dev/null +++ b/src/app/about/about.module.css @@ -0,0 +1,575 @@ +.main { + background-color: var(--color-dark); + color: var(--color-text-light); + min-height: 100vh; +} + +.hero { + padding: 10rem 2rem; + background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url('/images/hero-1.png'); + background-size: cover; + background-position: center; + display: flex; + align-items: center; + justify-content: center; + text-align: center; +} + +.heroContent { + max-width: 800px; +} + +.heroTitle { + font-family: var(--font-playfair); + font-size: 4rem; + color: var(--color-gold); + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.breadcrumb { + font-size: 1.1rem; + color: #C5A059; + font-family: var(--font-lato); +} + +.breadcrumb a { + color: #fff; + transition: color 0.3s; +} + +.breadcrumb a:hover { + color: var(--color-gold); +} + +.section { + padding: 80px 20px; + background-color: #f5e6d3; +} + +.featuresSection { + background-color: #3a0c08; + background-image: url('/images/section-bg.jpg'); + background-size: cover; + background-position: center; +} + +.testimonialsSection { + background-color: #3a0c08; + background-image: url('/images/about-testimonial-bg.jpg'); + background-size: cover; + background-position: center; + background-repeat: repeat; +} + +.container { + max-width: 1200px; + margin: 0 auto; + display: flex; + align-items: center; + gap: 4rem; + flex-wrap: wrap; +} + +.textBlock { + flex: 1; + min-width: 300px; +} + +.imageBlock { + flex: 1; + min-width: 300px; + display: flex; + justify-content: center; +} + +.sectionTitle { + font-family: var(--font-playfair); + font-size: 3rem; + color: #5d4037; + margin-bottom: 1.5rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.sectionTitleCenter { + font-family: var(--font-playfair); + font-size: 3.5rem; + color: #C5A059; + margin-bottom: 3rem; + text-align: center; + text-transform: uppercase; + letter-spacing: 2px; +} + +.text { + font-size: 1.1rem; + line-height: 1.8; + color: #5d4037; + margin-bottom: 1.5rem; + opacity: 0.9; + font-family: var(--font-lato); +} + +.image { + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + max-width: 100%; + height: auto; + border: 4px solid #C5A059; +} + +.menuButton { + display: inline-block; + padding: 15px 40px; + border: 2px solid #C5A059; + color: #C5A059; + font-family: var(--font-lato); + font-size: 1.1rem; + text-transform: uppercase; + text-decoration: none; + transition: all 0.3s ease; + background: transparent; + letter-spacing: 1px; + margin-top: 1rem; +} + +.menuButton:hover { + background-color: #C5A059; + color: #000; +} + +/* Features Section */ +.featuresGrid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 3rem; + max-width: 1200px; + margin: 0 auto; +} + +.featureCard { + background-color: #F5E6D3; + padding: 2.5rem 2rem; + text-align: center; + border: 4px solid #C5A059; + transition: transform 0.3s ease; +} + +.featureCard:hover { + transform: translateY(-10px); +} + +.featureImageWrapper { + position: relative; + width: 100%; + height: 200px; + margin-bottom: 1.5rem; + border-radius: 8px; + overflow: hidden; + border: 2px solid #C5A059; +} + +.featureImage { + object-fit: cover; +} + +.featureTitle { + font-family: var(--font-playfair); + font-size: 1.8rem; + color: #3e2723; + margin-bottom: 1rem; + font-weight: 600; +} + +.featureDesc { + font-family: var(--font-lato); + font-size: 1rem; + color: #5d4037; + line-height: 1.6; +} + +/* Testimonials Slider */ +.testimonialSlider { + display: flex; + align-items: center; + justify-content: center; + gap: 2rem; + max-width: 900px; + margin: 0 auto; + position: relative; +} + +.sliderBtn { + background: transparent; + border: 2px solid #C5A059; + color: #C5A059; + font-size: 2.5rem; + width: 50px; + height: 50px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s; + flex-shrink: 0; +} + +.sliderBtn:hover { + background-color: #C5A059; + color: #000; +} + +.testimonialCard { + background-color: #F5E6D3; + padding: 3rem 2.5rem; + border: 4px solid #C5A059; + display: flex; + flex-direction: column; + gap: 2rem; + flex: 1; + min-height: 300px; +} + +.testimonialText { + font-family: var(--font-lato); + font-size: 1.3rem; + color: #5d4037; + line-height: 1.8; + font-style: italic; + text-align: center; +} + +.testimonialAuthor { + display: flex; + align-items: center; + justify-content: center; + gap: 1.5rem; +} + +.authorImageWrapper { + position: relative; + width: 70px; + height: 70px; + border-radius: 50%; + overflow: hidden; + border: 3px solid #C5A059; + flex-shrink: 0; +} + +.authorImage { + object-fit: cover; +} + +.authorInfo { + display: flex; + flex-direction: column; + text-align: left; +} + +.authorName { + font-family: var(--font-playfair); + font-size: 1.2rem; + color: #3e2723; + font-weight: 600; +} + +.authorRole { + font-family: var(--font-lato); + font-size: 0.95rem; + color: #836839; +} + +.sliderDots { + display: flex; + justify-content: center; + gap: 1rem; + margin-top: 2rem; +} + +.dot { + width: 12px; + height: 12px; + border-radius: 50%; + background: transparent; + border: 2px solid #C5A059; + cursor: pointer; + transition: all 0.3s; + padding: 0; +} + +.dot:hover, +.activeDot { + background-color: #C5A059; +} + +/* Call to Action Section */ +.ctaSection { + padding: 5rem 2rem; + background-size: cover; + background-position: center; + position: relative; + text-align: center; +} + +.ctaOverlay { + position: relative; + z-index: 2; + background: rgba(58, 12, 8, 0.9); + padding: 4rem 2rem; + max-width: 800px; + margin: 0 auto; + border: 4px solid #C5A059; +} + +.ctaTitle { + font-family: var(--font-playfair); + font-size: 3rem; + color: #C5A059; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.ctaSubtitle { + font-family: var(--font-lato); + font-size: 1.3rem; + color: #F5E6D3; + margin-bottom: 2rem; +} + +.ctaButton { + display: inline-block; + padding: 15px 40px; + border: 2px solid #C5A059; + color: #C5A059; + font-family: var(--font-lato); + font-size: 1.1rem; + text-transform: uppercase; + text-decoration: none; + transition: all 0.3s ease; + background: transparent; + letter-spacing: 1px; +} + +.ctaButton:hover { + background-color: #C5A059; + color: #000; +} + +@media (max-width: 1024px) { + .featuresGrid { + grid-template-columns: 1fr; + } + + .testimonialSlider { + flex-direction: column; + } + + .sliderBtn { + order: 2; + } + + .testimonialCard { + order: 1; + } + + .faqContainer { + grid-template-columns: 1fr; + gap: 3rem; + } + + .faqImageBlock { + min-height: 400px; + } +} + +@media (max-width: 768px) { + .heroTitle { + font-size: 3rem; + } + + .container { + flex-direction: column; + gap: 2rem; + } + + .section { + padding: 60px 20px; + } + + .sectionTitleCenter { + font-size: 2rem; + } + + .ctaTitle { + font-size: 2rem; + } + + .sliderBtn { + width: 40px; + height: 40px; + font-size: 2rem; + } + + .faqSection { + padding: 60px 20px; + } + + .faqTitle { + font-size: 2rem; + } + + .faqSubtitle { + font-size: 1rem; + } + + .faqQuestion { + padding: 1.2rem 1.5rem; + font-size: 1rem; + } + + .faqAnswer { + padding: 1.2rem 1.5rem; + } + + .faqImageBlock { + min-height: 300px; + } +} + +/* FAQ Section Styles */ +.faqSection { + padding: 80px 20px; + background-color: #F5E6D3; +} + +.faqContainer { + max-width: 1400px; + margin: 0 auto; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: start; +} + +.faqImageBlock { + position: relative; + width: 100%; + height: 100%; + min-height: 600px; +} + +.faqImage { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 12px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.faqContentBlock { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.faqTitle { + font-family: var(--font-playfair); + font-size: 2.5rem; + color: #5d4037; + margin-bottom: 0.5rem; +} + +.faqSubtitle { + font-family: var(--font-lato); + font-size: 1.1rem; + color: #836839; + line-height: 1.6; + margin-bottom: 1rem; +} + +.faqAccordion { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.faqItem { + background: #fff; + border: 2px solid #C5A059; + border-radius: 8px; + overflow: hidden; + transition: all 0.3s ease; +} + +.faqItem:hover { + box-shadow: 0 4px 12px rgba(197, 160, 89, 0.2); +} + +.faqQuestion { + width: 100%; + padding: 1.5rem 2rem; + background: transparent; + border: none; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + font-family: var(--font-lato); + font-size: 1.1rem; + font-weight: 600; + color: #5d4037; + text-align: left; + transition: all 0.3s ease; +} + +.faqQuestion:hover { + background-color: #faf6f0; +} + +.faqQuestionActive { + background-color: #C5A059; + color: #fff; +} + +.faqIcon { + font-size: 1.5rem; + font-weight: bold; + color: #C5A059; + transition: transform 0.3s ease; + flex-shrink: 0; + margin-left: 1rem; +} + +.faqQuestionActive .faqIcon { + color: #fff; + transform: rotate(180deg); +} + +.faqAnswer { + padding: 1.5rem 2rem; + border-top: 1px solid #e0d5c7; + animation: slideDown 0.3s ease; +} + +.faqAnswer p { + font-family: var(--font-lato); + font-size: 1rem; + color: #5d4037; + line-height: 1.8; + margin: 0; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} \ No newline at end of file diff --git a/src/app/about/about.module.css.backup b/src/app/about/about.module.css.backup new file mode 100644 index 0000000..b0e050e --- /dev/null +++ b/src/app/about/about.module.css.backup @@ -0,0 +1,770 @@ +.main { + background-color: var(--color-dark); + color: var(--color-text-light); + min-height: 100vh; +} + +.hero { + padding: 10rem 2rem; + background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url('/images/hero-1.png'); + background-size: cover; + background-position: center; + display: flex; + align-items: center; + justify-content: center; + text-align: center; +} + +.heroContent { + max-width: 800px; +} + +.heroTitle { + font-family: var(--font-playfair); + font-size: 4rem; + color: var(--color-gold); + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.breadcrumb { + font-size: 1.1rem; + color: #C5A059; + font-family: var(--font-lato); +} + +.breadcrumb a { + color: #fff; + transition: color 0.3s; +} + +.breadcrumb a:hover { + color: var(--color-gold); +} + +.section { + padding: 80px 20px; + background-color: #0a0a0a; +} + +.featuresSection { + background-color: #3a0c08; + background-image: url('/images/section-bg.jpg'); + background-size: cover; + background-position: center; +} + +.testimonialsSection { + background-color: #0a0a0a; +} + +.container { + max-width: 1200px; + margin: 0 auto; + display: flex; + align-items: center; + gap: 4rem; + flex-wrap: wrap; +} + +.textBlock { + flex: 1; + min-width: 300px; +} + +.imageBlock { + flex: 1; + min-width: 300px; + display: flex; + justify-content: center; +} + +.sectionTitle { + font-family: var(--font-playfair); + font-size: 3rem; + color: #C5A059; + margin-bottom: 1.5rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.sectionTitleCenter { + font-family: var(--font-playfair); + font-size: 3.5rem; + color: #C5A059; + margin-bottom: 3rem; + text-align: center; + text-transform: uppercase; + letter-spacing: 2px; +} + +.text { + font-size: 1.1rem; + line-height: 1.8; + color: #C5A059; + margin-bottom: 1.5rem; + opacity: 0.9; + font-family: var(--font-lato); +} + +.image { + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + max-width: 100%; + height: auto; + border: 4px solid #C5A059; +} + +.menuButton { + display: inline-block; + padding: 15px 40px; + border: 2px solid #C5A059; + color: #C5A059; + font-family: var(--font-lato); + font-size: 1.1rem; + text-transform: uppercase; + text-decoration: none; + transition: all 0.3s ease; + background: transparent; + letter-spacing: 1px; + margin-top: 1rem; +} + +.menuButton:hover { + background-color: #C5A059; + color: #000; +} + +/* Features Section */ +.featuresGrid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 3rem; + max-width: 1200px; + margin: 0 auto; +} + +.featureCard { + background-color: #F5E6D3; + padding: 2.5rem 2rem; + text-align: center; + border: 4px solid #C5A059; + transition: transform 0.3s ease; +} + +.featureCard:hover { + transform: translateY(-10px); +} + +.featureImageWrapper { + position: relative; + width: 100%; + height: 200px; + margin-bottom: 1.5rem; + border-radius: 8px; + overflow: hidden; + border: 2px solid #C5A059; +} + +.featureImage { + object-fit: cover; +} + +.featureTitle { + font-family: var(--font-playfair); + font-size: 1.8rem; + color: #3e2723; + margin-bottom: 1rem; + font-weight: 600; +} + +.featureDesc { + font-family: var(--font-lato); + font-size: 1rem; + color: #5d4037; + line-height: 1.6; +} + +/* Testimonials Slider */ +.testimonialSlider { + display: flex; + align-items: center; + justify-content: center; + gap: 2rem; + max-width: 900px; + margin: 0 auto; + position: relative; +} + +.sliderBtn { + background: transparent; + border: 2px solid #C5A059; + color: #C5A059; + font-size: 2.5rem; + width: 50px; + height: 50px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s; + flex-shrink: 0; +} + +.sliderBtn:hover { + background-color: #C5A059; + color: #000; +} + +.testimonialCard { + background-color: #F5E6D3; + padding: 3rem 2.5rem; + border: 4px solid #C5A059; + display: flex; + flex-direction: column; + gap: 2rem; + flex: 1; + min-height: 300px; +} + +.testimonialText { + font-family: var(--font-lato); + font-size: 1.3rem; + color: #5d4037; + line-height: 1.8; + font-style: italic; + text-align: center; +} + +.testimonialAuthor { + display: flex; + align-items: center; + justify-content: center; + gap: 1.5rem; +} + +.authorImageWrapper { + position: relative; + width: 70px; + height: 70px; + border-radius: 50%; + overflow: hidden; + border: 3px solid #C5A059; + flex-shrink: 0; +} + +.authorImage { + object-fit: cover; +} + +.authorInfo { + display: flex; + flex-direction: column; + text-align: left; +} + +.authorName { + font-family: var(--font-playfair); + font-size: 1.2rem; + color: #3e2723; + font-weight: 600; +} + +.authorRole { + font-family: var(--font-lato); + font-size: 0.95rem; + color: #836839; +} + +.sliderDots { + display: flex; + justify-content: center; + gap: 1rem; + margin-top: 2rem; +} + +.dot { + width: 12px; + height: 12px; + border-radius: 50%; + background: transparent; + border: 2px solid #C5A059; + cursor: pointer; + transition: all 0.3s; + padding: 0; +} + +.dot:hover, +.activeDot { + background-color: #C5A059; +} + +/* Call to Action Section */ +.ctaSection { + padding: 5rem 2rem; + background-size: cover; + background-position: center; + position: relative; + text-align: center; +} + +.ctaOverlay { + position: relative; + z-index: 2; + background: rgba(58, 12, 8, 0.9); + padding: 4rem 2rem; + max-width: 800px; + margin: 0 auto; + border: 4px solid #C5A059; +} + +.ctaTitle { + font-family: var(--font-playfair); + font-size: 3rem; + color: #C5A059; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.ctaSubtitle { + font-family: var(--font-lato); + font-size: 1.3rem; + color: #F5E6D3; + margin-bottom: 2rem; +} + +.ctaButton { + display: inline-block; + padding: 15px 40px; + border: 2px solid #C5A059; + color: #C5A059; + font-family: var(--font-lato); + font-size: 1.1rem; + text-transform: uppercase; + text-decoration: none; + transition: all 0.3s ease; + background: transparent; + letter-spacing: 1px; +} + +.ctaButton:hover { + background-color: #C5A059; + color: #000; +} + +@media (max-width: 1024px) { + .featuresGrid { + grid-template-columns: 1fr; + } + + .testimonialSlider { + flex-direction: column; + } + + .sliderBtn { + order: 2; + } + + font-family: var(--font-playfair); + font-size: 1.8rem; + color: #3e2723; + margin-bottom: 1rem; + font-weight: 600; +} + +.featureDesc { + font-family: var(--font-lato); + font-size: 1rem; + color: #5d4037; + line-height: 1.6; +} + +/* Testimonials Slider */ +.testimonialSlider { + display: flex; + align-items: center; + justify-content: center; + gap: 2rem; + max-width: 900px; + margin: 0 auto; + position: relative; +} + +.sliderBtn { + background: transparent; + border: 2px solid #C5A059; + color: #C5A059; + font-size: 2.5rem; + width: 50px; + height: 50px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s; + flex-shrink: 0; +} + +.sliderBtn:hover { + background-color: #C5A059; + color: #000; +} + +.testimonialCard { + background-color: #F5E6D3; + padding: 3rem 2.5rem; + border: 4px solid #C5A059; + display: flex; + flex-direction: column; + gap: 2rem; + flex: 1; + min-height: 300px; +} + +.testimonialText { + font-family: var(--font-lato); + font-size: 1.3rem; + color: #5d4037; + line-height: 1.8; + font-style: italic; + text-align: center; +} + +.testimonialAuthor { + display: flex; + align-items: center; + justify-content: center; + gap: 1.5rem; +} + +.authorImageWrapper { + position: relative; + width: 70px; + height: 70px; + border-radius: 50%; + overflow: hidden; + border: 3px solid #C5A059; + flex-shrink: 0; +} + +.authorImage { + object-fit: cover; +} + +.authorInfo { + display: flex; + flex-direction: column; + text-align: left; +} + +.authorName { + font-family: var(--font-playfair); + font-size: 1.2rem; + color: #3e2723; + font-weight: 600; +} + +.authorRole { + font-family: var(--font-lato); + font-size: 0.95rem; + color: #836839; +} + +.sliderDots { + display: flex; + justify-content: center; + gap: 1rem; + margin-top: 2rem; +} + +.dot { + width: 12px; + height: 12px; + border-radius: 50%; + background: transparent; + border: 2px solid #C5A059; + cursor: pointer; + transition: all 0.3s; + padding: 0; +} + +.dot:hover, +.activeDot { + background-color: #C5A059; +} + +/* Call to Action Section */ +.ctaSection { + padding: 5rem 2rem; + background-size: cover; + background-position: center; + position: relative; + text-align: center; +} + +.ctaOverlay { + position: relative; + z-index: 2; + background: rgba(58, 12, 8, 0.9); + padding: 4rem 2rem; + max-width: 800px; + margin: 0 auto; + border: 4px solid #C5A059; +} + +.ctaTitle { + font-family: var(--font-playfair); + font-size: 3rem; + color: #C5A059; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.ctaSubtitle { + font-family: var(--font-lato); + font-size: 1.3rem; + color: #F5E6D3; + margin-bottom: 2rem; +} + +.ctaButton { + display: inline-block; + padding: 15px 40px; + border: 2px solid #C5A059; + color: #C5A059; + font-family: var(--font-lato); + font-size: 1.1rem; + text-transform: uppercase; + text-decoration: none; + transition: all 0.3s ease; + background: transparent; + letter-spacing: 1px; +} + +.ctaButton:hover { + background-color: #C5A059; + color: #000; +} + +@media (max-width: 1024px) { + .featuresGrid { + grid-template-columns: 1fr; + } + + .testimonialSlider { + flex-direction: column; + } + + .sliderBtn { + order: 2; + } + + .testimonialCard { + order: 1; + } +} + +@media (max-width: 768px) { + .heroTitle { + font-size: 3rem; + } + + .container { + flex-direction: column; + gap: 2rem; + } + + .section { + padding: 60px 20px; + } + + .sectionTitleCenter { + font-size: 2rem; + } + + .ctaTitle { + font-size: 2rem; + } + + .sliderBtn { + width: 40px; + height: 40px; + font-size: 2rem; + } +} + +/* FAQ Section Styles */ +.faqSection { + padding: 80px 20px; + background-color: #F5E6D3; +} + +.faqContainer { + max-width: 1400px; + margin: 0 auto; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: start; +} + +.faqImageBlock { + position: relative; + width: 100%; + height: 100%; + min-height: 600px; +} + +.faqImage { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 12px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.faqContentBlock { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.faqTitle { + font-family: var(--font-playfair); + font-size: 2.5rem; + color: #5d4037; + margin-bottom: 0.5rem; +} + +.faqSubtitle { + font-family: var(--font-lato); + font-size: 1.1rem; + color: #836839; + line-height: 1.6; + margin-bottom: 1rem; +} + +.faqAccordion { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.faqItem { + background: #fff; + border: 2px solid #C5A059; + border-radius: 8px; + overflow: hidden; + transition: all 0.3s ease; +} + +.faqItem:hover { + box-shadow: 0 4px 12px rgba(197, 160, 89, 0.2); +} + +.faqQuestion { + width: 100%; + padding: 1.5rem 2rem; + background: transparent; + border: none; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + font-family: var(--font-lato); + font-size: 1.1rem; + font-weight: 600; + color: #5d4037; + text-align: left; + transition: all 0.3s ease; +} + +.faqQuestion:hover { + background-color: #faf6f0; +} + +.faqQuestionActive { + background-color: #C5A059; + color: #fff; +} + +.faqIcon { + font-size: 1.5rem; + font-weight: bold; + color: #C5A059; + transition: transform 0.3s ease; + flex-shrink: 0; + margin-left: 1rem; +} + +.faqQuestionActive .faqIcon { + color: #fff; + transform: rotate(180deg); +} + +.faqAnswer { + padding: 1.5rem 2rem; + border-top: 1px solid #e0d5c7; + animation: slideDown 0.3s ease; +} + +.faqAnswer p { + font-family: var(--font-lato); + font-size: 1rem; + color: #5d4037; + line-height: 1.8; + margin: 0; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@media (max-width: 1024px) { + .faqContainer { + grid-template-columns: 1fr; + gap: 3rem; + } + + .faqImageBlock { + min-height: 400px; + } +} + +@media (max-width: 768px) { + .faqSection { + padding: 60px 20px; + } + + .faqTitle { + font-size: 2rem; + } + + .faqSubtitle { + font-size: 1rem; + } + + .faqQuestion { + padding: 1.2rem 1.5rem; + font-size: 1rem; + } + + .faqAnswer { + padding: 1.2rem 1.5rem; + } + + .faqImageBlock { + min-height: 300px; + } +} \ No newline at end of file diff --git a/src/app/about/about_temp.css b/src/app/about/about_temp.css new file mode 100644 index 0000000..3ccf7f6 --- /dev/null +++ b/src/app/about/about_temp.css @@ -0,0 +1,168 @@ +.main { + background-color: var(--color-dark); + color: var(--color-text-light); + min-height: 100vh; +} + +.hero { + padding: 10rem 2rem; + background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url('/images/hero-1.png'); + background-size: cover; + background-position: center; + display: flex; + align-items: center; + justify-content: center; + text-align: center; +} + +.heroContent { + max-width: 800px; +} + +.heroTitle { + font-family: var(--font-playfair); + font-size: 4rem; + color: var(--color-gold); + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.breadcrumb { + font-size: 1.1rem; + color: #C5A059; + font-family: var(--font-lato); +} + +.breadcrumb a { + color: #fff; + transition: color 0.3s; +} + +.breadcrumb a:hover { + color: var(--color-gold); +} + +.section { + padding: 80px 20px; + background-color: #f5e6d3; +} + +.featuresSection { + background-color: #3a0c08; + background-image: url('/images/section-bg.jpg'); + background-size: cover; + background-position: center; +} + +.testimonialsSection { + background-color: #0a0a0a; +} + +.container { + max-width: 1200px; + margin: 0 auto; + display: flex; + align-items: center; + gap: 4rem; + flex-wrap: wrap; +} + +.textBlock { + flex: 1; + min-width: 300px; +} + +.imageBlock { + flex: 1; + min-width: 300px; + display: flex; + justify-content: center; +} + +.sectionTitle { + font-family: var(--font-playfair); + font-size: 3rem; + color: #C5A059; + margin-bottom: 1.5rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.sectionTitleCenter { + font-family: var(--font-playfair); + font-size: 3.5rem; + color: #C5A059; + margin-bottom: 3rem; + text-align: center; + text-transform: uppercase; + letter-spacing: 2px; +} + +.text { + font-size: 1.1rem; + line-height: 1.8; + color: #C5A059; + margin-bottom: 1.5rem; + opacity: 0.9; + font-family: var(--font-lato); +} + +.image { + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + max-width: 100%; + height: auto; + border: 4px solid #C5A059; +} + +.menuButton { + display: inline-block; + padding: 15px 40px; + border: 2px solid #C5A059; + color: #C5A059; + font-family: var(--font-lato); + font-size: 1.1rem; + text-transform: uppercase; + text-decoration: none; + transition: all 0.3s ease; + background: transparent; + letter-spacing: 1px; + margin-top: 1rem; +} + +.menuButton:hover { + background-color: #C5A059; + color: #000; +} + +/* Features Section */ +.featuresGrid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 3rem; + max-width: 1200px; + margin: 0 auto; +} + +.featureCard { + background-color: #F5E6D3; + padding: 2.5rem 2rem; + text-align: center; + border: 4px solid #C5A059; + transition: transform 0.3s ease; +} + +.featureCard:hover { + transform: translateY(-10px); +} + +.featureImageWrapper { + position: relative; + width: 100%; + height: 200px; + margin-bottom: 1.5rem; + border-radius: 8px; + overflow: hidden; + border: 2px solid #C5A059; +} \ No newline at end of file diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx new file mode 100644 index 0000000..4032397 --- /dev/null +++ b/src/app/about/page.tsx @@ -0,0 +1,352 @@ +'use client' + +import Navbar from "@/components/Navbar/Navbar"; +import Footer from "@/components/Footer/Footer"; +import FAQ from "@/components/FAQ/FAQ"; +import Image from "next/image"; +import Link from "next/link"; +import styles from "./about.module.css"; +import { featuresData, testimonialData, ctaData, aboutFaqData } from "@/utils/constant"; +import { useState, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; + +export default function AboutPage() { + const [currentTestimonial, setCurrentTestimonial] = useState(0); + + // Animation variants + const fadeInUp = { + hidden: { opacity: 0, y: 30 }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.6 + } + } + }; + + const fadeIn = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { duration: 0.8 } + } + }; + + const slideInLeft = { + hidden: { opacity: 0, x: -50 }, + visible: { + opacity: 1, + x: 0, + transition: { + duration: 0.7 + } + } + }; + + const slideInRight = { + hidden: { opacity: 0, x: 50 }, + visible: { + opacity: 1, + x: 0, + transition: { + duration: 0.7 + } + } + }; + + const staggerContainer = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.2, + delayChildren: 0.1 + } + } + }; + + // Auto-slide testimonials + useEffect(() => { + const interval = setInterval(() => { + setCurrentTestimonial((prev) => (prev + 1) % testimonialData.length); + }, 5000); // Change every 5 seconds + + return () => clearInterval(interval); + }, []); + + const nextTestimonial = () => { + setCurrentTestimonial((prev) => (prev + 1) % testimonialData.length); + }; + + const prevTestimonial = () => { + setCurrentTestimonial((prev) => (prev - 1 + testimonialData.length) % testimonialData.length); + }; + + return ( +
+ + + {/* Hero Banner */} + +
+

About Us

+

+ Home / About +

+
+
+ + {/* About Section - No boxed structure */} +
+ + +

Our Story

+

+ Founded in 2010, Antalya Restaurant began with a simple mission: to bring the authentic flavors of Turkey to our community. + What started as a small family-run kitchen has grown into a beloved dining destination, known for its warm hospitality and traditional recipes passed down through generations. +

+

+ Every dish we serve tells a story of tradition, passion, and dedication to the culinary arts. Our chefs use time-honored techniques combined with the freshest ingredients to create an unforgettable dining experience. +

+ + View Our Menu + +
+ + Our Story + +
+
+ + {/* Features Section - With real images */} +
+ + What Makes Us Special + + + {featuresData.map((feature) => ( + +
+ {feature.title} +
+

{feature.title}

+

{feature.description}

+
+ ))} +
+
+ + {/* Why Choose Us Section */} +
+ + + Why Choose Us + + +

Why Choose Us

+

+ At Antalya Restaurant, we don't just serve food – we create experiences. Our commitment to authenticity means every spice, every ingredient, and every cooking method stays true to Turkish culinary traditions. +

+

+ From our charcoal-grilled kebabs to our handmade baklava, we take pride in delivering dishes that transport you straight to the streets of Istanbul and the coasts of Antalya. +

+
+
+
+ + {/* Testimonials Section - Auto Slider */} +
+ + What Our Guests Say + + + + + + +

"{testimonialData[currentTestimonial].text}"

+
+
+ {testimonialData[currentTestimonial].name} +
+
+

{testimonialData[currentTestimonial].name}

+

{testimonialData[currentTestimonial].role}

+
+
+
+
+ + +
+ + {/* Slider dots */} +
+ {testimonialData.map((_, index) => ( +
+
+ + {/* FAQ Section - Image Left, FAQ Right */} +
+ + + Frequently Asked Questions + + +

Frequently Asked Questions

+

+ Have questions about Antalya Restaurant? Find answers to our most commonly asked questions below. +

+
+ {aboutFaqData.map((faq, index) => ( + + ))} +
+
+
+
+ + {/* Call to Action */} + +
+ + {ctaData.title} + + + {ctaData.subtitle} + + + + {ctaData.buttonText} + + +
+
+ +
+ ); +} + +// FAQ Item Component +function FaqItem({ question, answer }: { question: string; answer: string }) { + const [isOpen, setIsOpen] = useState(false); + + return ( +
+ + {isOpen && ( +
+

{answer}

+
+ )} +
+ ); +} diff --git a/src/app/blog/[id]/blogDetail.module.css b/src/app/blog/[id]/blogDetail.module.css new file mode 100644 index 0000000..caa2590 --- /dev/null +++ b/src/app/blog/[id]/blogDetail.module.css @@ -0,0 +1,310 @@ +.main { + background-color: var(--color-dark); + color: var(--color-text-light); + min-height: 100vh; +} + +.hero { + padding: 10rem 2rem; + background-size: cover; + background-position: center; + text-align: center; +} + +.heroContent { + max-width: 800px; + margin: 0 auto; +} + +.heroTitle { + font-family: var(--font-playfair); + font-size: 4rem; + color: #C5A059; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.breadcrumb { + font-size: 1.1rem; + color: #C5A059; + font-family: var(--font-lato); +} + +.breadcrumb a { + color: #fff; + transition: color 0.3s; +} + +.breadcrumb a:hover { + color: var(--color-gold); +} + +.article { + background-color: #F5E6D3; + padding: 60px 20px; +} + +.contentContainer { + max-width: 1200px; + margin: 0 auto; + background: #fff; + padding: 3rem; + border: 4px solid #C5A059; +} + +.meta { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 2rem; + flex-wrap: wrap; + font-family: var(--font-lato); +} + +.category { + background: #C5A059; + color: #fff; + padding: 0.5rem 1rem; + border-radius: 4px; + font-size: 0.9rem; + font-weight: 600; + text-transform: uppercase; +} + +.date, +.comments { + color: #836839; + font-size: 0.95rem; +} + +.separator { + color: #d4c5b0; +} + +.featuredImage { + width: 100%; + height: 500px; + position: relative; + margin-bottom: 2.5rem; + border-radius: 8px; + overflow: hidden; + border: 3px solid #C5A059; +} + +.featuredImage img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.content { + font-family: var(--font-lato); + font-size: 1.1rem; + line-height: 1.8; + color: #5d4037; +} + +.content h2 { + font-family: var(--font-playfair); + font-size: 2rem; + color: #5d4037; + margin: 2rem 0 1rem; + padding-bottom: 0.5rem; + border-bottom: 2px solid #C5A059; +} + +.content h3 { + font-family: var(--font-playfair); + font-size: 1.6rem; + color: #5d4037; + margin: 1.5rem 0 1rem; +} + +.content h4 { + font-family: var(--font-playfair); + font-size: 1.3rem; + color: #5d4037; + margin: 1.2rem 0 0.8rem; +} + +.content p { + margin-bottom: 1.2rem; +} + +.content ul { + margin: 1.5rem 0; + padding-left: 2rem; +} + +.content li { + margin-bottom: 0.8rem; + color: #5d4037; +} + +.content strong { + color: #3e2723; + font-weight: 600; +} + +.content a { + color: #C5A059; + text-decoration: underline; + transition: color 0.3s; +} + +.content a:hover { + color: #b08f4a; +} + +.content .sec-title { + margin-top: 2rem; +} + +.content .mt-4 { + margin-top: 1.5rem; +} + +.content .list-style-two { + list-style: none; + padding-left: 0; +} + +.content .list-style-two li { + position: relative; + padding-left: 2rem; +} + +.content .list-style-two li:before { + content: "✓"; + position: absolute; + left: 0; + color: #C5A059; + font-weight: bold; + font-size: 1.2rem; +} + +/* Impressive Blockquote Styling */ +.content blockquote { + text-align: center; + background: linear-gradient(135deg, #faf6f0 0%, #f5ede0 100%); + border-left: 6px solid #c5a059; + /* border-right: 6px solid #c5a059; */ + /* max-width: 800px; */ + margin: 3rem 0px; + padding: 10px 20px 10px 20px; + position: relative; + box-shadow: 0 8px 24px #c5a05926; +} + +.content blockquote p { + font-family: var(--font-playfair); + font-size: 1.4rem; + font-style: italic; + color: #5d4037; + line-height: 1.8; + margin: 0; + position: relative; + z-index: 1; +} + +.content blockquote cite { + display: block; + margin-top: 1.5rem; + font-family: var(--font-lato); + font-size: 1rem; + font-style: normal; + color: #C5A059; + font-weight: 600; +} + +.content blockquote cite:before { + content: "— "; +} + +.navigation { + margin-top: 3rem; + padding-top: 2rem; + border-top: 2px solid #e0d5c7; +} + +.backButton { + display: inline-block; + padding: 12px 30px; + background: transparent; + border: 2px solid #C5A059; + color: #C5A059; + font-family: var(--font-lato); + font-size: 1rem; + font-weight: 600; + text-decoration: none; + transition: all 0.3s; + text-transform: uppercase; + letter-spacing: 1px; +} + +.backButton:hover { + background: #C5A059; + color: #fff; +} + +.errorContainer { + min-height: 60vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 4rem 2rem; +} + +.errorTitle { + font-family: var(--font-playfair); + font-size: 3rem; + color: #C5A059; + margin-bottom: 2rem; +} + +.backLink { + color: #C5A059; + font-size: 1.2rem; + text-decoration: underline; +} + +@media (max-width: 768px) { + .heroTitle { + font-size: 2.5rem; + } + + .contentContainer { + padding: 2rem 1.5rem; + } + + .featuredImage { + height: 300px; + } + + .content { + font-size: 1rem; + } + + .content h2 { + font-size: 1.6rem; + } + + .content h3 { + font-size: 1.3rem; + } + + .content blockquote { + padding: 2rem 2rem; + margin: 2rem auto; + } + + .content blockquote p { + font-size: 1.2rem; + } + + .meta { + font-size: 0.85rem; + } +} \ No newline at end of file diff --git a/src/app/blog/[id]/page.tsx b/src/app/blog/[id]/page.tsx new file mode 100644 index 0000000..a6070f8 --- /dev/null +++ b/src/app/blog/[id]/page.tsx @@ -0,0 +1,94 @@ +'use client' + +import { useParams } from 'next/navigation'; +import Navbar from "@/components/Navbar/Navbar"; +import Footer from "@/components/Footer/Footer"; +import FAQ from "@/components/FAQ/FAQ"; +import Image from "next/image"; +import Link from "next/link"; +import styles from "./blogDetail.module.css"; +import { blogData } from "@/utils/constant"; + +export default function BlogDetailPage() { + const params = useParams(); + const slug = params.id as string; + const blog = blogData.find((b) => b.slug === slug); + + if (!blog) { + return ( +
+ +
+

Blog Post Not Found

+ Back to Blog +
+
+ ); + } + + return ( +
+ + + {/* Banner Section */} +
+
+

{blog.title}

+

+ Home / Blog / {blog.title} +

+
+
+ + {/* Blog Content */} +
+
+ {/* Meta Information */} + + {/* Featured Image */} + {blog.detailImage && ( +
+ {blog.title} +
+ )} + +
+ {blog.category} + | + {blog.date} + | + {blog.comments} +
+ + + {/* Blog Content */} +
+ + {/* FAQ Section */} + {blog.faqs && blog.faqs.length > 0 && ( + + )} + + + {/* Navigation */} +
+ ← Back to All Blogs +
+
+
+ + +
+ ); +} diff --git a/src/app/blog/blog.module.css b/src/app/blog/blog.module.css new file mode 100644 index 0000000..6646cb1 --- /dev/null +++ b/src/app/blog/blog.module.css @@ -0,0 +1,143 @@ +.main { + background-color: var(--color-dark); + color: var(--color-text-light); + min-height: 100vh; + background-color: #f5e6d3; +} + +.hero { + padding: 10rem 2rem; + background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url('/images/dish-2.png'); + background-size: cover; + background-position: center; + text-align: center; +} + +.heroContent { + max-width: 800px; + margin: 0 auto; +} + +.heroTitle { + font-family: var(--font-playfair); + font-size: 4rem; + color: #C5A059; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.breadcrumb { + font-size: 1.1rem; + color: #C5A059; + font-family: var(--font-lato); +} + +.breadcrumb a { + color: #fff; + transition: color 0.3s; +} + +.breadcrumb a:hover { + color: var(--color-gold); +} + +.blogSection { + padding: 80px 20px; + max-width: 1200px; + margin: 0 auto; + /* background-color: #0a0a0a; */ +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; +} + +.card { + background-color: #F5E6D3; + border: 4px solid #C5A059; + overflow: hidden; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.card:hover { + transform: translateY(-10px); + box-shadow: 0 10px 30px rgba(197, 160, 89, 0.5); +} + +.imageContainer { + position: relative; + width: 100%; + height: 250px; + border-bottom: 1px solid #C5A059; +} + +.cardContent { + padding: 1.5rem; + display: flex; + flex-direction: column; + height: calc(100% - 250px); +} + +.blogTitle { + font-family: var(--font-playfair); + font-size: 1.6rem; + margin-bottom: 0.5rem; +} + +.blogTitle a { + color: #3e2723; + transition: color 0.3s; +} + +.blogTitle a:hover { + color: #C5A059; +} + +.date { + font-size: 0.9rem; + color: #836839; + margin-bottom: 1rem; + font-family: var(--font-lato); + font-weight: 600; +} + +.excerpt { + font-size: 1rem; + color: #5d4037; + line-height: 1.6; + margin-bottom: 1.5rem; + flex-grow: 1; + font-family: var(--font-lato); +} + +.button { + display: inline-block; + padding: 10px 25px; + border: 2px solid #C5A059; + color: #5d4037; + text-align: center; + transition: all 0.3s ease; + align-self: flex-start; + font-family: var(--font-lato); + text-transform: uppercase; + font-size: 0.85rem; + font-weight: 600; +} + +.button:hover { + background-color: #C5A059; + color: #fff; +} + +@media (max-width: 768px) { + .heroTitle { + font-size: 3rem; + } + + .blogSection { + padding: 60px 20px; + } +} \ No newline at end of file diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx new file mode 100644 index 0000000..5749e18 --- /dev/null +++ b/src/app/blog/page.tsx @@ -0,0 +1,110 @@ +'use client' + +import Navbar from "@/components/Navbar/Navbar"; +import Footer from "@/components/Footer/Footer"; +import Image from "next/image"; +import Link from "next/link"; +import styles from "./blog.module.css"; +import { blogData } from "@/utils/constant"; +import { motion } from "framer-motion"; + +export default function BlogPage() { + // Animation variants + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.15, + delayChildren: 0.2 + } + } + }; + + const cardVariants = { + hidden: { + opacity: 0, + y: 30 + }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.6 + } + } + }; + + const heroVariants = { + hidden: { opacity: 0, y: -20 }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.8 + } + } + }; + + return ( +
+ + + +
+

Our Blog

+

+ Home / Blog +

+
+
+ +
+ + {blogData.map((blog) => ( + +
+ {blog.title} +
+
+

+ {blog.title} +

+

{blog.date}

+

{blog.excerpt}

+ + Read More + +
+
+ ))} +
+
+ +
+ ); +} diff --git a/src/app/contact/contact.module.css b/src/app/contact/contact.module.css new file mode 100644 index 0000000..cf7343e --- /dev/null +++ b/src/app/contact/contact.module.css @@ -0,0 +1,315 @@ +.main { + background-color: var(--color-dark); + color: var(--color-text-light); + min-height: 100vh; +} + +.hero { + padding: 10rem 2rem; + background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url('/images/hero-2.png'); + background-size: cover; + background-position: center; + text-align: center; +} + +.heroContent { + max-width: 800px; + margin: 0 auto; +} + +.heroTitle { + font-family: var(--font-playfair); + font-size: 4rem; + color: #C5A059; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.breadcrumb { + font-size: 1.1rem; + color: #C5A059; + font-family: var(--font-lato); +} + +.breadcrumb a { + color: #fff; + transition: color 0.3s; +} + +.breadcrumb a:hover { + color: var(--color-gold); +} + +.contactSection { + padding: 80px 20px; + background-color: #F5E6D3; + /* background-image: url('/images/section-bg.jpg'); */ + background-size: cover; + background-position: center; +} + +.container { + max-width: 1400px; + margin: 0 auto; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; +} + +/* Left Side - Form Block */ +.formBlock { + background-color: #F5E6D3; + padding: 2.5rem; + border: 4px solid #C5A059; +} + +.title { + font-family: var(--font-cinzel), serif; + font-size: 2.5rem; + color: #5d4037; + margin-bottom: 1rem; + font-weight: 600; + border-bottom: 3px solid #C5A059; + padding-bottom: 0.5rem; + display: inline-block; +} + +.subtitle { + font-size: 1rem; + color: #5d4037; + line-height: 1.6; + margin-bottom: 2rem; + font-family: var(--font-lato); +} + +.form { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.formRow { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; +} + +.formGroup { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.formLabel { + font-family: var(--font-lato); + color: #C5A059; + font-size: 0.95rem; + font-weight: 600; +} + +.input, +.textarea { + background: #fff; + border: 1px solid #d4c5b0; + padding: 0.9rem; + color: #C5A059; + font-family: var(--font-lato); + font-size: 0.95rem; + transition: border-color 0.3s; + border-radius: 4px; +} + +.input::placeholder, +.textarea::placeholder { + color: #a89b87; +} + +.input:focus, +.textarea:focus { + outline: none; + border-color: #C5A059; +} + +.textarea { + resize: vertical; + min-height: 100px; +} + +.charCount { + font-size: 0.85rem; + color: #836839; + text-align: right; + font-family: var(--font-lato); +} + +.submitButton { + background: #C5A059; + border: none; + color: #fff; + padding: 15px 40px; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.3s; + text-transform: uppercase; + letter-spacing: 1px; + font-family: var(--font-lato); + border-radius: 50px; + margin-top: 1rem; +} + +.submitButton:hover { + background-color: #b08f4a; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(197, 160, 89, 0.3); +} + +/* Alert Messages */ +.alert { + padding: 1rem 1.5rem; + border-radius: 8px; + margin-bottom: 1.5rem; + font-family: var(--font-lato); + font-size: 0.95rem; +} + +.alertSuccess { + background-color: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; +} + +.alertDanger { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; +} + +.errorText { + color: #dc3545; + font-size: 0.85rem; + margin-top: 0.25rem; + display: block; + font-family: var(--font-lato); +} + +.recaptchaWrapper { + margin: 1rem 0; +} + +/* Right Side - Info Block */ +.infoBlock { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.locationCard { + background: #F5E6D3; + padding: 1.5rem; + border: 4px solid #C5A059; + display: flex; + align-items: flex-start; + gap: 1.5rem; + transition: transform 0.3s; +} + +.locationCard:hover { + transform: translateY(-5px); +} + +.iconWrapper { + background: #C5A059; + width: 50px; + height: 50px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.icon { + width: 24px; + height: 24px; + color: #fff; +} + +.locationInfo { + flex: 1; +} + +.locationTitle { + font-family: var(--font-cinzel), serif; + font-size: 1.3rem; + color: #5d4037; + margin-bottom: 0.3rem; + font-weight: 600; +} + +.locationSubtitle { + font-size: 0.9rem; + color: #836839; + margin-bottom: 0.5rem; + font-family: var(--font-lato); +} + +.locationAddress, +.locationPhone { + font-size: 1rem; + color: #5d4037; + line-height: 1.6; + font-family: var(--font-lato); +} + +.locationPhone { + font-size: 1.2rem; + font-weight: 600; + color: #C5A059; +} + +.mapContainer { + border: 4px solid #C5A059; + overflow: hidden; + margin-top: 0.5rem; +} + +.mapContainer iframe { + display: block; + width: 100%; + height: 300px; +} + +@media (max-width: 1024px) { + .container { + grid-template-columns: 1fr; + gap: 3rem; + } + + .formRow { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + .heroTitle { + font-size: 3rem; + } + + .contactSection { + padding: 60px 20px; + } + + .title { + font-size: 2rem; + } + + .formRow { + grid-template-columns: 1fr; + } + + .formBlock { + padding: 2rem 1.5rem; + } +} \ No newline at end of file diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx new file mode 100644 index 0000000..5b60a10 --- /dev/null +++ b/src/app/contact/page.tsx @@ -0,0 +1,384 @@ +'use client' + +import { useState, useEffect } from "react"; +import ReCAPTCHA from "react-google-recaptcha"; +import axios from "axios"; +import Navbar from "@/components/Navbar/Navbar"; +import Footer from "@/components/Footer/Footer"; +import Link from "next/link"; +import styles from "./contact.module.css"; +import { motion } from "framer-motion"; + +interface FormErrors { + name?: string; + email?: string; + phone?: string; + date?: string; + time?: string; + captcha?: string; +} + +export default function ContactPage() { + const [email, setEmail] = useState(""); + + useEffect(() => { + const user = "info"; + const domain = "antalya-restaurant.com"; + setEmail(`${user}@${domain}`); + }, []); + + const [formData, setFormData] = useState({ + name: "", + email: "", + phone: "", + guests: "2 Guests", + date: "", + time: "", + requests: "", + }); + + const [formErrors, setFormErrors] = useState({}); + const [captchaToken, setCaptchaToken] = useState(null); + const [alert, setAlert] = useState({ show: false, type: "", message: "" }); + const [charCount, setCharCount] = useState(0); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ ...prev, [name]: value })); + + if (name === "requests") { + setCharCount(value.length); + } + }; + + const handleCaptchaChange = (token: string | null) => { + setCaptchaToken(token); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const errors: FormErrors = {}; + if (!formData.name.trim()) errors.name = "Name is required."; + if (!formData.email.trim()) errors.email = "Email is required."; + if (!formData.phone.trim()) errors.phone = "Phone is required."; + if (!formData.date.trim()) errors.date = "Date is required."; + if (!formData.time.trim()) errors.time = "Time is required."; + if (!captchaToken) errors.captcha = "Please verify the CAPTCHA."; + + setFormErrors(errors); + if (Object.keys(errors).length > 0) return; + + const emailData = { + name: formData.name, + phone: formData.phone, + email: formData.email, + subject: `Table Reservation - ${formData.guests} on ${formData.date}`, + message: ` + Reservation Details:
+ Name: ${formData.name}
+ Email: ${formData.email}
+ Phone: ${formData.phone}
+ Guests: ${formData.guests}
+ Date: ${formData.date}
+ Time: ${formData.time}

+ Special Requests:
+ ${formData.requests || "None"} + `, + to: email, + senderName: "Antalya Restaurant - Table Reservation", + recaptchaToken: captchaToken, + }; + + try { + const res = await axios.post( + "https://mailserver.metatronnest.com/send", + emailData, + { headers: { "Content-Type": "application/json" } } + ); + + setAlert({ + show: true, + type: "success", + message: res?.data?.message || "Reservation request sent successfully! We'll contact you soon.", + }); + + setFormData({ + name: "", + email: "", + phone: "", + guests: "2 Guests", + date: "", + time: "", + requests: "", + }); + setCaptchaToken(null); + setFormErrors({}); + setCharCount(0); + } catch (error) { + setAlert({ + show: true, + type: "danger", + message: "Failed to send reservation request. Please try again later.", + }); + } + }; + + useEffect(() => { + if (alert.show) { + const timer = setTimeout(() => { + setAlert((prev) => ({ ...prev, show: false })); + }, 5000); + return () => clearTimeout(timer); + } + }, [alert.show]); + + return ( +
+ + + +
+

Contact Us

+

+ Home / Contact +

+
+
+ +
+ + {/* Left Side - Book a Table Form */} + +

Book a Table

+

+ Reserve your table for an unforgettable Turkish dining experience. We look forward to serving you our authentic cuisine. +

+ + {alert.show && ( +
+ {alert.message} +
+ )} + +
+
+
+ + + {formErrors.name && {formErrors.name}} +
+ +
+ + + {formErrors.email && {formErrors.email}} +
+
+ +
+
+ + + {formErrors.phone && {formErrors.phone}} +
+ +
+ + +
+
+ +
+
+ + + {formErrors.date && {formErrors.date}} +
+ +
+ + + {formErrors.time && {formErrors.time}} +
+
+ +
+ + + {charCount}/500 characters +
+ +
+ + {formErrors.captcha && {formErrors.captcha}} +
+ + +
+
+ + {/* Right Side - Location Info & Map */} + + {/* Location Cards */} +
+
+ + + + +
+
+

Kitchener Location

+

Main Restaurant

+

+ 1187 Fischer-Hallman Rd #411
+ Kitchener, ON N2T 4H2 +

+
+
+ +
+
+ + + + +
+
+

Burlington Location

+

Second Location

+

+ 1860 Appleby Line
+ Burlington, ON L7L 7H7 +

+
+
+ +
+
+ + + +
+
+

Call Us

+

For reservations & inquiries

+

(519) 570-4848

+
+
+ + {/* Map */} +
+ +
+
+
+
+ +
+
+ ); +} diff --git a/src/app/gallery/gallery.module.css b/src/app/gallery/gallery.module.css new file mode 100644 index 0000000..0757f6a --- /dev/null +++ b/src/app/gallery/gallery.module.css @@ -0,0 +1,219 @@ +.main { + background-color: var(--color-dark); + color: var(--color-text-light); + min-height: 100vh; + background-color: #f5e6d3; +} + +.hero { + padding: 10rem 2rem; + background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url('/images/hero-2.png'); + background-size: cover; + background-position: center; + text-align: center; +} + +.heroContent { + max-width: 800px; + margin: 0 auto; +} + +.heroTitle { + font-family: var(--font-playfair); + font-size: 4rem; + color: #C5A059; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.breadcrumb { + font-size: 1.1rem; + color: #C5A059; + font-family: var(--font-lato); +} + +.breadcrumb a { + color: #fff; + transition: color 0.3s; +} + +.breadcrumb a:hover { + color: var(--color-gold); +} + +.section { + padding: 80px 20px; + max-width: 1200px; + margin: 0 auto; + /* background-color: #0a0a0a; */ +} + +.tabs { + display: flex; + justify-content: center; + gap: 1.5rem; + margin-bottom: 3rem; +} + +.tab { + background: transparent; + border: 2px solid #C5A059; + color: #C5A059; + padding: 12px 30px; + font-size: 1rem; + cursor: pointer; + transition: all 0.3s ease; + font-family: var(--font-lato); + text-transform: uppercase; + letter-spacing: 1px; + font-weight: 600; +} + +.tab:hover, +.activeTab { + background-color: #C5A059; + color: #F5E6D3; +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1.5rem; +} + +.imageWrapper { + position: relative; + aspect-ratio: 4/3; + cursor: pointer; + overflow: hidden; + border: 4px solid #C5A059; +} + +.image { + object-fit: cover; + transition: transform 0.5s ease; +} + +.imageWrapper:hover .image { + transform: scale(1.1); +} + +.overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(58, 12, 8, 0.7); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.3s ease; +} + +.imageWrapper:hover .overlay { + opacity: 1; +} + +.viewText { + color: #C5A059; + font-size: 1.2rem; + border: 2px solid #C5A059; + padding: 0.5rem 1.5rem; + font-family: var(--font-lato); + text-transform: uppercase; + letter-spacing: 1px; +} + +/* Lightbox */ +.lightbox { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.95); + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; +} + +.lightboxContent { + position: relative; + width: 90%; + height: 80%; + max-width: 1000px; + display: flex; + align-items: center; + justify-content: center; +} + +.lightboxImageWrapper { + position: relative; + width: 100%; + height: 100%; +} + +.closeBtn { + position: absolute; + top: -40px; + right: 0; + background: none; + border: none; + color: #C5A059; + font-size: 2.5rem; + cursor: pointer; + transition: color 0.3s; +} + +.closeBtn:hover { + color: #fff; +} + +.navBtn { + position: absolute; + top: 50%; + transform: translateY(-50%); + background: rgba(197, 160, 89, 0.2); + border: 2px solid #C5A059; + color: #C5A059; + font-size: 2rem; + padding: 1rem; + cursor: pointer; + z-index: 10; + transition: all 0.3s; +} + +.navBtn:hover { + background: rgba(197, 160, 89, 0.5); + color: #fff; +} + +.prevBtn { + left: -60px; +} + +.nextBtn { + right: -60px; +} + +@media (max-width: 768px) { + .heroTitle { + font-size: 3rem; + } + + .prevBtn { + left: 10px; + } + + .nextBtn { + right: 10px; + } + + .tabs { + flex-wrap: wrap; + } +} \ No newline at end of file diff --git a/src/app/gallery/page.tsx b/src/app/gallery/page.tsx new file mode 100644 index 0000000..4cb4c09 --- /dev/null +++ b/src/app/gallery/page.tsx @@ -0,0 +1,209 @@ +'use client' + +import { useState } from 'react'; +import Navbar from "@/components/Navbar/Navbar"; +import Footer from "@/components/Footer/Footer"; +import Image from "next/image"; +import Link from "next/link"; +import styles from "./gallery.module.css"; +import { galleryData } from "@/utils/constant"; +import { motion, AnimatePresence } from "framer-motion"; + +const categories = ['All', 'Food', 'Interior']; + +export default function GalleryPage() { + const [activeTab, setActiveTab] = useState('All'); + const [lightboxOpen, setLightboxOpen] = useState(false); + const [currentIndex, setCurrentIndex] = useState(0); + + // Animation variants + const heroVariants = { + hidden: { opacity: 0, y: -20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.8 } + } + }; + + const tabsVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.6, + staggerChildren: 0.1 + } + } + }; + + const tabVariants = { + hidden: { opacity: 0, y: 10 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.4 } + } + }; + + const gridVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.08, + delayChildren: 0.1 + } + } + }; + + const imageVariants = { + hidden: { opacity: 0, scale: 0.9 }, + visible: { + opacity: 1, + scale: 1, + transition: { duration: 0.5 } + } + }; + + const filteredImages = activeTab === 'All' + ? galleryData + : galleryData.filter(img => img.category === activeTab); + + const openLightbox = (index: number) => { + setCurrentIndex(index); + setLightboxOpen(true); + }; + + const closeLightbox = () => { + setLightboxOpen(false); + }; + + const nextImage = (e: React.MouseEvent) => { + e.stopPropagation(); + setCurrentIndex((prev) => (prev + 1) % filteredImages.length); + }; + + const prevImage = (e: React.MouseEvent) => { + e.stopPropagation(); + setCurrentIndex((prev) => (prev - 1 + filteredImages.length) % filteredImages.length); + }; + + return ( +
+ + + +
+

Our Gallery

+

+ Home / Gallery +

+
+
+ +
+ + {categories.map(category => ( + setActiveTab(category)} + variants={tabVariants} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + {category} + + ))} + + + + {filteredImages.map((img, index) => ( + openLightbox(index)} + variants={imageVariants} + whileHover={{ + scale: 1.05, + transition: { duration: 0.3 } + }} + > + {img.alt} +
+ View +
+
+ ))} +
+
+ + + {lightboxOpen && ( + + e.stopPropagation()} + initial={{ scale: 0.8, opacity: 0 }} + animate={{ scale: 1, opacity: 1 }} + exit={{ scale: 0.8, opacity: 0 }} + transition={{ duration: 0.3 }} + > + + + + {filteredImages[currentIndex].alt} + + + + + )} + + +
+
+ ); +} diff --git a/src/app/globals.css b/src/app/globals.css index 3f92fd7..76f17bd 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,10 +1,11 @@ :root { - --color-gold: #bf9b30; + --color-gold: #b28839; --color-dark: #0a0a0a; --color-input-bg: #e8e0d5; --color-text-light: #f5f5f5; --font-playfair: 'Playfair Display', serif; --font-lato: 'Lato', sans-serif; + --font-cormorant: 'Cormorant Garamond', serif; } * { @@ -19,7 +20,8 @@ body { overflow-x: hidden; background-color: var(--color-dark); color: var(--color-text-light); - font-family: var(--font-lato); + font-family: var(--font-cormorant); + font-weight: 300; /* Ensure default font is applied */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5ea6e36..acd0b0f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import { Playfair_Display, Lato, Cinzel, Inter } from "next/font/google"; +import { Playfair_Display, Lato, Cinzel, Inter, Cormorant_Garamond } from "next/font/google"; import "./globals.css"; import ScrollToTop from "@/components/ScrollToTop/ScrollToTop"; @@ -28,6 +28,13 @@ const inter = Inter({ display: "swap", }); +const cormorantGaramond = Cormorant_Garamond({ + weight: ["300", "400"], + subsets: ["latin"], + variable: "--font-cormorant", + display: "swap", +}); + export const metadata: Metadata = { title: "Antalya Restaurant - Book A Table", description: "Experience luxury dining at Antalya Restaurant.", @@ -41,8 +48,8 @@ export default function RootLayout({ children: React.ReactNode }) { return ( - - + + {children} diff --git a/src/app/menu/menu.module.css b/src/app/menu/menu.module.css new file mode 100644 index 0000000..eb10222 --- /dev/null +++ b/src/app/menu/menu.module.css @@ -0,0 +1,289 @@ +.main { + background-color: var(--color-dark); + color: var(--color-text-light); + min-height: 100vh; + background-color: #f5e6d3; +} + +.hero { + padding: 10rem 2rem; + background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url('/images/hero-3.png'); + background-size: cover; + background-position: center; + background-attachment: fixed; + text-align: center; +} + +.heroContent { + max-width: 800px; + margin: 0 auto; +} + +.heroTitle { + font-family: var(--font-playfair); + font-size: 4rem; + color: #C5A059; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.breadcrumb { + font-size: 1.1rem; + color: #C5A059; + font-family: var(--font-lato); +} + +.breadcrumb a { + color: #fff; + transition: color 0.3s; +} + +.breadcrumb a:hover { + color: var(--color-gold); +} + +.menuSection { + max-width: 1200px; + margin: 0 auto; + padding: 60px 20px 80px; + /* background-color: #0a0a0a; */ +} + +/* Category Tabs */ +.tabs { + display: flex; + justify-content: center; + gap: 1.5rem; + margin-bottom: 3rem; + flex-wrap: wrap; +} + +.tab { + background: transparent; + border: 2px solid #C5A059; + color: #C5A059; + padding: 12px 30px; + font-size: 1rem; + cursor: pointer; + transition: all 0.3s ease; + font-family: var(--font-playfair); + text-transform: uppercase; + letter-spacing: 1px; + font-weight: 600; +} + +.tab:hover, +.activeTab { + background-color: #C5A059; + color: #F5E6D3; + transform: translateY(-2px); +} + +.menuGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 2rem; +} + +.menuCard { + display: flex; + gap: 1.2rem; + background: #c5a059; + padding: 1.2rem; + border: 1px solid rgba(197, 160, 89, 0.2); + transition: all 0.4s ease; + animation: fadeInUp 0.6s ease forwards; + opacity: 0; +} + +.menuCard:nth-child(1) { + animation-delay: 0.1s; +} + +.menuCard:nth-child(2) { + animation-delay: 0.2s; +} + +.menuCard:nth-child(3) { + animation-delay: 0.3s; +} + +.menuCard:nth-child(4) { + animation-delay: 0.4s; +} + +.menuCard:nth-child(5) { + animation-delay: 0.5s; +} + +.menuCard:nth-child(6) { + animation-delay: 0.6s; +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.menuCard:hover { + background: #c5a059; + border-color: #F5E6D3; + transform: translateY(-5px); + box-shadow: 0 10px 30px rgba(197, 160, 89, 0.2); +} + +.imageFrame { + position: relative; + width: 130px; + height: 130px; + flex-shrink: 0; + padding: 6px; + background: linear-gradient(135deg, rgba(197, 160, 89, 0.1), rgba(197, 160, 89, 0.05)); +} + +.imageWrapper { + position: relative; + width: 100%; + height: 100%; + border: 2px solid #F5E6D3; + overflow: hidden; +} + +.dishImage { + object-fit: cover; + transition: transform 0.5s ease; +} + +.menuCard:hover .dishImage { + transform: scale(1.1); +} + +/* Corner decorations */ +.cornerTL, +.cornerTR, +.cornerBL, +.cornerBR { + position: absolute; + width: 15px; + height: 15px; + border: 2px solid #F5E6D3; +} + +.cornerTL { + top: 0; + left: 0; + border-right: none; + border-bottom: none; +} + +.cornerTR { + top: 0; + right: 0; + border-left: none; + border-bottom: none; +} + +.cornerBL { + bottom: 0; + left: 0; + border-right: none; + border-top: none; +} + +.cornerBR { + bottom: 0; + right: 0; + border-left: none; + border-top: none; +} + +.cardContent { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; +} + +.cardHeader { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 0.6rem; +} + +.dishName { + font-family: var(--font-playfair); + font-size: 1.3rem; + color: #F5E6D3; + text-transform: capitalize; + transition: color 0.3s; +} + +.menuCard:hover .dishName { + color: #F5E6D3; +} + +.price { + font-size: 1.2rem; + color: #F5E6D3; + font-weight: bold; + font-family: var(--font-playfair); +} + +.description { + font-size: 0.9rem; + color: #F5E6D3; + line-height: 1.6; + font-family: var(--font-lato); + opacity: 0.85; +} + +@media (max-width: 968px) { + .menuGrid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + .heroTitle { + font-size: 3rem; + } + + .menuSection { + padding: 40px 20px 60px; + } + + .tabs { + gap: 1rem; + } + + .tab { + padding: 10px 20px; + font-size: 0.9rem; + } + + .menuCard { + flex-direction: column; + align-items: center; + text-align: center; + } + + .imageFrame { + width: 150px; + height: 150px; + } + + .cardHeader { + flex-direction: column; + gap: 0.5rem; + align-items: center; + } +} \ No newline at end of file diff --git a/src/app/menu/page.tsx b/src/app/menu/page.tsx new file mode 100644 index 0000000..f297a4b --- /dev/null +++ b/src/app/menu/page.tsx @@ -0,0 +1,155 @@ +'use client' + +import { useState } from 'react'; +import Navbar from "@/components/Navbar/Navbar"; +import Footer from "@/components/Footer/Footer"; +import Image from "next/image"; +import Link from "next/link"; +import styles from "./menu.module.css"; +import { menuData } from "@/utils/constant"; +import { motion } from "framer-motion"; + +export default function MenuPage() { + const [activeCategory, setActiveCategory] = useState(0); + + // Animation variants + const heroVariants = { + hidden: { opacity: 0, y: -20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.8 } + } + }; + + const tabsVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { + staggerChildren: 0.1, + delayChildren: 0.2 + } + } + }; + + const tabVariants = { + hidden: { opacity: 0, y: 10 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.4 } + } + }; + + const gridVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + delayChildren: 0.2 + } + } + }; + + const cardVariants = { + hidden: { opacity: 0, y: 30 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.5 } + } + }; + + return ( +
+ + + +
+

Our Menu

+

+ Home / Menu +

+
+
+ +
+ {/* Category Tabs */} + + {menuData.map((section, index) => ( + setActiveCategory(index)} + variants={tabVariants} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + {section.category} + + ))} + + + {/* Menu Items Grid */} + + {menuData[activeCategory].items.map((item, itemIndex) => ( + +
+
+ {item.name} +
+
+
+
+
+
+ +
+
+

{item.name}

+ {item.price} +
+

{item.description}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index f16f831..05406e0 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,4 +1,3 @@ -import Navbar from "@/components/Navbar/Navbar"; import Hero from "@/components/Hero/Hero"; import PopularDishes from "@/components/PopularDishes/PopularDishes"; import About from "@/components/About/About"; @@ -11,7 +10,6 @@ import Footer from "@/components/Footer/Footer"; export default function Home() { return (
- diff --git a/src/components/About/About.tsx b/src/components/About/About.tsx index 028c7d9..a13b4a2 100644 --- a/src/components/About/About.tsx +++ b/src/components/About/About.tsx @@ -1,20 +1,41 @@ +'use client' + import Image from 'next/image' import Link from 'next/link' import styles from './About.module.css' +import { motion } from 'framer-motion' export default function About() { return (
-
-
+ + About Antalya Interior -
-
+ + {/* Decorative Icon SVG */} @@ -31,8 +52,8 @@ export default function About() { Learn More -
-
+ +
) } diff --git a/src/components/Blogs/Blogs.module.css b/src/components/Blogs/Blogs.module.css index 5662e34..3552525 100644 --- a/src/components/Blogs/Blogs.module.css +++ b/src/components/Blogs/Blogs.module.css @@ -1,9 +1,9 @@ .section { padding: 80px 20px; background-color: #3a0c08; - /* Dark red/brown background */ - background-image: url('/images/pattern-overlay.png'); - /* Optional pattern */ + background-image: url('/images/section-bg.jpg'); + background-size: cover; + background-position: center; text-align: center; position: relative; overflow: hidden; @@ -21,19 +21,12 @@ .sliderContainer { display: flex; justify-content: center; - align-items: center; - gap: 20px; - max-width: 1400px; - margin: 0 auto; - position: relative; - padding: 0 40px; -} - -.grid { - display: grid; - grid-template-columns: repeat(3, 1fr); gap: 30px; + max-width: 1400px; width: 100%; + position: relative; + padding: 0 60px; + margin: 0 auto; } .card { @@ -46,7 +39,9 @@ flex-direction: column; align-items: center; transition: transform 0.3s ease; - height: 100%; + flex: 0 0 calc(33.333% - 30px); + max-width: calc(33.333% - 30px); + box-sizing: border-box; } .card:hover { @@ -103,20 +98,28 @@ } .arrow { - background: none; - border: none; + position: absolute; + top: 50%; + transform: translateY(-50%); color: #C5A059; font-size: 3rem; cursor: pointer; + background: none; + border: none; transition: transform 0.2s; - padding: 0 10px; - display: flex; - align-items: center; - justify-content: center; + z-index: 10; } .arrow:hover { - transform: scale(1.1); + transform: translateY(-50%) scale(1.1); +} + +.prevArrow { + left: 0; +} + +.nextArrow { + right: 0; } .viewMoreContainer { @@ -127,15 +130,13 @@ display: inline-block; padding: 15px 40px; border: 1px solid #C5A059; - color: #C5A059; + color: #fff; font-family: var(--font-inter), sans-serif; text-transform: uppercase; font-size: 1rem; text-decoration: none; transition: all 0.3s ease; background: transparent; - border-radius: 5px; - /* Slight radius as per some designs, or keep square */ border-radius: 8px; } @@ -144,12 +145,88 @@ color: #000; } -@media (max-width: 1024px) { - .grid { - grid-template-columns: 1fr; +/* Responsive adjustments for different viewport sizes */ +@media (max-width: 1400px) { + .sliderContainer { + padding: 0 50px; } .arrow { + font-size: 2.8rem; + } +} + +@media (max-width: 1200px) { + .sliderContainer { + padding: 0 40px; + } + + .arrow { + font-size: 2.5rem; + } +} + +@media (max-width: 1024px) { + .sliderContainer { + padding: 0 30px; + } + + .card { + flex: 0 0 100%; + max-width: 100%; + } + + /* Hide 2nd and 3rd cards on mobile, show only 1st */ + .card:nth-child(2), + .card:nth-child(3) { display: none; } + + .arrow { + font-size: 2.2rem; + } + + .prevArrow { + left: 5px; + } + + .nextArrow { + right: 5px; + } + + .title { + font-size: 2.5rem; + } +} + +@media (max-width: 768px) { + .sliderContainer { + padding: 0 20px; + } + + .arrow { + font-size: 2rem; + } + + .prevArrow { + left: 0; + } + + .nextArrow { + right: 0; + } +} + +@media (max-width: 480px) { + .sliderContainer { + padding: 0 15px; + } + + .arrow { + font-size: 1.8rem; + } + + .title { + font-size: 2rem; + } } \ No newline at end of file diff --git a/src/components/Blogs/Blogs.tsx b/src/components/Blogs/Blogs.tsx index 51ef48c..d7e6bcd 100644 --- a/src/components/Blogs/Blogs.tsx +++ b/src/components/Blogs/Blogs.tsx @@ -1,61 +1,73 @@ +'use client' + +import { useState, useEffect } from 'react' import Image from 'next/image' import Link from 'next/link' import styles from './Blogs.module.css' - -const blogs = [ - { - id: 1, - title: 'The Art of Turkish Tea', - image: '/images/dish-1.png', // Placeholder - excerpt: 'Lorem ipsum dolor sit amet, tuem cergat imlpecion diirm, iadioc-ticid est nt eedrama inapat.' - }, - { - id: 2, - title: 'Secrets of Charcoal Grilling', - image: '/images/hero-3.png', // Placeholder - excerpt: 'Lorem ipsum dolor sit amet, tuem cergat imlpecion diirm, iadioc-ticid est nt eedrama inapat.' - }, - { - id: 3, - title: 'A Taste of Sweet Legacy', - image: '/images/dish-2.png', // Placeholder - excerpt: 'Lorem ipsum dolor sit amet, tuem cergat imlpecion diirm, iadioc-ticid est nt eedrama inapat.' - } -] +import { blogData } from '@/utils/constant' export default function Blogs() { + const [currentIndex, setCurrentIndex] = useState(0) + + const nextSlide = () => { + setCurrentIndex((prev) => (prev + 1) % blogData.length) + } + + const prevSlide = () => { + setCurrentIndex((prev) => (prev === 0 ? blogData.length - 1 : prev - 1)) + } + + // Auto-slide effect + useEffect(() => { + const interval = setInterval(() => { + setCurrentIndex((prev) => (prev + 1) % blogData.length) + }, 5000) // Change slide every 5 seconds + + return () => clearInterval(interval) + }, [currentIndex]) + + // Get the 3 visible blogs + const getVisibleBlogs = () => { + const items = [] + for (let i = 0; i < 3; i++) { + const index = (currentIndex + i) % blogData.length + items.push(blogData[index]) + } + return items + } + + const visibleItems = getVisibleBlogs() + return (

OUR BLOGS

- + -
- {blogs.map((blog) => ( -
-
- {blog.title} -
-

{blog.title}

-

{blog.excerpt}

- - View More - + {visibleItems.map((blog) => ( +
+
+ {blog.title}
- ))} -
+

{blog.title}

+

{blog.para}

+ + View More + +
+ ))} - +
- + Read More Blogs
diff --git a/src/components/BookTable/BookTable.module.css b/src/components/BookTable/BookTable.module.css index 8da3484..77f2c43 100644 --- a/src/components/BookTable/BookTable.module.css +++ b/src/components/BookTable/BookTable.module.css @@ -62,16 +62,14 @@ .inputGroup { display: flex; - align-items: center; - gap: 1rem; + flex-direction: column; + gap: 0.5rem; } .label { font-family: var(--font-playfair); color: var(--color-gold); - width: 80px; font-size: 1.1rem; - text-align: right; } .input, @@ -83,7 +81,7 @@ padding: 0.8rem 1rem; font-family: var(--font-lato); font-size: 1rem; - color: #333; + color: #757575; outline: none; transition: box-shadow 0.3s ease; } @@ -107,7 +105,7 @@ font-size: 1rem; cursor: pointer; transition: all 0.3s ease; - align-self: flex-end; + align-self: center; margin-top: 1rem; border-radius: 4px; } @@ -124,6 +122,55 @@ font-family: var(--font-lato); } +/* Alert styles */ +.alert { + padding: 1rem; + margin-bottom: 1.5rem; + border-radius: 4px; + font-family: var(--font-lato); + text-align: center; +} + +.alertSuccess { + background-color: rgba(40, 167, 69, 0.2); + color: #28a745; + border: 1px solid #28a745; +} + +.alertDanger { + background-color: rgba(220, 53, 69, 0.2); + color: #dc3545; + border: 1px solid #dc3545; +} + +/* Error text */ +.errorText { + color: #dc3545; + font-size: 0.875rem; + margin-top: 0.25rem; + display: block; + font-family: var(--font-lato); +} + +/* Character count */ +.charCount { + color: var(--color-gold); + font-size: 0.875rem; + margin-top: 0.25rem; + display: block; + text-align: right; + font-family: var(--font-lato); +} + +/* ReCAPTCHA wrapper */ +.recaptchaWrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + margin: 1rem 0; +} + @media (min-width: 768px) { .imageContainer { display: block; diff --git a/src/components/BookTable/BookTable.tsx b/src/components/BookTable/BookTable.tsx index b25b4fa..9ecce6a 100644 --- a/src/components/BookTable/BookTable.tsx +++ b/src/components/BookTable/BookTable.tsx @@ -1,32 +1,121 @@ 'use client' -import { useState } from 'react' +import { useState, useEffect } from 'react' import Image from 'next/image' +import ReCAPTCHA from "react-google-recaptcha" +import axios from "axios" import styles from './BookTable.module.css' -import { submitReservation } from '@/app/actions' + +interface FormErrors { + name?: string; + phone?: string; + date?: string; + captcha?: string; +} export default function BookTable() { - const [isSubmitting, setIsSubmitting] = useState(false) - const [message, setMessage] = useState('') + const [email, setEmail] = useState(""); - async function handleSubmit(formData: FormData) { - setIsSubmitting(true) - setMessage('') + useEffect(() => { + const user = "info"; + const domain = "antalya-restaurant.com"; + setEmail(`${user}@${domain}`); + }, []); + + const [formData, setFormData] = useState({ + name: "", + phone: "", + date: "", + message: "", + }); + + const [formErrors, setFormErrors] = useState({}); + const [captchaToken, setCaptchaToken] = useState(null); + const [alert, setAlert] = useState({ show: false, type: "", message: "" }); + const [charCount, setCharCount] = useState(0); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ ...prev, [name]: value })); + + if (name === "message") { + setCharCount(value.length); + } + }; + + const handleCaptchaChange = (token: string | null) => { + setCaptchaToken(token); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const errors: FormErrors = {}; + if (!formData.name.trim()) errors.name = "Name is required."; + if (!formData.phone.trim()) errors.phone = "Phone is required."; + if (!formData.date.trim()) errors.date = "Date is required."; + if (!captchaToken) errors.captcha = "Please verify the CAPTCHA."; + + setFormErrors(errors); + if (Object.keys(errors).length > 0) return; + + const emailData = { + name: formData.name, + phone: formData.phone, + email: email, + subject: `Table Reservation - ${formData.name} on ${formData.date}`, + message: ` + Reservation Details:
+ Name: ${formData.name}
+ Phone: ${formData.phone}
+ Date: ${formData.date}

+ Message:
+ ${formData.message || "None"} + `, + to: email, + senderName: "Antalya Restaurant - Table Reservation", + recaptchaToken: captchaToken, + }; try { - const result = await submitReservation(formData) - if (result.success) { - setMessage(result.message) - const form = document.getElementById('reservation-form') as HTMLFormElement - form?.reset() - } + const res = await axios.post( + "https://mailserver.metatronnest.com/send", + emailData, + { headers: { "Content-Type": "application/json" } } + ); + + setAlert({ + show: true, + type: "success", + message: res?.data?.message || "Reservation request sent successfully! We'll contact you soon.", + }); + + setFormData({ + name: "", + phone: "", + date: "", + message: "", + }); + setCaptchaToken(null); + setFormErrors({}); + setCharCount(0); } catch (error) { - console.error('Error submitting form:', error) - setMessage('Something went wrong. Please try again.') - } finally { - setIsSubmitting(false) + setAlert({ + show: true, + type: "danger", + message: "Failed to send reservation request. Please try again later.", + }); } - } + }; + + useEffect(() => { + if (alert.show) { + const timer = setTimeout(() => { + setAlert((prev) => ({ ...prev, show: false })); + }, 5000); + return () => clearTimeout(timer); + } + }, [alert.show]); return (
@@ -44,65 +133,96 @@ export default function BookTable() {

Book A Table

+ {alert.show && ( +
+ {alert.message} +
+ )} +
+ {/* Name Input with Placeholder */}
- + + {formErrors.name && {formErrors.name}}
+ {/* Phone Input with Placeholder */}
- + + {formErrors.phone && {formErrors.phone}}
- + + {formErrors.date && {formErrors.date}}
+ {/* Message Textarea with Placeholder */}