From 005af0bb9e367649bed0562d5c51ef615bb20384 Mon Sep 17 00:00:00 2001 From: Skedley <48058984+Dudleyneedham@users.noreply.github.com> Date: Mon, 29 Jun 2020 14:30:52 +0200 Subject: [PATCH] feat: Integrating the quote (#258) * feat: adding the barebones of the quote * feat: adding more details for the quote and updating naming * feat: redux state for quote inprogress * feat: redux state for quote inprogress * feat: adding more depth * feat: created a unique id for the quotes * feat: adding the quote creation and viewing of the newly made quote * feat: cleaning up the code * feat: sending messages and signing * feat: all working need some clean up * feat: cleaning up adding my quote list view * feat: added the delete to the quote * feat: cleaning up * feat: fixed bug * feat: after resolving changes from the conflicts updated quote to work * feat: fixed all errors * feat: updated according to review * feat: updated according to review * Update src/containers/MyQuotesList/MyQuotesList.scss fix: removed space Co-authored-by: Raphael Flechtner <39338561+flechtnergalani@users.noreply.github.com> * Update src/containers/Tasks/SubmitTerms/SubmitTerms.tsx Co-authored-by: Raphael Flechtner <39338561+flechtnergalani@users.noreply.github.com> * refactor: changed from terms to legitimations * refactor: accordingly changes to review updated checks and removed code * refactor: updated the requestforattestation quesstion * refactor: added the date picker will check styling * refactor: saved quote only after sending claim * refactor: saving the agreed quote * refactor: removed the double date pickers * refactor: forgot to add the new dependancy * refactor: updating naming to make it more explict * feat: updating ui * feat: updating ui * feat: removing some ui changes for another ticket * feat: updating the styling * feat: updating the styling and hashing * feat: updating the logic to only delete the owner of the quote * feat: updating the logic to save claimer signature quote for claimer * feat: updating the redux logic to have one redux action for saving * feat: trying to solve error from styling date picker * feat: updated the config mini has been remov in later addition * feat: updated the config mini has been remov in later addition * feat: updated the config mini has been remov in later addition Co-authored-by: Raphael Flechtner <39338561+flechtnergalani@users.noreply.github.com> --- config/webpack.config.prod.js | 1 - package.json | 4 +- .../AttestedClaimsListView.scss | 6 +- .../AttestedClaimsListView.tsx | 14 +- .../CTypePresentation/CTypePresentation.tsx | 12 +- .../ContactPresentation.tsx | 12 +- .../CtypeListView/CtypeListView.tsx | 2 +- .../DevTools/DevTools.attestations.tsx | 24 +-- .../DevTools/data/attestations.json | 2 +- src/components/DevTools/data/claims.json | 2 +- src/components/DevTools/data/delegations.json | 32 +-- src/components/DevTools/data/identities.json | 2 +- src/components/DevTools/data/pcr.json | 32 +-- .../MessageDetailView/MessageDetailView.scss | 13 +- .../MessageDetailView/MessageDetailView.tsx | 17 +- .../MessageListView/MessageListView.scss | 8 +- .../MyClaimDetailView/MyClaimDetailView.tsx | 24 +-- .../MyClaimListView/MyClaimListView.tsx | 12 +- src/components/Navigation/Navigation.tsx | 1 + .../SelectAttestedClaims.tsx | 13 +- src/containers/ClaimView/ClaimView.tsx | 14 +- src/containers/CtypeView/CtypeView.tsx | 11 +- src/containers/MyQuotesList/MyQuotesList.scss | 43 ++++ src/containers/MyQuotesList/MyQuotesList.tsx | 109 ++++++++++ src/containers/QuoteCreate/QuoteCreate.scss | 9 + src/containers/QuoteCreate/QuoteCreate.tsx | 114 ++++++++++ src/containers/QuoteCreate/QuoteSchema.ts | 62 ++++++ src/containers/QuoteView/QuoteView.scss | 10 + src/containers/QuoteView/QuoteView.tsx | 118 +++++++++++ .../Tasks/AttestClaim/AttestClaim.tsx | 66 +++++- .../ImportAttestation/ImportAttestation.tsx | 2 +- .../RequestAttestation/RequestAttestation.tsx | 98 +++++++-- .../RequestTerms.scss} | 4 +- .../RequestTerms.tsx} | 55 ++--- .../SubmitTerms.scss} | 4 +- .../SubmitTerms.tsx} | 88 ++++++-- src/containers/Tasks/Tasks.tsx | 20 +- .../Tasks/VerifyClaim/VerifyClaim.tsx | 2 +- src/routes/index.tsx | 6 + src/services/AttestationWorkflow.ts | 48 +++-- src/services/QuoteServices.ts | 58 ++++++ src/state/PersistentStore.ts | 6 + src/state/ducks/Quotes.ts | 197 ++++++++++++++++++ src/utils/QuoteUtils/QuoteInputSchema.ts | 57 +++++ yarn.lock | 41 +++- 45 files changed, 1221 insertions(+), 254 deletions(-) create mode 100644 src/containers/MyQuotesList/MyQuotesList.scss create mode 100644 src/containers/MyQuotesList/MyQuotesList.tsx create mode 100644 src/containers/QuoteCreate/QuoteCreate.scss create mode 100644 src/containers/QuoteCreate/QuoteCreate.tsx create mode 100644 src/containers/QuoteCreate/QuoteSchema.ts create mode 100644 src/containers/QuoteView/QuoteView.scss create mode 100644 src/containers/QuoteView/QuoteView.tsx rename src/containers/Tasks/{RequestLegitimation/RequestLegitimation.scss => RequestTerms/RequestTerms.scss} (63%) rename src/containers/Tasks/{RequestLegitimation/RequestLegitimation.tsx => RequestTerms/RequestTerms.tsx} (68%) rename src/containers/Tasks/{SubmitLegitimations/SubmitLegitimations.scss => SubmitTerms/SubmitTerms.scss} (76%) rename src/containers/Tasks/{SubmitLegitimations/SubmitLegitimations.tsx => SubmitTerms/SubmitTerms.tsx} (68%) create mode 100644 src/services/QuoteServices.ts create mode 100644 src/state/ducks/Quotes.ts create mode 100644 src/utils/QuoteUtils/QuoteInputSchema.ts diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index 8cd71825..ab7641ac 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -243,7 +243,6 @@ module.exports = { loader: require.resolve('css-loader'), options: { importLoaders: 1, - minimize: true, sourceMap: shouldUseSourceMap, }, }, diff --git a/package.json b/package.json index ea45ca22..8ec48839 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,8 @@ "postcss-loader": "2.0.8", "promise": "8.0.1", "raf": "3.4.0", - "react": "^16.6.3", + "react": "^16.8.0", + "react-day-picker": "^7.4.8", "react-dev-utils": "^5.0.2", "react-dom": "^16.6.3", "react-json-view": "^1.19.1", @@ -75,6 +76,7 @@ "ts-jest": "22.0.1", "ts-loader": "^2.3.7", "tslib": "^1.10.0", + "types": "^0.1.1", "url-loader": "0.6.2", "utility-types": "^3.4.1", "whatwg-fetch": "2.0.3" diff --git a/src/components/AttestedClaimsListView/AttestedClaimsListView.scss b/src/components/AttestedClaimsListView/AttestedClaimsListView.scss index 5d32bb1f..7c70b243 100644 --- a/src/components/AttestedClaimsListView/AttestedClaimsListView.scss +++ b/src/components/AttestedClaimsListView/AttestedClaimsListView.scss @@ -5,7 +5,7 @@ margin-bottom: 0; } - & > div:not(.legitimations) { + & > div:not(.terms) { & > h2, & > .delegation { @@ -13,7 +13,7 @@ } } - & .legitimations { + & .terms { &, & > .delegation { @@ -160,7 +160,7 @@ & & { - & > .legitimations { + & > .terms { &, & > .attestations { diff --git a/src/components/AttestedClaimsListView/AttestedClaimsListView.tsx b/src/components/AttestedClaimsListView/AttestedClaimsListView.tsx index 32821904..1120e736 100644 --- a/src/components/AttestedClaimsListView/AttestedClaimsListView.tsx +++ b/src/components/AttestedClaimsListView/AttestedClaimsListView.tsx @@ -20,7 +20,7 @@ interface IPossibleLabels { interface ILabels { default: IPossibleLabels - legitimations: IPossibleLabels + terms: IPossibleLabels } const LABELS: ILabels = { @@ -29,16 +29,16 @@ const LABELS: ILabels = { h2Multi: 'Attested claims', h2Single: 'Attested claim', }, - legitimations: { - emptyList: 'No legitimations found.', - h2Multi: 'Legitimations', - h2Single: 'Legitimation', + terms: { + emptyList: 'No terms found.', + h2Multi: 'terms', + h2Single: 'term', }, } type Props = { attestedClaims: sdk.IAttestedClaim[] - context?: 'legitimations' + context?: 'terms' delegationId: sdk.IDelegationNode['id'] | null currentDelegationViewType?: ViewType @@ -172,7 +172,7 @@ class AttestedClaimsListView extends React.Component { diff --git a/src/components/CTypePresentation/CTypePresentation.tsx b/src/components/CTypePresentation/CTypePresentation.tsx index 04dcf658..f42828c1 100644 --- a/src/components/CTypePresentation/CTypePresentation.tsx +++ b/src/components/CTypePresentation/CTypePresentation.tsx @@ -5,9 +5,9 @@ import React, { ReactNode } from 'react' import { Link, RouteComponentProps, withRouter } from 'react-router-dom' import { RequestAcceptDelegationProps } from '../../containers/Tasks/RequestAcceptDelegation/RequestAcceptDelegation' import { RequestClaimsForCTypeProps } from '../../containers/Tasks/RequestClaimsForCType/RequestClaimsForCType' -import { RequestLegitimationsProps } from '../../containers/Tasks/RequestLegitimation/RequestLegitimation' +import { RequestTermsProps } from '../../containers/Tasks/RequestTerms/RequestTerms' import { SubmitClaimsForCTypeProps } from '../../containers/Tasks/SubmitClaimsForCType/SubmitClaimsForCType' -import { SubmitLegitimationsProps } from '../../containers/Tasks/SubmitLegitimations/SubmitLegitimations' +import { SubmitTermsProps } from '../../containers/Tasks/SubmitTerms/SubmitTerms' import CTypeRepository from '../../services/CtypeRepository' import * as UiState from '../../state/ducks/UiState' @@ -111,11 +111,11 @@ class CTypePresentation extends React.Component { objective: sdk.MessageBodyType.REQUEST_TERMS, props: { cTypeHash, - } as RequestLegitimationsProps, + } as RequestTermsProps, }) ) }, - label: 'Request legitimations', + label: 'Request Terms', }, { callback: () => { @@ -135,11 +135,11 @@ class CTypePresentation extends React.Component { objective: sdk.MessageBodyType.SUBMIT_TERMS, props: { claim: { cTypeHash }, - } as SubmitLegitimationsProps, + } as SubmitTermsProps, }) ) }, - label: 'Submit legitimations', + label: 'Submit Terms', }, { callback: () => { diff --git a/src/components/ContactPresentation/ContactPresentation.tsx b/src/components/ContactPresentation/ContactPresentation.tsx index 8305cfd6..7dd384c4 100644 --- a/src/components/ContactPresentation/ContactPresentation.tsx +++ b/src/components/ContactPresentation/ContactPresentation.tsx @@ -4,8 +4,8 @@ import _ from 'lodash' import React from 'react' import { connect, MapStateToProps } from 'react-redux' import { RequestAcceptDelegationProps } from '../../containers/Tasks/RequestAcceptDelegation/RequestAcceptDelegation' -import { RequestLegitimationsProps } from '../../containers/Tasks/RequestLegitimation/RequestLegitimation' -import { SubmitLegitimationsProps } from '../../containers/Tasks/SubmitLegitimations/SubmitLegitimations' +import { RequestTermsProps } from '../../containers/Tasks/RequestTerms/RequestTerms' +import { SubmitTermsProps } from '../../containers/Tasks/SubmitTerms/SubmitTerms' import ContactRepository from '../../services/ContactRepository' import * as Contacts from '../../state/ducks/Contacts' @@ -104,11 +104,11 @@ class ContactPresentation extends React.Component { objective: sdk.MessageBodyType.REQUEST_TERMS, props: { receiverAddresses: [address], - } as RequestLegitimationsProps, + } as RequestTermsProps, }) ) }, - label: 'Request legitimations', + label: 'Request Terms', }) actions.push({ @@ -132,11 +132,11 @@ class ContactPresentation extends React.Component { objective: sdk.MessageBodyType.SUBMIT_TERMS, props: { receiverAddresses: [address], - } as SubmitLegitimationsProps, + } as SubmitTermsProps, }) ) }, - label: 'Submit legitimations', + label: 'Submit Terms', }) actions.push({ diff --git a/src/components/CtypeListView/CtypeListView.tsx b/src/components/CtypeListView/CtypeListView.tsx index f5bb9c87..cbdc7a20 100644 --- a/src/components/CtypeListView/CtypeListView.tsx +++ b/src/components/CtypeListView/CtypeListView.tsx @@ -16,7 +16,7 @@ type StateProps = { } type OwnProps = { - onRequestLegitimation: (cType: ICTypeWithMetadata) => void + onRequestTerm: (cType: ICTypeWithMetadata) => void } type Props = RouteComponentProps<{}> & StateProps & OwnProps diff --git a/src/components/DevTools/DevTools.attestations.tsx b/src/components/DevTools/DevTools.attestations.tsx index fc48bfad..468475d8 100644 --- a/src/components/DevTools/DevTools.attestations.tsx +++ b/src/components/DevTools/DevTools.attestations.tsx @@ -22,7 +22,7 @@ type UpdateCallback = (bsAttestationKey: keyof BsAttestationsPool) => void type BsAttestationsPoolElement = { attest: { attesterKey: keyof BsIdentitiesPool - legitimations?: Array + terms?: Array delegationKey?: keyof BsDelegationsPool } claimKey: keyof BsClaimsPool @@ -152,7 +152,7 @@ class BsAttestation { claimerIdentity: IMyIdentity ): Promise { const { attest } = bsAttestationData - const { delegationKey, legitimations } = attest + const { delegationKey, terms } = attest // resolve delegation let delegation: IMyDelegation | undefined @@ -160,11 +160,10 @@ class BsAttestation { delegation = await BsDelegation.getDelegationByKey(delegationKey) } - // get legitimations of attester - let legitimationsFromPool: sdk.AttestedClaim[] = [] - if (legitimations && Array.isArray(legitimations) && legitimations.length) { - legitimationsFromPool = await Promise.all( - legitimations.map(bsAttestationKey => { + let termsFromPool: sdk.AttestedClaim[] = [] + if (terms && Array.isArray(terms) && terms.length) { + termsFromPool = await Promise.all( + terms.map(bsAttestationKey => { const bsAttestedClaim = bsAttestedClaims[bsAttestationKey] if (bsAttestedClaim) { return Promise.resolve(bsAttestedClaim) @@ -179,7 +178,7 @@ class BsAttestation { return sdk.RequestForAttestation.fromClaimAndIdentity( claimToAttest.claim, claimerIdentity.identity, - legitimationsFromPool, + termsFromPool, delegation ? delegation.id : null ) } @@ -247,7 +246,7 @@ class BsAttestation { owner: claimerIdentity.identity.address, } - // send request for legitimation from claimer to attester + // send request for term from claimer to attester const requestAcceptDelegation: sdk.IRequestTerms = { content: partialClaim, type: sdk.MessageBodyType.REQUEST_TERMS, @@ -258,17 +257,18 @@ class BsAttestation { ContactRepository.getContactFromIdentity(attesterIdentity) ) - // send legitimations from attester to claimer - const submitLegitimations: sdk.ISubmitTerms = { + // send terms from attester to claimer + const submitTerms: sdk.ISubmitTerms = { content: { claim: partialClaim, delegationId: attestedClaim.request.delegationId || undefined, legitimations: attestedClaim.request.legitimations, + quote: undefined, }, type: sdk.MessageBodyType.SUBMIT_TERMS, } await MessageRepository.singleSend( - submitLegitimations, + submitTerms, attesterIdentity, ContactRepository.getContactFromIdentity(claimerIdentity) ) diff --git a/src/components/DevTools/data/attestations.json b/src/components/DevTools/data/attestations.json index 71fd704d..cd7340ab 100644 --- a/src/components/DevTools/data/attestations.json +++ b/src/components/DevTools/data/attestations.json @@ -60,4 +60,4 @@ "delegationKey": "DRIVERS_LICENSE_2_1" } } -} \ No newline at end of file +} diff --git a/src/components/DevTools/data/claims.json b/src/components/DevTools/data/claims.json index c338adf4..3bb79981 100644 --- a/src/components/DevTools/data/claims.json +++ b/src/components/DevTools/data/claims.json @@ -38,4 +38,4 @@ "name": "CLAIMER" } } -} \ No newline at end of file +} diff --git a/src/components/DevTools/data/delegations.json b/src/components/DevTools/data/delegations.json index 561ba1e6..f99a6e58 100644 --- a/src/components/DevTools/data/delegations.json +++ b/src/components/DevTools/data/delegations.json @@ -7,41 +7,29 @@ "DRIVERS_LICENSE_1": { "alias": "Driving License Authority Berlin (Delegation)", "ownerKey": "DEPARTMENT_1", - "permissions": [ - "ATTEST", - "DELEGATE" - ], + "permissions": ["ATTEST", "DELEGATE"], "children": { "DRIVERS_LICENSE_1_1": { "alias": "Employee 1 Berlin (Delegation)", "ownerKey": "EMPLOYEE_1_1", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] }, "DRIVERS_LICENSE_1_2": { "alias": "Employee 2 Berlin (Delegation)", "ownerKey": "EMPLOYEE_1_2", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] } } }, "DRIVERS_LICENSE_2": { "alias": "Driving License Authority Munich (Delegation)", "ownerKey": "DEPARTMENT_2", - "permissions": [ - "ATTEST", - "DELEGATE" - ], + "permissions": ["ATTEST", "DELEGATE"], "children": { "DRIVERS_LICENSE_2_1": { "alias": "Employee 2 Munich (Delegation)", "ownerKey": "EMPLOYEE_2_1", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] } } } @@ -55,17 +43,13 @@ "IS_OFFICIAL_1": { "alias": "Driving License Authority Berlin (Delegation)", "ownerKey": "DEPARTMENT_1", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] }, "IS_OFFICIAL_2": { "alias": "Driving License Authority Munich (Delegation)", "ownerKey": "DEPARTMENT_2", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] } } } -} \ No newline at end of file +} diff --git a/src/components/DevTools/data/identities.json b/src/components/DevTools/data/identities.json index 64980bee..d437b127 100644 --- a/src/components/DevTools/data/identities.json +++ b/src/components/DevTools/data/identities.json @@ -8,4 +8,4 @@ "EMPLOYEE_2_1": "Employee 1 of DLA Munich", "CLAIMER": "Driver (Claimer)", "VERIFIER": "Cop (Verifier)" -} \ No newline at end of file +} diff --git a/src/components/DevTools/data/pcr.json b/src/components/DevTools/data/pcr.json index 9a33b3ca..16b123cf 100644 --- a/src/components/DevTools/data/pcr.json +++ b/src/components/DevTools/data/pcr.json @@ -7,41 +7,29 @@ "PCR_DRIVERS_LICENSE_1": { "alias": "Driving License Authority Berlin (PCR)", "ownerKey": "DEPARTMENT_1", - "permissions": [ - "ATTEST", - "DELEGATE" - ], + "permissions": ["ATTEST", "DELEGATE"], "children": { "PCR_DRIVERS_LICENSE_1_1": { "alias": "Employee 1 Berlin (PCR)", "ownerKey": "EMPLOYEE_1_1", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] }, "PCR_DRIVERS_LICENSE_1_2": { "alias": "Employee 2 Berlin (PCR)", "ownerKey": "EMPLOYEE_1_2", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] } } }, "PCR_DRIVERS_LICENSE_2": { "alias": "Driving License Authority Munich (PCR)", "ownerKey": "DEPARTMENT_2", - "permissions": [ - "ATTEST", - "DELEGATE" - ], + "permissions": ["ATTEST", "DELEGATE"], "children": { "PCR_DRIVERS_LICENSE_2_1": { "alias": "Employee 2 Munich (PCR)", "ownerKey": "EMPLOYEE_2_1", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] } } } @@ -55,17 +43,13 @@ "PCR_IS_OFFICIAL_1": { "alias": "Driving License Authority Berlin (PCR)", "ownerKey": "DEPARTMENT_1", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] }, "PCR_IS_OFFICIAL_2": { "alias": "Driving License Authority Munich (PCR)", "ownerKey": "DEPARTMENT_2", - "permissions": [ - "ATTEST" - ] + "permissions": ["ATTEST"] } } } -} \ No newline at end of file +} diff --git a/src/components/MessageDetailView/MessageDetailView.scss b/src/components/MessageDetailView/MessageDetailView.scss index b37b578c..a551d805 100644 --- a/src/components/MessageDetailView/MessageDetailView.scss +++ b/src/components/MessageDetailView/MessageDetailView.scss @@ -1,5 +1,4 @@ .MessageDetailView { - & h4 { display: flex; justify-content: space-between; @@ -7,11 +6,10 @@ } & .toggle-code { - @include button-icon-only($icon-code) + @include button-icon-only($icon-code); } & footer { - & .delete { @include button-primary; } @@ -25,7 +23,7 @@ & > * { @include border-radius($base-border-radius $base-border-radius 0 0); - border:{ + border: { width: 1px 1px 0 1px; style: solid; color: $color-grey-light; @@ -35,13 +33,10 @@ } & .code { - @each $message in encrypted, decrypted { - &.#{$message} { - & .#{$message} { - border:{ + border: { color: $color-grey-dark; } background: $color-grey-dark; @@ -50,4 +45,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/components/MessageDetailView/MessageDetailView.tsx b/src/components/MessageDetailView/MessageDetailView.tsx index e4619acd..a4379998 100644 --- a/src/components/MessageDetailView/MessageDetailView.tsx +++ b/src/components/MessageDetailView/MessageDetailView.tsx @@ -6,7 +6,7 @@ import AttestClaim from '../../containers/Tasks/AttestClaim/AttestClaim' import CreateDelegation from '../../containers/Tasks/CreateDelegation/CreateDelegation' import ImportAttestation from '../../containers/Tasks/ImportAttestation/ImportAttestation' import SubmitClaimsForCType from '../../containers/Tasks/SubmitClaimsForCType/SubmitClaimsForCType' -import SubmitLegitimations from '../../containers/Tasks/SubmitLegitimations/SubmitLegitimations' +import SubmitTerms from '../../containers/Tasks/SubmitTerms/SubmitTerms' import RequestAttestation from '../../containers/Tasks/RequestAttestation/RequestAttestation' import VerifyClaim from '../../containers/Tasks/VerifyClaim/VerifyClaim' import { IMessageOutput } from '../../services/MessageRepository' @@ -97,10 +97,13 @@ class MessageDetailView extends React.Component { + {showTask && message.sender ? ( - { ) : (
)} @@ -119,12 +122,11 @@ class MessageDetailView extends React.Component { return ( { (message.body as sdk.IRequestAttestationForClaim).content .requestForAttestation } + quoteData={ + (message.body as sdk.IRequestAttestationForClaim).content.quote + } onCancel={this.handleCancel} onFinished={this.handleDelete} /> diff --git a/src/components/MessageListView/MessageListView.scss b/src/components/MessageListView/MessageListView.scss index ddd70aad..3c6f635d 100644 --- a/src/components/MessageListView/MessageListView.scss +++ b/src/components/MessageListView/MessageListView.scss @@ -1,5 +1,5 @@ .MessageListView { - + & table { @include table; @include responsiveTable( @@ -38,19 +38,17 @@ } & .invalid { - &:before { @include icon($icon-warning); color: $color-danger; - margin:{ + margin: { right: $base-padding/4; } } } - @include breakpoint(phone-tablet-only){ - + @include breakpoint(phone-tablet-only) { & td { padding-bottom: $base-padding; } diff --git a/src/components/MyClaimDetailView/MyClaimDetailView.tsx b/src/components/MyClaimDetailView/MyClaimDetailView.tsx index d07aab07..07c22d66 100644 --- a/src/components/MyClaimDetailView/MyClaimDetailView.tsx +++ b/src/components/MyClaimDetailView/MyClaimDetailView.tsx @@ -13,7 +13,7 @@ type Props = { hideAttestedClaims?: boolean onRemoveClaim?: (claimEntry: Claims.Entry) => void onRequestAttestation?: (claimEntry: Claims.Entry) => void - onRequestLegitimation?: (claimEntry: Claims.Entry) => void + onRequestTerm?: (claimEntry: Claims.Entry) => void } type State = { @@ -25,7 +25,7 @@ class MyClaimDetailView extends Component { super(props) this.handleDelete = this.handleDelete.bind(this) this.requestAttestation = this.requestAttestation.bind(this) - this.requestLegitimation = this.requestLegitimation.bind(this) + this.requestTerm = this.requestTerm.bind(this) } private getActions(): JSX.Element { @@ -33,7 +33,7 @@ class MyClaimDetailView extends Component { cancelable, onRemoveClaim, onRequestAttestation, - onRequestLegitimation, + onRequestTerm, }: Props = this.props return (
@@ -49,14 +49,14 @@ class MyClaimDetailView extends Component { onClick={this.handleDelete} /> )} - {onRequestLegitimation && ( + {onRequestTerm && ( )} {onRequestAttestation && ( @@ -87,10 +87,10 @@ class MyClaimDetailView extends Component { } } - private requestLegitimation(): void { - const { claimEntry, onRequestLegitimation }: Props = this.props - if (claimEntry && onRequestLegitimation) { - onRequestLegitimation(claimEntry) + private requestTerm(): void { + const { claimEntry, onRequestTerm }: Props = this.props + if (claimEntry && onRequestTerm) { + onRequestTerm(claimEntry) } } diff --git a/src/components/MyClaimListView/MyClaimListView.tsx b/src/components/MyClaimListView/MyClaimListView.tsx index 4c306320..31a2bf0b 100644 --- a/src/components/MyClaimListView/MyClaimListView.tsx +++ b/src/components/MyClaimListView/MyClaimListView.tsx @@ -16,7 +16,7 @@ type Props = { onCreateClaimFromCType: (selectedCTypes: ICTypeWithMetadata[]) => void onRemoveClaim: (claimEntry: Claims.Entry) => void onRequestAttestation: (claimEntry: Claims.Entry) => void - onRequestLegitimation: (claimEntry: Claims.Entry) => void + onRequestTerm: (claimEntry: Claims.Entry) => void } type State = {} @@ -39,8 +39,8 @@ class MyClaimListView extends React.Component { private getActions(claimEntry: Claims.Entry): Actions { return [ { - callback: this.requestLegitimation.bind(this, claimEntry), - label: 'Request legitimations', + callback: this.requestTerm.bind(this, claimEntry), + label: 'Request terms', }, { callback: this.requestAttestation.bind(this, claimEntry), @@ -63,9 +63,9 @@ class MyClaimListView extends React.Component { onRequestAttestation(claimEntry) } - private requestLegitimation(claimEntry: Claims.Entry): void { - const { onRequestLegitimation } = this.props - onRequestLegitimation(claimEntry) + private requestTerm(claimEntry: Claims.Entry): void { + const { onRequestTerm } = this.props + onRequestTerm(claimEntry) } private openCTypeModal(): void { diff --git a/src/components/Navigation/Navigation.tsx b/src/components/Navigation/Navigation.tsx index d73e31f1..dc3b3e04 100644 --- a/src/components/Navigation/Navigation.tsx +++ b/src/components/Navigation/Navigation.tsx @@ -12,6 +12,7 @@ const links: NavLink[] = [ { url: 'claim', label: 'Claims' }, { url: 'attestations', label: 'Attestations' }, { url: 'delegations', label: 'Delegations' }, + { url: 'quote', label: 'Quotes' }, { url: 'pcrs', label: 'PCRs' }, { url: 'contacts', label: 'Contacts' }, { url: 'messages', label: 'Messages' }, diff --git a/src/components/SelectAttestedClaims/SelectAttestedClaims.tsx b/src/components/SelectAttestedClaims/SelectAttestedClaims.tsx index 3ad0316a..71941407 100644 --- a/src/components/SelectAttestedClaims/SelectAttestedClaims.tsx +++ b/src/components/SelectAttestedClaims/SelectAttestedClaims.tsx @@ -25,7 +25,7 @@ export type SelectAttestedClaimsLabels = { type AllLabels = { default: SelectAttestedClaimsLabels - legitimation: SelectAttestedClaimsLabels + term: SelectAttestedClaimsLabels } const LABELS: AllLabels = { @@ -42,18 +42,17 @@ const LABELS: AllLabels = { noClaimsFound: `No claims found.`, }, }, - legitimation: { + term: { buttons: { - createClaim: 'Create legitimation', + createClaim: 'Create Terms', requestAttestation: 'Request attestation(s)', }, text: { attestationsHeadline: 'Select attestation(s)', - includePropertiesHeadline: - 'Select property(s) to include in Legitimation', + includePropertiesHeadline: 'Select property(s) to include in Terms', noAttestationFound: 'No attestation found.', noClaimsForCTypeFound: `No attested claims found for CTYPE. `, - noClaimsFound: `No legitimations found.`, + noClaimsFound: `No terms found.`, }, }, } @@ -75,7 +74,7 @@ type StateProps = { type OwnProps = { cTypeHashes?: Array - context?: 'default' | 'legitimation' + context?: 'default' | 'terms' onChange: (claimSelectionData: ClaimSelectionData) => void } diff --git a/src/containers/ClaimView/ClaimView.tsx b/src/containers/ClaimView/ClaimView.tsx index fd2954cd..85677741 100644 --- a/src/containers/ClaimView/ClaimView.tsx +++ b/src/containers/ClaimView/ClaimView.tsx @@ -20,7 +20,7 @@ import { IContact, IMyIdentity } from '../../types/Contact' import './ClaimView.scss' import { ICTypeWithMetadata } from '../../types/Ctype' import { RequestAttestationProps } from '../Tasks/RequestAttestation/RequestAttestation' -import { RequestLegitimationsProps } from '../Tasks/RequestLegitimation/RequestLegitimation' +import { RequestTermsProps } from '../Tasks/RequestTerms/RequestTerms' type StateProps = { claimEntries: Claims.Entry[] @@ -40,14 +40,14 @@ type State = { } class ClaimView extends React.Component { - private static requestLegitimation(claimEntry: Claims.Entry): void { + private static requestTerm(claimEntry: Claims.Entry): void { PersistentStore.store.dispatch( UiState.Store.updateCurrentTaskAction({ objective: sdk.MessageBodyType.REQUEST_TERMS, props: { cTypeHash: claimEntry.claim.cTypeHash, preSelectedClaimEntries: [claimEntry], - } as RequestLegitimationsProps, + } as RequestTermsProps, }) ) } @@ -70,10 +70,8 @@ class ClaimView extends React.Component { super(props) this.state = {} this.deleteClaim = this.deleteClaim.bind(this) - this.cancelSelectAttesters = this.cancelSelectAttesters.bind(this) this.finishSelectAttesters = this.finishSelectAttesters.bind(this) - this.createClaimFromCType = this.createClaimFromCType.bind(this) } @@ -133,7 +131,7 @@ class ClaimView extends React.Component { if (claim) { if (this.claimIdToLegitimate) { - attestationWorkflow.requestLegitimations( + attestationWorkflow.requestTerms( [claim], selectedAttesters.map( (contact: IContact) => contact.publicIdentity.address @@ -194,7 +192,7 @@ class ClaimView extends React.Component { claimEntry={currentClaimEntry} onRemoveClaim={this.deleteClaim} onRequestAttestation={ClaimView.requestAttestation} - onRequestLegitimation={ClaimView.requestLegitimation} + onRequestTerm={ClaimView.requestTerm} /> )} {!isDetailView && ( @@ -203,7 +201,7 @@ class ClaimView extends React.Component { onCreateClaimFromCType={this.createClaimFromCType} onRemoveClaim={this.deleteClaim} onRequestAttestation={ClaimView.requestAttestation} - onRequestLegitimation={ClaimView.requestLegitimation} + onRequestTerm={ClaimView.requestTerm} /> )} {} diff --git a/src/containers/CtypeView/CtypeView.tsx b/src/containers/CtypeView/CtypeView.tsx index e2f86018..59434ef8 100644 --- a/src/containers/CtypeView/CtypeView.tsx +++ b/src/containers/CtypeView/CtypeView.tsx @@ -18,12 +18,13 @@ class CtypeView extends React.Component { constructor(props: Props) { super(props) - this.requestLegitimation = this.requestLegitimation.bind(this) + this.requestTerm = this.requestTerm.bind(this) + this.cancelSelectAttesters = this.cancelSelectAttesters.bind(this) this.finishSelectAttesters = this.finishSelectAttesters.bind(this) } - private requestLegitimation(cType: ICTypeWithMetadata): void { + private requestTerm(cType: ICTypeWithMetadata): void { if (cType && this.selectAttestersModal) { this.cTypeToLegitimate = cType this.selectAttestersModal.show() @@ -39,7 +40,7 @@ class CtypeView extends React.Component { private finishSelectAttesters(selectedAttesters: IContact[]): void { if (this.cTypeToLegitimate.cType.hash) { - attestationWorkflow.requestLegitimations( + attestationWorkflow.requestTerms( [{ cTypeHash: this.cTypeToLegitimate.cType.hash }], selectedAttesters.map( (contact: IContact) => contact.publicIdentity.address @@ -56,9 +57,7 @@ class CtypeView extends React.Component {

CTYPEs

{cTypeHash && } - {!cTypeHash && ( - - )} + {!cTypeHash && } { this.selectAttestersModal = el diff --git a/src/containers/MyQuotesList/MyQuotesList.scss b/src/containers/MyQuotesList/MyQuotesList.scss new file mode 100644 index 00000000..1f62eee9 --- /dev/null +++ b/src/containers/MyQuotesList/MyQuotesList.scss @@ -0,0 +1,43 @@ +.MyQuotesList { + @include labeledCard; + & h1 { + @include h1('Ctype'); + } + & > div { + @include label-input-combo; + } + & table { + @include table; + @include responsiveTable( + ( + phone: ( + 100px + false, + 100px + 30px, + + getActionTdWidth(2), + ), + tablet: ( + 100px + false, + 100px + 50px, + + getActionTdWidth(2), + ), + desktop: ( + 100px, + 100px, + 300px, + 50px, + getActionTdWidth(2), + ), + ) + ); + } + & .delete { + @include button-icon-only($icon-trash); + } + +} diff --git a/src/containers/MyQuotesList/MyQuotesList.tsx b/src/containers/MyQuotesList/MyQuotesList.tsx new file mode 100644 index 00000000..009b000f --- /dev/null +++ b/src/containers/MyQuotesList/MyQuotesList.tsx @@ -0,0 +1,109 @@ +import React from 'react' +import { RouteComponentProps, withRouter } from 'react-router-dom' +import { connect, MapStateToProps } from 'react-redux' + +import { State as ReduxState } from '../../state/PersistentStore' +import * as Quotes from '../../state/ducks/Quotes' +import * as Wallet from '../../state/ducks/Wallet' +import Code from '../../components/Code/Code' +import './MyQuotesList.scss' +import { IMyIdentity } from '../../types/Contact' + +type DispatchProps = { + removeQuote: (claimId: Quotes.Entry['quoteId']) => void +} + +type OwnProps = {} + +type StateProps = { + selectedIdentity: IMyIdentity + quoteEntries: Quotes.Entry[] +} + +type Props = RouteComponentProps<{ quoteId: Quotes.Entry['quoteId'] }> & + StateProps & + DispatchProps & + OwnProps + +class MyQuotesList extends React.Component { + constructor(props: Props) { + super(props) + this.state = {} + this.deleteQuote = this.deleteQuote.bind(this) + } + + private deleteQuote(quoteId: Quotes.Entry['quoteId']): void { + const { removeQuote } = this.props + removeQuote(quoteId) + } + + public render(): JSX.Element { + const { quoteEntries } = this.props + + return ( +
+

Quotes

+
+

My Quote list

+ {quoteEntries && quoteEntries.length ? ( + + + + + + + + + + + {quoteEntries.map((val: Quotes.Entry, index) => { + const quoteItem = val + + return ( + + + + + + + + ) + })} + +
Quote Id Owner Address Quote Actions
{quoteItem.quoteId}{quoteItem.owner} + {quoteItem} + +
+ ) : ( +
No Quotes
+ )} +
+
+ ) + } +} + +const mapStateToProps: MapStateToProps< + StateProps, + OwnProps, + ReduxState +> = state => ({ + selectedIdentity: Wallet.getSelectedIdentity(state), + quoteEntries: Quotes.getAllMyQuotes(state), +}) + +const mapDispatchToProps: DispatchProps = { + removeQuote: (quoteId: Quotes.Entry['quoteId']) => + Quotes.Store.removeQuote(quoteId), +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(withRouter(MyQuotesList)) diff --git a/src/containers/QuoteCreate/QuoteCreate.scss b/src/containers/QuoteCreate/QuoteCreate.scss new file mode 100644 index 00000000..694fae15 --- /dev/null +++ b/src/containers/QuoteCreate/QuoteCreate.scss @@ -0,0 +1,9 @@ +.QuoteCreate { + @include labeledCard; + & h1 { + @include h1('Ctype'); + } + & > div { + @include label-input-combo; + } +} diff --git a/src/containers/QuoteCreate/QuoteCreate.tsx b/src/containers/QuoteCreate/QuoteCreate.tsx new file mode 100644 index 00000000..40cd36a9 --- /dev/null +++ b/src/containers/QuoteCreate/QuoteCreate.tsx @@ -0,0 +1,114 @@ +import * as sdk from '@kiltprotocol/sdk-js' +import React from 'react' +import { withRouter, RouteComponentProps } from 'react-router' +import * as common from 'schema-based-json-editor' +import DayPickerInput from 'react-day-picker/DayPickerInput' +import QuoteInputSchema from '../../utils/QuoteUtils/QuoteInputSchema' +import SchemaEditor from '../../components/SchemaEditor/SchemaEditor' +import * as Quotes from '../../state/ducks/Quotes' + +import 'react-day-picker/lib/style.css' + +import './QuoteCreate.scss' + +type OwnProps = { + cTypeHash?: sdk.ICType['hash'] + claimerAddress?: string + attesterAddress?: string + onCancel?: () => void + newQuote: (quote: sdk.IQuote) => void +} + +type Props = RouteComponentProps<{ quoteId: Quotes.Entry['quoteId'] }> & + OwnProps + +type State = { + quote?: sdk.IQuote + initialValue?: object + startDate: Date +} + +class QuoteCreate extends React.Component { + constructor(props: Props) { + super(props) + const { attesterAddress, cTypeHash } = this.props + this.state = { + startDate: new Date(), + initialValue: { + attesterAddress, + cTypeHash, + }, + } + this.handleChange = this.handleChange.bind(this) + this.handleCancel = this.handleCancel.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + this.updateValue = this.updateValue.bind(this) + } + + public updateValue = (value: sdk.IQuote): void => { + const quote = value + const result = {} + Object.keys(value.cost.tax).forEach(entryKey => { + result[entryKey] = value.cost.tax[entryKey] + }) + quote.cost.tax = result + + this.setState({ quote }) + } + + private handleChange = (date: Date): void => { + this.setState({ + startDate: date, + }) + } + + private handleSubmit(): void { + const { claimerAddress, newQuote } = this.props + const { quote, startDate } = this.state + if (quote && claimerAddress) { + quote.timeframe = startDate + newQuote(quote) + } + } + + private handleCancel(): void { + const { onCancel } = this.props + + if (onCancel) { + onCancel() + } + } + + render(): JSX.Element { + const { onCancel } = this.props + const { initialValue } = this.state + + return ( +
+
+ + + +
+ +
+ {onCancel && ( + + )} + + +
+
+ ) + } +} + +export default withRouter(QuoteCreate) diff --git a/src/containers/QuoteCreate/QuoteSchema.ts b/src/containers/QuoteCreate/QuoteSchema.ts new file mode 100644 index 00000000..9b0c5771 --- /dev/null +++ b/src/containers/QuoteCreate/QuoteSchema.ts @@ -0,0 +1,62 @@ +export default { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'KILT:quote:v2', + type: 'object', + title: 'Quote', + properties: { + attesterAddress: { + type: 'string', + }, + cTypeHash: { + type: 'string', + }, + cost: { + type: 'object', + required: ['net', 'gross', 'tax'], + properties: { + net: { + type: 'number', + }, + gross: { + type: 'number', + }, + tax: { + type: 'array', + items: { + type: 'object', + properties: { + title: { + title: 'Tax code', + type: 'string', + default: 'New Property', + }, + value: { + type: 'number', + }, + }, + required: ['title', 'value'], + }, + collapsed: false, + }, + }, + }, + currency: { + type: 'string', + }, + termsAndConditions: { + type: 'string', + }, + timeframe: { + type: 'string', + format: 'date-time', + }, + }, + required: [ + 'attesterAddress', + 'cTypeHash', + 'cost', + 'currency', + 'termsAndConditions', + 'timeframe', + ], +} diff --git a/src/containers/QuoteView/QuoteView.scss b/src/containers/QuoteView/QuoteView.scss new file mode 100644 index 00000000..d8057120 --- /dev/null +++ b/src/containers/QuoteView/QuoteView.scss @@ -0,0 +1,10 @@ +.QuoteView { + & h1 { + @include h1('Ctype'); + } + @include labeledCard; + + & > div { + @include label-input-combo; + } +} diff --git a/src/containers/QuoteView/QuoteView.tsx b/src/containers/QuoteView/QuoteView.tsx new file mode 100644 index 00000000..b14f5727 --- /dev/null +++ b/src/containers/QuoteView/QuoteView.tsx @@ -0,0 +1,118 @@ +import './QuoteView.scss' +import React from 'react' +import { connect, MapStateToProps } from 'react-redux' +import { RouteComponentProps, withRouter } from 'react-router-dom' +import * as sdk from '@kiltprotocol/sdk-js' +import { State as ReduxState } from '../../state/PersistentStore' +import { IMyIdentity } from '../../types/Contact' +import * as Wallet from '../../state/ducks/Wallet' +import * as Quotes from '../../state/ducks/Quotes' +import QuoteCreate from '../QuoteCreate/QuoteCreate' +import Code from '../../components/Code/Code' + +type StateProps = { + selectedIdentity: IMyIdentity + quoteEntries?: Quotes.Entry[] +} + +type OwnProps = { + claim: sdk.IPartialClaim + senderAddress?: string + receiverAddress?: string + updateQuote: (quote: sdk.IQuote) => void +} + +type Props = RouteComponentProps<{ quoteId: Quotes.Entry['quoteId'] }> & + StateProps & + OwnProps + +type State = { + createNewQuote: boolean + newQuote?: sdk.IQuote +} + +class QuoteView extends React.Component { + constructor(props: Props) { + super(props) + this.state = { createNewQuote: false } + this.createQuote = this.createQuote.bind(this) + this.onCancelQuote = this.onCancelQuote.bind(this) + this.confirmQuote = this.confirmQuote.bind(this) + } + + public componentDidUpdate(): void { + const { updateQuote } = this.props + const { newQuote } = this.state + + if (newQuote) updateQuote(newQuote) + } + + private onCancelQuote(): void { + this.setState({ createNewQuote: false }) + } + + private createQuote(): void { + this.setState({ createNewQuote: true }) + } + + private confirmQuote(quote: sdk.IQuote): void { + if (quote) { + this.setState({ newQuote: quote, createNewQuote: false }) + } + } + + public render(): JSX.Element { + const { senderAddress, receiverAddress, claim } = this.props + + const { createNewQuote, newQuote } = this.state + return ( +
+

Quote

+ {newQuote ? ( +
+ + {newQuote} + +
+ ) : ( +
No Quote
+ )} + + {!createNewQuote ? ( +
+
+ +
+
+ ) : ( +
+ this.confirmQuote(quote)} + /> +
+ )} +
+ ) + } +} + +const mapStateToProps: MapStateToProps< + StateProps, + OwnProps, + ReduxState +> = state => ({ + selectedIdentity: Wallet.getSelectedIdentity(state), + quoteEntries: Quotes.getAllMyQuotes(state), +}) + +export default connect(mapStateToProps)(withRouter(QuoteView)) diff --git a/src/containers/Tasks/AttestClaim/AttestClaim.tsx b/src/containers/Tasks/AttestClaim/AttestClaim.tsx index 84c01983..cebc2427 100644 --- a/src/containers/Tasks/AttestClaim/AttestClaim.tsx +++ b/src/containers/Tasks/AttestClaim/AttestClaim.tsx @@ -1,23 +1,41 @@ import * as sdk from '@kiltprotocol/sdk-js' import React from 'react' +import { connect } from 'react-redux' import AttestedClaimsListView from '../../../components/AttestedClaimsListView/AttestedClaimsListView' import ClaimDetailView from '../../../components/ClaimDetailView/ClaimDetailView' import attestationWorkflow from '../../../services/AttestationWorkflow' +import * as Quotes from '../../../state/ducks/Quotes' +import * as Wallet from '../../../state/ducks/Wallet' +import PersistentStore from '../../../state/PersistentStore' + import FeedbackService, { notifyError } from '../../../services/FeedbackService' import { IContact } from '../../../types/Contact' import { BlockUi } from '../../../types/UserFeedback' +import Code from '../../../components/Code/Code' + +type DispatchProps = { + saveAgreedQuote: ( + agreedQuote: sdk.IQuoteAgreement, + ownerAddress: string + ) => void +} -type Props = { +type OwnProps = { claimerAddresses: Array requestForAttestation: sdk.IRequestForAttestation + quoteData?: sdk.IQuoteAgreement claimer?: sdk.IPublicIdentity onCancel?: () => void onFinished?: () => void } -type State = {} +type Props = OwnProps & DispatchProps + +type State = { + quoteData?: sdk.IQuoteAgreement +} class AttestClaim extends React.Component { constructor(props: Props) { @@ -28,6 +46,13 @@ class AttestClaim extends React.Component { this.attestClaim = this.attestClaim.bind(this) } + componentDidMount(): void { + const { quoteData } = this.props + if (quoteData) { + this.setState({ quoteData }) + } + } + private onCancel(): void { const { onCancel } = this.props if (onCancel) { @@ -40,12 +65,23 @@ class AttestClaim extends React.Component { requestForAttestation, onFinished, claimerAddresses, + saveAgreedQuote, claimer, } = this.props + const { quoteData } = this.state + const blockUi: BlockUi = FeedbackService.addBlockUi({ headline: 'Writing attestation to chain', }) + const selectedIdentity: sdk.Identity = Wallet.getSelectedIdentity( + PersistentStore.store.getState() + ).identity + + if (!selectedIdentity) { + throw new Error('No identity selected') + } + attestationWorkflow .approveAndSubmitAttestationForClaim( requestForAttestation, @@ -55,6 +91,9 @@ class AttestClaim extends React.Component { .then(() => { blockUi.remove() if (onFinished) { + if (quoteData && selectedIdentity) { + saveAgreedQuote(quoteData, selectedIdentity.address) + } onFinished() } }) @@ -66,6 +105,7 @@ class AttestClaim extends React.Component { public render(): JSX.Element { const { requestForAttestation } = this.props + const { quoteData } = this.state return (
@@ -73,8 +113,21 @@ class AttestClaim extends React.Component { + {quoteData ? ( + +

Quotes

+
+ {quoteData} +
+
+ ) : ( + +

Quotes

+
no Quote
+
+ )}
@@ -102,4 +107,4 @@ class RequestLegitimation extends React.Component< } } -export default RequestLegitimation +export default RequestTerms diff --git a/src/containers/Tasks/SubmitLegitimations/SubmitLegitimations.scss b/src/containers/Tasks/SubmitTerms/SubmitTerms.scss similarity index 76% rename from src/containers/Tasks/SubmitLegitimations/SubmitLegitimations.scss rename to src/containers/Tasks/SubmitTerms/SubmitTerms.scss index 29b5e642..f1427461 100644 --- a/src/containers/Tasks/SubmitLegitimations/SubmitLegitimations.scss +++ b/src/containers/Tasks/SubmitTerms/SubmitTerms.scss @@ -1,6 +1,6 @@ -.SubmitLegitimations { +.SubmitTerms { - & .selectLegitimations, + & .selectTerms, & .selectDelegation, & .preFillClaim { @include labeledCard; diff --git a/src/containers/Tasks/SubmitLegitimations/SubmitLegitimations.tsx b/src/containers/Tasks/SubmitTerms/SubmitTerms.tsx similarity index 68% rename from src/containers/Tasks/SubmitLegitimations/SubmitLegitimations.tsx rename to src/containers/Tasks/SubmitTerms/SubmitTerms.tsx index 1947eed9..843f06c2 100644 --- a/src/containers/Tasks/SubmitLegitimations/SubmitLegitimations.tsx +++ b/src/containers/Tasks/SubmitTerms/SubmitTerms.tsx @@ -2,6 +2,7 @@ import * as sdk from '@kiltprotocol/sdk-js' import React from 'react' import * as common from 'schema-based-json-editor' +import { connect } from 'react-redux' import SchemaEditor from '../../../components/SchemaEditor/SchemaEditor' import SelectAttestedClaims from '../../../components/SelectAttestedClaims/SelectAttestedClaims' import SelectDelegations from '../../../components/SelectDelegations/SelectDelegations' @@ -11,33 +12,44 @@ import withSelectAttestedClaims, { import AttestationWorkflow from '../../../services/AttestationWorkflow' import CTypeRepository from '../../../services/CtypeRepository' import { IMyDelegation } from '../../../state/ducks/Delegations' +import * as Quotes from '../../../state/ducks/Quotes' import { IContact } from '../../../types/Contact' import { ICTypeWithMetadata } from '../../../types/Ctype' import { getClaimInputModel } from '../../../utils/CtypeUtils' +import QuoteView from '../../QuoteView/QuoteView' +import PersistentStore from '../../../state/PersistentStore' +import * as Wallet from '../../../state/ducks/Wallet' +import './SubmitTerms.scss' -import './SubmitLegitimations.scss' +type DispatchProps = { + saveAttestersQuote: ( + attesterSignedQuote: sdk.IQuoteAttesterSigned, + ownerAddress: string + ) => void +} -export type SubmitLegitimationsProps = { +export type SubmitTermsProps = { claim: sdk.IPartialClaim receiverAddresses: Array + senderAddress?: string + receiverAddress?: string receiver?: sdk.IPublicIdentity - enablePreFilledClaim?: boolean - onFinished?: () => void onCancel?: () => void } -type Props = InjectedSelectProps & SubmitLegitimationsProps +type Props = InjectedSelectProps & SubmitTermsProps & DispatchProps type State = { claim: sdk.IPartialClaim cType?: ICTypeWithMetadata selectedDelegation?: IMyDelegation withPreFilledClaim?: boolean + quoteData?: sdk.IQuote } -class SubmitLegitimations extends React.Component { +class SubmitTerms extends React.Component { constructor(props: Props) { super(props) this.state = { @@ -49,6 +61,7 @@ class SubmitLegitimations extends React.Component { this.sendClaim = this.sendClaim.bind(this) this.updateClaim = this.updateClaim.bind(this) this.toggleWithPreFilledClaim = this.toggleWithPreFilledClaim.bind(this) + this.updateQuote = this.updateQuote.bind(this) } public componentDidMount(): void { @@ -118,6 +131,13 @@ class SubmitLegitimations extends React.Component { }) } + private updateQuote(quote: sdk.IQuote): void { + const { quoteData } = this.state + if (quoteData !== quote) { + this.setState({ quoteData: quote }) + } + } + private sendClaim(): void { const { getAttestedClaims, @@ -125,21 +145,40 @@ class SubmitLegitimations extends React.Component { receiver, receiverAddresses, onFinished, + saveAttestersQuote, } = this.props - const { claim, selectedDelegation, withPreFilledClaim } = this.state + const { + claim, + selectedDelegation, + withPreFilledClaim, + quoteData, + } = this.state if (enablePreFilledClaim && !withPreFilledClaim) { delete claim.contents } + const selectedIdentity: sdk.Identity = Wallet.getSelectedIdentity( + PersistentStore.store.getState() + ).identity - AttestationWorkflow.submitLegitimations( + if (!selectedIdentity) { + throw new Error('No identity selected') + } + const quote = quoteData + ? sdk.Quote.createAttesterSignature(quoteData, selectedIdentity) + : undefined + AttestationWorkflow.submitTerms( claim, getAttestedClaims(), receiverAddresses, + quote, receiver, selectedDelegation ).then(() => { if (onFinished) { + if (quote && selectedIdentity) { + saveAttestersQuote(quote, selectedIdentity.address) + } onFinished() } }) @@ -153,14 +192,16 @@ class SubmitLegitimations extends React.Component { const { claimSelectionData, enablePreFilledClaim, - onChange, + claim, + senderAddress, + receiverAddress, } = this.props const { cType, selectedDelegation } = this.state return ( -
+
{enablePreFilledClaim && cType && (

Prefill claim

@@ -169,8 +210,8 @@ class SubmitLegitimations extends React.Component { )} <> -
-

Select legitimation(s)…

+
+

Select Legitimation(s)…

@@ -182,6 +223,15 @@ class SubmitLegitimations extends React.Component { />
+
+ +
+
@@ -202,4 +252,14 @@ class SubmitLegitimations extends React.Component { } } -export default withSelectAttestedClaims(SubmitLegitimations) +const mapDispatchToProps: DispatchProps = { + saveAttestersQuote: ( + attesterSignedQuote: sdk.IQuoteAttesterSigned, + ownerAddress: string + ) => Quotes.Store.saveAttestersQuote(attesterSignedQuote, ownerAddress), +} + +export default connect( + null, + mapDispatchToProps +)(withSelectAttestedClaims(SubmitTerms)) diff --git a/src/containers/Tasks/Tasks.tsx b/src/containers/Tasks/Tasks.tsx index a6f14dda..99af8242 100644 --- a/src/containers/Tasks/Tasks.tsx +++ b/src/containers/Tasks/Tasks.tsx @@ -18,15 +18,11 @@ import RequestAttestation, { import RequestClaimsForCType, { RequestClaimsForCTypeProps, } from './RequestClaimsForCType/RequestClaimsForCType' -import RequestLegitimation, { - RequestLegitimationsProps, -} from './RequestLegitimation/RequestLegitimation' +import RequestTerm, { RequestTermsProps } from './RequestTerms/RequestTerms' import SubmitClaimsForCType, { SubmitClaimsForCTypeProps, } from './SubmitClaimsForCType/SubmitClaimsForCType' -import SubmitLegitimations, { - SubmitLegitimationsProps, -} from './SubmitLegitimations/SubmitLegitimations' +import SubmitTerms, { SubmitTermsProps } from './SubmitTerms/SubmitTerms' import './Tasks.scss' @@ -37,11 +33,11 @@ export type TaskProps = } | { objective: sdk.MessageBodyType.REQUEST_TERMS - props: RequestLegitimationsProps + props: RequestTermsProps } | { objective: sdk.MessageBodyType.SUBMIT_TERMS - props: SubmitLegitimationsProps + props: SubmitTermsProps } | { objective: sdk.MessageBodyType.REQUEST_ATTESTATION_FOR_CLAIM @@ -161,11 +157,11 @@ class Tasks extends React.Component { ? selectedCTypes[0].cType.hash : undefined return this.getModal( - 'Request legitimations', + 'Request Terms', <> {this.getCTypeSelect(false, [props.cTypeHash])} {!!selectedCTypes.length && !!selectedReceivers.length ? ( - { const { props } = currentTask const cTypeHash = props.claim ? props.claim.cTypeHash : undefined return this.getModal( - 'Submit legitimations', + 'Submit Terms', <> {this.getCTypeSelect(false, [cTypeHash])} {!!selectedCTypes.length && selectedCTypes[0].cType.hash && !!selectedReceivers.length ? ( - = ({ attestedClaims }) => ( diff --git a/src/routes/index.tsx b/src/routes/index.tsx index a72d6759..4050b6d4 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -13,12 +13,14 @@ import ClaimView from '../containers/ClaimView/ClaimView' import ContactList from '../containers/ContactList/ContactList' import CTypeCreate from '../containers/CtypeCreate/CtypeCreate' import CtypeView from '../containers/CtypeView/CtypeView' +import QuoteCreate from '../containers/QuoteCreate/QuoteCreate' import MessageList from '../containers/MessageView/MessageView' import WalletAdd from '../containers/WalletAdd/WalletAdd' import WalletView from '../containers/WalletView/WalletView' import requiresIdentity from './RequiresIdentity' import DelegationCreate from '../containers/DelegationCreate/DelegationCreate' import Setup from '../containers/Setup/Setup' +import MyQuotesList from '../containers/MyQuotesList/MyQuotesList' const Routes: React.FC = () => { // const bbqBirch = encodeURIComponent('wss://substrate-rpc.parity.io/') @@ -32,6 +34,10 @@ const Routes: React.FC = () => { + + + + ): Promise { @@ -40,34 +40,42 @@ class AttestationWorkflow { } /** - * Sends back the legitimation along with the originally given (partial) + * Sends back the term along with the originally given (partial) * claim to the claimer. * * @param claim the (partial) claim to attest - * @param legitimations the list of legitimations to be included in the + * @param terms the list of terms to be included in the * attestation - * @param receiverAddresses list of contact addresses who will receive the legitimation - * @param delegation delegation to add to legitimations + * @param receiverAddresses list of contact addresses who will receive the term + * @param delegation delegation to add to terms */ - public static async submitLegitimations( + public static async submitTerms( claim: IPartialClaim, - legitimations: sdk.IAttestedClaim[], + terms: sdk.IAttestedClaim[], receiverAddresses: Array, + quote?: sdk.IQuoteAttesterSigned, receiver?: sdk.IPublicIdentity, delegation?: IMyDelegation ): Promise { const messageBody: sdk.ISubmitTerms = { - content: { claim, legitimations, delegationId: undefined }, + content: { + claim, + legitimations: terms, + delegationId: undefined, + quote: undefined, + }, type: sdk.MessageBodyType.SUBMIT_TERMS, } - if (delegation) { messageBody.content.delegationId = delegation.id } - + if (quote) { + messageBody.content.quote = quote + } if (receiver) { return MessageRepository.sendToPublicIdentity(receiver, messageBody) } + return MessageRepository.sendToAddresses(receiverAddresses, messageBody) } @@ -95,15 +103,16 @@ class AttestationWorkflow { * * @param claim - the claim to attest * @param attesterAddresses - the addresses of attesters - * @param [legitimations] - the legitimations the claimer requested + * @param [terms] - the terms the claimer requested * beforehand from attester * @param [delegationId] - the delegation the attester added as legitimation */ public static async requestAttestationForClaim( claim: sdk.IClaim, attesterAddresses: Array, - legitimations: sdk.AttestedClaim[] = [], - delegationId: sdk.IDelegationNode['id'] | null = null + terms: sdk.AttestedClaim[] = [], + delegationId: sdk.IDelegationNode['id'] | null = null, + quoteAttesterSigned?: sdk.IQuoteAgreement ): Promise { const { identity } = Wallet.getSelectedIdentity( persistentStore.store.getState() @@ -111,14 +120,17 @@ class AttestationWorkflow { const requestForAttestation = sdk.RequestForAttestation.fromClaimAndIdentity( claim, identity, - legitimations, + terms, delegationId ) + const messageBody: IRequestAttestationForClaim = { - content: { requestForAttestation }, + content: { requestForAttestation, quote: undefined }, type: MessageBodyType.REQUEST_ATTESTATION_FOR_CLAIM, } + if (quoteAttesterSigned) messageBody.content.quote = quoteAttesterSigned + return MessageRepository.sendToAddresses(attesterAddresses, messageBody) } diff --git a/src/services/QuoteServices.ts b/src/services/QuoteServices.ts new file mode 100644 index 00000000..3ba6dd47 --- /dev/null +++ b/src/services/QuoteServices.ts @@ -0,0 +1,58 @@ +import * as sdk from '@kiltprotocol/sdk-js' +import * as Quotes from '../state/ducks/Quotes' +import PersistentStore from '../state/PersistentStore' +import ErrorService from './ErrorService' + +class QuoteServices { + public static saveAgreedQuoteInStore( + quoteEntry: Quotes.QuoteEntry, + ownerAddress: string + ): void { + PersistentStore.store.dispatch( + Quotes.Store.saveAgreedQuote(quoteEntry, ownerAddress) + ) + } + + public static createAgreedQuote( + claim: sdk.IClaim, + identity: sdk.Identity, + terms: sdk.AttestedClaim[] = [], + delegationId: sdk.IDelegationNode['id'] | null = null, + quoteAttesterSigned: sdk.IQuoteAttesterSigned | null = null + ): sdk.IQuoteAgreement | null { + if (!quoteAttesterSigned) return null + const requestForAttestation = sdk.RequestForAttestation.fromClaimAndIdentity( + claim, + identity, + (terms || []).map((legitimation: sdk.IAttestedClaim) => + sdk.AttestedClaim.fromAttestedClaim(legitimation) + ), + delegationId + ) + + const signature = identity.signStr( + sdk.Crypto.hashObjectAsStr(quoteAttesterSigned) + ) + + const quoteAgreement: sdk.IQuoteAgreement = { + ...quoteAttesterSigned, + rootHash: requestForAttestation.rootHash, + claimerSignature: signature, + } + + try { + QuoteServices.saveAgreedQuoteInStore(quoteAgreement, identity.address) + } catch (error) { + ErrorService.log({ + error, + message: 'Error storing Agreed Quote', + origin: 'QuoteServices.agreedQuote()', + type: 'ERROR.FETCH.POST', + }) + throw error + } + return quoteAgreement + } +} + +export default QuoteServices diff --git a/src/state/PersistentStore.ts b/src/state/PersistentStore.ts index 4ee70fa3..36b71348 100644 --- a/src/state/PersistentStore.ts +++ b/src/state/PersistentStore.ts @@ -9,6 +9,7 @@ import * as Delegations from './ducks/Delegations' import * as Parameters from './ducks/Parameters' import * as Contacts from './ducks/Contacts' import * as CTypes from './ducks/CTypes' +import * as Quotes from './ducks/Quotes' declare global { /* eslint-disable */ @@ -27,6 +28,7 @@ export type State = { cTypes: CTypes.ImmutableState delegations: Delegations.ImmutableState parameters: Parameters.ImmutableState + quotes: Quotes.ImmutableState uiState: UiState.ImmutableState wallet: Wallet.ImmutableState } @@ -37,6 +39,7 @@ type SerializedState = { contacts: Contacts.SerializedState delegations: Delegations.SerializedState parameters: Parameters.SerializedState + quotes: Quotes.SerializedState uiState: UiState.SerializedState wallet: Wallet.SerializedState } @@ -55,6 +58,7 @@ class PersistentStore { contacts: Contacts.Store.deserialize(obj.contacts), delegations: Delegations.Store.deserialize(obj.delegations), parameters: Parameters.Store.deserialize(obj.parameters), + quotes: Quotes.Store.deserialize(obj.quotes), uiState: UiState.Store.deserialize(), wallet: Wallet.Store.deserialize(obj.wallet), } @@ -67,6 +71,7 @@ class PersistentStore { contacts: Contacts.Store.serialize(state.contacts), delegations: Delegations.Store.serialize(state.delegations), parameters: Parameters.Store.serialize(state.parameters), + quotes: Quotes.Store.serialize(state.quotes), uiState: UiState.Store.serialize(), wallet: Wallet.Store.serialize(state.wallet), } @@ -96,6 +101,7 @@ class PersistentStore { contacts: Contacts.Store.reducer, delegations: Delegations.Store.reducer, parameters: Parameters.Store.reducer, + quotes: Quotes.Store.reducer, uiState: UiState.Store.reducer, wallet: Wallet.Store.reducer, }), diff --git a/src/state/ducks/Quotes.ts b/src/state/ducks/Quotes.ts new file mode 100644 index 00000000..1ea7ccb1 --- /dev/null +++ b/src/state/ducks/Quotes.ts @@ -0,0 +1,197 @@ +import Immutable from 'immutable' +import { createSelector } from 'reselect' +import * as sdk from '@kiltprotocol/sdk-js' +import errorService from '../../services/ErrorService' +import KiltAction from '../../types/Action' +import { State as ReduxState } from '../PersistentStore' +import * as Wallet from './Wallet' +import { IMyIdentity } from '../../types/Contact' + +export type QuoteEntry = sdk.IQuoteAgreement | sdk.IQuoteAttesterSigned + +interface ISaveQuoteAction extends KiltAction { + payload: { + quoteId: Entry['quoteId'] + owner: string + quote: QuoteEntry + } +} + +interface IRemoveAction extends KiltAction { + payload: sdk.IQuoteAttesterSigned['attesterSignature'] +} + +export type Action = IRemoveAction | ISaveQuoteAction + +export type Entry = { + quoteId: string + owner: string + quote: QuoteEntry +} + +type State = { + quotes: Immutable.Map +} + +export type ImmutableState = Immutable.Record + +export type SerializedState = { + quotes: Entry[] +} + +class Store { + public static serialize(state: ImmutableState): SerializedState { + const serialized: SerializedState = { + quotes: [], + } + + serialized.quotes = state + .get('quotes') + .toList() + .map((quoteEntry: Entry) => quoteEntry) + .toArray() + + return serialized + } + + public static deserialize( + quoteStateSerialized: SerializedState + ): ImmutableState { + if (!quoteStateSerialized) { + return Store.createState({ + quotes: Immutable.Map(), + }) + } + const quoteEntries = {} + + quoteStateSerialized.quotes.forEach(serializedQuote => { + try { + const quoteAsJson = JSON.parse(JSON.stringify(serializedQuote)) + + const quoteEntry: Entry = { + quoteId: quoteAsJson.quoteId, + owner: quoteAsJson.owner, + quote: quoteAsJson.quote, + } + quoteEntries[serializedQuote.quoteId] = quoteEntry + } catch (e) { + errorService.log({ + error: e, + message: '', + origin: 'Quotes.deserialize()', + }) + } + }) + + return Store.createState({ + quotes: Immutable.Map(quoteEntries), + }) + } + + public static reducer( + state: ImmutableState = Store.createState(), + action: Action + ): ImmutableState { + switch (action.type) { + case Store.ACTIONS.SAVE_QUOTE: { + const { quoteId, quote, owner } = (action as ISaveQuoteAction).payload + + return state.setIn(['quotes', quoteId], { + quoteId, + owner, + quote, + } as Entry) + } + case Store.ACTIONS.REMOVE_QUOTE: { + return state.deleteIn(['quotes', (action as IRemoveAction).payload]) + } + default: + return state + } + } + + public static saveAttestersQuote( + quote: QuoteEntry, + ownerAddress: string + ): Action { + return { + payload: { + quoteId: sdk.UUID.generate(), + owner: ownerAddress, + quote, + }, + type: Store.ACTIONS.SAVE_QUOTE, + } + } + + public static saveAgreedQuote( + quote: QuoteEntry, + ownerAddress: string + ): Action { + return { + payload: { + quoteId: sdk.UUID.generate(), + owner: ownerAddress, + quote, + }, + type: Store.ACTIONS.SAVE_QUOTE, + } + } + + public static removeQuote(quoteId: Entry['quoteId']): Action { + return { + payload: quoteId, + type: Store.ACTIONS.REMOVE_QUOTE, + } + } + + public static createState(obj?: State): ImmutableState { + return Immutable.Record({ + quotes: Immutable.Map(), + })(obj) + } + + private static ACTIONS = { + REMOVE_QUOTE: 'client/quotes/UPDATE_QUOTE', + SAVE_QUOTE: 'client/quotes/SAVE_QUOTE', + } +} + +const getAllQuotes = (state: ReduxState): Entry[] => + state.quotes + .get('quotes') + .toList() + .toArray() + +const getAllMyQuotes = createSelector( + [Wallet.getSelectedIdentity, getAllQuotes], + (selectedIdentity: IMyIdentity, entries: Entry[]) => { + return entries.filter((entry: Entry) => { + return ( + entry && + entry.quote && + entry.owner === selectedIdentity.identity.address + ) + }) + } +) + +const getQuoteByID = ( + state: ReduxState, + quoteId: Entry['quoteId'] +): Entry['quoteId'] => quoteId + +const getQuoteByQuoteID = createSelector( + [getAllMyQuotes, getQuoteByID], + (entries: Entry[], quoteId: Entry['quoteId']) => + entries.filter((entry: Entry) => entry.quoteId === quoteId) +) + +const getQuote = createSelector( + [getQuoteByID, getAllMyQuotes], + (quoteId: Entry['quoteId'], entries: Entry[]) => { + return entries.find((entry: Entry) => entry.quoteId === quoteId) + } +) + +export { Store, getAllMyQuotes, getQuoteByQuoteID, getQuote } diff --git a/src/utils/QuoteUtils/QuoteInputSchema.ts b/src/utils/QuoteUtils/QuoteInputSchema.ts new file mode 100644 index 00000000..f9ba13c7 --- /dev/null +++ b/src/utils/QuoteUtils/QuoteInputSchema.ts @@ -0,0 +1,57 @@ +export default { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'KILT:quote:v2', + type: 'object', + title: 'Quote', + properties: { + attesterAddress: { + type: 'string', + }, + cTypeHash: { + type: 'string', + }, + cost: { + type: 'object', + required: ['net', 'gross', 'tax'], + properties: { + net: { + type: 'number', + }, + gross: { + type: 'number', + }, + tax: { + type: 'array', + items: { + type: 'object', + properties: { + title: { + title: 'Tax code', + type: 'string', + default: 'New Property', + }, + value: { + type: 'number', + }, + }, + required: ['title', 'value'], + }, + collapsed: false, + }, + }, + }, + currency: { + type: 'string', + }, + termsAndConditions: { + type: 'string', + }, + }, + required: [ + 'attesterAddress', + 'cTypeHash', + 'cost', + 'currency', + 'termsAndConditions', + ], +} diff --git a/yarn.lock b/yarn.lock index e587346f..4e8dca1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7946,6 +7946,13 @@ react-copy-to-clipboard@^5.0.1: copy-to-clipboard "^3" prop-types "^15.5.8" +react-day-picker@^7.4.8: + version "7.4.8" + resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-7.4.8.tgz#675625240d3fae1b41c0a9d5177c968c8517c1d4" + integrity sha512-pp0hnxFVoRuBQcRdR1Hofw4CQtOCGVmzCNrscyvS0Q8NEc+UiYLEDqE5dk37bf0leSnBW4lheIt0CKKhuKzDVw== + dependencies: + prop-types "^15.6.2" + react-dev-utils@^5.0.2: version "5.0.3" resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.3.tgz#92f97668f03deb09d7fa11ea288832a8c756e35e" @@ -7990,13 +7997,14 @@ react-dom@^16.4.1: scheduler "^0.18.0" react-dom@^16.6.3: - version "16.6.3" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.6.3.tgz#8fa7ba6883c85211b8da2d0efeffc9d3825cccc0" + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" + integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.11.2" + scheduler "^0.19.1" react-error-overlay@^4.0.1: version "4.0.1" @@ -8013,9 +8021,9 @@ react-is@^16.3.2, react-is@^16.6.0, react-is@^16.6.3: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0" react-is@^16.8.1: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" - integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-json-view@^1.19.1: version "1.19.1" @@ -8147,14 +8155,14 @@ react@^16.4.1: object-assign "^4.1.1" prop-types "^15.6.2" -react@^16.6.3: - version "16.6.3" - resolved "https://registry.yarnpkg.com/react/-/react-16.6.3.tgz#25d77c91911d6bbdd23db41e70fb094cc1e0871c" +react@^16.8.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.11.2" read-pkg-up@^1.0.1: version "1.0.1" @@ -8748,6 +8756,14 @@ scheduler@^0.18.0: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-based-json-editor@^7.22.0: version "7.22.0" resolved "https://registry.yarnpkg.com/schema-based-json-editor/-/schema-based-json-editor-7.22.0.tgz#6e278ec7278ad4ee774daec28f67fbcaae26d782" @@ -9859,6 +9875,11 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +types@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/types/-/types-0.1.1.tgz#860c6859d11366293f835d8c95aebcf95029838e" + integrity sha1-hgxoWdETZik/g12Mla68+VApg44= + typescript-logging@^0.6.3: version "0.6.4" resolved "https://registry.yarnpkg.com/typescript-logging/-/typescript-logging-0.6.4.tgz#c752cb3e7c051a32d3636a9b277a0a5e5295051e"