import { toast } from "react-toastify"; import Cookies from "js-cookie"; import moment from "moment-jalaali"; import "moment-timezone"; // Data import specialties from "@/data/specialties.json"; import states from "@/data/state.json"; import cities from "@/data/city.json"; export const numberToArStyle = (num) => num?.toLocaleString("ar-AE") || ""; export const isActiveURL = (link, name) => link === name; // نگاشت پاسخ API بلاگ (image_url/tags/created_at/body/author-object) به شکلی که // کامپوننت‌های بلاگ انتظار دارند (images[{url}]/tag[{name}]/created/body.value/author-string). export const normalizeBlog = (blog) => { if (!blog) return null; return { ...blog, images: blog.image_url ? [{ url: blog.image_url }] : [], tag: Array.isArray(blog.tags) ? blog.tags.map((t) => (typeof t === "string" ? { name: t } : t)) : [], created: blog.created_at ?? blog.created ?? null, body: blog.body && typeof blog.body === "object" ? blog.body : { value: blog.body ?? "" }, author: blog.author && typeof blog.author === "object" ? blog.author.name : blog.author ?? null, }; }; // تبدیل مسیر نسبی تصویر (/uploads/...) به URL کامل backend. // مسیرهای absolute (http...) و asset های محلی (/assets، /default) دست‌نخورده می‌مانند. export const imageUrl = (url, fallback = "/assets/images/user.png") => { if (!url) return fallback; if (/^https?:\/\//.test(url)) return url; if (url.startsWith("/uploads")) { const base = process.env.NEXT_PUBLIC_API_URL || ""; return `${base}${url}`; } return url; }; export const convertToJalali = (date) => { const currentDate = date; const jalaliDate = moment(currentDate).format("jYYYY/jM/jD"); const jalaliYear = moment(jalaliDate, "jYYYY/jM/jD").jYear(); const jalaliMonth = moment(jalaliDate, "jYYYY/jM/jD").jMonth() + 1; const jalaliDay = moment(jalaliDate, "jYYYY/jM/jD").jDate(); return `${jalaliYear}/${jalaliMonth}/${jalaliDay}`; }; export const convertTimestampToJalali = (timestamp) => { if (!timestamp) return "--"; const date = moment.unix(timestamp); return date.format("jYYYY/jMM/jDD"); }; export const convertTimestampToTime = (timestamp) => { if (!timestamp) return "--"; const date = moment.unix(timestamp); return date.format("HH:mm"); }; export const handleTwoLength = (value) => String(value).length === 1 ? `0${value}` : value; export const checkAllValues = (data) => { for (let key in data) { const item = data[key]; if (typeof item === "object" && item !== null) { if ("value" in item) { if (!item.value) { return false; } } else { if (!checkAllValues(item)) { return false; } } } } return true; }; export const checkTimes = (arr) => { return arr.map((day) => { const hasMorningTime = day.morning_time.some((time) => time !== ""); const hasEveningTime = day.evening_time.some((time) => time !== ""); return hasMorningTime && hasEveningTime; }); }; export const uploadImage = async (event) => { let obj = { file: "", blob: "", }; const file = event.target.files[0]; if (file) { obj.file = file; const readFileAsDataURL = (file) => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = (error) => reject(error); reader.readAsDataURL(file); }); }; try { obj.blob = await readFileAsDataURL(file); return obj; } catch (error) { console.error("Error reading file: ", error); throw error; } } else { throw new Error("No file selected"); } }; export const alert = (type, text, prop) => { toast[type](

پیغام{" "} {type === "error" ? "خطا" : type === "warning" ? "هشدار" : type === "info" ? "اعلام" : "موفقیت"}

{text}

