diff --git a/app/dashboard/page.js b/app/dashboard/page.js index f17f92d..2257b12 100644 --- a/app/dashboard/page.js +++ b/app/dashboard/page.js @@ -27,20 +27,37 @@ export default async function Dashboard({ searchParams }) { // may still carry the OTP uuid, which 404s against user-profile. const userInfo = safeJsonParse(cookieStore.get("userInfo")?.value); const userUuid = userInfo?.uuid || cookieStore.get("uuid")?.value; - let profile = null; - if (userUuid) { - profile = await fetchReq( - `${process.env.NEXT_PUBLIC_API_URL}/api/v1/user-profile/${userUuid}`, - { headers: { Authorization: `Bearer ${token.value}` } } - ); - } + const authHeader = { headers: { Authorization: `Bearer ${token.value}` } }; + const API = process.env.NEXT_PUBLIC_API_URL; + + const [profile, appointmentsRes, paymentsRes] = await Promise.all([ + userUuid + ? fetchReq(`${API}/api/v1/user-profile/${userUuid}`, authHeader) + : Promise.resolve(null), + fetchReq(`${API}/api/v1/my/appointments?page=1&limit=1`, authHeader), + fetchReq(`${API}/api/v1/my/payments?page=1&limit=200`, authHeader), + ]); + + const numberOfTurns = appointmentsRes?.meta?.totalRecords ?? 0; + const payments = paymentsRes?.data ?? []; + const totalRials = payments + .filter((p) => p.status === "success") + .reduce((sum, p) => sum + (Number(p.amount_rials) || 0), 0); + const totalTransactions = Math.round(totalRials / 10); // ریال → تومان + + const extras = { + mobile: userInfo?.mobile_number ?? "", + realName: userInfo?.realName ?? "", + number_of_turns: numberOfTurns, + total_transactions: totalTransactions, + }; return ( ); } diff --git a/components/dashboard/userAccount/Head.js b/components/dashboard/userAccount/Head.js index 0d6ff9f..9b67ccf 100644 --- a/components/dashboard/userAccount/Head.js +++ b/components/dashboard/userAccount/Head.js @@ -37,7 +37,7 @@ function Head({ user }) {

- {user.total_transactions}{" "} + {Number(user?.total_transactions || 0).toLocaleString("fa-IR")}{" "} تومان

@@ -51,7 +51,7 @@ function Head({ user }) {

- {user.number_of_turns} + {Number(user?.number_of_turns || 0).toLocaleString("fa-IR")}

تعداد نوبت های شما diff --git a/components/dashboard/userAccount/detailUser/information/form/index.js b/components/dashboard/userAccount/detailUser/information/form/index.js index 7e822b9..f686934 100644 --- a/components/dashboard/userAccount/detailUser/information/form/index.js +++ b/components/dashboard/userAccount/detailUser/information/form/index.js @@ -25,7 +25,7 @@ function Form({ insurance, errors, changeData, information }) { name="work_phone" isTransparent={true} changeData={changeData} - data={parsedUserInfo?.username} + data={parsedUserInfo?.mobile_number ?? parsedUserInfo?.username} /> { + const file = e.target.files?.[0]; + if (!file) return; + setAvatarUploading(true); + try { + const res = await request.uploadUserAvatar(file); + const url = res?.data?.avatar || res?.data?.url; + if (url) { + setInformation((prev) => ({ ...(prev || {}), avatar: url })); + toast.success("عکس پروفایل به‌روزرسانی شد"); + } + } catch { + toast.error("خطا در آپلود عکس"); + } finally { + setAvatarUploading(false); + e.target.value = ""; + } + }; useEffect(() => { const parsedUserInfo = getParsedUserInfo(); @@ -101,6 +123,25 @@ function Information({ information, setInformation }) { return (

+
+ avatar + +
({ + ...a, + doctor: { uuid: a.doctor_uuid, name: a.doctor_name, specialties: [] }, + })); + setAppointments(normalized); } else { setAppointments([]); } - setTotalPages(1); + setTotalPages(response?.meta?.totalPages || 1); } catch (error) { console.error("Error fetching appointments:", error); } finally { diff --git a/lib/representationAdapters.js b/lib/representationAdapters.js index 94435f3..4f4a1ab 100644 --- a/lib/representationAdapters.js +++ b/lib/representationAdapters.js @@ -51,16 +51,19 @@ export function adaptRepresentationDashboard(dashboard) { }; } -export function buildPatientUser(profile) { +export function buildPatientUser(profile, extras = {}) { // GET /user-profile is double-nested: { data: { data: {...} } } (after fetchReq → response.data) const data = profile?.data?.data ?? profile?.data ?? profile ?? {}; return { - profile: data.avatar ?? "/assets/images/profile-user.png", - name: [data.label ?? data.name, data.family].filter(Boolean).join(" ").trim(), - phone: data.phone ?? data.mobile ?? "", - total_transactions: 0, - number_of_turns: 0, + profile: data.avatar ?? extras.avatar ?? "/assets/images/profile-user.png", + name: + [data.label ?? data.name, data.family].filter(Boolean).join(" ").trim() || + extras.realName || + "", + phone: data.phone ?? data.mobile ?? extras.mobile ?? "", + total_transactions: extras.total_transactions ?? 0, + number_of_turns: extras.number_of_turns ?? 0, national_code: data.national_code ?? "", gender: data.gender ?? "", date_of_birth: data.birthday ?? "", diff --git a/services/response.js b/services/response.js index fe212ef..f2c8888 100644 --- a/services/response.js +++ b/services/response.js @@ -39,6 +39,14 @@ export const request = { postUserProfile: (data) => api.post("api/v1/user-profile", data, { requireAuth: true }), patchUserProfile: (data, uuid) => api.patch(`api/v1/user-profile/${uuid}`, data, { requireAuth: true }), + uploadUserAvatar: (file) => + api.post("api/v1/user-profile/avatar", file, { + requireAuth: true, + headers: { + "Content-Type": "application/octet-stream", + "Content-Disposition": `filename="${file?.name || "avatar.jpg"}"`, + }, + }), getUserInfo: (headers) => api.get("oauth/userinfo", { ...headers, requireAuth: true }), getInsuranceType: () => api.get(`api/v1/insurances?type=basic`, { requireAuth: true }),