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/22] [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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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/22] 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}

}