, { prop } ); }; export const formatPhoneNumber = (input) => { if (typeof input !== "string") input = String(input); const normalized = input .replace(/[\u06F0-\u06F9]/g, (d) => String(d.charCodeAt(0) - 0x06f0)) .replace(/[\u0660-\u0669]/g, (d) => String(d.charCodeAt(0) - 0x0660)); const digits = normalized.replace(/\D/g, ""); const trimmed = digits.substring(0, 9); const match = trimmed.match(/^(\d{0,2})(\d{0,3})(\d{0,4})$/); if (!match) return trimmed; return [match[1], match[2], match[3]].filter(Boolean).join(" "); }; export function validatePhoneNumber(input) { if (!input) { return { valid: false, text: "شماره موبایل نمی‌تواند خالی باشد." }; } // چک کردن اینکه فقط اعداد انگلیسی باشد const hasNonEnglishDigits = /[^0-9]/.test(input); if (hasNonEnglishDigits) { return { valid: false, text: "فقط از اعداد انگلیسی استفاده کنید." }; } if (input.length !== 11) { return { valid: false, text: "شماره موبایل باید دقیقا 11 رقم باشد." }; } const isValid = /^09\d{9}$/.test(input); if (!isValid) { return { valid: false, text: "شماره موبایل نامعتبر است." }; } return { valid: true, text: null }; } export const searchOnList = (list, value, name) => { let newList = list; newList = list.filter((item) => item[name]?.toLowerCase()?.includes(value)); return newList; }; export function clearFilterParams(router, listRemove) { const currentParams = new URLSearchParams(window.location.search); const keysToRemove = listRemove; keysToRemove.forEach((key) => currentParams.delete(key)); const queryString = currentParams.toString(); const newUrl = queryString ? `/doctors?${queryString}` : "/doctors"; router.push(newUrl); } export function clearDoctorClinicParams(router, slug) { const clinicslug = slug; const newUrl = `/clinic/${clinicslug}`; router.push(newUrl) } export const filterList = (data) => { const parentList = specialties.filter((item) => !item.parent_id); const childrenList = specialties.filter((item) => item.parent_id); const filteredChildrenList = data && data.category ? childrenList.filter( (item) => String(item.parent_id) === String(data.category.id) ) : []; return { parentList: parentList, childrenList: filteredChildrenList, }; }; export function getCleanNumberValue(value, defaultValue = 0) { if (typeof value !== "string") value = String(value); const persianArabicToEnglish = value .replace(/[\u06F0-\u06F9]/g, (d) => String(d.charCodeAt(0) - 0x06f0)) .replace(/[\u0660-\u0669]/g, (d) => String(d.charCodeAt(0) - 0x0660)); const cleaned = persianArabicToEnglish.replace(/[^0-9.]/g, ""); const number = parseFloat(cleaned); return isNaN(number) ? defaultValue : number; } export function hasDataChanged(previousData, newData) { const oldDataString = JSON.stringify( previousData, Object.keys(previousData).sort() ); const newDataString = JSON.stringify(newData, Object.keys(newData).sort()); return oldDataString !== newDataString; } export const getParsedUserInfo = () => { const user = Cookies.get("userInfo"); if (!user) return null; try { return JSON.parse(user); } catch { return null; } }; export const handleTimeExpiresToken = (expires_in) => { const accessTokenExpires = new Date(); accessTokenExpires.setSeconds(accessTokenExpires.getSeconds() + expires_in); const refreshTokenExpires = new Date(); refreshTokenExpires.setDate(refreshTokenExpires.getDate() + 30); return { accessTokenExpires, refreshTokenExpires, }; }; export const removeAdditionalKeysDashboard = (information) => { // فقط متادیتای سمت فرانت حذف می‌شود؛ national_code باید ارسال شود تا قابل ثبت/آپدیت باشد. const { changed, created, id, status, prev_data, uuid, ...usedKays } = information; return usedKays; }; export const changeDateType = (data, toTimeStamp) => { const newData = { ...data }; const birthday = newData.birthday; if (toTimeStamp) { const m = moment(birthday, "jYYYY/jMM/jDD"); newData.birthday = m.unix(); } else { newData.birthday = moment.unix(birthday).format("jYYYY/jMM/jDD"); } return newData; }; export const removeTokenHead = { headers: { Authorization: "", }, }; export const dateToTimestamp = (date) => { // تنظیم timezone به تهران const tehranMoment = date.clone().tz("Asia/Tehran").startOf("day"); // Get the Unix timestamp in seconds const inSeconde = tehranMoment.unix(); return inSeconde; }; export const degreeVal = [ { name: "پزشک عمومی", val: "general" }, { name: "پزشک متخصص", val: "specialist" }, { name: "پزشک فوق تخصص", val: "subspecialistplus" }, ]; export const addLabelToList = (list) => list.map((item) => { return { ...item, label: item.name, }; }); export const QueryForDoctorsFilter = (newFilter) => { const query = {}; // 🔹 active => only when value === 1 if (newFilter.active === 1) { query.active = 1; } // 🔹 specialty or category if (newFilter.specialty) { query.specialty = newFilter.specialty.name; } else if (newFilter.category) { query.specialty = newFilter.category.name; } // 🔹 city if (newFilter.city) { query.city = newFilter.city.name; } // 🔹 state if (newFilter.state) { query.state = newFilter.state.name; } // 🔹 degree (only if not "همه موارد") if (newFilter.degree && newFilter.degree !== "همه موارد") { query.degree = newFilter.degree; } // 🔹 gender (only if not "هر دو") if (newFilter.gender && newFilter.gender !== "هر دو") { query.gender = newFilter.gender; } // 🔹 name (only if has value) if (newFilter.name && newFilter.name.trim() !== "") { query.name = newFilter.name; } // 🔹 sort => only if value is 3,4,5 if ([3, 4, 5].includes(Number(newFilter.sort))) { // query.sort = newFilter.sort === 3 ? "ASC" : "DESC"; query.sort = newFilter.sort; } return query; }; export const QueryForClinicDoctorFilter = (newFilter) => { const query = {}; // 🔹 active => only when value === 1 if (newFilter.active === 1) { query.active = 1; } // 🔹 specialty or category if (newFilter.specialty) { query.specialty = newFilter.specialty.name; } else if (newFilter.category) { query.specialty = newFilter.category.name; } // 🔹 degree (only if not "همه موارد") if (newFilter.degree && newFilter.degree !== "همه موارد") { query.degree = newFilter.degree; } // 🔹 gender (only if not "هر دو") if (newFilter.gender && newFilter.gender !== "هر دو") { query.gender = newFilter.gender; } // 🔹 name (only if has value) if (newFilter.name && newFilter.name.trim() !== "") { query.name = newFilter.name; } // 🔹 sort => only if value is 3,4,5 if ([3, 4, 5].includes(Number(newFilter.sort))) { // query.sort = newFilter.sort === 3 ? "ASC" : "DESC"; query.sort = newFilter.sort; } return query; } export const QueryForDoctorsReq = (newFilter) => { const params = {}; if (newFilter.active === 1) params.active = 1; if (newFilter.specialty?.id) { params.specialty_id = newFilter.specialty.id; } else if (newFilter.category?.id) { params.specialty_id = newFilter.category.id; } if (newFilter.city?.id) params.city_id = newFilter.city.id; if (newFilter.state?.id) params.state_id = newFilter.state.id; if (newFilter.degree && newFilter.degree !== "همه موارد") switch (newFilter.degree) { case "کارشناس": params.degree = "expert"; break; case "پزشک عمومی": params.degree = "general"; break; case "پزشک متخصص": params.degree = "specialist"; break; case "پزشک فوق تخصص": params.degree = "subspecialistplus"; break; default: break; } if (newFilter.gender && newFilter.gender !== "هر دو") params.gender = newFilter.gender === "مرد" ? "man" : "woman"; if (newFilter.name) params.name = newFilter.name; if (newFilter.sort) { if (newFilter.sort === 3) params.sort = "ASC"; else if (newFilter.sort === 4 || newFilter.sort === 5) params.sort = "DESC"; } return params; }; export const QueryForDoctorsClinicReq = (newFilter) => { const params = {}; if (newFilter.active === 1) params.active = 1; if (newFilter.specialty?.id) { params.specialty = newFilter.specialty.id; } else if (newFilter.category?.id) { params.specialty = newFilter.category.id; } if (newFilter.degree && newFilter.degree !== "همه موارد") switch (newFilter.degree) { case "کارشناس": params.degree = "expert"; break; case "پزشک عمومی": params.degree = "general"; break; case "پزشک متخصص": params.degree = "specialist"; break; case "پزشک فوق تخصص": params.degree = "subspecialistplus"; break; default: break; } if (newFilter.gender && newFilter.gender !== "هر دو") params.gender = newFilter.gender === "مرد" ? "man" : "woman"; if (newFilter.name) params.name = newFilter.name; if (newFilter.sort) { if (newFilter.sort === 3) params.sort = "ASC"; else if (newFilter.sort === 4 || newFilter.sort === 5) params.sort = "DESC"; } return params; } export const buildDoctorParams = (searchParams) => { const params = {}; // 🔹 active if (searchParams.active && Number(searchParams.active) === 1) { params.active = 1; } // 🔹 specialty (find by name in specialties.json) if (searchParams.specialty) { const spec = specialties.find((s) => s.name === searchParams.specialty); if (spec) { params.specialty_id = spec.id; } } // 🔹 city (find by name in city.json) if (searchParams.city) { const cityObj = cities.find((c) => c.name === searchParams.city); if (cityObj) { params.city_id = cityObj.id; } } // 🔹 state (find by name in state.json) if (searchParams.state) { const stateObj = states.find((s) => s.name === searchParams.state); if (stateObj) { params.state_id = stateObj.id; } } // 🔹 degree → convert fa → en if (searchParams.degree) { switch (searchParams.degree) { case "کارشناس": params.degree = "expert"; break; case "پزشک عمومی": params.degree = "general"; break; case "پزشک متخصص": params.degree = "specialist"; break; case "پزشک فوق تخصص": params.degree = "subspecialistplus"; break; default: break; } } // 🔹 gender if (searchParams.gender) { if (searchParams.gender === "مرد") params.gender = "man"; if (searchParams.gender === "زن") params.gender = "woman"; } // 🔹 name if (searchParams.name) { params.name = searchParams.name; } // 🔹 sort (3 = ASC, 4|5 = DESC) if (searchParams.sort) { const sortNum = Number(searchParams.sort); if (sortNum === 3) params.sort = "ASC"; else if ([4, 5].includes(sortNum)) params.sort = "DESC"; } // 🔹 page params.page = searchParams.page || 1; // 🔹 limit params.limit = 12; return params; }; export const QueryForClinicsFilter = (searchParams) => { const params = {}; // 🔹 specialty (find by name in specialties.json) if (searchParams.specialty) { const spec = specialties.find((s) => s.name === searchParams.specialty); if (spec) { params.specialty = spec.id; } } // 🔹 city (find by name in city.json) if (searchParams.city) { const cityObj = cities.find((c) => c.name === searchParams.city.name); if (cityObj) { params.city = cityObj.id; } } // 🔹 state (find by name in state.json) if (searchParams.province) { const stateObj = states.find((s) => s.name === searchParams.province.name); if (stateObj) { params.state = stateObj.id; } } // 🔹 page params.page = searchParams.page || 1; // 🔹 limit params.limit = 12; return params; }; export const buildClinicParams = (searchParams) => { const params = {}; // 🔹 city (find by name in city.json) if (searchParams.city) { const cityObj = cities.find((c) => c.name === searchParams.city); if (cityObj) { params.city = cityObj.id; } } // 🔹 state (find by name in state.json) if (searchParams.state) { const stateObj = states.find((s) => s.name === searchParams.state); if (stateObj) { params.state = stateObj.id; } } // 🔹 specialty (find by name in specialties.json) if (searchParams.specialty) { const spec = specialties.find((s) => s.name === searchParams.specialty); if (spec) { params.specialty = spec.id; } } // 🔹 page params.page = searchParams.page || 1; // 🔹 limit params.limit = 12; return params; }; export function timeAgo(timestamp) { const now = moment(); const then = moment.unix(timestamp); const diffSeconds = now.diff(then, "seconds"); if (diffSeconds < 60) { return diffSeconds === 0 ? "همین الان" : `${diffSeconds} ثانیه پیش`; } else if (diffSeconds < 3600) { const minutes = Math.floor(diffSeconds / 60); return `${minutes} دقیقه پیش`; } else if (diffSeconds < 86400) { const hours = Math.floor(diffSeconds / 3600); return `${hours} ساعت پیش`; } else if (diffSeconds < 2592000) { const days = Math.floor(diffSeconds / 86400); return `${days} روز پیش`; } else if (diffSeconds < 31104000) { const months = Math.floor(diffSeconds / 2592000); return `${months} ماه پیش`; } else { const years = Math.floor(diffSeconds / 31104000); return `${years} سال پیش`; } } export function isUserLoggedIn() { // access_token در memory است نه کوکی؛ userInfo کوکیِ non-httpOnly است که هنگام لاگین ست می‌شود. return !!Cookies.get("userInfo"); } export function convertTimestampToPersianDateSimple(timestamp) { const date = new Date(timestamp * 1000); const options = { timeZone: "Asia/Tehran", calendar: "persian", year: "numeric", month: "2-digit", day: "2-digit", weekday: "long", hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: true, }; return date.toLocaleString("fa-IR", options); } export function changeNumToDefault(str) { if (!str) return ""; return str .toString() .replace(/[۰-۹]/g, d => "۰۱۲۳۴۵۶۷۸۹".indexOf(d)) .replace(/[٠-٩]/g, d => "٠١٢٣٤٥٦٧٨٩".indexOf(d)); } // اعتبارسنجی کد ملی ایران: ۱۰ رقم + الگوریتم رقم کنترلی. export const isValidIranNationalCode = (input) => { if (!input) return false; const code = String(input).replace(/\D/g, ""); if (!/^\d{10}$/.test(code)) return false; // کدهای با ارقام یکسان (مثل 0000000000) نامعتبرند if (/^(\d)\1{9}$/.test(code)) return false; const check = +code[9]; let sum = 0; for (let i = 0; i < 9; i++) sum += +code[i] * (10 - i); const r = sum % 11; return r < 2 ? check === r : check === 11 - r; };