Merge branch 'dev' of into dev

This commit is contained in:
Moaz Dawalibi 2024-08-12 16:11:50 +03:00
commit 00dfd6c88c
143 changed files with 8823 additions and 7480 deletions

File diff suppressed because one or more lines are too long

View File

@ -5,18 +5,8 @@
<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 <link rel="icon" type="image/png" sizes="32x32" href="/App/Logo.png" />
rel="icon" <link rel="icon" type="image/png" sizes="16x16" href="/App/Logo.png" />
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"

View File

@ -1,66 +1,29 @@
{ {
"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",
"@types/draft-js": "^0.11.18",
"@types/katex": "^0.16.7",
"@types/mathjax": "^0.0.40",
"@types/node": "^20.14.0",
"@types/react-draft-wysiwyg": "^1.13.8",
"@types/react-helmet": "^6.1.11",
"algebra.js": "^0.2.6",
"antd": "^5.17.4", "antd": "^5.17.4",
"apexcharts": "^3.49.1",
"axios": "^1.7.2", "axios": "^1.7.2",
"better-react-mathjax": "^2.0.3",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"draft-js": "^0.11.7",
"draft-js-latex-plugin": "^0.1.2",
"equation-resolver": "^1.0.0",
"formik": "^2.4.6", "formik": "^2.4.6",
"html2canvas": "^1.4.1", "html-to-image": "^1.11.11",
"i18next": "^23.11.5", "i18next": "^23.11.5",
"jspdf": "^2.5.1",
"katex": "^0.16.11",
"lodash.debounce": "^4.0.8",
"mammoth": "^1.8.0",
"mathjax": "^3.2.2",
"mathjax-react": "^2.0.1",
"mathjax3-react": "^1.2.0",
"mathquill": "0.10.1-a",
"nmath": "^1.0.0",
"path-to-regexp": "^6.2.2", "path-to-regexp": "^6.2.2",
"quill-image-resize-module": "^3.0.0", "pdf-lib": "^1.17.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-apexcharts": "^1.4.1",
"react-contenteditable": "^3.3.7",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-draft-wysiwyg": "^1.15.0",
"react-equation": "^1.0.0",
"react-google-docs-viewer": "^1.0.1",
"react-helmet": "^6.1.0",
"react-i18next": "^13.5.0", "react-i18next": "^13.5.0",
"react-icons": "^4.12.0", "react-icons": "^4.12.0",
"react-katex": "^3.0.1",
"react-latex-next": "^3.0.0",
"react-math": "^0.0.1",
"react-math-keyboard": "^1.5.17",
"react-mathjax": "^1.0.1",
"react-mathjax2": "^0.0.2",
"react-mathquill": "^1.0.3",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-quill": "^2.0.0",
"react-router-dom": "^6.23.1", "react-router-dom": "^6.23.1",
"react-textarea-autosize": "^8.5.3",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"reactstrap": "^9.2.2", "reactstrap": "^9.2.2",
"sass": "^1.77.4", "sass": "^1.77.4",
"source-map-explorer": "^2.5.3", "ts-node": "^10.9.2",
"typescript": "^4.9.5", "vite-plugin-env-compatible": "^2.0.1",
"yup": "^1.4.0", "yup": "^1.4.0",
"zustand": "^4.5.2" "zustand": "^4.5.2"
}, },
@ -91,16 +54,25 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"@types/node": "^20.14.0",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@types/react-helmet": "^6.1.11",
"@vitejs/plugin-legacy": "^5.4.1",
"@vitejs/plugin-react": "^4.3.0", "@vitejs/plugin-react": "^4.3.0",
"jest": "^29.7.0", "jest": "^29.7.0",
"jsdom": "^24.1.0", "jsdom": "^24.1.0",
"prettier": "^3.3.0", "prettier": "^3.3.0",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"ts-jest": "^29.1.4", "ts-jest": "^29.1.4",
"vite": "^5.2.12" "ts-loader": "^9.5.1",
"typescript": "^4.9.5",
"vite": "^5.2.12",
"vite-plugin-compression": "^0.5.1",
"webpack": "^5.93.0",
"webpack-cli": "^5.1.4"
} }
} }

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 861 B

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

View File

