diff --git a/web/satellite/src/App.vue b/web/satellite/src/App.vue
index 07271023032e..a47878a5c20e 100644
--- a/web/satellite/src/App.vue
+++ b/web/satellite/src/App.vue
@@ -8,7 +8,7 @@
@@ -54,7 +54,6 @@ const theme = useTheme();
const route = useRoute();
const isLoading = ref(true);
-const isTrialExpirationDialogShown = ref(false);
/**
* Indicates whether an error page should be shown in place of the router view.
@@ -155,7 +154,7 @@ usersStore.$onAction(({ name, after }) => {
const expirationInfo = user.value.getExpirationInfo(configStore.state.config.daysBeforeTrialEndNotification);
if (user.value.freezeStatus.trialExpiredFrozen || expirationInfo.isCloseToExpiredTrial) {
- isTrialExpirationDialogShown.value = true;
+ appStore.toggleExpirationDialog(true);
}
});
});
diff --git a/web/satellite/src/components/ApplicationItem.vue b/web/satellite/src/components/ApplicationItem.vue
index 79c2eb862eab..2893b668a6dc 100644
--- a/web/satellite/src/components/ApplicationItem.vue
+++ b/web/satellite/src/components/ApplicationItem.vue
@@ -46,6 +46,7 @@ import { mdiArrowRight, mdiOpenInNew } from '@mdi/js';
import { Application } from '@/types/applications';
import { AccessType, Exposed } from '@/types/createAccessGrant';
+import { useTrialCheck } from '@/composables/useTrialCheck';
import CreateAccessDialog from '@/components/dialogs/CreateAccessDialog.vue';
@@ -53,6 +54,8 @@ const props = defineProps<{
app: Application
}>();
+const { withTrialCheck } = useTrialCheck();
+
const accessDialog = ref();
const dialog = ref(false);
@@ -61,7 +64,9 @@ const dialog = ref(false);
* Starts create S3 credentials flow.
*/
function onSetup(): void {
- accessDialog.value?.setTypes([AccessType.S3]);
- dialog.value = true;
+ withTrialCheck(() => {
+ accessDialog.value?.setTypes([AccessType.S3]);
+ dialog.value = true;
+ });
}
diff --git a/web/satellite/src/components/BucketsDataTable.vue b/web/satellite/src/components/BucketsDataTable.vue
index cb3621375abf..a92770a0a1ad 100644
--- a/web/satellite/src/components/BucketsDataTable.vue
+++ b/web/satellite/src/components/BucketsDataTable.vue
@@ -172,6 +172,7 @@ import { RouteConfig } from '@/types/router';
import { EdgeCredentials } from '@/types/accessGrants';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { ROUTES } from '@/router';
+import { useTrialCheck } from '@/composables/useTrialCheck';
import IconTrash from '@/components/icons/IconTrash.vue';
import IconShare from '@/components/icons/IconShare.vue';
@@ -187,8 +188,10 @@ const analyticsStore = useAnalyticsStore();
const bucketsStore = useBucketsStore();
const projectsStore = useProjectsStore();
const configStore = useConfigStore();
+
const notify = useNotify();
const router = useRouter();
+const { withTrialCheck } = useTrialCheck();
const FIRST_PAGE = 1;
const areBucketsFetching = ref(true);
@@ -412,33 +415,35 @@ function onUpdateSort(value: SortItem[]): void {
/**
* Navigates to bucket page.
*/
-async function openBucket(bucketName: string): Promise {
- if (!bucketName) {
- return;
- }
- bucketsStore.setFileComponentBucketName(bucketName);
- if (!promptForPassphrase.value) {
- if (!edgeCredentials.value.accessKeyId) {
- try {
- await bucketsStore.setS3Client(projectsStore.state.selectedProject.id);
- } catch (error) {
- notify.notifyError(error, AnalyticsErrorEventSource.BUCKET_TABLE);
- return;
- }
+function openBucket(bucketName: string): void {
+ withTrialCheck(async () => {
+ if (!bucketName) {
+ return;
}
+ bucketsStore.setFileComponentBucketName(bucketName);
+ if (!promptForPassphrase.value) {
+ if (!edgeCredentials.value.accessKeyId) {
+ try {
+ await bucketsStore.setS3Client(projectsStore.state.selectedProject.id);
+ } catch (error) {
+ notify.notifyError(error, AnalyticsErrorEventSource.BUCKET_TABLE);
+ return;
+ }
+ }
- analyticsStore.pageVisit(RouteConfig.Buckets.with(RouteConfig.UploadFile).path);
- await router.push({
- name: ROUTES.Bucket.name,
- params: {
- browserPath: bucketsStore.state.fileComponentBucketName,
- id: projectsStore.state.selectedProject.urlId,
- },
- });
- return;
- }
- passphraseDialogCallback = () => openBucket(selectedBucketName.value);
- isBucketPassphraseDialogOpen.value = true;
+ analyticsStore.pageVisit(RouteConfig.Buckets.with(RouteConfig.UploadFile).path);
+ await router.push({
+ name: ROUTES.Bucket.name,
+ params: {
+ browserPath: bucketsStore.state.fileComponentBucketName,
+ id: projectsStore.state.selectedProject.urlId,
+ },
+ });
+ return;
+ }
+ passphraseDialogCallback = () => openBucket(selectedBucketName.value);
+ isBucketPassphraseDialogOpen.value = true;
+ });
}
/**
@@ -461,14 +466,16 @@ function showDeleteBucketDialog(bucketName: string): void {
* Displays the Share Bucket dialog.
*/
function showShareBucketDialog(bucketName: string): void {
- shareBucketName.value = bucketName;
- if (promptForPassphrase.value) {
- bucketsStore.setFileComponentBucketName(bucketName);
- isBucketPassphraseDialogOpen.value = true;
- passphraseDialogCallback = () => isShareBucketDialogShown.value = true;
- return;
- }
- isShareBucketDialogShown.value = true;
+ withTrialCheck(() => {
+ shareBucketName.value = bucketName;
+ if (promptForPassphrase.value) {
+ bucketsStore.setFileComponentBucketName(bucketName);
+ isBucketPassphraseDialogOpen.value = true;
+ passphraseDialogCallback = () => isShareBucketDialogShown.value = true;
+ return;
+ }
+ isShareBucketDialogShown.value = true;
+ });
}
/**
diff --git a/web/satellite/src/components/billing/BillingHistoryTab.vue b/web/satellite/src/components/billing/BillingHistoryTab.vue
index 8d7e6f01bef9..051c9f6dd428 100644
--- a/web/satellite/src/components/billing/BillingHistoryTab.vue
+++ b/web/satellite/src/components/billing/BillingHistoryTab.vue
@@ -7,6 +7,7 @@
:loading="isLoading"
:headers="headers"
:items="historyItems"
+ :items-length="historyItems.length"
:must-sort="false"
no-data-text="No results found"
hover
diff --git a/web/satellite/src/components/dialogs/DetailedUsageReportDialog.vue b/web/satellite/src/components/dialogs/DetailedUsageReportDialog.vue
index 93ab45238835..afd96050bb52 100644
--- a/web/satellite/src/components/dialogs/DetailedUsageReportDialog.vue
+++ b/web/satellite/src/components/dialogs/DetailedUsageReportDialog.vue
@@ -101,7 +101,7 @@ const projectsStore = useProjectsStore();
const notify = useNotify();
const props = withDefaults(defineProps<{
- projectID: string
+ projectID?: string
}>(), {
projectID: '',
});
diff --git a/web/satellite/src/components/onboarding/OnboardingStepperComponent.vue b/web/satellite/src/components/onboarding/OnboardingStepperComponent.vue
index 90d14d14d332..e7b9d4825d01 100644
--- a/web/satellite/src/components/onboarding/OnboardingStepperComponent.vue
+++ b/web/satellite/src/components/onboarding/OnboardingStepperComponent.vue
@@ -21,7 +21,7 @@
:disabled="currentStep !== OnboardingStep.EncryptionPassphrase"
:prepend-icon="isPassphraseDone ? mdiCheck : ''"
block
- @click="isManagePassphraseDialogOpen = true"
+ @click="onManagePassphrase"
>
Set a Passphrase
@@ -45,7 +45,7 @@
:disabled="currentStep !== OnboardingStep.CreateBucket"
:prepend-icon="isBucketDone ? mdiCheck : ''"
block
- @click="isBucketDialogOpen = true"
+ @click="onCreateBucket"
>
Create a Bucket
@@ -139,6 +139,7 @@ import { useNotify } from '@/utils/hooks';
import { ROUTES } from '@/router';
import { OnboardingInfo } from '@/types/common';
import { AccessType, Exposed } from '@/types/createAccessGrant';
+import { useTrialCheck } from '@/composables/useTrialCheck';
import CreateBucketDialog from '@/components/dialogs/CreateBucketDialog.vue';
import NewAccessDialog from '@/components/dialogs/CreateAccessDialog.vue';
@@ -153,6 +154,7 @@ const userStore = useUsersStore();
const notify = useNotify();
const router = useRouter();
+const { withTrialCheck } = useTrialCheck();
let passphraseDialogCallback: () => void = () => {};
@@ -243,28 +245,48 @@ const edgeCredentials = computed((): EdgeCredentials => {
return bucketsStore.state.edgeCredentials;
});
+/**
+ * Starts set passphrase flow if user's free trial is not expired.
+ */
+function onManagePassphrase(): void {
+ withTrialCheck(() => {
+ isManagePassphraseDialogOpen.value = true;
+ });
+}
+
+/**
+ * Starts create bucket flow if user's free trial is not expired.
+ */
+function onCreateBucket(): void {
+ withTrialCheck(() => {
+ isBucketDialogOpen.value = true;
+ });
+}
+
/**
* Opens the file browser for the bucket being tracked in onboarding if any
* or select the only bucket the user has created
* or redirect to the buckets list.
*/
-async function uploadFilesClicked() {
- if (trackedBucketName.value) {
- await openTrackedBucket();
- } else {
- await bucketsStore.getBuckets(1, projectsStore.state.selectedProject.id);
- const buckets = bucketsStore.state.page.buckets;
- if (buckets.length === 1) {
- trackedBucketName.value = buckets[0].name;
+function uploadFilesClicked(): void {
+ withTrialCheck(async () => {
+ if (trackedBucketName.value) {
await openTrackedBucket();
} else {
- await progressStep();
- await router.push({
- name: ROUTES.Buckets.name,
- params: { id: selectedProject.value.urlId },
- });
+ await bucketsStore.getBuckets(1, projectsStore.state.selectedProject.id);
+ const buckets = bucketsStore.state.page.buckets;
+ if (buckets.length === 1) {
+ trackedBucketName.value = buckets[0].name;
+ await openTrackedBucket();
+ } else {
+ await progressStep();
+ await router.push({
+ name: ROUTES.Buckets.name,
+ params: { id: selectedProject.value.urlId },
+ });
+ }
}
- }
+ });
}
/**
@@ -299,22 +321,24 @@ async function openTrackedBucket(): Promise {
isBucketPassphraseDialogOpen.value = true;
}
-function onBucketCreated(bucketName: string) {
+function onBucketCreated(bucketName: string): void {
trackedBucketName.value = bucketName;
progressStep();
}
-async function openAccessDialog() {
- if (currentStep.value === OnboardingStep.UploadFiles) {
- // progress to access step so the onboarding will
- // end correctly after the access is created.
- await progressStep();
- }
- accessDialog.value?.setTypes([AccessType.S3]);
- isAccessDialogOpen.value = true;
+function openAccessDialog(): void {
+ withTrialCheck(async () => {
+ if (currentStep.value === OnboardingStep.UploadFiles) {
+ // progress to access step so the onboarding will
+ // end correctly after the access is created.
+ await progressStep();
+ }
+ accessDialog.value?.setTypes([AccessType.S3]);
+ isAccessDialogOpen.value = true;
+ });
}
-function onAccessCreated() {
+function onAccessCreated(): void {
// arbitrary delay so the disappear animation
// of the access dialog is visible
setTimeout(() => progressStep(true), 500);
@@ -325,7 +349,7 @@ function onAccessCreated() {
* and saves the progress in the user settings, conditionally
* ending the onboarding.
*/
-async function progressStep(onboardingEnd = false) {
+async function progressStep(onboardingEnd = false): Promise {
let onboardingStep = currentStep.value;
switch (userSettings.value.onboardingStep) {
case OnboardingStep.EncryptionPassphrase:
@@ -357,7 +381,7 @@ async function progressStep(onboardingEnd = false) {
/**
* Dismisses the onboarding stepper and abandons the onboarding process.
*/
-async function endOnboarding() {
+async function endOnboarding(): Promise {
try {
await userStore.updateSettings({ onboardingEnd: true });
analyticsStore.eventTriggered(AnalyticsEvent.ONBOARDING_ABANDONED);
diff --git a/web/satellite/src/composables/useTrialCheck.ts b/web/satellite/src/composables/useTrialCheck.ts
new file mode 100644
index 000000000000..aaf6a0569f84
--- /dev/null
+++ b/web/satellite/src/composables/useTrialCheck.ts
@@ -0,0 +1,43 @@
+// Copyright (C) 2024 Storj Labs, Inc.
+// See LICENSE for copying information.
+
+import { computed } from 'vue';
+
+import { useUsersStore } from '@/store/modules/usersStore';
+import { useAppStore } from '@/store/modules/appStore';
+import { useConfigStore } from '@/store/modules/configStore';
+import { User } from '@/types/users';
+
+export function useTrialCheck() {
+ const userStore = useUsersStore();
+ const appStore = useAppStore();
+ const configStore = useConfigStore();
+
+ const user = computed(() => userStore.state.user);
+
+ const isTrialExpirationBanner = computed(() => {
+ if (user.value.paidTier) return false;
+
+ const expirationInfo = user.value.getExpirationInfo(configStore.state.config.daysBeforeTrialEndNotification);
+
+ return user.value.freezeStatus.trialExpiredFrozen || expirationInfo.isCloseToExpiredTrial;
+ });
+
+ const isExpired = computed(() => user.value.freezeStatus.trialExpiredFrozen);
+
+ function withTrialCheck(callback: () => void | Promise): void {
+ const user = userStore.state.user;
+ if (!user.paidTier && user.freezeStatus.trialExpiredFrozen) {
+ appStore.toggleExpirationDialog(true);
+ return;
+ }
+
+ callback();
+ }
+
+ return {
+ isTrialExpirationBanner,
+ isExpired,
+ withTrialCheck,
+ };
+}
diff --git a/web/satellite/src/layouts/default/ProjectNav.vue b/web/satellite/src/layouts/default/ProjectNav.vue
index 540b6f5cef17..d2b64e143380 100644
--- a/web/satellite/src/layouts/default/ProjectNav.vue
+++ b/web/satellite/src/layouts/default/ProjectNav.vue
@@ -106,7 +106,7 @@
-
+
@@ -267,6 +267,7 @@ import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { ROUTES } from '@/router';
import { useConfigStore } from '@/store/modules/configStore';
+import { useTrialCheck } from '@/composables/useTrialCheck';
import IconProject from '@/components/icons/IconProject.vue';
import IconSettings from '@/components/icons/IconSettings.vue';
@@ -298,6 +299,7 @@ const bucketsStore = useBucketsStore();
const route = useRoute();
const router = useRouter();
+const { withTrialCheck } = useTrialCheck();
const { mdAndDown } = useDisplay();
const model = computed({
@@ -329,8 +331,6 @@ const teamURL = computed(() => `${projectURLBase.value}/${ROUTES.Team.pa
const appsURL = computed(() => `${projectURLBase.value}/${ROUTES.Applications.path}`);
-const isAppsPage = computed(() => configStore.state.config.applicationsPageEnabled);
-
/**
* Returns user's own projects.
*/
@@ -396,6 +396,15 @@ function trackViewSupportEvent(link: string): void {
window.open(link);
}
+/**
+ * Starts create project flow if user's free trial is not expired.
+ */
+function onCreateProject() {
+ withTrialCheck(() => {
+ isCreateProjectDialogShown.value = true;
+ });
+}
+
/**
* This comparator is used to sort projects by isSelected.
*/
diff --git a/web/satellite/src/router/index.ts b/web/satellite/src/router/index.ts
index 06c585ed6bb1..6dc7740f3ff9 100644
--- a/web/satellite/src/router/index.ts
+++ b/web/satellite/src/router/index.ts
@@ -147,6 +147,7 @@ const routes: RouteRecordRaw[] = [
children: [
{
path: '',
+ name: RouteName.Project,
redirect: (to: RouteLocation) => {
const projRoute = new NavigationLink(to.params.id as string, RouteName.Project);
return { path: ROUTES.Projects.with(projRoute).with(ROUTES.Dashboard).path };
diff --git a/web/satellite/src/store/modules/appStore.ts b/web/satellite/src/store/modules/appStore.ts
index 4e7f96f5d98a..877b553308b8 100644
--- a/web/satellite/src/store/modules/appStore.ts
+++ b/web/satellite/src/store/modules/appStore.ts
@@ -14,6 +14,7 @@ class AppState {
public isBrowserCardViewEnabled = LocalData.getBrowserCardViewEnabled();
public isNavigationDrawerShown = true;
public isUpgradeFlowDialogShown = false;
+ public isExpirationDialogShown = false;
public isAccountSetupDialogShown = false;
public isProjectPassphraseDialogShown = false;
public pathBeforeAccountPage: string | null = null;
@@ -73,6 +74,10 @@ export const useAppStore = defineStore('app', () => {
state.isUpgradeFlowDialogShown = isShown ?? !state.isUpgradeFlowDialogShown;
}
+ function toggleExpirationDialog(isShown?: boolean): void {
+ state.isExpirationDialogShown = isShown ?? !state.isExpirationDialogShown;
+ }
+
function toggleAccountSetup(isShown?: boolean): void {
state.isAccountSetupDialogShown = isShown ?? !state.isAccountSetupDialogShown;
}
@@ -116,6 +121,7 @@ export const useAppStore = defineStore('app', () => {
hasProjectTableViewConfigured,
toggleHasJustLoggedIn,
toggleProjectPassphraseDialog,
+ toggleExpirationDialog,
setUploadingModal,
setErrorPage,
removeErrorPage,
diff --git a/web/satellite/src/views/Access.vue b/web/satellite/src/views/Access.vue
index f94dd0ff59a1..072dea8e0bfd 100644
--- a/web/satellite/src/views/Access.vue
+++ b/web/satellite/src/views/Access.vue
@@ -3,12 +3,14 @@
+
+
-
+
New Access Key
@@ -29,11 +31,25 @@ import {
VBtn,
} from 'vuetify/components';
+import { useTrialCheck } from '@/composables/useTrialCheck';
+
import NewAccessDialog from '@/components/dialogs/CreateAccessDialog.vue';
import PageTitleComponent from '@/components/PageTitleComponent.vue';
import PageSubtitleComponent from '@/components/PageSubtitleComponent.vue';
import AccessTableComponent from '@/components/AccessTableComponent.vue';
import IconNew from '@/components/icons/IconNew.vue';
+import TrialExpirationBanner from '@/components/TrialExpirationBanner.vue';
const dialog = ref(false);
+
+const { isTrialExpirationBanner, isExpired, withTrialCheck } = useTrialCheck();
+
+/**
+ * Starts create access grant flow if user's free trial is not expired.
+ */
+function onCreateAccess(): void {
+ withTrialCheck(() => {
+ dialog.value = true;
+ });
+}
diff --git a/web/satellite/src/views/AccountSettings.vue b/web/satellite/src/views/AccountSettings.vue
index d1e02e56537e..046851e19585 100644
--- a/web/satellite/src/views/AccountSettings.vue
+++ b/web/satellite/src/views/AccountSettings.vue
@@ -5,6 +5,8 @@
+
+
@@ -169,6 +171,7 @@ import { useNotify } from '@/utils/hooks';
import { Duration } from '@/utils/time';
import { ROUTES } from '@/router';
import { useConfigStore } from '@/store/modules/configStore';
+import { useTrialCheck } from '@/composables/useTrialCheck';
import PageTitleComponent from '@/components/PageTitleComponent.vue';
import ChangePasswordDialog from '@/components/dialogs/ChangePasswordDialog.vue';
@@ -177,11 +180,14 @@ import EnableMFADialog from '@/components/dialogs/EnableMFADialog.vue';
import DisableMFADialog from '@/components/dialogs/DisableMFADialog.vue';
import MFACodesDialog from '@/components/dialogs/MFACodesDialog.vue';
import SetSessionTimeoutDialog from '@/components/dialogs/SetSessionTimeoutDialog.vue';
+import TrialExpirationBanner from '@/components/TrialExpirationBanner.vue';
const appStore = useAppStore();
const configStore = useConfigStore();
const usersStore = useUsersStore();
+
const notify = useNotify();
+const { isTrialExpirationBanner, isExpired } = useTrialCheck();
const isChangePasswordDialogShown = ref(false);
const isChangeNameDialogShown = ref(false);
@@ -213,7 +219,7 @@ const userSettings = computed((): UserSettings => {
* Returns user's paid tier status from store.
*/
const isPaidTier = computed(() => {
- return usersStore.state.user.paidTier;
+ return user.value.paidTier;
});
async function toggleEnableMFADialog() {
diff --git a/web/satellite/src/views/Applications.vue b/web/satellite/src/views/Applications.vue
index e7adbcf3c40f..6b586fbdfeef 100644
--- a/web/satellite/src/views/Applications.vue
+++ b/web/satellite/src/views/Applications.vue
@@ -5,6 +5,8 @@
+
+
([AppCategory.All]);
diff --git a/web/satellite/src/views/Buckets.vue b/web/satellite/src/views/Buckets.vue
index b1b386d82d16..9d4842575e27 100644
--- a/web/satellite/src/views/Buckets.vue
+++ b/web/satellite/src/views/Buckets.vue
@@ -3,6 +3,8 @@
+
+
-
+
New Bucket
@@ -36,11 +35,25 @@ import {
VBtn,
} from 'vuetify/components';
+import { useTrialCheck } from '@/composables/useTrialCheck';
+
import PageTitleComponent from '@/components/PageTitleComponent.vue';
import PageSubtitleComponent from '@/components/PageSubtitleComponent.vue';
import BucketsDataTable from '@/components/BucketsDataTable.vue';
import CreateBucketDialog from '@/components/dialogs/CreateBucketDialog.vue';
import IconNew from '@/components/icons/IconNew.vue';
+import TrialExpirationBanner from '@/components/TrialExpirationBanner.vue';
+
+const { isTrialExpirationBanner, isExpired, withTrialCheck } = useTrialCheck();
const isCreateBucketDialogOpen = ref(false);
+
+/**
+ * Starts create bucket flow if user's free trial is not expired.
+ */
+function onCreateBucket(): void {
+ withTrialCheck(() => {
+ isCreateBucketDialogOpen.value = true;
+ });
+}
diff --git a/web/satellite/src/views/Dashboard.vue b/web/satellite/src/views/Dashboard.vue
index 9b3a8576f8e1..7e32ef884a23 100644
--- a/web/satellite/src/views/Dashboard.vue
+++ b/web/satellite/src/views/Dashboard.vue
@@ -3,7 +3,7 @@
-
+
@@ -226,7 +226,7 @@
New Bucket
@@ -295,7 +295,7 @@ import { useLowTokenBalance } from '@/composables/useLowTokenBalance';
import { ROUTES } from '@/router';
import { AccountBalance, CreditCard } from '@/types/payments';
import { useLoading } from '@/composables/useLoading';
-import { User } from '@/types/users';
+import { useTrialCheck } from '@/composables/useTrialCheck';
import PageTitleComponent from '@/components/PageTitleComponent.vue';
import PageSubtitleComponent from '@/components/PageSubtitleComponent.vue';
@@ -335,6 +335,7 @@ const notify = useNotify();
const router = useRouter();
const isLowBalance = useLowTokenBalance();
const { isLoading, withLoading } = useLoading();
+const { isTrialExpirationBanner, isExpired, withTrialCheck } = useTrialCheck();
const chartWidth = ref(0);
const chartContainer = ref();
@@ -345,22 +346,6 @@ const isCreateBucketDialogOpen = ref(false);
const isDatePicker = ref(false);
const datePickerModel = ref([]);
-/**
- * Returns user entity from store.
- */
-const user = computed(() => usersStore.state.user);
-
-/**
- * Indicates if trial expiration banner is shown.
- */
-const trialExpirationBanner = computed(() => {
- if (user.value.paidTier) return false;
-
- const expirationInfo = user.value.getExpirationInfo(configStore.state.config.daysBeforeTrialEndNotification);
-
- return user.value.freezeStatus.trialExpiredFrozen || expirationInfo.isCloseToExpiredTrial;
-});
-
/**
* Returns formatted CO2 estimated info.
*/
@@ -620,6 +605,15 @@ function getValueAndUnit(value: number): ValueUnit {
return { value: newValue, unit };
}
+/**
+ * Starts create bucket flow if user's free trial is not expired.
+ */
+function onCreateBucket(): void {
+ withTrialCheck(() => {
+ isCreateBucketDialogOpen.value = true;
+ });
+}
+
/**
* Returns formatted amount.
*/
diff --git a/web/satellite/src/views/ProjectSettings.vue b/web/satellite/src/views/ProjectSettings.vue
index 0bd844910fa3..8884feaf2eed 100644
--- a/web/satellite/src/views/ProjectSettings.vue
+++ b/web/satellite/src/views/ProjectSettings.vue
@@ -5,6 +5,8 @@
+
+
@@ -161,11 +163,13 @@ import { decimalShift } from '@/utils/strings';
import { useNotify } from '@/utils/hooks';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { useAppStore } from '@/store/modules/appStore';
+import { useTrialCheck } from '@/composables/useTrialCheck';
import EditProjectDetailsDialog from '@/components/dialogs/EditProjectDetailsDialog.vue';
import EditProjectLimitDialog from '@/components/dialogs/EditProjectLimitDialog.vue';
import PageTitleComponent from '@/components/PageTitleComponent.vue';
import PageSubtitleComponent from '@/components/PageSubtitleComponent.vue';
+import TrialExpirationBanner from '@/components/TrialExpirationBanner.vue';
const isEditDetailsDialogShown = ref(false);
const isEditLimitDialogShown = ref(false);
@@ -178,6 +182,7 @@ const usersStore = useUsersStore();
const configStore = useConfigStore();
const notify = useNotify();
+const { isTrialExpirationBanner, isExpired } = useTrialCheck();
/**
* Indicates if billing features are enabled.
diff --git a/web/satellite/src/views/Projects.vue b/web/satellite/src/views/Projects.vue
index ab094b76d461..0daf0f481008 100644
--- a/web/satellite/src/views/Projects.vue
+++ b/web/satellite/src/views/Projects.vue
@@ -3,13 +3,14 @@
+
+
-
@@ -29,7 +30,6 @@
-
-
onInviteClicked(item)" />
-
-
+
@@ -116,6 +114,7 @@ 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';
@@ -127,6 +126,7 @@ 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();
@@ -137,6 +137,7 @@ const billingStore = useBillingStore();
const router = useRouter();
const isLowBalance = useLowTokenBalance();
+const { isTrialExpirationBanner, isExpired, withTrialCheck } = useTrialCheck();
const joiningItem = ref(null);
const isJoinProjectDialogShown = ref(false);
@@ -195,8 +196,10 @@ const items = computed((): ProjectItemModel[] => {
});
function newProjectClicked() {
- analyticsStore.eventTriggered(AnalyticsEvent.NEW_PROJECT_CLICKED);
- isCreateProjectDialogShown.value = true;
+ withTrialCheck(() => {
+ analyticsStore.eventTriggered(AnalyticsEvent.NEW_PROJECT_CLICKED);
+ isCreateProjectDialogShown.value = true;
+ });
}
/**
@@ -210,16 +213,20 @@ function redirectToBilling(): void {
* Displays the Join Project modal.
*/
function onJoinClicked(item: ProjectItemModel): void {
- joiningItem.value = item;
- isJoinProjectDialogShown.value = true;
+ withTrialCheck(() => {
+ joiningItem.value = item;
+ isJoinProjectDialogShown.value = true;
+ });
}
/**
* Displays the Add Members dialog.
*/
function onInviteClicked(item: ProjectItemModel): void {
- addMemberProjectId.value = item.id;
- isAddMemberDialogShown.value = true;
+ withTrialCheck(() => {
+ addMemberProjectId.value = item.id;
+ isAddMemberDialogShown.value = true;
+ });
}
/**
diff --git a/web/satellite/src/views/Team.vue b/web/satellite/src/views/Team.vue
index e358a26d5958..86a9116cc5e7 100644
--- a/web/satellite/src/views/Team.vue
+++ b/web/satellite/src/views/Team.vue
@@ -3,6 +3,8 @@
+
+
-
+
Add Members
@@ -29,16 +31,29 @@ import { computed, ref } from 'vue';
import { VContainer, VCol, VRow, VBtn } from 'vuetify/components';
import { useProjectsStore } from '@/store/modules/projectsStore';
+import { useTrialCheck } from '@/composables/useTrialCheck';
import PageTitleComponent from '@/components/PageTitleComponent.vue';
import PageSubtitleComponent from '@/components/PageSubtitleComponent.vue';
import TeamTableComponent from '@/components/TeamTableComponent.vue';
import AddTeamMemberDialog from '@/components/dialogs/AddTeamMemberDialog.vue';
import IconNew from '@/components/icons/IconNew.vue';
+import TrialExpirationBanner from '@/components/TrialExpirationBanner.vue';
const projectsStore = useProjectsStore();
+const { isTrialExpirationBanner, isExpired, withTrialCheck } = useTrialCheck();
+
const isAddMemberDialogShown = ref(false);
const selectedProjectID = computed((): string => projectsStore.state.selectedProject.id);
+
+/**
+ * Starts create bucket flow if user's free trial is not expired.
+ */
+function onAddMember(): void {
+ withTrialCheck(() => {
+ isAddMemberDialogShown.value = true;
+ });
+}