2 Commits

  1. 8
      next.config.js
  2. 147
      package-lock.json
  3. 1
      package.json
  4. BIN
      public/favicon.ico
  5. 1
      src/app/(account-pages)/(components)/Nav.tsx
  6. 33
      src/app/(account-pages)/account-password/page.tsx
  7. 79
      src/app/(account-pages)/account/page.tsx
  8. 2
      src/app/(account-pages)/my-trips/page.tsx
  9. 14
      src/app/(account-pages)/passengers-list/PassengerTable.tsx
  10. 270
      src/app/(account-pages)/passengers-list/[id]/page.tsx
  11. 8
      src/app/(account-pages)/passengers-list/page.tsx
  12. 1
      src/app/(client-components)/(Header)/MainNav1.tsx
  13. 2
      src/app/(client-components)/(HeroSearchForm)/GuestsInput.tsx
  14. 8
      src/app/(listing-detail)/(components)/MobileFooterSticky.tsx
  15. 54
      src/app/add-listing/[[...stepIndex]]/page.tsx
  16. 6
      src/app/layout.tsx
  17. 134
      src/app/page.tsx
  18. 4
      src/app/tours/SectionGridFilterCard.tsx
  19. 33
      src/app/tours/[slug]/page.tsx
  20. 2
      src/app/tours/page.tsx
  21. 73
      src/components/CardCategory3.tsx
  22. 6
      src/components/TourSuggestion.tsx
  23. 82
      src/components/contexts/tourDetails.tsx
  24. 8
      src/hooks/i18n.js.ts
  25. 6
      src/shared/Logo.tsx
  26. 2
      src/shared/Navigation/NavigationItem.tsx
  27. 87
      yarn.lock

8
next.config.js

