fixed some errors

This commit is contained in:
@jcarymuhammedow
2026-01-08 18:01:17 +05:00
parent 7538bdb813
commit 071b45b98a
15 changed files with 1809 additions and 727 deletions

View File

@@ -121,20 +121,22 @@
} }
} }
[data-sonner-toast] [data-description] {
color: #000 !important;
opacity: 0.9;
}
/* Animasyonları "utilities" katmanına ekliyoruz ki Tailwind sınıfları gibi davranabilsinler */
@layer utilities { @layer utilities {
/* Özel Renk Sınıfları (CSS değişkenlerini kullanmak için) */ .text-fg {
.text-fg { color: var(--fg); } color: var(--fg);
.bg-bg { background-color: var(--bg); } }
.stroke-primary { stroke: #005bff; } .bg-bg {
background-color: var(--bg);
}
.stroke-primary {
stroke: #005bff;
}
/* Dark mode track rengi için özel sınıf */
.stroke-track { .stroke-track {
stroke: hsla(var(--hue), 10%, 10%, 0.1); stroke: hsla(var(--hue), 10%, 10%, 0.1);
transition: stroke var(--trans-dur); transition: stroke var(--trans-dur);
@@ -145,11 +147,18 @@
} }
} }
/* Animasyon Sınıfları */ .animate-msg {
.animate-msg { animation: msg 0.3s 13.7s linear forwards; } animation: msg 0.3s 13.7s linear forwards;
.animate-msgLast { animation: msg 0.3s 14s linear reverse forwards; } }
.animate-cartLines { animation: cartLines 2s ease-in-out infinite; } .animate-msgLast {
.animate-cartTop { animation: cartTop 2s ease-in-out infinite; } animation: msg 0.3s 14s linear reverse forwards;
}
.animate-cartLines {
animation: cartLines 2s ease-in-out infinite;
}
.animate-cartTop {
animation: cartTop 2s ease-in-out infinite;
}
.animate-cartWheel1 { .animate-cartWheel1 {
animation: cartWheel1 2s ease-in-out infinite; animation: cartWheel1 2s ease-in-out infinite;
transform: rotate(-0.25turn); transform: rotate(-0.25turn);
@@ -160,33 +169,68 @@
transform: rotate(0.25turn); transform: rotate(0.25turn);
transform-origin: 102px 111px; transform-origin: 102px 111px;
} }
.animate-cartWheelStroke { animation: cartWheelStroke 2s ease-in-out infinite; } .animate-cartWheelStroke {
animation: cartWheelStroke 2s ease-in-out infinite;
}
} }
/* Keyframes Tanımları */
@keyframes msg { @keyframes msg {
from { opacity: 1; visibility: visible; } from {
99.9% { opacity: 0; visibility: visible; } opacity: 1;
to { opacity: 0; visibility: hidden; } visibility: visible;
}
99.9% {
opacity: 0;
visibility: visible;
}
to {
opacity: 0;
visibility: hidden;
}
} }
@keyframes cartLines { @keyframes cartLines {
from, to { opacity: 0; } from,
8%, 92% { opacity: 1; } to {
opacity: 0;
}
8%,
92% {
opacity: 1;
}
} }
@keyframes cartTop { @keyframes cartTop {
from { stroke-dashoffset: -338; } from {
50% { stroke-dashoffset: 0; } stroke-dashoffset: -338;
to { stroke-dashoffset: 338; } }
50% {
stroke-dashoffset: 0;
}
to {
stroke-dashoffset: 338;
}
} }
@keyframes cartWheel1 { @keyframes cartWheel1 {
from { transform: rotate(-0.25turn); } from {
to { transform: rotate(2.75turn); } transform: rotate(-0.25turn);
}
to {
transform: rotate(2.75turn);
}
} }
@keyframes cartWheel2 { @keyframes cartWheel2 {
from { transform: rotate(0.25turn); } from {
to { transform: rotate(3.25turn); } transform: rotate(0.25turn);
}
to {
transform: rotate(3.25turn);
}
} }
@keyframes cartWheelStroke { @keyframes cartWheelStroke {
from, to { stroke-dashoffset: 81.68; } from,
50% { stroke-dashoffset: 40.84; } to {
stroke-dashoffset: 81.68;
}
50% {
stroke-dashoffset: 40.84;
}
} }

View File

