From cd9507f16dec3bbf24dce3e0628cc9a833d5d799 Mon Sep 17 00:00:00 2001 From: Poowerllz Date: Wed, 26 Jul 2023 21:15:03 -0300 Subject: [PATCH 1/3] feat: download option --- package.json | 4 +- src/common/date/index.ts | 13 ++++++ src/common/download/index.ts | 11 +++++ src/components/HeaderPage/index.tsx | 38 +++++++++++---- src/i18n/langs/en.ts | 3 ++ src/pages/Coaches/index.tsx | 14 ++++-- src/pages/Competencies/index.tsx | 7 +++ src/pages/Schools/index.tsx | 10 ++-- src/pages/Sessions/index.tsx | 10 ++-- src/pages/Sync/index.tsx | 69 ++++++--------------------- src/pages/Teacher/index.tsx | 12 ++++- yarn.lock | 72 +++++++++++++++++++++++++++++ 12 files changed, 186 insertions(+), 77 deletions(-) create mode 100644 src/common/date/index.ts create mode 100644 src/common/download/index.ts diff --git a/package.json b/package.json index 590fc6e..58b932e 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@superset-ui/embedded-sdk": "^0.1.0-alpha.9", "@types/styled-system": "^5.1.16", "axios": "^1.3.4", + "date-fns": "^2.30.0", "framer-motion": "^10.2.3", "i18next": "^22.4.11", "react": "^18.2.0", @@ -28,7 +29,8 @@ "react-paginate": "^8.2.0", "react-router-dom": "6.4.3", "react-toastify": "^9.1.1", - "styled-system": "^5.1.5" + "styled-system": "^5.1.5", + "xlsx": "^0.18.5" }, "devDependencies": { "@babel/core": "^7.21.0", diff --git a/src/common/date/index.ts b/src/common/date/index.ts new file mode 100644 index 0000000..a21675f --- /dev/null +++ b/src/common/date/index.ts @@ -0,0 +1,13 @@ +import { format, isValid } from "date-fns"; +import { ptBR } from "date-fns/locale"; + +const formatDate = (date: Date | undefined, customFormat?: string) => { + if (date && isValid(date)) { + return format(date, customFormat || "dd MMM yyyy", { + locale: ptBR, + }); + } + return ""; +}; + +export default formatDate; diff --git a/src/common/download/index.ts b/src/common/download/index.ts new file mode 100644 index 0000000..ceaa6af --- /dev/null +++ b/src/common/download/index.ts @@ -0,0 +1,11 @@ +import * as XLSX from "xlsx"; +import formatDate from "../date"; + +const handleDownloadJSON = (data: any, name: string) => { + let wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(data), "Relatório"); + + XLSX.writeFile(wb, `${name} - ${formatDate(new Date(), "dd-MM-yyyy")}.xlsx`); +}; + +export default handleDownloadJSON; diff --git a/src/components/HeaderPage/index.tsx b/src/components/HeaderPage/index.tsx index 0f4d1e4..ee9b8cd 100644 --- a/src/components/HeaderPage/index.tsx +++ b/src/components/HeaderPage/index.tsx @@ -1,12 +1,14 @@ import React from "react"; import { Button, HStack, Text, VStack } from "@chakra-ui/react"; import Icon from "../Base/Icon"; +import { useTranslation } from "react-i18next"; type Props = { title: string; subtitle: string; newButtonValue?: string | null; onClickNew?: () => void; + onClickDownload?: () => void; }; const HeaderPage: React.FC = ({ @@ -14,7 +16,10 @@ const HeaderPage: React.FC = ({ subtitle, newButtonValue, onClickNew, + onClickDownload, }) => { + const { t } = useTranslation(); + return ( @@ -37,16 +42,29 @@ const HeaderPage: React.FC = ({ {title} - {newButtonValue && onClickNew && ( - - )} + + {onClickDownload && ( + + )} + + {newButtonValue && onClickNew && ( + + )} + ); }; diff --git a/src/i18n/langs/en.ts b/src/i18n/langs/en.ts index e333ded..b591d8c 100644 --- a/src/i18n/langs/en.ts +++ b/src/i18n/langs/en.ts @@ -32,6 +32,9 @@ const enTranslation = { view: "View", delete: "Delete", }, + general: { + "download-data": "Download data", + }, }; export default enTranslation; diff --git a/src/pages/Coaches/index.tsx b/src/pages/Coaches/index.tsx index f6eecf9..a57c68b 100644 --- a/src/pages/Coaches/index.tsx +++ b/src/pages/Coaches/index.tsx @@ -5,8 +5,6 @@ import { Box, Button, Center, - Flex, - Heading, Modal, ModalBody, ModalCloseButton, @@ -20,6 +18,7 @@ import CoachForm from "./CoachForm"; import CoachList from "./CoachList"; import HeaderPage from "@/components/HeaderPage"; import { useTranslation } from "react-i18next"; +import handleDownloadJSON from "@/common/download"; const CoachesPage: React.FC = () => { const { t } = useTranslation(); @@ -73,7 +72,16 @@ const CoachesPage: React.FC = () => { return ( - + + handleDownloadJSON( + coaches, + t("Navbar.coaches").toLowerCase().replaceAll(" ", "-") + ) + } + /> { const { t } = useTranslation(); @@ -59,6 +60,12 @@ const CompetenciesPage: React.FC = () => { title={t("Navbar.teaching-practices")} newButtonValue={t("competence.new-competence")} onClickNew={() => setNewCompetence(true)} + onClickDownload={() => + handleDownloadJSON( + competencies, + t("Navbar.teaching-practices").toLowerCase().replaceAll(" ", "-") + ) + } /> { const { t } = useTranslation(); @@ -80,6 +78,12 @@ const SchoolsPage: React.FC = () => { title={t("Navbar.schools")} newButtonValue={t("school.new-school")} onClickNew={() => setNewSchool(true)} + onClickDownload={() => + handleDownloadJSON( + schools, + t("Navbar.schools").toLowerCase().replaceAll(" ", "-") + ) + } /> { const { t } = useTranslation(); @@ -78,6 +76,12 @@ const SessionsPage: React.FC = () => { + handleDownloadJSON( + sessions, + t("Navbar.sessions").toLowerCase().replaceAll(" ", "-") + ) + } /> { const { t } = useTranslation(); - const [newSync, setNewSync] = useState(false); const [syncs, setSyncs] = useState([]); const [isLoadingList, setIsLoadingList] = useState(true); - const [isLoadingForm, setIsLoadingForm] = useState(false); - const [isLoadingDelete, setIsLoadingDelete] = useState(false); - const [syncToEdit, setSyncToEdit] = useState(); - const [syncToDelete, setSyncToDelete] = useState(); useEffect(() => { loadSyncs(); @@ -42,49 +24,24 @@ const SyncsPage: React.FC = () => { setIsLoadingList(false); }; - const closeForm = () => { - setNewSync(false); - setSyncToEdit(undefined); - }; - - const saveSync = async (sync: Partial) => { - setIsLoadingForm(true); - if (syncToEdit) { - await SyncService.updateSync({ ...syncToEdit, ...sync }); - } else { - await SyncService.saveSync(sync); - } - setIsLoadingForm(false); - loadSyncs(); - closeForm(); - }; - - const deleteSync = async () => { - setIsLoadingDelete(true); - if (syncToDelete) await SyncService.DeleteSync(syncToDelete?.id); - setIsLoadingDelete(false); - onCloseDeleteModal(); - loadSyncs(); - }; - - const onCloseDeleteModal = () => { - setSyncToDelete(undefined); - }; - return ( - {" "} - + + handleDownloadJSON( + syncs, + t("Navbar.syncs").toLowerCase().replaceAll(" ", "-") + ) + } + /> {isLoadingList ? (
) : ( - + {}} handleDelete={() => {}} /> )}
); diff --git a/src/pages/Teacher/index.tsx b/src/pages/Teacher/index.tsx index 62a5883..f8c0aea 100644 --- a/src/pages/Teacher/index.tsx +++ b/src/pages/Teacher/index.tsx @@ -21,6 +21,7 @@ import TeacherForm from "./TeacherForm"; import TeacherList from "./TeacherList"; import HeaderPage from "@/components/HeaderPage"; import { useTranslation } from "react-i18next"; +import handleDownloadJSON from "@/common/download"; const TeachersPage: React.FC = () => { const { t } = useTranslation(); @@ -74,7 +75,16 @@ const TeachersPage: React.FC = () => { return ( - + + handleDownloadJSON( + teachers, + t("Navbar.teachers").toLowerCase().replaceAll(" ", "-") + ) + } + /> Date: Tue, 1 Aug 2023 20:03:10 -0300 Subject: [PATCH 2/3] feat: cruds --- src/i18n/langs/en.ts | 14 ++++ src/pages/Coaches/CoachForm/index.tsx | 55 ++++++++------ src/pages/Coaches/CoachList/index.tsx | 39 +++++++++- src/pages/Settings/ChangePassword/index.tsx | 7 ++ src/pages/Settings/EditUser/index.tsx | 7 ++ src/pages/Settings/Users/index.tsx | 34 +++++++++ src/pages/Settings/index.tsx | 80 +++++++++++++++++++-- src/pages/Teacher/TeacherForm/index.tsx | 55 ++++++++------ src/pages/Teacher/TeacherList/index.tsx | 40 ++++++++++- src/pages/Teacher/index.tsx | 2 +- src/services/user/index.ts | 2 + 11 files changed, 278 insertions(+), 57 deletions(-) create mode 100644 src/pages/Settings/ChangePassword/index.tsx create mode 100644 src/pages/Settings/EditUser/index.tsx create mode 100644 src/pages/Settings/Users/index.tsx diff --git a/src/i18n/langs/en.ts b/src/i18n/langs/en.ts index b591d8c..a70aa1b 100644 --- a/src/i18n/langs/en.ts +++ b/src/i18n/langs/en.ts @@ -35,6 +35,20 @@ const enTranslation = { general: { "download-data": "Download data", }, + settings: { + title: "Settings", + tabs: { + user: { + title: "General", + }, + users: { + title: "Users", + }, + "change-password": { + title: "Change password", + }, + }, + }, }; export default enTranslation; diff --git a/src/pages/Coaches/CoachForm/index.tsx b/src/pages/Coaches/CoachForm/index.tsx index 5dd5fdb..fb347e6 100644 --- a/src/pages/Coaches/CoachForm/index.tsx +++ b/src/pages/Coaches/CoachForm/index.tsx @@ -5,12 +5,13 @@ import { FormLabel, FormControl, FormErrorMessage, - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, + Drawer, + DrawerOverlay, + DrawerContent, + DrawerCloseButton, + DrawerHeader, + DrawerBody, + DrawerFooter, } from "@chakra-ui/react"; import { useForm } from "react-hook-form"; @@ -36,13 +37,20 @@ const CoachForm: React.FC = ({ } = useForm(); return ( - - - - {coachToEdit ? "Edit" : "New"} Coach - - -
+ + + + + + + + {coachToEdit ? "New Teacher" : "Update teacher"} + + + Nome = ({ "Name is required"} - - -
-
-
+ + + + + ); }; diff --git a/src/pages/Coaches/CoachList/index.tsx b/src/pages/Coaches/CoachList/index.tsx index 09bf78c..c39bde2 100644 --- a/src/pages/Coaches/CoachList/index.tsx +++ b/src/pages/Coaches/CoachList/index.tsx @@ -1,7 +1,8 @@ -import { Box, Flex, IconButton, Text } from "@chakra-ui/react"; -import { EditIcon, DeleteIcon } from "@chakra-ui/icons"; +import { Flex, Menu, MenuButton, MenuItem, MenuList } from "@chakra-ui/react"; import { ICoach } from "@/types"; import Table from "@/components/Table"; +import Icon from "@/components/Base/Icon"; +import { useTranslation } from "react-i18next"; type Props = { coachs: ICoach[]; @@ -10,6 +11,8 @@ type Props = { }; const CoachList: React.FC = ({ coachs, handleDelete, handleEdit }) => { + const { t } = useTranslation(); + return ( = ({ coachs, handleDelete, handleEdit }) => { renderColumn: (item: ICoach) => item.name, title: "Name", }, + { + renderColumn: (item: ICoach) => ( + + + + + + + handleEdit(item)} + > + + {t("common.edit")} + + handleDelete(item)} + > + + {t("common.delete")} + + + + + ), + width: "85px", + title: "common.actions", + }, ]} /> ); diff --git a/src/pages/Settings/ChangePassword/index.tsx b/src/pages/Settings/ChangePassword/index.tsx new file mode 100644 index 0000000..edcaa18 --- /dev/null +++ b/src/pages/Settings/ChangePassword/index.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const ChangePassword = () => { + return <>; +}; + +export default ChangePassword; diff --git a/src/pages/Settings/EditUser/index.tsx b/src/pages/Settings/EditUser/index.tsx new file mode 100644 index 0000000..fa0fab9 --- /dev/null +++ b/src/pages/Settings/EditUser/index.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const EditUser = () => { + return <>; +}; + +export default EditUser; diff --git a/src/pages/Settings/Users/index.tsx b/src/pages/Settings/Users/index.tsx new file mode 100644 index 0000000..cd95be3 --- /dev/null +++ b/src/pages/Settings/Users/index.tsx @@ -0,0 +1,34 @@ +import Loader from "@/components/Base/Loader"; +import UserService from "@/services/user"; +import { IUser } from "@/types"; +import { Center, HStack, Text, VStack } from "@chakra-ui/react"; +import React, { useCallback, useEffect, useState } from "react"; + +const Users = () => { + const [users, setUsers] = useState({ + isLoading: true, + data: [] as IUser[], + }); + + const refreshUsers = useCallback(() => { + UserService.getUsers().then(users => setUsers({ isLoading: false, data: users })) + }, []); + + useEffect(() => { + refreshUsers(); + }, [refreshUsers]); + + return <> + {users.isLoading ? +
+ +
: + + {users.data.map(user => + + {user.name} + )} + }; +}; + +export default Users; diff --git a/src/pages/Settings/index.tsx b/src/pages/Settings/index.tsx index b472bf0..850d84b 100644 --- a/src/pages/Settings/index.tsx +++ b/src/pages/Settings/index.tsx @@ -1,12 +1,80 @@ -import { Center, Text } from "@chakra-ui/react"; +import { Box, HStack, Text, VStack } from "@chakra-ui/react"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import EditUser from "./EditUser"; +import ChangePassword from "./ChangePassword"; +import Users from "./Users"; +import Icon from "@/components/Base/Icon"; +import HeaderPage from "@/components/HeaderPage"; const SettingsPage: React.FC = () => { + const { t } = useTranslation(); + const [currentOption, setCurrentOption] = useState(0); + const options = [ + { + label: t("settings.tabs.user.title"), + icon: "user-circle", + component: , + }, + { + label: t("settings.tabs.change-password.title"), + icon: "lock", + component: , + }, + { + label: t("settings.tabs.users.title"), + icon: "user", + component: , + }, + ]; + return ( -
- - Settings - -
+ + + + + + {options.map((opt, index) => { + const isActive = index === currentOption; + + return ( + setCurrentOption(index)} + > + + + {opt.label} + + + ); + })} + + + {options[currentOption].component} + + + ); }; diff --git a/src/pages/Teacher/TeacherForm/index.tsx b/src/pages/Teacher/TeacherForm/index.tsx index d1fcd44..4e05e22 100644 --- a/src/pages/Teacher/TeacherForm/index.tsx +++ b/src/pages/Teacher/TeacherForm/index.tsx @@ -5,12 +5,13 @@ import { FormLabel, FormControl, FormErrorMessage, - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, + Drawer, + DrawerOverlay, + DrawerContent, + DrawerCloseButton, + DrawerHeader, + DrawerBody, + DrawerFooter, } from "@chakra-ui/react"; import { useForm } from "react-hook-form"; @@ -36,13 +37,20 @@ const TeacherForm: React.FC = ({ } = useForm(); return ( - - - - {teacherToEdit ? "Edit" : "New"} Teacher - - -
+ + + + + + + + {teacherToEdit ? "New Teacher" : "Update teacher"} + + + Nome = ({ "Name is required"} - - -
-
-
+ + + + + ); }; diff --git a/src/pages/Teacher/TeacherList/index.tsx b/src/pages/Teacher/TeacherList/index.tsx index 4ef1a5b..ec3cae9 100644 --- a/src/pages/Teacher/TeacherList/index.tsx +++ b/src/pages/Teacher/TeacherList/index.tsx @@ -1,7 +1,9 @@ -import { Box, Flex, IconButton, Text } from "@chakra-ui/react"; -import { EditIcon, DeleteIcon } from "@chakra-ui/icons"; +import { Flex, Menu, MenuButton, MenuItem, MenuList } from "@chakra-ui/react"; + import { ITeacher } from "@/types"; import Table from "@/components/Table"; +import { useTranslation } from "react-i18next"; +import Icon from "@/components/Base/Icon"; type Props = { teachers: ITeacher[]; @@ -14,6 +16,8 @@ const TeacherList: React.FC = ({ handleDelete, handleEdit, }) => { + const { t } = useTranslation(); + return (
= ({ renderColumn: (item: ITeacher) => item.name, title: "Name", }, + { + renderColumn: (item: ITeacher) => ( + + + + + + + handleEdit(item)} + > + + {t("common.edit")} + + handleDelete(item)} + > + + {t("common.delete")} + + + + + ), + width: "85px", + title: "common.actions", + }, ]} /> ); diff --git a/src/pages/Teacher/index.tsx b/src/pages/Teacher/index.tsx index f8c0aea..59adbac 100644 --- a/src/pages/Teacher/index.tsx +++ b/src/pages/Teacher/index.tsx @@ -108,7 +108,7 @@ const TeachersPage: React.FC = () => { Confirm Deletion - Are you sure you want to delete this school? + Are you sure you want to delete this teacher?