diff --git a/package-lock.json b/package-lock.json
index 18a11ba..21ce128 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,9 +25,11 @@
"dayjs": "^1.11.9",
"history": "^5.3.0",
"jest-docblock": "^29.4.3",
+ "notistack": "^3.0.1",
"react-hook-form": "^7.43.9",
"react-redux": "^8.0.7",
- "react-router-dom": "^6.14.0"
+ "react-router-dom": "^6.14.0",
+ "uuid": "^9.0.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
@@ -55,6 +57,7 @@
"react-dom": "^18.2.0",
"redux-mock-store": "^1.5.4",
"tailwindcss": "^3.3.2",
+ "tailwindcss-animated": "^1.0.1",
"vite": "^4.3.2"
}
},
@@ -477,6 +480,15 @@
"node": ">= 0.12"
}
},
+ "node_modules/@cypress/request/node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/@cypress/xvfb": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
@@ -4252,6 +4264,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/goober": {
+ "version": "2.1.13",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz",
+ "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@@ -6465,6 +6485,15 @@
"node": ">=8"
}
},
+ "node_modules/mochawesome/node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -6530,6 +6559,35 @@
"node": ">=0.10.0"
}
},
+ "node_modules/notistack": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz",
+ "integrity": "sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==",
+ "dependencies": {
+ "clsx": "^1.1.0",
+ "goober": "^2.0.33"
+ },
+ "engines": {
+ "node": ">=12.0.0",
+ "npm": ">=6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/notistack"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/notistack/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
@@ -8032,6 +8090,15 @@
"node": ">=14.0.0"
}
},
+ "node_modules/tailwindcss-animated": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss-animated/-/tailwindcss-animated-1.0.1.tgz",
+ "integrity": "sha512-u5wusj89ZwP8I+s8WZlaAd7aZTWBN/XEG6QgMKpkIKmAf3xP1A6WYf7oYIKmGaB10UAQaSqWopi/i1ozzZEs8Q==",
+ "dev": true,
+ "peerDependencies": {
+ "tailwindcss": ">=3.1.0"
+ }
+ },
"node_modules/tcomb": {
"version": "3.2.29",
"resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.29.tgz",
@@ -8367,10 +8434,9 @@
"dev": true
},
"node_modules/uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "dev": true,
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+ "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "dist/bin/uuid"
}
diff --git a/package.json b/package.json
index 9cda6b0..902cfe9 100644
--- a/package.json
+++ b/package.json
@@ -30,9 +30,11 @@
"dayjs": "^1.11.9",
"history": "^5.3.0",
"jest-docblock": "^29.4.3",
+ "notistack": "^3.0.1",
"react-hook-form": "^7.43.9",
"react-redux": "^8.0.7",
- "react-router-dom": "^6.14.0"
+ "react-router-dom": "^6.14.0",
+ "uuid": "^9.0.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
@@ -60,6 +62,7 @@
"react-dom": "^18.2.0",
"redux-mock-store": "^1.5.4",
"tailwindcss": "^3.3.2",
+ "tailwindcss-animated": "^1.0.1",
"vite": "^4.3.2"
}
}
diff --git a/src/App.jsx b/src/App.jsx
index 91f2f29..27dc2e3 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -11,6 +11,8 @@ import AppRouter from "./Components/AppRouter";
import { Provider } from "react-redux";
import DialogStack from "./dialog/DialogStack";
import Auth from "./Components/Auth";
+import SnackbarStack from "./Components/snackbars/SnackbarStack";
+import { SnackbarProvider } from "notistack";
const theme = createTheme({
components: {
@@ -60,9 +62,14 @@ function App() {
-
-
-
+
+
+
+
+
+
diff --git a/src/Components/snackbars/SnackbarStack.jsx b/src/Components/snackbars/SnackbarStack.jsx
new file mode 100644
index 0000000..2318167
--- /dev/null
+++ b/src/Components/snackbars/SnackbarStack.jsx
@@ -0,0 +1,32 @@
+import { Icon, IconButton } from "@mui/material";
+import { useSnackbar } from "notistack";
+import { useEffect } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { shiftSnackbar } from "../../slices/snackbarSlice";
+
+const SnackbarStack = () => {
+ const snackbarStack = useSelector(({ snackbar }) => snackbar.stack);
+ const dispatch = useDispatch();
+ const { enqueueSnackbar, closeSnackbar } = useSnackbar();
+
+ useEffect(() => {
+ const lastSnackbar = snackbarStack[snackbarStack.length - 1];
+ if (lastSnackbar) {
+ enqueueSnackbar(lastSnackbar?.message, {
+ variant: lastSnackbar?.variant,
+ action: (snackbarId) => (
+ closeSnackbar(snackbarId)}>
+ close
+
+ ),
+ });
+ setTimeout(() => {
+ dispatch(shiftSnackbar());
+ }, 10000);
+ }
+ }, [snackbarStack.length]);
+
+ return null;
+};
+
+export default SnackbarStack;
diff --git a/src/Pages/AppointmentInfo.jsx b/src/Pages/AppointmentInfo.jsx
index 2c40cfa..53b3fc6 100644
--- a/src/Pages/AppointmentInfo.jsx
+++ b/src/Pages/AppointmentInfo.jsx
@@ -21,6 +21,7 @@ import { createAppointment, updateAppointment, deleteAppointment } from "../serv
import DtxTextField from "../Components/Form/DtxTextField";
import DtxSelect from "../Components/Form/DtxSelect";
import DtxCheckbox from "../Components/Form/DtxCheckbox";
+import { showSnackbar } from '../slices/snackbarSlice';
const AppointmentInfo = ({ ...props }) => {
@@ -126,8 +127,24 @@ const AppointmentInfo = ({ ...props }) => {
await createAppointment(doctorId, appointmentDataToSend)
}
dispatch(incrementDataRevision({ event: "appointmentRevision" }))
+ dispatch(
+ showSnackbar({
+ message: isEditMode
+ ? "Cambios guardados exitosamente"
+ : "La cita ha sido registrada con éxito en el sistema",
+ variant: "success",
+ })
+ );
dispatch(popDialog())
} catch (err) {
+ dispatch(
+ showSnackbar({
+ message: `Ha ocurrido un error al momento de ${
+ isEditMode ? "actualizar" : "crear"
+ } la cita`,
+ variant: "error",
+ })
+ );
console.error(err)
} finally {
setLoading(false)
diff --git a/src/Pages/PatientInfo.jsx b/src/Pages/PatientInfo.jsx
index e935f83..d0ef5ef 100644
--- a/src/Pages/PatientInfo.jsx
+++ b/src/Pages/PatientInfo.jsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React from "react";
import { useState, memo } from "react";
import { useForm } from "react-hook-form";
@@ -32,6 +32,7 @@ import { createPatient, updatePatient } from "../services/patientServices";
import { useDispatch, useSelector } from "react-redux";
import { popDialog } from "../slices/dialogSlice";
import { incrementDataRevision } from "../slices/revisionSlice";
+import { showSnackbar } from "../slices/snackbarSlice";
function TabPanel(props) {
const { children, value, index, ...other } = props;
@@ -99,8 +100,24 @@ const PatientInfo = ({ onProgress, ...props }) => {
}
dispatch(incrementDataRevision({ event: "patientRevision" }));
+ dispatch(
+ showSnackbar({
+ message: isEditMode
+ ? "Cambios guardados exitosamente"
+ : "El paciente ha sido registrado con éxito en el sistema",
+ variant: "success",
+ })
+ );
dispatch(popDialog());
} catch (err) {
+ dispatch(
+ showSnackbar({
+ message: `Ha ocurrido un error al momento de ${
+ isEditMode ? "actualizar" : "crear"
+ } el paciente`,
+ variant: "error",
+ })
+ );
console.error(err);
} finally {
setLoading(false);
diff --git a/src/reducer/store.js b/src/reducer/store.js
index e11922e..ea58f12 100644
--- a/src/reducer/store.js
+++ b/src/reducer/store.js
@@ -4,13 +4,16 @@ import { dialogReducer } from "../slices/dialogSlice";
import { loginReducer } from "../slices/loginSlice";
import { userReducer } from "../slices/userSlice";
import { revisionReducer } from "../slices/revisionSlice";
+import { snackbarReducer } from "../slices/snackbarSlice";
+
const store = configureStore({
reducer: {
dialog: dialogReducer,
login: loginReducer,
user: userReducer,
- revision: revisionReducer
+ revision: revisionReducer,
+ snackbar: snackbarReducer
},
});
diff --git a/src/slices/snackbarSlice.js b/src/slices/snackbarSlice.js
new file mode 100644
index 0000000..b356e30
--- /dev/null
+++ b/src/slices/snackbarSlice.js
@@ -0,0 +1,22 @@
+import { createSlice } from "@reduxjs/toolkit";
+import { v4 as uuid } from "uuid";
+
+const snackbarSlice = createSlice({
+ name: "snackbar",
+ initialState: {
+ stack: [],
+ },
+ reducers: {
+ showSnackbar: (state, action) => {
+ const snackbarId = uuid();
+ state.stack.push({ id: snackbarId, ...action.payload });
+ },
+ shiftSnackbar: (state, action) => {
+ state.stack.shift();
+ },
+ },
+});
+
+export const snackbarReducer = snackbarSlice.reducer;
+export const { showSnackbar, shiftSnackbar } = snackbarSlice.actions;
+export default snackbarSlice;
diff --git a/tailwind.config.js b/tailwind.config.js
index d2cbb00..c28d623 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -13,5 +13,5 @@ export default {
},
},
},
- plugins: [],
+ plugins: [import('tailwindcss-animated')],
};