diff --git a/client/src/assets/styles/components/Alert.scss b/client/src/assets/styles/components/Alert.scss
index 62c53daf..9d02e15b 100644
--- a/client/src/assets/styles/components/Alert.scss
+++ b/client/src/assets/styles/components/Alert.scss
@@ -4,17 +4,18 @@
display: flex;
flex-direction: column;
- width: 340px;
+ width: 100%;
height: fit-content;
flex-shrink: 0;
border-radius: 15px;
background: $--grey-800;
padding: 15px 15px;
+ margin-block: 1rem;
justify-content: center;
- align-items: end;
+ direction: rtl;
- gap: 10px;
+ gap: 20px;
}
.top-bar {
@@ -62,13 +63,6 @@
}
.title {
- text-align: right;
- font-family: Noto Kufi Arabic;
- font-size: 19.2px;
- font-style: normal;
- font-weight: 500;
- line-height: normal;
-
&.main {
color: $--grey-300;
}
@@ -88,17 +82,6 @@
}
}
-.alert-info {
- height: 65%;
- text-align: right;
- color: $--grey-300;
- font-family: Noto Kufi Arabic;
- font-size: 13.33px;
- font-style: normal;
- font-weight: 400;
- line-height: normal;
-}
-
.alert-btn {
display: flex;
width: 100px;
@@ -113,12 +96,6 @@
color: $--grey-800;
- font-family: Noto Kufi Arabic;
- font-size: 13.33px;
- font-style: normal;
- font-weight: 400;
- line-height: normal;
-
&.red {
background: var(--Red-700, #C81E1E);
}
diff --git a/client/src/assets/styles/components/Inputs.scss b/client/src/assets/styles/components/Inputs.scss
index 49241bdc..cde26fad 100644
--- a/client/src/assets/styles/components/Inputs.scss
+++ b/client/src/assets/styles/components/Inputs.scss
@@ -7,6 +7,12 @@
direction: rtl;
width: 100%;
+ textarea {
+ resize: vertical;
+ width: 100%;
+ min-height: 8rem;
+ }
+
.radio-buttons {
display: flex;
flex-direction: row;
@@ -33,6 +39,20 @@
color: var(--grey-100);
}
+ input[type="radio"] {
+ appearance: none;
+ width: 1rem;
+ height: 1rem;
+ border: 4px solid var(--grey-600);
+ border-radius: 50%;
+ outline: none;
+ margin-inline: 0.5rem;
+}
+
+ input[type="radio"]:checked {
+ border: 6px solid var(--primary-500);
+ }
+
select {
direction: rtl;
}
diff --git a/client/src/assets/styles/components/NotificationBox.scss b/client/src/assets/styles/components/NotificationBox.scss
index 7bb34a54..86cc5fa7 100644
--- a/client/src/assets/styles/components/NotificationBox.scss
+++ b/client/src/assets/styles/components/NotificationBox.scss
@@ -1,2 +1,3 @@
.notificationBox {
+ margin-bottom: 8rem;
}
diff --git a/client/src/components/common/Alerts.jsx b/client/src/components/common/Alerts.jsx
index df3e87cf..b0b4886d 100644
--- a/client/src/components/common/Alerts.jsx
+++ b/client/src/components/common/Alerts.jsx
@@ -5,6 +5,14 @@ const Alert = ({ title, info, buttontext, Onclick, showRightBox, color }) => {
return (
+
-
-
-
{info}
+
{info}
diff --git a/client/src/components/common/Inputs.jsx b/client/src/components/common/Inputs.jsx
index bee38ed4..8e28c269 100644
--- a/client/src/components/common/Inputs.jsx
+++ b/client/src/components/common/Inputs.jsx
@@ -34,6 +34,40 @@ TextInput.propTypes = {
placeholder: PropTypes.string,
required: PropTypes.bool,
};
+
+function TextArea({
+ label,
+ type,
+ name,
+ value,
+ onChange,
+ placeholder,
+ required,
+}) {
+ return (
+
+ );
+}
+TextInput.propTypes = {
+ label: PropTypes.string.isRequired,
+ type: PropTypes.string,
+ name: PropTypes.string,
+ value: PropTypes.string,
+ onChange: PropTypes.func,
+ placeholder: PropTypes.string,
+ required: PropTypes.bool,
+};
+
function RadioInput({ label, name, required, valuesArr, onChange }) {
return (
diff --git a/client/src/components/notifications/notificationPage.jsx b/client/src/components/notifications/notificationPage.jsx
new file mode 100644
index 00000000..86a5ca1d
--- /dev/null
+++ b/client/src/components/notifications/notificationPage.jsx
@@ -0,0 +1,39 @@
+import PageTitle from '../common/PageTitle'
+import Alert from '../common/Alerts'
+import './notificationPage.scss'
+import { useGetAllAlertsQuery } from '../../redux/slices/alertApiSlice'
+
+export default function SendNotificationPage() {
+ const { data: alerts, isFetching: isFetchingAlerts } = useGetAllAlertsQuery(
+ { status: 'all', contentType: 'all' }
+ )
+
+ return (
+
+
+
+ {isFetchingAlerts
+ ? 'جاري التحميل'
+ : alerts?.body?.length === 0
+ ? 'لا يوجد إشعارات'
+ : alerts?.body?.map((alert) => {
+ return (
+
+ )
+ })}
+
+
+ )
+}
diff --git a/client/src/components/notifications/notificationPage.scss b/client/src/components/notifications/notificationPage.scss
new file mode 100644
index 00000000..d7407a97
--- /dev/null
+++ b/client/src/components/notifications/notificationPage.scss
@@ -0,0 +1,5 @@
+.notifications {
+ .notificationBox {
+ margin-bottom: 8rem;
+}
+}
diff --git a/client/src/components/send-notification/sendNotificationPage.jsx b/client/src/components/send-notification/sendNotificationPage.jsx
new file mode 100644
index 00000000..5b41b29d
--- /dev/null
+++ b/client/src/components/send-notification/sendNotificationPage.jsx
@@ -0,0 +1,139 @@
+import { useState } from 'react'
+import PageTitle from '../common/PageTitle'
+import { TextArea, RadioInput } from '../common/Inputs'
+import CustomSelect from '../common/CustomSelect'
+import Button from '../common/Button'
+import { useGetSectorsQuery } from '../../redux/slices/sectorApiSlice'
+import {
+ useSendAlertMutation,
+ useCreateAlertMutation,
+} from '../../redux/slices/alertApiSlice'
+import './sendNotificationPage.scss'
+import { toast } from 'react-toastify'
+
+export default function SendNotificationPage() {
+ const [message, setMessage] = useState('')
+ const [toWhom, setToWhom] = useState('')
+ const [receiver, setReceiver] = useState('')
+
+ const { data: sectorsData, isFetching: isFetchingSectors } =
+ useGetSectorsQuery()
+
+ const [createNotification, { isLoading: isLoadingCreateNotification }] =
+ useCreateAlertMutation()
+
+ const [sendNotification, { isLoading: isLoadingSendNotification }] =
+ useSendAlertMutation()
+
+ let sectors = []
+
+ if (sectorsData && !isFetchingSectors) {
+ sectors = sectorsData.body
+ if (sectors.length === 0) {
+ sectors = [{ baseName: 'لا يوجد قطاع', suffixName: '' }]
+ }
+ }
+
+ const handleSubmit = async (e) => {
+ e.preventDefault()
+ try {
+ let body = {
+ message: message,
+ contentType: 'other',
+ }
+ let res = await createNotification(body).unwrap()
+ if (res.status === 400 || res.status === 500)
+ throw new Error(
+ 'Something went wrong while creating notification'
+ )
+
+ const notificationId = res?.body?.notificationId
+
+ if (toWhom === 'إرسال إلى الكل') {
+ body = {}
+ } else {
+ body = {
+ sectorBaseName: receiver.split(' ')[0],
+ sectorSuffixName: receiver.split(' ')[1] || '',
+ }
+ }
+ res = await sendNotification(notificationId, body).unwrap()
+ if (res.status === 400 || res.status === 500)
+ throw new Error(
+ 'Something went wrong while sending notification'
+ )
+ toast.success('تم إرسال الإشعار بنجاح')
+ } catch (err) {
+ console.log(err)
+ toast.error('حدث خطأ أثناءإرسال الإشعار')
+ toast.error(JSON.stringify(err))
+ }
+ }
+
+ return (
+
+ )
+}
diff --git a/client/src/components/send-notification/sendNotificationPage.scss b/client/src/components/send-notification/sendNotificationPage.scss
new file mode 100644
index 00000000..83e2c063
--- /dev/null
+++ b/client/src/components/send-notification/sendNotificationPage.scss
@@ -0,0 +1,11 @@
+.send-notification {
+ .form-element {
+ margin-bottom: 1rem;
+ }
+ .send-button {
+ margin-block: 2rem;
+ width: 50%;
+ display: block;
+ margin-inline: auto;
+ }
+}
diff --git a/client/src/redux/slices/alertApiSlice.js b/client/src/redux/slices/alertApiSlice.js
new file mode 100644
index 00000000..60011274
--- /dev/null
+++ b/client/src/redux/slices/alertApiSlice.js
@@ -0,0 +1,62 @@
+import { apiSlice } from './apiSlice'
+
+const ALERT_URL = '/api/alert'
+
+export const alertApi = apiSlice.injectEndpoints({
+ endpoints: (builder) => ({
+ GetAllAlerts: builder.query({
+ query: (item) => ({
+ url: `${ALERT_URL}/all`,
+ method: 'GET',
+ params: item,
+ }),
+ providesTags: ['alert'],
+ }),
+ GetAlert: builder.query({
+ query: (id) => ({
+ url: `${ALERT_URL}/${id}`,
+ method: 'GET',
+ }),
+ providesTags: ['alert'],
+ }),
+ SendAlert: builder.mutation({
+ query: (id, item) => ({
+ url: `${ALERT_URL}/${id}`,
+ method: 'POST',
+ body: item,
+ }),
+ invalidatesTags: ['alert'],
+ }),
+ CreateAlert: builder.mutation({
+ query: (alert) => ({
+ url: `${ALERT_URL}`,
+ method: 'POST',
+ body: alert,
+ }),
+ invalidatesTags: ['alert'],
+ }),
+ UpdateStatus: builder.mutation({
+ query: (id) => ({
+ url: `${ALERT_URL}/${id}`,
+ method: 'PATCH',
+ }),
+ invalidatesTags: ['alert'],
+ }),
+ DeleteAlert: builder.mutation({
+ query: (id) => ({
+ url: `${ALERT_URL}/${id}`,
+ method: 'DELETE',
+ }),
+ invalidatesTags: ['alert'],
+ }),
+ }),
+})
+
+export const {
+ useGetAllAlertsQuery,
+ useGetAlertQuery,
+ useSendAlertMutation,
+ useCreateAlertMutation,
+ useUpdateStatusMutation,
+ useDeleteAlertMutation,
+} = alertApi
diff --git a/client/src/redux/slices/apiSlice.js b/client/src/redux/slices/apiSlice.js
index c539299b..0c7a625f 100644
--- a/client/src/redux/slices/apiSlice.js
+++ b/client/src/redux/slices/apiSlice.js
@@ -24,6 +24,7 @@ export const apiSlice = createApi({
"users",
"auth",
"sector",
+ "alert",
],
endpoints: () => ({}),
});
diff --git a/client/src/routes.jsx b/client/src/routes.jsx
index a70ca435..5fe0fc90 100644
--- a/client/src/routes.jsx
+++ b/client/src/routes.jsx
@@ -25,6 +25,8 @@ import InsertSector from "./components/insert-sector/InsertSector";
import AssignCaptainPage from "./components/assign-captain-page/AssignCaptainPage";
import InsertScoutPage from "./components/insert-scout/InsertScoutPage";
import UpdateScoutPage from "./components/update-scout/UpdateScoutPage";
+import SendNotificationPage from "./components/send-notification/sendNotificationPage";
+import NotificationsPage from "./components/notifications/notificationPage";
import ScoutsAttendance from "./components/scouts-attendance/ScoutsAttendance";
import MoneyPage from "./components/moneypage/MoneyPage";
import CaptainsAttendance from "./components/captains-attendance/CaptainAttendance";
@@ -45,6 +47,8 @@ function Routes() {
} />
} />
} />
+ } />
+ } />