Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[MS] Updated HomePage layout
Browse files Browse the repository at this point in the history
fabienSvstr committed Jan 29, 2025
1 parent 7b31b5e commit d58d7ba
Showing 12 changed files with 202 additions and 145 deletions.
3 changes: 3 additions & 0 deletions client/src/assets/images/background/blob-shape.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions client/src/assets/images/background/shapes-circles.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion client/src/locales/en-US.json
Original file line number Diff line number Diff line change
@@ -99,7 +99,11 @@
"expiredOrganization": "This organization has expired."
},
"HomePage": {
"sidebar": {
"tagline": "Keep your data safe with your control"
},
"topbar": {
"welcome": "Glad to see you back,",
"backToList": "Return to organizations",
"backToLogin": "Return to login",
"settings": "Settings",
@@ -126,7 +130,7 @@
"newVersionAvailable": "New update available"
},
"organizationList": {
"title": "Your organizations",
"title": "Access to your organizations",
"sortOrderAsc": "Ascending",
"sortOrderDesc": "Descending",
"sortByOrganization": "Organization name",
6 changes: 5 additions & 1 deletion client/src/locales/fr-FR.json
Original file line number Diff line number Diff line change
@@ -99,7 +99,11 @@
"expiredOrganization": "Cette organisation a expiré."
},
"HomePage": {
"sidebar": {
"tagline": "Garder le contrôle sur vos données en toute sécurité"
},
"topbar": {
"welcome": "Ravi de vous revoir,",
"backToList": "Retour aux organisations",
"backToLogin": "Retour à la connexion",
"settings": "Paramètres",
@@ -126,7 +130,7 @@
"newVersionAvailable": "Nouvelle version disponible"
},
"organizationList": {
"title": "Vos organisations",
"title": "Accéder à vos organisations",
"sortOrderAsc": "Croissant",
"sortOrderDesc": "Décroissant",
"sortByOrganization": "Nom de l'organisation",
2 changes: 1 addition & 1 deletion client/src/views/client-area/ClientAreaLoginPage.vue
Original file line number Diff line number Diff line change
@@ -66,7 +66,7 @@ function getCurrentSectionClass(): string {
display: flex;
max-width: 48rem;
max-height: 32rem;
margin: auto;
margin: 2rem auto;
}

.saas-login-container {
4 changes: 2 additions & 2 deletions client/src/views/devices/ImportRecoveryDevicePage.vue
Original file line number Diff line number Diff line change
@@ -271,7 +271,7 @@ async function onLoginClick(): Promise<void> {
width: 60vw;
max-width: var(--parsec-max-forgotten-pwd-width);
display: flex;
margin: auto;
margin: 2rem auto;
flex-direction: column;
align-items: center;
gap: 2rem;
@@ -280,7 +280,7 @@ async function onLoginClick(): Promise<void> {

.recovery-header {
&__title {
color: var(--parsec-color-light-secondary-white);
color: var(--parsec-color-light-secondary-text);
}
}

38 changes: 24 additions & 14 deletions client/src/views/home/HomePage.vue
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@
<ion-page>
<ion-content :fullscreen="true">
<div id="page">
<!-- sidebar -->
<home-page-sidebar />
<!-- organization list -->
<!-- organization -->
<div
@@ -95,6 +97,7 @@ import ImportRecoveryDevicePage from '@/views/devices/ImportRecoveryDevicePage.v
import CreateOrganizationModal from '@/views/organizations/creation/CreateOrganizationModal.vue';
import DeviceJoinOrganizationModal from '@/views/home/DeviceJoinOrganizationModal.vue';
import HomePageHeader from '@/views/home/HomePageHeader.vue';
import HomePageSidebar from '@/views/home/HomePageSidebar.vue';
import LoginPage from '@/views/home/LoginPage.vue';
import OrganizationListPage from '@/views/home/OrganizationListPage.vue';
import UserJoinOrganizationModal from '@/views/home/UserJoinOrganizationModal.vue';
@@ -547,29 +550,36 @@ function getBackButtonTitle(): string {
height: 100vh;
display: flex;
overflow: hidden;
padding: 0 2rem;
align-items: self-start;
background: var(--parsec-color-light-secondary-inversed-contrast);
z-index: -10;
background: var(--parsec-color-light-gradient);

&::before {
content: '';
position: absolute;
z-index: -2;
top: 0;
right: 0;
width: 100vw;
height: 100vh;
background: url('@/assets/images/background/homepage-rectangle.svg') repeat center;
background-size: cover;
}

.content {
width: 100%;
height: 100%;
display: flex;
gap: 2rem;
flex-direction: column;
position: relative;
max-width: var(--parsec-max-content-width);
padding: 6.26rem 5rem 0;

&::after {
content: '';
position: absolute;
height: 100%;
width: 100%;
max-width: 317px;
max-height: 326px;
bottom: 0;
right: 0;
background-image: url('@/assets/images/background/blob-shape.svg');
background-size: contain;
background-repeat: no-repeat;
background-position: top center;
opacity: 0.5;
filter: blur(250px);
}
}
}
</style>
149 changes: 62 additions & 87 deletions client/src/views/home/HomePageHeader.vue
Original file line number Diff line number Diff line change
@@ -2,43 +2,22 @@

<template>
<div class="topbar">
<ion-button
class="update-button form-label"
id="trigger-update-button"
fill="clear"
@click="update()"
>
{{ $msTranslate('notificationCenter.newVersionAvailable') }}
</ion-button>
<div class="topbar-left">
<div
class="topbar-left__logo"
@click="$emit('backClick')"
<ion-text
class="topbar-left__title title-h1"
v-if="!showBackButton"
>
<ms-image
:image="LogoRowWhite"
class="logo-img"
/>
</div>
{{ $msTranslate('HomePage.topbar.welcome') }}
</ion-text>
<!-- back button -->
<ion-button
slot="icon-only"
id="trigger-version-button"
class="topbar-buttons__item body"
fill="clear"
@click="$emit('aboutClick')"
v-if="!showBackButton"
>
<ion-icon
slot="start"
:icon="informationCircle"
size="small"
/>
</ion-button>
<!-- update button -->
<ion-button
v-if="updateAvailability !== null && !showBackButton"
class="topbar-buttons__item"
id="trigger-update-button"
fill="clear"
@click="update()"
>
<ion-icon :icon="sparkles" />
{{ $msTranslate('notificationCenter.newVersionAvailable') }}
</ion-button>
<ion-button
@click="$emit('backClick')"
v-if="showBackButton"
@@ -53,23 +32,30 @@
</div>
<div class="topbar-right">
<ion-buttons class="topbar-buttons">
<!-- about button -->
<ion-button
slot="icon-only"
id="trigger-version-button"
class="topbar-buttons__item body"
fill="clear"
@click="$emit('aboutClick')"
v-if="!showBackButton"
>
<ion-icon :icon="informationCircle" />
</ion-button>
<!-- doc button -->

<ion-button
class="topbar-buttons__item"
@click="Env.Links.openDocumentationLink"
>
<ion-icon :icon="documentText" />
{{ $msTranslate('HomePage.topbar.documentation') }}
</ion-button>
<!-- contact button -->

<ion-button
class="topbar-buttons__item"
@click="Env.Links.openContactLink"
>
<ion-icon :icon="chatbubbles" />
{{ $msTranslate('HomePage.topbar.contactUs') }}
</ion-button>
<!-- settings button -->
<ion-button
@@ -78,7 +64,6 @@
@click="$emit('settingsClick')"
>
<ion-icon :icon="cog" />
{{ $msTranslate('HomePage.topbar.settings') }}
</ion-button>
<!-- customer area button -->
<ion-button
@@ -94,11 +79,11 @@
</template>

<script setup lang="ts">
import { IonButton, IonButtons, IonIcon, modalController } from '@ionic/vue';
import { arrowBack, chatbubbles, cog, informationCircle, sparkles, documentText } from 'ionicons/icons';
import { IonButton, IonButtons, IonIcon, modalController, IonText } from '@ionic/vue';
import { arrowBack, chatbubbles, cog, informationCircle, documentText } from 'ionicons/icons';
import { EventData, Events, UpdateAvailabilityData } from '@/services/eventDistributor';
import { InjectionProvider, InjectionProviderKey } from '@/services/injectionProvider';
import { LogoRowWhite, MsImage, Translatable, MsModalResult } from 'megashark-lib';
import { Translatable, MsModalResult } from 'megashark-lib';
import { onMounted, onUnmounted, ref, inject, Ref } from 'vue';
import { Env } from '@/services/environment';
import { needsMocks } from '@/parsec';
@@ -173,15 +158,31 @@ defineEmits<{

<style lang="scss" scoped>
.topbar {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: space-between;
width: 100%;
max-width: var(--parsec-max-content-width);
padding: 0;
margin: 2rem auto 0;
padding: 0 0 2rem;
position: relative;
border-bottom: 1px solid var(--parsec-color-light-secondary-medium);
}

.update-button {
background: var(--parsec-color-light-primary-50);
color: var(--parsec-color-light-primary-700);
min-height: 1rem;
border: 1px solid var(--parsec-color-light-primary-100);
padding: 0 0.825rem;
border-radius: var(--parsec-radius-32);
position: absolute;
top: -3.5rem;
left: 50%;
transform: translate(-50%, 0);
transition: all 150ms linear;

&:hover {
--background-hover: none;
box-shadow: var(--parsec-shadow-light);
}
}

.topbar-left {
@@ -190,18 +191,17 @@ defineEmits<{
width: 100%;
gap: 1.5rem;

&__logo {
width: 8rem;
height: 1.5rem;
display: block;

.logo-img {
width: 100%;
height: 100%;
}
&__title {
background: var(--parsec-color-light-gradient-background);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 0;
}

&__back-button {
color: var(--parsec-color-light-secondary-soft-text);

&::part(native) {
background: none;
--background-hover: none;
@@ -227,7 +227,7 @@ defineEmits<{
.topbar-right {
display: flex;
justify-content: flex-end;
width: 100%;
width: fit-content;
}

.topbar-buttons {
@@ -240,8 +240,8 @@ defineEmits<{
}

.topbar-buttons__item {
background: var(--parsec-color-light-primary-30-opacity15);
color: var(--parsec-color-light-secondary-white);
background: var(--parsec-color-light-secondary-white);
color: var(--parsec-color-light-secondary-soft-text);
border-radius: var(--parsec-radius-32);
transition: all 150ms linear;

@@ -254,43 +254,18 @@ defineEmits<{
&:hover {
background: var(--parsec-color-light-primary-100);
color: var(--parsec-color-light-primary-600);
box-shadow: var(--parsec-shadow-strong);
box-shadow: var(--parsec-shadow-light);
}

ion-icon {
font-size: 1.25rem;
margin-right: 0.5rem;
}

&#trigger-customer-area-button {
background: var(--parsec-color-light-secondary-white);
color: var(--parsec-color-light-primary-600);
align-self: stretch;

&:hover {
background: var(--parsec-color-light-secondary-premiere);
outline: 1px solid var(--parsec-color-light-primary-100);
outline-offset: 2px;
}
}

&#trigger-version-button {
ion-icon {
margin: 0;
}
}

&#trigger-update-button {
background: linear-gradient(217deg, var(--parsec-color-light-primary-700), var(--parsec-color-light-primary-600)),
linear-gradient(127deg, var(--parsec-color-light-primary-100), var(--parsec-color-light-primary-300));
color: var(--parsec-color-light-secondary-white);
align-self: stretch;
transition: all 150ms linear;
outline: 0px solid var(--parsec-color-light-primary-700);
outline: 1px solid var(--parsec-color-light-secondary-text);

&:hover {
outline-width: 1px;
outline-offset: 2px;
outline: none;
}
}
}
61 changes: 61 additions & 0 deletions client/src/views/home/HomePageSidebar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<!-- Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS -->

<template>
<div class="sidebar-homepage">
<ms-image
:image="LogoRowWhite"
class="logo-img"
/>
<ion-text class="sidebar-homepage__tagline subtitles-lg">{{ $msTranslate('HomePage.sidebar.tagline') }}</ion-text>
</div>
</template>

<script setup lang="ts">
import { MsImage, LogoRowWhite } from 'megashark-lib';
import { IonText } from '@ionic/vue';
</script>

<style lang="scss" scoped>
.sidebar-homepage {
background: var(--parsec-color-light-gradient);
width: 100%;
max-width: 40rem;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: end;
position: relative;
gap: 1.5rem;
transition: all 0.3s ease;

// Should be edited later with responsive
@media screen and (max-width: 1500px) {
max-width: 30rem;
}

&::before {
content: '';
position: absolute;
height: 560px;
width: 100%;
max-height: 60vh;
top: 0;
right: 0;
background-image: url('@/assets/images/background/shapes-circles.svg');
background-size: cover;
background-repeat: no-repeat;
background-position: top left;
}

.logo-img {
width: 8.5rem;
}

&__tagline {
color: var(--parsec-color-light-secondary-white);
text-align: center;
margin-bottom: 25%;
}
}
</style>
17 changes: 3 additions & 14 deletions client/src/views/home/LoginPage.vue
Original file line number Diff line number Diff line change
@@ -142,24 +142,13 @@ defineExpose({
height: auto;
width: 100%;
max-width: 25rem;
margin: auto;
margin: 2rem auto;
display: flex;
gap: 1.5rem;
flex-direction: column;
align-items: center;
box-shadow: none;

.login-header {
display: flex;
flex-direction: column;
align-items: center;

&__title {
padding: 0;
margin-bottom: 2rem;
color: var(--parsec-color-light-secondary-white);
}
}

.login-card {
background: var(--parsec-color-light-secondary-white);
border: 1px solid var(--parsec-color-light-secondary-medium);
@@ -174,7 +163,7 @@ defineExpose({
transition: box-shadow 150ms ease-in-out;

&:has(.has-focus) {
box-shadow: var(--parsec-shadow-card);
box-shadow: var(--parsec-shadow-light);
}

&-header {
32 changes: 17 additions & 15 deletions client/src/views/home/OrganizationListPage.vue
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
<template>
<ion-card class="organization">
<ion-text
class="organization-title title-h1"
class="organization-title title-h3"
v-if="deviceList.length > 0"
>
{{ $msTranslate('HomePage.organizationList.title') }}
@@ -376,50 +376,52 @@ const filteredDevices = computed(() => {
.organization {
background: none;
height: auto;
width: 60vw;
max-width: var(--parsec-max-organization-width);
margin: auto;
width: 100%;
max-width: var(--parsec-max-content-width);
box-shadow: none;
display: flex;
align-items: center;
margin: 0;
flex-direction: column;
gap: 2rem;

&-title {
text-align: center;
padding: 0;
display: flex;
justify-content: center;
color: var(--parsec-color-light-secondary-white);
color: var(--parsec-color-light-secondary-text);
}
}

.organization-content {
display: flex;
padding: 2rem 2.5rem 0;
flex-direction: column;
gap: 1.5rem;
padding: 0;
width: 100%;
max-width: var(--parsec-max-content-width);
background: var(--parsec-color-light-secondary-white);
border-radius: var(--parsec-radius-12);
width: 100%;

.organization-filter {
display: flex;
justify-content: space-between;
align-items: center;
margin-inline: 0.5rem;
width: 100%;
max-width: 45rem;

#organization-filter-select {
margin-left: auto;
margin-right: 1rem;
}

#create-organization-button {
--background: var(--parsec-color-light-secondary-text);
--background-hover: var(--parsec-color-light-secondary-contrast);
}
}

.organization-list {
margin: 0;
overflow-y: auto;
--ion-grid-columns: 6;
max-width: 34.5rem;
max-height: 50vh;
min-height: 45vh;
}
@@ -437,11 +439,11 @@ const filteredDevices = computed(() => {
display: flex;
align-items: center;
position: absolute;
background: var(--parsec-color-light-secondary-white);
border-top: 1px solid var(--parsec-color-light-secondary-medium);
width: 100%;
gap: 0.5rem;
padding: 0.5rem 3rem;
background: var(--parsec-color-light-secondary-inversed-contrast);
padding: 0.5rem 0.5rem 1.5rem;
color: var(--parsec-color-light-secondary-grey);
bottom: 0;
z-index: 100;
15 changes: 5 additions & 10 deletions client/tests/e2e/specs/home_page.spec.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { answerQuestion, expect, fillIonInput, msTest, sortBy } from '@tests/e2e
const USER_NAMES = ['Alicey McAliceFace', 'Boby McBobFace', 'Malloryy McMalloryFace'];

msTest('Home default state with devices', async ({ home }) => {
await expect(home.locator('.organization-title')).toHaveText('Your organizations');
await expect(home.locator('.organization-title')).toHaveText('Access to your organizations');
await expect(home.locator('#organization-filter-select')).toHaveText('Organization name');
await expect(home.locator('#create-organization-button')).toHaveText('Create or join');
await expect(home.locator('#search-input-organization')).toBeVisible();
@@ -152,30 +152,25 @@ msTest('Logout and go back to devices list', async ({ home }) => {
const buttons = home.locator('.profile-header-popover').locator('.main-list').getByRole('listitem');
await buttons.nth(2).click();
await answerQuestion(home, true);
await expect(home.locator('.organization-title')).toHaveText('Your organizations');
await expect(home.locator('.organization-title')).toHaveText('Access to your organizations');
await expect(home).toBeHomePage();
});

msTest('Check header buttons', async ({ home }) => {
await expect(home.locator('.topbar-buttons').locator('ion-button')).toHaveText([
'Documentation',
'Contact us',
'Settings',
'Customer area',
]);
await expect(home.locator('.topbar-buttons').locator('ion-button')).toHaveCount(5);
});

msTest('Open documentation', async ({ home }) => {
const newTabPromise = home.waitForEvent('popup');
await home.locator('.topbar-buttons').locator('ion-button').nth(0).click();
await home.locator('.topbar-buttons').locator('ion-button').nth(1).click();
const newTab = await newTabPromise;
await newTab.waitForLoadState();
await expect(newTab).toHaveURL(new RegExp('https://docs.parsec.cloud/(en|fr)/[a-z0-9-+.]+'));
});

msTest('Open feedback', async ({ home }) => {
const newTabPromise = home.waitForEvent('popup');
await home.locator('.topbar-buttons').locator('ion-button').nth(1).click();
await home.locator('.topbar-buttons').locator('ion-button').nth(2).click();
const newTab = await newTabPromise;
await newTab.waitForLoadState();
await expect(newTab).toHaveURL(new RegExp('https://sign(-dev)?.parsec.cloud/contact'));

0 comments on commit d58d7ba

Please sign in to comment.