From 7a93439a8708639e13d42edbeeacc8c9eb5ab92b Mon Sep 17 00:00:00 2001 From: jonny paulino Date: Tue, 11 Jun 2024 10:28:34 -0300 Subject: [PATCH 1/5] ruler project --- src/Components/TextInput/index.tsx | 2 + src/Context/Project/ProjectOne/context.tsx | 6 +- src/Context/Project/ProjectOne/state.tsx | 12 ++- src/Context/Project/ProjectOne/type.tsx | 1 + src/Pages/Projects/ProjectOne/index.tsx | 53 ++++++--- src/Services/Project/controller.tsx | 120 +++++++++++++-------- src/Services/Project/request.tsx | 39 ++++++- src/Types/types.tsx | 3 +- 8 files changed, 170 insertions(+), 66 deletions(-) diff --git a/src/Components/TextInput/index.tsx b/src/Components/TextInput/index.tsx index 5f6f18a..76f94b3 100644 --- a/src/Components/TextInput/index.tsx +++ b/src/Components/TextInput/index.tsx @@ -9,12 +9,14 @@ const TextInput = ({ disabled, onBlur, name, + type }: PropsInputText) => { return (
(null); const ProjectOneProvider = ({ children }: { children: React.ReactNode }) => { - const { isLoading, project, updateProject } = ProjectOneState(); + const { isLoading, project, updateProject, rulerProject } = ProjectOneState(); return ( - + {children} ); diff --git a/src/Context/Project/ProjectOne/state.tsx b/src/Context/Project/ProjectOne/state.tsx index 0de5187..a43deb9 100644 --- a/src/Context/Project/ProjectOne/state.tsx +++ b/src/Context/Project/ProjectOne/state.tsx @@ -8,7 +8,8 @@ export const ProjectOneState = () => { const { id } = useParams(); const { data, isLoading } = useFetchRequestProjectOne(parseInt(id!)); const [project, setProject] = useState(); - const { requestUpdateprojectMutation } = ProjectController(); + const { requestUpdateprojectMutation, requestRulerprojectMutation } = + ProjectController(); useEffect(() => { if (data) { @@ -17,13 +18,20 @@ export const ProjectOneState = () => { }, [data]); const updateProject = (data: UpdateProject, id: number) => { - console.log(id) requestUpdateprojectMutation.mutate({ data: data, id: id }); }; + const rulerProject = (file: File, id: number) => { + const formData = new FormData(); + + formData.append("file", file); + requestRulerprojectMutation.mutate({ data: formData, id: id }); + }; + return { project, isLoading, updateProject, + rulerProject }; }; diff --git a/src/Context/Project/ProjectOne/type.tsx b/src/Context/Project/ProjectOne/type.tsx index 2a61e6e..5919ec2 100644 --- a/src/Context/Project/ProjectOne/type.tsx +++ b/src/Context/Project/ProjectOne/type.tsx @@ -4,6 +4,7 @@ export interface ProjectOneTypes { project: ApiProject | undefined; isLoading: boolean; updateProject: (data: UpdateProject, id: number) => void; + rulerProject: (file: File, id: number) => void } export interface ApiProject { diff --git a/src/Pages/Projects/ProjectOne/index.tsx b/src/Pages/Projects/ProjectOne/index.tsx index 772aa30..9bd7a14 100644 --- a/src/Pages/Projects/ProjectOne/index.tsx +++ b/src/Pages/Projects/ProjectOne/index.tsx @@ -19,6 +19,8 @@ import { ROLE } from "../../../Controller/controllerGlobal"; import { Column, Container, Padding, Row } from "../../../Styles/styles"; import { PropsAplicationContext } from "../../../Types/types"; +import img from "../../../Assets/images/regua.png" + const ProjectOne = () => { return ( @@ -42,6 +44,7 @@ const ProjectOnePage = () => { const initialValues = { name: props.project?.project.name, approval_percentage: props.project?.project?.approval_percentage, + file: undefined, }; if (props.isLoading) return ; @@ -64,10 +67,12 @@ const ProjectOnePage = () => { }, parseInt(id!) ); + console.log(values.file) + if (values.file) props.rulerProject(values.file, parseInt(id!)); setEdit(!edit); }} > - {({ values, errors, handleChange, touched }) => { + {({ values, errors, handleChange, touched, setFieldValue }) => { return (
@@ -116,6 +121,22 @@ const ProjectOnePage = () => {
) : null} +
+ + + setFieldValue("file", e.target?.files![0])} + name="file" + placeholder="Régua do projeto*" + type="file" + /> + + {errors.file && touched.file ? ( +
+ {errors.file} +
+ ) : null} +
@@ -141,6 +162,10 @@ const ProjectOnePage = () => { )} + +

Régua do projeto

+ + )} @@ -170,18 +195,20 @@ const ProjectOnePage = () => { {props?.project?.project.classrooms?.length! > 0 ? (
- {props.project?.project.classrooms?.map((item: any, index: number) => { - return ( -
- -
- ); - })} + {props.project?.project.classrooms?.map( + (item: any, index: number) => { + return ( +
+ +
+ ); + } + )}
) : ( diff --git a/src/Services/Project/controller.tsx b/src/Services/Project/controller.tsx index a472fd6..898186b 100644 --- a/src/Services/Project/controller.tsx +++ b/src/Services/Project/controller.tsx @@ -2,55 +2,85 @@ import { useMutation } from "react-query"; import { useNavigate } from "react-router-dom"; import Swal from "sweetalert2"; import styles from "../../Styles"; -import { CreateProject, UpdateProject } from "../../Context/Project/CreateList/type"; -import { requestCreateProject, requestUpdateProject } from "./request"; +import { + CreateProject, + UpdateProject, +} from "../../Context/Project/CreateList/type"; +import { + requestCreateProject, + requestRemoveRulerProject, + requestRulerProject, + requestUpdateProject, +} from "./request"; import queryClient from "../reactquery"; export const ProjectController = () => { + const history = useNavigate(); - const history = useNavigate() - - const requestCreateprojectMutation = useMutation( - (data: CreateProject) => requestCreateProject(data), - { - onError: (error: any) => { - alert(error?.response.data.message); - }, - onSuccess: (data) => { - Swal.fire({ - icon: 'success', - title: 'Projeto criado com sucesso!', - confirmButtonColor: styles.colors.colorsBaseProductNormal, - }).then((result) => { - if (result.isConfirmed) { - history('/projetos') - } - }) - }, - } - ); + const requestCreateprojectMutation = useMutation( + (data: CreateProject) => requestCreateProject(data), + { + onError: (error: any) => { + alert(error?.response.data.message); + }, + onSuccess: (data) => { + Swal.fire({ + icon: "success", + title: "Projeto criado com sucesso!", + confirmButtonColor: styles.colors.colorsBaseProductNormal, + }).then((result) => { + if (result.isConfirmed) { + history("/projetos"); + } + }); + }, + } + ); - const requestUpdateprojectMutation = useMutation( - ({data, id}:{data: UpdateProject, id: number}) => requestUpdateProject(data, id), - { - onError: (error: any) => { - alert(error?.response.data.message); - }, - onSuccess: (data) => { - Swal.fire({ - icon: 'success', - title: 'Projeto editado com sucesso!', - confirmButtonColor: styles.colors.colorsBaseProductNormal, - }).then((result) => { - if (result.isConfirmed) { - queryClient.refetchQueries("requestProjectOne"); - } - }) - }, - } - ); + const requestUpdateprojectMutation = useMutation( + ({ data, id }: { data: UpdateProject; id: number }) => + requestUpdateProject(data, id), + { + onError: (error: any) => { + alert(error?.response.data.message); + }, + onSuccess: (data) => { + Swal.fire({ + icon: "success", + title: "Projeto editado com sucesso!", + confirmButtonColor: styles.colors.colorsBaseProductNormal, + }).then((result) => { + if (result.isConfirmed) { + queryClient.refetchQueries("requestProjectOne"); + } + }); + }, + } + ); - return{ - requestCreateprojectMutation, requestUpdateprojectMutation + const requestRulerprojectMutation = useMutation( + ({ data, id }: { data: FormData; id: number }) => + requestRulerProject(data, id), + { + onError: (error: any) => { + alert(error?.response.data.message); + }, + onSuccess: (data) => {}, + } + ); + const requestRemoveRulerprojectMutation = useMutation( + ({ id }: { id: number }) => requestRemoveRulerProject(id), + { + onError: (error: any) => { + alert(error?.response.data.message); + }, + onSuccess: (data) => {}, } -} \ No newline at end of file + ); + return { + requestCreateprojectMutation, + requestUpdateprojectMutation, + requestRulerprojectMutation, + requestRemoveRulerprojectMutation, + }; +}; diff --git a/src/Services/Project/request.tsx b/src/Services/Project/request.tsx index 6627a86..9bb3093 100644 --- a/src/Services/Project/request.tsx +++ b/src/Services/Project/request.tsx @@ -1,4 +1,7 @@ -import { CreateProject, UpdateProject } from "../../Context/Project/CreateList/type"; +import { + CreateProject, + UpdateProject, +} from "../../Context/Project/CreateList/type"; import http from "../axios"; import { GetIdTs, GetIdUser, getYear, logout } from "../localstorage"; @@ -18,7 +21,35 @@ export const requestCreateProject = (data: CreateProject) => { export const requestUpdateProject = (data: UpdateProject, id: number) => { return http - .put("/project/"+ id, data) + .put("/project/" + id, data) + .then((response) => response.data) + .catch((err) => { + if (err.response.status === 401) { + logout(); + window.location.reload(); + } + alert(err.response.message); + throw err; + }); +}; + +export const requestRulerProject = (file: FormData, id: number) => { + return http + .put("/archive-project-bff?projectId="+id, file) + .then((response) => response.data) + .catch((err) => { + if (err.response.status === 401) { + logout(); + window.location.reload(); + } + alert(err.response.message); + throw err; + }); +}; + +export const requestRemoveRulerProject = (id: number) => { + return http + .put("/archive-project-bff/remove?projectId="+id) .then((response) => response.data) .catch((err) => { if (err.response.status === 401) { @@ -64,7 +95,9 @@ export const requestTsList = async (id: number | undefined) => { export const requestProjectOne = async (id: number) => { return await http - .get("/project-bff/one", { params: { idProject: id ?? GetIdTs(), year: getYear() } }) + .get("/project-bff/one", { + params: { idProject: id ?? GetIdTs(), year: getYear() }, + }) .then((response) => response.data) .catch((err) => { if (err.response.status === 401) { diff --git a/src/Types/types.tsx b/src/Types/types.tsx index 79a414b..ada97d7 100644 --- a/src/Types/types.tsx +++ b/src/Types/types.tsx @@ -3,7 +3,7 @@ import { InputMaskChangeEvent } from "primereact/inputmask"; import { InputNumberValueChangeEvent } from "primereact/inputnumber"; import { RadioButtonChangeEvent } from "primereact/radiobutton"; import { SelectItemOptionsType } from "primereact/selectitem"; -import { ChangeEventHandler, Dispatch, FocusEventHandler, SetStateAction } from "react"; +import { ChangeEventHandler, Dispatch, FocusEventHandler, HTMLInputTypeAttribute, SetStateAction } from "react"; import { User } from "../Context/Users/type"; export interface PropsInputText { @@ -13,6 +13,7 @@ export interface PropsInputText { disabled?: boolean | undefined, onBlur?: FocusEventHandler | undefined, name?: string + type?: HTMLInputTypeAttribute | undefined } export interface PropsInputMask { From eaf0c82597d44e5fcec03207faab237e8111f580 Mon Sep 17 00:00:00 2001 From: TI JONNY Date: Tue, 11 Jun 2024 17:59:09 -0300 Subject: [PATCH 2/5] =?UTF-8?q?r=C3=A9gua=20projeto=20pdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 43 +++++++++++++++++++ package.json | 1 + src/Context/Project/ProjectOne/type.tsx | 1 + src/Controller/controllerGlobal.tsx | 16 +++++++ .../Meeting/Beneficiarios/index.tsx | 17 ++++++++ .../ClassroomOne/Report/Pdf/index.tsx | 41 +++++++++++++----- src/Pages/Projects/ProjectOne/index.tsx | 27 ++++++------ src/Services/Classroom/type.tsx | 1 + 8 files changed, 123 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3a8bdf3..b902667 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@types/react": "^18.2.47", "@types/react-dom": "^18.2.18", "axios": "^1.6.7", + "buffer": "^6.0.3", "cross-env": "^7.0.3", "file-saver": "^2.0.5", "formik": "^2.4.5", @@ -5892,6 +5893,29 @@ "node": ">= 0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -9586,6 +9610,25 @@ "node": ">=4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", diff --git a/package.json b/package.json index 65e701c..eef5d73 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@types/react": "^18.2.47", "@types/react-dom": "^18.2.18", "axios": "^1.6.7", + "buffer": "^6.0.3", "cross-env": "^7.0.3", "file-saver": "^2.0.5", "formik": "^2.4.5", diff --git a/src/Context/Project/ProjectOne/type.tsx b/src/Context/Project/ProjectOne/type.tsx index 5919ec2..df9c447 100644 --- a/src/Context/Project/ProjectOne/type.tsx +++ b/src/Context/Project/ProjectOne/type.tsx @@ -18,6 +18,7 @@ export interface Project { active: boolean approval_percentage: number avartar_url: any + ruler_url: string social_technology_id: number createdAt: string updatedAt: string diff --git a/src/Controller/controllerGlobal.tsx b/src/Controller/controllerGlobal.tsx index 8f322cc..77a91f2 100644 --- a/src/Controller/controllerGlobal.tsx +++ b/src/Controller/controllerGlobal.tsx @@ -1,3 +1,7 @@ +import axios from "axios"; +import { Buffer } from 'buffer'; + + export const gerarIdAleatorio = (tamanho: number) => { const caracteres = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -98,4 +102,16 @@ export const loadImageFileAsBase64 = (imagePath: string): Promise => { }) .catch(reject); }); +}; + + +export const convertImageUrlToBase64 = async (imageUrl: string): Promise => { + try { + const response = await axios.get(imageUrl, { responseType: 'arraybuffer' }); + const base64 = Buffer.from(response.data, 'binary').toString('base64'); + const mimeType = response.headers['content-type']; + return `data:${mimeType};base64,${base64}`; + } catch (error: any) { + throw new Error(`Failed to convert image to Base64: ${error.message}`); + } }; \ No newline at end of file diff --git a/src/Pages/Classroom/ClassroomOne/MeetingList/Meeting/Beneficiarios/index.tsx b/src/Pages/Classroom/ClassroomOne/MeetingList/Meeting/Beneficiarios/index.tsx index 37744f9..f734d32 100644 --- a/src/Pages/Classroom/ClassroomOne/MeetingList/Meeting/Beneficiarios/index.tsx +++ b/src/Pages/Classroom/ClassroomOne/MeetingList/Meeting/Beneficiarios/index.tsx @@ -19,6 +19,7 @@ import { } from "../../../../../../Context/Classroom/Meeting/MeetingListRegistration/type"; import { Status, + convertImageUrlToBase64, loadImageFileAsBase64, } from "../../../../../../Controller/controllerGlobal"; import styles from "../../../../../../Styles"; @@ -39,6 +40,8 @@ const Beneficiarios = () => { const [logoBase64, setLogoBase64] = useState(null); const [logoBaseLeft64, setLogoBaseLeft64] = useState(null); + const [logoBaseRegua64, setLogoBaseRegua64] = useState(null); + const FilterRegistration = (fouls: any) => { const array = []; @@ -78,6 +81,20 @@ const Beneficiarios = () => { loadLogo(); }, []); + + useEffect(() => { + const loadLogo = async () => { + try { + const base64 = ""; + setLogoBaseRegua64(base64); + } catch (error) { + console.error("Error loading logo image:", error); + } + }; + + loadLogo(); + }, []); + const FilterId = (fouls: any) => { const array = []; for (const foul of fouls) { diff --git a/src/Pages/Classroom/ClassroomOne/Report/Pdf/index.tsx b/src/Pages/Classroom/ClassroomOne/Report/Pdf/index.tsx index bd44226..d3115d2 100644 --- a/src/Pages/Classroom/ClassroomOne/Report/Pdf/index.tsx +++ b/src/Pages/Classroom/ClassroomOne/Report/Pdf/index.tsx @@ -12,7 +12,7 @@ import imgLateral from "../../../../../Assets/images/logoleftpdf.png"; import pdfMake from "pdfmake/build/pdfmake"; import pdfFonts from "pdfmake/build/vfs_fonts"; import { TDocumentDefinitions } from "pdfmake/interfaces"; -import { loadImageFileAsBase64 } from "../../../../../Controller/controllerGlobal"; +import { convertImageUrlToBase64, loadImageFileAsBase64 } from "../../../../../Controller/controllerGlobal"; pdfMake.vfs = pdfFonts.pdfMake.vfs; @@ -21,7 +21,7 @@ export const ReportClassroom = () => { const [logoBase64, setLogoBase64] = useState(null); const [logoBaseLeft64, setLogoBaseLeft64] = useState(null); - // const [logoBaseRegua64, setLogoBaseRegua64] = useState(null); + const [logoBaseRegua64, setLogoBaseRegua64] = useState(null); const [report, setReport] = useState(); @@ -59,6 +59,24 @@ export const ReportClassroom = () => { loadLogo(); }, []); + useEffect(() => { + const loadLogo = async () => { + try { + if (report?.project.ruler_url) { + const base64 = await convertImageUrlToBase64(report?.project.ruler_url); + + setLogoBaseRegua64(base64); + } + + } catch (error) { + console.error("Error loading logo image:", error); + } + }; + + loadLogo(); + }, [report]); + + // useEffect(() => { // const loadLogo = async () => { // try { @@ -232,7 +250,7 @@ export const ReportClassroom = () => { const generatePDF = () => { const pageSize = 10; // Define the number of rows per page - + // Function to generate the table body for a specific subset of registrations const createTableBody = (registrationsSubset: any, startIndex: number) => { // Create the header row @@ -243,7 +261,7 @@ export const ReportClassroom = () => { "FREQUÊNCIA", "STATUS", ]; - + // Create the body rows const bodyRows = registrationsSubset.map((item: any, index: number) => { return [ @@ -254,10 +272,10 @@ export const ReportClassroom = () => { parseInt(bodyTotal(item).percentage) > report?.project?.approval_percentage! ? "Aprovado" : "Reprovado", ]; }); - + return [headerRow, ...bodyRows]; }; - + const docDefinition: TDocumentDefinitions = { pageOrientation: "landscape", content: [ @@ -351,11 +369,12 @@ export const ReportClassroom = () => { }, ]; }, - footer: (currentPage: number, pageCount: number) => { + footer: (currentPage, pageCount) => { return { - text: `${currentPage} de ${pageCount}`, + image: logoBaseRegua64 || '', alignment: "center", - margin: [0, 0, 20, 0], + margin: [0, 0, 20, 20], // Ensure there is enough bottom margin to prevent cutoff + fit: [500, 500] // Adjust the size to fit within the footer area }; }, pageMargins: [40, 60, 40, 60], @@ -367,10 +386,10 @@ export const ReportClassroom = () => { }; }, }; - + pdfMake.createPdf(docDefinition).open(); }; - + const bodyTotal = (rowData: RegisterClassroom) => { var count = 0; diff --git a/src/Pages/Projects/ProjectOne/index.tsx b/src/Pages/Projects/ProjectOne/index.tsx index 9bd7a14..310002d 100644 --- a/src/Pages/Projects/ProjectOne/index.tsx +++ b/src/Pages/Projects/ProjectOne/index.tsx @@ -19,7 +19,6 @@ import { ROLE } from "../../../Controller/controllerGlobal"; import { Column, Container, Padding, Row } from "../../../Styles/styles"; import { PropsAplicationContext } from "../../../Types/types"; -import img from "../../../Assets/images/regua.png" const ProjectOne = () => { return ( @@ -115,7 +114,7 @@ const ProjectOnePage = () => { /> {errors.approval_percentage && - touched.approval_percentage ? ( + touched.approval_percentage ? (
{errors.approval_percentage}
@@ -153,19 +152,21 @@ const ProjectOnePage = () => { {propsAplication.user?.role === (ROLE.ADMIN || ROLE.COORDINATORS) && ( -