web/satellite: removed old create access grant flow
Removed old flow and feature flag. Issue: https://github.com/storj/storj/issues/5407 Change-Id: I9dec18eb7d8c7912ead87188789466db5f59a5ca
This commit is contained in:
parent
b2422caaef
commit
af238e2ef9
@ -93,7 +93,6 @@ type Config struct {
|
||||
LinksharingURL string `help:"url link for linksharing requests" default:"https://link.storjshare.io" devDefault:"http://localhost:8001"`
|
||||
PathwayOverviewEnabled bool `help:"indicates if the overview onboarding step should render with pathways" default:"true"`
|
||||
NewProjectDashboard bool `help:"indicates if new project dashboard should be used" default:"true"`
|
||||
NewAccessGrantFlow bool `help:"indicates if new access grant flow should be used" default:"true"`
|
||||
NewBillingScreen bool `help:"indicates if new billing screens should be used" default:"true"`
|
||||
GeneratedAPIEnabled bool `help:"indicates if generated console api should be used" default:"false"`
|
||||
OptionalSignupSuccessURL string `help:"optional url to external registration success page" default:""`
|
||||
@ -467,7 +466,6 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
|
||||
NewProjectDashboard bool
|
||||
DefaultPaidStorageLimit memory.Size
|
||||
DefaultPaidBandwidthLimit memory.Size
|
||||
NewAccessGrantFlow bool
|
||||
NewBillingScreen bool
|
||||
InactivityTimerEnabled bool
|
||||
InactivityTimerDuration int
|
||||
@ -513,7 +511,6 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data.LoginHcaptchaEnabled = server.config.Captcha.Login.Hcaptcha.Enabled
|
||||
data.LoginHcaptchaSiteKey = server.config.Captcha.Login.Hcaptcha.SiteKey
|
||||
data.NewProjectDashboard = server.config.NewProjectDashboard
|
||||
data.NewAccessGrantFlow = server.config.NewAccessGrantFlow
|
||||
data.NewBillingScreen = server.config.NewBillingScreen
|
||||
data.InactivityTimerEnabled = server.config.Session.InactivityTimerEnabled
|
||||
data.InactivityTimerDuration = server.config.Session.InactivityTimerDuration
|
||||
|
3
scripts/testdata/satellite-config.yaml.lock
vendored
3
scripts/testdata/satellite-config.yaml.lock
vendored
@ -238,9 +238,6 @@ compensation.withheld-percents: 75,75,75,50,50,50,25,25,25,0,0,0,0,0,0
|
||||
# indicates if storj native token payments system is enabled
|
||||
# console.native-token-payments-enabled: false
|
||||
|
||||
# indicates if new access grant flow should be used
|
||||
# console.new-access-grant-flow: true
|
||||
|
||||
# indicates if new billing screens should be used
|
||||
# console.new-billing-screen: true
|
||||
|
||||
|
@ -34,7 +34,6 @@
|
||||
<meta name="new-project-dashboard" content="{{ .NewProjectDashboard }}">
|
||||
<meta name="default-paid-storage-limit" content="{{ .DefaultPaidStorageLimit }}">
|
||||
<meta name="default-paid-bandwidth-limit" content="{{ .DefaultPaidBandwidthLimit }}">
|
||||
<meta name="new-access-grant-flow" content="{{ .NewAccessGrantFlow }}">
|
||||
<meta name="new-billing-screen" content="{{ .NewBillingScreen }}">
|
||||
<meta name="inactivity-timer-enabled" content="{{ .InactivityTimerEnabled }}">
|
||||
<meta name="inactivity-timer-duration" content="{{ .InactivityTimerDuration }}">
|
||||
|
@ -1,33 +1,13 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="access-grants">
|
||||
<div v-if="!isNewAccessGrantFlow" class="access-grants__title-area">
|
||||
<h2 class="access-grants__title-area__title" aria-roledescription="title">Access Grants</h2>
|
||||
<div v-if="accessGrantsList.length" class="access-grants__title-area__right">
|
||||
<VButton
|
||||
v-if="selectedAccessGrantsAmount"
|
||||
:label="deleteButtonLabel"
|
||||
width="203px"
|
||||
height="40px"
|
||||
:on-press="onDeleteClick"
|
||||
:is-deletion="true"
|
||||
/>
|
||||
<VButton
|
||||
v-else
|
||||
label="Create Access Grant +"
|
||||
width="203px"
|
||||
height="44px"
|
||||
:on-press="onCreateClick"
|
||||
:is-disabled="areGrantsFetching"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isNewAccessGrantFlow" class="access-grants__new-title-area">
|
||||
<div class="access-grants__new-title-area">
|
||||
<h2 class="access-grants__title-area__title" aria-roledescription="title">Access Management</h2>
|
||||
<div class="access-grants__title-area__title-subtext" aria-roledescription="title">Create encryption keys to setup permissions to access your objects.</div>
|
||||
</div>
|
||||
<div v-if="isNewAccessGrantFlow" class="access-grants__flows-area">
|
||||
<div class="access-grants__flows-area">
|
||||
<div class="access-grants__flows-area__access-grant">
|
||||
<div class="access-grants__flows-area__icon-container">
|
||||
<AccessGrantsIcon />
|
||||
@ -127,115 +107,70 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isNewAccessGrantFlow">
|
||||
<div class="access-grants__header-container">
|
||||
<h3 class="access-grants__header-container__title">My Accesses</h3>
|
||||
<div class="access-grants__header-container__divider" />
|
||||
<VHeader
|
||||
class="access-header-component"
|
||||
placeholder="Accesses"
|
||||
:search="fetch"
|
||||
style-type="access"
|
||||
/>
|
||||
</div>
|
||||
<VLoader v-if="areGrantsFetching" width="100px" height="100px" class="grants-loader" />
|
||||
<div class="access-grants-items2">
|
||||
<v-table
|
||||
v-if="accessGrantsList.length && !areGrantsFetching"
|
||||
class="access-grants-items2__content"
|
||||
items-label="access grants"
|
||||
:limit="accessGrantLimit"
|
||||
:total-page-count="totalPageCount"
|
||||
:total-items-count="accessGrantsTotalCount"
|
||||
:on-page-click-callback="onPageClick"
|
||||
>
|
||||
<template #head>
|
||||
<th class="align-left">Name</th>
|
||||
<th class="align-left">Date Created</th>
|
||||
</template>
|
||||
<template #body>
|
||||
<AccessGrantsItem2
|
||||
v-for="(grant, key) in accessGrantsList"
|
||||
:key="key"
|
||||
:item-data="grant"
|
||||
:dropdown-key="key"
|
||||
:is-dropdown-open="activeDropdown === key"
|
||||
@openDropdown="openDropdown"
|
||||
@deleteClick="onDeleteClick"
|
||||
/>
|
||||
</template>
|
||||
</v-table>
|
||||
<div
|
||||
v-if="!accessGrantsList.length && !areGrantsFetching"
|
||||
class="access-grants-items2__empty-state"
|
||||
>
|
||||
<span class="access-grants-items2__empty-state__text">
|
||||
{{ emptyStateLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!isNewAccessGrantFlow">
|
||||
<VLoader v-if="areGrantsFetching" width="100px" height="100px" class="grants-loader" />
|
||||
<div v-if="accessGrantsList.length && !areGrantsFetching" class="access-grants-items">
|
||||
<v-table
|
||||
v-if="accessGrantsList.length && !areGrantsFetching"
|
||||
class="access-grants-items__content"
|
||||
items-label="access grants"
|
||||
:selectable="true"
|
||||
:limit="accessGrantLimit"
|
||||
:total-page-count="totalPageCount"
|
||||
:total-items-count="accessGrantsTotalCount"
|
||||
:on-page-click-callback="onPageClick"
|
||||
>
|
||||
<template #head>
|
||||
<th class="align-left">Name</th>
|
||||
<th class="align-left">Date Created</th>
|
||||
</template>
|
||||
<template #body>
|
||||
<AccessGrantsItem
|
||||
v-for="(grant, key) in accessGrantsList"
|
||||
:key="key"
|
||||
:item-data="grant"
|
||||
@accessGrantClick="toggleSelection"
|
||||
@selectChange="(_) => toggleSelection(grant)"
|
||||
/>
|
||||
</template>
|
||||
</v-table>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!isNewAccessGrantFlow">
|
||||
<ConfirmDeletePopup
|
||||
v-if="isDeleteClicked"
|
||||
@close="onClearSelection"
|
||||
/>
|
||||
<EmptyState v-if="!accessGrantsList.length && !areGrantsFetching" />
|
||||
</div>
|
||||
<div v-if="isNewAccessGrantFlow">
|
||||
<ConfirmDeletePopup2
|
||||
v-if="isDeleteClicked"
|
||||
@close="onClearSelection"
|
||||
<div class="access-grants__header-container">
|
||||
<h3 class="access-grants__header-container__title">My Accesses</h3>
|
||||
<div class="access-grants__header-container__divider" />
|
||||
<VHeader
|
||||
class="access-header-component"
|
||||
placeholder="Accesses"
|
||||
:search="fetch"
|
||||
style-type="access"
|
||||
/>
|
||||
</div>
|
||||
<VLoader v-if="areGrantsFetching" width="100px" height="100px" class="grants-loader" />
|
||||
<div class="access-grants-items">
|
||||
<v-table
|
||||
v-if="accessGrantsList.length && !areGrantsFetching"
|
||||
class="access-grants-items__content"
|
||||
items-label="access grants"
|
||||
:limit="accessGrantLimit"
|
||||
:total-page-count="totalPageCount"
|
||||
:total-items-count="accessGrantsTotalCount"
|
||||
:on-page-click-callback="onPageClick"
|
||||
>
|
||||
<template #head>
|
||||
<th class="align-left">Name</th>
|
||||
<th class="align-left">Date Created</th>
|
||||
</template>
|
||||
<template #body>
|
||||
<AccessGrantsItem
|
||||
v-for="(grant, key) in accessGrantsList"
|
||||
:key="key"
|
||||
:item-data="grant"
|
||||
:dropdown-key="key"
|
||||
:is-dropdown-open="activeDropdown === key"
|
||||
@openDropdown="openDropdown"
|
||||
@deleteClick="onDeleteClick"
|
||||
/>
|
||||
</template>
|
||||
</v-table>
|
||||
<div
|
||||
v-if="!accessGrantsList.length && !areGrantsFetching"
|
||||
class="access-grants-items__empty-state"
|
||||
>
|
||||
<span class="access-grants-items__empty-state__text">
|
||||
{{ emptyStateLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<ConfirmDeletePopup
|
||||
v-if="isDeleteClicked"
|
||||
@close="onClearSelection"
|
||||
/>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { AccessGrant, AccessGrantsOrderBy } from '@/types/accessGrants';
|
||||
import { SortDirection } from '@/types/common';
|
||||
import { AccessGrant } from '@/types/accessGrants';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import AccessGrantsItem from '@/components/accessGrants/AccessGrantsItem.vue';
|
||||
import AccessGrantsItem2 from '@/components/accessGrants/AccessGrantsItem2.vue';
|
||||
import ConfirmDeletePopup from '@/components/accessGrants/ConfirmDeletePopup.vue';
|
||||
import ConfirmDeletePopup2 from '@/components/accessGrants/ConfirmDeletePopup2.vue';
|
||||
import EmptyState from '@/components/accessGrants/EmptyState.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
import VHeader from '@/components/common/VHeader.vue';
|
||||
@ -249,23 +184,18 @@ const {
|
||||
FETCH,
|
||||
TOGGLE_SELECTION,
|
||||
CLEAR_SELECTION,
|
||||
SET_SORT_BY,
|
||||
SET_SORT_DIRECTION,
|
||||
SET_SEARCH_QUERY,
|
||||
} = ACCESS_GRANTS_ACTIONS;
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
AccessGrantsItem2,
|
||||
AccessGrantsItem,
|
||||
AccessGrantsIcon,
|
||||
CLIIcon,
|
||||
EmptyState,
|
||||
S3Icon,
|
||||
VButton,
|
||||
ConfirmDeletePopup,
|
||||
ConfirmDeletePopup2,
|
||||
VLoader,
|
||||
VHeader,
|
||||
VTable,
|
||||
@ -277,22 +207,9 @@ export default class AccessGrants extends Vue {
|
||||
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Indicates if the access modal should be shown and what the defaulted type of access should be defaulted.
|
||||
*/
|
||||
private showAccessModal = false;
|
||||
private modalAccessType = '';
|
||||
public activeDropdown = -1;
|
||||
|
||||
public areGrantsFetching = true;
|
||||
|
||||
/**
|
||||
* Indicates if navigation side bar is hidden.
|
||||
*/
|
||||
public get isNewAccessGrantFlow(): boolean {
|
||||
const isNewAccessGrantFlow = MetaUtils.getMetaContent('new-access-grant-flow');
|
||||
return isNewAccessGrantFlow === 'true';
|
||||
}
|
||||
/**
|
||||
* Lifecycle hook after initial render where list of existing access grants is fetched.
|
||||
*/
|
||||
@ -304,6 +221,7 @@ export default class AccessGrants extends Vue {
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`, AnalyticsErrorEventSource.ACCESS_GRANTS_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook before component destruction.
|
||||
* Clears existing access grants selection.
|
||||
@ -311,6 +229,7 @@ export default class AccessGrants extends Vue {
|
||||
public beforeDestroy(): void {
|
||||
this.onClearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles access grant selection.
|
||||
* @param accessGrant
|
||||
@ -318,6 +237,7 @@ export default class AccessGrants extends Vue {
|
||||
public async toggleSelection(accessGrant: AccessGrant): Promise<void> {
|
||||
await this.$store.dispatch(TOGGLE_SELECTION, accessGrant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches access grants page by clicked index.
|
||||
* @param index
|
||||
@ -329,28 +249,6 @@ export default class AccessGrants extends Vue {
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`, AnalyticsErrorEventSource.ACCESS_GRANTS_PAGE);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Used for sorting.
|
||||
* @param sortBy
|
||||
* @param sortDirection
|
||||
*/
|
||||
public async onHeaderSectionClickCallback(sortBy: AccessGrantsOrderBy, sortDirection: SortDirection): Promise<void> {
|
||||
await this.$store.dispatch(SET_SORT_BY, sortBy);
|
||||
await this.$store.dispatch(SET_SORT_DIRECTION, sortDirection);
|
||||
try {
|
||||
await this.$store.dispatch(FETCH, this.FIRST_PAGE);
|
||||
} catch (error) {
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`, AnalyticsErrorEventSource.ACCESS_GRANTS_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts create access grant flow.
|
||||
*/
|
||||
public onCreateClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
|
||||
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens AccessGrantItem2 dropdown.
|
||||
@ -372,6 +270,7 @@ export default class AccessGrants extends Vue {
|
||||
await this.$store.dispatch(TOGGLE_SELECTION, grant);
|
||||
this.isDeleteClicked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears access grants selection.
|
||||
*/
|
||||
@ -379,6 +278,7 @@ export default class AccessGrants extends Vue {
|
||||
this.isDeleteClicked = false;
|
||||
await this.$store.dispatch(CLEAR_SELECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches Access records by name depending on search query.
|
||||
*/
|
||||
@ -391,9 +291,7 @@ export default class AccessGrants extends Vue {
|
||||
await this.$notify.error(`Unable to fetch accesses: ${error.message}`, AnalyticsErrorEventSource.ACCESS_GRANTS_PAGE);
|
||||
}
|
||||
}
|
||||
public get deleteButtonLabel(): string {
|
||||
return `Remove Selected (${this.selectedAccessGrantsAmount})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns access grants pages count from store.
|
||||
*/
|
||||
@ -422,13 +320,6 @@ export default class AccessGrants extends Vue {
|
||||
return this.$store.state.accessGrantsModule.page.accessGrants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns selected access grants IDs amount from store.
|
||||
*/
|
||||
public get selectedAccessGrantsAmount(): number {
|
||||
return this.$store.state.accessGrantsModule.selectedAccessGrantsIds.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns search query from store.
|
||||
*/
|
||||
@ -606,13 +497,6 @@ export default class AccessGrants extends Vue {
|
||||
&__content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.access-grants-items2 {
|
||||
|
||||
&__content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&__empty-state {
|
||||
padding: 48px 0;
|
||||
|
@ -3,29 +3,117 @@
|
||||
|
||||
<template>
|
||||
<table-item
|
||||
:item="{ name: itemData.name, date: itemData.localDate() }"
|
||||
:selectable="true"
|
||||
:selected="itemData.isSelected"
|
||||
:on-click="(_) => $emit('accessGrantClick', itemData)"
|
||||
@selectChange="(value) => $emit('selectChange', value)"
|
||||
/>
|
||||
:item="itemToRender"
|
||||
:on-click="onClick"
|
||||
>
|
||||
<th slot="options" v-click-outside="closeDropdown" class="grant-item__functional options overflow-visible" @click.stop="openDropdown">
|
||||
<dots-icon />
|
||||
<div v-if="isDropdownOpen" class="grant-item__functional__dropdown">
|
||||
<div class="grant-item__functional__dropdown__item" @click.stop="onDeleteClick">
|
||||
<delete-icon />
|
||||
<p class="grant-item__functional__dropdown__item__label">Delete Access</p>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</table-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { Component, Prop } from 'vue-property-decorator';
|
||||
|
||||
import DeleteIcon from '../../../static/images/objects/delete.svg';
|
||||
import DotsIcon from '../../../static/images/objects/dots.svg';
|
||||
|
||||
import { AccessGrant } from '@/types/accessGrants';
|
||||
|
||||
import Resizable from '@/components/common/Resizable.vue';
|
||||
import TableItem from '@/components/common/TableItem.vue';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
TableItem,
|
||||
DeleteIcon,
|
||||
DotsIcon,
|
||||
},
|
||||
})
|
||||
export default class AccessGrantsItem extends Vue {
|
||||
export default class AccessGrantsItem extends Resizable {
|
||||
@Prop({ default: new AccessGrant('', '', new Date(), '') })
|
||||
private readonly itemData: AccessGrant;
|
||||
@Prop({ default: () => () => {} })
|
||||
public readonly onClick: () => void;
|
||||
@Prop({ default: false })
|
||||
public readonly isDropdownOpen: boolean;
|
||||
@Prop({ default: -1 })
|
||||
public readonly dropdownKey: number;
|
||||
|
||||
public get itemToRender(): { [key: string]: string | string[] } {
|
||||
if (!this.isMobile) return { name: this.itemData.name, date: this.itemData.localDate() };
|
||||
|
||||
return { info: [ this.itemData.name, `Created ${this.itemData.localDate()}` ] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes dropdown.
|
||||
*/
|
||||
public closeDropdown(): void {
|
||||
this.$emit('openDropdown', -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens dropdown.
|
||||
*/
|
||||
public openDropdown(): void {
|
||||
this.$emit('openDropdown', this.dropdownKey);
|
||||
}
|
||||
|
||||
public async onDeleteClick(): Promise<void> {
|
||||
this.$emit('deleteClick', this.itemData);
|
||||
this.closeDropdown();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.grant-item {
|
||||
|
||||
&__functional {
|
||||
padding: 0 10px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&__dropdown {
|
||||
position: absolute;
|
||||
top: 55px;
|
||||
right: 15px;
|
||||
background: #fff;
|
||||
box-shadow: 0 20px 34px rgb(10 27 44 / 28%);
|
||||
border-radius: 6px;
|
||||
width: 255px;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px 25px;
|
||||
width: calc(100% - 50px);
|
||||
|
||||
&__label {
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f4f5f7;
|
||||
font-family: 'font_medium', sans-serif;
|
||||
color: var(--c-blue-3);
|
||||
|
||||
svg :deep(path) {
|
||||
fill: var(--c-blue-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,119 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<table-item
|
||||
:item="itemToRender"
|
||||
:on-click="onClick"
|
||||
>
|
||||
<th slot="options" v-click-outside="closeDropdown" class="grant-item__functional options overflow-visible" @click.stop="openDropdown">
|
||||
<dots-icon />
|
||||
<div v-if="isDropdownOpen" class="grant-item__functional__dropdown">
|
||||
<div class="grant-item__functional__dropdown__item" @click.stop="onDeleteClick">
|
||||
<delete-icon />
|
||||
<p class="grant-item__functional__dropdown__item__label">Delete Access</p>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</table-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop } from 'vue-property-decorator';
|
||||
|
||||
import DeleteIcon from '../../../static/images/objects/delete.svg';
|
||||
import DotsIcon from '../../../static/images/objects/dots.svg';
|
||||
|
||||
import { AccessGrant } from '@/types/accessGrants';
|
||||
|
||||
import Resizable from '@/components/common/Resizable.vue';
|
||||
import TableItem from '@/components/common/TableItem.vue';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
TableItem,
|
||||
DeleteIcon,
|
||||
DotsIcon,
|
||||
},
|
||||
})
|
||||
export default class AccessGrantsItem extends Resizable {
|
||||
@Prop({ default: new AccessGrant('', '', new Date(), '') })
|
||||
private readonly itemData: AccessGrant;
|
||||
@Prop({ default: () => () => {} })
|
||||
public readonly onClick: () => void;
|
||||
@Prop({ default: false })
|
||||
public readonly isDropdownOpen: boolean;
|
||||
@Prop({ default: -1 })
|
||||
public readonly dropdownKey: number;
|
||||
|
||||
public get itemToRender(): { [key: string]: string | string[] } {
|
||||
if (!this.isMobile) return { name: this.itemData.name, date: this.itemData.localDate() };
|
||||
|
||||
return { info: [ this.itemData.name, `Created ${this.itemData.localDate()}` ] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes dropdown.
|
||||
*/
|
||||
public closeDropdown(): void {
|
||||
this.$emit('openDropdown', -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens dropdown.
|
||||
*/
|
||||
public openDropdown(): void {
|
||||
this.$emit('openDropdown', this.dropdownKey);
|
||||
}
|
||||
|
||||
public async onDeleteClick(): Promise<void> {
|
||||
this.$emit('deleteClick', this.itemData);
|
||||
this.closeDropdown();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.grant-item {
|
||||
|
||||
&__functional {
|
||||
padding: 0 10px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&__dropdown {
|
||||
position: absolute;
|
||||
top: 55px;
|
||||
right: 15px;
|
||||
background: #fff;
|
||||
box-shadow: 0 20px 34px rgb(10 27 44 / 28%);
|
||||
border-radius: 6px;
|
||||
width: 255px;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px 25px;
|
||||
width: calc(100% - 50px);
|
||||
|
||||
&__label {
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f4f5f7;
|
||||
font-family: 'font_medium', sans-serif;
|
||||
color: var(--c-blue-3);
|
||||
|
||||
svg :deep(path) {
|
||||
fill: var(--c-blue-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,15 +1,12 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// Copyright (C) 2022 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="confirm-delete">
|
||||
<div v-if="!isNewAccessGrantFlow" class="confirm-delete__container">
|
||||
<h1 class="confirm-delete__container__title">Are you sure?</h1>
|
||||
<div class="confirm-delete__container">
|
||||
<h1 class="confirm-delete__container__title">Delete Access</h1>
|
||||
<p class="confirm-delete__container__info">
|
||||
When an access grant is removed, users using it will no longer have access to the buckets or data.
|
||||
</p>
|
||||
<p class="confirm-delete__container__list-label">
|
||||
The following access grant(s) will be removed from this project:
|
||||
You won’t be able to access bucket(s) or object(s) related to the access:
|
||||
</p>
|
||||
<div class="confirm-delete__container__list">
|
||||
<div
|
||||
@ -24,53 +21,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p>This action cannot be undone.</p>
|
||||
</div>
|
||||
<VInput
|
||||
placeholder="Type the name of the access"
|
||||
@setData="setConfirmedInput"
|
||||
/>
|
||||
<div class="confirm-delete__container__buttons-area">
|
||||
<VButton
|
||||
class="cancel-button"
|
||||
label="Cancel"
|
||||
width="50%"
|
||||
height="44px"
|
||||
:on-press="onCancelClick"
|
||||
:is-white="true"
|
||||
:is-disabled="isLoading"
|
||||
/>
|
||||
<VButton
|
||||
label="Remove"
|
||||
width="50%"
|
||||
height="44px"
|
||||
:on-press="onDeleteClick"
|
||||
:is-disabled="isLoading"
|
||||
/>
|
||||
</div>
|
||||
<div class="confirm-delete__container__close-cross-container" @click="onCancelClick">
|
||||
<CloseCrossIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isNewAccessGrantFlow" class="confirm-delete__container">
|
||||
<div class="confirm-delete__text-container">
|
||||
<h1 class="confirm-delete__container__title">Delete Access</h1>
|
||||
<p class="confirm-delete__container__info-new">
|
||||
You wont be able to access bucket(s) or object(s) related to this access. This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
<div class="confirm-delete__container__list">
|
||||
<div
|
||||
v-for="accessGrant in selectedAccessGrants"
|
||||
:key="accessGrant.id"
|
||||
class="confirm-delete__container__list__container"
|
||||
>
|
||||
<div class="confirm-delete__container__list__container__item">
|
||||
<p class="confirm-delete__container__list__container__item__name">
|
||||
{{ accessGrant.name }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="confirm-delete__container__buttons-area">
|
||||
<VButton
|
||||
class="cancel-button"
|
||||
label="Cancel"
|
||||
width="50%"
|
||||
width="70px"
|
||||
height="44px"
|
||||
:on-press="onCancelClick"
|
||||
:is-white="true"
|
||||
@ -78,13 +40,13 @@
|
||||
/>
|
||||
<VButton
|
||||
label="Delete Access"
|
||||
width="50%"
|
||||
width="150px"
|
||||
height="44px"
|
||||
:is-solid-delete="true"
|
||||
:on-press="onDeleteClick"
|
||||
:is-disabled="isLoading"
|
||||
:is-disabled="isLoading || confirmedInput !== selectedAccessGrants[0].name"
|
||||
:is-solid-delete="true"
|
||||
has-trash-icon="true"
|
||||
/>
|
||||
<TrashIcon class="confirm-delete__trash-icon" />
|
||||
</div>
|
||||
<div class="confirm-delete__container__close-cross-container" @click="onCancelClick">
|
||||
<CloseCrossIcon />
|
||||
@ -96,27 +58,35 @@
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { AccessGrant } from '@/types/accessGrants';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
|
||||
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
|
||||
import TrashIcon from '@/../static/images/accessGrants/trashIcon.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VButton,
|
||||
VInput,
|
||||
CloseCrossIcon,
|
||||
TrashIcon,
|
||||
},
|
||||
})
|
||||
export default class ConfirmDeletePopup extends Vue {
|
||||
private FIRST_PAGE = 1;
|
||||
private isLoading = false;
|
||||
private confirmedInput = '';
|
||||
|
||||
private readonly FIRST_PAGE: number = 1;
|
||||
|
||||
/**
|
||||
* sets Comfirmed Input property to the given value.
|
||||
*/
|
||||
public setConfirmedInput(value: string): void {
|
||||
this.confirmedInput = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes selected access grants, fetches updated list and closes popup.
|
||||
@ -140,7 +110,7 @@ export default class ConfirmDeletePopup extends Vue {
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`, AnalyticsErrorEventSource.CONFIRM_DELETE_AG_MODAL);
|
||||
}
|
||||
|
||||
this.$emit('reset-pagination');
|
||||
this.$emit('resetPagination');
|
||||
this.isLoading = false;
|
||||
this.onCancelClick();
|
||||
}
|
||||
@ -151,13 +121,7 @@ export default class ConfirmDeletePopup extends Vue {
|
||||
public onCancelClick(): void {
|
||||
this.$emit('close');
|
||||
}
|
||||
/**
|
||||
* Checks for new access grant flag
|
||||
*/
|
||||
public get isNewAccessGrantFlow(): boolean {
|
||||
const isNewAccessGrantFlow = MetaUtils.getMetaContent('new-access-grant-flow');
|
||||
return isNewAccessGrantFlow === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of selected access grants from store.
|
||||
*/
|
||||
@ -194,12 +158,11 @@ export default class ConfirmDeletePopup extends Vue {
|
||||
|
||||
&__container {
|
||||
border-radius: 6px;
|
||||
max-width: 475px;
|
||||
padding: 50px 65px;
|
||||
max-width: 325px;
|
||||
padding: 40px 30px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
|
||||
&__title {
|
||||
@ -214,9 +177,8 @@ export default class ConfirmDeletePopup extends Vue {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
margin: 20px 0;
|
||||
margin: 25px 0 10px;
|
||||
}
|
||||
|
||||
&__info-new {
|
||||
@ -247,17 +209,18 @@ export default class ConfirmDeletePopup extends Vue {
|
||||
&__container {
|
||||
|
||||
&__item {
|
||||
padding: 25px;
|
||||
width: calc(100% - 50px);
|
||||
max-width: calc(100% - 50px);
|
||||
background: rgb(245 246 250 / 60%);
|
||||
padding: 3px 7px;
|
||||
max-width: fit-content;
|
||||
background: var(--c-grey-3);
|
||||
border-radius: 20px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__name {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
font-size: 17px;
|
||||
line-height: 30px;
|
||||
color: #1b2533;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -268,7 +231,7 @@ export default class ConfirmDeletePopup extends Vue {
|
||||
}
|
||||
|
||||
&__buttons-area {
|
||||
width: 100%;
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 30px;
|
||||
|
@ -1,261 +0,0 @@
|
||||
// Copyright (C) 2022 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="confirm-delete">
|
||||
<div class="confirm-delete__container">
|
||||
<h1 class="confirm-delete__container__title">Delete Access</h1>
|
||||
<p class="confirm-delete__container__info">
|
||||
You won’t be able to access bucket(s) or object(s) related to the access:
|
||||
</p>
|
||||
<div class="confirm-delete__container__list">
|
||||
<div
|
||||
v-for="accessGrant in selectedAccessGrants"
|
||||
:key="accessGrant.id"
|
||||
class="confirm-delete__container__list__container"
|
||||
>
|
||||
<div class="confirm-delete__container__list__container__item">
|
||||
<p class="confirm-delete__container__list__container__item__name">
|
||||
{{ accessGrant.name }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p>This action cannot be undone.</p>
|
||||
</div>
|
||||
<VInput
|
||||
placeholder="Type the name of the access"
|
||||
@setData="setConfirmedInput"
|
||||
/>
|
||||
<div class="confirm-delete__container__buttons-area">
|
||||
<VButton
|
||||
class="cancel-button"
|
||||
label="Cancel"
|
||||
width="70px"
|
||||
height="44px"
|
||||
:on-press="onCancelClick"
|
||||
:is-white="true"
|
||||
:is-disabled="isLoading"
|
||||
/>
|
||||
<VButton
|
||||
label="Delete Access"
|
||||
width="150px"
|
||||
height="44px"
|
||||
:on-press="onDeleteClick"
|
||||
:is-disabled="isLoading || confirmedInput !== selectedAccessGrants[0].name"
|
||||
:is-solid-delete="true"
|
||||
has-trash-icon="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="confirm-delete__container__close-cross-container" @click="onCancelClick">
|
||||
<CloseCrossIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { AccessGrant } from '@/types/accessGrants';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
|
||||
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VButton,
|
||||
VInput,
|
||||
CloseCrossIcon,
|
||||
},
|
||||
})
|
||||
export default class ConfirmDeletePopup extends Vue {
|
||||
private isLoading = false;
|
||||
private confirmedInput = '';
|
||||
|
||||
private readonly FIRST_PAGE: number = 1;
|
||||
|
||||
/**
|
||||
* sets Comfirmed Input property to the given value.
|
||||
*/
|
||||
public setConfirmedInput(value: string): void {
|
||||
this.confirmedInput = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes selected access grants, fetches updated list and closes popup.
|
||||
*/
|
||||
public async onDeleteClick(): Promise<void> {
|
||||
if (this.isLoading) return;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.DELETE);
|
||||
await this.$notify.success(`Access Grant deleted successfully`);
|
||||
} catch (error) {
|
||||
await this.$notify.error(error.message, AnalyticsErrorEventSource.CONFIRM_DELETE_AG_MODAL);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.FETCH, this.FIRST_PAGE);
|
||||
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR_SELECTION);
|
||||
} catch (error) {
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`, AnalyticsErrorEventSource.CONFIRM_DELETE_AG_MODAL);
|
||||
}
|
||||
|
||||
this.$emit('resetPagination');
|
||||
this.isLoading = false;
|
||||
this.onCancelClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes popup
|
||||
*/
|
||||
public onCancelClick(): void {
|
||||
this.$emit('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of selected access grants from store.
|
||||
*/
|
||||
public get selectedAccessGrants(): AccessGrant[] {
|
||||
return this.$store.getters.selectedAccessGrants;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.confirm-delete {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
background: rgb(27 37 51 / 75%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
|
||||
&__trash-icon {
|
||||
position: absolute;
|
||||
left: 57%;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
&__text-container {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__container {
|
||||
border-radius: 6px;
|
||||
max-width: 325px;
|
||||
padding: 40px 30px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 28px;
|
||||
line-height: 34px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #000;
|
||||
margin: 25px 0 10px;
|
||||
}
|
||||
|
||||
&__info-new {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
&__list-label {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: #e30011;
|
||||
font-family: 'font_medium', sans-serif;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
&__list {
|
||||
max-height: 255px;
|
||||
overflow-y: scroll;
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
|
||||
&__container {
|
||||
|
||||
&__item {
|
||||
padding: 3px 7px;
|
||||
max-width: fit-content;
|
||||
background: var(--c-grey-3);
|
||||
border-radius: 20px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__name {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
font-size: 17px;
|
||||
line-height: 30px;
|
||||
color: #1b2533;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons-area {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
&__close-cross-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: 30px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover .close-cross-svg-path {
|
||||
fill: #2683ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
margin-right: 15px;
|
||||
}
|
||||
</style>
|
@ -1,149 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="create-grant">
|
||||
<div class="create-grant__container">
|
||||
<ProgressBar v-if="!isProgressBarHidden" />
|
||||
<router-view />
|
||||
<div class="create-grant__container__close-cross-container" @click="onCloseClick">
|
||||
<CloseCrossIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import ProgressBar from '@/components/accessGrants/ProgressBar.vue';
|
||||
|
||||
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
ProgressBar,
|
||||
CloseCrossIcon,
|
||||
},
|
||||
})
|
||||
export default class CreateAccessGrant extends Vue {
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Closes popup.
|
||||
*/
|
||||
public onCloseClick(): void {
|
||||
this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR_SELECTION);
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
|
||||
this.$router.push(RouteConfig.AccessGrants.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if progress bar is hidden.
|
||||
*/
|
||||
public get isProgressBarHidden(): boolean {
|
||||
return this.$route.name === RouteConfig.CLIStep.name;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar-track,
|
||||
::-webkit-scrollbar-thumb {
|
||||
margin: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.create-grant {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: rgb(27 37 51 / 75%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&__container {
|
||||
background: #f5f6fa;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
|
||||
&__close-cross-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: 30px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover .close-cross-svg-path {
|
||||
fill: #2683ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 800px) {
|
||||
|
||||
.create-grant {
|
||||
padding: 50px 0 20px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 750px) {
|
||||
|
||||
.create-grant {
|
||||
padding: 100px 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 700px) {
|
||||
|
||||
.create-grant {
|
||||
padding: 150px 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 650px) {
|
||||
|
||||
.create-grant {
|
||||
padding: 200px 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 600px) {
|
||||
|
||||
.create-grant {
|
||||
padding: 250px 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 550px) {
|
||||
|
||||
.create-grant {
|
||||
padding: 300px 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 500px) {
|
||||
|
||||
.create-grant {
|
||||
padding: 350px 0 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,89 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="empty-state">
|
||||
<div class="empty-state__modal">
|
||||
<Key />
|
||||
<h4 class="empty-state__modal__heading">Create Your First Access Grant</h4>
|
||||
<p class="empty-state__modal__subheading">Get started by creating an Access to interact with your Buckets</p>
|
||||
<VButton
|
||||
label="Create Access Grant +"
|
||||
width="199px"
|
||||
height="44px"
|
||||
class="empty-state__modal__cta"
|
||||
:on-press="onCreateClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import Key from '@/../static/images/accessGrants/key.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
Key,
|
||||
VButton,
|
||||
},
|
||||
})
|
||||
export default class EmptyState extends Vue {
|
||||
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Starts create access grant flow.
|
||||
*/
|
||||
public onCreateClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
|
||||
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.empty-state {
|
||||
background-image: url('../../../static/images/accessGrants/access-grants-bg.png');
|
||||
background-size: contain;
|
||||
margin-top: 40px;
|
||||
|
||||
&__modal {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 660px;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
padding: 100px 30px;
|
||||
position: relative;
|
||||
top: 110px;
|
||||
|
||||
&__heading {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
line-height: 16px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
&__subheading {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
&__cta {
|
||||
margin: 25px auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,116 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-bar__item">
|
||||
<div class="progress-bar__item__line" :class="{ blue: isNameStep }" />
|
||||
<p class="progress-bar__item__label" :class="{ checked: isNameStep }">Name Access</p>
|
||||
</div>
|
||||
<div class="progress-bar__item">
|
||||
<div class="progress-bar__item__line" :class="{ blue: isPermissionsStep }" />
|
||||
<p class="progress-bar__item__label" :class="{ checked: isPermissionsStep }">Permissions</p>
|
||||
</div>
|
||||
<div class="progress-bar__item">
|
||||
<div class="progress-bar__item__line" :class="{ blue: isPassphraseStep }" />
|
||||
<p class="progress-bar__item__label" :class="{ checked: isPassphraseStep }">Passphrase</p>
|
||||
</div>
|
||||
<div class="progress-bar__item">
|
||||
<div class="progress-bar__item__line" :class="{ blue: isResultStep }" />
|
||||
<p class="progress-bar__item__label" :class="{ checked: isResultStep }">Access Grant</p>
|
||||
</div>
|
||||
<div v-if="isGatewayStep" class="progress-bar__item">
|
||||
<div class="progress-bar__item__line" :class="{ blue: isGatewayStep }" />
|
||||
<p class="progress-bar__item__label" :class="{ checked: isGatewayStep }">S3 Gateway</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
|
||||
// @vue/component
|
||||
@Component
|
||||
export default class ProgressBar extends Vue {
|
||||
/**
|
||||
* Indicates if current route is on name step.
|
||||
*/
|
||||
public get isNameStep(): boolean {
|
||||
return this.$route.name === RouteConfig.NameStep.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is on permissions step.
|
||||
*/
|
||||
public get isPermissionsStep(): boolean {
|
||||
return this.$route.name === RouteConfig.PermissionsStep.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is on passphrase step.
|
||||
*/
|
||||
public get isPassphraseStep(): boolean {
|
||||
return this.$route.name === RouteConfig.CreatePassphraseStep.name || this.$route.name === RouteConfig.EnterPassphraseStep.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is on result step.
|
||||
*/
|
||||
public get isResultStep(): boolean {
|
||||
return this.$route.name === RouteConfig.ResultStep.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is on gateway step.
|
||||
*/
|
||||
public get isGatewayStep(): boolean {
|
||||
return this.$route.name === RouteConfig.GatewayStep.name;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.progress-bar {
|
||||
padding: 55px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
background: #f5f6fa;
|
||||
border-radius: 6px 0 0 6px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__line {
|
||||
width: 7px;
|
||||
height: 75px;
|
||||
border-radius: 40px;
|
||||
background: #dcdde1;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
line-height: 15px;
|
||||
color: rgb(0 0 0 / 40%);
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checked {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.blue {
|
||||
background: #0068dc;
|
||||
}
|
||||
</style>
|
@ -1,119 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="sort-header-container">
|
||||
<div class="sort-header-container__name-item" @click="onHeaderItemClick(AccessGrantsOrderBy.NAME)">
|
||||
<p class="sort-header-container__name-item__title">NAME</p>
|
||||
<VerticalArrows
|
||||
:is-active="areAccessGrantsSortedByName"
|
||||
:direction="getSortDirection"
|
||||
/>
|
||||
</div>
|
||||
<div class="sort-header-container__date-item" @click="onHeaderItemClick(AccessGrantsOrderBy.CREATED_AT)">
|
||||
<p class="sort-header-container__date-item__title creation-date">DATE CREATED</p>
|
||||
<VerticalArrows
|
||||
:is-active="!areAccessGrantsSortedByName"
|
||||
:direction="getSortDirection"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { AccessGrantsOrderBy, OnHeaderClickCallback } from '@/types/accessGrants';
|
||||
import { SortDirection } from '@/types/common';
|
||||
|
||||
import VerticalArrows from '@/components/common/VerticalArrows.vue';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VerticalArrows,
|
||||
},
|
||||
})
|
||||
export default class SortAccessGrantsHeader extends Vue {
|
||||
@Prop({ default: () => () => new Promise(() => false) })
|
||||
private readonly onHeaderClickCallback: OnHeaderClickCallback;
|
||||
|
||||
public AccessGrantsOrderBy = AccessGrantsOrderBy;
|
||||
|
||||
public sortBy: AccessGrantsOrderBy = AccessGrantsOrderBy.NAME;
|
||||
public sortDirection: SortDirection = SortDirection.ASCENDING;
|
||||
|
||||
/**
|
||||
* Used for arrow styling.
|
||||
*/
|
||||
public get getSortDirection(): SortDirection {
|
||||
return this.sortDirection === SortDirection.DESCENDING ? SortDirection.ASCENDING : SortDirection.DESCENDING;
|
||||
}
|
||||
|
||||
public get areAccessGrantsSortedByName(): boolean {
|
||||
return this.sortBy === AccessGrantsOrderBy.NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sorting kind if different from current.
|
||||
* If same, changes sort direction.
|
||||
* @param sortBy
|
||||
*/
|
||||
public async onHeaderItemClick(sortBy: AccessGrantsOrderBy): Promise<void> {
|
||||
if (this.sortBy !== sortBy) {
|
||||
this.sortBy = sortBy;
|
||||
this.sortDirection = SortDirection.ASCENDING;
|
||||
|
||||
await this.onHeaderClickCallback(this.sortBy, this.sortDirection);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.sortDirection = this.sortDirection === SortDirection.DESCENDING ?
|
||||
SortDirection.ASCENDING
|
||||
: SortDirection.DESCENDING;
|
||||
|
||||
await this.onHeaderClickCallback(this.sortBy, this.sortDirection);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sort-header-container {
|
||||
display: flex;
|
||||
width: calc(100% - 32px);
|
||||
height: 40px;
|
||||
background-color: #fff;
|
||||
margin-top: 31px;
|
||||
padding: 16px 16px 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
|
||||
&__name-item,
|
||||
&__date-item {
|
||||
width: 60%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 16px;
|
||||
margin: 0 0 0 23px;
|
||||
color: #2a2a32;
|
||||
}
|
||||
|
||||
.creation-date {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__date-item {
|
||||
width: 40%;
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,120 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="sort-header-container">
|
||||
<div class="sort-header-container__name-item" @click="onHeaderItemClick(AccessGrantsOrderBy.NAME)">
|
||||
<p class="sort-header-container__name-item__title">NAME</p>
|
||||
<VerticalArrows
|
||||
:is-active="areAccessGrantsSortedByName"
|
||||
:direction="getSortDirection"
|
||||
/>
|
||||
</div>
|
||||
<div class="sort-header-container__date-item" @click="onHeaderItemClick(AccessGrantsOrderBy.CREATED_AT)">
|
||||
<p class="sort-header-container__date-item__title creation-date">DATE CREATED</p>
|
||||
<VerticalArrows
|
||||
:is-active="!areAccessGrantsSortedByName"
|
||||
:direction="getSortDirection"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { AccessGrantsOrderBy, OnHeaderClickCallback } from '@/types/accessGrants';
|
||||
import { SortDirection } from '@/types/common';
|
||||
|
||||
import VerticalArrows from '@/components/common/VerticalArrows.vue';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VerticalArrows,
|
||||
},
|
||||
})
|
||||
export default class SortAccessGrantsHeader extends Vue {
|
||||
@Prop({ default: () => new Promise(() => false) })
|
||||
private readonly onHeaderClickCallback: OnHeaderClickCallback;
|
||||
|
||||
public AccessGrantsOrderBy = AccessGrantsOrderBy;
|
||||
|
||||
public sortBy: AccessGrantsOrderBy = AccessGrantsOrderBy.NAME;
|
||||
public sortDirection: SortDirection = SortDirection.ASCENDING;
|
||||
|
||||
/**
|
||||
* Used for arrow styling.
|
||||
*/
|
||||
public get getSortDirection(): SortDirection {
|
||||
return this.sortDirection === SortDirection.DESCENDING ? SortDirection.ASCENDING : SortDirection.DESCENDING;
|
||||
}
|
||||
|
||||
public get areAccessGrantsSortedByName(): boolean {
|
||||
return this.sortBy === AccessGrantsOrderBy.NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sorting kind if different from current.
|
||||
* If same, changes sort direction.
|
||||
* @param sortBy
|
||||
*/
|
||||
public async onHeaderItemClick(sortBy: AccessGrantsOrderBy): Promise<void> {
|
||||
if (this.sortBy !== sortBy) {
|
||||
this.sortBy = sortBy;
|
||||
this.sortDirection = SortDirection.ASCENDING;
|
||||
|
||||
await this.onHeaderClickCallback(this.sortBy, this.sortDirection);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.sortDirection = this.sortDirection === SortDirection.DESCENDING ?
|
||||
SortDirection.ASCENDING
|
||||
: SortDirection.DESCENDING;
|
||||
|
||||
await this.onHeaderClickCallback(this.sortBy, this.sortDirection);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sort-header-container {
|
||||
display: flex;
|
||||
width: calc(100% - 32px);
|
||||
height: 40px;
|
||||
background-color: #fff;
|
||||
margin-top: 31px;
|
||||
padding: 16px 16px 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-bottom: 0;
|
||||
|
||||
&__name-item,
|
||||
&__date-item {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 16px;
|
||||
margin: 0 0 0 23px;
|
||||
color: #2a2a32;
|
||||
}
|
||||
|
||||
.creation-date {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__date-item {
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,257 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="cli-container" :class="{ 'border-radius': isOnboardingTour }">
|
||||
<BackIcon class="cli-container__back-icon" @click="onBackClick" />
|
||||
<h1 class="cli-container__title">Create Access Grant in CLI</h1>
|
||||
<p class="cli-container__sub-title">
|
||||
Run the 'setup' command in the uplink CLI and input the satellite address and token below when prompted to generate your access grant.
|
||||
</p>
|
||||
<div class="cli-container__token-area">
|
||||
<p class="cli-container__token-area__label">Satellite Address</p>
|
||||
<div class="cli-container__token-area__container">
|
||||
<p ref="addressContainer" class="cli-container__token-area__container__token" @click="selectAddress">{{ satelliteAddress }}</p>
|
||||
<VButton
|
||||
class="cli-container__token-area__container__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
:on-press="onCopyAddressClick"
|
||||
/>
|
||||
</div>
|
||||
<p class="cli-container__token-area__label">API Key</p>
|
||||
<div class="cli-container__token-area__container">
|
||||
<p class="cli-container__token-area__container__token">{{ restrictedKey }}</p>
|
||||
<VButton
|
||||
class="cli-container__token-area__container__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
:on-press="onCopyTokenClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<VButton
|
||||
class="cli-container__done-button"
|
||||
label="Done"
|
||||
width="100%"
|
||||
height="48px"
|
||||
:on-press="onDoneClick"
|
||||
/>
|
||||
<a
|
||||
class="cli-container__docs-link"
|
||||
href="https://docs.storj.io/getting-started/generate-access-grants-and-tokens/generate-a-token"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Visit the Docs
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import BackIcon from '@/../static/images/accessGrants/back.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
BackIcon,
|
||||
VButton,
|
||||
},
|
||||
})
|
||||
export default class CLIStep extends Vue {
|
||||
public key = '';
|
||||
public restrictedKey = '';
|
||||
public satelliteAddress: string = MetaUtils.getMetaContent('satellite-nodeurl');
|
||||
|
||||
public $refs!: {
|
||||
addressContainer: HTMLElement;
|
||||
};
|
||||
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Sets local key from props value.
|
||||
*/
|
||||
public mounted(): void {
|
||||
if (!this.$route.params.key && !this.$route.params.restrictedKey) {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.key = this.$route.params.key;
|
||||
this.restrictedKey = this.$route.params.restrictedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on back button click logic.
|
||||
* Redirects to previous step.
|
||||
*/
|
||||
public onBackClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).path);
|
||||
this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on done button click logic.
|
||||
* Redirects to upload step.
|
||||
*/
|
||||
public onDoneClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
|
||||
this.isOnboardingTour ? this.$router.push(RouteConfig.ProjectDashboard.path) : this.$router.push(RouteConfig.AccessGrants.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds selecting address logic for click event.
|
||||
*/
|
||||
public selectAddress(): void {
|
||||
const range: Range = document.createRange();
|
||||
const selection: Selection | null = window.getSelection();
|
||||
|
||||
range.selectNodeContents(this.$refs.addressContainer);
|
||||
|
||||
if (selection) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy button click logic.
|
||||
* Copies satellite address to clipboard.
|
||||
*/
|
||||
public onCopyAddressClick(): void {
|
||||
this.$copyText(this.satelliteAddress);
|
||||
this.$notify.success('Satellite address was copied successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy button click logic.
|
||||
* Copies token to clipboard.
|
||||
*/
|
||||
public onCopyTokenClick(): void {
|
||||
this.$copyText(this.restrictedKey);
|
||||
this.$notify.success('Token was copied successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is onboarding tour.
|
||||
*/
|
||||
public get isOnboardingTour(): boolean {
|
||||
return this.$route.path.includes(RouteConfig.OnboardingTour.path);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.cli-container {
|
||||
height: calc(100% - 60px);
|
||||
max-width: 485px;
|
||||
padding: 30px 65px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
|
||||
&__back-icon {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
&__sub-title {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
margin: 0 0 50px;
|
||||
}
|
||||
|
||||
&__token-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
margin-bottom: 30px;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #354049;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
|
||||
&__container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 10px 10px 20px;
|
||||
width: calc(100% - 30px);
|
||||
border: 1px solid rgb(56 75 101 / 40%);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&__token {
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #384b65;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__button {
|
||||
min-width: 66px;
|
||||
min-height: 30px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__docs-link {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 23px;
|
||||
color: #0068dc;
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.border-radius {
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
@ -1,500 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="create-passphrase">
|
||||
<BackIcon class="create-passphrase__back-icon" @click="onBackClick" />
|
||||
<div class="create-passphrase__container">
|
||||
<h1 class="create-passphrase__container__title">Encryption Passphrase</h1>
|
||||
<div class="create-passphrase__container__choosing">
|
||||
<p class="create-passphrase__container__choosing__label">Passphrase</p>
|
||||
<div class="create-passphrase__container__choosing__right">
|
||||
<p
|
||||
class="create-passphrase__container__choosing__right__option left-option"
|
||||
:class="{ active: isGenerateState }"
|
||||
@click="onChooseGenerate"
|
||||
>
|
||||
Generate Phrase
|
||||
</p>
|
||||
<p
|
||||
class="create-passphrase__container__choosing__right__option"
|
||||
:class="{ active: isEnterState }"
|
||||
@click="onChooseCreate"
|
||||
>
|
||||
Enter Phrase
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isEnterState" class="create-passphrase__container__enter-passphrase-box">
|
||||
<div class="create-passphrase__container__enter-passphrase-box__header">
|
||||
<GreenWarningIcon />
|
||||
<h2 class="create-passphrase__container__enter-passphrase-box__header__label">Enter an Existing Passphrase</h2>
|
||||
</div>
|
||||
<p class="create-passphrase__container__enter-passphrase-box__message">
|
||||
if you already have an encryption passphrase, enter your encryption passphrase here.
|
||||
</p>
|
||||
</div>
|
||||
<div class="create-passphrase__container__value-area">
|
||||
<div v-if="isGenerateState" class="create-passphrase__container__value-area__mnemonic">
|
||||
<p class="create-passphrase__container__value-area__mnemonic__value">{{ passphrase }}</p>
|
||||
<VButton
|
||||
class="create-passphrase__container__value-area__mnemonic__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
:on-press="onCopyClick"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="create-passphrase__container__value-area__password">
|
||||
<VInput
|
||||
placeholder="Enter encryption passphrase here"
|
||||
:error="errorMessage"
|
||||
@setData="onChangePassphrase"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isGenerateState" class="create-passphrase__container__warning">
|
||||
<h2 class="create-passphrase__container__warning__title">Save Your Encryption Passphrase</h2>
|
||||
<p class="create-passphrase__container__warning__message">
|
||||
You’ll need this passphrase to access data in the future. This is the only time it will be displayed.
|
||||
Be sure to write it down.
|
||||
</p>
|
||||
<label class="create-passphrase__container__warning__check-area" :class="{ error: isError }" for="pass-checkbox">
|
||||
<input
|
||||
id="pass-checkbox"
|
||||
v-model="isChecked"
|
||||
class="create-passphrase__container__warning__check-area__checkbox"
|
||||
type="checkbox"
|
||||
@change="isError = false"
|
||||
>
|
||||
Yes, I wrote this down or saved it somewhere.
|
||||
</label>
|
||||
</div>
|
||||
<VButton
|
||||
class="create-passphrase__container__next-button"
|
||||
label="Next"
|
||||
width="100%"
|
||||
height="48px"
|
||||
:on-press="onNextClick"
|
||||
:is-disabled="isButtonDisabled"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { generateMnemonic } from 'bip39';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
|
||||
import BackIcon from '@/../static/images/accessGrants/back.svg';
|
||||
import GreenWarningIcon from '@/../static/images/accessGrants/greenWarning.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
BackIcon,
|
||||
GreenWarningIcon,
|
||||
VButton,
|
||||
VInput,
|
||||
},
|
||||
})
|
||||
export default class CreatePassphraseStep extends Vue {
|
||||
private key = '';
|
||||
private restrictedKey = '';
|
||||
private access = '';
|
||||
private worker: Worker;
|
||||
private isLoading = true;
|
||||
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
public isGenerateState = true;
|
||||
public isEnterState = false;
|
||||
public isChecked = false;
|
||||
public isError = false;
|
||||
public passphrase = '';
|
||||
public errorMessage = '';
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Sets local key from props value.
|
||||
*/
|
||||
public async mounted(): Promise<void> {
|
||||
if (!this.$route.params.key && !this.$route.params.restrictedKey) {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
await this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.key = this.$route.params.key;
|
||||
this.restrictedKey = this.$route.params.restrictedKey;
|
||||
|
||||
this.setWorker();
|
||||
|
||||
this.passphrase = generateMnemonic();
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets local worker with worker instantiated in store.
|
||||
* Also sets worker's onmessage and onerror logic.
|
||||
*/
|
||||
public setWorker(): void {
|
||||
this.worker = this.$store.state.accessGrantsModule.accessGrantsWebWorker;
|
||||
this.worker.onerror = (error: ErrorEvent) => {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
this.$notify.error(error.message, null);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets passphrase from child component.
|
||||
*/
|
||||
public setPassphrase(passphrase: string): void {
|
||||
this.passphrase = passphrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on next button click logic.
|
||||
* Generates access grant and redirects to next step.
|
||||
*/
|
||||
public async onNextClick(): Promise<void> {
|
||||
if (!this.passphrase) {
|
||||
this.errorMessage = 'Passphrase can\'t be empty';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isChecked && this.isGenerateState) {
|
||||
this.isError = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isLoading) return;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
await this.analytics.eventTriggered(AnalyticsEvent.PASSPHRASE_CREATED);
|
||||
|
||||
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
|
||||
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
|
||||
|
||||
this.worker.postMessage({
|
||||
'type': 'GenerateAccess',
|
||||
'apiKey': this.restrictedKey,
|
||||
'passphrase': this.passphrase,
|
||||
'salt': salt,
|
||||
'satelliteNodeURL': satelliteNodeURL,
|
||||
});
|
||||
|
||||
const accessEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
if (accessEvent.data.error) {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
await this.$notify.error(accessEvent.data.error, null);
|
||||
this.isLoading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.access = accessEvent.data.value;
|
||||
await this.$notify.success('Access Grant was generated successfully');
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).path);
|
||||
await this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).name,
|
||||
params: {
|
||||
access: this.access,
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes state to generate passphrase.
|
||||
*/
|
||||
public onChooseGenerate(): void {
|
||||
if (this.passphrase && this.isGenerateState) return;
|
||||
|
||||
this.passphrase = generateMnemonic();
|
||||
|
||||
this.isEnterState = false;
|
||||
this.isGenerateState = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes state to create passphrase.
|
||||
*/
|
||||
public onChooseCreate(): void {
|
||||
if (this.passphrase && this.isEnterState) return;
|
||||
|
||||
this.errorMessage = '';
|
||||
this.passphrase = '';
|
||||
|
||||
this.isEnterState = true;
|
||||
this.isGenerateState = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy button click logic.
|
||||
* Copies passphrase to clipboard.
|
||||
*/
|
||||
public onCopyClick(): void {
|
||||
this.$copyText(this.passphrase);
|
||||
this.$notify.success('Passphrase was copied successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes passphrase data from input value.
|
||||
* @param value
|
||||
*/
|
||||
public onChangePassphrase(value: string): void {
|
||||
this.passphrase = value.trim();
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if button is disabled.
|
||||
*/
|
||||
public get isButtonDisabled(): boolean {
|
||||
return this.isLoading || !this.passphrase || (!this.isChecked && this.isGenerateState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on back button click logic.
|
||||
* Redirects to previous step.
|
||||
*/
|
||||
public onBackClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).path);
|
||||
this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is onboarding tour.
|
||||
*/
|
||||
private get isOnboardingTour(): boolean {
|
||||
return this.$route.path.includes(RouteConfig.OnboardingTour.path);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.create-passphrase {
|
||||
position: relative;
|
||||
|
||||
&__back-icon {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 65px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__container {
|
||||
padding: 25px 50px;
|
||||
max-width: 515px;
|
||||
min-width: 515px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0 0 30px;
|
||||
}
|
||||
|
||||
&__enter-passphrase-box {
|
||||
padding: 20px;
|
||||
background: #f9fffc;
|
||||
border: 1px solid #1a9666;
|
||||
border-radius: 9px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__warning {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
width: calc(100% - 40px);
|
||||
margin: 35px 0;
|
||||
background: #fff;
|
||||
border: 1px solid #e6e9ef;
|
||||
border-radius: 9px;
|
||||
|
||||
&__title {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
margin: 0 0 0 15px;
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
margin: 10px 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__check-area {
|
||||
margin-top: 27px;
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&__checkbox {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__choosing {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-bottom: 25px;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #354049;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__option {
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
color: #768394;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
border-bottom: 3px solid #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__value-area {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
&__mnemonic {
|
||||
display: flex;
|
||||
background: #f5f6fa;
|
||||
border-radius: 9px;
|
||||
padding: 10px;
|
||||
width: calc(100% - 20px);
|
||||
|
||||
&__value {
|
||||
font-family: 'Source Code Pro', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #384b65;
|
||||
word-break: break-word;
|
||||
margin: 0;
|
||||
word-spacing: 8px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-left: 10px;
|
||||
min-width: 66px;
|
||||
min-height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&__password {
|
||||
width: 100%;
|
||||
margin: 10px 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left-option {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.active {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
color: #0068dc;
|
||||
border-bottom: 3px solid #0068dc;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
:deep(.label-container__main) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
:deep(.label-container__main__label) {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: #7c8794;
|
||||
font-family: 'font_bold', sans-serif;
|
||||
}
|
||||
|
||||
:deep(.label-container__main__error) {
|
||||
margin: 0 0 0 10px;
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
}
|
||||
</style>
|
@ -1,215 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="enter-passphrase">
|
||||
<BackIcon class="enter-passphrase__back-icon" @click="onBackClick" />
|
||||
<h1 class="enter-passphrase__title">Enter Encryption Passphrase</h1>
|
||||
<p class="enter-passphrase__sub-title">Enter the passphrase you most recently generated for Access Grants</p>
|
||||
<VInput
|
||||
label="Encryption Passphrase"
|
||||
placeholder="Enter your passphrase here"
|
||||
:error="errorMessage"
|
||||
@setData="onChangePassphrase"
|
||||
/>
|
||||
<VButton
|
||||
class="enter-passphrase__next-button"
|
||||
label="Next"
|
||||
width="100%"
|
||||
height="48px"
|
||||
:on-press="onNextClick"
|
||||
:is-disabled="isLoading"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
|
||||
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import BackIcon from '@/../static/images/accessGrants/back.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VInput,
|
||||
VButton,
|
||||
BackIcon,
|
||||
},
|
||||
})
|
||||
export default class EnterPassphraseStep extends Vue {
|
||||
private key = '';
|
||||
private restrictedKey = '';
|
||||
private access = '';
|
||||
private worker: Worker;
|
||||
private isLoading = true;
|
||||
|
||||
public passphrase = '';
|
||||
public errorMessage = '';
|
||||
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Sets local key from props value.
|
||||
*/
|
||||
public async mounted(): Promise<void> {
|
||||
if (!this.$route.params.key && !this.$route.params.restrictedKey) {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
await this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.key = this.$route.params.key;
|
||||
this.restrictedKey = this.$route.params.restrictedKey;
|
||||
|
||||
this.setWorker();
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes passphrase data from input value.
|
||||
* @param value
|
||||
*/
|
||||
public onChangePassphrase(value: string): void {
|
||||
this.passphrase = value.trim();
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on next button click logic.
|
||||
* Generates access grant and redirects to next step.
|
||||
*/
|
||||
public async onNextClick(): Promise<void> {
|
||||
if (!this.passphrase) {
|
||||
this.errorMessage = 'Passphrase can`t be empty';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
const satelliteNodeURL = MetaUtils.getMetaContent('satellite-nodeurl');
|
||||
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
|
||||
|
||||
this.worker.postMessage({
|
||||
'type': 'GenerateAccess',
|
||||
'apiKey': this.restrictedKey,
|
||||
'passphrase': this.passphrase,
|
||||
'salt': salt,
|
||||
'satelliteNodeURL': satelliteNodeURL,
|
||||
});
|
||||
|
||||
const accessEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
if (accessEvent.data.error) {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
await this.$notify.error(accessEvent.data.error, null);
|
||||
this.isLoading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.access = accessEvent.data.value;
|
||||
await this.$notify.success('Access Grant was generated successfully');
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).path);
|
||||
await this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).name,
|
||||
params: {
|
||||
access: this.access,
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets local worker with worker instantiated in store.
|
||||
* Also sets worker's onmessage and onerror logic.
|
||||
*/
|
||||
public setWorker(): void {
|
||||
this.worker = this.$store.state.accessGrantsModule.accessGrantsWebWorker;
|
||||
this.worker.onerror = (error: ErrorEvent) => {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
this.$notify.error(error.message, null);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on back button click logic.
|
||||
* Redirects to previous step.
|
||||
*/
|
||||
public onBackClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).path);
|
||||
this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.enter-passphrase {
|
||||
height: calc(100% - 60px);
|
||||
padding: 30px 65px;
|
||||
max-width: 475px;
|
||||
min-width: 475px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
border-radius: 0 6px 6px 0;
|
||||
|
||||
&__back-icon {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 65px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0 0 30px;
|
||||
}
|
||||
|
||||
&__sub-title {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
margin: 0 0 75px;
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
&__next-button {
|
||||
margin-top: 93px;
|
||||
}
|
||||
}
|
||||
|
||||
.border-radius {
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,383 +0,0 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="gateway" :class="{ 'border-radius': isOnboardingTour }">
|
||||
<BackIcon class="gateway__back-icon" @click="onBackClick" />
|
||||
<h1 class="gateway__title">S3 Gateway</h1>
|
||||
<div class="gateway__container">
|
||||
<h3 class="gateway__container__title">
|
||||
Generate S3 Gateway Credentials
|
||||
</h3>
|
||||
<p class="gateway__container__disclaimer">
|
||||
By generating gateway credentials, you are opting in to Server-side encryption
|
||||
</p>
|
||||
<VButton
|
||||
v-if="!areKeysVisible"
|
||||
class="gateway__container__button"
|
||||
label="Generate Credentials"
|
||||
width="calc(100% - 4px)"
|
||||
height="48px"
|
||||
:is-blue-white="true"
|
||||
:on-press="onGenerateCredentialsClick"
|
||||
:is-disabled="isLoading"
|
||||
/>
|
||||
<div v-else class="gateway__container__keys-area">
|
||||
<div class="gateway__container__keys-area__label-area">
|
||||
<h3 class="gateway__container__keys-area__label-area__label">Access Key</h3>
|
||||
<VInfo class="gateway__container__keys-area__label-area__info-button">
|
||||
<template #icon>
|
||||
<InfoIcon />
|
||||
</template>
|
||||
<template #message>
|
||||
<p class="gateway__container__keys-area__label-area__info-button__message">
|
||||
The access key ID uniquely identifies your account.
|
||||
</p>
|
||||
</template>
|
||||
</VInfo>
|
||||
</div>
|
||||
<div class="gateway__container__keys-area__key">
|
||||
<p class="gateway__container__keys-area__key__value">{{ gatewayCredentials.accessKeyId }}</p>
|
||||
<VButton
|
||||
class="gateway__container__keys-area__key__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
:on-press="onCopyAccessClick"
|
||||
/>
|
||||
</div>
|
||||
<div class="gateway__container__keys-area__label-area">
|
||||
<h3 class="gateway__container__keys-area__label-area__label">Secret Key</h3>
|
||||
<VInfo class="gateway__container__keys-area__label-area__info-button">
|
||||
<template #icon>
|
||||
<InfoIcon />
|
||||
</template>
|
||||
<template #message>
|
||||
<p class="gateway__container__keys-area__label-area__info-button__message">
|
||||
Secret access keys are—as the name implies—secrets, like your password.
|
||||
</p>
|
||||
</template>
|
||||
</VInfo>
|
||||
</div>
|
||||
<div class="gateway__container__keys-area__key">
|
||||
<p class="gateway__container__keys-area__key__value">{{ gatewayCredentials.secretKey }}</p>
|
||||
<VButton
|
||||
class="gateway__container__keys-area__key__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
:on-press="onCopySecretClick"
|
||||
/>
|
||||
</div>
|
||||
<div class="gateway__container__keys-area__label-area">
|
||||
<h3 class="gateway__container__keys-area__label-area__label">End Point</h3>
|
||||
<VInfo class="gateway__container__keys-area__label-area__info-button">
|
||||
<template #icon>
|
||||
<InfoIcon />
|
||||
</template>
|
||||
<template #message>
|
||||
<p class="gateway__container__keys-area__label-area__info-button__message">
|
||||
The service to which you want to establish the connection.
|
||||
</p>
|
||||
</template>
|
||||
</VInfo>
|
||||
</div>
|
||||
<div class="gateway__container__keys-area__key">
|
||||
<p class="gateway__container__keys-area__key__value">{{ gatewayCredentials.endpoint }}</p>
|
||||
<VButton
|
||||
class="gateway__container__keys-area__key__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
:on-press="onCopyEndpointClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<VButton
|
||||
label="Done"
|
||||
width="100%"
|
||||
height="48px"
|
||||
:on-press="onDoneClick"
|
||||
:is-disabled="!gatewayCredentials.accessKeyId"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { EdgeCredentials } from '@/types/accessGrants';
|
||||
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import VInfo from '@/components/common/VInfo.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import InfoIcon from '@/../static/images/accessGrants/info.svg';
|
||||
import BackIcon from '@/../static/images/accessGrants/back.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VButton,
|
||||
VInfo,
|
||||
BackIcon,
|
||||
InfoIcon,
|
||||
},
|
||||
})
|
||||
export default class GatewayStep extends Vue {
|
||||
private key = '';
|
||||
private restrictedKey = '';
|
||||
private access = '';
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
public areKeysVisible = false;
|
||||
public isLoading = false;
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Sets local access from props value.
|
||||
*/
|
||||
public mounted(): void {
|
||||
if (!this.$route.params.access && !this.$route.params.key && !this.$route.params.resctrictedKey) {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.access = this.$route.params.access;
|
||||
this.key = this.$route.params.key;
|
||||
this.restrictedKey = this.$route.params.restrictedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy access key button click logic.
|
||||
* Copies key to clipboard.
|
||||
*/
|
||||
public onCopyAccessClick(): void {
|
||||
this.$copyText(this.gatewayCredentials.accessKeyId);
|
||||
this.$notify.success('Key was copied successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy secret key button click logic.
|
||||
* Copies secret to clipboard.
|
||||
*/
|
||||
public onCopySecretClick(): void {
|
||||
this.$copyText(this.gatewayCredentials.secretKey);
|
||||
this.$notify.success('Secret was copied successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy endpoint button click logic.
|
||||
* Copies endpoint to clipboard.
|
||||
*/
|
||||
public onCopyEndpointClick(): void {
|
||||
this.$copyText(this.gatewayCredentials.endpoint);
|
||||
this.$notify.success('Endpoint was copied successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on back button click logic.
|
||||
* Redirects to previous step.
|
||||
*/
|
||||
public onBackClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).path);
|
||||
this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).name,
|
||||
params: {
|
||||
access: this.access,
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on done button click logic.
|
||||
* Proceed to upload data step.
|
||||
*/
|
||||
public onDoneClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
|
||||
this.isOnboardingTour ? this.$router.push(RouteConfig.ProjectDashboard.path) : this.$router.push(RouteConfig.AccessGrants.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on generate credentials button click logic.
|
||||
* Generates gateway credentials.
|
||||
*/
|
||||
public async onGenerateCredentialsClick(): Promise<void> {
|
||||
if (this.isLoading) return;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.GET_GATEWAY_CREDENTIALS, { accessGrant: this.access });
|
||||
|
||||
await this.$notify.success('Gateway credentials were generated successfully');
|
||||
|
||||
await this.analytics.eventTriggered(AnalyticsEvent.GATEWAY_CREDENTIALS_CREATED);
|
||||
|
||||
this.areKeysVisible = true;
|
||||
} catch (error) {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
await this.$notify.error(error.message, null);
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns generated gateway credentials from store.
|
||||
*/
|
||||
public get gatewayCredentials(): EdgeCredentials {
|
||||
return this.$store.state.accessGrantsModule.gatewayCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is onboarding tour.
|
||||
*/
|
||||
public get isOnboardingTour(): boolean {
|
||||
return this.$route.path.includes(RouteConfig.OnboardingTour.path);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.gateway {
|
||||
height: calc(100% - 60px);
|
||||
padding: 30px 65px;
|
||||
max-width: 475px;
|
||||
min-width: 475px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
border-radius: 0 6px 6px 0;
|
||||
|
||||
&__back-icon {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 65px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__container {
|
||||
background: #f5f6fa;
|
||||
border-radius: 6px;
|
||||
padding: 50px;
|
||||
margin: 55px 0 40px;
|
||||
width: calc(100% - 100px);
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0 0 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__disclaimer {
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
color: #000;
|
||||
margin: 0 0 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__keys-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
&__label-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 20px 0 10px;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #354049;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__info-button {
|
||||
max-height: 20px;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
|
||||
&:hover {
|
||||
|
||||
.ag-info-rect {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.ag-info-path {
|
||||
fill: #2683ff;
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
color: #586c86;
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__key {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-radius: 9px;
|
||||
padding: 10px;
|
||||
width: calc(100% - 20px);
|
||||
max-width: calc(100% - 20px);
|
||||
border: 1px solid rgb(56 75 101 / 40%);
|
||||
background-color: #fff;
|
||||
|
||||
&__value {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__button {
|
||||
min-width: 66px;
|
||||
min-height: 30px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.border-radius {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
:deep(.info__box__message) {
|
||||
min-width: 300px;
|
||||
}
|
||||
</style>
|
@ -1,201 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="name-step" :class="{ 'border-radius': isOnboardingTour }">
|
||||
<h1 class="name-step__title" aria-roledescription="name-ag-title">Name Your Access Grant</h1>
|
||||
<p class="name-step__sub-title">Enter a name for your new Access grant to get started.</p>
|
||||
<VInput
|
||||
label="Access Grant Name"
|
||||
placeholder="Enter a name here..."
|
||||
:error="errorMessage"
|
||||
@setData="onChangeName"
|
||||
/>
|
||||
<div class="name-step__buttons-area">
|
||||
<VButton
|
||||
v-if="!isOnboardingTour"
|
||||
class="cancel-button"
|
||||
label="Cancel"
|
||||
width="50%"
|
||||
height="48px"
|
||||
:on-press="onCancelClick"
|
||||
:is-white="true"
|
||||
:is-disabled="isLoading"
|
||||
/>
|
||||
<VButton
|
||||
label="Next"
|
||||
width="50%"
|
||||
height="48px"
|
||||
:on-press="onNextClick"
|
||||
:is-disabled="isLoading"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
|
||||
import { AccessGrant } from '@/types/accessGrants';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VInput,
|
||||
VButton,
|
||||
},
|
||||
})
|
||||
export default class NameStep extends Vue {
|
||||
private name = '';
|
||||
private errorMessage = '';
|
||||
private isLoading = false;
|
||||
private key = '';
|
||||
|
||||
private readonly FIRST_PAGE = 1;
|
||||
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Changes name data from input value.
|
||||
* @param value
|
||||
*/
|
||||
public onChangeName(value: string): void {
|
||||
this.name = value.trim();
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on cancel button click logic.
|
||||
*/
|
||||
public onCancelClick(): void {
|
||||
this.onChangeName('');
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
|
||||
this.$router.push(RouteConfig.AccessGrants.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on next button click logic.
|
||||
* Creates AccessGrant common entity.
|
||||
*/
|
||||
public async onNextClick(): Promise<void> {
|
||||
if (this.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.name) {
|
||||
this.errorMessage = 'Access Grant name can\'t be empty';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
// Check if at least one project exists.
|
||||
// Used like backwards compatibility for the old accounts without any project.
|
||||
if (this.$store.getters.projects.length === 0) {
|
||||
try {
|
||||
await this.$store.dispatch(PROJECTS_ACTIONS.CREATE_DEFAULT_PROJECT);
|
||||
} catch (error) {
|
||||
this.isLoading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let createdAccessGrant: AccessGrant;
|
||||
try {
|
||||
createdAccessGrant = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CREATE, this.name);
|
||||
} catch (error) {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
await this.$notify.error(error.message, null);
|
||||
this.isLoading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.key = createdAccessGrant.secret;
|
||||
this.name = '';
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.FETCH, this.FIRST_PAGE);
|
||||
} catch (error) {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`, null);
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).path);
|
||||
await this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is onboarding tour.
|
||||
*/
|
||||
public get isOnboardingTour(): boolean {
|
||||
return this.$route.path.includes(RouteConfig.OnboardingTour.path);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.name-step {
|
||||
height: calc(100% - 60px);
|
||||
padding: 30px 65px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
border-radius: 0 6px 6px 0;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
&__sub-title {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
margin: 0 0 80px;
|
||||
}
|
||||
|
||||
&__buttons-area {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.border-radius {
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
@ -1,434 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="permissions" :class="{ 'border-radius': isOnboardingTour }">
|
||||
<BackIcon class="permissions__back-icon" @click="onBackClick" />
|
||||
<h1 class="permissions__title">Access Permissions</h1>
|
||||
<p class="permissions__sub-title">
|
||||
Assign permissions to this Access Grant.
|
||||
</p>
|
||||
<div class="permissions__content">
|
||||
<div class="permissions__content__left">
|
||||
<div class="permissions__content__left__item">
|
||||
<input id="download" v-model="isDownload" type="checkbox" name="download" :checked="isDownload">
|
||||
<label class="permissions__content__left__item__label" for="download">Download</label>
|
||||
</div>
|
||||
<div class="permissions__content__left__item">
|
||||
<input id="upload" v-model="isUpload" type="checkbox" name="upload" :checked="isUpload">
|
||||
<label class="permissions__content__left__item__label" for="upload">Upload</label>
|
||||
</div>
|
||||
<div class="permissions__content__left__item">
|
||||
<input id="list" v-model="isList" type="checkbox" name="list" :checked="isList">
|
||||
<label class="permissions__content__left__item__label" for="list">List</label>
|
||||
</div>
|
||||
<div class="permissions__content__left__item">
|
||||
<input id="delete" v-model="isDelete" type="checkbox" name="delete" :checked="isDelete">
|
||||
<label class="permissions__content__left__item__label" for="delete">Delete</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="permissions__content__right">
|
||||
<div class="permissions__content__right__duration-select">
|
||||
<p class="permissions__content__right__duration-select__label">Duration</p>
|
||||
<DurationSelection />
|
||||
</div>
|
||||
<div class="permissions__content__right__buckets-select">
|
||||
<p class="permissions__content__right__buckets-select__label">Buckets</p>
|
||||
<VLoader v-if="areBucketNamesFetching" width="50px" height="50px" />
|
||||
<BucketsSelection v-else />
|
||||
</div>
|
||||
<div class="permissions__content__right__bucket-bullets">
|
||||
<div
|
||||
v-for="(name, index) in selectedBucketNames"
|
||||
:key="index"
|
||||
class="permissions__content__right__bucket-bullets__container"
|
||||
>
|
||||
<BucketNameBullet :name="name" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<VButton
|
||||
class="permissions__button"
|
||||
label="Continue in Browser"
|
||||
width="100%"
|
||||
height="48px"
|
||||
:on-press="onContinueInBrowserClick"
|
||||
:is-disabled="isLoading || !isAccessGrantsWebWorkerReady || areBucketNamesFetching"
|
||||
/>
|
||||
<p
|
||||
class="permissions__cli-link"
|
||||
:class="{ disabled: !isAccessGrantsWebWorkerReady || isLoading || areBucketNamesFetching }"
|
||||
@click.stop="onContinueInCLIClick"
|
||||
>
|
||||
Continue in CLI
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
|
||||
import { DurationPermission } from '@/types/accessGrants';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import BucketNameBullet from '@/components/accessGrants/permissions/BucketNameBullet.vue';
|
||||
import BucketsSelection from '@/components/accessGrants/permissions/BucketsSelection.vue';
|
||||
import DurationSelection from '@/components/accessGrants/permissions/DurationSelection.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
|
||||
import BackIcon from '@/../static/images/accessGrants/back.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
BackIcon,
|
||||
BucketsSelection,
|
||||
BucketNameBullet,
|
||||
DurationSelection,
|
||||
VButton,
|
||||
VLoader,
|
||||
},
|
||||
})
|
||||
export default class PermissionsStep extends Vue {
|
||||
private key = '';
|
||||
private restrictedKey = '';
|
||||
private worker: Worker;
|
||||
|
||||
public isLoading = true;
|
||||
public isDownload = true;
|
||||
public isUpload = true;
|
||||
public isList = true;
|
||||
public isDelete = true;
|
||||
public areBucketNamesFetching = true;
|
||||
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Sets local key from props value.
|
||||
* Initializes web worker's onmessage functionality.
|
||||
*/
|
||||
public async mounted(): Promise<void> {
|
||||
if (!this.$route.params.key) {
|
||||
this.onBackClick();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.key = this.$route.params.key;
|
||||
|
||||
this.setWorker();
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(BUCKET_ACTIONS.FETCH_ALL_BUCKET_NAMES);
|
||||
|
||||
this.areBucketNamesFetching = false;
|
||||
} catch (error) {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
await this.$notify.error(`Unable to fetch all bucket names. ${error.message}`, null);
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on back button click logic.
|
||||
* Redirects to previous step.
|
||||
*/
|
||||
public onBackClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets local worker with worker instantiated in store.
|
||||
* Also sets worker's onmessage and onerror logic.
|
||||
*/
|
||||
public setWorker(): void {
|
||||
this.worker = this.$store.state.accessGrantsModule.accessGrantsWebWorker;
|
||||
this.worker.onerror = (error: ErrorEvent) => {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
this.$notify.error(error.message, null);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on continue in CLI button click logic.
|
||||
*/
|
||||
public async onContinueInCLIClick(): Promise<void> {
|
||||
if (this.isLoading || !this.isAccessGrantsWebWorkerReady) return;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
try {
|
||||
await this.setPermissions();
|
||||
} catch (error) {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
await this.$notify.error(error.message, null);
|
||||
this.isLoading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CLIStep)).path);
|
||||
await this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CLIStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on continue in browser button click logic.
|
||||
*/
|
||||
public async onContinueInBrowserClick(): Promise<void> {
|
||||
if (this.isLoading || !this.isAccessGrantsWebWorkerReady) return;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
try {
|
||||
await this.setPermissions();
|
||||
} catch (error) {
|
||||
// we pass null because we don't use this flow anymore. It will be removed entirely soon.
|
||||
await this.$notify.error(error.message, null);
|
||||
this.isLoading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
if (this.accessGrantsAmount > 1) {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.EnterPassphraseStep)).path);
|
||||
await this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.EnterPassphraseStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CreatePassphraseStep)).path);
|
||||
await this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CreatePassphraseStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if access grants web worker ready to use.
|
||||
*/
|
||||
public get isAccessGrantsWebWorkerReady(): boolean {
|
||||
return this.$store.state.accessGrantsModule.isAccessGrantsWebWorkerReady;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns selected bucket names.
|
||||
*/
|
||||
public get selectedBucketNames(): string[] {
|
||||
return this.$store.state.accessGrantsModule.selectedBucketNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is onboarding tour.
|
||||
*/
|
||||
public get isOnboardingTour(): boolean {
|
||||
return this.$route.path.includes(RouteConfig.OnboardingTour.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets chosen permissions for API Key.
|
||||
*/
|
||||
private async setPermissions(): Promise<void> {
|
||||
let permissionsMsg = {
|
||||
'type': 'SetPermission',
|
||||
'buckets': this.selectedBucketNames,
|
||||
'apiKey': this.key,
|
||||
'isDownload': this.isDownload,
|
||||
'isUpload': this.isUpload,
|
||||
'isList': this.isList,
|
||||
'isDelete': this.isDelete,
|
||||
};
|
||||
|
||||
if (this.notBeforePermission) permissionsMsg = Object.assign(permissionsMsg, { 'notBefore': this.notBeforePermission.toISOString() });
|
||||
if (this.notAfterPermission) permissionsMsg = Object.assign(permissionsMsg, { 'notAfter': this.notAfterPermission.toISOString() });
|
||||
|
||||
await this.worker.postMessage(permissionsMsg);
|
||||
|
||||
const keyEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve);
|
||||
if (keyEvent.data.error) {
|
||||
throw new Error(keyEvent.data.error);
|
||||
}
|
||||
|
||||
this.restrictedKey = keyEvent.data.value;
|
||||
await this.$notify.success('Permissions were set successfully');
|
||||
|
||||
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR_SELECTION);
|
||||
await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.SET_DURATION_PERMISSION, new DurationPermission());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns amount of access grants from store.
|
||||
*/
|
||||
private get accessGrantsAmount(): number {
|
||||
return this.$store.state.accessGrantsModule.page.accessGrants.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns not before date permission from store.
|
||||
*/
|
||||
private get notBeforePermission(): Date | null {
|
||||
return this.$store.state.accessGrantsModule.permissionNotBefore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns not after date permission from store.
|
||||
*/
|
||||
private get notAfterPermission(): Date | null {
|
||||
return this.$store.state.accessGrantsModule.permissionNotAfter;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar-track,
|
||||
::-webkit-scrollbar-thumb {
|
||||
margin: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.permissions {
|
||||
height: calc(100% - 60px);
|
||||
width: calc(100% - 130px);
|
||||
padding: 30px 65px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
border-radius: 0 6px 6px 0;
|
||||
|
||||
&__back-icon {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 65px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
&__sub-title {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
margin: 0 0 70px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
&__left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&__label {
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__right {
|
||||
width: 100%;
|
||||
margin-left: 100px;
|
||||
|
||||
&__buckets-select,
|
||||
&__duration-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #354049;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__duration-select {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
&__bucket-bullets {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin: 15px 0 0 85px;
|
||||
max-height: 100px;
|
||||
max-width: 235px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
&__cli-link {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 23px;
|
||||
color: #0068dc;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.border-radius {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
color: rgb(0 0 0 / 40%);
|
||||
}
|
||||
</style>
|
@ -1,308 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="generate-grant" :class="{ 'border-radius': isOnboardingTour }">
|
||||
<BackIcon class="generate-grant__back-icon" @click="onBackClick" />
|
||||
<h1 class="generate-grant__title">Generate Access Grant</h1>
|
||||
<div class="generate-grant__warning">
|
||||
<div class="generate-grant__warning__header">
|
||||
<WarningIcon />
|
||||
<h2 class="generate-grant__warning__header__label">This Information is Only Displayed Once</h2>
|
||||
</div>
|
||||
<p class="generate-grant__warning__message">
|
||||
Save this information in a password manager, or wherever you prefer to store sensitive information.
|
||||
</p>
|
||||
</div>
|
||||
<div class="generate-grant__grant-area">
|
||||
<h3 class="generate-grant__grant-area__label">Access Grant</h3>
|
||||
<div class="generate-grant__grant-area__container">
|
||||
<p class="generate-grant__grant-area__container__value">{{ access }}</p>
|
||||
<VButton
|
||||
class="generate-grant__grant-area__container__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
:on-press="onCopyGrantClick"
|
||||
/>
|
||||
<VButton
|
||||
class="generate-grant__grant-area__container__button"
|
||||
label="Download"
|
||||
width="80px"
|
||||
height="30px"
|
||||
:on-press="onDownloadGrantClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<VButton
|
||||
class="generate-grant__done-button"
|
||||
label="Done"
|
||||
width="100%"
|
||||
height="48px"
|
||||
:on-press="onDoneClick"
|
||||
/>
|
||||
<p v-if="isGatewayLinkVisible" class="generate-grant__gateway-link" @click="navigateToGatewayStep">
|
||||
Generate S3 Gateway Credentials
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
import { Download } from '@/utils/download';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import WarningIcon from '@/../static/images/accessGrants/warning.svg';
|
||||
import BackIcon from '@/../static/images/accessGrants/back.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
BackIcon,
|
||||
WarningIcon,
|
||||
VButton,
|
||||
},
|
||||
})
|
||||
export default class ResultStep extends Vue {
|
||||
private key = '';
|
||||
private restrictedKey = '';
|
||||
|
||||
public access = '';
|
||||
public isGatewayLinkVisible = false;
|
||||
|
||||
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Sets local access from props value.
|
||||
*/
|
||||
public mounted(): void {
|
||||
if (!this.$route.params.access && !this.$route.params.key && !this.$route.params.resctrictedKey) {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.access = this.$route.params.access;
|
||||
this.key = this.$route.params.key;
|
||||
this.restrictedKey = this.$route.params.restrictedKey;
|
||||
|
||||
const requestURL = MetaUtils.getMetaContent('gateway-credentials-request-url');
|
||||
if (requestURL) this.isGatewayLinkVisible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy access grant button click logic.
|
||||
* Copies token to clipboard.
|
||||
*/
|
||||
public onCopyGrantClick(): void {
|
||||
this.$copyText(this.access);
|
||||
this.$notify.success('Token was copied successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on download access grant button click logic.
|
||||
* Downloads a file with the access called access-grant-<timestamp>.key
|
||||
*/
|
||||
public onDownloadGrantClick(): void {
|
||||
const ts = new Date();
|
||||
const filename = 'access-grant-' + ts.toJSON() + '.key';
|
||||
|
||||
Download.file(this.access, filename);
|
||||
|
||||
this.$notify.success('Token was downloaded successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on back button click logic.
|
||||
* Redirects to previous step.
|
||||
*/
|
||||
public onBackClick(): void {
|
||||
if (this.accessGrantsAmount > 1) {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.EnterPassphraseStep)).path);
|
||||
this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.EnterPassphraseStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CreatePassphraseStep)).path);
|
||||
this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CreatePassphraseStep)).name,
|
||||
params: {
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on done button click logic.
|
||||
* Proceed to upload data step.
|
||||
*/
|
||||
public onDoneClick(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
|
||||
this.isOnboardingTour ? this.$router.push(RouteConfig.ProjectDashboard.path) : this.$router.push(RouteConfig.AccessGrants.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on link click logic.
|
||||
* Proceed to gateway step.
|
||||
*/
|
||||
public navigateToGatewayStep(): void {
|
||||
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.GatewayStep)).path);
|
||||
this.$router.push({
|
||||
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.GatewayStep)).name,
|
||||
params: {
|
||||
access: this.access,
|
||||
key: this.key,
|
||||
restrictedKey: this.restrictedKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if current route is onboarding tour.
|
||||
*/
|
||||
public get isOnboardingTour(): boolean {
|
||||
return this.$route.path.includes(RouteConfig.OnboardingTour.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns amount of access grants from store.
|
||||
*/
|
||||
private get accessGrantsAmount(): number {
|
||||
return this.$store.state.accessGrantsModule.page.accessGrants.length;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.generate-grant {
|
||||
height: calc(100% - 60px);
|
||||
padding: 30px 65px;
|
||||
max-width: 475px;
|
||||
min-width: 475px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
border-radius: 0 6px 6px 0;
|
||||
|
||||
&__back-icon {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 65px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0 0 30px;
|
||||
}
|
||||
|
||||
&__warning {
|
||||
padding: 15px;
|
||||
width: calc(100% - 32px);
|
||||
background: #fff9f7;
|
||||
border: 1px solid #f84b00;
|
||||
border-radius: 8px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__label {
|
||||
font-style: normal;
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
margin: 0 0 0 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
color: #1b2533;
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__grant-area {
|
||||
margin: 20px;
|
||||
width: 100%;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #354049;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
&__container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 9px;
|
||||
padding: 10px;
|
||||
width: calc(100% - 22px);
|
||||
border: 1px solid rgb(56 75 101 / 40%);
|
||||
|
||||
&__value {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__button {
|
||||
min-width: 85px;
|
||||
min-height: 30px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__done-button {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&__gateway-link {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 23px;
|
||||
text-align: center;
|
||||
color: #0068dc;
|
||||
margin: 30px 0 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.border-radius {
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
@ -86,9 +86,9 @@ export default class OverviewStep extends Vue {
|
||||
* Redirects to next step (creating access grant).
|
||||
*/
|
||||
public onUplinkCLIClick(): void {
|
||||
this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.CLIStep).with(RouteConfig.AGName).path);
|
||||
this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep).with(RouteConfig.AGName).path);
|
||||
this.analytics.linkEventTriggered(AnalyticsEvent.PATH_SELECTED, 'CLI');
|
||||
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.CLIStep).with(RouteConfig.AGName).path);
|
||||
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep).with(RouteConfig.AGName).path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,14 +9,6 @@ import { MetaUtils } from '@/utils/meta';
|
||||
|
||||
import AccessGrants from '@/components/accessGrants/AccessGrants.vue';
|
||||
import CreateAccessModal from '@/components/accessGrants/CreateAccessModal.vue';
|
||||
import CreateAccessGrant from '@/components/accessGrants/CreateAccessGrant.vue';
|
||||
import CLIStep from '@/components/accessGrants/steps/CLIStep.vue';
|
||||
import CreatePassphraseStep from '@/components/accessGrants/steps/CreatePassphraseStep.vue';
|
||||
import EnterPassphraseStep from '@/components/accessGrants/steps/EnterPassphraseStep.vue';
|
||||
import GatewayStep from '@/components/accessGrants/steps/GatewayStep.vue';
|
||||
import NameStep from '@/components/accessGrants/steps/NameStep.vue';
|
||||
import PermissionsStep from '@/components/accessGrants/steps/PermissionsStep.vue';
|
||||
import ResultStep from '@/components/accessGrants/steps/ResultStep.vue';
|
||||
import AccountArea from '@/components/account/AccountArea.vue';
|
||||
import AccountBilling from '@/components/account/billing/BillingArea.vue';
|
||||
import BillingOverview from '@/components/account/billing/billingTabs/Overview.vue';
|
||||
@ -101,14 +93,6 @@ export abstract class RouteConfig {
|
||||
|
||||
// access grant child paths
|
||||
public static CreateAccessModal = new NavigationLink('create-access-modal', 'Create Access Modal');
|
||||
public static CreateAccessGrant = new NavigationLink('create-grant', 'Create Access Grant');
|
||||
public static NameStep = new NavigationLink('name', 'Name Access Grant');
|
||||
public static PermissionsStep = new NavigationLink('permissions', 'Access Grant Permissions');
|
||||
public static CreatePassphraseStep = new NavigationLink('create-passphrase', 'Access Grant Create Passphrase');
|
||||
public static EnterPassphraseStep = new NavigationLink('enter-passphrase', 'Access Grant Enter Passphrase');
|
||||
public static ResultStep = new NavigationLink('result', 'Access Grant Result');
|
||||
public static GatewayStep = new NavigationLink('gateway', 'Access Grant Gateway');
|
||||
public static CLIStep = new NavigationLink('cli', 'Access Grant In CLI');
|
||||
|
||||
// onboarding tour child paths
|
||||
public static OverviewStep = new NavigationLink('overview', 'Onboarding Overview');
|
||||
@ -370,54 +354,6 @@ export const router = new Router({
|
||||
name: RouteConfig.CreateAccessModal.name,
|
||||
component: CreateAccessModal,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.CreateAccessGrant.path,
|
||||
name: RouteConfig.CreateAccessGrant.name,
|
||||
component: CreateAccessGrant,
|
||||
children: [
|
||||
{
|
||||
path: RouteConfig.NameStep.path,
|
||||
name: RouteConfig.NameStep.name,
|
||||
component: NameStep,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.PermissionsStep.path,
|
||||
name: RouteConfig.PermissionsStep.name,
|
||||
component: PermissionsStep,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.CreatePassphraseStep.path,
|
||||
name: RouteConfig.CreatePassphraseStep.name,
|
||||
component: CreatePassphraseStep,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.EnterPassphraseStep.path,
|
||||
name: RouteConfig.EnterPassphraseStep.name,
|
||||
component: EnterPassphraseStep,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.ResultStep.path,
|
||||
name: RouteConfig.ResultStep.name,
|
||||
component: ResultStep,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.GatewayStep.path,
|
||||
name: RouteConfig.GatewayStep.name,
|
||||
component: GatewayStep,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.CLIStep.path,
|
||||
name: RouteConfig.CLIStep.name,
|
||||
component: CLIStep,
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -38,7 +38,7 @@ const projectsApi = new ProjectsApiGql();
|
||||
const paymentsApi = new PaymentsHttpApi();
|
||||
const abTestingAPI = new ABHttpApi();
|
||||
|
||||
// We need to use a WebWorker facotory because jest testing does not support
|
||||
// We need to use a WebWorker factory because jest testing does not support
|
||||
// WebWorkers yet. This is a way to avoid a direct dependency to `new Worker`.
|
||||
const webWorkerFactory = {
|
||||
create(): Worker {
|
||||
@ -111,12 +111,6 @@ router.beforeEach(async (to, _, next) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigateToDefaultSubTab(to.matched, RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant))) {
|
||||
next(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigateToDefaultSubTab(to.matched, RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep))) {
|
||||
next(RouteConfig.OnboardingTour.path);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user