diff --git a/.changeset/good-terms-impress.md b/.changeset/good-terms-impress.md new file mode 100644 index 000000000..245b53b2d --- /dev/null +++ b/.changeset/good-terms-impress.md @@ -0,0 +1,5 @@ +--- +'@blockchain-lab-um/dapp': patch +--- + +Fixes dapp crashing, updated formatted panel view diff --git a/.changeset/kind-turkeys-destroy.md b/.changeset/kind-turkeys-destroy.md new file mode 100644 index 000000000..722f39306 --- /dev/null +++ b/.changeset/kind-turkeys-destroy.md @@ -0,0 +1,5 @@ +--- +'@blockchain-lab-um/masca-docs': minor +--- + +Added docs form `signData` RPC method diff --git a/.changeset/late-geckos-retire.md b/.changeset/late-geckos-retire.md new file mode 100644 index 000000000..bbdb88414 --- /dev/null +++ b/.changeset/late-geckos-retire.md @@ -0,0 +1,5 @@ +--- +'@blockchain-lab-um/masca': minor +--- + +Added `signData` RPC method (JWT and JWZ support) diff --git a/.changeset/modern-grapes-buy.md b/.changeset/modern-grapes-buy.md new file mode 100644 index 000000000..25f614830 --- /dev/null +++ b/.changeset/modern-grapes-buy.md @@ -0,0 +1,5 @@ +--- +'@blockchain-lab-um/masca': patch +--- + +Adds e2e tests for PolygonID authorization and credential offer flow. diff --git a/.changeset/neat-dogs-sip.md b/.changeset/neat-dogs-sip.md new file mode 100644 index 000000000..4b4d5566a --- /dev/null +++ b/.changeset/neat-dogs-sip.md @@ -0,0 +1,5 @@ +--- +'@blockchain-lab-um/masca-types': minor +--- + +Added types for `signData` rpc method. diff --git a/.changeset/pre.json b/.changeset/pre.json index 41a4bea97..999b07fb0 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -17,5 +17,21 @@ "@blockchain-lab-um/masca": "1.1.0-beta.0", "@blockchain-lab-um/masca-types": "1.1.0" }, - "changesets": [] + "changesets": [ + "chilly-boxes-sit", + "good-terms-impress", + "great-radios-love", + "heavy-hotels-build", + "heavy-timers-laugh", + "kind-turkeys-destroy", + "late-geckos-retire", + "long-ligers-brush", + "modern-grapes-buy", + "moody-cheetahs-perform", + "neat-dogs-sip", + "nine-ducks-explain", + "purple-jokes-hunt", + "rich-toes-marry", + "weak-frogs-occur" + ] } diff --git a/.changeset/rich-toes-marry.md b/.changeset/rich-toes-marry.md new file mode 100644 index 000000000..8b1537559 --- /dev/null +++ b/.changeset/rich-toes-marry.md @@ -0,0 +1,5 @@ +--- +'@blockchain-lab-um/masca-connector': minor +--- + +Added `signData` RPC method (JWT and JWZ support) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index f9575f358..1204efc03 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -17,8 +17,6 @@ module.exports = { '**/__tests__/**', '**/*.spec.ts', ], - plugins: ['jest', 'jest-extended'], - extends: ['plugin:jest/recommended', 'plugin:jest/style'], rules: { 'jest/prefer-expect-assertions': 'off', 'jest/no-conditional-expect': 'off', @@ -26,8 +24,6 @@ module.exports = { '@typescript-eslint/no-unsafe-assignment': 'off', 'jest/no-export': 'off', }, - - env: { jest: true }, }, ], plugins: ['@typescript-eslint', 'unused-imports'], @@ -92,6 +88,7 @@ module.exports = { '**/jest.d.ts', '**/test/**', '**/tests/**', + '**/vite.config.ts', ], }, ], diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml new file mode 100644 index 000000000..c536a6043 --- /dev/null +++ b/.github/workflows/cron.yml @@ -0,0 +1,30 @@ +name: CRON + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' + +jobs: + main: + name: Nx Cloud - Cron Job + uses: nrwl/ci/.github/workflows/nx-cloud-main.yml@v0.13.0 + secrets: + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + with: + pnpm-version: 8.6.10 + node-version: 18.16.0 + number-of-agents: 2 + main-branch-name: 'develop' + parallel-commands-on-agents: | + pnpm exec nx run-many --target=test:cron --parallel=2 + + agents: + name: Nx Cloud - Agents + uses: nrwl/ci/.github/workflows/nx-cloud-agents.yml@v0.13.0 + secrets: + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + with: + number-of-agents: 2 + pnpm-version: 8.6.10 + node-version: 18.16.0 diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index fa3df1ec2..b5fd5834f 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -17,6 +17,7 @@ on: - '@blockchain-lab-um/utils' - '@blockchain-lab-um/did-provider-key' - '@blockchain-lab-um/did-provider-ebsi' + - '@blockchain-lab-um/dapp' # Wait for the workflow to finish before starting a new one concurrency: ${{ github.workflow }}-${{ github.ref }} diff --git a/nx.json b/nx.json index 4ac99ffcd..2fc3876df 100644 --- a/nx.json +++ b/nx.json @@ -3,7 +3,7 @@ "default": { "runner": "nx-cloud", "options": { - "cacheableOperations": ["build", "test", "lint", "test:ci"] + "cacheableOperations": ["build", "test", "lint", "test:ci", "test:cron"] } } }, @@ -53,6 +53,11 @@ "inputs": ["default", "^production"], "dependsOn": ["^build"], "outputs": ["{projectRoot}/coverage"] + }, + "test:cron": { + "inputs": ["default", "^production"], + "dependsOn": ["^build"], + "outputs": ["{projectRoot}/coverage"] } }, "defaultBase": "develop" diff --git a/package.json b/package.json index 4af0e5dbe..3c2967dac 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "prepare": "is-ci || husky install", "prettier": "prettier --write .", "test": "pnpm nx run-many --target=test", - "test:ci": "pnpm nx run-many --target=test:ci" + "test:ci": "pnpm nx run-many --target=test:ci", + "test:cron": "cross-env CRON=true pnpm nx run-many --target=test:cron" }, "dependencies": { "@changesets/cli": "2.26.2" @@ -95,7 +96,7 @@ "cross-fetch@3.1.8": "patches/cross-fetch@3.1.8.patch", "cross-fetch@4.0.0": "patches/cross-fetch@4.0.0.patch", "@ceramicnetwork/common@2.30.0": "patches/@ceramicnetwork__common@2.30.0.patch", - "@metamask/snaps-types@1.0.1": "patches/@metamask__snaps-types@1.0.1.patch", + "@metamask/snaps-types@1.0.2": "patches/@metamask__snaps-types@1.0.2.patch", "@changesets/assemble-release-plan@5.2.4": "patches/@changesets__assemble-release-plan@5.2.4.patch" }, "allowNonAppliedPatches": true diff --git a/packages/connector/CHANGELOG.md b/packages/connector/CHANGELOG.md index be950d7e9..32975a17e 100644 --- a/packages/connector/CHANGELOG.md +++ b/packages/connector/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 1.2.0-beta.0 + +### Minor Changes + +- [#484](https://github.com/blockchain-lab-um/masca/pull/484) [`a07fdcc`](https://github.com/blockchain-lab-um/masca/commit/a07fdcc07e90ba949def10bbcac6b12fbe42e4c0) Thanks [@martines3000](https://github.com/martines3000)! - Added `signData` RPC method (JWT and JWZ support) + +### Patch Changes + +- Updated dependencies [[`a07fdcc`](https://github.com/blockchain-lab-um/masca/commit/a07fdcc07e90ba949def10bbcac6b12fbe42e4c0)]: + - @blockchain-lab-um/masca-types@1.2.0-beta.0 + ## 1.1.0 ### Minor Changes diff --git a/packages/connector/package.json b/packages/connector/package.json index 43f35a5d0..4f6ff6d56 100644 --- a/packages/connector/package.json +++ b/packages/connector/package.json @@ -1,6 +1,6 @@ { "name": "@blockchain-lab-um/masca-connector", - "version": "1.1.0", + "version": "1.2.0-beta.0", "description": "Library for using Masca on the frontend", "keywords": [ "MetaMask", @@ -41,7 +41,7 @@ "prepack": "pnpm build" }, "dependencies": { - "@blockchain-lab-um/masca-types": "1.1.0", + "@blockchain-lab-um/masca-types": "1.2.0-beta.0", "@blockchain-lab-um/utils": "1.3.7", "@didtools/pkh-ethereum": "0.4.0", "@metamask/detect-provider": "^2.0.0", diff --git a/packages/connector/src/snap.ts b/packages/connector/src/snap.ts index d6a6cc6ac..1a79c2790 100644 --- a/packages/connector/src/snap.ts +++ b/packages/connector/src/snap.ts @@ -1,5 +1,6 @@ import { ImportStateBackupRequestParams, + SignDataRequestParams, type AvailableCredentialStores, type AvailableMethods, type CreateCredentialRequestParams, @@ -59,7 +60,7 @@ async function sendSnapMethod( * * @return Result - list of VCs */ -export async function queryCredentials( +async function queryCredentials( this: Masca, params?: QueryCredentialsRequestParams ): Promise> { @@ -76,7 +77,7 @@ export async function queryCredentials( * @param params - parameters for creating a VP * @return Result - VP */ -export async function createPresentation( +async function createPresentation( this: Masca, params: CreatePresentationRequestParams ): Promise> { @@ -112,7 +113,7 @@ export async function createPresentation( * * @return Result - list of saved VCs */ -export async function saveCredential( +async function saveCredential( this: Masca, vc: W3CVerifiableCredential, options?: SaveCredentialOptions @@ -137,7 +138,7 @@ export async function saveCredential( * @param options - optional parameters for deleting a VC * @return Result - list of results for each VC */ -export async function deleteCredential( +async function deleteCredential( this: Masca, id: string, options?: DeleteCredentialsOptions @@ -160,7 +161,7 @@ export async function deleteCredential( * Get the DID of the currently selected MetaMask account * @return Result - DID */ -export async function getDID(this: Masca): Promise> { +async function getDID(this: Masca): Promise> { return sendSnapMethod({ method: 'getDID' }, this.snapId); } @@ -168,7 +169,7 @@ export async function getDID(this: Masca): Promise> { * Get the currently selected DID method * @return Result - DID method */ -export async function getSelectedMethod(this: Masca): Promise> { +async function getSelectedMethod(this: Masca): Promise> { return sendSnapMethod({ method: 'getSelectedMethod' }, this.snapId); } @@ -176,9 +177,7 @@ export async function getSelectedMethod(this: Masca): Promise> { * Get a list of available DID methods * @return Result - list of available DID methods */ -export async function getAvailableMethods( - this: Masca -): Promise> { +async function getAvailableMethods(this: Masca): Promise> { return sendSnapMethod({ method: 'getAvailableMethods' }, this.snapId); } @@ -187,7 +186,7 @@ export async function getAvailableMethods( * @param method - DID method to be switched to * @return Result - true if the switch was successful */ -export async function switchDIDMethod( +async function switchDIDMethod( this: Masca, method: AvailableMethods ): Promise> { @@ -204,7 +203,7 @@ export async function switchDIDMethod( * Enables/disables confirmation popup windows when retrieving VCs, generating VPs,... * @return Result - true if the switch was successful */ -export async function togglePopups(this: Masca): Promise> { +async function togglePopups(this: Masca): Promise> { return sendSnapMethod({ method: 'togglePopups' }, this.snapId); } @@ -213,7 +212,7 @@ export async function togglePopups(this: Masca): Promise> { * * @return Result - true if the addition was successful */ -export async function addFriendlyDapp(this: Masca): Promise> { +async function addFriendlyDapp(this: Masca): Promise> { return sendSnapMethod({ method: 'addFriendlyDapp' }, this.snapId); } @@ -222,7 +221,7 @@ export async function addFriendlyDapp(this: Masca): Promise> { * * @return Result - true if the removal was successful */ -export async function removeFriendlyDapp( +async function removeFriendlyDapp( this: Masca, id: string ): Promise> { @@ -237,7 +236,7 @@ export async function removeFriendlyDapp( * * @return Result> - status of available VC stores */ -export async function getCredentialStore( +async function getCredentialStore( this: Masca ): Promise>> { return sendSnapMethod({ method: 'getCredentialStore' }, this.snapId); @@ -247,7 +246,7 @@ export async function getCredentialStore( * Get a list of available VC stores * @return Result - list of available VC stores */ -export async function getAvailableCredentialStores( +async function getAvailableCredentialStores( this: Masca ): Promise> { return sendSnapMethod( @@ -262,7 +261,7 @@ export async function getAvailableCredentialStores( * @param value - true to enable, false to disable * @return Result - true if the switch was successful */ -export async function setCredentialStore( +async function setCredentialStore( this: Masca, store: AvailableCredentialStores, value: boolean @@ -277,7 +276,7 @@ export async function setCredentialStore( * Get account settings of currently selected account (i.e. DID method, VC stores,...) * @return Result - account settings */ -export async function getAccountSettings( +async function getAccountSettings( this: Masca ): Promise> { return sendSnapMethod({ method: 'getAccountSettings' }, this.snapId); @@ -287,9 +286,7 @@ export async function getAccountSettings( * Get Masca settings * @return Result - Masca settings */ -export async function getSnapSettings( - this: Masca -): Promise> { +async function getSnapSettings(this: Masca): Promise> { return sendSnapMethod({ method: 'getSnapSettings' }, this.snapId); } @@ -298,7 +295,7 @@ export async function getSnapSettings( * @param did - DID to be resolved * @return Result - DID resolution result */ -export async function resolveDID( +async function resolveDID( this: Masca, did: string ): Promise> { @@ -310,7 +307,7 @@ export async function resolveDID( * @param this - Masca instance * @param params - object with parameters for creating a Verifiable Credential */ -export async function createCredential( +async function createCredential( this: Masca, params: CreateCredentialRequestParams ): Promise> { @@ -347,7 +344,7 @@ export async function createCredential( * @param params.currentAccount - account address * @returns Result - true if successful */ -export async function setCurrentAccount( +async function setCurrentAccount( this: Masca, params: SetCurrentAccountRequestParams ): Promise> { @@ -366,7 +363,7 @@ export async function setCurrentAccount( * @param params - a Credential or a Presentation with optional verbose flag * @returns Result - true if the Credential/Presentation is valid, false otherwise */ -export async function verifyData( +async function verifyData( this: Masca, params: VerifyDataRequestParams ): Promise> { @@ -385,7 +382,7 @@ export async function verifyData( * @param params.credentialOffer - Credential Offer string * @returns Result - list of VCs if successful */ -export async function handleCredentialOffer( +async function handleCredentialOffer( this: Masca, params: HandleCredentialOfferRequestParams ): Promise> { @@ -404,7 +401,7 @@ export async function handleCredentialOffer( * @param params.authorizationRequest - Authorization Request string * @returns Result - void if successful */ -export async function handleAuthorizationRequest( +async function handleAuthorizationRequest( this: Masca, params: HandleAuthorizationRequestParams ): Promise> { @@ -423,7 +420,7 @@ export async function handleAuthorizationRequest( * @param serializedSession - serialized Ceramic session * @returns Result - true if successful */ -export async function setCeramicSession( +async function setCeramicSession( this: Masca, serializedSession: string ): Promise> { @@ -440,9 +437,8 @@ export async function setCeramicSession( * Validate the stored Ceramic session * @param this - Masca instance * @returns Result - true if successful - * @throws Error - if the stored Ceramic session is invalid */ -export async function validateStoredCeramicSession( +async function validateStoredCeramicSession( this: Masca ): Promise> { return sendSnapMethod( @@ -458,7 +454,7 @@ export async function validateStoredCeramicSession( * @param this - Masca instance * @returns Result - Encrypted Masca state */ -export async function exportStateBackup(this: Masca): Promise> { +async function exportStateBackup(this: Masca): Promise> { return sendSnapMethod( { method: 'exportStateBackup', @@ -473,7 +469,7 @@ export async function exportStateBackup(this: Masca): Promise> { * @param params - Encrypted Masca state * @returns Result - true if successful */ -export async function importStateBackup( +async function importStateBackup( this: Masca, params: ImportStateBackupRequestParams ): Promise> { @@ -489,11 +485,9 @@ export async function importStateBackup( /** * Get wallet ID * @param this - Masca instance - * @returns Result - true if successful - * @throws Error - if id creation failed + * @returns Result - walletId string if successful */ -export async function getWalletId(this: Masca): Promise> { - console.log('getWalletId connector'); +async function getWalletId(this: Masca): Promise> { return sendSnapMethod( { method: 'getWalletId', @@ -502,6 +496,24 @@ export async function getWalletId(this: Masca): Promise> { ); } +/** + * Sign data (JWT or JWZ) + * @param this - Masca instance + * @returns Result - signed data string (JWT or JWZ) if successful + */ +async function signData( + this: Masca, + params: SignDataRequestParams +): Promise> { + return sendSnapMethod( + { + method: 'signData', + params, + }, + this.snapId + ); +} + const wrapper = (fn: (...args: T) => Promise>) => async (...args: T): Promise> => { @@ -554,5 +566,6 @@ export class Masca { importStateBackup: wrapper(importStateBackup.bind(this)), exportStateBackup: wrapper(exportStateBackup.bind(this)), getWalletId: wrapper(getWalletId.bind(this)), + signData: wrapper(signData.bind(this)), }); } diff --git a/packages/dapp/CHANGELOG.md b/packages/dapp/CHANGELOG.md index 786858b37..a945971f6 100644 --- a/packages/dapp/CHANGELOG.md +++ b/packages/dapp/CHANGELOG.md @@ -1,5 +1,14 @@ # @blockchain-lab-um/dapp +## 1.1.0-beta.1 + +### Patch Changes + +- [#495](https://github.com/blockchain-lab-um/masca/pull/495) [`da2d90c`](https://github.com/blockchain-lab-um/masca/commit/da2d90cf030cd057761df274c6435cd3ff39cbe7) Thanks [@martines3000](https://github.com/martines3000)! - Fixes dapp crashing, updated formatted panel view + +- Updated dependencies [[`a07fdcc`](https://github.com/blockchain-lab-um/masca/commit/a07fdcc07e90ba949def10bbcac6b12fbe42e4c0)]: + - @blockchain-lab-um/masca-connector@1.2.0-beta.0 + ## 1.1.0-beta.0 ### Minor Changes diff --git a/packages/dapp/package.json b/packages/dapp/package.json index 8717f7a5c..bf64d9430 100644 --- a/packages/dapp/package.json +++ b/packages/dapp/package.json @@ -1,6 +1,6 @@ { "name": "@blockchain-lab-um/dapp", - "version": "1.1.0-beta.0", + "version": "1.1.0-beta.1", "private": true, "license": "(Apache-2.0 AND MIT)", "type": "commonjs", @@ -20,7 +20,7 @@ "start": "next start" }, "dependencies": { - "@blockchain-lab-um/masca-connector": "1.1.0", + "@blockchain-lab-um/masca-connector": "1.2.0-beta.0", "@blockchain-lab-um/oidc-types": "0.0.8", "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.0.18", @@ -33,7 +33,7 @@ "@veramo/core": "5.4.1", "@veramo/utils": "5.4.1", "@vercel/analytics": "^1.0.2", - "@vercel/og": "^0.5.7", + "@vercel/og": "^0.5.17", "clsx": "^2.0.0", "did-jwt-vc": "^3.2.8", "file-saver": "^2.0.5", @@ -41,7 +41,7 @@ "headless-stepper": "^1.9.1", "html5-qrcode": "^2.3.8", "luxon": "^3.4.3", - "next": "13.5.1", + "next": "13.5.3", "next-intl": "3.0.0-beta.9", "next-sitemap": "^4.2.3", "next-themes": "^0.2.1", diff --git a/packages/dapp/src/app/api/og/route.tsx b/packages/dapp/src/app/api/og/route.tsx index 9017f7d17..52d2e0ea3 100644 --- a/packages/dapp/src/app/api/og/route.tsx +++ b/packages/dapp/src/app/api/og/route.tsx @@ -1,9 +1,10 @@ /* eslint-disable @next/next/no-img-element */ +import { NextRequest } from 'next/server'; import { ImageResponse } from '@vercel/og'; export const runtime = 'edge'; -export async function GET(req: Request) { +export async function GET(req: NextRequest) { const interRegular = fetch( new URL('../../../../public/fonts/Inter-Regular.ttf', import.meta.url) ).then((res) => res.arrayBuffer()); diff --git a/packages/dapp/src/components/CredentialDisplay/FormatedPanel.tsx b/packages/dapp/src/components/CredentialDisplay/FormatedPanel.tsx index 048e5349d..38ba8330a 100644 --- a/packages/dapp/src/components/CredentialDisplay/FormatedPanel.tsx +++ b/packages/dapp/src/components/CredentialDisplay/FormatedPanel.tsx @@ -1,18 +1,16 @@ -import { Fragment, useMemo } from 'react'; +import { Fragment, useMemo, useState } from 'react'; import { CheckCircleIcon, ExclamationCircleIcon, } from '@heroicons/react/20/solid'; import { DocumentDuplicateIcon } from '@heroicons/react/24/outline'; import { VerifiableCredential } from '@veramo/core'; +import clsx from 'clsx'; import { useTranslations } from 'next-intl'; import Tooltip from '@/components/Tooltip'; -import { - camelToTitleCase, - convertTypes, - copyToClipboard, -} from '@/utils/string'; +import { convertTypes, copyToClipboard } from '@/utils/string'; +import JsonModal from '../JsonMdoal'; interface FormatedPanelProps { credential: VerifiableCredential; @@ -21,7 +19,7 @@ interface FormatedPanelProps { const DIDDisplay = ({ did }: { did: string }) => { const t = useTranslations('DIDDisplay'); return ( -
+