@ -1,57 +1,15 @@
import React, { Suspense, lazy, useEffect } from "react"; import { Suspense, lazy } from "react";
import { Route, Routes, useNavigate } from "react-router-dom"; import { Route, Routes } from "react-router-dom";
import Layout from "./Layout/Ui/Layout";
import { CrudRoute, menuItems } from "./Routes"; import { CrudRoute, menuItems } from "./Routes";
import { Spin } from "antd"; import { Spin } from "antd";
import { hasAbility } from "./utils/hasAbility"; import { hasAbility } from "./utils/hasAbility";
import { useChangeLanguage } from "./Hooks/useChangeLanguage"; import { renderRoutesRecursively } from "./Components/Routes/RenderRoutesRecursively";
import useAuthState from "./zustand/AuthState"; import { RenderRouteElement } from "./Components/Routes/RenderRouteElement";
import { TMenuItem } from "./types/App";
import { useTranslation } from "react-i18next";
import SpinContainer from "./Components/Layout/SpinContainer";
const Page404 = lazy(() => import("./Layout/Ui/NotFoundPage")); const Page404 = lazy(() => import("./Layout/Ui/NotFoundPage"));
const Auth = lazy(() => import("./Pages/Auth/Page")); const Auth = lazy(() => import("./Pages/Auth/Page"));
const App = () => { const App = () => {
const { changeLanguage } = useChangeLanguage();
const navigate = useNavigate();
const { isAuthenticated } = useAuthState();
const [t] = useTranslation()
useEffect(() => {
if (!isAuthenticated) {
navigate("/auth");
}
}, [isAuthenticated, navigate]);
const renderRouteElement = (route: any) => (
<Suspense fallback={<Layout> <SpinContainer/> </Layout>}>
{route.header ? (
<Layout>{route.element}</Layout>
) : (
route.element || <h1>please Create the Page</h1>
)}
</Suspense>
);
const renderRoutesRecursively = (routes:TMenuItem[]) =>
routes.map((route: TMenuItem) => {
const useAbility = hasAbility(route.abilities, route.abilities_value);
const tableHeader = t(`${route?.header}`);
// useSetPageTitle(tableHeader,route?.path);
if (useAbility) {
return (
<React.Fragment key={route.path}>
<Route path={route.path} element={renderRouteElement(route)} />
{route.children && renderRoutesRecursively(route.children)}
</React.Fragment>
);
}
return null;
});
return ( return (
<Routes> <Routes>
<Route <Route
@ -59,8 +17,7 @@ const App = () => {
path={"/auth"} path={"/auth"}
element={ element={
<Suspense fallback={<Spin />}> <Suspense fallback={<Spin />}>
{" "} <Auth />
<Auth />{" "}
</Suspense> </Suspense>
} }
/> />
@ -69,8 +26,7 @@ const App = () => {
path={"/*"} path={"/*"}
element={ element={
<Suspense fallback={<Spin />}> <Suspense fallback={<Spin />}>
{" "} <Page404 />
<Page404 />{" "}
</Suspense> </Suspense>
} }
/> />
@ -79,9 +35,6 @@ 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}`);
// useSetPageTitle(tableHeader,route?.path);
if (!useAbility) { if (!useAbility) {
return false; return false;
} }
@ -89,7 +42,7 @@ const App = () => {
<Route <Route
key={route.path ?? ""} key={route.path ?? ""}
path={route.path ?? ""} path={route.path ?? ""}
element={renderRouteElement(route)} element={RenderRouteElement(route)}
/> />
); );
})} })}

View File

@ -0,0 +1,16 @@
import { MdExpandLess, MdExpandMore } from "react-icons/md";
interface DropdownToggleProps {
isOpen: boolean;
onClick: () => void;
}
const DropdownToggle: React.FC<DropdownToggleProps> = ({ isOpen, onClick }) => {
return (
<div className="DropDownIcon" onClick={onClick}>
{isOpen ? <MdExpandLess /> : <MdExpandMore />}
</div>
);
};
export default DropdownToggle;

View File

@ -0,0 +1,40 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link, useNavigate } from "react-router-dom";
import DropdownToggle from "./DropdownToggle"; // Adjust the import path as necessary
import SubMenu from "./SubMenu"; // Adjust the import path as necessary
export const MenuItem = ({ item, location, index }: any) => {
const isActive = location.pathname.split("/")[1] === item.path?.slice(1);
const [openDropdown, setOpenDropdown] = useState<number | null>(null);
const [t] = useTranslation();
const navigate = useNavigate();
const handleDropdown = (index: number) => {
setOpenDropdown((prev) => (prev === index ? null : index));
};
const isDropdownOpen = openDropdown === index;
return (
<>
<div
className={`link ${isActive ? "active" : ""} ${item?.children && "DropDownLink"}`}
onClick={() => navigate(item.path || "/")}
>
<i>{item.icon}</i>
<Link to={item.path || "/"}>{t(item.text)}</Link>
{item?.children && (
<DropdownToggle
isOpen={isDropdownOpen}
onClick={() => handleDropdown(index)}
/>
)}
</div>
{item?.children && isDropdownOpen && (
<SubMenu items={item.children} location={location} />
)}
</>
);
};

View File

@ -0,0 +1,58 @@
import React from "react";
import { getLocalStorage } from "../../../utils/LocalStorage";
import { USER_KEY } from "../../../config/AppKey";
import { translateOptions } from "../../../utils/translatedOptions";
import { search_array } from "../../../Routes";
import { useTranslation } from "react-i18next";
import SearchFieldWithSelect from "../../DataTable/SearchFieldWithSelect";
import { Tooltip } from "antd";
import useModalHandler from "../../../utils/useModalHandler";
import { ModalEnum } from "../../../enums/Model";
import Image from "../../Ui/Image";
const NavBarRightSide = () => {
const userData = getLocalStorage(USER_KEY);
const [t] = useTranslation();
const translateArray = translateOptions(search_array, t);
const { handel_open_model } = useModalHandler();
const handleEdit = () => {
handel_open_model(ModalEnum.CHANGE_PASSWORD);
};
return (
<article>
<div className="header_search">
<SearchFieldWithSelect
options={translateArray}
placeholder={t("practical.search_here")}
/>
</div>
<span className="header_icons">
<div>
<Image src="/Icon/bell.png" alt="Notifications" />
</div>
<Tooltip
placement="top"
title={
<div onClick={handleEdit}>
{t("header.change_your_current_password")}
</div>
}
color="#E0E0E0"
>
<div className="gear">
<Image src="/Icon/gear.png" alt="Settings" />
</div>
</Tooltip>
</span>
<div className="header_profile">
<span>
<h6>{userData?.username}</h6>
<p>{userData?.type}</p>
</span>
<Image src="/Layout/DefaultStudentImage.png" alt="Profile" />
</div>
</article>
);
};
export default NavBarRightSide;

View File

@ -0,0 +1,24 @@
import React from "react";
import { MenuItem } from "./MenuItem"; // Adjust the import path as necessary
interface SubMenuProps {
items: any[];
location: any;
}
const SubMenu: React.FC<SubMenuProps> = ({ items, location }) => {
return (
<div className="sub-menu">
{items.map((childItem, index) => (
<MenuItem
key={index}
item={childItem}
location={location}
index={index}
/>
))}
</div>
);
};
export default SubMenu;

View File

@ -0,0 +1,63 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { MdExpandLess, MdExpandMore } from "react-icons/md";
import { Link, useNavigate } from "react-router-dom";
export const MenuItem = ({ item, location, index }: any) => {
const isActive = location.pathname.split("/")[1] === item.path?.slice(1);
// console.log(location.pathname.split("/")[1]);
const [openDropdown, setOpenDropdown] = useState<number | null>(null);
const handleDropdown = (index: number) => {
setOpenDropdown((prev) => (prev === index ? null : index));
};
const isDropdownOpen = openDropdown === index;
const [t] = useTranslation();
const navigate = useNavigate();
return (
<>
<div
className={`link ${isActive ? "active" : ""} ${item?.children && "DropDownLink"} `}
onClick={() => navigate(item.path || "/")}
>
<i>{item.icon}</i>
<Link to={item.path || "/"}>{t(item.text)}</Link>
{item?.children && (
<>
{isDropdownOpen ? (
<div
className="DropDownIcon"
onClick={() => handleDropdown(index)}
>
<MdExpandLess />
</div>
) : (
<div
className="DropDownIcon"
onClick={() => handleDropdown(index)}
>
<MdExpandMore />
</div>
)}
</>
)}
</div>
{item?.children && isDropdownOpen && (
<div className="sub-menu">
{item.children.map((childItem: any, index: any) => (
<MenuItem
key={index}
item={childItem}
location={location}
index={index}
/>
))}
</div>
)}
</>
);
};

View File

@ -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;

View File

@ -15,7 +15,7 @@ const TabsBar = ({ steps }: any) => {
!step.hidden && ( !step.hidden && (
<div <div
onClick={() => handleTabClick(index)} onClick={() => handleTabClick(index)}
className={`ModelBodyTab ${ActiveTab === index ? "activeModeltab" : ""}`} className={`ModelBodyTab ${ActiveTab === index ? "activeModelTab" : ""}`}
key={index} key={index}
> >
<div>{index + 1}</div> <div>{index + 1}</div>

View File

@ -0,0 +1,15 @@
import { Suspense } from "react";
import SpinContainer from "../Layout/SpinContainer";
import Layout from "../../Layout/Ui/Layout";
export const RenderRouteElement = (route: any) => (
<Suspense
fallback={
<Layout>
<SpinContainer />
</Layout>
}
>
{route.header ? <Layout>{route.element}</Layout> : route.element || <></>}
</Suspense>
);

View File

@ -0,0 +1,19 @@
import React from "react";
import { TMenuItem } from "../../types/App";
import { hasAbility } from "../../utils/hasAbility";
import { Route } from "react-router-dom";
import { RenderRouteElement } from "./RenderRouteElement";
export const renderRoutesRecursively = (routes: TMenuItem[]) =>
routes.map((route: TMenuItem) => {
const useAbility = hasAbility(route.abilities, route.abilities_value);
if (!useAbility) {
return false;
}
return (
<React.Fragment key={route.path}>
<Route path={route.path} element={RenderRouteElement(route)} />
{route.children && renderRoutesRecursively(route.children)}
</React.Fragment>
);
});

View File

@ -1,40 +1,39 @@
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();
const { setParamToSend, paramToSend } = useObjectToEdit(); const { setParamToSend, paramToSend } = useObjectToEdit();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const onChange: DatePickerProps["onChange"] = (date, dateString) => {
// console.log(date, dateString);
const newObj = { ...paramToSend };
newObj.date = dateString;
setParamToSend(newObj);
navigate(
`${location.pathname}?${paramToSend?.state ?? "all"}=${dateString}`,
);
};
const Today = new Date() as any;
const onChange: DatePickerProps["onChange"] = (date, dateString) => {
// console.log(date, dateString);
const newObj = { ...paramToSend };
newObj.date = dateString;
setParamToSend(newObj);
navigate(
`${location.pathname}?${paramToSend?.state ?? "all"}=${dateString}`,
);
};
const Today = new Date() as any;
return ( return (
<div className="CustomDatePicker"> <div className="CustomDatePicker">
<DatePicker <DatePicker
defaultValue={dayjs(Today)} defaultValue={dayjs(Today)}
placeholder={t(`input.select_date`)} placeholder={t(`input.select_date`)}
onChange={onChange} onChange={onChange}
format={DateEnum?.FORMATE} format={DateEnum?.FORMATE}
/> />
</div> </div>
) );
} };
export default CustomDatePicker export default CustomDatePicker;

View File

@ -1,54 +0,0 @@
import React, { memo } from "react";
import type { MenuProps } from "antd";
import { Button, Dropdown, Space } from "antd";
import { useChangeLanguage } from "../../Hooks/useChangeLanguage";
import { useTranslation } from "react-i18next";
const Translate: React.FC = () => {
const { currentLanguage, changeLanguage } = useChangeLanguage();
const { t } = useTranslation();
const EnLanguage = memo(() => (
<div className="MenuChange" onClick={EnLanguageClickHandler}>
<img alt="" src="../Layout/En.svg" width={20} height={20} />
{t("En")}
</div>
));
const ArLanguage = memo(() => (
<div className="MenuChange" onClick={ArLanguageClickHandler}>
<img alt="" src="../Layout/Ar.svg" width={20} height={20} />
{t("Ar")}
</div>
));
const EnLanguageClickHandler = React.useCallback(() => {
changeLanguage("en");
}, [changeLanguage]);
const ArLanguageClickHandler = React.useCallback(() => {
changeLanguage("ar");
}, [changeLanguage]);
const items: MenuProps["items"] = [
{
key: "1",
label: <EnLanguage />,
},
{
key: "2",
label: <ArLanguage />,
},
];
return (
<Space direction="vertical">
<Dropdown menu={{ items }} placement="top">
<Button disabled>
{currentLanguage === "en" ? <EnLanguage /> : <ArLanguage />}
</Button>
</Dropdown>
</Space>
);
};
export default Translate;

View File

@ -31,11 +31,11 @@ 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(
({ type, ...otherProps }:any) => { ({ type, ...otherProps }: any) => {
const Component = components[type as ValidationFieldType]; const Component = components[type as ValidationFieldType];
if (!Component) { if (!Component) {

View File

@ -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,
@ -14,7 +14,7 @@ const CheckboxField = ({
const { t, formik, isError, errorMsg } = useFormField(name, props); const { t, formik, isError, errorMsg } = useFormField(name, props);
const CheckboxhandleChange = (value: any) => { const CheckboxhandleChange = (value: any) => {
formik.setFieldValue(name, value?.target?.checked); formik.setFieldValue(name, value?.target?.checked);
}; };
return ( return (
<div className={Group ? "d-inline mt-3 Checkboxs" : ``}> <div className={Group ? "d-inline mt-3 Checkboxs" : ``}>

View File

@ -25,9 +25,7 @@ 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>
@ -58,7 +56,6 @@ const Default = ({
name={name} name={name}
disabled={isDisabled} disabled={isDisabled}
size="large" size="large"
{...(type === "number" && { min: 0 })} {...(type === "number" && { min: 0 })}
{...props} {...props}
/> />

View File

@ -12,27 +12,27 @@ const File = ({
className, className,
props, props,
}: any) => { }: any) => {
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,10 +70,9 @@ 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}
</Upload> </Upload>
</div> </div>
); );

View File

@ -16,12 +16,12 @@ const NumberField = ({
label_icon, label_icon,
...props ...props
}: ValidationFieldPropsInput) => { }: ValidationFieldPropsInput) => {
const { errorMsg, isError, t ,formik} = useFormField(name, props); const { errorMsg, isError, t, formik } = useFormField(name, props);
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);
}; };

View File

@ -63,7 +63,6 @@ const NumberFormate = ({
name={name} name={name}
disabled={isDisabled} disabled={isDisabled}
size="large" size="large"
// onChange={onChange ? onChange : handleChange} // onChange={onChange ? onChange : handleChange}
/> />

View File

@ -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>("");
@ -78,8 +78,7 @@ const SearchField = ({
onChange={onChange || SelectableChange} onChange={onChange || SelectableChange}
showSearch showSearch
optionFilterProp="label" optionFilterProp="label"
notFoundContent={isLoading ? <Spin/> : "لا يوجد" } notFoundContent={isLoading ? <Spin /> : "لا يوجد"}
onSearch={SearchHandleChange} onSearch={SearchHandleChange}
/> />
</Form.Item> </Form.Item>

View File

@ -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 = (
@ -33,14 +33,14 @@ const TextField = ({
) : label_icon ? ( ) : label_icon ? (
<div className="LabelWithIcon"> <div className="LabelWithIcon">
<label htmlFor={name} className="text"> <label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`) } {label2 ? label2 : t(`input.${label ? label : name}`)}
</label> </label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} /> <MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div> </div>
) : ( ) : (
<label htmlFor={name} className="text"> <label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`) } {label2 ? label2 : t(`input.${label ? label : name}`)}
</label> </label>
)} )}
<Form.Item <Form.Item
@ -57,9 +57,7 @@ const TextField = ({
showCount showCount
maxLength={1000} maxLength={1000}
onChange={onChange || TextFilehandleChange} onChange={onChange || TextFilehandleChange}
style={{height:120}} style={{ height: 120 }}
/> />
</Form.Item> </Form.Item>
</div> </div>

View File

@ -62,9 +62,8 @@
} }
.Checkboxs { .Checkboxs {
padding: 4%; padding: 4%;
} }
.ant-checkbox-wrapper{ .ant-checkbox-wrapper {
min-width: 100px; min-width: 100px;
} }
.SearchField { .SearchField {
@ -203,27 +202,26 @@ input:-webkit-autofill:hover {
margin-bottom: 20px; margin-bottom: 20px;
} }
.ant-checkbox-wrapper {
.ant-checkbox-wrapper{
margin-top: 25px !important; margin-top: 25px !important;
} }
.add_new_button{ .add_new_button {
margin-bottom: 20px; margin-bottom: 20px;
svg{ svg {
color: var(--primary); color: var(--primary);
} }
} }
.ValidationField:has(.input_number){ .ValidationField:has(.input_number) {
max-width: 100px; max-width: 100px;
.input_number{ .input_number {
max-width: 100px; max-width: 100px;
} }
} }
.flex{ .flex {
display: flex; display: flex;
gap: 30px ; gap: 30px;
max-width: 80% !important; max-width: 80% !important;
} }

View File

@ -1,9 +1,7 @@
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
} }

View File

@ -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;
@ -151,7 +151,7 @@ export interface ValidationFieldPropstext {
| "TextArea" | "TextArea"
| "NumberFormate"; | "NumberFormate";
label?: string; label?: string;
label2?:string; label2?: string;
className?: string; className?: string;
placeholder?: string; placeholder?: string;
isDisabled?: boolean; isDisabled?: boolean;
@ -161,31 +161,26 @@ 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

View File

@ -1,28 +1,26 @@
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);
setIsBseQuestion(true) setValues(null);
setValues(null) setFieldValue("isBase", 1);
setFieldValue("isBase",1)
} }
}; };
@ -33,7 +31,9 @@ const Header = () => {
</div> </div>
<div> <div>
<GoArrowSwitch onClick={handleChange} className="m-2" /> <GoArrowSwitch onClick={handleChange} className="m-2" />
{isBseQuestion || values?.isBase === 1 ? t("header.malty_exercise") :t("header.exercise") } {isBseQuestion || values?.isBase === 1
? t("header.malty_exercise")
: t("header.exercise")}
</div> </div>
</header> </header>
); );

View File

@ -1,4 +1,3 @@
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 [];

View File

@ -1,47 +0,0 @@
import { useCallback, useLayoutEffect, useState } from "react";
import translationEN from "../translate/en.json";
import translationAR from "../translate/ar.json";
import { initReactI18next } from "react-i18next";
import i18n from "i18next"; // Make sure this import is correct
import { LANGUAGE_KEY } from "../config/AppKey";
i18n.use(initReactI18next).init({
resources: {
en: {
translation: translationEN,
},
ar: {
translation: translationAR,
},
},
lng: localStorage.getItem(LANGUAGE_KEY) || "ar",
interpolation: {
escapeValue: false,
},
});
export const useChangeLanguage = () => {
const [currentLanguage, setCurrentLanguage] = useState(
localStorage.getItem(LANGUAGE_KEY) || "ar",
);
useLayoutEffect(() => {
if (currentLanguage === "ar") {
i18n.changeLanguage("ar");
document.body.setAttribute("dir", "rtl");
document.body.classList.remove("en");
} else if (currentLanguage === "en") {
i18n.changeLanguage("en");
document.body.setAttribute("dir", "ltr");
document.body.classList.add("en");
}
localStorage.setItem(LANGUAGE_KEY, currentLanguage);
}, [currentLanguage]);
const changeLanguage = useCallback((newLanguage: any) => {
setCurrentLanguage(newLanguage);
}, []);
return { currentLanguage, changeLanguage };
};

View File

@ -1,8 +1,12 @@
import { useEffect } from 'react'; import { useEffect } from "react";
type ModifierKey = 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; type ModifierKey = "ctrlKey" | "shiftKey" | "altKey" | "metaKey";
const useKeyPress = (targetKey: string, modifierKey: ModifierKey, callback:any) => { const useKeyPress = (
targetKey: string,
modifierKey: ModifierKey,
callback: any,
) => {
useEffect(() => { useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
if (event[modifierKey] && event.key === targetKey) { if (event[modifierKey] && event.key === targetKey) {
@ -11,10 +15,10 @@ const useKeyPress = (targetKey: string, modifierKey: ModifierKey, callback:any)
} }
}; };
document.addEventListener('keydown', handleKeyDown); document.addEventListener("keydown", handleKeyDown);
return () => { return () => {
document.removeEventListener('keydown', handleKeyDown); document.removeEventListener("keydown", handleKeyDown);
}; };
}, [targetKey, modifierKey, callback]); }, [targetKey, modifierKey, callback]);
}; };

View File

@ -1,16 +1,18 @@
import { useEffect } from 'react'; import { useEffect } from "react";
import { setLocalStorageQuestions } from '../utils/setLocalStorageQuestions'; import { setLocalStorageQuestions } from "../utils/setLocalStorageQuestions";
const useSaveOnDisconnect = (noChange: boolean, QUESTION_OBJECT_KEY: string, SavedQuestionData: any) => { const useSaveOnDisconnect = (
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);
} }
} }
@ -19,24 +21,22 @@ const useSaveOnDisconnect = (noChange: boolean, QUESTION_OBJECT_KEY: string, Sav
const handleOffline = () => { const handleOffline = () => {
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);
} }
} }
}; };
// 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
}; };

View File

@ -1,12 +1,12 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { usePage_titleState } from "../zustand/PageTitleState"; import { usePageTitleState } from "../zustand/PageTitleState";
const useSetPageTitle = (title: any) => { const useSetPageTitle = (title: any) => {
const setPage_title = usePage_titleState((state) => state.setPage_title); const setPageTitle = usePageTitleState((state) => state.setPageTitle);
useEffect(() => { useEffect(() => {
setPage_title(title); setPageTitle(title);
}, [title, setPage_title]); }, [title, setPageTitle]);
}; };
export default useSetPageTitle; export default useSetPageTitle;

View File

@ -30,7 +30,6 @@ const FormikFormModel: React.FC<FormikFormProps> = ({
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
{(formik) => { {(formik) => {
useEffect(() => { useEffect(() => {
@ -38,11 +37,10 @@ const FormikFormModel: React.FC<FormikFormProps> = ({
formik.setErrors({}); formik.setErrors({});
} }
if (isOpen === "isSuccess") { if (isOpen === "isSuccess") {
formik.setErrors({}); formik.setErrors({});
formik.resetForm(); formik.resetForm();
} }
}, [isOpen]); }, [isOpen]);
return <Form className="w-100">{children}</Form>; return <Form className="w-100">{children}</Form>;
}} }}

View File

@ -6,13 +6,12 @@ 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,
ModalEnum, ModalEnum,
useTranslation, useTranslation,
ABILITIES_ENUM, ABILITIES_ENUM,
ABILITIES_VALUES_ENUM, ABILITIES_VALUES_ENUM,
hasAbility hasAbility,
}; };

View File

@ -1,10 +1,7 @@
import React, { useEffect } from "react"; import React from "react";
import { useGetTitleFromRoute } from "../../Hooks/useGetTitleFromRoute";
import { Helmet } from "react-helmet";
import { useLocation } from "react-router-dom";
import NavBar from "./NavBar"; import NavBar from "./NavBar";
import SideBar from "./SideBar"; import SideBar from "./SideBar";
import { USER_KEY } from "../../config/AppKey"; import ProtectedRouteProvider from "../../lib/ProtectedRouteProvider";
const Layout = ({ const Layout = ({
children, children,
@ -13,21 +10,14 @@ const Layout = ({
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
}) => { }) => {
const location = useLocation();
return ( return (
<> <ProtectedRouteProvider className="Layout">
<Helmet> <main className={`${className} Layout_Body`}>
<title>{useGetTitleFromRoute(location.pathname)}</title> <NavBar />
</Helmet> <div className="Layout_Children">{children}</div>
<div className="Layout"> </main>
<main className={`${className} Layout_Body`}> <SideBar />
<NavBar /> </ProtectedRouteProvider>
<div className="Layout_Children">{children}</div>
</main>
<SideBar />
</div>
</>
); );
}; };

View File

@ -1,86 +1,38 @@
import React from "react"; import React, { lazy, Suspense } from "react";
import Image from "../../Components/Ui/Image";
import SearchField from "../../Components/DataTable/SearchFieldWithSelect"; import { usePageTitleState } from "../../zustand/PageTitleState";
import { usePage_titleState } from "../../zustand/PageTitleState";
import { MdOutlineArrowForwardIos } from "react-icons/md"; import { MdOutlineArrowForwardIos } from "react-icons/md";
import { useLocation, useNavigate, useParams } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { search_array } from "../../Routes";
import { BRANCH_OBJECT_KEY, USER_KEY } from "../../config/AppKey";
import { useTranslation } from "react-i18next";
import { translateOptions } from "../../utils/translatedOptions";
import { ParamsEnum } from "../../enums/params";
import { Tooltip } from "antd";
import useModalHandler from "../../utils/useModalHandler";
import { ModalEnum } from "../../enums/Model";
import ChangePasswordModel from "./model/AddModel";
import { getLocalStorage } from "../../utils/LocalStorage";
import { getPrevPathRoute } from "../../utils/getPrevPathRoute"; import { getPrevPathRoute } from "../../utils/getPrevPathRoute";
import { deletePathSegments } from "../../utils/deletePathSegments"; import { deletePathSegments } from "../../utils/deletePathSegments";
import SpinContainer from "../../Components/Layout/SpinContainer";
import NavBarRightSide from "../../Components/Layout/Navbar/NavBarRightSide";
// Lazy load the ChangePasswordModel
const ChangePasswordModel = lazy(() => import("./model/AddModel"));
const NavBar = () => { const NavBar = () => {
const { Page_title } = usePage_titleState((state) => state); const { PageTitle } = usePageTitleState((state) => state);
const userData = getLocalStorage(USER_KEY);
const [t] = useTranslation();
const location = useLocation(); const location = useLocation();
const translateArray = translateOptions(search_array, t);
const navigate = useNavigate(); const navigate = useNavigate();
const PrevPath = getPrevPathRoute(location.pathname); const PrevPath = getPrevPathRoute(location.pathname);
const handelNavigate = () => { const handelNavigate = () => {
if (PrevPath === 0) { if (PrevPath === 0) {
return 0; return;
} }
navigate(deletePathSegments(location.pathname, PrevPath)); navigate(deletePathSegments(location.pathname, PrevPath));
}; };
const { handel_open_model } = useModalHandler();
const handleEdit = (record: any) => {
handel_open_model(ModalEnum?.CHANGE_PASSWORD);
};
return ( return (
<div className="NavBar"> <div className="NavBar">
<Suspense fallback={<SpinContainer />}>
<span className="navbar_link" onClick={handelNavigate}> <span className="navbar_link" onClick={handelNavigate}>
<MdOutlineArrowForwardIos /> {Page_title} <MdOutlineArrowForwardIos /> {PageTitle}
</span> </span>
<article> <NavBarRightSide />
<div className="header_search"> <ChangePasswordModel />
{/* <NavBarSelect /> */} </Suspense>
<SearchField
options={translateArray}
placeholder={t("practical.search_here")}
/>
</div>
<span className="header_icons">
<div>
<Image src="/Icon/bell.png" />
</div>
<Tooltip
placement="top"
title={
<div onClick={handleEdit}>
{" "}
{t("header.change_your_current_password")}{" "}
</div>
}
color="#E0E0E0"
>
<div className="gear">
<Image src="/Icon/gear.png" />
</div>
</Tooltip>
</span>
<div className="header_profile">
<span>
<h6>{userData?.username}</h6>
<p>{userData?.type}</p>
</span>
<Image src="/Layout/DefultStudentImage.png" />
</div>
</article>
<ChangePasswordModel />
</div> </div>
); );
}; };

View File

@ -1,10 +1,11 @@
import React from "react"; import React from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import ProtectedRouteProvider from "../../lib/ProtectedRouteProvider";
function NotFoundPage() { function NotFoundPage() {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<div className="not_foound_page"> <ProtectedRouteProvider className="not_found_page">
<div className="container-not-found"> <div className="container-not-found">
<p> <p>
404 <h6>|</h6>This page could not be found 404 <h6>|</h6>This page could not be found
@ -14,7 +15,7 @@ function NotFoundPage() {
<button onClick={() => navigate("/", { replace: true })}>Home</button> <button onClick={() => navigate("/", { replace: true })}>Home</button>
</div> </div>
</div> </div>
</div> </ProtectedRouteProvider>
); );
} }

View File

@ -1,72 +1,14 @@
import React, { useState } from "react"; import React from "react";
import { Divider } from "antd"; import { Divider } from "antd";
import { Link, useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { menuItems } from "../../Routes"; import { menuItems } from "../../Routes";
import { MdLogout, MdExpandMore, MdExpandLess } from "react-icons/md"; import { MdLogout } from "react-icons/md";
import useAuthState from "../../zustand/AuthState"; import useAuthState from "../../zustand/AuthState";
import { hasAbility } from "../../utils/hasAbility"; import { hasAbility } from "../../utils/hasAbility";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { getLocalStorage } from "../../utils/LocalStorage"; import { getLocalStorage } from "../../utils/LocalStorage";
import { BRANCH_OBJECT_KEY } from "../../config/AppKey"; import { BRANCH_OBJECT_KEY } from "../../config/AppKey";
import { MenuItem } from "../../Components/Layout/SideBar/MenuItem";
const MenuItem = ({ item, location, index }: any) => {
const isActive = location.pathname.split("/")[1] === item.path?.slice(1);
// console.log(location.pathname.split("/")[1]);
const [openDropdown, setOpenDropdown] = useState<number | null>(null);
const handleDropdown = (index: number) => {
setOpenDropdown((prev) => (prev === index ? null : index));
};
const isDropdownOpen = openDropdown === index;
const [t] = useTranslation();
const navigate = useNavigate();
return (
<>
<div
className={`link ${isActive ? "active" : ""} ${item?.children && "DropDownLink"} `}
onClick={() => navigate(item.path || "/")}
>
<i>{item.icon}</i>
<Link to={item.path || "/"}>{t(item.text)}</Link>
{item?.children && (
<>
{isDropdownOpen ? (
<div
className="DropDownIcon"
onClick={() => handleDropdown(index)}
>
<MdExpandLess />
</div>
) : (
<div
className="DropDownIcon"
onClick={() => handleDropdown(index)}
>
<MdExpandMore />
</div>
)}
</>
)}
</div>
{item?.children && isDropdownOpen && (
<div className="sub-menu">
{item.children.map((childItem: any, index: any) => (
<MenuItem
key={index}
item={childItem}
location={location}
index={index}
/>
))}
</div>
)}
</>
);
};
const SideBar = () => { const SideBar = () => {
const location = useLocation(); const location = useLocation();
@ -79,8 +21,9 @@ const SideBar = () => {
return ( return (
<div className="side_bar"> <div className="side_bar">
<h1> <h1>
{/* {t("sidebar.dashboard")} */} {/* {t("sidebar.dashboard")} */}
{branch_name} </h1> {branch_name}{" "}
</h1>
<Divider /> <Divider />
<div className="side_bar_links"> <div className="side_bar_links">
{menuItems.map((item, index) => { {menuItems.map((item, index) => {

View File

@ -5,7 +5,6 @@ import FormikForm from "../../../Layout/Dashboard/FormikFormModel";
import ModelBody from "./Add"; import ModelBody from "./Add";
import { getInitialValues, getValidationSchema } from "./formUtil"; import { getInitialValues, getValidationSchema } from "./formUtil";
import { ModalEnum } from "../../../enums/Model"; import { ModalEnum } from "../../../enums/Model";
import { useParams } from "react-router-dom";
import { useObjectToEdit } from "../../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useUpdateAdmin } from "../../../api/users"; import { useUpdateAdmin } from "../../../api/users";

View File

@ -20,12 +20,10 @@ 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">

View File

@ -13,7 +13,6 @@ 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);
}; };

View File

@ -10,7 +10,9 @@ const AddModel: React.FC = () => {
const { mutate, status } = useAddTag(); const { mutate, status } = useAddTag();
const handleSubmit = (values: any) => { const handleSubmit = (values: any) => {
mutate({ ...values }); mutate({
...values,
});
}; };
return ( return (
<> <>

View File

@ -13,7 +13,7 @@ const EditModel: React.FC = () => {
const handleSubmit = (values: any) => { const handleSubmit = (values: any) => {
mutate({ mutate({
...values ...values,
}); });
}; };
return ( return (

View File

@ -3,16 +3,13 @@ import ValidationField from "../../../Components/ValidationField/ValidationField
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>
<ValidationField placeholder="name" label="name" name="name" /> <ValidationField placeholder="name" label="name" name="name" />
</Col> </Col>
<div> <div>
<DynamicTags/> <DynamicTags />
</div> </div>
</Row> </Row>
); );

View File

@ -2,17 +2,19 @@ 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 { 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 useSetPageTitle from "../../Hooks/useSetPageTitle";
import { useDeleteTag } from "../../api/tags"; import { useDeleteTag } from "../../api/tags";
const Table = lazy(() => import('./Table')); import DeleteModels from "../../Layout/Dashboard/DeleteModels";
const AddModalForm = lazy(() => import('./Model/AddModel')); const Table = lazy(() => import("./Table"));
const EditModalForm = lazy(() => import('./Model/EditModel')); const AddModalForm = lazy(() => import("./Model/AddModel"));
const DeleteModels = lazy(() => import('../../Layout/Dashboard/DeleteModels')); const EditModalForm = lazy(() => import("./Model/EditModel"));
const SearchField = lazy(() => import('../../Components/DataTable/SearchField')); const DeleteModalForm = lazy(() => import("./Model/Delete"));
const SearchField = lazy(
() => import("../../Components/DataTable/SearchField"),
);
const TableHeader = () => { const TableHeader = () => {
const { handel_open_model } = useModalHandler(); const { handel_open_model } = useModalHandler();
@ -24,24 +26,25 @@ const TableHeader = () => {
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">
<button <button
onClick={() => handel_open_model(ModalEnum?.TAGS_ADD)} onClick={() => handel_open_model(ModalEnum?.TAGS_ADD)}
className="add_button" className="add_button"
> >
{t("models.tags")} <FaPlus /> {t("models.tags")} <FaPlus />
</button> </button>
</div> </div>
)} )}
</header> </header>
<Table /> <Table />
<DeleteModels <DeleteModels
deleteMutation={deleteMutation} deleteMutation={deleteMutation}

View File

@ -11,7 +11,7 @@ const App: React.FC = () => {
pagination: true, pagination: true,
}); });
return <DataTable response={response} useColumns={useColumns} />; return <DataTable response={response} useColumns={useColumns} />;
}; };
export default App; export default App;

View File

@ -13,5 +13,5 @@ export {
AddModalForm, AddModalForm,
EditModalForm, EditModalForm,
DeleteModalForm, DeleteModalForm,
FaPlus FaPlus,
}; };

View File

@ -1,70 +1,56 @@
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>() console.log(formik?.values?.synonyms);
const [t] = useTranslation()
console.log(formik?.values?.synonyms);
const handleAddChoice = () => { const handleAddChoice = () => {
const length = formik?.values?.synonyms.length; const length = formik?.values?.synonyms.length;
const lastElement = formik?.values?.synonyms[length - 1]; const lastElement = formik?.values?.synonyms[length - 1];
if(lastElement !== ""){
formik.setFieldValue('synonyms', [...(formik?.values as any)?.synonyms as any[],""])
}else{
if (lastElement !== "") {
formik.setFieldValue("synonyms", [
...((formik?.values as any)?.synonyms as any[]),
"",
]);
} else {
} }
};
// console.log(formik?.values);
} // console.log(currentTag);
// console.log(formik?.values);
// console.log(currentTag);
return ( 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="tags">
{ <div className="tag_container">
(((formik?.values as any)?.synonyms as any[])||[]) .map((item:any,index:number)=>{ <div className="tags">
{(((formik?.values as any)?.synonyms as any[]) || []).map(
(item: any, index: number) => {
return <Tag key={index} index={index} data={item} />;
},
)}
</div>
return ( {formik?.values?.synonyms?.length > 0 && (
<Tag key={index} index={index} data={item}/> <p className="add_new_button">
<FaCirclePlus onClick={handleAddChoice} size={20} />
</p>
) )}
} </div>
)
}
</div>
{formik?.values?.synonyms?.length > 0 &&
<p className="add_new_button" >
<FaCirclePlus onClick={handleAddChoice} size={20} />
</p>
}
</div>
</div> </div>
) );
} };
export default DynamicTags export default DynamicTags;

View File

@ -1,66 +1,60 @@
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();
useEffect(() => {
if (inputRef.current) {
inputRef.current.style.width = `${(formik?.values?.synonyms[index]?.length + 1) * 8}px`;
}
}, [formik?.values?.synonyms[index]]);
console.log(formik?.values?.synonyms);
console.log(index);
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// console.log(e.target.value);
formik.setFieldValue(`synonyms[${index}]`, e.target.value);
setTagsSearch(e.target.value)
setCurrentTag(index)
}; useEffect(() => {
if (inputRef.current) {
inputRef.current.style.width = `${(formik?.values?.synonyms[index]?.length + 1) * 8}px`;
}
}, [formik?.values?.synonyms[index]]);
console.log(formik?.values?.synonyms);
console.log(index);
const handleInputBlur = () => { const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// setTagsSearch(null) // console.log(e.target.value);
}; formik.setFieldValue(`synonyms[${index}]`, e.target.value);
setTagsSearch(e.target.value);
setCurrentTag(index);
};
const handleDeleteChoice = () => { const handleInputBlur = () => {
console.log(data); // setTagsSearch(null)
// Create a copy of current tags array };
const currentTags = [...formik.values.synonyms];
// Remove the item at the specified index from the array const handleDeleteChoice = () => {
currentTags.splice(index, 1); console.log(data);
console.log(currentTags); // Log the updated tags array // Create a copy of current tags array
const currentTags = [...formik.values.synonyms];
// Update formik field value with the updated tags array // Remove the item at the specified index from the array
formik.setFieldValue('synonyms', currentTags); currentTags.splice(index, 1);
console.log(currentTags); // Log the updated tags array
// Reset search state if needed
setTagsSearch(null); // Update formik field value with the updated tags array
}; formik.setFieldValue("synonyms", currentTags);
return ( // Reset search state if needed
<div className='tag'> setTagsSearch(null);
<input };
ref={inputRef}
className="tagInput" return (
type="text" <div className="tag">
value={formik?.values?.synonyms[index]} <input
onChange={handleEditInputChange} ref={inputRef}
onBlur={handleInputBlur} className="tagInput"
type="text"
/> value={formik?.values?.synonyms[index]}
<FaTrash onClick={handleDeleteChoice}/> onChange={handleEditInputChange}
</div> onBlur={handleInputBlur}
); />
<FaTrash onClick={handleDeleteChoice} />
</div>
);
}; };
export default Tag; export default Tag;

View File

@ -1,5 +1,5 @@
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";
@ -20,27 +20,21 @@ 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( useSetPageTitle(t(`page_header.subject`) + "/" + t(`${SubjectName}`));
t(`page_header.subject`) +
"/" +
t(`${SubjectName}`),
);
return ( return (
<div className="TableWithHeader"> <div className="TableWithHeader">
<Suspense fallback={<Spin/>}> <Suspense fallback={<Spin />}>
<header> <header>
<h6> <h6>
{t("models.units")} {SubjectName} {t("models.units")} {SubjectName}
</h6> </h6>
</header>
</header>
<Table /> <Table />
<AddModalForm /> <AddModalForm />
<EditModalForm /> <EditModalForm />
@ -49,7 +43,6 @@ const TableHeader = () => {
ModelEnum={ModalEnum?.UNIT_DELETE} ModelEnum={ModalEnum?.UNIT_DELETE}
/> />
</Suspense> </Suspense>
</div> </div>
); );
}; };

View File

@ -7,10 +7,10 @@ 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");
return <DataTable response={response} useColumns={useColumns} />; return <DataTable response={response} useColumns={useColumns} />;
}; };

View File

@ -10,14 +10,19 @@ 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 { canAddUnit, canDeleteUnit, canEditUnit, canShowUnit } from "../../utils/hasAbilityFn"; import {
canAddUnit,
canDeleteUnit,
canEditUnit,
canShowUnit,
} from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons"; import ActionButtons from "../../Components/Table/ActionButtons";
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}`);

View File

@ -16,7 +16,7 @@ const AddModel: React.FC = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { mutate, isSuccess, status } = useAddLesson(); const { mutate, isSuccess, status } = useAddLesson();
const {unit_id} = useParams<ParamsEnum>() const { unit_id } = useParams<ParamsEnum>();
useEffect(() => { useEffect(() => {
if (isSuccess) { if (isSuccess) {
setIsOpen(""); setIsOpen("");

View File

@ -1,5 +1,5 @@
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 useSetPageTitle from "../../Hooks/useSetPageTitle"; import useSetPageTitle from "../../Hooks/useSetPageTitle";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@ -17,34 +17,31 @@ const TableHeader = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const deleteMutation = useDeleteLesson(); const deleteMutation = useDeleteLesson();
const { unit_id } = useParams<ParamsEnum>(); const { unit_id } = useParams<ParamsEnum>();
const { data: unit } = useGetAllUnit({ show: unit_id }); const { data: unit } = useGetAllUnit({ show: unit_id });
const unitName = unit?.data?.name ?? ""; const unitName = unit?.data?.name ?? "";
const SubjectName = unit?.data?.subject?.name ?? ""; const SubjectName = unit?.data?.subject?.name ?? "";
console.log(unit?.data); console.log(unit?.data);
useSetPageTitle( useSetPageTitle(
t(`page_header.subject`) + t(`page_header.subject`) +
"/" +
`${SubjectName}` +
"/" + "/" +
t(`page_title.unit`) + `${SubjectName}` +
"/" + "/" +
`${unitName}` t(`PageTitle.unit`) +
"/" +
`${unitName}`,
); );
return ( return (
<div className="TableWithHeader"> <div className="TableWithHeader">
<Suspense fallback={<Spin/>}> <Suspense fallback={<Spin />}>
<header> <header>
<h6> <h6>
{t("models.lessons")} {SubjectName} {unitName} {t("models.lessons")} {SubjectName} {unitName}
</h6> </h6>
</header>
</header>
<Table /> <Table />
<AddModalForm /> <AddModalForm />
<EditModalForm /> <EditModalForm />
@ -53,7 +50,6 @@ const TableHeader = () => {
ModelEnum={ModalEnum?.LESSON_DELETE} ModelEnum={ModalEnum?.LESSON_DELETE}
/> />
</Suspense> </Suspense>
</div> </div>
); );
}; };

View File

@ -6,8 +6,8 @@ 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 {unit_id} = useParams<ParamsEnum>() const { unit_id } = useParams<ParamsEnum>();
const response = useGetAllLesson({ unit_id:unit_id, pagination: true}); const response = useGetAllLesson({ unit_id: unit_id, pagination: true });
return <DataTable response={response} useColumns={useColumns} />; return <DataTable response={response} useColumns={useColumns} />;
}; };

View File

@ -5,16 +5,21 @@ import useModalHandler from "../../utils/useModalHandler";
import { ModalEnum } from "../../enums/Model"; import { ModalEnum } from "../../enums/Model";
import { useObjectToEdit } from "../../zustand/ObjectToEditState"; import { useObjectToEdit } from "../../zustand/ObjectToEditState";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "../../enums/abilities"; import { ABILITIES_ENUM } from "../../enums/abilities";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { canAddLesson, canDeleteLesson, canEditLesson, canShowLesson } from "../../utils/hasAbilityFn"; import {
canAddLesson,
canDeleteLesson,
canEditLesson,
canShowLesson,
} from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons"; import ActionButtons from "../../Components/Table/ActionButtons";
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.LESSON}/${record?.id}`); navigate(`${ABILITIES_ENUM.LESSON}/${record?.id}`);

View File

@ -1,7 +1,13 @@
import React, { Suspense, lazy, useEffect } from "react"; import React, { Suspense, lazy, useEffect } from "react";
import { Modal, Spin } from "antd"; import { Modal, Spin } from "antd";
import FormikForm from "../../Layout/Dashboard/FormikFormModel"; import FormikForm from "../../Layout/Dashboard/FormikFormModel";
import { getInitialValues, getValidationSchema ,getInitialValuesBase, getValidationSchemaBase, processTags} from "./Model/formUtil"; import {
getInitialValues,
getValidationSchema,
getInitialValuesBase,
getValidationSchemaBase,
processTags,
} from "./Model/formUtil";
import { useAddQuestion, useAddQuestionAsync } from "../../api/Question"; import { useAddQuestion, useAddQuestionAsync } from "../../api/Question";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
@ -27,60 +33,65 @@ import { useGetAllUnit } from "../../api/unit";
import { useGetAllLesson } from "../../api/lesson"; import { useGetAllLesson } from "../../api/lesson";
const AddPage: React.FC = () => { const AddPage: React.FC = () => {
const { isSuccess: isSuccessAsync, mutateAsync } = useAddQuestionAsync();
const { mutate, isSuccess, isLoading } = useAddQuestion();
const {
isBseQuestion,
setTagsSearch,
setObjectToEdit,
setSuccess,
SavedQuestionData,
} = useObjectToEdit();
const {isSuccess:isSuccessAsync,mutateAsync} = useAddQuestionAsync() const { subject_id, lesson_id, unit_id } = useParams<ParamsEnum>();
const { mutate,isSuccess, isLoading} = useAddQuestion();
const {isBseQuestion,setTagsSearch,setObjectToEdit,setSuccess,SavedQuestionData} = useObjectToEdit()
const {subject_id,lesson_id,unit_id} = useParams<ParamsEnum>()
const { setIsOpen } = useModalState((state) => state); const { setIsOpen } = useModalState((state) => state);
const { data: unit } = useGetAllUnit({ show: unit_id }); const { data: unit } = useGetAllUnit({ show: unit_id });
const { data: lesson } = useGetAllLesson({ show: lesson_id }); const { data: lesson } = useGetAllLesson({ show: lesson_id });
const [t] = useTranslation() const [t] = useTranslation();
const unitName = unit?.data?.name ?? ""; const unitName = unit?.data?.name ?? "";
const SubjectName = unit?.data?.subject?.name ?? ""; const SubjectName = unit?.data?.subject?.name ?? "";
const lessonName = lesson?.data?.name ?? ""; const lessonName = lesson?.data?.name ?? "";
useSetPageTitle( useSetPageTitle(
t(`page_header.subject`) + t(`page_header.subject`) +
"/" +
`${SubjectName}` +
"/" + "/" +
t(`page_title.unit`) + `${SubjectName}` +
"/" + "/" +
`${unitName}`+"/" t(`PageTitle.unit`) +
+ "/" +
t(`page_title.lesson`) + `${unitName}` +
"/" + "/" +
`${lessonName}`+ "/" + t(`PageTitle.lesson`) +
t(`page_title.questions`) "/" +
`${lessonName}` +
"/" +
t(`PageTitle.questions`),
); );
const handleSubmit = (
const handleSubmit = (values: any, { resetForm }: { resetForm: () => void }) => { values: any,
{ resetForm }: { resetForm: () => void },
) => {
const DataToSend = structuredClone(values); const DataToSend = structuredClone(values);
setTagsSearch(null); setTagsSearch(null);
console.log(isBseQuestion); console.log(isBseQuestion);
if (isBseQuestion || DataToSend?.isBase === 1) { if (isBseQuestion || DataToSend?.isBase === 1) {
const newBseQuestion = { const newBseQuestion = {
"subject_id": subject_id, subject_id: subject_id,
"content": DataToSend?.content, content: DataToSend?.content,
"image": DataToSend?.image ?? "", image: DataToSend?.image ?? "",
"isBase": 1, isBase: 1,
"lessons_ids":[lesson_id] lessons_ids: [lesson_id],
}; };
mutateAsync(newBseQuestion).then((data: any) => {
mutateAsync(newBseQuestion).then((data:any) => {
const newBseQuestionId = (data as any)?.data?.id; const newBseQuestionId = (data as any)?.data?.id;
const Questions = DataToSend?.Questions; const Questions = DataToSend?.Questions;
console.log(1); console.log(1);
Questions?.map((item: Question) => { Questions?.map((item: Question) => {
const tags = processTags(item); const tags = processTags(item);
console.log(item); console.log(item);
@ -88,109 +99,67 @@ const AddPage: React.FC = () => {
mutate({ mutate({
...item, ...item,
parent_id: newBseQuestionId, parent_id: newBseQuestionId,
"subject_id": subject_id, subject_id: subject_id,
tags, tags,
"lessons_ids":[lesson_id] lessons_ids: [lesson_id],
}); });
}); });
console.log(newBseQuestionId, "newBseQuestionId"); console.log(newBseQuestionId, "newBseQuestionId");
}); });
} else { } else {
const tags = processTags(DataToSend); const tags = processTags(DataToSend);
mutate({ ...values, subject_id: subject_id, tags , "lessons_ids":[lesson_id] }) mutate({
...values,
subject_id: subject_id,
tags,
lessons_ids: [lesson_id],
});
} }
}; };
const navigate = useNavigate()
useEffect(() => {
if (isSuccessAsync && ( SavedQuestionData?.Questions?.length > 0 ? isSuccess: true )) {
setObjectToEdit(null)
setSuccess(true)
localStorage.removeItem(QUESTION_OBJECT_KEY)
}
if(isSuccess && !(SavedQuestionData?.Questions?.length)){
toast.success(t("validation.the_possess_done_successful"))
setObjectToEdit(null)
setSuccess(true)
localStorage.removeItem(QUESTION_OBJECT_KEY)
}
}, [isSuccess,isSuccessAsync])
let cleanedQuestionOptions = cleanObject(SavedQuestionData);
let noChange =hasItems(cleanedQuestionOptions)
const navigate = useNavigate();
useEffect(() => {
if (
isSuccessAsync &&
(SavedQuestionData?.Questions?.length > 0 ? isSuccess : true)
) {
setObjectToEdit(null);
setSuccess(true);
localStorage.removeItem(QUESTION_OBJECT_KEY);
}
if (isSuccess && !SavedQuestionData?.Questions?.length) {
toast.success(t("validation.the_possess_done_successful"));
setObjectToEdit(null);
setSuccess(true);
localStorage.removeItem(QUESTION_OBJECT_KEY);
}
}, [isSuccess, isSuccessAsync]);
let cleanedQuestionOptions = cleanObject(SavedQuestionData);
let noChange = hasItems(cleanedQuestionOptions);
useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData); useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData);
const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY);
const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY)
const handleCancel = () => { const handleCancel = () => {
if(!noChange){ if (!noChange) {
navigate(-1) navigate(-1);
localStorage.removeItem(QUESTION_OBJECT_KEY) localStorage.removeItem(QUESTION_OBJECT_KEY);
} else {
}else{ setIsOpen(ModalEnum?.QUESTION_ACCEPT);
}
setIsOpen(ModalEnum?.QUESTION_ACCEPT);
}
}; };
if (isBseQuestion || SavedData?.isBase === 1) {
if(isBseQuestion || SavedData?.isBase === 1){
return ( return (
<div className="exercise_add"> <div className="exercise_add">
<FormikForm
handleSubmit={handleSubmit}
initialValues={getInitialValuesBase(SavedData)}
validationSchema={getValidationSchemaBase}
>
<main className="w-100 exercise_add_main">
<Header/>
<BaseForm/>
<div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div>
<button disabled={isLoading} className="relative" type="submit">
{t("practical.add")}
{isLoading && (
<span className="Spinier_Div">
<Spin />
</span>
)}
</button>
</div>
</main>
</FormikForm>
<Suspense fallback={<Spin/>}>
<AcceptModal/>
</Suspense>
</div>
);
}
return (
<div className="exercise_add">
<FormikForm <FormikForm
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
initialValues={getInitialValues(SavedData)} initialValues={getInitialValuesBase(SavedData)}
validationSchema={getValidationSchema} validationSchema={getValidationSchemaBase}
> >
<main className="w-100 exercise_add_main"> <main className="w-100 exercise_add_main">
<Header/> <Header/>
<ModelForm/> <ModelForm/>
@ -209,13 +178,42 @@ const AddPage: React.FC = () => {
</div> </div>
</main> </main>
</FormikForm> </FormikForm>
<Suspense fallback={<Spin/>}> <Suspense fallback={<Spin />}>
<AcceptModal/> <AcceptModal />
</Suspense> </Suspense>
</div>
);
}
return (
<div className="exercise_add">
<FormikForm
handleSubmit={handleSubmit}
initialValues={getInitialValues(SavedData)}
validationSchema={getValidationSchema}
>
<main className="w-100 exercise_add_main">
<Header />
<ModelForm />
<div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div>
<button disabled={isLoading} className="relative" type="submit">
{t("practical.add")}
{isLoading && (
<span className="Spinier_Div">
<Spin />
</span>
)}
</button>
</div>
</main>
</FormikForm>
<Suspense fallback={<Spin />}>
<AcceptModal />
</Suspense>
</div> </div>
); );
}; };
export default AddPage; export default AddPage;

View File

@ -1,8 +1,20 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Spin } from "antd"; import { Modal, Spin } from "antd";
import FormikForm from "../../Layout/Dashboard/FormikFormModel"; import FormikForm from "../../Layout/Dashboard/FormikFormModel";
import { getInitialValues, getValidationSchema ,getInitialValuesBase, getValidationSchemaBase, processTags} from "./Model/formUtil"; import {
import { useAddQuestion, useDeleteQuestion, useGetAllQuestion, useUpdateQuestion } from "../../api/Question"; getInitialValues,
getValidationSchema,
getInitialValuesBase,
getValidationSchemaBase,
processTags,
} from "./Model/formUtil";
import {
useAddQuestion,
useDeleteQuestion,
useGetAllQuestion,
useUpdateQuestion,
} from "../../api/Question";
import { useQueryClient } from "react-query";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { ParamsEnum } from "../../enums/params"; import { ParamsEnum } from "../../enums/params";
@ -18,239 +30,198 @@ import { useGetAllUnit } from "../../api/unit";
import { useGetAllLesson } from "../../api/lesson"; import { useGetAllLesson } from "../../api/lesson";
const EditPage: React.FC = () => { const EditPage: React.FC = () => {
const { question_id, subject_id, unit_id } = useParams<ParamsEnum>();
const {question_id,subject_id,unit_id} = useParams<ParamsEnum>() const { isBseQuestion, setIsBseQuestion, setTagsSearch, DeletedQuestions } =
const {isBseQuestion,setIsBseQuestion,setTagsSearch,DeletedQuestions} = useObjectToEdit() useObjectToEdit();
const { mutate, isSuccess, isLoading } = useUpdateQuestion(); const { mutate, isSuccess, isLoading } = useUpdateQuestion();
const { mutate:DeleteQuestion} = useDeleteQuestion(); const { mutate: DeleteQuestion } = useDeleteQuestion();
const { mutate:mutateAdd } = useAddQuestion(); const { mutate: mutateAdd } = useAddQuestion();
const {data,isLoading:dataLoading}= useGetAllQuestion({show:question_id}) const { data, isLoading: dataLoading } = useGetAllQuestion({
show: question_id,
});
const {data:Questions,isLoading:QuestionsDataLoading}= useGetAllQuestion({questionParentId:question_id ,onlyWithNoParents:false}) const { data: Questions, isLoading: QuestionsDataLoading } =
useGetAllQuestion({
const objectToEdit = {...data?.data,Questions:Questions?.data } ; questionParentId: question_id,
onlyWithNoParents: false,
const {lesson_id} = useParams() });
const objectToEdit = { ...data?.data, Questions: Questions?.data };
const { lesson_id } = useParams();
useEffect(() => { useEffect(() => {
if(objectToEdit?.isBase && isBseQuestion !== true){ if (objectToEdit?.isBase && isBseQuestion !== true) {
setIsBseQuestion(true) setIsBseQuestion(true);
} }
}, [objectToEdit?.isBase]) }, [objectToEdit?.isBase]);
const { data: unit } = useGetAllUnit({ show: unit_id }); const { data: unit } = useGetAllUnit({ show: unit_id });
const { data: lesson } = useGetAllLesson({ show: lesson_id }); const { data: lesson } = useGetAllLesson({ show: lesson_id });
const [t] = useTranslation() const [t] = useTranslation();
const unitName = unit?.data?.name ?? ""; const unitName = unit?.data?.name ?? "";
const SubjectName = unit?.data?.subject?.name ?? ""; const SubjectName = unit?.data?.subject?.name ?? "";
const lessonName = lesson?.data?.name ?? ""; const lessonName = lesson?.data?.name ?? "";
useSetPageTitle( useSetPageTitle(
t(`page_header.subject`) + t(`page_header.subject`) +
"/" +
`${SubjectName}` +
"/" + "/" +
t(`page_title.unit`) + `${SubjectName}` +
"/" + "/" +
`${unitName}`+"/" t(`PageTitle.unit`) +
+ "/" +
t(`page_title.lesson`) + `${unitName}` +
"/" + "/" +
`${lessonName}`+ "/" + t(`PageTitle.lesson`) +
t(`page_title.questions`) "/" +
`${lessonName}` +
"/" +
t(`PageTitle.questions`),
); );
const handleSubmit = (values: any) => { const handleSubmit = (values: any) => {
const DataToSend = structuredClone(values); const DataToSend = structuredClone(values);
setTagsSearch(null) setTagsSearch(null);
console.log(DataToSend); console.log(DataToSend);
if(isBseQuestion){ if (isBseQuestion) {
const UpdateBseQuestion = {
const UpdateBseQuestion = { id: DataToSend?.id,
"id":DataToSend?.id, content: DataToSend?.content,
"content" : DataToSend?.content, image: DataToSend?.image ?? "",
"image": DataToSend?.image ?? "", };
} if (
if( typeof UpdateBseQuestion?.image === "string" && UpdateBseQuestion?.image !== ""){ typeof UpdateBseQuestion?.image === "string" &&
delete UpdateBseQuestion["image"] UpdateBseQuestion?.image !== ""
} ) {
console.log(DeletedQuestions,"DeletedQuestions"); delete UpdateBseQuestion["image"];
console.log(UpdateBseQuestion); }
console.log(DeletedQuestions, "DeletedQuestions");
mutate(UpdateBseQuestion) console.log(UpdateBseQuestion);
mutate(UpdateBseQuestion);
DeletedQuestions?.map((item:any)=>{ DeletedQuestions?.map((item: any) => {
DeleteQuestion({id:item?.id}) DeleteQuestion({ id: item?.id });
});
})
const Questions = DataToSend?.Questions;
console.log(Questions, "Questions");
const Questions = DataToSend?.Questions; Questions?.map((item: Question) => {
console.log(Questions,"Questions"); console.log(item);
if (item?.id) {
Questions?.map((item:Question)=>{ const itemToSend = structuredClone(item);
console.log(item); const keysToRemove = ["image", "answer_image"];
if(item?.id){ const updatedObject = removeStringKeys(itemToSend, keysToRemove);
console.log(updatedObject, "updatedObject");
const itemToSend = structuredClone(item); const tags = processTags(updatedObject);
const keysToRemove = ['image', 'answer_image'];
const updatedObject = removeStringKeys(itemToSend, keysToRemove);
console.log(updatedObject,"updatedObject");
const tags = processTags(updatedObject)
const oldQuestionOptions = [] as any;
const newQuestionOptions = [] as any;
updatedObject?.QuestionOptions?.forEach((item:any) => {
if (item?.id) {
oldQuestionOptions.push(item);
} else {
newQuestionOptions.push(item);
}
});
const QuestionOptions = {
old: oldQuestionOptions,
new: newQuestionOptions
} ;
console.log(QuestionOptions);
mutate({
...updatedObject,
QuestionOptions,
tags
})
}else{
console.log(values?.id);
const tags = processTags(DataToSend);
mutateAdd({ ...item, subject_id: subject_id, tags , "lessons_ids":[lesson_id], parent_id:values?.id })
}
})
}else{
const keysToRemove = ['image', 'answer_image'];
const updatedObject = removeStringKeys(DataToSend, keysToRemove);
delete updatedObject["parent_id"];
const tags = processTags(updatedObject)
console.log(updatedObject,"updatedObject");
if(!(updatedObject?.image)){
updatedObject["image"] = "" ;
}
console.log(updatedObject);
const oldQuestionOptions = [] as any; const oldQuestionOptions = [] as any;
const newQuestionOptions = [] as any; const newQuestionOptions = [] as any;
updatedObject?.QuestionOptions?.forEach((item:any) => { updatedObject?.QuestionOptions?.forEach((item: any) => {
if (item?.id) { if (item?.id) {
oldQuestionOptions.push(item); oldQuestionOptions.push(item);
} else { } else {
newQuestionOptions.push(item); newQuestionOptions.push(item);
} }
}); });
const QuestionOptions = { const QuestionOptions = {
old: oldQuestionOptions, old: oldQuestionOptions,
new: newQuestionOptions new: newQuestionOptions,
}; };
mutate({ ...updatedObject,QuestionOptions,tags }); console.log(QuestionOptions);
mutate({
...updatedObject,
QuestionOptions,
tags,
});
} else {
console.log(values?.id);
const tags = processTags(DataToSend);
mutateAdd({
...item,
subject_id: subject_id,
tags,
lessons_ids: [lesson_id],
parent_id: values?.id,
});
}
});
} else {
const keysToRemove = ["image", "answer_image"];
const updatedObject = removeStringKeys(DataToSend, keysToRemove);
delete updatedObject["parent_id"];
const tags = processTags(updatedObject);
console.log(updatedObject, "updatedObject");
if (!updatedObject?.image) {
updatedObject["image"] = "";
} }
}; console.log(updatedObject);
const navigate = useNavigate() const oldQuestionOptions = [] as any;
const handleCancel = () => { const newQuestionOptions = [] as any;
navigate(-1)
updatedObject?.QuestionOptions?.forEach((item: any) => {
if (item?.id) {
// if(!item?.answer_image){
// item["answer_image"] = ""
// }
console.log(item);
oldQuestionOptions.push(item);
} else {
newQuestionOptions.push(item);
}
});
const QuestionOptions = {
old: oldQuestionOptions,
new: newQuestionOptions,
};
console.log(QuestionOptions, "QuestionOptions");
mutate({ ...updatedObject, QuestionOptions, tags });
}
}; };
const navigate = useNavigate();
const handleCancel = () => {
navigate(-1);
};
useEffect(() => { useEffect(() => {
if(isSuccess){ if (isSuccess) {
toast.success(t("validation.the_possess_done_successful")) toast.success(t("validation.the_possess_done_successful"));
navigate(-1) navigate(-1);
} }
}, [isSuccess]) }, [isSuccess]);
if (dataLoading || QuestionsDataLoading) {
if(dataLoading || QuestionsDataLoading){ return <SpinContainer />;
return <SpinContainer/>
} }
if(objectToEdit?.isBase){ if (objectToEdit?.isBase) {
return ( return (
<div className="exercise_add"> <div className="exercise_add">
<FormikForm
handleSubmit={handleSubmit}
initialValues={getInitialValuesBase(objectToEdit)}
validationSchema={getValidationSchemaBase}
>
<main className="w-100 exercise_add_main">
{/* <Header/> */}
<header className="exercise_add_header mb-4">
<div>
{t("practical.edit")} {t("models.exercise")}{" "}
</div>
<div>
{t("header.exercise") }
</div>
</header>
<BaseForm />
<div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div>
<button disabled={isLoading} className="relative" type="submit">
{t("practical.edit")}
{isLoading && (
<span className="Spinier_Div">
<Spin />
</span>
)}
</button>
</div>
</main>
</FormikForm>
</div>
);
}
return (
<div className="exercise_add">
<FormikForm <FormikForm
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
initialValues={getInitialValues(objectToEdit)} initialValues={getInitialValuesBase(objectToEdit)}
validationSchema={getValidationSchema} validationSchema={getValidationSchemaBase}
> >
<main className="w-100 exercise_add_main"> <main className="w-100 exercise_add_main">
{/* <Header/> */} {/* <Header/> */}
<header className="exercise_add_header mb-4"> <header className="exercise_add_header mb-4">
<div> <div>
{t("practical.edit")} {t("models.exercise")}{" "} {t("practical.edit")} {t("models.exercise")}{" "}
</div> </div>
<div> <div>{t("header.exercise")}</div>
</header>
{t("header.exercise") } <BaseForm />
</div>
</header>
<ModelForm />
<div className="exercise_add_buttons"> <div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div> <div onClick={handleCancel}>{t("practical.back")}</div>
<button disabled={isLoading} className="relative" type="submit"> <button disabled={isLoading} className="relative" type="submit">
@ -265,7 +236,40 @@ const EditPage: React.FC = () => {
</div> </div>
</main> </main>
</FormikForm> </FormikForm>
</div>
);
}
return (
<div className="exercise_add">
<FormikForm
handleSubmit={handleSubmit}
initialValues={getInitialValues(objectToEdit)}
validationSchema={getValidationSchema}
>
<main className="w-100 exercise_add_main">
{/* <Header/> */}
<header className="exercise_add_header mb-4">
<div>
{t("practical.edit")} {t("models.exercise")}{" "}
</div>
<div>{t("header.exercise")}</div>
</header>
<ModelForm />
<div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div>
<button disabled={isLoading} className="relative" type="submit">
{t("practical.edit")}
{isLoading && (
<span className="Spinier_Div">
<Spin />
</span>
)}
</button>
</div>
</main>
</FormikForm>
</div> </div>
); );
}; };

View File

@ -9,24 +9,20 @@ import { QUESTION_OBJECT_KEY } from "../../../config/AppKey";
const AcceptModal: React.FC = () => { const AcceptModal: React.FC = () => {
const { isOpen, setIsOpen } = useModalState((state) => state); const { isOpen, setIsOpen } = useModalState((state) => state);
const navigate = useNavigate() const navigate = useNavigate();
const handleSubmit = () => { const handleSubmit = () => {
localStorage.removeItem(QUESTION_OBJECT_KEY) localStorage.removeItem(QUESTION_OBJECT_KEY);
console.log("Handle submit clicked"); console.log("Handle submit clicked");
setIsOpen(""); setIsOpen("");
navigate(-1) navigate(-1);
}; };
const handleCancel = () => { const handleCancel = () => {
setIsOpen(""); setIsOpen("");
}; };
const [t] = useTranslation(); const [t] = useTranslation();
return ( return (
<> <>
@ -38,29 +34,20 @@ const AcceptModal: React.FC = () => {
open={isOpen === ModalEnum?.QUESTION_ACCEPT} open={isOpen === ModalEnum?.QUESTION_ACCEPT}
onCancel={handleCancel} onCancel={handleCancel}
> >
<header> <header> {t("practical.accept_back")}</header>
{" "}
{t("practical.accept_back")}
</header>
<main className="main_modal"> <main className="main_modal">
<div className="ValidationField w-100 mb-5"> <div className="ValidationField w-100 mb-5">
<label className="text h1 "> <label className="text h1 ">
{t("practical.Are you sure you want to go back and not save any changes?")}{" "} {t(
"practical.Are you sure you want to go back and not save any changes?",
)}{" "}
</label> </label>
</div> </div>
<div className="buttons"> <div className="buttons">
<div onClick={handleCancel}>{t("practical.cancel")}</div> <div onClick={handleCancel}>{t("practical.cancel")}</div>
<div <div onClick={handleSubmit}>{t("practical.accept")}</div>
onClick={handleSubmit}
>
{t("practical.accept")}
</div>
</div> </div>
</main> </main>
</Modal> </Modal>

View File

@ -12,33 +12,34 @@ const CheckboxField = ({
className, className,
props, props,
}: any) => { }: any) => {
const formik = useFormikContext<any>() const formik = useFormikContext<any>();
const [t] = useTranslation() const [t] = useTranslation();
const CheckboxhandleChange = (value: any, index: number) => { const CheckboxhandleChange = (value: any, index: number) => {
const allAreZero = formik?.values?.QuestionOptions?.some(
const allAreZero = formik?.values?.QuestionOptions?.some((item: any) => item.isCorrect === 1); (item: any) => item.isCorrect === 1,
);
if (allAreZero) {
if (allAreZero) {
formik?.values.QuestionOptions.forEach((item: any,index:number) => { formik?.values.QuestionOptions.forEach((item: any, index: number) => {
formik.setFieldValue(`QuestionOptions[${index}].isCorrect`, 0); formik.setFieldValue(`QuestionOptions[${index}].isCorrect`, 0);
}); });
} }
formik.setFieldValue(`QuestionOptions[${name}].isCorrect`, value?.target?.checked ? 1 : 0); formik.setFieldValue(
`QuestionOptions[${name}].isCorrect`,
value?.target?.checked ? 1 : 0,
);
}; };
return ( return (
<div className={Group ? "d-inline mt-5 Checkbox" : ``}> <div className={Group ? "d-inline mt-5 Checkbox" : ``}>
<Checkbox <Checkbox
onChange={onChange || CheckboxhandleChange} onChange={onChange || CheckboxhandleChange}
disabled={isDisabled} disabled={isDisabled}
checked={formik.values?.QuestionOptions?.[name]?.isCorrect === 1} checked={formik.values?.QuestionOptions?.[name]?.isCorrect === 1}
className={className} className={className}
> >
{t(`input.${label ? label : name}`)} {t(`input.${label ? label : name}`)}
</Checkbox> </Checkbox>
</div> </div>
); );
}; };

View File

@ -1,43 +1,60 @@
import React from 'react' import React from "react";
import { Choice } from '../../../../types/Item' import { Choice } from "../../../../types/Item";
import ValidationField from '../../../../Components/ValidationField/ValidationField' import ValidationField from "../../../../Components/ValidationField/ValidationField";
import { useFormikContext } from 'formik'; import { useFormikContext } from "formik";
import { useTranslation } from 'react-i18next'; import { useTranslation } from "react-i18next";
import { getCharFromNumber } from '../../../../utils/getCharFromNumber'; import { getCharFromNumber } from "../../../../utils/getCharFromNumber";
import CheckboxField from './CheckboxField'; import CheckboxField from "./CheckboxField";
import TextField from './TextField'; import TextField from "./TextField";
import File from './File'; import File from "./File";
import { FaCirclePlus, FaDeleteLeft } from 'react-icons/fa6'; import { FaCirclePlus, FaDeleteLeft } from "react-icons/fa6";
import { FaTrash } from 'react-icons/fa'; import { FaTrash } from "react-icons/fa";
const ChoiceFields = ({index,data}:{index:number , data :Choice }) => { const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const [t] = useTranslation() const [t] = useTranslation();
const handleDeleteChoice = () => { const handleDeleteChoice = () => {
console.log(index); console.log(index);
console.log(formik.values.QuestionOptions[index]); console.log(formik.values.QuestionOptions[index]);
const updatedQuestionOptions = formik.values.QuestionOptions.filter((_:any, i:any) => i !== index); const updatedQuestionOptions = formik.values.QuestionOptions.filter(
(_: any, i: any) => i !== index,
formik.setFieldValue('QuestionOptions', updatedQuestionOptions); );
};
console.log(formik.values); formik.setFieldValue("QuestionOptions", updatedQuestionOptions);
};
console.log(formik.values);
return ( return (
<div className='ChoiceFields'> <div className="ChoiceFields">
<TextField
className="textarea_exercise"
<TextField className="textarea_exercise" placeholder={"choice"} label2={t(`input.choice`) + ` ` + `(${(getCharFromNumber(index))})` } name={index} id={`choice_${index + 1}`} type="TextArea" /> placeholder={"choice"}
<File className="file_exercise" label={"attachment"} name={index} type="File" /> label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
name={index}
<CheckboxField className="" label="The_correct_answer" name={index} type="Checkbox" /> id={`choice_${index + 1}`}
<p className="delete_question_options" > type="TextArea"
<FaTrash onClick={handleDeleteChoice} size={17} /> />
</p> <File
</div> className="file_exercise"
) label={"attachment"}
} name={index}
type="File"
/>
export default ChoiceFields <CheckboxField
className=""
label="The_correct_answer"
name={index}
type="Checkbox"
/>
<p className="delete_question_options">
<FaTrash onClick={handleDeleteChoice} size={17} />
</p>
</div>
);
};
export default ChoiceFields;

View File

@ -12,30 +12,29 @@ const File = ({
className, className,
props, props,
}: any) => { }: any) => {
const newName = `QuestionOptions[${name}].answer_image`;
const newName = `QuestionOptions[${name}].answer_image` const { formik, t, isError, errorMsg } = useFormField(newName, props);
const { formik, t, isError,errorMsg } = useFormField(newName, props);
let imageUrl = formik?.values?.QuestionOptions[name]?.answer_image ?? null; let imageUrl = formik?.values?.QuestionOptions[name]?.answer_image ?? null;
// console.log(imageUrl); // console.log(imageUrl);
console.log(imageUrl); console.log(imageUrl);
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,
}, },
]; ];
@ -47,8 +46,10 @@ const File = ({
if (value.fileList.length === 0) { if (value.fileList.length === 0) {
formik.setFieldValue(newName, null); formik.setFieldValue(newName, null);
} else { } else {
formik.setFieldValue(`QuestionOptions[${name}].answer_image`, value?.file?.originFileObj); formik.setFieldValue(
`QuestionOptions[${name}].answer_image`,
value?.file?.originFileObj,
);
} }
}; };
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => { const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
@ -74,10 +75,9 @@ 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}
</Upload> </Upload>
</div> </div>
); );

View File

@ -16,9 +16,9 @@ const TextField = ({
no_label, no_label,
label_icon, label_icon,
id, id,
className className,
}: any) => { }: any) => {
const newName = `QuestionOptions[${name}].answer` const newName = `QuestionOptions[${name}].answer`;
const { formik, isError, errorMsg, t } = useFormField(newName, props); const { formik, isError, errorMsg, t } = useFormField(newName, props);
const TextFilehandleChange = ( const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
@ -26,7 +26,7 @@ const TextField = ({
// console.log('Change:', e.target.value); // console.log('Change:', e.target.value);
formik.setFieldValue(newName, e.target.value); formik.setFieldValue(newName, e.target.value);
}; };
return ( return (
<div className={`ValidationField w-100 ${className ?? ""} `}> <div className={`ValidationField w-100 ${className ?? ""} `}>
{no_label ? ( {no_label ? (
@ -36,14 +36,14 @@ const TextField = ({
) : label_icon ? ( ) : label_icon ? (
<div className="LabelWithIcon"> <div className="LabelWithIcon">
<label htmlFor={name} className="text"> <label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`) } {label2 ? label2 : t(`input.${label ? label : name}`)}
</label> </label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} /> <MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div> </div>
) : ( ) : (
<label htmlFor={name} className="text"> <label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`) } {label2 ? label2 : t(`input.${label ? label : name}`)}
</label> </label>
)} )}
<Form.Item <Form.Item
@ -60,9 +60,8 @@ const TextField = ({
showCount showCount
maxLength={1000} maxLength={1000}
onChange={onChange || TextFilehandleChange} onChange={onChange || TextFilehandleChange}
style={{height:120}} style={{ height: 120 }}
id={id} id={id}
/> />
</Form.Item> </Form.Item>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Col, Row } from "reactstrap"; import { Col, Row } from "reactstrap";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import ValidationField from "../../../../Components/ValidationField/ValidationField"; import ValidationField from "../../../../Components/ValidationField/ValidationField";
import { useFormikContext } from "formik"; import { useFormikContext } from "formik";
@ -15,121 +15,136 @@ const Form = () => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const { isOpen } = useModalState((state) => state); const { isOpen } = useModalState((state) => state);
// const {data} = useGetAllQuestion(); // const {data} = useGetAllQuestion();
const{setSuccess,Success,setSavedQuestionData} = useObjectToEdit() const { setSuccess, Success, setSavedQuestionData } = useObjectToEdit();
useEffect(() => { useEffect(() => {
if (Success) { if (Success) {
console.log(1); console.log(1);
formik.setErrors({}); formik.setErrors({});
formik.resetForm({ values: {} }); formik.resetForm({ values: {} });
setSuccess(false) setSuccess(false);
} }
}, [Success]); }, [Success]);
useEffect(() => { useEffect(() => {
setSavedQuestionData(formik.values) setSavedQuestionData(formik.values);
}, [formik?.values]) }, [formik?.values]);
// console.log(formik?.errors); // console.log(formik?.errors);
console.log(formik?.values?.Questions,"formik?.values?.Questions"); console.log(formik?.values?.Questions, "formik?.values?.Questions");
const handleAddChoice = (parent_index: number) => {
const handleAddChoice = (parent_index:number) => {
console.log(parent_index); console.log(parent_index);
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [...(formik?.values as any)?.Questions?.[parent_index].QuestionOptions as Choice[], formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
...((formik?.values as any)?.Questions?.[parent_index]
.QuestionOptions as Choice[]),
{ {
answer:null, answer: null,
answer_image:null, answer_image: null,
isCorrect:0 isCorrect: 0,
}]) },
} ]);
};
const handleAddQuestion = () => { const handleAddQuestion = () => {
formik.setFieldValue('Questions', [...(formik?.values as any)?.Questions as Choice[], formik.setFieldValue("Questions", [
...((formik?.values as any)?.Questions as Choice[]),
{ {
content: "", content: "",
image: "", image: "",
parent: '', parent: "",
isBase: 0, isBase: 0,
max_mark: 1, max_mark: 1,
min_mark_to_pass: 1, min_mark_to_pass: 1,
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }], QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
tags: [] tags: [],
}]) },
]);
const max_mark = formik?.values?.max_mark + 1 const max_mark = formik?.values?.max_mark + 1;
formik.setFieldValue('max_mark', max_mark) formik.setFieldValue("max_mark", max_mark);
} };
const [t] = useTranslation() const [t] = useTranslation();
console.log(formik.errors); console.log(formik.errors);
return ( return (
<Row className="w-100"> <Row className="w-100">
<div className="exercise_form"> <div className="exercise_form">
<ValidationField
className="textarea_exercise"
name="content"
label="main_question"
type="TextArea"
/>
<ValidationField
className="file_exercise"
name="image"
label="attachment"
type="File"
/>
<ValidationField className="textarea_exercise" name="content" label="main_question" type="TextArea" /> {/* <div className="">
<ValidationField className="file_exercise" name="image" label="attachment" type="File" />
{/* <div className="">
<ValidationField name="max_mark" label="max_mark" type="Number" className="inputSmall" disabled /> <ValidationField name="max_mark" label="max_mark" type="Number" className="inputSmall" disabled />
<ValidationField name="min_mark_to_pass" label="min_mark_to_pass" className="inputSmall" type="Number" /> <ValidationField name="min_mark_to_pass" label="min_mark_to_pass" className="inputSmall" type="Number" />
</div> */} </div> */}
<div> <div></div>
</div>
<div className=" flex "></div>
</div> {((formik?.values as any)?.Questions || [])?.map(
(item: Choice, parent_index: number) => {
</div> return (
<div className=" flex "> <div key={parent_index}>
<div className="exercise_form">
<QuestionFIeld
key={parent_index}
index={parent_index}
data={item}
/>
</div>
</div> {(
(formik?.values as any)?.Questions?.[parent_index]
?.QuestionOptions || []
).map((item: Choice, index: number) => {
return (
<ChoiceFields
key={index}
parent_index={parent_index}
index={index}
data={item}
/>
);
})}
{ {formik?.values?.Questions?.[parent_index]?.QuestionOptions
(((formik?.values as any)?.Questions)||[])?.map((item:Choice,parent_index:number)=>{ ?.length < 5 && (
<p className="add_new_button">
return ( <FaCirclePlus
<div key={parent_index}> onClick={() => handleAddChoice(parent_index)}
size={23}
<div className="exercise_form"> />{" "}
{t("header.add_new_choice")}
<QuestionFIeld key={parent_index} index={parent_index} data={item}/> </p>
</div> )}
{
(((formik?.values as any)?.Questions?.[parent_index]?.QuestionOptions)||[]) .map((item:Choice,index:number)=>{
return <ChoiceFields key={index} parent_index={parent_index} index={index} data={item}/>
}
)
}
{formik?.values?.Questions?.[parent_index]?.QuestionOptions?.length < 5 && (
<p className="add_new_button" >
<FaCirclePlus onClick={()=> handleAddChoice(parent_index)} size={23} /> {t("header.add_new_choice")}
</p>
<DynamicTags parent_index={parent_index} />
</div>
);
},
)} )}
<p className="add_new_button">
<DynamicTags parent_index={parent_index} /> <FaCirclePlus onClick={handleAddQuestion} size={23} />{" "}
</div> {t("header.add_new_question")}
) </p>
} </Row>
)
}
<p className="add_new_button" >
<FaCirclePlus onClick={handleAddQuestion} size={23} /> {t("header.add_new_question")}
</p>
</Row>
); );
}; };

View File

@ -13,36 +13,43 @@ const CheckboxField = ({
parent_index, parent_index,
props, props,
}: any) => { }: any) => {
const formik = useFormikContext<any>() const formik = useFormikContext<any>();
const [t] = useTranslation() const [t] = useTranslation();
const CheckboxhandleChange = (value: any) => { const CheckboxhandleChange = (value: any) => {
console.log(value?.target?.checked); console.log(value?.target?.checked);
const allAreZero = formik?.values?.Questions?.[
const allAreZero = formik?.values?.Questions?.[parent_index]?.QuestionOptions?.some((item: any) => item.isCorrect === 1); parent_index
if (allAreZero) { ]?.QuestionOptions?.some((item: any) => item.isCorrect === 1);
if (allAreZero) {
formik?.values?.Questions?.[parent_index]?.QuestionOptions.forEach((item: any,index:number) => { formik?.values?.Questions?.[parent_index]?.QuestionOptions.forEach(
(item: any, index: number) => {
formik.setFieldValue(`Questions[${parent_index}].QuestionOptions[${index}].isCorrect`, 0); formik.setFieldValue(
}); `Questions[${parent_index}].QuestionOptions[${index}].isCorrect`,
0,
);
},
);
} }
formik.setFieldValue(`Questions[${parent_index}].QuestionOptions[${name}].isCorrect`, value?.target?.checked ? 1 : 0); formik.setFieldValue(
`Questions[${parent_index}].QuestionOptions[${name}].isCorrect`,
value?.target?.checked ? 1 : 0,
}; );
};
return ( return (
<div className={Group ? "d-inline mt-5 Checkbox" : ``}> <div className={Group ? "d-inline mt-5 Checkbox" : ``}>
<Checkbox <Checkbox
onChange={onChange || CheckboxhandleChange} onChange={onChange || CheckboxhandleChange}
disabled={isDisabled} disabled={isDisabled}
checked={formik.values?.Questions?.[parent_index]?.QuestionOptions?.[name]?.isCorrect === 1} checked={
className={className} formik.values?.Questions?.[parent_index]?.QuestionOptions?.[name]
> ?.isCorrect === 1
{t(`input.${label ? label : name}`)} }
</Checkbox> className={className}
>
{t(`input.${label ? label : name}`)}
</Checkbox>
</div> </div>
); );
}; };

View File

@ -1,50 +1,79 @@
import React from 'react' import React from "react";
import { Choice } from '../../../../../types/Item' import { Choice } from "../../../../../types/Item";
import ValidationField from '../../../../../Components/ValidationField/ValidationField' import ValidationField from "../../../../../Components/ValidationField/ValidationField";
import { useFormikContext } from 'formik'; import { useFormikContext } from "formik";
import { useTranslation } from 'react-i18next'; import { useTranslation } from "react-i18next";
import { getCharFromNumber } from '../../../../../utils/getCharFromNumber'; import { getCharFromNumber } from "../../../../../utils/getCharFromNumber";
import CheckboxField from './CheckboxField'; import CheckboxField from "./CheckboxField";
import TextField from './TextField'; import TextField from "./TextField";
import File from './File'; import File from "./File";
import { FaTrash } from 'react-icons/fa'; import { FaTrash } from "react-icons/fa";
import { toast } from 'react-toastify'; import { toast } from "react-toastify";
const ChoiceFields = ({index,parent_index,data}:{index:number ,parent_index:number, data :Choice }) => { const ChoiceFields = ({
index,
parent_index,
data,
}: {
index: number;
parent_index: number;
data: Choice;
}) => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const [t] = useTranslation() const [t] = useTranslation();
const handleDeleteChoice = () => { const handleDeleteChoice = () => {
const arrayLength = formik.values.Questions?.[parent_index].QuestionOptions?.length const arrayLength =
formik.values.Questions?.[parent_index].QuestionOptions?.length;
console.log(arrayLength); console.log(arrayLength);
if(arrayLength === 1)
{
toast.error(t("validation.Sorry, the question must have at least one option"))
return ;
}
if (arrayLength === 1) {
const updatedQuestionOptions = formik.values.Questions?.[parent_index].QuestionOptions.filter((_:any, i:any) => i !== index); toast.error(
formik.setFieldValue(`Questions[${parent_index}].QuestionOptions`, updatedQuestionOptions); t("validation.Sorry, the question must have at least one option"),
);
return;
} }
;
const updatedQuestionOptions = formik.values.Questions?.[
parent_index
].QuestionOptions.filter((_: any, i: any) => i !== index);
formik.setFieldValue(
`Questions[${parent_index}].QuestionOptions`,
updatedQuestionOptions,
);
};
return ( return (
<div className='ChoiceFields'> <div className="ChoiceFields">
<TextField
className="textarea_exercise"
<TextField className="textarea_exercise" placeholder={"choice"} label2={t(`input.choice`) + ` ` + `(${(getCharFromNumber(index))})` } name={index} parent_index={parent_index} type="TextArea" /> placeholder={"choice"}
<File className="file_exercise" label={"attachment"} name={index} type="File" parent_index={parent_index} /> label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
name={index}
<CheckboxField className="" label="The_correct_answer" name={index} type="Checkbox" parent_index={parent_index} /> parent_index={parent_index}
<p className="delete_question_options" > type="TextArea"
<FaTrash onClick={handleDeleteChoice} size={17} /> />
</p> <File
</div> className="file_exercise"
) label={"attachment"}
} name={index}
type="File"
parent_index={parent_index}
/>
export default ChoiceFields <CheckboxField
className=""
label="The_correct_answer"
name={index}
type="Checkbox"
parent_index={parent_index}
/>
<p className="delete_question_options">
<FaTrash onClick={handleDeleteChoice} size={17} />
</p>
</div>
);
};
export default ChoiceFields;

View File

@ -13,29 +13,30 @@ const File = ({
parent_index, parent_index,
props, props,
}: any) => { }: any) => {
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer_image`;
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer_image` const { formik, t, isError, errorMsg } = useFormField(newName, props);
let imageUrl =
const { formik, t, isError,errorMsg } = useFormField(newName, props); formik?.values?.Questions?.[parent_index]?.QuestionOptions[name]
let imageUrl = formik?.values?.Questions?.[parent_index]?.QuestionOptions[name]?.answer_image ?? null; ?.answer_image ?? null;
// console.log(imageUrl); // console.log(imageUrl);
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,
}, },
]; ];
@ -47,8 +48,10 @@ const File = ({
if (value.fileList.length === 0) { if (value.fileList.length === 0) {
formik.setFieldValue(newName, null); formik.setFieldValue(newName, null);
} else { } else {
formik.setFieldValue(`Questions[${parent_index}].QuestionOptions[${name}].answer_image`, value?.file?.originFileObj); formik.setFieldValue(
`Questions[${parent_index}].QuestionOptions[${name}].answer_image`,
value?.file?.originFileObj,
);
} }
}; };
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => { const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
@ -74,10 +77,9 @@ 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}
</Upload> </Upload>
</div> </div>
); );

View File

@ -16,9 +16,9 @@ const TextField = ({
no_label, no_label,
label_icon, label_icon,
parent_index, parent_index,
className className,
}: any) => { }: any) => {
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer` const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer`;
const { formik, isError, errorMsg, t } = useFormField(newName, props); const { formik, isError, errorMsg, t } = useFormField(newName, props);
const TextFilehandleChange = ( const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
@ -26,7 +26,7 @@ const TextField = ({
// console.log('Change:', e.target.value); // console.log('Change:', e.target.value);
formik.setFieldValue(newName, e.target.value); formik.setFieldValue(newName, e.target.value);
}; };
return ( return (
<div className={`ValidationField w-100 ${className ?? ""} `}> <div className={`ValidationField w-100 ${className ?? ""} `}>
{no_label ? ( {no_label ? (
@ -36,14 +36,14 @@ const TextField = ({
) : label_icon ? ( ) : label_icon ? (
<div className="LabelWithIcon"> <div className="LabelWithIcon">
<label htmlFor={name} className="text"> <label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`) } {label2 ? label2 : t(`input.${label ? label : name}`)}
</label> </label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} /> <MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div> </div>
) : ( ) : (
<label htmlFor={name} className="text"> <label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`) } {label2 ? label2 : t(`input.${label ? label : name}`)}
</label> </label>
)} )}
<Form.Item <Form.Item
@ -60,9 +60,7 @@ const TextField = ({
showCount showCount
maxLength={1000} maxLength={1000}
onChange={onChange || TextFilehandleChange} onChange={onChange || TextFilehandleChange}
style={{height:120}} style={{ height: 120 }}
/> />
</Form.Item> </Form.Item>
</div> </div>

View File

@ -16,7 +16,7 @@ const Form = () => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const { isOpen } = useModalState((state) => state); const { isOpen } = useModalState((state) => state);
// const {data} = useGetAllQuestion(); // const {data} = useGetAllQuestion();
useEffect(() => { useEffect(() => {
if (isOpen === "") { if (isOpen === "") {
formik.setErrors({}); formik.setErrors({});
@ -24,101 +24,118 @@ const Form = () => {
} }
}, [isOpen]); }, [isOpen]);
// console.log(formik?.errors); // console.log(formik?.errors);
const [t] = useTranslation() const [t] = useTranslation();
const handleAddChoice = (parent_index: number) => {
const handleAddChoice = (parent_index:number) => {
console.log(parent_index); console.log(parent_index);
console.log(formik?.values?.Questions); console.log(formik?.values?.Questions);
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [...(formik?.values as any)?.Questions?.[parent_index].QuestionOptions as Choice[], formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
...((formik?.values as any)?.Questions?.[parent_index]
.QuestionOptions as Choice[]),
{ {
answer:null, answer: null,
answer_image:null, answer_image: null,
isCorrect:0 isCorrect: 0,
}]) },
} ]);
};
const handleAddQuestion = () => { const handleAddQuestion = () => {
formik.setFieldValue('Questions', [...(formik?.values as any)?.Questions as Choice[], formik.setFieldValue("Questions", [
...((formik?.values as any)?.Questions as Choice[]),
{ {
content: "", content: "",
image: "", image: "",
parent: '', parent: "",
isBase: 0, isBase: 0,
max_mark: 1, max_mark: 1,
min_mark_to_pass: 1, min_mark_to_pass: 1,
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }], QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
tags: [] tags: [],
}]) },
]);
const max_mark = formik?.values?.max_mark + 1 const max_mark = formik?.values?.max_mark + 1;
formik.setFieldValue('max_mark', max_mark) formik.setFieldValue("max_mark", max_mark);
} };
console.log(formik?.values); console.log(formik?.values);
return ( return (
<Row className="w-100"> <Row className="w-100">
<div className="exercise_form"> <div className="exercise_form">
<ValidationField
className="textarea_exercise"
name="content"
label="main_question"
type="TextArea"
/>
<ValidationField
className="file_exercise"
name="image"
label="attachment"
type="File"
/>
<ValidationField className="textarea_exercise" name="content" label="main_question" type="TextArea" /> {/* <div className="">
<ValidationField className="file_exercise" name="image" label="attachment" type="File" />
{/* <div className="">
<ValidationField name="max_mark" label="max_mark" type="Number" className="inputSmall" disabled /> <ValidationField name="max_mark" label="max_mark" type="Number" className="inputSmall" disabled />
<ValidationField name="min_mark_to_pass" label="min_mark_to_pass" className="inputSmall" type="Number" /> <ValidationField name="min_mark_to_pass" label="min_mark_to_pass" className="inputSmall" type="Number" />
</div> */} </div> */}
<div> <div></div>
</div>
<div className=" flex "></div>
</div> {((formik?.values as any)?.Questions || [])?.map(
(item: Choice, parent_index: number) => {
</div> return (
<div className=" flex "> <div key={parent_index}>
<div className="exercise_form">
<QuestionFIeld
key={parent_index}
index={parent_index}
data={item}
/>
</div>
</div> {(
(formik?.values as any)?.Questions?.[parent_index]
{ ?.QuestionOptions || []
(((formik?.values as any)?.Questions)||[])?.map((item:Choice,parent_index:number)=>{ ).map((item: Choice, index: number) => {
return (
return ( <ChoiceFields
<div key={parent_index}> key={index}
parent_index={parent_index}
<div className="exercise_form"> index={index}
data={item}
<QuestionFIeld key={parent_index} index={parent_index} data={item}/> />
</div> );
})}
{ {formik?.values?.Questions?.[parent_index]?.QuestionOptions
(((formik?.values as any)?.Questions?.[parent_index]?.QuestionOptions)||[]) .map((item:Choice,index:number)=>{ ?.length < 5 && (
<p className="add_new_button">
return <ChoiceFields key={index} parent_index={parent_index} index={index} data={item}/> <FaCirclePlus
} onClick={() => handleAddChoice(parent_index)}
) size={23}
} />{" "}
{formik?.values?.Questions?.[parent_index]?.QuestionOptions?.length < 5 && ( {t("header.add_new_choice")}
<p className="add_new_button" > </p>
<FaCirclePlus onClick={()=> handleAddChoice(parent_index)} size={23} /> {t("header.add_new_choice")} )}
</p>
<DynamicTags parent_index={parent_index} />
</div>
);
},
)} )}
<DynamicTags parent_index={parent_index} />
</div>
)
}
)
}
<p className="add_new_button" > <p className="add_new_button">
<FaCirclePlus onClick={handleAddQuestion} size={23} /> {t("header.add_new_question")} <FaCirclePlus onClick={handleAddQuestion} size={23} />{" "}
</p> {t("header.add_new_question")}
</Row> </p>
</Row>
); );
}; };

View File

@ -12,29 +12,28 @@ const File = ({
className, className,
props, props,
}: any) => { }: any) => {
const newName = `Questions[${name}].image`;
const newName = `Questions[${name}].image` const { formik, t, isError, errorMsg } = useFormField(newName, props);
const { formik, t, isError,errorMsg } = useFormField(newName, props);
let imageUrl = formik?.values?.Questions?.[name]?.image ?? null; let imageUrl = formik?.values?.Questions?.[name]?.image ?? null;
// console.log(imageUrl); // console.log(imageUrl);
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,
}, },
]; ];
@ -46,8 +45,10 @@ const File = ({
if (value.fileList.length === 0) { if (value.fileList.length === 0) {
formik.setFieldValue(newName, null); formik.setFieldValue(newName, null);
} else { } else {
formik.setFieldValue(`Questions[${name}].image`, value?.file?.originFileObj); formik.setFieldValue(
`Questions[${name}].image`,
value?.file?.originFileObj,
);
} }
}; };
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => { const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
@ -73,10 +74,9 @@ 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}
</Upload> </Upload>
</div> </div>
); );

View File

@ -1,47 +1,56 @@
import React, { useEffect } from 'react' import React, { useEffect } from "react";
import { Choice } from '../../../../../types/Item' import { Choice } from "../../../../../types/Item";
import ValidationField from '../../../../../Components/ValidationField/ValidationField' import ValidationField from "../../../../../Components/ValidationField/ValidationField";
import { useFormikContext } from 'formik'; import { useFormikContext } from "formik";
import { useTranslation } from 'react-i18next'; import { useTranslation } from "react-i18next";
import { getCharFromNumber } from '../../../../../utils/getCharFromNumber'; import { getCharFromNumber } from "../../../../../utils/getCharFromNumber";
import TextField from './TextField'; import TextField from "./TextField";
import File from './File'; import File from "./File";
import { FaTrash } from 'react-icons/fa'; import { FaTrash } from "react-icons/fa";
import { useObjectToEdit } from '../../../../../zustand/ObjectToEditState'; import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
import { toast } from 'react-toastify'; import { toast } from "react-toastify";
const QuestionFIeld = ({index,data}:{index:number , data :Choice }) => { const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
console.log(index); console.log(index);
const {setDeletedQuestions,DeletedQuestions} = useObjectToEdit() const { setDeletedQuestions, DeletedQuestions } = useObjectToEdit();
const [t] = useTranslation() const [t] = useTranslation();
useEffect(() => { useEffect(() => {
setDeletedQuestions([]) setDeletedQuestions([]);
}, [window?.location.pathname]) }, [window?.location.pathname]);
const handleDeleteQuestion = () => {
const DeleteQuestionId = formik.values.Questions?.[index];
if(DeleteQuestionId?.id){
setDeletedQuestions([...DeletedQuestions,DeleteQuestionId])
} const handleDeleteQuestion = () => {
const updatedQuestionOptions = formik.values.Questions.filter((_:any, i:any) => i !== index); const DeleteQuestionId = formik.values.Questions?.[index];
formik.setFieldValue(`Questions`, updatedQuestionOptions); if (DeleteQuestionId?.id) {
}; setDeletedQuestions([...DeletedQuestions, DeleteQuestionId]);
}
const updatedQuestionOptions = formik.values.Questions.filter(
(_: any, i: any) => i !== index,
);
formik.setFieldValue(`Questions`, updatedQuestionOptions);
};
return ( return (
<div className='ChoiceFields'> <div className="ChoiceFields">
<TextField
<TextField className="textarea_exercise" placeholder={"choice"} label2={t(`input.question`)+ ` ` +`${index + 1}`} name={index} type="TextArea" /> className="textarea_exercise"
<File className="file_exercise" label={"attachment"} name={index} type="File" /> placeholder={"choice"}
<p className="delete_question_options" > label2={t(`input.question`) + ` ` + `${index + 1}`}
<FaTrash onClick={handleDeleteQuestion} size={17} /> name={index}
</p> type="TextArea"
</div> />
) <File
} className="file_exercise"
label={"attachment"}
name={index}
type="File"
/>
<p className="delete_question_options">
<FaTrash onClick={handleDeleteQuestion} size={17} />
</p>
</div>
);
};
export default QuestionFIeld export default QuestionFIeld;

View File

@ -15,9 +15,9 @@ const TextField = ({
props, props,
no_label, no_label,
label_icon, label_icon,
className className,
}: any) => { }: any) => {
const newName = `Questions[${name}].content` const newName = `Questions[${name}].content`;
const { formik, isError, errorMsg, t } = useFormField(newName, props); const { formik, isError, errorMsg, t } = useFormField(newName, props);
const TextFilehandleChange = ( const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
@ -25,7 +25,7 @@ const TextField = ({
// console.log('Change:', e.target.value); // console.log('Change:', e.target.value);
formik.setFieldValue(newName, e.target.value); formik.setFieldValue(newName, e.target.value);
}; };
return ( return (
<div className={`ValidationField w-100 ${className ?? ""} `}> <div className={`ValidationField w-100 ${className ?? ""} `}>
{no_label ? ( {no_label ? (
@ -35,14 +35,14 @@ const TextField = ({
) : label_icon ? ( ) : label_icon ? (
<div className="LabelWithIcon"> <div className="LabelWithIcon">
<label htmlFor={name} className="text"> <label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`) } {label2 ? label2 : t(`input.${label ? label : name}`)}
</label> </label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} /> <MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div> </div>
) : ( ) : (
<label htmlFor={name} className="text"> <label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`) } {label2 ? label2 : t(`input.${label ? label : name}`)}
</label> </label>
)} )}
<Form.Item <Form.Item
@ -59,9 +59,7 @@ const TextField = ({
showCount showCount
maxLength={1000} maxLength={1000}
onChange={onChange || TextFilehandleChange} onChange={onChange || TextFilehandleChange}
style={{height:120}} style={{ height: 120 }}
/> />
</Form.Item> </Form.Item>
</div> </div>

View File

@ -1,109 +1,107 @@
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";
import { useObjectToEdit } from '../../../../../zustand/ObjectToEditState' import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
import { useGetAllTag } from '../../../../../api/tags' import { useGetAllTag } from "../../../../../api/tags";
const DynamicTags = ({parent_index}:{parent_index:number}) => { const DynamicTags = ({ parent_index }: { parent_index: number }) => {
const formik = useFormikContext<any>();
const [t] = useTranslation();
const { TagsSearch, setTagsSearch, currentTag, currentParentIndex } =
useObjectToEdit();
const { data } = useGetAllTag({
name: TagsSearch,
});
const suggests = data?.data;
const formik = useFormikContext<any>()
const [t] = useTranslation()
const { TagsSearch ,setTagsSearch,currentTag,currentParentIndex} = useObjectToEdit();
const {data} = useGetAllTag({
name : TagsSearch
})
const suggests = data?.data
const handleAddChoice = () => { const handleAddChoice = () => {
const length = formik?.values?.Questions?.[parent_index]?.tags.length; const length = formik?.values?.Questions?.[parent_index]?.tags.length;
const lastElement = formik?.values?.Questions?.[parent_index]?.tags[length - 1]?.name; const lastElement =
setTagsSearch(null) formik?.values?.Questions?.[parent_index]?.tags[length - 1]?.name;
setTagsSearch(null);
if(lastElement !== ""){ if (lastElement !== "") {
formik.setFieldValue(`Questions.[${parent_index}].tags`, [...(formik?.values as any)?.Questions?.[parent_index]?.tags as any[], formik.setFieldValue(`Questions.[${parent_index}].tags`, [
...((formik?.values as any)?.Questions?.[parent_index]?.tags as any[]),
{
id:length +"_new",
name:"",
key:length
}])
}else{
{
id: length + "_new",
name: "",
key: length,
},
]);
} else {
} }
};
// console.log(formik?.values);
} // console.log(currentTag);
// console.log(formik?.values);
// console.log(currentTag);
const handleChoice = (item: any) => { const handleChoice = (item: any) => {
const length = formik?.values?.Questions?.[parent_index]?.tags?.length;
console.log(currentTag);
const length = formik?.values?.Questions?.[parent_index]?.tags?.length; formik.setFieldValue(`Questions.[${parent_index}].tags[${currentTag}]`, {
console.log(currentTag); ...item,
key: length,
formik.setFieldValue(`Questions.[${parent_index}].tags[${currentTag}]`, {...item,key:length}); });
setTagsSearch(null); setTagsSearch(null);
} };
// console.log(formik?.values?.tags?.length);
// console.log(formik?.values?.tags?.length);
return ( return (
<div className='DynamicTags'> <div className="DynamicTags">
{formik?.values?.Questions?.[parent_index]?.tags?.length < 1 && {formik?.values?.Questions?.[parent_index]?.tags?.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_tag")} {t("header.add_tag")}
</p> </p>
} )}
<div className="tag_container">
<div className="tags">
{ <div className="tag_container">
(((formik?.values as any)?.Questions?.[parent_index]?.tags as any[])||[]) .map((item:any,index:number)=>{ <div className="tags">
{(
return ( ((formik?.values as any)?.Questions?.[parent_index]
<Tag key={index} parent_index={parent_index} index={index} data={item}/> ?.tags as any[]) || []
).map((item: any, index: number) => {
return (
) <Tag
} key={index}
) parent_index={parent_index}
} index={index}
</div> data={item}
/>
{formik?.values?.Questions?.[parent_index]?.tags?.length > 0 && );
})}
<p className="add_new_button" >
<FaCirclePlus onClick={handleAddChoice} size={20} />
</p>
}
</div>
{TagsSearch && currentParentIndex === parent_index &&
<div className="suggests">
{suggests?.map((item:any,index:number)=>{
console.log(currentParentIndex === parent_index);
return (
<div className='suggested' key={index} onClick={()=> handleChoice(item)}>
{item?.name}
</div>
)
})}
</div> </div>
}
{formik?.values?.Questions?.[parent_index]?.tags?.length > 0 && (
<p className="add_new_button">
<FaCirclePlus onClick={handleAddChoice} size={20} />
</p>
)}
</div>
{TagsSearch && currentParentIndex === parent_index && (
<div className="suggests">
{suggests?.map((item: any, index: number) => {
console.log(currentParentIndex === parent_index);
return (
<div
className="suggested"
key={index}
onClick={() => handleChoice(item)}
>
{item?.name}
</div>
);
})}
</div>
)}
</div> </div>
) );
} };
export default DynamicTags export default DynamicTags;

View File

@ -1,74 +1,80 @@
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,parent_index }: { data: any, index: number,parent_index:number }) => { const Tag = ({
const inputRef = useRef<HTMLInputElement>(null); data,
const timeoutRef = useRef<NodeJS.Timeout | null>(null); index,
const DEBOUNCE_DELAY = 500; parent_index,
const formik = useFormikContext<any>(); }: {
const { setTagsSearch ,setCurrentTag,setCurrentParentIndex} = useObjectToEdit(); data: any;
console.log(formik?.values?.Questions); index: number;
parent_index: number;
useEffect(() => { }) => {
if (inputRef.current) { const inputRef = useRef<HTMLInputElement>(null);
inputRef.current.style.width = `${(formik?.values?.Questions?.[parent_index]?.tags[index]?.name?.length + 1) * 8}px`; const timeoutRef = useRef<NodeJS.Timeout | null>(null);
} const DEBOUNCE_DELAY = 500;
}, [formik?.values?.Questions?.[parent_index]?.tags[index]?.name]); const formik = useFormikContext<any>();
const { setTagsSearch, setCurrentTag, setCurrentParentIndex } =
useObjectToEdit();
console.log(formik?.values?.Questions);
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { useEffect(() => {
// console.log(e.target.value); if (inputRef.current) {
inputRef.current.style.width = `${(formik?.values?.Questions?.[parent_index]?.tags[index]?.name?.length + 1) * 8}px`;
}
}, [formik?.values?.Questions?.[parent_index]?.tags[index]?.name]);
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
formik.setFieldValue(`Questions.[${parent_index}].tags[${index}]`, {key:parent_index, name :e.target.value , id:`${parent_index}_key`}); // console.log(e.target.value);
setCurrentTag(index)
setCurrentParentIndex(parent_index)
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
setTagsSearch(e.target.value)
}, DEBOUNCE_DELAY);
};
const handleDeleteChoice = () => { formik.setFieldValue(`Questions.[${parent_index}].tags[${index}]`, {
console.log(data); key: parent_index,
name: e.target.value,
// Create a copy of current tags array id: `${parent_index}_key`,
const currentTags = [...formik.values.tags]; });
setCurrentTag(index);
// Remove the item at the specified index from the array setCurrentParentIndex(parent_index);
currentTags.splice(index, 1); if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
console.log(currentTags); // Log the updated tags array }
// Update formik field value with the updated tags array
formik.setFieldValue(`Questions.[${parent_index}].tags`, currentTags);
// Reset search state if needed
setTagsSearch(null);
};
timeoutRef.current = setTimeout(() => {
setTagsSearch(e.target.value);
return ( }, DEBOUNCE_DELAY);
<div className='tag'> };
<input
ref={inputRef} const handleDeleteChoice = () => {
className="tagInput" console.log(data);
type="text"
value={formik?.values?.Questions?.[parent_index]?.tags[index]?.name} // Create a copy of current tags array
onChange={handleEditInputChange} const currentTags = [...formik.values.tags];
/> // Remove the item at the specified index from the array
<FaTrash onClick={handleDeleteChoice}/> currentTags.splice(index, 1);
</div>
); console.log(currentTags); // Log the updated tags array
// Update formik field value with the updated tags array
formik.setFieldValue(`Questions.[${parent_index}].tags`, currentTags);
// Reset search state if needed
setTagsSearch(null);
};
return (
<div className="tag">
<input
ref={inputRef}
className="tagInput"
type="text"
value={formik?.values?.Questions?.[parent_index]?.tags[index]?.name}
onChange={handleEditInputChange}
/>
<FaTrash onClick={handleDeleteChoice} />
</div>
);
}; };
export default Tag; export default Tag;

View File

@ -1,13 +1,15 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import MathJax from 'react-mathjax'; import MathJax from "react-mathjax";
const MathInput: React.FC = () => { const MathInput: React.FC = () => {
const [input, setInput] = useState<string>('a^2+b^2=c^2 (x+a)^n=x=(-b±√(b^2-4ac))/2a ∑_(k=0)^n▒〖(n¦k) x^k a^(n-k) 〗'); const [input, setInput] = useState<string>(
"a^2+b^2=c^2 (x+a)^n=x=(-b±√(b^2-4ac))/2a ∑_(k=0)^n▒〖(n¦k) x^k a^(n-k) 〗",
);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const formattedInput = event.target.value.replace("_", ' _ '); const formattedInput = event.target.value.replace("_", " _ ");
console.log(event.target.value); console.log(event.target.value);
setInput(formattedInput); setInput(formattedInput);
}; };

View File

@ -12,13 +12,13 @@ import { useEffect } from "react";
const Form = () => { const Form = () => {
const [t] = useTranslation() const [t] = useTranslation()
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const{setSuccess,Success,setSavedQuestionData} = useObjectToEdit() const { setSuccess, Success, setSavedQuestionData } = useObjectToEdit();
useEffect(() => { useEffect(() => {
if (Success) { if (Success) {
formik.setErrors({}); formik.setErrors({});
formik.resetForm({ values: {} }); formik.resetForm({ values: {} });
setSuccess(false) setSuccess(false);
} }
}, [Success]); }, [Success]);

View File

@ -1,106 +1,92 @@
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";
import { useObjectToEdit } from '../../../../zustand/ObjectToEditState' import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
import { useGetAllTag } from '../../../../api/tags' import { useGetAllTag } from "../../../../api/tags";
const DynamicTags = () => { const DynamicTags = () => {
const formik = useFormikContext<any>();
const [t] = useTranslation();
const { TagsSearch, setTagsSearch, currentTag } = useObjectToEdit();
const { data } = useGetAllTag({
name: TagsSearch,
});
const suggests = data?.data;
console.log(TagsSearch);
const formik = useFormikContext<any>()
const [t] = useTranslation()
const { TagsSearch ,setTagsSearch,currentTag} = useObjectToEdit();
const {data} = useGetAllTag({
name : TagsSearch
})
const suggests = data?.data
console.log(TagsSearch);
const handleAddChoice = () => { const handleAddChoice = () => {
const length = formik?.values?.tags.length; const length = formik?.values?.tags.length;
const lastElement = formik?.values?.tags[length - 1]?.name; const lastElement = formik?.values?.tags[length - 1]?.name;
setTagsSearch setTagsSearch;
if(lastElement !== ""){ if (lastElement !== "") {
formik.setFieldValue('tags', [...(formik?.values as any)?.tags as any[], formik.setFieldValue("tags", [
...((formik?.values as any)?.tags as any[]),
{
id:length +"_new",
name:"",
key:length
}])
}else{
{
id: length + "_new",
name: "",
key: length,
},
]);
} else {
} }
};
// console.log(formik?.values);
} // console.log(currentTag);
// console.log(formik?.values);
// console.log(currentTag);
const handleChoice = (item: any) => { const handleChoice = (item: any) => {
const length = formik.values.tags.length;
console.log(currentTag);
const length = formik.values.tags.length; formik.setFieldValue(`tags[${currentTag}]`, { ...item, key: length });
console.log(currentTag); setTagsSearch(null);
};
formik.setFieldValue(`tags[${currentTag}]`, {...item,key:length});
setTagsSearch(null);
}
// console.log(formik?.values?.tags?.length);
// console.log(formik?.values?.tags?.length);
return ( return (
<div className='DynamicTags'> <div className="DynamicTags">
{formik?.values?.tags?.length < 1 && {formik?.values?.tags?.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_tag")} {t("header.add_tag")}
</p> </p>
} )}
<div className="tag_container">
<div className="tags">
{ <div className="tag_container">
(((formik?.values as any)?.tags as any[])||[]) .map((item:any,index:number)=>{ <div className="tags">
{(((formik?.values as any)?.tags as any[]) || []).map(
return ( (item: any, index: number) => {
<Tag key={index} index={index} data={item}/> return <Tag key={index} index={index} data={item} />;
},
)}
)
}
)
}
</div>
{formik?.values?.tags?.length > 0 &&
<p className="add_new_button" >
<FaCirclePlus onClick={handleAddChoice} size={20} />
</p>
}
</div>
{TagsSearch &&
<div className="suggests">
{suggests?.map((item:any,index:number)=>{
return (
<div className='suggested' key={index} onClick={()=> handleChoice(item)}>
{item?.name}
</div>
)
})}
</div> </div>
}
{formik?.values?.tags?.length > 0 && (
<p className="add_new_button">
<FaCirclePlus onClick={handleAddChoice} size={20} />
</p>
)}
</div>
{TagsSearch && (
<div className="suggests">
{suggests?.map((item: any, index: number) => {
return (
<div
className="suggested"
key={index}
onClick={() => handleChoice(item)}
>
{item?.name}
</div>
);
})}
</div>
)}
</div> </div>
) );
} };
export default DynamicTags export default DynamicTags;

View File

@ -1,75 +1,75 @@
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();
const timeoutRef = useRef<NodeJS.Timeout | null>(null); const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const DEBOUNCE_DELAY = 500; const DEBOUNCE_DELAY = 500;
useEffect(() => { useEffect(() => {
if (inputRef.current) { if (inputRef.current) {
inputRef.current.style.width = `${(formik?.values?.tags[index]?.name?.length + 1) * 8}px`; inputRef.current.style.width = `${(formik?.values?.tags[index]?.name?.length + 1) * 8}px`;
} }
}, [formik?.values?.tags[index]?.name]); }, [formik?.values?.tags[index]?.name]);
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// console.log(e.target.value); // console.log(e.target.value);
formik.setFieldValue(`tags[${index}].name`, e.target.value); formik.setFieldValue(`tags[${index}].name`, e.target.value);
// setTagsSearch(e.target.value) // setTagsSearch(e.target.value)
formik.setFieldValue(`tags.[${index}]`, {key:index, name :e.target.value , id:`${index}_key`}); formik.setFieldValue(`tags.[${index}]`, {
key: index,
name: e.target.value,
id: `${index}_key`,
});
setCurrentTag(index) setCurrentTag(index);
if (timeoutRef.current) { if (timeoutRef.current) {
clearTimeout(timeoutRef.current); clearTimeout(timeoutRef.current);
} }
timeoutRef.current = setTimeout(() => {
setTagsSearch(e.target.value)
}, DEBOUNCE_DELAY);
};
const handleInputBlur = () => { timeoutRef.current = setTimeout(() => {
// setTagsSearch(null) setTagsSearch(e.target.value);
}; }, DEBOUNCE_DELAY);
};
const handleDeleteChoice = () => { const handleInputBlur = () => {
console.log(data); // setTagsSearch(null)
};
// Create a copy of current tags array
const currentTags = [...formik.values.tags]; const handleDeleteChoice = () => {
console.log(data);
// Remove the item at the specified index from the array
currentTags.splice(index, 1); // Create a copy of current tags array
const currentTags = [...formik.values.tags];
console.log(currentTags); // Log the updated tags array
// Remove the item at the specified index from the array
// Update formik field value with the updated tags array currentTags.splice(index, 1);
formik.setFieldValue('tags', currentTags);
console.log(currentTags); // Log the updated tags array
// Reset search state if needed
setTagsSearch(null); // Update formik field value with the updated tags array
}; formik.setFieldValue("tags", currentTags);
return ( // Reset search state if needed
<div className='tag'> setTagsSearch(null);
<input };
ref={inputRef}
className="tagInput" return (
type="text" <div className="tag">
value={formik?.values?.tags[index]?.name} <input
onChange={handleEditInputChange} ref={inputRef}
onBlur={handleInputBlur} className="tagInput"
type="text"
/> value={formik?.values?.tags[index]?.name}
<FaTrash onClick={handleDeleteChoice}/> onChange={handleEditInputChange}
</div> onBlur={handleInputBlur}
); />
<FaTrash onClick={handleDeleteChoice} />
</div>
);
}; };
export default Tag; export default Tag;

View File

@ -3,21 +3,18 @@ import { Question } from "../../../types/Item";
import { getLocalStorage } from "../../../utils/LocalStorage"; import { getLocalStorage } from "../../../utils/LocalStorage";
import { QUESTION_OBJECT_KEY } from "../../../config/AppKey"; import { QUESTION_OBJECT_KEY } from "../../../config/AppKey";
export const getInitialValues = (objectToEdit: Question): any => { export const getInitialValues = (objectToEdit: Question): any => {
const tags = objectToEdit?.tags?.map((item: any, index: number) => { const tags = objectToEdit?.tags?.map((item: any, index: number) => {
return { ...item, key: index } return { ...item, key: index };
}); });
return { return {
id: objectToEdit?.id ?? null, id: objectToEdit?.id ?? null,
content: objectToEdit?.content ?? "", content: objectToEdit?.content ?? "",
image: objectToEdit?.image ?? "", image: objectToEdit?.image ?? "",
subject_id: objectToEdit?.subject_id ?? '', subject_id: objectToEdit?.subject_id ?? "",
isBase: 0, isBase: 0,
parent_id: objectToEdit?.parent_id ?? '', parent_id: objectToEdit?.parent_id ?? "",
QuestionOptions: objectToEdit?.QuestionOptions ?? [], QuestionOptions: objectToEdit?.QuestionOptions ?? [],
tags: tags ?? [], tags: tags ?? [],
}; };
@ -28,47 +25,46 @@ export const getValidationSchema = () => {
return Yup.object().shape({ return Yup.object().shape({
image: Yup.string().nullable(), image: Yup.string().nullable(),
content: Yup.string().required("validation.required"), content: Yup.string().required("validation.required"),
QuestionOptions: Yup.array().of( QuestionOptions: Yup.array()
Yup.object().shape({ .of(
answer: Yup.string().required("validation.required"), Yup.object().shape({
answer_image: Yup.string().nullable(), answer: Yup.string().required("validation.required"),
isCorrect: Yup.boolean() answer_image: Yup.string().nullable(),
}) isCorrect: Yup.boolean(),
).nullable('Params are required') }),
)
.nullable("Params are required"),
}); });
}; };
export const getInitialValuesBase = (objectToEdit: Question): any => { export const getInitialValuesBase = (objectToEdit: Question): any => {
const tags = objectToEdit?.tags?.map((item: any, index: number) => { const tags = objectToEdit?.tags?.map((item: any, index: number) => {
return { ...item, key: index } return { ...item, key: index };
}); });
console.log(objectToEdit); console.log(objectToEdit);
const newQuestions = objectToEdit?.Questions?.map((item: any) => { const newQuestions = objectToEdit?.Questions?.map((item: any) => {
const tags = item?.tags?.map((tag: any) => ({ const tags = item?.tags?.map((tag: any) => ({
id: tag?.id, id: tag?.id,
name: tag?.name, name: tag?.name,
key: `${tag?.id}_key_${tag?.name}` key: `${tag?.id}_key_${tag?.name}`,
})); }));
return { return {
...item, ...item,
tags tags,
}; };
}); });
const questions = newQuestions ?? []; const questions = newQuestions ?? [];
return { return {
id: objectToEdit?.id ?? null, id: objectToEdit?.id ?? null,
content: objectToEdit?.content ?? "", content: objectToEdit?.content ?? "",
image: objectToEdit?.image ?? "", image: objectToEdit?.image ?? "",
subject_id: objectToEdit?.subject_id ?? '', subject_id: objectToEdit?.subject_id ?? "",
isBase: 1, isBase: 1,
parent_id: objectToEdit?.parent_id ?? '', parent_id: objectToEdit?.parent_id ?? "",
Questions: questions, Questions: questions,
}; };
}; };
@ -82,32 +78,36 @@ export const getValidationSchemaBase = () => {
Yup.object().shape({ Yup.object().shape({
image: Yup.string().nullable(), image: Yup.string().nullable(),
content: Yup.string().required("validation.required"), content: Yup.string().required("validation.required"),
QuestionOptions: Yup.array().of( QuestionOptions: Yup.array()
Yup.object().shape({ .of(
answer: Yup.string().required("validation.required"), Yup.object().shape({
answer_image: Yup.string().nullable(), answer: Yup.string().required("validation.required"),
isCorrect: Yup.boolean() answer_image: Yup.string().nullable(),
}) isCorrect: Yup.boolean(),
).nullable('required') }),
}) )
), .nullable("required"),
}),
),
}); });
}; };
export function processTags(DataToSend: any) { export function processTags(DataToSend: any) {
const oldTags = DataToSend?.tags?.map((item: any, index: number) => { const oldTags = DataToSend?.tags
?.map((item: any, index: number) => {
if (typeof item?.id === "number") { if (typeof item?.id === "number") {
return item?.id; return item?.id;
} }
}).filter((item:any) => item !== undefined); })
.filter((item: any) => item !== undefined);
const newTags = DataToSend?.tags?.map((item: any, index: number) => { const newTags = DataToSend?.tags
if (typeof item?.id === "string" && item?.name !== "" ) { ?.map((item: any, index: number) => {
return { name: item?.name }; if (typeof item?.id === "string" && item?.name !== "") {
return { name: item?.name };
} }
}).filter((item:any) => item !== undefined); })
.filter((item: any) => item !== undefined);
return { new:newTags, old:oldTags }; return { new: newTags, old: oldTags };
} }

View File

@ -1,16 +1,16 @@
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 DeleteModel from './Model/Delete' import DeleteModel from "./Model/Delete";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { ParamsEnum } from "../../enums/params"; import { ParamsEnum } from "../../enums/params";
import { useGetAllUnit } from "../../api/unit"; import { useGetAllUnit } from "../../api/unit";
import useSetPageTitle from "../../Hooks/useSetPageTitle"; import useSetPageTitle from "../../Hooks/useSetPageTitle";
import { useGetAllLesson } from "../../api/lesson"; import { useGetAllLesson } from "../../api/lesson";
import { useDeleteQuestion } from "../../api/Question";
import DeleteModels from "../../Layout/Dashboard/DeleteModels"; import DeleteModels from "../../Layout/Dashboard/DeleteModels";
import { ModalEnum } from "../../enums/Model"; import { ModalEnum } from "../../enums/Model";
import { useDeleteQuestion } from "../../api/Question"; const Table = lazy(() => import("./Table"));
const Table = lazy(() => import('./Table'));
const TableHeader = () => { const TableHeader = () => {
const [t] = useTranslation(); const [t] = useTranslation();
@ -26,28 +26,28 @@ const TableHeader = () => {
useSetPageTitle( useSetPageTitle(
t(`page_header.subject`) + t(`page_header.subject`) +
"/" +
`${SubjectName}` +
"/" + "/" +
t(`page_title.unit`) + `${SubjectName}` +
"/" + "/" +
`${unitName}`+"/" t(`PageTitle.unit`) +
+ "/" +
t(`page_title.lesson`) + `${unitName}` +
"/" + "/" +
`${lessonName}`+ "/" + t(`PageTitle.lesson`) +
t(`page_title.questions`) "/" +
`${lessonName}` +
"/" +
t(`PageTitle.questions`),
); );
return ( return (
<div className="TableWithHeader"> <div className="TableWithHeader">
<Suspense fallback={<Spin/>}> <Suspense fallback={<Spin />}>
<header> <header>
<h6> <h6>
{t("models.Question")} {SubjectName} {unitName} {lessonName} {t("models.Question")} {SubjectName} {unitName} {lessonName}
</h6> </h6>
</header>
</header>
<Table /> <Table />
</Suspense> </Suspense>
<DeleteModels <DeleteModels

View File

@ -6,9 +6,12 @@ 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 {lesson_id} = useParams<ParamsEnum>() const { lesson_id } = useParams<ParamsEnum>();
const response = useGetAllQuestion({ lesson_id:lesson_id, pagination: true }); const response = useGetAllQuestion({
return <DataTable response={response} useColumns={useColumns} />; lesson_id: lesson_id,
pagination: true,
});
return <DataTable response={response} useColumns={useColumns} />;
}; };
export default App; export default App;

View File

@ -7,30 +7,31 @@ import { useTranslation } from "react-i18next";
import { ABILITIES_ENUM } from "../../enums/abilities"; import { ABILITIES_ENUM } from "../../enums/abilities";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useModalState } from "../../zustand/Modal"; import { useModalState } from "../../zustand/Modal";
import { canAddQuestion, canDeleteQuestion, canEditQuestion } from "../../utils/hasAbilityFn"; import {
canAddQuestion,
canDeleteQuestion,
canEditQuestion,
} from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons"; import ActionButtons from "../../Components/Table/ActionButtons";
export const useColumns = () => { export const useColumns = () => {
const { setObjectToEdit } = useObjectToEdit((state) => state); const { setObjectToEdit } = useObjectToEdit((state) => state);
const navigate = useNavigate() const navigate = useNavigate();
const { setIsOpen } = useModalState((state) => state); const { setIsOpen } = useModalState((state) => state);
const handelAdd = () => { const handelAdd = () => {
setObjectToEdit({}) setObjectToEdit({});
navigate(`${ABILITIES_ENUM?.QUESTION}/add`) navigate(`${ABILITIES_ENUM?.QUESTION}/add`);
}; };
const handelDelete = (data: any) => { const handelDelete = (data: any) => {
setObjectToEdit(data); setObjectToEdit(data);
setIsOpen(ModalEnum?.QUESTION_DELETE); setIsOpen(ModalEnum?.QUESTION_DELETE);
}; };
const handleEdit = (record: any) => { const handleEdit = (record: any) => {
setObjectToEdit(record); setObjectToEdit(record);
navigate(`${ABILITIES_ENUM?.QUESTION}/${record?.id}`) navigate(`${ABILITIES_ENUM?.QUESTION}/${record?.id}`);
}; };
const [t] = useTranslation(); const [t] = useTranslation();
@ -48,14 +49,15 @@ export const useColumns = () => {
key: "name", key: "name",
align: "center", align: "center",
render: (text, record) => record?.content, render: (text, record) => record?.content,
ellipsis:true ellipsis: true,
}, },
{ {
title: t("columns.isBase"), title: t("columns.isBase"),
dataIndex: "isBase", dataIndex: "isBase",
key: "isBase", key: "isBase",
align: "center", align: "center",
render: (text, record) => record?.isBase ? t("practical.yes") : t ("practical.no"), render: (text, record) =>
record?.isBase ? t("practical.yes") : t("practical.no"),
}, },
{ {
title: `${t("columns.question_options_count")}`, title: `${t("columns.question_options_count")}`,
@ -66,10 +68,7 @@ export const useColumns = () => {
}, },
{ {
title: canAddQuestion ? ( title: canAddQuestion ? (
<button <button onClick={() => handelAdd()} className="add_button">
onClick={() => handelAdd() }
className="add_button"
>
{t("practical.add")} {t("models.Question")} <FaPlus /> {t("practical.add")} {t("models.Question")} <FaPlus />
</button> </button>
) : ( ) : (

View File

@ -1,43 +1,40 @@
import { ModalEnum } from "../../../enums/Model";
import useModalHandler from "../../../utils/useModalHandler";
import { FaPlus } from "react-icons/fa";
import { useTranslation } from "react-i18next";
import TablePage from "./TablePage";
import { ModalEnum } from '../../../enums/Model';
import useModalHandler from '../../../utils/useModalHandler';
import { FaPlus } from 'react-icons/fa';
import { useTranslation } from 'react-i18next';
import TablePage from './TablePage';
import AddSubjectModalForm from "../Model/AddModel"; import AddSubjectModalForm from "../Model/AddModel";
import EditSubjectModalForm from "../Model/EditModel"; import EditSubjectModalForm from "../Model/EditModel";
import useSetPageTitle from '../../../Hooks/useSetPageTitle'; import DeleteSubjectModalForm from "../Model/Delete";
import { canAddSubject } from '../../../utils/hasAbilityFn'; import useSetPageTitle from "../../../Hooks/useSetPageTitle";
import DeleteModels from '../../../Layout/Dashboard/DeleteModels'; import { canAddSubject } from "../../../utils/hasAbilityFn";
import { useDeleteSubject } from '../../../api/subject'; import DeleteModels from "../../../Layout/Dashboard/DeleteModels";
import { useDeleteSubject } from "../../../api/subject";
const TableWithHeader = () => { const TableWithHeader = () => {
const { handel_open_model } = useModalHandler(); const { handel_open_model } = useModalHandler();
const [t] = useTranslation(); const [t] = useTranslation();
const deleteMutation = useDeleteSubject(); const deleteMutation = useDeleteSubject();
useSetPageTitle( useSetPageTitle(t(`page_header.subject`));
t(`page_header.subject`),
);
return ( return (
<div className="TableWithHeader"> <div className="TableWithHeader">
<header> <header>
<h4 className="Bold Primary ">{t("models.subject")}</h4> <h4 className="Bold Primary ">{t("models.subject")}</h4>
{canAddSubject && ( {canAddSubject && (
<button <button
onClick={() => handel_open_model(ModalEnum?.SUBJECT_ADD)} onClick={() => handel_open_model(ModalEnum?.SUBJECT_ADD)}
className="add_button" className="add_button"
> >
{t("practical.add")} {t("models.subject")} <FaPlus /> {t("practical.add")} {t("models.subject")} <FaPlus />
</button> </button>
)} )}
</header> </header>
<TablePage /> <TablePage />
<AddSubjectModalForm /> <AddSubjectModalForm />
<EditSubjectModalForm /> <EditSubjectModalForm />
<DeleteModels <DeleteModels
deleteMutation={deleteMutation} deleteMutation={deleteMutation}
@ -47,4 +44,4 @@ const TableWithHeader = () => {
) )
} }
export default TableWithHeader export default TableWithHeader;

View File

@ -6,7 +6,7 @@ import { ParamsEnum } from "../../../enums/params";
const TablePage: React.FC = () => { const TablePage: React.FC = () => {
const { course_id } = useParams<ParamsEnum>(); const { course_id } = useParams<ParamsEnum>();
const response = useGetAllSubject({ course_id: course_id,pagination:true}); const response = useGetAllSubject({ course_id: course_id, pagination: true });
return <DataTable response={response} useColumns={useColumns} />; return <DataTable response={response} useColumns={useColumns} />;
}; };

View File

@ -1,12 +1,12 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { FaArrowRight, FaPlus } from "react-icons/fa"; import { FaArrowRight, FaPlus } from "react-icons/fa";
import { useButtonState } from "../../../zustand/ButtonState"; import { useButtonState } from "../../../zustand/ButtonState";
import { usePage_titleState } from "../../../zustand/PageTitleState"; import { usePageTitleState } from "../../../zustand/PageTitleState";
const FillterNavWithRadio = () => { const FillterNavWithRadio = () => {
const [activeButton, setActiveButton] = useState(0); const [activeButton, setActiveButton] = useState(0);
const { setActiveTab } = useButtonState((state) => state); const { setActiveTab } = useButtonState((state) => state);
const { title } = usePage_titleState(); const { title } = usePageTitleState();
// Function to handle button click // Function to handle button click
const handleButtonClick = (index: number) => { const handleButtonClick = (index: number) => {

View File

@ -1,36 +1,20 @@
import QueryProvider from "./lib/ReactQueryProvider"; import QueryProvider from "./lib/ReactQueryProvider";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import { Tchildren } from "./types/App"; import { ChildrenType } from "./types/App";
import ToastProvider from "./lib/ToastProvider"; import ToastProvider from "./lib/ToastProvider";
import { ConfigProvider } from "antd"; import AntdProvider from "./lib/AntdProvider";
import I18nProvider from "./lib/I18nProvider";
function ProviderContainer({ children }: Tchildren) {
const primaryColor = "#3182ce";
const bgColor = "rgb(255, 255, 255)";
function ProviderContainer({ children }: ChildrenType) {
return ( return (
<BrowserRouter basename="/"> <BrowserRouter basename="/">
{/* <ReduxT> */} <I18nProvider>
<QueryProvider> <QueryProvider>
<ToastProvider> <ToastProvider>
<ConfigProvider <AntdProvider>{children}</AntdProvider>
theme={{ </ToastProvider>
token: { </QueryProvider>
colorPrimary: primaryColor, </I18nProvider>
},
components: {
Table: {
headerBg: bgColor,
headerColor: primaryColor,
},
},
}}
>
{children}
</ConfigProvider>
</ToastProvider>
</QueryProvider>
{/* </ReduxT> */}
</BrowserRouter> </BrowserRouter>
); );
} }

View File

@ -1,28 +1,17 @@
import { TCrudRoute, TMenuItem } from "./types/App"; import { TCrudRoute, TMenuItem } from "./types/App";
import { FaHome, FaMoneyBill, FaUser } from "react-icons/fa"; import { FaHome, FaMoneyBill } from "react-icons/fa";
import { ImBooks } from "react-icons/im"; import React from "react";
import React, { lazy } from "react";
// import Home from "./Pages/Home/Page";
const Dummy = React.lazy(() => import("./Pages/Home/Dummy")); const Dummy = React.lazy(() => import("./Pages/Home/Dummy"));
const Subject = React.lazy(() => import("./Pages/subject/Table/Page")); const Subject = React.lazy(() => import("./Pages/subject/Table/Page"));
const Tags = React.lazy(() => import("./Pages/Tags/Page")); const Tags = React.lazy(() => import("./Pages/Tags/Page"));
const Unit = React.lazy(() => import("./Pages/Unit/Page")); const Unit = React.lazy(() => import("./Pages/Unit/Page"));
const Lesson = React.lazy(() => import("./Pages/lesson/Page")); const Lesson = React.lazy(() => import("./Pages/lesson/Page"));
const Question = React.lazy(() => import('./Pages/question/Page')) const Question = React.lazy(() => import("./Pages/question/Page"));
const AddQuestionPage = React.lazy(() => import('./Pages/question/AddPage')) const AddQuestionPage = React.lazy(() => import("./Pages/question/AddPage"));
const EditQuestionPage = React.lazy(() => import('./Pages/question/EditPage')) const EditQuestionPage = React.lazy(() => import("./Pages/question/EditPage"));
// const QuestionChildren = React.lazy(() => import('./Pages/question/children/Page'))
// const AddQuestionChildren = React.lazy(() => import('./Pages/question/children/Model/AddModel'))
// const EditQuestionChildren = React.lazy(() => import('./Pages/question/children/Model/EditModel'))
import { hasAbility } from "./utils/hasAbility"; import { hasAbility } from "./utils/hasAbility";
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "./enums/abilities"; import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "./enums/abilities";
@ -37,10 +26,9 @@ export const menuItems: TMenuItem[] = [
path: "/", path: "/",
abilities: ABILITIES_ENUM?.PASS, abilities: ABILITIES_ENUM?.PASS,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, prevPath: 0,
}, },
{ {
header: "page_header.subject", header: "page_header.subject",
element: <Subject />, element: <Subject />,
@ -49,7 +37,7 @@ export const menuItems: TMenuItem[] = [
path: `/${ABILITIES_ENUM?.SUBJECT}`, path: `/${ABILITIES_ENUM?.SUBJECT}`,
abilities: ABILITIES_ENUM?.SUBJECT, abilities: ABILITIES_ENUM?.SUBJECT,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, prevPath: 0,
}, },
{ {
header: "page_header.tags", header: "page_header.tags",
@ -59,23 +47,18 @@ export const menuItems: TMenuItem[] = [
path: `/${ABILITIES_ENUM?.TAG}`, path: `/${ABILITIES_ENUM?.TAG}`,
abilities: ABILITIES_ENUM?.TAG, abilities: ABILITIES_ENUM?.TAG,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, prevPath: 0,
}, },
]; ];
export const CrudRoute: TCrudRoute[] = [ export const CrudRoute: TCrudRoute[] = [
{ {
header: "page_header.subject_details", header: "page_header.subject_details",
element: <Unit />, element: <Unit />,
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}`, path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}`,
abilities: ABILITIES_ENUM?.UNIT, abilities: ABILITIES_ENUM?.UNIT,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 1, prevPath: 1,
}, },
{ {
header: "page_header.unit_details", header: "page_header.unit_details",
@ -83,7 +66,7 @@ export const CrudRoute: TCrudRoute[] = [
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}`, path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}`,
abilities: ABILITIES_ENUM?.LESSON, abilities: ABILITIES_ENUM?.LESSON,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 2, prevPath: 2,
}, },
{ {
@ -92,7 +75,7 @@ export const CrudRoute: TCrudRoute[] = [
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}`, path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}`,
abilities: ABILITIES_ENUM?.QUESTION, abilities: ABILITIES_ENUM?.QUESTION,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 2, prevPath: 2,
}, },
{ {
header: "page_header.add_Question", header: "page_header.add_Question",
@ -100,7 +83,7 @@ export const CrudRoute: TCrudRoute[] = [
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/add`, path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/add`,
abilities: ABILITIES_ENUM?.QUESTION, abilities: ABILITIES_ENUM?.QUESTION,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 2, prevPath: 2,
}, },
{ {
header: "page_header.edit_Question", header: "page_header.edit_Question",
@ -108,13 +91,10 @@ export const CrudRoute: TCrudRoute[] = [
path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/:${ParamsEnum?.QUESTION_ID}`, path: `/${ABILITIES_ENUM?.SUBJECT}/:${ParamsEnum?.SUBJECT_ID}/${ABILITIES_ENUM?.UNIT}/:${ParamsEnum?.UNIT_ID}/${ABILITIES_ENUM?.LESSON}/:${ParamsEnum?.LESSON_ID}/${ABILITIES_ENUM?.QUESTION}/:${ParamsEnum?.QUESTION_ID}`,
abilities: ABILITIES_ENUM?.QUESTION, abilities: ABILITIES_ENUM?.QUESTION,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 2, prevPath: 2,
}, },
]; ];
export const AppRoutes: Record<string, string> = Object.fromEntries( export const AppRoutes: Record<string, string> = Object.fromEntries(
menuItems.map((route) => [ menuItems.map((route) => [
route.path, route.path,
@ -134,9 +114,9 @@ export const search_array: { label: string; value: string }[] = menuItems
value: item.path as string, value: item.path as string,
})); }));
const AllRoute = [...menuItems, ...CrudRoute]; const AllRoute = [...menuItems, ...CrudRoute];
export const PrevRoutes: any = AllRoute.map((route) => ({ export const PrevRoutes: any = AllRoute.map((route) => ({
path: route.path, path: route.path,
prevPath: route.prevPath, prevPath: route.prevPath,
})); }));

