Feature/homepage (#1956)

* add  new card widget for empty services page

* add homepage

* fix icons

* copy and arrangement changes

* minor changes

* edit login page

* rcreate widget list component

* cchange change detection strategy

* show header in home, welcome in list (#1957)

* show hear in home but not in list

* adjust padding

Co-authored-by: Lucy Cifferello <12953208+elvece@users.noreply.github.com>

Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
This commit is contained in:
Lucy C
2022-11-20 22:18:39 -07:00
committed by Aiden McClelland
parent 4042b8f026
commit 35cb81518c
33 changed files with 460 additions and 116 deletions

View File

@@ -1,16 +0,0 @@
<div class="welcome">
<h2>
Welcome to
<ion-text color="danger" class="embassy">Embassy</ion-text>
</h2>
<p class="ion-text-wrap">Get started by installing your first service.</p>
</div>
<ion-button
color="dark"
routerLink="/marketplace"
routerDirection="root"
class="marketplace"
>
<ion-icon slot="start" name="storefront-outline"></ion-icon>
Marketplace
</ion-button>

View File

@@ -1,18 +0,0 @@
:host {
display: block;
}
.welcome {
display: flex;
flex-direction: column;
justify-content: center;
height: 40vh;
}
.embassy {
font-family: "Montserrat", sans-serif;
}
.marketplace {
width: 50%;
}

View File

@@ -1,9 +0,0 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'
@Component({
selector: 'app-list-empty',
templateUrl: 'app-list-empty.component.html',
styleUrls: ['app-list-empty.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppListEmptyComponent {}

View File

@@ -12,9 +12,9 @@ import { StatusComponentModule } from 'src/app/components/status/status.componen
import { LaunchablePipeModule } from 'src/app/pipes/launchable/launchable.module'
import { UiPipeModule } from 'src/app/pipes/ui/ui.module'
import { AppListIconComponent } from './app-list-icon/app-list-icon.component'
import { AppListEmptyComponent } from './app-list-empty/app-list-empty.component'
import { AppListPkgComponent } from './app-list-pkg/app-list-pkg.component'
import { PackageInfoPipe } from './package-info.pipe'
import { WidgetListComponentModule } from 'src/app/components/widget-list/widget-list.component.module'
const routes: Routes = [
{
@@ -34,11 +34,11 @@ const routes: Routes = [
IonicModule,
RouterModule.forChild(routes),
BadgeMenuComponentModule,
WidgetListComponentModule,
],
declarations: [
AppListPage,
AppListIconComponent,
AppListEmptyComponent,
AppListPkgComponent,
PackageInfoPipe,
],

View File

@@ -1,6 +1,6 @@
<ion-header>
<ion-toolbar>
<ion-title>Services</ion-title>
<ion-title>Installed Services</ion-title>
<ion-buttons slot="end">
<badge-menu-button></badge-menu-button>
</ion-buttons>
@@ -10,16 +10,14 @@
<ion-content class="ion-padding">
<!-- loaded -->
<ng-container *ngIf="pkgs$ | async as pkgs; else loading">
<app-list-empty
*ngIf="!pkgs.length; else list"
class="ion-text-center ion-padding"
></app-list-empty>
<ng-container *ngIf="!pkgs.length; else list">
<div class="welcome-header">
<h1>Welcome to embassyOS</h1>
</div>
<widget-list></widget-list>
</ng-container>
<ng-template #list>
<ion-item-divider class="ion-padding-bottom"
>Installed Services</ion-item-divider
>
<ion-grid>
<ion-row>
<ion-col *ngFor="let pkg of pkgs" sizeSm="12" sizeLg="6">

View File

@@ -0,0 +1,9 @@
.welcome-header {
padding-bottom: 1rem;
text-align: center;
h1 {
font-weight: bold;
font-size: 2rem;
}
}

View File

@@ -0,0 +1,26 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { IonicModule } from '@ionic/angular'
import { RouterModule, Routes } from '@angular/router'
import { HomePage } from './home.page'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { WidgetListComponentModule } from 'src/app/components/widget-list/widget-list.component.module'
const routes: Routes = [
{
path: '',
component: HomePage,
},
]
@NgModule({
imports: [
CommonModule,
IonicModule,
RouterModule.forChild(routes),
BadgeMenuComponentModule,
WidgetListComponentModule,
],
declarations: [HomePage],
})
export class HomePageModule {}

View File

@@ -0,0 +1,13 @@
<ion-header>
<ion-toolbar>
<ion-title>Home</ion-title>
<ion-buttons slot="end">
<badge-menu-button></badge-menu-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<div style="padding: 36px 0">
<widget-list></widget-list>
</div>
</ion-content>

View File

@@ -0,0 +1,8 @@
import { Component } from '@angular/core'
@Component({
selector: 'home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {}

View File

@@ -1,32 +1,55 @@
<ion-content>
<ion-grid style="height: 100%; max-width: 540px;">
<ion-row class="ion-align-items-center" style="height: 90%;">
<ion-grid style="height: 100%; max-width: 540px">
<ion-row class="ion-align-items-center" style="height: 90%">
<ion-col class="ion-text-center">
<div style="padding-bottom: 16px;">
<img src="assets/img/logo.png" style="max-width: 240px;" />
<div style="padding-bottom: 16px">
<img src="assets/img/logo.png" style="max-width: 240px" />
</div>
<ion-card color="dark">
<ion-card-header class="ion-text-center" style="padding-bottom: 8px;">
<ion-card-title>Log in to Embassy</ion-card-title>
<ion-card>
<ion-card-header class="ion-text-center" style="padding-bottom: 8px">
<ion-card-title>Embassy Login</ion-card-title>
</ion-card-header>
<ion-card-content class="ion-margin">
<form (submit)="submit()" style="margin-bottom: 12px;">
<ion-card-content class="ion-margin ion-text-center">
<form
class="inline"
(submit)="submit()"
style="margin-bottom: 12px"
>
<ion-item-group>
<p class="input-label">Password</p>
<ion-item color="dark">
<ion-icon slot="start" name="key-outline" style="margin-right: 16px;"></ion-icon>
<ion-input [type]="unmasked ? 'text' : 'password'" name="password" [(ngModel)]="password" (ionChange)="error = ''"></ion-input>
<ion-icon
slot="start"
name="key-outline"
style="margin-right: 16px"
></ion-icon>
<ion-input
[type]="unmasked ? 'text' : 'password'"
name="password"
[(ngModel)]="password"
(ionChange)="error = ''"
placeholder="Password"
></ion-input>
<ion-button fill="clear" color="light" (click)="toggleMask()">
<ion-icon slot="icon-only" [name]="unmasked ? 'eye-off-outline' : 'eye-outline'" size="small"></ion-icon>
<ion-icon
slot="icon-only"
[name]="unmasked ? 'eye-off-outline' : 'eye-outline'"
size="small"
></ion-icon>
</ion-button>
</ion-item>
<p style="text-align: left; padding-top: 4px"><ion-text color="danger">{{ error }}</ion-text></p>
<p style="text-align: left; padding-top: 4px">
<ion-text color="danger">{{ error }}</ion-text>
</p>
</ion-item-group>
<ion-button class="login-button" type="submit" expand="block">
<span style="font-size: larger; font-weight: bold;">Log In</span>
<ion-button
class="login-button"
type="submit"
expand="block"
color="tertiary"
>
<span style="font-size: larger; font-weight: bold">Login</span>
</ion-button>
</form>
</ion-card-content>
@@ -34,4 +57,4 @@
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
</ion-content>

View File

@@ -1,14 +1,33 @@
ion-card-title {
margin: 24px 0;
font-family: 'Montserrat';
font-weight: 500;
font-size: x-large;
--color: var(--ion-color-light);
font-variant: all-small-caps;
--color: var(--ion-color-dark);
}
ion-button {
--border-radius: 0 4px 4px 0;
}
ion-item {
--border-style: solid;
--border-width: 1px;
--border-color: var(--ion-color-light);
--border-radius: 4px 0 0 4px;
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12);
ion-button {
--border-radius: 4px;
}
}
ion-card {
background: var(--ion-color-step-200);
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
border-radius: 44px;
}
.input-label {
@@ -16,12 +35,22 @@ ion-item {
padding-bottom: 2px;
font-size: small;
font-weight: bold;
color: var(--ion-color-dark);
}
.login-button {
margin-inline-start: 0;
margin-inline-end: 0;
margin-top: 24px;
height: 48px;
--background: linear-gradient(45deg, var(--ion-color-light) 16%, var(--ion-color-dark) 150%);
height: 49px;
}
.inline {
* {
display: inline-block;
vertical-align: middle;
}
}
.item-interactive {
--highlight-background: var(--ion-color-tertiary) !important;
}

View File

@@ -1,13 +1,6 @@
<ion-header>
<ion-toolbar>
<ion-title *ngIf="name$ | async as name; else loadingTitle">
{{ name.current }}
</ion-title>
<ng-template #loadingTitle>
<ion-title>
<ion-title>Loading<span class="loading-dots"></span></ion-title>
</ion-title>
</ng-template>
<ion-title> System Settings </ion-title>
<ion-buttons slot="end">
<badge-menu-button></badge-menu-button>
</ion-buttons>

View File

@@ -33,7 +33,6 @@ export class ServerShowPage {
powerClicks = 0
readonly server$ = this.patch.watch$('server-info')
readonly name$ = this.serverNameService.name$
readonly showUpdate$ = this.eosService.showUpdate$
readonly showDiskRepair$ = this.ClientStorageService.showDiskRepair$
@@ -54,11 +53,11 @@ export class ServerShowPage {
) {}
async presentModalName(): Promise<void> {
const name = await firstValueFrom(this.name$)
const name = await firstValueFrom(this.serverNameService.name$)
const options: GenericInputOptions = {
title: 'Edit Device Name',
message: 'This is for your reference only.',
title: 'Set Device Name',
message: 'This will be displayed in your browser tab',
label: 'Device Name',
useMask: false,
placeholder: name.default,
@@ -349,8 +348,8 @@ export class ServerShowPage {
Backups: [
{
title: 'Create Backup',
description: 'Back up your Embassy and all its services',
icon: 'save-outline',
description: 'Back up your Embassy and service data',
icon: 'duplicate-outline',
action: () =>
this.navCtrl.navigateForward(['backup'], { relativeTo: this.route }),
detail: true,
@@ -358,7 +357,7 @@ export class ServerShowPage {
},
{
title: 'Restore From Backup',
description: 'Restore one or more services from a prior backup',
description: 'Restore one or more services from backup',
icon: 'color-wand-outline',
action: () =>
this.navCtrl.navigateForward(['restore'], { relativeTo: this.route }),
@@ -366,7 +365,7 @@ export class ServerShowPage {
disabled$: this.eosService.updatingOrBackingUp$,
},
],
Settings: [
Manage: [
{
title: 'Software Update',
description: 'Get the latest version of embassyOS',
@@ -379,8 +378,8 @@ export class ServerShowPage {
disabled$: this.eosService.updatingOrBackingUp$,
},
{
title: 'Device Name',
description: 'Edit the local display name of your Embassy',
title: 'Set Device Name',
description: 'Give your device a name for easy identification',
icon: 'pricetag-outline',
action: () => this.presentModalName(),
detail: false,
@@ -388,7 +387,8 @@ export class ServerShowPage {
},
{
title: 'LAN',
description: 'Access your Embassy on the Local Area Network',
description:
'Install your Embassy certificate for a secure local connection',
icon: 'home-outline',
action: () =>
this.navCtrl.navigateForward(['lan'], { relativeTo: this.route }),
@@ -397,7 +397,8 @@ export class ServerShowPage {
},
{
title: 'SSH',
description: 'Access your Embassy from the command line',
description:
'Manage your SSH keys to access your Embassy from the command line',
icon: 'terminal-outline',
action: () =>
this.navCtrl.navigateForward(['ssh'], { relativeTo: this.route }),
@@ -480,7 +481,7 @@ export class ServerShowPage {
Support: [
{
title: 'User Manual',
description: 'View the Embassy user manual and FAQ',
description: 'Discover what your Embassy can do',
icon: 'map-outline',
action: () =>
window.open(