@@ -54,10 +54,7 @@ interface FormErrors {
file?: string; file?: string;
} }
export default function OpenStorePage({ export default function OpenStorePage({}: OpenStorePageProps) {
locale = "ru",
translations,
}: OpenStorePageProps) {
const [formData, setFormData] = useState<FormData>({ const [formData, setFormData] = useState<FormData>({
firstName: "", firstName: "",
lastName: "", lastName: "",
@@ -76,21 +73,21 @@ export default function OpenStorePage({
const newErrors: FormErrors = {}; const newErrors: FormErrors = {};
if (!formData.firstName.trim()) { if (!formData.firstName.trim()) {
newErrors.firstName = t("firstNameRequired"); newErrors.firstName = t("requiredField");
} }
if (!formData.lastName.trim()) { if (!formData.lastName.trim()) {
newErrors.lastName = t("lastNameRequired"); newErrors.lastName = t("requiredField");
} }
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(formData.email)) { if (!emailRegex.test(formData.email)) {
newErrors.email = t("emailInvalid"); newErrors.email = t("requiredField");
} }
const phoneRegex = /^\+?[0-9]{6,15}$/; const phoneRegex = /^\+?[0-9]{6,15}$/;
if (!phoneRegex.test(formData.phone)) { if (!phoneRegex.test(formData.phone)) {
newErrors.phone = t("phoneInvalid"); newErrors.phone = t("requiredField");
} }
if (!formData.file) { if (!formData.file) {

View File

@@ -46,11 +46,10 @@ export default function ErrorPage() {
return ( return (
<div className="min-h-screen bg-white flex flex-col items-center justify-start overflow-hidden font-['Encode_Sans_Semi_Condensed',_sans-serif]"> <div className="min-h-screen bg-white flex flex-col items-center justify-start overflow-hidden font-['Encode_Sans_Semi_Condensed',_sans-serif]">
{/* CSS Animasyonlarını buraya gömüyoruz */}
<style dangerouslySetInnerHTML={{ __html: fontImport }} /> <style dangerouslySetInnerHTML={{ __html: fontImport }} />
<h1 <h1
className={`text-[10rem] leading-[10rem] font-extralight text-black transition-all duration-500 ease-linear className={`text-[10rem] leading-40 font-extralight text-black transition-all duration-500 ease-linear
${isLoading ? "mt-0 opacity-0" : "mt-[100px] opacity-100"}`} ${isLoading ? "mt-0 opacity-0" : "mt-[100px] opacity-100"}`}
> >
500 500
@@ -110,7 +109,6 @@ export default function ErrorPage() {
); );
} }
// Dişli çubukları için yardımcı bileşen
function GearBars() { function GearBars() {
return ( return (
<> <>

View File

@@ -1,4 +1,4 @@
"use client" "use client";
import { import {
CircleCheckIcon, CircleCheckIcon,
@@ -6,12 +6,12 @@ import {
Loader2Icon, Loader2Icon,
OctagonXIcon, OctagonXIcon,
TriangleAlertIcon, TriangleAlertIcon,
} from "lucide-react" } from "lucide-react";
import { useTheme } from "next-themes" import { useTheme } from "next-themes";
import { Toaster as Sonner, type ToasterProps } from "sonner" import { Toaster as Sonner, type ToasterProps } from "sonner";
const Toaster = ({ ...props }: ToasterProps) => { const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme() const { theme = "system" } = useTheme();
return ( return (
<Sonner <Sonner
@@ -24,17 +24,24 @@ const Toaster = ({ ...props }: ToasterProps) => {
error: <OctagonXIcon className="size-4" />, error: <OctagonXIcon className="size-4" />,
loading: <Loader2Icon className="size-4 animate-spin" />, loading: <Loader2Icon className="size-4 animate-spin" />,
}} }}
toastOptions={{
classNames: {
description: "text-foreground opacity-90",
title: "font-bold",
},
}}
style={ style={
{ {
"--normal-bg": "var(--popover)", "--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)", "--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)", "--normal-border": "var(--border)",
"--border-radius": "var(--radius)", "--border-radius": "var(--radius)",
"--description-color": "var(--popover-foreground)",
} as React.CSSProperties } as React.CSSProperties
} }
{...props} {...props}
/> />
) );
} };
export { Toaster } export { Toaster };

View File

@@ -155,7 +155,7 @@ export default function OrderSummary({
}`} }`}
/> />
{showValidation && name.trim() === "" && ( {showValidation && name.trim() === "" && (
<p className="text-xs text-red-500 mt-1">Bu alan zorunludur</p> <p className="text-xs text-red-500 mt-1">{t("requiredField")}</p>
)} )}
</div> </div>
<div> <div>
@@ -172,7 +172,7 @@ export default function OrderSummary({
}`} }`}
/> />
{showValidation && lastName.trim() === "" && ( {showValidation && lastName.trim() === "" && (
<p className="text-xs text-red-500 mt-1">Bu alan zorunludur</p> <p className="text-xs text-red-500 mt-1">{t("requiredField")}</p>
)} )}
</div> </div>
<div> <div>
@@ -190,7 +190,7 @@ export default function OrderSummary({
/> />
{showValidation && !isPhoneValid && ( {showValidation && !isPhoneValid && (
<p className="text-xs text-red-500 mt-1"> <p className="text-xs text-red-500 mt-1">
Telefon 8 rakamdan oluşmalıdır {t("requiredField")}
</p> </p>
)} )}
</div> </div>
@@ -226,7 +226,7 @@ export default function OrderSummary({
))} ))}
</div> </div>
{showValidation && !paymentType && ( {showValidation && !paymentType && (
<p className="text-xs text-red-500 mt-1">Ödeme türü seçiniz</p> <p className="text-xs text-red-500 mt-1">{t("requiredField")}</p>
)} )}
</div> </div>
@@ -264,7 +264,7 @@ export default function OrderSummary({
))} ))}
</RadioGroup> </RadioGroup>
{showValidation && !selectedRegion && ( {showValidation && !selectedRegion && (
<p className="text-xs text-red-500 mt-1">Bölge seçiniz</p> <p className="text-xs text-red-500 mt-1">{t("requiredField")}</p>
)} )}
</div> </div>
@@ -294,7 +294,7 @@ export default function OrderSummary({
</SelectContent> </SelectContent>
</Select> </Select>
{showValidation && !selectedProvince && ( {showValidation && !selectedProvince && (
<p className="text-xs text-red-500 mt-1">Adres seçiniz</p> <p className="text-xs text-red-500 mt-1">{t("requiredField")}</p>
)} )}
</div> </div>
)} )}

View File

@@ -382,7 +382,7 @@ export default function ProductCard({
) : ( ) : (
<> <>
<ShoppingCart className="h-4 w-4" /> <ShoppingCart className="h-4 w-4" />
{t("checkout")} {t("add_to_cart")}
</> </>
)} )}
</Button> </Button>

