Browse Source
feat(localization): add new translations for navigation and form fields in multiple languages
main
feat(localization): add new translations for navigation and form fields in multiple languages
main
sina_sajjadi
2 weeks ago
26 changed files with 490 additions and 1084 deletions
-
11public/locales/ar/common.json
-
4public/locales/ar/form.json
-
6public/locales/ar/navigation.json
-
11public/locales/en/common.json
-
3public/locales/en/form.json
-
6public/locales/en/navigation.json
-
11public/locales/id/common.json
-
4public/locales/id/form.json
-
6public/locales/id/navigation.json
-
11public/locales/ru/common.json
-
7public/locales/ru/navigation.json
-
17src/app/[locale]/(account-pages)/bills/BillCard.tsx
-
231src/app/[locale]/(account-pages)/bills/[slug]/page.tsx
-
2src/app/[locale]/(account-pages)/bills/page.tsx
-
301src/app/[locale]/(account-pages)/passengers-list/page.tsx
-
12src/app/[locale]/(client-components)/(HeroSearchForm2Mobile)/HeroSearchForm2Mobile.tsx
-
731src/app/[locale]/(stay-listings)/TabFilters.tsx
-
6src/app/[locale]/add-listing/[[...stepIndex]]/PageAddListing1.tsx
-
4src/app/[locale]/custom-trip/page.tsx
-
8src/app/[locale]/layout.tsx
-
124src/app/[locale]/tours/TabFilters.tsx
-
30src/app/[locale]/tours/[slug]/page.tsx
-
6src/components/Footer.tsx
-
2src/components/contexts/tourDetails.tsx
-
18src/shared/Navigation/NavMobile.tsx
-
2src/shared/SocialsList1.tsx
@ -1,269 +1,68 @@ |
|||
"use client"; |
|||
"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/[locale]/add-listing/FormItem"; |
|||
import getImageURL from "@/components/api/getImageURL"; |
|||
import PassengerTable from "./PassengerTable"; |
|||
import { IoPersonAddOutline } from "react-icons/io5"; |
|||
import axiosInstance from "@/components/api/axios"; |
|||
import { useRouter } from "next/navigation"; |
|||
import Link from "next/link"; |
|||
import { useUserContext } from "@/components/contexts/userContext"; |
|||
import { toast } from "react-toastify"; |
|||
import { useRouter } from "next/navigation"; |
|||
import { useTranslation } from "react-i18next"; |
|||
|
|||
export interface CommonLayoutProps { |
|||
params: { |
|||
id: string; |
|||
}; |
|||
} |
|||
|
|||
const EditPassenger: FC<CommonLayoutProps> = ({ params }) => { |
|||
const { user } = useUserContext(); |
|||
const router = useRouter(); |
|||
const { t } = useTranslation("form"); |
|||
|
|||
const [passenger, setPassenger] = useState({ |
|||
name: "", |
|||
passport: "", |
|||
number: "", |
|||
date: "", |
|||
image: "", |
|||
}); |
|||
|
|||
const [originalPassenger, setOriginalPassenger] = useState({ |
|||
name: "", |
|||
passport: "", |
|||
number: "", |
|||
date: "", |
|||
image: "", |
|||
}); |
|||
|
|||
const [loading, setLoading] = useState(false); |
|||
|
|||
// Fetch passenger data on component mount
|
|||
useEffect(() => { |
|||
if (Object.keys(user).length) { |
|||
axiosInstance |
|||
.get(`/api/account/passengers/${params.id}/`, { |
|||
headers: { |
|||
Authorization: `token ${user.token}`, |
|||
}, |
|||
}) |
|||
.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 || t("errorOccurred")); |
|||
}); |
|||
} |
|||
}, [user, params.id]); |
|||
|
|||
// Handle file change for uploading passport image
|
|||
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(t("imageUploaded")); |
|||
} catch (error) { |
|||
toast.error(t("imageUploadError")); |
|||
} finally { |
|||
setLoading(false); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// Validate form inputs before saving
|
|||
const validateForm = () => { |
|||
let formIsValid = true; |
|||
|
|||
if (!passenger.name) { |
|||
formIsValid = false; |
|||
toast.error(t("fullNameRequired")); |
|||
} |
|||
|
|||
if (!passenger.passport) { |
|||
formIsValid = false; |
|||
toast.error(t("passportRequired")); |
|||
} else if (!/^\d+$/.test(passenger.passport)) { |
|||
formIsValid = false; |
|||
toast.error(t("passportNumeric")); |
|||
} |
|||
|
|||
if (!passenger.date) { |
|||
formIsValid = false; |
|||
toast.error(t("dobRequired")); |
|||
} |
|||
|
|||
if (!passenger.number) { |
|||
formIsValid = false; |
|||
toast.error(t("phoneRequired")); |
|||
} else if (!/^\d+$/.test(passenger.number)) { |
|||
formIsValid = false; |
|||
toast.error(t("phoneNumeric")); |
|||
} |
|||
|
|||
if (!passenger.image) { |
|||
formIsValid = false; |
|||
toast.error(t("passportImageRequired")); |
|||
} |
|||
|
|||
return formIsValid; |
|||
}; |
|||
|
|||
// Save updated passenger details
|
|||
const handleSavePassenger = async () => { |
|||
if (!validateForm()) return; |
|||
interface data { |
|||
birthdate: string; |
|||
fullname: string; |
|||
id: number; |
|||
passport_image: string; |
|||
phone_number: string; |
|||
passport_number: string; |
|||
|
|||
const updatedFields: Partial<{ |
|||
fullname: string; |
|||
passport_number: string; |
|||
birthdate: string; |
|||
phone_number: string; |
|||
passport_image: string; |
|||
}> = {}; |
|||
|
|||
// Only include changed fields
|
|||
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(t("noChanges")); |
|||
return; |
|||
const PassengersList = () => { |
|||
const [passengers , setPassenger ] = useState([]) |
|||
const {user} = useUserContext() |
|||
const router = useRouter() |
|||
const {t} = useTranslation("form") |
|||
|
|||
useEffect(() => { |
|||
if (!Object.keys(user).length) { |
|||
router.replace("/signup"); |
|||
} |
|||
}, [user, router]); |
|||
|
|||
useEffect(()=>{ |
|||
axiosInstance.get("/api/account/passengers/" ,{ |
|||
headers :{ |
|||
Authorization : `token ${user.token}` |
|||
} |
|||
}) |
|||
.then((response)=>{ |
|||
setPassenger(response.data.results); |
|||
|
|||
try { |
|||
const response = await axiosInstance.patch( |
|||
`/api/account/passengers/${params.id}/`, |
|||
updatedFields, |
|||
{ |
|||
headers: { |
|||
Authorization: `token ${user.token}`, |
|||
"Content-Type": "application/json", |
|||
}, |
|||
} |
|||
); |
|||
}).catch((error)=>{ |
|||
console.error(error); |
|||
|
|||
}) |
|||
} , []) |
|||
|
|||
if (response.status === 200) { |
|||
toast.success(t("detailsUpdated")); |
|||
router.push("/passengers-list"); |
|||
} |
|||
} catch (error) { |
|||
toast.error(t("updateFailed")); |
|||
} |
|||
}; |
|||
|
|||
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">{t("editPassengerInfo")}</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div className="space-y-8"> |
|||
<FormItem label={t("fullName")} desc=""> |
|||
<Input |
|||
required |
|||
value={passenger.name} |
|||
onChange={(e) => |
|||
setPassenger((prev) => ({ ...prev, name: e.target.value })) |
|||
} |
|||
placeholder={t("enterFullName")} |
|||
/> |
|||
</FormItem> |
|||
|
|||
<FormItem label={t("passportNumber")} desc=""> |
|||
<Input |
|||
required |
|||
value={passenger.passport} |
|||
onChange={(e) => |
|||
setPassenger((prev) => ({ |
|||
...prev, |
|||
passport: e.target.value, |
|||
})) |
|||
} |
|||
type="text" |
|||
placeholder={t("enterPassportNumber")} |
|||
/> |
|||
</FormItem> |
|||
|
|||
<FormItem label={t("dateOfBirth")} desc=""> |
|||
<Input |
|||
required |
|||
value={passenger.date} |
|||
onChange={(e) => |
|||
setPassenger((prev) => ({ ...prev, date: e.target.value })) |
|||
} |
|||
type="date" |
|||
placeholder={t("dobPlaceholder")} |
|||
/> |
|||
</FormItem> |
|||
|
|||
<FormItem label={t("phoneNumber")} desc=""> |
|||
<Input |
|||
required |
|||
value={passenger.number} |
|||
onChange={(e) => |
|||
setPassenger((prev) => ({ |
|||
...prev, |
|||
number: e.target.value.replace(/\D/g, ""), |
|||
})) |
|||
} |
|||
type="text" |
|||
placeholder={t("enterPhoneNumber")} |
|||
/> |
|||
</FormItem> |
|||
|
|||
<FormItem label={t("passportImage")} desc=""> |
|||
<Input |
|||
required |
|||
onChange={handleFileChange} |
|||
type="file" |
|||
placeholder={t("uploadImage")} |
|||
/> |
|||
{loading && <p>{t("loading")}</p>} |
|||
</FormItem> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="flex justify-end space-x-5"> |
|||
<ButtonPrimary |
|||
onClick={(e) => { |
|||
e.preventDefault(); |
|||
handleSavePassenger(); |
|||
}} |
|||
> |
|||
{t("saveChanges")} |
|||
</ButtonPrimary> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
<div className="flex flex-col items-start space-y-6 sm:space-y-8"> |
|||
{/* Add New Passenger Section */} |
|||
<Link href={"/add-new-passenger"} className="flex items-center space-x-2 text-orange-500 cursor-pointer hover:text-orange-600"> |
|||
<IoPersonAddOutline className="text-xl" /> {/* Adjust icon size */} |
|||
<p className="text-sm font-medium">{t("addNewPassenger")}</p> |
|||
</Link> |
|||
|
|||
{/* Passenger Table */} |
|||
{passengers.map((item : data)=>( |
|||
|
|||
<PassengerTable key={item.id} data={item} /> |
|||
))} |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default EditPassenger; |
|||
export default PassengersList; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue