web/satellite/file_browser: open bucket on creation

WHAT:
open bucket after creation
back button for uploads page
fixed input's width
added client side bucket name validation

WHY:
bakeoff

Change-Id: I82b96d4180e4a80c01bf888adae5c08d0af15bec
This commit is contained in:
Vitalii Shpital 2021-04-14 23:28:48 +03:00 committed by JT Olio
parent 3b09d6c308
commit bd36a41a9e
13 changed files with 91 additions and 40 deletions

View File

@ -198,7 +198,7 @@ export default class EnterPassphraseStep extends Vue {
} }
&__input { &__input {
width: calc(100% - 12px); width: calc(100% - 2px);
} }
&__next-button { &__next-button {

View File

@ -210,7 +210,7 @@ export default class NameStep extends Vue {
} }
&__input { &__input {
width: calc(100% - 12px); width: calc(100% - 2px);
} }
&__buttons-area { &__buttons-area {

View File

@ -357,7 +357,7 @@ export default class GeneratePassphrase extends Vue {
margin: 10px 0 20px 0; margin: 10px 0 20px 0;
&__input { &__input {
width: 100%; width: calc(100% - 2px);
} }
} }
} }
@ -397,8 +397,4 @@ export default class GeneratePassphrase extends Vue {
} }
} }
} }
/deep/ .headered-input {
padding-right: 0 !important;
}
</style> </style>

View File

