Skip to content

Commit

Permalink
Check proof page UI (#9)
Browse files Browse the repository at this point in the history
* Add UI draft

* Add draft org overview

* Add verify proof alias

* Fix org overview layout

* Update org overview

* Update proof viewer

* Add loading states

* Add credential field

* Add link API

* Update proof check

* Chore code

* Update link types

* Add review fixes

* Load and parse proof schema

* Parse link ID

* Fix orgs includes

* Fix review comments
  • Loading branch information
ardier16 authored Jan 12, 2024
1 parent 3b93749 commit 12176d8
Show file tree
Hide file tree
Showing 29 changed files with 583 additions and 47 deletions.
1 change: 1 addition & 0 deletions src/api/modules/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './link'
export * from './orgs'
1 change: 1 addition & 0 deletions src/api/modules/link/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './proofs'
87 changes: 87 additions & 0 deletions src/api/modules/link/helpers/proofs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { api, type Proof, type ProofLink } from '@/api'

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const DUMMY_PROOFS: Proof[] = [
{
id: '550e8400-e29b-41d4-a716-446655440000',
type: 'proofs',
creator: 'did:ethr:0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef',
created_at: '2021-08-12T14:00:00Z',
proof: '{"pub_signals":[...],"proof":{"pi_a":[...],"pi_b":[],"pi_c":[...]}}',
proof_type: 'firstName',
org_id: '550e8400-e29b-41d4-a716-446655440000',
schema_url:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json',
},
{
id: '550e8400-e29b-41d4-a716-446655440001',
type: 'proofs',
creator: 'did:ethr:0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef',
created_at: '2021-08-12T14:00:00Z',
proof: '{"pub_signals":[...],"proof":{"pi_a":[...],"pi_b":[],"pi_c":[...]}}',
proof_type: 'lastName',
org_id: '550e8400-e29b-41d4-a716-446655440000',
schema_url:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCCountryOfResidenceCredential-v4.json',
},
{
id: '550e8400-e29b-41d4-a716-446655440002',
type: 'proofs',
creator: 'did:ethr:0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef',
created_at: '2021-08-12T14:00:00Z',
proof: '{"pub_signals":[...],"proof":{"pi_a":[...],"pi_b":[],"pi_c":[...]}}',
proof_type: 'telegram',
org_id: '550e8400-e29b-41d4-a716-446655440000',
schema_url:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json',
},
]

export const createProof = async (proof: string) => {
const { data } = await api.post<Proof>('/v1/proofs', {
body: {
data: {
type: 'proofs',
proof,
},
},
})

return data
}

export const getProofById = async (id: string) => {
const { data } = await api.get<Proof>(`/v1/proofs/${id}`)

return data
}

export const createLinkProofs = async (proofIds: string[]) => {
const { data } = await api.post<ProofLink>('/v1/proofs/link', {
body: {
data: {
proofs_ids: proofIds,
},
},
})

return data
}

export const getProofsByLinkId = async (linkId: string) => {
const { data } = await api.get<Proof[]>(`/v1/proofs/link/${linkId}`)

return data
}

export const getProofsByUserDid = async (userDid: string) => {
const { data } = await api.get<Proof[]>(`/v1/proofs/user/${userDid}`)

return data
}

export const getProofLinksByUserDid = async (userDid: string) => {
const { data } = await api.get<ProofLink[]>(`/v1/proofs/user/${userDid}/link`)

return data
}
1 change: 1 addition & 0 deletions src/api/modules/link/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './link-proofs'
29 changes: 29 additions & 0 deletions src/api/modules/link/hooks/link-proofs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useCallback } from 'react'

import { getProofsByLinkId } from '@/api'
import { useLoading } from '@/hooks'

export const useLinkProofs = (id: string) => {
const loadProofs = useCallback(async () => {
if (!id) return []

return getProofsByLinkId(id)
}, [id])

const {
data: proofs,
isLoading,
isLoadingError,
isEmpty,
} = useLoading([], loadProofs, {
loadOnMount: !!id,
loadArgs: [id],
})

return {
proofs,
isLoading,
isLoadingError,
isEmpty,
}
}
3 changes: 3 additions & 0 deletions src/api/modules/link/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './helpers'
export * from './hooks'
export * from './types'
1 change: 1 addition & 0 deletions src/api/modules/link/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './proofs'
17 changes: 17 additions & 0 deletions src/api/modules/link/types/proofs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type Proof = {
id: string
type: 'proofs'
creator: string
created_at: string
proof: string
proof_type: string
org_id: string
schema_url: string
}

