This commit is contained in:
payacom
2026-07-07 15:01:04 +03:30
parent d234c7f859
commit 378cf86fa6
57 changed files with 788 additions and 647 deletions

View File

@@ -74,7 +74,8 @@ function ChangePassword() {
<AuthPasswordInput
name="password"
placeholder="کلمه عبور جدید"
className={`border mt-4 ${
wrapperClassName="mt-4"
className={`border ${
formik.touched.password && formik.errors.password
? "border-red-500 dark:border-red-500"
: "border-gray-300"
@@ -92,7 +93,8 @@ function ChangePassword() {
<AuthPasswordInput
name="confirmPassword"
placeholder="تکرار کلمه عبور"
className={`border mt-4 ${
wrapperClassName="mt-4"
className={`border ${
formik.touched.confirmPassword && formik.errors.confirmPassword
? "border-red-500 dark:border-red-500"
: "border-gray-300"
@@ -107,8 +109,8 @@ function ChangePassword() {
</small>
)}
<AuthButton type="submit" className="mt-5" disabled={loading}>
{loading ? "در حال ارسال..." : "تغییر کلمه عبور"}
<AuthButton type="submit" className="mt-5" loading={loading} disabled={loading}>
تغییر کلمه عبور
</AuthButton>
</form>

View File

@@ -96,9 +96,9 @@ function ForgetPasswordOtp() {
<AuthButton
type="submit"
className="mt-5"
disabled={loading || otpExpired || formik.values.otp.length !== 6}
loading={loading} disabled={loading || otpExpired || formik.values.otp.length !== 6}
>
{loading ? "در حال ارسال..." : "تایید کد"}
تایید کد
</AuthButton>
</form>
<Link href={"/login-with-username"}>

View File

@@ -6,6 +6,7 @@ import AuthInput from "@/components/auth/AuthInput";
import Container from "@/components/elements/Container";
import Link from "next/link";
import React from "react";
import { useRouter } from "next/navigation";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
import * as yup from "yup";
@@ -52,6 +53,7 @@ function ForgetPassword() {
name="mobile"
maxLength={11}
type="text"
dir="ltr"
placeholder="09 _ _ _ _ _ _ _ _ _"
className={`border ${
formik.touched.mobile && formik.errors.mobile
@@ -67,8 +69,8 @@ function ForgetPassword() {
{formik.errors.mobile}
</small>
)}
<AuthButton type="submit" className="mt-5" disabled={loading}>
{loading ? "در حال ارسال..." : "ارسال کد"}
<AuthButton type="submit" className="mt-5" loading={loading} disabled={loading}>
ارسال کد
</AuthButton>
</form>
<Link href={"/register"}>

View File

@@ -104,7 +104,8 @@ function LoginWithUsername() {
<AuthPasswordInput
name="password"
placeholder="کلمه عبور"
className={`border mt-4 ${
wrapperClassName="mt-4"
className={`border ${
formik.touched.password && formik.errors.password
? "border-red-500 dark:border-red-500"
: "border-gray-300"
@@ -118,8 +119,8 @@ function LoginWithUsername() {
{formik.errors.password}
</small>
)}
<AuthButton type="submit" className="mt-5" disabled={loading}>
{loading ? "در حال ارسال..." : "ورود"}
<AuthButton type="submit" className="mt-5" loading={loading} disabled={loading}>
ورود
</AuthButton>
</form>
<Link href={"/forget-password"}>

View File

@@ -10,6 +10,7 @@ import React, { useState } from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
import * as yup from "yup";
import { useRouter } from "next/navigation";
import { markOtpSent } from "@/lib/auth/otpTimer";
// Validation schema using Yup
@@ -54,7 +55,8 @@ function Login() {
name="mobile"
maxLength={11}
type="tel"
pattern="[0-9]*"
pattern="[0-9]*"
dir="ltr"
placeholder="09 _ _ _ _ _ _ _ _ _"
className={`border ${
formik.touched.mobile && formik.errors.mobile
@@ -70,8 +72,8 @@ function Login() {
{formik.errors.mobile}
</small>
)}
<AuthButton type="submit" className="mt-5" disabled={loading}>
{loading ? "در حال ارسال..." : "ارسال کد"}
<AuthButton type="submit" className="mt-5" loading={loading} disabled={loading}>
ارسال کد
</AuthButton>
</form>
<Link href={"/login-with-username"}>

View File

@@ -133,9 +133,9 @@ function VerifyOtp() {
<AuthButton
type="submit"
className="mt-5"
disabled={loading || otpExpired || formik.values.otp.length !== 6}
loading={loading} disabled={loading || otpExpired || formik.values.otp.length !== 6}
>
{loading ? "در حال ارسال..." : "تایید کد"}
تایید کد
</AuthButton>
</form>
<Link href={"/login-with-username"}>

View File

@@ -105,9 +105,9 @@ function RegisterOtp() {
<AuthButton
type="submit"
className="mt-5"
disabled={loading || otpExpired || formik.values.otp.length !== 6}
loading={loading} disabled={loading || otpExpired || formik.values.otp.length !== 6}
>
{loading ? "در حال ارسال..." : "تایید کد"}
تایید کد
</AuthButton>
</form>
<Link href={"/forget-password"}>

View File

@@ -3,7 +3,10 @@
import AuthHead from "@/components/auth/AuthHead";
import AuthInput from "@/components/auth/AuthInput";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -45,17 +48,19 @@ function FullNamePage() {
});
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<AuthHead title="نام و نام خانوادگی" />
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<AuthPageLayout>
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<AuthHead title="نام و نام خانوادگی" />
<div className="w-full max-w-sm flex flex-col items-center">
<AuthInput
name="name"
type="text"
placeholder="نام"
dir="rtl"
className={`border mt-4 ${
formik.touched.name && formik.errors.name
? "border-red-500 dark:border-red-500"
@@ -66,7 +71,7 @@ function FullNamePage() {
onBlur={formik.handleBlur}
/>
{formik.touched.name && formik.errors.name && (
<small className="text-red-500 mt-2 block">
<small className="text-red-500 mt-2 block text-center">
{formik.errors.name}
</small>
)}
@@ -75,6 +80,7 @@ function FullNamePage() {
name="family"
type="text"
placeholder="نام خانوادگی"
dir="rtl"
className={`border mt-4 ${
formik.touched.family && formik.errors.family
? "border-red-500 dark:border-red-500"
@@ -85,17 +91,20 @@ function FullNamePage() {
onBlur={formik.handleBlur}
/>
{formik.touched.family && formik.errors.family && (
<small className="text-red-500 mt-2 block">
<small className="text-red-500 mt-2 block text-center">
{formik.errors.family}
</small>
)}
</div>
</AuthPageContent>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
<AuthFormFooter>
<AuthNextButton type="submit" loading={loading} disabled={loading}>
ثبت و ادامه
</AuthNextButton>
</form>
</div>
</Container>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -9,6 +9,7 @@ import React, { useState } from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
import * as yup from "yup";
import { useRouter } from "next/navigation";
import { markOtpSent } from "@/lib/auth/otpTimer";
// Validation schema using Yup
@@ -53,6 +54,7 @@ function Register() {
name="mobile"
maxLength={11}
type="text"
dir="ltr"
placeholder="09 _ _ _ _ _ _ _ _ _"
className={`border ${
formik.touched.mobile && formik.errors.mobile
@@ -68,8 +70,8 @@ function Register() {
{formik.errors.mobile}
</small>
)}
<AuthButton type="submit" className="mt-5" disabled={loading}>
{loading ? "در حال ارسال..." : "ارسال کد"}
<AuthButton type="submit" className="mt-5" loading={loading} disabled={loading}>
ارسال کد
</AuthButton>
</form>
<button onClick={() => setModalOpen(true)} className="mb-2">

View File

@@ -3,7 +3,10 @@
import AuthHead from "@/components/auth/AuthHead";
import AuthPasswordInput from "@/components/auth/AuthPasswordInput";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -61,17 +64,19 @@ function PasswordPage() {
});
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<AuthHead title="کلمه عبور" />
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<AuthPageLayout>
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<AuthHead title="کلمه عبور" />
<div className="w-full max-w-sm flex flex-col items-center">
<AuthPasswordInput
name="password"
placeholder="کلمه عبور"
className={`border mt-4 ${
wrapperClassName="mt-4"
className={`border ${
formik.touched.password && formik.errors.password
? "border-red-500 dark:border-red-500"
: "border-gray-300"
@@ -89,7 +94,8 @@ function PasswordPage() {
<AuthPasswordInput
name="confirmPassword"
placeholder="تکرار کلمه عبور"
className={`border mt-4 ${
wrapperClassName="mt-4"
className={`border ${
formik.touched.confirmPassword && formik.errors.confirmPassword
? "border-red-500 dark:border-red-500"
: "border-gray-300"
@@ -103,13 +109,16 @@ function PasswordPage() {
{formik.errors.confirmPassword}
</small>
)}
</div>
</AuthPageContent>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "تایید کلمه عبور"}
<AuthFormFooter>
<AuthNextButton type="submit" loading={loading} disabled={loading}>
تایید کلمه عبور
</AuthNextButton>
</form>
</div>
</Container>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -3,7 +3,10 @@
import AuthHead from "@/components/auth/AuthHead";
import AuthInput from "@/components/auth/AuthInput";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React, { useState } from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -12,12 +15,12 @@ import { IVerifyOtp } from "@/types/types";
import AuthNextButton from "@/components/auth/AuthNextButton";
import UsernameSuggestions from "@/components/auth/UsernameSuggestions";
import { usernameFormSchema } from "@/lib/validation/usernameSchema";
import { sanitizeUsernameInput } from "@/lib/validation/username";
import { sanitizeUsernameInput, USERNAME_MIN_LENGTH } from "@/lib/validation/username";
function UsernamePage() {
const router = useRouter();
const { request, loading } = useAxios();
const [duplicateError, setDuplicateError] = useState<string | null>(null);
const [isDuplicate, setIsDuplicate] = useState(false);
const [suggestions, setSuggestions] = useState<string[]>([]);
const mobile =
typeof window !== "undefined" ? localStorage.getItem("mobile") : null;
@@ -28,7 +31,7 @@ function UsernamePage() {
},
validationSchema: usernameFormSchema,
onSubmit: async (values) => {
setDuplicateError(null);
setIsDuplicate(false);
setSuggestions([]);
try {
@@ -41,16 +44,18 @@ function UsernamePage() {
router.push("/register/password");
} catch (err: any) {
const data = err?.response?.data;
if (data?.message) {
setDuplicateError(data.message);
if (data?.message?.includes("تکراری")) {
setIsDuplicate(true);
setSuggestions(Array.isArray(data.suggestions) ? data.suggestions : []);
} else if (data?.message) {
formik.setFieldError("username", data.message);
}
}
},
});
const handleUsernameChange = (value: string) => {
setDuplicateError(null);
setIsDuplicate(false);
setSuggestions([]);
formik.setFieldValue("username", sanitizeUsernameInput(value));
};
@@ -58,27 +63,28 @@ function UsernamePage() {
const handleSuggestionSelect = (username: string) => {
formik.setFieldValue("username", username);
formik.setFieldTouched("username", true, false);
setDuplicateError(null);
setIsDuplicate(false);
setSuggestions([]);
};
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<AuthHead title="نام کاربری" />
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<AuthPageLayout>
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<AuthHead title="نام کاربری" />
<div className="w-full max-w-sm flex flex-col items-center">
<AuthInput
name="username"
type="text"
placeholder="example_user"
placeholder="نام کاربری"
dir="ltr"
autoComplete="username"
spellCheck={false}
className={`border ${
(formik.touched.username && formik.errors.username) || duplicateError
(formik.touched.username && formik.errors.username) || isDuplicate
? "border-red-500 dark:border-red-500"
: "border-gray-300"
}`}
@@ -86,29 +92,37 @@ function UsernamePage() {
onChange={(e) => handleUsernameChange(e.target.value)}
onBlur={formik.handleBlur}
/>
<small className="text-gray-500 dark:text-gray-400 text-xs mt-2 text-center leading-5">
فقط حروف انگلیسی، اعداد و کاراکترهای . _ - (حداقل ۶ کاراکتر)
</small>
{formik.touched.username && formik.errors.username && !duplicateError && (
<small className="text-red-500 mt-2 block">
{isDuplicate && (
<small className="text-red-500 mt-2 block text-center font-medium">
نام کاربری تکراری است
</small>
)}
{formik.touched.username && formik.errors.username && !isDuplicate && (
<small className="text-red-500 mt-2 block text-center">
{formik.errors.username}
</small>
)}
<UsernameSuggestions
message={duplicateError || undefined}
suggestions={suggestions}
onSelect={handleSuggestionSelect}
/>
</div>
</AuthPageContent>
<AuthFormFooter>
<AuthNextButton
type="submit"
className="mt-20"
disabled={loading || formik.values.username.length < 6}
loading={loading}
disabled={loading || formik.values.username.length < USERNAME_MIN_LENGTH}
>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
ثبت و ادامه
</AuthNextButton>
</form>
</div>
</Container>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -2,7 +2,10 @@
"use client";
import AuthHead from "@/components/auth/AuthHead";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React, { useState } from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -58,14 +61,14 @@ function UsertypePage() {
});
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
{/* <AuthHead title="نوع کاربری" /> */}
<AuthHead title="" />
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<AuthPageLayout>
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<AuthHead title="" />
<div className="w-full max-w-sm flex flex-col items-center">
{/* User Type Buttons */}
{/* <AuthNextButton
type="button"
@@ -90,35 +93,36 @@ function UsertypePage() {
کارفرما
</AuthNextButton> */}
{/* Error Message */}
{formik.errors.selectedButton && formik.touched.selectedButton && (
<div className="text-red-500 mt-2 text-sm">
{formik.errors.selectedButton}
</div>
)}
</div>
</AuthPageContent>
{/* Submit Buttons */}
<AuthFormFooter>
<div className="flex gap-3">
<AuthNextButton
type="submit"
onClick={() => setSubmitAction("register")}
className="mt-20 !border-[#0C8002] !text-[#0C8002]"
className="!border-[#0C8002] !text-[#0C8002]"
disabled={loading}
>
{loading ? "در حال ارسال..." : "ادامه ثبت نام"}
ادامه ثبت نام
</AuthNextButton>
<AuthNextButton
type="submit"
onClick={() => setSubmitAction("login")}
className="mt-20 !border-[#FF0000] !text-[#FF0000]"
className="!border-[#FF0000] !text-[#FF0000]"
disabled={loading}
>
{loading ? "در حال ارسال..." : "ورود به برنامه"}
ورود به برنامه
</AuthNextButton>
</div>
</form>
</div>
</Container>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -69,17 +72,18 @@ function AuthPage() {
console.log(formik.values);
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center auth-page">
<span className="text-xl font-bold">احراز هویت</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<AuthPageLayout className="auth-page">
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<span className="text-xl font-bold">احراز هویت</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<div className="w-full max-w-sm flex flex-col items-center">
<small className="font-bold mt-10 mb-4"> جهت احراز هویت</small>
<div className="flex w-full gap-2 max-w-[300px]">
<div>
@@ -165,19 +169,16 @@ function AuthPage() {
<small className="font-bold mt-10">
شماره شبا باید به نام خود شخص باشد
</small>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
</div>
</AuthPageContent>
<AuthFormFooter onSkip={() => router.push("/verify/location")}>
<AuthNextButton type="submit" loading={loading} disabled={loading}>
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/location")}
type="button"
>
<span>رد کن</span>
</button>
</form>
</div>
</Container>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -1,7 +1,10 @@
"use client";
import React, { useEffect, useState, useCallback } from "react";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import AuthNextButton from "@/components/auth/AuthNextButton";
import { IMAGE_BASE_URL, buildStorageUrl } from "@/components/main/BaseUrl";
import useAxios from "@/hooks/useAxios";
@@ -20,7 +23,6 @@ function AvatarPage() {
const { request, loading } = useAxios();
const [userDetail, setUserDetail] = useState<IProfileData | null>(null);
const [avatar, setAvatar] = useState<string | null>(null);
const [originalImage, setOriginalImage] = useState<string | null>(null); // تصویر اصلی (سایز کامل)
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [loadingUpload, setLoadingUpload] = useState(false);
@@ -50,8 +52,7 @@ function AvatarPage() {
const reader = new FileReader();
reader.onload = (e) => {
const imageDataUrl = e.target?.result as string;
setAvatar(imageDataUrl); // برای کراپر
setOriginalImage(imageDataUrl); // تصویر اصلی (سایز کامل)
setAvatar(imageDataUrl);
};
reader.readAsDataURL(file);
}
@@ -105,7 +106,6 @@ function AvatarPage() {
} catch (err) {
console.log("Upload error:", err);
toast.error("خطا در ارسال تصویر!");
} finally {
setLoadingUpload(false);
}
};
@@ -120,28 +120,11 @@ function AvatarPage() {
};
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
{(loading || loadingUpload) && <p>Loading...</p>}
<AuthPageLayout>
<AuthPageContent>
<span className="text-xl font-bold mb-4">تصویر پروفایل</span>
{!loading && !loadingUpload && (
<>
<span className="text-xl font-bold mb-4">تصویر پروفایل</span>
{/* نمایش تصویر اصلی (سایز کامل) */}
{originalImage && (
<div className="relative w-[300px] h-[400px] bg-gray-200 rounded-2xl mb-4 overflow-hidden">
<Image
src={originalImage}
alt="Original image"
fill
className="object-contain"
/>
</div>
)}
{/* کراپر برای برش تصویر به مربع 1:1 */}
<div className="relative w-[250px] h-[250px] bg-gray-200 rounded-3xl overflow-hidden mb-4">
<div className="relative aspect-square w-[250px] bg-gray-200 rounded-3xl overflow-hidden mb-4">
{avatar ? (
<Cropper
image={avatar.startsWith("data:") ? avatar : IMAGE_BASE_URL + avatar}
@@ -178,31 +161,26 @@ function AvatarPage() {
onChange={selectImage}
/>
<AuthNextButton className="mt-4">
<label className="cursor-pointer w-full h-full" htmlFor="fileInput">
انتخاب/ویرایش تصویر
</label>
</AuthNextButton>
<AuthNextButton className="mt-4">
<label className="cursor-pointer w-full h-full" htmlFor="fileInput">
انتخاب/ویرایش تصویر
</label>
</AuthNextButton>
</AuthPageContent>
<AuthNextButton
onClick={uploadImage}
className="mt-10"
disabled={loadingUpload}
>
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={navigateHandler}
type="button"
>
رد کن
</button>
</>
)}
</div>
</Container>
<AuthFormFooter
onSkip={navigateHandler}
skipDisabled={loadingUpload}
>
<AuthNextButton
onClick={uploadImage}
loading={loadingUpload}
disabled={loadingUpload}
>
ثبت و ادامه
</AuthNextButton>
</AuthFormFooter>
</AuthPageLayout>
);
}

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React, { useState } from "react";
import useAxios from "@/hooks/useAxios";
import * as yup from "yup";
@@ -88,8 +91,8 @@ function Colors() {
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<AuthPageLayout>
<AuthPageContent>
<span className="text-xl font-bold">سایز</span>
<div className="mt-5">
<AuthUserDetails />
@@ -163,24 +166,19 @@ function Colors() {
</div>
)}
</div>
</AuthPageContent>
<AuthFormFooter onSkip={() => router.push("/verify/public-relations")}>
<AuthNextButton
type="button"
onClick={handleCheck}
className="mt-10"
loading={loading}
disabled={loading}
>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/public-relations")}
type="button"
>
<span>رد کن</span>
</button>
</div>
</Container>
</AuthFormFooter>
</AuthPageLayout>
);
}

View File

@@ -8,20 +8,23 @@ import AuthNextButton from "@/components/auth/AuthNextButton";
import Modal from "@/components/elements/Modal";
import Link from "next/link";
import Image from "next/image";
import ProfileAvatar from "@/components/main/ProfileAvatar";
import { useUser } from "@/hooks/useUser";
import { IMAGE_BASE_URL, buildStorageUrl } from "@/components/main/BaseUrl";
function Confirm() {
const user = useUser();
const router = useRouter();
const [showModal, setShowModal] = useState<boolean>(false);
const [navigating, setNavigating] = useState(false);
const userType: string | null =
typeof window !== "undefined" ? localStorage.getItem("usertype") : null;
const confirmHandler = () => {
setNavigating(true);
if (userType !== "employer") {
setShowModal(true);
setNavigating(false);
} else {
router.push("/");
}
@@ -36,13 +39,11 @@ function Confirm() {
ورود شما به خانواده مدستاگرام را تبریک می گوییم{" "}
</small>
<div className="flex flex-col justify-center items-center">
<Image
className="rounded-2xl object-cover"
width={280}
height={280}
<ProfileAvatar
src={user?.profile_image}
alt={user?.user_name || "user profile"}
src={buildStorageUrl(user?.profile_image)}
unoptimized={true}
size="xl"
rounded="2xl"
/>
<div className="flex flex-col justify-center items-center gap-1 mt-1">
<span>
@@ -117,7 +118,7 @@ function Confirm() {
</div>
</div>
{/* */}
<AuthNextButton onClick={confirmHandler} className="mt-20">
<AuthNextButton onClick={confirmHandler} className="mt-20" loading={navigating} disabled={navigating}>
ثبت و ادامه
</AuthNextButton>
<small className="font-bold mt-10 mb-1 text-center">

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -44,20 +47,22 @@ function CooperationType() {
});
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<span className="text-xl font-bold">نوع همکاری</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<AuthPageLayout>
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<span className="text-xl font-bold">نوع همکاری</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<p className="my-10 text-sm font-bold">
آیا مایل به همکاری خارج از محل سکونت خود هستید؟
</p>
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<p className="my-10 text-sm font-bold">
آیا مایل به همکاری خارج از محل سکونت خود هستید؟
</p>
<div className="w-full max-w-sm flex flex-col items-center">
<div className="flex w-full gap-2">
<AuthNextButton
type="button"
@@ -83,26 +88,21 @@ function CooperationType() {
</AuthNextButton>
</div>
{/* Error Message */}
{formik.errors.selectedButton && formik.touched.selectedButton && (
<div className="text-red-500 mt-2 text-sm">
{formik.errors.selectedButton}
</div>
)}
</div>
</AuthPageContent>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
<AuthFormFooter onSkip={() => router.push("/verify/public-relations")}>
<AuthNextButton type="submit" loading={loading} disabled={loading}>
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/public-relations")}
type="button"
>
<span>رد کن</span>
</button>
</form>
</div>
</Container>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React, { useEffect, useState } from "react";
import useAxios from "@/hooks/useAxios";
import * as yup from "yup";
@@ -89,8 +92,8 @@ function Expertise() {
);
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<AuthPageLayout>
<AuthPageContent>
<span className="text-xl font-bold">تخصص</span>
<AuthUserDetails />
@@ -126,25 +129,19 @@ function Expertise() {
</button>
))}
</div>
</AuthPageContent>
<AuthFormFooter onSkip={() => router.push("/verify/services")}>
<AuthNextButton
type="button"
onClick={handleCheck}
className="mt-20"
loading={loading}
disabled={loading}
>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/services")}
type="button"
>
<span>رد کن</span>
</button>
</div>
</Container>
</AuthFormFooter>
</AuthPageLayout>
);
}

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -50,16 +53,18 @@ function Gender() {
});
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<span className="text-xl font-bold">جنسیت</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<AuthPageLayout>
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<span className="text-xl font-bold">جنسیت</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<div className="w-full max-w-sm flex flex-col items-center">
<div className="flex w-full gap-4">
<AuthNextButton
type="button"
@@ -97,26 +102,21 @@ function Gender() {
</AuthNextButton>
</div>
{/* Error Message */}
{formik.errors.selectedButton && formik.touched.selectedButton && (
<div className="text-red-500 mt-2 text-sm">
{formik.errors.selectedButton}
</div>
)}
</div>
</AuthPageContent>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
<AuthFormFooter onSkip={() => router.push("/verify/sizes")}>
<AuthNextButton type="submit" loading={loading} disabled={loading}>
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/sizes")}
type="button"
>
<span>رد کن</span>
</button>
</form>
</div>
</Container>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React, { useEffect, useState } from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -118,20 +121,22 @@ function LocationPage() {
};
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center address-page ">
<span className="text-xl font-bold">لوکیشن</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<AuthPageLayout className="address-page">
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<span className="text-xl font-bold">لوکیشن</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<small className="font-bold mt-10 mb-4">
مشخص کنید در کدام شهر قادر به انجام فعالیت هستید
</small>
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<small className="font-bold mt-10 mb-4">
مشخص کنید در کدام شهر قادر به انجام فعالیت هستید
</small>
<div className="w-full max-w-sm flex flex-col items-center">
<SelectBox
className={`mt-4 ${
formik.touched.stateId && formik.errors.stateId
@@ -230,20 +235,16 @@ function LocationPage() {
اطلاعات لوکیشن شما برای همه قابل نمایش باشد
</span>
</label>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
</AuthNextButton>
</div>
</AuthPageContent>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/national-cart")}
type="button"
>
<span>رد کن</span>
</button>
</form>
</div>
</Container>
<AuthFormFooter onSkip={() => router.push("/verify/national-cart")}>
<AuthNextButton type="submit" loading={loading} disabled={loading}>
ثبت و ادامه
</AuthNextButton>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React, { useState } from "react";
import useAxios from "@/hooks/useAxios";
import { useRouter } from "next/navigation";
@@ -69,8 +72,8 @@ function NationalCart() {
};
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center address-page">
<AuthPageLayout className="address-page">
<AuthPageContent>
<span className="text-xl font-bold">احراز هویت</span>
<div className="mt-5">
<AuthUserDetails />
@@ -146,25 +149,19 @@ function NationalCart() {
</span>
</div>
<AuthRules isModalOpen={isModalOpen} setModalOpen={setModalOpen} />
</AuthPageContent>
<AuthFormFooter onSkip={() => router.push("/verify/confirm")}>
<AuthNextButton
onClick={uploadImage}
className="mt-20"
loading={loading}
disabled={loading}
>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/confirm")}
type="button"
>
رد کن
</button>
<AuthRules isModalOpen={isModalOpen} setModalOpen={setModalOpen} />
</div>
</Container>
</AuthFormFooter>
</AuthPageLayout>
);
}

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
@@ -46,18 +49,18 @@ function PublicRelations() {
});
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<span className="text-xl font-bold">روابط عمومی</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<form
onSubmit={formik.handleSubmit}
className="w-full max-w-sm flex flex-col items-center"
>
<AuthPageLayout>
<form
onSubmit={formik.handleSubmit}
className="flex w-full flex-1 flex-col items-center"
>
<AuthPageContent>
<span className="text-xl font-bold">روابط عمومی</span>
<div className="mt-5">
<AuthUserDetails />
</div>
<div className="w-full max-w-sm flex flex-col items-center">
<small className="font-bold mt-10"> در مورد خودتان چیزی بگویید</small>
<textarea
name="bio"
@@ -85,20 +88,16 @@ function PublicRelations() {
{formik.errors.bio}
</small>
)}
</div>
</AuthPageContent>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
<AuthFormFooter onSkip={() => router.push("/verify/location")}>
<AuthNextButton type="submit" loading={loading} disabled={loading}>
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/location")}
type="button"
>
<span>رد کن</span>
</button>
</form>
</div>
</Container>
</AuthFormFooter>
</form>
</AuthPageLayout>
);
}

View File

@@ -5,7 +5,10 @@ import { Service } from "@/types/types";
import ServiceItem from "@/components/main/Services/ServiceItem";
import AddServiceModal from "@/components/main/Services/AddServiceModal";
import AuthUserDetails from "@/components/auth/AuthUserDetails";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import AuthNextButton from "@/components/auth/AuthNextButton";
import useAxios from "@/hooks/useAxios";
import toast from "react-hot-toast";
@@ -80,8 +83,8 @@ const ServicesPage: React.FC = () => {
};
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center w-full">
<AuthPageLayout>
<AuthPageContent className="w-full">
<span className="text-xl font-bold">خدمات</span>
<div className="mt-5 w-full max-w-md">
@@ -98,23 +101,6 @@ const ServicesPage: React.FC = () => {
))}
</div>
<div className="flex gap-4 mt-6">
<AuthNextButton
onClick={handleSubmit}
disabled={loading}
className="mb-4 w-32"
>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
</AuthNextButton>
<AuthNextButton
onClick={() => setModalOpen(true)}
className="mb-4 !text-[#0066FF] !border-[#0066FF] w-32"
>
افزودن
</AuthNextButton>
</div>
{isModalOpen && (
<AddServiceModal
onClose={() => setModalOpen(false)}
@@ -122,15 +108,28 @@ const ServicesPage: React.FC = () => {
isModalOpen={isModalOpen}
/>
)}
<button
className="text-sm text-gray-500 mt-5"
onClick={Reject}
type="button"
>
<span>رد کن</span>
</button>
</div>
</Container>
</AuthPageContent>
<AuthFormFooter onSkip={Reject}>
<div className="flex gap-4">
<AuthNextButton
onClick={handleSubmit}
loading={loading}
disabled={loading}
className="w-32"
>
ثبت و ادامه
</AuthNextButton>
<AuthNextButton
onClick={() => setModalOpen(true)}
className="!text-[#0066FF] !border-[#0066FF] w-32"
>
افزودن
</AuthNextButton>
</div>
</AuthFormFooter>
</AuthPageLayout>
);
};

View File

@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import Container from "@/components/elements/Container";
import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
import React, { useState } from "react";
import useAxios from "@/hooks/useAxios";
import * as yup from "yup";
@@ -73,8 +76,8 @@ function Sizes() {
};
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
<AuthPageLayout>
<AuthPageContent>
<span className="text-xl font-bold">سایز</span>
<div className="mt-5">
<AuthUserDetails />
@@ -175,24 +178,19 @@ function Sizes() {
</div>
)}
</div>
</AuthPageContent>
<AuthFormFooter onSkip={() => router.push("/verify/colors")}>
<AuthNextButton
type="button"
onClick={handleCheck}
className="mt-10"
loading={loading}
disabled={loading}
>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
ثبت و ادامه
</AuthNextButton>
<button
className="text-sm text-gray-500 mt-5"
onClick={() => router.push("/verify/colors")}
type="button"
>
<span>رد کن</span>
</button>
</div>
</Container>
</AuthFormFooter>
</AuthPageLayout>
);
}

View File

@@ -502,7 +502,7 @@ select {
filter: invert(1) hue-rotate(180deg) brightness(1.2);
}
/* حذف پس‌زمینه سفید اضافی در برخی مرورگرها */
.custom-audio-player::-webkit-media-controls-panel {
background-color: transparent;
}
.profile-avatar {
aspect-ratio: 1 / 1;
object-fit: cover;
}

View File

@@ -149,9 +149,9 @@ function NationalCart() {
<AuthNextButton
onClick={uploadImage}
className="mt-20"
disabled={loading}
loading={loading} disabled={loading}
>
{loading ? "در حال ارسال..." : "ثبت و ادامه"}
ثبت و ادامه
</AuthNextButton>
<button

View File

@@ -6,6 +6,7 @@ import React, { useState, useEffect } from "react";
import AuthNextButton from "@/components/auth/AuthNextButton";
import Image from "next/image";
import ProfileAvatar from "@/components/main/ProfileAvatar";
import { useUser } from "@/hooks/useUser";
import { BASE_URL, IMAGE_BASE_URL, buildStorageUrl } from "@/components/main/BaseUrl";
import useAxios from "@/hooks/useAxios";
@@ -178,17 +179,13 @@ function LicensePage() {
<span className="text-xl mb-9 font-bold">بارگذاری مجوز</span>
<div className="flex flex-col justify-center items-center">
<Image
className="rounded-2xl object-cover"
width={130}
height={130}
<ProfileAvatar
src={user?.profile_image}
alt={user?.user_name || "user profile"}
src={
user?.profile_image
? buildStorageUrl(user.profile_image)
: "/images/placeholder.png"
}
unoptimized={true}
size="md"
rounded="2xl"
className="h-[130px] w-[130px]"
fallback="/images/placeholder.png"
/>
<div className="flex flex-col justify-center items-center gap-1 mt-1">
<span>
@@ -286,7 +283,7 @@ function LicensePage() {
</svg>
</div>
</div>
<AuthNextButton onClick={upload} className="mt-20">
<AuthNextButton onClick={upload} className="mt-20" loading={loadingUpload} disabled={loadingUpload}>
ثبت و ادامه
</AuthNextButton>
<small className="font-bold mt-10 mb-1 text-center">

View File

@@ -58,7 +58,6 @@ function AvatarPage() {
"Content-Type": "multipart/form-data",
},
});
setLoadingUpload(false);
router.push("/settings/edit");
} else {
setLoadingUpload(false);
@@ -80,13 +79,9 @@ function AvatarPage() {
return (
<Container>
<div className="p-4 flex flex-col items-center h-screen justify-center">
{loading || loadingUpload ? (
<p>Loading...</p>
) : (
<>
<div className="flex flex-col items-center mb-4">
<span className="text-xl font-bold">تصویر پروفایل</span>
<div className="relative w-[250px] h-[250px] flex items-center justify-center text-white rounded-3xl border border-[#676767] overflow-hidden mt-10">
<div className="flex flex-col items-center mb-4">
<span className="text-xl font-bold">تصویر پروفایل</span>
<div className="relative aspect-square w-[250px] flex items-center justify-center text-white rounded-3xl border border-[#676767] overflow-hidden mt-10">
{avatar ? (
<>
<img
@@ -96,7 +91,7 @@ function AvatarPage() {
: IMAGE_BASE_URL + avatar
}
alt="Avatar"
className="w-full h-full object-cover"
className="w-full h-full aspect-square object-cover"
/>
<button
onClick={removeImage}
@@ -141,15 +136,14 @@ function AvatarPage() {
</label>
</AuthNextButton>
<AuthNextButton
onClick={uploadImage}
className="mt-20"
disabled={loading}
>
ویرایش
</AuthNextButton>
</>
)}
<AuthNextButton
onClick={uploadImage}
className="mt-20"
loading={loadingUpload}
disabled={loadingUpload || loading}
>
ویرایش
</AuthNextButton>
</div>
</Container>
);

View File

@@ -75,8 +75,8 @@ function PublicRelations() {
</small>
)}
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ویرایش"}
<AuthNextButton type="submit" className="mt-20" loading={loading} disabled={loading}>
ویرایش
</AuthNextButton>
</form>
</div>

View File

@@ -159,9 +159,9 @@ function Colors() {
type="button"
onClick={handleCheck}
className="mt-10"
disabled={loading}
loading={loading} disabled={loading}
>
{loading ? "در حال ارسال..." : "ویرایش"}
ویرایش
</AuthNextButton>
</div>
</Container>

View File

@@ -89,8 +89,8 @@ function CooperationType() {
</div>
)}
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ویرایش"}
<AuthNextButton type="submit" className="mt-20" loading={loading} disabled={loading}>
ویرایش
</AuthNextButton>
</form>
</div>

View File

@@ -134,9 +134,9 @@ function Expertise() {
type="button"
onClick={handleCheck}
className="mt-20"
disabled={loading}
loading={loading} disabled={loading}
>
{loading ? "در حال ارسال..." : "ویرایش"}
ویرایش
</AuthNextButton>
</div>
</Container>

View File

@@ -259,8 +259,8 @@ function LocationPage() {
اطلاعات لوکیشن شما برای همه قابل نمایش باشد
</span>
</label>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ویرایش"}
<AuthNextButton type="submit" className="mt-20" loading={loading} disabled={loading}>
ویرایش
</AuthNextButton>
</form>
</div>

View File

@@ -67,7 +67,8 @@ function PasswordPage() {
<AuthPasswordInput
name="password"
placeholder="کلمه عبور"
className={`border mt-4 ${
wrapperClassName="mt-4"
className={`border ${
formik.touched.password && formik.errors.password
? "border-red-500 dark:border-red-500"
: "border-gray-300"
@@ -85,7 +86,8 @@ function PasswordPage() {
<AuthPasswordInput
name="confirmPassword"
placeholder="تکرار کلمه عبور"
className={`border mt-4 ${
wrapperClassName="mt-4"
className={`border ${
formik.touched.confirmPassword && formik.errors.confirmPassword
? "border-red-500 dark:border-red-500"
: "border-gray-300"
@@ -100,8 +102,8 @@ function PasswordPage() {
</small>
)}
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "تایید کلمه عبور"}
<AuthNextButton type="submit" className="mt-20" loading={loading} disabled={loading}>
تایید کلمه عبور
</AuthNextButton>
</form>
</div>

View File

@@ -123,8 +123,8 @@ function PublicRelations() {
</small>
)}
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ویرایش"}
<AuthNextButton type="submit" className="mt-20" loading={loading} disabled={loading}>
ویرایش
</AuthNextButton>
</form>
</div>

View File

@@ -4,6 +4,7 @@ import React, { useEffect, useState } from "react";
import { Service, User } from "@/types/types";
import ServiceItem from "@/components/main/Services/ServiceItem";
import AddServiceModal from "@/components/main/Services/AddServiceModal";
import AuthUserDetails from "@/components/auth/AuthUserDetails";
import Container from "@/components/elements/Container";
import AuthNextButton from "@/components/auth/AuthNextButton";
import useAxios from "@/hooks/useAxios";
@@ -93,6 +94,10 @@ const ServicesPage: React.FC = () => {
<div className="p-4 flex flex-col items-center h-screen justify-center">
<span className="text-xl font-bold">خدمات</span>
<div className="mt-5 w-full max-w-md">
<AuthUserDetails />
</div>
<div className="gap-4 min-h-96">
{services.map((service) => (
<ServiceItem
@@ -107,10 +112,10 @@ const ServicesPage: React.FC = () => {
<div className="flex gap-4">
<AuthNextButton
onClick={handleSubmit}
disabled={loading}
loading={loading} disabled={loading}
className="mb-4 w-32"
>
{loading ? "در حال ارسال..." : "ویرایش"}
ویرایش
</AuthNextButton>
<AuthNextButton
onClick={() => setModalOpen(true)}

View File

@@ -87,8 +87,8 @@ function AuthPage() {
<small className="font-bold mt-10">
شماره شبا باید به نام خود شخص باشد
</small>
<AuthNextButton type="submit" className="mt-20" disabled={loading}>
{loading ? "در حال ارسال..." : "ویرایش"}
<AuthNextButton type="submit" className="mt-20" loading={loading} disabled={loading}>
ویرایش
</AuthNextButton>
</form>
</div>

View File

@@ -175,9 +175,9 @@ function Sizes() {
type="button"
onClick={handleCheck}
className="mt-10"
disabled={loading}
loading={loading} disabled={loading}
>
{loading ? "در حال ارسال..." : "ویرایش"}
ویرایش
</AuthNextButton>
</div>
</Container>

View File

@@ -11,13 +11,13 @@ import AuthNextButton from "@/components/auth/AuthNextButton";
import Container from "@/components/elements/Container";
import UsernameSuggestions from "@/components/auth/UsernameSuggestions";
import { usernameFormSchema } from "@/lib/validation/usernameSchema";
import { sanitizeUsernameInput } from "@/lib/validation/username";
import { sanitizeUsernameInput, USERNAME_MIN_LENGTH } from "@/lib/validation/username";
function UsernamePage() {
const router = useRouter();
const user = useUser();
const { request, loading } = useAxios();
const [duplicateError, setDuplicateError] = useState<string | null>(null);
const [isDuplicate, setIsDuplicate] = useState(false);
const [suggestions, setSuggestions] = useState<string[]>([]);
const formik = useFormik({
@@ -27,7 +27,7 @@ function UsernamePage() {
validationSchema: usernameFormSchema,
enableReinitialize: true,
onSubmit: async (values) => {
setDuplicateError(null);
setIsDuplicate(false);
setSuggestions([]);
try {
@@ -38,16 +38,18 @@ function UsernamePage() {
router.push("/settings/edit");
} catch (err: any) {
const data = err?.response?.data;
if (data?.message) {
setDuplicateError(data.message);
if (data?.message?.includes("تکراری")) {
setIsDuplicate(true);
setSuggestions(Array.isArray(data.suggestions) ? data.suggestions : []);
} else if (data?.message) {
formik.setFieldError("username", data.message);
}
}
},
});
const handleUsernameChange = (value: string) => {
setDuplicateError(null);
setIsDuplicate(false);
setSuggestions([]);
formik.setFieldValue("username", sanitizeUsernameInput(value));
};
@@ -55,7 +57,7 @@ function UsernamePage() {
const handleSuggestionSelect = (username: string) => {
formik.setFieldValue("username", username);
formik.setFieldTouched("username", true, false);
setDuplicateError(null);
setIsDuplicate(false);
setSuggestions([]);
};
@@ -70,12 +72,12 @@ function UsernamePage() {
<AuthInput
name="username"
type="text"
placeholder="example_user"
placeholder="نام کاربری"
dir="ltr"
autoComplete="username"
spellCheck={false}
className={`border ${
(formik.touched.username && formik.errors.username) || duplicateError
(formik.touched.username && formik.errors.username) || isDuplicate
? "border-red-500 dark:border-red-500"
: "border-gray-300"
}`}
@@ -83,25 +85,30 @@ function UsernamePage() {
onChange={(e) => handleUsernameChange(e.target.value)}
onBlur={formik.handleBlur}
/>
<small className="text-gray-500 dark:text-gray-400 text-xs mt-2 text-center leading-5">
فقط حروف انگلیسی، اعداد و کاراکترهای . _ - (حداقل ۶ کاراکتر)
</small>
{formik.touched.username && formik.errors.username && !duplicateError && (
<small className="text-red-500 mt-2 block">
{isDuplicate && (
<small className="text-red-500 mt-2 block text-center font-medium">
نام کاربری تکراری است
</small>
)}
{formik.touched.username && formik.errors.username && !isDuplicate && (
<small className="text-red-500 mt-2 block text-center">
{formik.errors.username}
</small>
)}
<UsernameSuggestions
message={duplicateError || undefined}
suggestions={suggestions}
onSelect={handleSuggestionSelect}
/>
<AuthNextButton
type="submit"
className="mt-20"
disabled={loading || formik.values.username.length < 6}
loading={loading} disabled={loading || formik.values.username.length < USERNAME_MIN_LENGTH}
>
{loading ? "در حال ارسال..." : "ویرایش"}
ویرایش
</AuthNextButton>
</form>
</div>

View File

@@ -1,7 +1,5 @@
"use client"
import { IMAGE_BASE_URL, buildStorageUrl } from "@/components/main/BaseUrl";
import ProfileAvatar from "@/components/main/ProfileAvatar";
import { User } from "@/types/types";
import Image from "next/image";
import React, { useState } from "react";
import ModelHeadRowTwo from "../models/ModelPage/ModelHeadRowTwo";
import ModelHeadRowThree from "../models/ModelPage/ModelHeadRowThree";
@@ -108,25 +106,13 @@ function ModelHead({ user, item2 }: ModelHeadProps) {
</div>
</div>
</div>
{profile_image ? (
<Image
className="rounded-xl object-cover h-[72px] w-[72px] md:h-[120px] md:w-[120px]"
width={120}
height={120}
alt={user_name}
src={buildStorageUrl(profile_image)}
unoptimized={true}
/>
) : (
<Image
className="rounded-xl object-cover h-[72px] w-[72px] md:h-[120px] md:w-[120px]"
width={120}
height={120}
alt={user_name}
src={`/images/fake-avatar.png`}
unoptimized={true}
/>
)}
<ProfileAvatar
src={profile_image}
alt={user_name}
size="md"
rounded="xl"
className="md:h-[120px] md:w-[120px]"
/>
</div>
<ModelHeadRowTwo
first_name={first_name}

View File

@@ -1,13 +1,42 @@
import React, { ComponentProps } from "react";
type TButton = ComponentProps<"button"> & {};
function AuthButton({ children, style, className, ...rest }: TButton) {
import { cn } from "@/lib/utils";
type TButton = ComponentProps<"button"> & {
loading?: boolean;
loadingText?: string;
};
function AuthButton({
children,
style,
className,
loading = false,
loadingText = "در حال ارسال...",
disabled,
...rest
}: TButton) {
return (
<button
className={`w-full dir-ltr max-w-[290px] p-3 text-center text-white rounded-2xl bg-[#FC8EAC] hover:bg-[#f85e87] font-bold duration-300 ${className}`}
style={{ ...style }}
className={cn(
"w-full dir-ltr max-w-[290px] p-3 text-center text-white rounded-2xl bg-[#FC8EAC] hover:bg-[#f85e87] font-bold duration-300",
loading && "cursor-wait opacity-80",
className
)}
style={style}
disabled={disabled || loading}
{...rest}
>
{children}
{loading ? (
<span className="inline-flex items-center justify-center gap-2">
<span
className="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent"
aria-hidden
/>
<span>{loadingText}</span>
</span>
) : (
children
)}
</button>
);
}

View File

@@ -7,7 +7,7 @@ function AuthInput({ style, className, ...rest }: TInput) {
return (
<input
className={cn(
"w-full dir-ltr max-w-[290px] p-3.5 text-center text-lg font-semibold rounded-2xl",
"w-full max-w-[290px] p-3.5 text-center text-lg font-semibold rounded-2xl",
"border-2 border-gray-200 dark:border-gray-300",
"bg-white text-gray-900 placeholder:text-gray-400",
"dark:bg-white dark:text-gray-900 dark:placeholder:text-gray-500",

View File

@@ -1,13 +1,42 @@
import React, { ComponentProps } from "react";
type TButton = ComponentProps<"button"> & {};
function AuthNextButton({ children, style, className, ...rest }: TButton) {
import { cn } from "@/lib/utils";
type TButton = ComponentProps<"button"> & {
loading?: boolean;
loadingText?: string;
};
function AuthNextButton({
children,
style,
className,
loading = false,
loadingText = "در حال ارسال...",
disabled,
...rest
}: TButton) {
return (
<button
className={`border dir-ltr p-7 py-2 text-center text-border-secondary-light dark:text-border-secondary-dark border-border-secondary-light dark:border-border-secondary-dark rounded-3xl bg-tertiary-light dark:bg-tertiary-dark font-medium duration-300 hover:opacity-70 text-sm ${className}`}
style={{ ...style }}
className={cn(
"border dir-ltr p-7 py-2 text-center text-border-secondary-light dark:text-border-secondary-dark border-border-secondary-light dark:border-border-secondary-dark rounded-3xl bg-tertiary-light dark:bg-tertiary-dark font-medium duration-300 hover:opacity-70 text-sm",
loading && "cursor-wait opacity-70",
className
)}
style={style}
disabled={disabled || loading}
{...rest}
>
{children}
{loading ? (
<span className="inline-flex items-center justify-center gap-2">
<span
className="h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent"
aria-hidden
/>
<span>{loadingText}</span>
</span>
) : (
children
)}
</button>
);
}

View File

@@ -0,0 +1,79 @@
import React from "react";
import Container from "@/components/elements/Container";
import { cn } from "@/lib/utils";
type AuthPageLayoutProps = {
children: React.ReactNode;
className?: string;
};
export function AuthPageContent({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) {
return (
<div
className={cn(
"flex w-full flex-1 flex-col items-center justify-center",
className
)}
>
{children}
</div>
);
}
type AuthFormFooterProps = {
children: React.ReactNode;
onSkip?: () => void;
skipDisabled?: boolean;
className?: string;
};
export function AuthFormFooter({
children,
onSkip,
skipDisabled,
className,
}: AuthFormFooterProps) {
return (
<div
className={cn(
"mt-auto flex w-full flex-col items-center gap-3 pt-6 pb-[max(2rem,env(safe-area-inset-bottom))]",
className
)}
>
{children}
{onSkip && (
<button
type="button"
onClick={onSkip}
disabled={skipDisabled}
className="text-sm text-gray-500 disabled:opacity-50"
>
رد کن
</button>
)}
</div>
);
}
function AuthPageLayout({ children, className }: AuthPageLayoutProps) {
return (
<Container>
<div
className={cn(
"flex min-h-screen flex-col items-center p-4",
className
)}
>
{children}
</div>
</Container>
);
}
export default AuthPageLayout;

View File

@@ -1,7 +1,6 @@
"use client";
import React, { ComponentProps, useState } from "react";
import Image from "next/image";
import { cn } from "@/lib/utils";
type AuthPasswordInputProps = Omit<ComponentProps<"input">, "type"> & {
@@ -35,16 +34,18 @@ function AuthPasswordInput({
<button
type="button"
onClick={() => setVisible((prev) => !prev)}
className="absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-200 transition-colors"
className="absolute right-3 top-1/2 -translate-y-1/2 m-0 flex size-[22px] items-center justify-center rounded p-0 hover:opacity-70 transition-opacity"
aria-label={visible ? "مخفی کردن کلمه عبور" : "نمایش کلمه عبور"}
tabIndex={-1}
>
<Image
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={visible ? "/images/icons/eye-slash.png" : "/images/icons/eye-open.png"}
alt=""
width={22}
height={22}
className="opacity-80"
className="block size-[22px] object-contain object-center opacity-80"
draggable={false}
/>
</button>
</div>

View File

@@ -1,4 +1,5 @@
"use client";
import ProfileAvatar from "@/components/main/ProfileAvatar";
import useAxios from "@/hooks/useAxios";
import { IProfileData } from "@/types/types";
import React, { useEffect, useState } from "react";
@@ -6,7 +7,7 @@ import React, { useEffect, useState } from "react";
function AuthUserDetails() {
const [userDetail, setUserDetail] = useState<IProfileData | null>(null);
const { request } = useAxios();
// Fetch user data
const fetchData = async () => {
try {
const response = await request<{ user: IProfileData }>("GET", "/profile");
@@ -15,17 +16,25 @@ function AuthUserDetails() {
console.log(err);
}
};
useEffect(() => {
fetchData();
}, []);
return (
<>
<div className="mt-5 flex w-full max-w-md flex-col items-center text-center">
<ProfileAvatar
src={userDetail?.profile_image}
alt={userDetail?.user_name || "profile"}
size="md"
rounded="xl"
className="mb-3"
/>
<span className="text-lg font-bold">
{userDetail?.first_name} {userDetail?.last_name}
</span>
<p className="text-sm text-gray-500">{userDetail?.user_name}</p>
</>
</div>
);
}

View File

@@ -1,48 +1,30 @@
"use client";
import { cn } from "@/lib/utils";
import React from "react";
interface UsernameSuggestionsProps {
message?: string;
suggestions?: string[];
suggestions: string[];
onSelect: (username: string) => void;
className?: string;
}
export default function UsernameSuggestions({
message,
suggestions = [],
suggestions,
onSelect,
className,
}: UsernameSuggestionsProps) {
if (!message && suggestions.length === 0) return null;
if (!suggestions.length) return null;
return (
<div className={cn("w-full max-w-sm mt-3 space-y-3", className)}>
{message && (
<p className="text-red-500 text-sm text-center font-medium">{message}</p>
)}
{suggestions.length > 0 && (
<div className="space-y-2">
<p className="text-xs text-gray-600 dark:text-gray-300 text-center">
پیشنهادهای در دسترس:
</p>
<div className="flex flex-wrap justify-center gap-2">
{suggestions.map((item) => (
<button
key={item}
type="button"
onClick={() => onSelect(item)}
dir="ltr"
className="px-3 py-2 rounded-xl border-2 border-[#387E65]/40 bg-white dark:bg-white text-[#387E65] text-sm font-semibold hover:bg-[#387E65]/10 hover:border-[#387E65] transition-colors"
>
{item}
</button>
))}
</div>
</div>
)}
<div className="flex flex-wrap justify-center gap-2 mt-3 w-full max-w-[290px]">
{suggestions.map((username) => (
<button
key={username}
type="button"
onClick={() => onSelect(username)}
className="px-3 py-1.5 text-sm font-medium rounded-full border border-[#387E65] text-[#387E65] bg-white hover:bg-[#387E65]/10 transition-colors dir-ltr"
>
{username}
</button>
))}
</div>
);
}

View File

@@ -1,5 +1,4 @@
"use client"
import { IMAGE_BASE_URL, buildStorageUrl } from "@/components/main/BaseUrl";
import ProfileAvatar from "@/components/main/ProfileAvatar";
import { IAdvertisingProfile } from "@/types/types";
import Image from "next/image";
import React, { useState } from "react";
@@ -40,13 +39,12 @@ function AdProfileHead({ profile, id }: AdProfileHeadProps) {
</div>
</div>
{profile_image ? (
<Image
className="rounded-xl object-cover h-[72px] w-[72px] md:h-[120px] md:w-[120px]"
width={120}
height={120}
<ProfileAvatar
src={profile_image}
alt={profile?.vitrine_name || "Name"}
src={buildStorageUrl(profile_image)}
unoptimized={true}
size="md"
rounded="xl"
className="md:h-[120px] md:w-[120px]"
/>
) : (
<div className="border dark:border-border-primary-dark rounded-3xl p-2">
@@ -92,13 +90,12 @@ function AdProfileHead({ profile, id }: AdProfileHeadProps) {
</div>
</div>
{profile_image ? (
<Image
className="rounded-xl object-cover h-[72px] w-[72px] md:h-[120px] md:w-[120px]"
width={120}
height={120}
<ProfileAvatar
src={profile_image}
alt={profile?.vitrine_name || "Name"}
src={buildStorageUrl(profile_image)}
unoptimized={true}
size="md"
rounded="xl"
className="md:h-[120px] md:w-[120px]"
/>
) : (
<div className="border dark:border-border-primary-dark rounded-3xl p-2">

View File

@@ -5,7 +5,7 @@ import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { FiChevronRight } from "react-icons/fi";
import { IMAGE_BASE_URL, buildStorageUrl } from "../main/BaseUrl";
import ProfileAvatar from "../main/ProfileAvatar";
import { useRouter } from "next/navigation";
import ChatHeaderMenu from "./ChatHeaderMenu";
@@ -42,12 +42,11 @@ function ChatHeader({
className="gentle-transition flex min-w-0 flex-1 items-center gap-2 active:opacity-80"
>
{user?.profile_image && !user?.blocked_you ? (
<Image
width={40}
height={40}
alt=""
src={buildStorageUrl(user.profile_image)}
className="h-10 w-10 shrink-0 rounded-full object-cover"
<ProfileAvatar
src={user.profile_image}
alt={user.user_name || ""}
size="sm"
rounded="full"
/>
) : (
<div className="h-10 w-10 shrink-0 rounded-full bg-neutral-200 dark:bg-neutral-700" />

View File

@@ -0,0 +1,56 @@
import Image from "next/image";
import { cn } from "@/lib/utils";
import { buildStorageUrl } from "./BaseUrl";
const sizeMap = {
xs: "h-8 w-8",
sm: "h-10 w-10",
md: "h-[72px] w-[72px]",
lg: "h-[120px] w-[120px]",
xl: "h-[280px] w-[280px]",
};
type ProfileAvatarProps = {
src?: string | null;
alt?: string;
size?: keyof typeof sizeMap;
rounded?: "2xl" | "xl" | "full";
className?: string;
fallback?: string;
};
function ProfileAvatar({
src,
alt = "",
size = "md",
rounded = "2xl",
className,
fallback = "/images/fake-avatar.png",
}: ProfileAvatarProps) {
const roundedClass =
rounded === "full"
? "rounded-full"
: rounded === "xl"
? "rounded-xl"
: "rounded-2xl";
const imageSrc = src ? buildStorageUrl(src) : fallback;
return (
<Image
src={imageSrc}
alt={alt}
width={280}
height={280}
unoptimized
className={cn(
"profile-avatar aspect-square object-cover shrink-0",
sizeMap[size],
roundedClass,
className
)}
/>
);
}
export default ProfileAvatar;

View File

@@ -1,7 +1,7 @@
import Image from "next/image";
import React from "react";
import { IMAGE_BASE_URL, buildStorageUrl } from "./BaseUrl";
import Link from "next/link";
import ProfileAvatar from "./ProfileAvatar";
interface IUserInfoProps {
profile_image: string | null | undefined;
@@ -22,50 +22,22 @@ function UserInfo({
is_verified,
noLink,
}: IUserInfoProps) {
const avatar = (
<ProfileAvatar
src={profile_image}
alt={user_name || ""}
size="md"
rounded="2xl"
className="ml-3"
/>
);
return (
<div className="flex items-center text-xs max-sm:text-[10px] md:text-sm font-semibold">
{noLink ? (
profile_image ? (
<Image
className="rounded-2xl object-cover h-[72px] w-[72px] ml-3"
width={75}
height={75}
alt={user_name || ""}
src={buildStorageUrl(profile_image)}
unoptimized={true}
/>
) : (
<Image
className="rounded-2xl object-cover h-[72px] w-[72px] ml-3"
width={75}
height={75}
alt={user_name || ""}
src={`/images/fake-avatar.png`}
unoptimized={true}
/>
)
avatar
) : (
<Link href={`/users/${user_name}`}>
{profile_image ? (
<Image
className="rounded-2xl object-cover h-[72px] w-[72px] ml-3"
width={75}
height={75}
alt={user_name || ""}
src={buildStorageUrl(profile_image)}
unoptimized={true}
/>
) : (
<Image
className="rounded-2xl object-cover h-[72px] w-[72px] ml-3"
width={75}
height={75}
alt={user_name || ""}
src={`/images/fake-avatar.png`}
unoptimized={true}
/>
)}
</Link>
<Link href={`/users/${user_name}`}>{avatar}</Link>
)}
<div className="flex flex-col gap-1">
<span className="text-[#387E65]">
@@ -88,14 +60,16 @@ function UserInfo({
alt={"not-verify icon"}
src={`/images/icons/not-verify.svg`}
/>
) :is_verified === "true" ? (
) : is_verified === "true" ? (
<Image
width={22}
height={22}
alt="verify icon"
src="/images/icons/verify2.svg"
/>
): ("")}
) : (
""
)}
</h2>
</div>
</div>

View File

@@ -120,7 +120,7 @@ function CommentsModal({ isOpen, onClose, userId, postId }: CommentsModalProps)
className="mb-4 flex flex-row border-b border-neutral-100 pb-2 dark:border-neutral-800"
>
<Image
className="ml-2 min-h-16 max-h-16 min-w-16 max-w-16 rounded-xl"
className="ml-2 aspect-square min-h-16 max-h-16 min-w-16 max-w-16 rounded-xl object-cover"
width={50}
height={50}
alt={item?.user?.user_name}

View File

@@ -175,7 +175,7 @@ function MainModelCard({ postData }: { postData: Post }) {
}
alt={user_name}
fill
className="rounded-xl object-cover"
className="rounded-xl object-cover aspect-square"
priority
unoptimized
/>

View File

@@ -174,7 +174,7 @@ function MainModelCard({ postData }: { postData: Post }) {
}
alt={user_name}
fill
className="rounded-xl object-cover"
className="rounded-xl object-cover aspect-square"
priority
/>
</div>

View File

@@ -1,7 +1,5 @@
"use client"
import { IMAGE_BASE_URL, buildStorageUrl } from "@/components/main/BaseUrl";
import ProfileAvatar from "@/components/main/ProfileAvatar";
import { User } from "@/types/types";
import Image from "next/image";
import React, { useState } from "react";
import ModelHeadRowTwo from "./ModelHeadRowTwo";
import ModelHeadRowThree from "./ModelHeadRowThree";
@@ -108,25 +106,13 @@ function ModelHead({ user, item2 }: ModelHeadProps) {
</div>
</div>
</div>
{profile_image ? (
<Image
className="rounded-xl object-cover h-[72px] w-[72px] md:h-[120px] md:w-[120px]"
width={120}
height={120}
alt={user_name}
src={buildStorageUrl(profile_image)}
unoptimized={true}
/>
) : (
<Image
className="rounded-xl object-cover h-[72px] w-[72px] md:h-[120px] md:w-[120px]"
width={120}
height={120}
alt={user_name}
src={`/images/fake-avatar.png`}
unoptimized={true}
/>
)}
<ProfileAvatar
src={profile_image}
alt={user_name}
size="md"
rounded="xl"
className="md:h-[120px] md:w-[120px]"
/>
</div>
<ModelHeadRowTwo
first_name={first_name}

View File

@@ -1,7 +1,7 @@
import { IProjectCreator } from "@/types/types";
import React from "react";
import Image from "next/image";
import { IMAGE_BASE_URL, buildStorageUrl } from "@/components/main/BaseUrl";
import ProfileAvatar from "@/components/main/ProfileAvatar";
import Link from "next/link";
function ProjectCreator({ creator }: { creator: IProjectCreator }) {
@@ -11,13 +11,12 @@ function ProjectCreator({ creator }: { creator: IProjectCreator }) {
className="w-full flex items-center my-3"
>
{creator?.profile_image && (
<Image
className="rounded-2xl object-cover h-[72px] w-[72px] md:h-[120px] md:w-[120px] ml-2"
width={120}
height={120}
<ProfileAvatar
src={creator.profile_image}
alt={creator?.user_name}
src={buildStorageUrl(creator?.profile_image)}
unoptimized={true}
size="md"
rounded="2xl"
className="ml-2 md:h-[120px] md:w-[120px]"
/>
)}
<div className="flex flex-col gap-1">

View File

@@ -2,7 +2,7 @@
import Image from "next/image";
import React, { useEffect, useState } from "react";
import { IMAGE_BASE_URL, buildStorageUrl } from "../main/BaseUrl";
import ProfileAvatar from "../main/ProfileAvatar";
import CompleteRegister from "./settings/CompleteRegister";
import Link from "next/link";
import { useUser } from "@/hooks/useUser";
@@ -10,27 +10,23 @@ import { useUser } from "@/hooks/useUser";
function UserDetails() {
const [isRegister, setIsRegister] = useState<string | undefined>("false");
const user = useUser();
useEffect(() => {
setIsRegister(user?.is_Register);
}, [user]);
return (
<div>
<Link
href={"/settings/profile"}
className="flex items-center text-xs md:text-sm font-semibold"
>
<Image
className="rounded-2xl object-cover h-[72px] w-[72px] md:h-[120px] md:w-[120px] ml-3"
width={120}
height={120}
<ProfileAvatar
src={user?.profile_image}
alt={user?.user_name || "user profile"}
src={buildStorageUrl(user?.profile_image)}
unoptimized={true}
size="md"
rounded="2xl"
className="ml-3 md:h-[120px] md:w-[120px]"
/>
<div className="flex flex-col gap-1">
<span className="text-[#387E65]">
@@ -43,29 +39,29 @@ function UserDetails() {
<div className="flex items-center gap-1 mt-1">
{user?.user_name}
{isRegister === "verified" ? (
<Image
width={22}
height={22}
alt={"verify icon"}
src={`/images/icons/verify.svg`}
/>
) : isRegister === "pending" ? (
<Image
width={25}
height={25}
alt={"not-verify icon"}
src={`/images/icons/not-verify.svg`}
/>
) : isRegister === "true" ? (
<Image
width={22}
height={22}
alt="verify icon"
src="/images/icons/verify2.svg"
/>
) : (
""
)}
<Image
width={22}
height={22}
alt={"verify icon"}
src={`/images/icons/verify.svg`}
/>
) : isRegister === "pending" ? (
<Image
width={25}
height={25}
alt={"not-verify icon"}
src={`/images/icons/not-verify.svg`}
/>
) : isRegister === "true" ? (
<Image
width={22}
height={22}
alt="verify icon"
src="/images/icons/verify2.svg"
/>
) : (
""
)}
<span className="mr-4">{user?.mobile}</span>
</div>
</div>