web/satellite/vuetify-poc: add project creation dialog
This change implements a dialog box through which users can create projects. Resolves #6172 Change-Id: I99311f43ef49dc04bb852a6736771b42f26a3276
This commit is contained in:
parent
b66fc6dcdb
commit
973b54365e
@ -53,7 +53,9 @@
|
||||
</v-card-item>
|
||||
<v-card-text class="flex-grow-0">
|
||||
<v-divider class="mt-1 mb-4" />
|
||||
<v-btn v-if="!item" color="primary" size="small" class="mr-2">Create Project</v-btn>
|
||||
<v-btn v-if="!item" color="primary" size="small" class="mr-2" @click="emit('createClick')">
|
||||
Create Project
|
||||
</v-btn>
|
||||
<template v-else-if="item?.role === ProjectRole.Invited">
|
||||
<v-btn color="primary" size="small" class="mr-2" :disabled="isDeclining" @click="emit('joinClick')">
|
||||
Join Project
|
||||
@ -111,7 +113,8 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'joinClick'): void;
|
||||
'joinClick': [];
|
||||
'createClick': [];
|
||||
}>();
|
||||
|
||||
const analyticsStore = useAnalyticsStore();
|
||||
|
@ -0,0 +1,192 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<v-dialog
|
||||
v-model="model"
|
||||
width="410px"
|
||||
transition="fade-transition"
|
||||
:persistent="isLoading"
|
||||
>
|
||||
<v-card rounded="xlg">
|
||||
<v-card-item class="pl-7 py-4">
|
||||
<template #prepend>
|
||||
<img class="d-block" src="@/../static/images/common/blueBox.svg" alt="Box">
|
||||
</template>
|
||||
|
||||
<v-card-title class="font-weight-bold">
|
||||
{{ !isProjectLimitReached ? 'Create New Project' : 'Get More Projects' }}
|
||||
</v-card-title>
|
||||
|
||||
<template #append>
|
||||
<v-btn
|
||||
icon="$close"
|
||||
variant="text"
|
||||
size="small"
|
||||
color="default"
|
||||
:disabled="isLoading"
|
||||
@click="model = false"
|
||||
/>
|
||||
</template>
|
||||
</v-card-item>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-form v-model="formValid" class="pa-7" @submit.prevent>
|
||||
<v-row>
|
||||
<template v-if="!isProjectLimitReached">
|
||||
<v-col cols="12">
|
||||
Projects are where you and your team can upload and manage data, and view usage statistics and billing.
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
variant="outlined"
|
||||
:rules="nameRules"
|
||||
label="Project Name"
|
||||
:counter="MAX_NAME_LENGTH"
|
||||
persistent-counter
|
||||
autofocus
|
||||
/>
|
||||
<v-btn
|
||||
v-if="!isDescriptionShown"
|
||||
variant="text"
|
||||
size="small"
|
||||
color="default"
|
||||
prepend-icon="mdi-plus"
|
||||
@click="isDescriptionShown = true"
|
||||
>
|
||||
Add Description (Optional)
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col v-if="isDescriptionShown" cols="12">
|
||||
<v-text-field
|
||||
v-model="description"
|
||||
variant="outlined"
|
||||
:rules="descriptionRules"
|
||||
label="Project Description (Optional)"
|
||||
:counter="MAX_DESCRIPTION_LENGTH"
|
||||
persistent-counter
|
||||
/>
|
||||
</v-col>
|
||||
</template>
|
||||
<v-col v-else>
|
||||
Upgrade to Pro Account to create more projects and gain access to higher limits.
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-card-actions class="pa-7">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-btn variant="outlined" color="default" block :disabled="isLoading" @click="model = false">
|
||||
Cancel
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="flat"
|
||||
:loading="isLoading"
|
||||
block
|
||||
@click="() => !isProjectLimitReached && onCreateClicked()"
|
||||
>
|
||||
{{ !isProjectLimitReached ? 'Create Project' : 'Upgrade' }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, Component } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import {
|
||||
VDialog,
|
||||
VCard,
|
||||
VCardItem,
|
||||
VCardTitle,
|
||||
VCardActions,
|
||||
VBtn,
|
||||
VDivider,
|
||||
VForm,
|
||||
VRow,
|
||||
VCol,
|
||||
VTextField,
|
||||
} from 'vuetify/components';
|
||||
|
||||
import { RequiredRule, ValidationRule } from '@poc/types/common';
|
||||
import { MAX_DESCRIPTION_LENGTH, MAX_NAME_LENGTH, ProjectFields } from '@/types/projects';
|
||||
import { useLoading } from '@/composables/useLoading';
|
||||
import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean,
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean],
|
||||
}>();
|
||||
|
||||
const model = computed<boolean>({
|
||||
get: () => props.modelValue,
|
||||
set: value => emit('update:modelValue', value),
|
||||
});
|
||||
|
||||
const projectsStore = useProjectsStore();
|
||||
const usersStore = useUsersStore();
|
||||
const { isLoading, withLoading } = useLoading();
|
||||
const notify = useNotify();
|
||||
const router = useRouter();
|
||||
|
||||
const formValid = ref<boolean>(false);
|
||||
const name = ref<string>('');
|
||||
const description = ref<string>('');
|
||||
const isDescriptionShown = ref<boolean>(false);
|
||||
const isProjectLimitReached = ref<boolean>(false);
|
||||
|
||||
const nameRules: ValidationRule<string>[] = [
|
||||
RequiredRule,
|
||||
v => v.length <= MAX_NAME_LENGTH || 'Name is too long',
|
||||
];
|
||||
|
||||
const descriptionRules: ValidationRule<string>[] = [
|
||||
v => v.length <= MAX_DESCRIPTION_LENGTH || 'Description is too long',
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates new project.
|
||||
*/
|
||||
async function onCreateClicked(): Promise<void> {
|
||||
if (!formValid.value) return;
|
||||
await withLoading(async () => {
|
||||
let id: string;
|
||||
try {
|
||||
const fields = new ProjectFields(name.value, description.value, usersStore.state.user.id);
|
||||
id = await projectsStore.createProject(fields);
|
||||
} catch (error) {
|
||||
error.message = `Failed to create project. ${error.message}`;
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.CREATE_PROJECT_MODAL);
|
||||
return;
|
||||
}
|
||||
model.value = false;
|
||||
router.push(`/projects/${id}/dashboard`);
|
||||
notify.success('Project created.');
|
||||
});
|
||||
}
|
||||
|
||||
watch(() => model.value, shown => {
|
||||
if (!shown) return;
|
||||
isProjectLimitReached.value = projectsStore.state.projects.length >= usersStore.state.user.projectLimit;
|
||||
isDescriptionShown.value = false;
|
||||
name.value = '';
|
||||
description.value = '';
|
||||
});
|
||||
</script>
|
@ -79,7 +79,7 @@
|
||||
</v-list-item>
|
||||
|
||||
<!-- Create New Project -->
|
||||
<v-list-item link rounded="lg">
|
||||
<v-list-item link rounded="lg" @click="isCreateProjectDialogShown = true">
|
||||
<template #prepend>
|
||||
<IconNew />
|
||||
</template>
|
||||
@ -231,10 +231,12 @@
|
||||
</v-list>
|
||||
</v-sheet>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<create-project-dialog v-model="isCreateProjectDialogShown" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import {
|
||||
VNavigationDrawer,
|
||||
VSheet,
|
||||
@ -265,10 +267,13 @@ import IconDocs from '@poc/components/icons/IconDocs.vue';
|
||||
import IconForum from '@poc/components/icons/IconForum.vue';
|
||||
import IconSupport from '@poc/components/icons/IconSupport.vue';
|
||||
import IconResources from '@poc/components/icons/IconResources.vue';
|
||||
import CreateProjectDialog from '@poc/components/dialogs/CreateProjectDialog.vue';
|
||||
|
||||
const analyticsStore = useAnalyticsStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
|
||||
const isCreateProjectDialogShown = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* Returns the selected project from the store.
|
||||
*/
|
||||
|
@ -13,107 +13,13 @@
|
||||
color="default"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
@click="isCreateProjectDialogShown = true"
|
||||
>
|
||||
<svg width="14" height="14" class="mr-2" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 1C14.9706 1 19 5.02944 19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1ZM10 2.65C5.94071 2.65 2.65 5.94071 2.65 10C2.65 14.0593 5.94071 17.35 10 17.35C14.0593 17.35 17.35 14.0593 17.35 10C17.35 5.94071 14.0593 2.65 10 2.65ZM10.7496 6.8989L10.7499 6.91218L10.7499 9.223H12.9926C13.4529 9.223 13.8302 9.58799 13.8456 10.048C13.8602 10.4887 13.5148 10.8579 13.0741 10.8726L13.0608 10.8729L10.7499 10.873L10.75 13.171C10.75 13.6266 10.3806 13.996 9.925 13.996C9.48048 13.996 9.11807 13.6444 9.10066 13.2042L9.1 13.171L9.09985 10.873H6.802C6.34637 10.873 5.977 10.5036 5.977 10.048C5.977 9.60348 6.32857 9.24107 6.76882 9.22366L6.802 9.223H9.09985L9.1 6.98036C9.1 6.5201 9.46499 6.14276 9.925 6.12745C10.3657 6.11279 10.7349 6.45818 10.7496 6.8989Z" fill="currentColor" />
|
||||
</svg>
|
||||
<!-- <IconNew class="mr-2" width="12px"/> -->
|
||||
Create Project
|
||||
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
activator="parent"
|
||||
width="auto"
|
||||
min-width="400px"
|
||||
transition="fade-transition"
|
||||
>
|
||||
<v-card rounded="xlg">
|
||||
<v-sheet>
|
||||
<v-card-item class="pl-7 py-4">
|
||||
<template #prepend>
|
||||
<v-card-title class="font-weight-bold">
|
||||
<!-- <v-icon
|
||||
>
|
||||
<img src="../assets/icon-project.svg" alt="Project">
|
||||
</v-icon> -->
|
||||
Create New Project
|
||||
</v-card-title>
|
||||
</template>
|
||||
|
||||
<template #append>
|
||||
<v-btn
|
||||
icon="$close"
|
||||
variant="text"
|
||||
size="small"
|
||||
color="default"
|
||||
@click="dialog = false"
|
||||
/>
|
||||
</template>
|
||||
</v-card-item>
|
||||
</v-sheet>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-form v-model="valid" class="pa-7 pb-4">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<p>Enter project name (max 20 characters).</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col
|
||||
cols="12"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
variant="outlined"
|
||||
:rules="nameRules"
|
||||
label="Project Name"
|
||||
class="mt-2"
|
||||
required
|
||||
autofocus
|
||||
counter
|
||||
maxlength="20"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-btn variant="text" size="small" class="mb-4" color="default">+ Add Description (Optional)</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- <v-row>
|
||||
<v-col
|
||||
cols="12"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="description"
|
||||
variant="outlined"
|
||||
label="Project Description (Optional)"
|
||||
class="mt-2"
|
||||
multiple
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row> -->
|
||||
</v-form>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-card-actions class="pa-7">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-btn variant="outlined" color="default" block @click="dialog = false">Cancel</v-btn>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn color="primary" variant="flat" block>Create Project</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
||||
@ -182,7 +88,7 @@
|
||||
<v-row v-else>
|
||||
<!-- Card view -->
|
||||
<v-col v-if="!items.length" cols="12" sm="6" md="4" lg="3">
|
||||
<ProjectCard class="h-100" />
|
||||
<ProjectCard class="h-100" @create-click="isCreateProjectDialogShown = true" />
|
||||
</v-col>
|
||||
<v-col v-for="item in items" v-else :key="item.id" cols="12" sm="6" md="4" lg="3">
|
||||
<ProjectCard :item="item" class="h-100" @join-click="onJoinClicked(item)" />
|
||||
@ -196,6 +102,7 @@
|
||||
v-model="isJoinProjectDialogShown"
|
||||
:name="joiningItem.name"
|
||||
/>
|
||||
<create-project-dialog v-model="isCreateProjectDialogShown" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -229,6 +136,7 @@ import ProjectCard from '@poc/components/ProjectCard.vue';
|
||||
import PageTitleComponent from '@poc/components/PageTitleComponent.vue';
|
||||
import ProjectsTableComponent from '@poc/components/ProjectsTableComponent.vue';
|
||||
import JoinProjectDialog from '@poc/components/dialogs/JoinProjectDialog.vue';
|
||||
import CreateProjectDialog from '@poc/components/dialogs/CreateProjectDialog.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
@ -241,6 +149,7 @@ const isLoading = ref<boolean>(true);
|
||||
|
||||
const joiningItem = ref<ProjectItemModel | null>(null);
|
||||
const isJoinProjectDialogShown = ref<boolean>(false);
|
||||
const isCreateProjectDialogShown = ref<boolean>(false);
|
||||
|
||||
const nameRules = [
|
||||
value => (!!value || 'Project name is required.'),
|
||||
|
Loading…
Reference in New Issue
Block a user