diff --git a/inji-web/src/__tests__/components/DataShare/CustomTimes/CTContent.test.tsx b/inji-web/src/__tests__/components/DataShare/CustomTimes/CTContent.test.tsx new file mode 100644 index 00000000..69a1e79b --- /dev/null +++ b/inji-web/src/__tests__/components/DataShare/CustomTimes/CTContent.test.tsx @@ -0,0 +1,76 @@ +import {fireEvent, render, screen} from "@testing-library/react"; +import {CTContent} from "../../../../components/DataShare/CustomTimes/CTContent"; +import {useTranslation} from "react-i18next"; + +describe("Test the Layout of the Custom Expiry Content", () => { + + beforeEach(() => { + jest.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), + })); + } ) + + test("Test the presence of the Outer Container", ()=>{ + const expiryMockFn = jest.fn(); + render(); + const ctDocument = screen.getByTestId("CTContent-Outer-Container"); + expect(ctDocument).toBeInTheDocument(); + }) + test("Test the presence of the Time Range Container", ()=>{ + const expiryMockFn = jest.fn(); + render(); + const ctDocument = screen.getByTestId("CTContent-Times-Range-Container"); + expect(ctDocument).toBeInTheDocument(); + }) + test("Test the presence of the Time Range Increase Container", ()=>{ + const expiryMockFn = jest.fn(); + render(); + const ctDocument = screen.getByTestId("CTContent-Times-Range-Increase"); + expect(ctDocument).toBeInTheDocument(); + }) + test("Test the presence of the Time Range Decrease Container", ()=>{ + const expiryMockFn = jest.fn(); + render(); + const ctDocument = screen.getByTestId("CTContent-Times-Range-Decrease"); + expect(ctDocument).toBeInTheDocument(); + }) + test("Test the presence of the Time Range Value", ()=>{ + const expiryMockFn = jest.fn(); + render(); + const ctDocument = screen.getByTestId("CTContent-Times-Value"); + expect(ctDocument).toBeInTheDocument(); + }) + test("Test the presence of the Time Range Metrics", ()=>{ + const expiryMockFn = jest.fn(); + render(); + const ctDocument = screen.getByTestId("CTContent-Times-Metrics"); + expect(ctDocument).toBeInTheDocument(); + expect(ctDocument).toHaveTextContent("metrics"); + }) + + test("Test the Time Range Increase on Clicking the Increase Button", ()=>{ + const expiryTime=1; + const expiryMockFn = jest.fn(()=> expiryTime+1); + render(); + const increaseValueButton = screen.getByTestId("CTContent-Times-Range-Increase"); + const valueDiv = screen.getByTestId("CTContent-Times-Value"); + expect(valueDiv).toHaveValue(expiryTime + ""); + fireEvent.click(increaseValueButton); + expect(expiryMockFn).toHaveBeenCalledTimes(1); + expect(expiryMockFn).toHaveBeenCalledWith(expiryTime + 1); + }) + + test("Test the Time Range Decrease on Clicking the Decrease Button", ()=>{ + const expiryTime=3; + const expiryMockFn = jest.fn(()=> expiryTime+1); + render(); + const increaseValueButton = screen.getByTestId("CTContent-Times-Range-Decrease"); + const valueDiv = screen.getByTestId("CTContent-Times-Value"); + expect(valueDiv).toHaveValue(expiryTime + ""); + fireEvent.click(increaseValueButton); + expect(expiryMockFn).toHaveBeenCalledTimes(1); + expect(expiryMockFn).toHaveBeenCalledWith(expiryTime -1); + }) +}) diff --git a/inji-web/src/__tests__/components/DataShare/CustomTimes/CTHeader.test.tsx b/inji-web/src/__tests__/components/DataShare/CustomTimes/CTHeader.test.tsx new file mode 100644 index 00000000..517aa634 --- /dev/null +++ b/inji-web/src/__tests__/components/DataShare/CustomTimes/CTHeader.test.tsx @@ -0,0 +1,22 @@ +import {render, screen} from "@testing-library/react"; +import {CTHeader} from "../../../../components/DataShare/CustomTimes/CTHeader"; + +describe("Test the Layout of the Custom Expiry Header", () => { + + beforeEach( ()=> { + render(); + } ) + + test("Test the presence of the Outer Container", ()=>{ + const document = screen.getByTestId("CTHeader-Outer-Container"); + expect(document).toBeInTheDocument(); + }) + test("Test the presence of the Title Content", ()=>{ + const document = screen.getByTestId("CTHeader-Title-Content"); + expect(document).toBeInTheDocument(); + }) + test("Test to Have the content", ()=>{ + const document = screen.getByTestId("CTHeader-Title-Content"); + expect(document).toHaveTextContent("CTHeader"); + }) +}) diff --git a/inji-web/src/__tests__/components/DataShare/DSContent.test.tsx b/inji-web/src/__tests__/components/DataShare/DSContent.test.tsx new file mode 100644 index 00000000..8a7cbb97 --- /dev/null +++ b/inji-web/src/__tests__/components/DataShare/DSContent.test.tsx @@ -0,0 +1,58 @@ +import {fireEvent, render, screen} from "@testing-library/react"; +import {DSContent} from "../../../components/DataShare/DSContent"; +import {reduxStore} from "../../../redux/reduxStore"; +import {Provider} from "react-redux"; + +describe("Test the Layout of the Expiry Content", () => { + + const customMockFn = jest.fn(); + beforeEach(() => { + jest.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), + })); + render( + + ); + } ) + + test("Test the presence of the Outer Container", ()=>{ + const document = screen.getByTestId("DSContent-Outer-Container"); + expect(document).toBeInTheDocument(); + }) + test("Test the presence of the Outer Title", ()=>{ + const document = screen.getByTestId("DSContent-Outer-Title"); + expect(document).toBeInTheDocument(); + }) + test("Test the presence of the Issuer Logo", ()=>{ + const document = screen.getByTestId("DSContent-Issuer-Logo"); + expect(document).toBeInTheDocument(); + }) + test("Test the presence of the Issuer Name", ()=>{ + const document = screen.getByTestId("DSContent-Issuer-Name"); + expect(document).toBeInTheDocument(); + }) + test("Test the presence of the Consent Container", ()=>{ + const document = screen.getByTestId("DSContent-Consent-Container"); + expect(document).toBeInTheDocument(); + }) + test("Test the Validity Times Dropdown should not show custom as selected option at first", ()=>{ + const selectedDocument = screen.getByTestId("DSContent-Selected-Validity-Times"); + expect(selectedDocument).not.toHaveTextContent("Custom"); + expect(selectedDocument).toHaveTextContent("Once"); + const document = screen.queryByTestId("DSContent-Validity-Times-DropDown"); + expect(document).not.toBeInTheDocument(); + }) + test.skip("Test the Validity Times Dropdown should option when custom is selected", ()=>{ + let selectedDocument = screen.getByTestId("DSContent-Selected-Validity-Times"); + fireEvent.click(selectedDocument); + const customValidityDocument = screen.getByTestId("DSContent-Validity-Times-DropDown-Custom"); + fireEvent.click(customValidityDocument); + selectedDocument = screen.getByTestId("DSContent-Selected-Validity-Times"); + expect(selectedDocument).toHaveTextContent("Custom"); + const document = screen.getByTestId("DSContent-Validity-Times-DropDown"); + expect(document).toBeInTheDocument(); + expect(document.children.length).toBe(4); + }) +}) diff --git a/inji-web/src/__tests__/components/DataShare/DSDisclaimer.test.tsx b/inji-web/src/__tests__/components/DataShare/DSDisclaimer.test.tsx new file mode 100644 index 00000000..869d511c --- /dev/null +++ b/inji-web/src/__tests__/components/DataShare/DSDisclaimer.test.tsx @@ -0,0 +1,11 @@ +import {render, screen} from "@testing-library/react"; +import {DSDisclaimer} from "../../../components/DataShare/DSDisclaimer"; + +describe("Testing Layout of the Disclaimer", () => { + test("Test the presence of the Outer Container", ()=>{ + render(); + const document = screen.getByTestId("DSDisclaimer-Outer-Container"); + expect(document).toBeInTheDocument(); + expect(document).toHaveTextContent("Disclaimer"); + }) +}) diff --git a/inji-web/src/__tests__/components/DataShare/DSFooter.test.tsx b/inji-web/src/__tests__/components/DataShare/DSFooter.test.tsx new file mode 100644 index 00000000..e869d4eb --- /dev/null +++ b/inji-web/src/__tests__/components/DataShare/DSFooter.test.tsx @@ -0,0 +1,43 @@ +import {fireEvent, render, screen} from "@testing-library/react"; +import {DSFooter} from "../../../components/DataShare/DSFooter"; + +describe("Testing Layout of the Expiry Footer", () => { + + const successMockFn = jest.fn(); + const cancelMockFn = jest.fn(); + beforeEach(() => { + jest.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), + })); + render(); + } ) + + test("Test the presence of the Outer Container", ()=>{ + const document = screen.getByTestId("DSFooter-Outer-Container"); + expect(document).toBeInTheDocument(); + }) + test("Test the presence of the Cancel Button", ()=>{ + const document = screen.getByTestId("DSFooter-Cancel-Button"); + expect(document).toBeInTheDocument(); + }) + test("Test the presence of the Success Button", ()=>{ + const document = screen.getByTestId("DSFooter-Success-Button"); + expect(document).toBeInTheDocument(); + }) + + test("Test whether success button is invoked on clicking", ()=>{ + const successButton = screen.getByTestId("DSFooter-Success-Button"); + fireEvent.click(successButton); + expect(successMockFn).toBeCalled(); + expect(cancelMockFn).not.toBeCalled(); + }) + + test("Test whether cancel button is invoked on clicking", ()=>{ + const cancelButton = screen.getByTestId("DSFooter-Cancel-Button"); + fireEvent.click(cancelButton); + expect(successMockFn).not.toBeCalled(); + expect(cancelMockFn).toBeCalled(); + }) +}) diff --git a/inji-web/src/__tests__/components/DataShare/DSHeader.test.tsx b/inji-web/src/__tests__/components/DataShare/DSHeader.test.tsx new file mode 100644 index 00000000..0af46ca5 --- /dev/null +++ b/inji-web/src/__tests__/components/DataShare/DSHeader.test.tsx @@ -0,0 +1,22 @@ +import {render, screen} from "@testing-library/react"; +import {DSHeader} from "../../../components/DataShare/DSHeader"; + +describe("Testing Layout of the Expiry Header", () => { + beforeEach(()=>{ + render(); + }) + test("Test Presence of the Outer Container", ()=>{ + const document = screen.getByTestId("DSHeader-Outer-Container"); + expect(document).toBeInTheDocument(); + }) + test("Test Presence of the Header Title", ()=>{ + const document = screen.getByTestId("DSHeader-Header-Title"); + expect(document).toBeInTheDocument(); + expect(document).toHaveTextContent("title"); + }) + test("Test Presence of the Header SubTitle", ()=>{ + const document = screen.getByTestId("DSHeader-Header-SubTitle"); + expect(document).toBeInTheDocument(); + expect(document).toHaveTextContent("subTitle"); + }) +}) diff --git a/inji-web/src/components/Credentials/Crendential.tsx b/inji-web/src/components/Credentials/Crendential.tsx index ad7a0916..794e2bae 100644 --- a/inji-web/src/components/Credentials/Crendential.tsx +++ b/inji-web/src/components/Credentials/Crendential.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, {useState} from "react"; import {getObjectForCurrentLanguage} from "../../utils/i18n"; import {ItemBox} from "../Common/ItemBox"; import {generateCodeChallenge, generateRandomString} from "../../utils/misc"; @@ -8,26 +8,41 @@ import {api} from "../../utils/api"; import {CredentialProps} from "../../types/components"; import {CodeChallengeObject, CredentialConfigurationObject} from "../../types/data"; import {RootState} from "../../types/redux"; +import {DataShareExpiryModal} from "../../modals/DataShareExpiryModal"; +import {storage} from "../../utils/storage"; export const Credential: React.FC = (props) => { const selectedIssuer = useSelector((state: RootState) => state.issuers); + const [credentialExpiry, setCredentialExpiry] = useState(false); const language = useSelector((state: RootState) => state.common.language); const filteredCredentialConfig: CredentialConfigurationObject = props.credentialWellknown.credential_configurations_supported[props.credentialId]; const credentialObject = getObjectForCurrentLanguage(filteredCredentialConfig.display, language); - return { - const state = generateRandomString(); - const code_challenge: CodeChallengeObject = generateCodeChallenge(state); - window.open(api.authorization(selectedIssuer.selected_issuer, props.credentialWellknown, filteredCredentialConfig, state, code_challenge), '_self', 'noopener'); - addNewSession({ - selectedIssuer: selectedIssuer.selected_issuer, - certificateId: props.credentialId, - codeVerifier: state, - state: state, - }); - }} - /> + const vcExpiryTimes = useSelector((state: RootState) => state.common.vcExpiryTimes); + + const onSuccess = () => { + const state = generateRandomString(); + const code_challenge: CodeChallengeObject = generateCodeChallenge(state); + console.log("vcExpiryTimes => " + vcExpiryTimes); + window.open(api.authorization(selectedIssuer.selected_issuer, props.credentialWellknown, filteredCredentialConfig, state, code_challenge), '_self', 'noopener'); + addNewSession({ + selectedIssuer: selectedIssuer.selected_issuer, + certificateId: props.credentialId, + codeVerifier: state, + vcExpiryTimes: vcExpiryTimes ?? 1, + state: state, + }); + } + + return + setCredentialExpiry(true)}/> + {credentialExpiry && setCredentialExpiry(false)} + onSuccess={onSuccess} + credentialName={credentialObject.name} + credentialLogo={credentialObject.logo.url}/> } + } diff --git a/inji-web/src/components/DataShare/CustomTimes/CTContent.tsx b/inji-web/src/components/DataShare/CustomTimes/CTContent.tsx new file mode 100644 index 00000000..aee482fb --- /dev/null +++ b/inji-web/src/components/DataShare/CustomTimes/CTContent.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import {RiArrowDownSLine, RiArrowUpSLine} from "react-icons/ri"; +import {useTranslation} from "react-i18next"; + +export const CTContent:React.FC= (props) => { + + const {t} = useTranslation("CustomExpiryModal"); + return
+
+
+ + +
+
{props.setExpiryTime(isNaN(parseInt(event.target.value)) ? 0 : parseInt(event.target.value)); }} className={"appearance-none w-full p-2 focus:outline-none no-spinner"} placeholder={"Enter Number here"} data-testid={"CTContent-Times-Value"}/>
+
{t("metrics")}
+
+
; +} + +export type CTContentType = { + expiryTime: number; + setExpiryTime: (expiry:number) => void; +} diff --git a/inji-web/src/components/DataShare/CustomTimes/CTHeader.tsx b/inji-web/src/components/DataShare/CustomTimes/CTHeader.tsx new file mode 100644 index 00000000..f23b71a7 --- /dev/null +++ b/inji-web/src/components/DataShare/CustomTimes/CTHeader.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +export const CTHeader:React.FC = (props) => { + return
+
{props.title}
+
; +} + +export type CTHeaderType = { + title: string; +} diff --git a/inji-web/src/components/DataShare/DSContent.tsx b/inji-web/src/components/DataShare/DSContent.tsx new file mode 100644 index 00000000..2be1f11f --- /dev/null +++ b/inji-web/src/components/DataShare/DSContent.tsx @@ -0,0 +1,99 @@ +import React, {useState} from "react"; +import {MdOutlineKeyboardArrowDown} from "react-icons/md"; +import {DSDisclaimer} from "./DSDisclaimer"; +import {useDispatch, useSelector} from "react-redux"; +import {RootState} from "../../types/redux"; +import {storeVCExpiryTimes} from "../../redux/reducers/commonReducer"; +import {useTranslation} from "react-i18next"; + +export const DSContent:React.FC = (props) => { + + const [timesDropDown, setTimesDropDown] = useState(false); + const vcExpiryTimes = useSelector((state: RootState) => state.common.vcExpiryTimes); + const dispatch = useDispatch(); + const {t} = useTranslation("DataShareExpiryModal"); + + + const getExpiryDisplayName = (expiry: number) => { + let expiryDisplayName = expiry.toString(); + switch(expiry){ + case 1: + expiryDisplayName = t("content.validityTimesOptions.once"); + break; + case 3: + expiryDisplayName = t("content.validityTimesOptions.thrice"); + break; + case -1: + expiryDisplayName = t("content.validityTimesOptions.noLimit"); + break; + } + return expiryDisplayName; + } + + + return
+
+
{t("content.selectedDocument")}
+
+
+ Issuer Logo +
+
{props.credentialName}
+
+
+
+
{t("content.consent")}
+
+ + + +
+
+
setTimesDropDown(times => !times)}> +
+
+ + +
+
+ + {timesDropDown &&
+
+
+ + + + +
+
} + +
; +} + +export type DSContentType = { + credentialName: string; + credentialLogo: string; + setCustom: (custom: boolean) => void; +} diff --git a/inji-web/src/components/DataShare/DSDisclaimer.tsx b/inji-web/src/components/DataShare/DSDisclaimer.tsx new file mode 100644 index 00000000..c2578c1b --- /dev/null +++ b/inji-web/src/components/DataShare/DSDisclaimer.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +export const DSDisclaimer:React.FC = (props) => { + return
+
{props.content}
+
; +} + +export type DSDisclaimerType = { + content: string; +} diff --git a/inji-web/src/components/DataShare/DSFooter.tsx b/inji-web/src/components/DataShare/DSFooter.tsx new file mode 100644 index 00000000..9873f0ac --- /dev/null +++ b/inji-web/src/components/DataShare/DSFooter.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +export const DSFooter:React.FC = (props) => { + return
+ + +
; +} + +export type DSFooterType = { + success: string; + onSuccess: () => void; + cancel: string; + onCancel: () => void; +} + + diff --git a/inji-web/src/components/DataShare/DSHeader.tsx b/inji-web/src/components/DataShare/DSHeader.tsx new file mode 100644 index 00000000..f0c22f67 --- /dev/null +++ b/inji-web/src/components/DataShare/DSHeader.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +export const DSHeader:React.FC = (props) => { + return
+
+
{props.title}
+
{props.subTitle}
+
+
; +} + +export type DSHeaderType = { + title: string; + subTitle: string; +} diff --git a/inji-web/src/components/Help/HelpAccordion.tsx b/inji-web/src/components/Help/HelpAccordion.tsx index e3e614a8..c929a17f 100644 --- a/inji-web/src/components/Help/HelpAccordion.tsx +++ b/inji-web/src/components/Help/HelpAccordion.tsx @@ -29,7 +29,7 @@ export const HelpAccordion: React.FC = () => { }, { title: t("item6.title"), - content: [t("item6.description1")], + content: [t("item6.descrip tion1")], }, { title: t("item7.title"), diff --git a/inji-web/src/index.css b/inji-web/src/index.css index c08f8de1..7d8162f5 100644 --- a/inji-web/src/index.css +++ b/inji-web/src/index.css @@ -34,6 +34,15 @@ --iw-color-shieldSuccessShadow: #f1f7ee; --iw-color-shieldErrorShadow: #FEF2F2; --iw-color-shieldLoadingShadow: #f6dfbe; + --iw-color-backDrop: #000000; + --iw-color-borderDark: #717171; + --iw-color-borderLight: #E8EBEC; + --iw-color-arrowDown: #164EA840; + --iw-color-hoverBackGround: #164EA840; + --iw-color-text: #FFFFFF; + --iw-color-subText: #A0A8AC; + --iw-color-disclaimerBackGround: #FFF7E5; + --iw-color-disclaimerText: #8B6105; } [class="purple_theme"] { --iw-font-base: "Gentium Plus", serif; @@ -62,6 +71,15 @@ --iw-color-shieldSuccessShadow: #f1f7ee; --iw-color-shieldErrorShadow: #FEF2F2; --iw-color-shieldLoadingShadow: #f6dfbe; + --iw-color-backDrop: #000000; + --iw-color-borderDark: #717171; + --iw-color-borderLight: #E8EBEC; + --iw-color-arrowDown: #164EA840; + --iw-color-hoverBackGround: #164EA840; + --iw-color-text: #FFFFFF; + --iw-color-subText: #A0A8AC; + --iw-color-disclaimerBackGround: #FFF7E5; + --iw-color-disclaimerText: #8B6105; } } diff --git a/inji-web/src/locales/en.json b/inji-web/src/locales/en.json index 5338faad..f914b26a 100644 --- a/inji-web/src/locales/en.json +++ b/inji-web/src/locales/en.json @@ -127,5 +127,30 @@ "description3": "2. No issuers have been configured.", "description4": "In either case, please contact your technical team to address the issue." } + }, + "DataShareExpiryModal": { + "title": "Share Validity", + "subTitle": "Please provide your consent to set a validity for sharing the {{credentialName}}", + "content": { + "selectedDocument": "Selected Document", + "consent" : "Consent Validity *", + "validityTimesHeader": "Number of times", + "validityTimesOptions": { + "once": "Once", + "thrice": "Thrice", + "noLimit": "No Limit", + "custom": "Custom" + }, + "validityDate": "Date" + }, + "success": "Proceed", + "cancel": "Back", + "disclaimer": "Please note that the QR Code can be shared only for the number of times you have chosen here. To use the QR Code for sharing again, you have to download the PDF again." + }, + "CustomExpiryModal": { + "title": "Please enter the number of times you want to use this credential ", + "metrics": "Times", + "success": "Proceed", + "cancel": "Cancel" } } diff --git a/inji-web/src/modals/CustomExpiryModal.tsx b/inji-web/src/modals/CustomExpiryModal.tsx new file mode 100644 index 00000000..22f90d2c --- /dev/null +++ b/inji-web/src/modals/CustomExpiryModal.tsx @@ -0,0 +1,28 @@ +import React, {useState} from "react"; +import {ModalWrapper} from "./ModalWrapper"; +import {DSFooter} from "../components/DataShare/DSFooter"; +import {CTContent} from "../components/DataShare/CustomTimes/CTContent"; +import {CTHeader} from "../components/DataShare/CustomTimes/CTHeader"; +import {useDispatch, useSelector} from "react-redux"; +import {storeVCExpiryTimes} from "../redux/reducers/commonReducer"; +import {RootState} from "../types/redux"; +import {useTranslation} from "react-i18next"; + + +export const CustomExpiryModal: React.FC = (props) => { + + const vcExpiryTimes = useSelector((state: RootState) => state.common.vcExpiryTimes); + const [expiryTime, setExpiryTime] = useState(vcExpiryTimes); + const {t} = useTranslation("CustomExpiryModal"); + const dispatch = useDispatch(); + return } + content={} + footer={ {dispatch(storeVCExpiryTimes(expiryTime));props.onSuccess()} } onCancel={props.onCancel}/>} + size={"sm"} + zIndex={50} /> +} + +export type CustomExpiryModalType = { + onCancel: () => void; + onSuccess: () => void; +} diff --git a/inji-web/src/modals/DataShareExpiryModal.tsx b/inji-web/src/modals/DataShareExpiryModal.tsx new file mode 100644 index 00000000..d7a55e6f --- /dev/null +++ b/inji-web/src/modals/DataShareExpiryModal.tsx @@ -0,0 +1,29 @@ +import React, {useState} from "react"; +import {ModalWrapper} from "./ModalWrapper"; +import {DSHeader} from "../components/DataShare/DSHeader"; +import {DSFooter} from "../components/DataShare/DSFooter"; +import {DSContent} from "../components/DataShare/DSContent"; +import {CustomExpiryModal} from "./CustomExpiryModal"; +import {useTranslation} from "react-i18next"; + +export const DataShareExpiryModal: React.FC = (props) => { + + const [custom, setCustom] = useState(false); + const {t} = useTranslation("DataShareExpiryModal") + return + } + content={} + footer={} + size={"3xl"} + zIndex={40}/> + {custom && setCustom(false)} onCancel={() => setCustom(false)}/>} + + +} + +export type DataShareExpiryModalType = { + credentialName: string; + credentialLogo: string; + onSuccess: () => void; + onCancel: () => void; +} diff --git a/inji-web/src/modals/ModalWrapper.tsx b/inji-web/src/modals/ModalWrapper.tsx new file mode 100644 index 00000000..c6108454 --- /dev/null +++ b/inji-web/src/modals/ModalWrapper.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +export const ModalWrapper:React.FC = (props) => { + + return <> +
+
+
+ {props.header} + {props.content} + {props.footer} +
+
+
+
+ +} + +export type ModalWrapperType = { + header: React.ReactNode; + content: React.ReactNode; + footer: React.ReactNode; + zIndex: number; + size: string; +} diff --git a/inji-web/src/pages/RedirectionPage.tsx b/inji-web/src/pages/RedirectionPage.tsx index c2eb008d..e20451b1 100644 --- a/inji-web/src/pages/RedirectionPage.tsx +++ b/inji-web/src/pages/RedirectionPage.tsx @@ -37,8 +37,9 @@ export const RedirectionPage: React.FC = () => { const codeVerifier = activeSessionInfo?.codeVerifier; const issuerId = activeSessionInfo?.selectedIssuer?.credential_issuer ?? ""; const certificateId = activeSessionInfo?.certificateId; + const vcExpiryTimes = activeSessionInfo?.vcExpiryTimes; - const requestBody = new URLSearchParams(getTokenRequestBody(code, codeVerifier, issuerId, certificateId)); + const requestBody = new URLSearchParams(getTokenRequestBody(code, codeVerifier, issuerId, certificateId, vcExpiryTimes)); const apiRequest = api.fetchTokenAnddownloadVc; let credentialDownloadResponse = await fetchRequest( apiRequest.url(), diff --git a/inji-web/src/redux/reducers/commonReducer.ts b/inji-web/src/redux/reducers/commonReducer.ts index 6cd6070a..aaa3a577 100644 --- a/inji-web/src/redux/reducers/commonReducer.ts +++ b/inji-web/src/redux/reducers/commonReducer.ts @@ -4,10 +4,12 @@ import {defaultLanguage} from "../../utils/i18n"; const initialState = { language: storage.getItem(storage.SELECTED_LANGUAGE) ? storage.getItem(storage.SELECTED_LANGUAGE) : defaultLanguage, + vcExpiryTimes: 1 } const CommonReducerAction: CommonReducerActionType = { STORE_LANGUAGE: 'STORE_LANGUAGE', + STORE_VC_EXPIRY_TIMES: 'STORE_VC_EXPIRY_TIMES' } export const commonReducer = (state = initialState, actions: any) => { @@ -18,6 +20,12 @@ export const commonReducer = (state = initialState, actions: any) => { language: actions.language } } + case CommonReducerAction.STORE_VC_EXPIRY_TIMES: { + return { + ...state, + vcExpiryTimes: actions.vcExpiryTimes + } + } default : return state; } @@ -30,4 +38,11 @@ export const storeLanguage = (language: string) => { } } +export const storeVCExpiryTimes = (vcExpiryTimes: number) => { + return { + type: CommonReducerAction.STORE_VC_EXPIRY_TIMES, + vcExpiryTimes: vcExpiryTimes + } +} + diff --git a/inji-web/src/setupTests.ts b/inji-web/src/setupTests.ts index 8f2609b7..3d1ffda7 100644 --- a/inji-web/src/setupTests.ts +++ b/inji-web/src/setupTests.ts @@ -3,3 +3,11 @@ // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom import '@testing-library/jest-dom'; + +global.window._env_ = { + DEFAULT_FAVICON: "favicon.ico", + DEFAULT_FONT_URL: "", + DEFAULT_THEME: "purple_theme", + DEFAULT_TITLE: "Inji Web Test", + DEFAULT_LANG: 'en' +}; diff --git a/inji-web/src/types/data.d.ts b/inji-web/src/types/data.d.ts index 6b177776..bbfd06a1 100644 --- a/inji-web/src/types/data.d.ts +++ b/inji-web/src/types/data.d.ts @@ -77,6 +77,7 @@ export type SessionObject = { selectedIssuer?: IssuerObject; certificateId: string; codeVerifier: string; + vcExpiryTimes: number; state: string; } export type ApiRequest = { diff --git a/inji-web/src/types/redux.d.ts b/inji-web/src/types/redux.d.ts index cbfeb528..bafa2859 100644 --- a/inji-web/src/types/redux.d.ts +++ b/inji-web/src/types/redux.d.ts @@ -8,6 +8,7 @@ export type CredentialsReducerActionType = { } export type CommonReducerActionType = { STORE_LANGUAGE: string; + STORE_VC_EXPIRY_TIMES: string; } export type IssuersReducerActionType = { STORE_SELECTED_ISSUER: string; diff --git a/inji-web/src/utils/misc.ts b/inji-web/src/utils/misc.ts index 29f171ec..03fcc575 100644 --- a/inji-web/src/utils/misc.ts +++ b/inji-web/src/utils/misc.ts @@ -30,14 +30,15 @@ export const isObjectEmpty = (object: any) => { return object === null || object === undefined || Object.keys(object).length === 0; } -export const getTokenRequestBody = (code: string, codeVerifier: string, issuerId: string, credentialType: string) => { +export const getTokenRequestBody = (code: string, codeVerifier: string, issuerId: string, credentialType: string, vcExpiryTimes: string) => { return { 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': api.authorizationRedirectionUrl, 'code_verifier': codeVerifier, 'issuer': issuerId, - 'credential': credentialType + 'credential': credentialType, + 'vcExpiryTimes': vcExpiryTimes } } @@ -74,4 +75,3 @@ export const getErrorObject = (downloadResponse: any) => { message: "error.generic.subTitle" } } - diff --git a/inji-web/tailwind.config.js b/inji-web/tailwind.config.js index efc0ed6a..8fa071ac 100644 --- a/inji-web/tailwind.config.js +++ b/inji-web/tailwind.config.js @@ -9,6 +9,11 @@ module.exports = { fontFamily:{ base: 'var(--iw-font-base)', }, + zIndex: { + '50': '50', + '40': '40', + '30': '30' + }, colors: { iw: { background: 'var(--iw-color-background)', @@ -36,6 +41,15 @@ module.exports = { shieldSuccessShadow: 'var(--iw-color-shieldSuccessShadow)', shieldErrorShadow: 'var(--iw-color-shieldErrorShadow)', shieldLoadingShadow: 'var(--iw-color-shieldLoadingShadow)', + backDrop: 'var(--iw-color-backDrop)', + borderLight: 'var(--iw-color-borderLight)', + borderDark: 'var(--iw-color-borderDark)', + arrowDown: 'var(--iw-color-arrowDown)', + hoverBackGround: 'var(--iw-color-hoverBackGround)', + text: 'var(--iw-color-text)', + subText: 'var(--iw-color-subText)', + disclaimerBackGround: 'var(--iw-color-disclaimerBackGround)', + disclaimerText: 'var(--iw-color-disclaimerText)' } } },