View File

@ -36,7 +36,7 @@ svg {
cursor: pointer; cursor: pointer;
} }
.not_foound_page { .not_found_page {
background: black; background: black;
height: 100vh; height: 100vh;
display: flex; display: flex;

View File

@ -266,10 +266,10 @@ button:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed !important; /* examle: change cursor */ cursor: not-allowed !important; /* examle: change cursor */
} }
.relative{ .relative {
position: relative; position: relative;
} }
.h1{ .h1 {
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
@ -304,4 +304,4 @@ button:disabled {
.DeleteButton { .DeleteButton {
background: #dc2626; background: #dc2626;
} }
} }

View File

@ -1,4 +1,3 @@
@font-face { @font-face {
font-family: "Poppins_Bold"; font-family: "Poppins_Bold";
src: url("../../font/Poppins-Bold.ttf") format("truetype"); /* Specify the path to the font file */ src: url("../../font/Poppins-Bold.ttf") format("truetype"); /* Specify the path to the font file */

View File

@ -15,7 +15,7 @@
margin-inline: auto; margin-inline: auto;
height: 60%; height: 60%;
content: ""; content: "";
background: url("../../../public/Layout/BackgroundHeader.webp"); background: url("/Layout/BackgroundHeader.webp");
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
z-index: 1; z-index: 1;

View File

@ -23,7 +23,7 @@
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
background-image: url("../../../public/Layout/BackgroundHeader.webp"); background-image: url("/Layout/BackgroundHeader.webp");
} }
} }
@ -83,9 +83,8 @@
min-width: 120px; min-width: 120px;
} }
.SpinContainer {
.SpinContainer{
@include Flex; @include Flex;
height: 100%; height: 100%;
min-height: 20vh; min-height: 20vh;
} }