DID:

@@ -44,8 +42,34 @@ const DIDDisplay = ({ did }: { did: string }) => { ); }; +const AddressDisplay = ({ address }: { address: string }) => { + const t = useTranslations('AddressDisplay'); + return ( +
+

+ Address: +

+
+ + + {`${address.slice(0, 8)}...${address.slice(-8)}`} + + + +
+
+ ); +}; + const DisplayDate = ({ text, date }: { text: string; date: string }) => ( -
+

{text}:

@@ -55,91 +79,145 @@ const DisplayDate = ({ text, date }: { text: string; date: string }) => (
); +const CredentialSubject = ({ + data, + viewJsonText, + selectJsonData, +}: { + data: Record; + viewJsonText: string; + selectJsonData: React.Dispatch>; +}) => ( + <> + {Object.entries(data).map(([key, value]: [string, any]) => ( + + {(() => { + if (key === 'id') return ; + if (key === 'address') return ; + + const isObject = !( + typeof value === 'string' || typeof value === 'number' + ); + + return ( +
+

+ {key}: +

+
+ {isObject ? ( + // Small button, with outline, that opens a modal with the JSON data. + + ) : ( + value + )} +
+
+ ); + })()} +
+ ))} + +); + const FormatedPanel = ({ credential }: FormatedPanelProps) => { const t = useTranslations('FormatedPanel'); const types = useMemo(() => convertTypes(credential.type), [credential.type]); + + const [jsonModalOpen, setJsonModalOpen] = useState(false); + const [selectedJsonData, setSelectedJsonData] = useState({}); + + const selectJsonData = (data: any) => { + setSelectedJsonData(data); + setJsonModalOpen(true); + }; + const isValid = useMemo(() => { if (!credential.expirationDate) return true; return Date.parse(credential.expirationDate) > Date.now(); }, [credential]); return ( -
-
-
- -

- {types} -

-
-
-
- - {isValid ? ( - - ) : ( - - )} - -
-
-
-
-

- {t('subject')} -

- {Object.entries(credential.credentialSubject).map( - ([key, value]: [string, string]) => ( - - {key === 'id' ? ( - - ) : ( -
-

- {camelToTitleCase(key)}: -

-

- {value} -

-
- )} -
- ) - )} -
-
-
-
-

- {t('issuer')} + <> +
+
+
+ +

+ {types}

- -
-
-

- {t('dates')} -

- - {credential.expirationDate && ( + +
+
+ + {isValid ? ( + + ) : ( + + )} + +
+
+
+
+

+ {t('subject')} +

+ +
+
+
+
+

+ {t('issuer')} +

+ +
+
+

+ {t('dates')} +

- )} + {credential.expirationDate && ( + + )} +
-

+ + ); }; diff --git a/packages/dapp/src/components/JsonMdoal/index.tsx b/packages/dapp/src/components/JsonMdoal/index.tsx new file mode 100644 index 000000000..b008a8f3d --- /dev/null +++ b/packages/dapp/src/components/JsonMdoal/index.tsx @@ -0,0 +1,36 @@ +import { useTranslations } from 'next-intl'; + +import Button from '../Button'; +import Modal from '../Modal'; + +interface JsonModalProps { + isOpen: boolean; + setOpen: (open: boolean) => void; + data: any; +} +const JsonModal = ({ isOpen, setOpen, data }: JsonModalProps) => { + const t = useTranslations('JsonModal'); + + return ( + +
+
+