Merge branch 'dev' of into dev
This commit is contained in:
commit
00dfd6c88c
File diff suppressed because one or more lines are too long
14
index.html
14
index.html
|
|
@ -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"
|
||||||
|
|
|
||||||
56
package.json
56
package.json
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11489
pnpm-lock.yaml
11489
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 861 B After Width: | Height: | Size: 861 B |
Binary file not shown.
61
src/App.tsx
61
src/App.tsx
|
|
@ -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)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
16
src/Components/Layout/Navbar/DropdownToggle.tsx
Normal file
16
src/Components/Layout/Navbar/DropdownToggle.tsx
Normal 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;
|
||||||
40
src/Components/Layout/Navbar/MenuItem.tsx
Normal file
40
src/Components/Layout/Navbar/MenuItem.tsx
Normal 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} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
58
src/Components/Layout/Navbar/NavBarRightSide.tsx
Normal file
58
src/Components/Layout/Navbar/NavBarRightSide.tsx
Normal 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;
|
||||||
24
src/Components/Layout/Navbar/SubMenu.tsx
Normal file
24
src/Components/Layout/Navbar/SubMenu.tsx
Normal 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;
|
||||||
63
src/Components/Layout/SideBar/MenuItem.tsx
Normal file
63
src/Components/Layout/SideBar/MenuItem.tsx
Normal 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>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
15
src/Components/Routes/RenderRouteElement.tsx
Normal file
15
src/Components/Routes/RenderRouteElement.tsx
Normal 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>
|
||||||
|
);
|
||||||
19
src/Components/Routes/RenderRoutesRecursively.tsx
Normal file
19
src/Components/Routes/RenderRoutesRecursively.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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" : ``}>
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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 [];
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
|
||||||
};
|
|
||||||
|
|
@ -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]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const EditModel: React.FC = () => {
|
||||||
|
|
||||||
const handleSubmit = (values: any) => {
|
const handleSubmit = (values: any) => {
|
||||||
mutate({
|
mutate({
|
||||||
...values
|
...values,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,5 @@ export {
|
||||||
AddModalForm,
|
AddModalForm,
|
||||||
EditModalForm,
|
EditModalForm,
|
||||||
DeleteModalForm,
|
DeleteModalForm,
|
||||||
FaPlus
|
FaPlus,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}`);
|
||||||
|
|
|
||||||
|
|
@ -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("");
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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} />;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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}`);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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} />;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Reference in New Issue
Block a user