View File

@ -88,7 +88,7 @@
display: none !important; display: none !important;
} }
.Show_More_Button { .ShowMoreButton {
position: absolute; position: absolute;
bottom: 1vw; bottom: 1vw;
right: 0.5vw; right: 0.5vw;

View File

@ -9,14 +9,14 @@
.absence_icon { .absence_icon {
background-color: red; background-color: red;
} }
.lateArrival_icon { .late_arrival_icon {
background-color: orange; background-color: orange;
} }
.presence_icon { .presence_icon {
background-color: #31ce83 !important; background-color: #31ce83 !important;
} }
.earlyDeparture_Details { .earlyDepartureDetails {
// color: gray; // color: gray;
svg { svg {
color: #a098ae; color: #a098ae;
@ -33,7 +33,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.custom_add_button_column_Mark { .custom_add_button_column_mark {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -46,7 +46,7 @@
// font-size: 7px; // font-size: 7px;
// } // }
.column_studentBlock { .column_student_block {
width: 20px; width: 20px;
height: 20px; height: 20px;
border-radius: 4px; border-radius: 4px;
@ -72,16 +72,16 @@
border: 2px solid var(--absence); border: 2px solid var(--absence);
} }
.earlyDeparture_student { .earlyDepartureStudent {
background-color: var(--earlyDeparture); background-color: var(--earlyDeparture);
} }
.not_earlyDeparture_student { .not_earlyDepartureStudent {
border: 2px solid var(--earlyDeparture); border: 2px solid var(--earlyDeparture);
} }
.lateArrival_student { .lateArrivalStudent {
background-color: var(--lateArrival); background-color: var(--lateArrival);
} }
.not_lateArrival_student { .notLateArrivalStudent {
border: 2px solid var(--lateArrival); border: 2px solid var(--lateArrival);
} }

View File

@ -1,10 +1,8 @@
.hidden { .hidden {
display: none !important; display: none !important;
} }
.Show_More_Button { .ShowMoreButton {
position: absolute; position: absolute;
bottom: 1vw; bottom: 1vw;
right: 0.5vw; right: 0.5vw;
@ -66,24 +64,12 @@
} }
} }
.activeModeltab { .activeModelTab {
> div { > div {
background: #bfe2fd; background: #bfe2fd;
color: black; color: black;
} }
} }
&::after {
z-index: 1;
position: absolute;
bottom: 0;
right: -2.4vw;
content: "";
background: url("../../../public/Icon/cercile.svg");
width: 15vw;
height: 15vw;
background-repeat: no-repeat;
}
} }
.ModelBodyForm { .ModelBodyForm {
padding: 2vw; padding: 2vw;

View File

@ -405,4 +405,3 @@
border-left: none !important; border-left: none !important;
} }
} }

Some files were not shown because too many files have changed in this diff Show More