From 540e5e29972396003bc75b29e51e761c754be120 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Fri, 3 Nov 2023 22:14:42 +0530 Subject: [PATCH 01/10] add homepage param in microfrontend --- microfrontends/SignDocuments/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/microfrontends/SignDocuments/package.json b/microfrontends/SignDocuments/package.json index 3c8e28903..a5b1aa0fa 100644 --- a/microfrontends/SignDocuments/package.json +++ b/microfrontends/SignDocuments/package.json @@ -2,6 +2,7 @@ "name": "signmicroapp", "version": "0.1.0", "private": true, + "homepage": "mfbuild", "dependencies": { "@radix-ui/themes": "^1.1.2", "@react-pdf/renderer": "^3.1.9", From 71c5f5923ffceba852ab2a5ae619de4f52571ea9 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Fri, 3 Nov 2023 22:38:41 +0530 Subject: [PATCH 02/10] change homepage param in microfrontend --- microfrontends/SignDocuments/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microfrontends/SignDocuments/package.json b/microfrontends/SignDocuments/package.json index a5b1aa0fa..85f724853 100644 --- a/microfrontends/SignDocuments/package.json +++ b/microfrontends/SignDocuments/package.json @@ -2,7 +2,7 @@ "name": "signmicroapp", "version": "0.1.0", "private": true, - "homepage": "mfbuild", + "homepage": "/mfbuild", "dependencies": { "@radix-ui/themes": "^1.1.2", "@react-pdf/renderer": "^3.1.9", From 184b401582914668d1d03440f2f5752485b9ae18 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Fri, 3 Nov 2023 22:52:09 +0530 Subject: [PATCH 03/10] change homepage url of microfrontend --- apps/OpenSignServer/index.js | 1 + microfrontends/SignDocuments/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index f0d494c44..814d9cf58 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -63,6 +63,7 @@ export const config = { publicServerURL: process.env.SERVER_URL || 'http://localhost:8080/app', // Your apps name. This will appear in the subject and body of the emails that are sent. appName: 'Open Sign', + allowClientClassCreation: false, emailAdapter: process.env.MAILGUN_API_KEY ? { module: 'parse-server-api-mail-adapter', diff --git a/microfrontends/SignDocuments/package.json b/microfrontends/SignDocuments/package.json index 85f724853..90961884f 100644 --- a/microfrontends/SignDocuments/package.json +++ b/microfrontends/SignDocuments/package.json @@ -2,7 +2,7 @@ "name": "signmicroapp", "version": "0.1.0", "private": true, - "homepage": "/mfbuild", + "homepage": "./mfbuild", "dependencies": { "@radix-ui/themes": "^1.1.2", "@react-pdf/renderer": "^3.1.9", From e8350c331d0a919ca19df01a85c100689f30462d Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Fri, 3 Nov 2023 23:27:44 +0530 Subject: [PATCH 04/10] add Public_url in build scripts of microfrontend --- microfrontends/SignDocuments/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microfrontends/SignDocuments/package.json b/microfrontends/SignDocuments/package.json index 90961884f..97970e988 100644 --- a/microfrontends/SignDocuments/package.json +++ b/microfrontends/SignDocuments/package.json @@ -36,7 +36,7 @@ }, "scripts": { "start": "mf-cra start", - "build": "CI=false && BUILD_PATH='./mfbuild' mf-cra build && mv mfbuild ../../apps/OpenSign/public", + "build": "CI=false && PUBLIC_URL=./mfbuild BUILD_PATH='./mfbuild' mf-cra build && mv mfbuild ../../apps/OpenSign/public", "test": "react-scripts test", "eject": "react-scripts eject" }, From a7d0b10c95a0874d16c1a522faecd4d626e17e23 Mon Sep 17 00:00:00 2001 From: Andrew <148278535+andrew-opensignlabs@users.noreply.github.com> Date: Sun, 5 Nov 2023 03:22:22 +0530 Subject: [PATCH 05/10] docs: Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c2a7d86ea..d6e5a235b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
-The free open source alternative to DocuSign | PandaDoc | SignNow | Adobe Sign | Smartwaiver | SignRequest | HelloSign | Zoho sign. +The free and open source alternative to DocuSign [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/opensignlabs/opensign.svg)](http://isitmaintained.com/project/opensignlabs/opensign "Average time to resolve an issue") [![All Contributors](https://img.shields.io/github/all-contributors/opensignlabs/opensign?color=ee8449&style=flat-square)](#contributors) @@ -23,7 +23,7 @@ The free open source alternative to DocuSign | PandaDoc | SignNow | Adobe Sign | LinkedIn -## An Open-Source Alternative to DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, and HelloSign +## An open-source document e-signing solution ---
From 408d84daedccb37c352f174656254fff64fe5dc2 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Sun, 5 Nov 2023 11:24:06 +0530 Subject: [PATCH 06/10] fix: file upload issue --- .../src/components/fields/FileUpload.js | 72 ++++++++----------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/apps/OpenSign/src/components/fields/FileUpload.js b/apps/OpenSign/src/components/fields/FileUpload.js index 2b0840276..83a37eed7 100644 --- a/apps/OpenSign/src/components/fields/FileUpload.js +++ b/apps/OpenSign/src/components/fields/FileUpload.js @@ -1,8 +1,7 @@ import React, { useState, useEffect } from "react"; import { SaveFileSize } from "../../constant/saveFileSize"; -// import Parse from "parse"; -// import sanitizeFileName from "../../primitives/sanitizeFileName"; -import axios from "axios"; +import Parse from "parse"; +import sanitizeFileName from "../../primitives/sanitizeFileName"; const FileUpload = (props) => { const [parseBaseUrl] = useState(localStorage.getItem("baseUrl")); @@ -69,53 +68,44 @@ const FileUpload = (props) => { }; const handleFileUpload = async (file) => { + Parse.serverURL = parseBaseUrl; + Parse.initialize(parseAppId); setfileload(true); - const file_url = parseBaseUrl.slice(0, -4); - const url = `${file_url}file_upload`; - const formData = new FormData(); - formData.append("file", file); - const config = { - headers: { - "content-type": "multipart/form-data", - "X-Parse-Application-Id": parseAppId - }, - onUploadProgress: function (progressEvent) { - var percentCompleted = Math.round( - (progressEvent.loaded * 100) / progressEvent.total - ); - setpercentage(percentCompleted); - } - }; + const size = file.size; + const fileName = file.name; + const name = sanitizeFileName(fileName); + const pdfFile = file; + const parseFile = new Parse.File(name, pdfFile); try { - await axios - .post(url, formData, config) - .then((res) => { - if (res.data.status === "Error") { - alert(res.data.message); + const response = await parseFile.save({ + progress: (progressValue, loaded, total, { type }) => { + if (type === "upload" && progressValue !== null) { + const percentCompleted = Math.round((loaded * 100) / total); + console.log("percentCompleted ", percentCompleted); + setpercentage(percentCompleted); } - setFileUpload(res.data.imageUrl); - props.onChange(res.data.imageUrl); - setfileload(false); - setpercentage(0); + } + }); - if (res.data.imageUrl) { - SaveFileSize(file.size, res.data.imageUrl); - return res.data.imageUrl; - } - }) - .catch((err) => { - alert(`${err.message}`); - setfileload(false); - setpercentage(0); - }); + setFileUpload(response.url()); + props.onChange(response.url()); + setfileload(false); + // The response object will contain information about the uploaded file + // console.log("File uploaded:", response); + + // You can access the URL of the uploaded file using response.url() + // console.log("File URL:", response.url()); + if (response.url()) { + SaveFileSize(size, response.url()); + return response.url(); + } } catch (error) { - alert(error.message); setfileload(false); setpercentage(0); + console.error("Error uploading file:", error); } }; - let fileView = props.formData && props.schema.uploadtype === "s3viajw" ? null : props.formData && @@ -124,7 +114,7 @@ const FileUpload = (props) => { Download From 2acf85185292012c21009dc104b13720b7932244 Mon Sep 17 00:00:00 2001 From: lsprr <16653744+lsprr@users.noreply.github.com> Date: Sun, 5 Nov 2023 15:55:38 -0500 Subject: [PATCH 07/10] Refactor: Split Sidebar into Menu and SocialMedia components for modularity - Created separate Menu and SocialMedia components from the Sidebar. - Enhanced accessibility features in Menu.js, Sidebar.js, SubMenu.js, and SocialMedia.js. - Adjusted NavLink components to improve keyboard navigation accessibility. - Cleaned up and isolated component responsibilities for clarity and maintenance. --- apps/OpenSign/src/components/sidebar/Menu.js | 21 ++++ .../src/components/sidebar/Sidebar.js | 106 ++++++------------ .../src/components/sidebar/SocialMedia.js | 27 +++++ .../src/components/sidebar/SubMenu.js | 44 ++++++-- 4 files changed, 115 insertions(+), 83 deletions(-) create mode 100644 apps/OpenSign/src/components/sidebar/Menu.js create mode 100644 apps/OpenSign/src/components/sidebar/SocialMedia.js diff --git a/apps/OpenSign/src/components/sidebar/Menu.js b/apps/OpenSign/src/components/sidebar/Menu.js new file mode 100644 index 000000000..459451b23 --- /dev/null +++ b/apps/OpenSign/src/components/sidebar/Menu.js @@ -0,0 +1,21 @@ +import React from "react"; +import { NavLink } from "react-router-dom"; + +const Menu = ({ item, isOpen, closeSidebar }) => { + return ( +
  • + + + {item.title} + +
  • + ) +} + +export default Menu; \ No newline at end of file diff --git a/apps/OpenSign/src/components/sidebar/Sidebar.js b/apps/OpenSign/src/components/sidebar/Sidebar.js index 1d0ff7ed8..85ac268ec 100644 --- a/apps/OpenSign/src/components/sidebar/Sidebar.js +++ b/apps/OpenSign/src/components/sidebar/Sidebar.js @@ -1,115 +1,77 @@ import React, { useState, useEffect } from "react"; -import { Link, NavLink } from "react-router-dom"; + +import Menu from './Menu'; import Submenu from "./SubMenu"; -import dp from "../../assets/images/dp.png"; +import SocialMedia from './SocialMedia'; + import Parse from "parse"; +import dp from "../../assets/images/dp.png"; + const Sidebar = ({ isOpen, closeSidebar }) => { + const [menuList, setmenuList] = useState([]); let username = localStorage.getItem("username"); const image = localStorage.getItem("profileImg") || dp; const tenantname = localStorage.getItem("TenantName"); - const [menuList, setmenuList] = useState([]); useEffect(() => { if (localStorage.getItem("accesstoken")) { menuItem(); } }, []); + const menuItem = async () => { const parseBaseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); + try { Parse.serverURL = parseBaseUrl; Parse.initialize(parseAppId); var sideMenu = Parse.Object.extend("w_menu"); var query = new Parse.Query(sideMenu); query.equalTo("objectId", localStorage.getItem("defaultmenuid")); - + const results = await query.first(); const resultjson = results.toJSON(); - // console.log("resultjson ", resultjson); let result = resultjson; setmenuList(result.menuItems); } catch (e) { console.error("Problem", e); } + }; return ( -
    - img + Profile
    -

    {username && username}

    -

    - {tenantname && tenantname} +

    {username}

    +

    + {tenantname}

    -
      - {menuList.length > 0 && ( - <> - {menuList.map((item) => - !item.children ? ( -
    • closeSidebar()}> - - - - {item.title} - - -
    • - ) : ( - - {item.children.map((item) => ( -
    • closeSidebar()} className="pl-6 md:pl-8 hover:bg-[#eef1f5] cursor-pointer"> - - - - {item.title} - - -
    • - ))} -
      - ) - )} - - )} -
    -
    - - - - - - - - - - - - -
    -
    + +
    + +
    + ); }; diff --git a/apps/OpenSign/src/components/sidebar/SocialMedia.js b/apps/OpenSign/src/components/sidebar/SocialMedia.js new file mode 100644 index 000000000..813c47e15 --- /dev/null +++ b/apps/OpenSign/src/components/sidebar/SocialMedia.js @@ -0,0 +1,27 @@ +import React from "react"; +import { NavLink } from "react-router-dom"; + +const SocialMedia = () => { + return ( + + + + OpenSign's Github + + + + OpenSign's LinkedIn + + + + OpenSign's Twitter + + + + OpenSign's Discord + + + ) +} + +export default SocialMedia; \ No newline at end of file diff --git a/apps/OpenSign/src/components/sidebar/SubMenu.js b/apps/OpenSign/src/components/sidebar/SubMenu.js index 6b3d59582..3b3adac42 100644 --- a/apps/OpenSign/src/components/sidebar/SubMenu.js +++ b/apps/OpenSign/src/components/sidebar/SubMenu.js @@ -1,30 +1,52 @@ import React, { useState } from "react"; +import { NavLink } from "react-router-dom"; -const Submenu = ({ icon, title, children }) => { - const [isOpen, setIsOpen] = useState(false); +const Submenu = ({ item, closeSidebar }) => { + const [submenuOpen, setSubmenuOpen] = useState(false); + const { title, icon, children } = item; const toggleSubmenu = () => { - setIsOpen(!isOpen); + setSubmenuOpen(!submenuOpen); }; return ( -
    +
  • - {isOpen &&
      {children}
    } -
  • + {submenuOpen && ( + + )} + ); }; From e330285e240a3a25b032a28db156b1274d31669d Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Mon, 6 Nov 2023 13:17:01 +0530 Subject: [PATCH 08/10] add pagination in reports, fix navigation in signup --- .../src/components/fields/FileUpload.js | 2 +- .../src/primitives/GetReportDisplay.js | 110 ++++++++++++++---- apps/OpenSign/src/routes/Report.js | 65 +++++++++-- apps/OpenSign/src/routes/Signup.js | 2 +- 4 files changed, 149 insertions(+), 30 deletions(-) diff --git a/apps/OpenSign/src/components/fields/FileUpload.js b/apps/OpenSign/src/components/fields/FileUpload.js index 83a37eed7..226640d44 100644 --- a/apps/OpenSign/src/components/fields/FileUpload.js +++ b/apps/OpenSign/src/components/fields/FileUpload.js @@ -82,7 +82,7 @@ const FileUpload = (props) => { progress: (progressValue, loaded, total, { type }) => { if (type === "upload" && progressValue !== null) { const percentCompleted = Math.round((loaded * 100) / total); - console.log("percentCompleted ", percentCompleted); + // console.log("percentCompleted ", percentCompleted); setpercentage(percentCompleted); } } diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 22cea983d..0bf111c1a 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -1,9 +1,37 @@ -import React from "react"; +import React, { useState, useEffect, useMemo } from "react"; import pad from "../assets/images/pad.svg"; import { useNavigate } from "react-router-dom"; -const ReportTable = ({ ReportName, List, actions }) => { +const ReportTable = ({ + ReportName, + List, + actions, + setIsNextRecord, + isMoreDocs +}) => { const navigate = useNavigate(); + const [currentPage, setCurrentPage] = useState(1); + const docPerPage = 10; + // const pageNumbers = []; + + // For loop is used to calculate page numbers visible below table + // Initialize pageNumbers using useMemo to avoid unnecessary re-creation + const pageNumbers = useMemo(() => { + const calculatedPageNumbers = []; + for (let i = 1; i <= Math.ceil(List.length / docPerPage); i++) { + calculatedPageNumbers.push(i); + } + return calculatedPageNumbers; + }, [List]); + // below useEffect reset currenpage to 1 if user change route + useEffect(() => { + return () => setCurrentPage(1); + }, []); + + // `formatRow` is used to show data in poper manner like + // if data is of array type then it will join array items with "," + // if data is of object type then it Name values will be show in row + // if no data available it will show hyphen "-" const formatRow = (row) => { if (Array.isArray(row)) { let updateArr = row.map((x) => x.Name); @@ -14,24 +42,17 @@ const ReportTable = ({ ReportName, List, actions }) => { return "-"; } }; + // below useEffect is used to render next record if IsMoreDoc is true + // second last value of pageNumber array is same as currentPage + useEffect(() => { + if (isMoreDocs && pageNumbers[pageNumbers.length - 1] === currentPage) { + setIsNextRecord(true); + } + }, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]); + + // `handlemicroapp` is used to open microapp const handlemicroapp = (item, url) => { localStorage.removeItem("rowlevel"); - // const params = new URLSearchParams(url); - // let arr = []; - // for (let [key, value] of params.entries()) { - // if (key === "remoteUrl") { - // arr.push( - // `${key}=${window.btoa( - // window.location.origin + "/mfbuild/remoteEntry.js" - // )}` - // ); - // } else { - // arr.push(`${key}=${value}`); - // } - // } - // const remoteUrl = arr.join("&"); - // console.log("arr", remoteUrl); - navigate("/rpmf/" + url); localStorage.setItem("rowlevel", JSON.stringify(item)); // localStorage.setItem("rowlevelMicro"); @@ -39,6 +60,17 @@ const ReportTable = ({ ReportName, List, actions }) => { const handlebtn = () => { console.log("clicked"); }; + + // Get current list + const indexOfLastDoc = currentPage * docPerPage; + const indexOfFirstDoc = indexOfLastDoc - docPerPage; + // `currentLists` is total record render on current page + const currentLists = List.slice(indexOfFirstDoc, indexOfLastDoc); + + // Change page + const paginateFront = () => setCurrentPage(currentPage + 1); + const paginateBack = () => setCurrentPage(currentPage - 1); + return (

    {ReportName}

    @@ -60,7 +92,7 @@ const ReportTable = ({ ReportName, List, actions }) => { {List?.length > 0 ? ( <> - {List.map((item, index) => ( + {currentLists.map((item, index) => ( {index + 1} {item?.Name} @@ -121,8 +153,46 @@ const ReportTable = ({ ReportName, List, actions }) => { )} +
    + {List.length > 10 && ( + <> + {currentPage > 1 && ( + + )} + + )} + {pageNumbers.map((x) => ( + + ))} + {isMoreDocs && ( + + )} + {List.length > 10 && ( + <> + {pageNumbers.includes(currentPage + 1) && ( + + )} + + )} +
    {List?.length <= 0 && ( -
    +
    img
    diff --git a/apps/OpenSign/src/routes/Report.js b/apps/OpenSign/src/routes/Report.js index d82dc3630..efec224ac 100644 --- a/apps/OpenSign/src/routes/Report.js +++ b/apps/OpenSign/src/routes/Report.js @@ -12,14 +12,37 @@ const Report = () => { const [isLoader, setIsLoader] = useState(true); const [reportName, setReportName] = useState(""); const [actions, setActions] = useState([]); + const [isNextRecord, setIsNextRecord] = useState(false); + const [isMoreDocs, setIsMoreDocs] = useState(true); + const abortController = new AbortController(); + + // below useEffect is call when id param change useEffect(() => { setReportName(""); + setList([]); getReportData(); + + // Function returned from useEffect is called on unmount + return () => { + setIsLoader(true); + setList([]); + setIsNextRecord(false); + // Here it'll abort the fetch + abortController.abort(); + }; // eslint-disable-next-line }, [id]); - const getReportData = async () => { - setIsLoader(true); + // below useEffect call when isNextRecord state is true and fetch next record + useEffect(() => { + if (isNextRecord) { + getReportData(List.length, 200); + } + // eslint-disable-next-line + }, [isNextRecord]); + + const getReportData = async (skipUserRecord = 0, limit = 200) => { + // setIsLoader(true); const json = reportJson(id); if (json) { setActions(json.actions); @@ -39,8 +62,11 @@ const Report = () => { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; try { - const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&include=AuditTrail.UserPtr`; - const res = await axios.get(url, { headers: headers }); + const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skipUserRecord}&limit=${limit}&include=AuditTrail.UserPtr`; + const res = await axios.get(url, { + headers: headers, + signal: abortController.signal // is used to cancel fetch query + }); if (id === "4Hhwbp482K") { const listData = res.data?.results.filter( (x) => x.Signers.length > 0 @@ -66,14 +92,35 @@ const Report = () => { } } } - setList(arr); + if (arr.length === 10) { + setIsMoreDocs(true); + } else { + setIsMoreDocs(false); + } + setList((prevRecord) => + prevRecord.length > 0 ? [...prevRecord, ...arr] : arr + ); } else { - setList(res.data?.results); + if (res.data.results.length === 10) { + setIsMoreDocs(true); + console.log("here"); + } else { + setIsMoreDocs(false); + } + setIsNextRecord(false); + setList((prevRecord) => + prevRecord.length > 0 + ? [...prevRecord, ...res.data.results] + : res.data.results + ); } setIsLoader(false); } catch (err) { - console.log("err ", err); - setIsLoader(false); + const isCancel = axios.isCancel(err); + if (!isCancel) { + console.log("err ", err); + setIsLoader(false); + } } } else { setIsLoader(false); @@ -106,6 +153,8 @@ const Report = () => { ReportName={reportName} List={List} actions={actions} + setIsNextRecord={setIsNextRecord} + isMoreDocs={isMoreDocs} /> ) : (
    diff --git a/apps/OpenSign/src/routes/Signup.js b/apps/OpenSign/src/routes/Signup.js index ea3481cb7..6378e6785 100644 --- a/apps/OpenSign/src/routes/Signup.js +++ b/apps/OpenSign/src/routes/Signup.js @@ -251,7 +251,7 @@ const Signup = (props) => { element.pageType ); setState({ loading: false }); - navigate("/login"); + navigate("/"); } else { extendedInfo.forEach((x) => { if (x.TenantId) { From 015b752f5ba75a8d91cac18ce8bfded7b67eebb1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 08:26:12 +0000 Subject: [PATCH 09/10] docs: update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d6e5a235b..200487ed5 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ We would like to thank all our contributors and users for their support and feed Andrew
    Andrew

    💻 Rishab
    Rishab

    💻 Maurizio Pillitu
    Maurizio Pillitu

    🐛 + Luis Parra
    Luis Parra

    ️️️️♿️ From 6a98adebd23f82f438faae01615d890bd1bbc8e5 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 08:26:13 +0000 Subject: [PATCH 10/10] docs: update .all-contributorsrc --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f1976eec6..202612d32 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -169,6 +169,15 @@ "contributions": [ "bug" ] + }, + { + "login": "lsprr", + "name": "Luis Parra", + "avatar_url": "https://avatars.githubusercontent.com/u/16653744?v=4", + "profile": "https://luisparra.dev", + "contributions": [ + "a11y" + ] } ] }