export type ProofLink = {
id: string
type: 'proofs'
link: string
created_at: string
}
3 changes: 1 addition & 2 deletions src/api/modules/orgs/enums/orgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export enum OrgsRequestFilters {
}

export enum OrgsIncludes {
Organization = 'Organization',
Owner = 'Owner',
Owner = 'owner',
}

export enum OrgUserRoles {
Expand Down
42 changes: 18 additions & 24 deletions src/api/modules/orgs/helpers/org-groups-requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import omit from 'lodash/omit'
import {
api,
CredentialRequest,
CredentialSubject,
OrgGroupCreatedRequest,
OrgGroupCreateRequest,
OrgGroupRequest,
Expand Down Expand Up @@ -612,32 +613,25 @@ export const getOrgGroupPublishingRequests = async ({
return data
}

export const loadAndParseRequestCredentialSchemas = async (
request: OrgGroupRequest,
): Promise<
{
key: string
value: string
type: string
}[]
> => {
return Promise.all(
request.credential_requests.map(async el => {
const { data } = await fetcher.get<VCSchema>(el.credential_schema)

const [key, { type }] = Object.entries(
omit(data?.properties.credentialSubject.properties, 'id'),
)[0]
export const loadAndParseCredentialSchema = async (
schemaUrl: string,
credentialSubject?: CredentialSubject,
): Promise<{
key: string
type: string
value: string
}> => {
const { data } = await fetcher.get<VCSchema>(schemaUrl)

const value = el.credential_subject[key]
const [key, { type }] = Object.entries(
omit(data?.properties.credentialSubject.properties, 'id'),
)[0]

return {
key,
value,
type,
}
}),
)
return {
key,
type,
value: credentialSubject?.[key] ?? '',
}
}

export const loadOrgGroupReqMetadataById = async (
Expand Down
12 changes: 6 additions & 6 deletions src/api/modules/orgs/helpers/orgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ export const DUMMY_ORGS: Organization[] = [
id: '3a798290-caf1-496a-a7e5-4db32551b13d',
type: 'organizations',
did: 'did:iden3:readonly:tUDjWxnVJNi7t3FudukqrUcNwF5KVGoWgim5pp2jV',
domain: 'https://organization-domain.com',
domain: 'www.rarimo.com',
metadata: {
logoUrl: 'https://organization-domain.com/logo.png',
name: 'Organization Name',
description: 'Organization Description',
logoUrl: 'https://rarimo.com/favicon/favicon-dark.png',
name: 'Rarimo',
description: 'Organization description',
},
status: {
name: 'unverified',
value: OrgsStatuses.Unverified,
name: 'verified',
value: OrgsStatuses.Verified,
},
verification_code: '6A4GSfUNKwM9hHuZVg4aUw==',
issued_claims_count: '0',
Expand Down
6 changes: 5 additions & 1 deletion src/api/modules/orgs/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import {
OrgGroupRequestStatuses,
} from '@/api'

export type CredentialSubject = Record<string, string> & {
metadata_id: string
}

export type CredentialRequest = {
credential_schema: string
credential_subject: Record<string, string> & { metadata_id: string }
credential_subject: CredentialSubject
type: string
expiration: string
mt_proof: boolean
Expand Down
2 changes: 2 additions & 0 deletions src/enums/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { default as AccountCircleIcon } from '@mui/icons-material/AccountCircle'
import { default as Add } from '@mui/icons-material/Add'
import { default as AddPhotoAlternateOutlined } from '@mui/icons-material/AddPhotoAlternateOutlined'
import { default as CheckIcon } from '@mui/icons-material/Check'
import { default as ChevronLeft } from '@mui/icons-material/ChevronLeft'
import { default as Close } from '@mui/icons-material/Close'
import { default as DeleteIcon } from '@mui/icons-material/Delete'
import { default as ErrorOutlineIcon } from '@mui/icons-material/ErrorOutline'
Expand Down Expand Up @@ -32,4 +33,5 @@ export const ICON_COMPONENTS = {
addPhotoAlternativeOutlined: AddPhotoAlternateOutlined,
verified: Verified,
close: Close,
chevronLeft: ChevronLeft,
}
3 changes: 3 additions & 0 deletions src/enums/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export enum RoutePaths {
OrgsListAll = '/organisations/list/all',
OrgsListMy = '/organisations/list/my',
OrgsNew = '/organisations/new',

OrgsId = '/organisations/:id',
OrgsIdCheckProof = '/organisations/:id/check-proof',
OrgsIdGroups = '/organisations/:id/groups',
Expand All @@ -17,4 +18,6 @@ export enum RoutePaths {
OrgsIdGroupsIdListStatusCreated = '/organisations/:id/groups/:groupId/list/pending',
OrgsIdGroupsIdListStatusFilled = '/organisations/:id/groups/:groupId/list/filled',
OrgsIdGroupsNew = '/organisations/:id/groups/new',

VerifyProofAlias = '/v/:id',
}
2 changes: 1 addition & 1 deletion src/helpers/metamask.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { get } from 'lodash'
import get from 'lodash/get'

const OTHER_BROWSER_METAMASK_LINK = 'https://metamask.io/download/'
const CHROME_METAMASK_ADDON_LINK = 'https://chrome.google.com/webstore/detail/metamask/'
Expand Down
9 changes: 8 additions & 1 deletion src/hooks/loading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const useLoading = <T>(
reload: () => Promise<void>
isEmpty: boolean
update: () => Promise<void>
reset: () => void
} => {
const { loadArgs, loadOnMount: _loadOnMount } = options ?? {}
const loadOnMount = useMemo(() => _loadOnMount ?? true, [_loadOnMount])
Expand Down Expand Up @@ -61,5 +62,11 @@ export const useLoading = <T>(
}
}

return { data, isLoading, isLoadingError, isEmpty, reload, update }
const reset = () => {
setIsLoading(false)
setIsLoadingError(false)
setData(initialState as T)
}

return { data, isLoading, isLoadingError, isEmpty, reload, update, reset }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Box, Stack, Typography, useTheme } from '@mui/material'
import { generatePath, NavLink, useParams } from 'react-router-dom'

import { RoutePaths } from '@/enums'
import { UiIcon } from '@/ui'

export default function CheckProofHead() {
const { id = null } = useParams<{ id: string }>()
const { palette, spacing } = useTheme()

return (
<Box position={'relative'} mx={-8} px={6} pb={6} borderBottom={1} borderColor={palette.divider}>
<Box position={'absolute'} top={spacing(2)} left={spacing(6)}>
<NavLink to={generatePath(RoutePaths.OrgsId, { id })}>
<Stack direction={'row'} alignItems={'center'} spacing={2} color={palette.text.secondary}>
<UiIcon componentName={'chevronLeft'} size={5} />
<Typography variant='button' color={'inherit'}>
View organization
</Typography>
</Stack>
</NavLink>
</Box>

<Typography variant='h6' textAlign={'center'}>
Check Proof
</Typography>
</Box>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Avatar, Box, Divider, Stack, Typography, useTheme } from '@mui/material'

import { Organization, OrgsStatuses } from '@/api'
import { UiIcon } from '@/ui'

interface Props {
org: Organization
}

export default function OrgOverview({ org }: Props) {
const { palette, spacing } = useTheme()

return (
<Stack alignItems={'center'}>
<Avatar
src={org.metadata.logoUrl}
alt={org.metadata.name}
sx={{
width: spacing(20),
height: spacing(20),
borderRadius: 250,
objectFit: 'cover',
}}
/>

<Stack direction={'row'} spacing={1} alignItems={'center'} mt={4}>
<Typography variant={'h5'}>{org.metadata.name}</Typography>
{org.status.value === OrgsStatuses.Verified && (
<UiIcon componentName={'verified'} size={5} sx={{ color: palette.success.main }} />
)}
</Stack>

<Typography mt={2} variant={'body2'}>
{org.domain}
</Typography>

<Box mt={6} display={'grid'} gridTemplateColumns={'1fr 1px 1fr'} gap={6} textAlign={'center'}>
<Box>
<Typography variant={'h6'}>{org.members_count}</Typography>
<Typography variant={'body2'}>Associated people</Typography>
</Box>
<Divider orientation={'vertical'} />
<Box>
<Typography variant={'h6'}>{org.issued_claims_count}</Typography>
<Typography variant={'body2'}>Credentials</Typography>
</Box>
</Box>
</Stack>
)
}
Loading

0 comments on commit 12176d8

Please sign in to comment.