@ -2,6 +2,7 @@
module.exports = { module.exports = {
typescript: { typescript: {
// !! WARN !! // !! WARN !!
// Dangerously allow production builds to successfully complete even if // Dangerously allow production builds to successfully complete even if
@ -9,12 +10,13 @@ module.exports = {
// !! WARN !! // !! WARN !!
ignoreBuildErrors: true, ignoreBuildErrors: true,
}, },
ignoreBuildErrors: true,
eslint: { eslint: {
ignoreDuringBuilds: true, ignoreDuringBuilds: true,
}, },
typescript: {
ignoreBuildErrors: true,
i18n: {
locales: ['en'], // List all the languages you want to support
defaultLocale: 'en', // The default language of the website
localeDetection: true, // Enable or disable automatic locale detection based on browser settings
}, },
images: { images: {
remotePatterns: [ remotePatterns: [

147
package-lock.json

@ -26,6 +26,7 @@
"google-map-react": "^2.2.1", "google-map-react": "^2.2.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"next": "^13.4.3", "next": "^13.4.3",
"next-i18next": "^15.3.1",
"rc-slider": "^10.1.1", "rc-slider": "^10.1.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-datepicker": "^4.11.0", "react-datepicker": "^4.11.0",
@ -59,12 +60,11 @@
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
"integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
"license": "MIT",
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.13.11"
"regenerator-runtime": "^0.14.0"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -579,6 +579,15 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
"integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/js-cookie": { "node_modules/@types/js-cookie": {
"version": "2.2.7", "version": "2.2.7",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
@ -1348,6 +1357,16 @@
"toggle-selection": "^1.0.6" "toggle-selection": "^1.0.6"
} }
}, },
"node_modules/core-js": {
"version": "3.38.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz",
"integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -2679,6 +2698,23 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"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==",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/html-parse-stringify": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
"peer": true,
"dependencies": {
"void-elements": "3.1.0"
}
},
"node_modules/human-signals": { "node_modules/human-signals": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
@ -2694,6 +2730,34 @@
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/i18next": {
"version": "23.15.1",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.15.1.tgz",
"integrity": "sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==",
"funding": [
{
"type": "individual",
"url": "https://locize.com"
},
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.2"
}
},
"node_modules/i18next-fs-backend": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-2.3.2.tgz",
"integrity": "sha512-LIwUlkqDZnUI8lnUxBnEj8K/FrHQTT/Sc+1rvDm9E8YvvY5YxzoEAASNx+W5M9DfD5s77lI5vSAFWeTp26B/3Q=="
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.2.4", "version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -3548,6 +3612,41 @@
} }
} }
}, },
"node_modules/next-i18next": {
"version": "15.3.1",
"resolved": "https://registry.npmjs.org/next-i18next/-/next-i18next-15.3.1.tgz",
"integrity": "sha512-+pa2pZJb7B6k5PKW3TLVMmAodqkNaOBWVYlpWX56mgcEJz0UMW+MKSdKM9Z72CHp6Bp48g7OWwDnLqxXNp/84w==",
"funding": [
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
},
{
"type": "individual",
"url": "https://locize.com"
}
],
"dependencies": {
"@babel/runtime": "^7.23.2",
"@types/hoist-non-react-statics": "^3.3.4",
"core-js": "^3",
"hoist-non-react-statics": "^3.3.2",
"i18next-fs-backend": "^2.3.2"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"i18next": ">= 23.7.13",
"next": ">= 12.0.0",
"react": ">= 17.0.2",
"react-i18next": ">= 13.5.0"
}
},
"node_modules/next/node_modules/postcss": { "node_modules/next/node_modules/postcss": {
"version": "8.4.14", "version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
@ -4213,6 +4312,28 @@
"react": ">=16.8.0" "react": ">=16.8.0"
} }
}, },
"node_modules/react-i18next": {
"version": "15.0.2",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.0.2.tgz",
"integrity": "sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.25.0",
"html-parse-stringify": "^3.0.1"
},
"peerDependencies": {
"i18next": ">= 23.2.3",
"react": ">= 16.8.0"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/react-icons": { "node_modules/react-icons": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz",
@ -4346,10 +4467,9 @@
} }
}, },
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"license": "MIT"
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
}, },
"node_modules/regexp.prototype.flags": { "node_modules/regexp.prototype.flags": {
"version": "1.5.0", "version": "1.5.0",
@ -5311,6 +5431,15 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/void-elements": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/warning": { "node_modules/warning": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",

1
package.json

@ -33,6 +33,7 @@
"google-map-react": "^2.2.1", "google-map-react": "^2.2.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"next": "^13.4.3", "next": "^13.4.3",
"next-i18next": "^15.3.1",
"rc-slider": "^10.1.1", "rc-slider": "^10.1.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-datepicker": "^4.11.0", "react-datepicker": "^4.11.0",

BIN
public/favicon.ico

1
src/app/(account-pages)/(components)/Nav.tsx

@ -11,7 +11,6 @@ export const Nav = () => {
const listNav: Route[] = [ const listNav: Route[] = [
"/account", "/account",
"/my-trips", "/my-trips",
"/account-password",
"/passengers-list", "/passengers-list",
]; ];

33
src/app/(account-pages)/account-password/page.tsx

@ -1,33 +0,0 @@
import React from "react";
import Label from "@/components/Label";
import ButtonPrimary from "@/shared/ButtonPrimary";
import Input from "@/shared/Input";
const AccountPass = () => {
return (
<div className="space-y-6 sm:space-y-8">
{/* HEADING */}
<h2 className="text-3xl font-semibold">Update your password</h2>
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div>
<div className=" max-w-xl space-y-6">
<div>
<Label>Current password</Label>
<Input type="password" className="mt-1.5" />
</div>
<div>
<Label>New password</Label>
<Input type="password" className="mt-1.5" />
</div>
<div>
<Label>Confirm password</Label>
<Input type="password" className="mt-1.5" />
</div>
<div className="pt-2">
<ButtonPrimary>Update password</ButtonPrimary>
</div>
</div>
</div>
);
};
export default AccountPass;

79
src/app/(account-pages)/account/page.tsx

@ -94,47 +94,62 @@ const AccountPage: FC<AccountPageProps> = () => {
const changeHandler = async (): Promise<void> => { const changeHandler = async (): Promise<void> => {
setError(""); setError("");
setLoading({ change: true }); setLoading({ change: true });
const formData = new FormData(); const formData = new FormData();
formData.append("fullname", name);
formData.append("email", email);
if (name !== user.fullname) {
formData.append("fullname", name);
}
if (email !== user.email) {
formData.append("email", email);
}
if (imageURL) { if (imageURL) {
formData.append("avatar", imageURL); formData.append("avatar", imageURL);
} }
try {
const response = await axiosInstance.put<APIResponse>(
`/api/account/profile/update/`,
formData,
{
headers: {
Authorization: `token ${user.token}`,
"Content-Type": "multipart/form-data",
},
if (formData.has("fullname") || formData.has("email") || formData.has("avatar")) {
try {
const response = await axiosInstance.put<APIResponse>(
`/api/account/profile/update/`,
formData,
{
headers: {
Authorization: `token ${user.token}`,
"Content-Type": "multipart/form-data",
},
}
);
if (response.status === 200) {
console.log(response);
toast.success("Updated successfully");
setUser({
...user,
avatar: response.data.avatar,
email: response.data.email,
fullname: response.data.fullname,
phone_number: response.data.phone_number,
});
} else {
setError("Something went wrong");
} }
);
if (response.status === 200) {
toast.success("Updated successfully")
setUser({
...user,
avatar: response.data.avatar,
email: response.data.email,
fullname: response.data.fullname,
phone_number: response.data.phone_number,
});
} else {
setError("Something went wrong");
}
} catch (error: unknown) {
if (error instanceof Error) {
setError(error.message);
} else {
setError("An unknown error occurred");
} catch (error: unknown) {
if (error instanceof Error) {
setError(error.message);
} else {
setError("An unknown error occurred");
}
} finally {
setLoading({ change: false });
} }
} finally {
} else {
toast.info("No changes detected");
setLoading({ change: false }); setLoading({ change: false });
} }
}; };
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>): Promise<void> => { const handleFileChange = async (e: ChangeEvent<HTMLInputElement>): Promise<void> => {
const file : File | undefined = e.target.files?.[0]; const file : File | undefined = e.target.files?.[0];

2
src/app/(account-pages)/my-trips/page.tsx

@ -9,7 +9,7 @@ import {
DEMO_EXPERIENCES_LISTINGS, DEMO_EXPERIENCES_LISTINGS,
DEMO_STAY_LISTINGS, DEMO_STAY_LISTINGS,
} from "@/data/listings"; } from "@/data/listings";
import React, { Fragment, useEffect, useState } from "react";
import React, { Fragment, use, useEffect, useState } from "react";
import ButtonSecondary from "@/shared/ButtonSecondary"; import ButtonSecondary from "@/shared/ButtonSecondary";
import axiosInstance from "@/components/api/axios"; import axiosInstance from "@/components/api/axios";
import StayCard2 from "@/components/StayCard2"; import StayCard2 from "@/components/StayCard2";

14
src/app/(account-pages)/passengers-list/PassengerTable.tsx

@ -1,15 +1,17 @@
"use client"; "use client";
import axiosInstance from "@/components/api/axios"; import axiosInstance from "@/components/api/axios";
import { useUserContext } from "@/components/contexts/userContext"; import { useUserContext } from "@/components/contexts/userContext";
import { useRouter } from "next/navigation";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { IoMdTrash } from "react-icons/io"; import { IoMdTrash } from "react-icons/io";
import { MdEdit } from "react-icons/md"; import { MdEdit } from "react-icons/md";
const PassengerTable = ({ data }) => { const PassengerTable = ({ data }) => {
const { user } = useUserContext(); const { user } = useUserContext();
const router = useRouter()
const [show, setShow] = useState(true); const [show, setShow] = useState(true);
const deletHandler = async () => { const deletHandler = async () => {
try { try {
const response = await axiosInstance.delete( const response = await axiosInstance.delete(
@ -43,13 +45,11 @@ const PassengerTable = ({ data }) => {
{/* Action Icons */} {/* Action Icons */}
<div className="flex space-x-6 text-neutral-500"> <div className="flex space-x-6 text-neutral-500">
<button className="hover:text-red-500 transition-colors">
<button type="submit">
<IoMdTrash onClick={deletHandler} className="text-2xl" />{" "}
{/* Increased size using text-2xl */}
</button>
<button className="hover:text-red-500 transition-colors" type="submit">
<IoMdTrash onClick={deletHandler} className="text-2xl" />{" "}
{/* Increased size using text-2xl */}
</button> </button>
<button className="hover:text-blue-500 transition-colors">
<button onClick={()=>{router.push(`/passengers-list/${data.id}`)}} className="hover:text-blue-500 transition-colors">
<MdEdit className="text-2xl" /> {/* Increased size using text-2xl */} <MdEdit className="text-2xl" /> {/* Increased size using text-2xl */}
</button> </button>
</div> </div>

270
src/app/(account-pages)/passengers-list/[id]/page.tsx

@ -0,0 +1,270 @@
"use client";
import React, { useEffect, useState } from "react";
import { FC } from "react";
import ButtonPrimary from "@/shared/ButtonPrimary";
import Input from "@/shared/Input";
import FormItem from "@/app/add-listing/FormItem";
import getImageURL from "@/components/api/getImageURL";
import axiosInstance from "@/components/api/axios";
import { useRouter } from "next/navigation";
import { useUserContext } from "@/components/contexts/userContext";
import { toast } from "react-toastify"; // Import toast
export interface CommonLayoutProps {
params: {
id: string;
};
}
const EditPassenger: FC<CommonLayoutProps> = ({ params }) => {
const { user } = useUserContext();
const router = useRouter();
const [passenger, setPassenger] = useState({
name: "",
passport: "",
number: "",
date: "",
image: "",
});
const [originalPassenger, setOriginalPassenger] = useState({
name: "",
passport: "",
number: "",
date: "",
image: "",
});
const [loading, setLoading] = useState(false);
useEffect(() => {
if (Object.keys(user).length) {
axiosInstance
.get(`/api/account/passengers/${params.id}/`, {
headers: {
Authorization: `token ${user.token}`,
"Content-Type": "application/json",
"X-CSRFToken": "oli8in4JPf6taFVzg7tsY2g9Xpmox45yOBp1LrgU20tUFL5K4VkOrx7quXvOLUwW",
},
})
.then((response) => {
const passengerData = {
name: response.data.fullname,
passport: response.data.passport_number,
date: response.data.birthdate,
number: response.data.phone_number.replace(/\D/g, ""),
image: response.data.passport_image,
};
setPassenger(passengerData);
setOriginalPassenger(passengerData); // Save original data for comparison
})
.catch((error) => {
toast.error(error.message);
});
}
}, [user]);
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
setLoading(true);
const file = e.target.files?.[0];
if (file) {
try {
const image = await getImageURL(file);
setPassenger((prev) => ({ ...prev, image: image.url }));
toast.success("Image uploaded successfully!");
} catch (error) {
toast.error("Error uploading image.");
} finally {
setLoading(false);
}
}
};
const validateForm = () => {
let formIsValid = true;
if (!passenger.name) {
formIsValid = false;
toast.error("Full Name is required.");
}
if (!passenger.passport) {
formIsValid = false;
toast.error("Passport Number is required.");
} else if (!/^\d+$/.test(passenger.passport)) {
formIsValid = false;
toast.error("Passport Number must be numeric.");
}
if (!passenger.date) {
formIsValid = false;
toast.error("Date of Birth is required.");
}
if (!passenger.number) {
formIsValid = false;
toast.error("Phone Number is required.");
} else if (!/^\d+$/.test(passenger.number)) {
formIsValid = false;
toast.error("Phone Number must be numeric.");
}
if (!passenger.image) {
formIsValid = false;
toast.error("Passport image is required.");
}
return formIsValid;
};
const handleSavePassenger = async () => {
if (!validateForm()) return;
const updatedFields: Partial<typeof passenger> = {};
// Only add fields that were changed
if (passenger.name !== originalPassenger.name) {
updatedFields.fullname = passenger.name;
}
if (passenger.passport !== originalPassenger.passport) {
updatedFields.passport_number = passenger.passport;
}
if (passenger.date !== originalPassenger.date) {
updatedFields.birthdate = passenger.date;
}
if (passenger.number !== originalPassenger.number) {
updatedFields.phone_number = passenger.number;
}
if (passenger.image !== originalPassenger.image) {
updatedFields.passport_image = passenger.image;
}
if (Object.keys(updatedFields).length === 0) {
toast.info("No changes to update.");
return;
}
try {
const response = await axiosInstance.patch(
`https://aqila.nwhco.ir/api/account/passengers/${params.id}/`,
updatedFields,
{
headers: {
Authorization: `token ${user.token}`,
"Content-Type": "application/json",
"X-CSRFToken": "oli8in4JPf6taFVzg7tsY2g9Xpmox45yOBp1LrgU20tUFL5K4VkOrx7quXvOLUwW",
},
}
);
if (response.status === 200) {
toast.success("Passenger details updated successfully!");
router.push("/passengers-list");
}
} catch (error) {
toast.error("Error saving passenger details.");
}
};
return (
<div
className={`nc-PageAddListing1 px-4 max-w-3xl mx-auto pb-24 pt-14 sm:py-24 lg:pb-32`}
>
<div className="space-y-11">
<form>
<div className="listingSection__wrap">
<>
<h2 className="text-2xl font-semibold">Passenger Information</h2>
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div>
<div className="space-y-8">
<FormItem label="Full Name" desc="">
<Input
required
value={passenger.name}
onChange={(e) =>
setPassenger((prev) => ({
...prev,
name: e.target.value,
}))
}
placeholder="Full Name"
/>
</FormItem>
<FormItem label="Passport Number" desc="">
<Input
required
value={passenger.passport}
onChange={(e) =>
setPassenger((prev) => ({
...prev,
passport: e.target.value,
}))
}
type="text" // Changed from 'number' to 'text'
placeholder="Passport Number"
/>
</FormItem>
<FormItem label="Date of Birth" desc="">
<Input
required
value={passenger.date}
onChange={(e) =>
setPassenger((prev) => ({
...prev,
date: e.target.value,
}))
}
type="date"
placeholder="Date of Birth"
/>
</FormItem>
<FormItem label="Phone Number" desc="">
<Input
required
value={passenger.number} // Remove non-numeric characters
onChange={(e) =>
setPassenger((prev) => ({
...prev,
number: e.target.value.replace(/\D/g, ""), // Ensure only numeric input
}))
}
type="text" // Keep as 'text' to prevent unwanted behavior
placeholder="Phone Number"
/>
</FormItem>
<FormItem label="Upload Passport Image Here" desc="">
<Input
required
onChange={handleFileChange}
type="file"
placeholder="Passport"
/>
{loading && <p>Loading ...</p>}
</FormItem>
</div>
</>
</div>
<div className="flex justify-end space-x-5">
<ButtonPrimary
onClick={(e) => {
e.preventDefault();
handleSavePassenger();
}}
>
Continue
</ButtonPrimary>
</div>
</form>
</div>
</div>
);
};
export default EditPassenger;

8
src/app/(account-pages)/passengers-list/page.tsx

@ -6,10 +6,18 @@ import { IoPersonAddOutline } from "react-icons/io5";
import axiosInstance from "@/components/api/axios"; import axiosInstance from "@/components/api/axios";
import Link from "next/link"; import Link from "next/link";
import { useUserContext } from "@/components/contexts/userContext"; import { useUserContext } from "@/components/contexts/userContext";
import { useRouter } from "next/navigation";
const PassengersList = () => { const PassengersList = () => {
const [passengers , setPassenger ] = useState([]) const [passengers , setPassenger ] = useState([])
const {user} = useUserContext() const {user} = useUserContext()
const router = useRouter()
useEffect(() => {
if (!Object.keys(user).length) {
router.replace("/");
}
}, [user, router]);
useEffect(()=>{ useEffect(()=>{
axiosInstance.get("/api/account/passengers/" ,{ axiosInstance.get("/api/account/passengers/" ,{

1
src/app/(client-components)/(Header)/MainNav1.tsx

@ -20,6 +20,7 @@ export interface MainNav1Props {
const MainNav1: FC<MainNav1Props> = ({ className = "" }) => { const MainNav1: FC<MainNav1Props> = ({ className = "" }) => {
const {user} = useUserContext() const {user} = useUserContext()
console.log(Object.keys(user).length);
return ( return (
<div className={`nc-MainNav1 relative z-10 ${className}`}> <div className={`nc-MainNav1 relative z-10 ${className}`}>

2
src/app/(client-components)/(HeroSearchForm)/GuestsInput.tsx

@ -80,7 +80,7 @@ const GuestsInput: FC<GuestsInputProps> = ({
{/* BUTTON SUBMIT OF FORM */} {/* BUTTON SUBMIT OF FORM */}
{hasButtonSubmit && ( {hasButtonSubmit && (
<div className="pr-2 xl:pr-4"> <div className="pr-2 xl:pr-4">
<ButtonSubmit className={`${!details && "opacity-40" }`} href={`${details ? `/tours/${details?.slug}-${details?.id}` : ""}`} />
<ButtonSubmit className={`${!details && "opacity-40 pointer-events-none" }`} href={`/tours/${details?.slug}-${details?.id}`} />
</div> </div>
)} )}
</div> </div>

8
src/app/(listing-detail)/(components)/MobileFooterSticky.tsx

@ -62,13 +62,9 @@ const MobileFooterSticky = ({ data }) => {
className={`${ className={`${
data?.status === "AVAILABLE" && passengers data?.status === "AVAILABLE" && passengers
? "" ? ""
: "opacity-60 cursor-not-allowed"
}`}
href={`${
data?.status === "AVAILABLE" && passengers
? `/add-listing/${id}`
: `/tours/${data?.slug}-${id}`
: "opacity-60 pointer-events-none"
}`} }`}
href={`/add-listing/${id}`}
> >
Reserve Reserve
</ButtonPrimary> </ButtonPrimary>

54
src/app/add-listing/[[...stepIndex]]/page.tsx

@ -1,5 +1,4 @@
"use client";
"use client"
import React, { useContext, useState, useEffect } from "react"; import React, { useContext, useState, useEffect } from "react";
import { FC } from "react"; import { FC } from "react";
import ButtonPrimary from "@/shared/ButtonPrimary"; import ButtonPrimary from "@/shared/ButtonPrimary";
@ -34,7 +33,7 @@ const CommonLayout: FC<CommonLayoutProps> = ({ params }) => {
}); });
const [passengerID, setPassengerID] = useState([]); const [passengerID, setPassengerID] = useState([]);
const [selectedPassenger, setSelectedPassenger] = useState(null); const [selectedPassenger, setSelectedPassenger] = useState(null);
const [redirecting, setRedirecting] = useState(false); // New state for redirection
const [redirecting, setRedirecting] = useState(false);
const tourID = params.stepIndex[0]; const tourID = params.stepIndex[0];
const totalPassengers = useContext(Context).passengers; const totalPassengers = useContext(Context).passengers;
@ -43,32 +42,30 @@ const CommonLayout: FC<CommonLayoutProps> = ({ params }) => {
const backtHref = () => (index > 1 ? setIndex((prev) => prev - 1) : index); const backtHref = () => (index > 1 ? setIndex((prev) => prev - 1) : index);
const nextBtnText = index === totalPassengers ? "Save Passengers" : "Continue"; const nextBtnText = index === totalPassengers ? "Save Passengers" : "Continue";
useEffect(()=>{
useEffect(() => {
Object.values(errors).forEach((error) => { Object.values(errors).forEach((error) => {
toast.error(error, { toast.error(error, {
position: "top-right", position: "top-right",
autoClose: 5000, autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
}); });
}); });
} , [errors])
}, [errors]);
const sendPassengers = async () => { const sendPassengers = async () => {
const dataToSend = {
tour_id: tourID,
passengers: {
passenger_ids: passengerID,
new_passengers: passengers,
},
};
console.log("Data being sent:", dataToSend);
try { try {
const response = await axiosInstance.post( const response = await axiosInstance.post(
"/api/tours/orders/purchase/", "/api/tours/orders/purchase/",
{
tour_id: tourID,
passengers: {
passenger_ids: passengerID,
new_passengers: passengers,
},
},
dataToSend,
{ {
headers: { headers: {
Authorization: `token ${user.token}`, Authorization: `token ${user.token}`,
@ -77,7 +74,7 @@ const CommonLayout: FC<CommonLayoutProps> = ({ params }) => {
} }
); );
console.log(response); console.log(response);
setRedirecting(true);
setRedirecting(true);
} catch (error) { } catch (error) {
backtHref(); backtHref();
console.error("Error submitting passengers:", error); console.error("Error submitting passengers:", error);
@ -87,7 +84,7 @@ const CommonLayout: FC<CommonLayoutProps> = ({ params }) => {
useEffect(() => { useEffect(() => {
if (redirecting) { if (redirecting) {
router.replace("/my-trips"); router.replace("/my-trips");
toast.success("Purchased Successfully ")
toast.success("Purchased Successfully ");
} }
}, [redirecting, router]); }, [redirecting, router]);
@ -100,15 +97,20 @@ const CommonLayout: FC<CommonLayoutProps> = ({ params }) => {
const nextHandler = () => { const nextHandler = () => {
const validationErrors = validatePassenger(newPassenger); const validationErrors = validatePassenger(newPassenger);
if (Object.keys(validationErrors).length > 0 && passengerID.length < 0) {
if (Object.keys(validationErrors).length > 0 && passengerID.length === 0) {
setErrors(validationErrors); setErrors(validationErrors);
console.log("Validation errors:", validationErrors, passengerID); console.log("Validation errors:", validationErrors, passengerID);
console.log(passengerID.length > 0);
return; return;
} }
setSelectedPassenger(null);
if (selectedPassenger) {
// If an existing passenger is selected
setSelectedPassenger(null);
nextHref();
return;
}
// Add new passenger
setPassengers((prevPassengers) => [...prevPassengers, newPassenger]); setPassengers((prevPassengers) => [...prevPassengers, newPassenger]);
setNewPassenger({ setNewPassenger({
fullname: "", fullname: "",
@ -122,7 +124,6 @@ const CommonLayout: FC<CommonLayoutProps> = ({ params }) => {
nextHref(); nextHref();
}; };
// Prevent rendering while redirecting
if (redirecting) { if (redirecting) {
return null; return null;
} }
@ -151,9 +152,6 @@ const CommonLayout: FC<CommonLayoutProps> = ({ params }) => {
</div> </div>
<div className="flex justify-end space-x-5"> <div className="flex justify-end space-x-5">
{/* {index > 1 && (
<ButtonSecondary onClick={backtHref}>Go back</ButtonSecondary>
)} */}
<ButtonPrimary onClick={nextHandler}>{nextBtnText}</ButtonPrimary> <ButtonPrimary onClick={nextHandler}>{nextBtnText}</ButtonPrimary>
</div> </div>
</div> </div>

6
src/app/layout.tsx

@ -12,6 +12,9 @@ import Footer from "@/components/Footer";
import FooterNav from "@/components/FooterNav"; import FooterNav from "@/components/FooterNav";
import { UserProvider } from "@/components/contexts/userContext"; import { UserProvider } from "@/components/contexts/userContext";
import { ToastContainer } from "react-toastify"; import { ToastContainer } from "react-toastify";
import { appWithTranslation } from "next-i18next";
import "react-toastify/dist/ReactToastify.css";
const poppins = Poppins({ const poppins = Poppins({
subsets: ["latin"], subsets: ["latin"],
@ -19,7 +22,7 @@ const poppins = Poppins({
weight: ["300", "400", "500", "600", "700"], weight: ["300", "400", "500", "600", "700"],
}); });
export default function RootLayout({
function RootLayout({
children, children,
params, params,
}: { }: {
@ -54,3 +57,4 @@ export default function RootLayout({
</html> </html>
); );
} }
export default appWithTranslation(RootLayout)

134
src/app/page.tsx
File diff suppressed because it is too large
View File

4
src/app/tours/SectionGridFilterCard.tsx

@ -6,7 +6,7 @@ import { StayDataType } from "@/data/types";
import TabFilters from "./TabFilters"; import TabFilters from "./TabFilters";
import Heading2 from "@/shared/Heading2"; import Heading2 from "@/shared/Heading2";
import StayCard2 from "@/components/StayCard2"; import StayCard2 from "@/components/StayCard2";
import { Context } from "@/components/contexts/tourDetails";
import { Context, useToursContext } from "@/components/contexts/tourDetails";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import { motion, AnimatePresence, MotionConfig } from "framer-motion"; import { motion, AnimatePresence, MotionConfig } from "framer-motion";
import { useSwipeable } from "react-swipeable"; import { useSwipeable } from "react-swipeable";
@ -25,7 +25,7 @@ const SectionGridFilterCard: FC<SectionGridFilterCardProps> = ({
className = "", className = "",
data = DEMO_STAY_LISTINGS, data = DEMO_STAY_LISTINGS,
}) => { }) => {
const { countries, tours } = useContext(Context);
const { countries, tours } = useToursContext()
const [countryTours, setCountryTours] = useState(tours.results || []); const [countryTours, setCountryTours] = useState(tours.results || []);
const [checked, setChecked] = useState<{ [key: string]: boolean }>({}); const [checked, setChecked] = useState<{ [key: string]: boolean }>({});
const searchParams = useSearchParams(); const searchParams = useSearchParams();

33
src/app/tours/[slug]/page.tsx

@ -264,7 +264,7 @@ const ListingStayDetailPage: FC<ListingStayDetailPageProps> = ({}) => {
<div className="flow-root"> <div className="flow-root">
<div className="text-sm sm:text-base text-neutral-6000 dark:text-neutral-300 mb-4"> <div className="text-sm sm:text-base text-neutral-6000 dark:text-neutral-300 mb-4">
{iteneries?.map((item, index) => ( {iteneries?.map((item, index) => (
<div className="flex">
<div key={index} className="flex">
<div className="mt-2"> <div className="mt-2">
<div className="dark:bg-black mx-4 transform -translate-x-1/2 w-8 h-8 bg-white border-secondery-bronze border-[3px] rounded-full"></div> <div className="dark:bg-black mx-4 transform -translate-x-1/2 w-8 h-8 bg-white border-secondery-bronze border-[3px] rounded-full"></div>
{totalIteneries !== index + 1 && ( {totalIteneries !== index + 1 && (
@ -470,7 +470,7 @@ const ListingStayDetailPage: FC<ListingStayDetailPageProps> = ({}) => {
<ul className="mt-3 text-neutral-500 dark:text-neutral-400 space-y-2"> <ul className="mt-3 text-neutral-500 dark:text-neutral-400 space-y-2">
{details && {details &&
details.travel_tips.map((item) => ( details.travel_tips.map((item) => (
<li>
<li key={item.id}>
<h4 className="dark:text-neutral-400 space-y-2"> <h4 className="dark:text-neutral-400 space-y-2">
{item.title} {item.title}
</h4> </h4>
@ -511,7 +511,12 @@ const ListingStayDetailPage: FC<ListingStayDetailPageProps> = ({}) => {
<span> <span>
{details?.price} x {passengers} passengers {details?.price} x {passengers} passengers
</span> </span>
<span>{details?.price * passengers}</span>
<span>
{" "}
{isNaN(details?.price * passengers)
? "N/A" // Or any fallback value, like "0" or a string message
: (details?.price * passengers).toString()}
</span>
</div> </div>
<div className="flex justify-between text-neutral-6000 dark:text-neutral-300"> <div className="flex justify-between text-neutral-6000 dark:text-neutral-300">
<span>Service charge</span> <span>Service charge</span>
@ -520,17 +525,22 @@ const ListingStayDetailPage: FC<ListingStayDetailPageProps> = ({}) => {
<div className="border-b border-neutral-200 dark:border-neutral-700"></div> <div className="border-b border-neutral-200 dark:border-neutral-700"></div>
<div className="flex justify-between font-semibold"> <div className="flex justify-between font-semibold">
<span>Total</span> <span>Total</span>
<span>{details?.price * passengers}</span>
<span>
{isNaN(details?.price * passengers)
? "N/A" // Or any fallback value, like "0" or a string message
: (details?.price * passengers).toString()}
</span>
</div> </div>
</div> </div>
{/* SUBMIT */} {/* SUBMIT */}
<ButtonPrimary <ButtonPrimary
disabled = {true}
className={`${details?.status === "AVAILABLE" && passengers ? "" : "opacity-60 cursor-not-allowed"}`}
href={`${
details?.status && passengers === "AVAILABLE" ? `/add-listing/${id}` : `/tours/${details?.slug}-${id}`
className={`${
details?.status === "AVAILABLE" && passengers
? ""
: "opacity-60 pointer-events-none"
}`} }`}
href={`/add-listing/${id}`}
> >
Reserve Reserve
</ButtonPrimary> </ButtonPrimary>
@ -544,13 +554,13 @@ const ListingStayDetailPage: FC<ListingStayDetailPageProps> = ({}) => {
<header className="rounded-md sm:rounded-xl"> <header className="rounded-md sm:rounded-xl">
<div className="relative grid grid-cols-3 sm:grid-cols-4 gap-1 sm:gap-2"> <div className="relative grid grid-cols-3 sm:grid-cols-4 gap-1 sm:gap-2">
<div className="col-span-2 row-span-3 sm:row-span-2 relative rounded-md sm:rounded-xl overflow-hidden cursor-pointer"> <div className="col-span-2 row-span-3 sm:row-span-2 relative rounded-md sm:rounded-xl overflow-hidden cursor-pointer">
<Image
{details && <Image
fill fill
className="object-cover rounded-md sm:rounded-xl" className="object-cover rounded-md sm:rounded-xl"
src={details?.images[0]?.image_url.lg} src={details?.images[0]?.image_url.lg}
alt="" alt=""
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 50vw" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 50vw"
/>
/>}
{/* <div className="aspect-w-4 aspect-h-3 sm:aspect-w-6 sm:aspect-h-5"> {/* <div className="aspect-w-4 aspect-h-3 sm:aspect-w-6 sm:aspect-h-5">
<Image <Image
fill fill
@ -632,8 +642,7 @@ const ListingStayDetailPage: FC<ListingStayDetailPageProps> = ({}) => {
<div className="sticky top-28">{renderSidebar()}</div> <div className="sticky top-28">{renderSidebar()}</div>
</div> </div>
</main> </main>
<MobileFooterSticky passengers = {passengers} data={details} />
<MobileFooterSticky passengers={passengers} data={details} />
</div> </div>
); );
}; };

2
src/app/tours/page.tsx

@ -5,7 +5,7 @@ export interface ListingStayMapPageProps {}
const ListingStayMapPage: FC<ListingStayMapPageProps> = ({}) => { const ListingStayMapPage: FC<ListingStayMapPageProps> = ({}) => {
return ( return (
<div className="container pb-24 lg:pb-28 2xl:pl-10 xl:pr-0 xl:max-w-none">
<div className="container pb-24 lg:pb-28 2xl:pl-10 xl:pr-0 xl:max-w-none mt-10">
<SectionGridFilterCard /> <SectionGridFilterCard />
</div> </div>
); );

73
src/components/CardCategory3.tsx
File diff suppressed because it is too large
View File

6
src/components/TourSuggestion.tsx

@ -13,7 +13,7 @@ import NextBtn from "./NextBtn";
import { variants } from "@/utils/animationVariants"; import { variants } from "@/utils/animationVariants";
import { useWindowSize } from "react-use"; import { useWindowSize } from "react-use";
import axiosInstance from "./api/axios"; import axiosInstance from "./api/axios";
import { Context } from "./contexts/tourDetails";
import { Context, useToursContext } from "./contexts/tourDetails";
import CardCategory1 from "./CardCategory1"; import CardCategory1 from "./CardCategory1";
export interface TourSuggestionProps { export interface TourSuggestionProps {
@ -21,7 +21,6 @@ export interface TourSuggestionProps {
itemClassName?: string; itemClassName?: string;
heading?: string; heading?: string;
subHeading?: string; subHeading?: string;
categories?: TaxonomyType[];
categoryCardType?: "card3" | "card4" | "card5"; categoryCardType?: "card3" | "card4" | "card5";
itemPerRow?: 4 | 5; itemPerRow?: 4 | 5;
sliderStyle?: "style1" | "style2"; sliderStyle?: "style1" | "style2";
@ -107,7 +106,6 @@ const TourSuggestion: FC<TourSuggestionProps> = ({
subHeading = "Popular places to recommends for you", subHeading = "Popular places to recommends for you",
className = "", className = "",
itemClassName = "", itemClassName = "",
categories = DEMO_CATS,
itemPerRow = 5, itemPerRow = 5,
categoryCardType = "card3", categoryCardType = "card3",
sliderStyle = "style1", sliderStyle = "style1",
@ -115,7 +113,7 @@ const TourSuggestion: FC<TourSuggestionProps> = ({
const [currentIndex, setCurrentIndex] = useState(0); const [currentIndex, setCurrentIndex] = useState(0);
const [direction, setDirection] = useState(0); const [direction, setDirection] = useState(0);
const [numberOfItems, setNumberOfitem] = useState(0); const [numberOfItems, setNumberOfitem] = useState(0);
const { tours , countries } = useContext(Context);
const { tours , countries } = useToursContext()

82
src/components/contexts/tourDetails.tsx

@ -1,14 +1,50 @@
"use client"; "use client";
import axiosInstance from "../api/axios"; import axiosInstance from "../api/axios";
import React, { createContext, useContext, useEffect, useState } from "react";
import React, { createContext, useContext, useEffect, useState, ReactNode } from "react";
export const Context = createContext();
interface Country {
id: number;
name: string;
}
export const ContextProvider = ({ children }) => {
const [details, setDetails] = useState();
const [passengers, setPassengers] = useState(0);
const [tours, setTours] = useState([]);
const [countries, setCountries] = useState([]);
interface Tour {
id: number;
name: string;
description: string;
price: number;
duration: string;
}
interface TourDetails {
id: number;
name: string;
description: string;
startDate: string;
endDate: string;
passengers: number;
}
export const Context = createContext<ToursContextType | undefined>(undefined);
interface ToursContextType {
details: TourDetails | undefined;
passengers: number;
getTourData: (item: number) => Promise<void>;
setPassengers: React.Dispatch<React.SetStateAction<number>>;
setDetails: React.Dispatch<React.SetStateAction<TourDetails | undefined>>;
tours: Tour[];
countries: Country[];
}
interface ContextProviderProps {
children: ReactNode;
}
export const ContextProvider = ({ children }: ContextProviderProps) => {
const [details, setDetails] = useState<TourDetails | undefined>(undefined);
const [passengers, setPassengers] = useState<number>(0);
const [tours, setTours] = useState<Tour[]>([]);
const [countries, setCountries] = useState<Country[]>([]);
useEffect(() => { useEffect(() => {
axiosInstance axiosInstance
@ -32,26 +68,15 @@ export const ContextProvider = ({ children }) => {
}); });
}, []); }, []);
const getTourData = async (item) => {
await axiosInstance
.get(`/api/tours/${item}/`)
.then((response) => {
setDetails(response.data);
})
.catch((error) => {
console.error("Error fetching data:", error);
});
// await axiosInstance
// .get(`/api/tours/${item}/passengers/`)
// .then((response) => {
// setPassengers(response.data);
// })
// .catch((error) => {
// console.error("Error fetching data:", error);
// });
const getTourData = async (item: number) => {
try {
const response = await axiosInstance.get(`/api/tours/${item}/`);
setDetails(response.data);
} catch (error) {
console.error("Error fetching tour data:", error);
}
}; };
return ( return (
<Context.Provider <Context.Provider
value={{ value={{
@ -69,10 +94,11 @@ export const ContextProvider = ({ children }) => {
); );
}; };
export const useToursContext = () => {
// Custom hook
export const useToursContext = (): ToursContextType => {
const context = useContext(Context); const context = useContext(Context);
if (!context) { if (!context) {
throw new Error("useUserContext must be used within a UserProvider");
throw new Error("useToursContext must be used within a ContextProvider");
} }
return context; return context;
}; };

8
src/hooks/i18n.js.ts

@ -0,0 +1,8 @@
const NextI18Next = require('next-i18next').default;
const i18n = new NextI18Next({
defaultLanguage: 'en',
otherLanguages: ['en'],
});
module.exports = i18n;

6
src/shared/Logo.tsx

@ -36,7 +36,11 @@ const Logo: React.FC<LogoProps> = ({
src={img} src={img}
alt="Logo" alt="Logo"
/> />
<h3 className="hidden font-bold ml-2 lg:flex items-center text-black">AQILA</h3>
<div>
<h3 className=" text-lg hidden font-bold ml-2 lg:flex items-center text-black">Aqila</h3>
<h3 className=" text-xs hidden ml-2 lg:flex items-center text-black">Traveling Agency</h3>
</div>
</div> </div>

2
src/shared/Navigation/NavigationItem.tsx

@ -240,7 +240,7 @@ const NavigationItem: FC<NavigationItemWithRouterProps> = ({ menuItem }) => {
target={item.targetBlank ? "_blank" : undefined} target={item.targetBlank ? "_blank" : undefined}
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex items-center font-normal text-neutral-6000 dark:text-neutral-300 py-2 px-4 rounded-md hover:text-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-800 dark:hover:text-neutral-200 " className="flex items-center font-normal text-neutral-6000 dark:text-neutral-300 py-2 px-4 rounded-md hover:text-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-800 dark:hover:text-neutral-200 "
href={item.href || ""}
href={`/tours?country=${item.name}`}
> >
{item.name} {item.name}
{item.type && ( {item.type && (

87
yarn.lock

@ -7,12 +7,12 @@
resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz"
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0":
version "7.21.5"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz"
integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0":
version "7.25.6"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz"
integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==
dependencies: dependencies:
regenerator-runtime "^0.13.11"
regenerator-runtime "^0.14.0"
"@emotion/is-prop-valid@^0.8.2": "@emotion/is-prop-valid@^0.8.2":
version "0.8.8" version "0.8.8"
@ -234,6 +234,14 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/hoist-non-react-statics@^3.3.4":
version "3.3.5"
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz"
integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/js-cookie@^2.2.6": "@types/js-cookie@^2.2.6":
version "2.2.7" version "2.2.7"
resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz" resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz"
@ -657,6 +665,11 @@ copy-to-clipboard@^3.3.1:
dependencies: dependencies:
toggle-selection "^1.0.6" toggle-selection "^1.0.6"
core-js@^3:
version "3.38.1"
resolved "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz"
integrity sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==
cross-spawn@^7.0.2, cross-spawn@^7.0.3: cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3" version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
@ -1494,6 +1507,20 @@ has@^1.0.3:
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
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==
dependencies:
react-is "^16.7.0"
html-parse-stringify@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz"
integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
dependencies:
void-elements "3.1.0"
human-signals@^2.1.0: human-signals@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz"
@ -1509,6 +1536,18 @@ hyphenate-style-name@^1.0.3:
resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz" resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz"
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
i18next-fs-backend@^2.3.2:
version "2.3.2"
resolved "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-2.3.2.tgz"
integrity sha512-LIwUlkqDZnUI8lnUxBnEj8K/FrHQTT/Sc+1rvDm9E8YvvY5YxzoEAASNx+W5M9DfD5s77lI5vSAFWeTp26B/3Q==
"i18next@>= 23.2.3", "i18next@>= 23.7.13":
version "23.15.1"
resolved "https://registry.npmjs.org/i18next/-/i18next-23.15.1.tgz"
integrity sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==
dependencies:
"@babel/runtime" "^7.23.2"
ignore@^5.2.0: ignore@^5.2.0:
version "5.2.4" version "5.2.4"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz"
@ -1991,7 +2030,18 @@ natural-compare@^1.4.0:
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
next@^13.4.3:
next-i18next@^15.3.1:
version "15.3.1"
resolved "https://registry.npmjs.org/next-i18next/-/next-i18next-15.3.1.tgz"
integrity sha512-+pa2pZJb7B6k5PKW3TLVMmAodqkNaOBWVYlpWX56mgcEJz0UMW+MKSdKM9Z72CHp6Bp48g7OWwDnLqxXNp/84w==
dependencies:
"@babel/runtime" "^7.23.2"
"@types/hoist-non-react-statics" "^3.3.4"
core-js "^3"
hoist-non-react-statics "^3.3.2"
i18next-fs-backend "^2.3.2"
next@^13.4.3, "next@>= 12.0.0":
version "13.4.3" version "13.4.3"
resolved "https://registry.npmjs.org/next/-/next-13.4.3.tgz" resolved "https://registry.npmjs.org/next/-/next-13.4.3.tgz"
integrity sha512-FV3pBrAAnAIfOclTvncw9dDohyeuEEXPe5KNcva91anT/rdycWbgtu3IjUj4n5yHnWK8YEPo0vrUecHmnmUNbA== integrity sha512-FV3pBrAAnAIfOclTvncw9dDohyeuEEXPe5KNcva91anT/rdycWbgtu3IjUj4n5yHnWK8YEPo0vrUecHmnmUNbA==
@ -2378,12 +2428,20 @@ react-hooks-global-state@^2.1.0:
dependencies: dependencies:
zustand "4.0.0" zustand "4.0.0"
"react-i18next@>= 13.5.0":
version "15.0.2"
resolved "https://registry.npmjs.org/react-i18next/-/react-i18next-15.0.2.tgz"
integrity sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==
dependencies:
"@babel/runtime" "^7.25.0"
html-parse-stringify "^3.0.1"
react-icons@^5.3.0: react-icons@^5.3.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz" resolved "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz"
integrity sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg== integrity sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==
react-is@^16.12.0, react-is@^16.13.1:
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1" version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@ -2445,7 +2503,7 @@ react-use@^17.4.0:
ts-easing "^0.2.0" ts-easing "^0.2.0"
tslib "^2.1.0" tslib "^2.1.0"
react@*, "react@^15.5.x || ^16.x || ^17.x || ^18.x", "react@^16 || ^17 || ^18", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || 17 || 18", "react@^16.8.3 || ^17 || ^18", "react@^16.9.0 || ^17 || ^18", react@^18.0.0, react@^18.2.0, "react@>= 16", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=16.8, react@>=16.8.0, react@>=16.9.0, react@>=18:
react@*, "react@^15.5.x || ^16.x || ^17.x || ^18.x", "react@^16 || ^17 || ^18", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || 17 || 18", "react@^16.8.3 || ^17 || ^18", "react@^16.9.0 || ^17 || ^18", react@^18.0.0, react@^18.2.0, "react@>= 16", "react@>= 16.8.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>= 17.0.2", react@>=16.8, react@>=16.8.0, react@>=16.9.0, react@>=18:
version "18.2.0" version "18.2.0"
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
@ -2466,10 +2524,10 @@ readdirp@~3.6.0:
dependencies: dependencies:
picomatch "^2.2.1" picomatch "^2.2.1"
regenerator-runtime@^0.13.11:
version "0.13.11"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-runtime@^0.14.0:
version "0.14.1"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz"
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0:
version "1.5.0" version "1.5.0"
@ -2986,6 +3044,11 @@ util-deprecate@^1.0.2:
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
void-elements@3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz"
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
warning@^4.0.2: warning@^4.0.2:
version "4.0.3" version "4.0.3"
resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz" resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz"

Loading…
Cancel
Save