quik acess

This commit is contained in:
hamid zarghami
2025-02-25 12:24:43 +03:30
parent 24a5461d2a
commit 221bcc052c
14 changed files with 357 additions and 103 deletions
+3 -2
View File
@@ -7,7 +7,8 @@ import { useTranslation } from 'react-i18next'
import { ItemServiceType } from '../pages/service/types/ServiecTypes'
type Props = {
item: ItemServiceType
item: ItemServiceType,
className?: string,
}
const ServiceItem: FC<Props> = (props: Props) => {
@@ -16,7 +17,7 @@ const ServiceItem: FC<Props> = (props: Props) => {
const { item } = props
return (
<div className='flex-1 min-w-[45%] xl:min-w-[30%] bg-white rounded-3xl xl:p-6 p-4'>
<div className={`flex-1 min-w-[45%] xl:min-w-[30%] bg-white rounded-3xl xl:p-6 p-4 ${props.className}`}>
<div className='flex gap-2 items-center'>
<div className='xl:size-[50px] size-10 overflow-hidden rounded-xl'>
<img src={item.icon} alt={item.name} className='w-full h-full' />
+13
View File
@@ -419,5 +419,18 @@
"amount": "مبلغ پرداختی",
"date": "تاریخ",
"back": "بازگشت"
},
"discount": {
"number": "شماره",
"type": "نوع تحفیف",
"code": "کد تخفیف",
"CODE": "کد",
"start_date": "تاریخ شروع",
"end_date": "تاریخ پایان",
"percent_ammount": "درصد / مبلغ تخفیف",
"service": "سرویس",
"status": "وضعیت",
"active": "فعال",
"deactive": "غیرفعال"
}
}
+61
View File
@@ -0,0 +1,61 @@
import { FC } from 'react'
import { useGetMyDiscount } from './hooks/useDiscountData'
import PageLoading from '../../components/PageLoading'
import Td from '../../components/Td'
import { useTranslation } from 'react-i18next'
import { DiscountItemType } from './types/DiscountTypes'
import moment from 'moment-jalaali'
const MyDiscountList: FC = () => {
const { t } = useTranslation('global')
const getDiscounts = useGetMyDiscount()
return (
<div className='mt-4'>
{
getDiscounts.isPending ?
<PageLoading />
:
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
<table className='w-full text-sm '>
<thead className='thead'>
<tr>
<Td text={t('discount.number')} />
<Td text={t('discount.type')} />
<Td text={t('discount.code')} />
<Td text={t('discount.start_date')} />
<Td text={t('discount.end_date')} />
<Td text={t('discount.percent_ammount')} />
<Td text={t('discount.service')} />
<Td text={t('discount.status')} />
</tr>
</thead>
<tbody>
{
getDiscounts.data?.data?.discounts?.map((item: DiscountItemType, index: number) => {
return (
<tr key={item.id} className='tr'>
<Td text={String(index + 1)} />
<Td text={t(`discount.${item.type}`)} />
<Td text={item.code} />
<Td text={moment(item.startDate).format('jYYYY-jMM-jDD')} />
<Td text={moment(item.endDate).format('jYYYY-jMM-jDD')} />
<Td text={item.type === 'CODE' ? item.amount + '%' : item.amount + 'تومان'} />
<Td text={item.subscriptionPlans[0]?.service?.name} />
<Td text={''}>
{item.isActive ? <span className='text-green-400'>{t('discount.active')}</span> : <span className='text-gray-500'>{t('discount.inactive')}</span>}
</Td>
</tr>
)
})
}
</tbody>
</table>
</div>
}
</div>
)
}
export default MyDiscountList
@@ -0,0 +1,9 @@
import { useQuery } from "@tanstack/react-query";
import * as api from "../service/DiscountService";
export const useGetMyDiscount = () => {
return useQuery({
queryKey: ["my-discount"],
queryFn: () => api.getMyDiscounts(),
});
};
@@ -0,0 +1,6 @@
import axios from "../../../config/axios";
export const getMyDiscounts = async () => {
const { data } = await axios.get(`/discounts/user-discounts`);
return data;
};
@@ -0,0 +1,30 @@
export type DiscountItemType = {
id: string;
createdAt: string;
updatedAt: string;
title: string;
type: string;
calculationType: string;
amount: number;
isActive: boolean;
code: string;
startDate: string;
endDate: string;
subscriptionPlans: {
id: string;
createdAt: string;
updatedAt: string;
name: string;
duration: number;
price: number;
isActive: boolean;
service: {
id: string;
name: string;
title: string;
description: string;
link: string;
icon: string;
};
}[];
};
+121 -90
View File
@@ -1,4 +1,4 @@
import { FC } from 'react'
import { FC, Fragment } from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowLeft, Element3, Messages3, NotificationStatus, Receipt21 } from 'iconsax-react'
import ItemDashboard from './components/ItemDashboard'
@@ -10,110 +10,141 @@ import { Carousel } from "@material-tailwind/react";
import { useGetAds } from '../ads/hooks/useAdsData'
import { AdsDisplayLocation, AdsItemType } from '../ads/types/AdsTypes'
import { Pages } from '../../config/Pages'
import { useGetQuikAccess } from './hooks/useHomeData'
import { QuikAccessItemType } from './types/HomeTypes'
import ServiceItem from '../../components/ServiceItem'
import PageLoading from '../../components/PageLoading'
const Home: FC = () => {
const { t } = useTranslation('global')
const getAds = useGetAds(AdsDisplayLocation.HOMEPAGE_TOP)
const getQuikAccess = useGetQuikAccess()
return (
<div className='w-full flex gap-6'>
<div className='flex-1'>
<Carousel autoplay className="rounded-xl h-fit dltr" placeholder="" onPointerEnterCapture={() => { }} onPointerLeaveCapture={() => { }} >
{
getAds.data?.data?.ads?.map((item: AdsItemType) => {
return (
<div key={item.id} className='relative drtl'>
<img src={item.imageUrl} className='w-full max-h-[300px] h-full object-cover rounded-3xl' />
<div className='absolute bg-black bg-opacity-20 size-full top-0 right-0 py-6 px-8 flex flex-col justify-between'>
<button className="px-8 w-fit py-1.5 bg-white bg-opacity-10 text-white text-sm rounded-full shadow-md ">
{t('home.new')}
</button>
<div className='max-w-[340px] font-medium text-white text-lg leading-10'>
<div>
{item.title}
</div>
<div className='mt-3 text-border text-xs'>
۸ دقیقه مطالعه
</div>
{
<a href={item.link}>
<button className='mt-3 px-4 h-9 flex gap-2 items-center text-xs rounded-2.5 bg-black text-white'>
<div>
{t('home.study')}
</div>
<ArrowLeft size={14} color='white' />
<Fragment>
{
getAds.isPending || getQuikAccess.isPending ?
<div className='mt-5'>
<PageLoading />
</div>
:
<div className='w-full flex gap-6'>
<div className='flex-1'>
<Carousel autoplay className="rounded-xl h-fit dltr" placeholder="" onPointerEnterCapture={() => { }} onPointerLeaveCapture={() => { }} >
{
getAds.data?.data?.ads?.map((item: AdsItemType) => {
return (
<div key={item.id} className='relative drtl'>
<img src={item.imageUrl} className='w-full max-h-[300px] h-full object-cover rounded-3xl' />
<div className='absolute bg-black bg-opacity-20 size-full top-0 right-0 py-6 px-8 flex flex-col justify-between'>
<button className="px-8 w-fit py-1.5 bg-white bg-opacity-10 text-white text-sm rounded-full shadow-md ">
{t('home.new')}
</button>
</a>
}
<div className='max-w-[340px] font-medium text-white text-lg leading-10'>
<div>
{item.title}
</div>
<div className='mt-3 text-border text-xs'>
۸ دقیقه مطالعه
</div>
{
<a href={item.link}>
<button className='mt-3 px-4 h-9 flex gap-2 items-center text-xs rounded-2.5 bg-black text-white'>
<div>
{t('home.study')}
</div>
<ArrowLeft size={14} color='white' />
</button>
</a>
}
</div>
</div>
</div>
)
})
}
</Carousel>
<div className='mt-8 flex-wrap text-sm flex gap-6 items-center'>
<ItemDashboard
title={t('home.myservice')}
icon={<Element3 size={20} color='black' />}
color='#00D16C'
count={4}
description={t('home.active_service')}
link={Pages.services.mine}
/>
<ItemDashboard
title={t('home.ticket')}
icon={<Messages3 size={20} color='black' />}
color='#FF7B00'
count={4}
description={t('home.unread_messages')}
link={Pages.ticket.list}
/>
<ItemDashboard
title={t('receip.receip')}
icon={<Receipt21 size={20} color='black' />}
color='#0047FF'
count={4}
description={t('home.receip')}
link={Pages.receipts.index}
/>
<ItemDashboard
title={t('home.announcement')}
icon={<NotificationStatus size={20} color='black' />}
color='#FF0000'
count={4}
description={t('home.unread_announcement')}
link={Pages.announcement.list}
/>
</div>
<div className='w-full mt-8'>
<TitleLine title={t('home.access')} />
</div>
<div className='mt-8 flex flex-wrap gap-6 items-stretch'>
{
getQuikAccess?.data?.data?.quickAccesses?.length === 0 &&
<div className='flex-1 min-w-[40%] xl:min-w-[20%] min-h-[152px] rounded-3xl bg-white flex flex-col gap-4 justify-center items-center'>
<img src={AccessbilityImage} className='w-10' />
<div className='text-xs'>
{t('home.add_access')}
</div>
</div>
</div>
)
})
}
</Carousel>
}
{
getQuikAccess.data?.data?.quickAccesses?.map((item: QuikAccessItemType) => {
return (
<ServiceItem
key={item.service.id}
item={item.service}
className='flex-1 min-w-[40%] xl:!min-w-[20%] xl:p-6 p-4 flex flex-col'
/>
)
})
}
{Array.from({ length: 4 - (getQuikAccess.data?.data?.quickAccesses?.length || 1) }).map((_, index) => (
<BoxNewAccessbility key={index} />
))}
<div className='mt-8 flex-wrap text-sm flex gap-6 items-center'>
<ItemDashboard
title={t('home.myservice')}
icon={<Element3 size={20} color='black' />}
color='#00D16C'
count={4}
description={t('home.active_service')}
link={Pages.services.mine}
/>
<ItemDashboard
title={t('home.ticket')}
icon={<Messages3 size={20} color='black' />}
color='#FF7B00'
count={4}
description={t('home.unread_messages')}
link={Pages.ticket.list}
/>
<ItemDashboard
title={t('receip.receip')}
icon={<Receipt21 size={20} color='black' />}
color='#0047FF'
count={4}
description={t('home.receip')}
link={Pages.receipts.index}
/>
<ItemDashboard
title={t('home.announcement')}
icon={<NotificationStatus size={20} color='black' />}
color='#FF0000'
count={4}
description={t('home.unread_announcement')}
link={Pages.announcement.list}
/>
</div>
<div className='w-full mt-8'>
<TitleLine title={t('home.access')} />
</div>
<div className='mt-8 flex flex-wrap gap-6 items-center'>
<div className='flex-1 min-w-[40%] xl:min-w-[20%] h-[160px] rounded-3xl bg-white flex flex-col gap-4 justify-center items-center'>
<img src={AccessbilityImage} className='w-10' />
<div className='text-xs'>
{t('home.add_access')}
<div className='flex-1 min-w-[40%] xl:min-w-[20%] xl:p-6 p-4'></div>
<div className='flex-1 min-w-[40%] xl:min-w-[20%] xl:p-6 p-4'></div>
</div>
</div>
<DanakLearning />
</div>
<BoxNewAccessbility />
<BoxNewAccessbility />
<BoxNewAccessbility />
</div>
</div>
<DanakLearning />
</div>
}
</Fragment>
)
}
@@ -3,10 +3,11 @@ import { FC, useState } from 'react'
import DefaulModal from '../../../components/DefaulModal'
import { useTranslation } from 'react-i18next'
import Input from '../../../components/Input'
import SwitchComponent from '../../../components/Switch'
import { useGetMyServices } from '../../service/hooks/useServiceData'
import { MyServicesItem } from '../../service/types/ServiecTypes'
import PageLoading from '../../../components/PageLoading'
import { useGetQuikAccess, useQuikAccess, useRemoveQuikAccess } from '../hooks/useHomeData'
import ChangeQuikAccess from './ChangeQuikAccess'
const BoxNewAccessbility: FC = () => {
@@ -14,12 +15,31 @@ const BoxNewAccessbility: FC = () => {
const [open, setOpen] = useState<boolean>(false)
const { t } = useTranslation('global')
const getMyservices = useGetMyServices(search)
const getQuikAccess = useGetQuikAccess()
const quikAccess = useQuikAccess()
const removeQuikAceess = useRemoveQuikAccess()
const handleQuikAccess = (value: boolean, id: string) => {
if (value) {
quikAccess.mutate(id, {
onSuccess: () => {
getQuikAccess.refetch()
}
})
} else {
removeQuikAceess.mutate(id, {
onSuccess: () => {
getQuikAccess.refetch()
}
})
}
}
return (
<>
<div
onClick={() => setOpen(true)}
className="flex-1 min-w-[40%] xl:min-w-[20%] flex justify-center items-center h-[160px]"
className="flex-1 min-w-[40%] xl:p-6 p-4 xl:min-w-[20%] flex justify-center items-center min-h-[152px]"
style={{
position: 'relative',
}}
@@ -91,6 +111,7 @@ const BoxNewAccessbility: FC = () => {
</div>
:
getMyservices.data?.data?.subscriptions?.map((item: MyServicesItem) => {
const active = getQuikAccess.data?.data?.quickAccesses?.find((o: any) => o.service?.id === item.plan?.service?.id) ? true : false
return (
<div key={item.id} className='flex-1 xl:min-w-[40%] min-w-full flex justify-between items-center border-b border-[#8C90A3] border-opacity-30 py-2'>
<div className='flex gap-4'>
@@ -107,9 +128,10 @@ const BoxNewAccessbility: FC = () => {
</div>
</div>
<div>
<SwitchComponent
active
onChange={() => { }}
<ChangeQuikAccess
id={item.plan?.service?.id}
value={active}
onChange={handleQuikAccess}
/>
</div>
</div>
@@ -0,0 +1,26 @@
import { FC, useState } from 'react'
import SwitchComponent from '../../../components/Switch'
type Props = {
id: string,
value: boolean,
onChange: (value: boolean, id: string) => void
}
const ChangeQuikAccess: FC<Props> = (props: Props) => {
const [active, setActive] = useState<boolean>(props.value)
return (
<SwitchComponent
active={active}
onChange={(value) => {
props.onChange(value, props.id)
setActive(value)
}}
/>
)
}
export default ChangeQuikAccess
+21
View File
@@ -0,0 +1,21 @@
import * as api from "../service/HomeService";
import { useMutation, useQuery } from "@tanstack/react-query";
export const useQuikAccess = () => {
return useMutation({
mutationFn: (variables: string) => api.quikAccess(variables),
});
};
export const useGetQuikAccess = () => {
return useQuery({
queryKey: ["quik-access"],
queryFn: () => api.getQuikAccess(),
});
};
export const useRemoveQuikAccess = () => {
return useMutation({
mutationFn: (variables: string) => api.removeQuikAccess(variables),
});
};
+20
View File
@@ -0,0 +1,20 @@
import axios from "../../../config/axios";
export const quikAccess = async (serviceId: string) => {
const { data } = await axios.post(
`/subscriptions/${serviceId}/add-quick-access`
);
return data;
};
export const getQuikAccess = async () => {
const { data } = await axios.get(`/subscriptions/quick-access`);
return data;
};
export const removeQuikAccess = async (serviceId: string) => {
const { data } = await axios.delete(
`/subscriptions/${serviceId}/remove-quick-access`
);
return data;
};
+11
View File
@@ -0,0 +1,11 @@
import { ItemServiceType } from "../../service/types/ServiecTypes";
export type QuikAccessItemType = {
id: string;
createdAt: string;
isActive: boolean;
service: ItemServiceType;
author: string;
createDate: string;
updatedAt: string;
};
+7 -6
View File
@@ -95,10 +95,15 @@ const TicketDetail: FC = () => {
:
<div className='flex xl:flex-row flex-col-reverse gap-6 xl:mt-8 mt-6'>
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<div className='gap-6 rowTwoInput'>
<Input
label={t('ticket.subject')}
value={getMessages.data?.data?.ticket?.subject}
readOnly
/>
<div className='gap-6 rowTwoInput mt-5'>
<Input
label={t('ticket.select_your_service')}
value={'دی منو'}
value={getMessages.data?.data?.ticket?.danakService?.name}
readOnly
/>
<Input
@@ -108,10 +113,6 @@ const TicketDetail: FC = () => {
/>
</div>
<div className='mt-8 xl:text-sm text-xs'>
{getMessages.data?.data?.ticket?.subject}
</div>
{
getMessages.data?.data?.messages.map((item: any) => {
if (item?.author?.roles[0]?.name === 'user') {
+2
View File
@@ -26,6 +26,7 @@ import CreateTicket from '../pages/ticket/CreateTicket'
import TicketDetail from '../pages/ticket/Detail'
import Financial from '../pages/financial/Financial'
import CallBack from '../pages/wallet/CallBack'
import MyDiscountList from '../pages/discounts/List'
const MainRouter: FC = () => {
return (
@@ -55,6 +56,7 @@ const MainRouter: FC = () => {
<Route path={Pages.callback + ':id'} element={<CallBack />} />
<Route path={Pages.profile} element={<Profile />} />
<Route path={Pages.financial} element={<Financial />} />
<Route path={Pages.discounts} element={<MyDiscountList />} />
</Routes>
</div>
</div>