web/satellite: properly style all projects dashboard
This change matches the all projects dashboard styling to the figma as closely as currently possible. Issue: https://github.com/storj/storj/issues/5934 Change-Id: I93edd898fbcee954265737052a1967d318370924
This commit is contained in:
parent
8cdc5bd107
commit
3d1e429156
@ -6,7 +6,7 @@
|
|||||||
<div class="load" />
|
<div class="load" />
|
||||||
<LoaderImage class="loading-icon" />
|
<LoaderImage class="loading-icon" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else ref="dashboardContent" class="all-dashboard">
|
<div v-else class="all-dashboard">
|
||||||
<div class="all-dashboard__bars">
|
<div class="all-dashboard__bars">
|
||||||
<BetaSatBar v-if="isBetaSatellite" />
|
<BetaSatBar v-if="isBetaSatellite" />
|
||||||
<MFARecoveryCodeBar v-if="showMFARecoveryCodeBar" :open-generate-modal="generateNewMFARecoveryCodes" />
|
<MFARecoveryCodeBar v-if="showMFARecoveryCodeBar" :open-generate-modal="generateNewMFARecoveryCodes" />
|
||||||
@ -17,64 +17,6 @@
|
|||||||
<div class="all-dashboard__content">
|
<div class="all-dashboard__content">
|
||||||
<div class="all-dashboard__content__divider" />
|
<div class="all-dashboard__content__divider" />
|
||||||
|
|
||||||
<div class="all-dashboard__banners">
|
|
||||||
<UpgradeNotification
|
|
||||||
v-if="isPaidTierBannerShown"
|
|
||||||
class="all-dashboard__banners__upgrade"
|
|
||||||
:open-add-p-m-modal="togglePMModal"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-banner
|
|
||||||
v-if="isAccountFrozen && !isLoading && dashboardContent"
|
|
||||||
class="all-dashboard__banners__freeze"
|
|
||||||
severity="critical"
|
|
||||||
:dashboard-ref="dashboardContent"
|
|
||||||
>
|
|
||||||
<template #text>
|
|
||||||
<p class="medium">Your account was frozen due to billing issues. Please update your payment information.</p>
|
|
||||||
<p class="link" @click.stop.self="redirectToBillingPage">To Billing Page</p>
|
|
||||||
</template>
|
|
||||||
</v-banner>
|
|
||||||
|
|
||||||
<v-banner
|
|
||||||
v-if="isAccountWarned && !isLoading && dashboardContent"
|
|
||||||
class="all-dashboard__banners__warning"
|
|
||||||
severity="warning"
|
|
||||||
:dashboard-ref="dashboardContent"
|
|
||||||
>
|
|
||||||
<template #text>
|
|
||||||
<p class="medium">Your account will be frozen soon due to billing issues. Please update your payment information.</p>
|
|
||||||
<p class="link" @click.stop.self="redirectToBillingPage">To Billing Page</p>
|
|
||||||
</template>
|
|
||||||
</v-banner>
|
|
||||||
|
|
||||||
<v-banner
|
|
||||||
v-if="limitState.hundredIsShown && !isLoading && dashboardContent"
|
|
||||||
class="all-dashboard__banners__hundred-limit"
|
|
||||||
severity="critical"
|
|
||||||
:on-click="() => setIsHundredLimitModalShown(true)"
|
|
||||||
:dashboard-ref="dashboardContent"
|
|
||||||
>
|
|
||||||
<template #text>
|
|
||||||
<p class="medium">{{ limitState.hundredLabel }}</p>
|
|
||||||
<p class="link" @click.stop.self="togglePMModal">Upgrade now</p>
|
|
||||||
</template>
|
|
||||||
</v-banner>
|
|
||||||
|
|
||||||
<v-banner
|
|
||||||
v-if="limitState.eightyIsShown && !isLoading && dashboardContent"
|
|
||||||
class="all-dashboard__banners__eighty-limit"
|
|
||||||
severity="warning"
|
|
||||||
:on-click="() => setIsEightyLimitModalShown(true)"
|
|
||||||
:dashboard-ref="dashboardContent"
|
|
||||||
>
|
|
||||||
<template #text>
|
|
||||||
<p class="medium">{{ limitState.eightyLabel }}</p>
|
|
||||||
<p class="link" @click.stop.self="togglePMModal">Upgrade now</p>
|
|
||||||
</template>
|
|
||||||
</v-banner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<router-view />
|
<router-view />
|
||||||
|
|
||||||
<limit-warning-modal
|
<limit-warning-modal
|
||||||
@ -142,8 +84,6 @@ import BetaSatBar from '@/components/infoBars/BetaSatBar.vue';
|
|||||||
import MFARecoveryCodeBar from '@/components/infoBars/MFARecoveryCodeBar.vue';
|
import MFARecoveryCodeBar from '@/components/infoBars/MFARecoveryCodeBar.vue';
|
||||||
import AllModals from '@/components/modals/AllModals.vue';
|
import AllModals from '@/components/modals/AllModals.vue';
|
||||||
import LimitWarningModal from '@/components/modals/LimitWarningModal.vue';
|
import LimitWarningModal from '@/components/modals/LimitWarningModal.vue';
|
||||||
import VBanner from '@/components/common/VBanner.vue';
|
|
||||||
import UpgradeNotification from '@/components/notifications/UpgradeNotification.vue';
|
|
||||||
|
|
||||||
import LoaderImage from '@/../static/images/common/loadIcon.svg';
|
import LoaderImage from '@/../static/images/common/loadIcon.svg';
|
||||||
|
|
||||||
@ -181,7 +121,6 @@ const isSessionActive = ref<boolean>(false);
|
|||||||
const isSessionRefreshing = ref<boolean>(false);
|
const isSessionRefreshing = ref<boolean>(false);
|
||||||
const isHundredLimitModalShown = ref<boolean>(false);
|
const isHundredLimitModalShown = ref<boolean>(false);
|
||||||
const isEightyLimitModalShown = ref<boolean>(false);
|
const isEightyLimitModalShown = ref<boolean>(false);
|
||||||
const dashboardContent = ref<HTMLElement | null>(null);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the session duration from the store.
|
* Returns the session duration from the store.
|
||||||
@ -217,14 +156,7 @@ const isAccountFrozen = computed((): boolean => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if account was warned due to billing issues.
|
* Returns all needed information for limit modal when bandwidth or storage close to limits.
|
||||||
*/
|
|
||||||
const isAccountWarned = computed((): boolean => {
|
|
||||||
return usersStore.state.user.freezeStatus.warned;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all needed information for limit banner and modal when bandwidth or storage close to limits.
|
|
||||||
*/
|
*/
|
||||||
type LimitedState = {
|
type LimitedState = {
|
||||||
eightyIsShown: boolean;
|
eightyIsShown: boolean;
|
||||||
@ -297,13 +229,6 @@ const limitState = computed((): LimitedState => {
|
|||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the current route is the billing page.
|
|
||||||
*/
|
|
||||||
const isBillingPage = computed(() => {
|
|
||||||
return route.path.includes(RouteConfig.Billing2.path);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if satellite is in beta.
|
* Indicates if satellite is in beta.
|
||||||
*/
|
*/
|
||||||
@ -326,31 +251,6 @@ const showMFARecoveryCodeBar = computed((): boolean => {
|
|||||||
return user.isMFAEnabled && user.mfaRecoveryCodeCount < recoveryCodeWarningThreshold;
|
return user.isMFAEnabled && user.mfaRecoveryCodeCount < recoveryCodeWarningThreshold;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the user has reached project limits.
|
|
||||||
*/
|
|
||||||
const hasReachedProjectLimit = computed((): boolean => {
|
|
||||||
const projectLimit: number = usersStore.state.user.projectLimit;
|
|
||||||
const projectsCount: number = projectsStore.projectsCount(usersStore.state.user.id);
|
|
||||||
|
|
||||||
return projectsCount === projectLimit;
|
|
||||||
});
|
|
||||||
|
|
||||||
/* whether the paid tier banner should be shown */
|
|
||||||
const isPaidTierBannerShown = computed((): boolean => {
|
|
||||||
return !usersStore.state.user.paidTier
|
|
||||||
&& !isBillingPage.value
|
|
||||||
&& joinedWhileAgo.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
/* whether the user joined more than 7 days ago */
|
|
||||||
const joinedWhileAgo = computed((): boolean => {
|
|
||||||
const createdAt = usersStore.state.user.createdAt as Date | null;
|
|
||||||
if (!createdAt) return true; // true so we can show the banner regardless
|
|
||||||
const millisPerDay = 24 * 60 * 60 * 1000;
|
|
||||||
return ((Date.now() - createdAt.getTime()) / millisPerDay) > 7;
|
|
||||||
});
|
|
||||||
|
|
||||||
function setIsEightyLimitModalShown(value: boolean): void {
|
function setIsEightyLimitModalShown(value: boolean): void {
|
||||||
isEightyLimitModalShown.value = value;
|
isEightyLimitModalShown.value = value;
|
||||||
}
|
}
|
||||||
@ -359,13 +259,6 @@ function setIsHundredLimitModalShown(value: boolean): void {
|
|||||||
isHundredLimitModalShown.value = value;
|
isHundredLimitModalShown.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirects to Billing Page.
|
|
||||||
*/
|
|
||||||
async function redirectToBillingPage(): Promise<void> {
|
|
||||||
await router.push(RouteConfig.AccountSettings.with(RouteConfig.Billing2.with(RouteConfig.BillingPaymentMethods2)).path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirects to log in screen.
|
* Redirects to log in screen.
|
||||||
*/
|
*/
|
||||||
@ -714,19 +607,6 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__banners {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
&__upgrade,
|
|
||||||
&__project-limit,
|
|
||||||
&__freeze,
|
|
||||||
&__warning,
|
|
||||||
&__hundred-limit,
|
|
||||||
&__eighty-limit {
|
|
||||||
margin: 20px 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.load {
|
.load {
|
||||||
|
@ -0,0 +1,234 @@
|
|||||||
|
// Copyright (C) 2023 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="all-dashboard-banners">
|
||||||
|
<UpgradeNotification
|
||||||
|
v-if="isPaidTierBannerShown"
|
||||||
|
class="all-dashboard-banners__upgrade"
|
||||||
|
:open-add-p-m-modal="togglePMModal"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-banner
|
||||||
|
v-if="isAccountFrozen && parentRef"
|
||||||
|
class="all-dashboard-banners__freeze"
|
||||||
|
severity="critical"
|
||||||
|
:dashboard-ref="parentRef"
|
||||||
|
>
|
||||||
|
<template #text>
|
||||||
|
<p class="medium">Your account was frozen due to billing issues. Please update your payment information.</p>
|
||||||
|
<p class="link" @click.stop.self="redirectToBillingPage">To Billing Page</p>
|
||||||
|
</template>
|
||||||
|
</v-banner>
|
||||||
|
|
||||||
|
<v-banner
|
||||||
|
v-if="isAccountWarned && parentRef"
|
||||||
|
class="all-dashboard-banners__warning"
|
||||||
|
severity="warning"
|
||||||
|
:dashboard-ref="parentRef"
|
||||||
|
>
|
||||||
|
<template #text>
|
||||||
|
<p class="medium">Your account will be frozen soon due to billing issues. Please update your payment information.</p>
|
||||||
|
<p class="link" @click.stop.self="redirectToBillingPage">To Billing Page</p>
|
||||||
|
</template>
|
||||||
|
</v-banner>
|
||||||
|
|
||||||
|
<v-banner
|
||||||
|
v-if="limitState.hundredIsShown && parentRef"
|
||||||
|
class="all-dashboard-banners__hundred-limit"
|
||||||
|
severity="critical"
|
||||||
|
:on-click="() => setIsHundredLimitModalShown(true)"
|
||||||
|
:dashboard-ref="parentRef"
|
||||||
|
>
|
||||||
|
<template #text>
|
||||||
|
<p class="medium">{{ limitState.hundredLabel }}</p>
|
||||||
|
<p class="link" @click.stop.self="togglePMModal">Upgrade now</p>
|
||||||
|
</template>
|
||||||
|
</v-banner>
|
||||||
|
|
||||||
|
<v-banner
|
||||||
|
v-if="limitState.eightyIsShown && parentRef"
|
||||||
|
class="all-dashboard-banners__eighty-limit"
|
||||||
|
severity="warning"
|
||||||
|
:on-click="() => setIsEightyLimitModalShown(true)"
|
||||||
|
:dashboard-ref="parentRef"
|
||||||
|
>
|
||||||
|
<template #text>
|
||||||
|
<p class="medium">{{ limitState.eightyLabel }}</p>
|
||||||
|
<p class="link" @click.stop.self="togglePMModal">Upgrade now</p>
|
||||||
|
</template>
|
||||||
|
</v-banner>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { useUsersStore } from '@/store/modules/usersStore';
|
||||||
|
import { MODALS } from '@/utils/constants/appStatePopUps';
|
||||||
|
import { useAppStore } from '@/store/modules/appStore';
|
||||||
|
import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||||
|
import { RouteConfig } from '@/types/router';
|
||||||
|
|
||||||
|
import VBanner from '@/components/common/VBanner.vue';
|
||||||
|
import UpgradeNotification from '@/components/notifications/UpgradeNotification.vue';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const usersStore = useUsersStore();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const projectsStore = useProjectsStore();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
parentRef: HTMLElement;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const isHundredLimitModalShown = ref<boolean>(false);
|
||||||
|
const isEightyLimitModalShown = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all needed information for limit banner and modal when bandwidth or storage close to limits.
|
||||||
|
*/
|
||||||
|
type LimitedState = {
|
||||||
|
eightyIsShown: boolean;
|
||||||
|
hundredIsShown: boolean;
|
||||||
|
eightyLabel: string;
|
||||||
|
eightyModalLimitType: string;
|
||||||
|
eightyModalTitle: string;
|
||||||
|
hundredLabel: string;
|
||||||
|
hundredModalTitle: string;
|
||||||
|
hundredModalLimitType: string;
|
||||||
|
}
|
||||||
|
const limitState = computed((): LimitedState => {
|
||||||
|
const result: LimitedState = {
|
||||||
|
eightyIsShown: false,
|
||||||
|
hundredIsShown: false,
|
||||||
|
eightyLabel: '',
|
||||||
|
eightyModalLimitType: '',
|
||||||
|
eightyModalTitle: '',
|
||||||
|
hundredLabel: '',
|
||||||
|
hundredModalTitle: '',
|
||||||
|
hundredModalLimitType: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usersStore.state.user.paidTier || isAccountFrozen.value) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentLimits = projectsStore.state.currentLimits;
|
||||||
|
|
||||||
|
const limitTypeArr = [
|
||||||
|
{ name: 'egress', usedPercent: Math.round(currentLimits.bandwidthUsed * 100 / currentLimits.bandwidthLimit) },
|
||||||
|
{ name: 'storage', usedPercent: Math.round(currentLimits.storageUsed * 100 / currentLimits.storageLimit) },
|
||||||
|
{ name: 'segment', usedPercent: Math.round(currentLimits.segmentUsed * 100 / currentLimits.segmentLimit) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const hundredPercent: string[] = [];
|
||||||
|
const eightyPercent: string[] = [];
|
||||||
|
|
||||||
|
limitTypeArr.forEach((limitType) => {
|
||||||
|
if (limitType.usedPercent >= 80) {
|
||||||
|
if (limitType.usedPercent >= 100) {
|
||||||
|
hundredPercent.push(limitType.name);
|
||||||
|
} else {
|
||||||
|
eightyPercent.push(limitType.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (eightyPercent.length !== 0) {
|
||||||
|
result.eightyIsShown = true;
|
||||||
|
|
||||||
|
const eightyPercentString = eightyPercent.join(' and ');
|
||||||
|
|
||||||
|
result.eightyLabel = `You've used 80% of your ${eightyPercentString} limit. Avoid interrupting your usage by upgrading your account.`;
|
||||||
|
result.eightyModalTitle = `80% ${eightyPercentString} limit used`;
|
||||||
|
result.eightyModalLimitType = eightyPercentString;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hundredPercent.length !== 0) {
|
||||||
|
result.hundredIsShown = true;
|
||||||
|
|
||||||
|
const hundredPercentString = hundredPercent.join(' and ');
|
||||||
|
|
||||||
|
result.hundredLabel = `URGENT: You’ve reached the ${hundredPercentString} limit for your project. Upgrade to avoid any service interruptions.`;
|
||||||
|
result.hundredModalTitle = `URGENT: You’ve reached the ${hundredPercentString} limit for your project.`;
|
||||||
|
result.hundredModalLimitType = hundredPercentString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if account was frozen due to billing issues.
|
||||||
|
*/
|
||||||
|
const isAccountFrozen = computed((): boolean => {
|
||||||
|
return usersStore.state.user.freezeStatus.frozen;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if account was warned due to billing issues.
|
||||||
|
*/
|
||||||
|
const isAccountWarned = computed((): boolean => {
|
||||||
|
return usersStore.state.user.freezeStatus.warned;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* whether the paid tier banner should be shown */
|
||||||
|
const isPaidTierBannerShown = computed((): boolean => {
|
||||||
|
return !usersStore.state.user.paidTier
|
||||||
|
&& joinedWhileAgo.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* whether the user joined more than 7 days ago */
|
||||||
|
const joinedWhileAgo = computed((): boolean => {
|
||||||
|
const createdAt = usersStore.state.user.createdAt as Date | null;
|
||||||
|
if (!createdAt) return true; // true so we can show the banner regardless
|
||||||
|
const millisPerDay = 24 * 60 * 60 * 1000;
|
||||||
|
return ((Date.now() - createdAt.getTime()) / millisPerDay) > 7;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens add payment method modal.
|
||||||
|
*/
|
||||||
|
function togglePMModal(): void {
|
||||||
|
isHundredLimitModalShown.value = false;
|
||||||
|
isEightyLimitModalShown.value = false;
|
||||||
|
|
||||||
|
if (!usersStore.state.user.paidTier) {
|
||||||
|
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setIsEightyLimitModalShown(value: boolean): void {
|
||||||
|
isEightyLimitModalShown.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setIsHundredLimitModalShown(value: boolean): void {
|
||||||
|
isHundredLimitModalShown.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects to Billing Page.
|
||||||
|
*/
|
||||||
|
async function redirectToBillingPage(): Promise<void> {
|
||||||
|
await router.push(RouteConfig.AccountSettings.with(RouteConfig.Billing2.with(RouteConfig.BillingPaymentMethods2)).path);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.all-dashboard-banners {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&__upgrade,
|
||||||
|
&__project-limit,
|
||||||
|
&__freeze,
|
||||||
|
&__warning,
|
||||||
|
&__hundred-limit,
|
||||||
|
&__eighty-limit {
|
||||||
|
margin: 20px 0 0;
|
||||||
|
box-shadow: 0 0 20px rgb(0 0 0 / 4%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -9,6 +9,8 @@
|
|||||||
<VButton
|
<VButton
|
||||||
class="header__content__actions__docs"
|
class="header__content__actions__docs"
|
||||||
icon="resources"
|
icon="resources"
|
||||||
|
border-radius="8px"
|
||||||
|
font-size="12px"
|
||||||
is-white
|
is-white
|
||||||
:link="link"
|
:link="link"
|
||||||
:on-press="sendDocsEvent"
|
:on-press="sendDocsEvent"
|
||||||
@ -285,7 +287,19 @@ function sendDocsEvent(): void {
|
|||||||
|
|
||||||
&__docs {
|
&__docs {
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
border-radius: 8px;
|
box-shadow: 0 0 20px rgb(0 0 0 / 4%);
|
||||||
|
|
||||||
|
:deep(.label) {
|
||||||
|
|
||||||
|
& > svg {
|
||||||
|
height: 14px;
|
||||||
|
width: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: var(--c-black) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,14 +189,15 @@ async function onLogout(): Promise<void> {
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 14px 18px;
|
padding: 10px 16px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: var(--c-grey-6);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: var(--c-white);
|
background: var(--c-white);
|
||||||
border: 1px solid var(--c-grey-3);
|
border: 1px solid var(--c-grey-3);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
|
color: var(--c-black);
|
||||||
|
box-shadow: 0 0 20px rgb(0 0 0 / 4%);
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active,
|
&:active,
|
||||||
@ -213,13 +214,20 @@ async function onLogout(): Promise<void> {
|
|||||||
&__icon {
|
&__icon {
|
||||||
transition-duration: 0.5s;
|
transition-duration: 0.5s;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
|
||||||
|
:deep(path) {
|
||||||
|
fill: var(--c-black);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
font-family: 'font_medium', sans-serif;
|
font-family: 'font_medium', sans-serif;
|
||||||
font-size: 16px;
|
line-height: 20px;
|
||||||
line-height: 23px;
|
font-weight: 700;
|
||||||
color: var(--c-grey-6);
|
font-size: 12px;
|
||||||
|
color: var(--c-black);
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,30 @@
|
|||||||
// See LICENSE for copying information.
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="my-projects">
|
<div ref="content" class="my-projects">
|
||||||
<div class="my-projects__header">
|
<div class="my-projects__header">
|
||||||
<span class="my-projects__header__title">My Projects</span>
|
<div class="my-projects__header__title">
|
||||||
|
<span>My Projects</span>
|
||||||
|
<span class="my-projects__header__title__views">
|
||||||
|
<span
|
||||||
|
class="my-projects__header__title__views__icon"
|
||||||
|
@click="() => onViewChangeClicked('table')"
|
||||||
|
>
|
||||||
|
<table-icon />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="my-projects__header__title__views__icon"
|
||||||
|
@click="() => onViewChangeClicked('cards')"
|
||||||
|
>
|
||||||
|
<cards-icon />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="my-projects__header__right">
|
<span class="my-projects__header__right">
|
||||||
<span class="my-projects__header__right__text">View</span>
|
<span class="my-projects__header__right__text">View</span>
|
||||||
<v-chip
|
<v-chip
|
||||||
|
class="my-projects__header__right__table-chip"
|
||||||
label="Table"
|
label="Table"
|
||||||
:is-selected="isTableViewSelected"
|
:is-selected="isTableViewSelected"
|
||||||
:icon="TableIcon"
|
:icon="TableIcon"
|
||||||
@ -16,21 +33,37 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<v-chip
|
<v-chip
|
||||||
|
class="my-projects__header__right__card-chip"
|
||||||
label="Cards"
|
label="Cards"
|
||||||
:is-selected="!isTableViewSelected"
|
:is-selected="!isTableViewSelected"
|
||||||
:icon="CardsIcon"
|
:icon="CardsIcon"
|
||||||
@select="() => onViewChangeClicked('cards')"
|
@select="() => onViewChangeClicked('cards')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<VButton
|
||||||
|
class="my-projects__header__right__mobile-button"
|
||||||
|
icon="addcircle"
|
||||||
|
border-radius="8px"
|
||||||
|
font-size="12px"
|
||||||
|
is-white
|
||||||
|
:on-press="onCreateProjectClicked"
|
||||||
|
label="Create New Project"
|
||||||
|
/>
|
||||||
|
|
||||||
<VButton
|
<VButton
|
||||||
class="my-projects__header__right__button"
|
class="my-projects__header__right__button"
|
||||||
icon="addcircle"
|
icon="addcircle"
|
||||||
|
border-radius="8px"
|
||||||
|
font-size="12px"
|
||||||
is-white
|
is-white
|
||||||
:on-press="onCreateProjectClicked"
|
:on-press="onCreateProjectClicked"
|
||||||
label="Create a Project"
|
label="Create a Project"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<all-projects-dashboard-banners v-if="content" :parent-ref="content" />
|
||||||
|
|
||||||
<div v-if="projects.length || invites.length" class="my-projects__list">
|
<div v-if="projects.length || invites.length" class="my-projects__list">
|
||||||
<projects-table v-if="isTableViewSelected" :invites="invites" class="my-projects__list__table" />
|
<projects-table v-if="isTableViewSelected" :invites="invites" class="my-projects__list__table" />
|
||||||
<div v-else-if="!isTableViewSelected" class="my-projects__list__cards">
|
<div v-else-if="!isTableViewSelected" class="my-projects__list__cards">
|
||||||
@ -64,6 +97,7 @@ import { useAppStore } from '@/store/modules/appStore';
|
|||||||
import { useProjectsStore } from '@/store/modules/projectsStore';
|
import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||||
import { useConfigStore } from '@/store/modules/configStore';
|
import { useConfigStore } from '@/store/modules/configStore';
|
||||||
import ProjectsTable from '@/views/all-dashboard/components/ProjectsTable.vue';
|
import ProjectsTable from '@/views/all-dashboard/components/ProjectsTable.vue';
|
||||||
|
import AllProjectsDashboardBanners from '@/views/all-dashboard/components/AllProjectsDashboardBanners.vue';
|
||||||
|
|
||||||
import VButton from '@/components/common/VButton.vue';
|
import VButton from '@/components/common/VButton.vue';
|
||||||
import VChip from '@/components/common/VChip.vue';
|
import VChip from '@/components/common/VChip.vue';
|
||||||
@ -79,6 +113,8 @@ const projectsStore = useProjectsStore();
|
|||||||
|
|
||||||
const analytics = new AnalyticsHttpApi();
|
const analytics = new AnalyticsHttpApi();
|
||||||
|
|
||||||
|
const content = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const hasProjectTableViewConfigured = ref(appStore.hasProjectTableViewConfigured());
|
const hasProjectTableViewConfigured = ref(appStore.hasProjectTableViewConfigured());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,20 +174,51 @@ function onCreateProjectClicked(): void {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
@media screen and (width <= 425px) {
|
@media screen and (width <= 500px) {
|
||||||
|
margin-top: 20px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
&__button {
|
&__title {
|
||||||
width: 100% !important;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&__views {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
column-gap: 5px;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
|
||||||
|
:deep(path),
|
||||||
|
:deep(rect){
|
||||||
|
fill: var(--c-black);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
font-family: 'font_bold', sans-serif;
|
font-family: 'font_bold', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 31px;
|
line-height: 31px;
|
||||||
|
|
||||||
|
@media screen and (width <= 500px) {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__views {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__right {
|
&__right {
|
||||||
@ -161,15 +228,42 @@ function onCreateProjectClicked(): void {
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
column-gap: 12px;
|
column-gap: 12px;
|
||||||
|
|
||||||
|
@media screen and (width <= 500px) {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&__text,
|
||||||
|
&__button,
|
||||||
|
&__table-chip,
|
||||||
|
&__card-chip {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__mobile-button {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
color: var(--c-grey-6);
|
color: var(--c-grey-6);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__button {
|
&__button,
|
||||||
|
&__mobile-button {
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
border-radius: 8px;
|
box-shadow: 0 0 20px rgb(0 0 0 / 4%);
|
||||||
|
|
||||||
|
:deep(.label) {
|
||||||
|
color: var(--c-black) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__mobile-button {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,15 @@
|
|||||||
label="Join Project"
|
label="Join Project"
|
||||||
class="invitation-item__menu__button"
|
class="invitation-item__menu__button"
|
||||||
/>
|
/>
|
||||||
|
<v-button
|
||||||
|
:loading="isLoading"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:on-press="onJoinClicked"
|
||||||
|
border-radius="8px"
|
||||||
|
font-size="12px"
|
||||||
|
label="Join"
|
||||||
|
class="invitation-item__menu__mobile-button"
|
||||||
|
/>
|
||||||
<div class="invitation-item__menu">
|
<div class="invitation-item__menu">
|
||||||
<div class="invitation-item__menu__icon" @click.stop="toggleDropDown">
|
<div class="invitation-item__menu__icon" @click.stop="toggleDropDown">
|
||||||
<div class="invitation-item__menu__icon__content" :class="{open: isDropdownOpen}">
|
<div class="invitation-item__menu__icon__content" :class="{open: isDropdownOpen}">
|
||||||
@ -80,7 +89,7 @@ const itemToRender = computed((): { [key: string]: unknown | string[] } => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { info: [ props.invitation.projectName, `Created ${props.invitation.invitedDate}` ] };
|
return { info: [ props.invitation.projectName, props.invitation.projectDescription ] };
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,6 +158,19 @@ function closeDropDown() {
|
|||||||
|
|
||||||
&__button {
|
&__button {
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
|
|
||||||
|
@media screen and (width <= 500px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__mobile-button {
|
||||||
|
display: none;
|
||||||
|
padding: 10px 16px;
|
||||||
|
|
||||||
|
@media screen and (width <= 500px) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
|
@ -84,7 +84,7 @@ const itemToRender = computed((): { [key: string]: unknown | string[] } => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { info: [ props.project.name, `Created ${props.project.createdDate()}` ] };
|
return { info: [ props.project.name, props.project.description ] };
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user