Web/satellite: Create Access Flow Modal Refactor (#5015)

Extrapolated each stage in the Access Grant flow into its own component and replaced the code on CreateAccessModal with the new components.
This commit is contained in:
hovex023 2022-08-10 17:36:41 -05:00 committed by GitHub
parent d8b010f7bd
commit 4418216b4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 1721 additions and 1301 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,691 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div>
<div class="access-grant__modal-container__header-container">
<h2 class="access-grant__modal-container__header-container__title">Create Access</h2>
<div
class="access-grant__modal-container__header-container__close-cross-container" @click="onCloseClick"
>
<CloseCrossIcon />
</div>
</div>
<div class="access-grant__modal-container__body-container">
<TypesIcon class="access-grant__modal-container__body-container__type-icon" />
<div class="access-grant__modal-container__body-container__type">
<p>Type</p>
<div class="access-grant__modal-container__body-container__type__type-container">
<input
id="access-grant-check"
v-model="checkedType"
value="access"
type="radio"
:checked="checkedType === 'access'"
name="type"
>
<label for="access-grant-check">
Access Grant
</label>
<img
class="tooltip-icon"
src="../../../../static/images/accessGrants/create-access_information.png"
alt="tooltip icon"
@mouseover="toggleTooltipHover('access','over')"
@mouseleave="toggleTooltipHover('access','leave')"
>
<div
v-if="tooltipHover === 'access'"
class="access-tooltip"
@mouseover="toggleTooltipHover('access','over')"
@mouseleave="toggleTooltipHover('access','leave')"
>
<span class="tooltip-text">Keys to upload, delete, and view your project's data. <a class="tooltip-link" href="https://storj-labs.gitbook.io/dcs/concepts/access/access-grants" target="_blank" rel="noreferrer noopener">Learn More</a></span>
</div>
</div>
<div class="access-grant__modal-container__body-container__type__type-container">
<input
id="s3-check"
v-model="checkedType"
value="s3"
type="radio"
name="type"
:checked="checkedType === 's3'"
>
<label for="s3-check">S3 Credentials</label>
<img
class="tooltip-icon"
src="../../../../static/images/accessGrants/create-access_information.png"
alt="tooltip icon"
@mouseover="toggleTooltipHover('s3','over')"
@mouseleave="toggleTooltipHover('s3','leave')"
>
<div
v-if="tooltipHover === 's3'"
class="s3-tooltip"
@mouseover="toggleTooltipHover('s3','over')"
@mouseleave="toggleTooltipHover('s3','leave')"
>
<span class="tooltip-text">Generates access key, secret key, and endpoint to use in your S3-supporting application. <a class="tooltip-link" href="https://docs.storj.io/dcs/api-reference/s3-compatible-gateway" target="_blank" rel="noreferrer noopener">Learn More</a></span>
</div>
</div>
<div class="access-grant__modal-container__body-container__type__type-container">
<input
id="api-check"
v-model="checkedType"
value="api"
type="radio"
name="type"
:checked="checkedType === 'api'"
>
<label for="api-check">API Access</label>
<img
class="tooltip-icon"
src="../../../../static/images/accessGrants/create-access_information.png"
alt="tooltip icon"
@mouseover="toggleTooltipHover('api','over')"
@mouseleave="toggleTooltipHover('api','leave')"
>
<div
v-if="tooltipHover === 'api'"
class="api-tooltip"
@mouseover="toggleTooltipHover('api','over')"
@mouseleave="toggleTooltipHover('api','leave')"
>
<span class="tooltip-text">Creates access grant to run in the command line. <a class="tooltip-link" href="https://docs.storj.io/dcs/getting-started/quickstart-uplink-cli/generate-access-grants-and-tokens/generate-a-token/" target="_blank" rel="noreferrer noopener">Learn More</a></span>
</div>
</div>
</div>
<NameIcon class="access-grant__modal-container__body-container__name-icon" />
<div class="access-grant__modal-container__body-container__name">
<p>Name</p>
<input
v-model="accessName"
type="text"
placeholder="Input Access Name" class="access-grant__modal-container__body-container__name__input"
>
</div>
<PermissionsIcon class="access-grant__modal-container__body-container__permissions-icon" />
<div class="access-grant__modal-container__body-container__permissions">
<p>Permissions</p>
<div>
<input
id="permissions__all-check"
type="checkbox"
:checked="allPermissionsClicked"
@click="toggleAllPermission('all')"
>
<label for="permissions__all-check">All</label>
<Chevron :class="`permissions-chevron-${showAllPermissions.position}`" @click="togglePermissions" />
</div>
<div v-if="showAllPermissions.show === true">
<div v-for="(item, key) in permissionsList" :key="key">
<input
:id="`permissions__${item}-check`"
v-model="selectedPermissions"
:value="item"
type="checkbox"
:checked="checkedPermissions.item"
@click="toggleAllPermission(item)"
>
<label :for="`permissions__${item}-check`">{{ item }}</label>
</div>
</div>
</div>
<BucketsIcon class="access-grant__modal-container__body-container__buckets-icon" />
<div class="access-grant__modal-container__body-container__buckets">
<p>Buckets</p>
<div>
<BucketsSelection
class="access-bucket-container"
:show-scrollbar="true"
/>
</div>
<div class="access-grant__modal-container__body-container__buckets__bucket-bullets">
<div
v-for="(name, index) in selectedBucketNames"
:key="index"
class="access-grant__modal-container__body-container__buckets__bucket-bullets__container"
>
<BucketNameBullet :name="name" />
</div>
</div>
</div>
<DateIcon class="access-grant__modal-container__body-container__date-icon" />
<div class="access-grant__modal-container__body-container__duration">
<p>Duration</p>
<div v-if="addDateSelected">
<DurationSelection
container-style="access-date-container"
text-style="access-date-text"
picker-style="__access-date-container"
/>
</div>
<div
v-else
class="access-grant__modal-container__body-container__duration__text"
@click="addDateSelected = true"
>
Add Date (optional)
</div>
</div>
<!-- for future use when notes feature is implemented -->
<!-- <NotesIcon class="access-grant__modal-container__body-container__notes-icon"/>
<div class="access-grant__modal-container__body-container__notes">
<p>Notes</p>
<div>--Notes Section Here--</div>
</div> -->
</div>
<div class="access-grant__modal-container__footer-container">
<a href="https://docs.storj.io/dcs/concepts/access/access-grants/api-key/" target="_blank" rel="noopener noreferrer">
<v-button
label="Learn More"
width="150px"
height="50px"
is-transparent="true"
font-size="16px"
class="access-grant__modal-container__footer-container__learn-more-button"
/>
</a>
<v-button
:label="checkedType === 'api' ? 'Create Keys ⟶' : 'Encrypt My Access ⟶'"
font-size="16px"
width="auto"
height="50px"
class="access-grant__modal-container__footer-container__encrypt-button"
:on-press="checkedType === 'api' ? propogateInfo : encryptClickAction"
:is-disabled="selectedPermissions.length === 0 || accessName === ''"
/>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
import DateIcon from '@/../static/images/accessGrants/create-access_date.svg';
import VButton from '@/components/common/VButton.vue';
import BucketsSelection from '@/components/accessGrants/permissions/BucketsSelection.vue';
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
import TypesIcon from '@/../static/images/accessGrants/create-access_type.svg';
import NameIcon from '@/../static/images/accessGrants/create-access_name.svg';
import PermissionsIcon from '@/../static/images/accessGrants/create-access_permissions.svg';
import Chevron from '@/../static/images/accessGrants/chevron.svg';
import BucketsIcon from '@/../static/images/accessGrants/create-access_buckets.svg';
import BucketNameBullet from "@/components/accessGrants/permissions/BucketNameBullet.vue";
import DurationSelection from '@/components/accessGrants/permissions/DurationSelection.vue';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { AccessGrant } from '@/types/accessGrants';
// @vue/component
@Component({
components: {
Chevron,
CloseCrossIcon,
TypesIcon,
NameIcon,
PermissionsIcon,
BucketsSelection,
BucketsIcon,
BucketNameBullet,
DateIcon,
DurationSelection,
VButton
},
})
export default class CreateFormModal extends Vue {
@Prop({ default: '' })
private checkedType: string;
public showAllPermissions = {show: false, position: "up"};
private accessName = '';
private selectedPermissions : string[] = [];
private allPermissionsClicked = false;
private permissionsList = ["Read","Write","List","Delete"];
private checkedPermissions = {Read: false, Write: false, List: false, Delete: false};
private accessGrantList = this.accessGrantsList;
private addDateSelected = false;
public tooltipHover = '';
public tooltipVisibilityTimer;
public mounted(): void {
this.showAllPermissions = {show: false, position: "up"};
}
public onCloseClick(): void {
this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR_SELECTION);
this.$emit('close-modal');
}
/**
* Retrieves selected buckets for bucket bullets.
*/
public get selectedBucketNames(): string[] {
return this.$store.state.accessGrantsModule.selectedBucketNames;
}
/**
* propogates selected info to parent on flow progression.
*/
public propogateInfo(): void {
const payloadObject = {
'checkedType': this.checkedType,
'accessName': this.accessName,
'selectedPermissions': this.selectedPermissions,
}
this.$emit('propogateInfo', payloadObject, this.checkedType)
}
/**
* Toggles permissions list visibility.
*/
public togglePermissions(): void {
this.showAllPermissions.show = !this.showAllPermissions.show;
this.showAllPermissions.position = this.showAllPermissions.show ? 'up' : 'down';
}
public get accessGrantsList(): AccessGrant[] {
return this.$store.state.accessGrantsModule.page.accessGrants;
}
public encryptClickAction(): void {
let mappedList = this.accessGrantList.map((key) => (key.name))
if (mappedList.includes(this.accessName)) {
this.$notify.error(`validation: An API Key with this name already exists in this project, please use a different name`);
return
} else if (this.checkedType !== "api") {
// emit event here
this.propogateInfo()
this.$emit('encrypt');
}
}
public toggleAllPermission(type): void {
if (type === 'all' && !this.allPermissionsClicked) {
this.allPermissionsClicked = true;
this.selectedPermissions = this.permissionsList;
this.checkedPermissions = { Read: true, Write: true, List: true, Delete: true }
return
} else if(type === 'all' && this.allPermissionsClicked) {
this.allPermissionsClicked = false;
this.selectedPermissions = [];
this.checkedPermissions = { Read: false, Write: false, List: false, Delete: false };
return
} else if(this.checkedPermissions[type]) {
this.checkedPermissions[type] = false;
this.allPermissionsClicked = false;
return;
} else {
this.checkedPermissions[type] = true;
if(this.checkedPermissions.Read && this.checkedPermissions.Write && this.checkedPermissions.List && this.checkedPermissions.Delete) {
this.allPermissionsClicked = true;
}
}
}
/**
* Toggles tooltip visibility.
*/
public toggleTooltipHover(type, action): void {
if (this.tooltipHover === '' && action === 'over') {
this.tooltipHover = type;
return;
} else if (this.tooltipHover === type && action === 'leave') {
this.tooltipVisibilityTimer = setTimeout(() => {
this.tooltipHover = '';
},750);
return;
} else if (this.tooltipHover === type && action === 'over') {
clearTimeout(this.tooltipVisibilityTimer);
return;
} else if(this.tooltipHover !== type) {
clearTimeout(this.tooltipVisibilityTimer)
this.tooltipHover = type;
}
}
}
</script>
<style scoped lang="scss">
@mixin chevron {
padding-left: 4px;
transition: transform 0.3s;
}
@mixin tooltip-container {
position: absolute;
background: #56606d;
border-radius: 6px;
width: 253px;
color: #fff;
display: flex;
flex-direction: row;
align-items: flex-start;
padding: 8px;
z-index: 1;
transition: 250ms;
}
@mixin tooltip-arrow {
content: '';
position: absolute;
bottom: 0;
width: 0;
height: 0;
border: 6px solid transparent;
border-top-color: #56606d;
border-bottom: 0;
margin-left: -20px;
margin-bottom: -20px;
}
p {
font-weight: bold;
padding-bottom: 10px;
}
label {
margin-left: 8px;
padding-right: 10px;
}
.permissions-chevron-up {
@include chevron;
transform: rotate(-90deg);
}
.permissions-chevron-down {
@include chevron;
}
.tooltip-icon {
display: flex;
width: 14px;
height: 14px;
cursor: pointer;
}
.tooltip-text {
text-align: center;
font-weight: 500;
}
.access-grant {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
background: rgb(27 37 51 / 75%);
display: flex;
align-items: flex-start;
justify-content: center;
& > * {
font-family: sans-serif;
}
&__modal-container {
background: #fff;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: flex-start;
position: relative;
padding: 25px 40px;
margin-top: 40px;
width: 410px;
height: auto;
&__header-container {
text-align: left;
display: grid;
grid-template-columns: 2fr 1fr;
width: 100%;
padding-top: 10px;
&__title {
grid-column: 1;
}
&__close-cross-container {
grid-column: 2;
margin: auto 0 auto auto;
display: flex;
justify-content: center;
align-items: center;
right: 30px;
top: 30px;
height: 24px;
width: 24px;
cursor: pointer;
}
&__close-cross-container:hover .close-cross-svg-path {
fill: #2683ff;
}
}
&__body-container {
display: grid;
grid-template-columns: 1fr 6fr;
grid-template-rows: auto auto auto auto auto auto;
grid-row-gap: 24px;
width: 100%;
padding-top: 10px;
margin-top: 24px;
&__type-icon {
grid-column: 1;
grid-row: 1;
}
&__type {
grid-column: 2;
grid-row: 1;
display: flex;
flex-direction: column;
&__type-container {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
}
&__name-icon {
grid-column: 1;
grid-row: 2;
}
&__name {
grid-column: 2;
grid-row: 2;
display: flex;
flex-direction: column;
max-width: 238px;
&__input {
background: #fff;
border: 1px solid #c8d3de;
box-sizing: border-box;
border-radius: 6px;
height: 40px;
font-size: 17px;
padding: 10px;
}
&__input:focus {
border-color: #2683ff;
}
}
&__input:focus {
border-color: #2683ff;
}
&__permissions-icon {
grid-column: 1;
grid-row: 3;
}
&__permissions {
grid-column: 2;
grid-row: 3;
display: flex;
flex-direction: column;
}
&__buckets-icon {
grid-column: 1;
grid-row: 4;
}
&__buckets {
grid-column: 2;
grid-row: 4;
display: flex;
flex-direction: column;
&__bucket-bullets {
display: flex;
align-items: center;
max-width: 100%;
flex-wrap: wrap;
&__container {
display: flex;
margin-top: 5px;
}
}
}
&__date-icon {
grid-column: 1;
grid-row: 5;
}
&__duration {
grid-column: 2;
grid-row: 5;
display: flex;
flex-direction: column;
&__text {
color: #929fb1;
text-decoration: underline;
font-family: sans-serif;
cursor: pointer;
}
}
&__notes-icon {
grid-column: 1;
grid-row: 6;
}
&__notes {
grid-column: 2;
grid-row: 6;
display: flex;
flex-direction: column;
}
}
&__footer-container {
display: flex;
width: 100%;
justify-content: flex-start;
margin-top: 16px;
& ::v-deep .container:first-of-type {
margin-right: 8px;
}
&__learn-more-button {
padding: 0 15px;
}
&__encrypt-button {
padding: 0 15px;
}
}
}
}
::v-deep .buckets-selection {
margin-left: 0;
height: 40px;
border: 1px solid #c8d3de;
}
::v-deep .buckets-selection__toggle-container {
padding: 10px 20px;
}
.access-tooltip {
top: 68px;
left: 112px;
@include tooltip-container;
&:after {
left: 50%;
top: 100%;
@include tooltip-arrow;
}
}
.s3-tooltip {
top: 175px;
left: 121px;
@include tooltip-container;
&:after {
left: 50%;
top: -8%;
transform: rotate(180deg);
@include tooltip-arrow;
}
}
.api-tooltip {
top: 204px;
left: 96px;
@include tooltip-container;
&:after {
left: 50%;
top: -11%;
transform: rotate(180deg);
@include tooltip-arrow;
}
}
@media screen and (max-width: 500px) {
.access-grant__modal-container {
width: auto;
max-width: 80vw;
padding: 30px 24px;
&__body-container {
grid-template-columns: 1.2fr 6fr;
}
}
}
</style>

View File

@ -0,0 +1,486 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div>
<div class="access-grant__modal-container__header-container">
<h2 class="access-grant__modal-container__header-container__title">Select Encryption</h2>
<div
class="access-grant__modal-container__header-container__close-cross-container"
@click="onCloseClick"
>
<CloseCrossIcon />
</div>
</div>
<div class="access-grant__modal-container__body-container-encrypt">
<div class="access-grant__modal-container__body-container__encrypt">
<div
v-if="!(encryptSelect === 'create' && (isPassphraseDownloaded || isPassphraseCopied))"
class="access-grant__modal-container__body-container__encrypt__item"
>
<div class="access-grant__modal-container__body-container__encrypt__item__left-area">
<AccessKeyIcon
class="access-grant__modal-container__body-container__encrypt__item__icon"
:class="{ selected: encryptSelect === 'generate' }"
/>
<div class="access-grant__modal-container__body-container__encrypt__item__text">
<h3>Generate Passphrase</h3>
<p>Automatically Generate Seed</p>
</div>
</div>
<div class="access-grant__modal-container__body-container__encrypt__item__radio">
<input
id="generate-check"
v-model="encryptSelect"
value="generate"
type="radio"
name="type"
@change="onRadioInput"
>
</div>
</div>
<div
v-if="encryptSelect === 'generate'"
class="access-grant__modal-container__generated-passphrase"
>
{{ passphrase }}
</div>
<div
v-if="!(encryptSelect && (isPassphraseDownloaded || isPassphraseCopied))"
id="divider"
class="access-grant__modal-container__body-container__encrypt__divider"
:class="{ 'in-middle': encryptSelect === 'generate' }"
/>
<div
v-if="!(encryptSelect === 'generate' && (isPassphraseDownloaded || isPassphraseCopied))"
id="own"
:class="{ 'in-middle': encryptSelect === 'generate' }"
class="access-grant__modal-container__body-container__encrypt__item"
>
<div class="access-grant__modal-container__body-container__encrypt__item__left-area">
<ThumbPrintIcon
class="access-grant__modal-container__body-container__encrypt__item__icon"
:class="{ selected: encryptSelect === 'create' }"
/>
<div class="access-grant__modal-container__body-container__encrypt__item__text">
<h3>Create My Own Passphrase</h3>
<p>Make it Personalized</p>
</div>
</div>
<div class="access-grant__modal-container__body-container__encrypt__item__radio">
<input
id="create-check"
v-model="encryptSelect"
value="create"
type="radio"
name="type"
@change="onRadioInput"
>
</div>
</div>
<input
v-if="encryptSelect === 'create'"
v-model="passphrase"
type="text"
placeholder="Input Your Passphrase"
class="access-grant__modal-container__body-container__passphrase" :disabled="encryptSelect === 'generate'"
@input="resetSavedStatus"
>
<div
class="access-grant__modal-container__footer-container"
:class="{ 'in-middle': encryptSelect === 'generate' }"
>
<v-button
:label="isPassphraseCopied ? 'Copied' : 'Copy to clipboard'"
width="auto"
height="50px"
:is-transparent="!isPassphraseCopied"
:is-white-green="isPassphraseCopied"
class="access-grant__modal-container__footer-container__copy-button"
font-size="16px"
:on-press="onCopyPassphraseClick"
:is-disabled="passphrase.length < 1"
>
<template v-if="!isPassphraseCopied" #icon>
<copy-icon class="button-icon" :class="{ active: passphrase }" />
</template>
</v-button>
<v-button
label="Download .txt"
font-size="16px"
width="auto"
height="50px"
class="access-grant__modal-container__footer-container__download-button"
:is-green-white="isPassphraseDownloaded"
:on-press="downloadPassphrase"
:is-disabled="passphrase.length < 1"
>
<template v-if="!isPassphraseDownloaded" #icon>
<download-icon class="button-icon" />
</template>
</v-button>
</div>
</div>
<div v-if="isPassphraseDownloaded || isPassphraseCopied" :class="`access-grant__modal-container__acknowledgement-container ${acknowledgementCheck ? 'blue-background' : ''}`">
<input
v-model="acknowledgementCheck"
type="checkbox"
class="access-grant__modal-container__acknowledgement-container__check"
>
<div class="access-grant__modal-container__acknowledgement-container__text">I understand that Storj does not know or store my encryption passphrase. If I lose it, I won't be able to recover files.</div>
</div>
<div
v-if="isPassphraseDownloaded || isPassphraseCopied"
class="access-grant__modal-container__acknowledgement-buttons"
>
<v-button
label="Back"
width="auto"
height="50px"
:is-transparent="true"
class="access-grant__modal-container__footer-container__copy-button"
font-size="16px"
:on-press="backAction"
/>
<v-button
label="Create my Access ⟶"
font-size="16px"
width="auto"
height="50px"
class="access-grant__modal-container__footer-container__download-button"
:is-disabled="!acknowledgementCheck"
:on-press="createAccessGrant"
/>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { generateMnemonic } from "bip39";
import { Download } from "@/utils/download";
import CopyIcon from '../../../../static/images/common/copy.svg';
import VButton from '@/components/common/VButton.vue';
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
import AccessKeyIcon from '@/../static/images/accessGrants/accessKeyIcon.svg';
import ThumbPrintIcon from '@/../static/images/accessGrants/thumbPrintIcon.svg';
import DownloadIcon from '../../../../static/images/common/download.svg';
// @vue/component
@Component({
components: {
AccessKeyIcon,
CloseCrossIcon,
CopyIcon,
DownloadIcon,
ThumbPrintIcon,
VButton
},
})
export default class GrantCreatedModal extends Vue {
private encryptSelect = "create";
private isPassphraseCopied = false;
private isPassphraseDownloaded = false;
private passphrase = "";
private accessGrantStep = "create";
private acknowledgementCheck = false;
public currentDate = new Date().toISOString();
public createAccessGrant(): void {
this.$emit('create-access');
}
public onCloseClick(): void {
this.$emit('close-modal');
}
public onRadioInput(): void {
this.isPassphraseCopied = false;
this.isPassphraseDownloaded = false;
this.passphrase = '';
if (this.encryptSelect === "generate") {
this.passphrase = generateMnemonic();
}
}
public backAction(): void {
this.$emit('backAction')
}
public onCopyPassphraseClick(): void {
this.$copyText(this.passphrase);
this.isPassphraseCopied = true;
this.$notify.success(`Passphrase was copied successfully`);
}
/**
* Downloads passphrase to .txt file
*/
public downloadPassphrase(): void {
this.isPassphraseDownloaded = true;
Download.file(this.passphrase, `passphrase-${this.currentDate}.txt`)
}
}
</script>
<style scoped lang="scss">
.button-icon {
margin-right: 5px;
::v-deep path,
::v-deep rect {
stroke: white;
}
&.active {
::v-deep path,
::v-deep rect {
stroke: #56606d;
}
}
}
@mixin generated-text {
margin-top: 20px;
margin-bottom: 20px;
align-items: center;
padding: 10px 16px;
background: #ebeef1;
border: 1px solid #c8d3de;
border-radius: 7px;
}
.access-grant {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
background: rgb(27 37 51 / 75%);
display: flex;
align-items: flex-start;
justify-content: center;
& > * {
font-family: sans-serif;
}
&__modal-container {
background: #fff;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: flex-start;
position: relative;
padding: 25px 40px;
margin-top: 40px;
width: 410px;
height: auto;
&__generated-passphrase {
@include generated-text;
}
&__generated-credentials {
@include generated-text;
margin: 0 0 4px;
display: flex;
justify-content: space-between;
&__text {
width: 90%;
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
}
}
&__header-container {
text-align: left;
display: grid;
grid-template-columns: 2fr 1fr;
width: 100%;
padding-top: 10px;
&__title {
grid-column: 1;
}
&__close-cross-container {
grid-column: 2;
margin: auto 0 auto auto;
display: flex;
justify-content: center;
align-items: center;
right: 30px;
top: 30px;
height: 24px;
width: 24px;
cursor: pointer;
}
&__close-cross-container:hover .close-cross-svg-path {
fill: #2683ff;
}
}
&__acknowledgement-container {
border: 1px solid #c8d3de;
border-radius: 6px;
display: grid;
grid-template-columns: 1fr 6fr;
padding: 10px;
margin-top: 25px;
height: 80px;
align-content: center;
&__check {
margin: 0 auto auto;
border-radius: 4px;
height: 16px;
width: 16px;
}
&__text {
font-family: sans-serif;
}
}
&__acknowledgement-buttons {
display: flex;
padding-top: 25px;
}
&__body-container {
display: grid;
grid-template-columns: 1fr 6fr;
grid-template-rows: auto auto auto auto auto auto;
grid-row-gap: 24px;
width: 100%;
padding-top: 10px;
margin-top: 24px;
&__passphrase {
margin-top: 20px;
width: 100%;
background: #fff;
border: 1px solid #c8d3de;
box-sizing: border-box;
border-radius: 4px;
height: 40px;
font-size: 17px;
padding: 10px;
}
&__encrypt {
width: 100%;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
margin: 15px 0;
&__item {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 40px;
box-sizing: border-box;
&__left-area {
display: flex;
align-items: center;
justify-content: flex-start;
}
&__icon {
margin-right: 8px;
&.selected {
::v-deep circle {
fill: #e6edf7 !important;
}
::v-deep path {
fill: #003dc1 !important;
}
}
}
&__text {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
font-family: 'font_regular', sans-serif;
font-size: 12px;
h3 {
margin: 0 0 8px;
font-family: 'font_bold', sans-serif;
font-size: 14px;
}
p {
padding: 0;
}
}
&__radio {
display: flex;
align-items: center;
justify-content: center;
width: 10px;
height: 10px;
}
}
&__divider {
width: 100%;
height: 1px;
background: #ebeef1;
margin: 16px 0;
&.in-middle {
order: 4;
}
}
}
}
&__footer-container {
display: flex;
width: 100%;
justify-content: flex-start;
margin-top: 16px;
& ::v-deep .container:first-of-type {
margin-right: 8px;
}
&__copy-button {
width: 49% !important;
margin-right: 10px;
}
&__download-button {
width: 49% !important;
}
.in-middle {
order: 3;
}
}
}
}
</style>

View File

@ -0,0 +1,497 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div>
<div class="access-grant__modal-container__header-container">
<AccessGrantsIcon v-if="checkedType === 'access'" />
<S3Icon v-if="checkedType === 's3'" />
<CLIIcon v-if="checkedType === 'api'" />
<div class="access-grant__modal-container__header-container__close-cross-container" @click="onCloseClick">
<CloseCrossIcon />
</div>
<h2 class="access-grant__modal-container__header-container__title-complete">{{ accessName }}&nbsp;Created</h2>
</div>
<div class="access-grant__modal-container__body-container__created">
<p>Now copy and save the {{ checkedText[checkedType][0] }} will only appear once. Click on the {{ checkedText[checkedType][1] }}</p>
</div>
<div v-if="checkedType === 'access'">
<div class="access-grant__modal-container__generated-credentials__label first">
<span class="access-grant__modal-container__generated-credentials__label__text">
Access Grant
</span>
<a
href="https://docs.storj.io/dcs/concepts/access/access-grants/"
target="_blank"
>
<img
class="tooltip-icon"
alt="tooltip icon"
src="../../../../static/images/accessGrants/create-access_information.png"
>
</a>
</div>
<div
class="access-grant__modal-container__generated-credentials"
>
<span class="access-grant__modal-container__generated-credentials__text">
{{ access }}
</span>
<img
class="clickable-image"
alt="copy icon"
src="../../../../static/images/accessGrants/create-access_copy-icon.png"
@click="onCopyClick(access)"
>
</div>
</div>
<div v-if="checkedType === 's3'">
<div class="access-grant__modal-container__generated-credentials__label first">
<span class="access-grant__modal-container__generated-credentials__label__text">
Access Key
</span>
</div>
<div
class="access-grant__modal-container__generated-credentials"
>
<span class="access-grant__modal-container__generated-credentials__text">
{{ gatewayCredentials.accessKeyId }}
</span>
<img
class="clickable-image"
alt="copy icon"
src="../../../../static/images/accessGrants/create-access_copy-icon.png"
@click="onCopyClick(gatewayCredentials.accessKeyId)"
>
</div>
<div class="access-grant__modal-container__generated-credentials__label">
<span class="access-grant__modal-container__generated-credentials__label__text">
Secret Key
</span>
</div>
<div
class="access-grant__modal-container__generated-credentials"
>
<span class="access-grant__modal-container__generated-credentials__text">
{{ gatewayCredentials.secretKey }}
</span>
<img
class="clickable-image"
alt="copy icon"
src="../../../../static/images/accessGrants/create-access_copy-icon.png"
@click="onCopyClick(gatewayCredentials.secretKey)"
>
</div>
<div class="access-grant__modal-container__generated-credentials__label">
<span class="access-grant__modal-container__generated-credentials__label__text">
Endpoint
</span>
</div>
<div
class="access-grant__modal-container__generated-credentials"
>
<span class="access-grant__modal-container__generated-credentials__text">
{{ gatewayCredentials.endpoint }}
</span>
<img
class="clickable-image"
src="../../../../static/images/accessGrants/create-access_copy-icon.png"
target="_blank"
href="https://docs.storj.io/dcs/concepts/satellite/"
@click="onCopyClick(gatewayCredentials.endpoint)"
>
</div>
</div>
<div v-if="checkedType === 'api'">
<div class="access-grant__modal-container__generated-credentials__label first">
<span class="access-grant__modal-container__generated-credentials__label__text">
Satellite Address
</span>
<a
href="https://docs.storj.io/dcs/concepts/satellite/"
target="_blank"
>
<img
class="tooltip-icon"
alt="tooltip icon"
src="../../../../static/images/accessGrants/create-access_information.png"
>
</a>
</div>
<div
class="access-grant__modal-container__generated-credentials"
>
<span class="access-grant__modal-container__generated-credentials__text">
{{ satelliteAddress }}
</span>
<img
class="clickable-image"
src="../../../../static/images/accessGrants/create-access_copy-icon.png"
alt="copy icon"
@click="onCopyClick(satelliteAddress)"
>
</div>
<div class="access-grant__modal-container__generated-credentials__label">
<span class="access-grant__modal-container__generated-credentials__label__text">
API Key
</span>
<a
href="https://docs.storj.io/dcs/concepts/access/access-grants/api-key/"
target="_blank"
>
<img
class="tooltip-icon"
alt="tooltip icon"
src="../../../../static/images/accessGrants/create-access_information.png"
>
</a>
</div>
<div
class="access-grant__modal-container__generated-credentials"
>
<span class="access-grant__modal-container__generated-credentials__text">
{{ restrictedKey }}
</span>
<img
class="clickable-image"
alt="copy icon"
src="../../../../static/images/accessGrants/create-access_copy-icon.png"
@click="onCopyClick(restrictedKey)"
>
</div>
</div>
<div v-if="checkedType === 's3'" class="access-grant__modal-container__credential-buttons__container-s3">
<a
v-if="checkedType === 's3'"
href="https://docs.storj.io/dcs/api-reference/s3-compatible-gateway/"
target="_blank"
rel="noopener noreferrer"
>
<v-button
label="Learn More"
width="150px"
height="50px"
is-transparent="true"
font-size="16px"
class="access-grant__modal-container__footer-container__learn-more-button"
/>
</a>
<v-button
label="Download .txt"
font-size="16px"
width="182px"
height="50px"
class="access-grant__modal-container__credential-buttons__download-button"
:is-green-white="areCredentialsDownloaded"
:on-press="downloadCredentials"
/>
</div>
<div v-else class="access-grant__modal-container__credential-buttons__container">
<v-button
label="Download .txt"
font-size="16px"
width="182px"
height="50px"
class="access-grant__modal-container__credential-buttons__download-button"
:is-green-white="areCredentialsDownloaded"
:on-press="downloadCredentials"
/>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
import { MetaUtils } from '@/utils/meta';
import { Download } from "@/utils/download";
import AccessGrantsIcon from '@/../static/images/accessGrants/accessGrantsIcon.svg';
import CLIIcon from '@/../static/images/accessGrants/cli.svg';
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
import S3Icon from '@/../static/images/accessGrants/s3.svg';
import VButton from '@/components/common/VButton.vue';
import { EdgeCredentials } from '@/types/accessGrants';
// @vue/component
@Component({
components: {
AccessGrantsIcon,
CLIIcon,
CloseCrossIcon,
S3Icon,
VButton,
},
})
export default class GrantCreatedModal extends Vue {
@Prop({default: 'Default'})
private readonly checkedType: string;
@Prop({default: 'Default'})
private readonly restrictedKey: string;
@Prop({default: 'Default'})
private readonly accessName: string;
@Prop({default: 'Default'})
private readonly access: string;
private areCredentialsDownloaded = false;
private isAccessGrantCopied = false;
/**
* Global isLoading Variable
**/
private isLoading = false;
private checkedText = {access: ['Access Grant as it','information icon to learn more.'], s3: ['S3 credentials as they','Learn More button to access the documentation.'],api: ['Satellite Address and API Key as they','information icons to learn more.']};
public currentDate = new Date().toISOString();
public satelliteAddress: string = MetaUtils.getMetaContent('satellite-nodeurl');
public onCloseClick(): void {
this.$emit('close-modal');
}
public onCopyClick(item): void {
this.$copyText(item);
this.$notify.success(`credential was copied successfully`);
}
/**
* Returns generated gateway credentials from store.
*/
public get gatewayCredentials(): EdgeCredentials {
return this.$store.state.accessGrantsModule.gatewayCredentials;
}
public onCopyAccessGrantClick(): void {
this.$copyText(this.restrictedKey);
this.isAccessGrantCopied = true;
this.$notify.success(`Access Grant was copied successfully`);
}
/**
* Downloads credentials to .txt file
*/
public downloadCredentials(): void {
let credentialMap = {
access: [`access grant: ${this.access}`],
s3: [`access key: ${this.gatewayCredentials.accessKeyId}\nsecret key: ${this.gatewayCredentials.secretKey}\nendpoint: ${this.gatewayCredentials.endpoint}`],
api: [`satellite address: ${this.satelliteAddress}\nrestricted key: ${this.restrictedKey}`]
}
this.areCredentialsDownloaded = true;
Download.file(credentialMap[this.checkedType], `${this.checkedType}-credentials-${this.currentDate}.txt`)
}
/**
* Opens S3 documentation in a new tab
*/
public learnMore(): void{
window.open("https://docs.storj.io/dcs/api-reference/s3-compatible-gateway/", '_blank');
}
}
</script>
<style scoped lang="scss">
.button-icon {
margin-right: 5px;
}
.clickable-image {
cursor: pointer;
}
.tooltip-icon {
display: flex;
width: 14px;
height: 14px;
cursor: pointer;
}
@mixin generated-text {
margin-top: 20px;
align-items: center;
padding: 10px 16px;
background: #ebeef1;
border: 1px solid #c8d3de;
border-radius: 7px;
}
.access-grant {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
background: rgb(27 37 51 / 75%);
display: flex;
align-items: flex-start;
justify-content: center;
& > * {
font-family: sans-serif;
}
&__modal-container {
background: #fff;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: flex-start;
position: relative;
padding: 25px 40px;
margin-top: 40px;
width: 410px;
height: auto;
&__generated-credentials {
@include generated-text;
margin: 0 0 4px;
display: flex;
justify-content: space-between;
&__text {
width: 90%;
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
}
&__label {
display: flex;
margin: 24px 0 8px;
align-items: center;
&.first {
margin-top: 8px;
}
&__text {
font-family: sans-serif;
font-size: 14px;
font-weight: 700;
line-height: 20px;
letter-spacing: 0;
text-align: left;
padding: 0 6px 0 0;
}
}
}
&__credential-buttons {
&__container-s3 {
display: flex;
justify-content: space-between;
margin: 15px 0;
}
&__container {
display: flex;
justify-content: center;
margin: 15px 0;
}
}
&__header-container {
text-align: left;
display: grid;
grid-template-columns: 2fr 1fr;
width: 100%;
padding-top: 10px;
&__title {
grid-column: 1;
}
&__title-complete {
grid-column: 1;
margin-top: 10px;
}
&__close-cross-container {
grid-column: 2;
margin: auto 0 auto auto;
display: flex;
justify-content: center;
align-items: center;
right: 30px;
top: 30px;
height: 24px;
width: 24px;
cursor: pointer;
}
&__close-cross-container:hover .close-cross-svg-path {
fill: #2683ff;
}
}
&__body-container {
display: grid;
grid-template-columns: 1fr 6fr;
grid-template-rows: auto auto auto auto auto auto;
grid-row-gap: 24px;
width: 100%;
padding-top: 10px;
margin-top: 24px;
&__created {
width: 100%;
text-align: left;
display: grid;
font-family: 'font_regular', sans-serif;
font-size: 16px;
margin-top: 15px;
row-gap: 4ch;
padding-top: 10px;
p {
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 20px;
overflow-wrap: break-word;
text-align: left;
}
}
}
&__footer-container {
display: flex;
width: 100%;
justify-content: flex-start;
margin-top: 16px;
& ::v-deep .container:first-of-type {
margin-right: 8px;
}
&__learn-more-button {
padding: 0 15px;
}
&__copy-button {
width: 49% !important;
margin-right: 10px;
}
&__download-button {
width: 49% !important;
}
}
}
}
@media screen and (max-width: 500px) {
.access-grant__modal-container {
width: auto;
max-width: 80vw;
padding: 30px 24px;
&__body-container {
grid-template-columns: 1.2fr 6fr;
}
}
}
</style>

View File

@ -129,7 +129,7 @@ export default class DurationSelection extends Vue {
.access-date-container {
margin-left: 0;
height: 30px;
height: 40px;
border: 1px solid #c8d3de;
}