change it
This commit is contained in:
parent
c21796cfc1
commit
10af7490a1
3
public/Icon/QuestionIcon.svg
Normal file
3
public/Icon/QuestionIcon.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.41177 10.8602C9.60628 10.8602 9.77726 10.7862 9.92471 10.6381C10.0722 10.49 10.1462 10.3191 10.1468 10.1252C10.1475 9.9313 10.0734 9.76031 9.92471 9.61224C9.776 9.46416 9.60502 9.39012 9.41177 9.39012C9.21851 9.39012 9.04753 9.46416 8.89882 9.61224C8.75012 9.76031 8.67608 9.9313 8.67671 10.1252C8.67733 10.3191 8.75137 10.49 8.89882 10.6381C9.04628 10.7862 9.21726 10.8609 9.41177 10.8602ZM8.99576 8.20988H9.82777C9.85224 7.81522 9.91467 7.51812 10.0151 7.31859C10.1148 7.11906 10.3555 6.84047 10.7369 6.48282C11.1347 6.12141 11.4121 5.79828 11.5689 5.51341C11.7258 5.22981 11.8042 4.90384 11.8042 4.53553C11.8042 3.90181 11.5784 3.37381 11.1266 2.95153C10.6748 2.52863 10.1032 2.31718 9.41177 2.31718C8.8891 2.31718 8.42479 2.45835 8.01882 2.74071C7.61286 3.02306 7.30384 3.408 7.09177 3.89553L7.85506 4.23153C8.03263 3.86384 8.2491 3.58777 8.50447 3.4033C8.75984 3.21883 9.06227 3.1269 9.41177 3.12753C9.86102 3.12753 10.2334 3.26055 10.5289 3.52659C10.8238 3.79263 10.9713 4.13616 10.9713 4.55718C10.9713 4.81318 10.8998 5.0513 10.7567 5.27153C10.613 5.49177 10.3661 5.75686 10.016 6.06682C9.61945 6.41318 9.35028 6.73098 9.20847 7.02024C9.06667 7.30949 8.99576 7.70604 8.99576 8.20988ZM4.34447 13.1765C3.9109 13.1765 3.54918 13.0315 3.2593 12.7416C2.96941 12.4518 2.82416 12.09 2.82353 11.6565V1.52C2.82353 1.08706 2.96879 0.725335 3.2593 0.434825C3.54981 0.144316 3.91153 -0.000625423 4.34447 2.0284e-06H14.48C14.9129 2.0284e-06 15.2747 0.144943 15.5652 0.434825C15.8557 0.724708 16.0006 1.08643 16 1.52V11.6565C16 12.0894 15.8551 12.4508 15.5652 12.7407C15.2753 13.0306 14.9133 13.1758 14.4791 13.1765H4.34447ZM4.34447 12.2353H14.48C14.6243 12.2353 14.757 12.1751 14.8781 12.0546C14.9992 11.9341 15.0595 11.8014 15.0588 11.6565V1.52C15.0588 1.37569 14.9986 1.24298 14.8781 1.12188C14.7576 1.00079 14.6246 0.940551 14.4791 0.941178H4.34447C4.19953 0.941178 4.06651 1.00141 3.94541 1.12188C3.82432 1.24235 3.76408 1.37506 3.76471 1.52V11.6565C3.76471 11.8008 3.82494 11.9335 3.94541 12.0546C4.06588 12.1757 4.19859 12.2359 4.34353 12.2353M1.52 16C1.08706 16 0.725335 15.8551 0.434825 15.5652C0.144316 15.2753 -0.000625423 14.9136 2.0284e-06 14.48V3.40235H0.941178V14.48C0.941178 14.6243 1.00141 14.757 1.12188 14.8781C1.24235 14.9992 1.37506 15.0595 1.52 15.0588H12.5976V16H1.52Z" fill="#202C4B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -1,25 +1,34 @@
|
|||
import { useFormikContext } from "formik";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useState, useRef } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import './ImageBoxField.scss';
|
||||
import ImageIcon from "./ImageIcon";
|
||||
import ImageCancelIcon from "./ImageCancelIcon";
|
||||
import { getNestedValue } from "../../../utils/getNestedValue";
|
||||
import { generateImagePreview } from "./generateImagePreview";
|
||||
|
||||
// Helper function to generate image preview from a File
|
||||
|
||||
|
||||
const ImageBoxField = ({ name }: any) => {
|
||||
const formik = useFormikContext<any>();
|
||||
const [imagePreview, setImagePreview] = useState<string | null>("");
|
||||
const [t] = useTranslation();
|
||||
|
||||
const value = getNestedValue(formik.values, name);
|
||||
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (value instanceof File) {
|
||||
generateImagePreview(value, setImagePreview);
|
||||
} else if (typeof value === 'string') {
|
||||
setImagePreview(value);
|
||||
} else {
|
||||
setImagePreview(null);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const handleFileChange = (event: any) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setImagePreview(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
generateImagePreview(file, setImagePreview);
|
||||
formik.setFieldValue(name, file);
|
||||
}
|
||||
};
|
||||
|
|
@ -49,7 +58,9 @@ const ImageBoxField = ({ name }: any) => {
|
|||
<ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" />
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
<div className="VisibleHidden">
|
||||
hidden
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="ImageBox">
|
||||
|
|
@ -62,7 +73,7 @@ const ImageBoxField = ({ name }: any) => {
|
|||
<input
|
||||
id={`file-input-${name}`}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
accept="image/png, image/jpeg, image/webp"
|
||||
style={{ display: "none" }}
|
||||
onChange={handleFileChange}
|
||||
ref={fileInputRef}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
export const generateImagePreview = (file: File, setImagePreview: (result: string) => void) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setImagePreview(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
|
@ -62,7 +62,7 @@ const SearchField: React.FC<Props> = ({ placeholder, searchBy }) => {
|
|||
return (
|
||||
<div className={`search-field ${isOpen ? "open" : ""}`}>
|
||||
<div className="search-header" onClick={handleToggleDropdown}>
|
||||
<IoSearch className="search__icon" />
|
||||
{/* <IoSearch className="search__icon" /> */}
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ interface Props {
|
|||
options: Option[];
|
||||
placeholder: string;
|
||||
onSelect?: (option: Option) => void;
|
||||
withIcon?:boolean
|
||||
}
|
||||
|
||||
const SearchFieldWithSelect: React.FC<Props> = ({
|
||||
options,
|
||||
placeholder,
|
||||
onSelect,
|
||||
withIcon=false
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
|
||||
|
|
@ -58,17 +60,16 @@ const SearchFieldWithSelect: React.FC<Props> = ({
|
|||
return (
|
||||
<div ref={node} className={`search-field ${isOpen ? "open" : ""}`}>
|
||||
<div className="search-header" onClick={toggleDropdown}>
|
||||
<IoSearch className="search__icon" />
|
||||
{withIcon && <IoSearch className="search__icon" />}
|
||||
|
||||
{/* <p className="search__input_text">{placeholder}</p> */}
|
||||
<input
|
||||
type="text"
|
||||
className="search__input search__input_text"
|
||||
placeholder={selectedOption ? selectedOption.label : ""}
|
||||
// placeholder={selectedOption ? selectedOption.label : placeholder}
|
||||
placeholder={selectedOption ? selectedOption.label : placeholder}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<p className="search__input_text">{placeholder}</p>
|
||||
{/* <IoMdArrowDropdown className={`search_select_icon ${isOpen ? 'open' : ''}`} /> */}
|
||||
</div>
|
||||
{(isOpen || (searchTerm !== "" && filteredOptions.length > 0)) && (
|
||||
<div className="search-options">
|
||||
|
|
@ -80,7 +81,7 @@ const SearchFieldWithSelect: React.FC<Props> = ({
|
|||
onClick={() => handleOptionClick(option)}
|
||||
>
|
||||
<div>{option.label}</div>
|
||||
<IoSearch className="search__icon" />
|
||||
{/* {withIcon && <IoSearch className="search__icon" />} */}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,9 +26,12 @@ const Header = () => {
|
|||
|
||||
return (
|
||||
<header className="exercise_add_header mb-4">
|
||||
<article>
|
||||
<img src="/Icon/QuestionIcon.svg" alt="" />
|
||||
<div>
|
||||
{t("practical.add")} {t("models.exercise")}{" "}
|
||||
{t("practical.add")} {t("models.exercise")}{" "}
|
||||
</div>
|
||||
</article>
|
||||
<div>
|
||||
<GoArrowSwitch onClick={handleChange} className="m-2" />
|
||||
{isBseQuestion || values?.isBase === 1
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import OrderBySelect from '../../Components/Filter/OrderBySelect';
|
|||
import LayoutFilterModal from './LayoutFilterModal';
|
||||
import { BiFilterAlt } from 'react-icons/bi';
|
||||
import { MdKeyboardArrowDown } from "react-icons/md";
|
||||
import SearchField from '../../Components/DataTable/SearchField';
|
||||
|
||||
const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_children:any}) => {
|
||||
const {t} = useTranslation();
|
||||
|
|
@ -48,8 +49,8 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
|
|||
<p>{t("ادخالات")}</p>
|
||||
</span>
|
||||
<div className="header_search">
|
||||
<SearchFieldWithSelect
|
||||
options={translateArray}
|
||||
<SearchField
|
||||
searchBy=''
|
||||
placeholder={t("practical.search_here")}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ const NavBar = ({isOpen}:{isOpen:boolean}) => {
|
|||
<SearchFieldWithSelect
|
||||
options={translateArray}
|
||||
placeholder={t("practical.search_here")}
|
||||
withIcon
|
||||
/>
|
||||
</div>
|
||||
<NavBarRightSide />
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const AddModel: React.FC = () => {
|
|||
handleSubmit={handleSubmit}
|
||||
getInitialValues={getInitialValues({})}
|
||||
getValidationSchema={getValidationSchema}
|
||||
width="40vw"
|
||||
width="500px"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const EditModel: React.FC = () => {
|
|||
getInitialValues={getInitialValues(objectToEdit)}
|
||||
getValidationSchema={getValidationSchema}
|
||||
isAddModal={false}
|
||||
width="40vw"
|
||||
width="500px"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const TableHeader = () => {
|
|||
return (
|
||||
<div className="TableWithHeader">
|
||||
<Suspense fallback={<Spin />}>
|
||||
|
||||
<PageHeader
|
||||
pageTitle="grade"
|
||||
ModelAbility={ModalEnum?.GRADE_ADD}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ const AddModel: React.FC = () => {
|
|||
handleSubmit={handleSubmit}
|
||||
getInitialValues={getInitialValues({})}
|
||||
getValidationSchema={getValidationSchema}
|
||||
width="60vw"
|
||||
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const EditModel: React.FC = () => {
|
|||
getInitialValues={getInitialValues(objectToEdit)}
|
||||
getValidationSchema={getValidationSchema}
|
||||
isAddModal={false}
|
||||
width="60vw"
|
||||
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { enumToArray } from "../../../../api/utils/enumToArray";
|
|||
|
||||
const Form = () => {
|
||||
const termsArray = enumToArray(TermEnum);
|
||||
console.log(termsArray, "termsArray");
|
||||
console.log(termsArray);
|
||||
|
||||
return (
|
||||
<Row className="w-100">
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
} from "../../../utils/hasAbilityFn";
|
||||
import ActionButtons from "../../../Components/Table/ActionButtons";
|
||||
import { Unit } from "../../../types/Unit";
|
||||
import { ConvertEnumToTranslate } from "../../../utils/ConvertEnumToTranslate";
|
||||
|
||||
export const useColumns = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
|
|
@ -52,7 +53,7 @@ export const useColumns = () => {
|
|||
dataIndex: "name",
|
||||
key: "name",
|
||||
align: "center",
|
||||
render: (text, record) => record?.name,
|
||||
render: (text) => text,
|
||||
},
|
||||
|
||||
{
|
||||
|
|
@ -60,20 +61,10 @@ export const useColumns = () => {
|
|||
dataIndex: "term",
|
||||
key: "term",
|
||||
align: "center",
|
||||
render: (text, record) => record?.term,
|
||||
render: (text) => t(ConvertEnumToTranslate(text)),
|
||||
},
|
||||
|
||||
{
|
||||
// canAddUnit ? (
|
||||
// <button
|
||||
// onClick={() => handel_open_model(ModalEnum?.UNIT_ADD)}
|
||||
// className="add_button"
|
||||
// >
|
||||
// {t("practical.add")} {t("models.unit")} <FaPlus />
|
||||
// </button>
|
||||
// ) : (
|
||||
// ""
|
||||
// ),
|
||||
|
||||
title: t("columns.procedure"),
|
||||
key: "actions",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ const CheckboxField = ({
|
|||
disabled={isDisabled}
|
||||
checked={formik.values?.QuestionOptions?.[name]?.isCorrect === 1}
|
||||
className={className}
|
||||
|
||||
>
|
||||
{t(`input.${label ? label : name}`)}
|
||||
</Checkbox>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import File from "./File";
|
|||
import { FaCirclePlus, FaDeleteLeft } from "react-icons/fa6";
|
||||
import { GoTrash } from "react-icons/go";
|
||||
import { LuImagePlus } from "react-icons/lu";
|
||||
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||
|
||||
const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
||||
const formik = useFormikContext<any>();
|
||||
|
|
@ -26,37 +27,41 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
|||
|
||||
formik.setFieldValue("QuestionOptions", updatedQuestionOptions);
|
||||
};
|
||||
console.log(formik.values);
|
||||
|
||||
return (
|
||||
<div className="ChoiceFields">
|
||||
<TextField
|
||||
className="textarea_exercise"
|
||||
placeholder={"choice"}
|
||||
label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
|
||||
label2={t(`input.choice`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||
name={index}
|
||||
id={`choice_${index + 1}`}
|
||||
type="TextArea"
|
||||
/>
|
||||
<File
|
||||
{/* <File
|
||||
className="file_exercise"
|
||||
label={"attachment"}
|
||||
name={index}
|
||||
type="File"
|
||||
placeholder=""
|
||||
icon={<LuImagePlus/>}
|
||||
/>
|
||||
/> */}
|
||||
<ImageBoxField name={`QuestionOptions.${index}.answer_image`} />
|
||||
|
||||
<div className="answer_status">
|
||||
|
||||
<CheckboxField
|
||||
className=""
|
||||
label="The_correct_answer"
|
||||
name={index}
|
||||
type="Checkbox"
|
||||
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
{t("header.delete_choice")}
|
||||
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import DynamicTags from "./Tags/DynamicTags";
|
|||
import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
|
||||
import { useEffect } from "react";
|
||||
import { LuImagePlus } from "react-icons/lu";
|
||||
import ImageBoxField from "../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||
import SelectTag from "./Tags/SelectTag";
|
||||
|
||||
const Form = () => {
|
||||
const [t] = useTranslation()
|
||||
|
|
@ -41,8 +43,9 @@ const Form = () => {
|
|||
return (
|
||||
<Row className="w-100 exercise_form_container">
|
||||
<div className="exercise_form">
|
||||
<ValidationField className="textarea_exercise" name="content" label="details" type="TextArea" />
|
||||
<ValidationField className="file_exercise" name="image" label="attachment" icon={<LuImagePlus/>} placeholder="" type="File" />
|
||||
<ValidationField className="textarea_exercise" name="answer_content" label="answer_content" type="TextArea" />
|
||||
<ImageBoxField name="image" />
|
||||
|
||||
</div>
|
||||
{
|
||||
(((formik?.values as any)?.QuestionOptions as Choice[])||[]) .map((item:Choice,index:number)=>{
|
||||
|
|
@ -53,7 +56,8 @@ const Form = () => {
|
|||
<FaCirclePlus onClick={handleAddChoice} size={23} /> {t("header.add_new_choice")}
|
||||
</p>
|
||||
)}
|
||||
<DynamicTags/>
|
||||
{/* <DynamicTags/> */}
|
||||
{/* <SelectTag/> */}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ const DynamicTags = () => {
|
|||
name: TagsSearch,
|
||||
});
|
||||
const suggests = data?.data;
|
||||
console.log(TagsSearch);
|
||||
|
||||
const handleAddChoice = () => {
|
||||
const length = formik?.values?.tags.length;
|
||||
|
|
|
|||
78
src/Pages/Admin/question/Model/Tags/SelectTag.tsx
Normal file
78
src/Pages/Admin/question/Model/Tags/SelectTag.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import React, { useState, useMemo } from 'react';
|
||||
import { Select, Spin } from 'antd';
|
||||
import { useGetAllTag } from '../../../../../api/tags';
|
||||
import { useDebounce } from '../../../../../utils/useDebounce';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const SelectTag: React.FC = () => {
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
const [fieldValue, setFieldValue] = useState<string>('');
|
||||
|
||||
const handleChange = (value: string[]) => {
|
||||
setSearchValue('');
|
||||
setFieldValue('');
|
||||
};
|
||||
|
||||
const handleSearch = useDebounce((value: string) => {
|
||||
setSearchValue(value);
|
||||
|
||||
});
|
||||
|
||||
const handleFieldChange = (value: string) => {
|
||||
setFieldValue(value);
|
||||
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setSearchValue('');
|
||||
setFieldValue('');
|
||||
};
|
||||
|
||||
const { data, isLoading } = useGetAllTag({
|
||||
name: searchValue,
|
||||
});
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
const options = useMemo(() => {
|
||||
const apiOptions = data?.data ?? [];
|
||||
|
||||
if (searchValue && !apiOptions.some((opt: any) => opt.name === searchValue)) {
|
||||
return [...apiOptions, { id: searchValue, name: searchValue }];
|
||||
}
|
||||
|
||||
return apiOptions;
|
||||
}, [data, searchValue]);
|
||||
|
||||
return (
|
||||
<div className='SelectTag'>
|
||||
|
||||
<label htmlFor="">
|
||||
{t("models.tag")}
|
||||
</label>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
style={{ width: '100%' ,height:"40px"}}
|
||||
placeholder="Please select"
|
||||
fieldNames={{ label: 'name', value: 'id' }}
|
||||
onChange={handleChange}
|
||||
options={options}
|
||||
loading={isLoading}
|
||||
notFoundContent={isLoading ? <Spin /> : t("practical.not_found")}
|
||||
onSearch={(value) => {
|
||||
handleSearch(value);
|
||||
handleFieldChange(value);
|
||||
}}
|
||||
searchValue={fieldValue}
|
||||
onDropdownVisibleChange={(open) => {
|
||||
if (!open) {
|
||||
handleBlur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectTag;
|
||||
|
|
@ -7,7 +7,7 @@ import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ActionButtons from "../../../../Components/Table/ActionButtons";
|
||||
import { canDeleteSubject, canEditSubject } from "../../../../utils/hasAbilityFn";
|
||||
import { canDeleteSubject, canEditSubject, canShowSubject } from "../../../../utils/hasAbilityFn";
|
||||
import { ABILITIES_ENUM } from "../../../../enums/abilities";
|
||||
import { Subject } from "../../../../types/Subject";
|
||||
|
||||
|
|
@ -25,13 +25,17 @@ export const useColumns = () => {
|
|||
setIsOpen(ModalEnum?.SUBJECT_EDIT);
|
||||
};
|
||||
|
||||
const handelShow = (record: Subject) => {
|
||||
navigate(`${ABILITIES_ENUM?.SUBJECT}/${record?.id}`);
|
||||
};
|
||||
|
||||
const [t] = useTranslation();
|
||||
const columns: TableColumnsType<Subject> = [
|
||||
{
|
||||
title: t("columns.id"),
|
||||
dataIndex: "id",
|
||||
key: "id",
|
||||
align: "start",
|
||||
align: "center",
|
||||
},
|
||||
{
|
||||
title: t("columns.subject_name"),
|
||||
|
|
@ -50,23 +54,6 @@ export const useColumns = () => {
|
|||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: t("columns.card"),
|
||||
key: "name",
|
||||
align: "center",
|
||||
className: "SeeMoreEyeColumn",
|
||||
render: (text, record) => {
|
||||
const handelnavigate = () => {
|
||||
navigate(`${ABILITIES_ENUM?.SUBJECT}/${record?.id}`);
|
||||
};
|
||||
return (
|
||||
<div onClick={() => handelnavigate()} className="SeeMoreEye">
|
||||
<FaEye />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: t("columns.procedure"),
|
||||
key: "actions",
|
||||
|
|
@ -77,9 +64,11 @@ export const useColumns = () => {
|
|||
<ActionButtons
|
||||
canDelete={canEditSubject}
|
||||
canEdit={canDeleteSubject}
|
||||
canShow={canShowSubject}
|
||||
index={index}
|
||||
onDelete={() => handelDelete(record)}
|
||||
onEdit={() => handleEdit(record)}
|
||||
onShow={() => handelShow(record)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -298,4 +298,9 @@ button:disabled {
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
.VisibleHidden{
|
||||
visibility: hidden;
|
||||
}
|
||||
|
|
@ -2,9 +2,11 @@
|
|||
position: relative;
|
||||
z-index: 999;
|
||||
border-radius: 10px;
|
||||
box-shadow: 2px 2px 2px 3px rgba(0, 0, 0, 0.02);
|
||||
width: 18vw;
|
||||
direction: ltr;
|
||||
// box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.001);
|
||||
width: 220px;
|
||||
// direction: ltr;
|
||||
color: #6A7287B2;
|
||||
|
||||
}
|
||||
.NavBar {
|
||||
.search-header {
|
||||
|
|
@ -22,13 +24,13 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4vw;
|
||||
padding-inline: 5%;
|
||||
padding-inline: 5px;
|
||||
cursor: pointer;
|
||||
background-color: var(--bg);
|
||||
font-weight: bold;
|
||||
border-radius: 10px;
|
||||
width: 18vw;
|
||||
height: 3vw;
|
||||
// width: 18vw;
|
||||
height: 40px;
|
||||
transition: 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +40,7 @@
|
|||
}
|
||||
|
||||
.search__input {
|
||||
font-size: 1vw;
|
||||
font-size: 14px;
|
||||
width: 70%;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
|
|
@ -68,7 +70,7 @@
|
|||
overflow: auto;
|
||||
scroll-behavior: smooth;
|
||||
scroll-padding: 10rem;
|
||||
direction: ltr;
|
||||
direction: rtl;
|
||||
scroll-behavior: smooth;
|
||||
scroll-padding: 10rem;
|
||||
&::-webkit-scrollbar {
|
||||
|
|
@ -77,14 +79,14 @@
|
|||
|
||||
/* Handle */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: var(--primary);
|
||||
background-color: transparent;
|
||||
border-radius: 5px; /* Adjust border-radius as needed */
|
||||
}
|
||||
|
||||
/* Track */
|
||||
&::-webkit-scrollbar-track {
|
||||
border-radius: 5px; /* Adjust border-radius as needed */
|
||||
background-color: #d3d5d7; /* Set to desired background color */
|
||||
background-color: transparent; /* Set to desired background color */
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +131,7 @@
|
|||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
justify-content: flex-start;
|
||||
gap: 0.4vw;
|
||||
}
|
||||
|
||||
|
|
@ -143,3 +145,16 @@
|
|||
color: #fff !important;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.NavBar{
|
||||
.header_search{
|
||||
.search-field{
|
||||
input::placeholder{
|
||||
color: white !important;
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +43,8 @@
|
|||
}
|
||||
}
|
||||
.search-field{
|
||||
box-shadow: 2px 2px 7px 2px rgba(0, 0, 0, 0.1);
|
||||
// box-shadow: 2px 2px 7px 2px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
.search__icon{
|
||||
color: var(--primary);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
gap: 1vw;
|
||||
width: 100%;
|
||||
.textarea_exercise {
|
||||
max-width: 40vw;
|
||||
max-width: 50vw;
|
||||
}
|
||||
.file_exercise {
|
||||
min-width: 10vw;
|
||||
|
|
@ -141,10 +141,46 @@
|
|||
.delete_question_options{
|
||||
margin-top: 25px;
|
||||
color: var(--warning);
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
|
||||
padding-left: 10px;
|
||||
.trash_icon{
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.answer_status{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-block: 30px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.add_new_button{
|
||||
svg{
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.exercise_add_header{
|
||||
article{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-wrap: nowrap;
|
||||
gap: 10px;
|
||||
padding-inline: 20px;
|
||||
font-weight: bold;
|
||||
img{
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.SelectTag{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-block: 10px;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
export const enumToArray = (enumObj: any) => {
|
||||
return Object.keys(enumObj).map((key) => ({
|
||||
value: enumObj[key],
|
||||
label: enumObj[key],
|
||||
label: `select.enums.${enumObj[key]}`,
|
||||
}));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@
|
|||
"Pass": "نجاح",
|
||||
"users": "المستخدمين",
|
||||
"branchAdmin": "مسؤول الفروع",
|
||||
"grade": "الصفوف",
|
||||
"grade": "الدرجات",
|
||||
"homeworkAttachment": "مرفق الواجب المنزلي",
|
||||
"lateArrival": "وصول متأخر",
|
||||
"noteAttachment": "مرفق الملاحظة",
|
||||
|
|
@ -248,7 +248,7 @@
|
|||
"subjectAttachment": "مرفق المادة",
|
||||
"exercise": "تمارين",
|
||||
"exerciseAnswer": "إجابة التمارين",
|
||||
"tag": "العلامات",
|
||||
"tag": "كلمات مفتاحية",
|
||||
"Exam": "امتحانات",
|
||||
"ExamType": "نوع الامتحانات",
|
||||
"studentParent": "ولي أمر الطالب",
|
||||
|
|
@ -372,7 +372,8 @@
|
|||
"main_question": "النص الأساسي ",
|
||||
"question": "السؤال",
|
||||
"id": "الرقم التعريفي",
|
||||
"icon": "الايقونة"
|
||||
"icon": "الايقونة",
|
||||
"answer_content":"نص السؤال"
|
||||
},
|
||||
"select": {
|
||||
"Payments": {
|
||||
|
|
@ -624,6 +625,10 @@
|
|||
"Admin_type": {
|
||||
"admin": "المسؤول",
|
||||
"branchAdmin": "مسؤول الفروع"
|
||||
},
|
||||
"enums":{
|
||||
"first_term":"الفصل الأول",
|
||||
"second_term":"الفصل الثاني"
|
||||
}
|
||||
},
|
||||
"array": {
|
||||
|
|
@ -674,7 +679,7 @@
|
|||
"tags": "كلمات مفتاحية",
|
||||
"main_menu": "القائمة الرئيسية",
|
||||
"setting": "الإعدادات",
|
||||
"grade": "الصفوف",
|
||||
"grade": "الدرجات",
|
||||
"curriculum": "مقرر",
|
||||
"package": "حزمة",
|
||||
"subjects":"مواد",
|
||||
|
|
@ -737,8 +742,37 @@
|
|||
"Question": "لوحة القيادة /اسئلة ",
|
||||
"add_Question": "لوحة القيادة /إضافة اسئلة ",
|
||||
"edit_Question": "لوحة القيادة /تعديل اسئلة ",
|
||||
"grade":"الصفوف",
|
||||
"grade":"الدرجات",
|
||||
"report":"تقرير",
|
||||
"user":"مستخدم"
|
||||
},
|
||||
"alphabet":{
|
||||
"A": "أ",
|
||||
"B": "ب",
|
||||
"C": "ت",
|
||||
"D": "ث",
|
||||
"E": "ج",
|
||||
"F": "ح",
|
||||
"G": "خ",
|
||||
"H": "د",
|
||||
"I": "ذ",
|
||||
"J": "ر",
|
||||
"K": "ز",
|
||||
"L": "س",
|
||||
"M": "ش",
|
||||
"N": "ص",
|
||||
"O": "ض",
|
||||
"P": "ط",
|
||||
"Q": "ظ",
|
||||
"R": "ع",
|
||||
"S": "غ",
|
||||
"T": "ف",
|
||||
"U": "ق",
|
||||
"V": "ك",
|
||||
"W": "ل",
|
||||
"X": "م",
|
||||
"Y": "ن",
|
||||
"Z": "ه"
|
||||
|
||||
}
|
||||
}
|
||||
7
src/utils/ConvertEnumToTranslate.ts
Normal file
7
src/utils/ConvertEnumToTranslate.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
|
||||
export function ConvertEnumToTranslate(enum_value:string):string{
|
||||
|
||||
return `select.enums.${enum_value}`
|
||||
|
||||
}
|
||||
10
src/utils/getNestedValue.ts
Normal file
10
src/utils/getNestedValue.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export const getNestedValue = (obj: any, path: string): any => {
|
||||
return path.split('.').reduce((acc, part) => {
|
||||
const arrayMatch = part.match(/(\w+)\[(\d+)\]/);
|
||||
if (arrayMatch) {
|
||||
const [, key, index] = arrayMatch;
|
||||
return acc && acc[key] && acc[key][index];
|
||||
}
|
||||
return acc && acc[part];
|
||||
}, obj);
|
||||
};
|
||||
|
|
@ -112,6 +112,10 @@ export const canDeleteSubject = hasAbility(
|
|||
ABILITIES_VALUES_ENUM.DELETE,
|
||||
);
|
||||
|
||||
export const canShowSubject = hasAbility(
|
||||
ABILITIES_ENUM.SUBJECT,
|
||||
ABILITIES_VALUES_ENUM.SHOW,
|
||||
)
|
||||
/// Role
|
||||
|
||||
export const canAddRole = hasAbility(
|
||||
|
|
|
|||
25
src/utils/useDebounce.ts
Normal file
25
src/utils/useDebounce.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { useCallback, useRef } from 'react';
|
||||
|
||||
export const DEBOUNCE_DELAY = 500;
|
||||
|
||||
export const useDebounce = (
|
||||
callback: (...args: any[]) => void,
|
||||
delay: number = DEBOUNCE_DELAY,
|
||||
) => {
|
||||
const timeoutRef = useRef<any>(null);
|
||||
|
||||
const debouncedCallback = useCallback(
|
||||
(...args: any[]) => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
callback(...args);
|
||||
}, delay);
|
||||
},
|
||||
[callback, delay],
|
||||
);
|
||||
|
||||
return debouncedCallback;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user