diff --git a/src/Pages/Admin/Notifications/AddNotification/Add/Page.tsx b/src/Pages/Admin/Notifications/AddNotification/Add/Page.tsx
new file mode 100644
index 0000000..229e064
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Add/Page.tsx
@@ -0,0 +1,76 @@
+import { useTranslation } from "react-i18next";
+import useSetPageTitle from "../../../../../Hooks/useSetPageTitle";
+import PageHeader from "../../../../../Layout/Dashboard/PageHeader";
+import { Suspense, useEffect } from "react";
+import { Spin } from "antd";
+import { ModalEnum } from "../../../../../enums/Model";
+import PersonalDetailsForm from "../Form/PersonalDetailsForm";
+import { Formik, Form } from "formik";
+import { getInitialValues, getValidationSchema } from "../Form/formUtils";
+import TitleDetailsForm from "../Form/TitleDetailsForm";
+import AttachmentForm from "../Form/AttachmentForm";
+import { useNavigate } from "react-router-dom";
+import { QueryStatusEnum } from "../../../../../enums/QueryStatus";
+import { useAddNotification } from "../../../../../api/notification";
+
+const TableHeader = () => {
+ const [t] = useTranslation();
+ const Navigate = useNavigate();
+ const { mutate, isSuccess, status } = useAddNotification();
+ useSetPageTitle(t(`page_header.add_notification`));
+ const handleSubmit = (values: any) => {
+ const DataToSend = {
+ ...values,
+ location: {
+ lat: values.lat,
+ lng: values.lng,
+ },
+ };
+ mutate(DataToSend);
+ };
+ useEffect(() => {
+ if (isSuccess === true) {
+ console.log(1);
+ Navigate('/add_Notifications')
+ }
+ }, [isSuccess])
+
+ return (
+
+
}>
+
+
+
+ {({ dirty }) => (
+
+ )}
+
+
+
+
+ );
+};
+
+export default TableHeader;
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/AttachmentForm.tsx b/src/Pages/Admin/Notifications/AddNotification/Form/AttachmentForm.tsx
new file mode 100644
index 0000000..a47ec54
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/AttachmentForm.tsx
@@ -0,0 +1,30 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+import { FaImage } from "react-icons/fa";
+import ImageBoxField from "./ImageBoxField/ImageBoxField";
+import ValidationField from "../../../../../Components/ValidationField/ValidationField";
+
+const AttachmentForm = () => {
+ const [t] = useTranslation();
+
+ return (
+
+
+
+ {t("header.attachment")}
+
+
+
+ );
+};
+
+export default AttachmentForm;
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/FilterForm.tsx b/src/Pages/Admin/Notifications/AddNotification/Form/FilterForm.tsx
new file mode 100644
index 0000000..c91b911
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/FilterForm.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+import ValidationField from "../../../../../Components/ValidationField/ValidationField";
+import { Col, Row } from "reactstrap";
+
+const FilterForm = () => {
+ return (
+
+
+
+
+ {/* */}
+
+ {/*
+
+ */}
+
+
+ );
+};
+
+export default FilterForm;
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageBoxField.scss b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageBoxField.scss
new file mode 100644
index 0000000..66e2f2f
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageBoxField.scss
@@ -0,0 +1,38 @@
+.ImageBoxField {
+ .ImageBox {
+ width: 120px;
+ height: 120px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: max(1.5px, 0.1vw) dashed #a9c3f1;
+ margin-block: 10px;
+ border-radius: 5px;
+ z-index: 9999999 !important;
+ .ImageBoxIcon {
+ cursor: pointer;
+ }
+ .imagePreview {
+ max-width: 99%;
+ height: auto;
+ max-height: 99%;
+ object-fit: contain;
+ border-radius: 5px;
+ }
+ }
+ .ImageHeader {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 10px;
+ }
+
+ .ImageCancelIcon {
+ width: 16px !important;
+ height: 16px !important;
+ }
+ .ImageBoxIcon {
+ width: 20px !important;
+ height: 20px !important;
+ }
+}
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageBoxField.tsx b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageBoxField.tsx
new file mode 100644
index 0000000..fae09b2
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageBoxField.tsx
@@ -0,0 +1,87 @@
+import { useFormikContext } from "formik";
+import { useState, useRef, useEffect } from "react";
+import "./ImageBoxField.scss";
+import ImageIcon from "./ImageIcon";
+import ImageCancelIcon from "./ImageCancelIcon";
+import { generateImagePreview } from "./generateImagePreview";
+import { getNestedValue } from "../../../../../../utils/getNestedValue";
+import { useTranslation } from "react-i18next";
+
+// Helper function to generate image preview from a File
+
+const ImageBoxField = ({ name }: any) => {
+ const formik = useFormikContext();
+ const value = getNestedValue(formik.values, name);
+ const [imagePreview, setImagePreview] = useState(null);
+ const fileInputRef = useRef(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) {
+ generateImagePreview(file, setImagePreview);
+ formik.setFieldValue(name, file);
+ }
+ };
+
+ const handleButtonClick = () => {
+ const fileInput = fileInputRef.current;
+ if (fileInput) {
+ fileInput.click();
+ }
+ };
+
+ const handleCancel = () => {
+ setImagePreview("");
+ formik.setFieldValue(name, "");
+
+ if (fileInputRef.current) {
+ fileInputRef.current.value = "";
+ }
+ };
+ const [t] = useTranslation();
+ return (
+
+
+
+ {imagePreview ? (
+ <>
+
+
+ >
+ ) : (
+
hidden
+ )}
+
+
+ {imagePreview ? (
+

+ ) : (
+
+ )}
+
+
+
+ );
+};
+
+export default ImageBoxField;
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageCancelIcon.tsx b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageCancelIcon.tsx
new file mode 100644
index 0000000..d42ba53
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageCancelIcon.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+
+interface ImageCancelIconProps extends React.HTMLAttributes {}
+
+const ImageCancelIcon: React.FC = (props) => {
+ return (
+
+ );
+};
+
+export default ImageCancelIcon;
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageIcon.tsx b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageIcon.tsx
new file mode 100644
index 0000000..4ca597a
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/ImageIcon.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+
+interface ImageIconProps extends React.HTMLAttributes {}
+
+const ImageIcon: React.FC = (props) => {
+ return (
+
+ );
+};
+
+export default ImageIcon;
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/generateImagePreview.ts b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/generateImagePreview.ts
new file mode 100644
index 0000000..3f754d3
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/ImageBoxField/generateImagePreview.ts
@@ -0,0 +1,10 @@
+export const generateImagePreview = (
+ file: File,
+ setImagePreview: (result: string) => void,
+) => {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ setImagePreview(reader.result as string);
+ };
+ reader.readAsDataURL(file);
+};
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/PersonalDetailsForm.tsx b/src/Pages/Admin/Notifications/AddNotification/Form/PersonalDetailsForm.tsx
new file mode 100644
index 0000000..719233b
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/PersonalDetailsForm.tsx
@@ -0,0 +1,63 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+import { FaStore } from "react-icons/fa";
+import ValidationField from "../../../../../Components/ValidationField/ValidationField";
+import { convert_data_to_select } from "../../../../../Layout/app/Const";
+import { userTypeOptions } from "../../../../../config/userTypeOptions";
+import { statusType } from "../../../../../config/statusType";
+import { IoIosNotifications } from "react-icons/io";
+
+const PersonalDetailsForm = ({isEdit}:{isEdit?:boolean}) => {
+ const [t] = useTranslation();
+ return (
+
+
+
+ {t("header.notification_details")}
+
+
+
+
+
+
+
+
+ {isEdit? "" :
+
+ }
+
+
+
+ );
+};
+
+export default PersonalDetailsForm;
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/TitleDetailsForm.tsx b/src/Pages/Admin/Notifications/AddNotification/Form/TitleDetailsForm.tsx
new file mode 100644
index 0000000..cb04ba6
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/TitleDetailsForm.tsx
@@ -0,0 +1,31 @@
+import React, { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { FaRegAddressBook } from "react-icons/fa";
+import ValidationField from "../../../../../Components/ValidationField/ValidationField";
+import { useGetAllArea } from "../../../../../api/Area";
+import { useGetAllCity } from "../../../../../api/City";
+import { FaUsers } from "react-icons/fa6";
+
+const TitleDetailsForm = () => {
+ const [t] = useTranslation();
+ const {data:city} = useGetAllCity();
+ const [CityId, setCityId] = useState()
+
+ const {data} = useGetAllArea({
+ city_id:CityId
+ });
+
+ return (
+
+
+
+ {t("header.users")}
+
+
+ {/* */}
+
+
+ );
+};
+
+export default TitleDetailsForm;
diff --git a/src/Pages/Admin/Notifications/AddNotification/Form/formUtils.ts b/src/Pages/Admin/Notifications/AddNotification/Form/formUtils.ts
new file mode 100644
index 0000000..373d192
--- /dev/null
+++ b/src/Pages/Admin/Notifications/AddNotification/Form/formUtils.ts
@@ -0,0 +1,95 @@
+import * as Yup from "yup";
+import { objectToKeyValueArray } from "../../../../../utils/objectToKeyValueArray";
+
+interface Location {
+ lat: number;
+ lng: number;
+}
+interface User {
+ username: string;
+ phone_number?: number;
+ type?:string
+}
+
+
+interface PersonalDetailsForm {
+ id: number;
+ first_name: string | null;
+ last_name: string | null;
+ location: Location[];
+ contact_number1: string | null;
+ contact_number2: string | null;
+ card_number: string | null;
+ username: string | null;
+ password: string | null;
+ area_id: number | null;
+ lat:number,
+ lng:number
+}
+
+interface PersonalDetailsEditForm {
+ id: number;
+ first_name: string | null;
+ last_name: string | null;
+ location: Location[];
+ contact_number1: string | null;
+ contact_number2: string | null;
+ card_number: string | null;
+ user: User;
+ area_id: any | null;
+ lat:number,
+ lng:number
+}
+
+export const getInitialValues = (objectToEdit: Partial) => {
+ const location = objectToEdit?.location?.[0] || { lat: 33.5138, lng: 36.2765 };
+
+ return {
+ id: objectToEdit?.id ?? 0,
+ first_name: objectToEdit?.first_name ?? null,
+ last_name: objectToEdit?.last_name ?? null,
+ location_lat: location.lat,
+ location_lng: location.lng,
+ contact_number1: objectToEdit?.contact_number1 ?? null,
+ contact_number2: objectToEdit?.contact_number2 ?? null,
+ card_number: objectToEdit?.card_number ?? null,
+ username: objectToEdit?.username ?? null,
+ password: objectToEdit?.password ?? null,
+ area_id: objectToEdit?.area_id ?? null,
+ lat: location.lat ?? 33.5138,
+ lng: location.lng ?? 36.2765,
+ };
+};
+
+export const getValidationSchema = () => {
+ // validate input
+ return Yup.object().shape({
+ id: Yup.number().required(),
+ first_name: Yup.string().required('first_name is required'),
+ last_name: Yup.string().required('last_name is required'),
+ location_lat: Yup.string().required('lat is required'),
+ location_lng: Yup.string().required('lng is required'),
+ contact_number1: Yup.string().required('contact_number1 is required'),
+ contact_number2: Yup.string().required('contact_number2 is required'),
+ username: Yup.string().required('username is required'),
+ area_id: Yup.mixed().required('area_id is required'),
+ });
+};
+
+
+export const getInitialValuesEdit = (objectToEdit: Partial) => {
+ const location = objectToEdit?.location || { lat: 0, lng: 0 };
+
+ return {
+ id: objectToEdit?.id ?? 0,
+ first_name: objectToEdit?.first_name ?? null,
+ last_name: objectToEdit?.last_name ?? null,
+ location_lat: location.lat,
+ location_lng: location.lng,
+ contact_number1: objectToEdit?.contact_number1 ?? null,
+ contact_number2: objectToEdit?.contact_number2 ?? null,
+ card_number: objectToEdit?.card_number ?? null,
+ username: objectToEdit?.user?.username ?? null,
+ area_id: objectToEdit?.area_id ?? null,
+ };
+};
\ No newline at end of file
diff --git a/src/Pages/Admin/Notifications/AddNotification/Page.tsx b/src/Pages/Admin/Notifications/AddNotification/Page.tsx
index f600b15..e4e2e99 100644
--- a/src/Pages/Admin/Notifications/AddNotification/Page.tsx
+++ b/src/Pages/Admin/Notifications/AddNotification/Page.tsx
@@ -8,6 +8,7 @@ import FilterLayout from "../../../../Layout/Dashboard/FilterLayout";
import FilterForm from "./Model/FilterForm";
import { canAddNotification } from "../../../../utils/hasAbilityFn";
import { useDeleteNotification } from "../../../../api/notification";
+import { ABILITIES_ENUM } from "../../../../enums/abilities";
const Table = lazy(() => import("./Table"));
const AddModalForm = lazy(() => import("./Model/AddModel"));
@@ -30,14 +31,16 @@ const TableHeader = () => {
}>
} filterTitle="table.notification" />
-
+ {/* */}
import("./Pages/Admin/User/Page"));
const QuestionBank = React.lazy(() => import("./Pages/Admin/QuestionBank/Page"));
const AllNotifications = React.lazy(() => import("./Pages/Admin/Notifications/Page"));
const Notifications = React.lazy(() => import("./Pages/Admin/Notifications/AddNotification/Page"));
+const AddNotification = React.lazy(() => import("./Pages/Admin/Notifications/AddNotification/Add/Page"));
+
const Profile = React.lazy(() => import("./Pages/Admin/Profile/Page"));
const Setting = React.lazy(() => import("./Pages/Admin/Setting/Page"));
@@ -401,6 +403,16 @@ export const CrudRoute: TCrudRoute[] = [
// abilities_value: ABILITIES_VALUES_ENUM.INDEX,
// prevPath: 0,
// },
+
+ {
+ header: "page_header.add_notification",
+ element: ,
+ path: `/${ABILITIES_ENUM?.NOTIFICATIONS}/add`,
+ abilities: ABILITIES_ENUM?.NOTIFICATIONS,
+ abilities_value: ABILITIES_VALUES_ENUM.INDEX,
+ prevPath: 0,
+ },
+
//// RE_SELLER
{
header: "page_header.notifications",
diff --git a/src/translate/ar.json b/src/translate/ar.json
index 95a2dc7..d354c29 100644
--- a/src/translate/ar.json
+++ b/src/translate/ar.json
@@ -154,7 +154,9 @@
"show_preview":"عرض المعاينة",
"show_MMl":" MML عرض",
"financial_collection":"التحصيلات",
- "change_direction":"تغيير الاتجاه"
+ "change_direction":"تغيير الاتجاه",
+ "notification_details":"تفاصيل الإشعار",
+ "users":"المستخدمون"
},
"columns": {
"id": "الرقم التعريفي",
@@ -971,7 +973,8 @@
"Coupon":"قسيمة",
"financial_collection":"التحصيلات",
"show_collection":"حصيلة",
- "notification":"إدارة الاشعارات"
+ "notification":"إدارة الاشعارات",
+ "manage_notification":"إدارة الإشعارات"
},
"page_header": {
"home": "لوحة القيادة",