mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
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:
@@ -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>
|
||||
@@ -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%;
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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,
|
||||
],
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.welcome-header {
|
||||
padding-bottom: 1rem;
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
26
frontend/projects/ui/src/app/pages/home/home.module.ts
Normal file
26
frontend/projects/ui/src/app/pages/home/home.module.ts
Normal 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 {}
|
||||
13
frontend/projects/ui/src/app/pages/home/home.page.html
Normal file
13
frontend/projects/ui/src/app/pages/home/home.page.html
Normal 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>
|
||||
8
frontend/projects/ui/src/app/pages/home/home.page.ts
Normal file
8
frontend/projects/ui/src/app/pages/home/home.page.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Component } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'home',
|
||||
templateUrl: 'home.page.html',
|
||||
styleUrls: ['home.page.scss'],
|
||||
})
|
||||
export class HomePage {}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user