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="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/App/Logo.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="/App/Logo.png" />
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/App/Logo.png" />
|
<link
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/App/Logo.png" />
|
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" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "my-app",
|
"name": "my-app",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^5.3.7",
|
"@ant-design/icons": "^5.3.7",
|
||||||
|
|
|
||||||
7597
pnpm-lock.yaml
7597
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 |
15
src/App.tsx
15
src/App.tsx
|
|
@ -17,7 +17,7 @@ const App = () => {
|
||||||
const { changeLanguage } = useChangeLanguage();
|
const { changeLanguage } = useChangeLanguage();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { isAuthenticated } = useAuthState();
|
const { isAuthenticated } = useAuthState();
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
|
|
@ -26,14 +26,7 @@ const App = () => {
|
||||||
}, [isAuthenticated, navigate]);
|
}, [isAuthenticated, navigate]);
|
||||||
|
|
||||||
const renderRouteElement = (route: any) => (
|
const renderRouteElement = (route: any) => (
|
||||||
<Suspense
|
<Suspense fallback={<Layout> <SpinContainer/> </Layout>}>
|
||||||
fallback={
|
|
||||||
<Layout>
|
|
||||||
{" "}
|
|
||||||
<SpinContainer />{" "}
|
|
||||||
</Layout>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{route.header ? (
|
{route.header ? (
|
||||||
<Layout>{route.element}</Layout>
|
<Layout>{route.element}</Layout>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -46,7 +39,7 @@ const App = () => {
|
||||||
routes.map((route: TMenuItem) => {
|
routes.map((route: TMenuItem) => {
|
||||||
const useAbility = hasAbility(route.abilities, route.abilities_value);
|
const useAbility = hasAbility(route.abilities, route.abilities_value);
|
||||||
const tableHeader = t(`${route?.header}`);
|
const tableHeader = t(`${route?.header}`);
|
||||||
// useSetPageTitle(tableHeader,route?.path);
|
// useSetPage_title(tableHeader,route?.path);
|
||||||
|
|
||||||
if (useAbility) {
|
if (useAbility) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -87,7 +80,7 @@ const App = () => {
|
||||||
{CrudRoute.map((route) => {
|
{CrudRoute.map((route) => {
|
||||||
const useAbility = hasAbility(route.abilities, route.abilities_value);
|
const useAbility = hasAbility(route.abilities, route.abilities_value);
|
||||||
const tableHeader = t(`${route?.header}`);
|
const tableHeader = t(`${route?.header}`);
|
||||||
// useSetPageTitle(tableHeader,route?.path);
|
// useSetPage_title(tableHeader,route?.path);
|
||||||
|
|
||||||
if (!useAbility) {
|
if (!useAbility) {
|
||||||
return false;
|
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 = () => {
|
const SpinContainer = () => {
|
||||||
return (
|
return (
|
||||||
<div className="SpinContainer">
|
<div className='SpinContainer'>
|
||||||
<Spin/>
|
<Spin/>
|
||||||
</div>
|
</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 { DatePicker } from 'antd'
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
import { useObjectToEdit } from '../../../zustand/ObjectToEditState';
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import type { DatePickerProps } from "antd";
|
import type { DatePickerProps } from "antd";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { DateEnum } from "../../../enums/Date";
|
import { DateEnum } from '../../../enums/Date';
|
||||||
|
|
||||||
const CustomDatePicker = () => {
|
const CustomDatePicker = () => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
@ -24,6 +24,7 @@ const CustomDatePicker = () => {
|
||||||
};
|
};
|
||||||
const Today = new Date() as any;
|
const Today = new Date() as any;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="CustomDatePicker">
|
<div className="CustomDatePicker">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
|
|
@ -33,7 +34,7 @@ const CustomDatePicker = () => {
|
||||||
format={DateEnum?.FORMATE}
|
format={DateEnum?.FORMATE}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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,7 +31,7 @@ const components: { [key: string]: React.FC<any> } = {
|
||||||
MaltyFile: MaltyFile,
|
MaltyFile: MaltyFile,
|
||||||
Checkbox: CheckboxField,
|
Checkbox: CheckboxField,
|
||||||
NumberFormate: NumberFormate,
|
NumberFormate: NumberFormate,
|
||||||
Number: NumberField,
|
Number:NumberField
|
||||||
};
|
};
|
||||||
|
|
||||||
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { Checkbox, Form } from "antd";
|
import { Checkbox, Form } from "antd";
|
||||||
import { getNestedValue } from "../utils/getNestedValue";
|
import {getNestedValue} from '../utils/getNestedValue'
|
||||||
const CheckboxField = ({
|
const CheckboxField = ({
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ const Default = ({
|
||||||
<label htmlFor={name} className="text">
|
<label htmlFor={name} className="text">
|
||||||
{label2}
|
{label2}
|
||||||
</label>
|
</label>
|
||||||
) : no_label ? (
|
)
|
||||||
|
|
||||||
|
:no_label ? (
|
||||||
<label htmlFor={name} className="text">
|
<label htmlFor={name} className="text">
|
||||||
<span>empty</span>
|
<span>empty</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -56,6 +58,7 @@ const Default = ({
|
||||||
name={name}
|
name={name}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
size="large"
|
size="large"
|
||||||
|
|
||||||
{...(type === "number" && { min: 0 })}
|
{...(type === "number" && { min: 0 })}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -15,24 +15,24 @@ const File = ({
|
||||||
const { formik, t, isError,errorMsg } = useFormField(name, props);
|
const { formik, t, isError,errorMsg } = useFormField(name, props);
|
||||||
let imageUrl = formik?.values?.[name] ?? null;
|
let imageUrl = formik?.values?.[name] ?? null;
|
||||||
console.log(imageUrl);
|
console.log(imageUrl);
|
||||||
console.log(typeof imageUrl === "string");
|
console.log(typeof imageUrl === 'string');
|
||||||
|
|
||||||
const fileList: UploadFile[] = useMemo(() => {
|
const fileList: UploadFile[] = useMemo(() => {
|
||||||
if (!imageUrl) return [];
|
if (!imageUrl) return [];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
typeof imageUrl === "string"
|
typeof imageUrl === 'string'
|
||||||
? {
|
? {
|
||||||
uid: "-1",
|
uid: '-1',
|
||||||
name: "uploaded-image",
|
name: 'uploaded-image',
|
||||||
status: "done",
|
status: 'done',
|
||||||
url: imageUrl,
|
url: imageUrl,
|
||||||
thumbUrl: imageUrl,
|
thumbUrl: imageUrl,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
uid: imageUrl.uid || "-1",
|
uid: imageUrl.uid || '-1',
|
||||||
name: imageUrl.name || "uploaded-image",
|
name: imageUrl.name || 'uploaded-image',
|
||||||
status: "done",
|
status: 'done',
|
||||||
originFileObj: imageUrl,
|
originFileObj: imageUrl,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -70,6 +70,7 @@ const File = ({
|
||||||
icon={<UploadOutlined />}
|
icon={<UploadOutlined />}
|
||||||
>
|
>
|
||||||
{placholder ?? t("input.Click_to_upload_the_image")}
|
{placholder ?? t("input.Click_to_upload_the_image")}
|
||||||
|
|
||||||
</Button>
|
</Button>
|
||||||
<div className="Error_color"> {isError ? "required" : ""}</div>
|
<div className="Error_color"> {isError ? "required" : ""}</div>
|
||||||
{errorMsg}
|
{errorMsg}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ const NumberField = ({
|
||||||
const handleChange = (
|
const handleChange = (
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
) => {
|
) => {
|
||||||
console.log("Change:", e);
|
console.log('Change:', e);
|
||||||
formik.setFieldValue(name, e);
|
formik.setFieldValue(name, e);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ const NumberFormate = ({
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
size="large"
|
size="large"
|
||||||
|
|
||||||
|
|
||||||
// onChange={onChange ? onChange : handleChange}
|
// onChange={onChange ? onChange : handleChange}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const SearchField = ({
|
||||||
props,
|
props,
|
||||||
no_label,
|
no_label,
|
||||||
label_icon,
|
label_icon,
|
||||||
isLoading,
|
isLoading
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||||
|
|
@ -79,6 +79,7 @@ const SearchField = ({
|
||||||
showSearch
|
showSearch
|
||||||
optionFilterProp="label"
|
optionFilterProp="label"
|
||||||
notFoundContent={isLoading ? <Spin/> : "لا يوجد" }
|
notFoundContent={isLoading ? <Spin/> : "لا يوجد" }
|
||||||
|
|
||||||
onSearch={SearchHandleChange}
|
onSearch={SearchHandleChange}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const TextField = ({
|
||||||
props,
|
props,
|
||||||
no_label,
|
no_label,
|
||||||
label_icon,
|
label_icon,
|
||||||
className,
|
className
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const { formik, isError, errorMsg, t } = useFormField(name, props);
|
const { formik, isError, errorMsg, t } = useFormField(name, props);
|
||||||
const TextFilehandleChange = (
|
const TextFilehandleChange = (
|
||||||
|
|
@ -58,6 +58,8 @@ const TextField = ({
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
onChange={onChange || TextFilehandleChange}
|
onChange={onChange || TextFilehandleChange}
|
||||||
style={{height:120}}
|
style={{height:120}}
|
||||||
|
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
}
|
}
|
||||||
.Checkboxs {
|
.Checkboxs {
|
||||||
padding: 4%;
|
padding: 4%;
|
||||||
|
|
||||||
}
|
}
|
||||||
.ant-checkbox-wrapper{
|
.ant-checkbox-wrapper{
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
|
@ -202,6 +203,7 @@ input:-webkit-autofill:hover {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.ant-checkbox-wrapper{
|
.ant-checkbox-wrapper{
|
||||||
margin-top: 25px !important;
|
margin-top: 25px !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
|
||||||
|
|
||||||
export function getNestedValue(obj:any, path:any) {
|
export function getNestedValue(obj:any, path:any) {
|
||||||
return path
|
return path
|
||||||
.replace(/\?.\[|\]\[|\]\.?/g, ".") // Replace question mark and square brackets
|
.replace(/\?.\[|\]\[|\]\.?/g, '.') // Replace question mark and square brackets
|
||||||
.split(".") // Split by dots
|
.split('.') // Split by dots
|
||||||
.filter(Boolean) // Remove empty strings
|
.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[];
|
option: any[];
|
||||||
isMulti?: boolean;
|
isMulti?: boolean;
|
||||||
searchBy: string;
|
searchBy: string;
|
||||||
isLoading?: any;
|
isLoading?:any
|
||||||
}
|
}
|
||||||
export interface ValidationFieldPropsDataRange {
|
export interface ValidationFieldPropsDataRange {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -161,26 +161,31 @@ export interface ValidationFieldPropstext {
|
||||||
[key: string]: any; // Index signature to allow any additional props
|
[key: string]: any; // Index signature to allow any additional props
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///// new
|
///// new
|
||||||
export interface BaseField {
|
export interface BaseField {
|
||||||
name: string;
|
name: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
placeholder?: 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
|
export interface ValidationFieldPropsInput
|
||||||
extends Omit<InputProps, OmitBaseType>,
|
extends Omit<InputProps, OmitBaseType>,
|
||||||
BaseField {
|
BaseField {
|
||||||
type: "text" | "number" | "password" | "email" | "Number";
|
type: 'text' | 'number' | 'password' | 'email' | "Number";
|
||||||
isDisabled?: boolean;
|
isDisabled?:boolean
|
||||||
no_label?: string;
|
no_label?:string
|
||||||
label_icon?: string;
|
label_icon?:string
|
||||||
label2?: string;
|
label2?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type ValidationFieldProps =
|
export type ValidationFieldProps =
|
||||||
| ValidationFieldPropsInput
|
| ValidationFieldPropsInput
|
||||||
| ValidationFieldPropsSelect
|
| 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 { useFormikContext } from 'formik';
|
||||||
import React from "react";
|
import React from 'react';
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from 'react-i18next';
|
||||||
import { GoArrowSwitch } from "react-icons/go";
|
import { GoArrowSwitch } from 'react-icons/go';
|
||||||
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
|
import { useObjectToEdit } from '../../zustand/ObjectToEditState';
|
||||||
import { QUESTION_OBJECT_KEY } from "../../config/AppKey";
|
import { QUESTION_OBJECT_KEY } from '../../config/AppKey';
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const { values, setFieldValue,setValues } = useFormikContext<any>();
|
const { values, setFieldValue,setValues } = useFormikContext<any>();
|
||||||
const { isBseQuestion, setIsBseQuestion } = useObjectToEdit();
|
const {isBseQuestion,setIsBseQuestion} = useObjectToEdit()
|
||||||
const { setSavedQuestionData } = useObjectToEdit();
|
const {setSavedQuestionData} = useObjectToEdit()
|
||||||
const handleChange = () => {
|
const handleChange = () => {
|
||||||
setSavedQuestionData(null);
|
setSavedQuestionData(null)
|
||||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
localStorage.removeItem(QUESTION_OBJECT_KEY)
|
||||||
if (isBseQuestion) {
|
if (isBseQuestion) {
|
||||||
setIsBseQuestion(false);
|
setIsBseQuestion(false)
|
||||||
setValues(null);
|
setValues(null)
|
||||||
setFieldValue("isBase", 0);
|
setFieldValue("isBase",0)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
setIsBseQuestion(true);
|
|
||||||
setValues(null);
|
setIsBseQuestion(true)
|
||||||
setFieldValue("isBase", 1);
|
setValues(null)
|
||||||
|
setFieldValue("isBase",1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -31,9 +33,7 @@ const Header = () => {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<GoArrowSwitch onClick={handleChange} className="m-2" />
|
<GoArrowSwitch onClick={handleChange} className="m-2" />
|
||||||
{isBseQuestion || values?.isBase === 1
|
{isBseQuestion || values?.isBase === 1 ? t("header.malty_exercise") :t("header.exercise") }
|
||||||
? t("header.malty_exercise")
|
|
||||||
: t("header.exercise")}
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</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") => {
|
export const useAddKeyToData = (dataSource: any, identifier = "id") => {
|
||||||
if (!dataSource || !Array.isArray(dataSource)) {
|
if (!dataSource || !Array.isArray(dataSource)) {
|
||||||
return [];
|
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 = (
|
const useKeyPress = (targetKey: string, modifierKey: ModifierKey, callback:any) => {
|
||||||
targetKey: string,
|
|
||||||
modifierKey: ModifierKey,
|
|
||||||
callback: any,
|
|
||||||
) => {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
if (event[modifierKey] && event.key === targetKey) {
|
if (event[modifierKey] && event.key === targetKey) {
|
||||||
|
|
@ -15,10 +11,10 @@ const useKeyPress = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("keydown", handleKeyDown);
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("keydown", handleKeyDown);
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
};
|
};
|
||||||
}, [targetKey, modifierKey, callback]);
|
}, [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 { useEffect } from 'react';
|
||||||
import { setLocalStorageQuestions } from "../utils/setLocalStorageQuestions";
|
import { setLocalStorageQuestions } from '../utils/setLocalStorageQuestions';
|
||||||
|
import { setLocalStorageBaseQuestions } from '../utils/setLocalStorageBaseQuestions';
|
||||||
|
|
||||||
const useSaveOnDisconnect = (
|
const useSaveOnDisconnect = (noChange: boolean, QUESTION_OBJECT_KEY: string, SavedQuestionData: any) => {
|
||||||
noChange: boolean,
|
|
||||||
QUESTION_OBJECT_KEY: string,
|
|
||||||
SavedQuestionData: any,
|
|
||||||
) => {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
||||||
console.log("disconnect");
|
console.log("disconnect");
|
||||||
if (noChange) {
|
if (noChange) {
|
||||||
if(SavedQuestionData?.isBase ===1){
|
if(SavedQuestionData?.isBase ===1){
|
||||||
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -23,20 +22,22 @@ const useSaveOnDisconnect = (
|
||||||
if (noChange) {
|
if (noChange) {
|
||||||
if(SavedQuestionData?.isBase ===1){
|
if(SavedQuestionData?.isBase ===1){
|
||||||
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
setLocalStorageQuestions(QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add event listeners
|
// Add event listeners
|
||||||
window.addEventListener("beforeunload", handleBeforeUnload);
|
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||||
window.addEventListener("offline", handleOffline);
|
window.addEventListener('offline', handleOffline);
|
||||||
|
|
||||||
// Cleanup function
|
// Cleanup function
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("beforeunload", handleBeforeUnload);
|
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||||
window.removeEventListener("offline", handleOffline);
|
window.removeEventListener('offline', handleOffline);
|
||||||
};
|
};
|
||||||
}, [noChange, QUESTION_OBJECT_KEY, SavedQuestionData]); // Add dependencies to the hook
|
}, [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 { useEffect } from "react";
|
||||||
import { usePage_titleState } from "../zustand/PageTitleState";
|
import { usePage_titleState } from "../zustand/PageTitleState";
|
||||||
|
|
||||||
const useSetPageTitle = (title: any) => {
|
const useSetPage_title = (title: any) => {
|
||||||
const setPage_title = usePage_titleState((state) => state.setPage_title);
|
const setPage_title = usePage_titleState((state) => state.setPage_title);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -9,4 +9,4 @@ const useSetPageTitle = (title: any) => {
|
||||||
}, [title, setPage_title]);
|
}, [title, setPage_title]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useSetPageTitle;
|
export default useSetPage_title;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { AppRoutes, CrudRoutes } from "../Routes";
|
import { AppRoutes, CrudRoutes } from "../Routes";
|
||||||
import { PROJECT_NAME } from "../config/AppKey";
|
import { PROJECT_NAME } from "../config/AppKey";
|
||||||
|
|
||||||
export const useGetTitleFromRoute = (path: any) => {
|
export const usegetTitleFromRoute = (path: any) => {
|
||||||
if (AppRoutes[path]) {
|
if (AppRoutes[path]) {
|
||||||
return `${PROJECT_NAME} | ${AppRoutes[path]}`;
|
return `${PROJECT_NAME} | ${AppRoutes[path]}`;
|
||||||
} else if (CrudRoutes[path]) {
|
} else if (CrudRoutes[path]) {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ const FormikFormModel: React.FC<FormikFormProps> = ({
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
|
||||||
>
|
>
|
||||||
{(formik) => {
|
{(formik) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -42,6 +43,7 @@ const FormikFormModel: React.FC<FormikFormProps> = ({
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
|
|
||||||
return <Form className="w-100">{children}</Form>;
|
return <Form className="w-100">{children}</Form>;
|
||||||
}}
|
}}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
|
||||||
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../../enums/abilities";
|
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../../enums/abilities";
|
||||||
import { hasAbility } from "../../utils/hasAbility";
|
import { hasAbility } from "../../utils/hasAbility";
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SearchField,
|
SearchField,
|
||||||
useModalHandler,
|
useModalHandler,
|
||||||
|
|
@ -13,5 +14,5 @@ export {
|
||||||
useTranslation,
|
useTranslation,
|
||||||
ABILITIES_ENUM,
|
ABILITIES_ENUM,
|
||||||
ABILITIES_VALUES_ENUM,
|
ABILITIES_VALUES_ENUM,
|
||||||
hasAbility,
|
hasAbility
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useGetTitleFromRoute } from "../../Hooks/useGetTitleFromRoute";
|
import { usegetTitleFromRoute } from "../../Hooks/usegetTitleFromRoute";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import NavBar from "./NavBar";
|
import NavBar from "./NavBar";
|
||||||
|
|
@ -18,7 +18,7 @@ const Layout = ({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{useGetTitleFromRoute(location.pathname)}</title>
|
<title>{usegetTitleFromRoute(location.pathname)}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<div className="Layout">
|
<div className="Layout">
|
||||||
<main className={`${className} Layout_Body`}>
|
<main className={`${className} Layout_Body`}>
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,7 @@ const SideBar = () => {
|
||||||
<div className="side_bar">
|
<div className="side_bar">
|
||||||
<h1>
|
<h1>
|
||||||
{/* {t("sidebar.dashboard")} */}
|
{/* {t("sidebar.dashboard")} */}
|
||||||
{branch_name}{" "}
|
{branch_name} </h1>
|
||||||
</h1>
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<div className="side_bar_links">
|
<div className="side_bar_links">
|
||||||
{menuItems.map((item, index) => {
|
{menuItems.map((item, index) => {
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,13 @@ type FormFieldType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const FormField = ({ isLoading }: FormFieldType) => {
|
const FormField = ({ isLoading }: FormFieldType) => {
|
||||||
|
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Form className="AuthForm">
|
<Form className="AuthForm">
|
||||||
<Image src="../App/Logo.png" />
|
<Image src="../App/Logo.png" />
|
||||||
|
|
||||||
|
|
||||||
<div className="AuthInput">
|
<div className="AuthInput">
|
||||||
<label className="form-label" htmlFor="username">
|
<label className="form-label" htmlFor="username">
|
||||||
{t("input.Username")}
|
{t("input.Username")}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ const LoginForm = () => {
|
||||||
const { mutate, isLoading, isSuccess, data } = useLoginAdmin();
|
const { mutate, isLoading, isSuccess, data } = useLoginAdmin();
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const handelSubmit = (values: FormValues) => {
|
const handelSubmit = (values: FormValues) => {
|
||||||
|
|
||||||
mutate(values);
|
mutate(values);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@ import React from "react";
|
||||||
import Image from "../../Components/Ui/Image";
|
import Image from "../../Components/Ui/Image";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ABILITIES_ENUM } from "../../enums/abilities";
|
import { ABILITIES_ENUM } from "../../enums/abilities";
|
||||||
import useSetPageTitle from "../../Hooks/useSetPageTitle";
|
import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||||
|
|
||||||
const Dummy = () => {
|
const Dummy = () => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
useSetPageTitle(`${t(ABILITIES_ENUM?.MAIN_PAGE)} / ${t("dashboard")}`);
|
useSetPage_title(`${t(ABILITIES_ENUM?.MAIN_PAGE)} / ${t("dashboard")}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="DummyHomePage">
|
<div className="DammyHomePage">
|
||||||
<Image src="/App/SyriaLogo.webp" />
|
<Image src="/App/SyriaLogo.webp" />
|
||||||
</div>
|
</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;
|
||||||
|
|
@ -25,6 +25,8 @@ const Form = () => {
|
||||||
</Col>
|
</Col>
|
||||||
<div>
|
<div>
|
||||||
<DynamicTags/>
|
<DynamicTags/>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,9 @@ const ModalForm: React.FC = () => {
|
||||||
}, [setIsOpen, isSuccess]);
|
}, [setIsOpen, isSuccess]);
|
||||||
|
|
||||||
const handleSubmit = (values: any) => {
|
const handleSubmit = (values: any) => {
|
||||||
|
|
||||||
mutate({
|
mutate({
|
||||||
...values,
|
...values
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { useEffect } from "react";
|
||||||
import DynamicTags from "../synonyms/DynamicTags";
|
import DynamicTags from "../synonyms/DynamicTags";
|
||||||
|
|
||||||
const Form = () => {
|
const Form = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="w-100">
|
<Row className="w-100">
|
||||||
<Col>
|
<Col>
|
||||||
|
|
@ -13,6 +14,8 @@ const Form = () => {
|
||||||
</Col>
|
</Col>
|
||||||
<div>
|
<div>
|
||||||
<DynamicTags/>
|
<DynamicTags/>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ import ModelButtons from "../../../Components/models/ModelButtons";
|
||||||
|
|
||||||
const ModalForm: React.FC = () => {
|
const ModalForm: React.FC = () => {
|
||||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||||
const { objectToEdit, setObjectToEdit } = useObjectToEdit((state) => state);
|
const { objectToEdit, setObjectToEdit } = useObjectToEdit(
|
||||||
|
(state) => state,
|
||||||
|
);
|
||||||
const { mutate, isSuccess, isLoading } = useUpdateTag();
|
const { mutate, isSuccess, isLoading } = useUpdateTag();
|
||||||
// console.log(objectToEdit,"objectToEdit");
|
// console.log(objectToEdit,"objectToEdit");
|
||||||
const handleSubmit = (values: any) => {
|
const handleSubmit = (values: any) => {
|
||||||
|
|
@ -20,7 +22,7 @@ const ModalForm: React.FC = () => {
|
||||||
// phone_number: values?.number
|
// phone_number: values?.number
|
||||||
// });
|
// });
|
||||||
mutate({
|
mutate({
|
||||||
...values,
|
...values
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
|
|
@ -56,6 +58,7 @@ const ModalForm: React.FC = () => {
|
||||||
<main className="main_modal w-100">
|
<main className="main_modal w-100">
|
||||||
<ModelBody />
|
<ModelBody />
|
||||||
<ModelButtons isLoading={isLoading} />
|
<ModelButtons isLoading={isLoading} />
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
</FormikForm>
|
</FormikForm>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -3,33 +3,32 @@ import { FaPlus } from "react-icons/fa";
|
||||||
import useModalHandler from "../../utils/useModalHandler";
|
import useModalHandler from "../../utils/useModalHandler";
|
||||||
import { ModalEnum } from "../../enums/Model";
|
import { ModalEnum } from "../../enums/Model";
|
||||||
|
|
||||||
// import useSetPageTitle from "../../Hooks/useSetPageTitle";
|
// import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { lazy, Suspense } from "react";
|
import { lazy, Suspense } from 'react';
|
||||||
import { Spin } from "antd";
|
import { Spin } from "antd";
|
||||||
import { canAddTags } from "../../utils/hasAbilityFn";
|
import { canAddTags } from "../../utils/hasAbilityFn";
|
||||||
import useSetPageTitle from "../../Hooks/useSetPageTitle";
|
import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||||
const Table = lazy(() => import("./Table"));
|
const Table = lazy(() => import('./Table'));
|
||||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
const AddModalForm = lazy(() => import('./Model/AddModel'));
|
||||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
const EditModalForm = lazy(() => import('./Model/EditModel'));
|
||||||
const DeleteModalForm = lazy(() => import("./Model/Delete"));
|
const DeleteModalForm = lazy(() => import('./Model/Delete'));
|
||||||
const SearchField = lazy(
|
const SearchField = lazy(() => import('../../Components/DataTable/SearchField'));
|
||||||
() => import("../../Components/DataTable/SearchField"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const TableHeader = () => {
|
const TableHeader = () => {
|
||||||
const { handel_open_model } = useModalHandler();
|
const { handel_open_model } = useModalHandler();
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
useSetPageTitle(t(`page_header.tags`));
|
useSetPage_title(
|
||||||
|
t(`page_header.tags`),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="TableWithHeader">
|
<div className="TableWithHeader">
|
||||||
|
|
||||||
<Suspense fallback={<Spin/>}>
|
<Suspense fallback={<Spin/>}>
|
||||||
<header className="d-flex justify-content-between">
|
<header className="d-flex justify-content-between">
|
||||||
<SearchField
|
<SearchField searchBy="name" placeholder={t("practical.search_here")} />
|
||||||
searchBy="name"
|
|
||||||
placeholder={t("practical.search_here")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{canAddTags && (
|
{canAddTags && (
|
||||||
<div className="Selects">
|
<div className="Selects">
|
||||||
|
|
@ -43,6 +42,7 @@ const TableHeader = () => {
|
||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
||||||
<Table />
|
<Table />
|
||||||
<DeleteModalForm />
|
<DeleteModalForm />
|
||||||
<AddModalForm />
|
<AddModalForm />
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,5 @@ export {
|
||||||
AddModalForm,
|
AddModalForm,
|
||||||
EditModalForm,
|
EditModalForm,
|
||||||
DeleteModalForm,
|
DeleteModalForm,
|
||||||
FaPlus,
|
FaPlus
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import { useFormikContext } from "formik";
|
import { useFormikContext } from 'formik'
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FaCirclePlus } from "react-icons/fa6";
|
import { FaCirclePlus } from 'react-icons/fa6'
|
||||||
import Tag from "./Tag";
|
import Tag from './Tag'
|
||||||
|
|
||||||
const DynamicTags = () => {
|
const DynamicTags = () => {
|
||||||
const formik = useFormikContext<any>();
|
|
||||||
const [t] = useTranslation();
|
const formik = useFormikContext<any>()
|
||||||
|
const [t] = useTranslation()
|
||||||
|
|
||||||
|
|
||||||
console.log(formik?.values?.synonyms);
|
console.log(formik?.values?.synonyms);
|
||||||
|
|
||||||
|
|
@ -15,42 +17,54 @@ const DynamicTags = () => {
|
||||||
const lastElement = formik?.values?.synonyms[length - 1];
|
const lastElement = formik?.values?.synonyms[length - 1];
|
||||||
|
|
||||||
if(lastElement !== ""){
|
if(lastElement !== ""){
|
||||||
formik.setFieldValue("synonyms", [
|
formik.setFieldValue('synonyms', [...(formik?.values as any)?.synonyms as any[],""])
|
||||||
...((formik?.values as any)?.synonyms as any[]),
|
|
||||||
"",
|
|
||||||
]);
|
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
// console.log(formik?.values);
|
// console.log(formik?.values);
|
||||||
// console.log(currentTag);
|
// console.log(currentTag);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="DynamicTags">
|
<div className='DynamicTags'>
|
||||||
{formik?.values?.synonyms?.length < 1 && (
|
{formik?.values?.synonyms?.length < 1 &&
|
||||||
|
|
||||||
<p className="add_new_button" >
|
<p className="add_new_button" >
|
||||||
<FaCirclePlus size={23} onClick={handleAddChoice} />{" "}
|
<FaCirclePlus size={23} onClick={handleAddChoice} /> {t("header.add_synonyms")}
|
||||||
{t("header.add_synonyms")}
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
}
|
||||||
|
|
||||||
<div className="tag_container">
|
<div className="tag_container">
|
||||||
<div className="tags">
|
<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>
|
</div>
|
||||||
|
|
||||||
{formik?.values?.synonyms?.length > 0 && (
|
{formik?.values?.synonyms?.length > 0 &&
|
||||||
|
|
||||||
<p className="add_new_button" >
|
<p className="add_new_button" >
|
||||||
<FaCirclePlus onClick={handleAddChoice} size={20} />
|
<FaCirclePlus onClick={handleAddChoice} size={20} />
|
||||||
</p>
|
</p>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DynamicTags;
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DynamicTags
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { useFormikContext } from "formik";
|
import { useFormikContext } from 'formik';
|
||||||
import React, { useRef, useEffect } from "react";
|
import React, { useRef, useEffect } from 'react';
|
||||||
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
import { useObjectToEdit } from '../../../zustand/ObjectToEditState';
|
||||||
import { FaTrash } from "react-icons/fa";
|
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 inputRef = useRef<HTMLInputElement>(null);
|
||||||
const formik = useFormikContext<any>();
|
const formik = useFormikContext<any>();
|
||||||
const { setTagsSearch ,setCurrentTag} = useObjectToEdit();
|
const { setTagsSearch ,setCurrentTag} = useObjectToEdit();
|
||||||
|
|
@ -16,11 +16,13 @@ const Tag = ({ data, index }: { data: any; index: number }) => {
|
||||||
console.log(formik?.values?.synonyms);
|
console.log(formik?.values?.synonyms);
|
||||||
console.log(index);
|
console.log(index);
|
||||||
|
|
||||||
|
|
||||||
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
// console.log(e.target.value);
|
// console.log(e.target.value);
|
||||||
formik.setFieldValue(`synonyms[${index}]`, e.target.value);
|
formik.setFieldValue(`synonyms[${index}]`, e.target.value);
|
||||||
setTagsSearch(e.target.value);
|
setTagsSearch(e.target.value)
|
||||||
setCurrentTag(index);
|
setCurrentTag(index)
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputBlur = () => {
|
const handleInputBlur = () => {
|
||||||
|
|
@ -36,14 +38,14 @@ const Tag = ({ data, index }: { data: any; index: number }) => {
|
||||||
console.log(currentTags); // Log the updated tags array
|
console.log(currentTags); // Log the updated tags array
|
||||||
|
|
||||||
// Update formik field value with 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
|
// Reset search state if needed
|
||||||
setTagsSearch(null);
|
setTagsSearch(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tag">
|
<div className='tag'>
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className="tagInput"
|
className="tagInput"
|
||||||
|
|
@ -51,6 +53,7 @@ const Tag = ({ data, index }: { data: any; index: number }) => {
|
||||||
value={formik?.values?.synonyms[index]}
|
value={formik?.values?.synonyms[index]}
|
||||||
onChange={handleEditInputChange}
|
onChange={handleEditInputChange}
|
||||||
onBlur={handleInputBlur}
|
onBlur={handleInputBlur}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<FaTrash onClick={handleDeleteChoice}/>
|
<FaTrash onClick={handleDeleteChoice}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -58,3 +61,6 @@ const Tag = ({ data, index }: { data: any; index: number }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Tag;
|
export default Tag;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ export const useColumns = () => {
|
||||||
};
|
};
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const columns: TableColumnsType<tags> = [
|
const columns: TableColumnsType<tags> = [
|
||||||
{
|
{
|
||||||
title: t("columns.id"),
|
title: t("columns.id"),
|
||||||
|
|
@ -55,10 +57,16 @@ export const useColumns = () => {
|
||||||
placement="top"
|
placement="top"
|
||||||
title={t("practical.edit")}
|
title={t("practical.edit")}
|
||||||
color="#E0E0E0"
|
color="#E0E0E0"
|
||||||
|
|
||||||
>
|
>
|
||||||
<span onClick={() => handleEdit(record)}>
|
<span onClick={() => handleEdit(record)}>
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
<MdOutlineEdit
|
||||||
|
size={22}
|
||||||
|
style={{ color: "#A098AE" }}
|
||||||
|
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { lazy, Suspense } from "react";
|
import { lazy, Suspense } from 'react';
|
||||||
import { Spin } from "antd";
|
import { Spin } from "antd";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { ParamsEnum } from "../../enums/params";
|
import { ParamsEnum } from "../../enums/params";
|
||||||
import { useGetAllSubject } from "../../api/subject";
|
import { useGetAllSubject } from "../../api/subject";
|
||||||
import useSetPageTitle from "../../Hooks/useSetPageTitle";
|
import useSetPage_title from "../../Hooks/useSetPageTitle";
|
||||||
|
|
||||||
const Table = lazy(() => import("./Table"));
|
const Table = lazy(() => import('./Table'));
|
||||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
const AddModalForm = lazy(() => import('./Model/AddModel'));
|
||||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
const EditModalForm = lazy(() => import('./Model/EditModel'));
|
||||||
const DeleteModels = lazy(() => import("./Model/Delete"));
|
const DeleteModels = lazy(() => import('./Model/Delete'));
|
||||||
|
|
||||||
const TableHeader = () => {
|
const TableHeader = () => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
@ -17,12 +17,17 @@ const TableHeader = () => {
|
||||||
const { subject_id} = useParams<ParamsEnum>();
|
const { subject_id} = useParams<ParamsEnum>();
|
||||||
|
|
||||||
const { data: Subject } = useGetAllSubject({
|
const { data: Subject } = useGetAllSubject({
|
||||||
show: subject_id,
|
show:subject_id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const SubjectName = Subject?.data?.name ?? "";
|
const SubjectName = Subject?.data?.name ?? "";
|
||||||
|
|
||||||
useSetPageTitle(t(`page_header.subject`) + "/" + t(`${SubjectName}`));
|
useSetPage_title(
|
||||||
|
t(`page_header.subject`) +
|
||||||
|
"/" +
|
||||||
|
t(`${SubjectName}`),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="TableWithHeader">
|
<div className="TableWithHeader">
|
||||||
|
|
@ -31,12 +36,14 @@ const TableHeader = () => {
|
||||||
<h6>
|
<h6>
|
||||||
{t("models.units")} {SubjectName}
|
{t("models.units")} {SubjectName}
|
||||||
</h6>
|
</h6>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
<Table />
|
<Table />
|
||||||
<AddModalForm />
|
<AddModalForm />
|
||||||
<EditModalForm />
|
<EditModalForm />
|
||||||
<DeleteModels />
|
<DeleteModels />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { useParams } from "react-router-dom";
|
||||||
import { ParamsEnum } from "../../enums/params";
|
import { ParamsEnum } from "../../enums/params";
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
const { subject_id } = useParams<ParamsEnum>();
|
const {subject_id} = useParams<ParamsEnum>()
|
||||||
const response = useGetAllUnit({subject_id:subject_id, pagination: true});
|
const response = useGetAllUnit({subject_id:subject_id, pagination: true});
|
||||||
console.log(response?.data?.data,"response?.data");
|
console.log(response?.data?.data,"response?.data");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,23 +10,20 @@ import { useTranslation } from "react-i18next";
|
||||||
import { ABILITIES_ENUM } from "../../enums/abilities";
|
import { ABILITIES_ENUM } from "../../enums/abilities";
|
||||||
import { BsEyeFill } from "react-icons/bs";
|
import { BsEyeFill } from "react-icons/bs";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import {
|
import { canAddUnit, canDeleteUnit, canEditUnit, canShowUnit } from "../../utils/hasAbilityFn";
|
||||||
canAddUnit,
|
|
||||||
canDeleteUnit,
|
|
||||||
canEditUnit,
|
|
||||||
canShowUnit,
|
|
||||||
} from "../../utils/hasAbilityFn";
|
|
||||||
|
|
||||||
export const useColumns = () => {
|
export const useColumns = () => {
|
||||||
const { handel_open_model } = useModalHandler();
|
const { handel_open_model } = useModalHandler();
|
||||||
|
|
||||||
const { setObjectToEdit } = useObjectToEdit((state) => state);
|
const { setObjectToEdit } = useObjectToEdit((state) => state);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const handelShow = (record: any) => {
|
const handelShow = (record: any) => {
|
||||||
navigate(`${ABILITIES_ENUM?.UNIT}/${record?.id}`);
|
navigate(`${ABILITIES_ENUM?.UNIT}/${record?.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handelDelete = (data: any) => {
|
const handelDelete = (data: any) => {
|
||||||
setObjectToEdit(data);
|
setObjectToEdit(data);
|
||||||
handel_open_model(ModalEnum?.UNIT_DELETE);
|
handel_open_model(ModalEnum?.UNIT_DELETE);
|
||||||
|
|
@ -89,7 +86,11 @@ export const useColumns = () => {
|
||||||
color="#E0E0E0"
|
color="#E0E0E0"
|
||||||
>
|
>
|
||||||
<span onClick={() => handleEdit(record)}>
|
<span onClick={() => handleEdit(record)}>
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
<MdOutlineEdit
|
||||||
|
size={22}
|
||||||
|
style={{ color: "#A098AE" }}
|
||||||
|
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|
@ -101,12 +102,15 @@ export const useColumns = () => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{canShowUnit && (
|
{canShowUnit && (
|
||||||
|
|
||||||
<BsEyeFill
|
<BsEyeFill
|
||||||
onClick={() => handelShow(record)}
|
onClick={() => handelShow(record)}
|
||||||
size={22}
|
size={22}
|
||||||
style={{ color: "green" }}
|
style={{ color: "green" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ const Form = () => {
|
||||||
<Col>
|
<Col>
|
||||||
<ValidationField name="name" label="name" />
|
<ValidationField name="name" label="name" />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ const ModalForm: React.FC = () => {
|
||||||
|
|
||||||
const { mutate, isSuccess, isLoading } = useAddLesson();
|
const { mutate, isSuccess, isLoading } = useAddLesson();
|
||||||
|
|
||||||
const { unit_id } = useParams<ParamsEnum>();
|
const {unit_id} = useParams<ParamsEnum>()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
setIsOpen("");
|
setIsOpen("");
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ const Form = () => {
|
||||||
<Col>
|
<Col>
|
||||||
<ValidationField name="name" label="name" />
|
<ValidationField name="name" label="name" />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user