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:
@@ -16,7 +16,13 @@ const routes: Routes = [
|
||||
import('./pages/login/login.module').then(m => m.LoginPageModule),
|
||||
},
|
||||
{
|
||||
path: 'embassy',
|
||||
path: 'home',
|
||||
canActivate: [AuthGuard],
|
||||
loadChildren: () =>
|
||||
import('./pages/home/home.module').then(m => m.HomePageModule),
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
canActivate: [AuthGuard],
|
||||
canActivateChild: [AuthGuard],
|
||||
loadChildren: () =>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, OnDestroy } from '@angular/core'
|
||||
import { merge, take } from 'rxjs'
|
||||
import { merge } from 'rxjs'
|
||||
import { AuthService } from './services/auth.service'
|
||||
import { SplitPaneTracker } from './services/split-pane.service'
|
||||
import { PatchDataService } from './services/patch-data.service'
|
||||
@@ -28,9 +28,9 @@ export class AppComponent implements OnDestroy {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.serverNameService.name$
|
||||
.pipe(take(1))
|
||||
.subscribe(({ current }) => this.titleService.setTitle(current))
|
||||
this.serverNameService.name$.subscribe(({ current }) =>
|
||||
this.titleService.setTitle(current),
|
||||
)
|
||||
}
|
||||
|
||||
splitPaneVisible({ detail }: any) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<a class="logo ion-padding" routerLink="/services">
|
||||
<a class="logo ion-padding" routerLink="/home">
|
||||
<img alt="Start9" src="assets/img/logo.png" />
|
||||
</a>
|
||||
<div class="divider"></div>
|
||||
<ion-item-group class="menu">
|
||||
<ion-menu-toggle *ngFor="let page of pages" auto-hide="false">
|
||||
<ion-item
|
||||
@@ -23,7 +22,7 @@
|
||||
{{ page.title }}
|
||||
</ion-label>
|
||||
<ion-icon
|
||||
*ngIf="page.url === '/embassy' && (showEOSUpdate$ | async)"
|
||||
*ngIf="page.url === '/settings' && (showEOSUpdate$ | async)"
|
||||
color="success"
|
||||
size="small"
|
||||
name="rocket-outline"
|
||||
|
||||
@@ -23,25 +23,25 @@ export class MenuComponent {
|
||||
icon: 'grid-outline',
|
||||
},
|
||||
{
|
||||
title: 'Embassy',
|
||||
url: '/embassy',
|
||||
icon: 'cube-outline',
|
||||
title: 'Marketplace',
|
||||
url: '/marketplace',
|
||||
icon: 'storefront-outline',
|
||||
},
|
||||
{
|
||||
title: 'Updates',
|
||||
url: '/updates',
|
||||
icon: 'globe-outline',
|
||||
},
|
||||
{
|
||||
title: 'Marketplace',
|
||||
url: '/marketplace',
|
||||
icon: 'storefront-outline',
|
||||
},
|
||||
{
|
||||
title: 'Notifications',
|
||||
url: '/notifications',
|
||||
icon: 'notifications-outline',
|
||||
},
|
||||
{
|
||||
title: 'System Settings',
|
||||
url: '/settings',
|
||||
icon: 'settings-outline',
|
||||
},
|
||||
]
|
||||
|
||||
readonly notificationCount$ = this.patch.watch$(
|
||||
|
||||
@@ -28,9 +28,9 @@ const ICONS = [
|
||||
'color-wand-outline',
|
||||
'construct-outline',
|
||||
'copy-outline',
|
||||
'cube-outline',
|
||||
'desktop-outline',
|
||||
'download-outline',
|
||||
'duplicate-outline',
|
||||
'earth-outline',
|
||||
'ellipsis-horizontal',
|
||||
'eye-off-outline',
|
||||
@@ -46,7 +46,6 @@ const ICONS = [
|
||||
'information-circle-outline',
|
||||
'key-outline',
|
||||
'list-outline',
|
||||
'lock-closed-outline',
|
||||
'log-out-outline',
|
||||
'logo-bitcoin',
|
||||
'mail-outline',
|
||||
@@ -72,6 +71,7 @@ const ICONS = [
|
||||
'remove-outline',
|
||||
'rocket-outline',
|
||||
'save-outline',
|
||||
'settings-outline',
|
||||
'shield-checkmark-outline',
|
||||
'stop-outline',
|
||||
'storefront-outline',
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<ng-template #content>
|
||||
<ng-content></ng-content>
|
||||
</ng-template>
|
||||
|
||||
<a
|
||||
*ngIf="externalLink; else internal"
|
||||
[href]="link"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||
</a>
|
||||
<ng-template #internal>
|
||||
<a [routerLink]="link">
|
||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||
</a>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,12 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { RouterModule } from '@angular/router'
|
||||
|
||||
import { AnyLinkComponent } from './any-link.component'
|
||||
|
||||
@NgModule({
|
||||
declarations: [AnyLinkComponent],
|
||||
imports: [CommonModule, RouterModule.forChild([])],
|
||||
exports: [AnyLinkComponent],
|
||||
})
|
||||
export class AnyLinkModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: unset;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
ChangeDetectionStrategy,
|
||||
OnInit,
|
||||
} from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'any-link',
|
||||
templateUrl: './any-link.component.html',
|
||||
styleUrls: ['./any-link.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AnyLinkComponent implements OnInit {
|
||||
@Input() link!: string
|
||||
externalLink: boolean = false
|
||||
|
||||
ngOnInit() {
|
||||
try {
|
||||
const _ = new URL(this.link)
|
||||
this.externalLink = true
|
||||
} catch {
|
||||
this.externalLink = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<ion-card>
|
||||
<any-link link="{{ link }}">
|
||||
<div class="p1">
|
||||
<ion-card-header>
|
||||
<ion-card-title>{{ title }}</ion-card-title>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-icon name="{{ icon }}" style="color: {{ color }}"></ion-icon>
|
||||
</ion-card-content>
|
||||
<ion-footer>
|
||||
{{ description }}
|
||||
</ion-footer>
|
||||
</div>
|
||||
</any-link>
|
||||
</ion-card>
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { WidgetCardComponent } from './widget-card.component'
|
||||
import { AnyLinkModule } from 'src/app/components/any-link/any-link.component.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [WidgetCardComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
AnyLinkModule,
|
||||
],
|
||||
exports: [WidgetCardComponent],
|
||||
})
|
||||
export class WidgetCardComponentModule {}
|
||||
@@ -0,0 +1,50 @@
|
||||
ion-card {
|
||||
background: rgba(70, 70, 70, 0.31);
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 44px;
|
||||
margin: auto;
|
||||
max-width: 22rem;
|
||||
text-align: center;
|
||||
transition: all 350ms ease;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
transition-property: transform;
|
||||
transform: scale(1.05);
|
||||
transition-delay: 40ms;
|
||||
}
|
||||
|
||||
ion-card-title {
|
||||
font-family: 'Open Sans';
|
||||
padding: 0.6rem;
|
||||
font-weight: 600;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
ion-card-content {
|
||||
min-height: 9rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
ion-icon {
|
||||
font-size: 8rem;
|
||||
--ionicon-stroke-width: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
ion-footer {
|
||||
padding: 1rem;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.footer-md::before {
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
|
||||
.p1 {
|
||||
padding: 1.2rem;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'widget-card',
|
||||
templateUrl: './widget-card.component.html',
|
||||
styleUrls: ['./widget-card.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class WidgetCardComponent {
|
||||
@Input() title: string = ''
|
||||
@Input() icon: string = ''
|
||||
@Input() color: string = ''
|
||||
@Input() description: string = ''
|
||||
@Input() link: string = ''
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<ion-grid>
|
||||
<ion-row class="ion-justify-content-center ion-align-items-center">
|
||||
<ion-col
|
||||
*ngFor="let card of cards"
|
||||
size="auto"
|
||||
class="ion-align-self-center"
|
||||
>
|
||||
<widget-card
|
||||
title="{{ card.title }}"
|
||||
icon="{{ card.icon }}"
|
||||
color="{{ card.color }}"
|
||||
description="{{ card.description }}"
|
||||
link="{{ card.link }}"
|
||||
></widget-card>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { WidgetListComponent } from './widget-list.component'
|
||||
import { AnyLinkModule } from 'src/app/components/any-link/any-link.component.module'
|
||||
import { WidgetCardComponentModule } from '../widget-card/widget-card.component.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [WidgetListComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
AnyLinkModule,
|
||||
WidgetCardComponentModule,
|
||||
],
|
||||
exports: [WidgetListComponent],
|
||||
})
|
||||
export class WidgetListComponentModule {}
|
||||
@@ -0,0 +1,7 @@
|
||||
ion-row {
|
||||
grid-row-gap: 1rem;
|
||||
}
|
||||
|
||||
ion-col {
|
||||
padding: 0 0.5rem 0.5rem 1rem;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'widget-list',
|
||||
templateUrl: './widget-list.component.html',
|
||||
styleUrls: ['./widget-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class WidgetListComponent {
|
||||
constructor() {}
|
||||
|
||||
cards: Card[] = [
|
||||
{
|
||||
title: 'Visit the Marketplace',
|
||||
icon: 'storefront-outline',
|
||||
color: 'var(--alt-blue)',
|
||||
description: 'Shop for your favorite open source services',
|
||||
link: '/marketplace/browse',
|
||||
},
|
||||
{
|
||||
title: 'LAN Setup',
|
||||
icon: 'home-outline',
|
||||
color: 'var(--alt-orange)',
|
||||
description:
|
||||
'Install your Embassy certificate for a secure local connection',
|
||||
link: '/settings/lan',
|
||||
},
|
||||
{
|
||||
title: 'Create Backup',
|
||||
icon: 'duplicate-outline',
|
||||
color: 'var(--alt-purple)',
|
||||
description: 'Back up your Embassy and service data',
|
||||
link: '/settings/backup',
|
||||
},
|
||||
{
|
||||
title: 'Embassy Info',
|
||||
icon: 'information-circle-outline',
|
||||
color: 'var(--alt-green)',
|
||||
description: 'View basic information about your Embassy',
|
||||
link: '/settings/specs',
|
||||
},
|
||||
{
|
||||
title: 'User Manual',
|
||||
icon: 'map-outline',
|
||||
color: 'var(--alt-yellow)',
|
||||
description: 'Discover what your Embassy can do',
|
||||
link: 'https://docs.start9.com/latest/user-manual/index',
|
||||
},
|
||||
{
|
||||
title: 'Contact Support',
|
||||
icon: 'chatbubbles-outline',
|
||||
color: 'var(--alt-red)',
|
||||
description: 'Get help from the Start9 team and community',
|
||||
link: 'https://docs.start9.com/latest/support/contact',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
interface Card {
|
||||
title: string
|
||||
icon: string
|
||||
color: string
|
||||
description: string
|
||||
link: string
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -26,6 +26,13 @@
|
||||
src: url('/assets/fonts/Open_Sans/OpenSans-Regular.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('/assets/fonts/Open_Sans/OpenSans-SemiBold.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
|
||||
Reference in New Issue
Block a user