Vitron-Front/app/routes/profile.setting.tsx
Arda Samadi a67e81ad55 fix(desktop): constrain profile sub-pages so they don't stretch on desktop
These pages are desktop-enabled (they get the DesktopHeader via the /profile
allowlist) but had no desktop layout, so their content stretched edge-to-edge on
wide screens. Add a centered max-width container + a desktop title to the main
profile sub-pages (bookmarks, following, orders, settings, wallet, addresses,
sets). Grids use a wider max-width; single-column lists a narrower one.
ProfilePagesHeader is already lg:hidden, so the mobile header still disappears.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 11:53:28 +03:30

242 lines
8.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { MetaFunction } from "@remix-run/node";
import AppInput from "~/components/AppInput";
import ProfilePagesHeader from "../components/ProfilePagesHeader";
import { useEffect, useState } from "react";
import { days, months, years } from "~/assets/data/consts";
import AppSelectBox from "~/components/AppSelectBox";
import { RadioGroup, RadioGroupItem } from "~/components/ui/radio-group";
import { Button } from "~/components/ui/button";
import {
useServiceGetProfile,
useServiceUpdateProfile,
} from "~/utils/RequestHandler";
import { useToast } from "~/hooks/use-toast";
import { Loader2, Mail, Phone, User } from "lucide-react";
import { UserProfileUpdateSexEnum } from "src/api/types";
import { PushSettingToggle } from "~/components/notifications/PushSettingToggle";
export default function Setting() {
const { data: dataProfile, isFetching: isFetchingProfile } =
useServiceGetProfile();
const [email, setEmail] = useState("");
const [username, setUserName] = useState("");
const [gender, setGender] = useState<UserProfileUpdateSexEnum>("male");
const [phoneNumber, setPhoneNumber] = useState("");
const [birthdayYear, setBirthdayYear] = useState<string>("");
const [birthdayMonth, setBirthdayMonth] = useState<string>("");
const [birthdayDay, setBirthdayDay] = useState<string>("");
useEffect(() => {
if (dataProfile) {
setEmail(dataProfile.email ? dataProfile.email : "");
setUserName(dataProfile.username ? dataProfile.username : "");
setGender(dataProfile.sex ? dataProfile.sex : "male");
setPhoneNumber(dataProfile.phoneNumber ? dataProfile.phoneNumber : "");
setBirthdayYear(
dataProfile.birthdate ? dataProfile.birthdate.split("-")[0] : ""
);
setBirthdayMonth(
dataProfile.birthdate
? dataProfile.birthdate.split("-")[1]?.replace(/^0+/, "")
: ""
);
setBirthdayDay(
dataProfile.birthdate
? dataProfile.birthdate.split("-")[2]?.replace(/^0+/, "")
: ""
);
}
}, [dataProfile]);
const updateProfileMutation = useServiceUpdateProfile({
username: username || null,
email: email || null,
birthdate:
birthdayYear && birthdayMonth && birthdayDay
? `${birthdayYear}-${birthdayMonth}-${birthdayDay}`
: null,
sex: gender,
password: null,
});
const isValidForm = !!username;
const { toast } = useToast();
const onSave = () => {
if (!username) {
toast({
description: "نام کاربری باید پر شود.",
});
return;
}
updateProfileMutation.mutate(undefined, {
onSuccess: () => {
toast({
description: "اطلاعات کاربری با موفقیت بروزرسانی شد.",
});
window.location.href = "/profile";
},
onError: () => {
toast({
description: "خطا در بروزرسانی اطلاعات کاربری.",
variant: "destructive",
});
},
});
};
return (
<div className="flex flex-col gap-2 w-full pb-[100px] lg:max-w-[720px] lg:mx-auto lg:pt-6">
<ProfilePagesHeader title="اطلاعات کاربری" />
<h1 className="hidden lg:block text-[28px] font-extrabold px-5">
اطلاعات کاربری
</h1>
<div className="flex flex-col gap-12 pt-4 w-full px-5">
<AppInput
loading={isFetchingProfile}
inputTitle={"نام کاربری دلخواه خود را انتخاب کنید *"}
inputIcon={User}
inputValue={username}
inputPlaceholder={""}
inputOnChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
// Allow English letters, numbers, and specific special characters
if (
/^[a-zA-Z0-9!@#$%^&*()_+=\-0987654321`~|\\{}[\]<>?,./]*$/.test(
value
)
) {
setUserName(value);
}
}}
/>
<AppInput
loading={isFetchingProfile}
inputTitle={"شماره موبایل (غیر قابل ویرایش)!"}
inputIcon={Phone}
iconProps={{ size: 20, color: "#6B7280", strokeWidth: 2 }}
inputValue={phoneNumber}
inputPlaceholder={"۰۹۳۹۱۲۳۴۵۶۷"}
inputOnChange={() => {}}
disabled={true}
/>
<PushSettingToggle className="border border-WHITE3 rounded-2xl p-4" />
</div>
<Divider className={"my-10"} />
<div className="flex flex-col gap-12 w-full px-5">
<AppInput
loading={isFetchingProfile}
inputTitle={"ایمیل (اختیاری) :"}
inputIcon={Mail}
inputValue={email}
type="email"
inputPlaceholder={"@gmail.com"}
inputOnChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setEmail(value);
}}
/>
<div className="flex flex-col gap-2 w-full">
<p className={"text-R14"}>تاریخ تولد (اختیاری) :</p>
<div className="flex flex-row gap-2 w-full justify-center">
<AppSelectBox
loading={isFetchingProfile}
value={birthdayDay}
setValue={setBirthdayDay}
placeholder="روز: ۲۴"
items={days}
/>
<AppSelectBox
loading={isFetchingProfile}
value={birthdayMonth}
setValue={setBirthdayMonth}
placeholder="ماه: اردیبهشت"
items={months}
/>
<AppSelectBox
loading={isFetchingProfile}
value={birthdayYear}
setValue={setBirthdayYear}
placeholder="سال ۱۳۸۰:"
items={years}
/>
</div>
</div>
<div className="flex flex-row gap-4 w-full">
<p className={"text-R14"}>جنسیت (اختیاری) :</p>
<RadioGroup
className="flex"
onValueChange={(value) =>
setGender(value as UserProfileUpdateSexEnum)
}
value={gender}
>
<div className="flex items-center gap-1">
<label htmlFor="female">خانم</label>
<RadioGroupItem
value={UserProfileUpdateSexEnum.Female}
id="female"
/>
</div>
<div className="flex items-center gap-1">
<label htmlFor="male">آقا</label>
<RadioGroupItem value={UserProfileUpdateSexEnum.Male} id="male" />
</div>
</RadioGroup>
</div>
<div className="max-w-md mx-auto w-full">
<Button
disabled={!isValidForm}
className="w-full bg-VITROWN_BLUE text-white"
size={"xl"}
variant="dark"
onClick={() => onSave()}
>
{updateProfileMutation.isPending ? (
<>
<Loader2 className="animate-spin" />
درحال ذخیره...
</>
) : (
"ثبت اطلاعات"
)}
</Button>
</div>
</div>
</div>
);
}
const Divider = ({ className }: { className?: string }) => {
return <div className={`w-full h-[4px] bg-WHITE2 ${className}`} />;
};
export const meta: MetaFunction = () => {
const title = "ویرایش اطلاعات کاربری | ویترون";
const description = "ویرایش اطلاعات شخصی و تنظیمات حساب کاربری در ویترون";
const imageUrl =
"https://vitrownstatics.s3.ir-thr-at1.arvanstorage.ir/SVG-06.svg?versionId=";
return [
{ title },
{ name: "description", content: description },
{
name: "keywords",
content: "تنظیمات، ویرایش پروفایل، اطلاعات کاربری، ویترون",
},
{ name: "robots", content: "noindex, follow" },
// Open Graph
{ property: "og:type", content: "website" },
{ property: "og:title", content: title },
{ property: "og:description", content: description },
{ property: "og:image", content: imageUrl },
{ property: "og:site_name", content: "ویترون" },
{ property: "og:locale", content: "fa_IR" },
// Twitter Card
{ name: "twitter:card", content: "summary" },
{ name: "twitter:title", content: title },
{ name: "twitter:description", content: description },
];
};