This commit is contained in:
parent
f9d87ebb0f
commit
1e83413544
51
package-lock.json
generated
51
package-lock.json
generated
|
|
@ -68,6 +68,7 @@
|
|||
"typescript": "^4.9.5",
|
||||
"vite": "^5.4.8",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-env-compatible": "^2.0.1",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
|
|
@ -5275,6 +5276,23 @@
|
|||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-expand": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
|
||||
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
|
|
@ -14656,6 +14674,17 @@
|
|||
"vite": ">=2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-env-compatible": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-env-compatible/-/vite-plugin-env-compatible-2.0.1.tgz",
|
||||
"integrity": "sha512-DRrOZTg/W44ojVQQfGSMPEgYQGzp5TeIpt9cpaK35hTOC/b2D7Ffl8/RIgK8vQ0mlnDIUgETcA173bnMEkyzdw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dotenv": "8.2.0",
|
||||
"dotenv-expand": "5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/void-elements": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||
|
|
@ -18867,6 +18896,18 @@
|
|||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
|
||||
"dev": true
|
||||
},
|
||||
"dotenv-expand": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
|
||||
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
|
||||
"dev": true
|
||||
},
|
||||
"ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
|
|
@ -25325,6 +25366,16 @@
|
|||
"fs-extra": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"vite-plugin-env-compatible": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-env-compatible/-/vite-plugin-env-compatible-2.0.1.tgz",
|
||||
"integrity": "sha512-DRrOZTg/W44ojVQQfGSMPEgYQGzp5TeIpt9cpaK35hTOC/b2D7Ffl8/RIgK8vQ0mlnDIUgETcA173bnMEkyzdw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dotenv": "8.2.0",
|
||||
"dotenv-expand": "5.1.0"
|
||||
}
|
||||
},
|
||||
"void-elements": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
"typescript": "^4.9.5",
|
||||
"vite": "^5.4.8",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-env-compatible": "^2.0.1",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ interface Props {
|
|||
}
|
||||
|
||||
const SearchField: React.FC<Props> = ({ placeholder, searchBy }) => {
|
||||
const { setFilter, Filter } = useFilterStateState();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||
const [searchQuery, setSearchQuery] = useState<string>(Filter[searchBy] || "");
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const { setFilter, Filter } = useFilterStateState();
|
||||
const { page } = PaginationParams(location);
|
||||
|
||||
const handleInputChange = (value: string) => {
|
||||
|
|
@ -25,6 +25,7 @@ const SearchField: React.FC<Props> = ({ placeholder, searchBy }) => {
|
|||
if (Number(page) > 1) {
|
||||
}
|
||||
setFilter({
|
||||
...Filter,
|
||||
[searchBy]: value,
|
||||
});
|
||||
});
|
||||
|
|
@ -46,6 +47,7 @@ const SearchField: React.FC<Props> = ({ placeholder, searchBy }) => {
|
|||
};
|
||||
}, []);
|
||||
|
||||
console.log(searchQuery)
|
||||
const [t] = useTranslation();
|
||||
return (
|
||||
<div className={`search-field ${isOpen ? "open" : ""}`}>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ const SearchField = ({
|
|||
canChangePage,
|
||||
PageName,
|
||||
page,
|
||||
clear,
|
||||
...props
|
||||
}: SearchFieldProps) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
|
|
@ -92,6 +93,14 @@ const SearchField = ({
|
|||
}
|
||||
};
|
||||
|
||||
// to clear
|
||||
useEffect(()=>{
|
||||
if(clear?.value){
|
||||
formik.setFieldValue(name, null)
|
||||
clear?.method?.(false)
|
||||
}
|
||||
},[clear?.value])
|
||||
|
||||
const handleScroll = (event: any) => {
|
||||
const target = event.target;
|
||||
const isAtBottom =
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Select } from "antd";
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { translateOptions } from "../utils/translatedOptions";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
|
|
@ -18,6 +18,7 @@ const SelectField = ({
|
|||
no_label,
|
||||
label_icon,
|
||||
isLoading,
|
||||
clear,
|
||||
...props
|
||||
}: SelectFieldProps) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
|
|
@ -29,6 +30,13 @@ const SelectField = ({
|
|||
};
|
||||
const options = translateOptions(option, t);
|
||||
|
||||
// to clear
|
||||
useEffect(()=>{
|
||||
if(clear?.value){
|
||||
formik.setFieldValue(name, null)
|
||||
clear?.method?.(false)
|
||||
}
|
||||
},[clear?.value])
|
||||
return (
|
||||
<div className="ValidationField w-100">
|
||||
<ValidationFieldLabel
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ export type SelectFieldProps = BaseFieldProps &
|
|||
canChangePage?: boolean;
|
||||
PageName?: string;
|
||||
page?: number;
|
||||
clear?:{
|
||||
method:(value:boolean) => void,
|
||||
value: boolean,
|
||||
}
|
||||
};
|
||||
|
||||
export type SearchFieldProps = BaseFieldProps &
|
||||
|
|
@ -36,6 +40,10 @@ export type SearchFieldProps = BaseFieldProps &
|
|||
canChangePage: boolean;
|
||||
PageName: string;
|
||||
page: number;
|
||||
clear?:{
|
||||
method:(value:boolean) => void,
|
||||
value: boolean,
|
||||
}
|
||||
};
|
||||
|
||||
type DateFieldProps = BaseFieldProps & {
|
||||
|
|
|
|||
54
src/Components/dependensesFields/DependensesFields.tsx
Normal file
54
src/Components/dependensesFields/DependensesFields.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import React from "react";
|
||||
import { disapledDependsOn } from "./handles";
|
||||
|
||||
|
||||
|
||||
export type children = {
|
||||
defaultValue?:any
|
||||
dependensValue: number | string | undefined | Array<any> | any;
|
||||
props:{[key:string]:any}
|
||||
};
|
||||
export type parent = {
|
||||
defaultValue?:any,
|
||||
props:{[key:string]:any}
|
||||
};
|
||||
|
||||
type DependensesFieldesProps = {
|
||||
parent?: parent;
|
||||
childrens?: children[];
|
||||
Component: React.ComponentType<any>;
|
||||
};
|
||||
|
||||
function DependensesFieldes({
|
||||
parent,
|
||||
childrens,
|
||||
Component,
|
||||
}: DependensesFieldesProps) {
|
||||
return (
|
||||
<>
|
||||
{parent && React.createElement(Component, {
|
||||
...{...parent.props},
|
||||
...(parent.defaultValue && { defaultValue: parent.defaultValue }),
|
||||
})}
|
||||
|
||||
{childrens?.map((child: children, index: number) =>
|
||||
React.createElement(Component, {
|
||||
key: index,
|
||||
...{...child.props},
|
||||
...(child.defaultValue
|
||||
? {
|
||||
defaultValue: child.defaultValue,
|
||||
clear: disapledDependsOn(child.dependensValue)
|
||||
? { value: disapledDependsOn(child.dependensValue) }
|
||||
: undefined,
|
||||
}
|
||||
: { clear: { value: disapledDependsOn(child.dependensValue) } }),
|
||||
disabled:disapledDependsOn(child.dependensValue),
|
||||
|
||||
})
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default DependensesFieldes;
|
||||
11
src/Components/dependensesFields/handles.ts
Normal file
11
src/Components/dependensesFields/handles.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export const disapledDependsOn =(value:any) =>{
|
||||
let status = false
|
||||
if (!value || (Array.isArray(value) && value.length === 0)) {
|
||||
return true;
|
||||
}
|
||||
else if(Array.isArray(value)){
|
||||
value.filter((item) => disapledDependsOn(item) ).length > 0 ? status = true:''
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@ const FilterLayout = ({
|
|||
const { t } = useTranslation();
|
||||
const translateArray = translateOptions(search_array, t);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const isMultiSearch :boolean = search_by.split(" ").length > 1
|
||||
|
||||
const { FilterBody, FilterSubmit } = useFilter();
|
||||
return (
|
||||
|
|
@ -74,7 +75,14 @@ const FilterLayout = ({
|
|||
</span>
|
||||
|
||||
<div className="header_search">
|
||||
{haveSearch && (
|
||||
{isMultiSearch ?
|
||||
search_by.split(" ").map(searchBy =>
|
||||
<SearchField
|
||||
searchBy={searchBy}
|
||||
placeholder={t(`practical.${searchBy}`)}
|
||||
/>
|
||||
)
|
||||
: haveSearch && (
|
||||
<SearchField
|
||||
searchBy={search_by}
|
||||
placeholder={t("practical.search_here")}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { useGetAllUnit } from "../../../../api/unit";
|
|||
import { useGetAllSubject } from "../../../../api/subject";
|
||||
import { useGetAllLesson } from "../../../../api/lesson";
|
||||
import { useGetAllTag } from "../../../../api/tags";
|
||||
import DependensesFieldes from "../../../../Components/dependensesFields/DependensesFields";
|
||||
|
||||
const FilterForm = () => {
|
||||
const { ValidationParamState } = useValidationValidationParamState();
|
||||
|
|
@ -23,6 +24,13 @@ const FilterForm = () => {
|
|||
TagName,
|
||||
TagCurrentPage,
|
||||
} = ValidationParamState;
|
||||
const formik :any= useFormikContext()
|
||||
|
||||
|
||||
const grade_id = formik.values.grade_id?.id
|
||||
const subject_id = formik.values?.subject_id?.id
|
||||
const unit_id = formik.values?.unit_id?.id
|
||||
const lessonsIds = formik.values?.lessonsIds?.map((lesson:any) => lesson.id)
|
||||
|
||||
const { data: Grade, isLoading: isLoadingGrade } = useGetAllGrade({
|
||||
name: GradeName,
|
||||
|
|
@ -36,7 +44,12 @@ const FilterForm = () => {
|
|||
const { data: Subject, isLoading: isLoadingSubject } = useGetAllSubject({
|
||||
name: SubjectName,
|
||||
page: SubjectCurrentPage,
|
||||
});
|
||||
grade_id
|
||||
},
|
||||
{
|
||||
enabled: !!grade_id
|
||||
}
|
||||
);
|
||||
const SubjectOption = Subject?.data ?? [];
|
||||
const canChangeSubjectPage = !!Subject?.links?.next;
|
||||
const SubjectPage = Subject?.meta?.current_page;
|
||||
|
|
@ -45,7 +58,14 @@ const FilterForm = () => {
|
|||
const { data: Unit, isLoading: isLoadingUnit } = useGetAllUnit({
|
||||
name: UnitName,
|
||||
page: UnitCurrentPage,
|
||||
});
|
||||
grade_id,
|
||||
subject_id
|
||||
|
||||
},
|
||||
{
|
||||
enabled: !!grade_id || !!subject_id
|
||||
}
|
||||
);
|
||||
const UnitOption = Unit?.data ?? [];
|
||||
const canChangeUnitPage = !!Unit?.links?.next;
|
||||
const UnitPage = Unit?.meta?.current_page;
|
||||
|
|
@ -54,7 +74,14 @@ const FilterForm = () => {
|
|||
const { data: Lesson, isLoading: isLoadingLesson } = useGetAllLesson({
|
||||
name: LessonName,
|
||||
page: LessonCurrentPage,
|
||||
});
|
||||
grade_id,
|
||||
unit_id
|
||||
|
||||
},
|
||||
{
|
||||
enabled: !!grade_id || !!unit_id
|
||||
}
|
||||
);
|
||||
const LessonOption = Lesson?.data ?? [];
|
||||
const canChangeLessonPage = !!Lesson?.links?.next;
|
||||
const LessonPage = Lesson?.meta?.current_page;
|
||||
|
|
@ -63,97 +90,104 @@ const FilterForm = () => {
|
|||
const { data: Tag, isLoading: isLoadingTag } = useGetAllTag({
|
||||
name: TagName,
|
||||
page: TagCurrentPage,
|
||||
lessonsIds,
|
||||
grade_id,
|
||||
subject_id,
|
||||
unit_id
|
||||
});
|
||||
|
||||
const TagOption = Tag?.data ?? [];
|
||||
const canChangeTagPage = !!Tag?.links?.next;
|
||||
const TagPage = Tag?.meta?.current_page;
|
||||
|
||||
const colRight:any = [
|
||||
{
|
||||
props: {
|
||||
searchBy: "GradeName",
|
||||
name: "grade_id",
|
||||
label: "grade",
|
||||
type: "Search",
|
||||
option: GradeOption,
|
||||
isLoading: isLoadingGrade,
|
||||
canChangePage: canChangeGradePage,
|
||||
PageName: "GradeCurrentPage",
|
||||
page: GradePage,
|
||||
}
|
||||
},
|
||||
{
|
||||
dependensValue:grade_id,
|
||||
props: {
|
||||
searchBy: "SubjectName",
|
||||
name: "subject_id",
|
||||
label: "subject",
|
||||
type: "Search",
|
||||
option: SubjectOption,
|
||||
isLoading: isLoadingSubject,
|
||||
canChangePage: canChangeSubjectPage,
|
||||
PageName: "SubjectCurrentPage",
|
||||
page: SubjectPage,
|
||||
},
|
||||
},
|
||||
{
|
||||
dependensValue:subject_id,
|
||||
props: {
|
||||
searchBy: "UnitName",
|
||||
name: "unit_id",
|
||||
label: "unit",
|
||||
type: "Search",
|
||||
option: UnitOption,
|
||||
isLoading: isLoadingUnit,
|
||||
canChangePage: canChangeUnitPage,
|
||||
PageName: "UnitCurrentPage",
|
||||
page: UnitPage,
|
||||
},
|
||||
},
|
||||
];
|
||||
const colLeft :any=[
|
||||
|
||||
{
|
||||
dependensValue: unit_id,
|
||||
props: {
|
||||
searchBy: "LessonName",
|
||||
name: "lessonsIds",
|
||||
label: "lesson",
|
||||
type: "Search",
|
||||
option: LessonOption,
|
||||
isMulti: true,
|
||||
isLoading: isLoadingLesson,
|
||||
canChangePage: canChangeLessonPage,
|
||||
PageName: "LessonCurrentPage",
|
||||
page: LessonPage,
|
||||
},
|
||||
},
|
||||
{
|
||||
dependensValue: "none" ,
|
||||
props: {
|
||||
searchBy: "TagName",
|
||||
name: "tagsIds",
|
||||
label: "tag",
|
||||
type: "Search",
|
||||
option: TagOption,
|
||||
isMulti: true,
|
||||
isLoading: isLoadingTag,
|
||||
canChangePage: canChangeTagPage,
|
||||
PageName: "TagCurrentPage",
|
||||
page: TagPage,
|
||||
},
|
||||
}
|
||||
]
|
||||
console.log(formik.values)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col>
|
||||
{/*
|
||||
grade_id
|
||||
*/}
|
||||
<ValidationField
|
||||
searchBy="GradeName"
|
||||
name="grade_id"
|
||||
label="grade"
|
||||
type="Search"
|
||||
option={GradeOption}
|
||||
isLoading={isLoadingGrade}
|
||||
canChangePage={canChangeGradePage}
|
||||
PageName={"GradeCurrentPage"}
|
||||
page={GradePage}
|
||||
/>
|
||||
<DependensesFieldes parent={colRight[0]} Component={ValidationField} childrens={colRight.slice(1)}/>
|
||||
|
||||
{/*
|
||||
subject_id
|
||||
*/}
|
||||
|
||||
<ValidationField
|
||||
searchBy="SubjectName"
|
||||
name="subject_id"
|
||||
label="subject"
|
||||
type="Search"
|
||||
option={SubjectOption}
|
||||
isLoading={isLoadingSubject}
|
||||
canChangePage={canChangeSubjectPage}
|
||||
PageName={"SubjectCurrentPage"}
|
||||
page={SubjectPage}
|
||||
/>
|
||||
|
||||
{/*
|
||||
TagsIds
|
||||
*/}
|
||||
|
||||
<ValidationField
|
||||
searchBy="TagName"
|
||||
name="tagsIds"
|
||||
label="tag"
|
||||
type="Search"
|
||||
option={TagOption}
|
||||
isMulti
|
||||
isLoading={isLoadingTag}
|
||||
canChangePage={canChangeTagPage}
|
||||
PageName={"TagCurrentPage"}
|
||||
page={TagPage}
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
{/*
|
||||
unit_id
|
||||
*/}
|
||||
|
||||
<ValidationField
|
||||
searchBy="UnitName"
|
||||
name="unit_id"
|
||||
label="unit"
|
||||
type="Search"
|
||||
option={UnitOption}
|
||||
isLoading={isLoadingUnit}
|
||||
canChangePage={canChangeUnitPage}
|
||||
PageName={"UnitCurrentPage"}
|
||||
page={UnitPage}
|
||||
/>
|
||||
|
||||
{/*
|
||||
lessonsIds
|
||||
*/}
|
||||
|
||||
<ValidationField
|
||||
searchBy="LessonName"
|
||||
name="lessonsIds"
|
||||
label="lesson"
|
||||
type="Search"
|
||||
option={LessonOption}
|
||||
isMulti
|
||||
isLoading={isLoadingLesson}
|
||||
canChangePage={canChangeLessonPage}
|
||||
PageName={"LessonCurrentPage"}
|
||||
page={LessonPage}
|
||||
/>
|
||||
<DependensesFieldes Component={ValidationField} childrens={colLeft}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const TableHeader = () => {
|
|||
/>
|
||||
<FilterLayout
|
||||
width="700px"
|
||||
search_by="content"
|
||||
search_by="content hasAnswer hasRightAnswer"
|
||||
sub_children={<FilterForm />}
|
||||
filterTitle="table.QuestionBank"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ const App: React.FC = () => {
|
|||
unit_id: filterState?.unit_id?.id,
|
||||
lessonsIds: ConvertArrayToArrayOfIds(filterState?.lessonsIds),
|
||||
content: Filter?.content,
|
||||
hasAnswer: Filter?.hasAnswer,
|
||||
hasRightAnswer: Filter?.hasRightAnswer,
|
||||
sort_by,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export const useColumns = () => {
|
|||
// console.log(grade,"grade");
|
||||
|
||||
|
||||
setFilter({});
|
||||
// setFilter({});
|
||||
navigate(
|
||||
`/${ABILITIES_ENUM?.GRADE}/${grade?.id}/${ABILITIES_ENUM?.SUBJECT}/${subject?.id}/${ABILITIES_ENUM?.UNIT}/${unit?.id}/${ABILITIES_ENUM?.LESSON}/${lesson?.id}/${ABILITIES_ENUM?.QUESTION}/${record?.id}`,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -252,6 +252,9 @@
|
|||
"notifiable_type": "المستخدمون"
|
||||
},
|
||||
"practical": {
|
||||
"content":"السؤال",
|
||||
"hasAnswer":"الجواب",
|
||||
"hasRightAnswer":"الجواب الصحيح",
|
||||
"to_confirm_deletion_please_re_enter": "لتأكيد الحذف، يرجى إعادة الإدخال",
|
||||
"back": "العودة",
|
||||
"add": "إضافة",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user