View File

@@ -111,7 +111,7 @@ export function ProductInfoCard({
</Card> </Card>
{description && ( {description && (
<Card className="p-4 rounded-xl border-gray-200"> <Card className="p-4 rounded-xl border-gray-200 gap-2">
<h3 className="text-xl font-semibold mb-3"> <h3 className="text-xl font-semibold mb-3">
{t("product_description")} {t("product_description")}
</h3> </h3>

View File

@@ -119,6 +119,24 @@ export default function ProductPageContent({ slug }: ProductDetailProps) {
[product] [product]
); );
const transformedRelatedProducts = useMemo(() => {
if (!relatedProducts) return [];
return relatedProducts.map((p) => ({
id: p.id,
slug: p.slug,
name: p.name,
price_amount: p.price_amount,
old_price_amount: p.old_price_amount ?? undefined,
struct_price_text: `${p.price_amount} TMT`,
discount: null,
discount_text: null,
stock: p.stock,
media: p.media,
labels: [],
price_color: undefined,
}));
}, [relatedProducts]);
useEffect(() => { useEffect(() => {
if (!product?.id || isInitialized) return; if (!product?.id || isInitialized) return;
@@ -246,7 +264,7 @@ export default function ProductPageContent({ slug }: ProductDetailProps) {
} }
} catch (error) { } catch (error) {
setLocalQuantity(cartItem?.product_quantity || 1); setLocalQuantity(cartItem?.product_quantity || 1);
toast.error("Failed to update quantity", { toast.error(t("failed_to_update_quantity"), {
description: "Please try again", description: "Please try again",
}); });
@@ -438,10 +456,10 @@ export default function ProductPageContent({ slug }: ProductDetailProps) {
/> />
<ProductInfoCard <ProductInfoCard
brandName={product.brand?.name} brandName={product.brand?.name ?? undefined}
stock={product.stock} stock={product.stock}
barcode={product.barcode} barcode={product.barcode}
colour={product.colour} colour={product.colour ?? undefined}
properties={product.properties} properties={product.properties}
description={product.description} description={product.description}
averageRating={averageRating} averageRating={averageRating}
@@ -451,7 +469,7 @@ export default function ProductPageContent({ slug }: ProductDetailProps) {
<ProductPurchaseCard <ProductPurchaseCard
price={product.price_amount} price={product.price_amount}
oldPrice={product.old_price_amount} oldPrice={product.old_price_amount ?? undefined}
isInCart={isInCart} isInCart={isInCart}
localQuantity={localQuantity} localQuantity={localQuantity}
availableStock={availableStock} availableStock={availableStock}
@@ -475,7 +493,7 @@ export default function ProductPageContent({ slug }: ProductDetailProps) {
onWriteReview={() => setShowReviewModal(true)} onWriteReview={() => setShowReviewModal(true)}
/> />
<RelatedProductsSection products={relatedProducts || []} /> <RelatedProductsSection products={transformedRelatedProducts} />
</div> </div>
<StockLimitModal <StockLimitModal

View File

@@ -45,9 +45,7 @@ export function ProductPurchaseCard({
<div className="flex justify-between items-start mb-6"> <div className="flex justify-between items-start mb-6">
<span className="text-lg text-gray-500">{t("price")}:</span> <span className="text-lg text-gray-500">{t("price")}:</span>
<div className="flex flex-col items-end"> <div className="flex flex-col items-end">
<span className="text-3xl font-bold text-primary"> <span className="text-3xl font-bold text-primary">{price} TMT</span>
{price} TMT
</span>
{oldPrice && parseFloat(oldPrice) > 0 && ( {oldPrice && parseFloat(oldPrice) > 0 && (
<span className="text-lg text-gray-400 line-through"> <span className="text-lg text-gray-400 line-through">
{oldPrice} TMT {oldPrice} TMT
@@ -102,6 +100,7 @@ export function ProductPurchaseCard({
<Plus className="h-5 w-5" /> <Plus className="h-5 w-5" />
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
size="icon" size="icon"
@@ -123,24 +122,43 @@ export function ProductPurchaseCard({
</div> </div>
</> </>
) : ( ) : (
<Button <div className="flex items-center gap-2">
size="lg" <Button
onClick={onAddToCart} size="lg"
disabled={isSyncing || productStock === 0} onClick={onAddToCart}
className="w-full rounded-lg text-lg font-bold bg-[#005bff] hover:bg-[#0041c4] cursor-pointer" disabled={isSyncing || productStock === 0}
> className="flex-1 rounded-lg text-lg font-bold bg-[#005bff] hover:bg-[#0041c4] cursor-pointer"
{isSyncing ? ( >
<> {isSyncing ? (
<>{t("adding")}</>
) : (
<>
<ShoppingCart className="mr-2 h-5 w-5" />
{productStock === 0 ? t("out_of_stock") : t("add_to_cart")}
</>
)}
</Button>
{t("adding")}
</> <Button
) : ( variant="outline"
<> size="icon"
<ShoppingCart className="mr-2 h-5 w-5" /> onClick={onToggleFavorite}
{productStock === 0 ? t("out_of_stock") : t("add_to_cart")} className={`rounded-lg h-12 w-12 transition-all border cursor-pointer ${
</> isFavorite
)} ? "bg-[#F0F8FF] border-blue-300 hover:bg-blue-100"
</Button> : "hover:bg-gray-50"
}`}
>
<Heart
className={`h-6! w-6! transition-all ${
isFavorite
? "fill-[#005bff] text-[#005bff]"
: "text-[#005bff]"
}`}
/>
</Button>
</div>
)} )}
</div> </div>
</Card> </Card>
@@ -158,7 +176,11 @@ export function ProductPurchaseCard({
<h4 className="text-lg font-bold">{channelName}</h4> <h4 className="text-lg font-bold">{channelName}</h4>
</div> </div>
</div> </div>
<Button variant="outline" size="lg" className="w-full cursor-pointer rounded-lg"> <Button
variant="outline"
size="lg"
className="w-full cursor-pointer rounded-lg"
>
{t("write_to_store")} {t("write_to_store")}
</Button> </Button>
</Card> </Card>

View File

@@ -78,7 +78,7 @@ export default function ClientProfilePage(props: ProfilePageProps) {
const handleSave = useCallback(async () => { const handleSave = useCallback(async () => {
if (!formData.name.trim()) { if (!formData.name.trim()) {
toast.error(t("name_required") || "Name is required"); toast.error(t("requiredField") || "Name is required");
return; return;
} }

View File

@@ -33,9 +33,6 @@
"address_search": "Поиск адреса", "address_search": "Поиск адреса",
"address": "Адрес", "address": "Адрес",
"first_name": "Имя", "first_name": "Имя",
"building": "Дом",
"floor": "Этаж",
"apartment": "Кв",
"save": "Сохранить", "save": "Сохранить",
"enter_phone": "Введите свой номер телефона", "enter_phone": "Введите свой номер телефона",
"code_will_be_sent": "Мы вышлем вам код", "code_will_be_sent": "Мы вышлем вам код",
@@ -102,6 +99,7 @@
"empty_favorites": "У вас пока нет избранных товаров", "empty_favorites": "У вас пока нет избранных товаров",
"removed_from_favorites": "Товар удален из избранного", "removed_from_favorites": "Товар удален из избранного",
"added_to_cart": "Товар добавлен в корзину", "added_to_cart": "Товар добавлен в корзину",
"failed_to_update_quantity": "Количество не удалось обновить",
"removed_from_cart": "Товар удален из корзины", "removed_from_cart": "Товар удален из корзины",
"error": "Произошла ошибка", "error": "Произошла ошибка",
"out_of_stock": "Нет в наличии", "out_of_stock": "Нет в наличии",
@@ -190,7 +188,9 @@
"title": "Открыть магазин", "title": "Открыть магазин",
"enter_email": "Введите email", "enter_email": "Введите email",
"uploadPatent": "Загрузить патент", "uploadPatent": "Загрузить патент",
"outOfStock": "Нет в наличии" "outOfStock": "Нет в наличии",
"requiredField": "Обязательное поле",
"fileRequired": "Файл загрузить"
} }

View File

@@ -33,9 +33,6 @@
"address_search": "Adres gözleg", "address_search": "Adres gözleg",
"address": "Adres", "address": "Adres",
"first_name": "Ady", "first_name": "Ady",
"building": "Jaý",
"floor": "Gat",
"apartment": "Otag",
"save": "Ýatda sakla", "save": "Ýatda sakla",
"enter_phone": "Telefon belgisini giriziň", "enter_phone": "Telefon belgisini giriziň",
"code_will_be_sent": "Biz size kod ugradarys", "code_will_be_sent": "Biz size kod ugradarys",
@@ -78,7 +75,7 @@
"no": "Ýok", "no": "Ýok",
"yes": "Hawa", "yes": "Hawa",
"cart_empty": "Siziň söwda sebediňiz boş", "cart_empty": "Siziň söwda sebediňiz boş",
"add_to_cart": "Söwda sebedine üstünlikli goşuldy", "add_to_cart": "Sebede goş",
"go_to_cart": "Sebede geçmek", "go_to_cart": "Sebede geçmek",
"products": "Azyk harytlary", "products": "Azyk harytlary",
@@ -103,6 +100,7 @@
"empty_favorites": "Siziň saýlanan harytlaryňyz ýok", "empty_favorites": "Siziň saýlanan harytlaryňyz ýok",
"removed_from_favorites": "Haryt saýlanlardan aýryldy", "removed_from_favorites": "Haryt saýlanlardan aýryldy",
"added_to_cart": "Haryt sebede goşuldy", "added_to_cart": "Haryt sebede goşuldy",
"failed_to_update_quantity": "Mukdar täzelenip bolmady",
"removed_from_cart": "Haryt sebetden aýryldy", "removed_from_cart": "Haryt sebetden aýryldy",
"error": "Ýalňyşlyk ýüze çykdy", "error": "Ýalňyşlyk ýüze çykdy",
"out_of_stock": "Haryt ýok", "out_of_stock": "Haryt ýok",
@@ -191,5 +189,7 @@
"title": "Magazin aç", "title": "Magazin aç",
"enter_email": "Poçtaňyzy ýazyň", "enter_email": "Poçtaňyzy ýazyň",
"uploadPatent": "Patent goş", "uploadPatent": "Patent goş",
"outOfStock": "Ammarda ýok" "outOfStock": "Ammarda ýok",
"requiredField": "Zerur maglumat",
"fileRequired": "Fayl goş"
} }

View File

@@ -19,6 +19,7 @@ class APIClient {
constructor() { constructor() {
this.baseUrl = process.env.NEXT_PUBLIC_API_URL || "https://api.example.com"; this.baseUrl = process.env.NEXT_PUBLIC_API_URL || "https://api.example.com";
console.log("API URL:", this.baseUrl);
this.client = axios.create({ this.client = axios.create({
baseURL: `${this.baseUrl}/api/v1`, baseURL: `${this.baseUrl}/api/v1`,
@@ -42,7 +43,7 @@ class APIClient {
} }
// Add language parameter // Add language parameter
let lang = "tk"; let lang = "tm";
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
if ((window as any).i18n?.language) { if ((window as any).i18n?.language) {

View File

@@ -5,7 +5,7 @@ const withNextIntl = createNextIntlPlugin("./i18n/i18n.ts")
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
typescript: { typescript: {
ignoreBuildErrors: true, ignoreBuildErrors: false,
}, },
images: { images: {
unoptimized: true, unoptimized: true,

2243
package-lock.json generated

File diff suppressed because it is too large Load Diff