From e3eb7b4d5d80962903b9bf5fbf635072d6c7f60c Mon Sep 17 00:00:00 2001 From: "deno-deploy[bot]" <75045203+deno-deploy[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:42:15 +0000 Subject: [PATCH 01/54] [Deno Deploy] Update .github/workflows/deploy.yml From 95aeaa67367aab23a603afb29743926cee05d9fa Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:22:22 +0530 Subject: [PATCH 02/54] feat: support no ledger (#689) * wip: implemented ui for method selection Signed-off-by: bhavanakarwade * wip: modified no ledger screen Signed-off-by: bhavanakarwade * feat: modified ui for create did popup Signed-off-by: bhavanakarwade * refactor: worked on error handling Signed-off-by: bhavanakarwade * fix: validations on create did popup Signed-off-by: bhavanakarwade * refactor: mapping function Signed-off-by: bhavanakarwade * fix: resolved sonarlint issues Signed-off-by: bhavanakarwade * fix: input box issues Signed-off-by: bhavanakarwade * refactor: handled role wise conditions Signed-off-by: bhavanakarwade * fix: resolved code duplication errors Signed-off-by: bhavanakarwade * fix: enum values Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- package-lock.json | 90 ++ package.json | 1 + src/common/enums.ts | 17 +- .../configuration-settings/CreateDid.tsx | 686 +++++++------- .../configuration-settings/DidList.tsx | 26 +- .../organization/interfaces/index.ts | 24 + .../GenerateBtnPolygon.tsx | 2 +- .../SetDomainValueInput.tsx | 84 +- .../SetPrivateKeyValue.tsx | 228 ++++- .../walletCommonComponents/SharedAgent.tsx | 867 ++++++++---------- .../TokenWarningMessage.tsx | 6 +- .../walletCommonComponents/WalletSpinup.tsx | 53 +- .../interfaces/index.tsx | 4 +- src/config/envConfig.ts | 9 +- 14 files changed, 1135 insertions(+), 962 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87687a376..febbda2eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "dom-to-image": "^2.6.0", "downloadjs": "^1.4.7", "eslint": "^8.57.0", + "ethers": "^6.12.1", "flowbite": "^2.3.0", "flowbite-react": "^0.4.11", "flowbite-typography": "^1.0.3", @@ -83,6 +84,11 @@ "postcss": "^8.4.38" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -2047,6 +2053,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3137,6 +3165,11 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -6134,6 +6167,63 @@ "node": ">=0.10.0" } }, + "node_modules/ethers": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.12.1.tgz", + "integrity": "sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", diff --git a/package.json b/package.json index 81d3b060e..62a8c35ad 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "dom-to-image": "^2.6.0", "downloadjs": "^1.4.7", "eslint": "^8.57.0", + "ethers": "^6.12.1", "flowbite": "^2.3.0", "flowbite-react": "^0.4.11", "flowbite-typography": "^1.0.3", diff --git a/src/common/enums.ts b/src/common/enums.ts index dd66bea4f..83d5f5c6f 100644 --- a/src/common/enums.ts +++ b/src/common/enums.ts @@ -101,8 +101,17 @@ export enum BulkIssuanceStatus { } export enum DidMethod { - INDY = 'indy', - KEY = 'key', - WEB = 'web', - POLYGON = 'polygon' + INDY = 'did:indy', + KEY = 'did:key', + WEB = 'did:web', + POLYGON = 'did:polygon' +} + +export enum Network { + TESTNET = 'testnet', + MAINNET = 'mainnet' +} + +export enum CommonConstants { + BALANCELIMIT = 0.01 } \ No newline at end of file diff --git a/src/components/organization/configuration-settings/CreateDid.tsx b/src/components/organization/configuration-settings/CreateDid.tsx index d03369974..bf9ede751 100644 --- a/src/components/organization/configuration-settings/CreateDid.tsx +++ b/src/components/organization/configuration-settings/CreateDid.tsx @@ -1,5 +1,5 @@ import * as yup from 'yup'; -import { Button, Modal } from 'flowbite-react'; +import { Button, Checkbox, Label, Modal } from 'flowbite-react'; import { Field, Form, Formik } from 'formik'; import type { FormikHelpers as FormikActions } from 'formik'; import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; @@ -7,15 +7,14 @@ import { useEffect, useState } from 'react'; import { AlertComponent } from '../../AlertComponent'; import type { AxiosResponse } from 'axios'; import { createDid, getOrganizationById } from '../../../api/organization'; -import type { EditOrgdetailsModalProps, IFormikValues, Organisation } from '../interfaces'; -import { createPolygonKeyValuePair, getLedgerConfig } from '../../../api/Agent'; +import type { EditOrgdetailsModalProps, IFormikValues } from '../interfaces'; +import { createPolygonKeyValuePair } from '../../../api/Agent'; import { DidMethod } from '../../../common/enums'; import { nanoid } from 'nanoid'; import TokenWarningMessage from '../walletCommonComponents/TokenWarningMessage'; import CopyDid from '../../../commonComponents/CopyDid'; -import GenerateBtnPolygon from '../walletCommonComponents/GenerateBtnPolygon'; import { getFromLocalStorage } from '../../../api/Auth'; - +import { ethers } from 'ethers'; interface IPolygonKeys { privateKey: string; @@ -23,46 +22,23 @@ interface IPolygonKeys { address: string; } -interface ILedgerConfig { - [method: string]: { - [network: string]: string; - }; -} - -interface ILedgerItem { - name: string; - details: { - [network: string]: string; - }; -} - const CreateDIDModal = (props: EditOrgdetailsModalProps) => { const [loading, setLoading] = useState(false); - const [mappedData, setMappedData] = useState({}); - const [erroMsg, setErrMsg] = useState(null); + const [errMsg, setErrMsg] = useState(null); const [successMsg, setSuccessMsg] = useState(null); const [seed, setSeed] = useState(''); - const [selectedMethod, setSelectedMethod] = useState(''); const [generatedKeys, setGeneratedKeys] = useState(null); - const [ledgerName, setLedgerName] = useState(null); - const fetchLedgerConfig = async () => { - try { - const { data } = (await getLedgerConfig()) as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const ledgerdata: ILedgerConfig = {}; - data.data.forEach((item: ILedgerItem) => { - ledgerdata[item.name.toLowerCase()] = { ...item.details }; - }); - setMappedData(ledgerdata); - } - } catch (err) { - console.error('Error in fetching ledger config:::', err); - } - }; + const [ledgerValue, setLedgerValue] = useState(null); + const [method, setMethod] = useState(null); + const [networkValue, setNetworkValue] = useState(null); + const [completeDidMethodValue, setCompleteDidMethodValue] = useState(null); + const [havePrivateKey, setHavePrivateKey] = useState(false); + const [privateKeyValue, setPrivateKeyValue] = useState(''); + const [walletErrorMessage, setWalletErrorMessage] = useState(null); const fetchOrganizationDetails = async () => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const response = await getOrganizationById(orgId as string); + const response = await getOrganizationById(orgId); const { data } = response as AxiosResponse; setLoading(false); if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { @@ -70,52 +46,102 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { ?.split(':') .slice(0, 2) .join(':'); - let getLedgerName; - switch (didMethod) { - case 'did:indy': - case 'did:polygon': - getLedgerName = data?.data?.org_agents[0]?.ledgers?.name; - setLedgerName(getLedgerName); - - break; - case 'did:web': - case 'did:key': - getLedgerName = data?.data?.org_agents[0]?.orgDid - ?.split(':') - .slice(1)[0]; - setLedgerName(getLedgerName); - - break; - default: - console.error('Unsupported DID format'); + setMethod(didMethod); + + + let ledgerName; + if (didMethod === DidMethod.INDY || DidMethod.POLYGON) { + ledgerName = data?.data?.org_agents[0]?.orgDid.split(':')[1]; + } else { + ledgerName = 'No Ledger'; + } + setLedgerValue(ledgerName); + + let networkName; + + if (didMethod === DidMethod.INDY) { + networkName = data?.data?.org_agents[0]?.orgDid.split(':').slice(2, 4).join(':'); + } else if (didMethod === DidMethod.POLYGON) { + networkName = data?.data?.org_agents[0]?.orgDid.split(':')[2]; + } else { + networkName = ''; + } + setNetworkValue(networkName); + + + let completeDidMethod; + + if (didMethod === DidMethod.INDY) { + completeDidMethod = data?.data?.org_agents[0]?.orgDid.split(':').slice(0, 4).join(':'); + } else { + completeDidMethod = didMethod; + } + setCompleteDidMethodValue(completeDidMethod); + } else { console.error('Error in fetching organization:::'); } - setLoading(false); }; useEffect(() => { fetchOrganizationDetails(); }, []); + + const checkBalance = async (privateKey: string) => { + try { + const testnetUrl = 'https://rpc-amoy.polygon.technology'; + + const provider = new ethers.JsonRpcProvider(testnetUrl) + + const wallet = new ethers.Wallet(privateKey, provider); + const address = await wallet.getAddress(); + const balance = await provider.getBalance(address); + + const etherBalance = ethers.formatEther(balance); + + if (parseFloat(etherBalance) < 0.01) { + setWalletErrorMessage('You have insufficient funds.'); + } else { + setWalletErrorMessage(null); + } + + + return etherBalance; + } catch (error) { + console.error('Error checking wallet balance:', error); + return null; + } + }; + + useEffect(() => { + if (privateKeyValue && privateKeyValue.length === 64) { + checkBalance(privateKeyValue); + } else { + setWalletErrorMessage(null); + } + + }, [privateKeyValue]); + const createNewDid = async (values: IFormikValues) => { setLoading(true); + let network = ''; + if (values.method === DidMethod.INDY) { + network = values?.network; + } else if (values.method === DidMethod.POLYGON) { + network = `${values.ledger}:${values.network}`; + } const didData = { seed: values.method === DidMethod.POLYGON ? '' : seed, keyType: 'ed25519', - method: values.method, - network: - values.method === DidMethod.POLYGON - ? `${values.method}:${values.network}` - : values.method !== DidMethod.KEY - ? `${values.ledger}:${values.network}` - : '', + method: values.method.split(':')[1], + network: network, domain: values.method === DidMethod.WEB ? values.domain : '', role: values.method === DidMethod.INDY ? 'endorser' : '', privatekey: values.method === DidMethod.POLYGON ? values.privatekey : '', - did: '', + did: values?.did ?? '', endorserDid: values?.endorserDid || '', isPrimaryDid: false, }; @@ -130,10 +156,11 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { props.setOpenModal(false); props.setMessage(data?.message); setSuccessMsg(data?.message); - setLoading(false); + setLoading(true); } else { setErrMsg(response as string); setLoading(false); + props.setOpenModal(true); } } catch (error) { console.error('An error occurred while creating did:', error); @@ -149,84 +176,49 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { setGeneratedKeys(data?.data); + const privateKey = data?.data?.privateKey.slice(2) + setPrivateKeyValue( privateKeyValue || privateKey); + await checkBalance(privateKeyValue || privateKey); } } catch (err) { console.error('Generate private key ERROR::::', err); } }; - const showMethod = ( - method: string, - selectedLedger: string, - selectedMethod: string, - selectedNetwork: string, - ): string => { - switch (method) { - case DidMethod.POLYGON: { - return mappedData && selectedNetwork && method - ? mappedData[method][selectedNetwork] || '' - : ''; - } - case DidMethod.INDY: { - return mappedData && selectedLedger && selectedNetwork && method - ? mappedData[method][selectedLedger][selectedNetwork] || '' - : ''; - } - case DidMethod.KEY: - case DidMethod.WEB: { - return mappedData && method ? mappedData[method][method] || '' : ''; - } - default: - return ''; - } - }; - useEffect(() => { - fetchLedgerConfig(); setSeed(nanoid(32)); }, []); + useEffect(() => { + if (havePrivateKey) { + setPrivateKeyValue(''); + setWalletErrorMessage(null); + setGeneratedKeys(null); + } else { + setPrivateKeyValue(''); + setWalletErrorMessage(null); + } + }, [havePrivateKey]); + const validations = { - method: yup.string().required('Method is required').trim(), - ledger: yup.string(), - network: yup.string(), - domain: yup.string(), - privatekey: yup.string(), + ...(DidMethod.WEB === method) && { domain: yup.string().required('Domain is required') }, + ...(DidMethod.POLYGON === method) && { privatekey: yup.string().required('Private key is required').trim().length(64, 'Private key must be exactly 64 characters long') }, }; - if (selectedMethod === DidMethod.WEB) { - validations['domain'] = yup.string().required('Domain is required').trim(); - } - - if (selectedMethod === DidMethod.POLYGON) { - (validations['network'] = yup - .string() - .required('Network is required') - .trim()), - (validations['privatekey'] = yup - .string() - .required('Private key is required') - .trim() - .length(64, 'Private key must be exactly 64 characters long')); - } - - if (selectedMethod === DidMethod.INDY) { - (validations['ledger'] = yup.string().required('Ledger is required')), - (validations['network'] = yup.string().required('Network is required')); - } - return ( { - props.setOpenModal(false); + onClose={() => { setErrMsg(null); + setGeneratedKeys(null); + setHavePrivateKey(false); + props.setOpenModal(false); }} > Create DID { setErrMsg(null); @@ -235,11 +227,11 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { /> { values: IFormikValues, { resetForm }: FormikActions, ) => { - const didMethodValue = showMethod( - values.method, - values.ledger, - values.method, - values.network, - ); - const didMethodName = didMethodValue - .split(':') - .slice(0, 2) - .join(':'); - let selectedLedgerName; - - switch (didMethodName) { - case 'did:indy': - selectedLedgerName = didMethodValue - .split(':') - .slice(-2) - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - - break; - - case 'did:polygon': - selectedLedgerName = didMethodValue - .split(':') - .slice(1) - .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) - .join(' '); - - break; - - case 'did:web': - case 'did:key': - selectedLedgerName = didMethodValue.split(':')[1]; - - break; - - default: - console.error('Unsupported DID format'); - } - - if (ledgerName !== selectedLedgerName) { - setErrMsg('This ledger is not applicable to create a DID'); - } else { - await createNewDid(values); - setErrMsg(null); - window.location.reload(); - } + await createNewDid(values); + window.location.reload(); + }} > {(formikHandlers): JSX.Element => { return (
+
- - {formikHandlers?.errors?.method && - formikHandlers?.touched?.method && ( - - {formikHandlers?.errors?.method} - - )} +
- {formikHandlers.values.method !== DidMethod.POLYGON && - formikHandlers.values.method !== DidMethod.KEY && - formikHandlers.values.method !== DidMethod.WEB && ( -
- - - {formikHandlers?.errors?.ledger && - formikHandlers?.touched?.ledger && ( - - {formikHandlers?.errors?.ledger} - - )} -
- )} + {formikHandlers.values.method !== DidMethod.KEY && ( + +
+ + { + formikHandlers.handleChange(e); + setMethod(e.target.value); + }} + + id="method" + name="method" + className="bg-gray-50 text-gray-600 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:placeholder-gray-400 dark:text-white h-11" + readOnly + > + +
+ )} {formikHandlers.values.method !== DidMethod.WEB && formikHandlers.values.method !== DidMethod.KEY && ( @@ -389,185 +306,222 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { htmlFor="network" className="text-sm font-medium text-gray-900 dark:text-gray-300" > - Network - * + Network * - - {formikHandlers?.errors?.network && - formikHandlers?.touched?.network && ( - - {formikHandlers?.errors?.network} - - )} +
)} - {formikHandlers.values.method === DidMethod.POLYGON && ( -
- {formikHandlers.values.method === DidMethod.POLYGON && ( - - generatePolygonKeyValuePair() - } - /> - )} - {generatedKeys && ( -
-

- - Private Key: - -

- -
-

- -

- - Address: - -

- -
-

-
- )} - - {generatedKeys && - formikHandlers.values.method === DidMethod.POLYGON && ( - - )} - - {formikHandlers.values.method === DidMethod.POLYGON && ( -
-
- - -
- {formikHandlers?.errors?.privatekey && - formikHandlers?.touched?.privatekey && ( - - {formikHandlers?.errors?.privatekey} - - )} -
- )} -
- )} {formikHandlers.values.method === DidMethod.WEB && (
- + Domain * + -
- {formikHandlers?.errors?.domain && - formikHandlers?.touched?.domain && ( - - {formikHandlers?.errors?.domain} + {formikHandlers.errors?.domain && formikHandlers.touched?.domain && ( + + {formikHandlers.errors?.domain} )} + +
)} -
+
+ -
+ + + {formikHandlers.values.method === DidMethod.POLYGON && ( + <> +
+
+ setHavePrivateKey(e.target.checked)} /> + +
+ {!havePrivateKey ? ( + <> +
+
+
+ + +
+ {generatedKeys && ( + <> +
+ + +
+ +
+
+ + + {formikHandlers?.errors?.privatekey && + formikHandlers?.touched?.privatekey && ( + + {formikHandlers?.errors?.privatekey} + + )} + {walletErrorMessage && ( + + {walletErrorMessage} + + )} + +
+

+ + Address: + +

+ +
+

+
+ + )} + + ) : ( + <> + { + formikHandlers.setFieldValue('privatekey', e.target.value); + } + } + placeholder="Enter private key" /> +
+
+ {formikHandlers?.errors?.privatekey && + formikHandlers?.touched?.privatekey && ( + + {formikHandlers?.errors?.privatekey} + + )} + {walletErrorMessage && ( + + {walletErrorMessage} + + )} + + + )} +
+
+ +
    +
  1. + Step 1: +
    Copy the address and get the free tokens for the testnet.
    For eg. use https://faucet.polygon.technology/  + to get free token +
    +
    +
  2. +
  3. + Step 2: +
    Check that you have recieved the tokens.
    +
    For eg. copy the address and check the balance on + +
    +
  4. +
+
+ + )}
); }} +
- ); + + ) }; export default CreateDIDModal; diff --git a/src/components/organization/configuration-settings/DidList.tsx b/src/components/organization/configuration-settings/DidList.tsx index 55abea91a..d0e74f830 100644 --- a/src/components/organization/configuration-settings/DidList.tsx +++ b/src/components/organization/configuration-settings/DidList.tsx @@ -1,20 +1,21 @@ import React, { useEffect, useState } from "react" -import BreadCrumbs from '../../BreadCrumbs' import { Button } from "flowbite-react" import CopyDid from '../../../commonComponents/CopyDid' import CreateDidPopup from "./CreateDid" -import { getDids, updatePrimaryDid } from "../../../api/organization" +import { getDids, getOrganizationById, updatePrimaryDid } from "../../../api/organization" import { getFromLocalStorage } from "../../../api/Auth" import { apiStatusCodes, storageKeys } from "../../../config/CommonConstant" import type { AxiosResponse } from "axios" import { AlertComponent } from "../../AlertComponent" import type { IDidList, IUpdatePrimaryDid } from "../interfaces" +import { Roles } from "../../../utils/enums/roles" const DIDList = () => { const [didList, setDidList] = useState([]); const [showPopup, setShowPopup] = useState(false); const [erroMsg, setErrMsg] = useState(null); const [successMsg, setSuccessMsg] = useState(null); + const [role, setRole] = useState(null); const setPrimaryDid = async (id: string, did: string) => { try { @@ -35,6 +36,23 @@ const DIDList = () => { } } + const fetchOrganizationDetails = async () => { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + + const ownerRole = data?.data?.userOrgRoles.find(role => role?.orgRole.name === "owner"); + + const ownerRoleName = ownerRole ? ownerRole.orgRole.name : null; + + setRole(ownerRoleName); + + } else { + console.error('Error in fetching organization:::'); + } + }; + const getData = async () => { try { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); @@ -49,12 +67,13 @@ const DIDList = () => { setDidList(sortedDids) } } catch (error) { - console.log("ERROR::::", error); + console.error("ERROR::::", error); } } useEffect(() => { getData(); + fetchOrganizationDetails(); }, []) return ( @@ -72,6 +91,7 @@ const DIDList = () => {

DID Details

- - - )} - - - ) : ( - { - submitSharedWallet( - values, - privateKeyValue, - domainValue, - endPointValue, - ); - }} - > - {(formikHandlers) => ( -
-
-
-
+
+
+
- - {formikHandlers?.errors?.seed && - formikHandlers?.touched?.seed && ( - - {formikHandlers?.errors?.seed} - - )} -
+ + {formikHandlers?.errors?.did && + formikHandlers?.touched?.did && ( + + {formikHandlers?.errors?.did} + + )} +
-
-
-
+
+
+ +
+ {mappedData && + Object.keys(mappedData).map((ledger) => ( +
+ { + formikHandlers.handleChange(e); + handleLedgerChange(e); + setSelectedLedger(ledger); + setSelectedMethod(''); + setSeedVal(seeds); + setSelectedDid(''); + }} + className="mr-2" + /> + +
+ ))}
- - - {formikHandlers?.errors?.did && - formikHandlers?.touched?.did && ( - - {formikHandlers?.errors?.did} - - )} + {formikHandlers.errors.ledger && formikHandlers.touched.ledger && ( + + {formikHandlers.errors.ledger} + + )}
-
+
- - {formikHandlers?.errors?.method && - formikHandlers?.touched?.method && ( - - {formikHandlers?.errors?.method} - - )} +
+ {renderMethodOptions(formikHandlers)} +
+ {formikHandlers.errors.method && formikHandlers.touched.method && ( + + {formikHandlers.errors.method} + + )}
- {formikHandlers.values.method === DidMethod.POLYGON && ( - - generatePolygonKeyValuePair()}/> - + {selectedLedger !== 'noLedger' && ( +
+ +
+ {renderNetworkOptions(formikHandlers)} +
+ {formikHandlers.errors.network && formikHandlers.touched.network && ( + + {formikHandlers.errors.network} + + )} +
)} - {generatedKeys && ( -
-

- - Private Key: - -

- -
-

+ {selectedLedger !== 'noLedger' && ( -

- - Public Key Base58: - -

- -
-

-

- - Address: - -

- -
-

+
+ +
)} - {generatedKeys && - formikHandlers.values.method === DidMethod.POLYGON && ( +
+ + {selectedMethod === DidMethod.WEB && ( + - )} - {formikHandlers.values.method === DidMethod.POLYGON && ( - - setPrivateKeyValue(val)} privateKeyValue={privateKeyValue} formikHandlers={formikHandlers}/> - )} +
+
+
- setDomainValue(val)} domainValue={domainValue} formikHandlers={formikHandlers}/> +
+
+ {selectedMethod === 'did:polygon' && ( + <>
+ +
+
+ +
    +
  1. + Step 1: +
    + Copy the address and get the free tokens for the testnet. +
    For eg. use  + + `https://faucet.polygon.technology/`  + + to get free token +
    +
    +
  2. +
  3. + Step 2: +
    Check that you have recieved the tokens.
    +
    For eg. copy the address and check the balance on + +
    +
  4. +
+
)} +
- {formikHandlers.values.method !== DidMethod.POLYGON && - formikHandlers.values.method !== DidMethod.KEY && - formikHandlers.values.method !== DidMethod.WEB && ( -
- - - {formikHandlers?.errors?.ledger && - formikHandlers?.touched?.ledger && ( - - {formikHandlers?.errors?.ledger} - - )} -
- )} - {formikHandlers.values.method !== DidMethod.WEB && - formikHandlers.values.method !== DidMethod.KEY && ( - - < LedgerLessMethodsComponents formikHandlers={formikHandlers} setSelectedDid={(val: string) => setSelectedDid(val)} selectedDid={selectedDid} mappedData={mappedData} selectedLedger={selectedLedger} selectedNetwork={selectedNetwork} /> - )} - -
- -
- {showMethod(formikHandlers.values.method)} -
-
-
-
-
- -
- - )} - - )} +
+ +
+ + )} +
); + }; export default SharedAgentForm; \ No newline at end of file diff --git a/src/components/organization/walletCommonComponents/TokenWarningMessage.tsx b/src/components/organization/walletCommonComponents/TokenWarningMessage.tsx index 77a01c5cf..2348a51e9 100644 --- a/src/components/organization/walletCommonComponents/TokenWarningMessage.tsx +++ b/src/components/organization/walletCommonComponents/TokenWarningMessage.tsx @@ -1,14 +1,12 @@ -import React from "react"; - const TokenWarningMessage = () => ( - + (false); const [failure, setFailure] = useState(null); const [seeds, setSeeds] = useState(''); - - useEffect(() => { - setSeeds(nanoid(32)); - }, []); + const [maskedSeeds, setMaskedSeeds] = useState(''); + + const maskSeeds = (seed: string) => { + const visiblePart = seed.slice(0, -10); + const maskedPart = seed.slice(-10).replace(/./g, '*'); + return visiblePart + maskedPart; + }; + + useEffect(() => { + const generatedSeeds = nanoid(32); + const masked = maskSeeds(generatedSeeds); + setSeeds(generatedSeeds); + setMaskedSeeds(masked); + }, []); + const onRadioSelect = (type: string) => { setAgentType(type); @@ -84,19 +93,24 @@ const WalletSpinup = (props: { domain: string, ) => { setLoading(true); + const ledgerName = values?.network?.split(":")[2] + const network = values?.network?.split(":").slice(2).join(":"); + const polygonNetwork = values?.network?.split(":").slice(1).join(":"); + const payload = { keyType: values.keyType || 'ed25519', - method: values.method || '', - ledger: values.method === DidMethod.INDY ? values.ledger : '', + method: values.method.split(':')[1] || '', + ledger: values.method === DidMethod.INDY ? ledgerName : '', label: values.label, privatekey: values.method === DidMethod.POLYGON ? privatekey : '', - seed: values.method === DidMethod.POLYGON ? '' : values.seed || seeds, + seed: values.method === DidMethod.POLYGON ? '' : values?.seed || seeds, network: values.method === DidMethod.POLYGON - ? `${values?.method}:${values?.network}` - : `${values?.ledger}:${values?.network}`, + ? polygonNetwork + : network, domain: values.method === DidMethod.WEB ? domain : '', role: values.method === DidMethod.INDY ? values?.role ?? 'endorser' : '', + did: values?.did ?? '', endorserDid: values?.endorserDid ?? '', clientSocketId: SOCKET.id, }; @@ -182,6 +196,7 @@ const WalletSpinup = (props: { if (agentType === AgentType.SHARED) { formComponent = (
-
+
+
{!agentSpinupCall && !loading && ( -
-
    +
    +
    )} +
+ {formComponent}
-
- {agentType === AgentType.DEDICATED ? ( - - ) : ( - - )} -
); diff --git a/src/components/organization/walletCommonComponents/interfaces/index.tsx b/src/components/organization/walletCommonComponents/interfaces/index.tsx index 5dd278085..5cb29647d 100644 --- a/src/components/organization/walletCommonComponents/interfaces/index.tsx +++ b/src/components/organization/walletCommonComponents/interfaces/index.tsx @@ -40,14 +40,14 @@ export interface IPolygonKeys { export interface ISharedAgentForm { seeds: string; + maskedSeeds: string; isCopied: boolean; orgName: string; loading: boolean; submitSharedWallet: ( values: IValuesShared, privatekey: string, - domain: string, - endPoint: string, + domain: string ) => void; } diff --git a/src/config/envConfig.ts b/src/config/envConfig.ts index b979c8036..a8ea0b9b6 100644 --- a/src/config/envConfig.ts +++ b/src/config/envConfig.ts @@ -17,7 +17,7 @@ if (import.meta.env) { } } -const { PUBLIC_BASE_URL, PUBLIC_CRYPTO_PRIVATE_KEY,PUBLIC_SHOW_NAME_AS_LOGO, PUBLIC_PLATFORM_NAME, PUBLIC_PLATFORM_LOGO, PUBLIC_POWERED_BY, PUBLIC_PLATFORM_WEB_URL, PUBLIC_POWERED_BY_URL, PUBLIC_PLATFORM_DOCS_URL, PUBLIC_PLATFORM_GIT, PUBLIC_PLATFORM_SUPPORT_EMAIL, PUBLIC_PLATFORM_TWITTER_URL, PUBLIC_PLATFORM_SUPPORT_INVITE, PUBLIC_PLATFORM_DISCORD_URL, PUBLIC_ALLOW_DOMAIN }: any = envVariables; +const { PUBLIC_BASE_URL, PUBLIC_POLYGON_TESTNET_URL, PUBLIC_POLYGON_MAINNET_URL, PUBLIC_CRYPTO_PRIVATE_KEY,PUBLIC_SHOW_NAME_AS_LOGO, PUBLIC_PLATFORM_NAME, PUBLIC_PLATFORM_LOGO, PUBLIC_POWERED_BY, PUBLIC_PLATFORM_WEB_URL, PUBLIC_POWERED_BY_URL, PUBLIC_PLATFORM_DOCS_URL, PUBLIC_PLATFORM_GIT, PUBLIC_PLATFORM_SUPPORT_EMAIL, PUBLIC_PLATFORM_TWITTER_URL, PUBLIC_PLATFORM_SUPPORT_INVITE, PUBLIC_PLATFORM_DISCORD_URL, PUBLIC_ALLOW_DOMAIN }: any = envVariables; export const envConfig = { PUBLIC_BASE_URL: @@ -28,6 +28,13 @@ export const envConfig = { PLATFORM_DATA: { nameAsLogo: PUBLIC_SHOW_NAME_AS_LOGO || import.meta.env.PUBLIC_SHOW_NAME_AS_LOGO, + + polygonTestnet: + PUBLIC_POLYGON_TESTNET_URL || import.meta.env.PUBLIC_POLYGON_TESTNET_URL, + + polygonMainnet: + PUBLIC_POLYGON_MAINNET_URL || import.meta.env.PUBLIC_POLYGON_MAINNET_URL, + name: PUBLIC_PLATFORM_NAME || import.meta.env.PUBLIC_PLATFORM_NAME, logo: From a41d5aaba122909f070681fe05eeb34fa8e9eaf0 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Sun, 9 Jun 2024 16:38:59 +0530 Subject: [PATCH 03/54] fixed CSS and avatar issues (#696) * fix:css issues Signed-off-by: pranalidhanavade * fix:sonarcloud issues Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- .../Authentication/ResetPassword.tsx | 36 +++++++++++++++++-- .../EcosystemInvite/EcoInvitationList.tsx | 4 +-- .../invitations/ReceivedInvitations.tsx | 4 +-- src/components/publicProfile/OrgUserInfo.tsx | 4 +-- .../OrganisationPublicProfile.tsx | 4 +-- .../publicProfile/ProfilesDesign.tsx | 4 +-- 6 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/components/Authentication/ResetPassword.tsx b/src/components/Authentication/ResetPassword.tsx index 6d6864d68..44848ad4a 100644 --- a/src/components/Authentication/ResetPassword.tsx +++ b/src/components/Authentication/ResetPassword.tsx @@ -26,6 +26,37 @@ interface passwordValues { confirmPassword: string; } + +const EyeIconVisible = () => ( + + + + +); + +const EyeIconInvisible = () => ( + + + + +); + const ResetPassword = () => { const [loading, setLoading] = useState(false); const [erroMsg, setErrMsg] = useState(null); @@ -189,9 +220,10 @@ const ResetPassword = () => { className="bg-transparent absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500 dark:text-white hover:text-gray-800 dark:hover:text-white" > {passwordVisible ? ( - + ) : ( - + + )}
diff --git a/src/components/EcosystemInvite/EcoInvitationList.tsx b/src/components/EcosystemInvite/EcoInvitationList.tsx index b0348d574..c9e0575ff 100644 --- a/src/components/EcosystemInvite/EcoInvitationList.tsx +++ b/src/components/EcosystemInvite/EcoInvitationList.tsx @@ -9,9 +9,9 @@ const EcoInvitationList = (props: InvitationProps) => {
{logoUrl ? ( - + ) : ( - + )}
diff --git a/src/components/organization/invitations/ReceivedInvitations.tsx b/src/components/organization/invitations/ReceivedInvitations.tsx index 983fd4b3f..9e0e71128 100644 --- a/src/components/organization/invitations/ReceivedInvitations.tsx +++ b/src/components/organization/invitations/ReceivedInvitations.tsx @@ -160,12 +160,12 @@ const ReceivedInvitations = () => {
{invitation.organisation.logoUrl ? ( ) : ( )} diff --git a/src/components/publicProfile/OrgUserInfo.tsx b/src/components/publicProfile/OrgUserInfo.tsx index 056d98ba3..f6121ff9e 100644 --- a/src/components/publicProfile/OrgUserInfo.tsx +++ b/src/components/publicProfile/OrgUserInfo.tsx @@ -22,9 +22,9 @@ const OrgUserInfo = ({orgUsersData}) => { <> {orgUser?.profileImg ? ( - + ) : ( - + )}
{orgUser.firstName} {orgUser.lastName} diff --git a/src/components/publicProfile/OrganisationPublicProfile.tsx b/src/components/publicProfile/OrganisationPublicProfile.tsx index a36174459..028cf9299 100644 --- a/src/components/publicProfile/OrganisationPublicProfile.tsx +++ b/src/components/publicProfile/OrganisationPublicProfile.tsx @@ -122,9 +122,9 @@ const OrganisationPublicProfile = () => {
{org.logoUrl ? ( - + ) : ( - + )}
diff --git a/src/components/publicProfile/ProfilesDesign.tsx b/src/components/publicProfile/ProfilesDesign.tsx index 01e9ac2a1..22935d40e 100644 --- a/src/components/publicProfile/ProfilesDesign.tsx +++ b/src/components/publicProfile/ProfilesDesign.tsx @@ -8,9 +8,9 @@ const ProfilesDesign = ({ orgData }: IExploreOrg) => {
{orgData?.logoUrl ? ( - + ) : ( - + )}

{orgData?.name}

From eb793d8686d3103fcd02f9cfa90ed9f33a149a5f Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:17:16 +0530 Subject: [PATCH 04/54] feat: support no ledger (#697) * wip: implemented ui for method selection Signed-off-by: bhavanakarwade * wip: modified no ledger screen Signed-off-by: bhavanakarwade * feat: modified ui for create did popup Signed-off-by: bhavanakarwade * refactor: worked on error handling Signed-off-by: bhavanakarwade * fix: validations on create did popup Signed-off-by: bhavanakarwade * refactor: mapping function Signed-off-by: bhavanakarwade * fix: resolved sonarlint issues Signed-off-by: bhavanakarwade * fix: input box issues Signed-off-by: bhavanakarwade * refactor: handled role wise conditions Signed-off-by: bhavanakarwade * fix: resolved code duplication errors Signed-off-by: bhavanakarwade * fix: enum values Signed-off-by: bhavanakarwade * fix: added loading chnages Signed-off-by: bhavanakarwade * fix: ledgerconfig object changes Signed-off-by: bhavanakarwade * fix: resolved type errors Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- .../configuration-settings/CreateDid.tsx | 70 +++++++----- .../configuration-settings/DidList.tsx | 10 +- .../GenerateBtnPolygon.tsx | 6 +- .../SetPrivateKeyValue.tsx | 6 +- .../walletCommonComponents/SharedAgent.tsx | 101 ++++++++++++------ 5 files changed, 123 insertions(+), 70 deletions(-) diff --git a/src/components/organization/configuration-settings/CreateDid.tsx b/src/components/organization/configuration-settings/CreateDid.tsx index bf9ede751..2171810db 100644 --- a/src/components/organization/configuration-settings/CreateDid.tsx +++ b/src/components/organization/configuration-settings/CreateDid.tsx @@ -1,20 +1,21 @@ import * as yup from 'yup'; import { Button, Checkbox, Label, Modal } from 'flowbite-react'; import { Field, Form, Formik } from 'formik'; -import type { FormikHelpers as FormikActions } from 'formik'; +import type { FormikHelpers as FormikActions, FormikProps } from 'formik'; import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { AlertComponent } from '../../AlertComponent'; import type { AxiosResponse } from 'axios'; import { createDid, getOrganizationById } from '../../../api/organization'; import type { EditOrgdetailsModalProps, IFormikValues } from '../interfaces'; import { createPolygonKeyValuePair } from '../../../api/Agent'; -import { DidMethod } from '../../../common/enums'; import { nanoid } from 'nanoid'; import TokenWarningMessage from '../walletCommonComponents/TokenWarningMessage'; import CopyDid from '../../../commonComponents/CopyDid'; import { getFromLocalStorage } from '../../../api/Auth'; import { ethers } from 'ethers'; +import { envConfig } from '../../../config/envConfig'; +import { CommonConstants, Network, DidMethod } from '../../../common/enums'; interface IPolygonKeys { privateKey: string; @@ -24,6 +25,7 @@ interface IPolygonKeys { const CreateDIDModal = (props: EditOrgdetailsModalProps) => { const [loading, setLoading] = useState(false); + const [isLoading, setIsLoading] = useState(false); const [errMsg, setErrMsg] = useState(null); const [successMsg, setSuccessMsg] = useState(null); const [seed, setSeed] = useState(''); @@ -36,6 +38,8 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { const [privateKeyValue, setPrivateKeyValue] = useState(''); const [walletErrorMessage, setWalletErrorMessage] = useState(null); + const formikRef = useRef>(null); + const fetchOrganizationDetails = async () => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); const response = await getOrganizationById(orgId); @@ -89,11 +93,17 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { }, []); - const checkBalance = async (privateKey: string) => { + const checkBalance = async (privateKey: string, network: Network) => { try { - const testnetUrl = 'https://rpc-amoy.polygon.technology'; - const provider = new ethers.JsonRpcProvider(testnetUrl) + const rpcUrls = { + testnet: `${envConfig.PLATFORM_DATA.polygonTestnet}`, + mainnet: `${envConfig.PLATFORM_DATA.polygonMainnet}` + }; + + const networkUrl = rpcUrls?.[network]; + + const provider = new ethers.JsonRpcProvider(networkUrl) const wallet = new ethers.Wallet(privateKey, provider); const address = await wallet.getAddress(); @@ -101,7 +111,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { const etherBalance = ethers.formatEther(balance); - if (parseFloat(etherBalance) < 0.01) { + if (parseFloat(etherBalance) < CommonConstants.BALANCELIMIT) { setWalletErrorMessage('You have insufficient funds.'); } else { setWalletErrorMessage(null); @@ -117,7 +127,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { useEffect(() => { if (privateKeyValue && privateKeyValue.length === 64) { - checkBalance(privateKeyValue); + checkBalance(privateKeyValue, Network.TESTNET); } else { setWalletErrorMessage(null); } @@ -169,6 +179,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { }; const generatePolygonKeyValuePair = async () => { + setIsLoading(true); try { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); const resCreatePolygonKeys = await createPolygonKeyValuePair(orgId); @@ -176,9 +187,10 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { setGeneratedKeys(data?.data); + setIsLoading(false); const privateKey = data?.data?.privateKey.slice(2) - setPrivateKeyValue( privateKeyValue || privateKey); - await checkBalance(privateKeyValue || privateKey); + setPrivateKeyValue(privateKeyValue || privateKey); + await checkBalance(privateKeyValue || privateKey, Network.TESTNET); } } catch (err) { console.error('Generate private key ERROR::::', err); @@ -208,11 +220,12 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { return ( { + onClose={() => { setErrMsg(null); setGeneratedKeys(null); setHavePrivateKey(false); props.setOpenModal(false); + formikRef.current?.resetForm(); }} > Create DID @@ -226,6 +239,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { }} /> { await createNewDid(values); window.location.reload(); - + }} > {(formikHandlers): JSX.Element => { @@ -390,6 +404,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => {
@@ -516,7 +533,6 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { ); }} - diff --git a/src/components/organization/configuration-settings/DidList.tsx b/src/components/organization/configuration-settings/DidList.tsx index d0e74f830..1205bb1bc 100644 --- a/src/components/organization/configuration-settings/DidList.tsx +++ b/src/components/organization/configuration-settings/DidList.tsx @@ -15,7 +15,7 @@ const DIDList = () => { const [showPopup, setShowPopup] = useState(false); const [erroMsg, setErrMsg] = useState(null); const [successMsg, setSuccessMsg] = useState(null); - const [role, setRole] = useState(null); + const [roleName, setRoleName] = useState(null); const setPrimaryDid = async (id: string, did: string) => { try { @@ -43,10 +43,9 @@ const DIDList = () => { if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { const ownerRole = data?.data?.userOrgRoles.find(role => role?.orgRole.name === "owner"); - + const ownerRoleName = ownerRole ? ownerRole.orgRole.name : null; - - setRole(ownerRoleName); + setRoleName(ownerRoleName); } else { console.error('Error in fetching organization:::'); @@ -91,12 +90,11 @@ const DIDList = () => {

DID Details

- +
{ diff --git a/src/components/organization/walletCommonComponents/GenerateBtnPolygon.tsx b/src/components/organization/walletCommonComponents/GenerateBtnPolygon.tsx index 3101b7413..f073021d0 100644 --- a/src/components/organization/walletCommonComponents/GenerateBtnPolygon.tsx +++ b/src/components/organization/walletCommonComponents/GenerateBtnPolygon.tsx @@ -2,10 +2,11 @@ import React from "react" import { Button, Label } from 'flowbite-react'; interface IProps { - generatePolygonKeyValuePair:()=>void + generatePolygonKeyValuePair:()=>void; + loading: boolean; } -const GenerateButtonPolygon = ({generatePolygonKeyValuePair}:IProps) =>( +const GenerateButtonPolygon = ({generatePolygonKeyValuePair, loading}:IProps) =>(
@@ -15,6 +16,7 @@ const GenerateButtonPolygon = ({generatePolygonKeyValuePair}:IProps) =>(
{!havePrivateKey ? ( <> - generatePolygonKeyValuePair()} /> + generatePolygonKeyValuePair()} loading={loading}/> {generatedKeys && ( <> diff --git a/src/components/organization/walletCommonComponents/SharedAgent.tsx b/src/components/organization/walletCommonComponents/SharedAgent.tsx index 5a5fc68b1..85a7ec429 100644 --- a/src/components/organization/walletCommonComponents/SharedAgent.tsx +++ b/src/components/organization/walletCommonComponents/SharedAgent.tsx @@ -1,6 +1,6 @@ import { Button, Label, Checkbox } from "flowbite-react"; import { Field, Form, Formik } from "formik"; -import { useState, useEffect } from "react"; +import { useState, useEffect, type ChangeEvent } from "react"; import { getLedgerConfig, getLedgers } from "../../../api/Agent"; import { apiStatusCodes } from "../../../config/CommonConstant"; import * as yup from 'yup'; @@ -10,7 +10,31 @@ import { DidMethod } from '../../../common/enums'; import SetDomainValueInput from './SetDomainValueInput'; import SetPrivateKeyValueInput from './SetPrivateKeyValue'; import type { ISharedAgentForm, IValuesShared } from "./interfaces"; -import type { ILedgerDetails, ILedgerItem } from "../interfaces"; + +interface IDetails { + [key: string]: string | { [subKey: string]: string }; +} + +interface ILedgerItem { + name: string; + details: IDetails; +} + +interface ILedgerConfigData { + indy: { + 'did:indy': { + [key: string]: string; + }; + }; + polygon: { + 'did:polygon': { + [key: string]: string; + }; + }; + noLedger: { + [key: string]: string; + }; +} const SharedAgentForm = ({ orgName, @@ -30,47 +54,57 @@ const SharedAgentForm = ({ const fetchLedgerConfig = async () => { try { - const { data } = await getLedgerConfig(); - + const { data } = await getLedgerConfig() as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const ledgerConfigData = { + const ledgerConfigData: ILedgerConfigData = { indy: { - 'did:indy': {} + 'did:indy': {} }, polygon: { - 'did:polygon': {} + 'did:polygon': {} }, noLedger: {} - }; - - data.data.forEach(({ name, details }: ILedgerItem) => { + }; + + data.data.forEach(({ name, details }: ILedgerItem) => { const lowerName = name.toLowerCase(); - - if (lowerName === 'indy') { - for (const [key, subDetails] of Object.entries(details)) { - if (typeof subDetails === 'object') { - for (const [subKey, value] of Object.entries(subDetails)) { - ledgerConfigData.indy['did:indy'][`${key}:${subKey}`] = value; - } + + if (lowerName === 'indy' && details) { + for (const [key, subDetails] of Object.entries(details)) { + if (typeof subDetails === 'object' && subDetails !== null) { + for (const [subKey, value] of Object.entries(subDetails)) { + const formattedKey = `${key}:${subKey}`.replace('did:indy:', ''); + ledgerConfigData.indy['did:indy'][formattedKey] = value; + } + } } - } - } else if (lowerName === 'polygon') { - for (const [subKey, value] of Object.entries(details)) { - if (typeof value === 'string') { - ledgerConfigData.polygon['did:polygon'][subKey] = value; + } else if (lowerName === 'polygon' && details) { + for (const [key, value] of Object.entries(details)) { + if (typeof value === 'object' && value !== null) { + for (const [subKey, subValue] of Object.entries(value)) { + ledgerConfigData.polygon['did:polygon'][subKey] = subValue; + } + } else if (typeof value === 'string') { + ledgerConfigData.polygon['did:polygon'][key] = value; + } + } + } else if (lowerName === 'noledger' && details) { + for (const [key, value] of Object.entries(details)) { + ledgerConfigData.noLedger[key] = value as string; } - } - } else if (lowerName === 'key' || lowerName === 'web') { - ledgerConfigData.noLedger[`did:${lowerName}`] = details[lowerName as keyof ILedgerDetails] as string; } - }); - setMappedData(ledgerConfigData); + }); + + + setMappedData(ledgerConfigData); } } catch (err) { console.error('Fetch Network ERROR::::', err); } }; - + + const fetchNetworks = async () => { try { const { data } = (await getLedgers()) as AxiosResponse; @@ -83,18 +117,18 @@ const SharedAgentForm = ({ } }; - const handleLedgerChange = (e) => { + const handleLedgerChange = (e: ChangeEvent) => { setSelectedLedger(e.target.value); setSelectedMethod(''); setSelectedDid(''); }; - const handleMethodChange = (e) => { + const handleMethodChange = (e: ChangeEvent) => { setSelectedMethod(e.target.value); setSelectedDid(''); }; - const handleNetworkChange = (e) => { + const handleNetworkChange = (e: ChangeEvent) => { const didMethod = `${e.target.value}`; setSelectedDid(didMethod); }; @@ -131,7 +165,7 @@ const SharedAgentForm = ({ ...(DidMethod.POLYGON === selectedMethod) && { privatekey: yup.string().required('Private key is required').trim().length(64, 'Private key must be exactly 64 characters long') }, }; - const renderMethodOptions = (formikHandlers) => { + const renderMethodOptions = (formikHandlers: { handleChange: (e: React.ChangeEvent) => void }) => { if (!selectedLedger) { return null; } @@ -164,7 +198,7 @@ const SharedAgentForm = ({ )); }; - const renderNetworkOptions = (formikHandlers) => { + const renderNetworkOptions = (formikHandlers: { handleChange: (e: React.ChangeEvent) => void }) => { if (!selectedLedger || !selectedMethod) { return null; } @@ -234,6 +268,7 @@ const SharedAgentForm = ({ domain: '', privatekey: '', label: orgName, + keyType: '' }} validationSchema={yup.object().shape(validations)} onSubmit={(values: IValuesShared) => { From 252b504b3b156f08d8064029349b5be59bf70382 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Fri, 14 Jun 2024 09:20:14 +0530 Subject: [PATCH 05/54] refactor: responsiveness of the component (#699) * wip: implemented ui for method selection Signed-off-by: bhavanakarwade * wip: modified no ledger screen Signed-off-by: bhavanakarwade * feat: modified ui for create did popup Signed-off-by: bhavanakarwade * refactor: worked on error handling Signed-off-by: bhavanakarwade * fix: validations on create did popup Signed-off-by: bhavanakarwade * refactor: mapping function Signed-off-by: bhavanakarwade * fix: resolved sonarlint issues Signed-off-by: bhavanakarwade * fix: input box issues Signed-off-by: bhavanakarwade * refactor: handled role wise conditions Signed-off-by: bhavanakarwade * fix: resolved code duplication errors Signed-off-by: bhavanakarwade * fix: enum values Signed-off-by: bhavanakarwade * fix: added loading chnages Signed-off-by: bhavanakarwade * fix: ledgerconfig object changes Signed-off-by: bhavanakarwade * fix: resolved type errors Signed-off-by: bhavanakarwade * refactor: modified responsiveness of the component Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- .../GenerateBtnPolygon.tsx | 2 +- .../SetDomainValueInput.tsx | 2 +- .../SetPrivateKeyValue.tsx | 60 +++++++++++-------- .../walletCommonComponents/SharedAgent.tsx | 41 +++++++------ .../walletCommonComponents/WalletSpinup.tsx | 11 ++-- .../interfaces/index.tsx | 1 - 6 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/components/organization/walletCommonComponents/GenerateBtnPolygon.tsx b/src/components/organization/walletCommonComponents/GenerateBtnPolygon.tsx index f073021d0..f71379f9a 100644 --- a/src/components/organization/walletCommonComponents/GenerateBtnPolygon.tsx +++ b/src/components/organization/walletCommonComponents/GenerateBtnPolygon.tsx @@ -8,7 +8,7 @@ interface IProps { const GenerateButtonPolygon = ({generatePolygonKeyValuePair, loading}:IProps) =>( -
+
{!havePrivateKey ? ( <> - generatePolygonKeyValuePair()} loading={loading}/> + generatePolygonKeyValuePair()} loading={loading} /> {generatedKeys && ( <> @@ -127,7 +127,7 @@ const SetPrivateKeyValueInput = ({ type="text" id="privatekey" name="privatekey" - className="truncate bg-gray-50 border mt-2 border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-[480px] p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 h-11" + className="truncate bg-gray-50 border mt-2 border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-[480px] p-2.5 dark:bg-gray-700 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 h-11" value={generatedKeys.privateKey.slice(2)} placeholder="Generated private key" readOnly /> @@ -140,13 +140,16 @@ const SetPrivateKeyValueInput = ({
- {errorMessage && ( - - {errorMessage} - - )} +
+ {errorMessage && ( + + {errorMessage} + + )} + +
- +

@@ -159,25 +162,34 @@ const SetPrivateKeyValueInput = ({

- + )} ) : ( <> - ) => { - setPrivateKeyValue(e.target.value); - formikHandlers.handleChange(e); - }} - onBlur={formikHandlers.handleBlur} - placeholder="Enter private key" /> - +
+ + ) => { + setPrivateKeyValue(e.target.value); + formikHandlers.handleChange(e); + }} + onBlur={formikHandlers.handleBlur} + placeholder="Enter private key" /> + +
+ +
+
{formikHandlers.errors?.privatekey && formikHandlers.touched?.privatekey && diff --git a/src/components/organization/walletCommonComponents/SharedAgent.tsx b/src/components/organization/walletCommonComponents/SharedAgent.tsx index 85a7ec429..a66583e32 100644 --- a/src/components/organization/walletCommonComponents/SharedAgent.tsx +++ b/src/components/organization/walletCommonComponents/SharedAgent.tsx @@ -10,7 +10,6 @@ import { DidMethod } from '../../../common/enums'; import SetDomainValueInput from './SetDomainValueInput'; import SetPrivateKeyValueInput from './SetPrivateKeyValue'; import type { ISharedAgentForm, IValuesShared } from "./interfaces"; - interface IDetails { [key: string]: string | { [subKey: string]: string }; } @@ -162,7 +161,6 @@ const SharedAgentForm = ({ }, ...(DidMethod.INDY === selectedMethod || DidMethod.POLYGON === selectedMethod) && { network: yup.string().required('Network is required') }, ...(DidMethod.WEB === selectedMethod) && { domain: yup.string().required('Domain is required') }, - ...(DidMethod.POLYGON === selectedMethod) && { privatekey: yup.string().required('Private key is required').trim().length(64, 'Private key must be exactly 64 characters long') }, }; const renderMethodOptions = (formikHandlers: { handleChange: (e: React.ChangeEvent) => void }) => { @@ -176,7 +174,6 @@ const SharedAgentForm = ({ return null; } - return Object.keys(methods).map((method) => (
-
+
{!haveDidShared && ( -
+
@@ -272,16 +269,20 @@ const SharedAgentForm = ({ }} validationSchema={yup.object().shape(validations)} onSubmit={(values: IValuesShared) => { + + if (!values.privatekey) { + values.privatekey = privateKeyValue; + } + submitSharedWallet( values, - privateKeyValue, domainValue, ); }} > {(formikHandlers) => (
-
+
{haveDidShared && ( <>
@@ -324,7 +325,7 @@ const SharedAgentForm = ({ ) }
-
+
+
+ className="mt-2 bg-[#F4F4F4] w-[180px] md:w-[154px] dark:bg-gray-700 text-gray-900 dark:text-gray-300" /> +
)} @@ -446,7 +449,7 @@ const SharedAgentForm = ({ name="label" value={formikHandlers?.values?.label} onChange={formikHandlers.handleChange} - className="bg-gray-50 border mt-2 border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" + className="bg-gray-50 border mt-2 w-full border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" type="text" /> {formikHandlers?.errors?.label && formikHandlers?.touched?.label && ( {formikHandlers?.errors?.label} @@ -456,11 +459,11 @@ const SharedAgentForm = ({
-
+
{selectedMethod === 'did:polygon' && ( <>
+ privateKeyValue={privateKeyValue} formikHandlers={formikHandlers} />
  1. - Step 1: -
    + Step 1: +
    Copy the address and get the free tokens for the testnet.
    For eg. use  - + `https://faucet.polygon.technology/`  to get free token @@ -480,11 +483,11 @@ const SharedAgentForm = ({
  2. - Step 2: -
    Check that you have recieved the tokens.
    -
    For eg. copy the address and check the balance on + Step 2: +
    Check that you have recieved the tokens.
    +
    For eg. copy the address and check the balance on diff --git a/src/components/organization/walletCommonComponents/WalletSpinup.tsx b/src/components/organization/walletCommonComponents/WalletSpinup.tsx index b294a3504..855a9db58 100644 --- a/src/components/organization/walletCommonComponents/WalletSpinup.tsx +++ b/src/components/organization/walletCommonComponents/WalletSpinup.tsx @@ -89,7 +89,6 @@ const WalletSpinup = (props: { const submitSharedWallet = async ( values: IValuesShared, - privatekey: string, domain: string, ) => { setLoading(true); @@ -102,7 +101,7 @@ const WalletSpinup = (props: { method: values.method.split(':')[1] || '', ledger: values.method === DidMethod.INDY ? ledgerName : '', label: values.label, - privatekey: values.method === DidMethod.POLYGON ? privatekey : '', + privatekey: values.method === DidMethod.POLYGON ? values?.privatekey : '', seed: values.method === DidMethod.POLYGON ? '' : values?.seed || seeds, network: values.method === DidMethod.POLYGON @@ -242,11 +241,11 @@ const WalletSpinup = (props: {
    -
    +
    {!agentSpinupCall && !loading && (
    -
      -
    • +
        +
      • -
      • +
      • } onClickEvent={createOrganizationModel} + disabled={currentPage.totalCount >= 10} />
    From 40c3b7ec632bc09c3a90109c6af9ca3a7cbafd5e Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:14:49 +0530 Subject: [PATCH 09/54] fix: css issues (#704) * refactor: parameter name Signed-off-by: bhavanakarwade * refactor: added query parameter Signed-off-by: bhavanakarwade * fix: remove unnecessary code Signed-off-by: bhavanakarwade * fix: query param issue Signed-off-by: bhavanakarwade * fix: css issues Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- src/components/AddOrganizationInEcosystem.tsx | 1 + .../organization/walletCommonComponents/SharedAgent.tsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/AddOrganizationInEcosystem.tsx b/src/components/AddOrganizationInEcosystem.tsx index c4f5578f5..44cb30a11 100644 --- a/src/components/AddOrganizationInEcosystem.tsx +++ b/src/components/AddOrganizationInEcosystem.tsx @@ -409,6 +409,7 @@ const AddOrganizationInEcosystem = () => {
    )} From 7300fc6e13448a27aa708a081509e0678f0f274a Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:34:54 +0530 Subject: [PATCH 10/54] feat: add passkey warning message for unsupported devices (#706) * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * fix: removed commnets Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- public/images/sign-warning-svgrepo-com.svg | 9 +++++++ src/common/enums.ts | 4 +++ src/commonComponents/PasskeyAlert.tsx | 26 +++++++++++++++++++ .../Authentication/SignInUserPasskey.tsx | 18 ++++++++++--- .../Authentication/SignUpUserPasskey.tsx | 18 ++++++++++--- src/components/Profile/AddPasskey.tsx | 12 +++++++++ 6 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 public/images/sign-warning-svgrepo-com.svg create mode 100644 src/commonComponents/PasskeyAlert.tsx diff --git a/public/images/sign-warning-svgrepo-com.svg b/public/images/sign-warning-svgrepo-com.svg new file mode 100644 index 000000000..9d37d18d3 --- /dev/null +++ b/public/images/sign-warning-svgrepo-com.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/common/enums.ts b/src/common/enums.ts index 83d5f5c6f..df404ed18 100644 --- a/src/common/enums.ts +++ b/src/common/enums.ts @@ -114,4 +114,8 @@ export enum Network { export enum CommonConstants { BALANCELIMIT = 0.01 +} + +export enum Devices { +Linux = 'linux' } \ No newline at end of file diff --git a/src/commonComponents/PasskeyAlert.tsx b/src/commonComponents/PasskeyAlert.tsx new file mode 100644 index 000000000..a006c3bbe --- /dev/null +++ b/src/commonComponents/PasskeyAlert.tsx @@ -0,0 +1,26 @@ +import { Alert } from 'flowbite-react'; +import React, { useEffect, useState } from "react"; + +const PasskeyAlert =()=>{ + return ( + +
    + +
    + + + + + + + +

    This browser or device is reporting partial passkey support.

    +
    +
    +
    + + + ); +}; +export default PasskeyAlert; \ No newline at end of file diff --git a/src/components/Authentication/SignInUserPasskey.tsx b/src/components/Authentication/SignInUserPasskey.tsx index 65cf8b370..acb307eff 100644 --- a/src/components/Authentication/SignInUserPasskey.tsx +++ b/src/components/Authentication/SignInUserPasskey.tsx @@ -16,12 +16,13 @@ import { import type { AxiosError, AxiosResponse } from 'axios'; import SignInUser from './SignInUser'; import { startAuthentication } from '@simplewebauthn/browser'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import SignInUserPassword from './SignInUserPassword'; import { pathRoutes } from '../../config/pathRoutes'; import NavBar from './NavBar'; import FooterBar from './FooterBar'; -import { PlatformRoles } from '../../common/enums'; +import { Devices, PlatformRoles } from '../../common/enums'; +import PasskeyAlert from '../../commonComponents/PasskeyAlert'; interface signInUserProps { email: string; } @@ -33,7 +34,7 @@ const SignInUserPasskey = (signInUserProps: signInUserProps) => { const [fidoUserError, setFidoUserError] = useState(''); const [failure, setFailure] = useState(null); const [success, setSuccess] = useState(null); - const [email, setEmail] = useState("") + const [isDevice, setIsDevice] = useState(false); const handleSvgClick = () => { window.history.pushState(null, '', pathRoutes.auth.sinIn); @@ -45,6 +46,14 @@ const SignInUserPasskey = (signInUserProps: signInUserProps) => { setShowSignInUser(false); }; + useEffect(() => { + const platform = navigator.platform.toLowerCase(); + if (platform.includes(Devices.Linux)) { + setIsDevice(true); + } + }, []); + + const verifyAuthenticationMethod = async ( verifyAuthenticationObj: unknown, userData: { userName: string }, @@ -352,6 +361,9 @@ const SignInUserPasskey = (signInUserProps: signInUserProps) => { Passkey + {isDevice && ( + + )} ('email'); const [showSignUpUserName, setShowSignUpUserName] = useState(false); const [showPasskeyComponent, setShowPasskeyComponent] = useState(true); - + const [isDevice, setIsDevice] = useState(false); useEffect(() => { - if (window?.location?.search.length > 7) { setEmailAutoFill(window?.location?.search.split('=')[1]) } - }, []) + const platform = navigator.platform.toLowerCase(); + if (platform.includes(Devices.Linux)) { + setIsDevice(true); + } + }, []); + + const showFidoError = (error: unknown): void => { const err = error as AxiosError; if ( @@ -290,8 +298,10 @@ const SignUpUserPasskey = ({ email, firstName, lastName }: { email: string, firs Passkey - + {isDevice && ( + + )} (null); const [addfailure, setAddFailure] = useState(null); const [disableFlag, setDisableFlag] = useState(false); + const [isDevice, setIsDevice] = useState(false); const [openModel, setOpenModel] = useState(false); @@ -189,6 +194,10 @@ const AddPasskey = ({ responseMessages }: { responseMessages: (value: IResponse } else { setProfile(); } + const platform = navigator.platform.toLowerCase(); + if (platform.includes(Devices.Linux)) { + setIsDevice(true); + } }, [OrgUserEmail]); return ( @@ -282,6 +291,9 @@ const AddPasskey = ({ responseMessages }: { responseMessages: (value: IResponse Add Passkey + {isDevice && ( + + )}
    Date: Wed, 19 Jun 2024 16:06:10 +0530 Subject: [PATCH 11/54] Feat/passkey warning messages (#707) * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * fix: removed commnets Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade * feat: add passkey warning message for unsupported devices Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- src/commonComponents/PasskeyAlert.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commonComponents/PasskeyAlert.tsx b/src/commonComponents/PasskeyAlert.tsx index a006c3bbe..fdf3d0722 100644 --- a/src/commonComponents/PasskeyAlert.tsx +++ b/src/commonComponents/PasskeyAlert.tsx @@ -15,7 +15,7 @@ const PasskeyAlert =()=>{ -

    This browser or device is reporting partial passkey support.

    +

    This browser or device partially supports passkey.

    From 0dc76d10e561af561402b18d72b999d3d717fd17 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Thu, 20 Jun 2024 19:48:54 +0530 Subject: [PATCH 12/54] feat: dedicated agent workflow (#693) * feat:Working on dedicated agent workflow Signed-off-by: rohit.shitre * feat/dedicated agent ui form Signed-off-by: pranalidhanavade * feat/ conditional rendering of forms Signed-off-by: pranalidhanavade * feat:add support for dedicated agent Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * feat:dedicated agent workflow ui Signed-off-by: pranalidhanavade * feat:dedicated agent workflow Signed-off-by: pranalidhanavade * fix:sonarcloud issues Signed-off-by: pranalidhanavade * fix: duplication issue of sonarcloud Signed-off-by: pranalidhanavade * fix: duplication code issue of sonarcloud Signed-off-by: pranalidhanavade * fix: duplication code issue of sonarcloud Signed-off-by: pranalidhanavade * fix: sonarlint issues Signed-off-by: pranalidhanavade * fix: sonarlint duplication issue Signed-off-by: pranalidhanavade * feat:dedicated agent workflow Signed-off-by: pranalidhanavade * fixed:sonarcloud issues Signed-off-by: pranalidhanavade * fixed:sonarcloud issues Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * feat: support for dedicated agent Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: formik validations Signed-off-by: pranalidhanavade * fix:dedicated agent formik validations Signed-off-by: pranalidhanavade * fix: yup formik validations Signed-off-by: pranalidhanavade * fix: static values from enum Signed-off-by: pranalidhanavade --------- Signed-off-by: rohit.shitre Signed-off-by: pranalidhanavade Co-authored-by: rohit.shitre --- package-lock.json | 2 +- src/api/organization.ts | 30 +- src/common/enums.ts | 6 + src/commonComponents/EcosystemProfileCard.tsx | 4 +- .../Authentication/SignUpUserPasskey.tsx | 3 +- src/components/organization/Dashboard.tsx | 20 +- .../organization/OrganizationDetails.tsx | 17 +- .../organization/OrganizationsList.tsx | 2 +- .../organization/interfaces/index.ts | 6 + .../walletCommonComponents/DedicatedAgent.tsx | 675 ++++++++++++++---- .../walletCommonComponents/WalletSpinup.tsx | 115 ++- .../interfaces/index.tsx | 37 +- src/config/apiRoutes.ts | 3 +- 13 files changed, 733 insertions(+), 187 deletions(-) diff --git a/package-lock.json b/package-lock.json index febbda2eb..423c4788b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15934,4 +15934,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/api/organization.ts b/src/api/organization.ts index 30f87a683..eaf6b0180 100644 --- a/src/api/organization.ts +++ b/src/api/organization.ts @@ -10,7 +10,7 @@ import { apiRoutes } from '../config/apiRoutes'; import { getFromLocalStorage } from './Auth'; import { getHeaderConfigs } from '../config/GetHeaderConfigs'; import { storageKeys } from '../config/CommonConstant'; -import type { IUpdatePrimaryDid } from '../components/organization/interfaces'; +import type { IDedicatedAgentConfig, IUpdatePrimaryDid } from '../components/organization/interfaces'; export const createOrganization = async (data: object) => { const url = apiRoutes.organizations.create; @@ -166,6 +166,34 @@ export const spinupDedicatedAgent = async (data: object, orgId: string) => { } }; +export const setAgentConfigDetails = async (data: IDedicatedAgentConfig, orgId: string) => { + const url =`${apiRoutes.organizations.root}/${orgId}${apiRoutes.Agent.setAgentConfig}` + const payload = data; + + const token = await getFromLocalStorage(storageKeys.TOKEN); + + const config = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + }; + const axiosPayload = { + url, + payload, + config, + }; + + try { + return await axiosPost(axiosPayload); + } catch (error) { + const err = error as Error; + return err?.message; + } +}; + + + export const spinupSharedAgent = async (data: object, orgId: string) => { const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Agent.agentSharedSpinup}`; const payload = data; diff --git a/src/common/enums.ts b/src/common/enums.ts index df404ed18..f2733ef73 100644 --- a/src/common/enums.ts +++ b/src/common/enums.ts @@ -118,4 +118,10 @@ export enum CommonConstants { export enum Devices { Linux = 'linux' +} + +export enum Ledgers { + INDY = 'indy', + POLYGON = 'polygon', + NOLEDGER = 'noledger' } \ No newline at end of file diff --git a/src/commonComponents/EcosystemProfileCard.tsx b/src/commonComponents/EcosystemProfileCard.tsx index cea73b2f5..d1623d1ca 100644 --- a/src/commonComponents/EcosystemProfileCard.tsx +++ b/src/commonComponents/EcosystemProfileCard.tsx @@ -86,9 +86,9 @@ const EcosystemProfileCard = ({getEndorsementListData}:IEndorsement) => { >
    {ecosystemDetails?.logoUrl ? ( - + ) : ( - + )}
    diff --git a/src/components/Authentication/SignUpUserPasskey.tsx b/src/components/Authentication/SignUpUserPasskey.tsx index 870dfbd99..18e616501 100644 --- a/src/components/Authentication/SignUpUserPasskey.tsx +++ b/src/components/Authentication/SignUpUserPasskey.tsx @@ -42,7 +42,6 @@ const SignUpUserPasskey = ({ email, firstName, lastName }: { email: string, firs if (window?.location?.search.length > 7) { setEmailAutoFill(window?.location?.search.split('=')[1]) } - const platform = navigator.platform.toLowerCase(); if (platform.includes(Devices.Linux)) { setIsDevice(true); @@ -288,7 +287,7 @@ const SignUpUserPasskey = ({ email, firstName, lastName }: { email: string, firs id='signupcreatepasskey' isProcessing={loading} onClick={() => { - registerWithPasskey(true) + registerWithPasskey(true) }} > diff --git a/src/components/organization/Dashboard.tsx b/src/components/organization/Dashboard.tsx index 48fc2212e..eeb534465 100644 --- a/src/components/organization/Dashboard.tsx +++ b/src/components/organization/Dashboard.tsx @@ -15,6 +15,7 @@ import { pathRoutes } from '../../config/pathRoutes'; import { AlertComponent } from '../AlertComponent'; import WalletSpinup from './walletCommonComponents/WalletSpinup'; import DashboardCard from '../../commonComponents/DashboardCard'; +import React from 'react'; const Dashboard = () => { const [orgData, setOrgData] = useState(null); @@ -26,6 +27,9 @@ const Dashboard = () => { const [userRoles, setUserRoles] = useState([]); const [orgSuccess, setOrgSuccess] = useState(null); const [openModal, setOpenModal] = useState(false); + const [agentConfigure, setAgentConfigure]=useState(false); + const [isDidCreated, setIsDidCreated]=useState(false); + const EditOrgDetails = () => { setOpenModal(true); @@ -48,11 +52,14 @@ const Dashboard = () => { const response = await getOrganizationById(orgId as string); const { data } = response as AxiosResponse; setLoading(false) - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - if (data?.data?.org_agents && data?.data?.org_agents?.length > 0) { + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + + if (data?.data?.org_agents?.length > 0 && data?.data?.org_agents[0]?.orgDid) { setWalletStatus(true); } + setOrgData(data?.data); + const organizationData = orgInfoData ? JSON.parse(orgInfoData) : {}; const {id, name, description, logoUrl} = data?.data || {}; const orgInfo = { @@ -63,11 +70,13 @@ const Dashboard = () => { ...logoUrl && { logoUrl } } await setToLocalStorage(storageKeys.ORG_INFO, orgInfo); + } else { setFailure(response as string); } setLoading(false); }; + const fetchOrganizationDashboard = async () => { setLoading(true); @@ -84,7 +93,7 @@ const Dashboard = () => { } } setLoading(false); - }; + }; useEffect(() => { fetchOrganizationDetails(); @@ -112,6 +121,8 @@ const Dashboard = () => { const redirectOrgUsers = () => { window.location.href = pathRoutes.organizations.users; }; + + return (
    @@ -249,7 +260,8 @@ const Dashboard = () => {
    ) : ( walletStatus === true ? ( - + + ) : ( (userRoles.includes(Roles.OWNER) || userRoles.includes(Roles.ADMIN)) && ( diff --git a/src/components/organization/OrganizationDetails.tsx b/src/components/organization/OrganizationDetails.tsx index f43dcd2d7..e32093763 100644 --- a/src/components/organization/OrganizationDetails.tsx +++ b/src/components/organization/OrganizationDetails.tsx @@ -14,10 +14,11 @@ import { Tooltip } from 'flowbite-react'; import DIDList from './configuration-settings/DidList'; const OrganizationDetails = ({ orgData }: { orgData: Organisation | null }) => { + const { org_agents } = orgData as Organisation; const agentData: OrgAgent | null = - org_agents.length > 0 ? org_agents[0] : null; - + org_agents.length > 0 ? org_agents[0] : null; + const [loading, setLoading] = useState(true); const [connectionData, setConnectionData] = useState(null); @@ -43,8 +44,10 @@ const OrganizationDetails = ({ orgData }: { orgData: Organisation | null }) => { }, []); return ( - <> -
    +
    + +
    +

    Web Wallet Details @@ -231,8 +234,10 @@ const OrganizationDetails = ({ orgData }: { orgData: Organisation | null }) => {

    - )} - + )}
    + +
    + ); }; diff --git a/src/components/organization/OrganizationsList.tsx b/src/components/organization/OrganizationsList.tsx index 34b96050f..82c61c1f6 100644 --- a/src/components/organization/OrganizationsList.tsx +++ b/src/components/organization/OrganizationsList.tsx @@ -249,6 +249,7 @@ const OrganizationsList = () => {
    = 10} buttonTitle="Create" feature={Features.CRETAE_ORG} svgComponent={ @@ -268,7 +269,6 @@ const OrganizationsList = () => {
    } onClickEvent={createOrganizationModel} - disabled={currentPage.totalCount >= 10} />
diff --git a/src/components/organization/interfaces/index.ts b/src/components/organization/interfaces/index.ts index 6288ee943..8aa6fd52c 100644 --- a/src/components/organization/interfaces/index.ts +++ b/src/components/organization/interfaces/index.ts @@ -215,6 +215,7 @@ export interface IDidList { export interface IFormikValues { ledger: string; method: string; + keyType:string; network: string; did: string; domain: string; @@ -222,6 +223,11 @@ export interface IFormikValues { endorserDid: string; } +export interface IDedicatedAgentConfig { + walletName: string; + agentEndpoint: string; + apiKey: string; +} interface IndySubDetails { [key: string]: string; } diff --git a/src/components/organization/walletCommonComponents/DedicatedAgent.tsx b/src/components/organization/walletCommonComponents/DedicatedAgent.tsx index 452007415..17abdbd9c 100644 --- a/src/components/organization/walletCommonComponents/DedicatedAgent.tsx +++ b/src/components/organization/walletCommonComponents/DedicatedAgent.tsx @@ -1,48 +1,147 @@ import * as yup from 'yup'; -import { Button, Checkbox, Label } from 'flowbite-react'; +import { Button, Label } from 'flowbite-react'; import { Field, Form, Formik } from 'formik'; import { - apiStatusCodes, - passwordRegex, + apiStatusCodes, + storageKeys, } from '../../../config/CommonConstant'; -import { useEffect, useState } from 'react'; -import InputCopy from '../../InputCopy'; -import type { AxiosResponse } from 'axios'; +import { useEffect, useState, type ChangeEvent } from 'react'; +import { HttpStatusCode, type AxiosResponse } from 'axios'; import { + getLedgerConfig, getLedgers } from '../../../api/Agent'; -import NetworkInput from './NetworkInput'; -import type { IDedicatedAgentForm, IValues } from './interfaces'; +import { DidMethod, Ledgers} from '../../../common/enums'; +import type { IDedicatedAgentForm, ILedgerConfigData, ILedgerItem, IValuesShared, IDedicatedAgentData} from './interfaces'; +import { getFromLocalStorage } from '../../../api/Auth'; +import CopyDid from '../../../commonComponents/CopyDid'; +import SetDomainValueInput from './SetDomainValueInput'; +import SetPrivateKeyValueInput from './SetPrivateKeyValue'; +import { getOrganizationById, setAgentConfigDetails } from '../../../api/organization'; +import type { IDedicatedAgentConfig} from '../interfaces'; + +const RequiredAsterisk = () => * + const DedicatedAgentForm = ({ seeds, loading, - submitDedicatedWallet, -}: IDedicatedAgentForm) => { - const [haveDid, setHaveDid] = useState(false); - const [networks, setNetworks] = useState([]); - - const fetchNetworks = async () => { - try { - const { data } = (await getLedgers()) as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - return data?.data; - } - return []; - } catch (err) { - console.error('Fetch Network ERROR::::', err); - } - }; - - const getLedgerList = async () => { - const res = await fetchNetworks(); - setNetworks(res); + onConfigureDedicated, + submitDedicatedWallet, + }: IDedicatedAgentForm) => { + + const [createDidFormFlag, setCreateDidFormFlag]=useState(false) + const [seedVal, setSeedVal] = useState(''); + const [mappedDetails, setMappedDetails] = useState(null); + const [selectedLedger, setSelectedLedger] = useState(''); + const [selectedDid, setSelectedDid] = useState(''); + const [selectedMethod, setSelectedMethod]=useState('') + const [privateKeyValue, setPrivateKeyValue] = useState(''); + const [domainValue, setDomainValue] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + + const fetchLedgerConfigDetails = async () => { + try { + const { data } = await getLedgerConfig() as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const ledgerConfigDetails: ILedgerConfigData = { + indy: { + [`${DidMethod.INDY}`]: {} + }, + polygon: { + [`${DidMethod.POLYGON}`]: {} + }, + noLedger: {} + }; + + data.data.forEach(({ name, details }: ILedgerItem) => { + const lowerCaseName = name.toLowerCase(); + + if (lowerCaseName === Ledgers.INDY && details) { + for (const [key, subDetails] of Object.entries(details)) { + if (typeof subDetails === 'object' && subDetails !== null) { + for (const [subKey, value] of Object.entries(subDetails)) { + const formattedKey = `${key}:${subKey}`.replace(DidMethod.INDY, ''); + ledgerConfigDetails.indy[DidMethod.INDY][formattedKey] = value; + } + } + } + } else if (lowerCaseName === Ledgers.POLYGON && details) { + for (const [key, value] of Object.entries(details)) { + if (typeof value === 'object' && value !== null) { + for (const [subKey, subValue] of Object.entries(value)) { + ledgerConfigDetails.polygon[DidMethod.POLYGON][subKey] = subValue; + } + } else if (typeof value === 'string') { + ledgerConfigDetails.polygon[DidMethod.POLYGON][key] = value; + } + } + } else if (lowerCaseName === Ledgers.NOLEDGER && details) { + for (const [key, value] of Object.entries(details)) { + ledgerConfigDetails.noLedger[key] = value as string; + } + } + }); + + + setMappedDetails(ledgerConfigDetails); + } + } catch (err) { + console.error('Fetch Network ERROR::::', err); + } + }; + + + const fetchOrganizationDetails = async () => { + try { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId as string); + const { data } = response as AxiosResponse; + + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const walletName = data?.data?.org_agents[0]?.walletName + + if(walletName){ + setCreateDidFormFlag(true) + } + } + } + catch (error) { + console.error('Fetch organization details ERROR::::', error); + + } }; - useEffect(() => { - getLedgerList(); + const handleLedgerChanges = (e: ChangeEvent) => { + setSelectedLedger(e.target.value); + setSelectedMethod(''); + setSelectedDid(''); + }; + const handleMethodChanges = (e: ChangeEvent) => { + setSelectedMethod(e.target.value); + setSelectedDid(''); + }; + + const handleNetworkChanges = (e: ChangeEvent) => { + const didMethod = `${e.target.value}`; + setSelectedDid(didMethod); + }; + + + useEffect(() => { + fetchOrganizationDetails(); }, []); + useEffect(() => { + fetchLedgerConfigDetails(); + }, []); + + useEffect(() => { + setSeedVal(seeds) + }, [seeds]) const validation = { walletName: yup .string() @@ -51,141 +150,429 @@ const DedicatedAgentForm = ({ .trim() .required('Wallet name is required') .label('Wallet name'), - password: yup + + agentEndpoint:yup + .string() + .url() + .trim() + .required('agent endpoint is required') + .label('Agent Endpoint'), + + apiKey:yup .string() - .matches( - passwordRegex, - 'Password must contain one Capital, Special character', - ) - .required('Wallet password is required') - .label('Wallet password'), - did: haveDid ? yup.string().required('DID is required') : yup.string(), - network: yup.string().required('Network is required'), + .trim() + .required('Api key is required') + .label('Api key') }; - if (haveDid) { - validation.seed = yup.string().required('Seed is required'); + +const setAgentConfig=async (values: IDedicatedAgentConfig)=>{ + setIsLoading(true); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const payload = { + walletName: values.walletName, + agentEndpoint: values.agentEndpoint, + apiKey: values.apiKey + }; + + try { + const agentConfigResponse = await setAgentConfigDetails(payload, orgId) + + const { data } = agentConfigResponse as AxiosResponse; + + if (data?.statusCode === HttpStatusCode.Created) { + setIsLoading(false); + onConfigureDedicated() + await fetchOrganizationDetails() + } + } + catch (error) { + const err = error as Error + return err?.message + } +} + +const methodRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEvent) => void }) => { + if (!selectedLedger) { + return null; + } + + const methods = mappedDetails?.[selectedLedger]; + + if (!methods) { + return null; } + return Object.keys(methods).map((method) => ( +
+ { + formikHandlers.handleChange(e); + handleMethodChanges(e); + setSelectedMethod(method); + }} + className="mr-2" + /> + +
+ )); +}; + + const networkRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEvent) => void }) => { + if (!selectedLedger || !selectedMethod) { + return null; + } + const networks = mappedDetails?.[selectedLedger][selectedMethod]; + if (!networks) { + return null; + } + + return Object.keys(networks).map((network) => ( +
+ { + formikHandlers.handleChange(e) + handleNetworkChanges(e) + }} + className="mr-2" + /> + +
+ )); + }; + + return ( <> -
- setHaveDid(e.target.checked)} /> - -
- submitDedicatedWallet(values)} - > - {(formikHandlers): JSX.Element => ( - -
-
-
- { + await setAgentConfig(values) + + }} + > + {(formikHandlers):JSX.Element => ( + +
+
+
+
+ +
+ {formikHandlers?.errors?.walletName && + formikHandlers?.touched?.walletName && ( + + {formikHandlers?.errors?.walletName} + + )} +
+
+
+
+
+ +
+ {formikHandlers?.errors?.agentEndpoint && + formikHandlers?.touched?.agentEndpoint && ( + + {formikHandlers?.errors?.agentEndpoint} + + )} +
+
+
+
+
+ +
+ {formikHandlers?.errors?.apiKey && + formikHandlers?.touched?.apiKey && ( + + {formikHandlers?.errors?.apiKey} + + )} +
+
+
+ + + + )} + +: + + { + + submitDedicatedWallet( + values, + privateKeyValue, + domainValue + ); + }} + + > + + {(formikHandlers):JSX.Element => ( +
+
+ +
+
+
+ - {formikHandlers?.errors?.seed && - formikHandlers?.touched?.seed && ( +
+
+ +
+
+ +
+ + {mappedDetails && + + Object.keys(mappedDetails).map((ledger) => ( + +
+ { + formikHandlers.handleChange(e); + handleLedgerChanges(e); + setSelectedLedger(ledger); + setSelectedMethod(''); + setSeedVal(seeds); + setSelectedDid(''); + }} + className="mr-2" + /> + + + + +
+ + ))} +
+ {formikHandlers.errors.ledger && ( - {formikHandlers?.errors?.seed} + {formikHandlers.errors.ledger} )} -
- {haveDid && ( -
-
-
+
+ +
+ {methodRenderOptions(formikHandlers)}
+ {formikHandlers.errors.method && ( + + {formikHandlers.errors.method} + + )} +
- - {formikHandlers?.errors?.did && - formikHandlers?.touched?.did && ( + + {selectedLedger !== Ledgers.NOLEDGER && ( +
+ +
+ {networkRenderOptions(formikHandlers)} +
+ {formikHandlers.errors.network && ( - {formikHandlers?.errors?.did} + {formikHandlers.errors.network} )} -
- )} - +
+ )} -
-
-
- - {formikHandlers?.errors?.walletName && - formikHandlers?.touched?.walletName && ( - - {formikHandlers?.errors?.walletName} - + {selectedLedger !== Ledgers.NOLEDGER && ( + +
+ +
+ +
+ +
+ )} + +
+ + {selectedMethod === DidMethod.WEB && ( + )} -
-
-
-
- - {formikHandlers?.errors?.password && - formikHandlers?.touched?.password && ( - - {formikHandlers?.errors?.password} - - )}
- - - )} - + + +
+ + + + + + )} + + } + ); }; - export default DedicatedAgentForm; \ No newline at end of file diff --git a/src/components/organization/walletCommonComponents/WalletSpinup.tsx b/src/components/organization/walletCommonComponents/WalletSpinup.tsx index 855a9db58..496bc2518 100644 --- a/src/components/organization/walletCommonComponents/WalletSpinup.tsx +++ b/src/components/organization/walletCommonComponents/WalletSpinup.tsx @@ -1,6 +1,8 @@ import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; import { getFromLocalStorage, passwordEncryption } from '../../../api/Auth'; import { + createDid, + getOrganizationById, spinupDedicatedAgent, spinupSharedAgent, } from '../../../api/organization'; @@ -11,10 +13,13 @@ import SOCKET from '../../../config/SocketConfig'; import { nanoid } from 'nanoid'; import { AlertComponent } from '../../AlertComponent'; import { DidMethod } from '../../../common/enums'; -import DedicatedAgentForm from './DedicatedAgent'; +import DedicatedAgentForm from '../walletCommonComponents/DedicatedAgent'; import SharedAgentForm from './SharedAgent'; import WalletSteps from './WalletSteps'; import type { IValuesShared } from './interfaces'; +import React from 'react'; +import OrganizationDetails from '../OrganizationDetails'; +import type { Organisation } from '../interfaces'; interface Values { seed: string; @@ -41,6 +46,10 @@ const WalletSpinup = (props: { const [failure, setFailure] = useState(null); const [seeds, setSeeds] = useState(''); const [maskedSeeds, setMaskedSeeds] = useState(''); + const [orgData, setOrgData] = useState(null); + const [isShared, setIsShared] = useState(false); + const [isConfiguredDedicated, setIsConfiguredDedicated] = useState(false); + const maskSeeds = (seed: string) => { const visiblePart = seed.slice(0, -10); @@ -54,30 +63,73 @@ const WalletSpinup = (props: { setSeeds(generatedSeeds); setMaskedSeeds(masked); }, []); - + + const configureDedicatedWallet = ()=> { + setIsConfiguredDedicated(true); + } + const fetchOrganizationDetails = async () => { + setLoading(true); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const orgInfoData = await getFromLocalStorage(storageKeys.ORG_INFO); + const response = await getOrganizationById(orgId as string); + const { data } = response as AxiosResponse; + setLoading(false) + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + + const agentData = data?.data?.org_agents + + if (data?.data?.org_agents && data?.data?.org_agents[0]?.org_agent_type?.agent?.toLowerCase() === AgentType.DEDICATED){ + setIsConfiguredDedicated(true) + setAgentType(AgentType.DEDICATED) + } + + if (agentData.length > 0 && agentData?.orgDid) { + setOrgData(data?.data); + } + }; +} + + useEffect(() => { + fetchOrganizationDetails() + }, []); const onRadioSelect = (type: string) => { setAgentType(type); }; - const submitDedicatedWallet = async (values: Values) => { - const payload = { - walletName: values.walletName, - seed: values.seed || seeds, - walletPassword: passwordEncryption(values.password), - did: values.did, - ledgerId: [values.network.toString()], - clientSocketId: SOCKET.id, +const submitDedicatedWallet = async ( + values: IValuesShared, + privatekey: string, + domain: string +) => { + const didData = { + seed:values.method === DidMethod.POLYGON ? '' : seeds, + keyType: values.keyType || 'ed25519', + method: values.method.split(':')[1] || '', + network: + values.method === DidMethod.INDY ? + values.network?.split(':').slice(2).join(':') : + values.method === DidMethod.POLYGON + ? values.network?.split(':').slice(1).join(':') + : '', + domain: values.method === DidMethod.WEB ? domain : '', + role: values.method === DidMethod.INDY ? 'endorser' : '', + privatekey: values.method === DidMethod.POLYGON ? privatekey : '', + did: values.did || '', + endorserDid: values?.endorserDid || '', + isPrimaryDid: true, }; - setLoading(true); const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const spinupRes = await spinupDedicatedAgent(payload, orgId); + + const spinupRes = await createDid(didData); const { data } = spinupRes as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { - if (data?.data['agentSpinupStatus'] === 1) { - setAgentSpinupCall(true); + + if (data?.data?.did) { + setAgentSpinupCall(true); + window.location.reload(); + } else { setFailure(spinupRes as string); } @@ -121,6 +173,7 @@ const WalletSpinup = (props: { if (data?.data['agentSpinupStatus'] === 1) { setAgentSpinupCall(true); + setIsShared(true) } else { setFailure(spinupRes as string); } @@ -203,20 +256,31 @@ const WalletSpinup = (props: { isCopied={false} /> ); - } else { + } else { formComponent = ( ); } - } else { - formComponent = ( - - ); - } + } + + else { + + if (agentType === AgentType.SHARED) { + formComponent = ( + + ); + } + else { + formComponent = ( + + ); + } + } return (
@@ -249,6 +313,7 @@ const WalletSpinup = (props: {
); -}; +} + export default WalletSpinup; + diff --git a/src/components/organization/walletCommonComponents/interfaces/index.tsx b/src/components/organization/walletCommonComponents/interfaces/index.tsx index 0c957f4b0..902913508 100644 --- a/src/components/organization/walletCommonComponents/interfaces/index.tsx +++ b/src/components/organization/walletCommonComponents/interfaces/index.tsx @@ -9,7 +9,9 @@ export interface IValues { export interface IDedicatedAgentForm { seeds: string; loading: boolean; - submitDedicatedWallet: (values: IValues) => void; + submitDedicatedWallet: (values: IValuesShared, privatekey: string, + domain: string) => void; + onConfigureDedicated:() => void, } export interface IValuesShared { @@ -50,4 +52,37 @@ export interface ISharedAgentForm { ) => void; } +export interface ILedgerConfigData { + indy: { + 'did:indy': { + [key: string]: string; + }; + }; + polygon: { + 'did:polygon': { + [key: string]: string; + }; + }; + noLedger: { + [key: string]: string; + }; +} +export interface ILedgerItem { + name: string; + details: IDetails; +} +interface IDetails { + [key: string]: string | { [subKey: string]: string }; +} + +export interface IDedicatedAgentData { + walletName: string; + agentEndpoint: string; + apiKey: string; + seed:string; + keyType:string; + method:string; + network:string; + role:string; +} \ No newline at end of file diff --git a/src/config/apiRoutes.ts b/src/config/apiRoutes.ts index 1e8f95d11..9a2aa7366 100644 --- a/src/config/apiRoutes.ts +++ b/src/config/apiRoutes.ts @@ -81,7 +81,8 @@ export const apiRoutes = { agentDedicatedSpinup: '/agents/spinup', agentSharedSpinup: '/agents/wallet', getLedgerConfig: '/agents/ledgerConfig', - createPolygonKeys: '/agents/polygon/create-keys' + createPolygonKeys: '/agents/polygon/create-keys', + setAgentConfig:'/agents/configure' }, Platform: { getAllSchemaFromPlatform: '/platform/schemas', From 7d5a77de028ee94641f53b338bb1d9cdcd68d1f6 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:20:48 +0530 Subject: [PATCH 13/54] fix: formik validations for dedicated agent workflow (#708) * feat:Working on dedicated agent workflow Signed-off-by: rohit.shitre * feat/dedicated agent ui form Signed-off-by: pranalidhanavade * feat/ conditional rendering of forms Signed-off-by: pranalidhanavade * feat:add support for dedicated agent Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * feat:dedicated agent workflow integration Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * feat:dedicated agent workflow ui Signed-off-by: pranalidhanavade * feat:dedicated agent workflow Signed-off-by: pranalidhanavade * fix:sonarcloud issues Signed-off-by: pranalidhanavade * fix: duplication issue of sonarcloud Signed-off-by: pranalidhanavade * fix: duplication code issue of sonarcloud Signed-off-by: pranalidhanavade * fix: duplication code issue of sonarcloud Signed-off-by: pranalidhanavade * fix: sonarlint issues Signed-off-by: pranalidhanavade * fix: sonarlint duplication issue Signed-off-by: pranalidhanavade * feat:dedicated agent workflow Signed-off-by: pranalidhanavade * fixed:sonarcloud issues Signed-off-by: pranalidhanavade * fixed:sonarcloud issues Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * feat: support for dedicated agent Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: formik validations Signed-off-by: pranalidhanavade * fix:dedicated agent formik validations Signed-off-by: pranalidhanavade * fix: yup formik validations Signed-off-by: pranalidhanavade * fix: static values from enum Signed-off-by: pranalidhanavade * fix: polygon formik validation error Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade --------- Signed-off-by: rohit.shitre Signed-off-by: pranalidhanavade Co-authored-by: rohit.shitre --- src/common/enums.ts | 2 +- .../walletCommonComponents/DedicatedAgent.tsx | 71 ++++++++++--------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/common/enums.ts b/src/common/enums.ts index f2733ef73..52202d80a 100644 --- a/src/common/enums.ts +++ b/src/common/enums.ts @@ -123,5 +123,5 @@ Linux = 'linux' export enum Ledgers { INDY = 'indy', POLYGON = 'polygon', - NOLEDGER = 'noledger' + NO_LEDGER = 'noLedger' } \ No newline at end of file diff --git a/src/components/organization/walletCommonComponents/DedicatedAgent.tsx b/src/components/organization/walletCommonComponents/DedicatedAgent.tsx index 17abdbd9c..a82cd6115 100644 --- a/src/components/organization/walletCommonComponents/DedicatedAgent.tsx +++ b/src/components/organization/walletCommonComponents/DedicatedAgent.tsx @@ -11,7 +11,7 @@ import { getLedgerConfig, getLedgers } from '../../../api/Agent'; -import { DidMethod, Ledgers} from '../../../common/enums'; +import { DidMethod, Ledgers, Network} from '../../../common/enums'; import type { IDedicatedAgentForm, ILedgerConfigData, ILedgerItem, IValuesShared, IDedicatedAgentData} from './interfaces'; import { getFromLocalStorage } from '../../../api/Auth'; import CopyDid from '../../../commonComponents/CopyDid'; @@ -63,7 +63,7 @@ const DedicatedAgentForm = ({ for (const [key, subDetails] of Object.entries(details)) { if (typeof subDetails === 'object' && subDetails !== null) { for (const [subKey, value] of Object.entries(subDetails)) { - const formattedKey = `${key}:${subKey}`.replace(DidMethod.INDY, ''); + const formattedKey = `${key}:${subKey}`.replace(`${DidMethod.INDY}:`, ''); ledgerConfigDetails.indy[DidMethod.INDY][formattedKey] = value; } } @@ -78,7 +78,7 @@ const DedicatedAgentForm = ({ ledgerConfigDetails.polygon[DidMethod.POLYGON][key] = value; } } - } else if (lowerCaseName === Ledgers.NOLEDGER && details) { + } else if (lowerCaseName === Ledgers.NO_LEDGER.toLowerCase() && details) { for (const [key, value] of Object.entries(details)) { ledgerConfigDetails.noLedger[key] = value as string; } @@ -223,34 +223,40 @@ const methodRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEve )); }; - const networkRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEvent) => void }) => { - if (!selectedLedger || !selectedMethod) { - return null; - } - const networks = mappedDetails?.[selectedLedger][selectedMethod]; - if (!networks) { - return null; - } +const networkRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEvent) => void }) => { + if (!selectedLedger || !selectedMethod) { + return null; + } - return Object.keys(networks).map((network) => ( -
- { - formikHandlers.handleChange(e) - handleNetworkChanges(e) - }} - className="mr-2" - /> - -
- )); - }; + const networks = mappedDetails?.[selectedLedger][selectedMethod]; + if (!networks) { + return null; + } + + let filteredNetworks = Object.keys(networks); + if (selectedMethod === DidMethod.POLYGON) { + filteredNetworks = filteredNetworks.filter(network => network === Network.TESTNET); + } + + return filteredNetworks.map((network) => ( +
+ { + formikHandlers.handleChange(e); + handleNetworkChanges(e); + }} + className="mr-2" + /> + +
+ )); +}; @@ -362,7 +368,6 @@ const methodRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEve method: yup.string().required('Method is required'), ...(DidMethod.INDY === selectedMethod || DidMethod.POLYGON === selectedMethod) && { network: yup.string().required('Network is required') }, ...(DidMethod.WEB === selectedMethod) && { domain: yup.string().required('Domain is required') }, - ...(DidMethod.POLYGON === selectedMethod) && { privatekey: yup.string().required('Private key is required').trim().length(64, 'Private key must be exactly 64 characters long') }, })} @@ -459,7 +464,7 @@ const methodRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEve
- {selectedLedger !== Ledgers.NOLEDGER && ( + {selectedLedger !== Ledgers.NO_LEDGER && (
diff --git a/src/commonComponents/SchemaCard.tsx b/src/commonComponents/SchemaCard.tsx index 5d9c6dab1..e1128768c 100644 --- a/src/commonComponents/SchemaCard.tsx +++ b/src/commonComponents/SchemaCard.tsx @@ -2,6 +2,7 @@ import { Card } from 'flowbite-react'; import { dateConversion } from '../utils/DateConversion'; import DateTooltip from '../components/Tooltip'; import CopyDid from './CopyDid'; +import React from 'react'; interface IProps { className?: string, diff --git a/src/components/organization/Dashboard.tsx b/src/components/organization/Dashboard.tsx index eeb534465..24ba46201 100644 --- a/src/components/organization/Dashboard.tsx +++ b/src/components/organization/Dashboard.tsx @@ -15,7 +15,6 @@ import { pathRoutes } from '../../config/pathRoutes'; import { AlertComponent } from '../AlertComponent'; import WalletSpinup from './walletCommonComponents/WalletSpinup'; import DashboardCard from '../../commonComponents/DashboardCard'; -import React from 'react'; const Dashboard = () => { const [orgData, setOrgData] = useState(null); @@ -25,24 +24,34 @@ const Dashboard = () => { const [failure, setFailure] = useState(null); const [loading, setLoading] = useState(true); const [userRoles, setUserRoles] = useState([]); + const [ecosystemUserRoles, setEcosystemUserRoles] = useState(''); const [orgSuccess, setOrgSuccess] = useState(null); const [openModal, setOpenModal] = useState(false); - const [agentConfigure, setAgentConfigure]=useState(false); - const [isDidCreated, setIsDidCreated]=useState(false); + const EditOrgDetails = () => { setOpenModal(true); }; + const deleteOrgDetails = () => { + window.location.href = pathRoutes.organizations.deleteOrganization + }; + const getUserRoles = async () => { const orgRoles = await getFromLocalStorage(storageKeys.ORG_ROLES); const roles = orgRoles.split(','); setUserRoles(roles); }; + const getEcosystemRole = async () => { + const ecosysmetmRoles = await getFromLocalStorage(storageKeys.ECOSYSTEM_ROLE); + setEcosystemUserRoles(ecosysmetmRoles) + }; + useEffect(() => { getUserRoles(); + getEcosystemRole(); }, []); const fetchOrganizationDetails = async () => { @@ -176,10 +185,14 @@ const Dashboard = () => { )}
+ +
+ - {(userRoles.includes(Roles.OWNER) || +
+ {(userRoles.includes(Roles.OWNER) || userRoles.includes(Roles.ADMIN)) && ( -
+
)} +
+
+ { + userRoles.includes(Roles.OWNER) && ( +
+ + +
+ ) + } +
+
{ + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [organizationData, setOrganizationData] = useState(null); + const [deleteLoading, setDeleteLoading] = useState(false); + const [isWalletPresent, setIsWalletPresent] = useState(false); + const [message, setMessage] = useState(null); + const [showPopup, setShowPopup] = useState(false); + const [ecosystemUserRoles, setEcosystemUserRoles] = useState(''); + const [deleteAction, setDeleteAction] = useState<() => void>(() => {}); + const [confirmMessage, setConfirmMessage] = useState(''); + const [description, setDescription] = useState(""); + const [ecosystemRoles, setEcosystemRoles] = useState([]); + + + const getAllEcosystems = async () => { + try { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getEcosystems(orgId as string); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const ecosystemList = data?.data?.ecosystemList; + + if (ecosystemList && ecosystemList.length > 0) { + const leadEcosystemNames: string[] = []; + ecosystemList.forEach((ecosystem: { name: string; ecosystemOrgs: IEcosystemOrganizations[]; }) => { + ecosystem.ecosystemOrgs.forEach(org => { + const ecosystemRoleName = org.ecosystemRole?.name; + if (ecosystemRoleName === EcosystemRoles.ecosystemLead) { + setEcosystemUserRoles(ecosystemRoleName); + leadEcosystemNames.push(ecosystem.name); + } + }); + }); + setEcosystemRoles(leadEcosystemNames) + } + } + } catch (error) { + console.error('Fetch organization details ERROR::::', error); + } + }; + + const fetchOrganizationDetails = async () => { + try { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId as string); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const walletName = data?.data?.org_agents[0]?.walletName; + if (walletName) { + setIsWalletPresent(true); + } + else { + setIsWalletPresent(false); + } + } + } catch (error) { + console.error('Fetch organization details ERROR::::', error); + } + }; + + const fetchOrganizationReferences = async () => { + setLoading(true); + try { + const response = await getOrganizationReferences(); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const orgData = data?.data; + setOrganizationData(orgData); + } else { + setError(response as string); + } + } catch (error) { + console.error('An error occurred:', error); + setError(error as string); + } + setLoading(false); + }; + + useEffect(() => { + fetchOrganizationReferences(); + fetchOrganizationDetails(); + getAllEcosystems(); + }, []); + + const deleteHandler = async (deleteFunc: () => Promise) => { + setDeleteLoading(true); + try { + await deleteFunc(); + await fetchOrganizationReferences(); + + setShowPopup(false); + } catch (error) { + console.error('An error occurred:', error); + setError(error as string); + } + setDeleteLoading(false); + }; + + const deleteVerifications = async () => { + setDeleteLoading(true); + try { + const response = await deleteVerificationRecords(); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + toast.success(data?.message, {autoClose: 3000}) + await fetchOrganizationReferences(); + setShowPopup(false) + } else { + setError(response as string); + } + } catch (error) { + console.error('An error occurred:', error); + setError(error as string); + } + setDeleteLoading(false); + }; + + const deleteIssuance = async () => { + setDeleteLoading(true); + try { + const response = await deleteIssuanceRecords(); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + toast.success(data?.message, {autoClose: 3000}) + await fetchOrganizationReferences(); + setShowPopup(false) + } else { + setError(response as string); + } + } catch (error) { + console.error('An error occurred:', error); + setError(error as string); + } + setDeleteLoading(false); + }; + + const deleteConnection = async () => { + setDeleteLoading(true); + try { + const response = await deleteConnectionRecords(); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + toast.success(data?.message, {autoClose: 3000}) + await fetchOrganizationReferences(); + setShowPopup(false) + } else { + setError(response as string); + } + } catch (error) { + console.error('An error occurred:', error); + setError(error as string); + } + setDeleteLoading(false); + }; + + const deleteOrgFromEcosystem = async () => { + try { + const response = await deleteOrganizationFromEcosystem(); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + toast.success(data?.message, {autoClose: 3000}) + await fetchOrganizationReferences(); + setShowPopup(false) + } else { + setError(response as string); + } + } catch (error) { + console.error('An error occurred:', error); + setError(error as string); + } + }; + + const deleteOrgWallet = async () => { + try { + const response = await deleteOrganizationWallet(); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + toast.success(data?.message, {autoClose: 3000}) + setIsWalletPresent(false) + await fetchOrganizationReferences(); + setShowPopup(false) + } else { + setError(response as string); + } + } catch (error) { + console.error('An error occurred:', error); + setError(error as string); + } + }; + + const deleteOrganizations = async () => { + try { + const response = await deleteOrganization(); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + toast.success(data?.message, {autoClose: 3000}) + await fetchOrganizationReferences(); + setShowPopup(false) + } else { + setError(response as string); + } + } catch (error) { + console.error('An error occurred:', error); + setError(error as string); + } + await removeFromLocalStorage(storageKeys.ORG_INFO); + await removeFromLocalStorage(storageKeys.ORG_DETAILS); + await removeFromLocalStorage(storageKeys.ORG_ROLES); + await removeFromLocalStorage(storageKeys.ORG_ID); + setTimeout(() => { + window.location.href = pathRoutes.organizations.root; + }, 3000); + + }; + + const deleteFunctions = { + deleteVerifications, + deleteIssuance, + deleteConnection, + deleteOrgFromEcosystem, + deleteOrgWallet, + deleteOrganizations + }; + + const cardData = [ + { + title: "Verifications", + description: "Verifications is the list of verified Credentials", + count: organizationData?.verificationRecordsCount ?? 0, + deleteFunc: deleteFunctions.deleteVerifications, + confirmMessage:"Are you sure you want to delete verification records", + isDisabled: false + }, + { + title: "Issuance", + description: "Issuance is the list of Credentials", + count: organizationData?.issuanceRecordsCount ?? 0, + deleteFunc: deleteFunctions.deleteIssuance, + confirmMessage:"Are you sure you want to delete issuance records", + isDisabled: (organizationData?.verificationRecordsCount ?? 0) > 0 + }, + { + title: "Connections", + description: "Connections is the list of connections", + count: organizationData?.connectionRecordsCount ?? 0, + deleteFunc: deleteFunctions.deleteConnection, + confirmMessage:"Are you sure you want to delete connection records", + isDisabled: (organizationData?.issuanceRecordsCount ?? 0) > 0 || (organizationData?.verificationRecordsCount ?? 0) > 0 + }, + { + title: "Ecosystem members", + description: "Ecosystem members", + count: organizationData?.orgEcosystemsCount ?? 0, + deleteFunc: deleteFunctions.deleteOrgFromEcosystem, + confirmMessage:"Are you sure you want to remove your organization from eocystem", + isDisabled: ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) || ((organizationData?.connectionRecordsCount ?? 0) > 0 ||(organizationData?.issuanceRecordsCount ?? 0) > 0 || (organizationData?.verificationRecordsCount ?? 0) > 0) + }, + { + title: "Organization wallet", + description: "Organization wallet", + count: isWalletPresent ? 1 : 0, + deleteFunc: deleteFunctions.deleteOrgWallet, + confirmMessage: "Are you sure you want to delete organization wallet", + isDisabled: ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) || + ((organizationData?.orgEcosystemsCount ?? 0) > 0 || + (organizationData?.connectionRecordsCount ?? 0) > 0 || + (organizationData?.issuanceRecordsCount ?? 0) > 0 || + (organizationData?.verificationRecordsCount ?? 0) > 0) + + }, + { + title: "Organization", + description: "Organization", + deleteFunc: deleteFunctions.deleteOrganizations, + confirmMessage:"Are you sure you want to delete organization", + isDisabled: isWalletPresent || ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) + } + ]; + return ( +
+ +

+ Delete Organization +

+ + + { + setMessage(null); + setError(null); + }} + /> +{ecosystemRoles.length > 0 && +

+ You are Ecosystem Lead for {ecosystemRoles.join(', ')}. You cannot remove yourself from the ecosystem, delete the organization's wallet, and delete your organization. +

+ } + {organizationData && ( +
+ {cardData.map((card, index) => ( + { + setShowPopup(true); + setDeleteAction(() => deleteFunc); + setConfirmMessage(card.confirmMessage); + setDescription(card.description) + }} + /> + ))} + + setShowPopup(false)} + onSuccess={() => deleteHandler(deleteAction as () => Promise)} + message={confirmMessage} + buttonTitles={["No, cancel", "Yes, delete"]} + isProcessing={deleteLoading} + setFailure={setError} + setSuccess={setMessage} + warning={description} + /> +
+ )} +
+ ); +}; + +export default DeleteOrganizations; \ No newline at end of file diff --git a/src/components/organization/DeleteOrganizationsCard.tsx b/src/components/organization/DeleteOrganizationsCard.tsx new file mode 100644 index 000000000..20fffdcb7 --- /dev/null +++ b/src/components/organization/DeleteOrganizationsCard.tsx @@ -0,0 +1,50 @@ +// DeleteOrganizationsCard.tsx + +import React from "react"; +import { Card } from "flowbite-react"; + +interface CardProps { + title?: string; + description?: string; + count?: number; + deleteFunc?: () => void; + confirmMessage?: string; + isDisabled?: boolean; + onDeleteClick: (deleteFunc: () => void, confirmMessage: string) => void; +} + +const DeleteOrganizationsCard: React.FC = ({ + title, + description, + count, + deleteFunc, + confirmMessage, + isDisabled = false, + onDeleteClick +}) => ( + +
+

+

{title}

+

{description}

+ {count &&

Total: {count}

} +

+ +
+
+); + +export default DeleteOrganizationsCard; diff --git a/src/components/organization/interfaces/index.ts b/src/components/organization/interfaces/index.ts index 8aa6fd52c..4f9c93240 100644 --- a/src/components/organization/interfaces/index.ts +++ b/src/components/organization/interfaces/index.ts @@ -250,3 +250,31 @@ export interface ILedgerItem { deletedAt: string | null; } + + export interface IOrgCount { + verificationRecordsCount: number; + connectionRecordsCount: number; + issuanceRecordsCount: number; + orgEcosystemsCount: number; + orgInvitationsCount: number; + orgUsersCount: number; + } + export interface IEcosystemRole { + id: string; + name: string; + description: string; + createDateTime: string; + lastChangedDateTime: string; + deletedAt: string | null; + } + + export interface IEcosystemOrganizations { + id: string; + orgId: string; + status: string; + createDateTime: string; + lastChangedDateTime: string; + ecosystemId: string; + ecosystemRoleId: string; + ecosystemRole: IEcosystemRole; + } \ No newline at end of file diff --git a/src/config/apiRoutes.ts b/src/config/apiRoutes.ts index 9a2aa7366..f2a9b2f33 100644 --- a/src/config/apiRoutes.ts +++ b/src/config/apiRoutes.ts @@ -41,7 +41,12 @@ export const apiRoutes = { editUserROle: '/user-roles', didList: '/dids', createDid: '/agents/did', - primaryDid: '/primary-did' + primaryDid: '/primary-did', + getOrgReferences:'/activity-count', + deleteOrganization:'/organizations/deleteorganizations', + deleteVerifications:'/verification-records', + deleteIssaunce:'/issuance-records', + deleteConnections:'/connections' }, connection: { create: '/connections', @@ -82,7 +87,9 @@ export const apiRoutes = { agentSharedSpinup: '/agents/wallet', getLedgerConfig: '/agents/ledgerConfig', createPolygonKeys: '/agents/polygon/create-keys', - setAgentConfig:'/agents/configure' + setAgentConfig:'/agents/configure', + deleteWallet: '/agents/wallet', + }, Platform: { getAllSchemaFromPlatform: '/platform/schemas', @@ -107,6 +114,7 @@ export const apiRoutes = { invitations: '/invitations', usersInvitation: '/users/invitations', members: '/members', + deleteOrgFromEcosystem:'/member-org' }, setting:{ setting: '/client_credentials' diff --git a/src/config/pathRoutes.ts b/src/config/pathRoutes.ts index d4df16e15..2235a8645 100644 --- a/src/config/pathRoutes.ts +++ b/src/config/pathRoutes.ts @@ -1,3 +1,4 @@ +import { deleteOrganizationInvitation } from "../api/organization"; import { envConfig } from "./envConfig"; export const pathRoutes = { @@ -27,6 +28,10 @@ export const pathRoutes = { issuedCredentials: '/organizations/credentials', credentials: '/organizations/verification', createSchema: '/organizations/schemas/create', + deleteOrganization:'/organizations/deleteorganizations', + + + viewSchema: '/organizations/schemas/view-schema', Issuance: { issue: '/organizations/credentials/issue', diff --git a/src/pages/organizations/dashboard/index.astro b/src/pages/organizations/dashboard/index.astro index 3612ea7c9..32d1b96e9 100644 --- a/src/pages/organizations/dashboard/index.astro +++ b/src/pages/organizations/dashboard/index.astro @@ -13,4 +13,4 @@ if (!response.authorized) { - \ No newline at end of file + \ No newline at end of file diff --git a/src/pages/organizations/deleteorganizations.astro b/src/pages/organizations/deleteorganizations.astro new file mode 100644 index 000000000..59902bdd6 --- /dev/null +++ b/src/pages/organizations/deleteorganizations.astro @@ -0,0 +1,16 @@ +--- +import LayoutSidebar from '../../app/LayoutSidebar.astro'; +import { checkUserSession } from '../../utils/check-session'; +import { pathRoutes } from '../../config/pathRoutes'; +import DeleteOrganizations from '../../components/organization/DeleteOrganization'; + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + From d3fce1a4db1d47dd52a87533bf276ff6532252ad Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:28:59 +0530 Subject: [PATCH 17/54] fix: custom-avatar-css-issues (#714) Signed-off-by: pranalidhanavade --- src/commonComponents/EcosystemProfileCard.tsx | 9 +++++++-- src/components/EcosystemInvite/EcoInvitationList.tsx | 4 ++-- .../organization/invitations/ReceivedInvitations.tsx | 10 ++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/commonComponents/EcosystemProfileCard.tsx b/src/commonComponents/EcosystemProfileCard.tsx index d1623d1ca..51a18fae6 100644 --- a/src/commonComponents/EcosystemProfileCard.tsx +++ b/src/commonComponents/EcosystemProfileCard.tsx @@ -10,6 +10,7 @@ import { RoleTablet } from '../components/Ecosystem/Dashboard' import CustomSpinner from '../components/CustomSpinner'; import { pathRoutes } from '../config/pathRoutes'; import { EmptyListMessage } from '../components/EmptyListComponent'; +import React from 'react'; @@ -86,9 +87,13 @@ const EcosystemProfileCard = ({getEndorsementListData}:IEndorsement) => { >
{ecosystemDetails?.logoUrl ? ( - + ) : ( - + )}
diff --git a/src/components/EcosystemInvite/EcoInvitationList.tsx b/src/components/EcosystemInvite/EcoInvitationList.tsx index c9e0575ff..bb706d387 100644 --- a/src/components/EcosystemInvite/EcoInvitationList.tsx +++ b/src/components/EcosystemInvite/EcoInvitationList.tsx @@ -9,9 +9,9 @@ const EcoInvitationList = (props: InvitationProps) => {
{logoUrl ? ( - + ) : ( - + )}
diff --git a/src/components/organization/invitations/ReceivedInvitations.tsx b/src/components/organization/invitations/ReceivedInvitations.tsx index 9e0e71128..9282bd21d 100644 --- a/src/components/organization/invitations/ReceivedInvitations.tsx +++ b/src/components/organization/invitations/ReceivedInvitations.tsx @@ -159,14 +159,16 @@ const ReceivedInvitations = () => {
{invitation.organisation.logoUrl ? ( - ) : ( - )}
From 42bd8135c638998970a9390f73c8e61fcc22dfa6 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Wed, 26 Jun 2024 17:06:02 +0530 Subject: [PATCH 18/54] feat: delete wallet and organization (#715) * feat: delete organization and wallet Signed-off-by: pranalidhanavade * feat: delete org and wallet Signed-off-by: pranalidhanavade * feat: added custom card component for delet card Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: removed unused code Signed-off-by: pranalidhanavade * changes in delete organization svg image Signed-off-by: pranalidhanavade * changes in delete organization svg image Signed-off-by: pranalidhanavade * fix: resolved comments on pull request Signed-off-by: pranalidhanavade * feat: added custom message for ecosystem lead role Signed-off-by: pranalidhanavade * feat: added interfaces into interface file Signed-off-by: pranalidhanavade * fix: resolved comments on pull request Signed-off-by: pranalidhanavade * fix: dark mode css issues Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- .../organization/DeleteOrganization.tsx | 25 +++++++++++-------- .../organization/DeleteOrganizationsCard.tsx | 4 +-- src/config/pathRoutes.ts | 2 +- ...tions.astro => delete-organizations.astro} | 4 +-- 4 files changed, 20 insertions(+), 15 deletions(-) rename src/pages/organizations/{deleteorganizations.astro => delete-organizations.astro} (79%) diff --git a/src/components/organization/DeleteOrganization.tsx b/src/components/organization/DeleteOrganization.tsx index 6ba350424..87e751bac 100644 --- a/src/components/organization/DeleteOrganization.tsx +++ b/src/components/organization/DeleteOrganization.tsx @@ -37,6 +37,7 @@ const DeleteOrganizations = () => { const [confirmMessage, setConfirmMessage] = useState(''); const [description, setDescription] = useState(""); const [ecosystemRoles, setEcosystemRoles] = useState([]); + const [orgName, setOrgName] = useState(""); const getAllEcosystems = async () => { @@ -73,6 +74,10 @@ const DeleteOrganizations = () => { const { data } = response as AxiosResponse; if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { const walletName = data?.data?.org_agents[0]?.walletName; + const orgName = data?.data?.name; + if(orgName){ + setOrgName(orgName) + } if (walletName) { setIsWalletPresent(true); } @@ -258,7 +263,7 @@ const DeleteOrganizations = () => { const cardData = [ { title: "Verifications", - description: "Verifications is the list of verified Credentials", + description: "Verifications is the list of verification records", count: organizationData?.verificationRecordsCount ?? 0, deleteFunc: deleteFunctions.deleteVerifications, confirmMessage:"Are you sure you want to delete verification records", @@ -266,31 +271,31 @@ const DeleteOrganizations = () => { }, { title: "Issuance", - description: "Issuance is the list of Credentials", + description: "Issuance is the list of credential records", count: organizationData?.issuanceRecordsCount ?? 0, deleteFunc: deleteFunctions.deleteIssuance, - confirmMessage:"Are you sure you want to delete issuance records", + confirmMessage:"Are you sure you want to delete credential records", isDisabled: (organizationData?.verificationRecordsCount ?? 0) > 0 }, { title: "Connections", - description: "Connections is the list of connections", + description: "Connections is the list of connection records", count: organizationData?.connectionRecordsCount ?? 0, deleteFunc: deleteFunctions.deleteConnection, confirmMessage:"Are you sure you want to delete connection records", isDisabled: (organizationData?.issuanceRecordsCount ?? 0) > 0 || (organizationData?.verificationRecordsCount ?? 0) > 0 }, { - title: "Ecosystem members", - description: "Ecosystem members", + title: "Ecosystem", + description: "Ecosystems your organization has joined as a member", count: organizationData?.orgEcosystemsCount ?? 0, deleteFunc: deleteFunctions.deleteOrgFromEcosystem, - confirmMessage:"Are you sure you want to remove your organization from eocystem", + confirmMessage:"Are you sure you want to remove your organization from ecoystem", isDisabled: ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) || ((organizationData?.connectionRecordsCount ?? 0) > 0 ||(organizationData?.issuanceRecordsCount ?? 0) > 0 || (organizationData?.verificationRecordsCount ?? 0) > 0) }, { title: "Organization wallet", - description: "Organization wallet", + description: "", count: isWalletPresent ? 1 : 0, deleteFunc: deleteFunctions.deleteOrgWallet, confirmMessage: "Are you sure you want to delete organization wallet", @@ -305,7 +310,7 @@ const DeleteOrganizations = () => { title: "Organization", description: "Organization", deleteFunc: deleteFunctions.deleteOrganizations, - confirmMessage:"Are you sure you want to delete organization", + confirmMessage:`Are you sure you want to delete organization ${orgName}`, isDisabled: isWalletPresent || ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) } ]; @@ -326,7 +331,7 @@ const DeleteOrganizations = () => { }} /> {ecosystemRoles.length > 0 && -

+

You are Ecosystem Lead for {ecosystemRoles.join(', ')}. You cannot remove yourself from the ecosystem, delete the organization's wallet, and delete your organization.

} diff --git a/src/components/organization/DeleteOrganizationsCard.tsx b/src/components/organization/DeleteOrganizationsCard.tsx index 20fffdcb7..a6677dffa 100644 --- a/src/components/organization/DeleteOrganizationsCard.tsx +++ b/src/components/organization/DeleteOrganizationsCard.tsx @@ -23,11 +23,11 @@ const DeleteOrganizationsCard: React.FC = ({ onDeleteClick }) => ( -
+

{title}

{description}

- {count &&

Total: {count}

} + {count &&

Total:{count}

}

- - + Issue + +
+ )}
) diff --git a/src/components/Resources/Schema/Create.tsx b/src/components/Resources/Schema/Create.tsx index 5ed1c5957..6cb8add65 100644 --- a/src/components/Resources/Schema/Create.tsx +++ b/src/components/Resources/Schema/Create.tsx @@ -22,8 +22,8 @@ import type { ICheckEcosystem} from '../../../config/ecosystem'; import { createSchemaRequest } from '../../../api/ecosystem'; import EcosystemProfileCard from '../../../commonComponents/EcosystemProfileCard'; import ConfirmationModal from '../../../commonComponents/ConfirmationModal'; -import { SchemaType } from '../../../common/enums'; -import React from 'react'; +import { DidMethod, SchemaType, SchemaTypeValue } from '../../../common/enums'; +import { getOrganizationById } from '../../../api/organization'; const options = [ { @@ -60,6 +60,8 @@ const CreateSchema = () => { }); const [isEcosystemData, setIsEcosystemData] = useState(); const [loading, setLoading] = useState(false); + const [schemaTypeValues, setSchemaTypeValues]= useState() + const [type, setType] = useState(); const initFormData: IFormData = { schemaName: '', @@ -85,7 +87,7 @@ const CreateSchema = () => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); setOrgId(orgId); })(); - + fetchOrganizationDetails(); checkEcosystemData(); }, []); @@ -106,14 +108,53 @@ const CreateSchema = () => { return true; }; + const fetchOrganizationDetails = async () => { + setLoading(true); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId as string); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const did = data?.data?.org_agents?.[0]?.orgDid; + if (did) { + if (did.includes(DidMethod.INDY)) { + setSchemaTypeValues(SchemaTypeValue.INDY); + setType(SchemaType.INDY); + } else if (did.includes(DidMethod.POLYGON)) { + setType(SchemaType.W3C); + setSchemaTypeValues(SchemaTypeValue.POLYGON); + } + else if (did.includes(DidMethod.KEY)) { + setType(SchemaType.W3C); + setSchemaTypeValues(SchemaTypeValue.NO_LEDGER); + } + else if (did.includes(DidMethod.WEB)) { + setType(SchemaType.W3C); + setSchemaTypeValues(SchemaTypeValue.NO_LEDGER); + } + } + } else { + setFailure(response as string); + } + setLoading(false); + }; + + const submit = async (values: IFormData) => { setCreateLoader(true); + if (!type) { + setFailure("Schema type not determined."); + setCreateLoader(false); + return; + } const schemaFieldName: FieldName = { - type: SchemaType.INDY, + type: type, schemaPayload: { schemaName: values.schemaName, + schemaType:schemaTypeValues, schemaVersion: values.schemaVersion, attributes: values.attribute, + description:values.schemaName, orgId: orgId, } }; @@ -331,6 +372,21 @@ const CreateSchema = () => { } }; + let filteredOptions: any[] = []; + +if ( + schemaTypeValues === SchemaTypeValue.POLYGON || + schemaTypeValues === SchemaTypeValue.NO_LEDGER +) { + filteredOptions = options.filter( + (opt) => opt.label === 'String' || opt.label === 'Number' + ); +} else if (schemaTypeValues === SchemaTypeValue.INDY) { + filteredOptions = options; +} +console.log("Schema Type Values: ", schemaTypeValues); +console.log("Filtered Options: ", filteredOptions); + return (
@@ -542,12 +598,12 @@ const CreateSchema = () => { disabled={!areFirstInputsSelected} className="w-full bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" > - {options.map((opt) => { + {filteredOptions.map((opt) => { return ( diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index 89dd5dfe8..eb4567459 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -16,14 +16,14 @@ import RoleViewButton from '../../RoleViewButton'; import SchemaCard from '../../../commonComponents/SchemaCard'; import type { SchemaDetails } from '../../Verification/interface'; import SearchInput from '../../SearchInput'; -import { getFromLocalStorage } from '../../../api/Auth'; +import { getFromLocalStorage, setToLocalStorage } from '../../../api/Auth'; import { pathRoutes } from '../../../config/pathRoutes'; import { getOrganizationById } from '../../../api/organization'; import { checkEcosystem } from '../../../config/ecosystem'; import type { ICheckEcosystem } from '../../../config/ecosystem'; import { Create, SchemaEndorsement } from '../../Issuance/Constant'; -import { SchemaType } from '../../../common/enums'; +import { DidMethod, SchemaType } from '../../../common/enums'; const SchemaList = (props: { schemaSelectionCallback: ( @@ -48,6 +48,8 @@ const SchemaList = (props: { const [totalItem, setTotalItem] = useState(0); const [isEcosystemData, setIsEcosystemData] = useState(); const [searchValue, setSearchValue] = useState(''); + const [w3cSchema,setW3cSchema]= useState(false); + const [isNoLedger,setisNoLedger]= useState(false); const getSchemaList = async ( schemaListAPIParameter: GetAllSchemaListParameter, @@ -66,7 +68,8 @@ const SchemaList = (props: { organizationId, ); } - const { data } = schemaList as AxiosResponse; + + const { data } = schemaList as AxiosResponse; if (schemaList === 'Schema records not found') { setLoading(false); setSchemaList([]); @@ -102,7 +105,6 @@ const SchemaList = (props: { getSchemaList(schemaListAPIParameter, false); }, [schemaListAPIParameter, allSchemaFlag]); - const onSearch = async ( event: ChangeEvent, ): Promise => { @@ -161,9 +163,21 @@ const SchemaList = (props: { const response = await getOrganizationById(orgId); const { data } = response as AxiosResponse; if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const did = data?.data?.org_agents?.[0]?.orgDid; + if (data?.data?.org_agents && data?.data?.org_agents?.length > 0) { setWalletStatus(true); } + + if (did.includes(DidMethod.POLYGON) || did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { + setW3cSchema(true); + } + if (did.includes(DidMethod.INDY)) { + setW3cSchema(false); + } + if (did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { + setisNoLedger(true); + } } setLoading(false); }; @@ -189,6 +203,7 @@ const SchemaList = (props: { const emptyListBtn = isEcosystemData?.isEcosystemMember ? { title: 'Schema Endorsement', svg: } : { title: 'Create Schema', svg: }; + return (
@@ -266,6 +281,9 @@ const SchemaList = (props: { attributes={element['attributes']} created={element['createDateTime']} onClickCallback={schemaSelectionCallback} + w3cSchema={w3cSchema} + noLedger={isNoLedger} + />
))} diff --git a/src/components/Resources/Schema/interfaces/index.ts b/src/components/Resources/Schema/interfaces/index.ts index a29afb0dd..950d01380 100644 --- a/src/components/Resources/Schema/interfaces/index.ts +++ b/src/components/Resources/Schema/interfaces/index.ts @@ -1,3 +1,5 @@ +import type { SchemaTypeValue } from "../../../../common/enums"; + export interface GetAllSchemaListParameter { itemPerPage: number, page: number, @@ -64,7 +66,9 @@ export interface FieldName { export interface schemaDetails { schemaName: string; schemaVersion: string; - attributes: IAttributes[] + schemaType?:SchemaTypeValue; + attributes: IAttributes[]; + description?:string; orgId: string; } @@ -92,6 +96,8 @@ export interface IAttributes { export interface IFormData { schemaName: string; schemaVersion: string; + schemaType?:SchemaTypeValue; + description?:string; attribute: IAttributes[]; } diff --git a/src/components/organization/DeleteOrganization.tsx b/src/components/organization/DeleteOrganization.tsx index 87e751bac..040fb706a 100644 --- a/src/components/organization/DeleteOrganization.tsx +++ b/src/components/organization/DeleteOrganization.tsx @@ -34,7 +34,7 @@ const DeleteOrganizations = () => { const [showPopup, setShowPopup] = useState(false); const [ecosystemUserRoles, setEcosystemUserRoles] = useState(''); const [deleteAction, setDeleteAction] = useState<() => void>(() => {}); - const [confirmMessage, setConfirmMessage] = useState(''); + const [confirmMessage, setConfirmMessage] = useState(''); const [description, setDescription] = useState(""); const [ecosystemRoles, setEcosystemRoles] = useState([]); const [orgName, setOrgName] = useState(""); @@ -215,6 +215,9 @@ const DeleteOrganizations = () => { toast.success(data?.message, {autoClose: 3000}) setIsWalletPresent(false) await fetchOrganizationReferences(); + setTimeout(() => { + window.location.reload(); + }, 3000); setShowPopup(false) } else { setError(response as string); @@ -310,7 +313,7 @@ const DeleteOrganizations = () => { title: "Organization", description: "Organization", deleteFunc: deleteFunctions.deleteOrganizations, - confirmMessage:`Are you sure you want to delete organization ${orgName}`, + confirmMessage:{`Are you sure you want to delete organization `}"{orgName}", isDisabled: isWalletPresent || ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) } ]; From f24a5749c354ce7f7d7a343737705b4040fa8a44 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Tue, 30 Jul 2024 14:12:17 +0530 Subject: [PATCH 24/54] wip: verification with email (#722) * wip: email verification Signed-off-by: bhavanakarwade * wip: verification using email Signed-off-by: bhavanakarwade * wip: verification with email Signed-off-by: bhavanakarwade * wip: routes changes for verification dashboard Signed-off-by: bhavanakarwade * wip: routes changes Signed-off-by: bhavanakarwade * wip: Implemented custom dashboard component for veriifcation and issuance dashboard Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- src/commonComponents/Dashboard.tsx | 74 +++++++++++++++++++ src/components/Issuance/IssueDashboard.tsx | 60 +++------------ src/components/Verification/Verification.tsx | 2 +- .../VerificationCredentialList.tsx | 2 +- .../Verification/VerificationDashboard.tsx | 33 +++++++++ src/config/pathRoutes.ts | 18 +++-- .../email}/index.astro | 9 +-- .../schemas/cred-defs/attributes/index.astro | 16 ++++ .../email/schemas/cred-defs/index.astro | 18 +++++ .../email/schemas}/index.astro | 5 +- .../index.astro | 5 +- .../schemas/cred-defs/connections/index.astro | 17 +++++ .../cred-defs/connections/verification.astro | 9 ++- .../schemas/cred-defs/index.astro | 16 ++++ .../verify-credentials/schemas/index.astro | 17 +++++ 15 files changed, 227 insertions(+), 74 deletions(-) create mode 100644 src/commonComponents/Dashboard.tsx create mode 100644 src/components/Verification/VerificationDashboard.tsx rename src/pages/organizations/verification/{schemas/cred-defs => verify-credentials/email}/index.astro (63%) create mode 100644 src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro create mode 100644 src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro rename src/pages/organizations/verification/{schemas/cred-defs/connections => verify-credentials/email/schemas}/index.astro (68%) rename src/pages/organizations/verification/{schemas => verify-credentials}/index.astro (69%) create mode 100644 src/pages/organizations/verification/verify-credentials/schemas/cred-defs/connections/index.astro rename src/pages/organizations/verification/{ => verify-credentials}/schemas/cred-defs/connections/verification.astro (51%) create mode 100644 src/pages/organizations/verification/verify-credentials/schemas/cred-defs/index.astro create mode 100644 src/pages/organizations/verification/verify-credentials/schemas/index.astro diff --git a/src/commonComponents/Dashboard.tsx b/src/commonComponents/Dashboard.tsx new file mode 100644 index 000000000..4fb595b45 --- /dev/null +++ b/src/commonComponents/Dashboard.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import { Card } from 'flowbite-react'; +import BackButton from '../commonComponents/backbutton'; +import BreadCrumbs from '../components/BreadCrumbs' + +const Dashboard = ({ title, options, backButtonPath }) => { + return ( +
+
+ +
+
+

+ {title} +

+ +
+
+

+ Select the appropriate action +

+
+ {options.map((option) => ( + option.path && (window.location.href = option.path)} + > +
+
+
+ {option.heading} +
+

+ {option.description} +

+
+
+
+ ))} +
+
+
+ ); +}; + +export default Dashboard; diff --git a/src/components/Issuance/IssueDashboard.tsx b/src/components/Issuance/IssueDashboard.tsx index 43a106176..73cb90224 100644 --- a/src/components/Issuance/IssueDashboard.tsx +++ b/src/components/Issuance/IssueDashboard.tsx @@ -1,10 +1,7 @@ -import '../../common/global.css'; -import { Card } from 'flowbite-react'; -import BreadCrumbs from '../BreadCrumbs'; +import Dashboard from '../../commonComponents/Dashboard'; import { pathRoutes } from '../../config/pathRoutes'; -import BackButton from '../../commonComponents/backbutton'; -const IssueDashboard = () => { +const IssuanceDashboard = () => { const options = [ { heading: 'Connection', @@ -23,53 +20,14 @@ const IssueDashboard = () => { path: pathRoutes.organizations.Issuance.bulkIssuance, }, ]; + return ( -
-
- -
-
-

- Issue Credential -

- -
-
-

- Select the appropriate action for issuing credential(s){' '} -

-
- {options.map((option) => ( - (window.location.href = option?.path)} - > -
-
-
- {option.heading} -
-

- {option.description} -

-
-
-
- ))} -
-
-
+ ); }; -export default IssueDashboard; +export default IssuanceDashboard; diff --git a/src/components/Verification/Verification.tsx b/src/components/Verification/Verification.tsx index 7f85f7af6..cbd759c93 100644 --- a/src/components/Verification/Verification.tsx +++ b/src/components/Verification/Verification.tsx @@ -365,7 +365,7 @@ const VerificationCred = () => {
- +

Verification diff --git a/src/components/Verification/VerificationCredentialList.tsx b/src/components/Verification/VerificationCredentialList.tsx index a87872cb4..92d778785 100644 --- a/src/components/Verification/VerificationCredentialList.tsx +++ b/src/components/Verification/VerificationCredentialList.tsx @@ -324,7 +324,7 @@ const VerificationCredentialList = () => { }, [listAPIParameter]); const schemeSelection = () => { - window.location.href = pathRoutes.organizations.verification.schema; + window.location.href = pathRoutes.organizations.verification.requestProof; }; const refreshPage = () => { diff --git a/src/components/Verification/VerificationDashboard.tsx b/src/components/Verification/VerificationDashboard.tsx new file mode 100644 index 000000000..c98cbf7cf --- /dev/null +++ b/src/components/Verification/VerificationDashboard.tsx @@ -0,0 +1,33 @@ +import { pathRoutes } from '../../config/pathRoutes'; +import Dashboard from '../../commonComponents/Dashboard'; + +const VerificationDashboard = () => { + const options = [ + { + heading: 'Connection', + description: 'Verify credential(s) by selecting existing connections', + path: pathRoutes.organizations.verification.schema, + }, + { + heading: 'Email', + description: 'Verify credential(s) by entering email ID for specific user', + path: null, + }, + { + heading: 'Bulk', + description: 'Verify credential(s) in bulk by uploading .csv file records', + path: null, + }, + ]; + + return ( + + ); +}; + +export default VerificationDashboard; + diff --git a/src/config/pathRoutes.ts b/src/config/pathRoutes.ts index 932ea78a1..d61332156 100644 --- a/src/config/pathRoutes.ts +++ b/src/config/pathRoutes.ts @@ -48,11 +48,15 @@ export const pathRoutes = { emailHistory: '/organizations/credentials/issue/email/history', }, verification: { - schema: '/organizations/verification/schemas', - credDef: '/organizations/verification/schemas/cred-defs', - connections: '/organizations/verification/schemas/cred-defs/connections', + requestProof: '/organizations/verification/verify-credentials', + email: '/organizations/verification/verify-credentials/email/schemas', + schema: '/organizations/verification/verify-credentials/schemas', + credDef: '/organizations/verification/verify-credentials/schemas/cred-defs', + attributes: '/organizations/verification/verify-credentials/schemas/cred-defs/attributes', + emailCredDef: '/organizations/verification/verify-credentials/email/schemas/cred-defs', + connections: '/organizations/verification/verify-credentials/schemas/cred-defs/connections', verify: - '/organizations/verification/schemas/cred-defs/connections/verification', + '/organizations/verification/verify-credentials/schemas/cred-defs/connections/verification', }, }, ecosystem: { @@ -78,9 +82,9 @@ export const pathRoutes = { schemas: '/organizations/schemas', }, verification: { - credDef: '/organizations/verification/schemas/cred-defs', - schemas: '/organizations/verification/schemas', - verification: '/organizations/verification/schemas/cred-defs/connections', + credDef: '/organizations/verification/verify-credentials/schemas/cred-defs', + schemas: '/organizations/verification/verify-credentials/schemas', + verification: '/organizations/verification/verify-credentials/schemas/cred-defs/connections', }, issuance: { credDef: '/organizations/credentials/issue/schemas/cred-defs', diff --git a/src/pages/organizations/verification/schemas/cred-defs/index.astro b/src/pages/organizations/verification/verify-credentials/email/index.astro similarity index 63% rename from src/pages/organizations/verification/schemas/cred-defs/index.astro rename to src/pages/organizations/verification/verify-credentials/email/index.astro index dfd5a9641..9a5e5ed8b 100644 --- a/src/pages/organizations/verification/schemas/cred-defs/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/index.astro @@ -1,11 +1,8 @@ --- -import { useEffect } from 'react'; -import { getFromLocalStorage } from '../../../../../api/Auth'; import LayoutSidebar from '../../../../../app/LayoutSidebar.astro'; -import CredDefSelection from '../../../../../components/Verification/CredDefSelection'; -import { storageKeys } from '../../../../../config/CommonConstant'; -import { checkUserSession } from '../../../../../utils/check-session'; +// import EmailVerification from '../../../../../components/Verification/EmailVerification'; import { pathRoutes } from '../../../../../config/pathRoutes'; +import { checkUserSession } from '../../../../../utils/check-session'; const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); const route: string = pathRoutes.auth.sinIn @@ -15,5 +12,5 @@ if (!response.authorized) { --- - + diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro new file mode 100644 index 000000000..a930a1119 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro @@ -0,0 +1,16 @@ +--- +import LayoutSidebar from "../../../../../../../../app/LayoutSidebar.astro"; +// import EmailCredDefSelection from "../../../../../../../../components/Verification/EmailCredDefSelection"; +import { pathRoutes } from "../../../../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../../../../utils/check-session"; + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro new file mode 100644 index 000000000..9fabbb4d5 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro @@ -0,0 +1,18 @@ +--- +import LayoutSidebar from "../../../../../../../app/LayoutSidebar.astro"; +import CredDefSelection from "../../../../../../../components/Verification/CredDefSelection"; +// import EmailCredDefSelection from "../../../../../../../components/Verification/EmailCredDefSelection"; +import { pathRoutes } from "../../../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../../../utils/check-session"; + + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/verification/schemas/cred-defs/connections/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro similarity index 68% rename from src/pages/organizations/verification/schemas/cred-defs/connections/index.astro rename to src/pages/organizations/verification/verify-credentials/email/schemas/index.astro index 9001938f0..8acd29c88 100644 --- a/src/pages/organizations/verification/schemas/cred-defs/connections/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro @@ -1,6 +1,7 @@ --- import LayoutSidebar from "../../../../../../app/LayoutSidebar.astro"; -import Connections from "../../../../../../components/Verification/Connections"; +// import EmailSchemaSelection from "../../../../../../components/Verification/EmailSchemaSelection"; +import SchemaSelection from "../../../../../../components/Verification/SchemaSelection"; import { pathRoutes } from "../../../../../../config/pathRoutes"; import { checkUserSession } from "../../../../../../utils/check-session"; @@ -12,5 +13,5 @@ if (!response.authorized) { --- - + diff --git a/src/pages/organizations/verification/schemas/index.astro b/src/pages/organizations/verification/verify-credentials/index.astro similarity index 69% rename from src/pages/organizations/verification/schemas/index.astro rename to src/pages/organizations/verification/verify-credentials/index.astro index 9e3978aec..51eacdcf4 100644 --- a/src/pages/organizations/verification/schemas/index.astro +++ b/src/pages/organizations/verification/verify-credentials/index.astro @@ -1,8 +1,9 @@ --- import LayoutSidebar from '../../../../app/LayoutSidebar.astro'; -import SchemaSelection from '../../../../components/Verification/SchemaSelection' import { pathRoutes } from '../../../../config/pathRoutes'; import { checkUserSession } from '../../../../utils/check-session'; +import IssueDashboard from '../../../../components/Issuance/IssueDashboard' +import VerificationDashboard from '../../../../components/Verification/VerificationDashboard'; const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); const route: string = pathRoutes.auth.sinIn @@ -12,5 +13,5 @@ if (!response.authorized) { --- - + diff --git a/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/connections/index.astro b/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/connections/index.astro new file mode 100644 index 000000000..ee0e4fe77 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/connections/index.astro @@ -0,0 +1,17 @@ +--- +import LayoutSidebar from "../../../../../../../app/LayoutSidebar.astro"; +import Connections from "../../../../../../../components/Verification/Connections"; +import { pathRoutes } from "../../../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../../../utils/check-session"; + + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/verification/schemas/cred-defs/connections/verification.astro b/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/connections/verification.astro similarity index 51% rename from src/pages/organizations/verification/schemas/cred-defs/connections/verification.astro rename to src/pages/organizations/verification/verify-credentials/schemas/cred-defs/connections/verification.astro index 1bc2faf61..28de35046 100644 --- a/src/pages/organizations/verification/schemas/cred-defs/connections/verification.astro +++ b/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/connections/verification.astro @@ -1,8 +1,9 @@ --- -import LayoutSidebar from "../../../../../../app/LayoutSidebar.astro"; -import VerificationCred from "../../../../../../components/Verification/Verification"; -import { pathRoutes } from "../../../../../../config/pathRoutes"; -import { checkUserSession } from "../../../../../../utils/check-session"; +import LayoutSidebar from "../../../../../../../app/LayoutSidebar.astro"; +import VerificationCred from "../../../../../../../components/Verification/Verification"; +import { pathRoutes } from "../../../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../../../utils/check-session"; + const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); const route: string = pathRoutes.auth.sinIn diff --git a/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/index.astro b/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/index.astro new file mode 100644 index 000000000..c5bc7bfd1 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/index.astro @@ -0,0 +1,16 @@ +--- +import { checkUserSession } from '../../../../../../utils/check-session'; +import { pathRoutes } from '../../../../../../config/pathRoutes'; +import LayoutSidebar from '../../../../../../app/LayoutSidebar.astro'; +import CredDefSelection from '../../../../../../components/Verification/CredDefSelection'; + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/verification/verify-credentials/schemas/index.astro b/src/pages/organizations/verification/verify-credentials/schemas/index.astro new file mode 100644 index 000000000..e6f7659ea --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/schemas/index.astro @@ -0,0 +1,17 @@ +--- +import LayoutSidebar from "../../../../../app/LayoutSidebar.astro"; +import SchemaSelection from "../../../../../components/Verification/SchemaSelection"; +import { pathRoutes } from "../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../utils/check-session"; + + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + From 62b2d7e38e35b8d057a1518f47e15cb22f725013 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:16:44 +0530 Subject: [PATCH 25/54] feat: Implement w3c issuance with connection,email and bulk. (#721) * feat: create schema flow for w3c Signed-off-by: pranalidhanavade * feat: w3c create schema flow Signed-off-by: pranalidhanavade * feat: w3c issuance with connections, email and bulk Signed-off-by: pranalidhanavade * fix: removed console statements Signed-off-by: pranalidhanavade * fix: removed console statements in schema summarycard Signed-off-by: pranalidhanavade * fix: fixed changes in issuance payload for w3c Signed-off-by: pranalidhanavade * feat: w3c issuance astro route changes Signed-off-by: pranalidhanavade * fix: added static values from common enum Signed-off-by: pranalidhanavade * fix: w3c button on card Signed-off-by: pranalidhanavade * fix: comments on pull request Signed-off-by: pranalidhanavade * fix: delete organization pop up description messages Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- src/api/BulkIssuance.ts | 7 +- src/api/issuance.ts | 9 +- src/common/enums.ts | 10 + src/commonComponents/SchemaCard.tsx | 73 ++- src/commonComponents/SummaryCard.tsx | 103 ++-- src/commonComponents/interface.ts | 17 + src/components/Issuance/BulkIssuance.tsx | 78 ++- src/components/Issuance/Connections.tsx | 23 +- src/components/Issuance/EmailIssuance.tsx | 240 +++++--- src/components/Issuance/Issuance.tsx | 571 ++++++++++++------ src/components/Issuance/IssueDashboard.tsx | 31 +- src/components/Issuance/IssuedCrdentials.tsx | 20 +- src/components/Issuance/interface.ts | 103 +++- src/components/Resources/Schema/Create.tsx | 3 +- .../Resources/Schema/SchemasList.tsx | 28 +- .../organization/DeleteOrganization.tsx | 4 +- src/config/CommonConstant.ts | 6 +- src/config/pathRoutes.ts | 4 +- .../credentials/issue/connections/index.astro | 16 + .../issue/connections/issuance.astro | 16 + 20 files changed, 958 insertions(+), 404 deletions(-) create mode 100644 src/pages/organizations/credentials/issue/connections/index.astro create mode 100644 src/pages/organizations/credentials/issue/connections/issuance.astro diff --git a/src/api/BulkIssuance.ts b/src/api/BulkIssuance.ts index 0e40f5ff6..d1ff33669 100644 --- a/src/api/BulkIssuance.ts +++ b/src/api/BulkIssuance.ts @@ -1,3 +1,4 @@ +import type { SchemaTypes } from '../common/enums'; import { apiRoutes } from '../config/apiRoutes'; import { storageKeys } from '../config/CommonConstant'; import { @@ -8,7 +9,7 @@ import { axiosGet, axiosPost } from '../services/apiRequests'; import { getFromLocalStorage } from './Auth'; import type { IConnectionListAPIParameter } from './connection'; -export const getSchemaCredDef = async (schemaType: string) => { +export const getSchemaCredDef = async (schemaType: SchemaTypes) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.bulk.credefList}?schemaType=${schemaType}`; const axiosPayload = { @@ -24,7 +25,7 @@ export const getSchemaCredDef = async (schemaType: string) => { } }; -export const DownloadCsvTemplate = async (templateId: string, schemaType: string) => { +export const DownloadCsvTemplate = async (templateId: string, schemaType: SchemaTypes) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.download}`; @@ -48,7 +49,7 @@ export const DownloadCsvTemplate = async (templateId: string, schemaType: string export const uploadCsvFile = async ( payload: { file: Uint8Array | Blob; fileName: string }, templateId: string, - schemaType: string + schemaType: SchemaTypes ) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.bulk.uploadCsv}?templateId=${templateId}&schemaType=${schemaType}`; diff --git a/src/api/issuance.ts b/src/api/issuance.ts index 636d4fe4a..7adddfea7 100644 --- a/src/api/issuance.ts +++ b/src/api/issuance.ts @@ -1,3 +1,4 @@ +import type { CredentialType, SchemaType } from '../common/enums'; import { apiRoutes } from '../config/apiRoutes'; import { storageKeys } from '../config/CommonConstant'; import { @@ -45,9 +46,9 @@ export const getCredentialDefinitions = async (schemaId: string) => { } }; -export const issueCredential = async (data: object) => { +export const issueCredential = async (data: object, credentialType:SchemaType) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.issueCredential}`; + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.issueCredential}?credentialType=${credentialType}`; const payload = data; const axiosPayload = { @@ -64,9 +65,9 @@ export const issueCredential = async (data: object) => { } }; -export const issueOobEmailCredential = async (data: object) => { +export const issueOobEmailCredential = async (data: object, credentialType:CredentialType) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.issueOobEmailCredential}`; + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.issueOobEmailCredential}?credentialType=${credentialType}`; const payload = data; const axiosPayload = { diff --git a/src/common/enums.ts b/src/common/enums.ts index b4acc4726..8f2caa9d1 100644 --- a/src/common/enums.ts +++ b/src/common/enums.ts @@ -131,3 +131,13 @@ export enum SchemaTypeValue { POLYGON = 'polygon', NO_LEDGER = 'no_ledger' } + +export enum SchemaTypes { + schema_INDY = 'indy', + schema_W3C = 'w3c' +} + +export enum CredentialType { + INDY = 'indy', + JSONLD = 'jsonld' +} diff --git a/src/commonComponents/SchemaCard.tsx b/src/commonComponents/SchemaCard.tsx index 395113de7..45c8c9848 100644 --- a/src/commonComponents/SchemaCard.tsx +++ b/src/commonComponents/SchemaCard.tsx @@ -2,25 +2,30 @@ import { Button, Card } from 'flowbite-react'; import { dateConversion } from '../utils/DateConversion'; import DateTooltip from '../components/Tooltip'; import CopyDid from './CopyDid'; -import React from 'react'; - -interface IProps { - className?: string, - schemaName: string, - version: string, - schemaId: string, - issuerDid: string, - attributes: [], - created: string, - isClickable?: boolean - onClickCallback: (schemaId: string, attributes: string[], issuerDid: string, created: string) => void; - limitedAttributes?: boolean - w3cSchema:boolean - noLedger:boolean -} -const SchemaCard = (props: IProps) => { +import React, { useEffect } from 'react'; +import { pathRoutes } from '../config/pathRoutes'; +import { getFromLocalStorage } from '../api/Auth'; +import { storageKeys } from '../config/CommonConstant'; +import type { ISchemaCardProps } from './interface'; +const SchemaCard = (props: ISchemaCardProps) => { + const orgDidDetails = async ()=>{ + const orgDid = await getFromLocalStorage(storageKeys.ORG_DID) + } + useEffect(() => { + orgDidDetails(); + }, []); + const attributes = props.limitedAttributes !== false ? props?.attributes?.slice(0, 3) : props?.attributes + + + const handleIssueClick = () => { + if (props.onClickW3cIssue) { + props.onClickW3cIssue(props.schemaId, props.schemaName, props.version, props.issuerDid, props.attributes, props.created); + } + }; + + return ( { if (!props.w3cSchema) { @@ -36,14 +41,6 @@ const SchemaCard = (props: IProps) => { {props.schemaName} - {props.w3cSchema && ( - - W3C - - )}

@@ -51,11 +48,23 @@ const SchemaCard = (props: IProps) => { Version: {props.version}

-
+
+
+ {props.w3cSchema && ( + + W3C + + )} +
+
Created: {dateConversion(props.created)} -
+ +
@@ -99,9 +108,14 @@ const SchemaCard = (props: IProps) => { )}
- {props.w3cSchema && ( +
+ {props.w3cSchema && (
)} +
) diff --git a/src/commonComponents/SummaryCard.tsx b/src/commonComponents/SummaryCard.tsx index cbb0165e5..5ed22f6a3 100644 --- a/src/commonComponents/SummaryCard.tsx +++ b/src/commonComponents/SummaryCard.tsx @@ -1,57 +1,66 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Card } from 'flowbite-react'; import CopyDid from '../commonComponents/CopyDid'; +import { storageKeys } from '../config/CommonConstant'; +import { getFromLocalStorage } from '../api/Auth'; +import { DidMethod } from '../common/enums'; interface IProps { - schemaName: string; - version: string; - credDefId: string; - schemaId: string; - hideCredDefId: boolean; + schemaName: string; + version: string; + credDefId?: string; + schemaId: string; + hideCredDefId?: boolean; } const SummaryCard = ({ schemaName, version, credDefId, schemaId, hideCredDefId }: Readonly) => { - return ( -
- -
-
-
- {schemaName} -
-

- Version: {version} -

-
-
-
-

- Schema ID: - - - -

- { - hideCredDefId ? -

- Credential definition restriction: - - {credDefId ? ' Yes' : ' No'} - -

- : -

- Credential Definition: - - - -

+ const [isW3cDid, setIsW3cDid] = useState(false); - } -
-
+ const fetchOrgData = async () => { + const orgDid = await getFromLocalStorage(storageKeys.ORG_DID); + + if (orgDid.includes(DidMethod.POLYGON) || orgDid.includes(DidMethod.KEY) || orgDid.includes(DidMethod.WEB)) { + setIsW3cDid(true); + } else { + setIsW3cDid(false); + } + }; + useEffect(() => { + fetchOrgData(); + }, []); + + return ( +
+ +
+
+
+ {schemaName} +
+

+ Version: {version} +

+
- ) -} +
+

+ Schema ID: + + + +

+ {!isW3cDid && ( +

+ Credential Definition: + + + +

+ )} +
+
+
+ ); +}; -export default SummaryCard \ No newline at end of file +export default SummaryCard; diff --git a/src/commonComponents/interface.ts b/src/commonComponents/interface.ts index 2f9af90bf..ed96297e0 100644 --- a/src/commonComponents/interface.ts +++ b/src/commonComponents/interface.ts @@ -9,3 +9,20 @@ export interface IProps { setFailure: (flag: string | null) => void; setSuccess: (flag: string | null) => void; } + + +export interface ISchemaCardProps { + className?: string, + schemaName: string, + version: string, + schemaId: string, + issuerDid: string, + attributes: [], + created: string, + isClickable?: boolean + onClickCallback: (schemaId: string, attributes: string[], issuerDid: string, created: string) => void; + onClickW3cIssue?: (schemaId: string, schemaName: string, version: string, issuerDid: string, attributes: [], created: string) => void; + limitedAttributes?: boolean + w3cSchema:boolean + noLedger:boolean + } \ No newline at end of file diff --git a/src/components/Issuance/BulkIssuance.tsx b/src/components/Issuance/BulkIssuance.tsx index 9686e626c..1410f23d6 100644 --- a/src/components/Issuance/BulkIssuance.tsx +++ b/src/components/Issuance/BulkIssuance.tsx @@ -18,11 +18,11 @@ import BreadCrumbs from '../BreadCrumbs'; import BackButton from '../../commonComponents/backbutton' import { checkEcosystem } from '../../config/ecosystem'; import type { ICheckEcosystem} from '../../config/ecosystem'; -import type { ICredentials, IValues, IAttributes, IUploadMessage } from './interface'; +import type { ICredentials, IAttributes, IUploadMessage } from './interface'; import RoleViewButton from '../RoleViewButton'; import { Features } from '../../utils/enums/features'; import { Create, SchemaEndorsement } from './Constant'; -import { SchemaType } from '../../common/enums'; +import { DidMethod, SchemaTypes } from '../../common/enums'; export interface SelectRef { clearValue(): void; @@ -33,7 +33,7 @@ const BulkIssuance = () => { const [process, setProcess] = useState(false); const [loading, setLoading] = useState(true); const [credentialOptions, setCredentialOptions] = useState([]); - const [credentialSelected, setCredentialSelected] = useState(""); + const [credentialSelected, setCredentialSelected] = useState(); const [isFileUploaded, setIsFileUploaded] = useState(false); const [uploadedFileName, setUploadedFileName] = useState(''); const [uploadedFile, setUploadedFile] = useState(null); @@ -44,6 +44,9 @@ const BulkIssuance = () => { const [failure, setFailure] = useState(null); const [isEcosystemData, setIsEcosystemData] = useState(); const [mounted, setMounted] = useState(false) + const [schemaType, setSchemaType]= useState(); + const [selectedTemplate, setSelectedTemplate] = useState(); + const onPageChange = (page: number) => { setCurrentPage({ @@ -62,21 +65,42 @@ const BulkIssuance = () => { try { setLoading(true); const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - if (orgId) { - const response = await getSchemaCredDef(SchemaType.INDY); + + const orgDid = await getFromLocalStorage(storageKeys.ORG_DID); + + let currentSchemaType = schemaType; + + if (orgDid?.includes(DidMethod.POLYGON) || orgDid?.includes(DidMethod.KEY) || orgDid?.includes(DidMethod.WEB)) { + currentSchemaType = SchemaTypes.schema_W3C; + } else if (orgDid?.includes(DidMethod.INDY)) { + currentSchemaType = SchemaTypes.schema_INDY; + } + + setSchemaType(currentSchemaType); + if (currentSchemaType && orgId) { + const response = await getSchemaCredDef(currentSchemaType); const { data } = response as AxiosResponse; if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const credentialDefs = data.data; + const { data: credentialDefs } = data; const options = credentialDefs.map( - (credDef: ICredentials) => ({ - value: credDef.credentialDefinitionId, - label: `${credDef.schemaName} [${credDef.schemaVersion}] - (${credDef.credentialDefinition})`, - schemaName: credDef.schemaName, - schemaVersion: credDef.schemaVersion, - credentialDefinition: credDef.credentialDefinition, - schemaAttributes: credDef.schemaAttributes && typeof credDef.schemaAttributes === "string" && JSON.parse(credDef.schemaAttributes) + ({ + schemaName, + schemaVersion, + credentialDefinition, + credentialDefinitionId, + schemaIdentifier, + schemaAttributes + }: ICredentials) => ({ + value: schemaType===SchemaTypes.schema_INDY ? credentialDefinitionId : schemaVersion, + label: `${schemaName} [${schemaVersion}]${currentSchemaType === SchemaTypes.schema_INDY ? ` - (${credentialDefinition})` : ''}`, + schemaName: schemaName, + schemaVersion: schemaVersion, + credentialDefinition: credentialDefinition, + credentialDefinitionId: credentialDefinitionId, + schemaIdentifier: schemaIdentifier, + schemaAttributes: schemaAttributes && typeof schemaAttributes === "string" && JSON.parse(schemaAttributes) }), ); setCredentialOptions(options); @@ -122,7 +146,7 @@ const BulkIssuance = () => { if (credentialSelected) { try { setProcess(true); - const response = await DownloadCsvTemplate(credentialSelected, SchemaType.INDY); + const response = await DownloadCsvTemplate(selectedTemplate, schemaType); const { data } = response as AxiosResponse; if (data) { @@ -250,7 +274,7 @@ const BulkIssuance = () => { setUploadedFileName(file?.name); setUploadedFile(file); - const response = await uploadCsvFile(payload, credentialSelected, SchemaType.INDY); + const response = await uploadCsvFile(payload, selectedTemplate, schemaType); const { data } = response as AxiosResponse; if (data?.statusCode === apiStatusCodes?.API_STATUS_CREATED) { @@ -369,13 +393,13 @@ const BulkIssuance = () => { const handleReset = () => { handleDiscardFile(); - setCredentialSelected(""); + setCredentialSelected(null); setSuccess(null); onClear() }; const handleResetForConfirm = () => { handleDiscardFile(); - setCredentialSelected(""); + setCredentialSelected(null); }; const confirmCredentialIssuance = async () => { @@ -506,8 +530,14 @@ const BulkIssuance = () => { isSearchable={true} name="color" options={credentialOptions} - onChange={(value: IValues | null) => { - setCredentialSelected(value?.value ?? ""); + onChange={(value: ICredentials | null) => { + if (schemaType === SchemaTypes.schema_INDY) { + setSelectedTemplate(value?.credentialDefinitionId); + setCredentialSelected(value ?? null); + } else if (schemaType === SchemaTypes.schema_W3C) { + setCredentialSelected(value ?? null); + setSelectedTemplate(value?.schemaIdentifier) + } }} ref={selectInputRef} />: @@ -515,24 +545,24 @@ const BulkIssuance = () => { }
- {credentialSelected && selectedCred && ( + {credentialSelected &&(

Schema: - {selectedCred?.schemaName || ""}{' '} - [{selectedCred?.schemaVersion}] + {credentialSelected?.schemaName || ""}{' '} + [{credentialSelected?.schemaVersion}]

{' '} Credential Definition: {' '} - {selectedCred?.credentialDefinition} + {credentialSelected?.credentialDefinition}

Attributes:
- {selectedCred?.schemaAttributes.map( + {credentialSelected?.schemaAttributes.map( (element: IAttributes) => (
diff --git a/src/components/Issuance/Connections.tsx b/src/components/Issuance/Connections.tsx index 568fa7b01..66604f93c 100644 --- a/src/components/Issuance/Connections.tsx +++ b/src/components/Issuance/Connections.tsx @@ -1,7 +1,7 @@ import { Button } from 'flowbite-react'; -import { useState } from 'react'; -import { setToLocalStorage } from '../../api/Auth'; +import { useEffect, useState } from 'react'; +import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; import DataTable from '../../commonComponents/datatable'; import type { TableData } from '../../commonComponents/datatable/interface'; import { storageKeys } from '../../config/CommonConstant'; @@ -12,6 +12,8 @@ import BackButton from '../../commonComponents/backbutton'; import type { IConnectionList } from './interface'; import DateTooltip from '../Tooltip'; import { dateConversion } from '../../utils/DateConversion'; +import React from 'react'; +import { DidMethod } from '../../common/enums'; const Connections = () => { const [selectedConnections, setSelectedConnections] = useState< @@ -24,6 +26,17 @@ const Connections = () => { { columnName: 'Created on' } ]; +const [isW3cDid, setisW3cDid] = useState(false); +const orgData =async () =>{ + const orgDid = await getFromLocalStorage(storageKeys.ORG_DID); + if(orgDid.includes(DidMethod.POLYGON) || orgDid.includes(DidMethod.KEY) || orgDid.includes(DidMethod.WEB)){ + setisW3cDid(true) + } +} +useEffect(() => { + orgData(); +}, []); + const selectConnection = (connections: IConnectionList[]) => { try { const connectionsData = connections?.length > 0 && connections?.map((ele: IConnectionList) => { @@ -62,6 +75,12 @@ const Connections = () => { }); await setToLocalStorage(storageKeys.SELECTED_USER, selectedConnectionData); window.location.href = `${pathRoutes.organizations.Issuance.issuance}`; + + if(isW3cDid){ + window.location.href = `${pathRoutes.organizations.Issuance.w3cIssuance}`; + }else { + window.location.href = `${pathRoutes.organizations.Issuance.issuance}`; + } }; return ( diff --git a/src/components/Issuance/EmailIssuance.tsx b/src/components/Issuance/EmailIssuance.tsx index 6352e22ab..77dda22d7 100644 --- a/src/components/Issuance/EmailIssuance.tsx +++ b/src/components/Issuance/EmailIssuance.tsx @@ -12,7 +12,7 @@ import type { AxiosResponse } from 'axios'; import { getFromLocalStorage } from '../../api/Auth'; import { getSchemaCredDef } from '../../api/BulkIssuance'; import { storageKeys, apiStatusCodes } from '../../config/CommonConstant'; -import type { IAttributes, ICredentials, IValues } from './interface'; +import type { IAttributes, ICredentials, IEmailCredentialData, IIssueAttributes, ITransformedData } from './interface'; import { Field, FieldArray, Form, Formik } from 'formik'; import CustomSpinner from '../CustomSpinner'; import { issueOobEmailCredential } from '../../api/issuance'; @@ -24,20 +24,20 @@ import { checkEcosystem } from '../../config/ecosystem'; import type { ICheckEcosystem} from '../../config/ecosystem'; import { Features } from '../../utils/enums/features'; import { Create, SchemaEndorsement } from './Constant'; -import { SchemaType } from '../../common/enums'; +import { DidMethod, SchemaTypes, CredentialType, SchemaTypeValue } from '../../common/enums'; const EmailIssuance = () => { const [formData, setFormData] = useState(); const [userData, setUserData] = useState(); const [loading, setLoading] = useState(true); const [credentialOptions, setCredentialOptions] = useState([]); - const [credentialSelected, setCredentialSelected] = useState( - '', + const [credentialSelected, setCredentialSelected] = useState( + ); const [openModal, setOpenModal] = useState(false); const [batchName, setBatchName] = useState(''); const [openResetModal, setOpenResetModal] = useState(false); - const [attributes, setAttributes] = useState([]); + const [attributes, setAttributes] = useState([]); const [success, setSuccess] = useState(null); const [failure, setFailure] = useState(null); const [isEditing, setIsEditing] = useState(false); @@ -45,36 +45,72 @@ const EmailIssuance = () => { const [isEcosystemData, setIsEcosystemData] = useState(); const inputRef = useRef(null); const [mounted, setMounted] = useState(false) + const [schemaType, setSchemaType]= useState(); + const [credentialType, setCredentialType] = useState(); + const [credDefId, setCredDefId] = useState(); + const [schemasIdentifier, setSchemasIdentifier] = useState(); + const [schemaTypeValue, setSchemaTypeValue] = useState(); const getSchemaCredentials = async () => { try { setLoading(true); const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - if (orgId) { - const response = await getSchemaCredDef(SchemaType.INDY); + const orgDid = await getFromLocalStorage(storageKeys.ORG_DID); + let currentSchemaType = schemaType; + + if (orgDid?.includes(DidMethod.POLYGON)) { + currentSchemaType = SchemaTypes.schema_W3C; + setSchemaTypeValue(SchemaTypeValue.POLYGON) + setCredentialType(CredentialType.JSONLD) + + } else if ( orgDid?.includes(DidMethod.KEY) || orgDid?.includes(DidMethod.WEB)) { + currentSchemaType = SchemaTypes.schema_W3C; + setSchemaTypeValue(SchemaTypeValue.NO_LEDGER) + + setCredentialType(CredentialType.JSONLD) + } + else if (orgDid?.includes(DidMethod.INDY)) { + setCredentialType(CredentialType.INDY) + currentSchemaType = SchemaTypes.schema_INDY; + } + + setSchemaType(currentSchemaType); + if (currentSchemaType && orgId) { + const response = await getSchemaCredDef(currentSchemaType); const { data } = response as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const credentialDefs = data.data; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const credentialDefs = data.data; - const options = credentialDefs.map((credDef: ICredentials) => ({ - value: credDef.credentialDefinitionId, - label: `${credDef.schemaName} [${credDef.schemaVersion}] - (${credDef.credentialDefinition})`, - schemaName: credDef.schemaName, - schemaVersion: credDef.schemaVersion, - credentialDefinition: credDef.credentialDefinition, - schemaAttributes: - credDef.schemaAttributes && - typeof credDef.schemaAttributes === 'string' && - JSON.parse(credDef.schemaAttributes), - })); - setCredentialOptions(options); - } else { - setSuccess(null); - setFailure(null); - } - setLoading(false); - } + const options = credentialDefs.map(({ + schemaName, + schemaVersion, + credentialDefinition, + credentialDefinitionId, + schemaIdentifier, + schemaAttributes + } : ICredentials) => ({ + value: schemaType===SchemaTypes.schema_INDY ? credentialDefinitionId : schemaVersion, + label: `${schemaName} [${schemaVersion}]${currentSchemaType === SchemaTypes.schema_INDY ? ` - (${credentialDefinition})` : ''}`, + schemaName: schemaName, + schemaVersion: schemaVersion, + credentialDefinition: credentialDefinition, + schemaIdentifier: schemaIdentifier, + credentialDefinitionId: credentialDefinitionId, + schemaAttributes: + schemaAttributes && + typeof schemaAttributes === 'string' && + JSON.parse(schemaAttributes), + })); + setCredentialOptions(options); + + } else { + setSuccess(null); + setFailure(null); + } + setLoading(false); + } + } catch (error) { setSuccess(null); setFailure(null); @@ -102,28 +138,77 @@ const EmailIssuance = () => { const confirmOOBCredentialIssuance = async () => { setIssueLoader(true); + const existingData = userData; + const organizationDid = await getFromLocalStorage(storageKeys.ORG_DID); + + let transformedData: ITransformedData = { credentialOffer: [] }; + + if (existingData && existingData.formData) { + if (schemaType === SchemaTypes.schema_INDY) { + existingData.formData.forEach((entry: { email: string; attributes: IIssueAttributes[] }) => { + + const transformedEntry = { emailId: entry.email, attributes: [] }; + entry.attributes.forEach((attribute) => { + const transformedAttribute = { + value: String(attribute.value || ''), + name: attribute.name || '', + isRequired: attribute.isRequired, + }; + transformedEntry.attributes.push(transformedAttribute); + }); + transformedData.credentialOffer.push(transformedEntry); + }); + transformedData.credentialDefinitionId = credDefId; + + } else if (schemaType=== SchemaTypes.schema_W3C) { + existingData.formData.forEach((entry: { email: string; credentialData: IEmailCredentialData; attributes:IIssueAttributes[] }) => { + const credentialOffer = { + emailId: entry.email, + credential: { + "@context": [ + storageKeys.CREDENTIAL_CONTEXT_VALUE, + schemasIdentifier + ], + "type": [ + "VerifiableCredential", + credentialOptions[0].schemaName + ], + "issuer": { + "id": organizationDid + }, + "issuanceDate": new Date().toISOString(), + + credentialSubject: entry?.attributes?.reduce((acc, attr) => { + if (attr.value === null && !attr.isRequired && typeof attr.value === 'number') { + return acc; + } else { + if (attr.name === 'rollno' && attr.value === '') { + return acc; + } else { + acc[attr.name] = attr.value; + return acc; + } + } + }, { + }), + }, + options: { + proofType: schemaTypeValue===SchemaTypeValue.POLYGON ? "EcdsaSecp256k1Signature2019" : "Ed25519Signature2018", + proofPurpose: "assertionMethod" + } + }; - let transformedData = { credentialOffer: [] }; - if (existingData && existingData.formData) { - existingData?.formData?.forEach( - (entry: { email: any; attributes: any[] }) => { - const transformedEntry = { emailId: entry.email, attributes: [] }; - entry.attributes.forEach((attribute) => { - const transformedAttribute = { - value: String(attribute.value || ''), - name: attribute.name || '', - isRequired: attribute.isRequired, - }; - transformedEntry?.attributes?.push(transformedAttribute); - }); + transformedData.credentialOffer.push(credentialOffer); + }); - transformedData.credentialOffer.push(transformedEntry); - }, - ); - transformedData.credentialDefinitionId = credentialSelected; - const transformedJson = JSON.stringify(transformedData, null, 2); - const response = await issueOobEmailCredential(transformedJson); + transformedData.protocolVersion = "v2"; + transformedData.credentialType = CredentialType.JSONLD; + } + + + const transformedJson = JSON.stringify(transformedData, null, 2); + const response = await issueOobEmailCredential(transformedJson, credentialType); const { data } = response as AxiosResponse; if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { @@ -172,31 +257,9 @@ const EmailIssuance = () => { setFormData({ formData: [initFormData] }); }, [attributes]); - const isCredSelected = Boolean(credentialSelected); - - const selectedCred: ICredentials | boolean | undefined = - credentialOptions && - credentialOptions.length > 0 && - credentialOptions.find( - (item: { value: string }) => - item.value && item.value === credentialSelected, - ); - - const handleEditClick = () => { - if (isCredSelected) { - setIsEditing(!isEditing); - } - }; - const handleInputChange = (e: { - target: { value: React.SetStateAction }; - }) => { - setBatchName(e.target.value); - }; - - const handleBlur = () => { - setIsEditing(false); - }; + const isCredSelected = Boolean(credentialSelected); + const selectInputRef = React.useRef(null); const handleReset = () => { @@ -284,11 +347,20 @@ const EmailIssuance = () => { name="color" options={credentialOptions} - onChange={(value: IValues | null) => { - setBatchName(value?.label ?? ''); - setCredentialSelected(value?.value ?? ''); - setAttributes(value?.schemaAttributes); - }} + onChange={(value: ICredentials | null) => { + setBatchName(value?.label ?? ''); + + if (schemaType === SchemaTypes.schema_INDY) { + + setCredDefId(value?.credentialDefinitionId); + setCredentialSelected(value ?? null); + } else if (schemaType === SchemaTypes.schema_W3C) { + + setCredentialSelected(value ?? null); + setSchemasIdentifier(value?.schemaIdentifier) + } + setAttributes(value?.schemaAttributes ?? []); + }} ref={selectInputRef} /> : @@ -297,25 +369,31 @@ const EmailIssuance = () => {
{credentialSelected && ( + +

Schema: - {selectedCred?.schemaName || ''}{' '} - [{selectedCred?.schemaVersion}] + {credentialSelected?.schemaName || ''}{' '} + [{credentialSelected?.schemaVersion}]

+ { + schemaType === SchemaTypes.schema_INDY && +

{' '} Credential Definition: {' '} - {selectedCred?.credentialDefinition} + {credentialSelected?.credentialDefinition}

+ } Attributes:
- {selectedCred?.schemaAttributes?.map( + {credentialSelected?.schemaAttributes?.map( (element: IAttributes) => (
{ }} > {(formikHandlers): JSX.Element => ( -
+ { const [schemaLoader, setSchemaLoader] = useState(true); @@ -31,36 +35,105 @@ const IssueCred = () => { schemaId: '', credDefId: '', }); + const [w3cSchemaDetails, setW3cSchemaDetails] = useState({ + schemaName: '', + version: '', + schemaId: '', + w3cAttributes:[], + issuerDid:'' + }); const [issuanceFormPayload, setIssuanceFormPayload] = useState< IssuanceFormPayload[] >([]); const [issuanceLoader, setIssuanceLoader] = useState(false); const [failure, setFailure] = useState(null); const [schemaAttributesDetails, setSchemaAttributesDetails] = useState< - Attribute[] + IAttribute[] >([]); const [success, setSuccess] = useState(null); const [error, setError] = useState(null); + const [w3cSchema, setW3cSchema]= useState(false); + const [credentialType, setCredentialType] = useState(); + const [schemaType, setSchemaType] = useState(); - useEffect(() => { - getSchemaAndUsers(); - getSchemaDetails(); - return () => setUserLoader(false); - }, []); - - const getSchemaAndUsers = async () => { - const credDefId = await getFromLocalStorage(storageKeys.CRED_DEF_ID); - const schemaId = await getFromLocalStorage(storageKeys.SCHEMA_ID); - const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - - createSchemaPayload(schemaId, credDefId); - setUserLoader(true); - const selectedUsers = await getSelectedUsers(); - const attributes = await getSchemaDetails(); - if (attributes && attributes?.length) { - createIssuanceForm(selectedUsers, attributes, credDefId, orgId); - } else { - setFailure('Attributes are not available'); + useEffect(() => { + fetchOrganizationDetails(); + return () => setUserLoader(false); + }, []); + + const fetchOrganizationDetails = async () => { + try{ + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + if (!orgId) return; + const response = await getOrganizationById(orgId); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const did = data?.data?.org_agents?.[0]?.orgDid; + + if (did?.includes(DidMethod.POLYGON)) { + setW3cSchema(true); + setCredentialType(CredentialType.JSONLD); + setSchemaType(SchemaTypeValue.POLYGON) + await getSchemaAndUsers(true) + } + else if (did?.includes(DidMethod.KEY) || did?.includes(DidMethod.WEB)) { + setW3cSchema(true); + setSchemaType(SchemaTypeValue.NO_LEDGER) + setCredentialType(CredentialType.JSONLD); + await getSchemaAndUsers(true) + } + else if (did?.includes(DidMethod.INDY)) { + setW3cSchema(false); + setCredentialType(CredentialType.INDY); + await getSchemaAndUsers(false) + } + } + } catch(error){ + console.log('Error in getSchemaAndUsers:', error); + setFailure('Error fetching schema and users'); + } + + }; + const getSchemaAndUsers = async (w3cSchema: boolean) => { + try { + if (!w3cSchema) { + const credDefId = await getFromLocalStorage(storageKeys.CRED_DEF_ID); + const schemaId = await getFromLocalStorage(storageKeys.SCHEMA_ID); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + + createSchemaPayload(schemaId, credDefId); + setUserLoader(true); + const selectedUsers = await getSelectedUsers(); + const attributes = await getSchemaDetails(w3cSchema); + if (attributes && attributes?.length) { + createIssuanceForm(selectedUsers, attributes, credDefId, orgId); + } else { + setFailure('Attributes are not available'); + } + + } + if (w3cSchema) { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const getW3cSchemaDetails = await getFromLocalStorage(storageKeys.W3C_SCHEMA_DETAILS); + const parsedW3cSchemaDetails = JSON.parse(getW3cSchemaDetails); + + const schemaId = parsedW3cSchemaDetails?.schemaId; + createW3cSchemaPayload(schemaId, parsedW3cSchemaDetails); + + setUserLoader(true); + const selectedUsers = await getSelectedUsers(); + + const attributes = parsedW3cSchemaDetails?.attributes; + + if (attributes && attributes?.length) { + createW3cIssuanceForm(selectedUsers, attributes, orgId); + } else { + setFailure('Attributes are not available'); + } + } + } catch (error) { + console.error('Error in getSchemaAndUsers:', error); + setFailure('Error fetching schema and users'); } }; @@ -69,7 +142,7 @@ const IssueCred = () => { attributes: DataTypeAttributes[], credDefId: string, orgId: string, - ) => { + ) => { const credentialData = selectedUsers.map((user) => { const attributesArray = attributes.map((attr) => ({ name: attr.attributeName, @@ -94,6 +167,35 @@ const IssueCred = () => { setUserLoader(false); }; + const createW3cIssuanceForm = ( + selectedUsers: SelectedUsers[], + attributes: DataTypeAttributes[], + orgId: string, + ) => { + const credentialData = selectedUsers.map((user) => { + + const attributesArray = attributes.length > 0 && attributes.map((attr) => ({ + name: attr.attributeName, + value: '', + dataType: attr?.schemaDataType, + isRequired: attr.isRequired, + })); + + return { + connectionId: user.connectionId, + attributes: attributesArray, + }; + }); + + const issuancePayload = { + credentialData, + orgId, + }; + + setIssuanceFormPayload(issuancePayload); + setUserLoader(false); + }; + const createAttributeValidationSchema = ( name: string, value: string, @@ -135,15 +237,28 @@ const IssueCred = () => { ), }); - const getSchemaDetails = async (): Promise => { - const schemaAttributes = await getFromLocalStorage(storageKeys.SCHEMA_ATTR); - - const parsedSchemaAttributes = JSON.parse(schemaAttributes) || []; - - setSchemaAttributesDetails(parsedSchemaAttributes?.attribute); - - return parsedSchemaAttributes.attribute; - }; + const getSchemaDetails = async (w3cSchema: boolean) => { + try { + if (!w3cSchema) { + const schemaAttributes = await getFromLocalStorage(storageKeys.SCHEMA_ATTR); + + let parsedSchemaAttributes = []; + if (schemaAttributes) { + try { + parsedSchemaAttributes = JSON.parse(schemaAttributes); + } catch (e) { + } + } + setSchemaAttributesDetails(parsedSchemaAttributes?.attribute); + return parsedSchemaAttributes?.attribute || null; + } + + } catch (error) { + console.error('Error in getSchemaAndUsers:', error); + setFailure('Error fetching schema and users'); + } + return null; + }; const createSchemaPayload = async (schemaId: string, credDefId: string) => { if (schemaId) { @@ -155,34 +270,102 @@ const IssueCred = () => { setSchemaLoader(false); } }; + const createW3cSchemaPayload = async (schemaId: string, parsedW3cSchemaDetails: any) => { + if (schemaId) { - const getSelectedUsers = async (): Promise => { - const selectedUsers = await getFromLocalStorage(storageKeys.SELECTED_USER); - return JSON.parse(selectedUsers); + if (parsedW3cSchemaDetails) { + setW3cSchemaDetails(parsedW3cSchemaDetails); + + setSchemaLoader(false); + + } + } }; - const handleSubmit = async (values: IssuanceFormPayload) => { - const issuancePayload = { - credentialData: values.credentialData.map((item) => { - return { - ...item, - attributes: item.attributes.map((attr) => ({ - name: attr.name, - value: attr.value.toString(), - })), - }; - }), - credentialDefinitionId: values.credentialDefinitionId, - orgId: values.orgId, - }; +const getSelectedUsers = async (): Promise => { + try { + const selectedUsers = await getFromLocalStorage(storageKeys.SELECTED_USER); + if (!selectedUsers) { + return []; + } + return JSON.parse(selectedUsers); + } catch (error) { + console.error("Error parsing selectedUsers:", error); + return []; + } + }; + + const handleSubmit = async (values: IssuanceFormPayload) => { + + let issuancePayload; + + if (!w3cSchema) { + issuancePayload = { + credentialData: values.credentialData.map((item) => { + return { + ...item, + attributes: item?.attributes?.map((attr) => ({ + name: attr.name, + value: attr.value.toString(), + })), + }; + }), + + credentialDefinitionId: values.credentialDefinitionId, + orgId: values.orgId, + }; + } + if(w3cSchema) { + issuancePayload = { + credentialData: values?.credentialData.map((item) => { + return { + connectionId: item.connectionId, + credential: { + "@context": [ + storageKeys.CREDENTIAL_CONTEXT_VALUE, + w3cSchemaDetails.schemaId + ], + "type": [ + "VerifiableCredential", + w3cSchemaDetails.schemaName + ], + issuer: { + "id": w3cSchemaDetails.issuerDid + }, + issuanceDate: new Date().toISOString(), + credentialSubject: item?.attributes?.reduce((acc, attr) => { + if (attr.value === null && !attr.isRequired && typeof attr.value === 'number') { + return acc; + } else { + if (attr.name === 'rollno' && attr.value === '') { + return acc; + } else { + acc[attr.name] = attr.value; + return acc; + } + } + }, { + }), + }, + + options: { + proofType: schemaType=== SchemaTypeValue.POLYGON ? "EcdsaSecp256k1Signature2019" : "Ed25519Signature2018", + proofPurpose: "assertionMethod" + } + }; + }), + orgId: values.orgId, + }; + } + const convertedAttributesValues = { ...issuancePayload, }; setIssuanceLoader(true); - const issueCredRes = await issueCredential(convertedAttributesValues); - + const issueCredRes = await issueCredential(convertedAttributesValues, credentialType); + const { data } = issueCredRes as AxiosResponse; if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { @@ -215,15 +398,27 @@ const IssueCred = () => { Issuance
- {!schemaLoader && ( - - )} + + {!schemaLoader && !w3cSchema ? ( + + + ) : ( + + )} + {userLoader ? (
@@ -252,140 +447,148 @@ const IssueCred = () => { {(arrayHelpers) => ( <> - {values?.credentialData.map((user, index) => ( -
- -
-
-
- Connection Id -
- - : - -

- {user?.connectionId} -

-
- {values.credentialData.length > 1 && ( -
- -
- )} -
+ {values?.credentialData?.length > 0 && values?.credentialData?.map((user, index) => { + const attributes = w3cSchema ? issuanceFormPayload?.credentialData?.[0].attributes : user?.attributes; -

Attributes

-
-
- {user.attributes.map((attr, attrIndex) => ( + return ( +
+ +
+
+
+ Connection Id +
+ + : + +

+ {user?.connectionId} +

+
+ {values.credentialData.length > 1 && (
-
- -
- { - try { - Yup.reach( - validationSchema, - `credentialData.${index}.attributes.${attrIndex}.value`, - ).validateSync(value, { - abortEarly: false, - }); - } catch (error) { - return error.message; - } - }} + - {errors?.credentialData && - errors?.credentialData?.[index] && - errors?.credentialData[index] - ?.attributes && - errors?.credentialData[index] - ?.attributes[attrIndex] && - errors?.credentialData[index] - ?.attributes[attrIndex]?.value && - touched?.credentialData && - touched?.credentialData[index] - ?.attributes && - touched?.credentialData[index] - ?.attributes[attrIndex] - ?.value && ( -
- { - errors?.credentialData?.[ - index - ]?.attributes?.[attrIndex] - ?.value + + +
+ )} +
+ +

Attributes

+
+
+ + {attributes?.map((attr, attrIndex) => { + + return ( +
+
+ +
+ { + try { + Yup.reach( + validationSchema, + `credentialData.${index}.attributes.${attrIndex}.value`, + ).validateSync(value, { + abortEarly: false, + }); + } catch (error) { + return error.message; } -
- )}{' '} + }} + /> + {errors?.credentialData && + errors?.credentialData?.[index] && + errors?.credentialData[index] + ?.attributes && + errors?.credentialData[index] + ?.attributes[attrIndex] && + errors?.credentialData[index] + ?.attributes[attrIndex]?.value && + touched?.credentialData && + touched?.credentialData[index] + ?.attributes && + touched?.credentialData[index] + ?.attributes[attrIndex] + ?.value && ( +
+ { + errors?.credentialData?.[ + index + ]?.attributes?.[attrIndex] + ?.value + } +
+ )}{' '} +
-
- ))} + ) + })} +
-
-
-
- ))} + +
+ ) + })} )} diff --git a/src/components/Issuance/IssueDashboard.tsx b/src/components/Issuance/IssueDashboard.tsx index 73cb90224..beeed17d9 100644 --- a/src/components/Issuance/IssueDashboard.tsx +++ b/src/components/Issuance/IssueDashboard.tsx @@ -1,13 +1,34 @@ import Dashboard from '../../commonComponents/Dashboard'; import { pathRoutes } from '../../config/pathRoutes'; +import BackButton from '../../commonComponents/backbutton'; +import { getFromLocalStorage } from '../../api/Auth'; +import { storageKeys } from '../../config/CommonConstant'; +import { useEffect, useState } from 'react'; +import { DidMethod } from '../../common/enums'; + +const IssueDashboard = () => { + + const [isW3cDid, setisW3cDid] = useState(false); + +const orgData =async () =>{ + const orgDid = await getFromLocalStorage(storageKeys.ORG_DID); + if(orgDid.includes(DidMethod.POLYGON) || orgDid.includes(DidMethod.WEB) || orgDid.includes(DidMethod.KEY)){ + setisW3cDid(true) + } else { + setisW3cDid(false) + } +} + useEffect(() => { + orgData(); + }, []); -const IssuanceDashboard = () => { const options = [ - { + { heading: 'Connection', description: - 'Issue credential(s) by selecting connection from existing users', - path: pathRoutes.organizations.Issuance.schema, + 'Issue credential(s) by selecting connection from existing users', + path: isW3cDid ? pathRoutes.organizations.Issuance.connection : pathRoutes.organizations.Issuance.schema + }, { heading: 'Email', @@ -30,4 +51,4 @@ const IssuanceDashboard = () => { ); }; -export default IssuanceDashboard; +export default IssueDashboard; diff --git a/src/components/Issuance/IssuedCrdentials.tsx b/src/components/Issuance/IssuedCrdentials.tsx index 644af510a..c4b434e6d 100644 --- a/src/components/Issuance/IssuedCrdentials.tsx +++ b/src/components/Issuance/IssuedCrdentials.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import type { ChangeEvent } from 'react'; -import { IssueCredential, IssueCredentialUserText } from '../../common/enums'; +import { DidMethod, IssueCredential, IssueCredentialUserText } from '../../common/enums'; import { AlertComponent } from '../AlertComponent'; import type { AxiosResponse } from 'axios'; @@ -36,6 +36,7 @@ const CredentialList = () => { const [error, setError] = useState(null); const [issuedCredList, setIssuedCredList] = useState([]); const [walletCreated, setWalletCreated] = useState(false); + const [isW3c, setIsW3c] = useState(false); const [listAPIParameter, setListAPIParameter] = useState(initialPageState); const [totalItem, setTotalItem] = useState(0); @@ -195,9 +196,20 @@ const CredentialList = () => { }); }; - const schemeSelection = () => { - window.location.href = pathRoutes.organizations.Issuance.issue; - }; + const schemeSelection = async () => { + const orgDid = await getFromLocalStorage(storageKeys.ORG_DID); + if ( + orgDid.includes(DidMethod.POLYGON) || + orgDid.includes(DidMethod.KEY) || + orgDid.includes(DidMethod.WEB) + ) { + setIsW3c(true); + window.location.href = pathRoutes.organizations.Issuance.schema; + } else if (orgDid.includes(DidMethod.INDY)) { + setIsW3c(false); + window.location.href = pathRoutes.organizations.Issuance.issue; + } + }; const refreshPage = () => { getIssuedCredDefs(listAPIParameter); diff --git a/src/components/Issuance/interface.ts b/src/components/Issuance/interface.ts index 923810aff..1454c53aa 100644 --- a/src/components/Issuance/interface.ts +++ b/src/components/Issuance/interface.ts @@ -27,12 +27,15 @@ export interface IAttributes { } export interface ICredentials { - credentialDefinitionId: string; + value?:String; + label?: string; + credentialDefinitionId?: string; schemaCredDefName: string; schemaName: string; schemaVersion: string; - schemaAttributes: IAttributes | boolean; - credentialDefinition: string; + schemaIdentifier: string; + schemaAttributes: IAttributes[]; + credentialDefinition?: string; } export interface IUploadMessage { @@ -70,12 +73,36 @@ export interface SchemaDetails { credDefId: string; } +export interface W3cSchemaDetails { + schemaName: string; + version: string; + schemaId: string; + w3cAttributes?: IAttributesData[]; + issuerDid?:string; +} + +export interface IGetSchemaData { + schemaId: string; + schemaName: string; + version: string; + issuerDid: string; + attributes: IAttribute[]; + created: string; + } + +export interface IAttribute { + attributeName: string; + schemaDataType: string; + displayName: string; + isRequired: boolean; +} + export interface SelectedUsers { userName: string; connectionId: string; } -export interface Attributes { +export interface IAttributesData { isRequired: boolean; name: string; value: string; @@ -84,12 +111,45 @@ export interface Attributes { export interface ICredentialdata { connectionId: string; - attributes: Attributes[]; + options?:IOptions; + attributes?: IAttributesData[]; + credential?:IW3cPayload; +} +export interface IOptions { + proofType:string; + proofPurpose:string; +} + +export interface IEmailCredentialData{ + attributes?: IAttributesData[]; + credential?:IW3cPayload; } -export interface IssuanceFormPayload { + + export interface IW3cPayload { + "@context": string[]; + type: string[]; + issuer: IIssuerData; + issuanceDate: string; + credentialSubject:ICredentialSubjectData; + } + + export interface ICredentialSubjectData { + id: string; + [key: string]: any; + }; + export interface IIssuerData { + id: string; + } + export interface IssuanceFormPayload { + userName?: string; + credentialData: ICredentialdata[]; + credentialDefinitionId?: string; + orgId: string; + } + +export interface W3cIssuanceFormPayload { userName?: string; credentialData: ICredentialdata[]; - credentialDefinitionId: string; orgId: string; } @@ -99,9 +159,28 @@ export interface DataTypeAttributes { attributeName:string } -export interface Attribute { - isRequired: string; - attributeName: string; - schemaDataType: string; - displayName: string; +export interface IIssueAttributes { + isRequired: boolean; + name: string; + value: string; + dataType: string; } + +export interface ICredentialOffer { + emailId: string; + attributes?: IAttributesData[]; + credential?: IW3cPayload; + options?:IOptionData; + } + + export interface IOptionData { + proofType: string; + proofPurpose: string; + }; + + export interface ITransformedData { + credentialOffer: ICredentialOffer[]; + credentialDefinitionId?: string; + protocolVersion?: string; + credentialType?: string; + } \ No newline at end of file diff --git a/src/components/Resources/Schema/Create.tsx b/src/components/Resources/Schema/Create.tsx index 6cb8add65..e041f2f42 100644 --- a/src/components/Resources/Schema/Create.tsx +++ b/src/components/Resources/Schema/Create.tsx @@ -24,6 +24,7 @@ import EcosystemProfileCard from '../../../commonComponents/EcosystemProfileCard import ConfirmationModal from '../../../commonComponents/ConfirmationModal'; import { DidMethod, SchemaType, SchemaTypeValue } from '../../../common/enums'; import { getOrganizationById } from '../../../api/organization'; +import React from 'react'; const options = [ { @@ -384,8 +385,6 @@ if ( } else if (schemaTypeValues === SchemaTypeValue.INDY) { filteredOptions = options; } -console.log("Schema Type Values: ", schemaTypeValues); -console.log("Filtered Options: ", filteredOptions); return (
diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index eb4567459..9c0e2ceb0 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -75,7 +75,7 @@ const SchemaList = (props: { setSchemaList([]); } - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { if (data?.data?.data) { setTotalItem(data?.data?.lastPage); setSchemaList(data?.data?.data); @@ -145,6 +145,27 @@ const SchemaList = (props: { props.schemaSelectionCallback(schemaId, schemaDetails); }; + const handleW3CIssue = async ( + schemaId: string, + schemaName: string, + version: string, + issuerDid: string, + attributes: [], + created: string + ) => { + const schemaDetails = { + schemaId, + schemaName, + version, + issuerDid, + attributes, + created, + }; + await setToLocalStorage(storageKeys.W3C_SCHEMA_DETAILS, schemaDetails); + + }; + + const options = ['All schemas']; const handleFilter = (e: React.ChangeEvent) => { @@ -164,11 +185,11 @@ const SchemaList = (props: { const { data } = response as AxiosResponse; if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { const did = data?.data?.org_agents?.[0]?.orgDid; - + + await setToLocalStorage(storageKeys.ORG_DID, did) if (data?.data?.org_agents && data?.data?.org_agents?.length > 0) { setWalletStatus(true); } - if (did.includes(DidMethod.POLYGON) || did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { setW3cSchema(true); } @@ -281,6 +302,7 @@ const SchemaList = (props: { attributes={element['attributes']} created={element['createDateTime']} onClickCallback={schemaSelectionCallback} + onClickW3cIssue={handleW3CIssue} w3cSchema={w3cSchema} noLedger={isNoLedger} diff --git a/src/components/organization/DeleteOrganization.tsx b/src/components/organization/DeleteOrganization.tsx index 040fb706a..653ed4ba0 100644 --- a/src/components/organization/DeleteOrganization.tsx +++ b/src/components/organization/DeleteOrganization.tsx @@ -298,7 +298,7 @@ const DeleteOrganizations = () => { }, { title: "Organization wallet", - description: "", + description: "Organization Wallet is the data of your created DIDs.", count: isWalletPresent ? 1 : 0, deleteFunc: deleteFunctions.deleteOrgWallet, confirmMessage: "Are you sure you want to delete organization wallet", @@ -311,7 +311,7 @@ const DeleteOrganizations = () => { }, { title: "Organization", - description: "Organization", + description: "Organization is the collection of your users, schemas, cred-defs, connections and wallet.", deleteFunc: deleteFunctions.deleteOrganizations, confirmMessage:{`Are you sure you want to delete organization `}"{orgName}", isDisabled: isWalletPresent || ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) diff --git a/src/config/CommonConstant.ts b/src/config/CommonConstant.ts index b4b4e9320..661d2da2a 100644 --- a/src/config/CommonConstant.ts +++ b/src/config/CommonConstant.ts @@ -30,6 +30,7 @@ export const storageKeys = { SELECTED_CONNECTIONS: 'selected_connections', SCHEMA_ID:'schema_id', SCHEMA_ATTR:'schema_attr', + W3C_SCHEMA_DETAILS:'schemaDetails', CRED_DEF_ID:'cred_def_id', SCHEMA_DID: 'schema_did', LOGIN_USER_EMAIL: 'login_user_email', @@ -39,6 +40,9 @@ export const storageKeys = { SOCKET_ID: "socket_id", LEDGER_ID: "ledger_id", ORG_INFO:'organization_Info', + ORG_DID:'did', + SCHEMA_TYPE:'type', SELECT_ORG_IN_ECOSYSTEM: 'select_orgs_in_ecosystem', - ERROR_ORG_IN_ECOSYSTEM: 'error_orgs_in_ecosystem' + ERROR_ORG_IN_ECOSYSTEM: 'error_orgs_in_ecosystem', + CREDENTIAL_CONTEXT_VALUE: 'https://www.w3.org/2018/credentials/v1' } diff --git a/src/config/pathRoutes.ts b/src/config/pathRoutes.ts index d61332156..5fd85b4f0 100644 --- a/src/config/pathRoutes.ts +++ b/src/config/pathRoutes.ts @@ -30,17 +30,17 @@ export const pathRoutes = { createSchema: '/organizations/schemas/create', deleteOrganization:'/organizations/delete-organizations', - - viewSchema: '/organizations/schemas/view-schema', Issuance: { issue: '/organizations/credentials/issue', schema: '/organizations/credentials/issue/schemas', credDef: '/organizations/credentials/issue/schemas/cred-defs', + connection:'/organizations/credentials/issue/connections', connections: '/organizations/credentials/issue/schemas/cred-defs/connections', issuance: '/organizations/credentials/issue/schemas/cred-defs/connections/issuance', + w3cIssuance:'/organizations/credentials/issue/connections/issuance', history: '/organizations/credentials/issue/bulk-issuance/history', details: '/organizations/credentials/issue/bulk-issuance/history/details', bulkIssuance: '/organizations/credentials/issue/bulk-issuance', diff --git a/src/pages/organizations/credentials/issue/connections/index.astro b/src/pages/organizations/credentials/issue/connections/index.astro new file mode 100644 index 000000000..43ca1ccc0 --- /dev/null +++ b/src/pages/organizations/credentials/issue/connections/index.astro @@ -0,0 +1,16 @@ +--- +import LayoutSidebar from "../../../../../app/LayoutSidebar.astro"; +import Connections from "../../../../../components/Issuance/Connections"; +import { pathRoutes } from "../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../utils/check-session"; + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/credentials/issue/connections/issuance.astro b/src/pages/organizations/credentials/issue/connections/issuance.astro new file mode 100644 index 000000000..27eebb3fb --- /dev/null +++ b/src/pages/organizations/credentials/issue/connections/issuance.astro @@ -0,0 +1,16 @@ +--- +import LayoutSidebar from "../../../../../app/LayoutSidebar.astro"; +import IssueCred from "../../../../../components/Issuance/Issuance"; +import { pathRoutes } from "../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../utils/check-session"; + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + From 26036757f162d9eb2f7bf5b310136b5037de380b Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:26:58 +0530 Subject: [PATCH 26/54] feat: W3C verifcation with connections. (#729) * feat: w3c verification flow Signed-off-by: pranalidhanavade * fix: changes in create schema to remove version for w3c Signed-off-by: pranalidhanavade * fix: removed commnet Signed-off-by: pranalidhanavade * fix: removed comment Signed-off-by: pranalidhanavade * fix: changes in enum file Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: schema creddef popup on verification list Signed-off-by: pranalidhanavade * fix: schema creddef popup on verification list Signed-off-by: pranalidhanavade * fix: sonarlint issues Signed-off-by: pranalidhanavade * fix: sonarlint issues Signed-off-by: pranalidhanavade * fix: sonarlint issues by optimizing code Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- src/api/verification.ts | 6 +- src/common/enums.ts | 10 + src/commonComponents/SchemaCard.tsx | 45 ++- src/commonComponents/interface.ts | 1 + src/components/Issuance/EmailIssuance.tsx | 10 +- src/components/Issuance/Issuance.tsx | 23 +- src/components/Issuance/IssuedCrdentials.tsx | 45 ++- src/components/Resources/Schema/Create.tsx | 50 ++-- .../Resources/Schema/SchemasList.tsx | 14 +- .../Resources/Schema/interfaces/index.ts | 4 +- .../Verification/SchemaCredDefDetails.tsx | 74 ++--- .../Verification/SchemaSelection.tsx | 3 +- src/components/Verification/Verification.tsx | 282 ++++++++++++++---- .../VerificationCredentialList.tsx | 2 +- .../Verification/VerificationDashboard.tsx | 2 +- src/components/Verification/interface.ts | 32 +- src/config/CommonConstant.ts | 6 +- 17 files changed, 415 insertions(+), 194 deletions(-) diff --git a/src/api/verification.ts b/src/api/verification.ts index 7a70a8c40..317185bfc 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -1,4 +1,4 @@ -import type { IssueCredential } from '../common/enums'; +import type { IssueCredential, RequestType } from '../common/enums'; import { apiRoutes } from '../config/apiRoutes'; import { storageKeys } from '../config/CommonConstant'; import { getHeaderConfigs } from '../config/GetHeaderConfigs'; @@ -6,9 +6,9 @@ import { axiosDelete, axiosGet, axiosPost } from '../services/apiRequests'; import { getFromLocalStorage } from './Auth'; import type { IConnectionListAPIParameter } from './connection'; -export const verifyCredential = async (payload: any) => { +export const verifyCredential = async (payload: object, requestType:RequestType) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Verification.verifyCredential}`; + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Verification.verifyCredential}?requestType=${requestType}`; const axiosPayload = { url, payload, diff --git a/src/common/enums.ts b/src/common/enums.ts index 8f2caa9d1..c08a80c20 100644 --- a/src/common/enums.ts +++ b/src/common/enums.ts @@ -141,3 +141,13 @@ export enum CredentialType { INDY = 'indy', JSONLD = 'jsonld' } + +export enum RequestType { + INDY = 'indy', + PRESENTATION_EXCHANGE = 'presentationExchange' +} + +export enum ProofType { + polygon = 'EcdsaSecp256k1Signature2019', + no_ledger = 'Ed25519Signature2018' +} \ No newline at end of file diff --git a/src/commonComponents/SchemaCard.tsx b/src/commonComponents/SchemaCard.tsx index 45c8c9848..7b25fed9a 100644 --- a/src/commonComponents/SchemaCard.tsx +++ b/src/commonComponents/SchemaCard.tsx @@ -19,7 +19,7 @@ const SchemaCard = (props: ISchemaCardProps) => { const attributes = props.limitedAttributes !== false ? props?.attributes?.slice(0, 3) : props?.attributes - const handleIssueClick = () => { + const handleButtonClick = () => { if (props.onClickW3cIssue) { props.onClickW3cIssue(props.schemaId, props.schemaName, props.version, props.issuerDid, props.attributes, props.created); } @@ -109,11 +109,11 @@ const SchemaCard = (props: ISchemaCardProps) => {
- {props.w3cSchema && ( + {props.w3cSchema && !props.isVerification && (
- )} -
-
+ )} + + {props.isVerification && props.w3cSchema &&( +
+ +
+ )} +
+
) } diff --git a/src/commonComponents/interface.ts b/src/commonComponents/interface.ts index ed96297e0..91defea17 100644 --- a/src/commonComponents/interface.ts +++ b/src/commonComponents/interface.ts @@ -25,4 +25,5 @@ export interface ISchemaCardProps { limitedAttributes?: boolean w3cSchema:boolean noLedger:boolean + isVerification?:boolean } \ No newline at end of file diff --git a/src/components/Issuance/EmailIssuance.tsx b/src/components/Issuance/EmailIssuance.tsx index 77dda22d7..c6bddcdb5 100644 --- a/src/components/Issuance/EmailIssuance.tsx +++ b/src/components/Issuance/EmailIssuance.tsx @@ -11,7 +11,7 @@ import IssuancePopup from './IssuancePopup'; import type { AxiosResponse } from 'axios'; import { getFromLocalStorage } from '../../api/Auth'; import { getSchemaCredDef } from '../../api/BulkIssuance'; -import { storageKeys, apiStatusCodes } from '../../config/CommonConstant'; +import { storageKeys, apiStatusCodes, CREDENTIAL_CONTEXT_VALUE, proofPurpose } from '../../config/CommonConstant'; import type { IAttributes, ICredentials, IEmailCredentialData, IIssueAttributes, ITransformedData } from './interface'; import { Field, FieldArray, Form, Formik } from 'formik'; import CustomSpinner from '../CustomSpinner'; @@ -24,7 +24,7 @@ import { checkEcosystem } from '../../config/ecosystem'; import type { ICheckEcosystem} from '../../config/ecosystem'; import { Features } from '../../utils/enums/features'; import { Create, SchemaEndorsement } from './Constant'; -import { DidMethod, SchemaTypes, CredentialType, SchemaTypeValue } from '../../common/enums'; +import { DidMethod, SchemaTypes, CredentialType, SchemaTypeValue, ProofType } from '../../common/enums'; const EmailIssuance = () => { const [formData, setFormData] = useState(); @@ -167,7 +167,7 @@ const EmailIssuance = () => { emailId: entry.email, credential: { "@context": [ - storageKeys.CREDENTIAL_CONTEXT_VALUE, + CREDENTIAL_CONTEXT_VALUE, schemasIdentifier ], "type": [ @@ -194,8 +194,8 @@ const EmailIssuance = () => { }), }, options: { - proofType: schemaTypeValue===SchemaTypeValue.POLYGON ? "EcdsaSecp256k1Signature2019" : "Ed25519Signature2018", - proofPurpose: "assertionMethod" + proofType: schemaTypeValue===SchemaTypeValue.POLYGON ? ProofType.polygon : ProofType.no_ledger, + proofPurpose: proofPurpose } }; diff --git a/src/components/Issuance/Issuance.tsx b/src/components/Issuance/Issuance.tsx index e4166a63d..5da3ad377 100644 --- a/src/components/Issuance/Issuance.tsx +++ b/src/components/Issuance/Issuance.tsx @@ -3,7 +3,7 @@ import * as Yup from 'yup'; import { Alert, Button, Card } from 'flowbite-react'; import { Field, FieldArray, Form, Formik } from 'formik'; -import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; +import { apiStatusCodes, CREDENTIAL_CONTEXT_VALUE, storageKeys, proofPurpose } from '../../config/CommonConstant'; import { getFromLocalStorage, removeFromLocalStorage } from '../../api/Auth'; import { useEffect, useState } from 'react'; import BackButton from '../../commonComponents/backbutton'; @@ -24,7 +24,7 @@ import type { import SummaryCard from '../../commonComponents/SummaryCard'; import React from 'react'; import { getOrganizationById } from '../../api/organization'; -import { DidMethod, CredentialType, SchemaTypeValue } from '../../common/enums'; +import { DidMethod, CredentialType, SchemaTypeValue, ProofType } from '../../common/enums'; const IssueCred = () => { const [schemaLoader, setSchemaLoader] = useState(true); @@ -35,7 +35,7 @@ const IssueCred = () => { schemaId: '', credDefId: '', }); - const [w3cSchemaDetails, setW3cSchemaDetails] = useState({ + const [w3cSchemaDetails, setW3CSchemaDetails] = useState({ schemaName: '', version: '', schemaId: '', @@ -52,7 +52,7 @@ const IssueCred = () => { >([]); const [success, setSuccess] = useState(null); const [error, setError] = useState(null); - const [w3cSchema, setW3cSchema]= useState(false); + const [w3cSchema, setW3CSchema]= useState(false); const [credentialType, setCredentialType] = useState(); const [schemaType, setSchemaType] = useState(); @@ -71,19 +71,19 @@ const IssueCred = () => { const did = data?.data?.org_agents?.[0]?.orgDid; if (did?.includes(DidMethod.POLYGON)) { - setW3cSchema(true); + setW3CSchema(true); setCredentialType(CredentialType.JSONLD); setSchemaType(SchemaTypeValue.POLYGON) await getSchemaAndUsers(true) } else if (did?.includes(DidMethod.KEY) || did?.includes(DidMethod.WEB)) { - setW3cSchema(true); + setW3CSchema(true); setSchemaType(SchemaTypeValue.NO_LEDGER) setCredentialType(CredentialType.JSONLD); await getSchemaAndUsers(true) } else if (did?.includes(DidMethod.INDY)) { - setW3cSchema(false); + setW3CSchema(false); setCredentialType(CredentialType.INDY); await getSchemaAndUsers(false) } @@ -110,7 +110,6 @@ const IssueCred = () => { } else { setFailure('Attributes are not available'); } - } if (w3cSchema) { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); @@ -274,7 +273,7 @@ const IssueCred = () => { if (schemaId) { if (parsedW3cSchemaDetails) { - setW3cSchemaDetails(parsedW3cSchemaDetails); + setW3CSchemaDetails(parsedW3cSchemaDetails); setSchemaLoader(false); @@ -323,7 +322,7 @@ const getSelectedUsers = async (): Promise => { connectionId: item.connectionId, credential: { "@context": [ - storageKeys.CREDENTIAL_CONTEXT_VALUE, + CREDENTIAL_CONTEXT_VALUE, w3cSchemaDetails.schemaId ], "type": [ @@ -350,8 +349,8 @@ const getSelectedUsers = async (): Promise => { }, options: { - proofType: schemaType=== SchemaTypeValue.POLYGON ? "EcdsaSecp256k1Signature2019" : "Ed25519Signature2018", - proofPurpose: "assertionMethod" + proofType: schemaType=== SchemaTypeValue.POLYGON ? ProofType.polygon : ProofType.no_ledger, + proofPurpose: proofPurpose } }; }), diff --git a/src/components/Issuance/IssuedCrdentials.tsx b/src/components/Issuance/IssuedCrdentials.tsx index c4b434e6d..361fda30d 100644 --- a/src/components/Issuance/IssuedCrdentials.tsx +++ b/src/components/Issuance/IssuedCrdentials.tsx @@ -14,13 +14,14 @@ import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; import { dateConversion } from '../../utils/DateConversion'; import { getIssuedCredentials } from '../../api/issuance'; import { pathRoutes } from '../../config/pathRoutes'; -import { getFromLocalStorage } from '../../api/Auth'; +import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; import { getOrgDetails } from '../../config/ecosystem'; import type { IConnectionListAPIParameter } from '../../api/connection'; import type { IssuedCredential } from './interface'; import SortDataTable from '../../commonComponents/datatable/SortDataTable'; import RoleViewButton from '../RoleViewButton'; import { Features } from '../../utils/enums/features'; +import { getOrganizationById } from '../../api/organization'; const initialPageState = { itemPerPage: 10, @@ -36,7 +37,6 @@ const CredentialList = () => { const [error, setError] = useState(null); const [issuedCredList, setIssuedCredList] = useState([]); const [walletCreated, setWalletCreated] = useState(false); - const [isW3c, setIsW3c] = useState(false); const [listAPIParameter, setListAPIParameter] = useState(initialPageState); const [totalItem, setTotalItem] = useState(0); @@ -45,6 +45,8 @@ const CredentialList = () => { nextPage: '', lastPage: '', }); + const [w3cSchema, setW3CSchema]= useState(false); + const getIssuedCredDefs = async ( listAPIParameter: IConnectionListAPIParameter, @@ -163,8 +165,29 @@ const CredentialList = () => { setLoading(false); } }; + const fetchOrganizationDetails = async () => { + setLoading(true); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const did = data?.data?.org_agents?.[0]?.orgDid; + + await setToLocalStorage(storageKeys.ORG_DID, did) + if (did.includes(DidMethod.POLYGON) || did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { + setW3CSchema(true); + } + if (did.includes(DidMethod.INDY)) { + setW3CSchema(false); + } + } + setLoading(false); + }; + useEffect(() => { + fetchOrganizationDetails(); + let getData: NodeJS.Timeout; if (listAPIParameter.search.length >= 1) { @@ -197,19 +220,13 @@ const CredentialList = () => { }; const schemeSelection = async () => { - const orgDid = await getFromLocalStorage(storageKeys.ORG_DID); - if ( - orgDid.includes(DidMethod.POLYGON) || - orgDid.includes(DidMethod.KEY) || - orgDid.includes(DidMethod.WEB) - ) { - setIsW3c(true); - window.location.href = pathRoutes.organizations.Issuance.schema; - } else if (orgDid.includes(DidMethod.INDY)) { - setIsW3c(false); - window.location.href = pathRoutes.organizations.Issuance.issue; + if(w3cSchema){ + window.location.href = pathRoutes.organizations.Issuance.schema; } - }; + else if(!w3cSchema){ + window.location.href = pathRoutes.organizations.Issuance.issue; + } + }; const refreshPage = () => { getIssuedCredDefs(listAPIParameter); diff --git a/src/components/Resources/Schema/Create.tsx b/src/components/Resources/Schema/Create.tsx index e041f2f42..ccdc858b3 100644 --- a/src/components/Resources/Schema/Create.tsx +++ b/src/components/Resources/Schema/Create.tsx @@ -92,9 +92,13 @@ const CreateSchema = () => { checkEcosystemData(); }, []); - const areAllInputsFilled = (formData: IFormData) => { + const filledInputs = (formData: IFormData) => { const { schemaName, schemaVersion, attribute } = formData; - if (!schemaName || !schemaVersion) { + + if ( + (type === SchemaType.INDY && (!schemaName || !schemaVersion)) || + (type === SchemaType.W3C && !schemaName) + ) { return false; } const isAtLeastOneRequired = attribute.some((attr) => attr.isRequired); @@ -125,11 +129,7 @@ const CreateSchema = () => { setType(SchemaType.W3C); setSchemaTypeValues(SchemaTypeValue.POLYGON); } - else if (did.includes(DidMethod.KEY)) { - setType(SchemaType.W3C); - setSchemaTypeValues(SchemaTypeValue.NO_LEDGER); - } - else if (did.includes(DidMethod.WEB)) { + else if (did.includes(DidMethod.KEY) || (did.includes(DidMethod.WEB))) { setType(SchemaType.W3C); setSchemaTypeValues(SchemaTypeValue.NO_LEDGER); } @@ -152,8 +152,8 @@ const CreateSchema = () => { type: type, schemaPayload: { schemaName: values.schemaName, - schemaType:schemaTypeValues, - schemaVersion: values.schemaVersion, + ...(type === SchemaType.W3C && { schemaType: schemaTypeValues }), + ...(type === SchemaType.INDY && { schemaVersion: values.schemaVersion }), attributes: values.attribute, description:values.schemaName, orgId: orgId, @@ -407,13 +407,15 @@ if ( initialValues={formData} validationSchema={yup.object().shape({ schemaName: yup.string().trim().required('Schema is required'), - schemaVersion: yup - .string() - .matches( + ...(type === SchemaType.INDY && { + schemaVersion: yup + .string() + .matches( schemaVersionRegex, - 'Enter valid schema version (eg. 0.1 or 0.0.1)', - ) - .required('Schema version is required'), + 'Enter valid schema version (eg. 0.1 or 0.0.1)' + ) + .required('Schema version is required'), + }), attribute: yup .array() .of( @@ -480,14 +482,18 @@ if ( )}
-
+ >
+
{' '}
+ } +

You must select at least one attribute to create schema @@ -518,8 +526,8 @@ if ( const { values } = form; const { attribute } = values; - const areFirstInputsSelected = - values.schemaName && values.schemaVersion; + const areFirstInputsSelected = + type === SchemaType.INDY ? values.schemaName && values.schemaVersion : values.schemaName; return (

{attribute?.map( @@ -799,7 +807,7 @@ if ( }) } disabled={ - !areAllInputsFilled( + !filledInputs( formikHandlers.values, ) } @@ -873,7 +881,7 @@ if ( type="submit" color="bg-primary-700" disabled={ - !areAllInputsFilled(formikHandlers.values) || + !filledInputs(formikHandlers.values) || inValidAttributes(formikHandlers, 'attributeName') || inValidAttributes(formikHandlers, 'displayName') } diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index 9c0e2ceb0..22b0fa0f8 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -26,11 +26,14 @@ import { Create, SchemaEndorsement } from '../../Issuance/Constant'; import { DidMethod, SchemaType } from '../../../common/enums'; const SchemaList = (props: { - schemaSelectionCallback: ( + schemaSelectionCallback: ( schemaId: string, schemaDetails: SchemaDetails, ) => void; + verificationFlag?: boolean; }) => { + + const verificationFlag = props.verificationFlag ?? false; const [schemaList, setSchemaList] = useState([]); const [schemaListErr, setSchemaListErr] = useState(''); const [loading, setLoading] = useState(true); @@ -48,7 +51,7 @@ const SchemaList = (props: { const [totalItem, setTotalItem] = useState(0); const [isEcosystemData, setIsEcosystemData] = useState(); const [searchValue, setSearchValue] = useState(''); - const [w3cSchema,setW3cSchema]= useState(false); + const [w3cSchema,setW3CSchema]= useState(false); const [isNoLedger,setisNoLedger]= useState(false); const getSchemaList = async ( @@ -191,10 +194,10 @@ const SchemaList = (props: { setWalletStatus(true); } if (did.includes(DidMethod.POLYGON) || did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { - setW3cSchema(true); + setW3CSchema(true); } if (did.includes(DidMethod.INDY)) { - setW3cSchema(false); + setW3CSchema(false); } if (did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { setisNoLedger(true); @@ -305,7 +308,8 @@ const SchemaList = (props: { onClickW3cIssue={handleW3CIssue} w3cSchema={w3cSchema} noLedger={isNoLedger} - + isVerification={verificationFlag} + />
))} diff --git a/src/components/Resources/Schema/interfaces/index.ts b/src/components/Resources/Schema/interfaces/index.ts index 950d01380..444ade7eb 100644 --- a/src/components/Resources/Schema/interfaces/index.ts +++ b/src/components/Resources/Schema/interfaces/index.ts @@ -65,7 +65,7 @@ export interface FieldName { } export interface schemaDetails { schemaName: string; - schemaVersion: string; + schemaVersion?: string; schemaType?:SchemaTypeValue; attributes: IAttributes[]; description?:string; @@ -95,7 +95,7 @@ export interface IAttributes { } export interface IFormData { schemaName: string; - schemaVersion: string; + schemaVersion?: string; schemaType?:SchemaTypeValue; description?:string; attribute: IAttributes[]; diff --git a/src/components/Verification/SchemaCredDefDetails.tsx b/src/components/Verification/SchemaCredDefDetails.tsx index af8ca24b3..96c319d7f 100644 --- a/src/components/Verification/SchemaCredDefDetails.tsx +++ b/src/components/Verification/SchemaCredDefDetails.tsx @@ -1,59 +1,49 @@ + import CopyDid from "../../commonComponents/CopyDid" interface SchemaCredDefProps { - schemaCredDefList: object[] + schemaCredDefList: { + name: string; + credDefId?: string; + schemaId: string; + }[]; } -const SchemaCredDefDetails = ({ schemaCredDefList }: SchemaCredDefProps) => { +const IdDisplay = ({ label, value }: { label: string; value: string }) => ( +
+
+ {label} +
+
:
+
+
+ +
+
+
+); +const SchemaCredDefDetails = ({ schemaCredDefList }: SchemaCredDefProps) => { return ( <> - {schemaCredDefList.map((item) => ( -
+ {schemaCredDefList.map((item, index) => ( +
- - {Object.values(item)[2] ? ( -
-
- {Object.values(item)[2] ? 'Schema Id' : ''} -
{' '} -
- {' '} - : -
{' '} -
-
- -
-
-
- ) : ( - '' + {item.schemaId && !item.credDefId && ( + )} - - {Object.values(item)[1] ? ( -
-
- {Object.values(item)[1] ? 'CredDef Id' : ''} -
{' '} -
- {' '} - : -
{' '} -
-
- -
-
-
- ) : ( - '' + {item.schemaId && item.credDefId && ( + <> + + + )}
))} - ) + ); } -export default SchemaCredDefDetails +export default SchemaCredDefDetails; + diff --git a/src/components/Verification/SchemaSelection.tsx b/src/components/Verification/SchemaSelection.tsx index d542ed5df..a6316b45d 100644 --- a/src/components/Verification/SchemaSelection.tsx +++ b/src/components/Verification/SchemaSelection.tsx @@ -6,6 +6,7 @@ import SchemaList from "../Resources/Schema/SchemasList"; import type { SchemaDetails } from "./interface"; const SchemaSelection = () => { + const isVerification = true; const schemaSelectionCallback = async (schemaId: string, schemaDetails:SchemaDetails) => { await setToLocalStorage(storageKeys.SCHEMA_ID, schemaId) @@ -15,7 +16,7 @@ const SchemaSelection = () => { } return ( - + ) } diff --git a/src/components/Verification/Verification.tsx b/src/components/Verification/Verification.tsx index cbd759c93..bdcd165ae 100644 --- a/src/components/Verification/Verification.tsx +++ b/src/components/Verification/Verification.tsx @@ -11,14 +11,18 @@ import { verifyCredential } from '../../api/verification'; import { pathRoutes } from '../../config/pathRoutes'; import CustomSpinner from '../CustomSpinner'; import BackButton from '../../commonComponents/backbutton'; +import { v4 as uuidv4 } from 'uuid'; import type { IAttribute, ISelectedUser, SchemaDetail, SelectedUsers, - VerifyCredentialPayload, + IW3cSchemaDetails, + SelectedOption, } from './interface'; import SummaryCard from '../../commonComponents/SummaryCard'; +import { getOrganizationById } from '../../api/organization'; +import { DidMethod, RequestType } from '../../common/enums'; const VerificationCred = () => { const [attributeList, setAttributeList] = useState([]); @@ -31,13 +35,64 @@ const VerificationCred = () => { schemaId: '', credDefId: '', }); + const [w3cSchemaDetails, setW3CSchemaDetails] = useState({ + schemaName: '', + version: '', + schemaId: '', + w3cAttributes:[], + issuerDid:'' + }); + const [w3cSchema, setW3CSchema] = useState(false); + const [requestType, setRequestType] = useState(); + const [failure, setFailure] = useState(null); const [loading, setLoading] = useState(true); const [requestLoader, setRequestLoader] = useState(false); const [attributeData, setAttributeData] = useState( null, ); + useEffect(() => { + fetchOrganizationDetails(); + return () => { + setRequestLoader(false); + }; + }, []); + + + const fetchOrganizationDetails = async () => { + try{ + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + if (!orgId) return; + const response = await getOrganizationById(orgId); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const did = data?.data?.org_agents?.[0]?.orgDid; + + if (did?.includes(DidMethod.POLYGON)) { + setW3CSchema(true); + setRequestType(RequestType.PRESENTATION_EXCHANGE) + await getSchemaAndUsers(true); + } + if (did?.includes(DidMethod.KEY) || did?.includes(DidMethod.WEB)) { + setW3CSchema(true); + setRequestType(RequestType.PRESENTATION_EXCHANGE) + await getSchemaAndUsers(true); + + } + if (did?.includes(DidMethod.INDY)) { + setW3CSchema(false); + setRequestType(RequestType.INDY) + await getSchemaAndUsers(false); + } + } + } catch(error){ + console.error('Error in getSchemaAndUsers:', error); + setFailure('Error fetching schema and users'); + } + + }; const handleCheckboxChange = (attributeName: string) => { + setAttributeData( attributeData && attributeData?.map((attribute) => { @@ -52,11 +107,11 @@ const VerificationCred = () => { }; } return attribute; - }), + }) as ISelectedUser[], ); }; - const handleInputChange = (attributeName, value) => { + const handleInputChange = (attributeName:string, value:number) => { setAttributeData( attributeData && attributeData.map((attribute) => { @@ -68,7 +123,7 @@ const VerificationCred = () => { ); }; - const handleSelectChange = (attributeName, selectedOption) => { + const handleSelectChange = (attributeName:string, selectedOption:SelectedOption) => { setAttributeData( attributeData && attributeData?.map((attribute) => { @@ -160,18 +215,56 @@ const VerificationCred = () => { schemaId: schemaId, })); - const verifyCredentialPayload = { - connectionId: JSON.parse(userData)[0]?.connectionId, - comment: 'string', - orgId: orgId, - proofFormats: { - indy: { - attributes: attributes, + let verifyCredentialPayload; + + if (!w3cSchema) { + verifyCredentialPayload = { + connectionId: JSON.parse(userData)[0]?.connectionId, + comment: 'string', + orgId: orgId, + proofFormats: { + indy: { + attributes: attributes, + } + } + }; + + } + if(w3cSchema) { + + const attributePaths = attributes?.map( + (attr) => `$.credentialSubject['${attr.attributeName}']` + ); + verifyCredentialPayload = { + connectionId: JSON.parse(userData)[0]?.connectionId, + comment: 'proof request', + presentationDefinition: { + id: uuidv4(), + input_descriptors: [ + { + id:uuidv4(), + name:w3cSchemaDetails.schemaName, + schema: [ + { + uri: w3cSchemaDetails.schemaId + } + ], + constraints: { + fields: [ + { + path: attributePaths, + }, + ], + }, + purpose: 'Verify proof' + } + ] } + }; } - }; - if (attributes) { - const response = await verifyCredential(verifyCredentialPayload); + + if (attributes && verifyCredentialPayload) { + const response = await verifyCredential(verifyCredentialPayload, requestType); const { data } = response as AxiosResponse; if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { setProofReqSuccess(data?.message); @@ -191,31 +284,59 @@ const VerificationCred = () => { const fetchData = async () => { try { setLoading(true); - await getSchemaAndUsers(); + const schemaAttributes = await getFromLocalStorage( storageKeys.SCHEMA_ATTR, ); - const parsedSchemaDetails = JSON.parse(schemaAttributes) || []; - const inputArray: SelectedUsers[] = parsedSchemaDetails.attribute.map( - (attribute: IAttribute) => { - return { - displayName: attribute.displayName, - attributeName: attribute.attributeName, - isChecked: false, - value: '', - condition: '', - options: [ - { value: '', label: 'Select' }, - { value: '>', label: 'Greater than' }, - { value: '<', label: 'Less than' }, - { value: '>=', label: 'Greater than or equal to' }, - { value: '<=', label: 'Less than or equal to' }, - ], - dataType: attribute.schemaDataType, - }; - }, - ); - setAttributeData(inputArray); + + if(!w3cSchema){ + + const parsedSchemaDetails = JSON.parse(schemaAttributes) || []; + const inputArray: SelectedUsers[] = parsedSchemaDetails.attribute.map( + (attribute: IAttribute) => { + return { + displayName: attribute.displayName, + attributeName: attribute.attributeName, + isChecked: false, + value: '', + condition: '', + options: [ + { value: '', label: 'Select' }, + { value: '>', label: 'Greater than' }, + { value: '<', label: 'Less than' }, + { value: '>=', label: 'Greater than or equal to' }, + { value: '<=', label: 'Less than or equal to' }, + ], + dataType: attribute.schemaDataType, + }; + }, + + ); + + setAttributeData(inputArray); + } + + if(w3cSchema){ + + const getW3cAttributes = await getFromLocalStorage(storageKeys.W3C_SCHEMA_DETAILS); + const parsedSchemaAttributes = JSON.parse(getW3cAttributes) || []; + const w3cInputArray: SelectedUsers[] = parsedSchemaAttributes.attributes.map( + (attribute: IAttribute) => { + return { + displayName: attribute.displayName, + attributeName: attribute.attributeName, + isChecked: false, + value: '', + dataType: attribute.schemaDataType, + }; + + }, + ); + + setAttributeData(w3cInputArray); + + } + setLoading(false); } catch (error) { setLoading(false); @@ -223,6 +344,11 @@ const VerificationCred = () => { } }; + useEffect(() => { + fetchData(); + }, [w3cSchema]); + + const attributeFunction = () => { const attributes = attributeData && @@ -246,7 +372,7 @@ const VerificationCred = () => { ), }, { data: attribute?.displayName }, - { + !w3cSchema && { data: (
{attribute?.dataType === 'number' && ( @@ -268,7 +394,7 @@ const VerificationCred = () => { {attribute?.options?.map( ( option: { - value: number | undefined; + value: number | undefined | string; label: string | undefined; }, index: React.Key | null | undefined, @@ -288,7 +414,7 @@ const VerificationCred = () => {
), }, - { + !w3cSchema && { data: (
{attribute?.dataType === 'number' && ( @@ -320,28 +446,32 @@ const VerificationCred = () => { ], }; }); - - setAttributeList(attributes); - setDisplay( - attributeData?.some((attribute) => attribute?.dataType === 'number'), - ); - }; - - useEffect(() => { - fetchData(); - return () => { - setRequestLoader(false); + + setAttributeList(attributes); + + setDisplay( + attributeData?.some((attribute) => attribute?.dataType === 'number'), + ); }; - }, []); useEffect(() => { attributeData && attributeFunction(); }, [attributeData]); - const getSchemaAndUsers = async () => { + const getSchemaAndUsers = async (isW3c:boolean) => { + if (!isW3c) { const credDefId = await getFromLocalStorage(storageKeys.CRED_DEF_ID); const schemaId = await getFromLocalStorage(storageKeys.SCHEMA_ID); createSchemaPayload(schemaId, credDefId); + } + + if(isW3c){ + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const getW3cSchemaDetails = await getFromLocalStorage(storageKeys.W3C_SCHEMA_DETAILS); + const parsedW3cSchemaDetails = JSON.parse(getW3cSchemaDetails); + const schemaId = parsedW3cSchemaDetails?.schemaId + createW3cSchemaPayload(schemaId,parsedW3cSchemaDetails) + } }; const createSchemaPayload = async (schemaId: string, credDefId: string) => { @@ -351,13 +481,23 @@ const VerificationCred = () => { const version = parts[3]; setSchemaDetails({ schemaName, version, schemaId, credDefId }); } + + }; + const createW3cSchemaPayload = async (schemaId: string, parsedW3cSchemaDetails: any) => { + if (schemaId) { + + if (parsedW3cSchemaDetails) { + setW3CSchemaDetails(parsedW3cSchemaDetails); + } + } + }; const header = [ { columnName: '', width: 'w-0.5' }, { columnName: 'Attributes' }, - display && { columnName: 'Condition' }, - display && { columnName: 'Value', width: 'w-0.75' }, + display && !w3cSchema && { columnName: 'Condition' }, + display && !w3cSchema && { columnName: 'Value', width: 'w-0.75' }, ]; return ( @@ -372,18 +512,28 @@ const VerificationCred = () => {
{loading ? ( -
- -
- ) : ( - - )} +
+ +
+) : ( + !w3cSchema ? ( + + ) : ( + + + ) + )} {(proofReqSuccess || errMsg) && (
{ }, [listAPIParameter]); const schemeSelection = () => { - window.location.href = pathRoutes.organizations.verification.requestProof; + window.location.href = pathRoutes.organizations.verification.schema; }; const refreshPage = () => { diff --git a/src/components/Verification/VerificationDashboard.tsx b/src/components/Verification/VerificationDashboard.tsx index c98cbf7cf..24ef11367 100644 --- a/src/components/Verification/VerificationDashboard.tsx +++ b/src/components/Verification/VerificationDashboard.tsx @@ -6,7 +6,7 @@ const VerificationDashboard = () => { { heading: 'Connection', description: 'Verify credential(s) by selecting existing connections', - path: pathRoutes.organizations.verification.schema, + path: pathRoutes.organizations.verification.connections, }, { heading: 'Email', diff --git a/src/components/Verification/interface.ts b/src/components/Verification/interface.ts index 73f4250fc..c45d81145 100644 --- a/src/components/Verification/interface.ts +++ b/src/components/Verification/interface.ts @@ -60,6 +60,21 @@ export interface SchemaDetail { schemaId: string; credDefId: string; } +export interface IW3cSchemaDetails { + schemaName: string; + version: string; + schemaId: string; + w3cAttributes?: IAttributesData[]; + issuerDid?:string; +} + +export interface IAttributesData { + isRequired: boolean; + name: string; + value: string; + dataType: string; +} + export interface IInput { attributeName: string; @@ -88,18 +103,21 @@ export interface VerifyCredentialPayload { comment: string; orgId: string; } + +export type SelectedOption = "Select" | "Greater than" | "Less than" | "Greater than or equal to" | "Less than or equal to"; + export interface ISelectedUser { - name: any; - condition: string; + name?: string; + condition?: string; dataType: string; - displayName: string; + displayName?: string; attributeName: string; isChecked: false; value: number; - selectedOption: 'Select'; - inputError: ''; - selectError: ''; - options: [ + selectedOption?: SelectedOption; + inputError?: ''; + selectError?: ''; + options?: [ { label: string; value: string; diff --git a/src/config/CommonConstant.ts b/src/config/CommonConstant.ts index 661d2da2a..d32f5537f 100644 --- a/src/config/CommonConstant.ts +++ b/src/config/CommonConstant.ts @@ -5,8 +5,9 @@ export const imageSizeAccepted = 1 // mb export const IMG_MAX_WIDTH = 500 export const IMG_MAX_HEIGHT = 291 export const emailRegex = /(\.[a-zA-Z]{2,})$/ - +export const CREDENTIAL_CONTEXT_VALUE = 'https://www.w3.org/2018/credentials/v1' export const schemaVersionRegex = /^\d{1,5}(?=.*[0-9])(?:\.\d{1,5})?(?:\.\d{1,5})?$/gm +export const proofPurpose = 'assertionMethod' export const apiStatusCodes = { API_STATUS_SUCCESS : 200, @@ -43,6 +44,5 @@ export const storageKeys = { ORG_DID:'did', SCHEMA_TYPE:'type', SELECT_ORG_IN_ECOSYSTEM: 'select_orgs_in_ecosystem', - ERROR_ORG_IN_ECOSYSTEM: 'error_orgs_in_ecosystem', - CREDENTIAL_CONTEXT_VALUE: 'https://www.w3.org/2018/credentials/v1' + ERROR_ORG_IN_ECOSYSTEM: 'error_orgs_in_ecosystem' } From 874fcd0f38acd36ac7f52560c5588391f49c409a Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Fri, 16 Aug 2024 13:38:31 +0530 Subject: [PATCH 27/54] fiz: removed unwanted code from astro components Signed-off-by: pranalidhanavade --- .../verification/verify-credentials/email/index.astro | 2 -- .../email/schemas/cred-defs/attributes/index.astro | 2 -- .../verify-credentials/email/schemas/cred-defs/index.astro | 2 -- .../verification/verify-credentials/email/schemas/index.astro | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/pages/organizations/verification/verify-credentials/email/index.astro b/src/pages/organizations/verification/verify-credentials/email/index.astro index 9a5e5ed8b..49f7c7bc9 100644 --- a/src/pages/organizations/verification/verify-credentials/email/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/index.astro @@ -1,6 +1,5 @@ --- import LayoutSidebar from '../../../../../app/LayoutSidebar.astro'; -// import EmailVerification from '../../../../../components/Verification/EmailVerification'; import { pathRoutes } from '../../../../../config/pathRoutes'; import { checkUserSession } from '../../../../../utils/check-session'; @@ -12,5 +11,4 @@ if (!response.authorized) { --- - diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro index a930a1119..1a5fa482e 100644 --- a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro @@ -1,6 +1,5 @@ --- import LayoutSidebar from "../../../../../../../../app/LayoutSidebar.astro"; -// import EmailCredDefSelection from "../../../../../../../../components/Verification/EmailCredDefSelection"; import { pathRoutes } from "../../../../../../../../config/pathRoutes"; import { checkUserSession } from "../../../../../../../../utils/check-session"; @@ -12,5 +11,4 @@ if (!response.authorized) { --- - diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro index 9fabbb4d5..605a8dca5 100644 --- a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro @@ -1,7 +1,6 @@ --- import LayoutSidebar from "../../../../../../../app/LayoutSidebar.astro"; import CredDefSelection from "../../../../../../../components/Verification/CredDefSelection"; -// import EmailCredDefSelection from "../../../../../../../components/Verification/EmailCredDefSelection"; import { pathRoutes } from "../../../../../../../config/pathRoutes"; import { checkUserSession } from "../../../../../../../utils/check-session"; @@ -14,5 +13,4 @@ if (!response.authorized) { --- - diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro index 8acd29c88..6da80ddd8 100644 --- a/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro @@ -1,6 +1,5 @@ --- import LayoutSidebar from "../../../../../../app/LayoutSidebar.astro"; -// import EmailSchemaSelection from "../../../../../../components/Verification/EmailSchemaSelection"; import SchemaSelection from "../../../../../../components/Verification/SchemaSelection"; import { pathRoutes } from "../../../../../../config/pathRoutes"; import { checkUserSession } from "../../../../../../utils/check-session"; @@ -13,5 +12,4 @@ if (!response.authorized) { --- - From d43a09a1b2029ec28e67a59f4a1dd052c5e16cd6 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Thu, 22 Aug 2024 19:43:01 +0530 Subject: [PATCH 28/54] feat: oob verification using email (#732) * wip: email verification Signed-off-by: bhavanakarwade * wip: verification using email Signed-off-by: bhavanakarwade * wip: verification with email Signed-off-by: bhavanakarwade * wip: routes changes for verification dashboard Signed-off-by: bhavanakarwade * wip: routes changes Signed-off-by: bhavanakarwade * wip: Implemented custom dashboard component for veriifcation and issuance dashboard Signed-off-by: bhavanakarwade * wip: oob verification Signed-off-by: bhavanakarwade * feat: oob verification with email Signed-off-by: bhavanakarwade * wip: resolved conflicts Signed-off-by: bhavanakarwade * wip: oob verification with email Signed-off-by: bhavanakarwade * fix: resolved sonarlint issues Signed-off-by: bhavanakarwade * worked on sonarlint issues Signed-off-by: bhavanakarwade * fix: resolved sonar cloud issues Signed-off-by: bhavanakarwade * fix: resolved function name duplication error Signed-off-by: bhavanakarwade * refactor: get schema list details function Signed-off-by: bhavanakarwade * fix: interface issues Signed-off-by: bhavanakarwade * fix: resolved readability issues Signed-off-by: bhavanakarwade * fix: resolved spacing problems Signed-off-by: bhavanakarwade * resolved sonarlint issues Signed-off-by: bhavanakarwade * refactor: interface types Signed-off-by: bhavanakarwade * refactor: remove unnecessary tab list Signed-off-by: bhavanakarwade * fix: refactor enum file Signed-off-by: bhavanakarwade * refactor: common constant changes Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- src/api/Schema.ts | 22 + src/api/verification.ts | 17 + src/common/enums.ts | 13 +- src/commonComponents/CustomCheckbox.tsx | 80 ++++ src/commonComponents/SchemaCard.tsx | 79 ++-- src/commonComponents/datatable/index.tsx | 4 +- src/commonComponents/interface.ts | 47 +- src/components/Issuance/CredDefSelection.tsx | 1 + .../Resources/Schema/SchemasList.tsx | 1 + src/components/Verification/Connections.tsx | 6 - .../Verification/CredDefSelection.tsx | 7 +- .../Verification/EmailAttributesSelection.tsx | 403 +++++++++++++++++ .../Verification/EmailCredDefSelection.tsx | 195 ++++++++ .../Verification/EmailSchemaSelection.tsx | 10 + .../Verification/EmailVerification.tsx | 416 ++++++++++++++++++ .../Verification/SchemaSelection.tsx | 1 - .../Verification/VerificationDashboard.tsx | 2 +- .../Verification/VerificationSchemasList.tsx | 412 +++++++++++++++++ src/components/Verification/interface.ts | 81 ++++ src/config/CommonConstant.ts | 22 + src/config/apiRoutes.ts | 1 + src/config/pathRoutes.ts | 5 +- .../email/schemas/attributes/index.astro | 14 + .../attributes/verification-email/index.astro | 14 + .../schemas/cred-defs/attributes/index.astro | 4 +- .../attributes/verification-email/index.astro | 15 + .../email/schemas/cred-defs/index.astro | 3 +- .../email/schemas/index.astro | 3 +- .../schemas/cred-defs/index.astro | 2 +- 29 files changed, 1822 insertions(+), 58 deletions(-) create mode 100644 src/commonComponents/CustomCheckbox.tsx create mode 100644 src/components/Verification/EmailAttributesSelection.tsx create mode 100644 src/components/Verification/EmailCredDefSelection.tsx create mode 100644 src/components/Verification/EmailSchemaSelection.tsx create mode 100644 src/components/Verification/EmailVerification.tsx create mode 100644 src/components/Verification/VerificationSchemasList.tsx create mode 100644 src/pages/organizations/verification/verify-credentials/email/schemas/attributes/index.astro create mode 100644 src/pages/organizations/verification/verify-credentials/email/schemas/attributes/verification-email/index.astro create mode 100644 src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/verification-email/index.astro diff --git a/src/api/Schema.ts b/src/api/Schema.ts index 80f18c036..2fafeb825 100644 --- a/src/api/Schema.ts +++ b/src/api/Schema.ts @@ -159,3 +159,25 @@ export const getCredDeffById = async (schemaId: string, orgId: string) => { } } +export const getCredDefDetailsByCredDefId = async (credDefId: string, orgId: string) => { + const token = await getFromLocalStorage(storageKeys.TOKEN) + const details = { + url: `${apiRoutes.organizations.root}/${orgId}${apiRoutes.schema.createCredentialDefinition}/${credDefId}`, + config: { + headers: { + 'Content-type': 'application/json', + 'Authorization': `Bearer ${token}`, + }, + }, + } + + try { + const response = await axiosGet(details) + return response + } + catch (error) { + const err = error as Error + return err?.message + } +} + diff --git a/src/api/verification.ts b/src/api/verification.ts index 317185bfc..dd7aede30 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -23,6 +23,23 @@ export const verifyCredential = async (payload: object, requestType:RequestType) } }; +export const createOobProofRequest = async (payload: object, requestType: RequestType) => { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Verification.oobProofRequest}?requestType=${requestType}`; + const axiosPayload = { + url, + payload, + config: await getHeaderConfigs(), + }; + + try { + return await axiosPost(axiosPayload); + } catch (error) { + const err = error as Error; + return err?.message; + } +}; + export const getVerificationCredential = async (state: IssueCredential) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); const url = `${apiRoutes.Issuance.getIssuedCredentials}?orgId=${orgId}&state=${state}`; diff --git a/src/common/enums.ts b/src/common/enums.ts index c08a80c20..493400253 100644 --- a/src/common/enums.ts +++ b/src/common/enums.ts @@ -139,7 +139,18 @@ export enum SchemaTypes { export enum CredentialType { INDY = 'indy', - JSONLD = 'jsonld' + JSONLD = 'jsonld', +} + +export enum ProtocolVersion { + V1 = 'v1', + V2 = 'v2' +} + +export enum AutoAccept { + ALWAYS = "always", + CONTENT_APPROVED = "contentApproved", + NEVER = "never" } export enum RequestType { diff --git a/src/commonComponents/CustomCheckbox.tsx b/src/commonComponents/CustomCheckbox.tsx new file mode 100644 index 000000000..09a94ac03 --- /dev/null +++ b/src/commonComponents/CustomCheckbox.tsx @@ -0,0 +1,80 @@ +import React, { useEffect, useState } from 'react'; +import { setToLocalStorage } from '../api/Auth'; +import { storageKeys } from '../config/CommonConstant'; +import type { ICustomCheckboxProps, ISchemaData } from './interface'; + +const CustomCheckbox: React.FC = ({ showCheckbox, isVerificationUsingEmail, onChange, schemaData }) => { + const [checked, setChecked] = useState(false); + + useEffect(() => { + if (schemaData) { + try { + const selectedSchemas = JSON.parse(localStorage.getItem('selectedSchemas') ?? '[]'); + const isChecked = selectedSchemas.some((schema: ISchemaData) => schema.schemaId === schemaData.schemaId); + setChecked(isChecked); + } catch (error) { + console.error('Error parsing JSON from localStorage:', error); + } + } + }, [schemaData]); + + const handleCheckboxChange = async () => { + const newChecked = !checked; + setChecked(newChecked); + onChange(newChecked, schemaData); + + try { + const selectedSchemas = JSON.parse(localStorage.getItem('selectedSchemas') ?? '[]'); + + if (newChecked) { + selectedSchemas.push(schemaData); + } else { + const index = selectedSchemas.findIndex((schema: ISchemaData) => schema.schemaId === schemaData?.schemaId); + if (index > -1) { + selectedSchemas.splice(index, 1); + } + } + await setToLocalStorage(storageKeys.SELECTED_SCHEMAS, JSON.stringify(selectedSchemas)); + } catch (error) { + console.error('Error updating localStorage:', error); + } + }; + + return ( + <> + {showCheckbox && ( + + )} + + ); +}; + +export default CustomCheckbox; diff --git a/src/commonComponents/SchemaCard.tsx b/src/commonComponents/SchemaCard.tsx index 7b25fed9a..a9441727f 100644 --- a/src/commonComponents/SchemaCard.tsx +++ b/src/commonComponents/SchemaCard.tsx @@ -2,22 +2,24 @@ import { Button, Card } from 'flowbite-react'; import { dateConversion } from '../utils/DateConversion'; import DateTooltip from '../components/Tooltip'; import CopyDid from './CopyDid'; -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { pathRoutes } from '../config/pathRoutes'; import { getFromLocalStorage } from '../api/Auth'; import { storageKeys } from '../config/CommonConstant'; -import type { ISchemaCardProps } from './interface'; +import type { ISchemaCardProps, ISchemaData } from './interface'; +import CustomCheckbox from './CustomCheckbox'; -const SchemaCard = (props: ISchemaCardProps) => { - const orgDidDetails = async ()=>{ +const SchemaCard = (props: ISchemaCardProps) => { + const orgDidDetails = async () => { const orgDid = await getFromLocalStorage(storageKeys.ORG_DID) } useEffect(() => { - orgDidDetails(); - }, []); - + orgDidDetails(); + }, []); + const attributes = props.limitedAttributes !== false ? props?.attributes?.slice(0, 3) : props?.attributes - + + const handleButtonClick = () => { if (props.onClickW3cIssue) { @@ -25,9 +27,21 @@ const SchemaCard = (props: ISchemaCardProps) => { } }; +const handleCheckboxChange = (checked: boolean, schemaData?: ISchemaData) => { + + if (props.onChange) { + if (schemaData) { + props.onChange(checked, [schemaData]); + } else { + props.onChange(checked, []); + } + } +}; + return ( - { + { + if (!props.w3cSchema) { props.onClickCallback(props.schemaId, props.attributes, props.issuerDid, props.created) } @@ -36,35 +50,35 @@ const SchemaCard = (props: ISchemaCardProps) => { className={`transform transition duration-500 ${(props.isClickable !== false && !props.w3cSchema) ? "hover:scale-105 hover:bg-gray-50 cursor-pointer" : "hover:!cursor-default"} h-full w-full overflow-hidden`}>
-
+
{props.schemaName}
- +

Version: {props.version}

- {props.w3cSchema && ( - - W3C - - )} + {props.w3cSchema && ( + + W3C + + )}
- +
Created: {dateConversion(props.created)} - -
+ +
@@ -77,13 +91,13 @@ const SchemaCard = (props: ISchemaCardProps) => {

Issuer DID: - +

{!props.noLedger && -

- Ledger: {props?.issuerDid?.split(":")[2]} -

+

+ Ledger: {props?.issuerDid?.split(":")[2]} +

}
@@ -109,7 +123,7 @@ const SchemaCard = (props: ISchemaCardProps) => {
- {props.w3cSchema && !props.isVerification && ( + {props.w3cSchema && !props.isVerification && !props.isVerificationUsingEmail && (
)} - {props.isVerification && props.w3cSchema &&( + {props.isVerification && props.w3cSchema && !props.isVerificationUsingEmail && (
+ + {props.showCheckbox && ( + + )} + ) } diff --git a/src/commonComponents/datatable/index.tsx b/src/commonComponents/datatable/index.tsx index dd534fec1..da7b090d5 100644 --- a/src/commonComponents/datatable/index.tsx +++ b/src/commonComponents/datatable/index.tsx @@ -8,6 +8,7 @@ interface DataTableProps { callback?: (clickId: string | null | undefined) => void; displaySelect?: boolean; showBtn?: boolean; + isEmailVerification?: boolean; } const DataTable: React.FC = ({ @@ -17,6 +18,7 @@ const DataTable: React.FC = ({ loading, callback, showBtn, + isEmailVerification }) => { return (
@@ -37,7 +39,7 @@ const DataTable: React.FC = ({
{ele.columnName}
{ele.subColumnName && ( diff --git a/src/commonComponents/interface.ts b/src/commonComponents/interface.ts index 91defea17..4bca4e1c9 100644 --- a/src/commonComponents/interface.ts +++ b/src/commonComponents/interface.ts @@ -10,20 +10,43 @@ export interface IProps { setSuccess: (flag: string | null) => void; } +interface IAttribute { + attributeName: string; + schemaDataType: string; + displayName: string; + isRequired: boolean; +} + +export interface ISchemaData { + schemaId: string; + schemaName: string; + attributes: IAttribute[]; +} + +export interface ICustomCheckboxProps { + showCheckbox: boolean; + isVerificationUsingEmail?: boolean; + onChange: (checked: boolean, schemaData?: ISchemaData) => void; + schemaData?: ISchemaData; + } export interface ISchemaCardProps { - className?: string, - schemaName: string, - version: string, - schemaId: string, - issuerDid: string, - attributes: [], - created: string, - isClickable?: boolean + className?: string; + schemaName: string; + version: string; + schemaId: string; + issuerDid: string; + attributes: []; + created: string; + isClickable?: boolean; + showCheckbox?: boolean; onClickCallback: (schemaId: string, attributes: string[], issuerDid: string, created: string) => void; onClickW3cIssue?: (schemaId: string, schemaName: string, version: string, issuerDid: string, attributes: [], created: string) => void; - limitedAttributes?: boolean - w3cSchema:boolean - noLedger:boolean - isVerification?:boolean + onChange?: (checked: boolean, schemaData: ISchemaData[]) => void; + limitedAttributes?: boolean; + onSelectionChange?: (selectedSchemas: any[]) => void; + w3cSchema?:boolean; + noLedger?:boolean; + isVerification?: boolean; + isVerificationUsingEmail?: boolean; } \ No newline at end of file diff --git a/src/components/Issuance/CredDefSelection.tsx b/src/components/Issuance/CredDefSelection.tsx index ed9dca7b8..0f527646f 100644 --- a/src/components/Issuance/CredDefSelection.tsx +++ b/src/components/Issuance/CredDefSelection.tsx @@ -163,6 +163,7 @@ const CredDefSelection = () => { created={schemaDetailsState.createdDateTime} onClickCallback={schemaSelectionCallback} isClickable={false} + showCheckbox={false} limitedAttributes={false} />
diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index 22b0fa0f8..4bca878cf 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -304,6 +304,7 @@ const SchemaList = (props: { issuerDid={element['issuerId']} attributes={element['attributes']} created={element['createDateTime']} + showCheckbox={false} onClickCallback={schemaSelectionCallback} onClickW3cIssue={handleW3CIssue} w3cSchema={w3cSchema} diff --git a/src/components/Verification/Connections.tsx b/src/components/Verification/Connections.tsx index ffa84f497..aa1219d0b 100644 --- a/src/components/Verification/Connections.tsx +++ b/src/components/Verification/Connections.tsx @@ -44,18 +44,12 @@ const Connections = () => {
  • -
  • - -
  • -
    - -

    diff --git a/src/components/Verification/CredDefSelection.tsx b/src/components/Verification/CredDefSelection.tsx index 6bc5847e6..3f3030364 100644 --- a/src/components/Verification/CredDefSelection.tsx +++ b/src/components/Verification/CredDefSelection.tsx @@ -124,7 +124,7 @@ const CredDefSelection = () => {
    - +

    Schema @@ -137,7 +137,10 @@ const CredDefSelection = () => {

    :
    -
    } diff --git a/src/components/Verification/EmailAttributesSelection.tsx b/src/components/Verification/EmailAttributesSelection.tsx new file mode 100644 index 000000000..905c70936 --- /dev/null +++ b/src/components/Verification/EmailAttributesSelection.tsx @@ -0,0 +1,403 @@ + +import { Alert, Button } from 'flowbite-react'; +import { useEffect, useState } from 'react'; +import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; +import { apiStatusCodes, predicatesConditions, storageKeys } from '../../config/CommonConstant'; +import BreadCrumbs from '../BreadCrumbs'; +import DataTable from '../../commonComponents/datatable'; +import type { TableData } from '../../commonComponents/datatable/interface'; +import { pathRoutes } from '../../config/pathRoutes'; +import BackButton from '../../commonComponents/backbutton'; +import type { + ISelectedAttributes, +} from './interface'; +import CustomCheckbox from '../../commonComponents/CustomCheckbox'; +import { getOrganizationById } from '../../api/organization'; +import type { AxiosResponse } from 'axios'; +import { DidMethod } from '../../common/enums'; + +const EmailAttributesSelection = () => { + const [attributeList, setAttributeList] = useState([]); + const [proofReqSuccess, setProofReqSuccess] = useState(null); + const [errMsg, setErrMsg] = useState(null); + const [display, setDisplay] = useState(false); + const [loading, setLoading] = useState(true); + const [attributeData, setAttributeData] = useState( + null, + ); + const [w3cSchema, setW3cSchema] = useState(false); + + const handleAttributeChange = async ( + attributeName: string, + schemaId: string | undefined, + changeType: 'checkbox' | 'input' | 'select', + value: string | boolean, + ) => { + const updatedAttributes = attributeData?.map(attribute => { + if (attribute.attributeName === attributeName && attribute.schemaId === schemaId) { + switch (changeType) { + case 'checkbox': + return { + ...attribute, + isChecked: value as boolean, + value: attribute?.value || '', + selectedOption: attribute?.condition || 'Select', + inputError: '', + selectError: '', + }; + case 'input': + return { + ...attribute, + value: value as string, + inputError: '', + }; + case 'select': + return { + ...attribute, + selectedOption: value as string, + selectError: '', + }; + default: + return attribute; + } + } + return attribute; + }) ?? []; + + setAttributeData(updatedAttributes); + + await setToLocalStorage(storageKeys.ATTRIBUTE_DATA, JSON.stringify(updatedAttributes)); + }; + + + const getOrgDetails = async () => { + setLoading(true); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const did = data?.data?.org_agents?.[0]?.orgDid; + + if (did.includes(DidMethod.POLYGON) || did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { + setW3cSchema(true); + }else if (did.includes(DidMethod.INDY)) { + setW3cSchema(false); + } + } + + setLoading(false); + }; + + useEffect(() => { + getOrgDetails(); + }, []); + + + const handleSubmit = () => { + setErrMsg(null); + + if (w3cSchema) { + redirectToAppropriatePage(); + return; + } + + if (hasInvalidNumberAttributes()) { + return; + } + + redirectToAppropriatePage(); + }; + + const hasInvalidNumberAttributes = (): boolean => { + const numberAttributes = attributeData?.filter( + (attr) => attr.dataType === 'number' && attr.isChecked + ); + + for (const attribute of numberAttributes || []) { + if (isInvalidNumberAttribute(attribute)) { + return true; + } + } + + return false; + }; + + const isInvalidNumberAttribute = (attribute: any): boolean => { + if (attribute.selectedOption === 'Select' && !attribute.value) { + setErrMsg('Condition and value are required'); + return true; + } else if (!attribute.value) { + setErrMsg('Value is required'); + return true; + } else if (!attribute.selectedOption) { + setErrMsg('Condition is required'); + return true; + } + + return false; + }; + + const redirectToAppropriatePage = () => { + window.location.href = w3cSchema + ? `${pathRoutes.organizations.verification.w3cEmailVerification}` + : `${pathRoutes.organizations.verification.emailVerification}`; + }; + + const loadAttributesData = async () => { + + setLoading(true); + + try { + setAttributeData([]); + + if (w3cSchema) { + const getW3CSchemaDetails = await getFromLocalStorage(storageKeys.SELECTED_SCHEMAS); + const parsedW3CSchemaDetails = JSON.parse(getW3CSchemaDetails || '[]'); + + if (Array.isArray(parsedW3CSchemaDetails) && parsedW3CSchemaDetails.length > 0) { + const allAttributes = parsedW3CSchemaDetails.flatMap(schema => { + if (schema.attributes && Array.isArray(schema.attributes)) { + return schema.attributes.map(attribute => ({ + ...attribute, + schemaName: schema.name, + credDefName: '', + schemaId: schema.schemaLedgerId, + credDefId: '' + })); + } + return []; + }); + + const inputArray = allAttributes.map(attribute => ({ + displayName: attribute.displayName, + attributeName: attribute.attributeName, + isChecked: false, + value: '', + condition: '', + options: predicatesConditions, + dataType: attribute.schemaDataType, + schemaName: attribute.schemaName, + credDefName: attribute.credDefName, + schemaId: attribute.schemaId, + credDefId: attribute.credDefId, + selectedOption: 'Select', + inputError: '', + selectError: '' + })); + + setAttributeData(inputArray); + } else { + console.error('W3C schema details are not in the expected format.'); + } + + } else { + const getSelectedCredDefData = await getFromLocalStorage(storageKeys.CRED_DEF_DATA); + const selectedCredDefs = JSON.parse(getSelectedCredDefData || '[]'); + + const schemaAttributes = await getFromLocalStorage(storageKeys.SCHEMA_ATTRIBUTES); + const parsedSchemaDetails = JSON.parse(schemaAttributes || '[]'); + + + if (Array.isArray(parsedSchemaDetails) && parsedSchemaDetails.length > 0) { + const allAttributes = parsedSchemaDetails.flatMap(schema => { + if (schema.attributes && Array.isArray(schema.attributes)) { + return schema.attributes.flatMap(attribute => { + const matchingCredDefs = selectedCredDefs.filter( + credDef => credDef.schemaLedgerId === schema.schemaId + ); + + return matchingCredDefs.map(credDef => ({ + displayName: attribute.displayName, + attributeName: attribute.attributeName, + isChecked: false, + value: '', + condition: '', + options: predicatesConditions, + dataType: attribute.schemaDataType, + schemaName: schema.schemaId.split(':')[2], + credDefName: credDef.tag, + schemaId: schema.schemaId, + credDefId: credDef.credentialDefinitionId, + selectedOption: 'Select', + inputError: '', + selectError: '' + })); + }); + } + return []; + }); + + setAttributeData(allAttributes); + } else { + console.error('Parsed schema details are not in the expected format.'); + } + } + } catch (error) { + console.error('Error fetching data:', error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + loadAttributesData(); + }, [w3cSchema]); + + + const attributeFunction = async () => { + const attributes = attributeData?.map((attribute: ISelectedAttributes) => { + return { + data: [ + { + data: ( +
    + { + handleAttributeChange(attribute?.attributeName, attribute?.schemaId, 'checkbox', checked); + }} + /> +
    + ), + }, + { data: attribute?.displayName }, + { + data: !w3cSchema && ( +
    + {attribute?.dataType === 'number' && ( + + )} + {attribute?.selectError && ( +
    + {attribute?.selectError} +
    + )} +
    + ), + }, + { + data: !w3cSchema && ( +
    + {attribute?.dataType === 'number' && ( + + handleAttributeChange(attribute?.attributeName, attribute?.schemaId, 'input', e.target.value) + } + disabled={!attribute?.isChecked} + className={`${!attribute?.isChecked + ? 'opacity-50 cursor-not-allowed' + : 'cursor-pointer' + } p-1 border border-black rounded-md dark:text-gray-200 dark:bg-gray-700 dark:border-gray-300 dark:placeholder-gray-400 dark:text-white`} + /> + )} + {attribute?.inputError && ( +
    + {attribute?.inputError} +
    + )} +
    + ), + }, + { data: attribute.schemaName }, + { data: attribute.credDefName }, + ], + }; + }); + + setAttributeList(attributes); + setDisplay(attributeData?.some((attribute) => attribute?.dataType === 'number')); + }; + + + useEffect(() => { + attributeData && attributeFunction(); + }, [attributeData]); + + const header = [ + { columnName: '', width: 'w-0.5' }, + { columnName: 'Attributes' }, + display && !w3cSchema && { columnName: 'Condition' }, + display && !w3cSchema && { columnName: 'Value', width: 'w-0.75' }, + { columnName: 'Schema Name' }, + !w3cSchema && { columnName: 'Cred Def Name' }, + ]; + + return ( +
    +
    +
    + + +
    +
    + + {(proofReqSuccess || errMsg) && ( +
    + { + setProofReqSuccess(null); + setErrMsg(null); + }} + > + {proofReqSuccess ?? errMsg} + +
    + )} +
    +

    + Attributes +

    +
    +
    + +
    + +
    + +
    +
    + ); +}; + +export default EmailAttributesSelection; + diff --git a/src/components/Verification/EmailCredDefSelection.tsx b/src/components/Verification/EmailCredDefSelection.tsx new file mode 100644 index 000000000..7f8c04e1d --- /dev/null +++ b/src/components/Verification/EmailCredDefSelection.tsx @@ -0,0 +1,195 @@ +import { Button } from "flowbite-react"; +import React, { useEffect, useState, type ChangeEvent } from "react"; +import { getFromLocalStorage, removeFromLocalStorage, setToLocalStorage } from "../../api/Auth"; +import { apiStatusCodes, emailCredDefHeaders, storageKeys } from "../../config/CommonConstant"; +import { pathRoutes } from "../../config/pathRoutes"; +import BreadCrumbs from "../BreadCrumbs"; +import { AlertComponent } from "../AlertComponent"; +import type { CredDefData } from "./interface"; +import type { TableData } from "../../commonComponents/datatable/interface"; +import DataTable from "../../commonComponents/datatable"; +import { getCredentialDefinitionsForVerification } from "../../api/verification"; +import BackButton from '../../commonComponents/backbutton'; +import SearchInput from '../SearchInput'; +import { getSchemaById } from "../../api/Schema"; +import type { AxiosResponse } from "axios"; +import CustomCheckbox from "../../commonComponents/CustomCheckbox"; + +const EmailCredDefSelection = () => { + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [credDefList, setCredDefList] = useState([]); + const [searchValue, setSearchValue] = useState(''); + const [selectedCredDefs, setSelectedCredDefs] = useState([]); + + useEffect(() => { + const fetchData = async () => { + await removeFromLocalStorage(storageKeys.CRED_DEF_ID); + getSchemaAndCredDef(); + }; + + fetchData(); + }, []); + + const getSchemaAndCredDef = async () => { + try { + const schemaIdsJSON = await getFromLocalStorage(storageKeys.SCHEMA_IDS); + const schemaIds = schemaIdsJSON ? JSON.parse(schemaIdsJSON) : []; + + if (schemaIds && schemaIds.length > 0) { + getCredDefs(schemaIds); + } + } catch (error) { + console.error('Error fetching schema details:', error); + } + }; + + const handleContinue = () => { + if (selectedCredDefs.length === 0) { + setError('Please select at least one credential definition.'); + return; + } + window.location.href = `${pathRoutes.organizations.verification.attributes}`; + }; + + const getCredDefs = async (schemaIds: string[]) => { + setLoading(true); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + let allCredDefs: TableData[] = []; + let rawCredDefs: CredDefData[] = []; + + for (const schemaId of schemaIds) { + try { + const response = await getCredentialDefinitionsForVerification(schemaId); + + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const getSchemaDetails = await getSchemaById(schemaId, orgId); + const schemaName = getSchemaDetails?.data?.data?.schema?.name; + + const credDefs = data?.data?.map((ele: CredDefData) => { + rawCredDefs.push(ele); + + return { + data: [ + { + data: ( +
    + + { + selectConnection(ele?.credentialDefinitionId, checked); + }} + /> + {ele?.tag ? ele?.tag : 'Not available'} +
    + ) + }, + { data: schemaName || 'Not available' }, + { data: ele?.revocable === true ? Yes : No } + ] + }; + }); + + allCredDefs = [...allCredDefs, ...credDefs]; + + } else { + console.error(`Error fetching credential definitions for schema ${schemaId}`); + } + } catch (error) { + console.error(`Error fetching credential definitions for schema ${schemaId}:`, error); + } + } + + setLoading(false); + setCredDefList(allCredDefs); + await setToLocalStorage(storageKeys.SCHEMA_CRED_DEFS, rawCredDefs); + + if (allCredDefs.length === 0) { + setError('No Credential Definitions Found'); + } + }; + + const selectConnection = async (credDefId: string, checked: boolean) => { + if (credDefId) { + const getRawCredDefs = await getFromLocalStorage(storageKeys.SCHEMA_CRED_DEFS); + const parsedRawCredDefs = JSON.parse(getRawCredDefs); + + const selectedCredDef = parsedRawCredDefs.find((credDef: CredDefData) => credDef.credentialDefinitionId === credDefId); + + if (selectedCredDef) { + setSelectedCredDefs((prevSelected) => { + const isAlreadySelected = prevSelected.some( + (credDef) => credDef.credentialDefinitionId === selectedCredDef.credentialDefinitionId + ); + + if (!isAlreadySelected) { + const newSelected = [...prevSelected, selectedCredDef]; + + setToLocalStorage(storageKeys.CRED_DEF_DATA, JSON.stringify(newSelected)); + + return newSelected; + } + return prevSelected; + }); + } + } + else if (!checked) { + setSelectedCredDefs((prevSelected) => { + const newSelected = prevSelected.filter( + (credDef) => credDef.credentialDefinitionId !== credDefId + ); + + setToLocalStorage(storageKeys.CRED_DEF_DATA, JSON.stringify(newSelected)); + + return newSelected; + }); + } + }; + + + return ( +
    +
    +
    + + +
    +
    +

    + Credential-definition +

    + ) => setSearchValue(e.target.value)} /> +
    +
    + + {error && ( + { + setError(null); + }} + /> + )} + { }} /> +
    + +
    +
    + ); +}; + +export default EmailCredDefSelection; + diff --git a/src/components/Verification/EmailSchemaSelection.tsx b/src/components/Verification/EmailSchemaSelection.tsx new file mode 100644 index 000000000..4b78a4404 --- /dev/null +++ b/src/components/Verification/EmailSchemaSelection.tsx @@ -0,0 +1,10 @@ +import VerificationSchemasList from "./VerificationSchemasList"; + +const EmailSchemaSelection = () => { + + return ( + + ) +} + +export default EmailSchemaSelection; diff --git a/src/components/Verification/EmailVerification.tsx b/src/components/Verification/EmailVerification.tsx new file mode 100644 index 000000000..6fe995e8e --- /dev/null +++ b/src/components/Verification/EmailVerification.tsx @@ -0,0 +1,416 @@ +import { useEffect, useState } from 'react'; +import * as Yup from 'yup'; +import BreadCrumbs from '../BreadCrumbs'; +import { Button } from 'flowbite-react'; +import { Field, Form, Formik } from 'formik'; +import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; +import { createOobProofRequest } from '../../api/verification'; +import { AutoAccept, DidMethod, ProtocolVersion, RequestType } from '../../common/enums'; +import type { AxiosResponse } from 'axios'; +import { getFromLocalStorage, removeFromLocalStorage } from '../../api/Auth'; +import type { IEmailValues, IPredicate, IRequestedAttributes, ISelectedAttributes } from './interface'; +import { getOrganizationById } from '../../api/organization'; +import { pathRoutes } from '../../config/pathRoutes'; +import { AlertComponent } from '../AlertComponent'; + +const EmailVerification = () => { + const [loading, setLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + const [emailInputs, setEmailInputs] = useState([{ value: '' }]); + const [w3cSchema, setW3cSchema] = useState(false); + + const handleInputChange = (index: number, event: React.ChangeEvent) => { + const newEmailInputs = [...emailInputs]; + newEmailInputs[index].value = event.target.value; + setEmailInputs(newEmailInputs); + }; + + const handleAddInput = () => { + setEmailInputs([...emailInputs, { value: '' }]); + }; + + const handleDeleteInput = (index: number) => { + if (emailInputs.length > 1) { + const newEmailInputs = emailInputs.filter((_, i) => i !== index); + setEmailInputs(newEmailInputs); + } + }; + + const getOrganizationDetails = async () => { + setLoading(true); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const did = data?.data?.org_agents?.[0]?.orgDid; + + if (did.includes(DidMethod.POLYGON) || did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { + setW3cSchema(true); + } + if (did.includes(DidMethod.INDY)) { + setW3cSchema(false); + } + } + setLoading(false); + }; + + useEffect(() => { + getOrganizationDetails(); + }, []); + + const handleSubmit = async (values: IEmailValues) => { + setLoading(true); + setErrorMessage(null); + + try { + let payload; + + if (w3cSchema) { + const getSelectedW3CSchemaDetails = await getFromLocalStorage(storageKeys.ATTRIBUTE_DATA); + + const parsedW3CSchemaDetails = JSON.parse(getSelectedW3CSchemaDetails); + const groupedAttributes = parsedW3CSchemaDetails + .filter(attribute => attribute.isChecked) + .reduce((acc, attribute) => { + const schemaUri = attribute.schemaId; + if (!acc[schemaUri]) { + acc[schemaUri] = { + id: attribute.schemaId.split('/').pop(), + name: attribute.schemaName, + schema: [{ uri: schemaUri }], + constraints: { + fields: [] + }, + purpose: "Verify proof" + }; + } + acc[schemaUri].constraints.fields.push({ + path: `$.credentialSubject['${attribute.attributeName}']` + }); + return acc; + }, {}); + + const inputDescriptors = Object.values(groupedAttributes).map((descriptor) => ({ + ...descriptor, + constraints: { + fields: [{ + path: descriptor.constraints.fields.map(field => field.path) + }] + } + })); + + payload = { + goalCode: "verification", + willConfirm: true, + protocolVersion: ProtocolVersion.V2, + presentationDefinition: { + id: "32f54163-7166-48f1-93d8-ff217bdb0653", + input_descriptors: inputDescriptors + }, + comment: "proof request", + autoAcceptProof: AutoAccept.NEVER, + emailId: values.emailData.map(input => input.email), + reuseConnection: true + }; + } else { + + const selectedAttributes = await getFromLocalStorage(storageKeys.ATTRIBUTE_DATA); + const parsedSelectedAttributes = JSON.parse(selectedAttributes) || []; + + const selectedAttributesDetails = parsedSelectedAttributes.filter((attr: ISelectedAttributes) => attr.isChecked && attr.dataType !== 'number') || []; + const selectedPredicatesDetails = parsedSelectedAttributes.filter(attr => attr.isChecked && attr.dataType === 'number') || []; + + const requestedAttributes: Record = {}; + const requestedPredicates: Record = {}; + + const attributeGroups = selectedAttributesDetails.reduce((acc, attr) => { + const key = `${attr.attributeName}:${attr.schemaId}`; + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(attr.credDefId); + return acc; + }, {} as Record); + + Object.keys(attributeGroups).forEach(key => { + + const parts = key.split(':'); + const attributeName = parts[0]; + const schemaId = parts.slice(1).join(':'); + + if (!requestedAttributes[attributeName]) { + requestedAttributes[attributeName] = { + name: attributeName, + restrictions: [], + }; + } + requestedAttributes[attributeName].restrictions.push(...attributeGroups[key].map(credDefId => ({ + schema_id: schemaId, + cred_def_id: credDefId, + }))); + }); + + selectedPredicatesDetails.forEach(attr => { + if (attr.isChecked && attr.dataType === 'number') { + requestedPredicates[attr.attributeName] = { + name: attr.attributeName, + p_type: attr.selectedOption, + p_value: attr.value, + restrictions: [ + { + schema_id: attr.schemaId, + cred_def_id: attr.credDefId, + }, + ], + }; + } + }); + + const proofFormats = { + indy: { + name: "proof-request", + version: "1.0", + requested_attributes: requestedAttributes, + requested_predicates: requestedPredicates, + }, + }; + + payload = { + goalCode: "verification", + protocolVersion: ProtocolVersion.V1, + isShortenUrl: true, + autoAcceptProof: AutoAccept.NEVER, + emailId: values.emailData.map(input => input.email), + proofFormats, + }; + } + + const requestType = w3cSchema ? RequestType.PRESENTATION_EXCHANGE : RequestType.INDY; + const response = await createOobProofRequest(payload, requestType); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { + await removeFromLocalStorage(storageKeys.ATTRIBUTE_DATA); + window.location.href = pathRoutes.organizations.credentials; + + } else { + setErrorMessage('Failed to create proof request'); + console.error('API response data:', data); + } + } catch (error) { + console.error('Error during handleSubmit:', error); + setErrorMessage('An error occurred. Please try again.'); + } finally { + setLoading(false); + } + }; + + return ( +
    +
    +
    + +
    +
    +

    + Request Proof(s) to Email ID +

    + + Please enter an email address to request a proof to + +
    +
    + {(errorMessage) && ( + { + setErrorMessage(null); + }} + /> + )} +
    +
    +
    +
    +
    + ({ email: input.value })), + }} + validationSchema={Yup.object().shape({ + emailData: Yup.array().of( + Yup.object().shape({ + email: Yup.string() + .email('Invalid email address') + .required('Email is required'), + }), + ), + })} + validateOnBlur + validateOnChange + enableReinitialize + onSubmit={handleSubmit + + } + > + {({ values, errors, touched, resetForm }) => ( + +
    + {values.emailData.map((input, index) => ( +
    +
    +
    +
    + +
    + ) => handleInputChange(index, event)} + className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-md rounded-lg focus:ring-primary-500 focus:border-primary-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" + /> + {errors.emailData?.[index]?.email && touched.emailData?.[index]?.email && ( + + {errors.emailData[index].email} + + )} +
    +
    + + {emailInputs.length > 1 && + + } +
    +
    +
    + ))} +
    + +
    +
    + +
    + + +
    + + )} +
    +
    +
    +
    +
    +
    +
    + ); +}; + +export default EmailVerification; diff --git a/src/components/Verification/SchemaSelection.tsx b/src/components/Verification/SchemaSelection.tsx index a6316b45d..2a2edf64f 100644 --- a/src/components/Verification/SchemaSelection.tsx +++ b/src/components/Verification/SchemaSelection.tsx @@ -1,4 +1,3 @@ - import { removeFromLocalStorage, setToLocalStorage } from "../../api/Auth"; import { storageKeys } from "../../config/CommonConstant"; import { pathRoutes } from "../../config/pathRoutes"; diff --git a/src/components/Verification/VerificationDashboard.tsx b/src/components/Verification/VerificationDashboard.tsx index 24ef11367..244aab23f 100644 --- a/src/components/Verification/VerificationDashboard.tsx +++ b/src/components/Verification/VerificationDashboard.tsx @@ -11,7 +11,7 @@ const VerificationDashboard = () => { { heading: 'Email', description: 'Verify credential(s) by entering email ID for specific user', - path: null, + path: pathRoutes.organizations.verification.email, }, { heading: 'Bulk', diff --git a/src/components/Verification/VerificationSchemasList.tsx b/src/components/Verification/VerificationSchemasList.tsx new file mode 100644 index 000000000..d93b9a939 --- /dev/null +++ b/src/components/Verification/VerificationSchemasList.tsx @@ -0,0 +1,412 @@ + +import { Alert, Button, Pagination } from 'flowbite-react'; +import React, { useEffect, useState } from 'react'; +import type { ChangeEvent } from 'react'; +import type { AxiosResponse } from 'axios'; +import { checkEcosystem, type ICheckEcosystem } from '../../config/ecosystem'; +import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; +import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; +import { getAllSchemas, getAllSchemasByOrgId } from '../../api/Schema'; +import { DidMethod, SchemaType } from '../../common/enums'; +import { getOrganizationById } from '../../api/organization'; +import { Create, SchemaEndorsement } from '../Issuance/Constant'; +import BreadCrumbs from '../BreadCrumbs'; +import SearchInput from '../SearchInput'; +import RoleViewButton from '../RoleViewButton'; +import { Features } from '../../utils/enums/features'; +import { pathRoutes } from '../../config/pathRoutes'; +import CustomSpinner from '../CustomSpinner'; +import { EmptyListMessage } from '../EmptyListComponent'; +import SchemaCard from '../../commonComponents/SchemaCard'; +import type { IAttributesDetails, ISchema, ISchemaData } from './interface'; + +const VerificationSchemasList = () => { + const [schemasList, setSchemasList] = useState([]); + const [schemasDetailsErr, setSchemasDetailsErr] = useState(''); + const [loading, setLoading] = useState(true); + const [allSchemasFlag, setAllSchemasFlag] = useState(false); + const [schemasListParameter, setSchemasListParameter] = useState({ + itemPerPage: 9, + page: 1, + search: '', + sortBy: 'id', + sortingOrder: 'desc', + allSearch: '', + }); + const [walletStatus, setWalletStatus] = useState(false); + const [totalItems, setTotalItems] = useState(0); + const [isEcosystemData, setIsEcosystemData] = useState(); + const [searchValue, setSearchValue] = useState(''); + const [selectedSchemas, setSelectedSchemas] = useState([]); + const [w3cSchema, setW3cSchema] = useState(false); + const [isNoLedger, setIsNoLedger] = useState(false); + + const getSchemaListDetails = async ( + schemasListParameter: { + itemPerPage: number, + page: number, + search: string, + sortingOrder: string, + sortBy: string, + allSearch: string + }, + flag: boolean, + ) => { + try { + const organizationId = await getFromLocalStorage(storageKeys.ORG_ID); + setLoading(true); + let schemasList; + if (allSchemasFlag) { + schemasList = await getAllSchemas(schemasListParameter, SchemaType.INDY); + } else { + schemasList = await getAllSchemasByOrgId( + schemasListParameter, + organizationId, + ); + } + + const { data } = schemasList as AxiosResponse; + + if (schemasList === 'Schema records not found') { + setLoading(false); + setSchemasList([]); + } + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + if (data?.data?.data) { + setTotalItems(data?.data?.lastPage); + setSchemasList(data?.data?.data); + setLoading(false); + } else { + setLoading(false); + if (schemasList !== 'Schema records not found') { + setSchemasDetailsErr(schemasList as string); + } + } + } else { + setLoading(false); + if (schemasList !== 'Schema records not found') { + setSchemasDetailsErr(schemasList as string); + } + } + setTimeout(() => { + setSchemasDetailsErr(''); + }, 3000); + } catch (error) { + console.error('Error while fetching schema list:', error); + setLoading(false); + } + }; + + useEffect(() => { + getSchemaListDetails(schemasListParameter, false); + }, [schemasListParameter, allSchemasFlag]); + + + const onSchemaListParameterSearch = async ( + event: ChangeEvent, + ): Promise => { + event.preventDefault(); + const inputValue = event.target.value; + setSearchValue(inputValue); + + getSchemaListDetails( + { + ...schemasListParameter, + search: inputValue, + }, + false, + ); + + if (allSchemasFlag) { + getSchemaListDetails( + { + ...schemasListParameter, + allSearch: inputValue, + }, + false, + ); + } + }; + + const handleSchemaSelection = ( + schemaId: string, + attributes: IAttributesDetails[], + issuerId: string, + created: string, + ) => { + const schemaDetails = { + schemaId: schemaId, + attributes: attributes, + issuerId: issuerId, + createdDate: created, + }; + + const isSelected = selectedSchemas.some((schema) => schema.schemaId === schemaId); + if (isSelected) { + const updatedSchemas = selectedSchemas.filter((schema) => schema.schemaId !== schemaId); + + setSelectedSchemas(updatedSchemas); + } else { + setSelectedSchemas([...selectedSchemas, schemaDetails]); + } + }; + + + const handleW3cSchemas = async (checked: boolean, schemaData?: ISchemaData) => { + const updateSchemas = (prevSchemas: ISchemaData[]) => { + let updatedSchemas = [...prevSchemas]; + if (checked && schemaData) { + updatedSchemas = [...updatedSchemas, schemaData]; + } else { + updatedSchemas = updatedSchemas.filter(schema => schema?.schemaLedgerId !== schemaData?.schemaLedgerId); + } + + return updatedSchemas; + }; + + setSelectedSchemas(prevSchemas => { + if (!Array.isArray(prevSchemas)) { + console.error('Previous schemas is not an array:', prevSchemas); + return []; + } + + const updatedSchemas = updateSchemas(prevSchemas); + + setToLocalStorage(storageKeys.SELECTED_SCHEMAS, updatedSchemas) + .catch(error => console.error('Failed to save to local storage:', error)); + + return updatedSchemas; + }); + }; + + const fetchOrganizationDetails = async () => { + setLoading(true); + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const did = data?.data?.org_agents?.[0]?.orgDid; + + if (data?.data?.org_agents && data?.data?.org_agents?.length > 0) { + setWalletStatus(true); + } + if (did.includes(DidMethod.POLYGON) || did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { + setW3cSchema(true); + } + if (did.includes(DidMethod.INDY)) { + setW3cSchema(false); + } + if (did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { + setIsNoLedger(true); + } + } + setLoading(false); + }; + + const handleContinue = async () => { + const schemaIds = selectedSchemas?.map(schema => schema?.schemaId) + await setToLocalStorage(storageKeys.SCHEMA_IDS, schemaIds) + + const schemaAttributes = selectedSchemas.map(schema => ({ + schemaId: schema.schemaId, + attributes: schema.attributes, + })); + + await setToLocalStorage(storageKeys.SCHEMA_ATTRIBUTES, schemaAttributes); + + window.location.href = `${pathRoutes.organizations.verification.emailCredDef}`; + }; + + const handleW3CSchemaDetails = async () => { + const w3cSchemaDetails = await getFromLocalStorage(storageKeys.SELECTED_SCHEMAS) + + const parsedSchemaDetails = JSON.parse(w3cSchemaDetails); + + const w3cSchemaAttributes = parsedSchemaDetails.map(schema => ({ + schemaId: schema.schemaId, + attributes: schema.attributes, + schemaName: schema.schemaName + })) + await setToLocalStorage(storageKeys.W3C_SCHEMA_ATTRIBUTES, w3cSchemaAttributes); + + window.location.href = `${pathRoutes.organizations.verification.w3cAttributes}`; + }; + + const options = ['All schemas']; + + const handleFilter = (e: React.ChangeEvent) => { + if (e.target.value === 'All schemas') { + setAllSchemasFlag(true); + } else { + setAllSchemasFlag(false); + getSchemaListDetails(schemasListParameter, false); + } + }; + + useEffect(() => { + fetchOrganizationDetails(); + (async () => { + try { + const data: ICheckEcosystem = await checkEcosystem(); + setIsEcosystemData(data); + } catch (error) { + console.error(error); + } + })(); + setSearchValue(''); + }, []); + + const createSchemaButtonTitle = isEcosystemData?.isEcosystemMember + ? { title: 'Schema Endorsement', toolTip: 'Add new schema request', svg: } + : { title: 'Create', svg: , toolTip: 'Create new schema' }; + const emptySchemaListTitle = 'No Schemas'; + const emptySchemaListDescription = 'Get started by creating a new Schema'; + const emptySchemaListBtn = isEcosystemData?.isEcosystemMember + ? { title: 'Schema Endorsement', svg: } + : { title: 'Create Schema', svg: }; + return ( +
    +
    + +
    + +
    +
    +
    +

    + Schemas +

    + + + + +
    + {walletStatus ? ( + { + window.location.href = `${pathRoutes.organizations.createSchema}`; + }} + /> + ) : ( + { + window.location.href = `${pathRoutes.organizations.dashboard}`; + }} + /> + )} +
    +
    +
    + {schemasDetailsErr && ( + setSchemasDetailsErr(null)}> + +

    {schemasDetailsErr}

    +
    +
    + )} + {schemasList && schemasList.length > 0 ? ( +
    +
    + {schemasList && + schemasList.length > 0 && + schemasList.map((element) => ( +
    + handleW3cSchemas(checked, element)} + onClickCallback={handleSchemaSelection} + /> +
    + ))} +
    + +
    + +
    +
    + {totalItems > 1 && ( + { + setSchemasListParameter((prevState) => ({ + ...prevState, + page: page, + })); + }} + totalPages={totalItems} + /> + )} +
    +
    + ) : ( +
    + {loading ? ( +
    + +
    + ) : ( +
    + { + window.location.href = `${pathRoutes.organizations.createSchema}`; + }} + /> +
    + )} +
    + )} +
    +
    + ); +}; + +export default VerificationSchemasList; diff --git a/src/components/Verification/interface.ts b/src/components/Verification/interface.ts index c45d81145..7ff51f465 100644 --- a/src/components/Verification/interface.ts +++ b/src/components/Verification/interface.ts @@ -35,6 +35,9 @@ export interface SchemaDetails { attribute: string[]; issuerDid: string; createdDate: string; + schemaName?: string; + version?: string; + schemaId?: string; } export interface IProofRrquestDetails { @@ -84,6 +87,10 @@ export interface IAttribute { displayName: string; attributeName: string; schemaDataType: string; + schemaName?: string; + credDefName?: string; + schemaId?: string; + credDefId?: string; } export interface SelectedUsers { userName: string; @@ -112,6 +119,9 @@ export interface ISelectedUser { dataType: string; displayName?: string; attributeName: string; + schemaName?: string; + schemaId?: string; + credDefName?: string; isChecked: false; value: number; selectedOption?: SelectedOption; @@ -124,3 +134,74 @@ export interface ISelectedUser { }, ]; } + +export interface IOption { + value: string | number; + label: string; + } +export interface ISelectedAttributes { + displayName: string; + attributeName: string; + isChecked: boolean; + value: string; + condition: string; + options: IOption[]; + dataType: string; + schemaName?: string; + credDefName?: string; + schemaId?: string; + credDefId?: string; + selectedOption: string; + inputError: string; + selectError: string; + } + + export interface IRequestedAttributes { + name: string; + restrictions: Array<{ + schema_id: string; + cred_def_id: string; + }>; +} +interface IEmailData { + email: string; +} +export interface IEmailValues { + emailData: IEmailData[]; +} + +export interface IPredicate extends IRequestedAttributes { + p_type: string; + p_value: number; +} +export interface IRequestedPredicates { + [key: string]: IPredicate; +} + +export interface IAttributesDetails { + attributeName: string; + schemaDataType: string; + displayName: string; + isRequired: boolean; +} + +export interface ISchemaData { + createDateTime: string; + name: string; + version: string; + attributes: IAttributesDetails[]; + schemaLedgerId: string; + createdBy: string; + publisherDid: string; + orgId: string; + issuerId: string; + organizationName: string; + userName: string; +} + +export interface ISchema { + schemaId: string; + attributes: IAttributesDetails[]; + issuerId: string; + createdDate: string; +} diff --git a/src/config/CommonConstant.ts b/src/config/CommonConstant.ts index d32f5537f..8b119655b 100644 --- a/src/config/CommonConstant.ts +++ b/src/config/CommonConstant.ts @@ -28,8 +28,11 @@ export const storageKeys = { PERMISSIONS: 'user_permissions', USER_EMAIL: 'user_email', SELECTED_USER:'selected_user', + CRED_DEF_DATA: 'CRED_DEF_DATA', SELECTED_CONNECTIONS: 'selected_connections', SCHEMA_ID:'schema_id', + W3C_SCHEMA_DATA:'schema_data', + W3C_SCHEMA_ATTRIBUTES: 'w3c_schema_attributes', SCHEMA_ATTR:'schema_attr', W3C_SCHEMA_DETAILS:'schemaDetails', CRED_DEF_ID:'cred_def_id', @@ -38,6 +41,11 @@ export const storageKeys = { ECOSYSTEM_ID: "ecosystem_id", ORG_DETAILS: "org_details", ECOSYSTEM_ROLE: "ecosystem_role", + SCHEMA_IDS: "schemaIds", + SCHEMA_ATTRIBUTES: "schema_attributes", + SCHEMA_CRED_DEFS: "schema_cred_defs", + ATTRIBUTE_DATA: "attribute_data", + SELECTED_SCHEMAS:'selectedSchemas', SOCKET_ID: "socket_id", LEDGER_ID: "ledger_id", ORG_INFO:'organization_Info', @@ -46,3 +54,17 @@ export const storageKeys = { SELECT_ORG_IN_ECOSYSTEM: 'select_orgs_in_ecosystem', ERROR_ORG_IN_ECOSYSTEM: 'error_orgs_in_ecosystem' } + +export const emailCredDefHeaders = [ + { columnName: 'Cred def name' }, + { columnName: 'Schema name' }, + { columnName: 'Revocable' }, +]; + +export const predicatesConditions = [ + { value: '', label: 'Select' }, + { value: '>', label: 'Greater than' }, + { value: '<', label: 'Less than' }, + { value: '>=', label: 'Greater than or equal to' }, + { value: '<=', label: 'Less than or equal to' } +] diff --git a/src/config/apiRoutes.ts b/src/config/apiRoutes.ts index f2a9b2f33..3c6eb762a 100644 --- a/src/config/apiRoutes.ts +++ b/src/config/apiRoutes.ts @@ -77,6 +77,7 @@ export const apiRoutes = { Verification: { getAllRequestList: '/credentials/proofs', verifyCredential: '/proofs', + oobProofRequest: '/proofs/oob', presentationVerification: '/proofs', proofRequestAttributesVerification: '/verified-proofs', verificationCredDef: '/verifiation/cred-defs' diff --git a/src/config/pathRoutes.ts b/src/config/pathRoutes.ts index 5fd85b4f0..53810e790 100644 --- a/src/config/pathRoutes.ts +++ b/src/config/pathRoutes.ts @@ -52,7 +52,10 @@ export const pathRoutes = { email: '/organizations/verification/verify-credentials/email/schemas', schema: '/organizations/verification/verify-credentials/schemas', credDef: '/organizations/verification/verify-credentials/schemas/cred-defs', - attributes: '/organizations/verification/verify-credentials/schemas/cred-defs/attributes', + w3cAttributes: '/organizations/verification/verify-credentials/email/schemas/attributes', + attributes: '/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes', + emailVerification: '/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/verification-email', + w3cEmailVerification: '/organizations/verification/verify-credentials/email/schemas/attributes/verification-email', emailCredDef: '/organizations/verification/verify-credentials/email/schemas/cred-defs', connections: '/organizations/verification/verify-credentials/schemas/cred-defs/connections', verify: diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/attributes/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/attributes/index.astro new file mode 100644 index 000000000..c95f548f5 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/attributes/index.astro @@ -0,0 +1,14 @@ +--- +import LayoutSidebar from "../../../../../../../app/LayoutSidebar.astro"; +import EmailAttributesSelection from "../../../../../../../components/Verification/EmailAttributesSelection"; +import { checkUserSession } from "../../../../../../../utils/check-session"; + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/attributes/verification-email/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/attributes/verification-email/index.astro new file mode 100644 index 000000000..aa8a0c183 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/attributes/verification-email/index.astro @@ -0,0 +1,14 @@ +--- +import LayoutSidebar from "../../../../../../../../app/LayoutSidebar.astro"; +import EmailVerification from "../../../../../../../../components/Verification/EmailVerification"; +import { checkUserSession } from "../../../../../../../../utils/check-session"; + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro index 1a5fa482e..5503c6b90 100644 --- a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/index.astro @@ -1,14 +1,14 @@ --- import LayoutSidebar from "../../../../../../../../app/LayoutSidebar.astro"; -import { pathRoutes } from "../../../../../../../../config/pathRoutes"; +import EmailAttributesSelection from "../../../../../../../../components/Verification/EmailAttributesSelection"; import { checkUserSession } from "../../../../../../../../utils/check-session"; const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); -const route: string = pathRoutes.auth.sinIn if (!response.authorized) { return Astro.redirect(response.redirect); } --- + diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/verification-email/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/verification-email/index.astro new file mode 100644 index 000000000..cd4f921c5 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/attributes/verification-email/index.astro @@ -0,0 +1,15 @@ +--- +import LayoutSidebar from "../../../../../../../../../app/LayoutSidebar.astro"; +import EmailVerification from "../../../../../../../../../components/Verification/EmailVerification"; +import { checkUserSession } from "../../../../../../../../../utils/check-session"; + + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro index 605a8dca5..9d224c9e5 100644 --- a/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/cred-defs/index.astro @@ -1,6 +1,6 @@ --- import LayoutSidebar from "../../../../../../../app/LayoutSidebar.astro"; -import CredDefSelection from "../../../../../../../components/Verification/CredDefSelection"; +import EmailCredDefSelection from "../../../../../../../components/Verification/EmailCredDefSelection"; import { pathRoutes } from "../../../../../../../config/pathRoutes"; import { checkUserSession } from "../../../../../../../utils/check-session"; @@ -13,4 +13,5 @@ if (!response.authorized) { --- + diff --git a/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro b/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro index 6da80ddd8..4f1d3bb1d 100644 --- a/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro +++ b/src/pages/organizations/verification/verify-credentials/email/schemas/index.astro @@ -1,6 +1,6 @@ --- import LayoutSidebar from "../../../../../../app/LayoutSidebar.astro"; -import SchemaSelection from "../../../../../../components/Verification/SchemaSelection"; +import EmailSchemaSelection from "../../../../../../components/Verification/EmailSchemaSelection"; import { pathRoutes } from "../../../../../../config/pathRoutes"; import { checkUserSession } from "../../../../../../utils/check-session"; @@ -12,4 +12,5 @@ if (!response.authorized) { --- + diff --git a/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/index.astro b/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/index.astro index c5bc7bfd1..d5354bad4 100644 --- a/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/index.astro +++ b/src/pages/organizations/verification/verify-credentials/schemas/cred-defs/index.astro @@ -12,5 +12,5 @@ if (!response.authorized) { --- - + From d74f17fe95399948ac15889d0201c50785766101 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:22:32 +0530 Subject: [PATCH 29/54] fix: W3C issuance schemaName not displayed correctly on ADEYA. (#733) * fix: issuance schemaName issue Signed-off-by: pranalidhanavade * fix: verification breadcrumb fixes Signed-off-by: pranalidhanavade * fix: schema card hover issue Signed-off-by: pranalidhanavade * fix: delete organization delete svg issue Signed-off-by: pranalidhanavade * fix: sonarclous issues Signed-off-by: pranalidhanavade * fix: sonarlint issues duplication error Signed-off-by: pranalidhanavade * fix: added highlight tag for fixing code logic Signed-off-by: pranalidhanavade * fix: added limited attriutes value in common constant file Signed-off-by: pranalidhanavade * fix: resolved comments on pull request Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- src/commonComponents/SchemaCard.tsx | 116 ++++++++++-------- src/commonComponents/interface.ts | 19 ++- src/components/Issuance/EmailIssuance.tsx | 34 ++--- src/components/Issuance/Issuance.tsx | 31 ++--- .../Resources/Schema/SchemasList.tsx | 52 +++++++- src/components/Tooltip/dataTooltip.tsx | 21 ++++ src/components/Verification/Connections.tsx | 20 ++- .../Verification/SchemaSelection.tsx | 6 +- src/components/Verification/Verification.tsx | 10 +- .../VerificationCredentialList.tsx | 3 +- .../Verification/VerificationDashboard.tsx | 3 +- src/components/Verification/interface.ts | 1 + .../organization/DeleteOrganizationsCard.tsx | 11 +- .../walletCommonComponents/DedicatedAgent.tsx | 2 +- src/config/CommonConstant.ts | 3 +- src/config/pathRoutes.ts | 4 + .../schemas/connections/index.astro | 17 +++ .../schemas/connections/verification.astro | 17 +++ 18 files changed, 265 insertions(+), 105 deletions(-) create mode 100644 src/components/Tooltip/dataTooltip.tsx create mode 100644 src/pages/organizations/verification/verify-credentials/schemas/connections/index.astro create mode 100644 src/pages/organizations/verification/verify-credentials/schemas/connections/verification.astro diff --git a/src/commonComponents/SchemaCard.tsx b/src/commonComponents/SchemaCard.tsx index a9441727f..1dc20dc7b 100644 --- a/src/commonComponents/SchemaCard.tsx +++ b/src/commonComponents/SchemaCard.tsx @@ -1,12 +1,14 @@ import { Button, Card } from 'flowbite-react'; import { dateConversion } from '../utils/DateConversion'; import DateTooltip from '../components/Tooltip'; +import DataTooltip from '../components/Tooltip/dataTooltip' + import CopyDid from './CopyDid'; import { useEffect } from 'react'; import { pathRoutes } from '../config/pathRoutes'; import { getFromLocalStorage } from '../api/Auth'; -import { storageKeys } from '../config/CommonConstant'; -import type { ISchemaCardProps, ISchemaData } from './interface'; +import { limitedAttributesLength, storageKeys } from '../config/CommonConstant'; +import type { IAttribute, ISchemaCardProps, ISchemaData } from './interface'; import CustomCheckbox from './CustomCheckbox'; const SchemaCard = (props: ISchemaCardProps) => { @@ -19,7 +21,31 @@ const SchemaCard = (props: ISchemaCardProps) => { const attributes = props.limitedAttributes !== false ? props?.attributes?.slice(0, 3) : props?.attributes - + const AttributesList: React.FC<{ attributes: IAttribute[], limitedAttributes?: boolean }> = ({ attributes, limitedAttributes }) => { + const isLimited = limitedAttributes !== false && attributes.length > limitedAttributesLength; + const displayedAttributes = isLimited ? attributes.slice(0, 3) : attributes; + + return ( +
    +
    + Attributes: +
    + {displayedAttributes.map((element) => ( +
    + + {element?.attributeName} + +
    + ))} + {isLimited && ...} +
    +
    +
    + ); +} const handleButtonClick = () => { if (props.onClickW3cIssue) { @@ -43,11 +69,32 @@ const handleCheckboxChange = (checked: boolean, schemaData?: ISchemaData) => { { if (!props.w3cSchema) { - props.onClickCallback(props.schemaId, props.attributes, props.issuerDid, props.created) + const schemaData = { + schemaId: props.schemaId, + attributes: props.attributes, + issuerDid: props.issuerDid, + created: props.created, + }; + + props.onClickCallback(schemaData); } + + if (props.w3cSchema) { + const W3CSchemaData = { + schemaId: props.schemaId, + schemaName: props.schemaName, + version: props.version, + issuerDid: props.issuerDid, + attributes: props.attributes, + created: props.created, + }; + + props.onClickW3CCallback(W3CSchemaData); +} }} id="schema-cards" - className={`transform transition duration-500 ${(props.isClickable !== false && !props.w3cSchema) ? "hover:scale-105 hover:bg-gray-50 cursor-pointer" : "hover:!cursor-default"} h-full w-full overflow-hidden`}> + className={`transform transition duration-500 ${props.w3cSchema ? "" : (props.isClickable !== false) ? "hover:scale-105 hover:bg-gray-50 cursor-pointer" : "hover:!cursor-default"} h-full w-full overflow-hidden`} + >
    @@ -102,26 +149,19 @@ const handleCheckboxChange = (checked: boolean, schemaData?: ISchemaData) => {
    -
    - Attributes: -
    - {attributes && attributes.length > 0 && ( - <> - {attributes?.map((element) => ( -
    - - {element?.attributeName} - -
    - ))} - {props?.limitedAttributes !== false && props?.attributes?.length > 3 && ...} - - )} -
    -
    + {props.w3cSchema ? ( + attribute.attributeName} + > + + + + ) : ( + + + + )}
    {props.w3cSchema && !props.isVerification && !props.isVerificationUsingEmail && (
    @@ -139,37 +179,13 @@ const handleCheckboxChange = (checked: boolean, schemaData?: ISchemaData) => { style={{ height: '1.5rem', width: '100%', minWidth: '2rem' }} >
    -   +  
    Issue
    )} - - {props.isVerification && props.w3cSchema && !props.isVerificationUsingEmail && ( -
    - -
    - )}
    diff --git a/src/commonComponents/interface.ts b/src/commonComponents/interface.ts index 4bca4e1c9..7e5736f00 100644 --- a/src/commonComponents/interface.ts +++ b/src/commonComponents/interface.ts @@ -10,7 +10,7 @@ export interface IProps { setSuccess: (flag: string | null) => void; } -interface IAttribute { +export interface IAttribute { attributeName: string; schemaDataType: string; displayName: string; @@ -40,7 +40,22 @@ export interface ISchemaCardProps { created: string; isClickable?: boolean; showCheckbox?: boolean; - onClickCallback: (schemaId: string, attributes: string[], issuerDid: string, created: string) => void; + onClickCallback: (schemaData: { + schemaId: string; + attributes: string[]; + issuerDid: string; + created: string; + }) => void; + + onClickW3CCallback: (W3CSchemaData: { + schemaId: string; + schemaName: string; + version: string; + issuerDid: string; + attributes: []; + created: string; + }) => void; + onClickW3cIssue?: (schemaId: string, schemaName: string, version: string, issuerDid: string, attributes: [], created: string) => void; onChange?: (checked: boolean, schemaData: ISchemaData[]) => void; limitedAttributes?: boolean; diff --git a/src/components/Issuance/EmailIssuance.tsx b/src/components/Issuance/EmailIssuance.tsx index c6bddcdb5..badf364e6 100644 --- a/src/components/Issuance/EmailIssuance.tsx +++ b/src/components/Issuance/EmailIssuance.tsx @@ -103,6 +103,7 @@ const EmailIssuance = () => { JSON.parse(schemaAttributes), })); setCredentialOptions(options); + } else { setSuccess(null); @@ -140,6 +141,7 @@ const EmailIssuance = () => { setIssueLoader(true); const existingData = userData; + const organizationDid = await getFromLocalStorage(storageKeys.ORG_DID); let transformedData: ITransformedData = { credentialOffer: [] }; @@ -160,8 +162,10 @@ const EmailIssuance = () => { transformedData.credentialOffer.push(transformedEntry); }); transformedData.credentialDefinitionId = credDefId; + } else if (schemaType=== SchemaTypes.schema_W3C) { + existingData.formData.forEach((entry: { email: string; credentialData: IEmailCredentialData; attributes:IIssueAttributes[] }) => { const credentialOffer = { emailId: entry.email, @@ -172,27 +176,25 @@ const EmailIssuance = () => { ], "type": [ "VerifiableCredential", - credentialOptions[0].schemaName + credentialSelected?.schemaName ], "issuer": { "id": organizationDid }, "issuanceDate": new Date().toISOString(), - - credentialSubject: entry?.attributes?.reduce((acc, attr) => { - if (attr.value === null && !attr.isRequired && typeof attr.value === 'number') { - return acc; - } else { - if (attr.name === 'rollno' && attr.value === '') { - return acc; - } else { - acc[attr.name] = attr.value; - return acc; - } - } - }, { - }), - }, + + //FIXME: Logic for passing default value as 0 for empty value of number dataType attributes. + credentialSubject: entry?.attributes?.reduce((acc, attr) => { + if (attr.schemaDataType === 'number' && (attr.value === '' || attr.value === null)) { + acc[attr.name] = 0; + } else if (attr.schemaDataType === 'string' && attr.value === '') { + acc[attr.name] = ''; + } else if (attr.value !== null) { + acc[attr.name] = attr.value; + } + return acc; + }, {}), + }, options: { proofType: schemaTypeValue===SchemaTypeValue.POLYGON ? ProofType.polygon : ProofType.no_ledger, proofPurpose: proofPurpose diff --git a/src/components/Issuance/Issuance.tsx b/src/components/Issuance/Issuance.tsx index 5da3ad377..720790535 100644 --- a/src/components/Issuance/Issuance.tsx +++ b/src/components/Issuance/Issuance.tsx @@ -333,21 +333,23 @@ const getSelectedUsers = async (): Promise => { "id": w3cSchemaDetails.issuerDid }, issuanceDate: new Date().toISOString(), + //FIXME: Logic for passing default value as 0 for empty value of number dataType attributes. credentialSubject: item?.attributes?.reduce((acc, attr) => { - if (attr.value === null && !attr.isRequired && typeof attr.value === 'number') { - return acc; - } else { - if (attr.name === 'rollno' && attr.value === '') { - return acc; - } else { - acc[attr.name] = attr.value; - return acc; - } - } - }, { - }), - }, - + + if (attr.dataType === 'number' && (attr.value === '' || attr.value === null)) { + + acc[attr.name] = 0; + } else if (attr.dataType === 'string' && attr.value === '') { + + acc[attr.name] = ''; + } else if (attr.value !== null) { + + acc[attr.name] = attr.value; + } + return acc; + }, {}), + }, + options: { proofType: schemaType=== SchemaTypeValue.POLYGON ? ProofType.polygon : ProofType.no_ledger, proofPurpose: proofPurpose @@ -356,6 +358,7 @@ const getSelectedUsers = async (): Promise => { }), orgId: values.orgId, }; + } const convertedAttributesValues = { diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index 4bca878cf..e3b7e51c5 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -14,7 +14,7 @@ import { EmptyListMessage } from '../../EmptyListComponent'; import { Features } from '../../../utils/enums/features'; import RoleViewButton from '../../RoleViewButton'; import SchemaCard from '../../../commonComponents/SchemaCard'; -import type { SchemaDetails } from '../../Verification/interface'; +import type { IW3cSchemaDetails, SchemaDetails } from '../../Verification/interface'; import SearchInput from '../../SearchInput'; import { getFromLocalStorage, setToLocalStorage } from '../../../api/Auth'; import { pathRoutes } from '../../../config/pathRoutes'; @@ -30,6 +30,12 @@ const SchemaList = (props: { schemaId: string, schemaDetails: SchemaDetails, ) => void; + + W3CSchemaSelectionCallback: ( + schemaId: string, + w3cSchemaDetails: IW3cSchemaDetails, + ) => void; + verificationFlag?: boolean; }) => { @@ -134,12 +140,17 @@ const SchemaList = (props: { } }; - const schemaSelectionCallback = ( - schemaId: string, - attributes: string[], + const schemaSelectionCallback = ({ + schemaId, + attributes, + issuerId, + created, + }: { + schemaId: string; + attributes: string[]; issuerId: string, - created: string, - ) => { + created: string; + }) => { const schemaDetails = { attribute: attributes, issuerDid: issuerId, @@ -148,6 +159,34 @@ const SchemaList = (props: { props.schemaSelectionCallback(schemaId, schemaDetails); }; + const W3CSchemaSelectionCallback = async ({ + schemaId, + schemaName, + version, + issuerDid, + attributes, + created, + }: { + schemaId: string, + schemaName: string, + version: string, + issuerDid: string, + attributes: [], + created: string + }) => { + const w3cSchemaDetails = { + schemaId, + schemaName, + version, + issuerDid, + attributes, + created, + }; + props.W3CSchemaSelectionCallback(schemaId, w3cSchemaDetails); + await setToLocalStorage(storageKeys.W3C_SCHEMA_DATA, w3cSchemaDetails); + }; + + const handleW3CIssue = async ( schemaId: string, schemaName: string, @@ -306,6 +345,7 @@ const SchemaList = (props: { created={element['createDateTime']} showCheckbox={false} onClickCallback={schemaSelectionCallback} + onClickW3CCallback={W3CSchemaSelectionCallback} onClickW3cIssue={handleW3CIssue} w3cSchema={w3cSchema} noLedger={isNoLedger} diff --git a/src/components/Tooltip/dataTooltip.tsx b/src/components/Tooltip/dataTooltip.tsx new file mode 100644 index 000000000..41e885046 --- /dev/null +++ b/src/components/Tooltip/dataTooltip.tsx @@ -0,0 +1,21 @@ +import { Tooltip } from 'flowbite-react'; +import React from 'react'; + +interface TooltipProps { + data: Type[]; + renderItem: (item: Type) => string; + id?: string; + children?: React.ReactNode; +} + +const DataTooltip = ({ data, renderItem, children }: TooltipProps) => { + const content = data.map(renderItem).join(', '); + + return ( + + {children} + + ); +}; + +export default DataTooltip; diff --git a/src/components/Verification/Connections.tsx b/src/components/Verification/Connections.tsx index aa1219d0b..1abf872ef 100644 --- a/src/components/Verification/Connections.tsx +++ b/src/components/Verification/Connections.tsx @@ -1,7 +1,7 @@ import { Button } from "flowbite-react"; -import { useState } from "react"; -import { setToLocalStorage } from "../../api/Auth"; +import { useEffect, useState } from "react"; +import { getFromLocalStorage, setToLocalStorage } from "../../api/Auth"; import DataTable from "../../commonComponents/datatable"; import type { TableData } from "../../commonComponents/datatable/interface"; import { storageKeys } from "../../config/CommonConstant"; @@ -10,8 +10,10 @@ import BreadCrumbs from "../BreadCrumbs"; import ConnectionList from "./ConnectionList"; import EmailList from "./EmailList"; import BackButton from '../../commonComponents/backbutton' +import { DidMethod } from "../../common/enums"; const Connections = () => { + const [isW3cDid, setIsW3cDid] = useState(false); const [selectedConnectionList, setSelectedConnectionList] = useState([]) const selectedConnectionHeader = [ { columnName: 'User' }, @@ -21,13 +23,25 @@ const Connections = () => { const selectConnection = (connections: TableData[]) => { setSelectedConnectionList(connections) } + const fetchOrgData = async () => { + const orgDid = await getFromLocalStorage(storageKeys.ORG_DID); + + if (orgDid.includes(DidMethod.POLYGON) || orgDid.includes(DidMethod.KEY) || orgDid.includes(DidMethod.WEB)) { + setIsW3cDid(true); + } else { + setIsW3cDid(false); + } + }; + useEffect(() => { + fetchOrgData(); + }, []); const continueToVerify = async () => { const selectedConnections = selectedConnectionList.map(ele =>{ return {userName: ele.data[0].data, connectionId:ele.data[1].data} }) await setToLocalStorage(storageKeys.SELECTED_USER, selectedConnections) - window.location.href = `${pathRoutes.organizations.verification.verify}` + window.location.href = isW3cDid ? `${pathRoutes.organizations.verification.W3CVerification}` : `${pathRoutes.organizations.verification.verify}` } return ( diff --git a/src/components/Verification/SchemaSelection.tsx b/src/components/Verification/SchemaSelection.tsx index 2a2edf64f..ea8926aae 100644 --- a/src/components/Verification/SchemaSelection.tsx +++ b/src/components/Verification/SchemaSelection.tsx @@ -14,8 +14,12 @@ const SchemaSelection = () => { window.location.href = `${pathRoutes.organizations.verification.credDef}` } + const W3CSchemaSelectionCallback = async () => { + window.location.href = `${pathRoutes.organizations.verification.W3CConnections}` + } + return ( - + ) } diff --git a/src/components/Verification/Verification.tsx b/src/components/Verification/Verification.tsx index bdcd165ae..14864b373 100644 --- a/src/components/Verification/Verification.tsx +++ b/src/components/Verification/Verification.tsx @@ -318,8 +318,10 @@ const VerificationCred = () => { if(w3cSchema){ - const getW3cAttributes = await getFromLocalStorage(storageKeys.W3C_SCHEMA_DETAILS); + const getW3cAttributes = await getFromLocalStorage(storageKeys.W3C_SCHEMA_DATA); + const parsedSchemaAttributes = JSON.parse(getW3cAttributes) || []; + const w3cInputArray: SelectedUsers[] = parsedSchemaAttributes.attributes.map( (attribute: IAttribute) => { return { @@ -447,7 +449,7 @@ const VerificationCred = () => { }; }); - setAttributeList(attributes); + setAttributeList(attributes); setDisplay( attributeData?.some((attribute) => attribute?.dataType === 'number'), @@ -467,7 +469,8 @@ const VerificationCred = () => { if(isW3c){ const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const getW3cSchemaDetails = await getFromLocalStorage(storageKeys.W3C_SCHEMA_DETAILS); + const getW3cSchemaDetails = await getFromLocalStorage(storageKeys.W3C_SCHEMA_DATA); + const parsedW3cSchemaDetails = JSON.parse(getW3cSchemaDetails); const schemaId = parsedW3cSchemaDetails?.schemaId createW3cSchemaPayload(schemaId,parsedW3cSchemaDetails) @@ -531,7 +534,6 @@ const VerificationCred = () => { version={w3cSchemaDetails.version} hideCredDefId={true} /> - ) )} {(proofReqSuccess || errMsg) && ( diff --git a/src/components/Verification/VerificationCredentialList.tsx b/src/components/Verification/VerificationCredentialList.tsx index a87872cb4..510209747 100644 --- a/src/components/Verification/VerificationCredentialList.tsx +++ b/src/components/Verification/VerificationCredentialList.tsx @@ -324,7 +324,8 @@ const VerificationCredentialList = () => { }, [listAPIParameter]); const schemeSelection = () => { - window.location.href = pathRoutes.organizations.verification.schema; + window.location.href = pathRoutes.organizations.verification.requestProof; + }; const refreshPage = () => { diff --git a/src/components/Verification/VerificationDashboard.tsx b/src/components/Verification/VerificationDashboard.tsx index 244aab23f..16888675a 100644 --- a/src/components/Verification/VerificationDashboard.tsx +++ b/src/components/Verification/VerificationDashboard.tsx @@ -6,7 +6,8 @@ const VerificationDashboard = () => { { heading: 'Connection', description: 'Verify credential(s) by selecting existing connections', - path: pathRoutes.organizations.verification.connections, + path: pathRoutes.organizations.verification.schema, + }, { heading: 'Email', diff --git a/src/components/Verification/interface.ts b/src/components/Verification/interface.ts index 7ff51f465..3ea8f57ad 100644 --- a/src/components/Verification/interface.ts +++ b/src/components/Verification/interface.ts @@ -69,6 +69,7 @@ export interface IW3cSchemaDetails { schemaId: string; w3cAttributes?: IAttributesData[]; issuerDid?:string; + created?:string; } export interface IAttributesData { diff --git a/src/components/organization/DeleteOrganizationsCard.tsx b/src/components/organization/DeleteOrganizationsCard.tsx index a6677dffa..69625d6f8 100644 --- a/src/components/organization/DeleteOrganizationsCard.tsx +++ b/src/components/organization/DeleteOrganizationsCard.tsx @@ -30,11 +30,12 @@ const DeleteOrganizationsCard: React.FC = ({ {count &&

    Total:{count}

    }

    diff --git a/src/config/CommonConstant.ts b/src/config/CommonConstant.ts index 8b119655b..3bfde0115 100644 --- a/src/config/CommonConstant.ts +++ b/src/config/CommonConstant.ts @@ -8,6 +8,7 @@ export const emailRegex = /(\.[a-zA-Z]{2,})$/ export const CREDENTIAL_CONTEXT_VALUE = 'https://www.w3.org/2018/credentials/v1' export const schemaVersionRegex = /^\d{1,5}(?=.*[0-9])(?:\.\d{1,5})?(?:\.\d{1,5})?$/gm export const proofPurpose = 'assertionMethod' +export const limitedAttributesLength = 3 export const apiStatusCodes = { API_STATUS_SUCCESS : 200, @@ -31,7 +32,7 @@ export const storageKeys = { CRED_DEF_DATA: 'CRED_DEF_DATA', SELECTED_CONNECTIONS: 'selected_connections', SCHEMA_ID:'schema_id', - W3C_SCHEMA_DATA:'schema_data', + W3C_SCHEMA_DATA:'w3cSchemaDetails', W3C_SCHEMA_ATTRIBUTES: 'w3c_schema_attributes', SCHEMA_ATTR:'schema_attr', W3C_SCHEMA_DETAILS:'schemaDetails', diff --git a/src/config/pathRoutes.ts b/src/config/pathRoutes.ts index 53810e790..c8415ff82 100644 --- a/src/config/pathRoutes.ts +++ b/src/config/pathRoutes.ts @@ -58,8 +58,12 @@ export const pathRoutes = { w3cEmailVerification: '/organizations/verification/verify-credentials/email/schemas/attributes/verification-email', emailCredDef: '/organizations/verification/verify-credentials/email/schemas/cred-defs', connections: '/organizations/verification/verify-credentials/schemas/cred-defs/connections', + W3CConnections: '/organizations/verification/verify-credentials/schemas/connections', + verify: '/organizations/verification/verify-credentials/schemas/cred-defs/connections/verification', + W3CVerification: + '/organizations/verification/verify-credentials/schemas/connections/verification', }, }, ecosystem: { diff --git a/src/pages/organizations/verification/verify-credentials/schemas/connections/index.astro b/src/pages/organizations/verification/verify-credentials/schemas/connections/index.astro new file mode 100644 index 000000000..0ea57bf87 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/schemas/connections/index.astro @@ -0,0 +1,17 @@ +--- +import LayoutSidebar from "../../../../../../app/LayoutSidebar.astro"; +import Connections from "../../../../../../components/Verification/Connections"; +import { pathRoutes } from "../../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../../utils/check-session"; + + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/organizations/verification/verify-credentials/schemas/connections/verification.astro b/src/pages/organizations/verification/verify-credentials/schemas/connections/verification.astro new file mode 100644 index 000000000..2dbff36f5 --- /dev/null +++ b/src/pages/organizations/verification/verify-credentials/schemas/connections/verification.astro @@ -0,0 +1,17 @@ +--- +import LayoutSidebar from "../../../../../../app/LayoutSidebar.astro"; +import VerificationCred from "../../../../../../components/Verification/Verification"; +import { pathRoutes } from "../../../../../../config/pathRoutes"; +import { checkUserSession } from "../../../../../../utils/check-session"; + + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route: string = pathRoutes.auth.sinIn +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + From 801cadebde1d6cc4f1d61d487e99f9c337fc3e5f Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:44:47 +0530 Subject: [PATCH 30/54] refactor: predicates number vaidations (#734) * wip: email verification Signed-off-by: bhavanakarwade * wip: verification using email Signed-off-by: bhavanakarwade * wip: verification with email Signed-off-by: bhavanakarwade * wip: routes changes for verification dashboard Signed-off-by: bhavanakarwade * wip: routes changes Signed-off-by: bhavanakarwade * wip: Implemented custom dashboard component for veriifcation and issuance dashboard Signed-off-by: bhavanakarwade * wip: oob verification Signed-off-by: bhavanakarwade * feat: oob verification with email Signed-off-by: bhavanakarwade * wip: resolved conflicts Signed-off-by: bhavanakarwade * wip: oob verification with email Signed-off-by: bhavanakarwade * fix: resolved sonarlint issues Signed-off-by: bhavanakarwade * worked on sonarlint issues Signed-off-by: bhavanakarwade * fix: resolved sonar cloud issues Signed-off-by: bhavanakarwade * fix: resolved function name duplication error Signed-off-by: bhavanakarwade * refactor: get schema list details function Signed-off-by: bhavanakarwade * fix: interface issues Signed-off-by: bhavanakarwade * fix: resolved readability issues Signed-off-by: bhavanakarwade * fix: resolved spacing problems Signed-off-by: bhavanakarwade * resolved sonarlint issues Signed-off-by: bhavanakarwade * refactor: interface types Signed-off-by: bhavanakarwade * refactor: remove unnecessary tab list Signed-off-by: bhavanakarwade * fix: refactor enum file Signed-off-by: bhavanakarwade * refactor: common constant changes Signed-off-by: bhavanakarwade * fix: unselected fields validations Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- src/components/Verification/EmailCredDefSelection.tsx | 5 +---- src/components/Verification/EmailVerification.tsx | 2 +- src/components/Verification/VerificationSchemasList.tsx | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/Verification/EmailCredDefSelection.tsx b/src/components/Verification/EmailCredDefSelection.tsx index 7f8c04e1d..aeddbaaff 100644 --- a/src/components/Verification/EmailCredDefSelection.tsx +++ b/src/components/Verification/EmailCredDefSelection.tsx @@ -45,10 +45,6 @@ const EmailCredDefSelection = () => { }; const handleContinue = () => { - if (selectedCredDefs.length === 0) { - setError('Please select at least one credential definition.'); - return; - } window.location.href = `${pathRoutes.organizations.verification.attributes}`; }; @@ -178,6 +174,7 @@ const EmailCredDefSelection = () => { { }} />
    - } + + )}
    @@ -338,9 +334,7 @@ const EmailVerification = () => { d="M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" /> - - Add another - + Add another
    @@ -355,11 +349,7 @@ const EmailVerification = () => { }} disabled={loading} className="dark:text-white bg-secondary-700 ring-primary-700 bg-white-700 hover:bg-secondary-700 ring-2 text-black font-medium rounded-lg text-base px-4 lg:px-5 py-2 lg:py-2.5 ml-auto dark:hover:text-black" - style={{ - height: '2.6rem', - width: '6rem', - minWidth: '2rem', - }} + style={{ height: '2.6rem', width: '6rem', minWidth: '2rem' }} > { > - Request Proof
    + )}
    From a22ec341de314fb837b115c808f23232d9213d82 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:22:24 +0530 Subject: [PATCH 33/54] fix: create wallet UI for shared and dedicated agents. (#738) * fix: create did ui fixes Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- .../Resources/Schema/SchemasList.tsx | 8 +++++-- .../walletCommonComponents/DedicatedAgent.tsx | 17 +++++++++++++++ .../walletCommonComponents/SharedAgent.tsx | 21 ++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index 0c3760db6..bc1f9cfb6 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -23,7 +23,7 @@ import { checkEcosystem } from '../../../config/ecosystem'; import type { ICheckEcosystem } from '../../../config/ecosystem'; import { Create, SchemaEndorsement } from '../../Issuance/Constant'; -import { DidMethod, SchemaType } from '../../../common/enums'; +import { DidMethod, SchemaType, SchemaTypes } from '../../../common/enums'; const SchemaList = (props: { schemaSelectionCallback: ( @@ -57,6 +57,8 @@ const SchemaList = (props: { const [totalItem, setTotalItem] = useState(0); const [isEcosystemData, setIsEcosystemData] = useState(); const [searchValue, setSearchValue] = useState(''); + const [schemaType, setSchemaType] = useState(''); + const [w3cSchema,setW3CSchema]= useState(false); const [isNoLedger,setisNoLedger]= useState(false); @@ -70,7 +72,7 @@ const SchemaList = (props: { setLoading(true); let schemaList; if (allSchemaFlag) { - schemaList = await getAllSchemas(schemaListAPIParameter, SchemaType.INDY); + schemaList = await getAllSchemas(schemaListAPIParameter, schemaType); } else { schemaList = await getAllSchemasByOrgId( schemaListAPIParameter, @@ -229,9 +231,11 @@ const SchemaList = (props: { } if (did.includes(DidMethod.POLYGON) || did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { setW3CSchema(true); + setSchemaType(SchemaTypes.schema_W3C) } if (did.includes(DidMethod.INDY)) { setW3CSchema(false); + setSchemaType(SchemaTypes.schema_INDY) } if (did.includes(DidMethod.KEY) || did.includes(DidMethod.WEB)) { setisNoLedger(true); diff --git a/src/components/organization/walletCommonComponents/DedicatedAgent.tsx b/src/components/organization/walletCommonComponents/DedicatedAgent.tsx index 60817dcfe..f1c0b7369 100644 --- a/src/components/organization/walletCommonComponents/DedicatedAgent.tsx +++ b/src/components/organization/walletCommonComponents/DedicatedAgent.tsx @@ -19,6 +19,7 @@ import SetDomainValueInput from './SetDomainValueInput'; import SetPrivateKeyValueInput from './SetPrivateKeyValue'; import { getOrganizationById, setAgentConfigDetails } from '../../../api/organization'; import type { IDedicatedAgentConfig} from '../interfaces'; +import React from 'react'; const RequiredAsterisk = () => * @@ -36,6 +37,7 @@ const DedicatedAgentForm = ({ const [selectedLedger, setSelectedLedger] = useState(''); const [selectedDid, setSelectedDid] = useState(''); const [selectedMethod, setSelectedMethod]=useState('') + const [isSelectedNetwork, setIsSelectedNetwork]=useState('') const [privateKeyValue, setPrivateKeyValue] = useState(''); const [domainValue, setDomainValue] = useState(''); const [isLoading, setIsLoading] = useState(false); @@ -118,6 +120,7 @@ const DedicatedAgentForm = ({ const handleLedgerChanges = (e: ChangeEvent) => { setSelectedLedger(e.target.value); setSelectedMethod(''); + setIsSelectedNetwork(''); setSelectedDid(''); }; const handleMethodChanges = (e: ChangeEvent) => { @@ -248,6 +251,7 @@ const networkRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEv onChange={(e) => { formikHandlers.handleChange(e); handleNetworkChanges(e); + setIsSelectedNetwork(networks[network]) }} className="mr-2" /> @@ -258,7 +262,19 @@ const networkRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEv )); }; +const isSubmitButtonDisabled = () => { + if (!selectedLedger) { + return true; + } + else if ((selectedLedger === Ledgers.POLYGON && !privateKeyValue) || (selectedLedger === Ledgers.INDY && (!selectedMethod || !isSelectedNetwork))) { + return true; + } + else if ((selectedLedger === Ledgers.NO_LEDGER && !selectedMethod) ||(selectedLedger === Ledgers.NO_LEDGER && selectedMethod === DidMethod.WEB && !domainValue)) { + return true; + } + return false; +}; return ( <> @@ -565,6 +581,7 @@ const networkRenderOptions = (formikHandlers: { handleChange: (e: React.ChangeEv
    diff --git a/src/components/Issuance/interface.ts b/src/components/Issuance/interface.ts index 1454c53aa..8c27ddd77 100644 --- a/src/components/Issuance/interface.ts +++ b/src/components/Issuance/interface.ts @@ -27,6 +27,11 @@ export interface IAttributes { } export interface ICredentials { + name?:string; + version?:string; + type?:string; + attributes?:IAttributes[]; + schemaLedgerId?:string; value?:String; label?: string; credentialDefinitionId?: string; @@ -34,7 +39,7 @@ export interface ICredentials { schemaName: string; schemaVersion: string; schemaIdentifier: string; - schemaAttributes: IAttributes[]; + schemaAttributes?: IAttributes[]; credentialDefinition?: string; } diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index bc1f9cfb6..6191bc1a6 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -207,14 +207,18 @@ const SchemaList = (props: { const options = ['All schemas']; - const handleFilter = (e: React.ChangeEvent) => { + const handleFilter = async (e: React.ChangeEvent) => { console.log('Handle filter', e.target.value); if (e.target.value === 'All schemas') { setAllSchemaFlag(true); + await setToLocalStorage (storageKeys.ALL_SCHEMAS, `true`); + } else { setAllSchemaFlag(false); + await setToLocalStorage (storageKeys.ALL_SCHEMAS, `false`); getSchemaList(schemaListAPIParameter, false); } + }; const fetchOrganizationDetails = async () => { @@ -254,6 +258,10 @@ const SchemaList = (props: { console.log(error); } })(); + + (async () => { + await setToLocalStorage (storageKeys.ALL_SCHEMAS, `false`); + })(); setSearchValue(''); }, []); diff --git a/src/components/Resources/Schema/interfaces/index.ts b/src/components/Resources/Schema/interfaces/index.ts index 444ade7eb..8cd5a4346 100644 --- a/src/components/Resources/Schema/interfaces/index.ts +++ b/src/components/Resources/Schema/interfaces/index.ts @@ -1,11 +1,11 @@ import type { SchemaTypeValue } from "../../../../common/enums"; export interface GetAllSchemaListParameter { - itemPerPage: number, - page: number, - search: string, - sortBy: string, - allSearch: string + itemPerPage?: number, + page?: number, + search?: string, + sortBy?: string, + allSearch?: string } export interface IAttributes { diff --git a/src/config/CommonConstant.ts b/src/config/CommonConstant.ts index 3bfde0115..e39008aab 100644 --- a/src/config/CommonConstant.ts +++ b/src/config/CommonConstant.ts @@ -53,7 +53,8 @@ export const storageKeys = { ORG_DID:'did', SCHEMA_TYPE:'type', SELECT_ORG_IN_ECOSYSTEM: 'select_orgs_in_ecosystem', - ERROR_ORG_IN_ECOSYSTEM: 'error_orgs_in_ecosystem' + ERROR_ORG_IN_ECOSYSTEM: 'error_orgs_in_ecosystem', + ALL_SCHEMAS:'allSchemaFlag' } export const emailCredDefHeaders = [ From a27d6986572ab34a034e8e9fe742a31cd9908839 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:44:01 +0530 Subject: [PATCH 36/54] fix: radio button state issue on shared and dedicated wallet creation (#743) Signed-off-by: pranalidhanavade --- src/components/Ecosystem/Endorsement/index.tsx | 4 ++-- src/components/Issuance/BulkIssuance.tsx | 4 ++-- src/components/Issuance/EmailIssuance.tsx | 4 ++-- src/components/Resources/Schema/SchemasList.tsx | 4 ++-- src/components/User/UserDashBoard.tsx | 4 ++-- src/components/Verification/VerificationSchemasList.tsx | 4 ++-- .../organization/walletCommonComponents/DedicatedAgent.tsx | 1 + .../organization/walletCommonComponents/SharedAgent.tsx | 3 ++- src/config/CommonConstant.ts | 1 + 9 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/Ecosystem/Endorsement/index.tsx b/src/components/Ecosystem/Endorsement/index.tsx index 138030654..47681e953 100644 --- a/src/components/Ecosystem/Endorsement/index.tsx +++ b/src/components/Ecosystem/Endorsement/index.tsx @@ -2,7 +2,7 @@ import { Alert, Pagination } from 'flowbite-react'; import { useEffect, useState } from 'react'; import type { ChangeEvent } from 'react'; -import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; +import { apiStatusCodes, itemPerPage, storageKeys } from '../../../config/CommonConstant'; import EndorsementPopup from './EndorsementPopup'; import type { AxiosResponse } from 'axios'; import BreadCrumbs from '../../BreadCrumbs'; @@ -36,7 +36,7 @@ const EndorsementList = () => { const [orgId, setOrgId] = useState(''); const [endorsementListAPIParameter, setEndorsementListAPIParameter] = useState({ - itemPerPage: 9, + itemPerPage: itemPerPage, page: 1, search: '', sortBy: 'id', diff --git a/src/components/Issuance/BulkIssuance.tsx b/src/components/Issuance/BulkIssuance.tsx index 6c99f46e4..8a209bf50 100644 --- a/src/components/Issuance/BulkIssuance.tsx +++ b/src/components/Issuance/BulkIssuance.tsx @@ -7,7 +7,7 @@ import { uploadCsvFile, } from '../../api/BulkIssuance'; import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; -import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; +import { apiStatusCodes, itemPerPage, storageKeys } from '../../config/CommonConstant'; import { AlertComponent } from '../AlertComponent'; import type { AxiosResponse } from 'axios'; import { pathRoutes } from '../../config/pathRoutes'; @@ -50,7 +50,7 @@ const BulkIssuance = () => { const [selectedTemplate, setSelectedTemplate] = useState(); const [isAllSchema, setIsAllSchema] = useState(); const [schemaListAPIParameters, setSchemaListAPIParameters] = useState({ - itemPerPage: 9, + itemPerPage: itemPerPage, page: 1, search: '', sortBy: 'id', diff --git a/src/components/Issuance/EmailIssuance.tsx b/src/components/Issuance/EmailIssuance.tsx index 59d8d4ea8..862803a52 100644 --- a/src/components/Issuance/EmailIssuance.tsx +++ b/src/components/Issuance/EmailIssuance.tsx @@ -11,7 +11,7 @@ import IssuancePopup from './IssuancePopup'; import type { AxiosResponse } from 'axios'; import { getFromLocalStorage } from '../../api/Auth'; import { getSchemaCredDef } from '../../api/BulkIssuance'; -import { storageKeys, apiStatusCodes, CREDENTIAL_CONTEXT_VALUE, proofPurpose } from '../../config/CommonConstant'; +import { storageKeys, apiStatusCodes, CREDENTIAL_CONTEXT_VALUE, proofPurpose, itemPerPage } from '../../config/CommonConstant'; import type { IAttributes, ICredentials, IEmailCredentialData, IIssueAttributes, ITransformedData } from './interface'; import { Field, FieldArray, Form, Formik } from 'formik'; import CustomSpinner from '../CustomSpinner'; @@ -34,7 +34,7 @@ const EmailIssuance = () => { const [loading, setLoading] = useState(true); const [credentialOptions, setCredentialOptions] = useState([]); const [schemaListAPIParameter, setSchemaListAPIParameter] = useState({ - itemPerPage: 9, + itemPerPage: itemPerPage, page: 1, search: '', sortBy: 'id', diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index 6191bc1a6..c93b39ede 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react'; import type { ChangeEvent } from 'react'; import type { GetAllSchemaListParameter } from './interfaces'; -import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; +import { apiStatusCodes, itemPerPage, storageKeys } from '../../../config/CommonConstant'; import { getAllSchemas, getAllSchemasByOrgId } from '../../../api/Schema'; import type { AxiosResponse } from 'axios'; @@ -46,7 +46,7 @@ const SchemaList = (props: { const [allSchemaFlag, setAllSchemaFlag] = useState(false); const [orgId, setOrgId] = useState(''); const [schemaListAPIParameter, setSchemaListAPIParameter] = useState({ - itemPerPage: 9, + itemPerPage: itemPerPage, page: 1, search: '', sortBy: 'id', diff --git a/src/components/User/UserDashBoard.tsx b/src/components/User/UserDashBoard.tsx index f60de25a1..ab06f7650 100644 --- a/src/components/User/UserDashBoard.tsx +++ b/src/components/User/UserDashBoard.tsx @@ -4,7 +4,7 @@ import type { AxiosResponse } from 'axios'; import CustomAvatar from '../Avatar/index'; import type { Organisation } from '../organization/interfaces'; import type { UserActivity } from './interfaces'; -import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; +import { apiStatusCodes, itemPerPage, storageKeys } from '../../config/CommonConstant'; import { getOrganizationById, getOrganizations } from '../../api/organization'; import { getUserActivity } from '../../api/users'; import { @@ -68,7 +68,7 @@ const UserDashBoard = () => { const [schemaCount, setSchemaCount] = useState(0); const [schemaList, setSchemaList] = useState | null>(null); const [schemaListAPIParameter, setSchemaListAPIParameter] = useState({ - itemPerPage: 9, + itemPerPage: itemPerPage, page: 1, search: '', sortBy: 'id', diff --git a/src/components/Verification/VerificationSchemasList.tsx b/src/components/Verification/VerificationSchemasList.tsx index c177271c6..298288dd6 100644 --- a/src/components/Verification/VerificationSchemasList.tsx +++ b/src/components/Verification/VerificationSchemasList.tsx @@ -5,7 +5,7 @@ import type { ChangeEvent } from 'react'; import type { AxiosResponse } from 'axios'; import { checkEcosystem, type ICheckEcosystem } from '../../config/ecosystem'; import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; -import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; +import { apiStatusCodes, itemPerPage, storageKeys } from '../../config/CommonConstant'; import { getAllSchemas, getAllSchemasByOrgId } from '../../api/Schema'; import { DidMethod, SchemaType } from '../../common/enums'; import { getOrganizationById } from '../../api/organization'; @@ -26,7 +26,7 @@ const VerificationSchemasList = () => { const [loading, setLoading] = useState(true); const [allSchemasFlag, setAllSchemasFlag] = useState(false); const [schemasListParameter, setSchemasListParameter] = useState({ - itemPerPage: 9, + itemPerPage: itemPerPage, page: 1, search: '', sortBy: 'id', diff --git a/src/components/organization/walletCommonComponents/DedicatedAgent.tsx b/src/components/organization/walletCommonComponents/DedicatedAgent.tsx index f1c0b7369..3ab8d7a08 100644 --- a/src/components/organization/walletCommonComponents/DedicatedAgent.tsx +++ b/src/components/organization/walletCommonComponents/DedicatedAgent.tsx @@ -388,6 +388,7 @@ const isSubmitButtonDisabled = () => { })} onSubmit={async (values: IValuesShared) => { + values.network = isSelectedNetwork; submitDedicatedWallet( values, diff --git a/src/components/organization/walletCommonComponents/SharedAgent.tsx b/src/components/organization/walletCommonComponents/SharedAgent.tsx index dc364d72f..682b4d062 100644 --- a/src/components/organization/walletCommonComponents/SharedAgent.tsx +++ b/src/components/organization/walletCommonComponents/SharedAgent.tsx @@ -10,6 +10,7 @@ import { DidMethod, Ledgers, Network } from '../../../common/enums'; import SetDomainValueInput from './SetDomainValueInput'; import SetPrivateKeyValueInput from './SetPrivateKeyValue'; import type { ISharedAgentForm, IValuesShared } from "./interfaces"; +import React from "react"; interface IDetails { [key: string]: string | { [subKey: string]: string }; } @@ -298,7 +299,7 @@ const SharedAgentForm = ({ if (!values.privatekey) { values.privatekey = privateKeyValue; } - + values.network = selectedNetwork; submitSharedWallet( values, domainValue, diff --git a/src/config/CommonConstant.ts b/src/config/CommonConstant.ts index e39008aab..aa13004e8 100644 --- a/src/config/CommonConstant.ts +++ b/src/config/CommonConstant.ts @@ -9,6 +9,7 @@ export const CREDENTIAL_CONTEXT_VALUE = 'https://www.w3.org/2018/credentials/v1' export const schemaVersionRegex = /^\d{1,5}(?=.*[0-9])(?:\.\d{1,5})?(?:\.\d{1,5})?$/gm export const proofPurpose = 'assertionMethod' export const limitedAttributesLength = 3 +export const itemPerPage = 9 export const apiStatusCodes = { API_STATUS_SUCCESS : 200, From 58a7fec268d3948b50e18dee6e62c712006e8446 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:40:56 +0530 Subject: [PATCH 37/54] fix: bugs (#744) * refactor: parameter name Signed-off-by: bhavanakarwade * refactor: added query parameter Signed-off-by: bhavanakarwade * fix: remove unnecessary code Signed-off-by: bhavanakarwade * fix: query param issue Signed-off-by: bhavanakarwade * fix: css issues Signed-off-by: bhavanakarwade * fix: create did button restrictions Signed-off-by: bhavanakarwade * fix: popup card fixes Signed-off-by: bhavanakarwade * fix: remove click on card Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- src/commonComponents/SchemaCard.tsx | 2 +- src/components/Issuance/EmailIssuance.tsx | 3 +- src/components/Issuance/interface.ts | 1 + .../Verification/AttributesListData.tsx | 104 ++++++++++++++---- .../Verification/EmailCredDefSelection.tsx | 47 ++++---- .../Verification/ProofRequestPopup.tsx | 30 ++--- 6 files changed, 122 insertions(+), 65 deletions(-) diff --git a/src/commonComponents/SchemaCard.tsx b/src/commonComponents/SchemaCard.tsx index e00d582a2..7a10475c1 100644 --- a/src/commonComponents/SchemaCard.tsx +++ b/src/commonComponents/SchemaCard.tsx @@ -68,7 +68,7 @@ const handleCheckboxChange = (checked: boolean, schemaData?: ISchemaData) => { return ( { - if (!props.w3cSchema) { + if (!props.w3cSchema && !props.isVerificationUsingEmail) { props.onClickCallback(props.schemaId, props.attributes, props.issuerDid, props.created) } diff --git a/src/components/Issuance/EmailIssuance.tsx b/src/components/Issuance/EmailIssuance.tsx index 862803a52..fab81522d 100644 --- a/src/components/Issuance/EmailIssuance.tsx +++ b/src/components/Issuance/EmailIssuance.tsx @@ -210,8 +210,8 @@ const EmailIssuance = () => { transformedData.credentialOffer.push(transformedEntry); }); transformedData.credentialDefinitionId = credDefId; + transformedData.isReuseConnection = true; - } else if (schemaType=== SchemaTypes.schema_W3C) { existingData.formData.forEach((entry: { email: string; credentialData: IEmailCredentialData; attributes:IIssueAttributes[] }) => { @@ -253,6 +253,7 @@ const EmailIssuance = () => { }); transformedData.protocolVersion = "v2"; + transformedData.isReuseConnection = true; transformedData.credentialType = CredentialType.JSONLD; } diff --git a/src/components/Issuance/interface.ts b/src/components/Issuance/interface.ts index 8c27ddd77..fe161edc9 100644 --- a/src/components/Issuance/interface.ts +++ b/src/components/Issuance/interface.ts @@ -187,5 +187,6 @@ export interface ICredentialOffer { credentialOffer: ICredentialOffer[]; credentialDefinitionId?: string; protocolVersion?: string; + isReuseConnection?: boolean; credentialType?: string; } \ No newline at end of file diff --git a/src/components/Verification/AttributesListData.tsx b/src/components/Verification/AttributesListData.tsx index 9eac679dd..0d89dbb7b 100644 --- a/src/components/Verification/AttributesListData.tsx +++ b/src/components/Verification/AttributesListData.tsx @@ -2,45 +2,101 @@ import { Card } from 'flowbite-react'; import CopyDid from '../../commonComponents/CopyDid'; interface AttributesListProps { - attributeDataList: { [key: string]: any }[] + attributeDataList: { [key: string]: any }[]; } +const groupAndMergeAttributes = (data: { [key: string]: any }[], key: string): { [key: string]: any }[] => { + const grouped = data.reduce((result, item) => { + const groupKey = item[key] || item['schemaId']; + if (!result[groupKey]) { + result[groupKey] = {}; + } + // Merge attributes into a single object + result[groupKey] = { ...result[groupKey], ...item }; + return result; + }, {} as { [key: string]: any }); + + // Convert the grouped object back to an array + return Object.values(grouped); +}; + const AttributesListData = ({ attributeDataList }: AttributesListProps): JSX.Element => { - return ( - <> - {attributeDataList?.map((item, index) => ( - -
    -
    - {`Credential ${index + 1}`} -
    + const mergedData = groupAndMergeAttributes(attributeDataList, 'credDefId'); + return ( + <> + {mergedData?.map((item, index) => ( + +
    +
    + {`Credential ${index + 1}`} +
    - {Object.entries(item).map(([key, value], idx) => ( -
    -
    - {key} +
    +
    +
    + Attributes +
    +
    +
    + {' '} + Values
    -
    - : +
    + + {Object.entries(item) + .filter(([key]) => key !== 'credDefId' && key !== 'schemaId') + .map(([key, value], idx) => ( +
    +
    + + {key} +
    +
    + : +
    +
    + {value} +
    +
    + ))} +
    + +
    +
    +
    + schemaId
    +
    :
    - {key === 'schemaId' || key === 'credDefId' ? ( +
    + +
    +
    +
    +
    + + {item.credDefId && ( +
    +
    +
    + credDefId +
    +
    :
    +
    - +
    - ) : ( - {value} - )} +
    - ))} + )}
    ))} - - ); -} + + ); +}; export default AttributesListData; diff --git a/src/components/Verification/EmailCredDefSelection.tsx b/src/components/Verification/EmailCredDefSelection.tsx index aeddbaaff..0448f4608 100644 --- a/src/components/Verification/EmailCredDefSelection.tsx +++ b/src/components/Verification/EmailCredDefSelection.tsx @@ -110,43 +110,42 @@ const EmailCredDefSelection = () => { }; const selectConnection = async (credDefId: string, checked: boolean) => { - if (credDefId) { - const getRawCredDefs = await getFromLocalStorage(storageKeys.SCHEMA_CRED_DEFS); - const parsedRawCredDefs = JSON.parse(getRawCredDefs); - - const selectedCredDef = parsedRawCredDefs.find((credDef: CredDefData) => credDef.credentialDefinitionId === credDefId); - - if (selectedCredDef) { - setSelectedCredDefs((prevSelected) => { + if (!credDefId) return; + + const getRawCredDefs = await getFromLocalStorage(storageKeys.SCHEMA_CRED_DEFS); + const parsedRawCredDefs = JSON.parse(getRawCredDefs); + + const selectedCredDef = parsedRawCredDefs.find( + (credDef: CredDefData) => credDef.credentialDefinitionId === credDefId + ); + + if (selectedCredDef) { + setSelectedCredDefs((prevSelected) => { + if (checked) { const isAlreadySelected = prevSelected.some( (credDef) => credDef.credentialDefinitionId === selectedCredDef.credentialDefinitionId ); - + if (!isAlreadySelected) { const newSelected = [...prevSelected, selectedCredDef]; - setToLocalStorage(storageKeys.CRED_DEF_DATA, JSON.stringify(newSelected)); - return newSelected; } - return prevSelected; - }); - } - } - else if (!checked) { - setSelectedCredDefs((prevSelected) => { - const newSelected = prevSelected.filter( - (credDef) => credDef.credentialDefinitionId !== credDefId - ); - - setToLocalStorage(storageKeys.CRED_DEF_DATA, JSON.stringify(newSelected)); - - return newSelected; + } else { + const newSelected = prevSelected.filter( + (credDef) => credDef.credentialDefinitionId !== selectedCredDef.credentialDefinitionId + ); + setToLocalStorage(storageKeys.CRED_DEF_DATA, JSON.stringify(newSelected)); + return newSelected; + } + + return prevSelected; }); } }; + return (
    diff --git a/src/components/Verification/ProofRequestPopup.tsx b/src/components/Verification/ProofRequestPopup.tsx index edcc9242b..ee1d207b4 100644 --- a/src/components/Verification/ProofRequestPopup.tsx +++ b/src/components/Verification/ProofRequestPopup.tsx @@ -87,21 +87,7 @@ const ProofRequest = (props: IProofRrquestDetails) => { {' '} Verification Details

    - {props.verifyLoading ? ( -
    - -
    - ) : ( -
    -
    - - - -
    -
    - )} -
    - {succesMsg && ( + {succesMsg && (
    {
    )} + {props.verifyLoading ? ( +
    + +
    + ) : ( +
    +
    + + + +
    +
    + )} +
    ))} From 0f6e71ab0784c52efaaa47f7d74bd57dec61f95d Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:31:39 +0530 Subject: [PATCH 40/54] fix: schema name is not visible in credentials list (#750) * refactor: parameter name Signed-off-by: bhavanakarwade * refactor: added query parameter Signed-off-by: bhavanakarwade * fix: remove unnecessary code Signed-off-by: bhavanakarwade * fix: query param issue Signed-off-by: bhavanakarwade * fix: css issues Signed-off-by: bhavanakarwade * fix: create did button restrictions Signed-off-by: bhavanakarwade * fix: popup card fixes Signed-off-by: bhavanakarwade * fix: remove click on card Signed-off-by: bhavanakarwade * fix: remove card click Signed-off-by: bhavanakarwade * fix: cred def details Signed-off-by: bhavanakarwade * fix: schema card click issue Signed-off-by: bhavanakarwade * fix: schema name is not visible in credentials list Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- src/components/Issuance/IssuedCrdentials.tsx | 5 ++--- src/components/Issuance/interface.ts | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Issuance/IssuedCrdentials.tsx b/src/components/Issuance/IssuedCrdentials.tsx index 361fda30d..b09fb0ce4 100644 --- a/src/components/Issuance/IssuedCrdentials.tsx +++ b/src/components/Issuance/IssuedCrdentials.tsx @@ -73,9 +73,8 @@ const CredentialList = () => { }); const credentialList = data?.data?.data?.map( (issuedCredential: IssuedCredential) => { - const schemaName = issuedCredential.schemaId - ? issuedCredential.schemaId.split(':').slice(2).join(':') - : 'Not available'; + + const schemaName = issuedCredential?.schemaName ?? 'Not available'; return { data: [ { diff --git a/src/components/Issuance/interface.ts b/src/components/Issuance/interface.ts index fe161edc9..ba2f27521 100644 --- a/src/components/Issuance/interface.ts +++ b/src/components/Issuance/interface.ts @@ -55,6 +55,7 @@ export interface IssuedCredential { state: string; isRevocable: boolean; schemaId: string; + schemaName: string; } export interface IProps { From 7c2865eaf2f9e857d2ba66a8b75f6ceed3ba61dd Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:07:00 +0530 Subject: [PATCH 41/54] fix: Schemas Page Pagination Issue (#751) * fix: create did ui fixes Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: OOB issuance with other organizations schema Signed-off-by: pranalidhanavade * fix: removed consoles Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: pagination issues on schema list Signed-off-by: pranalidhanavade * fix: sonarcloud issues Signed-off-by: pranalidhanavade * fix: sonarlint issues Signed-off-by: pranalidhanavade * fix: sonarlint issues Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- src/components/Issuance/BulkIssuance.tsx | 8 ++- src/components/Issuance/EmailIssuance.tsx | 71 ++++++++++--------- .../Resources/Schema/SchemasList.tsx | 27 ++++--- 3 files changed, 60 insertions(+), 46 deletions(-) diff --git a/src/components/Issuance/BulkIssuance.tsx b/src/components/Issuance/BulkIssuance.tsx index 8a209bf50..709db7f74 100644 --- a/src/components/Issuance/BulkIssuance.tsx +++ b/src/components/Issuance/BulkIssuance.tsx @@ -91,7 +91,7 @@ const BulkIssuance = () => { } setSchemaType(currentSchemaType); - if (currentSchemaType && orgId && isAllSchemaSelectedFlag =='false') { + if ((currentSchemaType === SchemaTypes.schema_INDY && isAllSchemaSelectedFlag === 'true') || (currentSchemaType && orgId && isAllSchemaSelectedFlag =='false')) { const response = await getSchemaCredDef(currentSchemaType); const { data } = response as AxiosResponse; @@ -117,6 +117,7 @@ const BulkIssuance = () => { schemaAttributes: schemaAttributes && typeof schemaAttributes === "string" && JSON.parse(schemaAttributes) }), ); + setCredentialOptionsData(options); } else { setUploadMessage({message: response as string, type: "failure"}); @@ -126,7 +127,10 @@ const BulkIssuance = () => { setLoading(false); } - if (currentSchemaType && orgId &&isAllSchemaSelectedFlag =='true') { + + else if (currentSchemaType === SchemaTypes.schema_W3C && orgId && isAllSchemaSelectedFlag === 'true') { + + const response = await getAllSchemas(schemaListAPIParameters,currentSchemaType); const { data } = response as AxiosResponse; diff --git a/src/components/Issuance/EmailIssuance.tsx b/src/components/Issuance/EmailIssuance.tsx index fab81522d..8841e5a0a 100644 --- a/src/components/Issuance/EmailIssuance.tsx +++ b/src/components/Issuance/EmailIssuance.tsx @@ -91,44 +91,45 @@ const EmailIssuance = () => { setSchemaType(currentSchemaType); - if (currentSchemaType && orgId && allSchemaSelectedFlag === 'false') { - - const response = await getSchemaCredDef(currentSchemaType); - const { data } = response as AxiosResponse; + if((currentSchemaType === SchemaTypes.schema_INDY && orgId && allSchemaSelectedFlag === 'true') || (currentSchemaType && allSchemaSelectedFlag === 'false')){ - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const credentialDefs = data.data; - - const options = credentialDefs.map(({ - schemaName, - schemaVersion, - credentialDefinition, - credentialDefinitionId, - schemaIdentifier, - schemaAttributes - } : ICredentials) => ({ - value: schemaType===SchemaTypes.schema_INDY ? credentialDefinitionId : schemaVersion, - label: `${schemaName} [${schemaVersion}]${currentSchemaType === SchemaTypes.schema_INDY ? ` - (${credentialDefinition})` : ''}`, - schemaName: schemaName, - schemaVersion: schemaVersion, - credentialDefinition: credentialDefinition, - schemaIdentifier: schemaIdentifier, - credentialDefinitionId: credentialDefinitionId, - schemaAttributes: - schemaAttributes && - typeof schemaAttributes === 'string' && - JSON.parse(schemaAttributes), - })); - setCredentialOptions(options); - } else { - setSuccess(null); - setFailure(null); - } - setLoading(false); + const response = await getSchemaCredDef(currentSchemaType); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const credentialDefs = data.data; + + const options = credentialDefs.map(({ + schemaName, + schemaVersion, + credentialDefinition, + credentialDefinitionId, + schemaIdentifier, + schemaAttributes + } : ICredentials) => ({ + value: schemaType===SchemaTypes.schema_INDY ? credentialDefinitionId : schemaVersion, + label: `${schemaName} [${schemaVersion}]${currentSchemaType === SchemaTypes.schema_INDY ? ` - (${credentialDefinition})` : ''}`, + schemaName: schemaName, + schemaVersion: schemaVersion, + credentialDefinition: credentialDefinition, + schemaIdentifier: schemaIdentifier, + credentialDefinitionId: credentialDefinitionId, + schemaAttributes: + schemaAttributes && + typeof schemaAttributes === 'string' && + JSON.parse(schemaAttributes), + })); + + setCredentialOptions(options); + } else { + setSuccess(null); + setFailure(null); + } + setLoading(false); } + - if (currentSchemaType === SchemaTypes.schema_W3C && orgId && allSchemaSelectedFlag === 'true') { - + else if (currentSchemaType === SchemaTypes.schema_W3C && orgId && allSchemaSelectedFlag === 'true') { const response = await getAllSchemas(schemaListAPIParameter,currentSchemaType); const { data } = response as AxiosResponse; diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index c93b39ede..2bdf2ac0a 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -16,12 +16,12 @@ import RoleViewButton from '../../RoleViewButton'; import SchemaCard from '../../../commonComponents/SchemaCard'; import type { IW3cSchemaDetails, SchemaDetails } from '../../Verification/interface'; import SearchInput from '../../SearchInput'; -import { getFromLocalStorage, setToLocalStorage } from '../../../api/Auth'; +import { getFromLocalStorage, removeFromLocalStorage, setToLocalStorage } from '../../../api/Auth'; import { pathRoutes } from '../../../config/pathRoutes'; import { getOrganizationById } from '../../../api/organization'; import { checkEcosystem } from '../../../config/ecosystem'; import type { ICheckEcosystem } from '../../../config/ecosystem'; - +import Select, { type SingleValue, type ActionMeta } from 'react-select'; import { Create, SchemaEndorsement } from '../../Issuance/Constant'; import { DidMethod, SchemaType, SchemaTypes } from '../../../common/enums'; @@ -59,6 +59,8 @@ const SchemaList = (props: { const [searchValue, setSearchValue] = useState(''); const [schemaType, setSchemaType] = useState(''); + const [defaultDropdownValue]= useState([`Organization's schema`,'All schemas']); + const[selectedValue,setSelectedValue]=useState(defaultDropdownValue[0]) const [w3cSchema,setW3CSchema]= useState(false); const [isNoLedger,setisNoLedger]= useState(false); @@ -204,10 +206,13 @@ const SchemaList = (props: { }; - - const options = ['All schemas']; - const handleFilter = async (e: React.ChangeEvent) => { + + setSchemaListAPIParameter((prevState) => ({ + ...prevState, + page: 1, + })); + setSelectedValue(e.target.value) console.log('Handle filter', e.target.value); if (e.target.value === 'All schemas') { setAllSchemaFlag(true); @@ -248,7 +253,12 @@ const SchemaList = (props: { setLoading(false); }; + useEffect(() => { + setSelectedValue(defaultDropdownValue[0]) + }, []); + useEffect(() => { + fetchOrganizationDetails(); (async () => { try { @@ -291,12 +301,11 @@ const SchemaList = (props: { - - { - ecosystemOptions && ecosystemOptions?.map(item => ( - - )) - } - - } - - Go to Dashboard - -
    - -
    - ) : !ecosystemDetails && loading ? ( -
    - -
    - ) : - - - } /> - } - - ) -} - -export default EcosystemProfileCard diff --git a/src/commonComponents/EndorsementTooltip.tsx b/src/commonComponents/EndorsementTooltip.tsx deleted file mode 100644 index cff62d726..000000000 --- a/src/commonComponents/EndorsementTooltip.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { Tooltip } from 'flowbite-react'; -import React from 'react' - -export default function EndorsementTooltip() { - - const tooltipData = [ - { - outerClass: 'hidden sm:block dark:hidden', - placement: 'top-start', - style: 'light', - content: ( - - ), - }, - { - outerClass: 'block sm:hidden dark:hidden', - placement: '', - style: 'light', - content: ( - - ), - }, - { - outerClass: 'hidden dark:block', - placement: 'top-start', - style: 'dark', - content: ( - - ), - }, - ]; - - return ( -
    - - {tooltipData.map((item) => ( -
    - - - - - - -
    - ))} -
    - ) -} diff --git a/src/commonComponents/StatusTabletTag.tsx b/src/commonComponents/StatusTabletTag.tsx deleted file mode 100644 index a8335fe76..000000000 --- a/src/commonComponents/StatusTabletTag.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { EndorsementStatus } from "../common/enums" - -interface IStatusTabletTag { - status: string -} - -const StatusTabletTag = ({ status }: IStatusTabletTag) => { - const color = () => { - switch (true) { - case status === EndorsementStatus.signed: - return { - style: `bg-[#fca20033] text-[#FCA200]`, - title: "Endorsed" - } - case status === EndorsementStatus.rejected: - return { - style: `bg-[#FFE4E4] text-[#EA5455]`, - title: "Declined" - } - case status === EndorsementStatus.requested: - return { - style: `bg-[#1f4ead33] text-[#1F4EAD]`, - title: "Requested" - } - case status === EndorsementStatus.submited: - return { - style: `bg-[#70ffa01a] text-[#28C76F]`, - title: "Submitted" - } - default: - return { - style: `bg-[#FFE4E4] text-[#EA5455]`, - title: "NA" - } - } - } - - const { style, title } = color() - - return ( -
    - {title} -
    - ) -} - -export default StatusTabletTag \ No newline at end of file diff --git a/src/components/AddOrganizationInEcosystem.tsx b/src/components/AddOrganizationInEcosystem.tsx deleted file mode 100644 index 44cb30a11..000000000 --- a/src/components/AddOrganizationInEcosystem.tsx +++ /dev/null @@ -1,435 +0,0 @@ - -import type { AxiosResponse } from 'axios'; -import { useEffect, useState } from 'react'; -import type { ChangeEvent } from 'react'; -import type { TableData } from '../commonComponents/datatable/interface'; -import { apiStatusCodes, storageKeys } from '../config/CommonConstant'; -import { AlertComponent } from './AlertComponent'; -import BreadCrumbs from './BreadCrumbs'; -import { getFromLocalStorage, removeFromLocalStorage, setToLocalStorage } from '../api/Auth'; -import SortDataTable from '../commonComponents/datatable/SortDataTable'; -import { getOrganizations } from '../api/organization'; -import CustomAvatar from '../components/Avatar'; - -import type { Organisation } from '../components/organization/interfaces'; -import { Roles } from '../utils/enums/roles'; -import { Button } from 'flowbite-react'; -import { addOrganizationInEcosystem } from '../api/ecosystem'; -import { pathRoutes } from '../config/pathRoutes'; - - -const initialPageState = { - page: 1, - search: '', - sortingOrder: 'desc', - pageSize: 10, - role: Roles.OWNER -}; - -interface IErrorOrg { - id: string; - error: string; -} - -interface IErrorResponse { - statusCode: number; - message: string; - data?: { - orgId: string; - } - error?: string; -} - -interface ICurrentPage { - page: number; - pageSize: number; - search: string; - role: string; -} - -const AddOrganizationInEcosystem = () => { - const [listAPIParameter, setListAPIParameter] = useState(initialPageState); - const [errorList, setErrorList] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [success, setSuccess] = useState(null); - const [localOrgs, setLocalOrgs] = useState([]); - const [pageInfo, setPageInfo] = useState({ - totalItem: 0, - nextPage: 0, - lastPage: 0, - }); - const [totalPages, setTotalPages] = useState(0); - const [loader, setLoader] = useState(false); - const [organizationsList, setOrganizationsList] = useState | null>(null); - const [tableData, setTableData] = useState([]) - - const selectOrganization = async (item: Organisation, checked: boolean) => { - try { - const index = localOrgs?.length > 0 ? localOrgs.findIndex(ele => ele === item.id) : -1 - - if (index === -1) { - setLocalOrgs((prev: string[]) => [...prev, item.id]) - } else { - const updateLocalOrgs = [...localOrgs] - if (!checked) { - updateLocalOrgs.splice(index, 1); - } - setLocalOrgs(updateLocalOrgs) - } - } catch (error) { - throw new Error(`SELECTED ORGANIZATION:::${error}`); - } - } - - const generateTable = async (organizationsList: Organisation[] | null) => { - const id = await getFromLocalStorage(storageKeys.ECOSYSTEM_ID); - const connections = organizationsList && organizationsList?.length > 0 && organizationsList?.map((ele: Organisation) => { - const isChecked = localOrgs.includes(ele.id) - const alreadyAdded = ele.ecosystemOrgs?.some(item => item.ecosystemId === id) - const title = alreadyAdded ? "Already exists in the ecosystem" : "" - const error = errorList.find(item => item.id === ele.id)?.error || ele.error; - - return { - data: [ - { - data: ( -
    - ) => { - const inputElement = event.target as HTMLInputElement; - - const updateOrgList: Organisation[] = organizationsList?.map(item => { - if (item.id === ele.id) { - selectOrganization(item, inputElement.checked) - return { - ...item, - checked: inputElement.checked - } - } - return item - }) - setOrganizationsList(updateOrgList) - }} - disabled={alreadyAdded} - defaultChecked={(ele.checked || isChecked) && !alreadyAdded} - className={`w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded-lg dark:ring-offset-gray-800 dark:bg-gray-700 dark:border-gray-600 disabled:opacity-100 ${alreadyAdded ? "cursor-not-allowed" : "cursor-pointer"}`} - /> -
    - ), - }, - { - data: ( -
    -
    - {(ele?.logoUrl) ? - : - } -
    -
    - {ele.name} -
    -
    - ) - }, - { data: (
    {ele.id}
    ) }, - { - data: ( -
    - { - ele?.roles?.length > 0 && ele?.roles?.map(item => ( - - {item} - - )) - } -
    - ), - }, - { - data: ( -
    - { -
    {error || "-"}
    - } -
    - ), - } - ], - }; - }); - setTableData(connections); - } - - useEffect(() => { - generateTable(organizationsList); - }, [organizationsList, localOrgs]) - - const getOwnerOrganizations = async (currentPage: ICurrentPage) => { - setLoading(true); - const response = await getOrganizations( - currentPage.page, - currentPage.pageSize, - currentPage.search, - currentPage.role - ); - const { data } = response as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const totalPages = data?.data?.totalPages; - const orgList = data?.data?.organizations.map((userOrg: Organisation) => { - const roles: string[] = userOrg.userOrgRoles.map( - (role) => role.orgRole.name, - ); - userOrg.roles = roles; - return userOrg; - }); - setPageInfo({ - ...pageInfo, - totalItem: data?.data?.totalCount, - lastPage: data?.data?.totalPages, - nextPage: listAPIParameter?.page + 1 - }) - setOrganizationsList(orgList); - setTotalPages(totalPages); - } else { - setError(response as string); - } - setLoading(false); - }; - - const header = [ - { columnName: '', width: 'w-0.5' }, - { columnName: 'Organization' }, - { columnName: 'Id' }, - { columnName: 'Role(s)' }, - { columnName: 'Error' }, - ]; - - //onChange of Search input text - const searchInputChange = (e: ChangeEvent) => { - setListAPIParameter({ - ...listAPIParameter, - search: e.target.value, - page: 1, - }); - }; - - const refreshPage = () => { - setLocalOrgs([]); - getOwnerOrganizations(listAPIParameter); - }; - - const updateLocalOrgs = async () => { - const res = await getFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM) - const selectedOrg = res ? JSON.parse(res) : [] - setLocalOrgs(selectedOrg); - - const err = await getFromLocalStorage(storageKeys.ERROR_ORG_IN_ECOSYSTEM) - const errOrgs = err ? JSON.parse(err) : [] - setErrorList(errOrgs); - } - - const handleAddOrganization = async () => { - const orgId = (await getFromLocalStorage(storageKeys.ORG_ID)) || ''; - const ecosystemId = - (await getFromLocalStorage(storageKeys.ECOSYSTEM_ID)) || ''; - setLoader(true); - try { - const response = await addOrganizationInEcosystem( - localOrgs, - ecosystemId, - orgId, - ); - const { data } = response as AxiosResponse; - setLoader(false); - setLocalOrgs([]); - setErrorList([]); - setOrganizationsList( - (prevState) => - prevState?.map((org) => ({ ...org, checked: false, error: '' })) || - [], - ); - - switch (data?.statusCode) { - case apiStatusCodes.API_STATUS_CREATED: - await removeFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM); - setSuccess(data.message); - setTimeout(() => { - window.location.href = pathRoutes.ecosystem.dashboard; - }, 1000); - break; - - case apiStatusCodes.API_STATUS_PARTIALLY_COMPLETED: - await removeFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM); - const errors = data?.data?.filter( - (item: IErrorResponse) => - item.statusCode !== apiStatusCodes.API_STATUS_CREATED, - ); - const errorData = errors.map((item: IErrorResponse) => ({ - id: item?.data?.orgId || '', - error: item.message, - })); - await setToLocalStorage( - storageKeys.ERROR_ORG_IN_ECOSYSTEM, - JSON.stringify(errorData), - ); - setErrorList(errorData); - setLocalOrgs([]); - - const updateWithError = - organizationsList && organizationsList?.length > 0 - ? organizationsList?.map((item) => ({ - ...item, - error: - errors?.find( - (ele: IErrorResponse) => ele?.data?.orgId === item.id, - )?.message || '', - checked: false - })) - : []; - setSuccess(data?.message); - setOrganizationsList(updateWithError); - setErrorList([]); - break; - default: - setError((response as string) || data?.message); - setErrorList([]); - setLocalOrgs([]); - setOrganizationsList( - (prevState) => - prevState?.map((org) => ({ - ...org, - checked: false, - error: '', - })) || [], - ); - - break; - } - } catch (error) { - setError(error.message as string); - setLoader(false); - setLocalOrgs([]); - setErrorList([]); - setOrganizationsList( - (prevState) => - prevState?.map((org) => ({ ...org, checked: false, error: '' })) || - [], - ); - } - }; - - useEffect(() => { - const clearLocalStorage = async () => { - await removeFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM); - await removeFromLocalStorage(storageKeys.ERROR_ORG_IN_ECOSYSTEM); - }; - clearLocalStorage(); - refreshPage(); - - }, []); - - useEffect(() => { - getOwnerOrganizations(listAPIParameter); - updateLocalOrgs() - }, [listAPIParameter]); - - useEffect(() => { - updateLocalOrgs(); - (async () => { - await removeFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM); - await removeFromLocalStorage(storageKeys.ERROR_ORG_IN_ECOSYSTEM); - })() - }, []) - - - useEffect(() => { - (async () => { - await setToLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM, JSON.stringify(localOrgs)) - })() - }, [localOrgs]) - - return ( -
    - -
    -

    - Add Organizations -

    -
    - {(error || success) && ( - { - setError(null); - setSuccess(null); - }} - /> - )} - { - setListAPIParameter((prevState) => ({ - ...prevState, - page, - })); - }} - totalPages={totalPages} - pageInfo={pageInfo} - isHeader={true} - isSearch={true} - isRefresh={true} - isSort={false} - message={'No Organizations'} - discription={"You don't have any Organization to add"} - itemPerPage={listAPIParameter.pageSize} - > -
    - -
    -
    - ); -}; - -export default AddOrganizationInEcosystem; diff --git a/src/components/Authentication/SignInUserPasskey.tsx b/src/components/Authentication/SignInUserPasskey.tsx index acb307eff..67ea308cc 100644 --- a/src/components/Authentication/SignInUserPasskey.tsx +++ b/src/components/Authentication/SignInUserPasskey.tsx @@ -79,9 +79,14 @@ const SignInUserPasskey = (signInUserProps: signInUserProps) => { (element: { orgRole: { name: string } }) => permissionArray.push(element?.orgRole?.name), ); - const { id, profileImg, firstName, email, enableEcosystem, multiEcosystemSupport } = data?.data || {} + const { id, + profileImg, + firstName, + email, + } = data?.data || {} const userProfile = { - id, profileImg, firstName, email, enableEcosystem, multiEcosystemSupport + id, profileImg, firstName, email, + } await setToLocalStorage(storageKeys.PERMISSIONS, permissionArray); await setToLocalStorage(storageKeys.USER_PROFILE, userProfile); diff --git a/src/components/Authentication/SignInUserPassword.tsx b/src/components/Authentication/SignInUserPassword.tsx index e9440ec2a..dec3d7768 100644 --- a/src/components/Authentication/SignInUserPassword.tsx +++ b/src/components/Authentication/SignInUserPassword.tsx @@ -56,9 +56,12 @@ const SignInUserPassword = (signInUserProps: SignInUser3Props) => { (element: { orgRole: { name: string } }) => permissionArray.push(element?.orgRole?.name), ); - const { id, profileImg, firstName, email, enableEcosystem, multiEcosystemSupport } = data?.data || {} + const { id, profileImg, firstName, email, + + } = data?.data || {} const userProfile = { - id, profileImg, firstName, email, enableEcosystem, multiEcosystemSupport + id, profileImg, firstName, email, + } await setToLocalStorage(storageKeys.USER_PROFILE, userProfile); await setToLocalStorage(storageKeys.USER_EMAIL, data?.data?.email); diff --git a/src/components/CreateEcosystemOrgModal/index.tsx b/src/components/CreateOrgModal/index.tsx similarity index 77% rename from src/components/CreateEcosystemOrgModal/index.tsx rename to src/components/CreateOrgModal/index.tsx index 09558b114..dd3c870f9 100644 --- a/src/components/CreateEcosystemOrgModal/index.tsx +++ b/src/components/CreateOrgModal/index.tsx @@ -11,23 +11,18 @@ import { apiStatusCodes, imageSizeAccepted, storageKeys, -} from '../../config/CommonConstant'; -import { calculateSize, dataURItoBlob } from '../../utils/CompressImage'; +} from '../../config/CommonConstant.js'; +import { calculateSize, dataURItoBlob } from '../../utils/CompressImage.js'; import React, { useEffect, useState } from 'react'; -import { AlertComponent } from '../AlertComponent'; +import { AlertComponent } from '../AlertComponent/index.js'; import type { AxiosResponse } from 'axios'; import { asset } from '../../lib/data.js'; -import { createOrganization } from '../../api/organization'; -import { getFromLocalStorage } from '../../api/Auth'; -import { createEcosystems } from '../../api/ecosystem'; -import { getOrgDetails } from '../../config/ecosystem'; -import EndorsementTooltip from '../../commonComponents/EndorsementTooltip'; -import { processImage } from '../../utils/processImage'; +import { createOrganization } from '../../api/organization.js'; +import { processImage } from '../../utils/processImage.js'; interface Values { name: string; description: string; - autoEndorsement:boolean; } interface ILogoImage { @@ -35,11 +30,6 @@ interface ILogoImage { imagePreviewUrl: string | ArrayBuffer | null | File; fileName: string; } -interface EcoValues { - name: string; - description: string; - autoEndorsement: boolean; -} interface IProps { openModal: boolean; @@ -48,7 +38,7 @@ interface IProps { setOpenModal: (flag: boolean) => void; } -const CreateEcosystemOrgModal = (props: IProps) => { +const CreateOrgModal = (props: IProps) => { const [logoImage, setLogoImage] = useState({ logoFile: '', imagePreviewUrl: '', @@ -59,18 +49,15 @@ const CreateEcosystemOrgModal = (props: IProps) => { const [formData, setFormData] = useState({ name: '', description: '', - autoEndorsement: false, }); const [errMsg, setErrMsg] = useState(null); const [imgError, setImgError] = useState(''); - const [autoEndorse, setautoEndorse] = useState(false); useEffect(() => { setFormData({ name: '', description: '', - autoEndorsement: false, }); setLogoImage({ ...logoImage, @@ -128,10 +115,6 @@ const CreateEcosystemOrgModal = (props: IProps) => { }; }; - const isEmpty = (object: any): boolean => { - return true; - }; - const handleImageChange = (event: any): void => { setImgError(''); processImage(event, (result, error) => { @@ -172,46 +155,14 @@ const CreateEcosystemOrgModal = (props: IProps) => { setErrMsg(resCreateOrg as string); } }; - const submitCreateEcosystem = async (values: EcoValues) => { - try { - setLoading(true); - const orgDetails = await getOrgDetails(); - const user_data = JSON.parse( - await getFromLocalStorage(storageKeys.USER_PROFILE), - ); - const ecoData = { - name: values.name, - description: values.description, - logo: (logoImage?.imagePreviewUrl as string) || '', - tags: '', - userId: user_data?.id, - autoEndorsement: autoEndorse, - }; - const resCreateEco = await createEcosystems(ecoData); - - const { data } = resCreateEco as AxiosResponse; - setLoading(false); - - if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { - if (props.setMessage) { - props.setMessage(data?.message); - } - props.setOpenModal(false); - } else { - setErrMsg(resCreateEco as string); - } - } catch (error) { - console.error('An error occurred:', error); - setLoading(false); - } - }; + - const submit = (values: EcoValues | Values) => { + const submit = (values: + + Values) => { if (props.isorgModal) { submitCreateOrganization(values); - } else { - submitCreateEcosystem(values); - } + } }; const orgErrorMsg = { @@ -228,21 +179,8 @@ const CreateEcosystemOrgModal = (props: IProps) => { .required('Description is required'), }; - const ecoErrorMsg = { - name: yup - .string() - .min(2, 'Ecosystem name must be at least 2 characters') - .max(50, 'Ecosystem name must be at most 50 characters') - .required('Ecosystem name is required') - .trim(), - description: yup - .string() - .min(2, 'Description must be at least 2 characters') - .max(255, 'Description must be at most 255 characters') - .required('Description is required'), - }; - const renderEcosystemModal = () => { - const popupName = props.isorgModal ? 'Organization' : 'Ecosystem'; + const renderOrganizationModal = () => { + const popupName = 'Organization' return ( { setFormData({ name: ' ', description: ' ', - autoEndorsement: false, }); props.setOpenModal(false); setImgError(' ') @@ -279,7 +216,7 @@ const CreateEcosystemOrgModal = (props: IProps) => { validationSchema={yup .object() .shape( - props.isorgModal ? { ...orgErrorMsg } : { ...ecoErrorMsg }, + orgErrorMsg, )} validateOnBlur validateOnChange @@ -372,9 +309,8 @@ const CreateEcosystemOrgModal = (props: IProps) => { if (value.length > 50) { formikHandlers.setFieldError( 'name', - props.isorgModal - ? 'Organization name must be at most 50 characters' - : 'Ecosystem name must be at most 50 characters', + 'Organization name must be at most 50 characters' + ); } }} @@ -423,43 +359,6 @@ const CreateEcosystemOrgModal = (props: IProps) => { )}
    - - {!props.isorgModal && ( -
    -
    -
    - -
    - setautoEndorse(false)} - /> - - Sign - -
    - -
    - setautoEndorse(true)} - /> - - Sign and Submit - -
    -
    - )}
    - - {dropdownOpen && isAccess ? ( - ( - - )} - dismissOnClick={true} - > - { - EditEcosystemOrgModal(); - }} - > -
    Edit Ecosystem
    -
    -
    - ) : ( - - )} -
    -
    -
    -
    - ) : ( -
    -
    -
    - {ecosystemDetails?.logoUrl ? ( - - ) : ( - - )} -
    -
    -
    -

    - {ecosystemDetails?.name} -

    -

    - {ecosystemDetails?.description} -

    -
    -
    - - Ecosystem Owner - - :{' '} - - {leadOrg} - -
    -
    - - Ecosystem Lead - {' '} - : - - {leadOrg} - -
    -
    - - Joined since - {' '} - : - - - {dateConversion(ecosystemDetails.joinedDate || '')} - - -
    -
    -
    - - Endorsement Flow - {' '} - : - - {ecosystemDetails.autoEndorsement - ? ' Sign and Submit' - : ' Sign'} - -
    -
    -
    - Role: {' '} - -
    -
    - )} - -
    -
    - - - (window.location.href = pathRoutes.ecosystem.endorsements) - } - /> -
    -
    -
    - -
    - - { - setSuccess(value); - }} - isOrganization={false} - onEditSuccess={handleEditModalClose} - entityData={ecosystemDetails} - /> -
    - ) : ( -
    - {!ecosystemDetails && loading ? ( -
    - -
    - ) : ( -
    -
    - { - setSuccess(value); - if (isOrgModal && value) { - setTimeout(() => { - window.location.reload(); - }, 2000); - } else { - getDashboardData(); - } - }} - isorgModal={isOrgModal} - /> - - - - } - onClick={() => { - setIsOrgModal(Boolean(!orgId)); - createEcosystemModel(); - }} - /> -
    -
    - )} -
    - )} -
    - ); -}; - -export default Dashboard; diff --git a/src/components/Ecosystem/EcosystemList.tsx b/src/components/Ecosystem/EcosystemList.tsx deleted file mode 100644 index 86c48f8c3..000000000 --- a/src/components/Ecosystem/EcosystemList.tsx +++ /dev/null @@ -1,376 +0,0 @@ - -import { Card, Pagination } from 'flowbite-react'; -import { useEffect, useState } from 'react'; -import type { ChangeEvent } from 'react'; -import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; - -import { AlertComponent } from '../AlertComponent'; -import type { AxiosResponse } from 'axios'; -import BreadCrumbs from '../BreadCrumbs'; -import CustomAvatar from '../Avatar/index'; -import { Features } from '../../utils/enums/features'; -import RoleViewButton from '../RoleViewButton'; -import SearchInput from '../SearchInput'; -import { pathRoutes } from '../../config/pathRoutes'; -import { - getFromLocalStorage, - removeFromLocalStorage, - setToLocalStorage, -} from '../../api/Auth'; -import { EmptyListMessage } from '../EmptyListComponent'; -import CustomSpinner from '../CustomSpinner'; -import CreateEcosystemOrgModal from '../CreateEcosystemOrgModal'; -import { getEcosystems } from '../../api/ecosystem'; -import type { IEcosystem } from './interfaces'; -import { checkEcosystem, getOrgDetails } from '../../config/ecosystem'; -import type { ICheckEcosystem} from '../../config/ecosystem'; - -const initialPageState = { - pageNumber: 1, - pageSize: 9, - total: 100, -}; - -const EcosystemList = () => { - const [openModal, setOpenModal] = useState(false); - const [loading, setLoading] = useState(true); - const [isWalletSpinUp, setIsWalletSpinUp] = useState(false); - const [selectedOrgId, setSelectedOrgId] = useState(""); - const [message, setMessage] = useState(null); - const [error, setError] = useState(null); - const [currentPage, setCurrentPage] = useState(initialPageState); - const onPageChange = (page: number) => { - setCurrentPage({ - ...currentPage, - pageNumber: page, - }); - }; - const [searchText, setSearchText] = useState(''); - - const [ecosystemList, setEcosystemList] = useState | null>( - null, - ); - const [isEcosystemData, setIsEcosystemData] = useState(); - - const createOrganizationModel = () => { - setOpenModal(true); - }; - - const fetchEcosystems = async () => { - const id = await getFromLocalStorage(storageKeys.ORG_ID); - setSelectedOrgId(id) - const orgData = await getOrgDetails(); - setIsWalletSpinUp(Boolean(orgData?.orgDid)) - setLoading(true); - if (id) { - const response = await getEcosystems( - id, - currentPage.pageNumber, - currentPage.pageSize, - searchText, - ); - const { data } = response as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const ecosystemData = data?.data.ecosystemList; - if (ecosystemData) { - - setEcosystemList(ecosystemData); - } else { - await removeFromLocalStorage(storageKeys.ECOSYSTEM_ID); - setError(response as string); - } - } else { - await removeFromLocalStorage(storageKeys.ECOSYSTEM_ID); - setError(response as string); - } - } - setLoading(false); - }; - - //This useEffect is called when the searchText changes - useEffect(() => { - let getData: NodeJS.Timeout; - - if (searchText.length >= 1) { - getData = setTimeout(() => { - fetchEcosystems(); - }, 1000); - return () => clearTimeout(getData); - } else { - fetchEcosystems(); - } - - return () => clearTimeout(getData); - }, [searchText, openModal, currentPage.pageNumber]); - - useEffect(() => { - const queryParameters = new URLSearchParams(window?.location?.search); - const isModel = queryParameters.get('orgModal') === 'true' || false; - - if (isModel) { - setOpenModal(true); - } - - (async () => { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemData(data); - })(); - }, []); - - //onCHnage of Search input text - const searchInputChange = (e: ChangeEvent) => { - setSearchText(e.target.value); - }; - - const redirectOrgDashboard = async ( - ecosystemId: string, - ecosystemRole: string, - ) => { - await setToLocalStorage(storageKeys.ECOSYSTEM_ID, ecosystemId); - await setToLocalStorage(storageKeys.ECOSYSTEM_ROLE, ecosystemRole); - window.location.href = pathRoutes.ecosystem.dashboard; - }; - - const emptyListMessage = (isWalletSpinUp: boolean, selectedOrgId: boolean, ecosystemList: boolean) => { - try { - switch (true) { - case !isWalletSpinUp: - return window.location.assign(pathRoutes.organizations.dashboard)} - svgComponent={ - - - - } - /> - case !selectedOrgId: - return - - - } - /> - case ecosystemList: - return - - - } - /> - default: - break; - } - } catch (error) { - console.error(error) - } - } - - const isEcosystemList = Boolean(ecosystemList && ecosystemList?.length > 0); - const showCreateButton = Boolean( - isEcosystemList && - (isEcosystemData?.isMultiEcosystem || isEcosystemData?.isEcosystemLead), - ); - - return ( -
    -
    - -
    -
    -

    - Ecosystems -

    -
    - -
    - {showCreateButton && ( - - - - -
    - } - onClickEvent={createOrganizationModel} - /> - )} -
    -
    -
    - - - { - setMessage(null); - setError(null); - }} - /> - - {loading ? ( -
    - -
    - ) : isEcosystemList ? ( -
    - {ecosystemList?.map((item) => { - const role = - (item?.ecosystemOrgs && - item?.ecosystemOrgs.length > 0 && - item?.ecosystemOrgs[0]?.ecosystemRole?.name) || - ''; - return ( - redirectOrgDashboard(item.id, role)} - className="transform transition duration-500 hover:scale-105 hover:bg-gray-50 cursor-pointer overflow-hidden max-w-full max-h-fit overflow-auto" - > -
    - {item.logoUrl ? ( - - ) : ( - - )} - -
    -
    - {item.name} -
    -

    - {item.description} -

    -
    -
      -
    • -
      -
      - Role: - {item?.ecosystemOrgs && - item?.ecosystemOrgs?.length > 0 && - item?.ecosystemOrgs[0].ecosystemRole && - item?.ecosystemOrgs[0]?.ecosystemRole - ?.name && ( - - { - item?.ecosystemOrgs[0]?.ecosystemRole - ?.name - } - - )} -
      -
      -
      -

      - Endorsement Flow:{' '} - {item.autoEndorsement ? 'Sign and Submit' : 'Sign'} -

      -
      -
    • -
    -
    -
    -
    -
    - ); - })} -
    - - ) : ( - <> - {emptyListMessage(isWalletSpinUp, Boolean(selectedOrgId), Boolean(ecosystemList && ecosystemList?.length === 0))} - - )} - -
    - {isEcosystemList && ecosystemList && ecosystemList?.length > 10 && ( - - )} -
    - { - setMessage(value); - if (value) { - setTimeout(() => { - window.location.reload(); - }, 2000); - } else { - fetchEcosystems(); - } - }} - isorgModal={false} - /> -
    -
    -
    - - ) -} -export default EcosystemList; diff --git a/src/components/Ecosystem/EcosystemSidebarOption.tsx b/src/components/Ecosystem/EcosystemSidebarOption.tsx index c6e1f950a..7da52e718 100644 --- a/src/components/Ecosystem/EcosystemSidebarOption.tsx +++ b/src/components/Ecosystem/EcosystemSidebarOption.tsx @@ -1,24 +1,58 @@ import { useEffect, useState } from 'react'; -import { checkEcosystem } from '../../config/ecosystem.ts'; -import type { ICheckEcosystem } from '../../config/ecosystem.ts'; -import { pathRoutes } from '../../config/pathRoutes.ts'; +import { getOrganizations } from '../../api/organization'; +import type { AxiosResponse } from 'axios'; +import { apiStatusCodes } from '../../config/CommonConstant'; +import type { Organisation } from '../organization/interfaces'; +import { pathRoutes } from '../../config/pathRoutes'; +import { envConfig } from '../../config/envConfig'; + + +const initialPageState = { + pageNumber: 1, + pageSize: 10, + total: 0, +}; + const EcosystemSidebarOption = () => { const [isEcosystemEnabled, setIsEcosystemEnabled] = useState(false); + //TODO: Logic to disable ecosystem when organzaition and wallet is not created + + // const [currentPage, setCurrentPage] = useState(initialPageState); + // const [orgCount, setOrgCount] = useState(0); + // const [error, setError] = useState(null); + + // const getAllOrganizations = async () => { + // const response = await getOrganizations( + // currentPage.pageNumber, + // currentPage.pageSize, + // '', + // ); + // const { data } = response as AxiosResponse; + // if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + // setOrgCount(data?.data?.totalCount); + // } else { + // setError(response as string); + // } + // }; useEffect(() => { const checkEcosystemData = async () => { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemEnabled(data.isEnabledEcosystem); + setIsEcosystemEnabled(true); }; checkEcosystemData(); + //TODO: Logic to disable ecosystem when organzaition and wallet is not created + // getAllOrganizations(); }, []); - if (isEcosystemEnabled) { + + if (isEcosystemEnabled + // && orgCount + ) { return (
  • diff --git a/src/components/Ecosystem/Endorsement/EndorsementCard.tsx b/src/components/Ecosystem/Endorsement/EndorsementCard.tsx deleted file mode 100644 index 7a50f3af6..000000000 --- a/src/components/Ecosystem/Endorsement/EndorsementCard.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { Card } from 'flowbite-react'; -import { dateConversion } from '../../../utils/DateConversion'; -import DateTooltip from '../../../components/Tooltip'; -import { EndorsementStatus, EndorsementType } from '../../../common/enums'; -import StatusTabletTag from '../../../commonComponents/StatusTabletTag'; -import { checkEcosystem } from '../../../config/ecosystem'; -import type { ICheckEcosystem} from '../../../config/ecosystem'; -import { useEffect, useState } from 'react'; - -interface IProps { - data: any, - fromEndorsementList?: boolean, - cardTransitionDisabled?: boolean - allAttributes?: boolean - onClickCallback?: (data: any) => void; -} - -interface IAttributeData { - data: string[], - sliced: boolean -} - -const EndorsementCard = ({ fromEndorsementList, data, onClickCallback, cardTransitionDisabled, allAttributes }: IProps) => { - const [isEcosystemLead, setIsEcosystemLead] = useState(false); - const isSchema = data?.type === EndorsementType.schema - - useEffect(() => { - const checkEcosystemData = async () => { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemLead(data.isEcosystemLead) - } - checkEcosystemData(); - }, []) - - const isSliced = (list: string[]): boolean => { - return Boolean(list && list?.length > 3) - } - - const getAttributes = (): IAttributeData | null => { - try { - switch (true) { - case isSchema: - if (requestData?.attr_names && requestData?.attr_names.length > 0) { - if (allAttributes) { - return { - data: requestData?.attr_names, - sliced: isSliced(requestData?.attr_names) - } - } - return { - data: requestData?.attr_names?.slice(0, 3), - sliced: isSliced(requestData?.attr_names) - } - } - return null - case !isSchema: - if (data?.requestBody && data?.requestBody?.schemaDetails && data?.requestBody?.schemaDetails?.attributes && data?.requestBody?.schemaDetails?.attributes.length > 0) { - if (allAttributes) { - return { - data: data?.requestBody?.schemaDetails?.attributes, - sliced: isSliced(data?.requestBody?.schemaDetails?.attributes) - } - } - return { - data: data?.requestBody?.schemaDetails?.attributes?.slice(0, 3), - sliced: isSliced(data?.requestBody?.schemaDetails?.attributes) - } - } - return null - default: - return null - } - } catch (err) { - console.log("Attribute Error::", err) - return null - } - } - - const enableAction = (!fromEndorsementList && data?.status === EndorsementStatus.signed) || Boolean(fromEndorsementList) - - const requestPayload = data?.requestPayload && JSON.parse(data?.requestPayload) - - const requestData = isSchema ? requestPayload?.operation?.data : requestPayload?.operation - const attributesData: IAttributeData | null = getAttributes() - - return ( - { - if (enableAction && onClickCallback) { - onClickCallback(data) - } - }} - id="schema-cards" - className={`${cardTransitionDisabled ? "" : "transform transition duration-500 hover:scale-105 hover:bg-gray-50 min-[w-320px]: cursor-default"} ${enableAction && "lg:cursor-pointer cursor-default"}${cardTransitionDisabled ? "cursor-default" : "lg:cursor-not-allowed cursor-default"} ${cardTransitionDisabled && "shadow-none"} m-3 h-full`} - > -
    -
    -
    - {isSchema ? requestData?.name : requestData?.tag} -
    - { - isSchema && -

    - Version: {requestData?.version} -

    - } -
    -
    -
    - - Requested: {dateConversion(data?.createDateTime)} - -
    -
    -
    -
    - { - data?.status && -
    -
    - Status: -
    -
    - -
    -
    - } -
    - {isSchema ? "Schema" : "Credential Definition"} -
    -
    - < div className="min-w-0 flex-none" > - {!isSchema && - <> -

    - Schema Name: {data?.requestBody?.schemaDetails?.name || "-"} -

    -

    - Schema Version: {data?.requestBody?.schemaDetails?.version || "-"} -

    - - } -

    - Author DID: {data?.authorDid} -

    -

    - Endorser DID: {data?.endorserDid} -

    -

    - Ledger: {data?.authorDid ? data?.authorDid?.split(":")[2] : "-"} -

    - {isEcosystemLead && -

    - Organization: {data?.ecosystemOrgs?.organisation?.name} -

    - } -
  • - {attributesData?.data && attributesData?.data?.length > 0 && -
    -
      -
    • -
      -
      - Attributes: -
      -
      - - {attributesData?.data?.map((element: string | { attributeName: string }) => { - const attribute = typeof element === "string" ? element : element?.attributeName - return ( -
      - - {attribute} - -
      - ) - })} - {!allAttributes && attributesData.sliced && ...} -
      -
      -
    • -
    -
    - } - - ) -} -export default EndorsementCard diff --git a/src/components/Ecosystem/Endorsement/EndorsementPopup.tsx b/src/components/Ecosystem/Endorsement/EndorsementPopup.tsx deleted file mode 100644 index 3846306ee..000000000 --- a/src/components/Ecosystem/Endorsement/EndorsementPopup.tsx +++ /dev/null @@ -1,294 +0,0 @@ -import { Button, Modal } from 'flowbite-react'; -import { EndorsementStatus, EndorsementType } from '../../../common/enums'; -import { - checkEcosystem, - getEcosystemId, - getOwnerAdminRole, -} from '../../../config/ecosystem'; -import type { ICheckEcosystem} from '../../../config/ecosystem'; -import EndorsementCard from './EndorsementCard'; -import React, { useEffect, useState } from 'react'; -import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; -import { - RejectEndorsementRequest, - SignEndorsementRequest, - SubmitEndorsementRequest, -} from '../../../api/ecosystem'; -import type { AxiosResponse } from 'axios'; -import { AlertComponent } from '../../AlertComponent'; -import { getFromLocalStorage } from '../../../api/Auth'; - -const EndorsementPopup = (props: { - openModal: boolean; - closeModal: () => void; - isAccepted: (flag: boolean) => void; - setMessage: (message: string) => void; - endorsementData: any; - onAlertClose: boolean; -}) => { - const [loading, setLoading] = useState(false); - const [loadingReject, setLoadingReject] = useState(false); - const [errMsg, setErrMsg] = useState(null); - const [isEcosystemData, setIsEcosystemData] = useState(); - const [isAccess, setIsAccess] = useState(false); - - const checkEcosystemData = async () => { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemData(data); - }; - const checkEcosystemAccess = async () => { - const data = await getOwnerAdminRole(); - setIsAccess(data); - }; - - useEffect(() => { - checkEcosystemData(); - checkEcosystemAccess(); - }, [props?.openModal]); - - useEffect(() => { - props.setMessage(''); - }, [props.onAlertClose]); - - const SignEndorsement = async (endorsementId: string) => { - try { - setLoading(true); - const organizationId = await getFromLocalStorage(storageKeys.ORG_ID); - const ecoId = await getEcosystemId(); - const SignEndorsementrequest = await SignEndorsementRequest( - ecoId, - organizationId, - endorsementId, - ); - - const response = SignEndorsementrequest as AxiosResponse; - if (response?.data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { - props.isAccepted(true); - props.setMessage(response?.data.message); - } else { - setErrMsg(response as unknown as string); - } - setLoading(false); - } catch (error) { - setLoading(false); - console.error('Error while Sign schema:', error); - } - }; - - const SubmitEndorsement = async (endorsementId: string) => { - try { - setLoading(true); - setLoadingReject(true); - const organizationId = await getFromLocalStorage(storageKeys.ORG_ID); - const ecoId = await getEcosystemId(); - const SubmitEndorsementrequest = await SubmitEndorsementRequest( - ecoId, - organizationId, - endorsementId, - ); - - const response = SubmitEndorsementrequest as AxiosResponse; - if (response?.data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { - props.isAccepted(true); - props.setMessage(response?.data.message); - } else { - setErrMsg(response as unknown as string); - } - setLoading(false); - } catch (error) { - setLoading(false); - setLoadingReject(false); - console.error('Error while Submit schema:', error); - } - }; - - const RejectEndorsement = async (endorsementId: string) => { - try { - setLoadingReject(true); - const organizationId = await getFromLocalStorage(storageKeys.ORG_ID); - const ecoId = await getEcosystemId(); - const RejectEndorsementrequest = await RejectEndorsementRequest( - ecoId, - organizationId, - endorsementId, - ); - - const response = RejectEndorsementrequest as AxiosResponse; - if (response?.data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - props.isAccepted(true); - props.setMessage(response?.data.message); - } else { - setErrMsg(response as unknown as string); - } - setLoadingReject(false); - } catch (error) { - setLoadingReject(false); - console.error('Error while Reject schema:', error); - } - }; - - return ( - { - props.closeModal(); - setErrMsg(null); - }} - size="xl" - > - - {isEcosystemData?.isEcosystemLead ? ( -
    - Requested{' '} - {props.endorsementData?.type === EndorsementType.credDef - ? 'Credential Definition' - : 'Schema'} -
    - ) : ( -
    - View{' '} - {props.endorsementData?.type === EndorsementType.credDef - ? 'Credential Definition' - : 'Schema'} -
    - )} -
    -
    - { - setErrMsg(null); - }} - /> -
    - - -
    -
    - {isEcosystemData?.isEcosystemLead && - props.endorsementData?.status === EndorsementStatus.requested ? ( -
    - - - -
    - ) : ( - <> - {!isEcosystemData?.isEcosystemLead && - isEcosystemData?.isEcosystemMember && - props.endorsementData?.status === EndorsementStatus.signed ? ( -
    - -
    - ) : ( -
    - -
    - )} - - )} -
    -
    -
    - ); -}; - -export default EndorsementPopup; diff --git a/src/components/Ecosystem/Endorsement/index.tsx b/src/components/Ecosystem/Endorsement/index.tsx deleted file mode 100644 index 47681e953..000000000 --- a/src/components/Ecosystem/Endorsement/index.tsx +++ /dev/null @@ -1,357 +0,0 @@ - -import { Alert, Pagination } from 'flowbite-react'; -import { useEffect, useState } from 'react'; -import type { ChangeEvent } from 'react'; -import { apiStatusCodes, itemPerPage, storageKeys } from '../../../config/CommonConstant'; -import EndorsementPopup from './EndorsementPopup'; -import type { AxiosResponse } from 'axios'; -import BreadCrumbs from '../../BreadCrumbs'; -import CustomSpinner from '../../CustomSpinner'; -import { EmptyListMessage } from '../../EmptyListComponent'; -import SearchInput from '../../SearchInput'; -import { getFromLocalStorage } from '../../../api/Auth'; -import { getOrganizationById } from '../../../api/organization'; -import { - checkEcosystem, - getEcosystemId, -} from '../../../config/ecosystem'; -import type { ICheckEcosystem} from '../../../config/ecosystem'; -import EndorsementCard from './EndorsementCard'; -import { - getEndorsementList, -} from '../../../api/ecosystem'; -import type { GetEndorsementListParameter} from '../../../api/ecosystem'; -import { EndorsementStatus, EndorsementType } from '../../../common/enums'; -import { AlertComponent } from '../../AlertComponent'; -import { Features } from '../../../utils/enums/features'; -import EcosystemProfileCard from '../../../commonComponents/EcosystemProfileCard'; -import type { IEndorsementList, ISelectedRequest } from '../interfaces'; - - -const EndorsementList = () => { - const [schemaList, setSchemaList] = useState([]); - const [schemaListErr, setSchemaListErr] = useState(''); - const [loading, setLoading] = useState(true); - const [alertClose, setAlertClose] = useState(true); - const [orgId, setOrgId] = useState(''); - const [endorsementListAPIParameter, setEndorsementListAPIParameter] = - useState({ - itemPerPage: itemPerPage, - page: 1, - search: '', - sortBy: 'id', - sortingOrder: 'desc', - type: '', - status: '', - }); - const [totalPages, setTotalPages] = useState(0); - const [walletStatus, setWalletStatus] = useState(false); - const [showPopup, setShowPopup] = useState(false); - const [selectedRequest, setSelectedRequest] = useState(); - const [isEcosystemData, setIsEcosystemData] = useState(); - const [successMessage, setSuccessMessage] = useState(null); - - const options = [ - { - name: 'Select Status', - value: '', - }, - { - name: 'Accepted', - value: EndorsementStatus.signed, - }, - { - name: 'Requested', - value: EndorsementStatus.requested, - }, - { - name: 'Rejected', - value: EndorsementStatus.rejected, - }, - { - name: 'Submitted', - value: EndorsementStatus.submited, - }, - ]; - - const typeOptions = [ - { - name: 'Select Type', - value: '', - }, - { - name: 'Schema', - value: EndorsementType.schema, - }, - { - name: 'Credential-definition', - value: EndorsementType.credDef, - }, - ]; - - const getEndorsementListData = async ( - endorsementListAPIParameter: GetEndorsementListParameter, - ) => { - try { - const organizationId = await getFromLocalStorage(storageKeys.ORG_ID); - setOrgId(organizationId); - const id = await getEcosystemId(); - if (id && organizationId) { - const endorsementList = await getEndorsementList( - endorsementListAPIParameter, - organizationId, - id, - ); - - const { data } = endorsementList as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - if (data?.data?.transactions) { - setSchemaList(data?.data?.transactions); - setTotalPages(data?.data?.totalPages); - setLoading(false); - } else { - setLoading(false); - } - } else { - setLoading(false); - } - } - } catch (error) { - console.error('Error while fetching schema list:', error); - } - }; - - const onPageChange = (page: number) => { - setEndorsementListAPIParameter({ - ...endorsementListAPIParameter, - page, - }); - }; - - useEffect(() => { - getEndorsementListData(endorsementListAPIParameter); - }, [endorsementListAPIParameter]); - - const onSearch = async ( - event: ChangeEvent, - ): Promise => { - event.preventDefault(); - setEndorsementListAPIParameter({ - ...endorsementListAPIParameter, - search: event.target.value, - }); - }; - - const requestSelectionCallback = (data: any) => { - setSelectedRequest(data); - setShowPopup(true); - }; - - const handleFilter = ( - e: React.ChangeEvent, - filter: 'typeFilter' | 'statusFilter', - ) => { - const updateFilter = endorsementListAPIParameter; - if (filter === 'typeFilter') { - updateFilter['type'] = e.target.value; - } - if (filter === 'statusFilter') { - updateFilter['status'] = e.target.value; - } - setEndorsementListAPIParameter(updateFilter); - getEndorsementListData(updateFilter); - }; - - const fetchOrganizationDetails = async () => { - setLoading(true); - const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const response = await getOrganizationById(orgId); - const { data } = response as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - if (data?.data?.org_agents && data?.data?.org_agents?.length > 0) { - setWalletStatus(true); - } - } - setLoading(false); - }; - - const hidePopup = () => { - setShowPopup(false); - }; - - useEffect(() => { - fetchOrganizationDetails(); - - const checkEcosystemData = async () => { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemData(data); - }; - checkEcosystemData(); - }, []); - - return ( -
    -
    - -
    - { - isEcosystemData?.isEnabledEcosystem && -
    - getEndorsementListData(endorsementListAPIParameter)} /> -
    - } -

    - Endorsements -

    -
    -
    -
    -
    - - - -
    -
    -
    - {successMessage && ( - setAlertClose(!alertClose)} - /> - )} - {schemaListErr && ( - setSchemaListErr(null)}> - -

    {schemaListErr}

    -
    -
    - )} - {schemaList && schemaList.length > 0 ? ( -
    -
    - {schemaList.map((item: IEndorsementList) => ( -
    - -
    - ))} -
    - { - totalPages > 1 && -
    - -
    - } -
    - ) : ( -
    - {!(schemaList && schemaList.length > 0) && loading ? ( -
    - -
    - ) : ( -
    - {orgId ? ( - - - - } - /> - ) : ( - - - - } - /> - )} -
    - )} -
    - )} -
    - { - if (flag) { - getEndorsementListData(endorsementListAPIParameter); - setShowPopup(false); - } - }} - onAlertClose={alertClose} - endorsementData={selectedRequest} - setMessage={setSuccessMessage} - /> -
    - ); -}; - -export default EndorsementList; diff --git a/src/components/Ecosystem/MemberList.tsx b/src/components/Ecosystem/MemberList.tsx deleted file mode 100644 index 13b0ff9f9..000000000 --- a/src/components/Ecosystem/MemberList.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import { useEffect, useState } from 'react'; -import type { ChangeEvent } from 'react'; -import { getEcosystemMemberList } from '../../api/ecosystem'; -import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; -import type { AxiosResponse } from 'axios'; -import type { Data } from '../../commonComponents/datatable/interface'; -import DateTooltip from '../Tooltip'; -import { dateConversion } from '../../utils/DateConversion'; -import { AlertComponent } from '../AlertComponent'; -import { getFromLocalStorage } from '../../api/Auth'; -import CopyDid from '../../commonComponents/CopyDid'; -import SortDataTable from '../../commonComponents/datatable/SortDataTable'; - -const initialPageState = { - itemPerPage: 10, - page: 1, - search: '', - sortBy: 'createDateTime', - sortingOrder: 'ASC', - allSearch: '', -}; - -interface IMemberList { - orgId: string; - ecosystemId: string; - ecosystem: { createDateTime: string }; - ecosystemRole: { name: string }; - organisation: { - name: string; - orgSlug: string; - org_agents: { orgDid: string }[]; - }; - role: string; - createDateTime: any; - status: string; - copied?: boolean; - data: Data[]; -} -export interface IMemberListAPIParameter { - itemPerPage: number; - page: number; - search: string; - sortBy: string; - sortingOrder: string; - filter?: string; -} -const MemberList = () => { - const [loading, setLoading] = useState(false); - const [listAPIParameter, setListAPIParameter] = useState(initialPageState); - const [memberList, setMemberList] = useState([]); - const [error, setError] = useState(null); - const [totalItem, setTotalItem] = useState(0); - const [pageInfo, setPageInfo] = useState({ - totalItem: '', - nextPage: '', - lastPage: '', - }); - - useEffect(() => { - let getData: NodeJS.Timeout; - - if (listAPIParameter?.search?.length >= 1) { - getData = setTimeout(() => { - getEcosystemMembers(listAPIParameter); - }, 1000); - return () => clearTimeout(getData); - } else { - getEcosystemMembers(listAPIParameter); - } - - return () => clearTimeout(getData); - }, [listAPIParameter]); - - const refreshPage = () => { - getEcosystemMembers(listAPIParameter); - }; - - const searchInputChange = (e: ChangeEvent) => { - setListAPIParameter({ - ...listAPIParameter, - search: e.target.value, - page: 1, - }); - }; - - const searchSortByValue = (value: any) => { - setListAPIParameter({ - ...listAPIParameter, - page: 1, - sortingOrder: value, - }); - }; - - const getEcosystemMembers = async (apiParameter: IMemberListAPIParameter) => { - const userProfile = await getFromLocalStorage(storageKeys.USER_PROFILE) - const parsedUserProfileData = JSON.parse(userProfile); - const userId = parsedUserProfileData.id; - - setLoading(true); - const response = await getEcosystemMemberList(apiParameter); - const { data } = response as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - setTotalItem(data?.data.totalItems); - const { totalItems, nextPage, lastPage } = data.data; - - setPageInfo({ - totalItem: totalItems, - nextPage: nextPage, - lastPage: lastPage, - }); - const memberList = - data?.data?.data?.length > 0 && - data?.data?.data?.map((member: IMemberList) => { - let orgDid = 'Not available'; - if (member.organisation.org_agents.length > 0) { - const orgAgent = member.organisation.org_agents[0]; - orgDid = orgAgent.orgDid; - } - return { - data: [ - { - data: member?.organisation.name || 'Not available', - }, - { - data: - ( - - {dateConversion(member?.createDateTime)}{' '} - - ) || 'Not available', - }, - { - data: orgDid ? ( -
    - -
    - ) : ( - - Not available - - ), - }, - { - data: member?.ecosystemRole?.name ? ( - - - {member?.ecosystemRole?.name} - - {member.organisation.userOrgRoles.some((item) => item.userId === userId) ? '(You)' : ''} - - - ) : ( - 'Not available' - ), - }, - { - data: member.status ? ( - - {member.status.charAt(0) + - member.status.slice(1).toLowerCase()} - - ) : ( - 'Not available' - ), - }, - ], - }; - }); - - setMemberList(memberList); - } else { - setError(response as string); - } - setLoading(false); - }; - - useEffect(() => { - getEcosystemMembers(listAPIParameter); - }, []); - - const header = [ - { columnName: 'Organization' }, - { columnName: 'Member Since' }, - { columnName: 'Organization Did', width: 'w-1.5 pl-6' }, - { columnName: 'Role', width: 'pl-7' }, - { columnName: 'Status' }, - ]; - - return ( -
    -

    - Ecosystem Members -

    -
    - { - setError(null); - }} - /> -
    - { - setListAPIParameter((prevState) => ({ - ...prevState, - page, - })); - }} - searchSortByValue={searchSortByValue} - totalPages={totalItem} - pageInfo={pageInfo} - isHeader={true} - isSearch={true} - isRefresh={true} - isSort={true} - isPagination={true} - message={'No Members'} - discription={"The ecosystem doesn't have any matching member"} - noExtraHeight={true} - sortOrder={listAPIParameter.sortingOrder} - > -
    - ); -}; - -export default MemberList; diff --git a/src/components/Ecosystem/interfaces/index.ts b/src/components/Ecosystem/interfaces/index.ts deleted file mode 100644 index 33757eeff..000000000 --- a/src/components/Ecosystem/interfaces/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { IAttributes } from '../../Resources/Schema/interfaces'; - -export interface IEcosystem { - id: string - name: string - description: string - logoUrl: string - joinedDate?: string - role?: string - autoEndorsement:boolean - ecosystemOrgs?: { - ecosystemRole: { - name: string - } - }[] -} - -export interface Ecosystem { - id: string - createDateTime: string - createdBy: string - lastChangedDateTime: string - lastChangedBy: string - autoEndorsement:boolean - name: string - description: string - logoUrl: string - website: string - roles: string[] - logoFile:string -} - -export interface EditEntityModalProps { - openModal: boolean; - setMessage: (message: string) => void; - setOpenModal: (flag: boolean) => void; - onEditSuccess?: () => void; - entityData: Ecosystem | null; - isOrganization: boolean; -} - -export interface EditEntityValues { - name: string; - description: string; -} - -export interface ILogoImage { - logoFile: string | File; - imagePreviewUrl: string | ArrayBuffer | null | File; - fileName: string; -} - -export interface ISelectedRequest { - attribute: IAttributes[]; - issuerDid: string; - createdDate: string; - schemaId: string; -} - -export interface IEndorsementList { - id: string; - endorserDid: string; - authorDid: string; - status: string; - type: string; - ecosystemOrgs: { - orgId: string; - }; - requestPayload: string; - responsePayload: string; - createDateTime: string; -} - -export interface IEndorsement { - getEndorsementListData: () => void; -} diff --git a/src/components/EcosystemInvite/EcoInvitationList.tsx b/src/components/EcosystemInvite/EcoInvitationList.tsx deleted file mode 100644 index bb706d387..000000000 --- a/src/components/EcosystemInvite/EcoInvitationList.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type { InvitationProps, NetworkDetails } from "./EcoSystemReceivedInvitations"; -import CustomAvatar from '../Avatar'; - -const EcoInvitationList = (props: InvitationProps) => { - const { invitationId, ecosystem } = props || {}; - const { name, logoUrl, networkDetails } = ecosystem || {}; - - return ( -
    -
    - {logoUrl ? ( - - ) : ( - - )} -
    - -
    -

    - {name} -

    - -
    -
      -
    • -
      -
      - Role:{' '} - - Ecosystem Member - -
      -
      -
    • -
    -
    -
    - Network:{' '} - {Array.isArray(networkDetails) && networkDetails.length > 0 && - - {networkDetails.map((network: NetworkDetails) => network.name)?.join(', ')} - - } -
    -
    -
    - ); -}; - -export default EcoInvitationList; diff --git a/src/components/EcosystemInvite/EcoSystemInvitation.tsx b/src/components/EcosystemInvite/EcoSystemInvitation.tsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/components/EcosystemInvite/EcoSystemReceivedInvitations.tsx b/src/components/EcosystemInvite/EcoSystemReceivedInvitations.tsx deleted file mode 100644 index 5ee67f4ab..000000000 --- a/src/components/EcosystemInvite/EcoSystemReceivedInvitations.tsx +++ /dev/null @@ -1,424 +0,0 @@ -import { Button, Card, Pagination } from 'flowbite-react'; -import React, { useEffect, useState } from 'react'; -import { - acceptRejectEcosystemInvitations, - getUserEcosystemInvitations, -} from '../../api/invitations'; -import { AlertComponent } from '../AlertComponent'; -import type { AxiosResponse } from 'axios'; -import BreadCrumbs from '../BreadCrumbs'; -import type { Organisation } from '../organization/interfaces'; -import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; -import { pathRoutes } from '../../config/pathRoutes'; -import { EmptyListMessage } from '../EmptyListComponent'; -import CustomSpinner from '../CustomSpinner'; -import { getFromLocalStorage } from '../../api/Auth'; -import { getOrganizationById, getOrganizations } from '../../api/organization'; -import EcoInvitationList from './EcoInvitationList'; -import BackButton from '../../commonComponents/backbutton'; - -const initialPageState = { - pageNumber: 1, - pageSize: 10, - total: 0, -}; - -interface IOrgData { - orgDid: string; - orgName: string; -} - -export interface NetworkDetails { - id: string; - name: string; - indyNamespace: string; -} - -export interface IPropsEcoInvitationList { - name: string; - logoUrl: string; - networkDetails?: NetworkDetails[]; -} -export interface InvitationProps { - invitationId: string; - ecosystem: IPropsEcoInvitationList; -} -export interface EcosystemInvitation { - ecosystem: { name: string; logoUrl: string }; - id: string; - createDateTime: string; - createdBy: string; - lastChangedDateTime: string; - lastChangedBy: string; - deletedAt: any; - userId: string; - orgId: string; - status: string; - email: string; - selected?: boolean; - orgData?: IOrgData; -} - -const ReceivedInvitations = () => { - const [openModal, setOpenModal] = useState(false); - const [loading, setLoading] = useState(true); - const [message, setMessage] = useState(null); - const [error, setError] = useState(null); - const [organizationsList, setOrganizationsList] = - useState | null>(null); - const [currentPage, setCurrentPage] = useState(initialPageState); - const [selectedId, setSelectedId] = useState(''); - const [invitationsData, setInvitationsData] = - useState | null>(null); - const [getOrgError, setGetOrgError] = useState(null); - - const onPageChange = (page: number) => { - setCurrentPage({ - ...currentPage, - pageNumber: page, - }); - }; - - const getAllOrganizationsForEcosystem = async () => { - setLoading(true); - const response = await getOrganizations( - currentPage.pageNumber, - currentPage.pageSize, - '', - ); - const { data } = response as AxiosResponse; - if (data?.statusCode !== apiStatusCodes.API_STATUS_SUCCESS) { - setError(response as string); - } else { - const orgList = data?.data?.organizations.map((userOrg: Organisation) => { - const roles: string[] = userOrg.userOrgRoles.map( - (role) => role.orgRole.name, - ); - userOrg.roles = roles; - return userOrg; - }); - setOrganizationsList(orgList); - } - setLoading(false); - }; - - const getAllInvitations = async () => { - setLoading(true); - const response = await getUserEcosystemInvitations( - currentPage.pageNumber, - currentPage.pageSize, - '', - ); - const { data } = response as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const totalPages = data?.data?.totalPages; - - const invitationList = data?.data?.invitations?.filter( - (invitation: { status: string }) => { - return invitation.status === 'pending'; - }, - ); - setInvitationsData(invitationList); - setCurrentPage({ - ...currentPage, - total: totalPages, - }); - } else { - setError(response as string); - } - setLoading(false); - }; - - useEffect(() => { - getAllOrganizationsForEcosystem(); - getAllInvitations(); - }, [openModal, currentPage.pageNumber]); - - const respondToEcosystemInvitations = async ( - invite: EcosystemInvitation, - status: string, - ) => { - try { - const response = await acceptRejectEcosystemInvitations( - invite.id, - selectedId, - status, - ); - setLoading(false); - const { data } = response as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - setMessage(data?.message); - getAllInvitations(); - } else { - setError(response as string); - } - - setLoading(false); - } catch (err) { - console.log('ERROR - Accept/Reject Ecosystem::', err); - setLoading(false); - } - }; - - const getSelectedOrgDetails = async ( - orgId: string, - ): Promise => { - try { - const response = await getOrganizationById(orgId); - const { data } = response as AxiosResponse; - const orgData = data?.data; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - if (orgData.org_agents[0]?.orgDid) { - setGetOrgError(null); - return { - orgDid: orgData.org_agents[0]?.orgDid || '', - orgName: orgData.name, - }; - } else { - setGetOrgError( - 'Please create your wallet for this organization to accept the invitation.', - ); - return null; - } - } - return null; - } catch (err) { - console.log('ERROR::', err); - return null; - } - }; - - const handleDropdownChange = async ( - e: { target: { value: any } }, - id: string, - ) => { - const value = e.target.value; - const orgData: IOrgData | null | undefined = await getSelectedOrgDetails( - value, - ); - const updateInvitationData = - invitationsData && invitationsData.length > 0 - ? invitationsData.map((item) => { - if (id === item.id) { - return { - ...item, - orgId: value, - selected: true, - orgData: orgData ?? undefined, - }; - } - return { - ...item, - selected: false, - }; - }) - : null; - setInvitationsData(updateInvitationData); - setSelectedId(value); - }; - - const getOrgId = async () => { - const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - if (orgId) { - setSelectedId(orgId); - } - }; - - useEffect(() => { - getOrgId(); - }, []); - - const rejectEnv = ( - - - - ); - - const acceptEnv = ( - - - - - ); - - return ( -
    -
    - -
    -

    - Received Ecosystem Invitations -

    - -
    -
    -
    -
    - { - setMessage(null); - setError(null); - }} - /> - - {loading ? ( -
    - -
    - ) : invitationsData && invitationsData?.length > 0 ? ( -
    -
    -
      - {invitationsData.map((invitation) => { - const ecosystem: IPropsEcoInvitationList = - invitation?.ecosystem; - return ( - -
      -
      - - -
      - - -
      -
      -
      - -
      -
      - {invitation.selected && ( - { - setGetOrgError(null); - }} - /> - )} -
      - ); - })} -
    -
    -
    - ) : ( - invitationsData && ( - - ) - )} - - {currentPage.total > 1 && ( -
    - -
    - )} -
    -
    -
    - ); -}; - -export default ReceivedInvitations; diff --git a/src/components/EcosystemInvite/SentInvitations.tsx b/src/components/EcosystemInvite/SentInvitations.tsx deleted file mode 100644 index 771dbfd00..000000000 --- a/src/components/EcosystemInvite/SentInvitations.tsx +++ /dev/null @@ -1,321 +0,0 @@ -import { Button, Pagination } from 'flowbite-react'; -import { useEffect, useState } from 'react'; -import { - getEcosystemInvitations, - deleteEcosystemInvitations, -} from '../../api/invitations'; -import { AlertComponent } from '../AlertComponent'; -import type { AxiosResponse } from 'axios'; -import BreadCrumbs from '../BreadCrumbs'; -import type { Invitation } from '../organization/interfaces/invitations'; -import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; -import { EmptyListMessage } from '../EmptyListComponent'; -import CustomSpinner from '../CustomSpinner'; -import RoleViewButton from '../RoleViewButton'; -import SendInvitationModal from '../organization/invitations/SendInvitationModal'; -import { getFromLocalStorage } from '../../api/Auth'; -import { Features } from '../../utils/enums/features'; -import { getOwnerAdminRole } from '../../config/ecosystem'; -import { OrganizationRoles } from '../../common/enums'; - -const initialPageState = { - pageNumber: 1, - pageSize: 10, - total: 0, -}; - -const SentInvitations = () => { - const [loading, setLoading] = useState(true); - const [message, setMessage] = useState(null); - const [error, setError] = useState(null); - const [currentPage, setCurrentPage] = useState(initialPageState); - const [searchText, setSearchText] = useState(''); - const [invitationsList, setInvitationsList] = - useState | null>(null); - const [ecosystemId, setEcosystemId] = useState(''); - const [openModal, setOpenModal] = useState(false); - const [isAccess, setIsAccess] = useState(false); - - const onPageChange = (page: number) => { - setCurrentPage({ - ...currentPage, - pageNumber: page, - }); - }; - - const checkEcosystemAccess = async () => { - const data = await getOwnerAdminRole(OrganizationRoles.organizationOwner); - setIsAccess(data); - }; - - const getAllSentInvitations = async () => { - const ecosystemId = await getFromLocalStorage(storageKeys.ECOSYSTEM_ID); - setEcosystemId(ecosystemId); - setLoading(true); - const response = await getEcosystemInvitations( - currentPage.pageNumber, - currentPage.pageSize, - searchText, - ); - const { data } = response as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const totalPages = data?.data?.totalPages; - const invitationList = data?.data?.invitations; - - setInvitationsList(invitationList); - setCurrentPage({ - ...currentPage, - total: totalPages, - }); - } else { - setError(response as string); - } - setLoading(false); - }; - - const deletInvitations = async (invitationId: string) => { - const response = await deleteEcosystemInvitations(invitationId); - const { data } = response as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - setLoading(true); - await getAllSentInvitations(); - setMessage('Invitation deleted successfully'); - } else { - setError(response as string); - } - setLoading(false); - }; - - const createInvitationsModel = () => { - setOpenModal(true); - }; - - useEffect(() => { - let getData: NodeJS.Timeout; - - if (searchText.length >= 1) { - getData = setTimeout(() => { - getAllSentInvitations(); - }, 1000); - } else { - getAllSentInvitations(); - } - checkEcosystemAccess(); - return () => clearTimeout(getData); - }, [searchText, currentPage.pageNumber]); - - return ( -
    -
    - -
    -
    -
    -
    -

    - Sent Ecosystem Invitations -

    -
    - setMessage(data)} - setOpenModal={setOpenModal} - /> - - - - } - onClickEvent={createInvitationsModel} - /> -
    -
    - - { - setMessage(null); - setError(null); - }} - /> - - {invitationsList && invitationsList?.length > 0 ? ( -
    -
    -
      - {invitationsList.map((invitation) => ( -
    • -
      -
      -
      - - - - - - - - - - - - - - -
      -

      - {invitation.email} -

      - -
      -
        -
      • -
        -
        - Role:{' '} - - Ecosystem Member - -
        -
        -
      • -
      -
      -
      -
      -
      - -
      -
      - Status: - - {invitation?.status?.charAt(0).toUpperCase() + - invitation?.status?.slice(1)} - -
      - {invitation.status === 'pending' && ( -
      - -
      - )} -
      -
      -
    • - ))} -
    -
    -
    - ) : ( -
    - {!(invitationsList && invitationsList?.length > 0) && loading ? ( -
    - -
    - ) : ( - - )} -
    - )} - - {currentPage.total > 1 && ( -
    - -
    - )} -
    -
    -
    - ); -}; - -export default SentInvitations; diff --git a/src/components/EditEcosystemOrgModal/index.tsx b/src/components/EditEcosystemOrgModal/index.tsx deleted file mode 100644 index 2e2831794..000000000 --- a/src/components/EditEcosystemOrgModal/index.tsx +++ /dev/null @@ -1,368 +0,0 @@ -import * as yup from 'yup'; -import { Avatar, Button, Label, Modal } from 'flowbite-react'; -import { Field, Form, Formik } from 'formik'; -import type { FormikHelpers as FormikActions } from 'formik'; -import { apiStatusCodes } from '../../config/CommonConstant'; -import { AlertComponent } from '../AlertComponent'; -import type { AxiosResponse } from 'axios'; -import { updateOrganization } from '../../api/organization'; -import { updateEcosystem } from '../../api/ecosystem'; -import type { - EditEntityModalProps, - EditEntityValues, - ILogoImage, -} from '../Ecosystem/interfaces'; -import React, { useEffect, useState } from 'react'; -import EndorsementTooltip from '../../commonComponents/EndorsementTooltip'; -import { processImage } from '../../utils/processImage'; -import FormikErrorMessage from '../../commonComponents/formikerror/index' - -const EditPopupModal = (props: EditEntityModalProps) => { - const [logoImage, setLogoImage] = useState({ - logoFile: '', - imagePreviewUrl: props?.entityData?.logoUrl ?? '', - fileName: '', - }); - - const [loading, setLoading] = useState(false); - const [initialEntityData, setInitialEntityData] = useState({ - name: '', - description: '', - }); - - useEffect(() => { - if (props.openModal && props.entityData) { - setInitialEntityData({ - name: props.entityData.name ?? '', - description: props.entityData.description ?? '', - }); - setIsAutoEndorse(props.entityData.autoEndorsement); - setLogoImage({ - logoFile: '', - imagePreviewUrl: props.entityData.logoUrl ?? '', - fileName: props.entityData.logoFile ?? '', - }); - } - }, [props.entityData, props.openModal]); - - const [errMsg, setErrMsg] = useState(null); - const [imgError, setImgError] = useState(''); - const [isAutoEndorse, setIsAutoEndorse] = useState(false); - - useEffect(() => { - if (!props.openModal) { - setInitialEntityData({ - name: '', - description: '', - }); - - setLogoImage({ - logoFile: '', - imagePreviewUrl: '', - fileName: '', - }); - setImgError(''); - setErrMsg(null); - setLoading(false); - } - }, [props.openModal]); - - const handleImageChange = (event: any): void => { - setImgError(''); - processImage(event, (result: any, error: any) => { - if (result) { - setLogoImage({ - logoFile: '', - imagePreviewUrl: result, - fileName: event.target.files[0].name, - }); - } else { - setImgError(error || 'An error occurred while processing the image.'); - } - }); - }; - - const submitUpdateEntity = async (values: EditEntityValues) => { - setLoading(true); - - const entityData = { - id: props?.entityData?.id, - name: values.name, - description: values.description, - logo: - (logoImage?.imagePreviewUrl as string) || props?.entityData?.logoUrl, - autoEndorsement: isAutoEndorse, - }; - - try { - if (props.isOrganization) { - const response = await updateOrganization( - entityData, - entityData.id?.toString() as string, - ); - const { data } = response as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - if (props?.onEditSuccess) { - props?.onEditSuccess(); - } - props.setOpenModal(false); - props.setMessage(data?.message); - } else { - setErrMsg(response as string); - setLoading(false); - } - } else { - const response = await updateEcosystem(entityData); - const { data } = response as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - if (props?.onEditSuccess) { - props?.onEditSuccess(); - } - props.setOpenModal(false); - props.setMessage(data?.message); - } else { - setErrMsg(response as string); - setLoading(false); - } - } - } catch (error) { - console.error('An error occurred:', error); - setLoading(false); - } - }; - - return ( - { - setLogoImage({ - logoFile: '', - imagePreviewUrl: '', - fileName: '', - }); - setInitialEntityData({ - name: '', - description: '', - }); - props.setOpenModal(false); - }} - > - - Edit {props.isOrganization ? 'Organization' : 'Ecosystem'} - - - { - setErrMsg(null); - }} - /> - , - ) => { - await submitUpdateEntity(values); - }} - > - {(formikHandlers): JSX.Element => ( -
    -
    -
    - {logoImage.imagePreviewUrl ? ( - {`${ - ) : ( - - )} - -
    -

    - {props.isOrganization - ? 'Organization Logo' - : 'Ecosystem Logo'} -

    -
    - JPG, JPEG and PNG. Max size of 1MB -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - { - const value = e.target.value; - formikHandlers.setFieldValue('name', value); - formikHandlers.setFieldTouched('name', true, false); - - if (value.length > 50) { - formikHandlers.setFieldError( - 'name', - props.isOrganization - ? 'Organization name must be at most 50 characters' - : 'Ecosystem name must be at most 50 characters', - ); - } - }} - /> - -
    -
    -
    -
    - { - const value = e.target.value; - formikHandlers.setFieldValue('description', value); - formikHandlers.setFieldTouched('description', true, false); - - if (value.length > 50) { - formikHandlers.setFieldError( - 'description', - 'Description must be at most 255 characters', - ); - } - }} - /> - -
    -
    -
    -
    -
    - setIsAutoEndorse(false)} - /> - - Sign - -
    -
    - setIsAutoEndorse(true)} - /> - - Sign and Submit - -
    -
    - -
    - )} -
    -
    -
    - ); -}; - -export default EditPopupModal; diff --git a/src/components/Issuance/BulkIssuance.tsx b/src/components/Issuance/BulkIssuance.tsx index b9129abba..294d98585 100644 --- a/src/components/Issuance/BulkIssuance.tsx +++ b/src/components/Issuance/BulkIssuance.tsx @@ -16,12 +16,10 @@ import SOCKET from '../../config/SocketConfig'; import { ToastContainer, toast } from 'react-toastify'; import BreadCrumbs from '../BreadCrumbs'; import BackButton from '../../commonComponents/backbutton' -import { checkEcosystem } from '../../config/ecosystem'; -import type { ICheckEcosystem} from '../../config/ecosystem'; import type { ICredentials, IAttributes, IUploadMessage } from './interface'; import RoleViewButton from '../RoleViewButton'; import { Features } from '../../utils/enums/features'; -import { Create, SchemaEndorsement } from './Constant'; +import { Create } from './Constant'; import { DidMethod, SchemaTypes } from '../../common/enums'; import type { GetAllSchemaListParameter } from '../Resources/Schema/interfaces'; import { getAllSchemas } from '../../api/Schema'; @@ -44,7 +42,6 @@ const BulkIssuance = () => { const [uploadMessage, setUploadMessage] = useState(null) const [success, setSuccess] = useState(null); const [failure, setFailure] = useState(null); - const [isEcosystemData, setIsEcosystemData] = useState(); const [mounted, setMounted] = useState(false) const [schemaType, setSchemaType]= useState(); const [selectedTemplate, setSelectedTemplate] = useState(); @@ -205,17 +202,9 @@ const BulkIssuance = () => { }, [isAllSchema]); useEffect(() => { setMounted(true); - (async () => { - try { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemData(data); - } catch (error) { - console.log(error); - } - })(); }, []); - + const downloadFile = (url: string, fileName: string) => { const link = document.createElement('a'); link.href = url; @@ -523,9 +512,7 @@ const BulkIssuance = () => { item.value === credentialSelected, ); - const createSchemaTitle = isEcosystemData?.isEcosystemMember - ? { title: 'Schema Endorsement', svg: } - : { title: 'Create Schema', svg: }; + const createSchemaTitle = { title: 'Create Schema', svg: }; return (
    diff --git a/src/components/Issuance/Constant.tsx b/src/components/Issuance/Constant.tsx index 0144e0cb0..c4183a797 100644 --- a/src/components/Issuance/Constant.tsx +++ b/src/components/Issuance/Constant.tsx @@ -1,20 +1,3 @@ - -export const SchemaEndorsement = () => ( - - - -); - export const Create = () => (
    (
    ); + +export const SchemaEndorsement = () => ( + + + + ); + + diff --git a/src/components/Issuance/EmailIssuance.tsx b/src/components/Issuance/EmailIssuance.tsx index c0258be07..646b28f43 100644 --- a/src/components/Issuance/EmailIssuance.tsx +++ b/src/components/Issuance/EmailIssuance.tsx @@ -20,10 +20,8 @@ import { EmptyListMessage } from '../EmptyListComponent'; import ResetPopup from './ResetPopup'; import type { SelectRef } from './BulkIssuance'; import RoleViewButton from '../RoleViewButton'; -import { checkEcosystem } from '../../config/ecosystem'; -import type { ICheckEcosystem} from '../../config/ecosystem'; import { Features } from '../../utils/enums/features'; -import { Create, SchemaEndorsement } from './Constant'; +import { Create } from './Constant'; import { DidMethod, SchemaTypes, CredentialType, SchemaTypeValue, ProofType, SchemaType } from '../../common/enums'; import { getAllSchemas } from '../../api/Schema'; import type { GetAllSchemaListParameter } from '../Resources/Schema/interfaces'; @@ -52,7 +50,6 @@ const EmailIssuance = () => { const [failure, setFailure] = useState(null); const [isEditing, setIsEditing] = useState(false); const [issueLoader, setIssueLoader] = useState(false); - const [isEcosystemData, setIsEcosystemData] = useState(); const inputRef = useRef(null); const [mounted, setMounted] = useState(false) const [schemaType, setSchemaType]= useState(); @@ -202,14 +199,6 @@ const EmailIssuance = () => { useEffect(() => { setMounted(true); - (async () => { - try { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemData(data); - } catch (error) { - console.log(error); - } - })(); }, []); useEffect(() => { @@ -374,9 +363,7 @@ const EmailIssuance = () => { setOpenResetModal(true); }; - const createSchemaTitle = isEcosystemData?.isEcosystemMember - ? { title: 'Schema Endorsement', svg: } - : { title: 'Create Schema', svg: }; + const createSchemaTitle = { title: 'Create Schema', svg: }; return (
    diff --git a/src/components/PlatformSetting/index.tsx b/src/components/PlatformSetting/index.tsx index 615130855..99f4090c9 100755 --- a/src/components/PlatformSetting/index.tsx +++ b/src/components/PlatformSetting/index.tsx @@ -14,8 +14,6 @@ interface IForm { sgApiKey: string; apiEndPoint: string; emailFrom: string; - enableEcosystem: boolean; - multiEcosystemSupport: boolean; } interface IMessage { @@ -25,25 +23,21 @@ interface IMessage { const getConfigKeys = (data: AxiosResponse) => { const platformConfig = data?.data?.platform_config && data?.data?.platform_config.length > 0 && data?.data?.platform_config[0] - const ecosystemConfig = data?.data?.ecosystem_config && data?.data?.ecosystem_config.length > 0 && data?.data?.ecosystem_config - const enableEcosystem = ecosystemConfig?.find((item: { key: string; }) => item.key === "enableEcosystem").value === "true" - const multiEcosystemSupport = ecosystemConfig?.find((item: { key: string; }) => item.key === "multiEcosystemSupport").value === "true" const { externalIp, lastInternalId, sgApiKey, emailFrom, apiEndpoint } = platformConfig || {} return { - externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint: apiEndpoint, enableEcosystem, multiEcosystemSupport + externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint: apiEndpoint, } } const PlatformSetting = ({ data }: any) => { - const { externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint, enableEcosystem, multiEcosystemSupport } = getConfigKeys(data) + const { externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint, + } = getConfigKeys(data) const initFormData: IPlatformSetting = { externalIp: externalIp || "", sgApiKey: sgApiKey || "", apiEndPoint: apiEndPoint || "", emailFrom: emailFrom || "", lastInternalId: lastInternalId || "", - enableEcosystem: enableEcosystem || false, - multiEcosystemSupport: multiEcosystemSupport || false } const [formData, setFormData] = useState(initFormData) const [loading, setLoading] = useState(false) @@ -71,15 +65,14 @@ const PlatformSetting = ({ data }: any) => { const fetchSettings = async () => { try { const { data } = await getPlatformSettings() as AxiosResponse - const { externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint, enableEcosystem, multiEcosystemSupport } = getConfigKeys(data) + const { externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint, + } = getConfigKeys(data) setFormData({ externalIp, sgApiKey, apiEndPoint, emailFrom, lastInternalId, - enableEcosystem, - multiEcosystemSupport }) } catch (err) { setAlertMessage({ @@ -92,15 +85,14 @@ const PlatformSetting = ({ data }: any) => { const updateSettings = async (values: IPlatformSetting) => { setLoading(true) try { - const { externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint, enableEcosystem, multiEcosystemSupport } = values || {} + const { externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint, + } = values || {} const payload = { externalIp, lastInternalId, sgApiKey, emailFrom, apiEndPoint, - enableEcosystem, - multiEcosystemSupport } const response = await updatePlatformSettings(payload) const { data } = response as AxiosResponse @@ -168,10 +160,6 @@ const PlatformSetting = ({ data }: any) => { 'Email domain is invalid', ) .trim(), - enableEcosystem: yup - .boolean(), - multiEcosystemSupport: yup - .boolean() })} validateOnBlur @@ -278,25 +266,7 @@ const PlatformSetting = ({ data }: any) => {

    -
    -

    Ecosystem

    -
    -
    -
    - formikHandlers.handleChange(e)} /> -
    -
    -
    -
    - formikHandlers.handleChange(e)} /> -
    -
    + { message.message && { if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { setPrePopulatedUserProfile(data?.data); - const { id, profileImg, firstName, email, enableEcosystem, multiEcosystemSupport } = data?.data || {} + const { id, profileImg, firstName, email, + } = data?.data || {} const userProfile = { - id, profileImg, firstName, email, enableEcosystem, multiEcosystemSupport + id, profileImg, firstName, email, } await setToLocalStorage(storageKeys.USER_PROFILE, userProfile) await setToLocalStorage(storageKeys.USER_EMAIL, data?.data?.email) diff --git a/src/components/Resources/Schema/Create.tsx b/src/components/Resources/Schema/Create.tsx index ccdc858b3..748220380 100644 --- a/src/components/Resources/Schema/Create.tsx +++ b/src/components/Resources/Schema/Create.tsx @@ -16,15 +16,12 @@ import type { FieldName, IFormData, IAttributes } from './interfaces'; import { createSchemas } from '../../../api/Schema'; import { getFromLocalStorage } from '../../../api/Auth'; import { pathRoutes } from '../../../config/pathRoutes'; -import { checkEcosystem, getEcosystemId } from '../../../config/ecosystem'; -import type { ICheckEcosystem} from '../../../config/ecosystem'; - -import { createSchemaRequest } from '../../../api/ecosystem'; -import EcosystemProfileCard from '../../../commonComponents/EcosystemProfileCard'; import ConfirmationModal from '../../../commonComponents/ConfirmationModal'; import { DidMethod, SchemaType, SchemaTypeValue } from '../../../common/enums'; -import { getOrganizationById } from '../../../api/organization'; +import { createSchemaRequest, getOrganizationById } from '../../../api/organization'; import React from 'react'; +import { checkEcosystem, type ICheckEcosystem, getEcosystemId } from '../../../config/ecosystem'; +import { envConfig } from '../../../config/envConfig'; const options = [ { @@ -53,13 +50,13 @@ interface IPopup { const CreateSchema = () => { const [failure, setFailure] = useState(null); const [success, setSuccess] = useState(null); + const [isEcosystemData, setIsEcosystemData] = useState(); const [orgId, setOrgId] = useState(''); const [createLoader, setCreateLoader] = useState(false); const [showPopup, setShowPopup] = useState({ show: false, type: 'reset', }); - const [isEcosystemData, setIsEcosystemData] = useState(); const [loading, setLoading] = useState(false); const [schemaTypeValues, setSchemaTypeValues]= useState() const [type, setType] = useState(); @@ -76,6 +73,7 @@ const CreateSchema = () => { }, ], }; + const checkEcosystemData = async () => { const data: ICheckEcosystem = await checkEcosystem(); setIsEcosystemData(data); @@ -215,7 +213,8 @@ const CreateSchema = () => { if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { setSuccess(data?.message); setCreateLoader(false); - window.location.href = pathRoutes.ecosystem.endorsements; + window.location.href = `${envConfig.PUBLIC_ECOSYSTEM_FRONT_END_URL}${pathRoutes.organizations.createSchema}` + setTimeout(() => { setSuccess(null); }, 2000); @@ -234,10 +233,10 @@ const CreateSchema = () => { setCreateLoader(false); }, 2000); }; - + const formTitle = isEcosystemData?.isEcosystemMember - ? 'Schema Endorsement' - : 'Create Schema'; + ? 'Schema Endorsement' + : 'Create Schema'; const submitButtonTitle = isEcosystemData?.isEcosystemMember ? { title: 'Request Endorsement', @@ -277,27 +276,27 @@ const CreateSchema = () => { ), }; - const confirmCreateSchema = () => { - if ( - isEcosystemData?.isEnabledEcosystem && - isEcosystemData?.isEcosystemMember - ) { - submitSchemaCreationRequest(formData); - } else { - formData.attribute.forEach((element: any) => { - if (!element.schemaDataType) { - element.schemaDataType = 'string'; - } - }); - const updatedAttribute: Array = []; - formData.attribute.forEach((element) => { - updatedAttribute.push(Number(element)); - }); - - submit(formData); - } - }; - + const confirmCreateSchema = () => { + if ( + isEcosystemData?.isEnabledEcosystem && + isEcosystemData?.isEcosystemMember + ) { + submitSchemaCreationRequest(formData); + } else { + formData.attribute.forEach((element: any) => { + if (!element.schemaDataType) { + element.schemaDataType = 'string'; + } + }); + const updatedAttribute: Array = []; + formData.attribute.forEach((element) => { + updatedAttribute.push(Number(element)); + }); + + submit(formData); + } + }; + const validSameAttribute = ( formikHandlers: FormikProps, index: number, @@ -391,11 +390,6 @@ if (
    - {isEcosystemData?.isEnabledEcosystem && ( -
    - -
    - )}
    diff --git a/src/components/Resources/Schema/SchemasList.tsx b/src/components/Resources/Schema/SchemasList.tsx index 2bdf2ac0a..76aa7dd4a 100644 --- a/src/components/Resources/Schema/SchemasList.tsx +++ b/src/components/Resources/Schema/SchemasList.tsx @@ -6,7 +6,8 @@ import type { ChangeEvent } from 'react'; import type { GetAllSchemaListParameter } from './interfaces'; import { apiStatusCodes, itemPerPage, storageKeys } from '../../../config/CommonConstant'; import { getAllSchemas, getAllSchemasByOrgId } from '../../../api/Schema'; - +import { checkEcosystem } from '../../../config/ecosystem'; +import type { ICheckEcosystem } from '../../../config/ecosystem'; import type { AxiosResponse } from 'axios'; import BreadCrumbs from '../../BreadCrumbs'; import CustomSpinner from '../../CustomSpinner'; @@ -19,11 +20,10 @@ import SearchInput from '../../SearchInput'; import { getFromLocalStorage, removeFromLocalStorage, setToLocalStorage } from '../../../api/Auth'; import { pathRoutes } from '../../../config/pathRoutes'; import { getOrganizationById } from '../../../api/organization'; -import { checkEcosystem } from '../../../config/ecosystem'; -import type { ICheckEcosystem } from '../../../config/ecosystem'; import Select, { type SingleValue, type ActionMeta } from 'react-select'; import { Create, SchemaEndorsement } from '../../Issuance/Constant'; import { DidMethod, SchemaType, SchemaTypes } from '../../../common/enums'; +import { envConfig } from '../../../config/envConfig'; const SchemaList = (props: { schemaSelectionCallback: ( @@ -45,6 +45,7 @@ const SchemaList = (props: { const [loading, setLoading] = useState(true); const [allSchemaFlag, setAllSchemaFlag] = useState(false); const [orgId, setOrgId] = useState(''); + const [isEcosystemData, setIsEcosystemData] = useState(); const [schemaListAPIParameter, setSchemaListAPIParameter] = useState({ itemPerPage: itemPerPage, page: 1, @@ -55,7 +56,6 @@ const SchemaList = (props: { }); const [walletStatus, setWalletStatus] = useState(false); const [totalItem, setTotalItem] = useState(0); - const [isEcosystemData, setIsEcosystemData] = useState(); const [searchValue, setSearchValue] = useState(''); const [schemaType, setSchemaType] = useState(''); @@ -268,22 +268,22 @@ const SchemaList = (props: { console.log(error); } })(); - (async () => { await setToLocalStorage (storageKeys.ALL_SCHEMAS, `false`); })(); setSearchValue(''); }, []); + const createSchemaTitle = isEcosystemData?.isEcosystemMember ? { title: 'Schema Endorsement', toolTip: 'Add new schema request', svg: } - : { title: 'Create', svg: , toolTip: 'Create new schema' }; - const emptyListTitle = 'No Schemas'; + : { title: 'Create', svg: , toolTip: 'Create new schema' }; const emptyListTitle = 'No Schemas'; const emptyListDesc = 'Get started by creating a new Schema'; const emptyListBtn = isEcosystemData?.isEcosystemMember - ? { title: 'Schema Endorsement', svg: } - : { title: 'Create Schema', svg: }; + ? { title: 'Schema Endorsement', svg: } + : { title: 'Create Schema', svg: }; + return (
    @@ -319,8 +319,13 @@ const SchemaList = (props: { feature={Features.CRETAE_SCHEMA} svgComponent={createSchemaTitle.svg} onClickEvent={() => { - window.location.href = `${pathRoutes.organizations.createSchema}`; - }} + if (createSchemaTitle.title === 'Schema Endorsement') { + window.location.href = `${envConfig.PUBLIC_ECOSYSTEM_FRONT_END_URL}${pathRoutes.organizations.schemas}` + + } else { + window.location.href = `${pathRoutes.organizations.createSchema}`; + } + }} /> ) : ( { const [failure, setFailure] = useState(null); const [orgId, setOrgId] = useState(''); const [credDefAuto, setCredDefAuto] = useState(''); - const [isEcosystemData, setIsEcosystemData] = useState(); const [ledgerPlatformLoading, setLedgerPlatformLoading] = useState(false); const [currentPage, setCurrentPage] = useState(initialPageState); @@ -136,7 +130,6 @@ const ViewSchemas = ({ schemaId }: { schemaId: string }) => { } }; const fetchData = async () => { - await checkEcosystemData(); const organizationId = await getFromLocalStorage(storageKeys.ORG_ID); setOrgId(String(organizationId)); const id = encodeURIComponent(schemaId); @@ -155,52 +148,13 @@ const ViewSchemas = ({ schemaId }: { schemaId: string }) => { const roles = orgRoles.split(','); setUserRoles(roles); }; - const checkEcosystemData = async () => { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemData(data); - }; useEffect(() => { getUserRoles(); - checkEcosystemData(); }, []); const submit = async (values: Values) => { - if ( - isEcosystemData?.isEnabledEcosystem && - isEcosystemData?.isEcosystemMember - ) { - console.log('Submitted for endorsement by ecosystem member'); - setCreateLoader(true); - const schemaId = schemaDetails?.schemaId || ''; - const requestPayload = { - endorse: true, - tag: values?.tagName, - schemaId, - schemaDetails: { - name: schemaDetails?.schema?.name, - version: schemaDetails?.schema?.version, - attributes: schemaDetails?.schema?.attrNames, - }, - }; - - const ecoId = await getEcosystemId(); - - const createCredDeff = await createCredDefRequest( - requestPayload, - ecoId, - orgId, - ); - const { data } = createCredDeff as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { - setCreateLoader(false); - setSuccess(data?.message); - } else { - setFailure(createCredDeff as string); - setCreateLoader(false); - } - getCredentialDefinitionList(schemaId, orgId); - } else { + setCreateLoader(true); const schemaId = schemaDetails?.schemaId || ''; const CredDeffFieldName: CredDeffFieldNameType = { @@ -223,7 +177,7 @@ const ViewSchemas = ({ schemaId }: { schemaId: string }) => { setCreateLoader(false); } getCredentialDefinitionList(schemaId, orgId); - } + }; const credDefSelectionCallback = async () => { @@ -246,30 +200,8 @@ const ViewSchemas = ({ schemaId }: { schemaId: string }) => { } }; - const formTitle = isEcosystemData?.isEcosystemMember - ? 'Credential Definition Endorsement' - : 'Create Credential Definition'; - const submitButtonTitle = isEcosystemData?.isEcosystemMember - ? { - title: 'Request Endorsement', - svg: ( - - - - ), - tooltip: 'Add new credential-definition request' - } - : { + const formTitle = 'Create Credential Definition'; + const submitButtonTitle = { title: 'Create', svg: (
    @@ -295,12 +227,6 @@ const ViewSchemas = ({ schemaId }: { schemaId: string }) => {
    - {isEcosystemData?.isEnabledEcosystem && ( -
    - -
    - )} -

    Schemas diff --git a/src/components/RoleViewButton/index.tsx b/src/components/RoleViewButton/index.tsx index 7b0b2b9c5..8e50abe40 100644 --- a/src/components/RoleViewButton/index.tsx +++ b/src/components/RoleViewButton/index.tsx @@ -51,13 +51,6 @@ const RoleViewButton = ({ title, buttonTitle, svgComponent, onClickEvent, featur return true } return false - } else if (feature === Features.CREATE_ECOSYSTEMS) { - if (userRoles.includes(Roles.OWNER) - || userRoles.includes(Roles.ADMIN) - ) { - return true - } - return false } else if (userRoles.includes(Roles.OWNER) || userRoles.includes(Roles.ADMIN)) { return true } else { diff --git a/src/components/User/UserDashBoard.tsx b/src/components/User/UserDashBoard.tsx index ab06f7650..7aa739e55 100644 --- a/src/components/User/UserDashBoard.tsx +++ b/src/components/User/UserDashBoard.tsx @@ -19,15 +19,12 @@ import { Roles } from '../../utils/enums/roles'; import { Button, Tooltip } from 'flowbite-react'; import { getAllCredDef, getAllSchemasByOrgId } from '../../api/Schema'; import type { GetAllSchemaListParameter } from '../Resources/Schema/interfaces'; -import { getEcosystems } from '../../api/ecosystem'; import React from 'react'; import { - EcoRoles, - EcosystemRoles, OrganizationRoles, } from '../../common/enums'; import CustomSpinner from '../CustomSpinner'; -import { getOwnerAdminRole } from '../../config/ecosystem'; +import { envConfig } from '../../config/envConfig'; const initialPageState = { pageNumber: 1, @@ -75,8 +72,6 @@ const UserDashBoard = () => { sortingOrder: 'desc', allSearch: '', }); - const [ecoCount, setEcoCount] = useState(0); - const [ecosystemListDetails, setEcosystemListDetails] = useState([]); const [credDefList, setCredDefList] = useState([]); const [credDefCount, setCredDefCount] = useState(0); const [walletData, setWalletData] = useState([]); @@ -113,6 +108,26 @@ const UserDashBoard = () => { } setLoading(false); }; + //Fetch the user organization list + const getAllOrganizations = async () => { + setOrgLoading(true); + const response = await getOrganizations( + currentPage.pageNumber, + currentPage.pageSize, + '', + ); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + setOrgCount(data?.data?.totalCount); + const orgList = data?.data?.organizations.filter( + (userOrg: Organisation, index: number) => index < 3, + ); + setOrganizationList(orgList); + } else { + setError(response as string); + } + setOrgLoading(false); + }; const getAllEcosystemInvitations = async () => { setLoading(true); @@ -130,7 +145,6 @@ const UserDashBoard = () => { data?.data?.invitations?.filter((invitation: { status: string }) => { return invitation.status === 'pending'; }); - if (invitationPendingList && invitationPendingList.length > 0) { setEcoMessage(`You have received invitation to join ecosystem `); setViewButton(true); @@ -142,29 +156,8 @@ const UserDashBoard = () => { } else { setError(response as string); } - setLoading(false); }; - //Fetch the user organization list - const getAllOrganizations = async () => { - setOrgLoading(true); - const response = await getOrganizations( - currentPage.pageNumber, - currentPage.pageSize, - '', - ); - const { data } = response as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - setOrgCount(data?.data?.totalCount); - const orgList = data?.data?.organizations.filter( - (userOrg: Organisation, index: number) => index < 3, - ); - setOrganizationList(orgList); - } else { - setError(response as string); - } - setOrgLoading(false); - }; const getUserRecentActivity = async () => { setLoading(true); @@ -230,37 +223,6 @@ const UserDashBoard = () => { } }; - const fetchEcosystems = async () => { - let organizationId = await getFromLocalStorage(storageKeys.ORG_ID); - if (!organizationId && organizationsList) { - organizationId = organizationsList[0].id; - } - if (organizationId) { - setEcoLoading(true); - const response = await getEcosystems( - organizationId, - currentPage.pageNumber, - currentPage.pageSize, - '', - ); - const { data } = response as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - setEcoCount(data?.data?.totalItems); - const ecosystemData = data?.data?.ecosystemList.filter( - (ecosystem: Organisation, index: number) => index < 3, - ); - if (ecosystemData) { - setEcosystemListDetails(ecosystemData); - } else { - setError(response as string); - } - } else { - setError(response as string); - } - } - setEcoLoading(false); - }; - const getSchemaCredentials = async () => { try { let orgId = await getFromLocalStorage(storageKeys.ORG_ID); @@ -308,11 +270,6 @@ const UserDashBoard = () => { } setWalletLoading(false); }; - - const checkEcosystemAccess = async () => { - const data = await getOwnerAdminRole(); - setIsAccess(data); - }; const getAllResponses = async () => { const role = await getFromLocalStorage(storageKeys.ORG_ROLES); @@ -341,9 +298,7 @@ const UserDashBoard = () => { if (organizationsList && organizationsList?.length > 0) { fetchOrganizationDetails(); getSchemaList(schemaListAPIParameter, false); - fetchEcosystems(); getSchemaCredentials(); - checkEcosystemAccess() } }, [organizationsList]); @@ -384,11 +339,6 @@ const UserDashBoard = () => { await setToLocalStorage(storageKeys.ORG_INFO, orgInfo); }; - const goToEcoDashboard = async (ecosystemId: string) => { - await setToLocalStorage(storageKeys.ECOSYSTEM_ID, ecosystemId); - window.location.href = pathRoutes.ecosystem.dashboard; - }; - const goToOrgSchema = async ( org: Organisation, orgId: string, @@ -425,11 +375,6 @@ const UserDashBoard = () => { window.location.href = pathRoutes.organizations.credentials; }; - const navigateToInvitation = async (ecosystemId: string) => { - await setToLocalStorage(storageKeys.ECOSYSTEM_ID, ecosystemId); - window.location.href = pathRoutes.ecosystem.sentinvitation; - }; - const ToolTipDataForOrganization = () => { return (
    @@ -451,26 +396,6 @@ const UserDashBoard = () => {
    ); }; - const ToolTipDataForEcosystem = () => { - return ( -
    -

    What is Ecosystem?

    - Ecosystem is a trusted network of organizations -
    - that empowers people and businesses with safe -
    - and secure ways of identifying themselves online -
    - and communicating confidentially with others. -
    - Examples are supply chain, marketplace, healthcare, -
    - banking, etc. where participants exchange -
    - information in an interoperable & trusted manner. -
    - ); - }; const ToolTipDataForSchema = () => { return ( @@ -530,7 +455,8 @@ const UserDashBoard = () => { message={ecoMessage} type={'warning'} viewButton={viewButton} - path={pathRoutes.ecosystem.invitation} + path={`${envConfig.PUBLIC_ECOSYSTEM_FRONT_END_URL}${pathRoutes.users.dashboard}` } + onAlertClose={() => { setEcoMessage(null); setError(null); @@ -1052,197 +978,10 @@ const UserDashBoard = () => { className="grid w-full grid-cols-1 gap-4 mt-0 mb-4 xl:grid-cols-3" style={{ minHeight: '300px' }} > +
    -
    -
    -
    -

    - Ecosystems{' '} -

    - } - placement="bottom" - className="items-center text-center dark:text-white" - > - - - - -
    -
    - - {ecoCount ?? 0} - -
    -
    -
    - - {!ecoLoading ? ( - <> - {ecosystemListDetails && ecosystemListDetails.length > 0 ? ( - <> - {ecosystemListDetails?.map((ecosystem: any) => { - return ( -
    - -
    - - {ecosystem?.ecosystemOrgs[0]?.ecosystemRole - ?.name === EcosystemRoles.ecosystemLead && ( - - )} - -
    -
    - ); - })} - - {ecosystemListDetails && ecosystemListDetails?.length > 0 && ( -
    - View All - - )} - - ) : ( -
    -

    - You have no ecosystems created or joined -

    -
    - )} - - ) : ( -
    - -
    - )} -
    -
    -
    @@ -1325,8 +1064,7 @@ const UserDashBoard = () => { )}
    -
    -
    +

    @@ -1369,6 +1107,9 @@ const UserDashBoard = () => { )}

    +
    + +
    ); }; diff --git a/src/components/Verification/VerificationCredentialList.tsx b/src/components/Verification/VerificationCredentialList.tsx index 510209747..48eb6a10b 100644 --- a/src/components/Verification/VerificationCredentialList.tsx +++ b/src/components/Verification/VerificationCredentialList.tsx @@ -24,7 +24,7 @@ import RoleViewButton from '../RoleViewButton'; import type { TableData } from '../../commonComponents/datatable/interface'; import { dateConversion } from '../../utils/DateConversion'; import { pathRoutes } from '../../config/pathRoutes'; -import { getFromLocalStorage, removeFromLocalStorage } from '../../api/Auth'; +import { getFromLocalStorage } from '../../api/Auth'; import { getOrgDetails } from '../../config/ecosystem'; import type { IConnectionListAPIParameter } from '../../api/connection'; import SortDataTable from '../../commonComponents/datatable/SortDataTable'; diff --git a/src/components/Verification/VerificationSchemasList.tsx b/src/components/Verification/VerificationSchemasList.tsx index 35a3d25cc..62404cfa4 100644 --- a/src/components/Verification/VerificationSchemasList.tsx +++ b/src/components/Verification/VerificationSchemasList.tsx @@ -3,13 +3,12 @@ import { Alert, Button, Pagination } from 'flowbite-react'; import React, { useEffect, useState } from 'react'; import type { ChangeEvent } from 'react'; import type { AxiosResponse } from 'axios'; -import { checkEcosystem, type ICheckEcosystem } from '../../config/ecosystem'; import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; import { apiStatusCodes, itemPerPage, storageKeys } from '../../config/CommonConstant'; import { getAllSchemas, getAllSchemasByOrgId } from '../../api/Schema'; import { DidMethod, SchemaTypes } from '../../common/enums'; import { getOrganizationById } from '../../api/organization'; -import { Create, SchemaEndorsement } from '../Issuance/Constant'; +import { Create } from '../Issuance/Constant'; import BreadCrumbs from '../BreadCrumbs'; import SearchInput from '../SearchInput'; import RoleViewButton from '../RoleViewButton'; @@ -35,7 +34,6 @@ const VerificationSchemasList = () => { }); const [walletStatus, setWalletStatus] = useState(false); const [totalItems, setTotalItems] = useState(0); - const [isEcosystemData, setIsEcosystemData] = useState(); const [searchValue, setSearchValue] = useState(''); const [selectedSchemas, setSelectedSchemas] = useState([]); const [w3cSchema, setW3cSchema] = useState(false); @@ -237,25 +235,13 @@ const VerificationSchemasList = () => { }; useEffect(() => { fetchOrganizationDetails(); - (async () => { - try { - const data: ICheckEcosystem = await checkEcosystem(); - setIsEcosystemData(data); - } catch (error) { - console.error(error); - } - })(); setSearchValue(''); }, []); - const createSchemaButtonTitle = isEcosystemData?.isEcosystemMember - ? { title: 'Schema Endorsement', toolTip: 'Add new schema request', svg: } - : { title: 'Create', svg: , toolTip: 'Create new schema' }; + const createSchemaButtonTitle = { title: 'Create', svg: , toolTip: 'Create new schema' }; const emptySchemaListTitle = 'No Schemas'; const emptySchemaListDescription = 'Get started by creating a new Schema'; - const emptySchemaListBtn = isEcosystemData?.isEcosystemMember - ? { title: 'Schema Endorsement', svg: } - : { title: 'Create Schema', svg: }; + const emptySchemaListBtn = { title: 'Create Schema', svg: }; return (
    diff --git a/src/components/organization/Dashboard.tsx b/src/components/organization/Dashboard.tsx index 24ba46201..feec8943a 100644 --- a/src/components/organization/Dashboard.tsx +++ b/src/components/organization/Dashboard.tsx @@ -1,6 +1,6 @@ import type { OrgDashboard, Organisation } from './interfaces'; import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; -import { getOrgDashboard, getOrganizationById } from '../../api/organization'; +import { getEcosystems, getOrgDashboard, getOrganizationById } from '../../api/organization'; import { useEffect, useState } from 'react'; import { Alert } from 'flowbite-react'; import type { AxiosResponse } from 'axios'; @@ -15,6 +15,14 @@ import { pathRoutes } from '../../config/pathRoutes'; import { AlertComponent } from '../AlertComponent'; import WalletSpinup from './walletCommonComponents/WalletSpinup'; import DashboardCard from '../../commonComponents/DashboardCard'; +import { envConfig } from '../../config/envConfig'; + +const initialPageState = { + pageNumber: 1, + pageSize: 10, + total: 0, +}; + const Dashboard = () => { const [orgData, setOrgData] = useState(null); @@ -24,9 +32,13 @@ const Dashboard = () => { const [failure, setFailure] = useState(null); const [loading, setLoading] = useState(true); const [userRoles, setUserRoles] = useState([]); - const [ecosystemUserRoles, setEcosystemUserRoles] = useState(''); const [orgSuccess, setOrgSuccess] = useState(null); const [openModal, setOpenModal] = useState(false); + const [currentPage, setCurrentPage] = useState(initialPageState); + const [ecoCount, setEcoCount] = useState(0); + const [error, setError] = useState(null); + const [redirectToEndorsment, setRedirectToEndorsment] = useState(); + const [ecosystemUserRoles, setEcosystemUserRoles] = useState(''); @@ -43,17 +55,17 @@ const Dashboard = () => { const roles = orgRoles.split(','); setUserRoles(roles); }; - - const getEcosystemRole = async () => { - const ecosysmetmRoles = await getFromLocalStorage(storageKeys.ECOSYSTEM_ROLE); - setEcosystemUserRoles(ecosysmetmRoles) - }; - + // const getEcosystemRole = async () => { + // const ecosysmetmRoles = await getFromLocalStorage(storageKeys.ECOSYSTEM_ROLE); + // console.log("ecosysmetmRoles",ecosysmetmRoles); + // setEcosystemUserRoles(ecosysmetmRoles) + // }; useEffect(() => { getUserRoles(); - getEcosystemRole(); + // getEcosystemRole(); }, []); + const fetchOrganizationDetails = async () => { setLoading(true); const orgId = await getFromLocalStorage(storageKeys.ORG_ID); @@ -85,6 +97,39 @@ const Dashboard = () => { } setLoading(false); }; + + const fetchEcosystems = async () => { + let organizationId = await getFromLocalStorage(storageKeys.ORG_ID); + + if (organizationId) { + const response = await getEcosystems( + organizationId, + currentPage.pageNumber, + currentPage.pageSize, + '', + ); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + setEcoCount(data?.data?.totalItems); + const ecosystems = data?.data?.ecosystemList; + let isLead = false; + + ecosystems.forEach((ecosystem: any) => { + ecosystem.ecosystemOrgs.forEach((org: any) => { + const role = org.ecosystemRole?.name; + if (role === 'Ecosystem Lead') { + isLead = true; + } + }); + }); + if (!isLead && ecoCount > 0) { + setRedirectToEndorsment(true); + } + } else { + setError(response as string); + } + } + }; const fetchOrganizationDashboard = async () => { @@ -107,6 +152,7 @@ const Dashboard = () => { useEffect(() => { fetchOrganizationDetails(); fetchOrganizationDashboard(); + fetchEcosystems(); }, []); const handleEditModalClose = () => { @@ -257,9 +303,12 @@ const Dashboard = () => { label="Schemas" value={orgDashboard?.schemasCount ?? 0} onClickHandler={() => { - if (walletStatus) { + if (walletStatus && !redirectToEndorsment) { window.location.href = pathRoutes.organizations.schemas; } + else if(walletStatus && redirectToEndorsment) { + window.location.href = `${envConfig.PUBLIC_ECOSYSTEM_FRONT_END_URL}${pathRoutes.organizations.schemas}` + } }} /> { const [loading, setLoading] = useState(true); @@ -32,14 +51,37 @@ const DeleteOrganizations = () => { const [isWalletPresent, setIsWalletPresent] = useState(false); const [message, setMessage] = useState(null); const [showPopup, setShowPopup] = useState(false); - const [ecosystemUserRoles, setEcosystemUserRoles] = useState(''); const [deleteAction, setDeleteAction] = useState<() => void>(() => {}); const [confirmMessage, setConfirmMessage] = useState(''); const [description, setDescription] = useState(""); - const [ecosystemRoles, setEcosystemRoles] = useState([]); const [orgName, setOrgName] = useState(""); + const [ecosystemRoles, setEcosystemRoles] = useState([]); + const [ecosystemUserRoles, setEcosystemUserRoles] = useState(''); + const fetchOrganizationDetails = async () => { + try { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId as string); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const walletName = data?.data?.org_agents[0]?.walletName; + const orgName = data?.data?.name; + if(orgName){ + setOrgName(orgName) + } + if (walletName) { + setIsWalletPresent(true); + } + else { + setIsWalletPresent(false); + } + } + } catch (error) { + console.error('Fetch organization details ERROR::::', error); + } + }; + const getAllEcosystems = async () => { try { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); @@ -67,29 +109,6 @@ const DeleteOrganizations = () => { } }; - const fetchOrganizationDetails = async () => { - try { - const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const response = await getOrganizationById(orgId as string); - const { data } = response as AxiosResponse; - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - const walletName = data?.data?.org_agents[0]?.walletName; - const orgName = data?.data?.name; - if(orgName){ - setOrgName(orgName) - } - if (walletName) { - setIsWalletPresent(true); - } - else { - setIsWalletPresent(false); - } - } - } catch (error) { - console.error('Fetch organization details ERROR::::', error); - } - }; - const fetchOrganizationReferences = async () => { setLoading(true); try { @@ -188,24 +207,6 @@ const DeleteOrganizations = () => { setDeleteLoading(false); }; - const deleteOrgFromEcosystem = async () => { - try { - const response = await deleteOrganizationFromEcosystem(); - const { data } = response as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { - toast.success(data?.message, {autoClose: 3000}) - await fetchOrganizationReferences(); - setShowPopup(false) - } else { - setError(response as string); - } - } catch (error) { - console.error('An error occurred:', error); - setError(error as string); - } - }; - const deleteOrgWallet = async () => { try { const response = await deleteOrganizationWallet(); @@ -258,7 +259,6 @@ const DeleteOrganizations = () => { deleteVerifications, deleteIssuance, deleteConnection, - deleteOrgFromEcosystem, deleteOrgWallet, deleteOrganizations }; @@ -288,22 +288,14 @@ const DeleteOrganizations = () => { confirmMessage:"Are you sure you want to delete connection records", isDisabled: (organizationData?.issuanceRecordsCount ?? 0) > 0 || (organizationData?.verificationRecordsCount ?? 0) > 0 }, - { - title: "Ecosystem", - description: "Ecosystems your organization has joined as a member", - count: organizationData?.orgEcosystemsCount ?? 0, - deleteFunc: deleteFunctions.deleteOrgFromEcosystem, - confirmMessage:"Are you sure you want to remove your organization from ecoystem", - isDisabled: ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) || ((organizationData?.connectionRecordsCount ?? 0) > 0 ||(organizationData?.issuanceRecordsCount ?? 0) > 0 || (organizationData?.verificationRecordsCount ?? 0) > 0) - }, { title: "Organization wallet", description: "Organization Wallet is the data of your created DIDs.", count: isWalletPresent ? 1 : 0, deleteFunc: deleteFunctions.deleteOrgWallet, confirmMessage: "Are you sure you want to delete organization wallet", - isDisabled: ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) || - ((organizationData?.orgEcosystemsCount ?? 0) > 0 || + isDisabled: ecosystemUserRoles.includes(EcosystemRoles.ecosystemLead) || + ( (organizationData?.connectionRecordsCount ?? 0) > 0 || (organizationData?.issuanceRecordsCount ?? 0) > 0 || (organizationData?.verificationRecordsCount ?? 0) > 0) @@ -333,11 +325,13 @@ const DeleteOrganizations = () => { setError(null); }} /> + {ecosystemRoles.length > 0 &&

    You are Ecosystem Lead for {ecosystemRoles.join(', ')}. You cannot remove yourself from the ecosystem, delete the organization's wallet, and delete your organization.

    } + {organizationData && (
    {cardData.map((card, index) => ( diff --git a/src/components/organization/OrgDropDown.tsx b/src/components/organization/OrgDropDown.tsx index a1797161c..f0b075124 100644 --- a/src/components/organization/OrgDropDown.tsx +++ b/src/components/organization/OrgDropDown.tsx @@ -45,8 +45,6 @@ const OrgDropDown = () => { const goToOrgDashboard = async (org: Organisation) => { await removeFromLocalStorage(storageKeys.ORG_INFO); await removeFromLocalStorage(storageKeys.ORG_DETAILS); - await removeFromLocalStorage(storageKeys.ECOSYSTEM_ID); - await removeFromLocalStorage(storageKeys.ECOSYSTEM_ROLE); if (org) { // Added check await setOrgRoleDetails(org); diff --git a/src/components/organization/OrganizationsList.tsx b/src/components/organization/OrganizationsList.tsx index 82c61c1f6..2be76ed88 100644 --- a/src/components/organization/OrganizationsList.tsx +++ b/src/components/organization/OrganizationsList.tsx @@ -20,7 +20,7 @@ import { } from '../../api/Auth'; import { EmptyListMessage } from '../EmptyListComponent'; import CustomSpinner from '../CustomSpinner'; -import CreateEcosystemOrgModal from '../CreateEcosystemOrgModal'; +import CreateOrgModal from '../CreateOrgModal'; const initialPageState = { pageNumber: 1, @@ -112,8 +112,6 @@ const OrganizationsList = () => { }; const redirectOrgDashboard = async (activeOrg: Organisation) => { - await removeFromLocalStorage(storageKeys.ECOSYSTEM_ID); - await removeFromLocalStorage(storageKeys.ECOSYSTEM_ROLE); await removeFromLocalStorage(storageKeys.ORG_DETAILS); await setToLocalStorage(storageKeys.ORG_ID, activeOrg.id.toString()); @@ -273,7 +271,7 @@ const OrganizationsList = () => {
    - setMessage(data)} setOpenModal={props.setOpenModal} diff --git a/src/components/organization/interfaces/index.ts b/src/components/organization/interfaces/index.ts index 4f9c93240..7fae1784f 100644 --- a/src/components/organization/interfaces/index.ts +++ b/src/components/organization/interfaces/index.ts @@ -6,10 +6,6 @@ export interface UserOrgRole { orgRole: OrgRole } -interface IEcosystemOrgs { - ecosystemId: string; -} - export interface Organisation { logoFile: string id: string @@ -26,7 +22,6 @@ export interface Organisation { org_agents: OrgAgent[] publicProfile: boolean checked?: boolean | undefined - ecosystemOrgs?: IEcosystemOrgs[] error?: string; } @@ -116,11 +111,6 @@ export interface Connection { lastChangedBy: string } - export interface EcosystemDashboard { - membersCount: number - endorsementsCount: number - } - export interface OrgInterface { name: string; website: string; @@ -255,26 +245,6 @@ export interface ILedgerItem { verificationRecordsCount: number; connectionRecordsCount: number; issuanceRecordsCount: number; - orgEcosystemsCount: number; orgInvitationsCount: number; orgUsersCount: number; } - export interface IEcosystemRole { - id: string; - name: string; - description: string; - createDateTime: string; - lastChangedDateTime: string; - deletedAt: string | null; - } - - export interface IEcosystemOrganizations { - id: string; - orgId: string; - status: string; - createDateTime: string; - lastChangedDateTime: string; - ecosystemId: string; - ecosystemRoleId: string; - ecosystemRole: IEcosystemRole; - } \ No newline at end of file diff --git a/src/components/organization/interfaces/invitations.ts b/src/components/organization/interfaces/invitations.ts index 0d7500b29..823234bd0 100644 --- a/src/components/organization/interfaces/invitations.ts +++ b/src/components/organization/interfaces/invitations.ts @@ -1,7 +1,6 @@ import type { OrgRole, Organisation } from "." export interface Invitation { - ecosystem: any id: string createDateTime: string createdBy: string diff --git a/src/components/organization/invitations/SendInvitationModal.tsx b/src/components/organization/invitations/SendInvitationModal.tsx index 4abcd2ae5..53f3ffb85 100644 --- a/src/components/organization/invitations/SendInvitationModal.tsx +++ b/src/components/organization/invitations/SendInvitationModal.tsx @@ -10,7 +10,6 @@ import { AlertComponent } from '../../AlertComponent'; import type { AxiosResponse } from 'axios'; import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; import { - createEcoSystemInvitations, createInvitations, } from '../../../api/invitations'; import { getOrganizationRoles } from '../../../api/organization'; @@ -34,7 +33,6 @@ interface RoleI { const SendInvitationModal = (props: { getAllSentInvitations?: () => void; - ecosystemId?: string; flag?: boolean; openModal: boolean; setMessage: (message: string) => void; @@ -123,34 +121,6 @@ const SendInvitationModal = (props: { setLoading(false); }; - const sendEcoSystemInvitations = async () => { - - setLoading(true); - const invitationPayload = invitations.map((invitation) => { - return { - email: invitation.email, - }; - }); - - const resCreateOrg = await createEcoSystemInvitations( - invitationPayload, - props?.ecosystemId, - ); - - const { data } = resCreateOrg as AxiosResponse; - - if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { - - props?.setMessage(data?.message); - props?.setOpenModal(false); - props?.getAllSentInvitations() - - } else { - setErrMsg(resCreateOrg as string); - } - setLoading(false); - }; - useEffect(() => { const getEmail = async () => { const email = await getFromLocalStorage(storageKeys.USER_EMAIL) @@ -325,7 +295,7 @@ const SendInvitationModal = (props: {
    - +
    ); })} {organizationsList && organizationsList?.length > 0 && ( @@ -941,10 +955,10 @@ const UserDashBoard = () => { className="flex items-center space-x-2 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white rounded-md" > - {schema?.name} + {schema?.name || ''} - {schema?.version} + {schema?.version || ''} @@ -1040,10 +1054,10 @@ const UserDashBoard = () => { className="flex items-center space-x-2 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white rounded-md" > - {cred?.tag} + {cred?.tag || ''} - {cred?.credentialDefinitionId} + {cred?.credentialDefinitionId || ''} @@ -1111,6 +1125,7 @@ const UserDashBoard = () => {
    + ); }; export default UserDashBoard; From 9f274f7af7527b4e65181cbe4e35156ac8c47652 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:36:58 +0530 Subject: [PATCH 54/54] fix: type errors in display user profile (#771) * fix: typeerrors in display user profile Signed-off-by: pranalidhanavade * fix: warning on input fields Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- src/components/Profile/DisplayProfileImg.tsx | 8 ++++-- src/components/Profile/DisplayUser.tsx | 11 ++++++-- src/components/organization/OrgDropDown.tsx | 18 ++++++++---- .../configuration-settings/CreateDid.tsx | 28 ++++++++++--------- 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/components/Profile/DisplayProfileImg.tsx b/src/components/Profile/DisplayProfileImg.tsx index bebfd4d53..9dd2ceff5 100644 --- a/src/components/Profile/DisplayProfileImg.tsx +++ b/src/components/Profile/DisplayProfileImg.tsx @@ -10,9 +10,11 @@ const DisplayProfileImg = () => { const getUserDetails = async () => { const userProfile = await getFromLocalStorage(storageKeys.USER_PROFILE) const orgRoles = await getFromLocalStorage(storageKeys.ORG_ROLES) - const parsedUser = userProfile ? JSON.parse(userProfile) : null; - parsedUser.roles = orgRoles - setUserObj(parsedUser) + if (userProfile) { + const parsedUser = JSON.parse(userProfile); + parsedUser.roles = orgRoles || []; + setUserObj(parsedUser); + } } useEffect(() => { diff --git a/src/components/Profile/DisplayUser.tsx b/src/components/Profile/DisplayUser.tsx index 83a704006..e69bd7d83 100644 --- a/src/components/Profile/DisplayUser.tsx +++ b/src/components/Profile/DisplayUser.tsx @@ -12,9 +12,14 @@ const DisplayUser = () => { const getUserDetails = async () => { const userProfile = await getFromLocalStorage(storageKeys.USER_PROFILE) const orgRoles = await getFromLocalStorage(storageKeys.ORG_ROLES) - const parsedUser = userProfile ? JSON.parse(userProfile) : null - parsedUser.roles = orgRoles - setUserObj(parsedUser) + if (userProfile) { + const parsedUser = JSON.parse(userProfile); + if (parsedUser) { + parsedUser.roles = orgRoles || []; + setUserObj(parsedUser); + } + } + } useEffect(() => { const fetchData = async () => { diff --git a/src/components/organization/OrgDropDown.tsx b/src/components/organization/OrgDropDown.tsx index f0b075124..1c2f9b70f 100644 --- a/src/components/organization/OrgDropDown.tsx +++ b/src/components/organization/OrgDropDown.tsx @@ -1,3 +1,6 @@ +import '../../common/global.css'; + +import type { IOrgInfo, Organisation } from './interfaces'; import { apiStatusCodes, storageKeys } from '../../config/CommonConstant.ts'; import { getFromLocalStorage, @@ -5,12 +8,11 @@ import { setToLocalStorage, } from '../../api/Auth'; import { useEffect, useState } from 'react'; -import '../../common/global.css'; + +import { AiOutlineSearch } from 'react-icons/ai'; import type { AxiosResponse } from 'axios'; import { BiChevronDown } from 'react-icons/bi'; -import { AiOutlineSearch } from 'react-icons/ai'; import CustomAvatar from '../Avatar/index.tsx'; -import type { IOrgInfo, Organisation } from './interfaces'; import { getOrganizations } from '../../api/organization.ts'; import { pathRoutes } from '../../config/pathRoutes.ts'; @@ -74,8 +76,14 @@ const OrgDropDown = () => { const handleActiveOrg = async (organizations: Organisation[]) => { let activeOrgDetails; const orgInfoDetails = await getFromLocalStorage(storageKeys.ORG_INFO); - activeOrgDetails = orgInfoDetails ? JSON.parse(orgInfoDetails) : null; - + + if (orgInfoDetails) { + try { + activeOrgDetails = JSON.parse(orgInfoDetails); + } catch (error) { + console.error('Error parsing orgInfoDetails from LocalStorage:', error); + } + } if (activeOrgDetails && Object.keys(activeOrgDetails)?.length > 0) { setActiveOrg(activeOrgDetails); } else if (organizations?.[0]) { diff --git a/src/components/organization/configuration-settings/CreateDid.tsx b/src/components/organization/configuration-settings/CreateDid.tsx index 2171810db..c6ab2d68e 100644 --- a/src/components/organization/configuration-settings/CreateDid.tsx +++ b/src/components/organization/configuration-settings/CreateDid.tsx @@ -1,21 +1,23 @@ import * as yup from 'yup'; + import { Button, Checkbox, Label, Modal } from 'flowbite-react'; +import { CommonConstants, DidMethod, Network } from '../../../common/enums'; +import type { EditOrgdetailsModalProps, IFormikValues } from '../interfaces'; import { Field, Form, Formik } from 'formik'; import type { FormikHelpers as FormikActions, FormikProps } from 'formik'; import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; +import { createDid, getOrganizationById } from '../../../api/organization'; import { useEffect, useRef, useState } from 'react'; + import { AlertComponent } from '../../AlertComponent'; import type { AxiosResponse } from 'axios'; -import { createDid, getOrganizationById } from '../../../api/organization'; -import type { EditOrgdetailsModalProps, IFormikValues } from '../interfaces'; -import { createPolygonKeyValuePair } from '../../../api/Agent'; -import { nanoid } from 'nanoid'; -import TokenWarningMessage from '../walletCommonComponents/TokenWarningMessage'; import CopyDid from '../../../commonComponents/CopyDid'; -import { getFromLocalStorage } from '../../../api/Auth'; -import { ethers } from 'ethers'; +import TokenWarningMessage from '../walletCommonComponents/TokenWarningMessage'; +import { createPolygonKeyValuePair } from '../../../api/Agent'; import { envConfig } from '../../../config/envConfig'; -import { CommonConstants, Network, DidMethod } from '../../../common/enums'; +import { ethers } from 'ethers'; +import { getFromLocalStorage } from '../../../api/Auth'; +import { nanoid } from 'nanoid'; interface IPolygonKeys { privateKey: string; @@ -276,7 +278,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { * { formikHandlers.handleChange(e); setLedgerValue(e.target.value); @@ -298,7 +300,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { >Method * { formikHandlers.handleChange(e); setMethod(e.target.value); @@ -323,7 +325,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { Network * { formikHandlers.handleChange(e); setNetworkValue(e.target.value); @@ -375,7 +377,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { id="did-method" disabled={true} name="did-method" - value={completeDidMethodValue} + value={completeDidMethodValue || ''} className="bg-gray-50 text-gray-600 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:placeholder-gray-400 dark:text-white h-11" placeholder="DID Method" readOnly @@ -466,7 +468,7 @@ const CreateDIDModal = (props: EditOrgdetailsModalProps) => { id="privatekey" name="privatekey" className="truncate bg-gray-50 border mt-2 border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 h-11" - value={formikHandlers.values.privatekey} + value={formikHandlers.values.privatekey || ''} onChange={(e) => { formikHandlers.setFieldValue('privatekey', e.target.value); setWalletErrorMessage(null);