web/satellite: rework styling of create project passphrase flow
Reworked styling of create project passphrase flow to be consistent with create access grant flow. Issue: https://github.com/storj/storj/issues/5815 Change-Id: I54db5b3c37a8cf96d939bfcb99cf0896af25c4cc
This commit is contained in:
parent
4929ae3c08
commit
2e2978d647
@ -27,16 +27,18 @@
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
:on-press="onBack"
|
||||
:is-white="true"
|
||||
/>
|
||||
</template>
|
||||
<template #rightButton>
|
||||
<VButton
|
||||
label="Create Access ->"
|
||||
:label="isProjectPassphrase ? 'Continue ->' : 'Create Access ->'"
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
:on-press="onContinue"
|
||||
:is-disabled="isButtonDisabled"
|
||||
/>
|
||||
@ -53,14 +55,17 @@ import Toggle from '@/components/accessGrants/createFlow/components/Toggle.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
isProjectPassphrase?: boolean;
|
||||
isNewPassphrase: boolean;
|
||||
info: string;
|
||||
passphrase: string;
|
||||
setPassphrase: (value: string) => void;
|
||||
onBack: () => void;
|
||||
onContinue: () => void;
|
||||
}>();
|
||||
}>(), {
|
||||
isProjectPassphrase: false,
|
||||
});
|
||||
|
||||
const isPassphraseSaved = ref<boolean>(false);
|
||||
|
||||
|
@ -3,7 +3,11 @@
|
||||
|
||||
<template>
|
||||
<div class="generated">
|
||||
<p class="generated__info">
|
||||
<p v-if="isProjectPassphrase" class="generated__info">
|
||||
Please note that Storj does not know or store your encryption passphrase. If you lose it, you will not be
|
||||
able to recover your files.
|
||||
</p>
|
||||
<p v-else class="generated__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.
|
||||
</p>
|
||||
@ -54,16 +58,18 @@
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
:on-press="onBack"
|
||||
:is-white="true"
|
||||
/>
|
||||
</template>
|
||||
<template #rightButton>
|
||||
<VButton
|
||||
label="Create Access ->"
|
||||
:label="isProjectPassphrase ? 'Continue ->' : 'Create Access ->'"
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
:on-press="onContinue"
|
||||
:is-disabled="isButtonDisabled"
|
||||
/>
|
||||
@ -85,12 +91,15 @@ import ValueWithBlur from '@/components/accessGrants/createFlow/components/Value
|
||||
import Toggle from '@/components/accessGrants/createFlow/components/Toggle.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
name: string;
|
||||
passphrase: string;
|
||||
onBack: () => void;
|
||||
onContinue: () => void;
|
||||
}>();
|
||||
isProjectPassphrase?: boolean;
|
||||
}>(), {
|
||||
isProjectPassphrase: false,
|
||||
});
|
||||
|
||||
const notify = useNotify();
|
||||
|
||||
|
@ -10,54 +10,34 @@
|
||||
:is-generate="selectedOption === CreatePassphraseOption.Generate"
|
||||
:set-generate="() => setOption(CreatePassphraseOption.Generate)"
|
||||
:set-enter="() => setOption(CreatePassphraseOption.Enter)"
|
||||
:on-cancel="onCancelOrBack"
|
||||
:on-continue="onContinue"
|
||||
/>
|
||||
<PassphraseGeneratedStep
|
||||
v-if="activeStep === CreateProjectPassphraseStep.PassphraseGenerated"
|
||||
:passphrase="passphrase"
|
||||
:on-back="onCancelOrBack"
|
||||
:on-continue="onContinue"
|
||||
:passphrase="generatedPassphrase"
|
||||
name="storj"
|
||||
/>
|
||||
<EnterPassphraseStep
|
||||
v-if="activeStep === CreateProjectPassphraseStep.EnterPassphrase"
|
||||
:set-passphrase="setPassphrase"
|
||||
:enter-error="enterError"
|
||||
:passphrase="passphrase"
|
||||
:on-back="onCancelOrBack"
|
||||
:on-continue="onContinue"
|
||||
/>
|
||||
<SuccessStep
|
||||
v-if="activeStep === CreateProjectPassphraseStep.Success"
|
||||
:on-continue="onContinue"
|
||||
/>
|
||||
<SuccessStep v-if="activeStep === CreateProjectPassphraseStep.Success" />
|
||||
<div v-if="isCheckVisible" class="modal__save-container" @click="toggleSaved">
|
||||
<div class="modal__save-container__check" :class="{checked: passphraseSaved}">
|
||||
<CheckIcon />
|
||||
</div>
|
||||
<div class="modal__save-container__info">
|
||||
<h2 class="modal__save-container__info__title">
|
||||
Yes I understand and saved the passphrase.
|
||||
</h2>
|
||||
<p class="modal__save-container__info__msg">
|
||||
Check the box to continue.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal__buttons">
|
||||
<VButton
|
||||
v-if="activeStep !== CreateProjectPassphraseStep.Success"
|
||||
:label="activeStep === CreateProjectPassphraseStep.SelectMode ? 'Cancel' : 'Back'"
|
||||
width="100%"
|
||||
height="48px"
|
||||
:is-white="true"
|
||||
:on-press="onCancelOrBack"
|
||||
/>
|
||||
<VButton
|
||||
label="Continue ->"
|
||||
:width="activeStep === CreateProjectPassphraseStep.Success ? '200px' : '100%'"
|
||||
height="48px"
|
||||
:on-press="onContinue"
|
||||
:is-disabled="continueButtonDisabled"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { generateMnemonic } from 'bip39';
|
||||
|
||||
import { useNotify, useRouter } from '@/utils/hooks';
|
||||
@ -66,16 +46,14 @@ import { RouteConfig } from '@/router';
|
||||
import { EdgeCredentials } from '@/types/accessGrants';
|
||||
import { useAppStore } from '@/store/modules/appStore';
|
||||
import { useBucketsStore } from '@/store/modules/bucketsStore';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import VModal from '@/components/common/VModal.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import SelectPassphraseModeStep from '@/components/modals/createProjectPassphrase/SelectPassphraseModeStep.vue';
|
||||
import PassphraseGeneratedStep from '@/components/modals/createProjectPassphrase/PassphraseGeneratedStep.vue';
|
||||
import EnterPassphraseStep from '@/components/modals/createProjectPassphrase/EnterPassphraseStep.vue';
|
||||
import SuccessStep from '@/components/modals/createProjectPassphrase/SuccessStep.vue';
|
||||
|
||||
import CheckIcon from '@/../static/images/projectPassphrase/check.svg';
|
||||
|
||||
enum CreateProjectPassphraseStep {
|
||||
SelectMode = 'SelectMode',
|
||||
PassphraseGenerated = 'PassphraseGenerated',
|
||||
@ -94,26 +72,11 @@ const notify = useNotify();
|
||||
const nativeRouter = useRouter();
|
||||
const router = reactive(nativeRouter);
|
||||
|
||||
const generatedPassphrase = generateMnemonic();
|
||||
|
||||
const selectedOption = ref<CreatePassphraseOption>(CreatePassphraseOption.Generate);
|
||||
const activeStep = ref<CreateProjectPassphraseStep>(CreateProjectPassphraseStep.SelectMode);
|
||||
const passphrase = ref<string>('');
|
||||
const enterError = ref<string>('');
|
||||
const passphraseSaved = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* Indicates if save passphrase checkbox container is shown.
|
||||
*/
|
||||
const isCheckVisible = computed((): boolean => {
|
||||
return activeStep.value === CreateProjectPassphraseStep.PassphraseGenerated ||
|
||||
activeStep.value === CreateProjectPassphraseStep.EnterPassphrase;
|
||||
});
|
||||
|
||||
/**
|
||||
* Indicates if continue button is disabled.
|
||||
*/
|
||||
const continueButtonDisabled = computed((): boolean => {
|
||||
return isCheckVisible.value && !passphraseSaved.value;
|
||||
});
|
||||
|
||||
/**
|
||||
* Sets passphrase input value to local variable.
|
||||
@ -121,10 +84,6 @@ const continueButtonDisabled = computed((): boolean => {
|
||||
* @param value
|
||||
*/
|
||||
function setPassphrase(value: string): void {
|
||||
if (enterError.value) {
|
||||
enterError.value = '';
|
||||
}
|
||||
|
||||
passphrase.value = value;
|
||||
}
|
||||
|
||||
@ -136,13 +95,6 @@ function setOption(option: CreatePassphraseOption): void {
|
||||
selectedOption.value = option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles save passphrase checkbox.
|
||||
*/
|
||||
function toggleSaved(): void {
|
||||
passphraseSaved.value = !passphraseSaved.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes modal.
|
||||
*/
|
||||
@ -161,7 +113,7 @@ async function onContinue(): Promise<void> {
|
||||
passphrase.value = '';
|
||||
}
|
||||
|
||||
passphrase.value = generateMnemonic();
|
||||
passphrase.value = generatedPassphrase;
|
||||
activeStep.value = CreateProjectPassphraseStep.PassphraseGenerated;
|
||||
return;
|
||||
}
|
||||
@ -180,8 +132,7 @@ async function onContinue(): Promise<void> {
|
||||
activeStep.value === CreateProjectPassphraseStep.EnterPassphrase
|
||||
) {
|
||||
if (!passphrase.value) {
|
||||
enterError.value = 'Passphrase can\'t be empty';
|
||||
|
||||
notify.error('Passphrase can\'t be empty', AnalyticsErrorEventSource.CREATE_PROJECT_PASSPHRASE_MODAL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -218,84 +169,18 @@ function onCancelOrBack(): void {
|
||||
activeStep.value === CreateProjectPassphraseStep.EnterPassphrase
|
||||
) {
|
||||
passphrase.value = '';
|
||||
if (passphraseSaved.value) {
|
||||
passphraseSaved.value = false;
|
||||
}
|
||||
|
||||
activeStep.value = CreateProjectPassphraseStep.SelectMode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.modal {
|
||||
padding: 43px 60px 53px;
|
||||
padding: 32px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
|
||||
@media screen and (max-width: 615px) {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: 33px;
|
||||
margin-top: 20px;
|
||||
|
||||
@media screen and (max-width: 530px) {
|
||||
column-gap: unset;
|
||||
flex-direction: column-reverse;
|
||||
row-gap: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&__save-container {
|
||||
padding: 14px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin-top: 16px;
|
||||
background: #fafafb;
|
||||
border: 1px solid #c8d3de;
|
||||
border-radius: 10px;
|
||||
|
||||
&__check {
|
||||
background: #fff;
|
||||
border: 1px solid #c8d3de;
|
||||
border-radius: 8px;
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin-left: 12px;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #091c45;
|
||||
margin-bottom: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__msg {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #091c45;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checked {
|
||||
background: #00ac26;
|
||||
border-color: #00ac26;
|
||||
}
|
||||
</style>
|
||||
|
@ -3,35 +3,38 @@
|
||||
|
||||
<template>
|
||||
<div class="enter-step">
|
||||
<LockIcon />
|
||||
<h1 class="enter-step__title">Enter Passphrase</h1>
|
||||
<p class="enter-step__info">
|
||||
Please note that Storj does not know or store your encryption passphrase. If you lose it, you will not be
|
||||
able to recover your files. Save your passphrase and keep it safe.
|
||||
</p>
|
||||
<VInput
|
||||
label="Encryption Passphrase"
|
||||
:is-password="true"
|
||||
width="100%"
|
||||
height="56px"
|
||||
placeholder="Enter Encryption Passphrase"
|
||||
:error="enterError"
|
||||
@setData="setPassphrase"
|
||||
<div class="enter-step__header">
|
||||
<AccessEncryptionIcon />
|
||||
<h1 class="enter-step__header__title">Enter Passphrase</h1>
|
||||
</div>
|
||||
<EnterPassphraseStep
|
||||
is-new-passphrase
|
||||
is-project-passphrase
|
||||
:on-back="onBack"
|
||||
:on-continue="onContinue"
|
||||
:passphrase="passphrase"
|
||||
:set-passphrase="setPassphrase"
|
||||
info="Please note that Storj does not know or store your encryption passphrase. If you lose it, you will
|
||||
not be able to recover your files."
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
import EnterPassphraseStep from '@/components/accessGrants/createFlow/steps/EnterPassphraseStep.vue';
|
||||
|
||||
import LockIcon from '@/../static/images/projectPassphrase/lock.svg';
|
||||
import AccessEncryptionIcon from '@/../static/images/accessGrants/newCreateFlow/accessEncryption.svg';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
passphrase: string,
|
||||
setPassphrase?: (v: string) => void,
|
||||
enterError?: string,
|
||||
onBack: () => void,
|
||||
onContinue: () => void,
|
||||
}>(), {
|
||||
passphrase: '',
|
||||
setPassphrase: () => () => {},
|
||||
enterError: '',
|
||||
onBack: () => () => {},
|
||||
onContinue: () => () => {},
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -39,23 +42,23 @@ const props = withDefaults(defineProps<{
|
||||
.enter-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
max-width: 433px;
|
||||
max-width: 350px;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 32px;
|
||||
line-height: 39px;
|
||||
color: #1b2533;
|
||||
margin: 14px 0;
|
||||
}
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
|
||||
&__info {
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: #354049;
|
||||
margin-bottom: 24px;
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 31px;
|
||||
color: var(--c-grey-8);
|
||||
margin-left: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -3,168 +3,56 @@
|
||||
|
||||
<template>
|
||||
<div class="generated-step">
|
||||
<GeneratedIcon />
|
||||
<h1 class="generated-step__title">Passphrase Generated</h1>
|
||||
<p class="generated-step__info">
|
||||
Please note that Storj does not know or store your encryption passphrase. If you lose it, you will not be
|
||||
able to recover your files. Save your passphrase and keep it safe.
|
||||
</p>
|
||||
<div class="generated-step__mnemonic">
|
||||
<p class="generated-step__mnemonic__value">
|
||||
{{ passphrase }}
|
||||
</p>
|
||||
<div class="generated-step__mnemonic__buttons">
|
||||
<v-button
|
||||
class="copy-button"
|
||||
:label="isPassphraseCopied ? 'Copied' : 'Copy to clipboard'"
|
||||
width="156px"
|
||||
height="40px"
|
||||
:is-white="!isPassphraseCopied"
|
||||
:is-white-green="isPassphraseCopied"
|
||||
font-size="13px"
|
||||
:on-press="onCopyPassphraseClick"
|
||||
>
|
||||
<template #icon>
|
||||
<copy-icon v-if="!isPassphraseCopied" class="copy-icon" />
|
||||
<check-icon v-else class="check-icon" />
|
||||
</template>
|
||||
</v-button>
|
||||
<v-button
|
||||
:label="isPassphraseDownloaded ? 'Downloaded' : 'Download'"
|
||||
font-size="13px"
|
||||
width="100%"
|
||||
height="40px"
|
||||
:is-green="isPassphraseDownloaded"
|
||||
:on-press="downloadPassphrase"
|
||||
>
|
||||
<template #icon>
|
||||
<download-icon v-if="!isPassphraseDownloaded" />
|
||||
<check-icon v-else />
|
||||
</template>
|
||||
</v-button>
|
||||
</div>
|
||||
<div class="generated-step__header">
|
||||
<PassphraseGeneratedIcon />
|
||||
<h1 class="generated-step__header__title">Passphrase Generated</h1>
|
||||
</div>
|
||||
<PassphraseGeneratedStep
|
||||
:on-back="onBack"
|
||||
:on-continue="onContinue"
|
||||
:passphrase="passphrase"
|
||||
:name="name"
|
||||
is-project-passphrase
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import PassphraseGeneratedStep from '@/components/accessGrants/createFlow/steps/PassphraseGeneratedStep.vue';
|
||||
|
||||
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
import { useCopy, useNotify } from '@/utils/hooks';
|
||||
import { Download } from '@/utils/download';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import GeneratedIcon from '@/../static/images/projectPassphrase/generated.svg';
|
||||
import CheckIcon from '@/../static/images/common/check.svg';
|
||||
import CopyIcon from '@/../static/images/common/copy.svg';
|
||||
import DownloadIcon from '@/../static/images/common/download.svg';
|
||||
import PassphraseGeneratedIcon from '@/../static/images/accessGrants/newCreateFlow/passphraseGenerated.svg';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
name: string,
|
||||
onBack: () => void,
|
||||
onContinue: () => void,
|
||||
passphrase?: string,
|
||||
}>(), {
|
||||
passphrase: '',
|
||||
});
|
||||
|
||||
const notify = useNotify();
|
||||
|
||||
const currentDate = new Date().toISOString();
|
||||
|
||||
const isPassphraseCopied = ref<boolean>(false);
|
||||
const isPassphraseDownloaded = ref<boolean>(false);
|
||||
const analytics = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Copies passphrase to clipboard.
|
||||
*/
|
||||
function onCopyPassphraseClick(): void {
|
||||
navigator.clipboard.writeText(props.passphrase);
|
||||
isPassphraseCopied.value = true;
|
||||
analytics.eventTriggered(AnalyticsEvent.COPY_TO_CLIPBOARD_CLICKED);
|
||||
notify.success(`Passphrase was copied successfully`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads passphrase to .txt file.
|
||||
*/
|
||||
function downloadPassphrase(): void {
|
||||
isPassphraseDownloaded.value = true;
|
||||
Download.file(props.passphrase, `passphrase-${currentDate}.txt`);
|
||||
analytics.eventTriggered(AnalyticsEvent.DOWNLOAD_TXT_CLICKED);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.generated-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
max-width: 433px;
|
||||
max-width: 350px;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 32px;
|
||||
line-height: 39px;
|
||||
color: #1b2533;
|
||||
margin: 14px 0;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: #354049;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&__mnemonic {
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #ebeef1;
|
||||
border: 1px solid #d8dee3;
|
||||
border-radius: 10px;
|
||||
padding: 10px 15px 10px 23px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
|
||||
@media screen and (max-width: 530px) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 16px;
|
||||
line-height: 26px;
|
||||
letter-spacing: -0.02em;
|
||||
color: #091c45;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
margin-left: 14px;
|
||||
|
||||
@media screen and (max-width: 530px) {
|
||||
margin: 15px 0 0;
|
||||
}
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 31px;
|
||||
color: var(--c-grey-8);
|
||||
margin-left: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.copy-icon {
|
||||
|
||||
:deep(rect),
|
||||
:deep(path) {
|
||||
stroke: var(--c-grey-6);
|
||||
}
|
||||
}
|
||||
|
||||
.check-icon :deep(path) {
|
||||
fill: var(--c-green-5);
|
||||
}
|
||||
</style>
|
||||
|
@ -3,67 +3,87 @@
|
||||
|
||||
<template>
|
||||
<div class="passphrase-mode">
|
||||
<LockIcon />
|
||||
<h1 class="passphrase-mode__title">Encryption Passphrase</h1>
|
||||
<div class="passphrase-mode__header">
|
||||
<AccessEncryptionIcon />
|
||||
<h1 class="passphrase-mode__header__title">Encryption Passphrase</h1>
|
||||
</div>
|
||||
<p class="passphrase-mode__info">
|
||||
The encryption passphrase will be used to encrypt all the files you upload in this project. We encourage
|
||||
you to generate the encryption passphrase. You can also enter your own passphrase.
|
||||
The encryption passphrase will be used to encrypt the files you upload in this project. You can generate
|
||||
a new encryption passphrase, or enter your own.
|
||||
</p>
|
||||
<div
|
||||
class="passphrase-mode__option"
|
||||
:class="{selected: isGenerate}"
|
||||
@click="setGenerate"
|
||||
>
|
||||
<div
|
||||
class="passphrase-mode__option__check"
|
||||
:class="{'selected-check': isGenerate}"
|
||||
>
|
||||
<CheckIcon />
|
||||
</div>
|
||||
<div class="passphrase-mode__option__info">
|
||||
<h2 class="passphrase-mode__option__info__title">
|
||||
Generate passphrase
|
||||
</h2>
|
||||
<p class="passphrase-mode__option__info__msg">
|
||||
Automatically generate 12-word passphrase.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="passphrase-mode__option"
|
||||
:class="{selected: !isGenerate}"
|
||||
@click="setEnter"
|
||||
>
|
||||
<div
|
||||
class="passphrase-mode__option__check"
|
||||
:class="{'selected-check': !isGenerate}"
|
||||
>
|
||||
<CheckIcon />
|
||||
</div>
|
||||
<div class="passphrase-mode__option__info">
|
||||
<h2 class="passphrase-mode__option__info__title">
|
||||
Enter passphrase
|
||||
</h2>
|
||||
<p class="passphrase-mode__option__info__msg">
|
||||
You can also enter your own passphrase.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<ContainerWithIcon :icon-and-title="FUNCTIONAL_CONTAINER_ICON_AND_TITLE[FunctionalContainer.EncryptionPassphrase]">
|
||||
<template #functional>
|
||||
<div class="passphrase-mode__radios">
|
||||
<Radio
|
||||
id="generate passphrase"
|
||||
:checked="isGenerate"
|
||||
:on-check="setGenerate"
|
||||
label="Generate 12-word passphrase"
|
||||
info="Create this access with a new encryption passphrase that will be generated for you on
|
||||
the next step. The access will not be able to manage any existing data."
|
||||
/>
|
||||
<Radio
|
||||
id="new passphrase"
|
||||
:checked="!isGenerate"
|
||||
:on-check="setEnter"
|
||||
label="Enter a new passphrase"
|
||||
info="Create this access with a new encryption passphrase that you can enter on the next step.
|
||||
The access will not be able to manage any existing data."
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ContainerWithIcon>
|
||||
<ButtonsContainer>
|
||||
<template #leftButton>
|
||||
<VButton
|
||||
label="Cancel"
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
:on-press="onCancel"
|
||||
:is-white="true"
|
||||
/>
|
||||
</template>
|
||||
<template #rightButton>
|
||||
<VButton
|
||||
label="Continue ->"
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
:on-press="onContinue"
|
||||
/>
|
||||
</template>
|
||||
</ButtonsContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import LockIcon from '@/../static/images/projectPassphrase/lock.svg';
|
||||
import CheckIcon from '@/../static/images/projectPassphrase/check.svg';
|
||||
import {
|
||||
FUNCTIONAL_CONTAINER_ICON_AND_TITLE,
|
||||
FunctionalContainer,
|
||||
} from '@/types/createAccessGrant';
|
||||
|
||||
import ContainerWithIcon from '@/components/accessGrants/createFlow/components/ContainerWithIcon.vue';
|
||||
import Radio from '@/components/accessGrants/createFlow/components/Radio.vue';
|
||||
import ButtonsContainer from '@/components/accessGrants/createFlow/components/ButtonsContainer.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import AccessEncryptionIcon from '@/../static/images/accessGrants/newCreateFlow/accessEncryption.svg';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
isGenerate?: boolean,
|
||||
isGenerate?: boolean
|
||||
setGenerate?: () => void
|
||||
setEnter?: () => void
|
||||
onContinue?: () => void
|
||||
onCancel?: () => void
|
||||
}>(), {
|
||||
isGenerate: true,
|
||||
setGenerate: () => () => {},
|
||||
setEnter: () => () => {},
|
||||
onContinue: () => () => {},
|
||||
onCancel: () => () => {},
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -71,86 +91,39 @@ const props = withDefaults(defineProps<{
|
||||
.passphrase-mode {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
max-width: 433px;
|
||||
max-width: 350px;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 32px;
|
||||
line-height: 39px;
|
||||
color: #1b2533;
|
||||
margin: 14px 0;
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 31px;
|
||||
color: var(--c-grey-8);
|
||||
margin-left: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: #354049;
|
||||
margin-bottom: 24px;
|
||||
color: var(--c-blue-6);
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__option {
|
||||
padding: 14px 20px;
|
||||
&__radios {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #d8dee3;
|
||||
background-color: #fafafb;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 9px;
|
||||
width: calc(100% - 40px);
|
||||
cursor: pointer;
|
||||
|
||||
&__check {
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #c8d3de;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin-left: 16px;
|
||||
align-items: flex-start;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #091c45;
|
||||
text-align: left;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&__msg {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #091c45;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #fff;
|
||||
border-color: #0149ff;
|
||||
}
|
||||
flex-direction: column;
|
||||
row-gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: #fff;
|
||||
border-color: #929fb1;
|
||||
|
||||
&:hover {
|
||||
border-color: #929fb1;
|
||||
}
|
||||
}
|
||||
|
||||
.selected-check {
|
||||
border-color: #00ac26;
|
||||
background-color: #00ac26;
|
||||
}
|
||||
</style>
|
||||
|
@ -3,58 +3,65 @@
|
||||
|
||||
<template>
|
||||
<div class="success-step">
|
||||
<SuccessIcon class="success-step__big-icon" />
|
||||
<SmallSuccessIcon class="success-step__small-icon" />
|
||||
<h1 class="success-step__title">Success</h1>
|
||||
<div class="success-step__header">
|
||||
<SuccessIcon />
|
||||
<h1 class="success-step__header__title">Success</h1>
|
||||
</div>
|
||||
<p class="success-step__info">
|
||||
Your encryption passphrase is ready to use. Now you can upload files into your buckets securely using an
|
||||
encryption only you know.
|
||||
encryption passphrase only you know.
|
||||
</p>
|
||||
<VButton
|
||||
label="Continue ->"
|
||||
width="100%"
|
||||
height="40px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
:on-press="onContinue"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SuccessIcon from '@/../static/images/projectPassphrase/success.svg';
|
||||
import SmallSuccessIcon from '@/../static/images/projectPassphrase/smallSuccess.svg';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import SuccessIcon from '@/../static/images/accessGrants/newCreateFlow/accessCreated.svg';
|
||||
|
||||
const props = defineProps<{
|
||||
onContinue: () => void
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.success-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
max-width: 433px;
|
||||
max-width: 350px;
|
||||
|
||||
&__small-icon {
|
||||
display: none;
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
|
||||
@media screen and (max-width: 530px) {
|
||||
display: block;
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 31px;
|
||||
color: var(--c-grey-8);
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 32px;
|
||||
line-height: 39px;
|
||||
color: #1b2533;
|
||||
margin: 14px 0;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: #354049;
|
||||
margin-bottom: 24px;
|
||||
max-width: 376px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 530px) {
|
||||
|
||||
.success-step__big-icon {
|
||||
display: none;
|
||||
color: var(--c-blue-6);
|
||||
padding: 16px 0;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -81,6 +81,7 @@ export enum AnalyticsErrorEventSource {
|
||||
ADD_TOKEN_FUNDS_MODAL = 'Add token funds modal',
|
||||
CHANGE_PASSWORD_MODAL = 'Change password modal',
|
||||
CREATE_PROJECT_MODAL = 'Create project modal',
|
||||
CREATE_PROJECT_PASSPHRASE_MODAL = 'Create project passphrase modal',
|
||||
DELETE_BUCKET_MODAL = 'Delete bucket modal',
|
||||
ENABLE_MFA_MODAL = 'Enable MFA modal',
|
||||
DISABLE_MFA_MODAL = 'Disable MFA modal',
|
||||
|
@ -172,7 +172,6 @@ import CloudIcon from '@/../static/images/notifications/cloudAlert.svg';
|
||||
import WarningIcon from '@/../static/images/notifications/circleWarning.svg';
|
||||
|
||||
const bucketsStore = useBucketsStore();
|
||||
|
||||
const configStore = useConfigStore();
|
||||
const appStore = useAppStore();
|
||||
const agStore = useAccessGrantsStore();
|
||||
@ -565,7 +564,7 @@ async function refreshSession(): Promise<void> {
|
||||
try {
|
||||
LocalData.setSessionExpirationDate(await auth.refreshSession());
|
||||
} catch (error) {
|
||||
await notify.error((error instanceof ErrorUnauthorized) ? 'Your session was timed out.' : error.message, AnalyticsErrorEventSource.OVERALL_SESSION_EXPIRED_ERROR);
|
||||
notify.error((error instanceof ErrorUnauthorized) ? 'Your session was timed out.' : error.message, AnalyticsErrorEventSource.OVERALL_SESSION_EXPIRED_ERROR);
|
||||
await handleInactive();
|
||||
isSessionRefreshing.value = false;
|
||||
return;
|
||||
@ -625,7 +624,7 @@ async function handleInactive(): Promise<void> {
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorUnauthorized) return;
|
||||
|
||||
await notify.error(error.message, AnalyticsErrorEventSource.OVERALL_SESSION_EXPIRED_ERROR);
|
||||
notify.error(error.message, AnalyticsErrorEventSource.OVERALL_SESSION_EXPIRED_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -652,7 +651,7 @@ async function generateNewMFARecoveryCodes(): Promise<void> {
|
||||
await usersStore.generateUserMFARecoveryCodes();
|
||||
toggleMFARecoveryModal();
|
||||
} catch (error) {
|
||||
await notify.error(error.message, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
notify.error(error.message, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -722,7 +721,7 @@ onMounted(async () => {
|
||||
} catch (error) {
|
||||
if (!(error instanceof ErrorUnauthorized)) {
|
||||
appStore.changeState(FetchState.ERROR);
|
||||
await notify.error(error.message, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
notify.error(error.message, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
setTimeout(async () => await router.push(RouteConfig.Login.path), 1000);
|
||||
@ -734,26 +733,26 @@ onMounted(async () => {
|
||||
agStore.stopWorker();
|
||||
await agStore.startWorker();
|
||||
} catch (error) {
|
||||
await notify.error(`Unable to set access grants wizard. ${error.message}`, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
notify.error(`Unable to set access grants wizard. ${error.message}`, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
const couponType = await billingStore.setupAccount();
|
||||
if (couponType === CouponType.NoCoupon) {
|
||||
await notify.error(`The coupon code was invalid, and could not be applied to your account`, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
notify.error(`The coupon code was invalid, and could not be applied to your account`, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
if (couponType === CouponType.SignupCoupon) {
|
||||
await notify.success(`The coupon code was added successfully`);
|
||||
notify.success(`The coupon code was added successfully`);
|
||||
}
|
||||
} catch (error) {
|
||||
await notify.error(`Unable to setup account. ${error.message}`, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
notify.error(`Unable to setup account. ${error.message}`, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
await billingStore.getCreditCards();
|
||||
} catch (error) {
|
||||
await notify.error(`Unable to get credit cards. ${error.message}`, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
notify.error(`Unable to get credit cards. ${error.message}`, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
let projects: Project[] = [];
|
||||
|
@ -1,3 +0,0 @@
|
||||
<svg width="17" height="12" viewBox="0 0 17 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.6626 0.438444C16.2482 1.02345 16.2486 1.97241 15.6636 2.558L6.67014 11.5605C6.38912 11.8418 6.00783 11.9999 5.61021 12C5.21259 12.0001 4.83122 11.8422 4.55006 11.561L0.438974 7.44994C-0.146325 6.86464 -0.146325 5.91568 0.438974 5.33039C1.02427 4.74509 1.97323 4.74509 2.55853 5.33039L5.60931 8.38117L13.543 0.439504C14.128 -0.146087 15.077 -0.146562 15.6626 0.438444Z" fill="white"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 544 B |
@ -1,7 +0,0 @@
|
||||
<svg width="112" height="112" viewBox="0 0 112 112" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.0385 0H65.3697C80.8738 0 87.0253 1.71842 93.0589 4.94527C99.0926 8.17211 103.828 12.9074 107.055 18.941L107.3 19.4056C110.328 25.2188 111.957 31.3599 112 46.0385V65.3698C112 80.8738 110.282 87.0253 107.055 93.059C103.828 99.0926 99.0926 103.828 93.0589 107.055L92.5944 107.3C86.7812 110.328 80.6401 111.957 65.9615 112H46.6302C31.1262 112 24.9747 110.282 18.941 107.055C12.9074 103.828 8.17211 99.0926 4.94527 93.059L4.70003 92.5944C1.67181 86.7812 0.0431812 80.6401 0 65.9615V46.6303C0 31.1262 1.71842 24.9747 4.94527 18.941C8.17211 12.9074 12.9074 8.17211 18.941 4.94527L19.4056 4.70003C25.2188 1.67181 31.3599 0.0431812 46.0385 0Z" fill="#00AC26"/>
|
||||
<path d="M56.4626 76.3289C68.2203 76.3289 77.7519 66.7975 77.7519 55.0399C77.7519 43.2824 68.2203 33.751 56.4626 33.751C44.7049 33.751 35.1733 43.2824 35.1733 55.0399C35.1733 66.7975 44.7049 76.3289 56.4626 76.3289Z" fill="#00AC26"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.9994 28.1968C71.5912 28.1968 84.2309 40.629 84.2309 55.9648C84.2309 71.3007 71.5912 83.7329 55.9994 83.7329C41.3473 83.7329 29.3022 72.7541 27.9036 58.7039C28.1179 53.787 28.5845 50.0463 29.2911 46.9459C33.0972 36.0371 43.6174 28.1968 55.9994 28.1968ZM55.9994 36.5272C44.9779 36.5272 36.0985 45.2608 36.0985 55.9648C36.0985 66.6688 44.9779 75.4025 55.9994 75.4025C67.0209 75.4025 75.9003 66.6688 75.9003 55.9648C75.9003 45.2608 67.0209 36.5272 55.9994 36.5272Z" fill="#00AC26"/>
|
||||
<rect x="18.5703" y="19.5112" width="73.8262" height="73.8262" rx="36.9131" fill="#00AC26"/>
|
||||
<path d="M55.4835 23.2026C73.8314 23.2026 88.7053 38.0765 88.7053 56.4244C88.7053 74.7723 73.8314 89.6462 55.4835 89.6462C37.1356 89.6462 22.2617 74.7723 22.2617 56.4244C22.2617 38.0765 37.1356 23.2026 55.4835 23.2026ZM55.4835 29.2933C40.4994 29.2933 28.3524 41.4403 28.3524 56.4244C28.3524 71.4085 40.4994 83.5555 55.4835 83.5555C70.4676 83.5555 82.6146 71.4085 82.6146 56.4244C82.6146 41.4403 70.4676 29.2933 55.4835 29.2933ZM69.6538 47.2132C70.814 48.3734 70.8423 50.237 69.7387 51.4316L69.6538 51.5199L54.6698 66.5038C54.2226 66.9511 53.6733 67.2363 53.096 67.3582C52.1033 67.6899 50.9649 67.4806 50.1469 66.7153L50.1114 66.6815L41.2136 57.7843C40.0243 56.595 40.0243 54.6668 41.2136 53.4775C42.3738 52.3172 44.2374 52.2889 45.432 53.3926L45.5203 53.4775L52.3019 60.2583L65.347 47.2132C66.5363 46.0239 68.4645 46.0239 69.6538 47.2132Z" fill="white"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
@ -1,4 +0,0 @@
|
||||
<svg width="113" height="113" viewBox="0 0 113 113" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="113" height="113" rx="56.5" fill="#00AC26"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M83.5283 37.4363C85.7359 39.6417 85.7377 43.219 83.5323 45.4266L49.6285 79.3643C48.5691 80.4247 47.1318 81.0207 45.6328 81.0211C44.1338 81.0215 42.6962 80.4262 41.6362 79.3663L26.1382 63.8683C23.9318 61.6618 23.9318 58.0844 26.1382 55.878C28.3447 53.6715 31.9221 53.6715 34.1286 55.878L45.6294 67.3788L75.538 37.4403C77.7434 35.2327 81.3207 35.2309 83.5283 37.4363Z" fill="white"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 604 B |
@ -1,13 +0,0 @@
|
||||
<svg width="437" height="140" viewBox="0 0 437 140" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="156" y="27" width="113" height="113" rx="56.5" fill="#00AC26"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M239.528 64.4363C241.736 66.6417 241.738 70.219 239.532 72.4266L205.629 106.364C204.569 107.425 203.132 108.021 201.633 108.021C200.134 108.021 198.696 107.426 197.636 106.366L182.138 90.8683C179.932 88.6618 179.932 85.0844 182.138 82.878C184.345 80.6715 187.922 80.6715 190.129 82.878L201.629 94.3788L231.538 64.4403C233.743 62.2327 237.321 62.2309 239.528 64.4363Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M314.247 63.5977H337.161V86.5115H314.247V63.5977Z" fill="#00E567"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M150.577 0H166.944V16.367H150.577V0Z" fill="#FF458B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M342.304 29.9287H352.124V39.7489H342.304V29.9287Z" fill="#FF458B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M76.6919 30.3955H86.5121V40.2157H76.6919V30.3955Z" fill="#00E567"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M427.18 16.3672H437V26.1874H427.18V16.3672Z" fill="#0149FF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 83.4717H9.82022V93.2919H0V83.4717Z" fill="#0149FF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M392.576 54.2451H402.396V64.0653H392.576V54.2451Z" fill="#FFC600"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M103.346 78.3284H132.806V107.789H103.346V78.3284Z" fill="#FFC600"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M268.886 13.0938H291.8V36.0076H268.886V13.0938Z" fill="#0149FF"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
Loading…
Reference in New Issue
Block a user