diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4255730db..294b49dcd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,9 +1,9 @@ -name: Deploy main to Deno Deploy +name: Deploy on: push: - branches: main + branches: develop pull_request: - branches: main + branches: develop jobs: deploy: @@ -16,24 +16,19 @@ jobs: steps: - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install Deno uses: denoland/setup-deno@v1 with: deno-version: v1.x - - name: Install Node.js - uses: actions/setup-node@v3 - with: - node-version: lts/* - - name: Build step - run: "npm install && npm run build" # 📝 Update the build command(s) if necessary + run: "npm install && npm run build" - name: Upload to Deno Deploy uses: denoland/deployctl@v1 with: - project: "credebl-studio" - entrypoint: "server/entry.mjs" # 📝 Update the entrypoint if necessary - root: "dist" # 📝 Update the root if necessary \ No newline at end of file + project: "credebl-dev-ui" + entrypoint: "server/entry.mjs" + root: "dist" diff --git a/Dockerfile b/Dockerfile index 56f54472e..16aadce3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,4 +27,4 @@ COPY --from=build /app/node_modules ./node_modules COPY --from=build /app/package.json ./ COPY --from=build /app/dist ./dist EXPOSE 3000 -CMD [ "npm", "run", "preview" ] \ No newline at end of file +CMD ["deno", "run", "--allow-net", "--allow-read", "--allow-env", "./dist/server/entry.mjs"] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 95b718644..aa86b780d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@faker-js/faker": "^7.6.0", - "@simplewebauthn/browser": "^7.2.0", + "@simplewebauthn/browser": "9.0.1", "@supabase/supabase-js": "^2.31.0", "@tanstack/react-form": "^0.0.11", "@types/react": "^18.2.14", @@ -1189,17 +1189,17 @@ } }, "node_modules/@simplewebauthn/browser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-7.2.0.tgz", - "integrity": "sha512-HHIvRPpqKy0UV/BsGAmx4rQRZuZTUFYLLH65FwpSOslqHruiHx3Ql/bq7A75bjWuJ296a+4BIAq3+SPaII01TQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-9.0.1.tgz", + "integrity": "sha512-wD2WpbkaEP4170s13/HUxPcAV5y4ZXaKo1TfNklS5zDefPinIgXOpgz1kpEvobAsaLPa2KeH7AKKX/od1mrBJw==", "dependencies": { - "@simplewebauthn/typescript-types": "*" + "@simplewebauthn/types": "^9.0.1" } }, - "node_modules/@simplewebauthn/typescript-types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/typescript-types/-/typescript-types-7.0.0.tgz", - "integrity": "sha512-bV+xACCFTsrLR/23ozHO06ZllHZaxC8LlI5YCo79GvU2BrN+rePDU2yXwZIYndNWcMQwRdndRdAhpafOh9AC/g==" + "node_modules/@simplewebauthn/types": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/types/-/types-9.0.1.tgz", + "integrity": "sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==" }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", @@ -13382,17 +13382,17 @@ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, "@simplewebauthn/browser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-7.2.0.tgz", - "integrity": "sha512-HHIvRPpqKy0UV/BsGAmx4rQRZuZTUFYLLH65FwpSOslqHruiHx3Ql/bq7A75bjWuJ296a+4BIAq3+SPaII01TQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-9.0.1.tgz", + "integrity": "sha512-wD2WpbkaEP4170s13/HUxPcAV5y4ZXaKo1TfNklS5zDefPinIgXOpgz1kpEvobAsaLPa2KeH7AKKX/od1mrBJw==", "requires": { - "@simplewebauthn/typescript-types": "*" + "@simplewebauthn/types": "^9.0.1" } }, - "@simplewebauthn/typescript-types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/typescript-types/-/typescript-types-7.0.0.tgz", - "integrity": "sha512-bV+xACCFTsrLR/23ozHO06ZllHZaxC8LlI5YCo79GvU2BrN+rePDU2yXwZIYndNWcMQwRdndRdAhpafOh9AC/g==" + "@simplewebauthn/types": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/types/-/types-9.0.1.tgz", + "integrity": "sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==" }, "@socket.io/component-emitter": { "version": "3.1.0", diff --git a/package.json b/package.json index 0fc1023cf..e9b678994 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@faker-js/faker": "^7.6.0", - "@simplewebauthn/browser": "^7.2.0", + "@simplewebauthn/browser": "9.0.1", "@supabase/supabase-js": "^2.31.0", "@tanstack/react-form": "^0.0.11", "@types/react": "^18.2.14", diff --git a/src/api/ecosystem.ts b/src/api/ecosystem.ts index 32aa96821..112476acc 100644 --- a/src/api/ecosystem.ts +++ b/src/api/ecosystem.ts @@ -257,3 +257,27 @@ export const getEcosystemMemberList = async ({ return err?.message; } }; + + +export const addOrganizationInEcosystem = async ( + data: string[], + ecosystemId: string, + orgId: string, +) => { + const url = `${apiRoutes.Ecosystem.root}/${ecosystemId}/${orgId}${apiRoutes.organizations.root}`; + const payload = { + organizationIds: data + }; + const axiosPayload = { + url, + payload, + config: await getHeaderConfigs(), + }; + + try { + return await axiosPost(axiosPayload); + } catch (error) { + const err = error as Error; + return err?.message; + } +}; diff --git a/src/api/issuance.ts b/src/api/issuance.ts index 8544d06ba..981702bfd 100644 --- a/src/api/issuance.ts +++ b/src/api/issuance.ts @@ -14,7 +14,7 @@ export const getIssuedCredentials = async ({page, sortingOrder, filter}: IConnectionListAPIParameter) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.getIssuedCredentials}?pageSize=${itemPerPage}&pageNumber=${page}&searchByText=${search}&sortBy=${sortingOrder}&sortField=${sortBy}`; + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Issuance.getIssuedCredentials}?pageSize=${itemPerPage}&pageNumber=${page}&search=${search}&sortBy=${sortingOrder}&sortField=${sortBy}`; const axiosPayload = { url, config: await getHeaderConfigs(), diff --git a/src/api/organization.ts b/src/api/organization.ts index 8a78fd7b3..30f87a683 100644 --- a/src/api/organization.ts +++ b/src/api/organization.ts @@ -10,6 +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'; export const createOrganization = async (data: object) => { const url = apiRoutes.organizations.create; @@ -65,8 +66,10 @@ export const getOrganizations = async ( pageNumber: number, pageSize: number, search = '', + role = '' ) => { - const url = `${apiRoutes.organizations.getAll}?pageNumber=${pageNumber}&pageSize=${pageSize}&search=${search}`; + const roleQuery = role ? `&role=${role}` : '' + const url = `${apiRoutes.organizations.getAll}?pageNumber=${pageNumber}&pageSize=${pageSize}&search=${search}${roleQuery}`; const token = await getFromLocalStorage(storageKeys.TOKEN); @@ -362,3 +365,62 @@ export const deleteOrganizationInvitation = async ( return err?.message; } }; + +export const getDids = async (orgId: string) => { + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.organizations.didList}`; + + const token = await getFromLocalStorage(storageKeys.TOKEN); + + const config = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + }; + const axiosPayload = { + url, + config, + }; + + try { + return await axiosGet(axiosPayload); + } catch (error) { + const err = error as Error; + return err?.message; + } +}; + +export const createDid = async (payload: any) => { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.organizations.createDid}`; + + const axiosPayload = { + url, + payload, + config: await getHeaderConfigs(), + }; + + try { + return await axiosPost(axiosPayload); + } catch (error) { + const err = error as Error; + return err?.message; + } +}; + +export const updatePrimaryDid = async (orgId: string, payload: IUpdatePrimaryDid) => { + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.organizations.primaryDid}`; + + const axiosPayload = { + url, + payload, + config: await getHeaderConfigs(), + }; + + try { + return await axiosPut(axiosPayload); + } catch (error) { + const err = error as Error; + return err?.message; + } +}; \ No newline at end of file diff --git a/src/api/verification.ts b/src/api/verification.ts index f47b654ec..cf32e8bd2 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -47,7 +47,7 @@ export const getVerificationList = async ({ sortingOrder, }: IConnectionListAPIParameter) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Verification.verifyCredential}?pageSize=${itemPerPage}&pageNumber=${page}&searchByText=${search}&sortBy=${sortingOrder}&sortField=${sortBy}`; + const url = `${apiRoutes.organizations.root}/${orgId}${apiRoutes.Verification.verifyCredential}?pageSize=${itemPerPage}&pageNumber=${page}&search=${search}&sortBy=${sortingOrder}&sortField=${sortBy}`; const axiosPayload = { url, diff --git a/src/app/constants.ts b/src/app/constants.ts index c2f668a52..000ad46cf 100644 --- a/src/app/constants.ts +++ b/src/app/constants.ts @@ -55,3 +55,4 @@ export const RANDOMIZE = Boolean(env.RANDOMIZE) || true; // ], // }, // ]; + diff --git a/src/commonComponents/datatable/SortDataTable.tsx b/src/commonComponents/datatable/SortDataTable.tsx index 3791ab2ab..e4a963677 100644 --- a/src/commonComponents/datatable/SortDataTable.tsx +++ b/src/commonComponents/datatable/SortDataTable.tsx @@ -2,7 +2,7 @@ import type { IDataTable } from './interface'; import CustomSpinner from '../../components/CustomSpinner'; import SearchInput from '../../components/SearchInput'; import { Pagination } from 'flowbite-react'; -import { ChangeEvent, useState } from 'react'; +import { useState } from 'react'; import { EmptyListMessage } from '../../components/EmptyListComponent'; const SortDataTable: React.FC = ({ @@ -28,13 +28,14 @@ const SortDataTable: React.FC = ({ discription, noExtraHeight, sortOrder, + itemPerPage }) => { const [selectedValue, setSelectedValue] = useState(sortOrder ?? ''); const handleSortByValues = (event: { target: { value: any } }) => { const newSelectedValue = event.target.value; setSelectedValue(newSelectedValue); - if(searchSortByValue){ + if (searchSortByValue) { searchSortByValue(newSelectedValue); } }; @@ -48,9 +49,8 @@ const SortDataTable: React.FC = ({ nextPage?: number; lastPage?: number; }; - - const startItem = (nextPage - 2) * 10 + 1; - const endItem = Math.min((nextPage - 1) * 10, totalItem); + const startItem = (nextPage - 2) * (itemPerPage || 10) + 1; + const endItem = Math.min((nextPage - 1) * (itemPerPage || 10), totalItem); const sortValues = [ { @@ -177,13 +177,12 @@ const SortDataTable: React.FC = ({ ) : ( - {data.length ? ( - data.map((ele, index) => ( + {data?.length ? ( + data?.map((ele, index) => ( {ele.data.map((subEle, index) => ( void; totalPages: number; - searchSortByValue: (value: any) => void; + searchSortByValue?: (value: string) => void; isPagination?: boolean; isSearch: boolean; isRefresh: boolean; @@ -47,4 +47,5 @@ export interface IDataTable { } | {}; sortOrder?:string; + itemPerPage?: number; } diff --git a/src/components/AddOrganizationInEcosystem.tsx b/src/components/AddOrganizationInEcosystem.tsx new file mode 100644 index 000000000..e96d9e1fd --- /dev/null +++ b/src/components/AddOrganizationInEcosystem.tsx @@ -0,0 +1,371 @@ +'use client'; + +import type { AxiosResponse } from 'axios'; +import { ChangeEvent, useEffect, useState } from 'react'; +import type { TableData } from '../commonComponents/datatable/interface'; +import { apiStatusCodes, storageKeys } from '../config/CommonConstant'; +import { AlertComponent } from './AlertComponent'; +import BreadCrumbs from './BreadCrumbs'; +import { getFromLocalStorage, removeFromLocalStorage, setToLocalStorage } from '../api/Auth'; +import SortDataTable from '../commonComponents/datatable/SortDataTable'; +import { getOrganizations } from '../api/organization'; +import CustomAvatar from '../components/Avatar'; + +import type { Organisation } from '../components/organization/interfaces'; +import { Roles } from '../utils/enums/roles'; +import { Button } from 'flowbite-react'; +import { addOrganizationInEcosystem } from '../api/ecosystem'; +import { pathRoutes } from '../config/pathRoutes'; + + +const initialPageState = { + page: 1, + search: '', + sortingOrder: 'desc', + pageSize: 10, + role: Roles.OWNER +}; + +interface IErrorOrg { + id: string; + error: string; +} + +interface IErrorResponse { + statusCode: number; + message: string; + data?: { + orgId: string; + } + error?: string; +} + +interface ICurrentPage { + page: number; + pageSize: number; + search: string; + role: string; +} + +const AddOrganizationInEcosystem = () => { + const [listAPIParameter, setListAPIParameter] = useState(initialPageState); + const [errorList, setErrorList] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(null); + const [localOrgs, setLocalOrgs] = useState([]); + const [pageInfo, setPageInfo] = useState({ + totalItem: 0, + nextPage: 0, + lastPage: 0, + }); + const [totalPages, setTotalPages] = useState(0); + const [loader, setLoader] = useState(false); + const [organizationsList, setOrganizationsList] = useState | null>(null); + const [tableData, setTableData] = useState([]) + + const selectOrganization = async (item: Organisation, checked: boolean) => { + try { + const index = localOrgs?.length > 0 ? localOrgs.findIndex(ele => ele === item.id) : -1 + + if (index === -1) { + setLocalOrgs((prev: string[]) => [...prev, item.id]) + } else { + const updateLocalOrgs = [...localOrgs] + if (!checked) { + updateLocalOrgs.splice(index, 1); + } + setLocalOrgs(updateLocalOrgs) + } + } catch (error) { + throw new Error(`SELECTED ORGANIZATION:::${error}`); + } + } + + const generateTable = async (organizationsList: Organisation[] | null) => { + const id = await getFromLocalStorage(storageKeys.ECOSYSTEM_ID); + const connections = organizationsList && organizationsList?.length > 0 && organizationsList?.map((ele: Organisation) => { + const isChecked = localOrgs.includes(ele.id) + const alreadyAdded = ele.ecosystemOrgs?.some(item => item.ecosystemId === id) + const title = alreadyAdded ? "Already exists in the ecosystem" : "" + const error = errorList.find(item => item.id === ele.id)?.error || ele.error; + + return { + data: [ + { + data: ( +
+ ) => { + const inputElement = event.target as HTMLInputElement; + + const updateOrgList: Organisation[] = organizationsList?.map(item => { + if (item.id === ele.id) { + selectOrganization(item, inputElement.checked) + return { + ...item, + checked: inputElement.checked + } + } + return item + }) + setOrganizationsList(updateOrgList) + }} + disabled={alreadyAdded} + checked={(ele.checked || isChecked) && !alreadyAdded} + className={`w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded-lg dark:ring-offset-gray-800 dark:bg-gray-700 dark:border-gray-600 disabled:opacity-100 ${alreadyAdded ? "cursor-not-allowed" : "cursor-pointer"}`} + /> +
+ ), + }, + { + data: ( +
+
+ {(ele?.logoUrl) ? + : + } +
+
+ {ele.name} +
+
+ ) + }, + { data: (
{ele.id}
) }, + { + data: ( +
+ { + ele?.roles?.length > 0 && ele?.roles?.map(item => ( + + {item} + + )) + } +
+ ), + }, + { + data: ( +
+ { +
{error || "-"}
+ } +
+ ), + } + ], + }; + }); + setTableData(connections); + } + + useEffect(() => { + generateTable(organizationsList); + }, [organizationsList, localOrgs]) + + const getOwnerOrganizations = async (currentPage: ICurrentPage) => { + setLoading(true); + const response = await getOrganizations( + currentPage.page, + currentPage.pageSize, + currentPage.search, + currentPage.role + ); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const totalPages = data?.data?.totalPages; + const orgList = data?.data?.organizations.map((userOrg: Organisation) => { + const roles: string[] = userOrg.userOrgRoles.map( + (role) => role.orgRole.name, + ); + userOrg.roles = roles; + return userOrg; + }); + setPageInfo({ + ...pageInfo, + totalItem: data?.data?.totalCount, + lastPage: data?.data?.totalPages, + nextPage: listAPIParameter?.page + 1 + }) + setOrganizationsList(orgList); + setTotalPages(totalPages); + } else { + setError(response as string); + } + setLoading(false); + }; + + const header = [ + { columnName: '', width: 'w-0.5' }, + { columnName: 'Organization' }, + { columnName: 'Id' }, + { columnName: 'Role(s)' }, + { columnName: 'Error' }, + ]; + + //onChange of Search input text + const searchInputChange = (e: ChangeEvent) => { + setListAPIParameter({ + ...listAPIParameter, + search: e.target.value, + page: 1, + }); + }; + + const refreshPage = () => { + getOwnerOrganizations(listAPIParameter); + }; + + const updateLocalOrgs = async () => { + const res = await getFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM) + const selectedOrg = res ? JSON.parse(res) : [] + setLocalOrgs(selectedOrg); + + const err = await getFromLocalStorage(storageKeys.ERROR_ORG_IN_ECOSYSTEM) + const errOrgs = err ? JSON.parse(err) : [] + setErrorList(errOrgs); + } + + const handleAddOrganization = async () => { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID) || ""; + const ecosystemId = await getFromLocalStorage(storageKeys.ECOSYSTEM_ID) || ""; + setLoader(true) + try { + const response = await addOrganizationInEcosystem(localOrgs, ecosystemId, orgId); + const { data } = response as AxiosResponse; + setLoader(false) + switch (data?.statusCode) { + case apiStatusCodes.API_STATUS_CREATED: + await removeFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM) + setSuccess(data.message) + setTimeout(() => { + window.location.href = pathRoutes.ecosystem.dashboard; + }, 1000); + break; + case apiStatusCodes.API_STATUS_PARTIALLY_COMPLETED: + await removeFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM) + const errors = data?.data?.filter((item: IErrorResponse) => item.statusCode !== apiStatusCodes.API_STATUS_CREATED) + const errorData = errors.map((item: IErrorResponse) => ({ id: item?.data?.orgId || "", error: item.message })) + await setToLocalStorage(storageKeys.ERROR_ORG_IN_ECOSYSTEM, JSON.stringify(errorData)) + setErrorList(errorData) + const updateWithError = organizationsList && organizationsList?.length > 0 ? organizationsList?.map((item => ({ + ...item, + error: errors?.find((ele: IErrorResponse) => ele?.data?.orgId === item.id)?.message || "" + }))) : [] + setSuccess(data?.message); + setOrganizationsList(updateWithError) + break; + default: + setError(response as string || data?.message) + break; + } + } catch (error) { + setError(error.message as string) + setLoader(false) + } + } + + useEffect(() => { + getOwnerOrganizations(listAPIParameter); + updateLocalOrgs() + }, [listAPIParameter]); + + useEffect(() => { + updateLocalOrgs(); + (async () => { + await removeFromLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM); + await removeFromLocalStorage(storageKeys.ERROR_ORG_IN_ECOSYSTEM); + })() + }, []) + + useEffect(() => { + (async () => { + await setToLocalStorage(storageKeys.SELECT_ORG_IN_ECOSYSTEM, JSON.stringify(localOrgs)) + })() + }, [localOrgs]) + + return ( +
+ +
+

+ Add Organizations +

+
+ {(error || success) && ( + { + setError(null); + setSuccess(null); + }} + /> + )} + { + setListAPIParameter((prevState) => ({ + ...prevState, + page, + })); + }} + totalPages={totalPages} + pageInfo={pageInfo} + isHeader={true} + isSearch={true} + isRefresh={true} + isSort={false} + message={'No Organizations'} + discription={"You don't have any Organization to add"} + itemPerPage={listAPIParameter.pageSize} + > +
+ +
+
+ ); +}; + +export default AddOrganizationInEcosystem; diff --git a/src/components/Authentication/Svg.tsx b/src/components/Authentication/Svg.tsx index b7fab88e2..f0fab302e 100644 --- a/src/components/Authentication/Svg.tsx +++ b/src/components/Authentication/Svg.tsx @@ -1,5 +1,5 @@ export const PassVisible = () => ( - { {ecosystemDetails ? (
{isEcosystemLead ? ( +
-
-
- {ecosystemDetails?.logoUrl ? ( - - ) : ( - - )} -
-
-
-

- {ecosystemDetails?.name} -

-

- {ecosystemDetails?.description} -

-
- - Role:{' '} - {' '} - -
-
- - Endorsement Flow - {' '} - : - - {ecosystemDetails.autoEndorsement - ? ' Sign and Submit' - : ' Sign'} - -
+ > +
+ {ecosystemDetails?.logoUrl ? ( + + ) : ( + + )} +
+
+
+

+ {ecosystemDetails?.name} +

+

+ {ecosystemDetails?.description} +

+
+ + Role:{' '} + {' '} +
-
-
+
+
+ - {dropdownOpen && isAccess ? ( - ( - - )} - dismissOnClick={true} - > - { - EditEcosystemOrgModal(); - }} + + + + My Organizations + + + + {dropdownOpen && isAccess ? ( + ( +
Edit Ecosystem
-
-
- ) : ( - + + )} + dismissOnClick={true} + > + { + EditEcosystemOrgModal(); + }} > - - - )} -
+
Edit Ecosystem
+ + + ) : ( + + )}
+
) : ( +
+
+
+ {ecosystemDetails?.logoUrl ? ( + + ) : ( + + )} +
+
-
-
- {ecosystemDetails?.logoUrl ? ( - - ) : ( - - )} +

+ {ecosystemDetails?.name} +

+

+ {ecosystemDetails?.description} +

+
+
+ + Ecosystem Owner + + :{' '} + + {leadOrg} +
-
-
-

- {ecosystemDetails?.name} -

-

- {ecosystemDetails?.description} -

-
-
- - Ecosystem Owner - - :{' '} - - {leadOrg} - -
-
- - Ecosystem Lead - {' '} - : - - {leadOrg} - -
-
- - Joined since - {' '} - : - - - {dateConversion(ecosystemDetails.joinedDate || '')} - - -
+
+ + Ecosystem Lead + {' '} + : + + {leadOrg} +
- Endorsement Flow + Joined since {' '} : - {ecosystemDetails.autoEndorsement - ? ' Sign and Submit' - : ' Sign'} + + {dateConversion(ecosystemDetails.joinedDate || '')} +
-
- Role: {' '} - +
+ + Endorsement Flow + {' '} + : + + {ecosystemDetails.autoEndorsement + ? ' Sign and Submit' + : ' Sign'} +
+
+ Role: {' '} + +
+
)}
@@ -526,11 +548,10 @@ const Dashboard = () => { { }; const getEcosystemMembers = async (apiParameter: IMemberListAPIParameter) => { - const userOrgId = await getFromLocalStorage(storageKeys.ORG_ID); + const userProfile = await getFromLocalStorage(storageKeys.USER_PROFILE) + const parsedUserProfileData = JSON.parse(userProfile); + const userId = parsedUserProfileData.id; + setLoading(true); const response = await getEcosystemMemberList(apiParameter); const { data } = response as AxiosResponse; @@ -151,7 +155,8 @@ const MemberList = () => { > {member?.ecosystemRole?.name} - {member?.orgId === userOrgId ? '(You)' : ''} + {member.organisation.userOrgRoles.some((item) => item.userId === userId) ? '(You)' : ''} + ) : ( 'Not available' diff --git a/src/components/Issuance/ConnectionList.tsx b/src/components/Issuance/ConnectionList.tsx index 253faba28..d0bea4c98 100644 --- a/src/components/Issuance/ConnectionList.tsx +++ b/src/components/Issuance/ConnectionList.tsx @@ -7,12 +7,13 @@ import { getConnectionsByOrg, } from '../../api/connection'; import type { TableData } from '../../commonComponents/datatable/interface'; -import { apiStatusCodes } from '../../config/CommonConstant'; +import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; import { AlertComponent } from '../AlertComponent'; import { dateConversion } from '../../utils/DateConversion'; import DateTooltip from '../Tooltip'; import type { IConnectionList } from './interface'; import NewDataTable from '../../commonComponents/datatable/SortDataTable'; +import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; const initialPageState = { itemPerPage: 10, @@ -22,14 +23,20 @@ const initialPageState = { sortingOrder: 'desc', allSearch: '', }; + +type LocalOrgs = { + connectionId: string; + theirLabel: string; + createDateTime: string; +} + const ConnectionList = (props: { - selectConnection: (connections: TableData[]) => void; + selectConnection: (connections: IConnectionList[]) => void; }) => { const [listAPIParameter, setListAPIParameter] = useState(initialPageState); - const [connectionList, setConnectionList] = useState([]); - const [selectedConnectionList, setSelectedConnectionList] = useState< - TableData[] - >([]); + const [tableData, setTableData] = useState([]); + const [connectionList, setConnectionList] = useState([]) + const [localOrgs, setLocalOrgs] = useState([]); const [loading, setLoading] = useState(false); const [totalItem, setTotalItem] = useState(0); @@ -40,19 +47,91 @@ const ConnectionList = (props: { lastPage: '', }); - useEffect(() => { - let getData: NodeJS.Timeout; + const selectOrganization = async (item: IConnectionList, checked: boolean) => { + try { + const index = localOrgs?.length > 0 ? localOrgs.findIndex(ele => ele.connectionId === item.connectionId) : -1 - if (listAPIParameter?.search?.length >= 1) { - getData = setTimeout(() => { - getConnections(listAPIParameter); - }, 1000); - return () => clearTimeout(getData); - } else { - getConnections(listAPIParameter); + const { connectionId, theirLabel, createDateTime } = item || {}; + if (index === -1) { + setLocalOrgs((prev: LocalOrgs[]) => [...prev, { + connectionId, + theirLabel, + createDateTime + }]) + } else { + const updateLocalOrgs = [...localOrgs] + if (!checked) { + updateLocalOrgs.splice(index, 1); + } + setLocalOrgs(updateLocalOrgs) + } + } catch (error) { + console.error("SELECTED ORGANIZATION:::", error) } - return () => clearTimeout(getData); - }, [listAPIParameter]); + } + + const generateTable = async (connections: IConnectionList[]) => { + try { + const connectionsData = connections?.length > 0 && connections?.map((ele: IConnectionList) => { + const createdOn = ele?.createDateTime + ? ele?.createDateTime + : 'Not available'; + const connectionId = ele.connectionId + ? ele.connectionId + : 'Not available'; + const userName = ele?.theirLabel ? ele.theirLabel : 'Not available'; + + const isChecked = localOrgs.map(item => item.connectionId).includes(ele.connectionId) + + return { + data: [ + { + data: ( +
+ ) => { + const inputElement = event.target as HTMLInputElement; + + const updateConnectionList = connections?.map(item => { + if (item.connectionId === ele.connectionId) { + selectOrganization(item, inputElement.checked) + return { + ...item, + checked: inputElement.checked + } + } + return item + }) + setConnectionList(updateConnectionList) + }} + checked={ele.checked || isChecked} + className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded-lg dark:ring-offset-gray-800 dark:bg-gray-700 dark:border-gray-600 cursor-pointer" + /> +
+ ), + }, + { data: userName }, + { data: connectionId }, + { + data: ( + + {' '} + {dateConversion(createdOn)}{' '} + + ), + }, + ], + }; + }); + + setTableData(connectionsData) + } catch (err) { + + } + } const getConnections = async (apiParameter: IConnectionListAPIParameter) => { setLoading(true); @@ -63,58 +142,12 @@ const ConnectionList = (props: { if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { const { totalItems, nextPage, lastPage } = data.data; setTotalItem(data?.data.totalItems); - setPageInfo({ totalItem: totalItems, nextPage: nextPage, lastPage: lastPage, }); - const connections = data?.data?.data?.map((ele: IConnectionList) => { - const createdOn = ele?.createDateTime - ? ele?.createDateTime - : 'Not available'; - const connectionId = ele.connectionId - ? ele.connectionId - : 'Not available'; - const userName = ele?.theirLabel ? ele.theirLabel : 'Not available'; - return { - data: [ - { - data: ( -
- ) => { - const inputElement = event.target as HTMLInputElement; - selectConnection( - userName, - connectionId, - inputElement.checked, - ); - }} - value="" - className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded-lg dark:ring-offset-gray-800 dark:bg-gray-700 dark:border-gray-600 cursor-pointer" - /> -
- ), - }, - { data: userName }, - { data: connectionId }, - { - data: ( - - {' '} - {dateConversion(createdOn)}{' '} - - ), - }, - ], - }; - }); - - setConnectionList(connections); + setConnectionList(data?.data?.data); setError(null); } else { setConnectionList([]); @@ -143,45 +176,6 @@ const ConnectionList = (props: { }); }; - const selectConnection = ( - user: string, - connectionId: string, - checked: boolean, - ) => { - if (checked) { - // Needed for multiple connection selection - // setSelectedConnectionList((prevList) => [...prevList, { - // data: [ - // { - // data: user, - // }, { - // data: connectionId, - // }] - // }] - // ) - - // It is for single connection selection - setSelectedConnectionList([ - { - data: [ - { - data: user, - }, - { - data: connectionId, - }, - ], - }, - ]); - } else { - setSelectedConnectionList((prevList) => - prevList.filter( - (connection) => connection.data[1].data !== connectionId, - ), - ); - } - }; - const searchSortByValue = (value: any) => { setListAPIParameter({ ...listAPIParameter, @@ -191,13 +185,46 @@ const ConnectionList = (props: { }; const refreshPage = () => { - setSelectedConnectionList([]); getConnections(listAPIParameter); }; + const updateLocalOrgs = async () => { + const res = await getFromLocalStorage(storageKeys.SELECTED_CONNECTIONS) + const selectedOrg = res ? JSON.parse(res) : [] + setLocalOrgs(selectedOrg); + } + + useEffect(() => { + props.selectConnection(localOrgs); + }, [localOrgs]); + + useEffect(() => { + generateTable(connectionList); + }, [connectionList, localOrgs]) + + useEffect(() => { + (async () => { + await setToLocalStorage(storageKeys.SELECTED_CONNECTIONS, JSON.stringify(localOrgs)) + })() + }, [localOrgs]) + + useEffect(() => { + let getData: NodeJS.Timeout; + updateLocalOrgs() + if (listAPIParameter?.search?.length >= 1) { + getData = setTimeout(() => { + getConnections(listAPIParameter); + }, 1000); + return () => clearTimeout(getData); + } else { + getConnections(listAPIParameter); + } + return () => clearTimeout(getData); + }, [listAPIParameter]); + useEffect(() => { - props.selectConnection(selectedConnectionList); - }, [selectedConnectionList]); + updateLocalOrgs() + }, []) return (
@@ -224,7 +251,7 @@ const ConnectionList = (props: { onInputChange={searchInputChange} refresh={refreshPage} header={header} - data={connectionList} + data={tableData} loading={loading} currentPage={listAPIParameter?.page} onPageChange={(page: number) => { diff --git a/src/components/Issuance/Connections.tsx b/src/components/Issuance/Connections.tsx index 319f549e4..bf03cf8f9 100644 --- a/src/components/Issuance/Connections.tsx +++ b/src/components/Issuance/Connections.tsx @@ -10,26 +10,58 @@ import { pathRoutes } from '../../config/pathRoutes'; import BreadCrumbs from '../BreadCrumbs'; import ConnectionList from './ConnectionList'; import BackButton from '../../commonComponents/backbutton'; +import type { IConnectionList } from './interface'; +import DateTooltip from '../Tooltip'; +import { dateConversion } from '../../utils/DateConversion'; const Connections = () => { - const [selectedConnectionList, setSelectedConnectionList] = useState< + const [selectedConnections, setSelectedConnections] = useState< TableData[] >([]); const selectedConnectionHeader = [ { columnName: 'User' }, { columnName: 'Connection ID' }, + { columnName: 'Created on' } ]; - const selectConnection = (connections: TableData[]) => { - setSelectedConnectionList(connections); + const selectConnection = (connections: IConnectionList[]) => { + try { + const connectionsData = connections?.length > 0 && connections?.map((ele: IConnectionList) => { + const createdOn = ele?.createDateTime + ? ele?.createDateTime + : 'Not available'; + const connectionId = ele.connectionId + ? ele.connectionId + : 'Not available'; + const userName = ele?.theirLabel ? ele.theirLabel : 'Not available'; + + return { + data: [ + { data: userName }, + { data: connectionId }, + { + data: ( + + {' '} + {dateConversion(createdOn)}{' '} + + ), + }, + ], + }; + }) + setSelectedConnections(connectionsData); + } catch (error) { + console.log("ERROR IN TABLE GENERATION::", error); + } }; const continueToIssue = async () => { - const selectedConnections = selectedConnectionList.map((ele) => { + const selectedConnectionData = selectedConnections.map((ele) => { return { userName: ele.data[0].data, connectionId: ele.data[1].data }; }); - await setToLocalStorage(storageKeys.SELECTED_USER, selectedConnections); + await setToLocalStorage(storageKeys.SELECTED_USER, selectedConnectionData); window.location.href = `${pathRoutes.organizations.Issuance.issuance}`; }; @@ -62,20 +94,6 @@ const Connections = () => { Connection - {/* Keep this code as it is, this is required in future use. */} - {/*
  • - -
  • */}
    @@ -86,7 +104,7 @@ const Connections = () => { aria-labelledby="profile-tab" > -
    +

    Selected Users

    @@ -94,10 +112,10 @@ const Connections = () => {
    - {selectedConnectionList.length ? ( + {selectedConnections.length ? (
    - {/* Keep this code as it is, this is required in future use. */} - {/*
    - -
    */}
    ); diff --git a/src/components/Issuance/Issuance.tsx b/src/components/Issuance/Issuance.tsx index 95adae670..694c03737 100644 --- a/src/components/Issuance/Issuance.tsx +++ b/src/components/Issuance/Issuance.tsx @@ -3,9 +3,9 @@ import * as Yup from 'yup'; import { Alert, Button, Card } from 'flowbite-react'; -import { Field, Form, Formik } from 'formik'; +import { Field, FieldArray, Form, Formik } from 'formik'; import { apiStatusCodes, storageKeys } from '../../config/CommonConstant'; -import { getFromLocalStorage } from '../../api/Auth'; +import { getFromLocalStorage, removeFromLocalStorage, setToLocalStorage } from '../../api/Auth'; import React, { useEffect, useState } from 'react'; import BackButton from '../../commonComponents/backbutton'; import type { AxiosResponse } from 'axios'; @@ -71,58 +71,76 @@ const IssueCred = () => { credDefId: string, orgId: string, ) => { - const attrObj = attributes.map((attr) => ({ - name: attr?.attributeName, - value: '', - dataType: attr?.schemaDataType, - isRequired: attr?.isRequired, - })); - const issuancePayload = selectedUsers.map((user) => { + + const credentialData = selectedUsers.map((user) => { + const attributesArray = attributes.map((attr) => ({ + name: attr.attributeName, + value: '', + dataType: attr?.schemaDataType, + isRequired: attr.isRequired + })); + return { - connectionId: user?.connectionId, - attributes: attrObj, - credentialDefinitionId: credDefId, - orgId, + connectionId: user.connectionId, + attributes: attributesArray, }; }); + + const issuancePayload = { + credentialData, + credentialDefinitionId: credDefId, + orgId, + }; + setIssuanceFormPayload(issuancePayload); setUserLoader(false); }; - + const createAttributeValidationSchema = ( - dataType: string, - isRequired: boolean, + name: string, + value: string, + isRequired: boolean ) => { - let attributeSchema; + let attributeSchema = Yup.string();; + if (name) { + name = name + .split('_') + .map(item => item.charAt(0).toUpperCase() + item.slice(1)) + .join(' '); + } + if (isRequired) { - attributeSchema = Yup.string().required('This field is required'); - } else if (dataType === 'string') { - attributeSchema = Yup.string().typeError('Value must be a string'); - } else if (dataType === 'number') { - attributeSchema = Yup.number().typeError('Value must be a number'); - } else if (dataType === 'date') { - attributeSchema = Yup.date().typeError('Value must be a valid date'); - } else { - attributeSchema = Yup.mixed(); + if (!value) { + attributeSchema = Yup.string().required(`${name} is required`); + } } + return Yup.object().shape({ value: attributeSchema, }); }; const validationSchema = Yup.object().shape({ - attributes: Yup.array().of( - Yup.lazy(({ dataType, isRequired }) => - createAttributeValidationSchema(dataType, isRequired), - ), - ), - }); + credentialData: Yup.array().of( + Yup.object().shape({ + attributes: Yup.array().of( + Yup.lazy((attr) => { + return createAttributeValidationSchema(attr?.name, attr?.value, attr?.isRequired) + }), + ), + }), + ), + }); + const getSchemaDetails = async (): Promise => { const schemaAttributes = await getFromLocalStorage(storageKeys.SCHEMA_ATTR); + const parsedSchemaAttributes = JSON.parse(schemaAttributes) || []; + setSchemaAttributesDetails(parsedSchemaAttributes?.attribute); + return parsedSchemaAttributes.attribute; }; @@ -143,29 +161,40 @@ const IssueCred = () => { }; const handleSubmit = async (values: IssuanceFormPayload) => { - const convertedAttributes = values?.attributes.map((attr) => ({ - ...attr, - value: String(attr.value), - })); + const issuancePayload = { + credentialData: values.credentialData.map(item => { + return { + ...item, + attributes: item.attributes.map(attr => ({ + name: attr.name, + value: attr.value.toString() + })) + } + }), + credentialDefinitionId: values.credentialDefinitionId, + orgId: values.orgId + }; const convertedAttributesValues = { - ...values, - attributes: convertedAttributes, + ...issuancePayload, }; - + setIssuanceLoader(true); const issueCredRes = await issueCredential(convertedAttributesValues); + const { data } = issueCredRes as AxiosResponse; - + if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { setSuccess(data?.message); window.location.href = `${pathRoutes.organizations.issuedCredentials}`; + await removeFromLocalStorage(storageKeys.SELECTED_CONNECTIONS); + await removeFromLocalStorage(storageKeys.SELECTED_USER); } else { setFailure(issueCredRes as string); setIssuanceLoader(false); } }; - + return (
    @@ -200,137 +229,210 @@ const IssueCred = () => {
    ) : ( <> - {issuanceFormPayload?.length - ? issuanceFormPayload?.map((user) => ( - - {({ values, errors, touched, isValid }) => ( -
    - -
    -
    - {user.userName} -
    -
    -
    -
    - Connection Id -
    - - : - -

    - {user?.connectionId} -

    -
    -

    Attributes

    -
    -
    - {schemaAttributesDetails && - schemaAttributesDetails?.length > 0 && - schemaAttributesDetails?.map((attr, index) => ( -
    +
    + + {({ values, errors, touched, isValid }) => ( + + {failure && ( +
    + setFailure(null)}> + +

    {failure}

    +
    +
    +
    + )} + + {(arrayHelpers) => ( + <> + {values?.credentialData.map((user, index) => ( +
    + +
    +
    +
    + Connection Id +
    + + : + +

    + {user?.connectionId} +

    +
    + {values.credentialData.length > 1 && (
    - -
    - - {errors?.attributes && - errors?.attributes[index] && - touched?.attributes && - touched?.attributes[index] && - errors?.attributes[index]?.value && ( -
    - {errors?.attributes[index]?.value} -
    - )} -
    + + + +
    + )} +
    + +

    Attributes

    +
    +
    + {schemaAttributesDetails && + schemaAttributesDetails?.length > 0 && + schemaAttributesDetails?.map( + (attr, attrIndex) => ( +
    +
    + +
    + { + try { + Yup.reach( + validationSchema, + `credentialData.${index}.attributes.${attrIndex}.value`, + ).validateSync(value, { + abortEarly: false, + }); + } catch (error) { + return error.message; + } + }} + /> + {errors?.credentialData?.[index] + ?.attributes?.[attrIndex] + ?.value && + touched?.credentialData?.[index] + ?.attributes?.[attrIndex] + ?.value && ( +
    + { + errors?.credentialData?.[ + index + ]?.attributes?.[attrIndex] + ?.value + } +
    + )}{' '} +
    +
    +
    + ), + )}
    - ))} +
    +
    -
    - - {failure && ( -
    - setFailure(null)} - > - -

    {failure}

    -
    -
    -
    - )} -
    - + + +
    - - )} - - )) - : ''} + Issue + +
    + + )} + +
    )}
    diff --git a/src/components/Issuance/interface.ts b/src/components/Issuance/interface.ts index 72179bcd7..923810aff 100644 --- a/src/components/Issuance/interface.ts +++ b/src/components/Issuance/interface.ts @@ -60,6 +60,7 @@ export interface IConnectionList { theirLabel: string; connectionId: string; createDateTime: string; + checked?: boolean; } export interface SchemaDetails { @@ -80,10 +81,14 @@ export interface Attributes { value: string; dataType: string; } -export interface IssuanceFormPayload { - userName?: string; + +export interface ICredentialdata { connectionId: string; attributes: Attributes[]; +} +export interface IssuanceFormPayload { + userName?: string; + credentialData: ICredentialdata[]; credentialDefinitionId: string; orgId: string; } @@ -95,7 +100,7 @@ export interface DataTypeAttributes { } export interface Attribute { - isRequired: string; + isRequired: string; attributeName: string; schemaDataType: string; displayName: string; diff --git a/src/components/Verification/Verification.tsx b/src/components/Verification/Verification.tsx index 7ed5c107a..d9abcb51e 100644 --- a/src/components/Verification/Verification.tsx +++ b/src/components/Verification/Verification.tsx @@ -161,13 +161,16 @@ const VerificationCred = () => { schemaId: schemaId, })); - const verifyCredentialPayload: VerifyCredentialPayload = { + const verifyCredentialPayload = { connectionId: JSON.parse(userData)[0]?.connectionId, - attributes: attributes, comment: 'string', orgId: orgId, + proofFormats: { + indy: { + attributes: attributes, + } + } }; - if (attributes) { const response = await verifyCredential(verifyCredentialPayload); const { data } = response as AxiosResponse; diff --git a/src/components/organization/EditOrgdetailsModal.tsx b/src/components/organization/EditOrgdetailsModal.tsx index ca25fdbc9..71ba96ce2 100644 --- a/src/components/organization/EditOrgdetailsModal.tsx +++ b/src/components/organization/EditOrgdetailsModal.tsx @@ -13,6 +13,15 @@ import defaultUserIcon from '../../../public/images/person_FILL1_wght400_GRAD0_o import { processImage } from '../../utils/processImage'; import FormikErrorMessage from '../../commonComponents/formikerror/index' +interface IUpdateOrgPayload { + orgId: string | undefined; + name: string; + description: string; + website: string; + isPublic: boolean | undefined; + logo?: string; +} + const EditOrgdetailsModal = (props: EditOrgdetailsModalProps) => { const [logoImage, setLogoImage] = useState({ logoFile: '', @@ -82,14 +91,20 @@ const EditOrgdetailsModal = (props: EditOrgdetailsModalProps) => { const submitUpdateOrganization = async (values: Values) => { setLoading(true); - const orgData = { + const orgData: IUpdateOrgPayload = { orgId: props?.orgData?.id, name: values.name, description: values.description, - logo: (logoImage?.imagePreviewUrl as string) || props?.orgData?.logoUrl, website: values.website, isPublic: isPublic, }; + + const logo = (logoImage?.imagePreviewUrl as string) || props?.orgData?.logoUrl + + if ((logo?.includes('data:image/') && logo?.includes(';base64'))) { + orgData['logo'] = logo; + } + try { const response = await updateOrganization( orgData, diff --git a/src/components/organization/OrganizationDetails.tsx b/src/components/organization/OrganizationDetails.tsx index 30ef3bbe9..8167f6b15 100644 --- a/src/components/organization/OrganizationDetails.tsx +++ b/src/components/organization/OrganizationDetails.tsx @@ -11,6 +11,7 @@ import DateTooltip from '../Tooltip'; import CopyDid from '../../commonComponents/CopyDid'; import { setToLocalStorage } from '../../api/Auth'; import { Tooltip } from 'flowbite-react'; +import DIDList from './configuration-settings/DidList'; const OrganizationDetails = ({ orgData }: { orgData: Organisation | null }) => { const { org_agents } = orgData as Organisation; @@ -43,14 +44,16 @@ const OrganizationDetails = ({ orgData }: { orgData: Organisation | null }) => { return ( <> -
    +
    +
    +

    + Web Wallet Details +

    +
    -

    - Web Wallet Details -

    • @@ -173,8 +176,10 @@ const OrganizationDetails = ({ orgData }: { orgData: Organisation | null }) => { ) )}
    +
    + +
    - {agentData?.orgDid?.startsWith('did:web') && (
    diff --git a/src/components/organization/WalletSpinup.tsx b/src/components/organization/WalletSpinup.tsx index 17f7a429f..562cf43a9 100644 --- a/src/components/organization/WalletSpinup.tsx +++ b/src/components/organization/WalletSpinup.tsx @@ -240,7 +240,12 @@ const SharedAgentForm = ({ }; const validations = { - label: yup.string().required('Wallet label is required'), + label: yup + .string() + .required('Wallet label is required') + .trim() + .min(2, 'Wallet label must be at least 2 characters') + .max(25, 'Wallet label must be at most 25 characters'), method: yup.string().required('Method is required'), ...(DidMethod.INDY === selectedLedger || DidMethod.POLYGON === selectedLedger) && { network: yup.string().required('Network is required') }, ...(DidMethod.INDY === selectedLedger) && { ledger: yup.string().required('Ledger is required') }, @@ -710,10 +715,6 @@ const DedicatedAgentForm = ({ .max(20, 'Wallet name must be at most 20 characters') .trim() .required('Wallet name is required') - .matches( - /^[A-Za-z0-9-][^ !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]*$/, - 'Wallet name must be alphanumeric only', - ) .label('Wallet name'), password: yup .string() @@ -934,7 +935,7 @@ const WalletSpinup = (props: { : `${values?.ledger}:${values?.network}`, domain: values.method === DidMethod.WEB ? domain : '', role: values.method === DidMethod.INDY ? values?.role || 'endorser' : '', - endorserDid: values?.endorserDid, + endorserDid: values?.endorserDid || '', clientSocketId: SOCKET.id, }; const orgId = await getFromLocalStorage(storageKeys.ORG_ID); diff --git a/src/components/organization/configuration-settings/CreateDid.tsx b/src/components/organization/configuration-settings/CreateDid.tsx new file mode 100644 index 000000000..ccce6bf20 --- /dev/null +++ b/src/components/organization/configuration-settings/CreateDid.tsx @@ -0,0 +1,570 @@ +import * as yup from 'yup'; +import { Button, Modal } from 'flowbite-react'; +import { Field, Form, Formik, FormikHelpers } from 'formik'; +import { apiStatusCodes, storageKeys } from '../../../config/CommonConstant'; +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 { 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'; + +interface IPolygonKeys { + privateKey: string; + publicKeyBase58: string; + 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 [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 fetchOrganizationDetails = async () => { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getOrganizationById(orgId as string); + const { data } = response as AxiosResponse; + setLoading(false); + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const didMethod = data?.data?.org_agents[0]?.orgDid + ?.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'); + } + } else { + console.error('Error in fetching organization:::'); + } + setLoading(false); + }; + + useEffect(() => { + fetchOrganizationDetails(); + }, []); + + const createNewDid = async (values: IFormikValues) => { + setLoading(true); + + 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}` + : '', + domain: values.method === DidMethod.WEB ? values.domain : '', + role: values.method === DidMethod.INDY ? 'endorser' : '', + privatekey: values.method === DidMethod.POLYGON ? values.privatekey : '', + did: '', + endorserDid: values?.endorserDid || '', + isPrimaryDid: false, + }; + try { + const response = await createDid(didData); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { + if (props?.onEditSucess) { + props?.onEditSucess(); + } + props.setOpenModal(false); + props.setMessage(data?.message); + setSuccessMsg(data?.message); + setLoading(false); + } else { + setErrMsg(response as string); + setLoading(false); + } + } catch (error) { + console.error('An error occurred while creating did:', error); + setLoading(false); + } + }; + + const generatePolygonKeyValuePair = async () => { + try { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const resCreatePolygonKeys = await createPolygonKeyValuePair(orgId); + const { data } = resCreatePolygonKeys as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { + setGeneratedKeys(data?.data); + } + } 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)); + }, []); + + const validations = { + method: yup.string().required('Method is required').trim(), + ledger: yup.string(), + network: yup.string(), + domain: yup.string(), + privatekey: yup.string(), + }; + + 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); + setErrMsg(null); + }} + > + Create DID + + { + setErrMsg(null); + setSuccessMsg(null); + }} + /> + , + ) => { + 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(); + } + }} + > + {(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.WEB && + formikHandlers.values.method !== DidMethod.KEY && ( +
    + + + {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 && ( +
    +
    + + +
    + {formikHandlers?.errors?.domain && + formikHandlers?.touched?.domain && ( + + {formikHandlers?.errors?.domain} + + )} +
    + )} +
    + + +
    +
    +
    + +
    +
    + ); + }} +
    +
    +
    + ); +}; + +export default CreateDIDModal; diff --git a/src/components/organization/configuration-settings/DidList.tsx b/src/components/organization/configuration-settings/DidList.tsx new file mode 100644 index 000000000..55abea91a --- /dev/null +++ b/src/components/organization/configuration-settings/DidList.tsx @@ -0,0 +1,145 @@ +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 { 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" + +const DIDList = () => { + const [didList, setDidList] = useState([]); + const [showPopup, setShowPopup] = useState(false); + const [erroMsg, setErrMsg] = useState(null); + const [successMsg, setSuccessMsg] = useState(null); + + const setPrimaryDid = async (id: string, did: string) => { + try { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const payload: IUpdatePrimaryDid = { + id, + did + } + const response = await updatePrimaryDid(orgId, payload); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) { + window.location.reload(); + } else { + setErrMsg(response as string); + } + } catch (error) { + } + } + + const getData = async () => { + try { + const orgId = await getFromLocalStorage(storageKeys.ORG_ID); + const response = await getDids(orgId); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + const sortedDids = data?.data.sort((a, b) => { + if (a.isPrimaryDid && !b.isPrimaryDid) return -1; + if (!a.isPrimaryDid && b.isPrimaryDid) return 1; + return 0; + }); + setDidList(sortedDids) + } + } catch (error) { + console.log("ERROR::::", error); + } + } + + useEffect(() => { + getData(); + }, []) + + return ( + <> +
    + { + setErrMsg(null); + setSuccessMsg(null); + }} + /> +
    +

    DID Details

    + + +
    +
    + { + didList.map((item: IDidList, index: number) => { + const primary = item.id; + return ( +
    +
    +

    DID {index + 1}

    +

    :

    + {item?.did ? ( + + ) : ( + + Not available + + )} + { + primary && item.isPrimaryDid ? +
    + Primary DID +
    + : +
    + +
    + } +
    +
    + ) + }) + } +
    +
    + setShowPopup(value)} + loading={false} + success={"message"} + failure={""} + openModal={showPopup} + closeModal={() => setShowPopup(false)} + onSuccess={() => console.log("On Success")} + message={'Would you like to proceed? Keep in mind that this action cannot be undone.'} + buttonTitles={["No, cancel", "Yes, I'm sure"]} + isProcessing={false} + setFailure={() => console.log("SET Error")} + setSuccess={() => console.log("SET Success")} + /> + + ) +} + + +export default DIDList; \ No newline at end of file diff --git a/src/components/organization/interfaces/index.ts b/src/components/organization/interfaces/index.ts index 566b67047..99bc7d5ca 100644 --- a/src/components/organization/interfaces/index.ts +++ b/src/components/organization/interfaces/index.ts @@ -6,6 +6,10 @@ export interface UserOrgRole { orgRole: OrgRole } +interface IEcosystemOrgs { + ecosystemId: string; +} + export interface Organisation { logoFile: string id: string @@ -21,7 +25,9 @@ export interface Organisation { userOrgRoles: UserOrgRole[] org_agents: OrgAgent[] publicProfile: boolean - + checked?: boolean | undefined + ecosystemOrgs?: IEcosystemOrgs[] + error?: string; } export interface OrgRole { @@ -192,3 +198,25 @@ export interface IOrgInfo { id: string; roles: string[] } + +export interface IUpdatePrimaryDid { + id: string; + did: string; +} + +export interface IDidList { + id: string; + did: string; + isPrimaryDid: boolean; + createDateTime: string; + lastChangedDateTime: string; +} + +export interface IFormikValues { + ledger: string; + method: string; + network: string; + domain: string; + privatekey: string; + endorserDid: string; +} diff --git a/src/components/organization/walletCommonComponents/SetPrivateKeyValue.tsx b/src/components/organization/walletCommonComponents/SetPrivateKeyValue.tsx index ba987dca8..8307cf20a 100644 --- a/src/components/organization/walletCommonComponents/SetPrivateKeyValue.tsx +++ b/src/components/organization/walletCommonComponents/SetPrivateKeyValue.tsx @@ -1,5 +1,6 @@ import {Label } from 'flowbite-react'; import { Field} from 'formik'; +import type { ChangeEvent } from 'react'; interface IProps { setPrivateKeyValue:(val:string)=>void privateKeyValue:string @@ -22,11 +23,12 @@ const SetPrivateKeyValueInput = ({
    { + onChange={(e: ChangeEvent) => { setPrivateKeyValue(e.target.value); formikHandlers.handleChange(e); }} diff --git a/src/config/CommonConstant.ts b/src/config/CommonConstant.ts index 5a45f8842..b4b4e9320 100644 --- a/src/config/CommonConstant.ts +++ b/src/config/CommonConstant.ts @@ -11,7 +11,8 @@ export const schemaVersionRegex = /^\d{1,5}(?=.*[0-9])(?:\.\d{1,5})?(?:\.\d{1,5} export const apiStatusCodes = { API_STATUS_SUCCESS : 200, API_STATUS_CREATED : 201, - API_STATUS_DELETED : 202, + API_STATUS_DELETED : 202, + API_STATUS_PARTIALLY_COMPLETED : 206, API_STATUS_BAD_REQUEST : 400, API_STATUS_UNAUTHORIZED : 401, API_STATUS_NOT_FOUND : 404 @@ -26,6 +27,7 @@ export const storageKeys = { PERMISSIONS: 'user_permissions', USER_EMAIL: 'user_email', SELECTED_USER:'selected_user', + SELECTED_CONNECTIONS: 'selected_connections', SCHEMA_ID:'schema_id', SCHEMA_ATTR:'schema_attr', CRED_DEF_ID:'cred_def_id', @@ -36,7 +38,7 @@ export const storageKeys = { ECOSYSTEM_ROLE: "ecosystem_role", SOCKET_ID: "socket_id", LEDGER_ID: "ledger_id", - ORG_INFO:'organization_Info' + ORG_INFO:'organization_Info', + SELECT_ORG_IN_ECOSYSTEM: 'select_orgs_in_ecosystem', + ERROR_ORG_IN_ECOSYSTEM: 'error_orgs_in_ecosystem' } - -export const allowedDomains = "https://dev-service.ngotag.com wss://dev-service.ngotag.com https://cdnjs.cloudflare.com https://tailwindcss.com https://www.blockster.global https://www.ayanworks.com https://qaapi.credebl.id https://devapi.credebl.id https://api.credebl.id https://*.credebl.id https://fonts.googleapis.com https://fonts.gstatic.com https://avatars.githubusercontent.com https://dev-org-logo.s3.ap-south-1.amazonaws.com https://flowbite-admin-dashboard.vercel.app/ wss://devapi.credebl.id wss://qaapi.credebl.id wss://api.credebl.id wss://*.credebl.id https://qa.credebl.id https://dev.credebl.id https://credebl.id" \ No newline at end of file diff --git a/src/config/GetHeaderConfigs.ts b/src/config/GetHeaderConfigs.ts index f053665b1..e69a6f8a1 100644 --- a/src/config/GetHeaderConfigs.ts +++ b/src/config/GetHeaderConfigs.ts @@ -1,6 +1,8 @@ import { getFromLocalStorage } from '../api/Auth'; -import { allowedDomains, storageKeys } from './CommonConstant'; +import { storageKeys } from './CommonConstant'; +import { envConfig } from './envConfig'; +const allowedDomains = envConfig.PUBLIC_ALLOW_DOMAIN; const commonHeaders = { 'Content-Security-Policy': `default-src 'self'; script-src 'unsafe-inline' ${allowedDomains}; style-src 'unsafe-inline' ${allowedDomains}; font-src ${allowedDomains}; img-src 'self' ${allowedDomains}; frame-src 'self' ${allowedDomains}; object-src 'none'; media-src 'self'; connect-src 'self' ${allowedDomains}; form-action 'self'; frame-ancestors 'self'; `, 'X-Frame-Options': "DENY", diff --git a/src/config/apiRoutes.ts b/src/config/apiRoutes.ts index d20fe4216..b11c9febb 100644 --- a/src/config/apiRoutes.ts +++ b/src/config/apiRoutes.ts @@ -39,6 +39,9 @@ export const apiRoutes = { invitations: '/invitations', orgRoles: '/orgs/roles', editUserROle: '/user-roles', + didList: '/dids', + createDid: '/agents/did', + primaryDid: '/primary-did' }, connection: { create: '/connections', diff --git a/src/config/envConfig.ts b/src/config/envConfig.ts index f0be039b9..b979c8036 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 }: any = envVariables; +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; export const envConfig = { PUBLIC_BASE_URL: @@ -55,5 +55,6 @@ export const envConfig = { discord: PUBLIC_PLATFORM_DISCORD_URL || import.meta.env.PUBLIC_PLATFORM_DISCORD_URL, - } + }, + PUBLIC_ALLOW_DOMAIN: PUBLIC_ALLOW_DOMAIN || import.meta.env.PUBLIC_ALLOW_DOMAIN } \ No newline at end of file diff --git a/src/config/pathRoutes.ts b/src/config/pathRoutes.ts index 163af1fa3..d4df16e15 100644 --- a/src/config/pathRoutes.ts +++ b/src/config/pathRoutes.ts @@ -56,6 +56,7 @@ export const pathRoutes = { endorsements: '/ecosystems/endorsement', invitation: '/ecosystems/invitation', sentinvitation: '/ecosystems/invitations', + addOrgs: '/ecosystems/dashboard/add-organizations' }, documentation: { root: envConfig.PLATFORM_DATA.docs diff --git a/src/middleware.ts b/src/middleware.ts index 609986946..c73dc4a46 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,11 +1,13 @@ -import { allowedDomains } from "./config/CommonConstant"; +import { envConfig } from "./config/envConfig"; +import { pathRoutes } from "./config/pathRoutes"; export const onRequest = async (context: any, next: any) => { const response = await next(); const html = await response.text(); + const domains = envConfig.PUBLIC_ALLOW_DOMAIN; - const allowedDomain = `${context.url.origin} ${allowedDomains}` + const allowedDomain = `${context.url.origin} ${domains}` const nonce = "dynamicNONCE" + new Date().getTime().toString(); @@ -22,6 +24,11 @@ export const onRequest = async (context: any, next: any) => { let updatedHtml = await html.split(" { const body = await request.json(); const sessionCookie = body?.data - + setToCookies(cookies, "session", sessionCookie?.access_token as string, { path: "/" }) diff --git a/src/pages/ecosystems/dashboard/add-organizations.astro b/src/pages/ecosystems/dashboard/add-organizations.astro new file mode 100644 index 000000000..8c53d6e36 --- /dev/null +++ b/src/pages/ecosystems/dashboard/add-organizations.astro @@ -0,0 +1,16 @@ +--- +import LayoutSidebar from '../../../app/LayoutSidebar.astro'; +import { checkUserSession } from '../../../utils/check-session'; +import { pathRoutes } from '../../../config/pathRoutes'; +import AddOrganizationInEcosystem from '../../../components/AddOrganizationInEcosystem'; + +const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); +const route = pathRoutes.auth.sinIn; +if (!response.authorized) { + return Astro.redirect(response.redirect); +} +--- + + + + diff --git a/src/pages/ecosystems/dashboard.astro b/src/pages/ecosystems/dashboard/index.astro similarity index 56% rename from src/pages/ecosystems/dashboard.astro rename to src/pages/ecosystems/dashboard/index.astro index 7d351ab56..f417ea61c 100644 --- a/src/pages/ecosystems/dashboard.astro +++ b/src/pages/ecosystems/dashboard/index.astro @@ -1,8 +1,8 @@ --- -import LayoutSidebar from '../../app/LayoutSidebar.astro'; -import Dashboard from '../../components/Ecosystem/Dashboard'; -import { checkUserSession } from '../../utils/check-session'; -import { pathRoutes } from '../../config/pathRoutes'; +import LayoutSidebar from '../../../app/LayoutSidebar.astro'; +import Dashboard from '../../../components/Ecosystem/Dashboard'; +import { checkUserSession } from '../../../utils/check-session'; +import { pathRoutes } from '../../../config/pathRoutes'; const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); const route: string = pathRoutes.auth.sinIn diff --git a/src/pages/organizations/dashboard.astro b/src/pages/organizations/dashboard/index.astro similarity index 56% rename from src/pages/organizations/dashboard.astro rename to src/pages/organizations/dashboard/index.astro index 138de6f12..3612ea7c9 100644 --- a/src/pages/organizations/dashboard.astro +++ b/src/pages/organizations/dashboard/index.astro @@ -1,8 +1,8 @@ --- -import LayoutSidebar from '../../app/LayoutSidebar.astro'; -import Dashboard from '../../components/organization/Dashboard'; -import { checkUserSession } from '../../utils/check-session'; -import { pathRoutes } from '../../config/pathRoutes'; +import LayoutSidebar from '../../../app/LayoutSidebar.astro'; +import Dashboard from '../../../components/organization/Dashboard'; +import { checkUserSession } from '../../../utils/check-session'; +import { pathRoutes } from '../../../config/pathRoutes'; const response = await checkUserSession({cookies: Astro.cookies, currentPath: Astro.url.pathname}); const route: string = pathRoutes.auth.sinIn