web/satellite: add modal for creating a project
github issue: https://github.com/storj/storj/issues/4372 Change-Id: I9b8cf64aa76f2d75c740ee8a0158f0382f4a1918
This commit is contained in:
parent
23e5ccb9c9
commit
9cd91c6df8
@ -4,6 +4,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<CreateProjectPromptModal v-if="isCreateProjectPromptModal" />
|
||||
<CreateProjectModal v-if="isCreateProjectModal" />
|
||||
<AddPaymentMethodModal v-if="isAddPMModal" />
|
||||
<OpenBucketModal v-if="isOpenBucketModal" />
|
||||
</div>
|
||||
@ -13,6 +14,7 @@
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import CreateProjectPromptModal from '@/components/modals/CreateProjectPromptModal.vue';
|
||||
import CreateProjectModal from '@/components/modals/CreateProjectModal.vue'
|
||||
import AddPaymentMethodModal from "@/components/modals/AddPaymentMethodModal.vue";
|
||||
import OpenBucketModal from "@/components/modals/OpenBucketModal.vue";
|
||||
|
||||
@ -20,6 +22,7 @@ import OpenBucketModal from "@/components/modals/OpenBucketModal.vue";
|
||||
@Component({
|
||||
components: {
|
||||
CreateProjectPromptModal,
|
||||
CreateProjectModal,
|
||||
AddPaymentMethodModal,
|
||||
OpenBucketModal,
|
||||
},
|
||||
@ -32,6 +35,13 @@ export default class AllModals extends Vue {
|
||||
return this.$store.state.appStateModule.appState.isCreateProjectPromptModalShown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if create project prompt modal is shown.
|
||||
*/
|
||||
public get isCreateProjectModal(): boolean {
|
||||
return this.$store.state.appStateModule.appState.isCreateProjectModalShown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if add payment method modal is shown.
|
||||
*/
|
||||
|
253
web/satellite/src/components/modals/CreateProjectModal.vue
Normal file
253
web/satellite/src/components/modals/CreateProjectModal.vue
Normal file
@ -0,0 +1,253 @@
|
||||
// Copyright (C) 2022 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<VModal :on-close="closeModal">
|
||||
<template #content>
|
||||
<div class="modal">
|
||||
<img
|
||||
class="modal__icon"
|
||||
src="@/../static/images/account/billing/paidTier/prompt.png"
|
||||
alt="Prompt Image"
|
||||
>
|
||||
<h1 class="modal__title" aria-roledescription="modal-title">
|
||||
Create a Project
|
||||
</h1>
|
||||
<VInput
|
||||
label="Project Name"
|
||||
additional-label="Up To 20 Characters"
|
||||
placeholder="Enter Project Name"
|
||||
class="full-input"
|
||||
is-limit-shown="true"
|
||||
:current-limit="projectName.length"
|
||||
:max-symbols="20"
|
||||
:error="nameError"
|
||||
@setData="setProjectName"
|
||||
/>
|
||||
<VInput
|
||||
label="Description"
|
||||
placeholder="Enter Project Description"
|
||||
additional-label="Optional"
|
||||
class="full-input"
|
||||
is-multiline="true"
|
||||
height="100px"
|
||||
is-limit-shown="true"
|
||||
:current-limit="description.length"
|
||||
:max-symbols="100"
|
||||
@setData="setProjectDescription"
|
||||
/>
|
||||
<div class="modal__button-container">
|
||||
<VButton
|
||||
class="modal__button-container__cancel"
|
||||
label="Cancel"
|
||||
width="210px"
|
||||
height="48px"
|
||||
:on-press="closeModal"
|
||||
is-transparent="true"
|
||||
/>
|
||||
<VButton
|
||||
label="Create Project +"
|
||||
width="210px"
|
||||
height="48px"
|
||||
:on-press="onCreateProjectClick"
|
||||
:is-disabled="!projectName"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="isLoading" class="modal__blur">
|
||||
<VLoader class="modal__blur__loader" width="50px" height="50px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { APP_STATE_MUTATIONS } from "@/store/mutationConstants";
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VModal from '@/components/common/VModal.vue';
|
||||
import VInput from '@/components/common/VInput.vue';
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
|
||||
import { ProjectFields } from '@/types/projects';
|
||||
import { LocalData } from '@/utils/localData';
|
||||
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VButton,
|
||||
VModal,
|
||||
VInput,
|
||||
VLoader,
|
||||
},
|
||||
})
|
||||
export default class CreateProjectModal extends Vue {
|
||||
private description = '';
|
||||
private createdProjectId = '';
|
||||
private isLoading = false;
|
||||
|
||||
public projectName = '';
|
||||
public nameError = '';
|
||||
|
||||
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
/**
|
||||
* Sets project name from input value.
|
||||
*/
|
||||
public setProjectName(value: string): void {
|
||||
this.projectName = value;
|
||||
this.nameError = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets project description from input value.
|
||||
*/
|
||||
public setProjectDescription(value: string): void {
|
||||
this.description = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates project and refreshes store.
|
||||
*/
|
||||
public async onCreateProjectClick(): Promise<void> {
|
||||
if (this.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
this.projectName = this.projectName.trim();
|
||||
|
||||
const project = new ProjectFields(
|
||||
this.projectName,
|
||||
this.description,
|
||||
this.$store.getters.user.id,
|
||||
);
|
||||
|
||||
try {
|
||||
project.checkName();
|
||||
} catch (error) {
|
||||
this.isLoading = false;
|
||||
this.nameError = error.message;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const createdProject = await this.$store.dispatch(PROJECTS_ACTIONS.CREATE, project);
|
||||
this.createdProjectId = createdProject.id;
|
||||
} catch (error) {
|
||||
this.isLoading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectCreatedProject();
|
||||
|
||||
await this.$notify.success('Project created successfully!');
|
||||
|
||||
this.isLoading = false;
|
||||
this.closeModal()
|
||||
|
||||
this.analytics.pageVisit(RouteConfig.ProjectDashboard.path);
|
||||
await this.$router.push(RouteConfig.ProjectDashboard.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects just created project.
|
||||
*/
|
||||
private selectCreatedProject(): void {
|
||||
this.$store.dispatch(PROJECTS_ACTIONS.SELECT, this.createdProjectId);
|
||||
LocalData.setSelectedProjectId(this.createdProjectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on button click logic.
|
||||
* Closes this modal and opens create project modal.
|
||||
*/
|
||||
public onClick(): void {
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_POPUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes create project modal.
|
||||
*/
|
||||
public closeModal(): void {
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_POPUP);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.modal {
|
||||
width: 400px;
|
||||
padding: 50px 65px 65px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
|
||||
&__icon {
|
||||
max-height: 154px;
|
||||
max-width: 118px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 28px;
|
||||
line-height: 34px;
|
||||
color: #1b2533;
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
text-align: center;
|
||||
color: #354049;
|
||||
margin: 15px 0 45px;
|
||||
}
|
||||
|
||||
&__button-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 30px;
|
||||
|
||||
&__cancel {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__blur {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: rgb(229 229 229 / 20%);
|
||||
border-radius: 8px;
|
||||
z-index: 100;
|
||||
|
||||
&__loader {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.full-input {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
@ -291,7 +291,7 @@ export default class NavigationArea extends Vue {
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PROMPT_POPUP);
|
||||
} else {
|
||||
this.analytics.pageVisit(RouteConfig.CreateProject.path);
|
||||
this.$router.push(RouteConfig.CreateProject.path);
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_POPUP);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,7 @@ export default class ProjectSelection extends Vue {
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PROMPT_POPUP);
|
||||
} else {
|
||||
this.analytics.pageVisit(RouteConfig.CreateProject.path);
|
||||
this.$router.push(RouteConfig.CreateProject.path);
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_POPUP);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,9 @@ import { PM_ACTIONS } from '@/utils/constants/actionNames';
|
||||
import { LocalData } from '@/utils/localData';
|
||||
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
import { APP_STATE_MUTATIONS } from "@/store/mutationConstants";
|
||||
import { User } from "@/types/users";
|
||||
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
const {
|
||||
FETCH_OWNED,
|
||||
@ -107,8 +110,17 @@ export default class Projects extends Vue {
|
||||
* Redirects to create project page.
|
||||
*/
|
||||
public onCreateClick(): void {
|
||||
this.analytics.eventTriggered(AnalyticsEvent.NEW_PROJECT_CLICKED);
|
||||
|
||||
const user: User = this.$store.getters.user;
|
||||
const ownProjectsCount: number = this.$store.getters.projectsCount;
|
||||
|
||||
if (!user.paidTier && user.projectLimit === ownProjectsCount) {
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PROMPT_POPUP);
|
||||
} else {
|
||||
this.analytics.pageVisit(RouteConfig.CreateProject.path);
|
||||
this.$router.push(RouteConfig.CreateProject.path);
|
||||
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_POPUP);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,7 @@ class ViewsState {
|
||||
public isUploadCancelPopupVisible = false,
|
||||
public isSuccessfulPasswordResetShown = false,
|
||||
public isCreateProjectPromptModalShown = false,
|
||||
public isCreateProjectModalShown = false,
|
||||
public isAddPMModalShown = false,
|
||||
public isOpenBucketModalShown = false,
|
||||
|
||||
@ -121,6 +122,9 @@ export const appStateModule = {
|
||||
[APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PROMPT_POPUP](state: State): void {
|
||||
state.appState.isCreateProjectPromptModalShown = !state.appState.isCreateProjectPromptModalShown;
|
||||
},
|
||||
[APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_POPUP](state: State): void {
|
||||
state.appState.isCreateProjectModalShown = !state.appState.isCreateProjectModalShown;
|
||||
},
|
||||
[APP_STATE_MUTATIONS.TOGGLE_IS_ADD_PM_MODAL_SHOWN](state: State): void {
|
||||
state.appState.isAddPMModalShown = !state.appState.isAddPMModalShown;
|
||||
},
|
||||
|
@ -30,6 +30,7 @@ export const APP_STATE_MUTATIONS = {
|
||||
TOGGLE_CHANGE_PASSWORD_POPUP: 'TOGGLE_CHANGE_PASSWORD_POPUP',
|
||||
TOGGLE_UPLOAD_CANCEL_POPUP: 'TOGGLE_UPLOAD_CANCEL_POPUP',
|
||||
TOGGLE_CREATE_PROJECT_PROMPT_POPUP: 'TOGGLE_CREATE_PROJECT_PROMPT_POPUP',
|
||||
TOGGLE_CREATE_PROJECT_POPUP: 'TOGGLE_CREATE_PROJECT_POPUP',
|
||||
TOGGLE_IS_ADD_PM_MODAL_SHOWN: 'TOGGLE_IS_ADD_PM_MODAL_SHOWN',
|
||||
TOGGLE_OPEN_BUCKET_MODAL_SHOWN: 'TOGGLE_OPEN_BUCKET_MODAL_SHOWN',
|
||||
SHOW_DELETE_PAYMENT_METHOD_POPUP: 'SHOW_DELETE_PAYMENT_METHOD_POPUP',
|
||||
|
Loading…
Reference in New Issue
Block a user