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 {
width: calc(100% - 12px);
width: calc(100% - 2px);
}
&__next-button {

View File

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

View File

@ -357,7 +357,7 @@ export default class GeneratePassphrase extends Vue {
margin: 10px 0 20px 0;
&__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>

View File

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

View File

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

View File

@ -5,7 +5,7 @@
<div class="buckets-view">
<div class="buckets-view__title-area">
<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/>
<p class="buckets-view__title-area__button__label">New Bucket</p>
</div>
@ -68,6 +68,7 @@ import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { AccessGrant, GatewayCredentials } from '@/types/accessGrants';
import { MetaUtils } from '@/utils/meta';
import { Validator } from '@/utils/validation';
@Component({
components: {
@ -189,8 +190,22 @@ export default class BucketsView extends Vue {
public async onCreateBucketClick(): Promise<void> {
if (this.isRequestProcessing) return;
if (!this.createBucketName) {
this.errorMessage = 'Bucket name can\'t be empty';
if (this.createBucketName.length < 3 || this.createBucketName.length > 63) {
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;
@ -200,11 +215,16 @@ export default class BucketsView extends Vue {
await this.$store.dispatch(OBJECTS_ACTIONS.FETCH_BUCKETS);
} catch (error) {
await this.$notify.error(error.message);
this.isRequestProcessing = false;
return;
}
this.isRequestProcessing = false;
const bucket = 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 {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }

View File

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

View File

@ -2,8 +2,11 @@
// See LICENSE for copying information.
<template>
<div class="file-browser">
<FileBrowser></FileBrowser>
<div>
<p @click="goToBuckets" class="back"><- Back to Buckets</p>
<div class="file-browser">
<FileBrowser></FileBrowser>
</div>
</div>
</template>
@ -25,16 +28,6 @@ export default class UploadFile extends Vue {
private linksharingURL = '';
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.
* Checks if bucket is chosen.
@ -57,10 +50,6 @@ export default class UploadFile extends Vue {
* Initiates file browser.
*/
public created(): void {
if (!this.bucket) {
return;
}
this.$store.commit('files/init', {
endpoint: this.$store.state.objectsModule.gatewayCredentials.endpoint,
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.
*/
@ -195,6 +191,20 @@ export default class UploadFile extends Vue {
<style scoped>
@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 {
font-family: 'font_regular', sans-serif;
padding-bottom: 200px;

View File

@ -21,4 +21,22 @@ export class Validator {
public static password(password: string): boolean {
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 AuthIcon from '@/../static/images/AuthImage.svg';
import InfoIcon from '@/../static/images/info.svg';
import LogoIcon from '@/../static/images/dcs-logo.svg';
import InfoIcon from '@/../static/images/info.svg';
import { AuthHttpApi } from '@/api/auth';
import { RouteConfig } from '@/router';

View File

@ -13,7 +13,7 @@ exports[`HeaderedInput.vue renders correctly with default props 1`] = `
<!---->
</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>
`;
@ -30,7 +30,7 @@ exports[`HeaderedInput.vue renders correctly with input error 1`] = `
<!---->
</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>
`;
@ -46,7 +46,7 @@ exports[`HeaderedInput.vue renders correctly with isMultiline props 1`] = `
</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>
`;

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">
<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>
</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 class="input-container full-input">
<div class="label-container">
@ -29,7 +29,7 @@ exports[`CreateProject.vue renders correctly 1`] = `
</div>
<h3 class="label-container__limit">0/100</h3>
</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 class="create-project-area__container__button-container">