new things

This commit is contained in:
nabijonovdavronbek619@gmail.com
2026-04-27 12:29:17 +05:00
parent 5b7dbd5019
commit cfa84d04ab
6 changed files with 117 additions and 43 deletions

View File

@@ -8,6 +8,7 @@ import { usePathname, useParams } from "next/navigation";
import { logoImg } from "@/assets"; import { logoImg } from "@/assets";
import { useSubCategory } from "@/store/subCategory"; import { useSubCategory } from "@/store/subCategory";
import { minimumValues } from "@/data/minimimValues"; import { minimumValues } from "@/data/minimimValues";
import { useTranslation } from "react-i18next";
import { LoadingSkeleton } from "@/components/loadingProduct"; import { LoadingSkeleton } from "@/components/loadingProduct";
import { EmptyState } from "@/components/emptyState"; import { EmptyState } from "@/components/emptyState";
import { ErrorState } from "@/components/errorState"; import { ErrorState } from "@/components/errorState";
@@ -32,6 +33,7 @@ function checkCategory(categoryName: string | undefined, lang: string) {
} }
export default function CarDetailPage() { export default function CarDetailPage() {
const { t } = useTranslation();
const [modalOpen, setModalOpen] = useState<boolean>(false); const [modalOpen, setModalOpen] = useState<boolean>(false);
const initialSubCategory = useSubCategory( const initialSubCategory = useSubCategory(
@@ -107,7 +109,8 @@ export default function CarDetailPage() {
); );
} }
const firstData = cars[0]; const firstData = cars[0]?.data[0];
console.log("First Data: ", firstData);
if (!firstData) { if (!firstData) {
return ( return (
@@ -138,7 +141,7 @@ export default function CarDetailPage() {
alt={firstData?.name ? firstData?.name : "image"} alt={firstData?.name ? firstData?.name : "image"}
width={600} width={600}
height={200} height={200}
className="rounded-lg object-cover border border-gray-200 w-full" className="rounded-lg object-contain border border-gray-200 w-full"
/> />
</div> </div>
@@ -147,7 +150,9 @@ export default function CarDetailPage() {
<div className="text-lg font-semibold flex gap-2"> <div className="text-lg font-semibold flex gap-2">
{isMinimum ? <p>{text}</p> : <Text txt="hour-price" />} : {isMinimum ? <p>{text}</p> : <Text txt="hour-price" />} :
<span className="font-medium flex gap-2 text-gray-500"> <span className="font-medium flex gap-2 text-gray-500">
{firstData.price?.toLocaleString("uz-UZ")} {firstData.price
? Math.round(Number(firstData.price)).toLocaleString("ru-RU")
: ""}
<Text txt="wallet" /> <Text txt="wallet" />
</span> </span>
</div> </div>
@@ -181,12 +186,13 @@ export default function CarDetailPage() {
</div> </div>
{/* Texnik xususiyatlar */} {/* Texnik xususiyatlar */}
{firstData?.features.length > 0 && (
<div className="w-full border-t border-gray-300 pt-6"> <div className="w-full border-t border-gray-300 pt-6">
<h2 className="text-xl font-semibold mb-4 text-secondary"> <h2 className="text-xl font-semibold mb-4 text-secondary">
Texnik xususiyatlari {t("tech-features")}
</h2> </h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 text-gray-700"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 text-gray-700">
{firstData?.features.map((item: any) => ( {firstData?.features?.map((item: any) => (
<div <div
key={item.id} key={item.id}
className="p-3 rounded-md bg-gray-50 border border-gray-200 hover:bg-gray-100 transition" className="p-3 rounded-md bg-gray-50 border border-gray-200 hover:bg-gray-100 transition"
@@ -197,6 +203,7 @@ export default function CarDetailPage() {
))} ))}
</div> </div>
</div> </div>
)}
{/* Ijara modal */} {/* Ijara modal */}
<CarRentalModal <CarRentalModal

View File

@@ -1,4 +1,3 @@
import { dir } from "i18next";
import Header from "@/components/nav_foot/header"; import Header from "@/components/nav_foot/header";
import Navbar from "@/components/nav_foot/navbar"; import Navbar from "@/components/nav_foot/navbar";
import Footer from "@/components/nav_foot/footer"; import Footer from "@/components/nav_foot/footer";
@@ -17,7 +16,7 @@ export default async function LangLayout({
const { lang } = await params; // ✅ await bilan destructuring const { lang } = await params; // ✅ await bilan destructuring
return ( return (
<html lang={lang} dir="rtl"> <html lang={lang} dir="rtl" suppressHydrationWarning>
<body> <body>
<Suspense fallback={null}> <Suspense fallback={null}>
<Header /> <Header />

View File

@@ -3,6 +3,7 @@
import { innerCardTypes } from "@/types"; import { innerCardTypes } from "@/types";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import axios from "axios"; import axios from "axios";
import { useTranslation } from "react-i18next";
interface CarRentalModalProps { interface CarRentalModalProps {
car: innerCardTypes; car: innerCardTypes;
@@ -15,8 +16,10 @@ export default function CarRentalModal({
isOpen, isOpen,
onClose, onClose,
}: CarRentalModalProps) { }: CarRentalModalProps) {
const { t } = useTranslation();
const [userName, setUserName] = useState(""); const [userName, setUserName] = useState("");
const [phone, setPhone] = useState(""); const [phone, setPhone] = useState("+998 ");
const [phoneError, setPhoneError] = useState("");
const [hours, setHours] = useState<number>(1); const [hours, setHours] = useState<number>(1);
const [total, setTotal] = useState<number | undefined>(car?.price); const [total, setTotal] = useState<number | undefined>(car?.price);
@@ -24,12 +27,40 @@ export default function CarRentalModal({
if (car.price) setTotal(hours * car.price); if (car.price) setTotal(hours * car.price);
}, [hours, car.price]); }, [hours, car.price]);
const formatPhone = (value: string): string => {
let digits = value.replace(/\D/g, "");
if (digits.startsWith("998")) digits = digits.slice(3);
digits = digits.slice(0, 9);
let formatted = "+998";
if (digits.length > 0) formatted += " " + digits.slice(0, 2);
if (digits.length > 2) formatted += " " + digits.slice(2, 5);
if (digits.length > 5) formatted += " " + digits.slice(5, 7);
if (digits.length > 7) formatted += " " + digits.slice(7, 9);
return formatted;
};
const isPhoneValid = (value: string): boolean => {
const digits = value.replace(/\D/g, "");
return digits.length === 12;
};
const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const formatted = formatPhone(e.target.value);
setPhone(formatted);
if (phoneError) setPhoneError("");
};
// 🧩 Telegramga yuboruvchi funksiya // 🧩 Telegramga yuboruvchi funksiya
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
if (!userName || !phone || !hours) { if (!userName || !phone || !hours) {
alert("Iltimos, barcha maydonlarni toldiring!"); alert(t("modal-fill-required"));
return;
}
if (!isPhoneValid(phone)) {
setPhoneError(t("modal-phone-error"));
return; return;
} }
@@ -65,14 +96,14 @@ export default function CarRentalModal({
parse_mode: "Markdown", parse_mode: "Markdown",
}); });
alert("✅ Buyurtmangiz muvaffaqiyatli yuborildi!"); alert(t("modal-success"));
onClose(); onClose();
setUserName(""); setUserName("");
setPhone(""); setPhone("+998 ");
setHours(1); setHours(1);
} catch (error) { } catch (error) {
console.error("Yuborishda xatolik:", error); console.error("Yuborishda xatolik:", error);
alert("❌ Xatolik yuz berdi. Qayta urinib koring!"); alert(t("modal-error"));
} }
}; };
@@ -95,9 +126,11 @@ export default function CarRentalModal({
<h2 className="text-2xl font-bold text-gray-800">{car.name}</h2> <h2 className="text-2xl font-bold text-gray-800">{car.name}</h2>
<p className="text-gray-600 mt-2">{car.path}</p> <p className="text-gray-600 mt-2">{car.path}</p>
<p className="text-gray-700 mt-2 font-semibold"> <p className="text-gray-700 mt-2 font-semibold">
Soatlik narx:{" "} {t("modal-hourly-price")}{" "}
<span className="text-red-600"> <span className="text-red-600">
{car.price?.toLocaleString()} UZS {car.price
? Math.round(Number(car.price)).toLocaleString("ru-RU")
: ""} UZS
</span> </span>
</p> </p>
</div> </div>
@@ -107,7 +140,7 @@ export default function CarRentalModal({
<form onSubmit={handleSubmit} className="mt-6 space-y-4"> <form onSubmit={handleSubmit} className="mt-6 space-y-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
Ismingiz {t("modal-name-label")}
</label> </label>
<input <input
type="text" type="text"
@@ -115,27 +148,30 @@ export default function CarRentalModal({
className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-secondary focus:border-transparent" className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-secondary focus:border-transparent"
value={userName} value={userName}
onChange={(e) => setUserName(e.target.value)} onChange={(e) => setUserName(e.target.value)}
placeholder="Ismingizni kiriting" placeholder={t("modal-name-placeholder")}
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
Telefon {t("modal-phone-label")}
</label> </label>
<input <input
type="tel" type="tel"
name="phone" name="phone"
className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-secondary focus:border-transparent" className={`w-full border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-secondary focus:border-transparent ${phoneError ? "border-red-500" : "border-gray-300"}`}
value={phone} value={phone}
onChange={(e) => setPhone(e.target.value)} onChange={handlePhoneChange}
placeholder="+998 90 123 45 67" placeholder={t("modal-phone-placeholder")}
/> />
{phoneError && (
<p className="text-red-500 text-xs mt-1">{phoneError}</p>
)}
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
Qancha vaqt (soat) {t("modal-hours-label")}
</label> </label>
<input <input
type="number" type="number"
@@ -148,9 +184,11 @@ export default function CarRentalModal({
</div> </div>
<div className="text-lg font-semibold text-gray-800 mt-2"> <div className="text-lg font-semibold text-gray-800 mt-2">
Jami summa:{" "} {t("modal-total")}{" "}
<span className="text-green-600"> <span className="text-green-600">
{total?.toLocaleString()} UZS {total
? Math.round(Number(total)).toLocaleString("ru-RU")
: ""} UZS
</span> </span>
</div> </div>
@@ -158,7 +196,7 @@ export default function CarRentalModal({
type="submit" type="submit"
className="w-full bg-secondary text-white py-2 rounded-lg hover:bg-primary transition font-medium" className="w-full bg-secondary text-white py-2 rounded-lg hover:bg-primary transition font-medium"
> >
Buyurtma berish {t("book")}
</button> </button>
</form> </form>
</div> </div>

View File

@@ -53,7 +53,7 @@ export default function Contact() {
event.preventDefault(); event.preventDefault();
if (!phone || !isValidPhone(phone)) { if (!phone || !isValidPhone(phone)) {
alert("Iltimos, telefon raqamingizni to'g'ri kiriting!"); alert(t("contact-phone-error"));
return; return;
} }
@@ -77,11 +77,11 @@ export default function Contact() {
text: message, text: message,
}); });
alert("✅ Muvaffaqiyatli yuborildi!"); alert(t("contact-success"));
setPhone(""); setPhone("");
} catch (error) { } catch (error) {
console.error("Yuborishda xatolik:", error); console.error("Yuborishda xatolik:", error);
alert("❌ Yuborishda xatolik yuz berdi!"); alert(t("contact-error"));
} }
}; };

View File

@@ -188,5 +188,20 @@
"retry": "Повторить", "retry": "Повторить",
"downloadError": "Ошибка при загрузке данных", "downloadError": "Ошибка при загрузке данных",
"noData": "Специальная техника не найдена", "noData": "Специальная техника не найдена",
"noDataDesc": "На данный момент товары отсутствуют. Пожалуйста, попробуйте позже." "noDataDesc": "На данный момент товары отсутствуют. Пожалуйста, попробуйте позже.",
"tech-features": "Технические характеристики",
"modal-fill-required": "Пожалуйста, заполните все поля!",
"modal-phone-error": "Введите полный номер телефона: +998 XX XXX XX XX",
"modal-success": "Ваш заказ успешно отправлен!",
"modal-error": "Произошла ошибка. Попробуйте ещё раз!",
"modal-hourly-price": "Почасовая цена:",
"modal-name-label": "Ваше имя",
"modal-name-placeholder": "Введите ваше имя",
"modal-phone-label": "Телефон",
"modal-phone-placeholder": "+998 90 123 45 67",
"modal-hours-label": "Количество часов",
"modal-total": "Итоговая сумма:",
"contact-phone-error": "Пожалуйста, введите правильный номер телефона!",
"contact-success": "Успешно отправлено!",
"contact-error": "Произошла ошибка при отправке!"
} }

View File

@@ -188,5 +188,20 @@
"retry": "Qayta urinish", "retry": "Qayta urinish",
"downloadError": "Ma'lumotlarni yuklashda xatolik", "downloadError": "Ma'lumotlarni yuklashda xatolik",
"noData": "Mahsus texnikalar topilmadi", "noData": "Mahsus texnikalar topilmadi",
"noDataDesc": "Hozircha mahsulotlar mavjud emas. Iltimos, keyinroq qayta urinib ko'ring." "noDataDesc": "Hozircha mahsulotlar mavjud emas. Iltimos, keyinroq qayta urinib ko'ring.",
"tech-features": "Texnik xususiyatlari",
"modal-fill-required": "Iltimos, barcha maydonlarni to'ldiring!",
"modal-phone-error": "Telefon raqamini to'liq kiriting: +998 XX XXX XX XX",
"modal-success": "Buyurtmangiz muvaffaqiyatli yuborildi!",
"modal-error": "Xatolik yuz berdi. Qayta urinib ko'ring!",
"modal-hourly-price": "Soatlik narx:",
"modal-name-label": "Ismingiz",
"modal-name-placeholder": "Ismingizni kiriting",
"modal-phone-label": "Telefon",
"modal-phone-placeholder": "+998 90 123 45 67",
"modal-hours-label": "Qancha vaqt (soat)",
"modal-total": "Jami summa:",
"contact-phone-error": "Iltimos, telefon raqamingizni to'g'ri kiriting!",
"contact-success": "Muvaffaqiyatli yuborildi!",
"contact-error": "Yuborishda xatolik yuz berdi!"
} }