web/satellite: confirm details step for new access grant flow

Added extra step for new access grant flow where user can confirm all selected caveats (except passphrase).

Change-Id: I9ac603d588016f30a95d4b578b3752e256a496da
This commit is contained in:
Vitalii 2023-02-23 12:04:41 +02:00 committed by Vitalii Shpital
parent 552272d189
commit cd192164d2
8 changed files with 155 additions and 30 deletions

View File

@ -28,7 +28,7 @@
:selected-permissions="selectedPermissions"
:on-back="setFirstStepBasedOnAccessType"
:on-continue="() => setStep(
selectedAccessTypes.includes(AccessType.APIKey) ? setLastStep() : CreateAccessStep.AccessEncryption
selectedAccessTypes.includes(AccessType.APIKey) ? CreateAccessStep.ConfirmDetails : CreateAccessStep.AccessEncryption
)"
:selected-buckets="selectedBuckets"
:on-select-bucket="selectBucket"
@ -38,7 +38,6 @@
:on-set-not-after="setNotAfter"
:not-after-label="notAfterLabel"
:on-set-not-after-label="setNotAfterLabel"
:is-loading="isLoading"
/>
<AccessEncryptionStep
v-if="step === CreateAccessStep.AccessEncryption"
@ -46,36 +45,43 @@
:on-continue="setStepBasedOnPassphraseOption"
:passphrase-option="passphraseOption"
:set-option="setPassphraseOption"
:is-loading="isLoading"
/>
<EnterPassphraseStep
v-if="step === CreateAccessStep.EnterMyPassphrase"
:is-new-passphrase="false"
:on-back="() => setStep(CreateAccessStep.AccessEncryption)"
:on-continue="setLastStep"
:on-continue="() => setStep(CreateAccessStep.ConfirmDetails)"
:passphrase="enteredPassphrase"
:set-passphrase="setPassphrase"
info="Enter the encryption passphrase used for this project to create this access grant."
:is-loading="isLoading"
/>
<EnterPassphraseStep
v-if="step === CreateAccessStep.EnterNewPassphrase"
:is-new-passphrase="true"
:on-back="() => setStep(CreateAccessStep.AccessEncryption)"
:on-continue="setLastStep"
:on-continue="() => setStep(CreateAccessStep.ConfirmDetails)"
:passphrase="enteredPassphrase"
:set-passphrase="setPassphrase"
info="This passphrase will be used to encrypt all the files you upload using this access grant.
You will need it to access these files in the future."
:is-loading="isLoading"
/>
<PassphraseGeneratedStep
v-if="step === CreateAccessStep.PassphraseGenerated"
:on-back="() => setStep(CreateAccessStep.AccessEncryption)"
:on-continue="setLastStep"
:on-continue="() => setStep(CreateAccessStep.ConfirmDetails)"
:passphrase="generatedPassphrase"
:name="accessName"
/>
<ConfirmDetailsStep
v-if="step === CreateAccessStep.ConfirmDetails"
:access-types="selectedAccessTypes"
:is-loading="isLoading"
:not-after-label="notAfterLabel"
:selected-buckets="selectedBuckets"
:name="accessName"
:selected-permissions="selectedPermissions"
:on-back="setPreviousStepFromConfirm"
:on-continue="setLastStep"
/>
<AccessCreatedStep
v-if="step === CreateAccessStep.AccessCreated"
@ -133,6 +139,7 @@ import EncryptionInfoStep from '@/components/accessGrants/newCreateFlow/steps/En
import AccessCreatedStep from '@/components/accessGrants/newCreateFlow/steps/AccessCreatedStep.vue';
import CLIAccessCreatedStep from '@/components/accessGrants/newCreateFlow/steps/CLIAccessCreatedStep.vue';
import S3CredentialsCreatedStep from '@/components/accessGrants/newCreateFlow/steps/S3CredentialsCreatedStep.vue';
import ConfirmDetailsStep from '@/components/accessGrants/newCreateFlow/steps/ConfirmDetailsStep.vue';
const router = useRouter();
const route = useRoute();
@ -254,6 +261,28 @@ function setNotAfter(date: Date | undefined): void {
notAfter.value = date;
}
/**
* Sets previous step from confirm step.
*/
function setPreviousStepFromConfirm(): void {
switch (true) {
case selectedAccessTypes.value.includes(AccessType.APIKey):
step.value = CreateAccessStep.ChoosePermission;
break;
case passphraseOption.value === PassphraseOption.SetMyProjectPassphrase:
step.value = CreateAccessStep.EnterMyPassphrase;
break;
case passphraseOption.value === PassphraseOption.UseExistingPassphrase:
step.value = CreateAccessStep.AccessEncryption;
break;
case passphraseOption.value === PassphraseOption.EnterNewPassphrase:
step.value = CreateAccessStep.EnterNewPassphrase;
break;
case passphraseOption.value === PassphraseOption.GenerateNewPassphrase:
step.value = CreateAccessStep.PassphraseGenerated;
}
}
/**
* Sets not after (end date) label.
*/
@ -382,7 +411,7 @@ function setFirstStepBasedOnAccessType(): void {
/**
* Sets next step depending on selected passphrase option.
*/
async function setStepBasedOnPassphraseOption(): Promise<void> {
function setStepBasedOnPassphraseOption(): void {
switch (passphraseOption.value) {
case PassphraseOption.SetMyProjectPassphrase:
step.value = CreateAccessStep.EnterMyPassphrase;
@ -394,7 +423,7 @@ async function setStepBasedOnPassphraseOption(): Promise<void> {
step.value = CreateAccessStep.PassphraseGenerated;
break;
case PassphraseOption.UseExistingPassphrase:
await setLastStep();
step.value = CreateAccessStep.ConfirmDetails;
}
}

View File

@ -77,7 +77,6 @@
font-size="14px"
:on-press="onBack"
:is-white="true"
:is-disabled="isLoading"
/>
</template>
<template #rightButton>
@ -87,7 +86,6 @@
height="48px"
font-size="14px"
:on-press="onContinue"
:is-disabled="isLoading"
/>
</template>
</ButtonsContainer>
@ -117,7 +115,6 @@ const props = defineProps<{
setOption: (option: PassphraseOption) => void;
onBack: () => void;
onContinue: () => void;
isLoading: boolean;
}>();
const store = useStore();

View File

@ -110,7 +110,6 @@
font-size="14px"
:on-press="onBack"
:is-white="true"
:is-disabled="isLoading"
/>
</template>
<template #rightButton>
@ -120,7 +119,7 @@
height="48px"
font-size="14px"
:on-press="handleContinue"
:is-disabled="isButtonDisabled || isLoading"
:is-disabled="isButtonDisabled"
/>
</template>
</ButtonsContainer>
@ -159,7 +158,6 @@ const props = withDefaults(defineProps<{
notAfterLabel: string;
onBack: () => void;
onContinue: () => void;
isLoading: boolean;
notAfter?: Date;
}>(), {
notAfter: undefined,

View File

@ -0,0 +1,93 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="confirm">
<ContainerWithIcon :icon-and-title="FUNCTIONAL_CONTAINER_ICON_AND_TITLE[FunctionalContainer.Type]">
<template #functional>
<p class="confirm__label">{{ accessTypes.join(', ') }}</p>
</template>
</ContainerWithIcon>
<ContainerWithIcon :icon-and-title="FUNCTIONAL_CONTAINER_ICON_AND_TITLE[FunctionalContainer.Name]">
<template #functional>
<p class="confirm__label">{{ name }}</p>
</template>
</ContainerWithIcon>
<ContainerWithIcon :icon-and-title="FUNCTIONAL_CONTAINER_ICON_AND_TITLE[FunctionalContainer.Permissions]">
<template #functional>
<p class="confirm__label">{{ selectedPermissions.length === 4 ? 'All' : selectedPermissions.join(', ') }}</p>
</template>
</ContainerWithIcon>
<ContainerWithIcon :icon-and-title="FUNCTIONAL_CONTAINER_ICON_AND_TITLE[FunctionalContainer.Buckets]">
<template #functional>
<p class="confirm__label">{{ selectedBuckets.length === 0 ? 'All' : selectedBuckets.join(', ') }}</p>
</template>
</ContainerWithIcon>
<ContainerWithIcon :icon-and-title="FUNCTIONAL_CONTAINER_ICON_AND_TITLE[FunctionalContainer.EndDate]">
<template #functional>
<p class="confirm__label">{{ notAfterLabel }}</p>
</template>
</ContainerWithIcon>
<ButtonsContainer>
<template #leftButton>
<VButton
label="Back"
width="100%"
height="48px"
font-size="14px"
:on-press="onBack"
:is-white="true"
:is-disabled="isLoading"
/>
</template>
<template #rightButton>
<VButton
label="Confirm"
width="100%"
height="48px"
font-size="14px"
:on-press="onContinue"
:is-disabled="isLoading"
/>
</template>
</ButtonsContainer>
</div>
</template>
<script setup lang="ts">
import {
AccessType,
FUNCTIONAL_CONTAINER_ICON_AND_TITLE,
FunctionalContainer,
Permission,
} from '@/types/createAccessGrant';
import ContainerWithIcon from '@/components/accessGrants/newCreateFlow/components/ContainerWithIcon.vue';
import ButtonsContainer from '@/components/accessGrants/newCreateFlow/components/ButtonsContainer.vue';
import VButton from '@/components/common/VButton.vue';
const props = defineProps<{
name: string;
accessTypes: AccessType[];
selectedPermissions: Permission[];
selectedBuckets: string[];
notAfterLabel: string;
onBack: () => void;
onContinue: () => void;
isLoading: boolean;
}>();
</script>
<style lang="scss" scoped>
.confirm {
font-family: 'font_regular', sans-serif;
&__label {
font-size: 14px;
line-height: 20px;
color: var(--c-black);
text-align: left;
word-break: break-word;
}
}
</style>

View File

@ -29,7 +29,6 @@
font-size="14px"
:on-press="onBack"
:is-white="true"
:is-disabled="isLoading"
/>
</template>
<template #rightButton>
@ -39,7 +38,7 @@
height="48px"
font-size="14px"
:on-press="onContinue"
:is-disabled="isButtonDisabled || isLoading"
:is-disabled="isButtonDisabled"
/>
</template>
</ButtonsContainer>
@ -63,7 +62,6 @@ const props = defineProps<{
setPassphrase: (value: string) => void;
onBack: () => void;
onContinue: () => void;
isLoading: boolean;
}>();
const store = useStore();

View File

@ -10,7 +10,6 @@
<ButtonsContainer label="Save your encryption passphrase">
<template #leftButton>
<VButton
v-clipboard:copy="passphrase"
:label="isPassphraseCopied ? 'Copied' : 'Copy'"
width="100%"
height="40px"
@ -57,7 +56,6 @@
font-size="14px"
:on-press="onBack"
:is-white="true"
:is-disabled="isLoading"
/>
</template>
<template #rightButton>
@ -67,7 +65,7 @@
height="48px"
font-size="14px"
:on-press="onContinue"
:is-disabled="isButtonDisabled || isLoading"
:is-disabled="isButtonDisabled"
/>
</template>
</ButtonsContainer>
@ -92,7 +90,6 @@ const props = defineProps<{
passphrase: string;
onBack: () => void;
onContinue: () => void;
isLoading: boolean;
}>();
const store = useStore();
@ -123,6 +120,7 @@ function togglePassphraseSaved(): void {
*/
function onCopy(): void {
isPassphraseCopied.value = true;
navigator.clipboard.writeText(props.passphrase);
analytics.eventTriggered(AnalyticsEvent.COPY_TO_CLIPBOARD_CLICKED);
notify.success(`Passphrase was copied successfully`);
}

View File

@ -11,6 +11,7 @@ import AccessCreatedIcon from '@/../static/images/accessGrants/newCreateFlow/acc
import CLIAccessCreatedIcon from '@/../static/images/accessGrants/newCreateFlow/cliAccessCreated.svg';
import CredentialsCreatedIcon from '@/../static/images/accessGrants/newCreateFlow/credentialsCreated.svg';
import EncryptionInfoIcon from '@/../static/images/accessGrants/newCreateFlow/encryptionInfo.svg';
import ConfirmDetailsIcon from '@/../static/images/accessGrants/newCreateFlow/confirmDetails.svg';
import TypeIcon from '@/../static/images/accessGrants/newCreateFlow/typeIcon.svg';
import NameIcon from '@/../static/images/accessGrants/newCreateFlow/nameIcon.svg';
import PermissionsIcon from '@/../static/images/accessGrants/newCreateFlow/permissionsIcon.svg';
@ -24,9 +25,9 @@ export interface IconAndTitle {
}
export enum AccessType {
APIKey = 'apikey',
S3 = 's3',
AccessGrant = 'accessGrant',
APIKey = 'API Key',
S3 = 'S3 Credentials',
AccessGrant = 'Access Grant',
}
export enum PassphraseOption {
@ -44,6 +45,7 @@ export enum CreateAccessStep {
PassphraseGenerated = 'passphraseGenerated',
EnterMyPassphrase = 'enterMyPassphrase',
EnterNewPassphrase = 'enterNewPassphrase',
ConfirmDetails = 'confirmDetails',
AccessCreated = 'accessCreated',
CLIAccessCreated = 'cliAccessCreated',
CredentialsCreated = 'credentialsCreated',
@ -51,10 +53,10 @@ export enum CreateAccessStep {
export enum Permission {
All = 'all',
Read = 'read',
Write = 'write',
List = 'list',
Delete = 'delete',
Read = 'Read',
Write = 'Write',
List = 'List',
Delete = 'Delete',
}
export const STEP_ICON_AND_TITLE: Record<CreateAccessStep, IconAndTitle> = {
@ -86,6 +88,10 @@ export const STEP_ICON_AND_TITLE: Record<CreateAccessStep, IconAndTitle> = {
icon: AccessEncryptionIcon,
title: 'Enter a new passphrase',
},
[CreateAccessStep.ConfirmDetails]: {
icon: ConfirmDetailsIcon,
title: 'Confirm details',
},
[CreateAccessStep.AccessCreated]: {
icon: AccessCreatedIcon,
title: 'Access created',

View File

@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4423 0H23.3463C28.8835 0 31.0805 0.613723 33.2353 1.76617C35.3902 2.91861 37.0814 4.60978 38.2338 6.76466L38.3214 6.93056C39.4029 9.00672 39.9846 11.2 40 16.4423V23.3463C40 28.8835 39.3863 31.0805 38.2338 33.2353C37.0814 35.3902 35.3902 37.0814 33.2353 38.2338L33.0694 38.3214C30.9933 39.4029 28.8 39.9846 23.5577 40H16.6537C11.1165 40 8.91954 39.3863 6.76466 38.2338C4.60977 37.0814 2.91861 35.3902 1.76617 33.2353L1.67858 33.0695C0.597074 30.9933 0.0154219 28.8 0 23.5577V16.6537C0 11.1165 0.613723 8.91954 1.76617 6.76466C2.91861 4.60978 4.60977 2.91861 6.76466 1.76617L6.93055 1.67858C9.00672 0.597074 11.2 0.0154219 16.4423 0Z" fill="#0218A7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.63178 17.0363C10.6489 16.0192 12.298 16.0192 13.3151 17.0363L19.7348 23.456C20.7519 24.4732 20.7519 26.1222 19.7348 27.1393C18.7177 28.1565 17.0686 28.1565 16.0515 27.1393L9.63178 20.7196C8.61466 19.7025 8.61466 18.0534 9.63178 17.0363Z" fill="#FF458B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.5392 12.8976C31.5563 13.9147 31.5563 15.5638 30.5392 16.5809L19.7348 27.3852C18.7177 28.4024 17.0686 28.4024 16.0515 27.3852C15.0344 26.3681 15.0344 24.7191 16.0515 23.7019L26.8559 12.8976C27.873 11.8805 29.5221 11.8805 30.5392 12.8976Z" fill="#00E366"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.0243 21.7275L19.8643 23.4907C20.9529 24.5793 20.9529 26.3443 19.8643 27.4329C18.7757 28.5215 17.0107 28.5215 15.9221 27.4329L15.3451 26.8568C14.8907 25.8335 15.083 24.5926 15.9221 23.7536L18.0243 21.7275Z" fill="#FFC600"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB