plan users

This commit is contained in:
hamid zarghami
2025-05-07 10:22:18 +03:30
parent 7e58aa0d3d
commit 32741a0c62
12 changed files with 193 additions and 6 deletions
+1
View File
@@ -110,5 +110,6 @@ export const Pages = {
create: "/support/create", create: "/support/create",
list: "/support/list", list: "/support/list",
detail: "/support/detail/", detail: "/support/detail/",
planUsers: "/support/plan-users/",
}, },
}; };
+1
View File
@@ -6,4 +6,5 @@ export const SideBarItemHasSubMenu = [
"tickets", "tickets",
"learning", "learning",
"blog", "blog",
"support",
]; ];
+16 -2
View File
@@ -102,7 +102,10 @@
"add_user": "افزودن کاربر", "add_user": "افزودن کاربر",
"role_list": "لیست نقش ها", "role_list": "لیست نقش ها",
"role_create": "افزودن نقش", "role_create": "افزودن نقش",
"reviews": "نظرات" "reviews": "نظرات",
"create_plan": "ایجاد پلن",
"plan_users": "کاربران پلن",
"plan_list": "لیست پلن ها"
}, },
"review": { "review": {
"title": "عنوان", "title": "عنوان",
@@ -779,12 +782,23 @@
"on_site_support": "پشتیبانی در محل", "on_site_support": "پشتیبانی در محل",
"on_site_training": "آموزش در محل", "on_site_training": "آموزش در محل",
"ticket_limit": "ظرفیت ارسال تیکت", "ticket_limit": "ظرفیت ارسال تیکت",
"technical_issue_resolution": "رفع ایراد فنی",
"service_functionality_test": "تست عملکرد سرویس",
"plans": "پلن ها", "plans": "پلن ها",
"plan_name": "نام پلن", "plan_name": "نام پلن",
"price": "قیمت", "price": "قیمت",
"duration": "مدت", "duration": "مدت",
"features": "ویژگی ها", "features": "ویژگی ها",
"actions": "عملیات", "actions": "عملیات",
"new_plan": "پلن جدید" "new_plan": "پلن جدید",
"plan_users": "کاربران ",
"ACTIVE": "فعال",
"INACTIVE": "غیرفعال",
"EXPIRED": "منقضی شده",
"start_date": "تاریخ شروع",
"end_date": "تاریخ پایان",
"status": "وضعیت",
"full_name": "نام و نام خانوادگی",
"users": "کاربران"
} }
} }
+12 -1
View File
@@ -41,17 +41,28 @@ const SupportList: FC = () => {
<Td text={t('support.plan_name')} /> <Td text={t('support.plan_name')} />
<Td text={t('support.price')} /> <Td text={t('support.price')} />
<Td text={t('support.duration')} /> <Td text={t('support.duration')} />
<Td text={t('support.users')} />
<Td text={''} /> <Td text={''} />
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{ {
data?.data?.supportPlans?.map((item: { id: string, name: string, price: number, duration: string }) => { data?.data?.supportPlans?.map((item: { id: string, name: string, price: number, duration: string, userCount: number }) => {
return ( return (
<tr className='tr' key={item.id}> <tr className='tr' key={item.id}>
<Td text={item.name} /> <Td text={item.name} />
<Td text={NumberFormat(item.price)} /> <Td text={NumberFormat(item.price)} />
<Td text={item.duration} /> <Td text={item.duration} />
<Td text={''}>
<Link to={Pages.support.planUsers + item.id}>
<div className='flex items-center gap-2 text-blue-500'>
<div>
{item.userCount}
</div>
کاربر
</div>
</Link>
</Td>
<Td text={''}> <Td text={''}>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Link to={Pages.support.detail + item.id}> <Link to={Pages.support.detail + item.id}>
+56
View File
@@ -0,0 +1,56 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { useGetPlanUsers } from './hooks/useSupportData'
import { useParams } from 'react-router-dom'
import Td from '../../components/Td'
import moment from 'moment-jalaali'
import { PlanItemUserType } from './types/SupportTypes'
const PlanUsers: FC = () => {
const { t } = useTranslation('global')
const { id } = useParams()
const { data } = useGetPlanUsers(id)
return (
<div className='mt-4'>
<div>
{t('support.plan_users')} {data?.data?.users?.[0]?.supportPlan?.name}
</div>
<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('support.full_name')} />
<Td text={t('support.start_date')} />
<Td text={t('support.end_date')} />
<Td text={t('support.status')} />
<Td text={''} />
</tr>
</thead>
<tbody>
{data?.data?.users?.map((item: PlanItemUserType) => (
<tr className='tr' key={item.id}>
<Td text={item.user.firstName + ' ' + item.user.lastName} />
<Td text={''}>
<div className='dltr text-right'>
{moment(item.startDate).format('jYYYY/jMM/jDD HH:mm')}
</div>
</Td>
<Td text={''}>
<div className='dltr text-right'>
{moment(item.endDate).format('jYYYY/jMM/jDD HH:mm')}
</div>
</Td>
<Td text={t(`support.${item.status}`)} />
<Td text={''} />
</tr>
))}
</tbody>
</table>
</div>
</div>
)
}
export default PlanUsers
+4 -2
View File
@@ -1,11 +1,13 @@
export enum SupportPlanFeatureKey { export enum SupportPlanFeatureKey {
LEARNING_DOCS = "learning_docs", // راهنما و مستندات آموزشی LEARNING_DOCS = "learning_docs", // راهنما و مستندات آموزشی
TICKET_SUPPORT = "ticket_support", // ارسال تیکت TICKET_SUPPORT = "ticket_support", // ارسال تیکت
TICKET_LIMIT = "ticket_limit", // ظرفیت ارسال تیکت
RESPONSE_TIME = "response_time", // زمان پاسخگویی به تیکت RESPONSE_TIME = "response_time", // زمان پاسخگویی به تیکت
PHONE_SUPPORT = "phone_support", // پاسخگویی تلفنی PHONE_SUPPORT = "phone_support", // پاسخگویی تلفنی
BACKUP_VERSION = "backup_version", // نسخه بکاپ TECHNICAL_ISSUE_RESOLUTION = "technical_issue_resolution", // رفع ایراد فنی
TECHNICAL_EXPERT_ACCESS = "technical_expert_access", // دسترسی به متخصصین فنی TECHNICAL_EXPERT_ACCESS = "technical_expert_access", // دسترسی به متخصصین فنی
BACKUP_VERSION = "backup_version", // نسخه بکاپ
SERVICE_FUNCTIONALITY_TEST = "service_functionality_test", // تست عملکرد سرویس
ON_SITE_SUPPORT = "on_site_support", // پشتیبانی در محل ON_SITE_SUPPORT = "on_site_support", // پشتیبانی در محل
ON_SITE_TRAINING = "on_site_training", // آموزش در محل ON_SITE_TRAINING = "on_site_training", // آموزش در محل
TICKET_LIMIT = "ticket_limit", // ظرفیت ارسال تیکت
} }
@@ -34,3 +34,11 @@ export const useDeletePlan = () => {
mutationFn: (id: string) => api.deletePlan(id), mutationFn: (id: string) => api.deletePlan(id),
}); });
}; };
export const useGetPlanUsers = (id?: string) => {
return useQuery({
queryKey: ["plan-users", id],
queryFn: () => api.getPlanUsers(id),
enabled: !!id,
});
};
@@ -25,3 +25,8 @@ export const deletePlan = async (id: string) => {
const { data } = await axios.delete(`/support-plans/${id}`); const { data } = await axios.delete(`/support-plans/${id}`);
return data; return data;
}; };
export const getPlanUsers = async (id?: string) => {
const { data } = await axios.get(`/support-plans/${id}/users`);
return data;
};
+37
View File
@@ -10,3 +10,40 @@ export type CreatePlanType = {
featureType: "boolean" | "text"; featureType: "boolean" | "text";
}[]; }[];
}; };
export type PlanItemUserType = {
id: string;
createdAt: string;
updatedAt: string;
user: {
id: string;
createdAt: string;
updatedAt: string;
email: string;
phone: string;
userName: string;
firstName: string;
lastName: string;
birthDate: string;
nationalCode: string;
profilePic: string | null;
emailVerified: boolean;
financialType: string;
deletedAt: string | null;
};
supportPlan: {
id: string;
createdAt: string;
updatedAt: string;
name: string;
description: string;
price: number;
isFree: boolean;
duration: number;
isActive: boolean;
deletedAt: string | null;
};
startDate: string;
endDate: string;
status: string;
};
+2
View File
@@ -68,6 +68,7 @@ import Comments from '../pages/blog/Comments'
import CreatePlan from '../pages/support/Create' import CreatePlan from '../pages/support/Create'
import SupportList from '../pages/support/List' import SupportList from '../pages/support/List'
import UpdateSupport from '../pages/support/Update' import UpdateSupport from '../pages/support/Update'
import PlanUsers from '../pages/support/PlanUsers'
const MainRouter: FC = () => { const MainRouter: FC = () => {
const { hasSubMenu } = useSharedStore() const { hasSubMenu } = useSharedStore()
@@ -147,6 +148,7 @@ const MainRouter: FC = () => {
<Route path={Pages.support.create} element={<CreatePlan />} /> <Route path={Pages.support.create} element={<CreatePlan />} />
<Route path={Pages.support.list} element={<SupportList />} /> <Route path={Pages.support.list} element={<SupportList />} />
<Route path={Pages.support.detail + ':id'} element={<UpdateSupport />} /> <Route path={Pages.support.detail + ':id'} element={<UpdateSupport />} />
<Route path={Pages.support.planUsers + ':id'} element={<PlanUsers />} />
</Routes> </Routes>
</div> </div>
</div> </div>
+4 -1
View File
@@ -17,6 +17,7 @@ import UsersSubMenu from './components/UsersSubMenu'
import LearningSubMenu from './components/LearningSubMenu' import LearningSubMenu from './components/LearningSubMenu'
import BlogSubMenu from './components/BlogSubMenu' import BlogSubMenu from './components/BlogSubMenu'
import { useGetProfile } from '../pages/profile/hooks/useProfileData' import { useGetProfile } from '../pages/profile/hooks/useProfileData'
import SupportSubMenu from './components/SupportSubMenu'
const SideBar: FC = () => { const SideBar: FC = () => {
const { t } = useTranslation('global') const { t } = useTranslation('global')
@@ -324,7 +325,9 @@ const SideBar: FC = () => {
<LearningSubMenu /> <LearningSubMenu />
: subMenuName === 'blog' ? : subMenuName === 'blog' ?
<BlogSubMenu /> <BlogSubMenu />
: null : subMenuName === 'support' ?
<SupportSubMenu />
: null
} }
</div> </div>
} }
+47
View File
@@ -0,0 +1,47 @@
import { Headphone } from 'iconsax-react'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { useLocation } from 'react-router-dom'
import { Pages } from '../../config/Pages'
import SubMenuItem from './SubMenuItem'
const SupportSubMenu: FC = () => {
const location = useLocation()
const { t } = useTranslation('global')
const isActive = (name: string) => {
return location.pathname.includes(name)
}
return <div className='py-12'>
<div className='flex gap-3 items-center border-b pb-10 px-6'>
<Headphone
size={24}
color='#000'
variant='Bold'
/>
<div className='text-xs'>
{t('sidebar.support')}
</div>
</div>
<div className='flex flex-col mt-10 gap-4'>
<SubMenuItem
title={t('submenu.plan_list')}
isActive={isActive('list')}
link={Pages.support.list}
/>
<SubMenuItem
title={t('submenu.create_plan')}
isActive={isActive('create')}
link={Pages.support.create}
/>
</div>
</div>
}
export default SupportSubMenu