mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
41 changed files with 514 additions and 404 deletions
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@ -1,67 +1,65 @@ |
|||||
<sqx-title message="i18n:apps.listPageTitle" /> |
<sqx-title message="i18n:apps.listPageTitle" /> |
||||
@if (authState.userChanges | async; as user) { |
|
||||
<div class="panel-container page"> |
|
||||
<div class="apps-section"> |
|
||||
<h1 class="apps-title">{{ "apps.welcomeTitle" | sqxTranslate: { user: user.displayName } }}</h1> |
|
||||
|
|
||||
<div class="subtext">{{ "apps.welcomeSubtitle" | sqxTranslate }}</div> |
|
||||
</div> |
|
||||
|
|
||||
@if (groupedApps | async; as groups) { |
|
||||
<div class="apps-section" sqxTourStep="allApps"> |
|
||||
@for (group of groups; track trackByGroup($index, group)) { |
|
||||
<div class="team"> |
|
||||
@if (group.team) { |
|
||||
<div class="team-header"><sqx-team (leave)="leaveTeam($event)" [team]="group.team" /></div> |
|
||||
} |
|
||||
|
|
||||
<div class="team-body" [class.padded]="group.team"> |
@if (authState.userChanges | async; as user) { |
||||
@for (app of group.apps; track app.id) { |
<div class="panel-container page flex-grow d-flex flex-grow flex-column justify-content-between" style="flex-grow: 1"> |
||||
<sqx-app [app]="app" (leave)="leaveApp($event)" /> |
<div class="apps"> |
||||
} @empty { |
|
||||
<small class="team-empty"> {{ "teams.empty" | sqxTranslate }} </small> |
|
||||
} |
|
||||
</div> |
|
||||
</div> |
|
||||
} @empty { |
|
||||
<div class="empty"> |
|
||||
<h3 class="empty-headline">{{ "apps.empty" | sqxTranslate }}</h3> |
|
||||
</div> |
|
||||
} |
|
||||
</div> |
|
||||
} |
|
||||
|
|
||||
@if ((uiState.settings | async)?.canCreateApps) { |
|
||||
<div class="apps-section"> |
<div class="apps-section"> |
||||
<div class="card card-template card-href" (click)="createNewApp()" data-testid="new-app" sqxTourStep="addApp"> |
<div class="row align-items-center"> |
||||
<div class="card-body"> |
<div class="col"> |
||||
<div class="card-image"><img src="./images/add-app.svg" /></div> |
<h1 class="apps-title">{{ "apps.welcomeTitle" | sqxTranslate: { user: user.displayName } }}</h1> |
||||
|
|
||||
<h3 class="card-title">{{ "apps.createBlankApp" | sqxTranslate }}</h3> |
<div class="subtext">{{ "apps.welcomeSubtitle" | sqxTranslate }}</div> |
||||
<sqx-form-hint> {{ "apps.createBlankAppDescription" | sqxTranslate }} </sqx-form-hint> |
|
||||
</div> |
</div> |
||||
|
|
||||
|
@if ((uiState.settings | async)?.canCreateApps) { |
||||
|
<div class="col-auto"> |
||||
|
<button class="btn btn-block btn-success" (click)="addAppDialog.show()" data-testid="new-app" sqxTourStep="addApp" type="button"> |
||||
|
<i class="icon-plus"></i> {{ "apps.appsButtonCreate" | sqxTranslate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
} |
||||
</div> |
</div> |
||||
|
</div> |
||||
|
|
||||
@for (template of templates | async; track template) { |
@if (groupedApps | async; as groups) { |
||||
<div class="card card-template card-href" (click)="createNewApp(template)"> |
<div class="apps-section" sqxTourStep="allApps"> |
||||
<div class="card-body"> |
@for (group of groups; track trackByGroup($index, group)) { |
||||
<div class="card-image"><img src="./images/add-template.svg" /></div> |
<div class="team"> |
||||
|
@if (group.team) { |
||||
|
<div class="team-header"><sqx-team (leave)="leaveTeam($event)" [team]="group.team" /></div> |
||||
|
} |
||||
|
|
||||
<h3 class="card-title">{{ template.title }}</h3> |
<div class="team-body row g-2" [class.padded]="group.team"> |
||||
<sqx-form-hint> {{ template.description }} </sqx-form-hint> |
@for (app of group.apps; track app.id) { |
||||
|
<div class="col-12 col-md-6 col-lg-4"> |
||||
|
<sqx-app [app]="app" (leave)="leaveApp($event)" /> |
||||
|
</div> |
||||
|
} @empty { |
||||
|
<small class="team-empty"> {{ "teams.empty" | sqxTranslate }} </small> |
||||
|
} |
||||
|
</div> |
||||
</div> |
</div> |
||||
</div> |
} @empty { |
||||
} |
<div class="empty"> |
||||
</div> |
<h3 class="empty-headline">{{ "apps.empty" | sqxTranslate }}</h3> |
||||
} |
</div> |
||||
|
} |
||||
|
</div> |
||||
|
} |
||||
|
</div> |
||||
|
|
||||
@if (info) { |
@if (generalInfo) { |
||||
<div class="apps-section"> |
<div class="apps-section"> |
||||
<small class="info">{{ info }}</small> |
<small class="info">{{ generalInfo }}</small> |
||||
</div> |
</div> |
||||
} |
} |
||||
</div> |
</div> |
||||
} |
} |
||||
<sqx-app-form (dialogClose)="addAppDialog.hide()" *sqxModal="addAppDialog" [template]="addAppTemplate" /> |
|
||||
<sqx-onboarding-dialog (dialogClose)="onboardingDialog.hide()" *sqxModal="onboardingDialog" /> |
<sqx-onboarding-dialog (dialogClose)="onboardingDialog.hide()" *sqxModal="onboardingDialog" /> |
||||
|
|
||||
|
@if (starters | async; as templates) { |
||||
|
<sqx-app-form (dialogClose)="addAppDialog.hide()" *sqxModal="addAppDialog" [template]="addAppTemplate" [templates]="templates" /> |
||||
|
} |
||||
|
|
||||
<sqx-news-dialog (dialogClose)="newsDialog.hide()" [features]="newsFeatures!" *sqxModal="newsDialog" /> |
<sqx-news-dialog (dialogClose)="newsDialog.hide()" [features]="newsFeatures!" *sqxModal="newsDialog" /> |
||||
|
|||||
@ -1,31 +0,0 @@ |
|||||
<div class="table-items-row table-items-row-expandable"> |
|
||||
<div class="table-items-row-summary row gx-2 align-items-center"> |
|
||||
<div class="col"> |
|
||||
{{ template.title }} <sqx-form-hint class="truncate">{{ template.description }}</sqx-form-hint> |
|
||||
</div> |
|
||||
|
|
||||
<div class="col-auto"> |
|
||||
<div class="float-end"> |
|
||||
<button class="btn btn-outline-secondary btn-expand me-1" [class.expanded]="isExpanded" (click)="toggleExpanded()" type="button"> |
|
||||
<i class="icon-download"></i> |
|
||||
</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
@if (isExpanded) { |
|
||||
<div class="table-items-row-details"> |
|
||||
<div class="table-items-row-details-tabs clearfix"> |
|
||||
<h4>{{ "common.details" | sqxTranslate }}</h4> |
|
||||
</div> |
|
||||
|
|
||||
<div class="table-items-row-details-tab"> |
|
||||
@if (details | async; as loadedDetails) { |
|
||||
<div class="help" [sqxMarkdown]="loadedDetails"></div> |
|
||||
} @else { |
|
||||
<sqx-loader /> |
|
||||
} |
|
||||
</div> |
|
||||
</div> |
|
||||
} |
|
||||
</div> |
|
||||
@ -1,17 +0,0 @@ |
|||||
@import 'mixins'; |
|
||||
@import 'vars'; |
|
||||
|
|
||||
h4 { |
|
||||
font-size: 1rem; |
|
||||
font-weight: 500; |
|
||||
margin: 0; |
|
||||
margin-top: .5rem; |
|
||||
} |
|
||||
|
|
||||
.help { |
|
||||
::ng-deep { |
|
||||
h1 { |
|
||||
display: none; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,65 +0,0 @@ |
|||||
/* |
|
||||
* Squidex Headless CMS |
|
||||
* |
|
||||
* @license |
|
||||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
||||
*/ |
|
||||
|
|
||||
import { AsyncPipe } from '@angular/common'; |
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; |
|
||||
import { map, Observable, shareReplay } from 'rxjs'; |
|
||||
import { AppsState, ClientsState, FormHintComponent, LoaderComponent, MarkdownDirective, TemplateDetailsDto, TemplateDto, TemplatesService, TranslatePipe } from '@app/shared'; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'sqx-template', |
|
||||
styleUrls: ['./template.component.scss'], |
|
||||
templateUrl: './template.component.html', |
|
||||
changeDetection: ChangeDetectionStrategy.OnPush, |
|
||||
imports: [ |
|
||||
AsyncPipe, |
|
||||
FormHintComponent, |
|
||||
LoaderComponent, |
|
||||
MarkdownDirective, |
|
||||
TranslatePipe, |
|
||||
], |
|
||||
}) |
|
||||
export class TemplateComponent { |
|
||||
@Input({ required: true }) |
|
||||
public template!: TemplateDto; |
|
||||
|
|
||||
public isExpanded = false; |
|
||||
|
|
||||
public details?: Observable<string>; |
|
||||
|
|
||||
constructor( |
|
||||
private readonly clientsState: ClientsState, |
|
||||
private readonly appsState: AppsState, |
|
||||
private readonly templatesService: TemplatesService, |
|
||||
) { |
|
||||
} |
|
||||
|
|
||||
public ngOnChanges() { |
|
||||
this.details = this.templatesService.getTemplate(this.template).pipe(map(x => this.buildDetails(x)), shareReplay(1)); |
|
||||
} |
|
||||
|
|
||||
public toggleExpanded() { |
|
||||
this.isExpanded = !this.isExpanded; |
|
||||
} |
|
||||
|
|
||||
private buildDetails(dto: TemplateDetailsDto) { |
|
||||
const app = this.appsState.appName; |
|
||||
|
|
||||
let details = dto.details.replace(/<APP>/g, app); |
|
||||
|
|
||||
const client = this.clientsState.snapshot.clients[0]; |
|
||||
|
|
||||
if (client) { |
|
||||
const clientId = `${app}:${client.id}`; |
|
||||
|
|
||||
details = details.replace(/\<CLIENT_ID>/g, clientId); |
|
||||
details = details.replace(/\<CLIENT_SECRET>/g, client.secret); |
|
||||
} |
|
||||
|
|
||||
return details; |
|
||||
} |
|
||||
} |
|
||||
@ -1,37 +0,0 @@ |
|||||
<sqx-title message="i18n:common.templates" /> |
|
||||
<sqx-layout innerWidth="50" layout="main" titleIcon="download" titleText="i18n:common.templates"> |
|
||||
<ng-container menu> |
|
||||
<button class="btn btn-text-secondary me-2" (click)="reload()" shortcut="CTRL + B" title="i18n:templates.refreshTooltip" type="button"> |
|
||||
<i class="icon-reset"></i> {{ "common.refresh" | sqxTranslate }} |
|
||||
</button> |
|
||||
</ng-container> |
|
||||
<ng-container> |
|
||||
<sqx-list-view innerWidth="50rem" [isLoading]="templatesState.isLoading | async"> |
|
||||
<sqx-form-alert light="true"> |
|
||||
<div inline="true" [sqxMarkdown]="'templates.cliHint' | sqxTranslate"></div> |
|
||||
</sqx-form-alert> |
|
||||
@if ((templatesState.isLoaded | async) && (templatesState.templates | async); as templates) { |
|
||||
@for (template of templates; track template.name) { |
|
||||
<sqx-template [template]="template" /> |
|
||||
} |
|
||||
} |
|
||||
</sqx-list-view> |
|
||||
</ng-container> |
|
||||
<ng-template sidebarMenu> |
|
||||
<div class="panel-nav"> |
|
||||
<a |
|
||||
class="panel-link" |
|
||||
attr.aria-label="{{ 'common.help' | sqxTranslate }}" |
|
||||
queryParamsHandling="preserve" |
|
||||
replaceUrl="true" |
|
||||
routerLink="help" |
|
||||
routerLinkActive="active" |
|
||||
sqxTourStep="help" |
|
||||
title="i18n:common.help" |
|
||||
titlePosition="left"> |
|
||||
<i class="icon-help2"></i> |
|
||||
</a> |
|
||||
</div> |
|
||||
</ng-template> |
|
||||
</sqx-layout> |
|
||||
<router-outlet /> |
|
||||
@ -1,52 +0,0 @@ |
|||||
/* |
|
||||
* Squidex Headless CMS |
|
||||
* |
|
||||
* @license |
|
||||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
||||
*/ |
|
||||
|
|
||||
import { AsyncPipe } from '@angular/common'; |
|
||||
import { Component, OnInit } from '@angular/core'; |
|
||||
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; |
|
||||
import { ClientsState, FormAlertComponent, LayoutComponent, ListViewComponent, MarkdownDirective, ShortcutDirective, SidebarMenuDirective, TemplatesState, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; |
|
||||
import { TemplateComponent } from './template.component'; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'sqx-templates-page', |
|
||||
styleUrls: ['./templates-page.component.scss'], |
|
||||
templateUrl: './templates-page.component.html', |
|
||||
imports: [ |
|
||||
AsyncPipe, |
|
||||
FormAlertComponent, |
|
||||
LayoutComponent, |
|
||||
ListViewComponent, |
|
||||
MarkdownDirective, |
|
||||
RouterLink, |
|
||||
RouterLinkActive, |
|
||||
RouterOutlet, |
|
||||
ShortcutDirective, |
|
||||
SidebarMenuDirective, |
|
||||
TemplateComponent, |
|
||||
TitleComponent, |
|
||||
TooltipDirective, |
|
||||
TourStepDirective, |
|
||||
TranslatePipe, |
|
||||
], |
|
||||
}) |
|
||||
export class TemplatesPageComponent implements OnInit { |
|
||||
constructor( |
|
||||
public readonly clientsState: ClientsState, |
|
||||
public readonly templatesState: TemplatesState, |
|
||||
) { |
|
||||
} |
|
||||
|
|
||||
public ngOnInit() { |
|
||||
this.clientsState.load(); |
|
||||
|
|
||||
this.templatesState.load(); |
|
||||
} |
|
||||
|
|
||||
public reload() { |
|
||||
this.templatesState.load(true); |
|
||||
} |
|
||||
} |
|
||||
@ -1,2 +1,31 @@ |
|||||
|
@use 'sass:color'; |
||||
@import 'mixins'; |
@import 'mixins'; |
||||
@import 'vars'; |
@import 'vars'; |
||||
|
|
||||
|
.card-image { |
||||
|
width: 30%; |
||||
|
} |
||||
|
|
||||
|
.card-title { |
||||
|
margin: .25rem 0; |
||||
|
} |
||||
|
|
||||
|
.col-info { |
||||
|
background-color: color.adjust($color-background, $lightness: 2%); |
||||
|
border-radius: 0; |
||||
|
border-left: 1px solid $color-border; |
||||
|
padding: 1.5rem 2rem; |
||||
|
width: 18rem; |
||||
|
} |
||||
|
|
||||
|
.col-left { |
||||
|
padding: 1.5rem 2rem; |
||||
|
} |
||||
|
|
||||
|
.help { |
||||
|
::ng-deep { |
||||
|
h1 { |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue