-
Notifications
You must be signed in to change notification settings - Fork 387
/
Projects.vue
251 lines (225 loc) · 8.52 KB
/
Projects.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<v-container>
<trial-expiration-banner v-if="isTrialExpirationBanner" :expired="isExpired" />
<low-token-balance-banner
v-if="isLowBalance && billingEnabled"
cta-label="Go to billing"
@click="redirectToBilling"
/>
<PageTitleComponent title="All Projects" />
<v-row>
<v-col>
<v-btn
class="mr-3"
color="default"
variant="outlined"
density="comfortable"
@click="newProjectClicked"
>
<IconNew class="mr-2" size="14" bold />
New Project
</v-btn>
</v-col>
<template v-if="items.length">
<v-spacer />
<v-col class="text-right">
<v-btn-toggle
mandatory
border
inset
density="comfortable"
class="pa-1"
>
<v-btn
size="small"
rounded="xl"
active-class="active"
:active="!isTableView"
aria-label="Toggle Cards View"
@click="isTableView = false"
>
<icon-card-view />
Cards
</v-btn>
<v-btn
size="small"
rounded="xl"
active-class="active"
:active="isTableView"
aria-label="Toggle Table View"
@click="isTableView = true"
>
<icon-table-view />
List
</v-btn>
</v-btn-toggle>
</v-col>
</template>
</v-row>
<v-row v-if="isTableView">
<v-col>
<ProjectsTableComponent :items="items" @join-click="onJoinClicked" @invite-click="(item) => onInviteClicked(item)" />
</v-col>
</v-row>
<v-row v-else>
<v-col v-if="!items.length" cols="12" sm="6" md="4" lg="3">
<ProjectCard class="h-100" @create-click="newProjectClicked" />
</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)" @invite-click="onInviteClicked(item)" />
</v-col>
</v-row>
</v-container>
<join-project-dialog
v-if="joiningItem"
:id="joiningItem.id"
v-model="isJoinProjectDialogShown"
:name="joiningItem.name"
/>
<create-project-dialog v-model="isCreateProjectDialogShown" />
<add-team-member-dialog v-model="isAddMemberDialogShown" :project-id="addMemberProjectId" />
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import {
VContainer,
VRow,
VCol,
VBtn,
VSpacer,
VBtnToggle,
} from 'vuetify/components';
import { useRouter } from 'vue-router';
import { ProjectItemModel } from '@/types/projects';
import { useProjectsStore } from '@/store/modules/projectsStore';
import { useUsersStore } from '@/store/modules/usersStore';
import { ProjectRole } from '@/types/projectMembers';
import { useAppStore } from '@/store/modules/appStore';
import { useLowTokenBalance } from '@/composables/useLowTokenBalance';
import { useConfigStore } from '@/store/modules/configStore';
import { useBillingStore } from '@/store/modules/billingStore';
import { ROUTES } from '@/router';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { Dimensions, Size } from '@/utils/bytesSize';
import { useTrialCheck } from '@/composables/useTrialCheck';
import ProjectCard from '@/components/ProjectCard.vue';
import PageTitleComponent from '@/components/PageTitleComponent.vue';
import ProjectsTableComponent from '@/components/ProjectsTableComponent.vue';
import JoinProjectDialog from '@/components/dialogs/JoinProjectDialog.vue';
import CreateProjectDialog from '@/components/dialogs/CreateProjectDialog.vue';
import AddTeamMemberDialog from '@/components/dialogs/AddTeamMemberDialog.vue';
import IconCardView from '@/components/icons/IconCardView.vue';
import IconTableView from '@/components/icons/IconTableView.vue';
import IconNew from '@/components/icons/IconNew.vue';
import LowTokenBalanceBanner from '@/components/LowTokenBalanceBanner.vue';
import TrialExpirationBanner from '@/components/TrialExpirationBanner.vue';
const analyticsStore = useAnalyticsStore();
const appStore = useAppStore();
const projectsStore = useProjectsStore();
const usersStore = useUsersStore();
const configStore = useConfigStore();
const billingStore = useBillingStore();
const router = useRouter();
const isLowBalance = useLowTokenBalance();
const { isTrialExpirationBanner, isExpired, withTrialCheck } = useTrialCheck();
const joiningItem = ref<ProjectItemModel | null>(null);
const isJoinProjectDialogShown = ref<boolean>(false);
const isCreateProjectDialogShown = ref<boolean>(false);
const addMemberProjectId = ref<string>('');
const isAddMemberDialogShown = ref<boolean>(false);
/**
* Indicates if billing features are enabled.
*/
const billingEnabled = computed<boolean>(() => configStore.getBillingEnabled(usersStore.state.user.hasVarPartner));
/**
* Returns whether to use the table view.
*/
const isTableView = computed<boolean>({
get: () => {
if (!items.value.length) return false;
if (!appStore.hasProjectTableViewConfigured() && items.value.length > 8) return true;
return appStore.state.isProjectTableViewEnabled;
},
set: value => appStore.toggleProjectTableViewEnabled(value),
});
/**
* Returns the project items from the store.
*/
const items = computed((): ProjectItemModel[] => {
const projects: ProjectItemModel[] = [];
projects.push(...projectsStore.state.invitations.map<ProjectItemModel>(invite => new ProjectItemModel(
invite.projectID,
invite.projectName,
invite.projectDescription,
ProjectRole.Invited,
null,
invite.createdAt,
)));
projects.push(...projectsStore.projects.map<ProjectItemModel>(project => new ProjectItemModel(
project.id,
project.name,
project.description,
project.ownerId === usersStore.state.user.id ? ProjectRole.Owner : ProjectRole.Member,
project.memberCount,
new Date(project.createdAt),
formattedValue(new Size(project.storageUsed, 2)),
formattedValue(new Size(project.bandwidthUsed, 2)),
)).sort((projA, projB) => {
if (projA.role === ProjectRole.Owner && projB.role === ProjectRole.Member) return -1;
if (projA.role === ProjectRole.Member && projB.role === ProjectRole.Owner) return 1;
return 0;
}));
return projects;
});
function newProjectClicked() {
withTrialCheck(() => {
analyticsStore.eventTriggered(AnalyticsEvent.NEW_PROJECT_CLICKED);
isCreateProjectDialogShown.value = true;
}, true);
}
/**
* Redirects to Billing Page tab.
*/
function redirectToBilling(): void {
router.push({ name: ROUTES.Billing.name });
}
/**
* Displays the Join Project modal.
*/
function onJoinClicked(item: ProjectItemModel): void {
joiningItem.value = item;
isJoinProjectDialogShown.value = true;
}
/**
* Displays the Add Members dialog.
*/
function onInviteClicked(item: ProjectItemModel): void {
withTrialCheck(() => {
addMemberProjectId.value = item.id;
isAddMemberDialogShown.value = true;
}, true);
}
/**
* Formats value to needed form and returns it.
*/
function formattedValue(value: Size): string {
switch (value.label) {
case Dimensions.Bytes:
return '0';
default:
return `${value.formattedBytes.replace(/\.0+$/, '')}${value.label}`;
}
}
onMounted(() => {
if (configStore.state.config.nativeTokenPaymentsEnabled && billingEnabled.value) {
Promise.all([
billingStore.getBalance(),
billingStore.getCreditCards(),
billingStore.getNativePaymentsHistory(),
]).catch(_ => {});
}
});
</script>