diff --git a/public/images/icons/verify-blue.svg b/public/images/icons/verify-blue.svg new file mode 100644 index 0000000..1f73680 --- /dev/null +++ b/public/images/icons/verify-blue.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/public/images/icons/verify-gold.svg b/public/images/icons/verify-gold.svg new file mode 100644 index 0000000..a17ad67 --- /dev/null +++ b/public/images/icons/verify-gold.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/public/images/icons/verify-gray.svg b/public/images/icons/verify-gray.svg new file mode 100644 index 0000000..c0f61f3 --- /dev/null +++ b/public/images/icons/verify-gray.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/public/images/icons/verify-green.svg b/public/images/icons/verify-green.svg new file mode 100644 index 0000000..e584428 --- /dev/null +++ b/public/images/icons/verify-green.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/src/api/fetchStories.ts b/src/api/fetchStories.ts index 4b72eaa..346eb93 100644 --- a/src/api/fetchStories.ts +++ b/src/api/fetchStories.ts @@ -1,6 +1,8 @@ import { getApiBaseUrl } from "@/components/main/BaseUrl"; +import type { PostLinkKind } from "@/lib/postLinkCaption"; export type StoryTextOverlay = { + type?: "text"; text: string; x: number; y: number; @@ -11,12 +13,25 @@ export type StoryTextOverlay = { align?: "left" | "center" | "right"; }; +/** اورلی لینک روی استوری — همون ۶ مورد قابل‌لینک ثبت پست (پکیج/پروژه/بیلبورد/فروشگاه/کالا/رزرو) */ +export type StoryLinkOverlay = { + type: "link"; + link_kind: PostLinkKind; + link_id: string; + link_title: string; + x: number; + y: number; + rotation?: number; +}; + +export type StoryOverlay = StoryTextOverlay | StoryLinkOverlay; + export type StoryItem = { _id: string; media_path: string; media_type: "image" | "video"; linked_post_id?: string | null; - overlays?: StoryTextOverlay[]; + overlays?: StoryOverlay[]; createdAt?: string; expires_at?: string; viewed?: boolean; @@ -31,6 +46,7 @@ export type StoryFeedUser = { first_name: string; last_name: string; profile_image?: string; + verify_badge?: string; }; stories: StoryItem[]; has_unviewed: boolean; @@ -73,7 +89,7 @@ export async function markStoryViewed( export async function createStoryBase64( file: { name: string; type: string; data: string }, token: string, - overlays: StoryTextOverlay[] = [] + overlays: StoryOverlay[] = [] ): Promise { const res = await fetch(`${getApiBaseUrl()}/stories/create-base64`, { method: "POST", @@ -91,7 +107,7 @@ export async function createStoryBase64( export async function updateStoryOverlays( storyId: string, - overlays: StoryTextOverlay[], + overlays: StoryOverlay[], token: string ): Promise { const res = await fetch(`${getApiBaseUrl()}/stories/${storyId}`, { diff --git a/src/api/trackExploreInteraction.ts b/src/api/trackExploreInteraction.ts index 7425539..cf13a53 100644 --- a/src/api/trackExploreInteraction.ts +++ b/src/api/trackExploreInteraction.ts @@ -1,10 +1,16 @@ import { getApiBaseUrl } from "@/components/main/BaseUrl"; export type ExploreInteractionPayload = { - targetType: "post" | "profile" | "academy"; + targetType: + | "post" + | "profile" + | "academy" + | "shop_product" + | "academy_course" + | "billboard"; targetId: string; authorId?: string; - contentType?: "image" | "video" | "academy"; + contentType?: "image" | "video" | "academy" | "shop_product" | "billboard"; action?: | "view" | "profile_visit" diff --git a/src/app/(auth)/verify/confirm/page.tsx b/src/app/(auth)/verify/confirm/page.tsx index 155444d..cc7dc66 100644 --- a/src/app/(auth)/verify/confirm/page.tsx +++ b/src/app/(auth)/verify/confirm/page.tsx @@ -27,14 +27,14 @@ function Confirm() { useEffect(() => { const markComplete = async () => { try { - const response = await request<{ is_verified?: string }>( + const response = await request<{ verify_badge?: string }>( "POST", "/verify/complete", {} ); - setVerifiedStatus(response?.is_verified ?? "none"); + setVerifiedStatus(response?.verify_badge ?? "none"); } catch { - setVerifiedStatus(user?.is_verified ?? "none"); + setVerifiedStatus(user?.verify_badge ?? "none"); } }; markComplete(); @@ -62,6 +62,8 @@ function Confirm() { alt={user?.user_name || "user profile"} size="xl" rounded="2xl" + userId={user?._id} + verifyBadge={verifiedStatus ?? user?.verify_badge} />
{formatFullName(user?.first_name, user?.last_name)} @@ -69,7 +71,7 @@ function Confirm() { {user?.user_name}
diff --git a/src/app/(projects)/academy/[id]/[title]/CourseClient.tsx b/src/app/(projects)/academy/[id]/[title]/CourseClient.tsx index f0ed792..332c603 100644 --- a/src/app/(projects)/academy/[id]/[title]/CourseClient.tsx +++ b/src/app/(projects)/academy/[id]/[title]/CourseClient.tsx @@ -28,6 +28,7 @@ import VerificationBadge from "@/components/main/VerificationBadge"; import ProfileAvatar from "@/components/main/ProfileAvatar"; import { usePathname } from "next/navigation"; import Cookies from "js-cookie"; +import { trackExploreInteraction } from "@/api/trackExploreInteraction"; import BillboardContactInfoModal from "@/components/billboards/BillboardPage/BillboardContactInfoModal"; import { AcademyCourseDetailSkeleton } from "@/components/academy/AcademySkeletons"; import { useTranslation } from "react-i18next"; @@ -372,6 +373,24 @@ export default function CourseDetail({ } }, [id, request]); + useEffect(() => { + const course = courses[0]; + if (!course?._id) return; + const token = Cookies.get("token") || ""; + if (!token) return; + trackExploreInteraction( + { + targetType: "academy_course", + targetId: course._id, + authorId: course.user_id || course.academyId, + contentType: "academy", + action: "view", + }, + token + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [courses[0]?._id]); + // خرید دوره // const handlePurchase = async () => { // setPurchasing(true); @@ -743,6 +762,8 @@ useEffect(() => { size="chat" rounded="full" fallback="/images/default-avatar.png" + userId={typeof item?.user_id === "object" ? item.user_id?._id : undefined} + verifyBadge={typeof item?.user_id === "object" ? item.user_id?.verify_badge : undefined} /> @@ -751,7 +772,7 @@ useEffect(() => {
{item?.user_id?.user_name || t("common.user")} - + {item?.rate > 0 && ( diff --git a/src/app/(projects)/academy/profile/[academyId]/AcademyProfileClient.tsx b/src/app/(projects)/academy/profile/[academyId]/AcademyProfileClient.tsx index e4aa898..c006a7a 100644 --- a/src/app/(projects)/academy/profile/[academyId]/AcademyProfileClient.tsx +++ b/src/app/(projects)/academy/profile/[academyId]/AcademyProfileClient.tsx @@ -33,6 +33,7 @@ interface AcademyOwner { first_name?: string; last_name?: string; is_verified?: string; + verify_badge?: string; is_Register?: string | boolean; user_score?: string | number; } @@ -199,7 +200,7 @@ export default function AcademyProfileClient() { const displayUserName = owner?.user_name || ownerFromApi?.user_name; const displayUserScore = owner?.user_score ?? ownerFromApi?.user_score ?? 0; - const isVerified = owner?.is_verified ?? ownerFromApi?.is_verified; + const isVerified = owner?.verify_badge ?? ownerFromApi?.verify_badge; const isRegister = owner?.is_Register ?? ownerFromApi?.is_Register; const stats = academy.stats; const shareUrl = `modstagram.com/academy/profile/${academyId}`; @@ -250,6 +251,8 @@ export default function AcademyProfileClient() { : undefined } alt={academyName} + userId={owner?._id ?? ownerFromApi?._id} + verifyBadge={isVerified} size="md" rounded="xl" className="md:h-[120px] md:w-[120px]" @@ -293,10 +296,7 @@ export default function AcademyProfileClient() { )} > {displayUserName} - + ) : null}
diff --git a/src/app/booking/[userId]/page.tsx b/src/app/booking/[userId]/page.tsx index 8a1a073..7cc768a 100644 --- a/src/app/booking/[userId]/page.tsx +++ b/src/app/booking/[userId]/page.tsx @@ -31,15 +31,21 @@ type TimeSlot = { start_time: string; end_time: string }; const DAY_KEYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]; -function buildUpcomingDates(config: BookingConfig, count = 21) { +function buildUpcomingDates( + config: BookingConfig, + repeatExpiresAt: string | null, + count = 21 +) { const openDays = new Set(config.weeklySchedule.map((entry) => entry.day)); const holidaySet = new Set(config.holidayDates.map((d) => d.slice(0, 10))); + const expiresAtTime = repeatExpiresAt ? new Date(repeatExpiresAt).getTime() : null; const dates: string[] = []; const cursor = new Date(); for (let i = 0; i < 60 && dates.length < count; i++) { const date = new Date(cursor); date.setDate(cursor.getDate() + i); + if (expiresAtTime !== null && date.getTime() >= expiresAtTime) break; const iso = date.toISOString().slice(0, 10); const dayKey = DAY_KEYS[date.getDay()]; if (openDays.has(dayKey) && !holidaySet.has(iso)) { @@ -56,6 +62,7 @@ function BookingFlowPage() { const params = useParams<{ userId: string }>(); const [config, setConfig] = useState(undefined); + const [repeatExpiresAt, setRepeatExpiresAt] = useState(null); const [step, setStep] = useState<"service" | "datetime" | "confirm">("service"); const [selectedService, setSelectedService] = useState(null); const [selectedDate, setSelectedDate] = useState(null); @@ -63,16 +70,19 @@ function BookingFlowPage() { const [selectedSlot, setSelectedSlot] = useState(null); useEffect(() => { - request<{ config: BookingConfig | null }>( + request<{ config: BookingConfig | null; repeatExpiresAt?: string | null }>( "GET", `/bookings/public/${params.userId}` - ).then((res) => setConfig(res?.config ?? null)); + ).then((res) => { + setConfig(res?.config ?? null); + setRepeatExpiresAt(res?.repeatExpiresAt ?? null); + }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [params.userId]); const upcomingDates = useMemo( - () => (config ? buildUpcomingDates(config) : []), - [config] + () => (config ? buildUpcomingDates(config, repeatExpiresAt) : []), + [config, repeatExpiresAt] ); useEffect(() => { diff --git a/src/app/gift/[userId]/page.tsx b/src/app/gift/[userId]/page.tsx index 8c2abef..63a8b4b 100644 --- a/src/app/gift/[userId]/page.tsx +++ b/src/app/gift/[userId]/page.tsx @@ -93,7 +93,8 @@ function GiftPage({ params }: GiftPageProps) {
([]); - const [storyOverlays, setStoryOverlays] = useState([]); + const [selectedBooking, setSelectedBooking] = useState( + [] + ); + const [storyOverlays, setStoryOverlays] = useState([]); const [videoCover, setVideoCover] = useState(null); const [existingVideoPath, setExistingVideoPath] = useState(null); const [locationNoticeOpen, setLocationNoticeOpen] = useState(false); const [expertiseModalOpen, setExpertiseModalOpen] = useState(false); const [loadingEdit, setLoadingEdit] = useState(false); + // شهر/استان اختیاری پست — مستقل از موقعیت پروفایل کاربر، مثل بیلبورد/پروژه + const [provinces, setProvinces] = useState(null); + const [cities, setCities] = useState(null); + const [provinceId, setProvinceId] = useState(""); + const [cityId, setCityId] = useState(""); + + useEffect(() => { + request<{ provinces: IProvince[] }>("GET", "/provinces") + .then((res) => setProvinces(res?.provinces || null)) + .catch(() => {}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (!provinceId) { + setCities(null); + return; + } + request<{ cities: ICity[] }>("GET", `/cities/${provinceId}`) + .then((res) => setCities(res?.cities || [])) + .catch(() => {}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [provinceId]); + const modeLabels: Record = useMemo( () => ({ image: t("posts.modeImage"), @@ -310,7 +338,8 @@ export default function NewPostPage() { selectedBillboards, captionLang, selectedShops, - selectedShopProducts + selectedShopProducts, + selectedBooking ); if (caption.length > POST_CAPTION_MAX_LENGTH) { @@ -442,6 +471,9 @@ export default function NewPostPage() { linked_billboard_id: selectedBillboards[0]?._id ?? null, linked_shop_id: selectedShops[0]?._id ?? null, linked_shop_product_id: selectedShopProducts[0]?._id ?? null, + linked_booking_id: selectedBooking[0]?._id ?? null, + province_id: provinceId || null, + city_id: cityId || null, }, { noToast: true }); toast.success(t("posts.postPublished"), { id: "post-upload" }); router.push("/"); @@ -494,7 +526,8 @@ export default function NewPostPage() { selectedBillboards, captionLang, selectedShops, - selectedShopProducts + selectedShopProducts, + selectedBooking ); const isStory = mode === "story"; @@ -674,12 +707,43 @@ export default function NewPostPage() { selectedBillboards={selectedBillboards} selectedShops={selectedShops} selectedShopProducts={selectedShopProducts} + selectedBooking={selectedBooking} onPackagesChange={setSelectedPackages} onProjectsChange={setSelectedProjects} onBillboardsChange={setSelectedBillboards} onShopsChange={setSelectedShops} onShopProductsChange={setSelectedShopProducts} + onBookingChange={setSelectedBooking} /> + +
+ { + setProvinceId(e.target.value); + setCityId(""); + }} + > + + {provinces?.map((item) => ( + + ))} + + setCityId(e.target.value)} + disabled={!provinceId} + > + + {cities?.map((item) => ( + + ))} + +
)} diff --git a/src/app/offer/[id]/page.tsx b/src/app/offer/[id]/page.tsx index 45371e2..84d6055 100644 --- a/src/app/offer/[id]/page.tsx +++ b/src/app/offer/[id]/page.tsx @@ -202,7 +202,8 @@ function TicketChat({ params }: ITicketChatProps) {
@@ -101,7 +101,7 @@ function BookingNamePage() { )} - + -
+
{t("booking.previewTitle")} @@ -109,6 +110,11 @@ function BookingPreviewPage() { {t("booking.holidaysTitle")}: {config.holidayDates.join("، ")}

)} + {config.weeklyRepeatCount ? ( +

+ {t("booking.weeklyRepeatCountTitle")}: {config.weeklyRepeatCount} +

+ ) : null}

{t("booking.depositTitle")}:{" "} {config.depositRequired ? t("common.yes") : t("common.no")} @@ -117,7 +123,7 @@ function BookingPreviewPage() {

)} - + ([]); const [newHoliday, setNewHoliday] = useState(""); - const [depositRequired, setDepositRequired] = useState(false); + const [depositRequired, setDepositRequired] = useState(true); + const [weeklyRepeatCount, setWeeklyRepeatCount] = useState(""); useEffect(() => { if (!configId) return; @@ -48,6 +49,7 @@ function BookingSchedulePage() { weeklySchedule?: WeeklyScheduleEntry[]; holidayDates?: string[]; depositRequired?: boolean; + weeklyRepeatCount?: number | null; }; }>("GET", `/bookings/${configId}`).then((res) => { const config = res?.config; @@ -67,7 +69,10 @@ function BookingSchedulePage() { config.holidayDates.map((d) => new Date(d).toISOString().slice(0, 10)) ); } - if (config.depositRequired) setDepositRequired(true); + setDepositRequired(Boolean(config.depositRequired)); + setWeeklyRepeatCount( + config.weeklyRepeatCount ? String(config.weeklyRepeatCount) : "" + ); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [configId]); @@ -130,11 +135,18 @@ function BookingSchedulePage() { return; } + const trimmedRepeatCount = weeklyRepeatCount.trim(); + if (trimmedRepeatCount && (!/^\d+$/.test(trimmedRepeatCount) || Number(trimmedRepeatCount) < 1)) { + toast.error(t("booking.weeklyRepeatCountInvalid")); + return; + } + try { await request("PATCH", `/bookings/${configId}/schedule`, { weeklySchedule, holidayDates, depositRequired, + weeklyRepeatCount: trimmedRepeatCount ? Number(trimmedRepeatCount) : null, }); router.push("/settings/booking/new/preview"); } catch (err: unknown) { @@ -148,7 +160,7 @@ function BookingSchedulePage() { return ( -
+
{t("booking.scheduleTitle")} @@ -206,6 +218,22 @@ function BookingSchedulePage() { ))}
+
+ {t("booking.weeklyRepeatCountTitle")} +

+ {t("booking.weeklyRepeatCountHint")} +

+ setWeeklyRepeatCount(e.target.value)} + placeholder={t("booking.weeklyRepeatCountPlaceholder")} + className="mt-2 w-24 rounded-xl border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-50" + /> +
+
{t("booking.holidaysTitle")} @@ -260,7 +288,7 @@ function BookingSchedulePage() {

)} - + (null); const [selectedIds, setSelectedIds] = useState>(new Set()); + const [isAddModalOpen, setAddModalOpen] = useState(false); + const [adding, setAdding] = useState(false); - useEffect(() => { + const fetchServices = () => request<{ user: User }>("GET", "/profile?services=1").then((res) => { setServices(res?.user?.services || []); }); + + useEffect(() => { + fetchServices(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -53,6 +60,54 @@ function BookingServicesPage() { }); }; + const handleAddService = async (newService: Service) => { + if (!services) return; + setAdding(true); + const previousIds = new Set( + services.map((s) => s.id || s._id || "").filter(Boolean) + ); + const nextServices = [...services, newService]; + + try { + const formData = new FormData(); + formData.append("services", JSON.stringify(nextServices)); + nextServices.forEach((service) => { + if (!service.image) return; + if (service.image.startsWith("/services/")) { + formData.append(`existingServiceImages[${service.id}]`, service.image); + } else { + const blob = dataURLtoBlob(service.image); + formData.append(`serviceImages[${service.id}]`, blob, `service-${service.id}.jpg`); + } + }); + + await request("POST", "/verify/services", formData, { + headers: { Accept: "application/json", "Content-Type": "multipart/form-data" }, + }); + + const res = await request<{ user: User }>("GET", "/profile?services=1"); + const refreshed = res?.user?.services || []; + setServices(refreshed); + setAddModalOpen(false); + + const addedService = refreshed.find( + (s) => !previousIds.has(s.id || s._id || "") + ); + if (addedService) { + const addedId = addedService.id || addedService._id || ""; + if (addedId) toggle(addedId); + } + toast.success(t("settings.edit.save")); + } catch (err: unknown) { + const message = + (err as { response?: { data?: { message?: string } } })?.response + ?.data?.message || t("shops.unknownError"); + toast.error(message); + } finally { + setAdding(false); + } + }; + const handleSubmit = async () => { if (!configId || selectedIds.size === 0) return; try { @@ -71,11 +126,20 @@ function BookingServicesPage() { return ( -
+
- - {t("booking.servicesTitle")} - +
+ + {t("booking.servicesTitle")} + + +
{services === null ? (

@@ -97,19 +161,23 @@ function BookingServicesPage() { const id = service.id || service._id || ""; const selected = selectedIds.has(id); return ( - +

); })}
)} - +
+ {isAddModalOpen && ( + !adding && setAddModalOpen(false)} + onAdd={handleAddService} + /> + )} ); diff --git a/src/app/settings/booking/reservations/page.tsx b/src/app/settings/booking/reservations/page.tsx index f8d2ada..6d0e22a 100644 --- a/src/app/settings/booking/reservations/page.tsx +++ b/src/app/settings/booking/reservations/page.tsx @@ -17,6 +17,7 @@ type BookingBuyer = { profile_image?: string; user_level?: string; is_verified?: string; + verify_badge?: string; }; type ReceivedBooking = { @@ -87,7 +88,8 @@ function BookingReservationsPage() { first_name={booking.buyer?.first_name} last_name={booking.buyer?.last_name} user_name={booking.buyer?.user_name} - is_verified={booking.buyer?.is_verified} + is_verified={booking.buyer?.verify_badge} + userId={booking.buyer?._id} />
{item.user_name} - +
{(item.first_name || item.last_name) && (

diff --git a/src/app/settings/chats/page.tsx b/src/app/settings/chats/page.tsx index d2c8c07..dc1b60a 100644 --- a/src/app/settings/chats/page.tsx +++ b/src/app/settings/chats/page.tsx @@ -6,7 +6,7 @@ import ProfileAvatar from "@/components/main/ProfileAvatar"; import VerificationBadge from "@/components/main/VerificationBadge"; import PageTitle from "@/components/settings/PageTitle"; import UserDetails from "@/components/settings/UserDetails"; -import StoryRing from "@/components/stories/StoryRing"; +import StoryRing, { verifyBadgeToTierColor } from "@/components/stories/StoryRing"; import useInfiniteScroll from "@/hooks/useInfiniteScroll"; import { useUser } from "@/hooks/useUser"; import Link from "next/link"; @@ -30,6 +30,7 @@ export interface IMessage { last_name: string; user_name: string; is_verified: string; + verify_badge?: string; profile_image?: string; last_online?: string; _id: string; @@ -236,6 +237,7 @@ function Chats() { {item.user_name} - +

diff --git a/src/app/settings/chats/rooms/[roomId]/page.tsx b/src/app/settings/chats/rooms/[roomId]/page.tsx index 46c3cbe..d71e7b2 100644 --- a/src/app/settings/chats/rooms/[roomId]/page.tsx +++ b/src/app/settings/chats/rooms/[roomId]/page.tsx @@ -54,6 +54,7 @@ type RoomMember = { last_name?: string; profile_image?: string; is_verified?: string; + verify_badge?: string; }; type RoomMessage = { @@ -186,7 +187,7 @@ function RoomMessageRow({ className="h-5 w-5 shrink-0" /> {senderName} - + ) : null} {msg.replyTo ? ( diff --git a/src/app/settings/chats/shop/[shopId]/[buyerId]/page.tsx b/src/app/settings/chats/shop/[shopId]/[buyerId]/page.tsx index a1b45c9..cbe6a5c 100644 --- a/src/app/settings/chats/shop/[shopId]/[buyerId]/page.tsx +++ b/src/app/settings/chats/shop/[shopId]/[buyerId]/page.tsx @@ -6,7 +6,7 @@ import Container from "@/components/elements/Container"; import ProfileAvatar from "@/components/main/ProfileAvatar"; import LocalePageShell from "@/components/i18n/LocalePageShell"; import ChatBoldIcon from "@/components/chat/ChatBoldIcon"; -import { chatHeaderCircleBtn, chatHeaderInfoPill } from "@/components/chat/ChatHeader"; +import { chatHeaderCircleBtn, chatHeaderInfoPill, chatHeaderGlassStyle } from "@/components/chat/ChatHeader"; import VideoMessageBubble from "@/components/chat/VideoMessageBubble"; import IOSSpinner from "@/components/ui/IOSSpinner"; import useAxios from "@/hooks/useAxios"; @@ -153,12 +153,13 @@ export default function ShopChatPage() { type="button" onClick={() => router.back()} className={chatHeaderCircleBtn} + style={chatHeaderGlassStyle} aria-label={t("chats.actions.back")} > -
+
{counterpart?.name} @@ -273,6 +274,7 @@ export default function ShopChatPage() { disabled={sending} onClick={() => attachmentInputRef.current?.click()} className={cn(chatHeaderCircleBtn, "gentle-transition disabled:opacity-40")} + style={chatHeaderGlassStyle} aria-label={t("chats.aria.attach")} > @@ -299,6 +301,7 @@ export default function ShopChatPage() { disabled={!text.trim() || sending} onClick={() => void handleSend()} className={cn(chatHeaderCircleBtn, "gentle-transition active:scale-90 disabled:opacity-40")} + style={chatHeaderGlassStyle} aria-label={t("chats.aria.send")} > diff --git a/src/app/settings/edit/License/page.tsx b/src/app/settings/edit/License/page.tsx index 77305dc..2e26b55 100644 --- a/src/app/settings/edit/License/page.tsx +++ b/src/app/settings/edit/License/page.tsx @@ -191,6 +191,8 @@ function LicensePage() { rounded="2xl" className="h-[130px] w-[130px]" fallback="/images/placeholder.png" + userId={user?._id} + verifyBadge={user?.verify_badge} />
{formatFullName(user?.first_name, user?.last_name)} diff --git a/src/app/settings/profile/user-settings/page.tsx b/src/app/settings/profile/user-settings/page.tsx index 2274552..fc3b54e 100644 --- a/src/app/settings/profile/user-settings/page.tsx +++ b/src/app/settings/profile/user-settings/page.tsx @@ -4,6 +4,7 @@ import React, { useEffect, useState } from "react"; import Container from "@/components/elements/Container"; import PageTitle from "@/components/settings/PageTitle"; import LanguageSettingRow from "@/components/settings/LanguageSettingRow"; +import AccountAnalyticsWidget from "@/components/settings/AccountAnalyticsWidget"; import LocalePageShell from "@/components/i18n/LocalePageShell"; import useAxios from "@/hooks/useAxios"; import Link from "next/link"; @@ -157,6 +158,8 @@ export default function UserSettingsPage() {
)} + +
+ + {t("settings.nav.booking")} + + +