@ -146,6 +146,7 @@ export default class HeaderedInput extends HeaderlessInput {
resize: none; resize: none;
height: 48px; height: 48px;
width: 100%; width: 100%;
padding: 0;
text-indent: 20px; text-indent: 20px;
border-color: rgba(56, 75, 101, 0.4); border-color: rgba(56, 75, 101, 0.4);
border-radius: 6px; border-radius: 6px;

View File

@ -198,7 +198,7 @@ export default class HeaderlessInput extends Vue {
inputStyle: { inputStyle: {
width: this.width, width: this.width,
height: this.height, height: this.height,
padding: this.withIcon ? '0 30px 0 50px' : '0 30px 0 0', padding: this.withIcon ? '0 30px 0 50px' : '',
}, },
labelStyle: { labelStyle: {
color: this.isWhite ? 'white' : '#354049', color: this.isWhite ? 'white' : '#354049',

View File

@ -5,7 +5,7 @@
<div class="buckets-view"> <div class="buckets-view">
<div class="buckets-view__title-area"> <div class="buckets-view__title-area">
<h1 class="buckets-view__title-area__title">Buckets</h1> <h1 class="buckets-view__title-area__title">Buckets</h1>
<div class="buckets-view__title-area__button" @click="showCreateBucketPopup"> <div class="buckets-view__title-area__button" :class="{ disabled: isLoading }" @click="showCreateBucketPopup">
<BucketIcon/> <BucketIcon/>
<p class="buckets-view__title-area__button__label">New Bucket</p> <p class="buckets-view__title-area__button__label">New Bucket</p>
</div> </div>
@ -68,6 +68,7 @@ import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { OBJECTS_ACTIONS } from '@/store/modules/objects'; import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { AccessGrant, GatewayCredentials } from '@/types/accessGrants'; import { AccessGrant, GatewayCredentials } from '@/types/accessGrants';
import { MetaUtils } from '@/utils/meta'; import { MetaUtils } from '@/utils/meta';
import { Validator } from '@/utils/validation';
@Component({ @Component({
components: { components: {
@ -189,8 +190,22 @@ export default class BucketsView extends Vue {
public async onCreateBucketClick(): Promise<void> { public async onCreateBucketClick(): Promise<void> {
if (this.isRequestProcessing) return; if (this.isRequestProcessing) return;
if (!this.createBucketName) { if (this.createBucketName.length < 3 || this.createBucketName.length > 63) {
this.errorMessage = 'Bucket name can\'t be empty'; this.errorMessage = 'Name must be not less than 3 and not more than 63 characters length';
return;
}
if (!Validator.bucketName(this.createBucketName)) {
this.errorMessage = 'Name must include only lowercase latin characters';
return;
}
if (!Validator.oneWordString(this.createBucketName)) {
this.errorMessage = 'Name must be 1-word string';
return;
} }
this.isRequestProcessing = true; this.isRequestProcessing = true;
@ -200,11 +215,16 @@ export default class BucketsView extends Vue {
await this.$store.dispatch(OBJECTS_ACTIONS.FETCH_BUCKETS); await this.$store.dispatch(OBJECTS_ACTIONS.FETCH_BUCKETS);
} catch (error) { } catch (error) {
await this.$notify.error(error.message); await this.$notify.error(error.message);
this.isRequestProcessing = false;
return;
} }
this.isRequestProcessing = false; const bucket = this.createBucketName;
this.createBucketName = ''; this.createBucketName = '';
this.hideCreateBucketPopup(); this.isRequestProcessing = false;
this.openBucket(bucket);
} }
/** /**
@ -435,6 +455,12 @@ export default class BucketsView extends Vue {
} }
} }
.disabled {
pointer-events: none;
background-color: #dadde5;
border-color: #dadde5;
}
@keyframes spin { @keyframes spin {
0% { transform: rotate(0deg); } 0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); } 100% { transform: rotate(360deg); }

View File

@ -8,7 +8,7 @@
<p class="objects-popup__container__sub-title">{{subTitle}}</p> <p class="objects-popup__container__sub-title">{{subTitle}}</p>
<HeaderedInput <HeaderedInput
class="objects-popup__container__input" class="objects-popup__container__input"
label="Bucket Name (lowercase letters)" label="Bucket Name"
placeholder="Enter bucket name" placeholder="Enter bucket name"
@setData="onChangeName" @setData="onChangeName"
:init-value="defaultInputValue" :init-value="defaultInputValue"
@ -46,7 +46,7 @@ import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
}) })
export default class ObjectsPopup extends Vue { export default class ObjectsPopup extends Vue {
@Prop({ default: () => ''}) @Prop({ default: () => ''})
public readonly onClick; public readonly onClick: () => void;
@Prop({ default: ''}) @Prop({ default: ''})
public readonly title: string; public readonly title: string;
@Prop({ default: ''}) @Prop({ default: ''})

View File

@ -2,8 +2,11 @@
// See LICENSE for copying information. // See LICENSE for copying information.
<template> <template>
<div class="file-browser"> <div>
<FileBrowser></FileBrowser> <p @click="goToBuckets" class="back"><- Back to Buckets</p>
<div class="file-browser">
<FileBrowser></FileBrowser>
</div>
</div> </div>
</template> </template>
@ -25,16 +28,6 @@ export default class UploadFile extends Vue {
private linksharingURL = ''; private linksharingURL = '';
private worker: Worker; private worker: Worker;
/**
* Lifecycle hook before initial render.
* Checks if bucket is chosen.
*/
public beforeMount(): void {
if (!this.bucket) {
this.$router.push(RouteConfig.Objects.with(RouteConfig.EnterPassphrase).path);
}
}
/** /**
* Lifecycle hook after initial render. * Lifecycle hook after initial render.
* Checks if bucket is chosen. * Checks if bucket is chosen.
@ -57,10 +50,6 @@ export default class UploadFile extends Vue {
* Initiates file browser. * Initiates file browser.
*/ */
public created(): void { public created(): void {
if (!this.bucket) {
return;
}
this.$store.commit('files/init', { this.$store.commit('files/init', {
endpoint: this.$store.state.objectsModule.gatewayCredentials.endpoint, endpoint: this.$store.state.objectsModule.gatewayCredentials.endpoint,
accessKey: this.$store.state.objectsModule.gatewayCredentials.accessKeyId, accessKey: this.$store.state.objectsModule.gatewayCredentials.accessKeyId,
@ -72,6 +61,13 @@ export default class UploadFile extends Vue {
}); });
} }
/**
* Redirects to buckets list view.
*/
public goToBuckets(): void {
this.$router.push(RouteConfig.Objects.with(RouteConfig.BucketsManagement).path);
}
/** /**
* Generates a URL for an object map. * Generates a URL for an object map.
*/ */
@ -195,6 +191,20 @@ export default class UploadFile extends Vue {
<style scoped> <style scoped>
@import '../../../node_modules/browser/dist/browser.css'; @import '../../../node_modules/browser/dist/browser.css';
.back {
font-family: 'font_medium', sans-serif;
color: #000;
font-size: 20px;
cursor: pointer;
margin: 0 0 30px 15px;
display: inline-block;
}
.back:hover {
color: #007bff;
text-decoration: underline;
}
.file-browser { .file-browser {
font-family: 'font_regular', sans-serif; font-family: 'font_regular', sans-serif;
padding-bottom: 200px; padding-bottom: 200px;

View File

@ -21,4 +21,22 @@ export class Validator {
public static password(password: string): boolean { public static password(password: string): boolean {
return typeof password !== 'undefined' && password.length >= 6; return typeof password !== 'undefined' && password.length >= 6;
} }
/**
* Checks string to satisfy bucket name rules.
*/
public static bucketName(value: string): boolean {
const rgx = /^[a-z0-9]+$/;
return rgx.test(value);
}
/**
* Checks string to consist of 1 word.
*/
public static oneWordString(value: string): boolean {
const trimmed = value.trim();
return trimmed.indexOf(' ') === -1;
}
} }

View File

@ -12,8 +12,8 @@ import PasswordStrength from '@/components/common/PasswordStrength.vue';
import RegistrationSuccess from '@/components/common/RegistrationSuccess.vue'; import RegistrationSuccess from '@/components/common/RegistrationSuccess.vue';
import AuthIcon from '@/../static/images/AuthImage.svg'; import AuthIcon from '@/../static/images/AuthImage.svg';
import InfoIcon from '@/../static/images/info.svg';
import LogoIcon from '@/../static/images/dcs-logo.svg'; import LogoIcon from '@/../static/images/dcs-logo.svg';
import InfoIcon from '@/../static/images/info.svg';
import { AuthHttpApi } from '@/api/auth'; import { AuthHttpApi } from '@/api/auth';
import { RouteConfig } from '@/router'; import { RouteConfig } from '@/router';

View File

@ -13,7 +13,7 @@ exports[`HeaderedInput.vue renders correctly with default props 1`] = `
<!----> <!---->
</div> </div>
<!----> <!---->
<!----> <input id="" placeholder="default" type="text" class="headered-input" style="width: 100%; height: 48px; padding: 0px 30px 0px 0px;"> <!----> <input id="" placeholder="default" type="text" class="headered-input" style="width: 100%; height: 48px;">
</div> </div>
`; `;
@ -30,7 +30,7 @@ exports[`HeaderedInput.vue renders correctly with input error 1`] = `
<!----> <!---->
</div> </div>
<!----> <!---->
<!----> <input id="" placeholder="default" type="text" class="headered-input" style="width: 100%; height: 48px; padding: 0px 30px 0px 0px;"> <!----> <input id="" placeholder="default" type="text" class="headered-input" style="width: 100%; height: 48px;">
</div> </div>
`; `;
@ -46,7 +46,7 @@ exports[`HeaderedInput.vue renders correctly with isMultiline props 1`] = `
</div> </div>
<!----> <!---->
</div> </div>
<!----> <textarea id="" placeholder="default" rows="5" cols="40" wrap="hard" class="headered-textarea" style="width: 100%; height: 48px; padding: 0px 30px 0px 0px;"></textarea> <!----> <textarea id="" placeholder="default" rows="5" cols="40" wrap="hard" class="headered-textarea" style="width: 100%; height: 48px;"></textarea>
<!----> <!---->
</div> </div>
`; `;

View File

@ -6,7 +6,7 @@ exports[`HeaderlessInput.vue renders correctly with default props 1`] = `
<!----> <!---->
<!----> <!---->
<!----> <!---->
</div> <input placeholder="default" type="text" class="headerless-input" style="width: 100%; height: 48px; padding: 0px 30px 0px 0px;"> </div> <input placeholder="default" type="text" class="headerless-input" style="width: 100%; height: 48px;">
<!----> <!---->
<!----> <!---->
<!----> <!---->
@ -20,7 +20,7 @@ exports[`HeaderlessInput.vue renders correctly with isPassword prop 1`] = `
<!----> <!---->
<!----> <!---->
<!----> <!---->
</div> <input placeholder="default" type="password" class="headerless-input password" style="width: 100%; height: 48px; padding: 0px 30px 0px 0px;"> </div> <input placeholder="default" type="password" class="headerless-input password" style="width: 100%; height: 48px;">
<!----> <!---->
<!----> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="input-wrap__image"> <!----> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="input-wrap__image">
<path d="M10 4C4.70642 4 1 10 1 10C1 10 3.6999 16 10 16C16.3527 16 19 10 19 10C19 10 15.3472 4 10 4ZM10 13.8176C7.93537 13.8176 6.2946 12.1271 6.2946 10C6.2946 7.87285 7.93537 6.18239 10 6.18239C12.0646 6.18239 13.7054 7.87285 13.7054 10C13.7054 12.1271 12.0646 13.8176 10 13.8176Z" fill="#AFB7C1" class="input-wrap__image__path"></path> <path d="M10 4C4.70642 4 1 10 1 10C1 10 3.6999 16 10 16C16.3527 16 19 10 19 10C19 10 15.3472 4 10 4ZM10 13.8176C7.93537 13.8176 6.2946 12.1271 6.2946 10C6.2946 7.87285 7.93537 6.18239 10 6.18239C12.0646 6.18239 13.7054 7.87285 13.7054 10C13.7054 12.1271 12.0646 13.8176 10 13.8176Z" fill="#AFB7C1" class="input-wrap__image__path"></path>
@ -36,7 +36,7 @@ exports[`HeaderlessInput.vue renders correctly with size props 1`] = `
<!----> <!---->
<!----> <!---->
<!----> <!---->
</div> <input placeholder="test" type="text" class="headerless-input" style="width: 30px; height: 20px; padding: 0px 30px 0px 0px;"> </div> <input placeholder="test" type="text" class="headerless-input" style="width: 30px; height: 20px;">
<!----> <!---->
<!----> <!---->
<!----> <!---->

View File

@ -16,7 +16,7 @@ exports[`CreateProject.vue renders correctly 1`] = `
<h3 class="label-container__limit">0/20</h3> <h3 class="label-container__limit">0/20</h3>
</div> </div>
<!----> <!---->
<!----> <input id="Project Name" placeholder="Enter Project Name" type="text" class="headered-input" style="width: 100%; height: 48px; padding: 0px 30px 0px 0px;"> <!----> <input id="Project Name" placeholder="Enter Project Name" type="text" class="headered-input" style="width: 100%; height: 48px;">
</div> </div>
<div class="input-container full-input"> <div class="input-container full-input">
<div class="label-container"> <div class="label-container">
@ -29,7 +29,7 @@ exports[`CreateProject.vue renders correctly 1`] = `
</div> </div>
<h3 class="label-container__limit">0/100</h3> <h3 class="label-container__limit">0/100</h3>
</div> </div>
<!----> <textarea id="Description" placeholder="Enter Project Description" rows="5" cols="40" wrap="hard" class="headered-textarea" style="height: 60px; padding: 0px 30px 0px 0px;"></textarea> <!----> <textarea id="Description" placeholder="Enter Project Description" rows="5" cols="40" wrap="hard" class="headered-textarea" style="height: 60px;"></textarea>
<!----> <!---->
</div> </div>
<div class="create-project-area__container__button-container"> <div class="create-project-area__container__button-container">