From 08f70a1514583672864a5ebd8a084ac55954d269 Mon Sep 17 00:00:00 2001 From: kirahsapong <102400653+kirahsapong@users.noreply.github.com> Date: Fri, 7 Jul 2023 13:05:47 -0700 Subject: [PATCH] Add evidence form (#89) * Add evidence JSON property to issuing a credential * Add evidence form and radio card set * Fix evidence * Fix radio card set active selection * Add hover to details summary --- src/App.scss | 62 +++- src/assets/swirl.png | Bin 0 -> 4600 bytes src/components/RadioCardSet/RadioCardSet.tsx | 48 +++ .../CredentialManifests.tsx | 2 +- .../CredentialManifests/Details/Details.scss | 2 +- .../CredentialManifests/Details/Details.tsx | 2 +- .../Details/IssueModal/IssueModal.scss | 69 ---- .../Details/IssueModal/IssueModal.tsx | 268 --------------- .../IssueModalWithRender.scss | 64 ---- .../IssueModalWithRender.tsx | 240 ------------- .../IssueModalWithRender/samples/mock.tsx | 23 -- .../views/CredentialManifests/Modal/Modal.tsx | 2 +- .../IssuedCredentials/Details/Details.scss | 2 +- .../IssuedCredentials/Details/Details.tsx | 3 +- .../views/IssuedCredentials/Modal/Modal.scss | 5 + .../views/IssuedCredentials/Modal/Modal.tsx | 314 +++++++++++++++--- .../Modal/evidence/EvidenceForm.tsx | 155 +++++++++ .../Modal/evidence/schemes.tsx | 33 ++ .../Modal}/samples/mock.tsx | 0 .../Admin/DIDs/views/MyDIDs/Modal/Modal.scss | 8 +- .../Admin/DIDs/views/MyDIDs/Modal/Modal.tsx | 2 +- src/utils/helpers.tsx | 9 +- 22 files changed, 572 insertions(+), 741 deletions(-) create mode 100644 src/assets/swirl.png create mode 100644 src/components/RadioCardSet/RadioCardSet.tsx delete mode 100644 src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModal/IssueModal.scss delete mode 100644 src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModal/IssueModal.tsx delete mode 100644 src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModalWithRender/IssueModalWithRender.scss delete mode 100644 src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModalWithRender/IssueModalWithRender.tsx delete mode 100644 src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModalWithRender/samples/mock.tsx create mode 100644 src/pages/Admin/Credentials/views/IssuedCredentials/Modal/evidence/EvidenceForm.tsx create mode 100644 src/pages/Admin/Credentials/views/IssuedCredentials/Modal/evidence/schemes.tsx rename src/pages/Admin/Credentials/views/{CredentialManifests/Details/IssueModal => IssuedCredentials/Modal}/samples/mock.tsx (100%) diff --git a/src/App.scss b/src/App.scss index 5aa2a81..71e515c 100644 --- a/src/App.scss +++ b/src/App.scss @@ -351,32 +351,62 @@ fieldset { padding: 0; margin: 0.5rem 0 1.5rem; display: inline-flex; + width: 100%; } legend { margin-bottom: 0.5rem; } -input[type="radio"] { - display: none; -} +.radio-container { + position: relative; -input[type="radio"] ~ label { - padding: 2px 10px; - border-radius: var(--radius-round); - font-size: var(--font-sm); - margin-right: 0.5rem; - background-color: var(--grey-10); - border: 2px solid var(--grey-30); - color: var(--grey-50); -} + input[type="radio"] { + appearance: none; + position: absolute; -input[type="radio"]:checked ~ label { - background-color: var(--grey-30); - border: 1px solid var(--color-white); - color: var(--color-white); + &:focus-visible ~ label { + outline-offset: 2px; + outline: -webkit-focus-ring-color auto 1px; + } + } + + input[type="radio"] ~ label { + padding: 2px 10px; + border-radius: var(--radius-sm); + font-size: var(--font-sm); + background-color: var(--grey-10); + border: 1px solid var(--grey-30); + color: var(--grey-50); + text-align: center; + padding-inline: 1rem; + margin-right: 0.5rem; + + .radio-control-content { + .radio-control-heading { + font-size: var(--font-sm); + margin: 0.25rem; + } + + .radio-control-description { + font-size: var(--font-xs); + margin: 0; + } + } + } + + input[type="radio"]:checked ~ label { + background-color: var(--grey-30); + border: 1px solid var(--color-white); + color: var(--color-white); + + p { + color: var(--color-white); + } + } } + #output-container { padding: 1rem; border: 1px solid var(--grey-50); diff --git a/src/assets/swirl.png b/src/assets/swirl.png new file mode 100644 index 0000000000000000000000000000000000000000..f42839aa0797f2bf5ea9bfa9719854793e028f37 GIT binary patch literal 4600 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 = (props) => { + return ( +
+ + + + {props.description && } + + {(option) => +
+ + +
+ } +
+
+ + ) +} + +export default RadioCardSet; \ No newline at end of file diff --git a/src/pages/Admin/Credentials/views/CredentialManifests/CredentialManifests.tsx b/src/pages/Admin/Credentials/views/CredentialManifests/CredentialManifests.tsx index 80c7da7..01a8095 100644 --- a/src/pages/Admin/Credentials/views/CredentialManifests/CredentialManifests.tsx +++ b/src/pages/Admin/Credentials/views/CredentialManifests/CredentialManifests.tsx @@ -3,7 +3,7 @@ import "./CredentialManifests.scss"; import Icon, { ExternalArrow, Plus } from "@/icons/Icon"; import Modal from "./Modal/Modal"; import { useNavigate } from "@solidjs/router"; -import IssueModal from "./Details/IssueModal/IssueModal"; +import IssueModal from "../IssuedCredentials/Modal/Modal"; import { store } from "@/utils/store"; const CredentialManifests: Component = () => { diff --git a/src/pages/Admin/Credentials/views/CredentialManifests/Details/Details.scss b/src/pages/Admin/Credentials/views/CredentialManifests/Details/Details.scss index a36ca9c..4e69545 100644 --- a/src/pages/Admin/Credentials/views/CredentialManifests/Details/Details.scss +++ b/src/pages/Admin/Credentials/views/CredentialManifests/Details/Details.scss @@ -49,7 +49,7 @@ max-width: 100%; box-shadow: 0px 4px 8px rgba(6, 6, 6, 0.5); background: var(--grey-50); - background-image: url(https://www.toptal.com/designers/subtlepatterns/uploads/swirl.png); + background-image: url('/src/assets/swirl.png'); color: var(--color-black); overflow: hidden; padding-block-end: 1.5rem; diff --git a/src/pages/Admin/Credentials/views/CredentialManifests/Details/Details.tsx b/src/pages/Admin/Credentials/views/CredentialManifests/Details/Details.tsx index 2a0518a..899d445 100644 --- a/src/pages/Admin/Credentials/views/CredentialManifests/Details/Details.tsx +++ b/src/pages/Admin/Credentials/views/CredentialManifests/Details/Details.tsx @@ -1,7 +1,7 @@ import { Component } from "solid-js"; import "./Details.scss"; import "@/components/Panel/Panel.scss"; -import IssueModal, { getSchemaForSubject } from "./IssueModal/IssueModal"; +import IssueModal, { getSchemaForSubject } from "../../IssuedCredentials/Modal/Modal"; import { useNavigate, useParams, useSearchParams } from "@solidjs/router"; import { store } from "@/utils/store"; import Icon, { ExternalArrow } from "@/icons/Icon"; diff --git a/src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModal/IssueModal.scss b/src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModal/IssueModal.scss deleted file mode 100644 index 3fdc337..0000000 --- a/src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModal/IssueModal.scss +++ /dev/null @@ -1,69 +0,0 @@ -.dialog { - background: var(--grey-5); - color: var(--color-white); - border: 1px solid var(--grey-10); - box-shadow: 0 10px 20px var(--color-black); - width: 580px; - - &::backdrop { - background: rgba(0,0,0,0.5); - } - - &-header { - margin-block-end: 1rem; - button { - .icon-container > svg { - width: 24px; - } - } - } - - &-body { - margin-inline: 2rem; - - .modal-input-note { - font-size: var(--font-xs); - margin-block: 0.125rem 0.25rem; - } - - .banner { - padding: 1rem; - margin-block-end: 1rem; - display: flex; - gap: 8px; - - &-danger { - border: 1px solid var(--color-red); - background: var(--red-20); - - .icon-container { - color: var(--color-red); - } - } - - &-success { - border: 1px solid var(--color-green); - background: var(--green-20); - - .icon-container { - color: var(--color-green); - } - } - - &-warn { - border: 1px solid var(--color-orange); - background: var(--orange-20); - - .icon-container { - color: var(--color-orange); - } - } - } - - .button-row { - margin-block: 4rem 2rem; - justify-content: flex-end; - } - } -} - diff --git a/src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModal/IssueModal.tsx b/src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModal/IssueModal.tsx deleted file mode 100644 index 936e591..0000000 --- a/src/pages/Admin/Credentials/views/CredentialManifests/Details/IssueModal/IssueModal.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import { Component, JSX, createSignal, onCleanup } from "solid-js"; -import "./IssueModal.scss"; -import Icon, { ArrowUpDown, Beaker, DangerAlert, XCross } from "@/icons/Icon"; -import { formatTextAreaOnKeyDown, handleRequest, insertSampleInput, updateFormOnInput } from "@/utils/helpers"; -import { credentialInputJson } from "./samples/mock"; -import SSI from "@/utils/service"; -import { store } from "@/utils/store"; -import { hydrateCredentialsStore } from "@/utils/setup"; -import { useNavigate } from "@solidjs/router"; -import { parseIDFromUrl } from "@/utils/helpers"; - - - -const IssueModal: Component<{ content }> = (props) => { - //pass in these props - - const schemaProperties = JSON.stringify(getSchemaForSubject(props.content.schemaId)?.properties, null, 2); - - let initialFormValues = { - properties: schemaProperties, - expires: null, - suspendable: null, - revocable: null, - expiry: '', - issuer: Object.keys(store.user)[0], - subject: '' - } - - // the component - - const [formValues, setFormValues] = createSignal(initialFormValues); - const [isLoading, setIsLoading] = createSignal(false); - const [isSuccess, setIsSuccess] = createSignal(false); - const [isError, setIsError] = createSignal(false); - - const resetForm = () => { - setFormValues(initialFormValues); - setIsLoading(false); - setIsSuccess(false); - setIsError(false); - } - - //dialog magic - let dialog; - const showModal = () => { - dialog.showModal(); - document.body.classList.add('no-scroll'); - dialog.addEventListener('close', () => { - document.body.classList.remove('no-scroll'); - resetForm(); - }); - } - - const closeModal = () => { - return dialog.close(); - } - - const navigate = useNavigate(); - const [credentialId, setCredentialId] = createSignal(); - - //actual form calls - const handleSubmit = async (event) => { - event.preventDefault(); - const data = { - "@context": "https://www.w3.org/2018/credentials/v1", - "data": JSON.parse(formValues().properties), - "issuer": store.user[formValues().issuer]["did"], - "issuerKid": store.user[formValues().issuer]["kid"], - "schemaId": props.content.schemaId, - "subject": formValues().subject, - ...formValues().expiry && { "expiry": new Date(formValues().expiry).toISOString() }, - ...formValues().revocable && { "revocable": formValues().revocable }, - ...formValues().suspendable && { "suspendable": formValues().suspendable } - } - const request = SSI.putCredential(data); - const setters = { setIsLoading, setIsSuccess, setIsError }; - const res = await handleRequest(event, request, setters); - setCredentialId(parseIDFromUrl((await res.json()).id)); - hydrateCredentialsStore(); - }; - - const handleInput = (event) => { - updateFormOnInput(event, { setIsError, setFormValues }) - }; - - const handleKeyDown = (event) => { - formatTextAreaOnKeyDown(event, { setFormValues }); - } - - const isFormValid = () => { - const isExpiryValid = formValues().expires ? formValues().expiry !== '' : true; - const isSubjectValid = formValues().subject !== ''; - const isSubjectDataValid = formValues().properties?.trim() !== '' - try { - JSON.parse(formValues().properties) - } catch { - return false; - } - return isExpiryValid && isSubjectValid && isSubjectDataValid && !isError(); - } - - return ( - <> - - -
- -
- -
-

Issue Verifiable Credential

-
- {!isLoading() && !isSuccess() && ( - <> - {isError() && - - } - -
- - -
-
- - -
-