learnong
This commit is contained in:
@@ -14,7 +14,10 @@ export const Paths = {
|
||||
detail: '/announcement/',
|
||||
},
|
||||
criticisms: '/criticisms',
|
||||
learning: '/learning',
|
||||
learning: {
|
||||
list: '/learning/list',
|
||||
detail: '/learning/',
|
||||
},
|
||||
auth: {
|
||||
login: '/auth/login',
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { CreateCriticismsTypes } from "../types/CriticismsTypes";
|
||||
import { type CreateCriticismsTypes } from "../types/CriticismsTypes";
|
||||
import * as api from "../service/CriticismsService";
|
||||
|
||||
export const useCreateCriticisms = () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from "../../../config/axios";
|
||||
import { CreateCriticismsTypes } from "../types/CriticismsTypes";
|
||||
import { type CreateCriticismsTypes } from "../types/CriticismsTypes";
|
||||
|
||||
export const createCriticisms = async (params: CreateCriticismsTypes) => {
|
||||
const { data } = await axios.post(`/criticisms`, params);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { type FC } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useGetLearningDetail } from './hooks/useLearningData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
// import DanakLearning from '../home/components/DanakLearning'
|
||||
import { Calendar2, Clock, Element3 } from 'iconsax-react'
|
||||
import moment from 'moment-jalaali'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import { type LearningItemType } from './types/LearningTypes'
|
||||
|
||||
const LearningDetail: FC = () => {
|
||||
|
||||
const { id } = useParams()
|
||||
const getLearningDetail = useGetLearningDetail(id ? id : '')
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<Helmet>
|
||||
<title>
|
||||
داناک | آموزش
|
||||
</title>
|
||||
</Helmet>
|
||||
{
|
||||
getLearningDetail.isPending ?
|
||||
<PageLoading />
|
||||
:
|
||||
<div className='xl:mt-9 mt-4 flex flex-wrap xl:gap-8 gap-6 text-sm'>
|
||||
<div className='flex-1'>
|
||||
|
||||
|
||||
<div className='bg-white p-8 rounded-3xl'>
|
||||
<video className='w-full rounded-3xl' controls>
|
||||
<source src={(getLearningDetail.data as { data: { learning: LearningItemType } })?.data?.learning?.videoUrl} />
|
||||
</video>
|
||||
|
||||
<div className='mt-7 flex xl:flex-row flex-col gap-4 justify-between'>
|
||||
<div className='xl:text-lg'>
|
||||
{(getLearningDetail.data as { data: { learning: LearningItemType } })?.data?.learning?.title}
|
||||
</div>
|
||||
|
||||
<div className='flex xl:flex-row flex-col xl:max-h-5 gap-3 text-description text-xs'>
|
||||
|
||||
<div className='flex gap-2 items-center'>
|
||||
<Clock
|
||||
size={18}
|
||||
color={'#888888'}
|
||||
/>
|
||||
<div>
|
||||
{(getLearningDetail.data as { data: { learning: LearningItemType } })?.data?.learning?.videoDuration + ' دقیقه'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2 items-center xl:border-r xl:pr-3 border-description'>
|
||||
<Element3
|
||||
size={18}
|
||||
color={'#888888'}
|
||||
/>
|
||||
<div>
|
||||
{(getLearningDetail.data as { data: { learning: LearningItemType } })?.data?.learning?.category?.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2 items-center xl:border-r xl:pr-3 border-description'>
|
||||
<Calendar2
|
||||
size={18}
|
||||
color={'#888888'}
|
||||
/>
|
||||
<div>
|
||||
{moment((getLearningDetail.data as { data: { learning: LearningItemType } })?.data?.learning?.createdAt).format('jYYYY-jMM-jDD')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div className='mt-6 text-description'>
|
||||
{(getLearningDetail.data as { data: { learning: LearningItemType } })?.data?.learning?.description}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{/* <DanakLearning /> */}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LearningDetail
|
||||
@@ -0,0 +1,106 @@
|
||||
import { type FC, useState } from 'react'
|
||||
// import { useTranslation } from 'react-i18next'
|
||||
import BannerImage from '../../assets/images/support1.png'
|
||||
import Input from '../../components/Input'
|
||||
import { useGetLearnings } from './hooks/useLearningData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
import { type LearningItemType } from './types/LearningTypes'
|
||||
import moment from 'moment-jalaali'
|
||||
import { Link } from 'react-router-dom'
|
||||
// import { Pages } from '../../config/Pages'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
|
||||
const LearningList: FC = () => {
|
||||
|
||||
// const { t } = useTranslation('global')
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const getLearnings = useGetLearnings(search)
|
||||
|
||||
return (
|
||||
<div className='w-full flex gap-6 mt-4'>
|
||||
<Helmet>
|
||||
<title>
|
||||
داناک | آموزش
|
||||
</title>
|
||||
</Helmet>
|
||||
<div className='flex-1'>
|
||||
<div>
|
||||
آموزش
|
||||
</div>
|
||||
|
||||
<div className='xl:mt-8 mt-4 flex xl:gap-6 gap-4 items-center'>
|
||||
<div className='xl:w-[300px] w-full'>
|
||||
<Input
|
||||
variant='search'
|
||||
placeholder='جستجو...'
|
||||
className='bg-white w-full'
|
||||
onChangeSearchFinal={(value) => setSearch(value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* <div>
|
||||
<Select
|
||||
items={[
|
||||
{ label: t('all'), value: 'all' },
|
||||
{ label: 'Active', value: 'active' },
|
||||
{ label: 'Inactive', value: 'inactive' },
|
||||
]}
|
||||
className='max-w-[100px]'
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
{
|
||||
getLearnings.isPending ?
|
||||
<div className='mt-5'>
|
||||
<PageLoading />
|
||||
</div>
|
||||
:
|
||||
<div className='xl:mt-9 mt-4 flex flex-wrap xl:gap-8 gap-6 text-sm'>
|
||||
{
|
||||
(getLearnings.data as { data: { learnings: LearningItemType[] } })?.data?.learnings?.map((item: LearningItemType) => {
|
||||
return (
|
||||
<Link to={`/learning/${item.id}`} className='bg-white p-4 rounded-3xl xl:min-w-[30%] min-w-full flex-1'>
|
||||
<img src={item.coverUrl} className='w-full rounded-[20px]' />
|
||||
<div className='mt-4'>
|
||||
{item.title}
|
||||
</div>
|
||||
<div className='flex gap-1 text-xs text-description mt-2'>
|
||||
<div>{item.category.name} ,</div>
|
||||
<div>{item.videoDuration} دقیقه</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-3 text-xs text-description'>
|
||||
{moment(item.createdAt).format('jYYYY/jMM/jDD')}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
<div className='min-w-[30%] flex-1 px-4'></div>
|
||||
<div className='min-w-[30%] flex-1 px-4'></div>
|
||||
</div>
|
||||
}
|
||||
<div className='h-14 xl:hidden'></div>
|
||||
</div>
|
||||
|
||||
<div className='bg-white w-[300px] hidden xl:block h-fit rounded-3xl overflow-hidden relative'>
|
||||
<img
|
||||
src={BannerImage}
|
||||
className='w-full backdrop-blur-[100px] h-[550px] object-cover'
|
||||
/>
|
||||
|
||||
<div className='absolute flex items-center px-4 w-full bottom-0 h-[86px] bg-primary bg-opacity-5 backdrop-blur-[14px]'>
|
||||
<div className='max-w-[80%] text-sm leading-6 text-white'>
|
||||
سفارش نرمافزار اختصاصی: سرمایهای برای آینده یا فقط هزینهای برای امروز؟
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LearningList
|
||||
@@ -0,0 +1,138 @@
|
||||
// import type { LearningItemType } from "../types/LearningTypes";
|
||||
|
||||
export const staticLearningsData = {
|
||||
data: {
|
||||
learnings: [
|
||||
{
|
||||
id: "1",
|
||||
title: "آموزش کامل React و TypeScript",
|
||||
description:
|
||||
"در این دوره شما با مفاهیم پایه و پیشرفته React و TypeScript آشنا خواهید شد و پروژههای عملی انجام خواهید داد.",
|
||||
coverUrl: "/src/assets/images/new_order.png",
|
||||
videoUrl:
|
||||
"https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4",
|
||||
videoDuration: "120",
|
||||
createdAt: "2024-01-15T10:30:00Z",
|
||||
updatedAt: "2024-01-15T10:30:00Z",
|
||||
category: {
|
||||
id: "1",
|
||||
name: "Frontend",
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "Node.js و Express.js از صفر تا صد",
|
||||
description:
|
||||
"یادگیری کامل Node.js و Express.js برای توسعه سمت سرور و ایجاد API های قدرتمند.",
|
||||
coverUrl: "/src/assets/images/support1.png",
|
||||
videoUrl:
|
||||
"https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_2mb.mp4",
|
||||
videoDuration: "180",
|
||||
createdAt: "2024-01-20T14:15:00Z",
|
||||
updatedAt: "2024-01-20T14:15:00Z",
|
||||
category: {
|
||||
id: "2",
|
||||
name: "Backend",
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "آموزش MongoDB و Mongoose",
|
||||
description:
|
||||
"کار با پایگاه داده NoSQL MongoDB و کتابخانه Mongoose برای مدیریت دادهها.",
|
||||
coverUrl: "/src/assets/images/logo.svg",
|
||||
videoUrl:
|
||||
"https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_5mb.mp4",
|
||||
videoDuration: "90",
|
||||
createdAt: "2024-01-25T09:45:00Z",
|
||||
updatedAt: "2024-01-25T09:45:00Z",
|
||||
category: {
|
||||
id: "3",
|
||||
name: "Database",
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
title: "Docker و Containerization",
|
||||
description:
|
||||
"آموزش کامل Docker برای containerization و deployment اپلیکیشنها.",
|
||||
coverUrl: "/src/assets/images/avatar_image.png",
|
||||
videoUrl:
|
||||
"https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_10mb.mp4",
|
||||
videoDuration: "150",
|
||||
createdAt: "2024-02-01T16:20:00Z",
|
||||
updatedAt: "2024-02-01T16:20:00Z",
|
||||
category: {
|
||||
id: "4",
|
||||
name: "DevOps",
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
title: "آموزش Git و GitHub",
|
||||
description: "مدیریت نسخهها با Git و همکاری تیمی با GitHub.",
|
||||
coverUrl: "/src/assets/images/new_order.png",
|
||||
videoUrl:
|
||||
"https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4",
|
||||
videoDuration: "75",
|
||||
createdAt: "2024-02-05T11:10:00Z",
|
||||
updatedAt: "2024-02-05T11:10:00Z",
|
||||
category: {
|
||||
id: "5",
|
||||
name: "Tools",
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
title: "آموزش CSS و Tailwind CSS",
|
||||
description:
|
||||
"طراحی و استایلدهی صفحات وب با CSS و فریمورک Tailwind CSS.",
|
||||
coverUrl: "/src/assets/images/support1.png",
|
||||
videoUrl:
|
||||
"https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_2mb.mp4",
|
||||
videoDuration: "105",
|
||||
createdAt: "2024-02-10T13:30:00Z",
|
||||
updatedAt: "2024-02-10T13:30:00Z",
|
||||
category: {
|
||||
id: "6",
|
||||
name: "Styling",
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const staticLearningDetailData = {
|
||||
data: {
|
||||
learning: {
|
||||
id: "1",
|
||||
title: "آموزش کامل React و TypeScript",
|
||||
description:
|
||||
"در این دوره شما با مفاهیم پایه و پیشرفته React و TypeScript آشنا خواهید شد و پروژههای عملی انجام خواهید داد. این دوره شامل موارد زیر است:\n\n• نصب و راهاندازی محیط توسعه\n• آشنایی با JSX و Components\n• State و Props در React\n• Hooks و Lifecycle Methods\n• TypeScript و Type Safety\n• Routing و Navigation\n• State Management\n• Testing و Debugging\n• Deployment و Production",
|
||||
coverUrl: "/src/assets/images/new_order.png",
|
||||
videoUrl:
|
||||
"https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4",
|
||||
videoDuration: "120",
|
||||
createdAt: "2024-01-15T10:30:00Z",
|
||||
updatedAt: "2024-01-15T10:30:00Z",
|
||||
category: {
|
||||
id: "1",
|
||||
name: "Frontend",
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
// import * as api from "../service/LearningService";
|
||||
import {
|
||||
staticLearningsData,
|
||||
staticLearningDetailData,
|
||||
} from "../data/staticData";
|
||||
|
||||
export const useGetLearnings = (search: string, limit?: number) => {
|
||||
return useQuery({
|
||||
queryKey: ["learnings", search, limit],
|
||||
// queryFn: () => api.getLearnings(search, limit),
|
||||
queryFn: () => {
|
||||
// شبیهسازی delay سرور
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
let filteredData = staticLearningsData;
|
||||
|
||||
// فیلتر کردن بر اساس جستجو
|
||||
if (search) {
|
||||
filteredData = {
|
||||
data: {
|
||||
learnings: staticLearningsData.data.learnings.filter(
|
||||
(item) =>
|
||||
item.title.toLowerCase().includes(search.toLowerCase()) ||
|
||||
item.description
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase()) ||
|
||||
item.category.name
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase())
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// محدود کردن تعداد نتایج
|
||||
if (limit) {
|
||||
filteredData = {
|
||||
data: {
|
||||
learnings: filteredData.data.learnings.slice(0, limit),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
resolve(filteredData);
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetLearningDetail = (id: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["learning", id],
|
||||
// queryFn: () => api.getLearningDetail(id),
|
||||
queryFn: () => {
|
||||
// شبیهسازی delay سرور
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
// پیدا کردن آموزش بر اساس ID
|
||||
const learning = staticLearningsData.data.learnings.find(
|
||||
(item) => item.id === id
|
||||
);
|
||||
if (learning) {
|
||||
resolve({
|
||||
data: {
|
||||
learning: learning,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
resolve(staticLearningDetailData);
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
},
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
// import axios from "../../../config/axios";
|
||||
|
||||
// Service calls کامنت شده - در حال حاضر از دیتای استاتیک استفاده میشود
|
||||
// export const getLearnings = async (search: string, limit?: number) => {
|
||||
// const { data } = await axios.get(
|
||||
// `/learnings?q=${search}&limit=${limit ? limit : 20}`
|
||||
// );
|
||||
// return data;
|
||||
// };
|
||||
|
||||
// export const getLearningDetail = async (id: string) => {
|
||||
// const { data } = await axios.get(`/learnings/${id}`);
|
||||
// return data;
|
||||
// };
|
||||
@@ -0,0 +1,16 @@
|
||||
export type LearningItemType = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
coverUrl: string;
|
||||
videoUrl: string;
|
||||
videoDuration: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
category: {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
};
|
||||
@@ -14,6 +14,8 @@ import TicketDetail from '@/pages/ticket/Detail'
|
||||
import AnnouncementList from '@/pages/annoncement/List'
|
||||
import AnnouncementDetail from '@/pages/annoncement/Detail'
|
||||
import AddCriticisms from '@/pages/criticisms/Add'
|
||||
import LearningList from '@/pages/learning/List'
|
||||
import LearningDetail from '@/pages/learning/Detail'
|
||||
|
||||
const MainRouter: FC = () => {
|
||||
return (
|
||||
@@ -37,6 +39,8 @@ const MainRouter: FC = () => {
|
||||
<Route path={Paths.announcement.list} element={<AnnouncementList />} />
|
||||
<Route path={`${Paths.announcement.detail}:id`} element={<AnnouncementDetail />} />
|
||||
<Route path={Paths.criticisms} element={<AddCriticisms />} />
|
||||
<Route path={Paths.learning.list} element={<LearningList />} />
|
||||
<Route path={`${Paths.learning.detail}:id`} element={<LearningDetail />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -96,7 +96,7 @@ const SideBar: FC = () => {
|
||||
icon={<Teacher size={iconSizeSideBar} color='#4F5260' />}
|
||||
title={t('sidebar.learning')}
|
||||
isActive={isActive('/learning')}
|
||||
link={Paths.learning}
|
||||
link={Paths.learning.list}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user