From f384c2440fe116e7761c27518194933c27f96331 Mon Sep 17 00:00:00 2001 From: Wayex <30770802+alexrobaina@users.noreply.github.com> Date: Fri, 17 May 2024 15:12:31 -0300 Subject: [PATCH] add appointments (#252) * add appointments * delete consoles --- .eslintrc.cjs | 6 +- package.json | 6 + public/locales/en/appointments.json | 7 + public/locales/en/common.json | 240 +- public/locales/es/appointments.json | 7 + public/locales/es/common.json | 240 +- public/locales/fr/appointments.json | 7 + public/locales/fr/common.json | 237 +- src/App.tsx | 9 +- src/api/appointments.ts | 38 + src/api/utils/resizeImages.ts | 2 +- src/assets/icons/iconCalendarStats.tsx | 22 + src/assets/icons/iconChevronLeft.tsx | 22 +- src/assets/icons/index.ts | 2 + .../components/AppointmentDetails/indesx.tsx | 61 + src/components/AppointmentsCalendar/index.tsx | 171 + .../AppointmentsCalendar/styles.css | 45 + src/components/AppointmentsCalendar/types.ts | 21 + src/components/Navbar/index.tsx | 76 +- .../components/ButtonNavigate/index.tsx | 2 +- .../Navigation/components/SideBar/index.tsx | 146 +- .../UserRoleSelectorModal/index.tsx | 2 +- src/components/common/BaseButton/index.tsx | 2 +- src/components/common/BaseButton/utils.ts | 2 +- src/components/common/BaseInput/index.tsx | 4 +- .../BaseInputCalendar.css | 13 + .../BaseInputRangeCalendar.module.css | 13 + .../BaseInputRangeCalendar/calendar.css | 91 + .../common/BaseInputRangeCalendar/index.tsx | 110 + src/components/common/BaseSelect/index.tsx | 10 +- src/components/common/ContactCard/index.tsx | 6 +- src/components/common/DeleteModal/index.tsx | 14 +- .../common/GoogleAutocomplete/index.tsx | 2 +- src/components/common/ReactModal/index.tsx | 5 +- src/components/common/SliderModal/index.tsx | 10 +- src/components/index.ts | 17 + src/hooks/appointments/useAppointmentList.tsx | 11 + .../appointments/useAppointmentUpdate.tsx | 22 + .../appointments/useCreateAppointment.tsx | 21 + src/hooks/appointments/useGetAppointment.tsx | 12 + .../useCreateMedicalRecord.tsx | 2 +- .../useDeleteMedicalRecord.tsx | 2 +- .../useGetMedicalRecord.tsx | 2 +- .../useMedicalRecordUpdate.tsx | 2 +- src/hooks/{ => pets}/useCreatePet.tsx | 2 +- src/hooks/{ => pets}/useDeletePet.tsx | 2 +- src/hooks/{ => pets}/useGetPet.tsx | 2 +- src/hooks/{ => pets}/useGetPets.tsx | 2 +- src/hooks/{ => pets}/usePet.tsx | 2 +- src/hooks/{ => pets}/usePetUpdate.tsx | 2 +- src/hooks/{ => ui}/useScreenWidth.tsx | 2 +- src/hooks/{ => user}/useUser.tsx | 2 +- src/hooks/{ => user}/useUserList.tsx | 2 +- src/hooks/{ => user}/useUserPets.tsx | 12 +- src/hooks/{ => user}/useUserUpdate.tsx | 2 +- src/hooks/{ => vaccine}/useDelecteVaccine.tsx | 2 +- .../{ => vaccine}/useUpdatePetVaccine.tsx | 2 +- src/hooks/{ => vaccine}/useVaccine.tsx | 2 +- src/i18n.ts | 33 +- src/index.css | 2 + src/pages/AdoptionPetPage/index.tsx | 2 +- .../components/AppointmentForm/index.tsx | 100 + .../components/AppointmentForm/types.tsx | 42 + .../components/ContactInfo/index.tsx | 85 + src/pages/AppointmentsPage/constants.ts | 166 + .../hooks/useAppointmentForm.tsx | 70 + src/pages/AppointmentsPage/index.tsx | 115 + src/pages/CommunityPage/index.tsx | 2 +- .../components/CreatePetForm/index.tsx | 8 +- .../DashboardTable/DashboardTable.tsx | 55 +- src/pages/DashboardPage/index.tsx | 61 +- .../components/CreateMedicalRecord/index.tsx | 8 +- .../components/EditVaccineModal/index.tsx | 10 +- .../components/GeneralPetInfo/index.tsx | 65 +- .../components/HealthData/index.tsx | 4 +- .../components/VaccinesTable/index.tsx | 2 +- src/pages/ProfilePetPage/index.tsx | 4 +- .../PersonalInformationForm/index.tsx | 10 +- .../components/SocialMediaForm/index.tsx | 4 +- src/pages/SettingsPage/index.tsx | 4 +- .../components/UserProfile/index.tsx | 17 +- src/pages/UserProfilePage/index.tsx | 2 +- .../components/BaseButton/BaseButton.spec.tsx | 6 +- src/tests/test-utils.ts | 4 +- src/utils/images.tsx | 34 +- tailwind.config.ts | 3 + tsconfig.node.tsbuildinfo | 3413 ++++++++++++++++- yarn.lock | 1879 +++++---- 88 files changed, 6573 insertions(+), 1417 deletions(-) create mode 100644 public/locales/en/appointments.json create mode 100644 public/locales/es/appointments.json create mode 100644 public/locales/fr/appointments.json create mode 100644 src/api/appointments.ts create mode 100644 src/assets/icons/iconCalendarStats.tsx create mode 100644 src/components/AppointmentsCalendar/components/AppointmentDetails/indesx.tsx create mode 100644 src/components/AppointmentsCalendar/index.tsx create mode 100644 src/components/AppointmentsCalendar/styles.css create mode 100644 src/components/AppointmentsCalendar/types.ts create mode 100644 src/components/common/BaseInputRangeCalendar/BaseInputCalendar.css create mode 100644 src/components/common/BaseInputRangeCalendar/BaseInputRangeCalendar.module.css create mode 100644 src/components/common/BaseInputRangeCalendar/calendar.css create mode 100644 src/components/common/BaseInputRangeCalendar/index.tsx create mode 100644 src/components/index.ts create mode 100644 src/hooks/appointments/useAppointmentList.tsx create mode 100644 src/hooks/appointments/useAppointmentUpdate.tsx create mode 100644 src/hooks/appointments/useCreateAppointment.tsx create mode 100644 src/hooks/appointments/useGetAppointment.tsx rename src/hooks/{ => medicalRecords}/useCreateMedicalRecord.tsx (89%) rename src/hooks/{ => medicalRecords}/useDeleteMedicalRecord.tsx (90%) rename src/hooks/{ => medicalRecords}/useGetMedicalRecord.tsx (85%) rename src/hooks/{ => medicalRecords}/useMedicalRecordUpdate.tsx (90%) rename src/hooks/{ => pets}/useCreatePet.tsx (94%) rename src/hooks/{ => pets}/useDeletePet.tsx (95%) rename src/hooks/{ => pets}/useGetPet.tsx (85%) rename src/hooks/{ => pets}/useGetPets.tsx (92%) rename src/hooks/{ => pets}/usePet.tsx (84%) rename src/hooks/{ => pets}/usePetUpdate.tsx (93%) rename src/hooks/{ => ui}/useScreenWidth.tsx (94%) rename src/hooks/{ => user}/useUser.tsx (85%) rename src/hooks/{ => user}/useUserList.tsx (91%) rename src/hooks/{ => user}/useUserPets.tsx (77%) rename src/hooks/{ => user}/useUserUpdate.tsx (92%) rename src/hooks/{ => vaccine}/useDelecteVaccine.tsx (91%) rename src/hooks/{ => vaccine}/useUpdatePetVaccine.tsx (91%) rename src/hooks/{ => vaccine}/useVaccine.tsx (85%) create mode 100644 src/pages/AppointmentsPage/components/AppointmentForm/index.tsx create mode 100644 src/pages/AppointmentsPage/components/AppointmentForm/types.tsx create mode 100644 src/pages/AppointmentsPage/components/ContactInfo/index.tsx create mode 100644 src/pages/AppointmentsPage/constants.ts create mode 100644 src/pages/AppointmentsPage/hooks/useAppointmentForm.tsx create mode 100644 src/pages/AppointmentsPage/index.tsx diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 800036ed..5bff90a7 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,6 +1,10 @@ module.exports = { root: true, - env: { browser: true, es2020: true }, + env: { + browser: true, + es6: true, + node: true, + }, extends: [ 'prettier', 'eslint:recommended', diff --git a/package.json b/package.json index f258db38..a7d82757 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,12 @@ "@react-google-maps/api": "^2.19.2", "@types/lodash": "^4.17.0", "@types/qrcode": "^1.5.4", + "@types/react-big-calendar": "^1.8.9", + "@types/react-date-range": "^1.4.9", "@vitejs/plugin-react-swc": "^3.3.2", "autoprefixer": "^10.4.15", "axios": "^1.5.0", + "date-fns": "^3.6.0", "dotenv": "^16.3.1", "formik": "^2.4.5", "framer-motion": "^10.16.4", @@ -33,10 +36,13 @@ "lodash": "^4.17.21", "mobx": "^6.10.2", "mobx-react": "^9.0.1", + "moment": "^2.30.1", "postcss": "^8.4.29", "qrcode": "^1.5.3", "qrcode.react": "^3.1.0", "react": "^18.2.0", + "react-big-calendar": "^1.12.1", + "react-date-range": "^2.0.1", "react-dom": "^18.2.0", "react-i18next": "^14.1.0", "react-image-file-resizer": "^0.4.8", diff --git a/public/locales/en/appointments.json b/public/locales/en/appointments.json new file mode 100644 index 00000000..49c46d17 --- /dev/null +++ b/public/locales/en/appointments.json @@ -0,0 +1,7 @@ +{ + "createAppointmentForPets": "Create Appointment for Pets", + "selectAdopterOrShelter": "Select Adopter or Shelter", + "addAppointment": "Add Appointment", + "organizeYourAppointments": "Organize your appointments", + "calendarDescription": "Here you can see all the appointments you have created for your pets. You can view them by day, week or month." +} diff --git a/public/locales/en/common.json b/public/locales/en/common.json index ae4d6db8..6759721c 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -1,112 +1,136 @@ { - "community": "Community", - "searchPets": "Search Pets", - "dashboard": "Dashboard", - "settings": "Settings", - "logout": "logout", - "resetFilters": "Clear filters", - "searchByName": "Search by name", - "dogs": "Dogs", - "cats": "Cats", - "exotics": "Exotics", - "males": "Males", - "females": "Females", + "community": "Community", + "searchPets": "Search Pets", + "dashboard": "Dashboard", + "settings": "Settings", + "logout": "logout", + "resetFilters": "Clear filters", + "searchByName": "Search by name", + "dogs": "Dogs", + "cats": "Cats", + "exotics": "Exotics", + "males": "Males", + "females": "Females", + "dog": "Dog", + "cat": "Cat", + "exotic": "Exotic", + "male": "Male", + "editPet": "Edit pet", + "createPet": "Create pet", + "female": "Female", + "adopted": "Adopted", + "inAdoption": "In adoption", + "category": "Categoría", + "gender": "Gender", + "age": "Age", + "size": "Size", + "status": "Status", + "pets": "Pets", + "areYouSureDelete": "Are you sure you want to delete", + "available": "Available", + "showing": "Showing", + "of": "of", + "results": "results", + "previous": "Previous", + "name": "Name", + "next": "Next", + "selectImages": "Select images", + "selectImagesInfo": "JPG, GIF or PNG. 1.5MB max.", + "petName": "Pet name", + "breed": "Breed", + "weight": "Weight", + "massUnit": "Mass units", + "petDescription": "Description of pet", + "description": "Description", + "adopterBy": "Adoptado por", + "petGuardians": "Pet Guardians", + "adoptedBy": "Adopted by", + "vet": "Vet", + "shelter": "Shelter", + "select": "Select", + "selectVeterinary": "Select veterinary", + "selectShelter": "Select shelter", + "selectAdopter": "Select adopter", + "petBreed": "Pet breed", + "rabbits": "Rabbits", + "title": "Title", + "notes": "Notes", + "appointment": "Appointment", + "appointments": "Appointments", + "selectDate": "Select Date", + "isRequired": "This field is required", + "start": "Start", + "end": "End", + "selectTime": "Select Time", + "edit": "Edit", + "editAppointment": "Edit appointment", + "createAppointment": "Create appointment", + "pet": "Pet", + "selectPet": "Select pet", + "allDay": "All day", + "today": "Today", + "month": "Month", + "week": "Week", + "day": "Day", + "agenda": "Agenda", + "date": "Date", + "time": "Time", + "event": "Event", + "showMore": "+ Show more ({{total}})", + "myPets": "My pets", + "sizePet": { + "small": "Small", + "medium": "Medium", + "large": "Large", + "extraLarge": "Extra large" + }, + "categoryPet": { "dog": "Dog", "cat": "Cat", - "exotic": "Exotic", + "rabbit": "Rabbit", + "hamster": "Hamster", + "bird": "Bird", + "exotic": "Exotic" + }, + "agePet": { + "baby": "Baby", + "young": "Young", + "adult": "Adult", + "senior": "Senior" + }, + "genderPet": { "male": "Male", - "editPet": "Edit pet", - "createPet": "Create pet", - "female": "Female", - "adopted": "Adopted", - "inAdoption": "In adoption", - "category": "Categoría", - "gender": "Gender", - "age": "Age", - "size": "Size", - "status": "Status", - "pets": "Pets", - "areYouSureDelete": "Are you sure you want to delete", - "available": "Available", - "showing": "Showing", - "of": "of", - "results": "results", - "previous": "Previous", - "name": "Name", - "next": "Next", - "selectImages": "Select images", - "selectImagesInfo": "JPG, GIF or PNG. 1.5MB max.", - "petName": "Pet name", - "breed": "Breed", - "weight": "Weight", - "massUnit": "Mass units", - "petDescription": "Description of pet", - "description": "Description", - "adopterBy": "Adoptado por", - "petGuardians": "Pet Guardians", - "adoptedBy": "Adopted by", - "vet": "Vet", - "shelter": "Shelter", - "select": "Select", - "selectVeterinary": "Select veterinary", - "selectShelter": "Select shelter", - "selectAdopter": "Select adopter", - "petBreed": "Pet breed", - "rabbits": "Rabbits", - "myPets": "My pets", - "sizePet": { - "small": "Small", - "medium": "Medium", - "large": "Large", - "extraLarge": "Extra large" - }, - "categoryPet": { - "dog": "Dog", - "cat": "Cat", - "rabbit": "Rabbit", - "hamster": "Hamster", - "bird": "Bird", - "exotic": "Exotic" - }, - "agePet": { - "baby": "Baby", - "young": "Young", - "adult": "Adult", - "senior": "Senior" - }, - "genderPet": { - "male": "Male", - "female": "Female" - }, - "cancel": "Cancel", - "save": "Save", - "veterinarians": "Veterinarians", - "veterinarian": "Veterinary", - "shelters": "Shelters", - "contact": "Contact", - "email": "Email", - "role": "Role", - "phone": "Phone", - "address": "Address", - "location": "Location", - "delete": "Delete", - "SHELTER": "Shelter", - "VET": "Veterinary", - "ADOPTER": "Adopter", - "adopter": "Adopter", - "searchByLocation": "Search by location", - "actions": "Actions", - "adoptPet": "Adopt pet", - "login": "Login", - "locationUnavailable": "Location unavailable", - "petAvailable": "Pet available", - "petDeleted": "Pet deleted successfully", - "petNotDeleted": "Pet not deleted", - "contactsForAdoption": "Contacts for adoption", - "emailInvalid": "Email invalid", - "requiredField": "Required field", - "petNotAvailable": "Pet not available", - "wantAdoption": "I want to adopt to", - "aboutUs": "About Us", - "last": "Last" -} \ No newline at end of file + "female": "Female" + }, + "cancel": "Cancel", + "save": "Save", + "veterinarians": "Veterinarians", + "veterinarian": "Veterinary", + "shelters": "Shelters", + "contact": "Contact", + "email": "Email", + "role": "Role", + "phone": "Phone", + "address": "Address", + "location": "Location", + "delete": "Delete", + "SHELTER": "Shelter", + "VET": "Veterinary", + "ADOPTER": "Adopter", + "adopter": "Adopter", + "searchByLocation": "Search by location", + "actions": "Actions", + "adoptPet": "Adopt pet", + "login": "Login", + "locationUnavailable": "Location unavailable", + "petAvailable": "Pet available", + "petDeleted": "Pet deleted successfully", + "petNotDeleted": "Pet not deleted", + "contactsForAdoption": "Contacts for adoption", + "emailInvalid": "Email invalid", + "requiredField": "Required field", + "petNotAvailable": "Pet not available", + "wantAdoption": "I want to adopt to", + "aboutUs": "About Us", + "last": "Last" +} diff --git a/public/locales/es/appointments.json b/public/locales/es/appointments.json new file mode 100644 index 00000000..0696a369 --- /dev/null +++ b/public/locales/es/appointments.json @@ -0,0 +1,7 @@ +{ + "createAppointmentForPets": "Crear Cita para Mascotas", + "selectAdopterOrShelter": "Seleccionar Adoptante o Refugio", + "addAppointment": "Crear Cita", + "organizeYourAppointments": "Organiza tus Citas", + "calendarDescription": "Aquí puedes ver todas las citas que has creado para tus mascotas. Puedes verlas por día, semana o mes." +} diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 034504b2..aa58e1b8 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -1,113 +1,137 @@ { - "community": "Comunidad", - "searchPets": "Buscar mascotas", - "dashboard": "Panel de control", - "settings": "Ajustes", - "logout": "Cerrar sesión", - "resetFilters": "Limpiar filtros", - "searchByName": "Buscar por nombre", - "dogs": "Perros", - "cats": "Gatos", - "exotics": "Exoticos", - "males": "Machos", - "females": "Hembras", + "community": "Comunidad", + "searchPets": "Buscar mascotas", + "dashboard": "Panel de control", + "settings": "Ajustes", + "logout": "Cerrar sesión", + "resetFilters": "Limpiar filtros", + "searchByName": "Buscar por nombre", + "dogs": "Perros", + "cats": "Gatos", + "exotics": "Exoticos", + "males": "Machos", + "females": "Hembras", + "dog": "Perro", + "cat": "Gato", + "exotic": "Exotico", + "male": "Macho", + "editPet": "Editar mascota", + "createPet": "Crear mascota", + "female": "Hembra", + "adopted": "Adoptados", + "inAdoption": "En adoption", + "pets": "Mascotas", + "title": "Título", + "notes": "Notas", + "isRequired": "Este campo es requerido", + "appointment": "Cita", + "appointments": "Citas", + "selectDate": "Seleccionar Fecha", + "areYouSureDelete": "Estas por eliminar a", + "available": "Disponible", + "showing": "Mostrados del", + "to": "a", + "of": "de", + "results": "resultados", + "previous": "Anterior", + "next": "Siguiente", + "selectVeterinary": "Selecciona un veterinario", + "selectImages": "Selecciona imagenes", + "selectImagesInfo": "JPG, GIF o PNG. 1.5MB max.", + "petName": "Nombre de la mascota", + "select": "Seleccionar", + "breed": "Raza", + "weight": "Peso", + "name": "Nombre", + "gender": "Genero", + "age": "Edad", + "size": "Tamaño", + "adoptedBy": "Adoptado por", + "description": "Descripción", + "status": "Estatus", + "massUnit": "Unidades de peso", + "petDescription": "Descripción de la mascota", + "adopterBy": "Adopted By", + "petGuardians": "Guardianes de mascotas", + "vet": "Veterinario", + "category": "Categoría", + "shelter": "Refugio", + "selectShelter": "Selecciona un refugio", + "selectAdopter": "Selecciona un adoptante", + "petBreed": "Raza de la mascota", + "rabbits": "Conejos", + "sizePet": { + "small": "Pequeño", + "medium": "Mediano", + "large": "Grande", + "extraLarge": "Super Grande" + }, + "categoryPet": { "dog": "Perro", "cat": "Gato", "exotic": "Exotico", + "rabbit": "Conejo", + "hamster": "Hamster", + "bird": "Ave" + }, + "agePet": { + "baby": "Bebe", + "young": "Joven", + "adult": "Adulto", + "senior": "Viejito" + }, + "genderPet": { "male": "Macho", - "editPet": "Editar mascota", - "createPet": "Crear mascota", - "female": "Hembra", - "adopted": "Adoptados", - "inAdoption": "En adoption", - "pets": "Mascotas", - "areYouSureDelete": "Estas por eliminar a", - "available": "Disponible", - "showing": "Mostrados del", - "to": "a", - "of": "de", - "results": "resultados", - "previous": "Anterior", - "next": "Siguiente", - "selectVeterinary": "Selecciona un veterinario", - "selectImages": "Selecciona imagenes", - "selectImagesInfo": "JPG, GIF o PNG. 1.5MB max.", - "petName": "Nombre de la mascota", - "select": "Seleccionar", - "breed": "Raza", - "weight": "Peso", - "name": "Nombre", - "gender": "Genero", - "age": "Edad", - "size": "Tamaño", - "adoptedBy": "Adoptado por", - "description": "Descripción", - "status": "Estatus", - "massUnit": "Unidades de peso", - "petDescription": "Descripción de la mascota", - "adopterBy": "Adopted By", - "petGuardians": "Guardianes de mascotas", - "vet": "Veterinario", - "category": "Categoría", - "shelter": "Refugio", - "selectShelter": "Selecciona un refugio", - "selectAdopter": "Selecciona un adoptante", - "petBreed": "Raza de la mascota", - "rabbits": "Conejos", - "sizePet": { - "small": "Pequeño", - "medium": "Mediano", - "large": "Grande", - "extraLarge": "Super Grande" - }, - "categoryPet": { - "dog": "Perro", - "cat": "Gato", - "exotic": "Exotico", - "rabbit": "Conejo", - "hamster": "Hamster", - "bird": "Ave" - }, - "agePet": { - "baby": "Bebe", - "young": "Joven", - "adult": "Adulto", - "senior": "Viejito" - }, - "genderPet": { - "male": "Macho", - "female": "Hembra" - }, - "cancel": "Cancelar", - "save": "Guardar", - "veterinarians": "Veterinarios", - "veterinarian": "Veterinario", - "shelters": "Refugios", - "role": "Role", - "myPets": "Mis mascotas", - "contact": "Contacto", - "email": "Correo", - "phone": "Teléfono", - "address": "Dirección", - "location": "Ubicación", - "delete": "Borrar", - "SHELTER": "Refugio", - "VET": "Veterinario", - "ADOPTER": "Adoptante", - "adopter": "Adoptante", - "searchByLocation": "Buscar por ubicación", - "actions": "Acciones", - "adoptPet": "Adoptar mascota", - "login": "Iniciar sesión", - "locationUnavailable": "Ubicación no disponible", - "petAvailable": "Mascota disponible", - "petNotAvailable": "No encontramos ninguna mascota disponible", - "petDeleted": "Mascota eliminada", - "petNotDeleted": "Mascota no eliminada", - "emailInvalid": "Correo invalido", - "requiredField": "Campo requerido", - "contactsForAdoption": "Contactos para adopctar", - "wantAdoption": "Quiero adoptar a", - "aboutUs": "Sobre nosotros", - "last": "Last" -} \ No newline at end of file + "female": "Hembra" + }, + "cancel": "Cancelar", + "save": "Guardar", + "veterinarians": "Veterinarios", + "veterinarian": "Veterinario", + "shelters": "Refugios", + "role": "Role", + "myPets": "Mis mascotas", + "start": "Start", + "end": "End", + "selectTime": "Seleccionar hora", + "edit": "Editar", + "editAppointment": "Editar cita", + "createAppointment": "Crear cita", + "pet": "Pet", + "selectPet": "Select pet", + "allDay": "Todo el día", + "today": "Hoy", + "month": "Mes", + "week": "Semana", + "day": "Día", + "agenda": "Agenda", + "date": "Fecha", + "time": "Hora", + "event": "Evento", + "showMore": "+ Ver más ({{total}})", + "contact": "Contacto", + "email": "Correo", + "phone": "Teléfono", + "address": "Dirección", + "location": "Ubicación", + "delete": "Borrar", + "SHELTER": "Refugio", + "VET": "Veterinario", + "ADOPTER": "Adoptante", + "adopter": "Adoptante", + "searchByLocation": "Buscar por ubicación", + "actions": "Acciones", + "adoptPet": "Adoptar mascota", + "login": "Iniciar sesión", + "locationUnavailable": "Ubicación no disponible", + "petAvailable": "Mascota disponible", + "petNotAvailable": "No encontramos ninguna mascota disponible", + "petDeleted": "Mascota eliminada", + "petNotDeleted": "Mascota no eliminada", + "emailInvalid": "Correo invalido", + "requiredField": "Campo requerido", + "contactsForAdoption": "Contactos para adopctar", + "wantAdoption": "Quiero adoptar a", + "aboutUs": "Sobre nosotros", + "last": "Last" +} diff --git a/public/locales/fr/appointments.json b/public/locales/fr/appointments.json new file mode 100644 index 00000000..14bd6599 --- /dev/null +++ b/public/locales/fr/appointments.json @@ -0,0 +1,7 @@ +{ + "createAppointmentForPets": "Créer un rendez-vous pour les animaux", + "selectAdopterOrShelter": "Sélectionnez Adoptant ou Refuge", + "addAppointment": "Ajouter un rendez-vous", + "organizeYourAppointments": "Organisez vos rendez-vous", + "calendarDescription": "Le calendrier vous permet de visualiser les rendez-vous de vos animaux et de les organiser en fonction de vos besoins." +} diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json index 90aff3eb..392d0eb1 100644 --- a/public/locales/fr/common.json +++ b/public/locales/fr/common.json @@ -1,112 +1,133 @@ { - "community": "Communauté", - "searchPets": "Rechercher des animaux", - "dashboard": "Tableau de bord", - "settings": "Paramètres", - "logout": "déconnexion", - "resetFilters": "Effacer les filtres", - "searchByName": "Recherche par nom", - "dogs": "Chiens", - "cats": "Chats", - "exotics": "Exotiques", - "males": "Mâles", - "females": "Femelles", + "community": "Communauté", + "searchPets": "Rechercher des animaux", + "dashboard": "Tableau de bord", + "settings": "Paramètres", + "logout": "déconnexion", + "resetFilters": "Effacer les filtres", + "searchByName": "Recherche par nom", + "dogs": "Chiens", + "cats": "Chats", + "exotics": "Exotiques", + "males": "Mâles", + "females": "Femelles", + "dog": "Chien", + "cat": "Chat", + "exotic": "Exotique", + "male": "Mâle", + "start": "Début", + "end": "Fin", + "selectTime": "Sélectionner l'heure", + "edit": "Modifier", + "editAppointment": "Modifier le rendez-vous", + "createAppointment": "Créer un rendez-vous", + "pet": "Animal", + "selectPet": "Sélectionner un animal", + "allDay": "Toute la journée", + "today": "Aujourd'hui", + "month": "Mois", + "week": "Semaine", + "day": "Jour", + "agenda": "Agenda", + "date": "Date", + "time": "Heure", + "event": "Événement", + "showMore": "+ Voir plus ({{total}})", + "editPet": "Modifier l'animal", + "createPet": "Créer un animal", + "female": "Femelle", + "isRequired": "est requis", + "adopted": "Adopté", + "inAdoption": "En adoption", + "category": "Catégorie", + "gender": "Genre", + "age": "Âge", + "size": "Taille", + "appointment": "Rendez-vous", + "appointments": "Rendez-vous", + "status": "Statut", + "pets": "Animaux", + "areYouSureDelete": "Êtes-vous sûr de vouloir supprimer", + "available": "Disponible", + "showing": "Affichage", + "of": "de", + "results": "résultats", + "previous": "Précédent", + "name": "Nom", + "next": "Suivant", + "selectImages": "Sélectionner des images", + "selectImagesInfo": "JPG, GIF ou PNG. Maximum 1,5 Mo.", + "petName": "Nom de l'animal", + "breed": "Race", + "weight": "Poids", + "massUnit": "Unités de masse", + "petDescription": "Description de l'animal", + "description": "Description", + "adopterBy": "Adopté par", + "petGuardians": "Gardiens de l'animal", + "adoptedBy": "Adopté par", + "vet": "Vétérinaire", + "shelter": "Refuge", + "select": "Sélectionner", + "selectVeterinary": "Sélectionner un vétérinaire", + "selectShelter": "Sélectionner un refuge", + "selectAdopter": "Sélectionner un adoptant", + "petBreed": "Race de l'animal", + "rabbits": "Lapins", + "myPets": "Mes animaux", + "sizePet": { + "small": "Petit", + "medium": "Moyen", + "large": "Grand", + "extraLarge": "Très grand" + }, + "categoryPet": { "dog": "Chien", "cat": "Chat", - "exotic": "Exotique", + "rabbit": "Lapin", + "hamster": "Hamster", + "bird": "Oiseau", + "exotic": "Exotique" + }, + "agePet": { + "baby": "Bébé", + "young": "Jeune", + "adult": "Adulte", + "senior": "Senior" + }, + "genderPet": { "male": "Mâle", - "editPet": "Modifier l'animal", - "createPet": "Créer un animal", - "female": "Femelle", - "adopted": "Adopté", - "inAdoption": "En adoption", - "category": "Catégorie", - "gender": "Genre", - "age": "Âge", - "size": "Taille", - "status": "Statut", - "pets": "Animaux", - "areYouSureDelete": "Êtes-vous sûr de vouloir supprimer", - "available": "Disponible", - "showing": "Affichage", - "of": "de", - "results": "résultats", - "previous": "Précédent", - "name": "Nom", - "next": "Suivant", - "selectImages": "Sélectionner des images", - "selectImagesInfo": "JPG, GIF ou PNG. Maximum 1,5 Mo.", - "petName": "Nom de l'animal", - "breed": "Race", - "weight": "Poids", - "massUnit": "Unités de masse", - "petDescription": "Description de l'animal", - "description": "Description", - "adopterBy": "Adopté par", - "petGuardians": "Gardiens de l'animal", - "adoptedBy": "Adopté par", - "vet": "Vétérinaire", - "shelter": "Refuge", - "select": "Sélectionner", - "selectVeterinary": "Sélectionner un vétérinaire", - "selectShelter": "Sélectionner un refuge", - "selectAdopter": "Sélectionner un adoptant", - "petBreed": "Race de l'animal", - "rabbits": "Lapins", - "myPets": "Mes animaux", - "sizePet": { - "small": "Petit", - "medium": "Moyen", - "large": "Grand", - "extraLarge": "Très grand" - }, - "categoryPet": { - "dog": "Chien", - "cat": "Chat", - "rabbit": "Lapin", - "hamster": "Hamster", - "bird": "Oiseau", - "exotic": "Exotique" - }, - "agePet": { - "baby": "Bébé", - "young": "Jeune", - "adult": "Adulte", - "senior": "Senior" - }, - "genderPet": { - "male": "Mâle", - "female": "Femelle" - }, - "cancel": "Annuler", - "save": "Sauvegarder", - "veterinarians": "Vétérinaires", - "veterinarian": "Vétérinaire", - "shelters": "Refuges", - "contact": "Contact", - "email": "Email", - "role": "Rôle", - "phone": "Téléphone", - "address": "Adresse", - "location": "Localisation", - "delete": "Supprimer", - "SHELTER": "Refuge", - "VET": "Vétérinaire", - "ADOPTER": "Adoptant", - "adopter": "Adoptant", - "searchByLocation": "Recherche par localisation", - "actions": "Actions", - "adoptPet": "Adopter un animal", - "login": "Connexion", - "locationUnavailable": "Localisation indisponible", - "petAvailable": "Animal disponible", - "petDeleted": "Animal supprimé avec succès", - "petNotDeleted": "Animal non supprimé", - "contactsForAdoption": "Contacts pour l'adoption", - "emailInvalid": "Email invalide", - "requiredField": "Champ requis", - "petNotAvailable": "Animal non disponible", - "wantAdoption": "Je souhaite adopter", - "aboutUs": "À propos de nous", - "last": "Dernier" -} \ No newline at end of file + "female": "Femelle" + }, + "cancel": "Annuler", + "save": "Sauvegarder", + "veterinarians": "Vétérinaires", + "veterinarian": "Vétérinaire", + "shelters": "Refuges", + "contact": "Contact", + "email": "Email", + "role": "Rôle", + "phone": "Téléphone", + "address": "Adresse", + "location": "Localisation", + "delete": "Supprimer", + "SHELTER": "Refuge", + "VET": "Vétérinaire", + "ADOPTER": "Adoptant", + "adopter": "Adoptant", + "searchByLocation": "Recherche par localisation", + "actions": "Actions", + "adoptPet": "Adopter un animal", + "login": "Connexion", + "locationUnavailable": "Localisation indisponible", + "petAvailable": "Animal disponible", + "petDeleted": "Animal supprimé avec succès", + "petNotDeleted": "Animal non supprimé", + "contactsForAdoption": "Contacts pour l'adoption", + "emailInvalid": "Email invalide", + "requiredField": "Champ requis", + "petNotAvailable": "Animal non disponible", + "wantAdoption": "Je souhaite adopter", + "aboutUs": "À propos de nous", + "last": "Dernier" +} diff --git a/src/App.tsx b/src/App.tsx index ad1c28d6..358dbc16 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import { observer } from 'mobx-react' import { FC, useEffect, useState } from 'react' -import { createBrowserRouter, RouterProvider} from 'react-router-dom' +import { createBrowserRouter, RouterProvider } from 'react-router-dom' import { ToastContainer } from 'react-toastify' import Navigation from './components/Navigation' @@ -12,6 +12,7 @@ import { DashboardPage } from './pages/DashboardPage' import { ProfilePetPage } from './pages/ProfilePetPage' import { SettingsPage } from './pages/SettingsPage' import { UserProfilePage } from './pages/UserProfilePage' +import { AppointmentsPage } from './pages/AppointmentsPage' import { AppContext, AppContextProps } from './services/AppContext' import './api/axiosInstance' @@ -53,6 +54,10 @@ const router = createBrowserRouter([ path: '/searchPets', element: , }, + { + path: '/appointments', + element: , + }, { path: '/', element: , @@ -99,7 +104,7 @@ const App: FC = observer((props) => { location: { address: props?.appContext?.user?.address || '', }, - }} + }} isOpenRoleModal={isOpenRoleModal} setOpenRoleModal={setOpenRoleModal} /> diff --git a/src/api/appointments.ts b/src/api/appointments.ts new file mode 100644 index 00000000..142f8fda --- /dev/null +++ b/src/api/appointments.ts @@ -0,0 +1,38 @@ +// src/services/appointmentService.ts +import axios from 'axios' + +const API_URL = '/api/v1/appointments' + +export const createAppointment = async (data: unknown) => { + try { + const response = await axios.post(API_URL, data) + return response.data + } catch (error) { + return error + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const updateAppointment = async (data: any) => { + const response = await axios.put(`${API_URL}/${data.id}`, data) + return response.data +} + +export const deleteAppointment = async (id: string) => { + const response = await axios.delete(`${API_URL}/${id}`) + return response.data +} + +export const getAppointmentById = async (id: string) => { + const response = await axios.get(`${API_URL}/${id}`) + return response.data +} + +export const listAppointments = async () => { + try { + const response = await axios.get(API_URL) + return response.data + } catch (error) { + return error + } +} diff --git a/src/api/utils/resizeImages.ts b/src/api/utils/resizeImages.ts index 774f38b1..8f6b9ca4 100644 --- a/src/api/utils/resizeImages.ts +++ b/src/api/utils/resizeImages.ts @@ -22,7 +22,7 @@ export const resizeImages = async (images: Image[]): Promise => { 'JPEG', // Image format 100, // Quality 0, // Rotation - (uri: string | Blob | File | ProgressEvent) => { + (uri: string | Blob | File | Progressappointment) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const byteString = atob(uri.split(',')[1]) diff --git a/src/assets/icons/iconCalendarStats.tsx b/src/assets/icons/iconCalendarStats.tsx new file mode 100644 index 00000000..8bb968fe --- /dev/null +++ b/src/assets/icons/iconCalendarStats.tsx @@ -0,0 +1,22 @@ +const IconCalendarStats = ({ width = 18, height = 18, color = '#2c3e50' }) => ( + + + + + + + + + +) + +export default IconCalendarStats diff --git a/src/assets/icons/iconChevronLeft.tsx b/src/assets/icons/iconChevronLeft.tsx index 997826c1..c949abaa 100644 --- a/src/assets/icons/iconChevronLeft.tsx +++ b/src/assets/icons/iconChevronLeft.tsx @@ -1,9 +1,17 @@ const IconChevronLeft = ({ width = 18, height = 18, color = '#2c3e50' }) => ( - - - - -); + + + + +) - -export default IconChevronLeft; +export default IconChevronLeft diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 40a28190..5ab95fcb 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -1,4 +1,5 @@ import IconBack from './iconBack.svg' +import IconCalendarStats from './iconCalendarStats' import IconChevronLeft from './iconChevronLeft' import IconChevronRight from './iconChevronRight' import IconClose from './iconClose.svg' @@ -20,6 +21,7 @@ import IconUser from './iconUser.svg' import IconWhatsapp from './iconWhatsapp.svg' export { + IconCalendarStats, IconEdit, IconTrash, IconChevronLeft, diff --git a/src/components/AppointmentsCalendar/components/AppointmentDetails/indesx.tsx b/src/components/AppointmentsCalendar/components/AppointmentDetails/indesx.tsx new file mode 100644 index 00000000..fcf12a7d --- /dev/null +++ b/src/components/AppointmentsCalendar/components/AppointmentDetails/indesx.tsx @@ -0,0 +1,61 @@ +import moment from 'moment' +import { FC } from 'react' +import { useTranslation } from 'react-i18next' + +import { BaseButton } from '../../../common/BaseButton' +import { ReactModal } from '../../../common/ReactModal' +import { Appointment } from '../../types' + +interface Props { + isOpen: boolean + onClose: () => void + handleClose: () => void + appointment: Appointment | null + onEdit: (value: boolean) => void + onDelete: (value: boolean) => void +} + +export const AppointmentDetailsModal: FC = ({ + onEdit, + onClose, + onDelete, + appointment, +}) => { + const { t } = useTranslation() + return ( + +
+
+

{`${t('common:day')}: ${moment(appointment?.startDate).format('DD/MM/YYYY')}`}

+

{`${t('common:hour')}: ${moment(appointment?.startDate).format('hh:mm')}`}

+
+

{appointment?.description}

+
+
+ onDelete(true)} + /> + + onEdit(true)} + /> +
+
+
+
+ ) +} diff --git a/src/components/AppointmentsCalendar/index.tsx b/src/components/AppointmentsCalendar/index.tsx new file mode 100644 index 00000000..5d44cf15 --- /dev/null +++ b/src/components/AppointmentsCalendar/index.tsx @@ -0,0 +1,171 @@ +import axios from 'axios' +import moment from 'moment' +import { useState, useEffect, FC } from 'react' +import { Calendar, momentLocalizer, Event } from 'react-big-calendar' +import { useTranslation } from 'react-i18next' + +import { useGetApointment } from '../../hooks/appointments/useGetAppointment' +import { DeleteModal } from '../common/DeleteModal' + +import 'react-big-calendar/lib/css/react-big-calendar.css' +import './styles.css' + +interface Props { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + resetForm: any + events: Appointment[] + setOpenAppointmentForm: (isOpen: boolean) => void +} + +interface Appointment { + id: string + end: string + petId: string + start: string + title: string + endDate: string + startDate: string + recipientId?: string + description?: string + values: { + startDay: string + endDate: string + } +} + +export const AppointmentsCalendar: FC = ({ + events, + resetForm, + setOpenAppointmentForm, +}) => { + const { t } = useTranslation(['common']) + const [deleteModalAppointment, setDeleteModalAppointment] = useState(false) + const [appointments, setAppointments] = useState([]) + const [selectedAppointment, setSelectedAppointment] = + useState(null) + const { data: appointment } = useGetApointment( + selectedAppointment?.id || undefined, + ) + + const localizer = momentLocalizer(moment) + + const handleEventSelect = (appointment: Event): void => { + setSelectedAppointment(appointment as unknown as Appointment) + setOpenAppointmentForm(true) + } + + const handleDeleteAppointment = async ( + selectedAppointment: Appointment | null, + ) => { + try { + await axios.delete(`/appointments/${selectedAppointment?.id}`) + } catch (error) { + console.log(error) + } + setDeleteModalAppointment(false) + } + + const eventStyleGetter = ( + _event: Event, + _start: string, + _end: string, + isSelected: boolean, + ) => { + const newStyle = { + backgroundColor: isSelected ? '#8ad3c1' : '#d6f1e9', // Default color + color: isSelected ? '#0d2624' : '#0d2624', // Text color + } + + return { + className: '', + style: newStyle, + } + } + + const formatAppointments = async (events: Appointment[]) => { + const appointments = events.map((event) => ({ + id: event.id, + allDay: false, + petId: event?.petId || null, + title: event.title, + start: new Date(event?.startDate), + recipientId: event?.recipientId || null, + description: event?.description, + end: new Date(event?.endDate), + })) + setAppointments(appointments) + } + + useEffect(() => { + formatAppointments(events) + }, [events]) + + const messages = { + allDay: t('common:allDay'), + previous: t('common:previous'), + next: t('common:next'), + today: t('common:today'), + month: t('common:month'), + week: t('common:week'), + day: t('common:day'), + agenda: t('common:agenda'), + date: t('common:date'), + time: t('common:time'), + event: t('common:event'), + showMore: t('common:showMore'), + } + + useEffect(() => { + if (selectedAppointment) { + const appointmentFormatted = { + petId: appointment?.petId || null, + id: selectedAppointment?.id || '', + endDate: selectedAppointment?.end || '', + title: selectedAppointment?.title || '', + recipientId: appointment?.recipientId || null, + startDate: selectedAppointment?.start || '', + description: selectedAppointment?.description || '', + } + + resetForm({ values: appointmentFormatted }) + } + }, [selectedAppointment, resetForm, appointment]) + + return ( +
+ `+ Ver más (${count})`, + }} + /> + setDeleteModalAppointment(false)} + handleDelete={() => { + handleDeleteAppointment(selectedAppointment) + setDeleteModalAppointment(false) + }} + title={`${t('common:areYouSureDelete')} this appointment?`} + /> +
+ ) +} diff --git a/src/components/AppointmentsCalendar/styles.css b/src/components/AppointmentsCalendar/styles.css new file mode 100644 index 00000000..66646d2a --- /dev/null +++ b/src/components/AppointmentsCalendar/styles.css @@ -0,0 +1,45 @@ +.rbc-today { + background-color: #f3faf8 !important; + font-family: 'Lato', sans-serif; +} + +.rbc-calendar { + font-size: 16px; + font-family: 'Lato', sans-serif !important; +} + +.rbc-event { + font-size: 16px; + font-family: 'Lato', sans-serif; + font-weight: medium; +} + +.rbc-show-more { + color: #4fb29c; + font-family: 'Lato', sans-serif; +} + +.rbc-btn-group { + background-color: transparent; +} + +.rbc-btn-group button { + font-family: 'Lato', sans-serif !important; + color: #0d2624 !important; + &&:hover { + background: #d6f1e9 !important; + } + &&:focus { + background: #ace3d3 !important; + } + &&::active { + background: #369683 !important; + } + border-color: #4fb29c; + font-weight: medium; + font-size: 14px; +} + +.rbc-off-range-bg { + background-color: #f3faf8; +} diff --git a/src/components/AppointmentsCalendar/types.ts b/src/components/AppointmentsCalendar/types.ts new file mode 100644 index 00000000..1e1d8e05 --- /dev/null +++ b/src/components/AppointmentsCalendar/types.ts @@ -0,0 +1,21 @@ +export interface Appointment { + id: string + end: string + start: string + title: string + endDate?: string + startDate?: string + description?: string + recipientId?: string +} + +export interface Props { + resetForm: (values: Appointment) => void // Better type definition is needed + events: Appointment[] + setOpenAppointment: (isOpen: boolean) => void +} + +export interface EventProps extends Appointment { + start: string + end: string +} diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 9647aaf5..a5c5cc16 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -26,28 +26,24 @@ export const Navbar: FC = () => { e.stopPropagation() setLng({ lng: lng, - symbol: lng + symbol: lng, }) i18n.changeLanguage(lng) setIsOpenLngToggle(false) } - useEffect(() => { if (locale === 'fr') { -return setLng({ + return setLng({ lng: 'French', - symbol: locale + symbol: locale, + }) + } + setLng({ + lng: locale === 'en' ? 'English' : 'Español', + symbol: locale, }) - } - setLng({ - lng: locale === 'en' ? 'English' : 'Español', - symbol: locale -}) - -}, [locale]) - - + }, [locale]) return ( <> @@ -75,31 +71,43 @@ return setLng({ -
+
- {isOpenLngToggle && -
- - - -
- } + {isOpenLngToggle && ( +
+ + + +
+ )}
diff --git a/src/components/Navigation/components/ButtonNavigate/index.tsx b/src/components/Navigation/components/ButtonNavigate/index.tsx index eac61ebf..ba6c133f 100644 --- a/src/components/Navigation/components/ButtonNavigate/index.tsx +++ b/src/components/Navigation/components/ButtonNavigate/index.tsx @@ -20,7 +20,7 @@ export const ButtonNavigate: FC = ({ onClick={handleNavigation} className={`${isSelected ? 'bg-primary-400' : ''} flex justify-start gap-2 p-2 pl-[10px] w-full h-[48px] bg-primary-200 rounded-md items-center hover:bg-primary-100`} > -
+
{icon}
{!menuIsCollapsed &&

{text}

} diff --git a/src/components/Navigation/components/SideBar/index.tsx b/src/components/Navigation/components/SideBar/index.tsx index f298f2aa..9bbc8bce 100644 --- a/src/components/Navigation/components/SideBar/index.tsx +++ b/src/components/Navigation/components/SideBar/index.tsx @@ -1,8 +1,16 @@ -import { FC, ReactElement, MouseEvent, useContext, useState, useEffect } from 'react' +import { + FC, + ReactElement, + MouseEvent, + useContext, + useState, + useEffect, +} from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import { + IconCalendarStats, IconChevronLeft, IconChevronRight, IconHomeInfinity, @@ -26,6 +34,11 @@ interface Props { const TOP_NAVIGATION = [ { to: '/dashboard', icon: , text: 'dashboard' }, { to: '/community', icon: , text: 'community' }, + { + to: '/appointments', + icon: , + text: 'appointments', + }, { to: '/searchPets', icon: , text: 'searchPets' }, ] @@ -39,7 +52,7 @@ export const SideBar: FC = ({ setMenuIsCollapsed, }) => { const [isOpenLngToggle, setIsOpenLngToggle] = useState(false) - const { t } = useTranslation(['common']); + const { t } = useTranslation(['common']) const locale = i18n.language const [lng, setLng] = useState({ @@ -51,7 +64,7 @@ export const SideBar: FC = ({ const handleError = (e: React.SyntheticEvent) => { const target = e.target as HTMLImageElement - target.onerror = null // Prevents infinite loop if local image is also not found + target.onerror = null // Prappointments infinite loop if local image is also not found target.src = MidDog } @@ -83,7 +96,7 @@ export const SideBar: FC = ({ e.stopPropagation() setLng({ lng: lng, - symbol: lng + symbol: lng, }) i18n.changeLanguage(lng) setIsOpenLngToggle(false) @@ -101,27 +114,53 @@ export const SideBar: FC = ({ ? context?.user?.image : `${import.meta.env.VITE_BUCKET_NAME}users/avatar/${context?.user?.image}` - useEffect(() => { - setLng({ - lng: locale === 'en' ? 'English' : 'Español', - symbol: locale + useEffect(() => { + let getLng + if (locale === 'es') { + getLng = { + lng: 'es', + symbol: 'es', + } + } else if (locale === 'en') { + getLng = { + lng: 'en', + symbol: 'en', + } + } else { + getLng = { + lng: 'fr', + symbol: 'fr', + } + } + setLng({ + lng: getLng, + symbol: locale, }) }, [locale]) -const isSelected = (path: string) => { - const pathname = window.location.pathname - return pathname.includes(path) -} + const isSelected = (path: string) => { + const pathname = window.location.pathname + return pathname.includes(path) + } return (
{/* Fixed Sidebar */}
-
-
- {menuIsCollapsed ? : } +
+
+ {menuIsCollapsed ? ( + + ) : ( + + )} +
-
{ }} /> ))} -
+
- {isOpenLngToggle && -
- - - -
- } + {isOpenLngToggle && ( +
+ + + +
+ )}