diff --git a/src/components/modal.tsx b/src/components/modal.tsx index 594ab288..5df651c1 100644 --- a/src/components/modal.tsx +++ b/src/components/modal.tsx @@ -106,7 +106,7 @@ export default function Modal({ aria-labelledby={typeof title === 'string' ? title : 'modal-title'} aria-modal="true" onClick={() => closeOnBackdropClick && onClose()} - className="modal modal-middle flex items-center justify-center transition-opacity duration-200 opacity-100 p-2 md:p-4" + className="flex items-center justify-center p-2 transition-opacity duration-200 opacity-100 modal modal-middle md:p-4" >
{(title || showCloseButton) && ( -
+
{title && ( diff --git a/src/components/section-panel.tsx b/src/components/section-panel.tsx index 464d5fe5..eb6e6e57 100644 --- a/src/components/section-panel.tsx +++ b/src/components/section-panel.tsx @@ -47,11 +47,11 @@ export function SectionPanel({ title, children, size = 'md', icon }: SectionPane return (
-
+
+ {icon && React.cloneElement(icon, {})}

{title}

- {icon && React.cloneElement(icon, {})}
{children}
diff --git a/src/components/tab-manager.tsx b/src/components/tab-manager.tsx index aa146e1c..7ef7798d 100644 --- a/src/components/tab-manager.tsx +++ b/src/components/tab-manager.tsx @@ -65,7 +65,7 @@ export const TabManager = ({ const headClass = tabPosition === 'top' ? 'flex-col gap-1 h-[80vh]' - : 'flex-col md:flex-row gap-4 h-[60vh]' + : 'flex-col md:flex-row gap-4 h-[80vh]' const contentClass = tabPosition === 'top' ? 'shrink-0 md:overflow-y-auto w-full' diff --git a/src/layouts/navbar/navbar.layout.tsx b/src/layouts/navbar/navbar.layout.tsx index f7477a06..76b6591d 100644 --- a/src/layouts/navbar/navbar.layout.tsx +++ b/src/layouts/navbar/navbar.layout.tsx @@ -123,7 +123,7 @@ export function NavbarLayout(): JSX.Element { : '-bottom-32 opacity-0 scale-95 pointer-events-none' }`} > -
+ )} +
+ ) + + return ( + <> + + + + + ) +} diff --git a/src/layouts/setting/tabs/account/components/occupation-selector.tsx b/src/layouts/setting/tabs/account/components/occupation-selector.tsx new file mode 100644 index 00000000..6d5d5e5a --- /dev/null +++ b/src/layouts/setting/tabs/account/components/occupation-selector.tsx @@ -0,0 +1,88 @@ +import { useState, useRef } from 'react' +import { ClickableTooltip } from '@/components/clickableTooltip' +import type { ProfileMetaItem } from '@/services/hooks/profile/getProfileMeta.hook' + +interface OccupationSelectorProps { + occupations: ProfileMetaItem[] + selectedOccupation: string | null + onSelect: (occupationId: string | null) => void + isLoading?: boolean + triggerElement: React.ReactNode +} + +export const OccupationSelector = ({ + occupations, + selectedOccupation, + onSelect, + isLoading = false, + triggerElement, +}: OccupationSelectorProps) => { + const [isOpen, setIsOpen] = useState(false) + const triggerRef = useRef(null) + + const handleSelect = (occupationId: string) => { + if (selectedOccupation === occupationId) { + onSelect(null) + } else { + onSelect(occupationId) + } + setIsOpen(false) + } + + const content = ( +
+ {isLoading ? ( +
+ درحال بارگذاری... +
+ ) : ( +
+ {occupations.map((occupation) => { + const isActive = selectedOccupation === occupation.id + return ( + + ) + })} +
+ )} +
+ ) + + return ( + <> + + + + + ) +} diff --git a/src/layouts/setting/tabs/account/components/profile-date-picker.tsx b/src/layouts/setting/tabs/account/components/profile-date-picker.tsx index da66340d..4eab16ae 100644 --- a/src/layouts/setting/tabs/account/components/profile-date-picker.tsx +++ b/src/layouts/setting/tabs/account/components/profile-date-picker.tsx @@ -1,18 +1,10 @@ -import jalaliMoment from 'jalali-moment' -import { useEffect, useState } from 'react' -import { SelectBox } from '@/components/selectbox/selectbox' -import { showToast } from '@/common/toast' +import { Button } from '@/components/button/button' +import { ClickableTooltip } from '@/components/clickableTooltip' +import { useRef, useState, useEffect } from 'react' +import { FiCheck } from 'react-icons/fi' +import { LuCalendarDays, LuChevronRight } from 'react-icons/lu' -interface JalaliDatePickerProps { - id: string - label: string - value: string - onChange: (jalaliDate: string) => void - disabled?: boolean - className?: string -} - -const JALALI_MONTHS = [ +const PERSIAN_MONTHS = [ 'فروردین', 'اردیبهشت', 'خرداد', @@ -27,174 +19,287 @@ const JALALI_MONTHS = [ 'اسفند', ] -const JALALI_YEARS = Array.from({ length: 100 }, (_, i) => 1300 + i) // 1300 تا 1399 +const MONTH_DAYS = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29] -function getJalaliDaysInMonth(year: number, month: number): number { - if (month <= 6) return 31 - if (month <= 11) return 30 - const isLeapYear = jalaliMoment.jIsLeapYear(year) - return isLeapYear ? 30 : 29 +interface JalaliDatePickerProps { + id?: string + label?: string + value: string + onChange: (value: string) => void + enable: boolean } export default function JalaliDatePicker({ - id, - label, value, onChange, - className = '', - disabled, + enable, }: JalaliDatePickerProps) { - const [year, setYear] = useState('') - const [month, setMonth] = useState('') - const [day, setDay] = useState('') + const currentYear = 1403 + const [isOpen, setIsOpen] = useState(false) + const triggerRef = useRef(null) - useEffect(() => { - if (!value) { - setYear('') - setMonth('') - setDay('') - return + const [tempDate, setTempDate] = useState({ year: 1380, month: 1, day: 1 }) + + const parseValue = (val: string) => { + if (!val) return { year: 1380, month: 1, day: 1 } + + const parts = val.includes('-') ? val.split('-') : val.split('/') + return { + year: parseInt(parts[0], 10) || 1380, + month: parseInt(parts[1], 10) || 1, + day: parseInt(parts[2], 10) || 1, } + } - const jalaliMomentDate = jalaliMoment(value, 'jYYYY-jMM-jDD') - if (jalaliMomentDate.isValid()) { - const yearValue = jalaliMomentDate.jYear().toString() - const monthValue = (jalaliMomentDate.jMonth() + 1).toString() - const dayValue = jalaliMomentDate.jDate().toString() + useEffect(() => { + if (isOpen) { + const parsed = parseValue(value) + setTempDate(parsed) + } + }, [isOpen, value]) - setYear(yearValue) - setMonth(monthValue) - setDay(dayValue) - } else { - showToast('تاریخ نامعتبر است', 'error') + const getDaysInMonth = (m: number, y: number) => { + if (m === 12) { + const isLeap = ((y % 33) * 8 + 13) % 33 < 8 + return isLeap ? 30 : 29 } - }, [value]) + return MONTH_DAYS[m - 1] + } + + const handleYearChange = (y: number) => { + const maxDay = getDaysInMonth(tempDate.month, y) + const validDay = Math.min(tempDate.day, maxDay) + setTempDate({ ...tempDate, year: y, day: validDay }) + } - const updateDate = (newYear: string, newMonth: string, newDay: string) => { - if (disabled) return - if (!newYear || !newMonth || !newDay) { - onChange('') + const handleMonthChange = (m: number) => { + const maxDay = getDaysInMonth(m, tempDate.year) + const validDay = Math.min(tempDate.day, maxDay) + setTempDate({ ...tempDate, month: m, day: validDay }) + } + + const handleDayChange = (d: number) => { + setTempDate({ ...tempDate, day: d }) + } + + const handleConfirm = () => { + const formattedDate = `${tempDate.year}-${tempDate.month.toString().padStart(2, '0')}-${tempDate.day.toString().padStart(2, '0')}` + onChange(formattedDate) + setIsOpen(false) + } + + const handleCancel = () => { + setIsOpen(false) + } + + const onClickToOpen = () => { + if (!enable) { return } - const jalaliDate = jalaliMoment() - .jYear(parseInt(newYear, 10)) - .jMonth(parseInt(newMonth, 10) - 1) - .jDate(parseInt(newDay, 10)) + setIsOpen(true) + } + + return ( + <> + + + +
+ + + +
- if (jalaliDate.isValid()) { - const formattedDate = jalaliDate.format('jYYYY-jMM-jDD') - onChange(formattedDate) - } else { - onChange('') +
+ + +
+
+ } + position="bottom" + offset={8} + contentClassName="p-4 border shadow-xl bg-glass bg-content border-base-300/20 rounded-2xl max-w-none" + closeOnClickOutside={true} + /> + + ) +} + +interface ScrollWheelProps { + label: string + value: number + max: number + onChange: (value: number) => void + type: 'number' | 'month' | 'year' + startYear?: number +} + +function ScrollWheel({ value, max, onChange, type, startYear }: ScrollWheelProps) { + const containerRef = useRef(null) + const scrollTimeoutRef = useRef(null) + const ITEM_HEIGHT = 40 + + const items = + type === 'number' + ? Array.from({ length: max }, (_, i) => i + 1) + : type === 'month' + ? PERSIAN_MONTHS + : Array.from({ length: max }, (_, i) => (startYear || 1403) - i) + + useEffect(() => { + if (!containerRef.current) return + + let index = 0 + if (type === 'number') { + index = value - 1 + } else if (type === 'month') { + index = value - 1 + } else if (type === 'year') { + // @ts-expect-error + index = items.indexOf(value) } - } - const handleYearChange = (selectedYear: string) => { - setYear(selectedYear) + containerRef.current.scrollTop = index * ITEM_HEIGHT + }, [value, type, items]) - if (selectedYear && month && day) { - const maxDays = getJalaliDaysInMonth( - parseInt(selectedYear, 10), - parseInt(month, 10) - ) - const currentDay = parseInt(day, 10) + const handleScroll = () => { + if (!containerRef.current) return - if (currentDay > maxDays) { - setDay('1') - updateDate(selectedYear, month, '1') - } else { - updateDate(selectedYear, month, day) - } + if (scrollTimeoutRef.current) { + clearTimeout(scrollTimeoutRef.current) } - } - const handleMonthChange = (selectedMonth: string) => { - setMonth(selectedMonth) + scrollTimeoutRef.current = setTimeout(() => { + if (!containerRef.current) return - if (year && selectedMonth && day) { - const maxDays = getJalaliDaysInMonth( - parseInt(year, 10), - parseInt(selectedMonth, 10) - ) - const currentDay = parseInt(day, 10) + const scrollTop = containerRef.current.scrollTop + const index = Math.round(scrollTop / ITEM_HEIGHT) + const clampedIndex = Math.max(0, Math.min(index, items.length - 1)) - if (currentDay > maxDays) { - setDay('1') - updateDate(year, selectedMonth, '1') + let newValue: number + if (type === 'number' || type === 'month') { + newValue = clampedIndex + 1 } else { - updateDate(year, selectedMonth, day) + newValue = items[clampedIndex] as number } - } - } - const handleDayChange = (selectedDay: string) => { - setDay(selectedDay) - updateDate(year, month, selectedDay) + onChange(newValue) + + containerRef.current.scrollTo({ + top: clampedIndex * ITEM_HEIGHT, + behavior: 'smooth', + }) + }, 150) } - const availableDays = - year && month - ? getJalaliDaysInMonth(Number.parseInt(year, 10), Number.parseInt(month, 10)) - : 31 - - const yearOptions = [ - { value: '', label: 'انتخاب سال' }, - ...JALALI_YEARS.map((y) => ({ value: y.toString(), label: y.toString() })), - ] - - const monthOptions = [ - { value: '', label: 'انتخاب ماه' }, - ...JALALI_MONTHS.map((monthName, index) => ({ - value: (index + 1).toString(), - label: monthName, - })), - ] - - const dayOptions = [ - { value: '', label: 'انتخاب روز' }, - ...Array.from({ length: availableDays }, (_, i) => ({ - value: (i + 1).toString(), - label: (i + 1).toString(), - })), - ] + const handleItemClick = (index: number) => { + let newValue: number + if (type === 'number' || type === 'month') { + newValue = index + 1 + } else { + newValue = items[index] as number + } + onChange(newValue) + } return ( -
- - -
- - - - - +
+
+ +
e.stopPropagation()} + > +
+ {items.map((item, index) => { + let isActive = false + if (type === 'number' || type === 'month') { + isActive = index + 1 === value + } else { + isActive = item === value + } + + return ( +
handleItemClick(index)} + className="flex items-center justify-center transition-all cursor-pointer" + style={{ height: `${ITEM_HEIGHT}px` }} + > + + {item} + +
+ ) + })} +
+ +
+
) } diff --git a/src/layouts/setting/tabs/account/components/profile-display.tsx b/src/layouts/setting/tabs/account/components/profile-display.tsx index 35955433..dcd24291 100644 --- a/src/layouts/setting/tabs/account/components/profile-display.tsx +++ b/src/layouts/setting/tabs/account/components/profile-display.tsx @@ -1,6 +1,7 @@ import moment from 'jalali-moment' import { BsGenderAmbiguous, BsGenderFemale, BsGenderMale } from 'react-icons/bs' -import { FiCalendar, FiEdit, FiMail } from 'react-icons/fi' +import { FiCalendar, FiEdit, FiMail, FiUser } from 'react-icons/fi' +import { LuBriefcase, LuHeart } from 'react-icons/lu' import { AvatarComponent } from '@/components/avatar.component' import { Button } from '@/components/button/button' import { OfflineIndicator } from '@/components/offline-indicator' @@ -13,151 +14,155 @@ interface ProfileDisplayProps { } const getGenderInfo = (gender: 'MALE' | 'FEMALE' | 'OTHER' | null | undefined) => { - if (gender === 'MALE') - return { - label: 'مذکر', - icon: , - } - if (gender === 'FEMALE') - return { - label: 'مؤنث', - icon: , - } - if (gender === 'OTHER') - return { - label: 'نامشخص', - icon: , - } - - return { - label: 'نامشخص', - icon: , - } + if (gender === 'MALE') return { label: 'مذکر', icon: } + if (gender === 'FEMALE') return { label: 'مؤنث', icon: } + return { label: 'نامشخص', icon: } } const formatJalaliDate = (dateString: string | null | undefined): string => { - if (!dateString) return '-' + if (!dateString) return 'تنظیم نشده' try { const jalaliDate = moment(dateString, 'jYYYY-jMM-jDD') - - if (!jalaliDate.isValid()) { - return dateString - } - - return jalaliDate.locale('fa').format('jD jMMMM jYYYY') + return jalaliDate.isValid() + ? jalaliDate.locale('fa').format('jD jMMMM jYYYY') + : dateString } catch { - return dateString + return dateString || '-' } } export const ProfileDisplay = ({ profile, onEditToggle }: ProfileDisplayProps) => { - return ( -
-
-
-
-
+ const genderInfo = getGenderInfo(profile?.gender) -
-
-
-
- + return ( +
+
+ {profile?.joinedAt && ( +
+
+ +
+ + شروعِ ماجرا از + + + {moment(profile.joinedAt) + .locale('fa') + .format('jMMMM jYYYY')} + +
-
+ )} +
+
+ +
+
+
-
-
-
-
-

- {profile?.name || 'کاربر'} -

-

- @{profile?.username || '-'} -

-
- {profile?.coins !== undefined && ( -
- -
- )} -
-
+

+ {profile?.name || 'کاربر'} +

+

+ @{profile?.username || 'username'} +

-
-
-
- -
-
-

- ایمیل -

-

- {profile?.email || '-'} -

-
-
+ {profile?.coins !== undefined && ( +
+ +
+ )} +
-
-
- {getGenderInfo(profile?.gender).icon && ( -
- {getGenderInfo(profile?.gender).icon} -
- )} -
-
-

- جنسیت -

-

- {getGenderInfo(profile?.gender).label} -

-
-
+
+ } + label="نام و نام خانوادگی" + value={profile?.name} + /> -
-
- -
-
-

- تاریخ تولد -

-

- {formatJalaliDate(profile?.birthDate)} -

-
-
-
+ } + label="ایمیل" + value={profile?.email} + isLtr + /> -
- -
-
-
+ {genderInfo.icon}
} + label="جنسیت" + value={genderInfo.label} + /> - {profile?.inCache && ( -
- -
- )} + } + label="تاریخ تولد" + value={formatJalaliDate(profile?.birthDate)} + /> + + } + label="شغل" + value={profile?.occupation?.label} + /> + + } + label="علایق" + value={ + profile?.interests?.map((i) => i.label).join('، ') || + 'انتخاب نشده' + } + />
+ + {/* دکمه ویرایش */} + + + {profile?.inCache && ( +
+ +
+ )}
) } + +const DisplayRow = ({ + icon, + label, + value, + isLtr = false, +}: { + icon: React.ReactNode + label: string + value?: string + isLtr?: boolean +}) => ( +
+
+
+ {icon} +
+ {label} +
+ + {value || '-'} + +
+) diff --git a/src/layouts/setting/tabs/account/components/profile-edit-form.tsx b/src/layouts/setting/tabs/account/components/profile-edit-form.tsx index ca904108..05ede312 100644 --- a/src/layouts/setting/tabs/account/components/profile-edit-form.tsx +++ b/src/layouts/setting/tabs/account/components/profile-edit-form.tsx @@ -1,19 +1,27 @@ import type { AxiosError } from 'axios' import moment from 'jalali-moment' import { useEffect, useRef, useState } from 'react' +import { LuCamera, LuUser, LuBriefcase, LuChevronRight } from 'react-icons/lu' import { AvatarComponent } from '@/components/avatar.component' import { Button } from '@/components/button/button' -import { ItemSelector } from '@/components/item-selector' import { TextInput } from '@/components/text-input' +import { OccupationSelector } from '@/layouts/setting/tabs/account/components/occupation-selector' +import { InterestsSelector } from '@/layouts/setting/tabs/account/components/interests-selector' import { safeAwait } from '@/services/api' import { useUpdateUsername, useUpdateUserProfile, } from '@/services/hooks/auth/authService.hook' import type { UserProfile } from '@/services/hooks/user/userService.hook' +import { + useGetOccupations, + useGetInterests, +} from '@/services/hooks/profile/getProfileMeta.hook' import { translateError } from '@/utils/translate-error' import JalaliDatePicker from './profile-date-picker' import { showToast } from '@/common/toast' +import { SectionPanel } from '@/components/section-panel' +import { FaMars, FaQuestion, FaVenus } from 'react-icons/fa' interface ProfileEditFormProps { profile?: UserProfile @@ -21,6 +29,21 @@ interface ProfileEditFormProps { onSuccess: () => void } +const options = { + MALE: { + label: 'آقا هستم', + icon: , + }, + FEMALE: { + label: 'خانم هستم', + icon: , + }, + OTHER: { + label: 'بماند', + icon: , + }, +} + export const ProfileEditForm = ({ profile, onCancel, @@ -32,13 +55,17 @@ export const ProfileEditForm = ({ birthdate: '', avatar: null as File | null, username: null as string | null, + occupation: null as string | null, + interests: [] as string[], }) const [error, setError] = useState(null) const updateProfileMutation = useUpdateUserProfile() const updateUsernameMutation = useUpdateUsername() - + const { data: occupations = [], isLoading: occupationsLoading } = useGetOccupations() + const { data: interests = [], isLoading: interestsLoading } = useGetInterests() const avatarRef = useRef(null) + useEffect(() => { setFormData({ name: profile?.name || '', @@ -46,6 +73,8 @@ export const ProfileEditForm = ({ birthdate: profile?.birthDate || '', avatar: null, username: profile?.username || null, + occupation: profile?.occupation?.id || null, + interests: profile?.interests.map((i) => i.id) || [], }) }, [profile]) @@ -53,7 +82,6 @@ export const ProfileEditForm = ({ e.preventDefault() const data = new FormData() data.append('name', formData.name) - if (formData.gender) data.append('gender', formData.gender) if (formData.birthdate) { const gregorianDate = moment(formData.birthdate, 'jYYYY-jMM-jDD') @@ -62,31 +90,25 @@ export const ProfileEditForm = ({ data.append('birthdate', gregorianDate) } if (formData.avatar) data.append('avatar', formData.avatar) + if (formData.occupation) data.append('occupationId', formData.occupation) + + formData.interests.map((id) => data.append('interestIds[]', id)) try { if (formData.username && formData.username !== profile?.username) { const usernameRegex = /^[a-zA-Z0-9_]{4,250}$/ if (!usernameRegex.test(formData.username)) { - setError( - 'نام کاربری باید فقط شامل حروف انگلیسی، اعداد و زیرخط باشد و بین 4 تا 250 کاراکتر باشد.' - ) + setError('نام کاربری نامعتبر است') return } - - const [err, response] = await safeAwait( + const [err, _] = await safeAwait( updateUsernameMutation.mutateAsync(formData.username) ) if (err) { errorHandling(err, setError) return - } else { - setFormData((prev) => ({ - ...prev, - username: response.username || '', - })) } } - await updateProfileMutation.mutateAsync(data) setError(null) onSuccess() @@ -98,162 +120,245 @@ export const ProfileEditForm = ({ const onChangeAvatar = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file && file.size > 1024 * 1024) { - showToast('فایل باید کمتر از 1 مگابایت باشد', 'error') + showToast('فایل بزرگتر از ۱ مگابایت است', 'error') return } - const validTypes = ['image/png', 'image/jpeg', 'image/webp'] if (file && !validTypes.includes(file.type)) { - showToast('فقط فرمت‌های png، jpeg و webp مجاز هستند', 'error') + showToast('فرمت فایل نامعتبر است', 'error') return } - - setFormData((prev) => ({ - ...prev, - avatar: file || null, - })) + setFormData((prev) => ({ ...prev, avatar: file || null })) } - const onChangeGender = (gender: 'MALE' | 'FEMALE' | 'OTHER') => { - setFormData((prev) => ({ - ...prev, - gender, - })) - } + return ( +
+
- const onChangeBirthdate = (date: any) => { - setFormData((prev) => ({ - ...prev, - birthdate: date, - })) - } +
+
+
+
+ avatarRef.current?.click()} + /> +
+ + +
+
- return ( -
-
{error && ( -
-

{error}

+
+ {error}
)} +
-
-
- - - setFormData((prev) => ({ - ...prev, - name: value, - })) - } - className="w-full" - /> + } + size="xs" + > +
+
+ + + setFormData((p) => ({ ...p, name: val })) + } + /> +
+
+ + + setFormData((p) => ({ ...p, username: val })) + } + /> +
-
-
-
- onChangeBirthdate(val)} - /> - {!profile?.isBirthDateEditable && ( -

- تاریخ تولد شما به تازگی ویرایش شده، کمی صبر کنید. -

- )} -
-
- -
- onChangeGender('MALE')} - /> - onChangeGender('FEMALE')} - /> - onChangeGender('OTHER')} />
-
+
-
- - - { - avatarRef.current?.click() - }} - /> -
-
+
@@ -264,16 +369,11 @@ export const ProfileEditForm = ({ function errorHandling(err: AxiosError, setError: any) { if (err.response) { - const translate: string | Record = translateError(err) - if (typeof translate === 'string') { - setError(translate) - } else { - const keys = Object.keys(translate) - const messages: Array = [] - keys.forEach((key) => { - messages.push(`${key}: ${translate[key]}`) - }) - setError(messages.join('\n')) - } + const translate = translateError(err) + setError( + typeof translate === 'string' + ? translate + : Object.values(translate).join('\n') + ) } } diff --git a/src/services/hooks/profile/getProfileMeta.hook.ts b/src/services/hooks/profile/getProfileMeta.hook.ts new file mode 100644 index 00000000..229e1dd9 --- /dev/null +++ b/src/services/hooks/profile/getProfileMeta.hook.ts @@ -0,0 +1,40 @@ +import { useQuery } from '@tanstack/react-query' +import { getMainClient } from '@/services/api' + +export interface ProfileMetaItem { + id: string + type: 'OCCUPATION' | 'INTEREST' + title: string + slug: string + isActive: boolean + order: number + createdAt: string +} + +async function fetchProfileMeta( + type: 'OCCUPATION' | 'INTEREST' +): Promise { + const client = await getMainClient() + const response = await client.get(`/profile-meta?type=${type}`) + return response.data +} + +export function useGetOccupations(enabled: boolean = true) { + return useQuery({ + queryKey: ['profile-meta-occupations'], + queryFn: () => fetchProfileMeta('OCCUPATION'), + enabled, + staleTime: 5 * 60 * 1000, // 5 minutes + gcTime: 10 * 60 * 1000, // 10 minutes + }) +} + +export function useGetInterests(enabled: boolean = true) { + return useQuery({ + queryKey: ['profile-meta-interests'], + queryFn: () => fetchProfileMeta('INTEREST'), + enabled, + staleTime: 5 * 60 * 1000, // 5 minutes + gcTime: 10 * 60 * 1000, // 10 minutes + }) +} diff --git a/src/services/hooks/user/userService.hook.ts b/src/services/hooks/user/userService.hook.ts index fafa15d1..49544b7e 100644 --- a/src/services/hooks/user/userService.hook.ts +++ b/src/services/hooks/user/userService.hook.ts @@ -33,6 +33,16 @@ interface FetchedProfile { city?: { id: string } + + occupation: { + id: string + label: string + } + interests: Array<{ + id: string + label: string + }> + joinedAt: string } export interface UserProfile extends FetchedProfile {