Compare commits
No commits in common. "b873ac0bba800e8764b4195fb8127bbe3dc161ca" and "f5e0bebeb026bb43ae9639b74927f9a2664af2a8" have entirely different histories.
b873ac0bba
...
f5e0bebeb0
File diff suppressed because one or more lines are too long
14
index.html
14
index.html
|
|
@ -5,8 +5,18 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/App/Logo.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/App/Logo.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/App/Logo.png" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/App/Logo.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/App/Logo.png"
|
||||
/>
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<meta
|
||||
name="description"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"name": "my-app",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.3.7",
|
||||
|
|
|
|||
7713
pnpm-lock.yaml
7713
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
BIN
public/App/BackgroundHeader.webp
Normal file
BIN
public/App/BackgroundHeader.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
public/Icon/add_students.png
Normal file
BIN
public/Icon/add_students.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
17
src/App.tsx
17
src/App.tsx
|
|
@ -17,7 +17,7 @@ const App = () => {
|
|||
const { changeLanguage } = useChangeLanguage();
|
||||
const navigate = useNavigate();
|
||||
const { isAuthenticated } = useAuthState();
|
||||
const [t] = useTranslation();
|
||||
const [t] = useTranslation()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
|
|
@ -26,14 +26,7 @@ const App = () => {
|
|||
}, [isAuthenticated, navigate]);
|
||||
|
||||
const renderRouteElement = (route: any) => (
|
||||
<Suspense
|
||||
fallback={
|
||||
<Layout>
|
||||
{" "}
|
||||
<SpinContainer />{" "}
|
||||
</Layout>
|
||||
}
|
||||
>
|
||||
<Suspense fallback={<Layout> <SpinContainer/> </Layout>}>
|
||||
{route.header ? (
|
||||
<Layout>{route.element}</Layout>
|
||||
) : (
|
||||
|
|
@ -42,11 +35,11 @@ const App = () => {
|
|||
</Suspense>
|
||||
);
|
||||
|
||||
const renderRoutesRecursively = (routes: TMenuItem[]) =>
|
||||
const renderRoutesRecursively = (routes:TMenuItem[]) =>
|
||||
routes.map((route: TMenuItem) => {
|
||||
const useAbility = hasAbility(route.abilities, route.abilities_value);
|
||||
const tableHeader = t(`${route?.header}`);
|
||||
// useSetPageTitle(tableHeader,route?.path);
|
||||
// useSetPage_title(tableHeader,route?.path);
|
||||
|
||||
if (useAbility) {
|
||||
return (
|
||||
|
|
@ -87,7 +80,7 @@ const App = () => {
|
|||
{CrudRoute.map((route) => {
|
||||
const useAbility = hasAbility(route.abilities, route.abilities_value);
|
||||
const tableHeader = t(`${route?.header}`);
|
||||
// useSetPageTitle(tableHeader,route?.path);
|
||||
// useSetPage_title(tableHeader,route?.path);
|
||||
|
||||
if (!useAbility) {
|
||||
return false;
|
||||
|
|
|
|||
58
src/Components/Chart/AreaChart.tsx
Normal file
58
src/Components/Chart/AreaChart.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import ReactApexChart from "react-apexcharts";
|
||||
|
||||
const AreaChart: React.FC = () => {
|
||||
const chartRef = useRef<any>(null); // Ref for the chart component
|
||||
const [options, setOptions] = useState<any>({
|
||||
chart: {
|
||||
type: "area",
|
||||
toolbar: {
|
||||
show: false, // Disable the toolbar
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
curve: "smooth",
|
||||
},
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
categories: [
|
||||
"2018-09-19T00:00:00.000Z",
|
||||
"2018-09-19T01:30:00.000Z",
|
||||
"2018-09-19T02:30:00.000Z",
|
||||
"2018-09-19T03:30:00.000Z",
|
||||
"2018-09-19T04:30:00.000Z",
|
||||
"2018-09-19T05:30:00.000Z",
|
||||
"2018-09-19T06:30:00.000Z",
|
||||
],
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: "dd/MM/yy HH:mm",
|
||||
},
|
||||
},
|
||||
});
|
||||
const [series, setSeries] = useState<any>([
|
||||
{ name: "series1", data: [31, 40, 28, 51, 42, 109, 100], color: "#FCC43E" },
|
||||
{ name: "series2", data: [11, 32, 45, 32, 34, 52, 41], color: "#FB7D5B" },
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="AreaChart">
|
||||
<div id="chart">
|
||||
<ReactApexChart
|
||||
options={options}
|
||||
series={series}
|
||||
type="area"
|
||||
className="ReactApexChartArea"
|
||||
height={400}
|
||||
/>
|
||||
</div>
|
||||
<div id="html-dist"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AreaChart;
|
||||
95
src/Components/Chart/ColumnChart.tsx
Normal file
95
src/Components/Chart/ColumnChart.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { Radio, Select } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import ReactApexChart from "react-apexcharts";
|
||||
|
||||
import { IoMdArrowDropdown } from "react-icons/io";
|
||||
|
||||
const ColumnChart: React.FC = () => {
|
||||
const series = [
|
||||
{
|
||||
name: "غياب",
|
||||
data: [44, 55, 57, 56, 61, 58],
|
||||
color: "#FB7D5B",
|
||||
},
|
||||
{
|
||||
name: "حضور",
|
||||
data: [76, 85, 70, 98, 87, 80],
|
||||
color: "#FCC43E",
|
||||
},
|
||||
];
|
||||
|
||||
const options: any = {
|
||||
chart: {
|
||||
type: "bar",
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
columnWidth: "35%",
|
||||
endingShape: "rounded",
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
xaxis: {
|
||||
categories: ["Feb", "Mar", "Apr", "May", "Jun", "Jul"],
|
||||
},
|
||||
// yaxis: {
|
||||
// title: {
|
||||
// text: '$ (thousands)'
|
||||
// }
|
||||
// },
|
||||
fill: {
|
||||
opacity: 1,
|
||||
colors: ["#FB7D5B", "#FCC43E"], // Red and Green colors
|
||||
},
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: function (val: number) {
|
||||
return val + " طالب";
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: false, // Hide the legend
|
||||
},
|
||||
};
|
||||
const [value, setValue] = useState(1);
|
||||
|
||||
const handleChangeSelect = (value: string) => {};
|
||||
return (
|
||||
<div className="ColumnChart">
|
||||
<div className="ColumnChart_header">
|
||||
<span>الغياب المتكرر</span>
|
||||
|
||||
<span className="Legend">
|
||||
<div data-attr="red">غياب</div>
|
||||
<div>حضور</div>
|
||||
</span>
|
||||
|
||||
<Select
|
||||
defaultValue="هذا الأسبوع"
|
||||
onChange={handleChangeSelect}
|
||||
suffixIcon={<IoMdArrowDropdown size={30} />}
|
||||
options={[
|
||||
{ value: "هذا الأسبوع", label: "هذا الأسبوع" },
|
||||
{ value: "هذا الأسبوع", label: "هذا الأسبوع" },
|
||||
{ value: "هذا الأسبوع", label: "هذا الأسبوع" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className="ColumnChartWrapper" id="chart">
|
||||
<ReactApexChart
|
||||
options={options}
|
||||
series={series}
|
||||
type="bar"
|
||||
height={"100%"}
|
||||
/>
|
||||
</div>
|
||||
<div id="html-dist"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColumnChart;
|
||||
61
src/Components/Chart/LineChart.tsx
Normal file
61
src/Components/Chart/LineChart.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import React from "react";
|
||||
import Chart from "react-apexcharts";
|
||||
|
||||
const LineChart: React.FC = () => {
|
||||
const series = [
|
||||
{
|
||||
name: "Desktops",
|
||||
data: [10, 41, 35, 51, 49, 62, 69, 91, 148],
|
||||
},
|
||||
];
|
||||
|
||||
const options: any = {
|
||||
chart: {
|
||||
height: 350,
|
||||
type: "line",
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
curve: "straight",
|
||||
},
|
||||
title: {
|
||||
text: "Product Trends by Month",
|
||||
align: "left",
|
||||
},
|
||||
grid: {
|
||||
row: {
|
||||
colors: ["#f3f3f3", "transparent"], // takes an array which will be repeated on columns
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
categories: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div id="chart">
|
||||
<Chart options={options} series={series} type="line" height={350} />
|
||||
</div>
|
||||
<div id="html-dist"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LineChart;
|
||||
38
src/Components/Chart/PieChart.tsx
Normal file
38
src/Components/Chart/PieChart.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import React from "react";
|
||||
import Chart from "react-apexcharts";
|
||||
|
||||
const PieChart = () => {
|
||||
const series = [44, 55, 13] as any;
|
||||
const options: any = {
|
||||
series: [44, 55, 13],
|
||||
chart: {
|
||||
width: 380,
|
||||
type: "pie",
|
||||
},
|
||||
labels: ["Team A", "Team B", "Team C", "Team D", "Team E"],
|
||||
legend: {
|
||||
position: "bottom",
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
chart: {
|
||||
width: 200,
|
||||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<div id="chart">
|
||||
<Chart options={options} series={series} type="pie" width={380} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PieChart;
|
||||
14
src/Components/Classes/ClassesSegmented.tsx
Normal file
14
src/Components/Classes/ClassesSegmented.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import React from "react";
|
||||
import { Segmented } from "antd";
|
||||
|
||||
const ClassesSegmented: React.FC = () => (
|
||||
<Segmented<string>
|
||||
options={["الأحد", "اللإثنين", "الثلاثاء", "الأربعاء", "الخميس"]}
|
||||
onChange={(value) => {}}
|
||||
className="Segmented"
|
||||
block
|
||||
defaultChecked
|
||||
/>
|
||||
);
|
||||
|
||||
export default ClassesSegmented;
|
||||
105
src/Components/Course/CourseHeaderSection.tsx
Normal file
105
src/Components/Course/CourseHeaderSection.tsx
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaPlus, FaUser } from "react-icons/fa";
|
||||
import { IoMdArrowDropdown, IoMdArrowDropup } from "react-icons/io";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Popover, Spin } from "antd";
|
||||
import { CourseItem } from "../../types/Item";
|
||||
import { ModalEnum } from "../../enums/Model";
|
||||
import useModalHandler from "../../utils/useModalHandler";
|
||||
import { FaEllipsis } from "react-icons/fa6";
|
||||
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../../enums/abilities";
|
||||
import { hasAbility } from "../../utils/hasAbility";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const HeaderCardsSection = ({ data, isLoading }: any) => {
|
||||
const [showAll, setShowAll] = useState<boolean>(false);
|
||||
const [t] = useTranslation();
|
||||
|
||||
const toggleShowAll = () => {
|
||||
setShowAll(!showAll);
|
||||
};
|
||||
const navigate = useNavigate();
|
||||
|
||||
const index_eduClass_ability = hasAbility(
|
||||
ABILITIES_ENUM?.EDUCATION_CLASS,
|
||||
ABILITIES_VALUES_ENUM?.INDEX,
|
||||
);
|
||||
|
||||
const handelnavigate = (item: CourseItem) => {
|
||||
if (index_eduClass_ability) {
|
||||
navigate(
|
||||
`/${ABILITIES_ENUM?.COURSE}/${item?.id}/${ABILITIES_ENUM?.EDUCATION_CLASS}`,
|
||||
);
|
||||
// set_title(item?.name);
|
||||
}
|
||||
};
|
||||
|
||||
const { handel_open_model } = useModalHandler();
|
||||
const Courses = data?.data || [];
|
||||
|
||||
if (isLoading) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
const PopoverContent = (
|
||||
<div className="PopoverContent">
|
||||
<p onClick={() => handel_open_model(ModalEnum?.COURSE_ADD)}>
|
||||
{" "}
|
||||
{t("practical.add")} {t("models.course")} <FaPlus />{" "}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const store_course_ability = hasAbility(
|
||||
ABILITIES_ENUM?.COURSE,
|
||||
ABILITIES_VALUES_ENUM?.STORE,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="header_cards_section">
|
||||
<div className="CountCards">
|
||||
{Courses?.length > 0 &&
|
||||
Courses?.map((item: CourseItem, index: number) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => handelnavigate(item)}
|
||||
key={item?.id}
|
||||
className={`CountCard ${!showAll && index >= 6 ? "hidden" : ""}`}
|
||||
>
|
||||
<i>
|
||||
<FaUser />
|
||||
</i>
|
||||
<h4>{item?.name}</h4>
|
||||
<h5>
|
||||
{item?.student_count} {t("models.student")}{" "}
|
||||
</h5>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{Courses?.length > 4 &&
|
||||
(showAll ? (
|
||||
<span className="Show_More_Button" onClick={toggleShowAll}>
|
||||
<IoMdArrowDropup />
|
||||
</span>
|
||||
) : (
|
||||
<span className="Show_More_Button" onClick={toggleShowAll}>
|
||||
<IoMdArrowDropdown />
|
||||
</span>
|
||||
))}
|
||||
{store_course_ability && (
|
||||
<Popover
|
||||
content={PopoverContent}
|
||||
overlayClassName="Popover"
|
||||
arrow={false}
|
||||
placement="bottomRight"
|
||||
trigger={"click"}
|
||||
>
|
||||
<FaEllipsis />
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderCardsSection;
|
||||
67
src/Components/Division/DivisionsHeaderSection.tsx
Normal file
67
src/Components/Division/DivisionsHeaderSection.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import React, { useState } from "react";
|
||||
import { useModalState } from "../../zustand/Modal";
|
||||
import { ModalEnum } from "../../enums/Model";
|
||||
import { FaMoneyBill } from "react-icons/fa";
|
||||
import { IoMdArrowDropdown, IoMdArrowDropup } from "react-icons/io";
|
||||
|
||||
const DivisionsHeaderSection = () => {
|
||||
const [showAll, setShowAll] = useState<boolean>(false);
|
||||
|
||||
const toggleShowAll = () => {
|
||||
setShowAll(!showAll);
|
||||
};
|
||||
|
||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||
|
||||
const handelOpenModal = () => {
|
||||
setIsOpen(ModalEnum?.DIVISION_ADD);
|
||||
};
|
||||
|
||||
// Define the interface for your data
|
||||
interface Item {
|
||||
icon: JSX.Element;
|
||||
name: string;
|
||||
}
|
||||
const data: Item[] = [
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات" },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات" },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات" },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات" },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات" },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات" },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات" },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="Divisions_header_section">
|
||||
<div className="CountCards">
|
||||
{data?.map((item: Item, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`CountCard ${!showAll && index >= 5 ? "hidden" : ""}`}
|
||||
>
|
||||
<i>{item?.icon}</i>
|
||||
<h4>{item?.name}</h4>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{showAll ? (
|
||||
<span className="Show_More_Button" onClick={toggleShowAll}>
|
||||
<IoMdArrowDropup />
|
||||
</span>
|
||||
) : (
|
||||
<span className="Show_More_Button" onClick={toggleShowAll}>
|
||||
<IoMdArrowDropdown />
|
||||
</span>
|
||||
)}
|
||||
<button className="Add_button" onClick={handelOpenModal}>
|
||||
إضافة مادة
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DivisionsHeaderSection;
|
||||
128
src/Components/EduClass/EduClassHeaderSection.tsx
Normal file
128
src/Components/EduClass/EduClassHeaderSection.tsx
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import React, { useState } from "react";
|
||||
import { ModalEnum } from "../../enums/Model";
|
||||
import { FaPlus, FaUser } from "react-icons/fa";
|
||||
import { IoMdArrowDropdown, IoMdArrowDropup } from "react-icons/io";
|
||||
import { FaEllipsis } from "react-icons/fa6";
|
||||
import { Popover, Spin } from "antd";
|
||||
import { useGetAllEduClass } from "../../api/eduClass";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { EduClassItem } from "../../types/Item";
|
||||
import useModalHandler from "../../utils/useModalHandler";
|
||||
import { usePage_titleState } from "../../zustand/PageTitleState";
|
||||
import { hasAbility } from "../../utils/hasAbility";
|
||||
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../../enums/abilities";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const HeaderSection = () => {
|
||||
const [showAll, setShowAll] = useState<boolean>(false);
|
||||
|
||||
const toggleShowAll = () => {
|
||||
setShowAll(!showAll);
|
||||
};
|
||||
const { course_id } = useParams();
|
||||
const { data, isLoading } = useGetAllEduClass({ course_id: course_id });
|
||||
const eduClass = data?.data || [];
|
||||
const { set_title } = usePage_titleState((state) => state);
|
||||
const { handel_open_model } = useModalHandler();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const show_eduClass_ability = hasAbility(
|
||||
ABILITIES_ENUM?.EDUCATION_CLASS,
|
||||
ABILITIES_VALUES_ENUM?.SHOW,
|
||||
);
|
||||
|
||||
const handelNavigate = (item: EduClassItem) => {
|
||||
if (show_eduClass_ability) {
|
||||
navigate(`${item?.id}`);
|
||||
set_title(`${item?.name}`);
|
||||
}
|
||||
};
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
const store_eduClass_ability = hasAbility(
|
||||
ABILITIES_ENUM?.EDUCATION_CLASS,
|
||||
ABILITIES_VALUES_ENUM?.STORE,
|
||||
);
|
||||
const delete_course_ability = hasAbility(
|
||||
ABILITIES_ENUM?.COURSE,
|
||||
ABILITIES_VALUES_ENUM?.DELETE,
|
||||
);
|
||||
const store_note_ability = hasAbility(
|
||||
ABILITIES_ENUM?.NOTE,
|
||||
ABILITIES_VALUES_ENUM?.STORE,
|
||||
);
|
||||
|
||||
const PopoverContent = (
|
||||
<div className="PopoverContent">
|
||||
{store_eduClass_ability && (
|
||||
<p onClick={() => handel_open_model(ModalEnum?.EDUClASS_ADD)}>
|
||||
{" "}
|
||||
{t("practical.add")} {t("models.education_class")} <FaPlus />{" "}
|
||||
</p>
|
||||
)}
|
||||
{store_note_ability && (
|
||||
<p
|
||||
onClick={() => handel_open_model(ModalEnum?.COURSE_SENDNOTIFICATION)}
|
||||
>
|
||||
{" "}
|
||||
{t("practical.Send_Direct_Notification")}
|
||||
</p>
|
||||
)}
|
||||
{delete_course_ability && (
|
||||
<p onClick={() => handel_open_model(ModalEnum?.COURSE_DELETE)}>
|
||||
{" "}
|
||||
{t("practical.delete")} {t("models.course")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="header_cards_section">
|
||||
<div className="CountCards">
|
||||
{eduClass?.map((item: EduClassItem, index: number) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => handelNavigate(item)}
|
||||
key={index}
|
||||
className={`CountCard ${!showAll && index >= 6 ? "hidden" : ""}`}
|
||||
>
|
||||
<i>
|
||||
<FaUser />
|
||||
</i>
|
||||
<h4>{item?.name}</h4>
|
||||
<h5>
|
||||
{item?.students_count} {t("models.student")}{" "}
|
||||
</h5>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{eduClass?.length > 4 &&
|
||||
(showAll ? (
|
||||
<span className="Show_More_Button" onClick={toggleShowAll}>
|
||||
<IoMdArrowDropup />
|
||||
</span>
|
||||
) : (
|
||||
<span className="Show_More_Button" onClick={toggleShowAll}>
|
||||
<IoMdArrowDropdown />
|
||||
</span>
|
||||
))}
|
||||
<Popover
|
||||
content={PopoverContent}
|
||||
overlayClassName="Popover"
|
||||
arrow={false}
|
||||
placement="bottomRight"
|
||||
>
|
||||
<FaEllipsis />
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderSection;
|
||||
103
src/Components/EduClass/SingleEduClassHeaderSection.tsx
Normal file
103
src/Components/EduClass/SingleEduClassHeaderSection.tsx
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import React, { useState } from "react";
|
||||
import { ModalEnum } from "../../enums/Model";
|
||||
import { FaUser } from "react-icons/fa";
|
||||
import { IoMdArrowDropdown, IoMdArrowDropup } from "react-icons/io";
|
||||
import { FaEllipsis } from "react-icons/fa6";
|
||||
import { Popover } from "antd";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { Item } from "../../types/Item";
|
||||
import useModalHandler from "../../utils/useModalHandler";
|
||||
import { useButtonState } from "../../zustand/ButtonState";
|
||||
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ABILITIES_VALUES_ENUM } from "../../enums/abilities";
|
||||
// import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||
import { EduClassComponents } from "../../Pages/EduClass/show/ActiveTable";
|
||||
import { hasAbility } from "../../utils/hasAbility";
|
||||
|
||||
const HeaderSection = () => {
|
||||
const [showAll, setShowAll] = useState<boolean>(true);
|
||||
|
||||
const toggleShowAll = () => {
|
||||
setShowAll(!showAll);
|
||||
};
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
// useSetPage_title(
|
||||
// `${t(ABILITIES_ENUM?.MAIN_PAGE)} / ${t(`models.${ABILITIES_ENUM.COURSE}`)} / ${t(`models.${ABILITIES_ENUM.EDUCATION_CLASS}`)} / ${t(`input.details`)}`,
|
||||
// );
|
||||
|
||||
const { handel_open_model } = useModalHandler();
|
||||
|
||||
const filteredComponents = EduClassComponents?.filter((component) =>
|
||||
hasAbility(component.abilities, ABILITIES_VALUES_ENUM.INDEX),
|
||||
);
|
||||
|
||||
const PopoverContent = (
|
||||
<div className="PopoverContent">
|
||||
<p
|
||||
onClick={() => handel_open_model(ModalEnum?.EDUClASS_SENDNOTIFICATION)}
|
||||
>
|
||||
{" "}
|
||||
{t("practical.Send_Direct_Notification")}
|
||||
</p>
|
||||
<p onClick={() => handel_open_model(ModalEnum?.EDUClASS_DELETE)}>
|
||||
{" "}
|
||||
{t("practical.delete")} {t("models.education_class")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
const [, setActiveButton] = useState(0);
|
||||
const { setActiveTab, ActiveTab } = useButtonState((state) => state);
|
||||
const { setParamToSend } = useObjectToEdit();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleButtonClick = (index: number) => {
|
||||
setActiveButton(index);
|
||||
setActiveTab(index);
|
||||
setParamToSend({});
|
||||
navigate(`${location.pathname}`);
|
||||
};
|
||||
return (
|
||||
<div className="header_cards_section">
|
||||
<div className="CountCards">
|
||||
{filteredComponents?.map((item: Item, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => handleButtonClick(index)}
|
||||
className={`CountCard ${!showAll && index >= 6 ? "hidden" : ""} ${ActiveTab === index ? "active" : ""} `}
|
||||
>
|
||||
<i>
|
||||
<FaUser />
|
||||
</i>
|
||||
<h4> {t(`education_class_actions.${item?.name}`)} </h4>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{filteredComponents?.length > 4 &&
|
||||
(showAll ? (
|
||||
<span className="Show_More_Button" onClick={toggleShowAll}>
|
||||
<IoMdArrowDropup />
|
||||
</span>
|
||||
) : (
|
||||
<span className="Show_More_Button" onClick={toggleShowAll}>
|
||||
<IoMdArrowDropdown />
|
||||
</span>
|
||||
))}
|
||||
<Popover
|
||||
content={PopoverContent}
|
||||
overlayClassName="Popover"
|
||||
arrow={false}
|
||||
placement="bottomRight"
|
||||
>
|
||||
<FaEllipsis />
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderSection;
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { Col, Row } from "reactstrap";
|
||||
import React from "react";
|
||||
import ValidationField from "../../../../ValidationField/ValidationField";
|
||||
|
||||
const AttachmentsDetails = () => {
|
||||
return (
|
||||
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
|
||||
<Col xs="6" sm="6">
|
||||
<ValidationField
|
||||
name="attachment1"
|
||||
type="DropFile"
|
||||
label="school_document"
|
||||
/>
|
||||
{/* <ValidationField name='attachment2' type='DropFile' label='الوثيقة'/> */}
|
||||
</Col>
|
||||
<Col xs="6" sm="6"></Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default AttachmentsDetails;
|
||||
36
src/Components/EduClass/student/Add/Form/ContactDetails.tsx
Normal file
36
src/Components/EduClass/student/Add/Form/ContactDetails.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { Col, Row } from "reactstrap";
|
||||
import React from "react";
|
||||
import ValidationField from "../../../../ValidationField/ValidationField";
|
||||
|
||||
const ContactDetails = () => {
|
||||
return (
|
||||
<div className="bigRow">
|
||||
<ValidationField
|
||||
type="number"
|
||||
name="phone_number_1"
|
||||
label="phone_number"
|
||||
/>
|
||||
|
||||
<ValidationField
|
||||
type="number"
|
||||
name="phone_number_2"
|
||||
label="additional_phone_number"
|
||||
/>
|
||||
|
||||
<ValidationField
|
||||
type="TextArea"
|
||||
name="address"
|
||||
label="address"
|
||||
placeholder="address"
|
||||
/>
|
||||
<ValidationField
|
||||
type="TextArea"
|
||||
name="notes"
|
||||
label="note"
|
||||
placeholder="note"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactDetails;
|
||||
34
src/Components/EduClass/student/Add/Form/ParentDetails.tsx
Normal file
34
src/Components/EduClass/student/Add/Form/ParentDetails.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { Col, Row } from "reactstrap";
|
||||
import React, { useRef } from "react";
|
||||
import ValidationField from "../../../../ValidationField/ValidationField";
|
||||
|
||||
const ParentDetails = () => {
|
||||
const handelClick = () => {};
|
||||
return (
|
||||
<div className="bigRow">
|
||||
<ValidationField name="father_name" label="father_name" />
|
||||
|
||||
<ValidationField name="father_job" label="father_job" />
|
||||
|
||||
<ValidationField
|
||||
type="number"
|
||||
name="father_phone_number"
|
||||
label="father_phone_number"
|
||||
/>
|
||||
<ValidationField name="mother_name" label="mother_name" />
|
||||
|
||||
<ValidationField name="mother_job" label="mother_job" />
|
||||
|
||||
<ValidationField
|
||||
type="number"
|
||||
name="mother_phone_number"
|
||||
label="mother_phone_number"
|
||||
/>
|
||||
{/* <Col xs="8" sm="8">
|
||||
|
||||
</Col> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParentDetails;
|
||||
72
src/Components/EduClass/student/Add/Form/PersonalDetails.tsx
Normal file
72
src/Components/EduClass/student/Add/Form/PersonalDetails.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { Col, Row } from "reactstrap";
|
||||
import React from "react";
|
||||
import ValidationField from "../../../../ValidationField/ValidationField";
|
||||
import {
|
||||
Sex_Select_options_Student,
|
||||
nationalities,
|
||||
religion_Select_options,
|
||||
} from "../../../../../types/App";
|
||||
|
||||
const PersonalDetails = () => {
|
||||
return (
|
||||
<Row>
|
||||
<Col xs="8" sm="8">
|
||||
<ValidationField name="email" label="email" />
|
||||
<div className="TowValidationItems">
|
||||
<ValidationField name="first_name" label="first_name" />
|
||||
<ValidationField name="last_name" label="last_name" />
|
||||
</div>
|
||||
|
||||
<div className="TowValidationItems">
|
||||
<ValidationField name="username" label="username" />
|
||||
|
||||
<ValidationField name="password" label="password" />
|
||||
</div>
|
||||
|
||||
<div className="TowValidationItems">
|
||||
<ValidationField
|
||||
name="nationality"
|
||||
type="LocalSearch"
|
||||
option={nationalities}
|
||||
label="nationality"
|
||||
placeholder="nationality"
|
||||
/>
|
||||
<ValidationField
|
||||
name="religion"
|
||||
type="Select"
|
||||
option={religion_Select_options}
|
||||
no_label
|
||||
placeholder="religion"
|
||||
/>
|
||||
</div>
|
||||
<div className="TowValidationItems">
|
||||
<ValidationField
|
||||
name="birthday"
|
||||
type="Date"
|
||||
label="birthday"
|
||||
placeholder="birthday"
|
||||
/>
|
||||
<ValidationField
|
||||
name="birth_place"
|
||||
no_label
|
||||
placeholder="birth_place"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ValidationField
|
||||
name="sex"
|
||||
label="sex"
|
||||
type="Select"
|
||||
option={Sex_Select_options_Student}
|
||||
placeholder="sex"
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col xs="4" sm="4">
|
||||
<ValidationField name="image" type="DropFile" label="image" />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default PersonalDetails;
|
||||
46
src/Components/EduClass/student/Add/Form/Successful.tsx
Normal file
46
src/Components/EduClass/student/Add/Form/Successful.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import React from "react";
|
||||
import Image from "../../../../Ui/Image";
|
||||
import { useModalTabsState } from "../../../../../zustand/ModalTabsState";
|
||||
import { useModalState } from "../../../../../zustand/Modal";
|
||||
import { useFormikContext } from "formik";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const Successful = () => {
|
||||
const { setActiveTab } = useModalTabsState((state) => state);
|
||||
const { setIsOpen } = useModalState((state) => state);
|
||||
const formik = useFormikContext<any>();
|
||||
const [t] = useTranslation();
|
||||
return (
|
||||
<div className="AddedSuccessfully">
|
||||
<Image src="/DataState/successfully.png" />
|
||||
<h1> {t("header.Student_added_successfully")} </h1>
|
||||
<p>
|
||||
{t(
|
||||
"header.the_student_has_been_added_Do_you_want_to_add_another_student",
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className="TowButton">
|
||||
<button
|
||||
onClick={() => {
|
||||
setActiveTab(0);
|
||||
formik?.resetForm();
|
||||
}}
|
||||
>
|
||||
{t("header.Add_a_new_student")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsOpen("");
|
||||
setActiveTab(0);
|
||||
formik?.resetForm();
|
||||
}}
|
||||
>
|
||||
{t("practical.skip")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Successful;
|
||||
80
src/Components/EduClass/student/Add/Model/Modal.tsx
Normal file
80
src/Components/EduClass/student/Add/Model/Modal.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { Modal } from "antd";
|
||||
import { useModalState } from "../../../../../zustand/Modal";
|
||||
import FormikForm from "../../../../../Layout/Dashboard/FormikForm";
|
||||
import ModelBody from "./ModelBody";
|
||||
import TabsSubmite from "./TabsSubmite";
|
||||
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||
import { ModalEnum } from "../../../../../enums/Model";
|
||||
import { useAddStudent } from "../../../../../api/student";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useModalTabsState } from "../../../../../zustand/ModalTabsState";
|
||||
|
||||
const ModalForm: React.FC = () => {
|
||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||
const { mutate, isSuccess, isLoading } = useAddStudent();
|
||||
const { edu_class_id } = useParams();
|
||||
const handleSubmit = (values: any) => {
|
||||
const Data_To_Send = { ...values };
|
||||
delete Data_To_Send["id"];
|
||||
|
||||
Data_To_Send.attachments = [];
|
||||
if (Data_To_Send.attachment1 !== null) {
|
||||
Data_To_Send.attachments.push({
|
||||
name: "attachment1",
|
||||
file: Data_To_Send.attachment1,
|
||||
});
|
||||
}
|
||||
|
||||
if (Data_To_Send.attachment2 !== null) {
|
||||
Data_To_Send.attachments.push({
|
||||
name: "attachment2",
|
||||
file: Data_To_Send.attachment2,
|
||||
});
|
||||
}
|
||||
|
||||
Data_To_Send["edu_class_id"] = edu_class_id;
|
||||
Data_To_Send["birthday"] = values.birthday?.format(
|
||||
"YYYY-MM-DD HH:mm:ss.SSS",
|
||||
);
|
||||
|
||||
if (!values?.attachment1) {
|
||||
delete Data_To_Send["attachments"];
|
||||
}
|
||||
// console.log(Data_To_Send);
|
||||
mutate(Data_To_Send);
|
||||
};
|
||||
const { setActiveTab } = useModalTabsState((state) => state);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
setActiveTab(4);
|
||||
getInitialValues([]);
|
||||
}
|
||||
}, [isSuccess, setActiveTab]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
className="AddModalForm"
|
||||
centered
|
||||
width={"80vw"}
|
||||
footer={null}
|
||||
open={isOpen === ModalEnum.STUDENT_ADD}
|
||||
onOk={() => setIsOpen("")}
|
||||
onCancel={() => setIsOpen("")}
|
||||
>
|
||||
<FormikForm
|
||||
handleSubmit={handleSubmit}
|
||||
initialValues={getInitialValues([])}
|
||||
validationSchema={getValidationSchema}
|
||||
>
|
||||
<ModelBody />
|
||||
<TabsSubmite isLoading={isLoading} steps={4} />
|
||||
</FormikForm>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalForm;
|
||||
67
src/Components/EduClass/student/Add/Model/ModelBody.tsx
Normal file
67
src/Components/EduClass/student/Add/Model/ModelBody.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import PersonalDetails from "../Form/PersonalDetails";
|
||||
import TabsBar from "../../../../Layout/Tabs/TabsBar";
|
||||
import ActiveTabs from "../../../../Layout/Tabs/ActiveTabs";
|
||||
import { usePage_titleState } from "../../../../../zustand/PageTitleState";
|
||||
import ParentDetails from "../Form/ParentDetails";
|
||||
import ContactDetails from "../Form/ContactDetails";
|
||||
import AttachmentsDetails from "../Form/AttachmentsDetails";
|
||||
import Successful from "../Form/Successful";
|
||||
import { useModalState } from "../../../../../zustand/Modal";
|
||||
import { useFormikContext } from "formik";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const ModelBody = () => {
|
||||
const { title } = usePage_titleState((state) => state);
|
||||
const [t] = useTranslation();
|
||||
const steps = [
|
||||
{
|
||||
title: `${t(`practical.Step`)} 1`,
|
||||
description: t("header.Student_Information"),
|
||||
component: <PersonalDetails />,
|
||||
},
|
||||
{
|
||||
title: `${t(`practical.Step`)} 2`,
|
||||
description: t("header.Parent_Information"),
|
||||
component: <ParentDetails />,
|
||||
},
|
||||
{
|
||||
title: `${t(`practical.Step`)} 3`,
|
||||
description: t("header.Contact_Information"),
|
||||
component: <ContactDetails />,
|
||||
},
|
||||
{
|
||||
title: `${t(`practical.Step`)} 4`,
|
||||
description: t("header.Attachment_Images"),
|
||||
component: <AttachmentsDetails />,
|
||||
},
|
||||
{
|
||||
title: "hidden",
|
||||
description: "hidden",
|
||||
hidden: true,
|
||||
component: <Successful />,
|
||||
},
|
||||
];
|
||||
|
||||
const { isOpen } = useModalState((state) => state);
|
||||
const formik = useFormikContext();
|
||||
useEffect(() => {
|
||||
formik.setErrors({});
|
||||
}, [isOpen === ""]);
|
||||
|
||||
return (
|
||||
<div className="ModelBody">
|
||||
<TabsBar steps={steps} />
|
||||
<div className="ModelBodyForm">
|
||||
<header>
|
||||
{" "}
|
||||
{t("practical.add")} {t("models.student")}{" "}
|
||||
{" " + title === null ? "" : title}{" "}
|
||||
</header>
|
||||
<ActiveTabs steps={steps} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelBody;
|
||||
128
src/Components/EduClass/student/Add/Model/TabsSubmite.tsx
Normal file
128
src/Components/EduClass/student/Add/Model/TabsSubmite.tsx
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { useModalTabsState } from "../../../../../zustand/ModalTabsState";
|
||||
import { useModalState } from "../../../../../zustand/Modal";
|
||||
import { useFormikContext } from "formik";
|
||||
import { toast } from "react-toastify";
|
||||
import { Spin } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface TabsSubmiteProps {
|
||||
steps: number;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const TabsSubmite: React.FC<TabsSubmiteProps> = ({ steps, isLoading }) => {
|
||||
const { values, isValid, submitForm } = useFormikContext<any>();
|
||||
|
||||
const { ActiveTab, setActiveTab } = useModalTabsState((state) => state);
|
||||
const IsSubmite = ActiveTab + 1 !== steps;
|
||||
const {
|
||||
first_name,
|
||||
last_name,
|
||||
username,
|
||||
password,
|
||||
nationality,
|
||||
religion,
|
||||
birthday,
|
||||
birth_place,
|
||||
sex,
|
||||
} = values;
|
||||
|
||||
const {
|
||||
father_name,
|
||||
father_job,
|
||||
father_phone_number,
|
||||
mother_name,
|
||||
mother_job,
|
||||
mother_phone_number,
|
||||
} = values;
|
||||
|
||||
const { address, attachment1 } = values;
|
||||
|
||||
const is_1_valued =
|
||||
first_name &&
|
||||
username &&
|
||||
last_name &&
|
||||
password &&
|
||||
nationality &&
|
||||
religion &&
|
||||
birthday &&
|
||||
birth_place &&
|
||||
sex;
|
||||
const is_2_valued =
|
||||
father_name &&
|
||||
father_job &&
|
||||
father_phone_number &&
|
||||
mother_name &&
|
||||
mother_job &&
|
||||
mother_phone_number;
|
||||
const is_3_valued = address;
|
||||
const is_4_valued = attachment1;
|
||||
|
||||
const [t] = useTranslation();
|
||||
function handelNext() {
|
||||
// console.log("submited");
|
||||
// console.log(isValid,"isValid");
|
||||
|
||||
if (!is_1_valued && Number(ActiveTab) === 0) {
|
||||
toast.error(t("validation.pleas_fill_all_label"));
|
||||
return;
|
||||
}
|
||||
if (!is_2_valued && Number(ActiveTab) === 1) {
|
||||
toast.error(t("validation.pleas_fill_all_label"));
|
||||
return;
|
||||
}
|
||||
if (!is_3_valued && Number(ActiveTab) === 2) {
|
||||
toast.error(t("validation.pleas_fill_all_label"));
|
||||
return;
|
||||
}
|
||||
if (!is_4_valued && Number(ActiveTab) === 3) {
|
||||
toast.error(t("validation.pleas_fill_all_label"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number(ActiveTab) >= steps) {
|
||||
return;
|
||||
}
|
||||
setActiveTab(Number(ActiveTab) + 1);
|
||||
}
|
||||
|
||||
function handelPre() {
|
||||
setActiveTab(Number(ActiveTab) - 1);
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (isValid) {
|
||||
submitForm();
|
||||
} else {
|
||||
submitForm();
|
||||
// console.log('Validation errors:', formik.errors);
|
||||
// setActiveTab(4)
|
||||
toast.error(t("validation.pleas_fill_all_label"));
|
||||
}
|
||||
};
|
||||
// console.log(ActiveTab,"ActiveTab");
|
||||
|
||||
if (ActiveTab === steps) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<div className="SubmitButton">
|
||||
<div onClick={handelPre}>
|
||||
{ActiveTab > 0 ? "رجوع للخطوة السابقة" : ""}
|
||||
</div>
|
||||
<span
|
||||
onClick={isLoading ? undefined : IsSubmite ? handelNext : handleSubmit}
|
||||
{...(!IsSubmite && !isLoading ? { type: "submit" } : {})}
|
||||
>
|
||||
{isLoading
|
||||
? "جار الاضافة ...."
|
||||
: IsSubmite
|
||||
? "الخطوة التالية"
|
||||
: "إضافة الطالب"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabsSubmite;
|
||||
76
src/Components/EduClass/student/Add/Model/formUtil.ts
Normal file
76
src/Components/EduClass/student/Add/Model/formUtil.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import * as Yup from "yup";
|
||||
|
||||
export const getInitialValues = (objectToEdit: any): any => {
|
||||
return {
|
||||
id: objectToEdit?.id ?? null,
|
||||
// username:objectToEdit?.username ?? null ,
|
||||
email: objectToEdit?.email ?? "",
|
||||
username: objectToEdit?.username ?? null,
|
||||
|
||||
password: objectToEdit?.password ?? null,
|
||||
notes: objectToEdit?.notes ?? null,
|
||||
image: objectToEdit?.image ?? null,
|
||||
nationality: objectToEdit?.nationality ?? null,
|
||||
birth_place: objectToEdit?.birth_place ?? null,
|
||||
birthday: objectToEdit?.birthday ?? null,
|
||||
phone_number_2: objectToEdit?.phone_number_2 ?? null,
|
||||
phone_number_1: objectToEdit?.phone_number_1 ?? null,
|
||||
mother_name: objectToEdit?.mother_name ?? null,
|
||||
father_name: objectToEdit?.father_name ?? null,
|
||||
last_name: objectToEdit?.last_name ?? null,
|
||||
first_name: objectToEdit?.first_name ?? null,
|
||||
religion: objectToEdit?.religion ?? null,
|
||||
attachments: objectToEdit?.attachments ?? null,
|
||||
attachment1: objectToEdit?.attachment1 ?? null,
|
||||
attachment2: objectToEdit?.attachment2 ?? null,
|
||||
address: objectToEdit?.address ?? null,
|
||||
|
||||
father_job: objectToEdit?.father_job ?? null,
|
||||
mother_phone_number: objectToEdit?.mother_phone_number ?? null,
|
||||
father_phone_number: objectToEdit?.father_phone_number ?? null,
|
||||
mother_job: objectToEdit?.mother_job ?? null,
|
||||
edu_class_id: objectToEdit?.edu_class_id ?? null,
|
||||
sex: objectToEdit?.sex ?? null,
|
||||
};
|
||||
};
|
||||
|
||||
export const getValidationSchema = () => {
|
||||
return Yup.object().shape({
|
||||
// username: Yup.string().required('Username is required'),
|
||||
email: Yup.string().email("validation.Invalid_email"),
|
||||
username: Yup.string().required("validation.required"),
|
||||
// .required("validation.Email_is_required"),
|
||||
password: Yup.string()
|
||||
.required("validation.Password_is_required")
|
||||
.min(8, "validation.Password_must_be_at_least_8_characters_long"),
|
||||
// notes: Yup.string(),
|
||||
// image: Yup.string().required(),
|
||||
nationality: Yup.string().required("validation.Nationality_is_required"),
|
||||
address: Yup.string().required("validation.Address_is_required"),
|
||||
|
||||
birth_place: Yup.string().required("validation.Place_of_birth_is_required"),
|
||||
birthday: Yup.string().required("validation.Date_of_birth_is_required"),
|
||||
// phone_number_2: Yup.number(),
|
||||
// phone_number_1: Yup.number(),
|
||||
mother_name: Yup.string().required("validation.Mother's_name_is_required"),
|
||||
father_name: Yup.string().required("validation.Father's_name_is_required"),
|
||||
last_name: Yup.string().required("validation.Last_name_is_required"),
|
||||
first_name: Yup.string().required("validation.First_name_is_required"),
|
||||
religion: Yup.string().required("validation.Religion_is_required"),
|
||||
sex: Yup.string().required("validation.Gender_is_required"),
|
||||
|
||||
// attachments: Yup.string(),
|
||||
// attachment1: Yup.string(),
|
||||
// attachment2: Yup.string(),
|
||||
// attachment1: Yup.string().required("validation.Attachment1_is_required"),
|
||||
|
||||
father_job: Yup.string().required("validation.Father's_job_is_required"),
|
||||
// mother_phone_number: Yup.string().required(
|
||||
// "validation.Mother's_phone_number_is_required",
|
||||
// ),
|
||||
father_phone_number: Yup.string().required(
|
||||
"validation.Father's_phone_number_is_required",
|
||||
),
|
||||
// mother_job: Yup.string().required("validation.Mother's_job_is_required"),
|
||||
});
|
||||
};
|
||||
48
src/Components/HOC/WithDrawer.tsx
Normal file
48
src/Components/HOC/WithDrawer.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import React, { useState, ReactNode } from "react";
|
||||
import type { DrawerProps } from "antd";
|
||||
import { Drawer, Space } from "antd";
|
||||
|
||||
interface WithDrawerProps {
|
||||
button: React.ReactNode;
|
||||
children: ReactNode;
|
||||
title: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const WithDrawer: React.FC<WithDrawerProps> = ({
|
||||
button,
|
||||
children,
|
||||
title = "Basic Drawer",
|
||||
className,
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [placement, setPlacement] = useState<DrawerProps["placement"]>("right");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space>
|
||||
{React.cloneElement(button as React.ReactElement, {
|
||||
onClick: () => setOpen(true),
|
||||
})}
|
||||
</Space>
|
||||
<Drawer
|
||||
title={title}
|
||||
placement={placement}
|
||||
closable={false}
|
||||
onClose={() => setOpen(false)}
|
||||
open={open}
|
||||
key={placement}
|
||||
>
|
||||
<div className={className}>{children}</div>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default WithDrawer;
|
||||
|
||||
// <WithDrawer
|
||||
// button={<Button type="primary">Open</Button>}
|
||||
// >
|
||||
// {/* Your content goes here */}
|
||||
// </WithDrawer>
|
||||
28
src/Components/HOC/WithFormik.tsx
Normal file
28
src/Components/HOC/WithFormik.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { Form, Formik } from "formik";
|
||||
import React from "react";
|
||||
import * as Yup from "yup";
|
||||
|
||||
const WithFormik = ({ children }: any) => {
|
||||
const getInitialValues = () => {
|
||||
return { name: "" };
|
||||
};
|
||||
const getValidationSchema = () => {
|
||||
return Yup.object().shape({});
|
||||
};
|
||||
const handleSubmit = () => {};
|
||||
return (
|
||||
<div className="WithFormik">
|
||||
{
|
||||
<Formik
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={getInitialValues}
|
||||
validationSchema={getValidationSchema}
|
||||
>
|
||||
{(formik) => <Form>{children}</Form>}
|
||||
</Formik>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WithFormik;
|
||||
32
src/Components/HOC/withVisibale.tsx
Normal file
32
src/Components/HOC/withVisibale.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
// This is your Layout Component
|
||||
const Visibale = ({ children, ...props }: any) => {
|
||||
const ref = useRef<any>();
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(([entry]) => {
|
||||
if (isVisible === false) {
|
||||
setIsVisible(entry.isIntersecting);
|
||||
}
|
||||
});
|
||||
if (ref.current) {
|
||||
observer.observe(ref.current);
|
||||
}
|
||||
return () => {
|
||||
if (ref.current) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
observer.unobserve(ref.current);
|
||||
}
|
||||
};
|
||||
}, [isVisible]); // Empty array ensures effect is only run on mount and unmount
|
||||
|
||||
return (
|
||||
<div ref={ref} {...props}>
|
||||
{isVisible && children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Visibale;
|
||||
59
src/Components/Home/ActivitySection.tsx
Normal file
59
src/Components/Home/ActivitySection.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import React from "react";
|
||||
import Image from "../Ui/Image";
|
||||
|
||||
const ActivitySection = () => {
|
||||
// Static fake data array
|
||||
const ActivitysData = [
|
||||
{
|
||||
id: 1,
|
||||
image: "/Image/1.png",
|
||||
info: "3 طالبات حصلوا على جائزة في مسابقة الشطرنج",
|
||||
time: "منذ 3 ساعات",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
image: "/Image/1.png",
|
||||
info: "3 طالبات حصلوا على جائزة في مسابقة الشطرنج",
|
||||
time: "منذ 3 ساعات",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
image: "/Image/1.png",
|
||||
info: "3 طالبات حصلوا على جائزة في مسابقة الشطرنج",
|
||||
time: "منذ 3 ساعات",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
image: "/Image/1.png",
|
||||
info: "3 طالبات حصلوا على جائزة في مسابقة الشطرنج",
|
||||
time: "منذ 3 ساعات",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
image: "/Image/1.png",
|
||||
info: "3 طالبات حصلوا على جائزة في مسابقة الشطرنج",
|
||||
time: "منذ 3 ساعات",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="ActivitySection">
|
||||
<header>
|
||||
<h4>النشاط الطلابي </h4>
|
||||
</header>
|
||||
<div className="ActivityScrollerChanger">
|
||||
<div className="Activitys">
|
||||
{ActivitysData.map((Activity) => (
|
||||
<article key={Activity.id}>
|
||||
<Image src={Activity.image} />
|
||||
<h5>{Activity.info}</h5>
|
||||
<p>{Activity?.time}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActivitySection;
|
||||
123
src/Components/Home/CalendarSection.tsx
Normal file
123
src/Components/Home/CalendarSection.tsx
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import React, { useState } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import type { Dayjs } from "dayjs";
|
||||
import dayLocaleData from "dayjs/plugin/localeData";
|
||||
import {
|
||||
Calendar,
|
||||
Col,
|
||||
DatePicker,
|
||||
Popover,
|
||||
Radio,
|
||||
Row,
|
||||
Select,
|
||||
Typography,
|
||||
theme,
|
||||
} from "antd";
|
||||
import type { CalendarProps } from "antd";
|
||||
import { IoMdArrowDropdown } from "react-icons/io";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { RiDeleteBin6Fill } from "react-icons/ri";
|
||||
|
||||
dayjs.extend(dayLocaleData);
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [year, setYear] = useState<number>(dayjs().year());
|
||||
const [month, setMonth] = useState<number>(dayjs().month());
|
||||
const onChangeDatePicker = (value: any) => {
|
||||
if (value) {
|
||||
setYear(value.$y); // Extract year from the selected date and update the year state
|
||||
setMonth(value.$M); // Extract month from the selected date and update the month state
|
||||
} else {
|
||||
const today = dayjs(); // Get the current date
|
||||
setYear(today.year()); // Update the year state to the current year
|
||||
setMonth(today.month());
|
||||
}
|
||||
};
|
||||
const popoverDays = [
|
||||
{
|
||||
day: 10,
|
||||
month: 3,
|
||||
year: 2024,
|
||||
title: "مسابقة الشطرنج الدولية",
|
||||
time: "الساعة 11",
|
||||
},
|
||||
{
|
||||
day: 22,
|
||||
month: 3,
|
||||
year: 2024,
|
||||
title: "مسابقة الشطرنج الدولية",
|
||||
time: "الساعة 12",
|
||||
},
|
||||
];
|
||||
|
||||
const [actionsVisible, setActionsVisible] = useState(0);
|
||||
|
||||
const toggleActionsVisibility = (day: number) => {
|
||||
setActionsVisible(day);
|
||||
};
|
||||
const dateCellRender = (value: dayjs.Dayjs) => {
|
||||
const day = value.date();
|
||||
const month = value.month() + 1;
|
||||
const year = value.year();
|
||||
|
||||
const matchingDay = popoverDays.find(
|
||||
(date) => date.day === day && date.month === month && date.year === year,
|
||||
);
|
||||
|
||||
if (matchingDay) {
|
||||
return (
|
||||
<Popover
|
||||
placement="top"
|
||||
className="CalenderPop"
|
||||
rootClassName="CalenderPop"
|
||||
trigger="hover"
|
||||
content={
|
||||
<div
|
||||
className="Calendar_Popover"
|
||||
onClick={() => toggleActionsVisibility(day)}
|
||||
>
|
||||
<h5>{matchingDay.title}</h5>
|
||||
<h6>{matchingDay.time}</h6>
|
||||
{actionsVisible === day && ( // Only show actions if actionsVisible is true
|
||||
<span className="Actions">
|
||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
||||
<RiDeleteBin6Fill size={22} style={{ color: "#C11313" }} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
// open={true}
|
||||
>
|
||||
<div className="Calendar_ActiveDiv">{day}</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="Calendar_Div">{day}</div>;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={"CalendarSection"}>
|
||||
<Calendar
|
||||
fullscreen={false}
|
||||
fullCellRender={dateCellRender}
|
||||
value={dayjs(`${year}-${month + 1}-01`)}
|
||||
headerRender={() => {
|
||||
return (
|
||||
<div className="CalendarHeader">
|
||||
<h4>تقويم المدرسة</h4>
|
||||
<DatePicker
|
||||
suffixIcon={<IoMdArrowDropdown size={30} />}
|
||||
onChange={(newDate: number) => onChangeDatePicker(newDate)}
|
||||
picker="month"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
31
src/Components/Home/CountSection.tsx
Normal file
31
src/Components/Home/CountSection.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import React from "react";
|
||||
import { FaMoneyBill } from "react-icons/fa";
|
||||
import Image from "../Ui/Image";
|
||||
|
||||
const CountSection = () => {
|
||||
const data = [
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات", number: 900 },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات", number: 900 },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات", number: 900 },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات", number: 900 },
|
||||
{ icon: <FaMoneyBill />, name: "الطالبات", number: 900 },
|
||||
];
|
||||
return (
|
||||
<div className="CountSection">
|
||||
<div className="CountCards">
|
||||
{data?.map((item: any, index: any) => {
|
||||
return (
|
||||
<div key={index} className="CountCard">
|
||||
<i>{item?.icon}</i>
|
||||
<h4>{item?.name}</h4>
|
||||
<h6>{item?.number}</h6>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Image src="../Home/HomeCounter.png" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CountSection;
|
||||
82
src/Components/Home/NoteSection.tsx
Normal file
82
src/Components/Home/NoteSection.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import React from "react";
|
||||
import { FaMoneyBill } from "react-icons/fa";
|
||||
|
||||
const NoteSection = () => {
|
||||
// Static fake data array
|
||||
const notesData = [
|
||||
{
|
||||
id: 1,
|
||||
icon: <FaMoneyBill />,
|
||||
title: "ملاحظة مالية",
|
||||
content: [
|
||||
"الرسوم الدراسية: المبلغ المطلوب من الأهل 200000 ل.س",
|
||||
"مبلغ أخر: المبلغ المطلوب من الأهل 300000 ل.س",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: <FaMoneyBill />,
|
||||
title: "ملاحظة مالية",
|
||||
content: [
|
||||
"الرسوم الدراسية: المبلغ المطلوب من الأهل 200000 ل.س",
|
||||
"تكلفة أخرى: المبلغ المطلوب من الأهل 150000 ل.س",
|
||||
"تكلفة أخرى: المبلغ المطلوب من الأهل 150000 ل.س",
|
||||
"تكلفة أخرى: المبلغ المطلوب من الأهل 150000 ل.س",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: <FaMoneyBill />,
|
||||
title: "ملاحظة مالية",
|
||||
content: [
|
||||
"الرسوم الدراسية: المبلغ المطلوب من الأهل 200000 ل.س",
|
||||
"تكلفة أخرى: المبلغ المطلوب من الأهل 150000 ل.س",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
icon: <FaMoneyBill />,
|
||||
title: "ملاحظة مالية",
|
||||
content: [
|
||||
"الرسوم الدراسية: المبلغ المطلوب من الأهل 200000 ل.س",
|
||||
"تكلفة أخرى: المبلغ المطلوب من الأهل 150000 ل.س",
|
||||
"تكلفة أخرى: المبلغ المطلوب من الأهل 150000 ل.س",
|
||||
"تكلفة أخرى: المبلغ المطلوب من الأهل 150000 ل.س",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
icon: <FaMoneyBill />,
|
||||
title: "ملاحظة مالية",
|
||||
content: [
|
||||
"الرسوم الدراسية: المبلغ المطلوب من الأهل 200000 ل.س",
|
||||
"تكلفة أخرى: المبلغ المطلوب من الأهل 150000 ل.س",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="NoteSection">
|
||||
<header>
|
||||
<h4>ملاحظات اليوم</h4> <h6>8/3/2021</h6>
|
||||
</header>
|
||||
<div className="NoteScrollerChanger">
|
||||
<div className="Notes">
|
||||
{notesData.map((note) => (
|
||||
<article key={note.id}>
|
||||
<div>
|
||||
<i>{note.icon}</i>
|
||||
<h5>{note.title}</h5>
|
||||
</div>
|
||||
{note.content.map((item, index) => (
|
||||
<span key={index}>{item}</span>
|
||||
))}
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoteSection;
|
||||
72
src/Components/Home/StudentSubjects.tsx
Normal file
72
src/Components/Home/StudentSubjects.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { Progress, Select } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaLaptop } from "react-icons/fa";
|
||||
import { IoMdArrowDropdown } from "react-icons/io";
|
||||
import Image from "../Ui/Image";
|
||||
|
||||
const StudentSubjects = () => {
|
||||
const [selectedOption, setSelectedOption] = useState("هذا الأسبوع");
|
||||
const Options = [
|
||||
{ value: "هذا الأسبوع", label: "هذا الأسبوع" },
|
||||
{ value: "الأسبوع الماضي", label: "الأسبوع الماضي" },
|
||||
{ value: "الشهر الماضي", label: "الشهر الماضي" },
|
||||
];
|
||||
|
||||
const handleChangeSelect = (value: any) => {
|
||||
setSelectedOption(value);
|
||||
// console.log(`Selected ${value}`);
|
||||
};
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
// Define static data
|
||||
const staticData = [
|
||||
{
|
||||
SubjectImage: "/Subject/algebra.png",
|
||||
SubjectName: "طالبات",
|
||||
StudentSubjectsPercent: 50,
|
||||
},
|
||||
{
|
||||
SubjectImage: "/Subject/flask.png",
|
||||
SubjectName: "طالبات",
|
||||
StudentSubjectsPercent: 60,
|
||||
},
|
||||
{
|
||||
SubjectImage: "/Subject/koran.png",
|
||||
SubjectName: "طالبات",
|
||||
StudentSubjectsPercent: 70,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="StudentSubjects">
|
||||
<header>
|
||||
<h6>{t("عدد الطالبات")}</h6>
|
||||
<Select
|
||||
defaultValue={selectedOption}
|
||||
onChange={handleChangeSelect}
|
||||
suffixIcon={<IoMdArrowDropdown size={30} />}
|
||||
options={Options}
|
||||
/>
|
||||
</header>
|
||||
<main>
|
||||
{staticData.map((item, index) => (
|
||||
<div key={index}>
|
||||
<Progress
|
||||
type="circle"
|
||||
size={"small"}
|
||||
strokeColor={"#0052B4"}
|
||||
strokeWidth={7}
|
||||
percent={item?.StudentSubjectsPercent}
|
||||
/>
|
||||
<h6>{item.SubjectName}</h6>
|
||||
<Image src={item?.SubjectImage} />
|
||||
</div>
|
||||
))}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StudentSubjects;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { Spin } from "antd";
|
||||
import { Spin } from 'antd'
|
||||
|
||||
const SpinContainer = () => {
|
||||
return (
|
||||
<div className="SpinContainer">
|
||||
<Spin />
|
||||
<div className='SpinContainer'>
|
||||
<Spin/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default SpinContainer;
|
||||
export default SpinContainer
|
||||
46
src/Components/Student/Add/Model/Form.tsx
Normal file
46
src/Components/Student/Add/Model/Form.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { Col, Row } from "reactstrap";
|
||||
import React from "react";
|
||||
import ValidationField from "../../../../Components/ValidationField/ValidationField";
|
||||
|
||||
const Form = () => {
|
||||
return (
|
||||
<Row>
|
||||
<Col xs="8" sm="8">
|
||||
{" "}
|
||||
{/* This column will take up 8 units on extra small screens and 6 units on small screens */}
|
||||
<ValidationField name="الجنس" />
|
||||
<ValidationField name="الاسم الأول *" />
|
||||
<ValidationField name="اسم العائلة" />
|
||||
<div className="TowValidationItems">
|
||||
<ValidationField name="اسم العائلة" />
|
||||
<ValidationField name="اسم العائلة" no_label />
|
||||
</div>
|
||||
<div className="TowValidationItems">
|
||||
<ValidationField name="اسم العائلة" type="Select" option={[]} />
|
||||
<ValidationField name="اسم العائلة" />
|
||||
</div>
|
||||
{/* <div className='TowValidationItems'>
|
||||
<ValidationField name='اسم العائلة'/>
|
||||
<ValidationField name='اسم العائلة' no_label/>
|
||||
</div>
|
||||
<div className='TowValidationItems'>
|
||||
<ValidationField name='اسم العائلة'/>
|
||||
<ValidationField name='اسم العائلة' no_label/>
|
||||
</div> */}
|
||||
</Col>
|
||||
|
||||
<Col xs="4" sm="4">
|
||||
{" "}
|
||||
{/* This column will take up 4 units on extra small screens and 6 units on small screens */}
|
||||
<ValidationField name="DropFile" type="DropFile" label="الصورة *" />
|
||||
<ValidationField
|
||||
name="DropFile"
|
||||
type="DropFile"
|
||||
label="الوثيقة المدرسية *"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default Form;
|
||||
38
src/Components/Student/Add/Model/Modal.tsx
Normal file
38
src/Components/Student/Add/Model/Modal.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import React, { useState } from "react";
|
||||
import { Modal } from "antd";
|
||||
import { useModalState } from "../../../../zustand/Modal";
|
||||
import FormikForm from "../../../../Layout/Dashboard/FormikForm";
|
||||
import * as Yup from "yup";
|
||||
import ModelBody from "./ModelBody";
|
||||
import TabsSubmite from "../../../../Components/Layout/Tabs/TabsSubmite";
|
||||
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||
import { ModalEnum } from "../../../../enums/Model";
|
||||
|
||||
const ModalForm: React.FC = () => {
|
||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
className="AddModalForm"
|
||||
centered
|
||||
width={"80vw"}
|
||||
footer={null}
|
||||
open={isOpen === ModalEnum.STUDENT_ADD}
|
||||
onOk={() => setIsOpen("")}
|
||||
onCancel={() => setIsOpen("")}
|
||||
>
|
||||
<FormikForm
|
||||
handleSubmit={() => {}}
|
||||
initialValues={getInitialValues}
|
||||
validationSchema={getValidationSchema}
|
||||
>
|
||||
<ModelBody />
|
||||
{/* <TabsSubmite steps={5} /> */}
|
||||
</FormikForm>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalForm;
|
||||
60
src/Components/Student/Add/Model/ModelBody.tsx
Normal file
60
src/Components/Student/Add/Model/ModelBody.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import { Divider, Steps } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTabsState } from "../../../../zustand/TabsState";
|
||||
import { useModalTabsState } from "../../../../zustand/ModalTabsState";
|
||||
import Form from "./Form";
|
||||
import TabsBar from "../../../../Components/Layout/Tabs/TabsBar";
|
||||
import ActiveTabs from "../../../../Components/Layout/Tabs/ActiveTabs";
|
||||
|
||||
const ModelBody = () => {
|
||||
const { ActiveTab, setActiveTab } = useModalTabsState((state) => state);
|
||||
function handelTabClick(index: number) {
|
||||
setActiveTab(index);
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: "الخطوة 1",
|
||||
description: "التفاصيل الشخصية",
|
||||
component: <Form />,
|
||||
},
|
||||
{
|
||||
title: "الخطوة 2",
|
||||
description: "التفاصيل الشخصية",
|
||||
component: <>H2</>,
|
||||
},
|
||||
{
|
||||
title: "الخطوة 3",
|
||||
description: "التفاصيل الشخصية",
|
||||
component: <>H2</>,
|
||||
},
|
||||
{
|
||||
title: "الخطوة 4",
|
||||
description: "التفاصيل الشخصية",
|
||||
component: <>H2</>,
|
||||
},
|
||||
{
|
||||
title: "الخطوة 5",
|
||||
description: "التفاصيل الشخصية",
|
||||
component: <>H2</>,
|
||||
},
|
||||
{
|
||||
title: "الخطوة 5",
|
||||
description: "التفاصيل الشخصية",
|
||||
component: <>H2</>,
|
||||
hidden: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="ModelBody">
|
||||
<TabsBar steps={steps} />
|
||||
<div className="ModelBodyForm">
|
||||
<header>إضافة طالب</header>
|
||||
<ActiveTabs steps={steps} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelBody;
|
||||
15
src/Components/Student/Add/Model/formUtil.ts
Normal file
15
src/Components/Student/Add/Model/formUtil.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import * as Yup from "yup";
|
||||
|
||||
export const getInitialValues = (objectToEdit: any): any => {
|
||||
return {
|
||||
id: objectToEdit?.id ?? 0,
|
||||
name: objectToEdit?.name ?? "",
|
||||
};
|
||||
};
|
||||
|
||||
export const getValidationSchema = () => {
|
||||
// validate input
|
||||
return Yup.object().shape({
|
||||
name: Yup.string().required("مطلوب"),
|
||||
});
|
||||
};
|
||||
26
src/Components/Student/CountSection.tsx
Normal file
26
src/Components/Student/CountSection.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import React from "react";
|
||||
import { FaMoneyBill } from "react-icons/fa";
|
||||
import Image from "../Ui/Image";
|
||||
|
||||
const CountSection = () => {
|
||||
return (
|
||||
<div className="CountSection">
|
||||
<div className="CountCards">
|
||||
{[1, 2, 3, 4, 5]?.map((item: any, index: any) => {
|
||||
return (
|
||||
<div key={index} className="CountCard">
|
||||
<i>
|
||||
<FaMoneyBill />
|
||||
</i>
|
||||
<h4>الطالبات</h4>
|
||||
<h6>932</h6>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* <Image src='../Home/HomeCounter.png'/> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CountSection;
|
||||
21
src/Components/Student/DetailsCard.tsx
Normal file
21
src/Components/Student/DetailsCard.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import React from "react";
|
||||
import { FaChalkboardTeacher } from "react-icons/fa";
|
||||
import { IoSchool } from "react-icons/io5";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const DetailsCard = () => {
|
||||
const navigate = useNavigate();
|
||||
const handelclick = () => {
|
||||
navigate("/student/add");
|
||||
};
|
||||
return (
|
||||
<div className="DetailsCard" onClick={() => handelclick()}>
|
||||
<h6>إضافة طالب</h6>
|
||||
<div>
|
||||
<IoSchool />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DetailsCard;
|
||||
56
src/Components/Student/StudentCount.tsx
Normal file
56
src/Components/Student/StudentCount.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { Select } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaLaptop } from "react-icons/fa";
|
||||
import { IoMdArrowDropdown } from "react-icons/io";
|
||||
|
||||
const StudentCount = () => {
|
||||
const [selectedOption, setSelectedOption] = useState("هذا الأسبوع");
|
||||
const Options = [
|
||||
{ value: "هذا الأسبوع", label: "هذا الأسبوع" },
|
||||
{ value: "الأسبوع الماضي", label: "الأسبوع الماضي" },
|
||||
{ value: "الشهر الماضي", label: "الشهر الماضي" },
|
||||
];
|
||||
|
||||
const handleChangeSelect = (value: any) => {
|
||||
setSelectedOption(value);
|
||||
// console.log(`Selected ${value}`);
|
||||
};
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
// Define static data
|
||||
const staticData = [
|
||||
{ studentName: "طالبات الشعبة الأولى 1", studentCount: 50 },
|
||||
{ studentName: "طالبات الشعبة الأولى 2", studentCount: 60 },
|
||||
{ studentName: "طالبات الشعبة الأولى 3", studentCount: 70 },
|
||||
{ studentName: "طالبات الشعبة الأولى 4", studentCount: 80 },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="StudentCount">
|
||||
<header>
|
||||
<h6>{t("عدد الطالبات")}</h6>
|
||||
<Select
|
||||
defaultValue={selectedOption}
|
||||
onChange={handleChangeSelect}
|
||||
suffixIcon={<IoMdArrowDropdown size={30} />}
|
||||
options={Options}
|
||||
/>
|
||||
</header>
|
||||
<main>
|
||||
{staticData.map((item, index) => (
|
||||
<div key={index}>
|
||||
<span>
|
||||
<h6>{item.studentName}</h6>
|
||||
<p>{item.studentCount} طالب</p>
|
||||
</span>
|
||||
<FaLaptop />
|
||||
</div>
|
||||
))}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StudentCount;
|
||||
54
src/Components/Student/activity/MoneyState.tsx
Normal file
54
src/Components/Student/activity/MoneyState.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import React from "react";
|
||||
import { FaMoneyBill } from "react-icons/fa";
|
||||
import Image from "../../Ui/Image";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useGetOverView } from "../../../api/payment";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Currency } from "../../../config/AppKey";
|
||||
import { formatNumber } from "../../../utils/formatNumber";
|
||||
|
||||
const MoneyState = () => {
|
||||
const navigation = useNavigate();
|
||||
const { student_id } = useParams();
|
||||
const handelnavigate = () => {
|
||||
navigation(`payment`);
|
||||
};
|
||||
const { data } = useGetOverView({
|
||||
student_id: student_id,
|
||||
});
|
||||
const [t] = useTranslation();
|
||||
const to_be_paid = Number(data?.data?.to_be_paid) || 0;
|
||||
const paid = Number(data?.data?.paid) || 0;
|
||||
|
||||
const dues = formatNumber(to_be_paid - paid);
|
||||
return (
|
||||
<div className="MoneyState">
|
||||
<header>{t("header.The_student_financial_situation")}</header>
|
||||
<main>
|
||||
<button>
|
||||
{t("select.Payments.dues")} : {dues} {Currency}
|
||||
</button>
|
||||
<div>
|
||||
<div>
|
||||
<FaMoneyBill />
|
||||
</div>
|
||||
<span>
|
||||
<h6>{t("select.Payments.to_be_paid")}</h6>
|
||||
<p>
|
||||
{formatNumber(to_be_paid)} {Currency}
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
<article>
|
||||
<span onClick={handelnavigate}>
|
||||
<Image src="/Icon/cash.png" />
|
||||
<h5>{t("models.payment")}</h5>
|
||||
<button>{t("practical.details")}</button>
|
||||
</span>
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoneyState;
|
||||
35
src/Components/Test/Test.tsx
Normal file
35
src/Components/Test/Test.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import React, { useState } from "react";
|
||||
|
||||
function FakeIntegration() {
|
||||
const [integrationInput, setIntegrationInput] = useState("");
|
||||
const [integrationResult, setIntegrationResult] = useState("");
|
||||
|
||||
const handleSubmit = (event: any) => {
|
||||
event.preventDefault();
|
||||
// Simulated integration result
|
||||
const fakeResult = "∫(x^2)dx = (1/3)x^3 + C";
|
||||
setIntegrationResult(fakeResult);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
value={integrationInput}
|
||||
onChange={(e) => setIntegrationInput(e.target.value)}
|
||||
placeholder="Enter integration expression"
|
||||
/>
|
||||
<button type="submit">Calculate</button>
|
||||
</form>
|
||||
<div>
|
||||
<h2>Integration Input:</h2>
|
||||
<p>{integrationInput}</p>
|
||||
<h2>Integration Result:</h2>
|
||||
<p>{integrationResult}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FakeIntegration;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { DatePicker } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { DatePicker } from 'antd'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useObjectToEdit } from '../../../zustand/ObjectToEditState';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import type { DatePickerProps } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import { DateEnum } from "../../../enums/Date";
|
||||
import { DateEnum } from '../../../enums/Date';
|
||||
|
||||
const CustomDatePicker = () => {
|
||||
const [t] = useTranslation();
|
||||
|
|
@ -24,6 +24,7 @@ const CustomDatePicker = () => {
|
|||
};
|
||||
const Today = new Date() as any;
|
||||
|
||||
|
||||
return (
|
||||
<div className="CustomDatePicker">
|
||||
<DatePicker
|
||||
|
|
@ -33,7 +34,7 @@ const CustomDatePicker = () => {
|
|||
format={DateEnum?.FORMATE}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomDatePicker;
|
||||
export default CustomDatePicker
|
||||
|
|
|
|||
56
src/Components/Utils/Theme.tsx
Normal file
56
src/Components/Utils/Theme.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import React, { memo } from "react";
|
||||
import type { MenuProps } from "antd";
|
||||
import { Button, Dropdown, Space } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BsFillMoonStarsFill, BsFillSunFill } from "react-icons/bs";
|
||||
import { useChangeTheme } from "../../Hooks/useChangeTheme";
|
||||
|
||||
const Theme: React.FC = () => {
|
||||
const { currentTheme, changeTheme } = useChangeTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const LightTheme = memo(() => (
|
||||
<div className="MenuChange" onClick={lightThemeClickHandler}>
|
||||
<BsFillSunFill />
|
||||
{t("light")}
|
||||
</div>
|
||||
));
|
||||
|
||||
const DarkTheme = memo(() => (
|
||||
<div className="MenuChange" onClick={darkThemeClickHandler}>
|
||||
<BsFillMoonStarsFill />
|
||||
{t("dark")}
|
||||
</div>
|
||||
));
|
||||
|
||||
const lightThemeClickHandler = React.useCallback(() => {
|
||||
changeTheme("light");
|
||||
}, [changeTheme]);
|
||||
|
||||
const darkThemeClickHandler = React.useCallback(() => {
|
||||
changeTheme("dark");
|
||||
}, [changeTheme]);
|
||||
|
||||
const items: MenuProps["items"] = [
|
||||
{
|
||||
key: "1",
|
||||
label: <LightTheme />,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
label: <DarkTheme />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<Dropdown menu={{ items }} placement="top">
|
||||
<Button>
|
||||
{currentTheme === "dark" ? <DarkTheme /> : <LightTheme />}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default Theme;
|
||||
|
|
@ -31,11 +31,11 @@ const components: { [key: string]: React.FC<any> } = {
|
|||
MaltyFile: MaltyFile,
|
||||
Checkbox: CheckboxField,
|
||||
NumberFormate: NumberFormate,
|
||||
Number: NumberField,
|
||||
Number:NumberField
|
||||
};
|
||||
|
||||
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
||||
({ type, ...otherProps }: any) => {
|
||||
({ type, ...otherProps }:any) => {
|
||||
const Component = components[type as ValidationFieldType];
|
||||
|
||||
if (!Component) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { Checkbox, Form } from "antd";
|
||||
import { getNestedValue } from "../utils/getNestedValue";
|
||||
import {getNestedValue} from '../utils/getNestedValue'
|
||||
const CheckboxField = ({
|
||||
name,
|
||||
label,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ const Default = ({
|
|||
<label htmlFor={name} className="text">
|
||||
{label2}
|
||||
</label>
|
||||
) : no_label ? (
|
||||
)
|
||||
|
||||
:no_label ? (
|
||||
<label htmlFor={name} className="text">
|
||||
<span>empty</span>
|
||||
</label>
|
||||
|
|
@ -56,6 +58,7 @@ const Default = ({
|
|||
name={name}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
|
||||
{...(type === "number" && { min: 0 })}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -12,27 +12,27 @@ const File = ({
|
|||
className,
|
||||
props,
|
||||
}: any) => {
|
||||
const { formik, t, isError, errorMsg } = useFormField(name, props);
|
||||
const { formik, t, isError,errorMsg } = useFormField(name, props);
|
||||
let imageUrl = formik?.values?.[name] ?? null;
|
||||
console.log(imageUrl);
|
||||
console.log(typeof imageUrl === "string");
|
||||
console.log(typeof imageUrl === 'string');
|
||||
|
||||
const fileList: UploadFile[] = useMemo(() => {
|
||||
if (!imageUrl) return [];
|
||||
|
||||
return [
|
||||
typeof imageUrl === "string"
|
||||
typeof imageUrl === 'string'
|
||||
? {
|
||||
uid: "-1",
|
||||
name: "uploaded-image",
|
||||
status: "done",
|
||||
uid: '-1',
|
||||
name: 'uploaded-image',
|
||||
status: 'done',
|
||||
url: imageUrl,
|
||||
thumbUrl: imageUrl,
|
||||
}
|
||||
: {
|
||||
uid: imageUrl.uid || "-1",
|
||||
name: imageUrl.name || "uploaded-image",
|
||||
status: "done",
|
||||
uid: imageUrl.uid || '-1',
|
||||
name: imageUrl.name || 'uploaded-image',
|
||||
status: 'done',
|
||||
originFileObj: imageUrl,
|
||||
},
|
||||
];
|
||||
|
|
@ -70,6 +70,7 @@ const File = ({
|
|||
icon={<UploadOutlined />}
|
||||
>
|
||||
{placholder ?? t("input.Click_to_upload_the_image")}
|
||||
|
||||
</Button>
|
||||
<div className="Error_color"> {isError ? "required" : ""}</div>
|
||||
{errorMsg}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ const NumberField = ({
|
|||
label_icon,
|
||||
...props
|
||||
}: ValidationFieldPropsInput) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
const { errorMsg, isError, t ,formik} = useFormField(name, props);
|
||||
|
||||
const handleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
console.log("Change:", e);
|
||||
console.log('Change:', e);
|
||||
formik.setFieldValue(name, e);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ const NumberFormate = ({
|
|||
disabled={isDisabled}
|
||||
size="large"
|
||||
|
||||
|
||||
// onChange={onChange ? onChange : handleChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const SearchField = ({
|
|||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
isLoading,
|
||||
isLoading
|
||||
}: any) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||
|
|
@ -78,7 +78,8 @@ const SearchField = ({
|
|||
onChange={onChange || SelectableChange}
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
notFoundContent={isLoading ? <Spin /> : "لا يوجد"}
|
||||
notFoundContent={isLoading ? <Spin/> : "لا يوجد" }
|
||||
|
||||
onSearch={SearchHandleChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const TextField = ({
|
|||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
className,
|
||||
className
|
||||
}: any) => {
|
||||
const { formik, isError, errorMsg, t } = useFormField(name, props);
|
||||
const TextFilehandleChange = (
|
||||
|
|
@ -33,13 +33,13 @@ const TextField = ({
|
|||
) : label_icon ? (
|
||||
<div className="LabelWithIcon">
|
||||
<label htmlFor={name} className="text">
|
||||
{label2 ? label2 : t(`input.${label ? label : name}`)}
|
||||
{label2 ? label2 : t(`input.${label ? label : name}`) }
|
||||
</label>
|
||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
||||
</div>
|
||||
) : (
|
||||
<label htmlFor={name} className="text">
|
||||
{label2 ? label2 : t(`input.${label ? label : name}`)}
|
||||
{label2 ? label2 : t(`input.${label ? label : name}`) }
|
||||
</label>
|
||||
)}
|
||||
|
||||
|
|
@ -57,7 +57,9 @@ const TextField = ({
|
|||
showCount
|
||||
maxLength={1000}
|
||||
onChange={onChange || TextFilehandleChange}
|
||||
style={{ height: 120 }}
|
||||
style={{height:120}}
|
||||
|
||||
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -62,8 +62,9 @@
|
|||
}
|
||||
.Checkboxs {
|
||||
padding: 4%;
|
||||
|
||||
}
|
||||
.ant-checkbox-wrapper {
|
||||
.ant-checkbox-wrapper{
|
||||
min-width: 100px;
|
||||
}
|
||||
.SearchField {
|
||||
|
|
@ -202,26 +203,27 @@ input:-webkit-autofill:hover {
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ant-checkbox-wrapper {
|
||||
|
||||
.ant-checkbox-wrapper{
|
||||
margin-top: 25px !important;
|
||||
}
|
||||
|
||||
.add_new_button {
|
||||
.add_new_button{
|
||||
margin-bottom: 20px;
|
||||
svg {
|
||||
svg{
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
.ValidationField:has(.input_number) {
|
||||
.ValidationField:has(.input_number){
|
||||
max-width: 100px;
|
||||
|
||||
.input_number {
|
||||
.input_number{
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.flex {
|
||||
.flex{
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
gap: 30px ;
|
||||
max-width: 80% !important;
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
export function getNestedValue(obj: any, path: any) {
|
||||
|
||||
|
||||
export function getNestedValue(obj:any, path:any) {
|
||||
return path
|
||||
.replace(/\?.\[|\]\[|\]\.?/g, ".") // Replace question mark and square brackets
|
||||
.split(".") // Split by dots
|
||||
.replace(/\?.\[|\]\[|\]\.?/g, '.') // Replace question mark and square brackets
|
||||
.split('.') // Split by dots
|
||||
.filter(Boolean) // Remove empty strings
|
||||
.reduce((acc: any, key: any) => acc && acc[key], obj); // Access nested properties
|
||||
.reduce((acc:any, key:any) => acc && acc[key], obj); // Access nested properties
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ export interface ValidationFieldPropsSearch {
|
|||
option: any[];
|
||||
isMulti?: boolean;
|
||||
searchBy: string;
|
||||
isLoading?: any;
|
||||
isLoading?:any
|
||||
}
|
||||
export interface ValidationFieldPropsDataRange {
|
||||
name: string;
|
||||
|
|
@ -151,7 +151,7 @@ export interface ValidationFieldPropstext {
|
|||
| "TextArea"
|
||||
| "NumberFormate";
|
||||
label?: string;
|
||||
label2?: string;
|
||||
label2?:string;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
isDisabled?: boolean;
|
||||
|
|
@ -161,26 +161,31 @@ export interface ValidationFieldPropstext {
|
|||
[key: string]: any; // Index signature to allow any additional props
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///// new
|
||||
export interface BaseField {
|
||||
name: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
export type OmitBaseType = "placeholder" | "name" | "label" | "type";
|
||||
export type OmitBaseType = 'placeholder' | 'name' | 'label' | 'type';
|
||||
|
||||
export type OmitPicker = OmitBaseType | "format";
|
||||
export type OmitPicker = OmitBaseType | 'format';
|
||||
|
||||
export interface ValidationFieldPropsInput
|
||||
extends Omit<InputProps, OmitBaseType>,
|
||||
BaseField {
|
||||
type: "text" | "number" | "password" | "email" | "Number";
|
||||
isDisabled?: boolean;
|
||||
no_label?: string;
|
||||
label_icon?: string;
|
||||
label2?: string;
|
||||
type: 'text' | 'number' | 'password' | 'email' | "Number";
|
||||
isDisabled?:boolean
|
||||
no_label?:string
|
||||
label_icon?:string
|
||||
label2?:string
|
||||
}
|
||||
|
||||
|
||||
export type ValidationFieldProps =
|
||||
| ValidationFieldPropsInput
|
||||
| ValidationFieldPropsSelect
|
||||
|
|
|
|||
60
src/Components/activity/StudentBehavior.tsx
Normal file
60
src/Components/activity/StudentBehavior.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import Image from "../Ui/Image";
|
||||
import { Student } from "../../types/Item";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useGetStudent } from "../../api/student";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const StudentBehavior = () => {
|
||||
const { student_id } = useParams();
|
||||
const { data: studentData } = useGetStudent({
|
||||
show: student_id,
|
||||
});
|
||||
|
||||
const userData: Student | {} = studentData?.data ?? {};
|
||||
|
||||
const { positiveNote, warningNote, alertNote } = userData as Student;
|
||||
const [t] = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const handelnavigate = () => {
|
||||
navigate(`note`);
|
||||
};
|
||||
const Data = [
|
||||
{
|
||||
icon: "/Icon/medal.png",
|
||||
title: t("array.UserInfo.appreciation"),
|
||||
value: positiveNote,
|
||||
buttonText: t("practical.show"),
|
||||
},
|
||||
{
|
||||
icon: "/Icon/warning.png",
|
||||
title: t("array.UserInfo.warning"),
|
||||
value: warningNote,
|
||||
buttonText: t("practical.show"),
|
||||
},
|
||||
{
|
||||
icon: "/Icon/Error.png",
|
||||
title: t("array.UserInfo.alert"),
|
||||
value: alertNote,
|
||||
buttonText: t("practical.show"),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div className="StudentBehavior">
|
||||
<header>{t("header.Student_classroom_behavior")}</header>
|
||||
<main>
|
||||
{Data.map((item, index) => (
|
||||
<article key={index} onClick={handelnavigate}>
|
||||
<Image src={item.icon} />
|
||||
<div>
|
||||
<h6>{item.title}</h6>
|
||||
<p>{item.value}</p>
|
||||
</div>
|
||||
<button>{item.buttonText}</button>
|
||||
</article>
|
||||
))}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StudentBehavior;
|
||||
97
src/Components/activity/StudentsTime.tsx
Normal file
97
src/Components/activity/StudentsTime.tsx
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import React from "react";
|
||||
import { getColorName } from "../../Hooks/usePercentage";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useGetStudent } from "../../api/student";
|
||||
import { Student } from "../../types/Item";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface StudentTimeData {
|
||||
id: number;
|
||||
absences: number;
|
||||
justified: number;
|
||||
unjustified: number;
|
||||
totalDays: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const StudentsTime = () => {
|
||||
const { student_id } = useParams();
|
||||
const { data: studentData } = useGetStudent({
|
||||
show: student_id,
|
||||
});
|
||||
|
||||
const userData: Student | {} = studentData?.data ?? {};
|
||||
|
||||
const { ealryDeparture, absence, lateArrival } = userData as Student;
|
||||
const [t] = useTranslation();
|
||||
const data = [
|
||||
{
|
||||
name: t("models.absence"),
|
||||
id: 1,
|
||||
absences: absence?.justified + absence?.not_justified,
|
||||
justified: absence?.justified,
|
||||
unjustified: absence?.not_justified,
|
||||
totalDays: absence?.total,
|
||||
},
|
||||
{
|
||||
name: t("models.late_arrival"),
|
||||
id: 2,
|
||||
absences: lateArrival?.justified + lateArrival?.not_justified,
|
||||
justified: lateArrival?.justified,
|
||||
unjustified: lateArrival?.not_justified,
|
||||
totalDays: lateArrival?.total,
|
||||
},
|
||||
{
|
||||
name: t("models.earlyDeparture"),
|
||||
id: 3,
|
||||
absences: ealryDeparture?.justified + ealryDeparture?.not_justified,
|
||||
justified: ealryDeparture?.justified,
|
||||
unjustified: ealryDeparture?.not_justified,
|
||||
totalDays: ealryDeparture?.total,
|
||||
},
|
||||
];
|
||||
const navigate = useNavigate();
|
||||
const handel_naviagate = () => {
|
||||
navigate(`status`);
|
||||
};
|
||||
return (
|
||||
<div className="StudentsTime">
|
||||
<header>{t("header.student_status")}</header>
|
||||
<main>
|
||||
{data?.length > 0 &&
|
||||
data.map((item: StudentTimeData, index: number) => {
|
||||
return (
|
||||
<article key={item.id} onClick={handel_naviagate}>
|
||||
<span>
|
||||
<h6>{item?.name}</h6>
|
||||
<p data-color={getColorName(item.absences)}>
|
||||
{item.absences}
|
||||
</p>
|
||||
</span>
|
||||
<div>
|
||||
<span>
|
||||
<div>
|
||||
{t("select.Student_Type.justified")}
|
||||
<span>{item.justified}</span>
|
||||
</div>
|
||||
<div>
|
||||
{t("select.Student_Type.not_justified")}
|
||||
<span>{item.unjustified}</span>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div className="StudentsTimeTotal">المجموع</div>
|
||||
<p>
|
||||
{item.absences} من {item.totalDays} يوم
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StudentsTime;
|
||||
|
|
@ -1,26 +1,28 @@
|
|||
import { useFormikContext } from "formik";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { GoArrowSwitch } from "react-icons/go";
|
||||
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
|
||||
import { QUESTION_OBJECT_KEY } from "../../config/AppKey";
|
||||
import { useFormikContext } from 'formik';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { GoArrowSwitch } from 'react-icons/go';
|
||||
import { useObjectToEdit } from '../../zustand/ObjectToEditState';
|
||||
import { QUESTION_OBJECT_KEY } from '../../config/AppKey';
|
||||
|
||||
const Header = () => {
|
||||
const [t] = useTranslation();
|
||||
const { values, setFieldValue, setValues } = useFormikContext<any>();
|
||||
const { isBseQuestion, setIsBseQuestion } = useObjectToEdit();
|
||||
const { setSavedQuestionData } = useObjectToEdit();
|
||||
const { values, setFieldValue,setValues } = useFormikContext<any>();
|
||||
const {isBseQuestion,setIsBseQuestion} = useObjectToEdit()
|
||||
const {setSavedQuestionData} = useObjectToEdit()
|
||||
const handleChange = () => {
|
||||
setSavedQuestionData(null);
|
||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
setSavedQuestionData(null)
|
||||
localStorage.removeItem(QUESTION_OBJECT_KEY)
|
||||
if (isBseQuestion) {
|
||||
setIsBseQuestion(false);
|
||||
setValues(null);
|
||||
setFieldValue("isBase", 0);
|
||||
setIsBseQuestion(false)
|
||||
setValues(null)
|
||||
setFieldValue("isBase",0)
|
||||
|
||||
} else {
|
||||
setIsBseQuestion(true);
|
||||
setValues(null);
|
||||
setFieldValue("isBase", 1);
|
||||
|
||||
setIsBseQuestion(true)
|
||||
setValues(null)
|
||||
setFieldValue("isBase",1)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -31,9 +33,7 @@ const Header = () => {
|
|||
</div>
|
||||
<div>
|
||||
<GoArrowSwitch onClick={handleChange} className="m-2" />
|
||||
{isBseQuestion || values?.isBase === 1
|
||||
? t("header.malty_exercise")
|
||||
: t("header.exercise")}
|
||||
{isBseQuestion || values?.isBase === 1 ? t("header.malty_exercise") :t("header.exercise") }
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
|
|
|||
62
src/Components/testInput/MathInput.tsx
Normal file
62
src/Components/testInput/MathInput.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import katex from 'katex';
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
const MathInput: React.FC = () => {
|
||||
const [latex, setLatex] = useState<string>('f(x) = \\frac{1}{1+e^{-x}}');
|
||||
const displayRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
renderLatex();
|
||||
}, [latex]);
|
||||
//// tow input show and edit
|
||||
const renderLatex = () => {
|
||||
if (displayRef.current) {
|
||||
try {
|
||||
katex.render(latex, displayRef.current, {
|
||||
throwOnError: false,
|
||||
displayMode: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('KaTeX rendering error:', error);
|
||||
displayRef.current.textContent = 'Error rendering LaTeX';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setLatex(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<input
|
||||
type="text"
|
||||
value={latex}
|
||||
onChange={handleInputChange}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
fontSize: '16px',
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '4px',
|
||||
fontFamily: 'monospace',
|
||||
marginBottom: '10px',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
ref={displayRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
fontSize: '16px',
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '4px',
|
||||
minHeight: '40px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MathInput;
|
||||
68
src/Components/testInput/MathInputEdatble.tsx
Normal file
68
src/Components/testInput/MathInputEdatble.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import katex from 'katex';
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
const MathInput: React.FC = () => {
|
||||
const [latex, setLatex] = useState<string>('f(x) = \\frac{1}{1+e^{-x}}');
|
||||
const inputRef = useRef<HTMLDivElement>(null);
|
||||
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
renderLatex();
|
||||
}, [latex, isEditing]);
|
||||
|
||||
const renderLatex = () => {
|
||||
if (inputRef.current && !isEditing) {
|
||||
try {
|
||||
katex.render(latex, inputRef.current, {
|
||||
throwOnError: false,
|
||||
displayMode: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('KaTeX rendering error:', error);
|
||||
inputRef.current.textContent = 'Error rendering LaTeX';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleInput = () => {
|
||||
if (inputRef.current) {
|
||||
setLatex(inputRef.current.textContent || '');
|
||||
}
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
setIsEditing(true);
|
||||
if (inputRef.current) {
|
||||
inputRef.current.textContent = latex;
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<div
|
||||
ref={inputRef}
|
||||
contentEditable
|
||||
onInput={handleInput}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
fontSize: '16px',
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '4px',
|
||||
minHeight: '40px',
|
||||
fontFamily: isEditing ? 'monospace' : 'inherit',
|
||||
cursor: 'text',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MathInput;
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
export const useAddKeyToData = (dataSource: any, identifier = "id") => {
|
||||
if (!dataSource || !Array.isArray(dataSource)) {
|
||||
return [];
|
||||
|
|
|
|||
23
src/Hooks/useChangeTheme.tsx
Normal file
23
src/Hooks/useChangeTheme.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
export const useChangeTheme = () => {
|
||||
const [currentTheme, setCurrentTheme] = useState(
|
||||
localStorage.getItem("theme") ?? "light",
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentTheme === "dark") {
|
||||
document.body.classList.add("dark");
|
||||
} else {
|
||||
document.body.classList.remove("dark");
|
||||
}
|
||||
|
||||
localStorage.setItem("theme", currentTheme);
|
||||
}, [currentTheme]);
|
||||
|
||||
const changeTheme = useCallback((newTheme: any) => {
|
||||
setCurrentTheme(newTheme);
|
||||
}, []);
|
||||
|
||||
return { currentTheme, changeTheme };
|
||||
};
|
||||
18
src/Hooks/useDifferenceData.tsx
Normal file
18
src/Hooks/useDifferenceData.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { useState } from "react";
|
||||
|
||||
const useDifferenceData = (values: any, data: any) => {
|
||||
const [newData, setNewData] = useState({});
|
||||
|
||||
for (const key in data) {
|
||||
if (values[key] !== data[key]) {
|
||||
setNewData((prevData) => ({
|
||||
...prevData,
|
||||
[key]: values[key],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return newData;
|
||||
};
|
||||
|
||||
export default useDifferenceData;
|
||||
40
src/Hooks/useDisableShortcutsAndRightClick.tsx
Normal file
40
src/Hooks/useDisableShortcutsAndRightClick.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
const useDisableShortcutsAndRightClick = () => {
|
||||
useEffect(() => {
|
||||
const disableShortcutsAndRightClick = (event: Event) => {
|
||||
// Check for Ctrl+Shift+C or F12
|
||||
if (
|
||||
(event as KeyboardEvent).ctrlKey &&
|
||||
(event as KeyboardEvent).shiftKey &&
|
||||
((event as KeyboardEvent).key === "C" ||
|
||||
(event as KeyboardEvent).key === "c" ||
|
||||
(event as KeyboardEvent).keyCode === 123) /* F12 key code */
|
||||
) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const disableRightClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", disableShortcutsAndRightClick);
|
||||
document.addEventListener("contextmenu", disableRightClick);
|
||||
|
||||
// Attempt to prevent F12 key default behavior
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "F12" || event.keyCode === 123) {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", disableShortcutsAndRightClick);
|
||||
document.removeEventListener("contextmenu", disableRightClick);
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default useDisableShortcutsAndRightClick;
|
||||
|
|
@ -1,12 +1,8 @@
|
|||
import { useEffect } from "react";
|
||||
import { useEffect } from 'react';
|
||||
|
||||
type ModifierKey = "ctrlKey" | "shiftKey" | "altKey" | "metaKey";
|
||||
type ModifierKey = 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey';
|
||||
|
||||
const useKeyPress = (
|
||||
targetKey: string,
|
||||
modifierKey: ModifierKey,
|
||||
callback: any,
|
||||
) => {
|
||||
const useKeyPress = (targetKey: string, modifierKey: ModifierKey, callback:any) => {
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event[modifierKey] && event.key === targetKey) {
|
||||
|
|
@ -15,10 +11,10 @@ const useKeyPress = (
|
|||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [targetKey, modifierKey, callback]);
|
||||
};
|
||||
|
|
|
|||
24
src/Hooks/useLoadingState.tsx
Normal file
24
src/Hooks/useLoadingState.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { useState, useEffect } from "react";
|
||||
|
||||
function useLoadingState(
|
||||
initialValue: boolean,
|
||||
duration: number,
|
||||
): [boolean, () => void] {
|
||||
const [loading, setLoading] = useState<boolean>(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, duration);
|
||||
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [duration]);
|
||||
|
||||
const resetLoading = () => {
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
return [loading, resetLoading];
|
||||
}
|
||||
|
||||
export default useLoadingState;
|
||||
45
src/Hooks/usePagination.tsx
Normal file
45
src/Hooks/usePagination.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { Pagination } from "antd";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
||||
|
||||
export const PaginationBody = ({ data, no_label, label_icon }: any) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const pagination = location?.search || "";
|
||||
const currentPage = parseInt(
|
||||
new URLSearchParams(location.search).get("page") || "1",
|
||||
10,
|
||||
);
|
||||
const pageSize = parseInt(
|
||||
new URLSearchParams(location.search).get("per_page") || "8",
|
||||
10,
|
||||
);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const onChange = (page: number, pageSize?: number) => {
|
||||
navigate(
|
||||
`?page=${page}&per_page=${pageSize || data?.per_page}&search=${searchParams.get("search")}`,
|
||||
{ replace: true },
|
||||
);
|
||||
};
|
||||
|
||||
const onShowSizeChange = (current: number, pageSize: number) => {
|
||||
navigate(
|
||||
`?page=${current}&per_page=${pageSize}&search=${searchParams.get("search")}`,
|
||||
{ replace: true },
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Pagination
|
||||
className="text-center mt-3 paginateStyle"
|
||||
total={data}
|
||||
showTotal={(total: any) => `Total ${total} items`}
|
||||
pageSize={pageSize}
|
||||
pageSizeOptions={[8, 16, 24, 32, 40]}
|
||||
defaultCurrent={currentPage}
|
||||
current={currentPage} // Adding this line will set the current page correctly
|
||||
onChange={onChange}
|
||||
onShowSizeChange={onShowSizeChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
72
src/Hooks/usePercentage.tsx
Normal file
72
src/Hooks/usePercentage.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { RiArrowLeftDownLine, RiArrowRightUpLine } from "react-icons/ri";
|
||||
|
||||
export const PercentageImageSrc = (percentage: number) => {
|
||||
if (percentage >= 90) {
|
||||
return "../ArrowType/90.png";
|
||||
} else if (percentage >= 80) {
|
||||
return "../ArrowType/80.png";
|
||||
} else if (percentage >= 60) {
|
||||
return "../ArrowType/60.png";
|
||||
} else if (percentage >= 40) {
|
||||
return "../ArrowType/40.png";
|
||||
} else if (percentage >= 20) {
|
||||
return "../ArrowType/20.png";
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
export const getColorName = (percentage: number) => {
|
||||
if (percentage >= 6) {
|
||||
return "Red";
|
||||
} else if (percentage >= 4) {
|
||||
return "Orange";
|
||||
} else if (percentage >= 2) {
|
||||
return "Green";
|
||||
} else {
|
||||
return "Green";
|
||||
}
|
||||
};
|
||||
|
||||
export const getStrokeColor = (percentage: number) => {
|
||||
if (percentage >= 90) {
|
||||
return "#87d068"; // Green color
|
||||
} else if (percentage >= 80) {
|
||||
return "#87d068"; // Blue color
|
||||
} else if (percentage >= 60) {
|
||||
return "#ffec3d"; // Yellow color
|
||||
} else if (percentage >= 40) {
|
||||
return "#faad14"; // Orange color
|
||||
} else if (percentage >= 20) {
|
||||
return "#f5222d"; // Red color
|
||||
} else {
|
||||
return "#f5222d"; // Red color
|
||||
}
|
||||
};
|
||||
|
||||
export const getPercentageIcon = (percentage: number) => {
|
||||
if (percentage >= 60) {
|
||||
return <RiArrowRightUpLine />;
|
||||
} else if (percentage >= 40) {
|
||||
return <></>;
|
||||
} else {
|
||||
return <RiArrowLeftDownLine />;
|
||||
}
|
||||
};
|
||||
|
||||
export const getPercentageText = (percentage: number) => {
|
||||
if (percentage >= 60) {
|
||||
return "مرتفع";
|
||||
} else if (percentage >= 40) {
|
||||
return "متوسط";
|
||||
} else {
|
||||
return "منخفض";
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
/* <RiArrowRightUpLine /> */
|
||||
}
|
||||
|
||||
{
|
||||
/* <RiArrowLeftDownLine /> */
|
||||
}
|
||||
32
src/Hooks/usePreviousRoute.tsx
Normal file
32
src/Hooks/usePreviousRoute.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
const usePreviousPath = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [previousPath, setPreviousPath] = useState(null);
|
||||
console.log(previousPath);
|
||||
|
||||
useEffect(() => {
|
||||
// Update the previous path whenever the location changes
|
||||
const currentPath = location.pathname as any;
|
||||
setPreviousPath(currentPath);
|
||||
|
||||
// Clean up
|
||||
return () => {
|
||||
setPreviousPath(null);
|
||||
};
|
||||
}, [location]);
|
||||
|
||||
const navigateToPreviousPath = () => {
|
||||
if (previousPath) {
|
||||
navigate(previousPath);
|
||||
} else {
|
||||
navigate(-1); // Fallback to default behavior
|
||||
}
|
||||
};
|
||||
|
||||
return { navigateToPreviousPath };
|
||||
};
|
||||
|
||||
export default usePreviousPath;
|
||||
42
src/Hooks/useResponsive.tsx
Normal file
42
src/Hooks/useResponsive.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { useState, useEffect } from "react";
|
||||
|
||||
interface ResponsiveHookProps {
|
||||
initialState: boolean;
|
||||
setIs: any;
|
||||
widthThreshold: number;
|
||||
}
|
||||
|
||||
const useResponsive = ({
|
||||
initialState,
|
||||
setIs,
|
||||
widthThreshold,
|
||||
}: ResponsiveHookProps) => {
|
||||
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
||||
const [isBoolean, setIsBoolean] = useState<boolean>(initialState);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setWindowWidth(window.innerWidth);
|
||||
};
|
||||
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (windowWidth < widthThreshold && isBoolean) {
|
||||
setIs(false);
|
||||
setIsBoolean(false);
|
||||
} else if (windowWidth >= widthThreshold && !isBoolean) {
|
||||
setIs(true);
|
||||
setIsBoolean(true);
|
||||
}
|
||||
}, [windowWidth, isBoolean, setIs, widthThreshold]);
|
||||
|
||||
return { windowWidth, isBoolean, setIsBoolean };
|
||||
};
|
||||
|
||||
export default useResponsive;
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
import { useEffect } from "react";
|
||||
import { setLocalStorageQuestions } from "../utils/setLocalStorageQuestions";
|
||||
import { useEffect } from 'react';
|
||||
import { setLocalStorageQuestions } from '../utils/setLocalStorageQuestions';
|
||||
import { setLocalStorageBaseQuestions } from '../utils/setLocalStorageBaseQuestions';
|
||||
|
||||
const useSaveOnDisconnect = (
|
||||
noChange: boolean,
|
||||
QUESTION_OBJECT_KEY: string,
|
||||
SavedQuestionData: any,
|
||||
) => {
|
||||
const useSaveOnDisconnect = (noChange: boolean, QUESTION_OBJECT_KEY: string, SavedQuestionData: any) => {
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
||||
console.log("disconnect");
|
||||
if (noChange) {
|
||||
if (SavedQuestionData?.isBase === 1) {
|
||||
if(SavedQuestionData?.isBase ===1){
|
||||
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||
} else {
|
||||
|
||||
}else{
|
||||
|
||||
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,22 +20,24 @@ const useSaveOnDisconnect = (
|
|||
const handleOffline = () => {
|
||||
console.log("disconnect");
|
||||
if (noChange) {
|
||||
if (SavedQuestionData?.isBase === 1) {
|
||||
if(SavedQuestionData?.isBase ===1){
|
||||
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||
} else {
|
||||
|
||||
}else{
|
||||
|
||||
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add event listeners
|
||||
window.addEventListener("beforeunload", handleBeforeUnload);
|
||||
window.addEventListener("offline", handleOffline);
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
window.removeEventListener("beforeunload", handleBeforeUnload);
|
||||
window.removeEventListener("offline", handleOffline);
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
}, [noChange, QUESTION_OBJECT_KEY, SavedQuestionData]); // Add dependencies to the hook
|
||||
};
|
||||
|
|
|
|||
21
src/Hooks/useSearchResults.tsx
Normal file
21
src/Hooks/useSearchResults.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
const useSearchResults = (data: any[] | undefined, query: string | null) => {
|
||||
const [results, setResults] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const filteredResults =
|
||||
data?.filter((item: any) =>
|
||||
item?.toLowerCase()?.includes(query?.toLowerCase()),
|
||||
) ?? [];
|
||||
|
||||
setResults(filteredResults);
|
||||
|
||||
if (results?.length === 0 && query != null && query?.length > 1) {
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export default useSearchResults;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect } from "react";
|
||||
import { usePage_titleState } from "../zustand/PageTitleState";
|
||||
|
||||
const useSetPageTitle = (title: any) => {
|
||||
const useSetPage_title = (title: any) => {
|
||||
const setPage_title = usePage_titleState((state) => state.setPage_title);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -9,4 +9,4 @@ const useSetPageTitle = (title: any) => {
|
|||
}, [title, setPage_title]);
|
||||
};
|
||||
|
||||
export default useSetPageTitle;
|
||||
export default useSetPage_title;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { AppRoutes, CrudRoutes } from "../Routes";
|
||||
import { PROJECT_NAME } from "../config/AppKey";
|
||||
|
||||
export const useGetTitleFromRoute = (path: any) => {
|
||||
export const usegetTitleFromRoute = (path: any) => {
|
||||
if (AppRoutes[path]) {
|
||||
return `${PROJECT_NAME} | ${AppRoutes[path]}`;
|
||||
} else if (CrudRoutes[path]) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const FormikFormModel: React.FC<FormikFormProps> = ({
|
|||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
|
||||
>
|
||||
{(formik) => {
|
||||
useEffect(() => {
|
||||
|
|
@ -42,6 +43,7 @@ const FormikFormModel: React.FC<FormikFormProps> = ({
|
|||
}
|
||||
}, [isOpen]);
|
||||
|
||||
|
||||
return <Form className="w-100">{children}</Form>;
|
||||
}}
|
||||
</Formik>
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ import { useTranslation } from "react-i18next";
|
|||
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../../enums/abilities";
|
||||
import { hasAbility } from "../../utils/hasAbility";
|
||||
|
||||
|
||||
export {
|
||||
SearchField,
|
||||
useModalHandler,
|
||||
ModalEnum,
|
||||
useTranslation,
|
||||
ABILITIES_ENUM,
|
||||
ModalEnum,
|
||||
useTranslation,
|
||||
ABILITIES_ENUM,
|
||||
ABILITIES_VALUES_ENUM,
|
||||
hasAbility,
|
||||
hasAbility
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { useGetTitleFromRoute } from "../../Hooks/useGetTitleFromRoute";
|
||||
import { usegetTitleFromRoute } from "../../Hooks/usegetTitleFromRoute";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import NavBar from "./NavBar";
|
||||
|
|
@ -18,7 +18,7 @@ const Layout = ({
|
|||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{useGetTitleFromRoute(location.pathname)}</title>
|
||||
<title>{usegetTitleFromRoute(location.pathname)}</title>
|
||||
</Helmet>
|
||||
<div className="Layout">
|
||||
<main className={`${className} Layout_Body`}>
|
||||
|
|
|
|||
|
|
@ -80,8 +80,7 @@ const SideBar = () => {
|
|||
<div className="side_bar">
|
||||
<h1>
|
||||
{/* {t("sidebar.dashboard")} */}
|
||||
{branch_name}{" "}
|
||||
</h1>
|
||||
{branch_name} </h1>
|
||||
<Divider />
|
||||
<div className="side_bar_links">
|
||||
{menuItems.map((item, index) => {
|
||||
|
|
|
|||
|
|
@ -20,11 +20,13 @@ type FormFieldType = {
|
|||
};
|
||||
|
||||
const FormField = ({ isLoading }: FormFieldType) => {
|
||||
|
||||
const [t] = useTranslation();
|
||||
return (
|
||||
<Form className="AuthForm">
|
||||
<Image src="../App/Logo.png" />
|
||||
|
||||
|
||||
<div className="AuthInput">
|
||||
<label className="form-label" htmlFor="username">
|
||||
{t("input.Username")}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const LoginForm = () => {
|
|||
const { mutate, isLoading, isSuccess, data } = useLoginAdmin();
|
||||
const [t] = useTranslation();
|
||||
const handelSubmit = (values: FormValues) => {
|
||||
|
||||
mutate(values);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ import React from "react";
|
|||
import Image from "../../Components/Ui/Image";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ABILITIES_ENUM } from "../../enums/abilities";
|
||||
import useSetPageTitle from "../../Hooks/useSetPageTitle";
|
||||
import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||
|
||||
const Dummy = () => {
|
||||
const [t] = useTranslation();
|
||||
useSetPageTitle(`${t(ABILITIES_ENUM?.MAIN_PAGE)} / ${t("dashboard")}`);
|
||||
useSetPage_title(`${t(ABILITIES_ENUM?.MAIN_PAGE)} / ${t("dashboard")}`);
|
||||
|
||||
return (
|
||||
<div className="DummyHomePage">
|
||||
<div className="DammyHomePage">
|
||||
<Image src="/App/SyriaLogo.webp" />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
19
src/Pages/Home/Page.tsx
Normal file
19
src/Pages/Home/Page.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import ChartSection from "./Section/ChartSection";
|
||||
import NoteSections from "./Section/NoteSections";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ABILITIES_ENUM } from "../../enums/abilities";
|
||||
// import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||
|
||||
const Page = () => {
|
||||
const [t] = useTranslation();
|
||||
// useSetPage_title(`${t(ABILITIES_ENUM?.MAIN_PAGE)} / ${t("dashboard")}`);
|
||||
|
||||
return (
|
||||
<div className="HomePage">
|
||||
<ChartSection />
|
||||
<NoteSections />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
26
src/Pages/Home/Section/ChartSection.tsx
Normal file
26
src/Pages/Home/Section/ChartSection.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import React from "react";
|
||||
import CountSection from "../../../Components/Home/CountSection";
|
||||
import ColumnChart from "../../../Components/Chart/ColumnChart";
|
||||
import AreaChart from "../../../Components/Chart/AreaChart";
|
||||
import StudentSubjects from "../../../Components/Home/StudentSubjects";
|
||||
import CalendarSection from "../../../Components/Home/CalendarSection";
|
||||
|
||||
const ChartSection = () => {
|
||||
return (
|
||||
<div className="ChartSection">
|
||||
<CountSection />
|
||||
<div className="TowItem">
|
||||
<ColumnChart />
|
||||
<CalendarSection />
|
||||
</div>
|
||||
<AreaChart />
|
||||
|
||||
<div className="TowItem">
|
||||
<StudentSubjects />
|
||||
<ColumnChart />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChartSection;
|
||||
14
src/Pages/Home/Section/NoteSections.tsx
Normal file
14
src/Pages/Home/Section/NoteSections.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import React from "react";
|
||||
import NoteSection from "../../../Components/Home/NoteSection";
|
||||
import ActivitySection from "../../../Components/Home/ActivitySection";
|
||||
|
||||
const NoteSections = () => {
|
||||
return (
|
||||
<div className="NoteSections">
|
||||
<NoteSection />
|
||||
<ActivitySection />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoteSections;
|
||||
|
|
@ -24,7 +24,9 @@ const Form = () => {
|
|||
<ValidationField placeholder="name" label="name" name="name" />
|
||||
</Col>
|
||||
<div>
|
||||
<DynamicTags />
|
||||
<DynamicTags/>
|
||||
|
||||
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ const ModalForm: React.FC = () => {
|
|||
}, [setIsOpen, isSuccess]);
|
||||
|
||||
const handleSubmit = (values: any) => {
|
||||
|
||||
mutate({
|
||||
...values,
|
||||
...values
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,16 @@ import { useEffect } from "react";
|
|||
import DynamicTags from "../synonyms/DynamicTags";
|
||||
|
||||
const Form = () => {
|
||||
|
||||
return (
|
||||
<Row className="w-100">
|
||||
<Col>
|
||||
<ValidationField placeholder="name" label="name" name="name" />
|
||||
</Col>
|
||||
<div>
|
||||
<DynamicTags />
|
||||
<DynamicTags/>
|
||||
|
||||
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import ModelButtons from "../../../Components/models/ModelButtons";
|
|||
|
||||
const ModalForm: React.FC = () => {
|
||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||
const { objectToEdit, setObjectToEdit } = useObjectToEdit((state) => state);
|
||||
const { objectToEdit, setObjectToEdit } = useObjectToEdit(
|
||||
(state) => state,
|
||||
);
|
||||
const { mutate, isSuccess, isLoading } = useUpdateTag();
|
||||
// console.log(objectToEdit,"objectToEdit");
|
||||
const handleSubmit = (values: any) => {
|
||||
|
|
@ -20,7 +22,7 @@ const ModalForm: React.FC = () => {
|
|||
// phone_number: values?.number
|
||||
// });
|
||||
mutate({
|
||||
...values,
|
||||
...values
|
||||
});
|
||||
};
|
||||
const handleCancel = () => {
|
||||
|
|
@ -56,6 +58,7 @@ const ModalForm: React.FC = () => {
|
|||
<main className="main_modal w-100">
|
||||
<ModelBody />
|
||||
<ModelButtons isLoading={isLoading} />
|
||||
|
||||
</main>
|
||||
</FormikForm>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,34 @@
|
|||
import { FaPlus } from "react-icons/fa";
|
||||
import { FaPlus } from "react-icons/fa";
|
||||
|
||||
import useModalHandler from "../../utils/useModalHandler";
|
||||
import { ModalEnum } from "../../enums/Model";
|
||||
|
||||
// import useSetPageTitle from "../../Hooks/useSetPageTitle";
|
||||
// import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { Spin } from "antd";
|
||||
import { canAddTags } from "../../utils/hasAbilityFn";
|
||||
import useSetPageTitle from "../../Hooks/useSetPageTitle";
|
||||
const Table = lazy(() => import("./Table"));
|
||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||
const DeleteModalForm = lazy(() => import("./Model/Delete"));
|
||||
const SearchField = lazy(
|
||||
() => import("../../Components/DataTable/SearchField"),
|
||||
);
|
||||
import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||
const Table = lazy(() => import('./Table'));
|
||||
const AddModalForm = lazy(() => import('./Model/AddModel'));
|
||||
const EditModalForm = lazy(() => import('./Model/EditModel'));
|
||||
const DeleteModalForm = lazy(() => import('./Model/Delete'));
|
||||
const SearchField = lazy(() => import('../../Components/DataTable/SearchField'));
|
||||
|
||||
|
||||
const TableHeader = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
const [t] = useTranslation();
|
||||
useSetPageTitle(t(`page_header.tags`));
|
||||
useSetPage_title(
|
||||
t(`page_header.tags`),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
<Suspense fallback={<Spin />}>
|
||||
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<header className="d-flex justify-content-between">
|
||||
<SearchField
|
||||
searchBy="name"
|
||||
placeholder={t("practical.search_here")}
|
||||
/>
|
||||
<SearchField searchBy="name" placeholder={t("practical.search_here")} />
|
||||
|
||||
{canAddTags && (
|
||||
<div className="Selects">
|
||||
|
|
@ -43,6 +42,7 @@ const TableHeader = () => {
|
|||
)}
|
||||
</header>
|
||||
|
||||
|
||||
<Table />
|
||||
<DeleteModalForm />
|
||||
<AddModalForm />
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ export {
|
|||
AddModalForm,
|
||||
EditModalForm,
|
||||
DeleteModalForm,
|
||||
FaPlus,
|
||||
FaPlus
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { useFormikContext } from "formik";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaCirclePlus } from "react-icons/fa6";
|
||||
import Tag from "./Tag";
|
||||
import { useFormikContext } from 'formik'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaCirclePlus } from 'react-icons/fa6'
|
||||
import Tag from './Tag'
|
||||
|
||||
const DynamicTags = () => {
|
||||
const formik = useFormikContext<any>();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const formik = useFormikContext<any>()
|
||||
const [t] = useTranslation()
|
||||
|
||||
|
||||
console.log(formik?.values?.synonyms);
|
||||
|
||||
|
|
@ -14,43 +16,55 @@ const DynamicTags = () => {
|
|||
const length = formik?.values?.synonyms.length;
|
||||
const lastElement = formik?.values?.synonyms[length - 1];
|
||||
|
||||
if (lastElement !== "") {
|
||||
formik.setFieldValue("synonyms", [
|
||||
...((formik?.values as any)?.synonyms as any[]),
|
||||
"",
|
||||
]);
|
||||
} else {
|
||||
if(lastElement !== ""){
|
||||
formik.setFieldValue('synonyms', [...(formik?.values as any)?.synonyms as any[],""])
|
||||
}else{
|
||||
|
||||
}
|
||||
};
|
||||
// console.log(formik?.values);
|
||||
// console.log(currentTag);
|
||||
|
||||
|
||||
}
|
||||
// console.log(formik?.values);
|
||||
// console.log(currentTag);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="DynamicTags">
|
||||
{formik?.values?.synonyms?.length < 1 && (
|
||||
<p className="add_new_button">
|
||||
<FaCirclePlus size={23} onClick={handleAddChoice} />{" "}
|
||||
{t("header.add_synonyms")}
|
||||
<div className='DynamicTags'>
|
||||
{formik?.values?.synonyms?.length < 1 &&
|
||||
|
||||
<p className="add_new_button" >
|
||||
<FaCirclePlus size={23} onClick={handleAddChoice} /> {t("header.add_synonyms")}
|
||||
</p>
|
||||
)}
|
||||
}
|
||||
|
||||
<div className="tag_container">
|
||||
<div className="tags">
|
||||
{(((formik?.values as any)?.synonyms as any[]) || []).map(
|
||||
(item: any, index: number) => {
|
||||
return <Tag key={index} index={index} data={item} />;
|
||||
},
|
||||
)}
|
||||
|
||||
{
|
||||
(((formik?.values as any)?.synonyms as any[])||[]) .map((item:any,index:number)=>{
|
||||
|
||||
return (
|
||||
<Tag key={index} index={index} data={item}/>
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{formik?.values?.synonyms?.length > 0 && (
|
||||
<p className="add_new_button">
|
||||
{formik?.values?.synonyms?.length > 0 &&
|
||||
|
||||
<p className="add_new_button" >
|
||||
<FaCirclePlus onClick={handleAddChoice} size={20} />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</p>
|
||||
|
||||
export default DynamicTags;
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DynamicTags
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import { useFormikContext } from "formik";
|
||||
import React, { useRef, useEffect } from "react";
|
||||
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||
import { FaTrash } from "react-icons/fa";
|
||||
import { useFormikContext } from 'formik';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useObjectToEdit } from '../../../zustand/ObjectToEditState';
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
|
||||
const Tag = ({ data, index }: { data: any; index: number }) => {
|
||||
const Tag = ({ data, index }: { data: any, index: number }) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const formik = useFormikContext<any>();
|
||||
const { setTagsSearch, setCurrentTag } = useObjectToEdit();
|
||||
const { setTagsSearch ,setCurrentTag} = useObjectToEdit();
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current) {
|
||||
|
|
@ -16,11 +16,13 @@ const Tag = ({ data, index }: { data: any; index: number }) => {
|
|||
console.log(formik?.values?.synonyms);
|
||||
console.log(index);
|
||||
|
||||
|
||||
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// console.log(e.target.value);
|
||||
formik.setFieldValue(`synonyms[${index}]`, e.target.value);
|
||||
setTagsSearch(e.target.value);
|
||||
setCurrentTag(index);
|
||||
setTagsSearch(e.target.value)
|
||||
setCurrentTag(index)
|
||||
|
||||
};
|
||||
|
||||
const handleInputBlur = () => {
|
||||
|
|
@ -36,14 +38,14 @@ const Tag = ({ data, index }: { data: any; index: number }) => {
|
|||
console.log(currentTags); // Log the updated tags array
|
||||
|
||||
// Update formik field value with the updated tags array
|
||||
formik.setFieldValue("synonyms", currentTags);
|
||||
formik.setFieldValue('synonyms', currentTags);
|
||||
|
||||
// Reset search state if needed
|
||||
setTagsSearch(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="tag">
|
||||
<div className='tag'>
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="tagInput"
|
||||
|
|
@ -51,10 +53,14 @@ const Tag = ({ data, index }: { data: any; index: number }) => {
|
|||
value={formik?.values?.synonyms[index]}
|
||||
onChange={handleEditInputChange}
|
||||
onBlur={handleInputBlur}
|
||||
|
||||
/>
|
||||
<FaTrash onClick={handleDeleteChoice} />
|
||||
<FaTrash onClick={handleDeleteChoice}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tag;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ export const useColumns = () => {
|
|||
};
|
||||
const [t] = useTranslation();
|
||||
|
||||
|
||||
|
||||
const columns: TableColumnsType<tags> = [
|
||||
{
|
||||
title: t("columns.id"),
|
||||
|
|
@ -55,10 +57,16 @@ export const useColumns = () => {
|
|||
placement="top"
|
||||
title={t("practical.edit")}
|
||||
color="#E0E0E0"
|
||||
|
||||
>
|
||||
<span onClick={() => handleEdit(record)}>
|
||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
||||
<MdOutlineEdit
|
||||
size={22}
|
||||
style={{ color: "#A098AE" }}
|
||||
|
||||
/>
|
||||
</span>
|
||||
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,49 @@
|
|||
import { useTranslation } from "react-i18next";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { Spin } from "antd";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ParamsEnum } from "../../enums/params";
|
||||
import { useGetAllSubject } from "../../api/subject";
|
||||
import useSetPageTitle from "../../Hooks/useSetPageTitle";
|
||||
import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||
|
||||
const Table = lazy(() => import("./Table"));
|
||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||
const DeleteModels = lazy(() => import("./Model/Delete"));
|
||||
const Table = lazy(() => import('./Table'));
|
||||
const AddModalForm = lazy(() => import('./Model/AddModel'));
|
||||
const EditModalForm = lazy(() => import('./Model/EditModel'));
|
||||
const DeleteModels = lazy(() => import('./Model/Delete'));
|
||||
|
||||
const TableHeader = () => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const { subject_id } = useParams<ParamsEnum>();
|
||||
const { subject_id} = useParams<ParamsEnum>();
|
||||
|
||||
const { data: Subject } = useGetAllSubject({
|
||||
show: subject_id,
|
||||
show:subject_id
|
||||
});
|
||||
|
||||
|
||||
const SubjectName = Subject?.data?.name ?? "";
|
||||
|
||||
useSetPageTitle(t(`page_header.subject`) + "/" + t(`${SubjectName}`));
|
||||
useSetPage_title(
|
||||
t(`page_header.subject`) +
|
||||
"/" +
|
||||
t(`${SubjectName}`),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="TableWithHeader">
|
||||
<Suspense fallback={<Spin />}>
|
||||
<Suspense fallback={<Spin/>}>
|
||||
<header>
|
||||
<h6>
|
||||
{t("models.units")} {SubjectName}
|
||||
</h6>
|
||||
|
||||
</header>
|
||||
<Table />
|
||||
<AddModalForm />
|
||||
<EditModalForm />
|
||||
<DeleteModels />
|
||||
</Suspense>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import { useParams } from "react-router-dom";
|
|||
import { ParamsEnum } from "../../enums/params";
|
||||
|
||||
const App: React.FC = () => {
|
||||
const { subject_id } = useParams<ParamsEnum>();
|
||||
const response = useGetAllUnit({ subject_id: subject_id, pagination: true });
|
||||
console.log(response?.data?.data, "response?.data");
|
||||
const {subject_id} = useParams<ParamsEnum>()
|
||||
const response = useGetAllUnit({subject_id:subject_id, pagination: true});
|
||||
console.log(response?.data?.data,"response?.data");
|
||||
|
||||
return <DataTable response={response} useColumns={useColumns} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,23 +10,20 @@ import { useTranslation } from "react-i18next";
|
|||
import { ABILITIES_ENUM } from "../../enums/abilities";
|
||||
import { BsEyeFill } from "react-icons/bs";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
canAddUnit,
|
||||
canDeleteUnit,
|
||||
canEditUnit,
|
||||
canShowUnit,
|
||||
} from "../../utils/hasAbilityFn";
|
||||
import { canAddUnit, canDeleteUnit, canEditUnit, canShowUnit } from "../../utils/hasAbilityFn";
|
||||
|
||||
export const useColumns = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
|
||||
const { setObjectToEdit } = useObjectToEdit((state) => state);
|
||||
const navigate = useNavigate();
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handelShow = (record: any) => {
|
||||
navigate(`${ABILITIES_ENUM?.UNIT}/${record?.id}`);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handelDelete = (data: any) => {
|
||||
setObjectToEdit(data);
|
||||
handel_open_model(ModalEnum?.UNIT_DELETE);
|
||||
|
|
@ -89,7 +86,11 @@ export const useColumns = () => {
|
|||
color="#E0E0E0"
|
||||
>
|
||||
<span onClick={() => handleEdit(record)}>
|
||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
||||
<MdOutlineEdit
|
||||
size={22}
|
||||
style={{ color: "#A098AE" }}
|
||||
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
|
@ -101,12 +102,15 @@ export const useColumns = () => {
|
|||
/>
|
||||
)}
|
||||
{canShowUnit && (
|
||||
|
||||
<BsEyeFill
|
||||
onClick={() => handelShow(record)}
|
||||
size={22}
|
||||
style={{ color: "green" }}
|
||||
/>
|
||||
|
||||
)}
|
||||
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ const Form = () => {
|
|||
<Col>
|
||||
<ValidationField name="name" label="name" />
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const ModalForm: React.FC = () => {
|
|||
|
||||
const { mutate, isSuccess, isLoading } = useAddLesson();
|
||||
|
||||
const { unit_id } = useParams<ParamsEnum>();
|
||||
const {unit_id} = useParams<ParamsEnum>()
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
setIsOpen("");
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const ModalForm: React.FC = () => {
|
|||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const { mutate, isSuccess, isLoading } = useDeleteLesson();
|
||||
const { objectToEdit, setObjectToEdit } = useObjectToEdit((state) => state);
|
||||
const { objectToEdit,setObjectToEdit } = useObjectToEdit((state) => state);
|
||||
const queryClient = useQueryClient();
|
||||
const [t] = useTranslation();
|
||||
const handleSubmit = () => {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user