Browse Source

Small UI improvements.

pull/1233/head
Sebastian Stehle 11 months ago
parent
commit
f3fb651287
  1. 8
      frontend/angular.json
  2. 2
      frontend/src/app/features/settings/pages/clients/client-add-form.component.html
  3. 2
      frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.html
  4. 2
      frontend/src/app/features/settings/pages/jobs/jobs-page.component.html
  5. 2
      frontend/src/app/features/settings/pages/languages/language-add-form.component.html
  6. 2
      frontend/src/app/features/settings/pages/plans/plans-page.component.html
  7. 48
      frontend/src/app/features/settings/pages/roles/role-add-form.component.html
  8. 2
      frontend/src/app/features/settings/pages/roles/roles-page.component.html
  9. 2
      frontend/src/app/features/settings/pages/templates/templates-page.component.html
  10. 48
      frontend/src/app/features/settings/pages/workflows/workflow-add-form.component.html
  11. 8
      frontend/src/app/features/settings/pages/workflows/workflows-page.component.html
  12. 2
      frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.html
  13. 7
      frontend/src/app/framework/angular/modals/dialog-renderer.component.html
  14. 47
      frontend/src/app/framework/angular/modals/dialog-renderer.component.scss
  15. 139
      frontend/src/app/framework/angular/modals/dialog-renderer.stories.ts
  16. 4
      frontend/src/app/theme/_bootstrap.scss

8
frontend/angular.json

@ -200,7 +200,13 @@
"configDir": ".storybook",
"browserTarget": "squidex:build",
"compodoc": false,
"port": 6006
"port": 6006,
"stylePreprocessorOptions": {
"includePaths": [
"./src/app/theme",
"node_modules"
]
}
}
},
"build-storybook": {

2
frontend/src/app/features/settings/pages/clients/client-add-form.component.html

@ -1,4 +1,4 @@
<div class="card card-body mb-4">
<div class="card card-body card-create mb-4">
<h5 class="card-title">{{ "clients.add.title" | sqxTranslate }}</h5>
<form [formGroup]="addClientForm.form" (ngSubmit)="addClient()">

2
frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.html

@ -1,4 +1,4 @@
<div class="card card-body mb-4">
<div class="card card-body card-create mb-4">
<h5 class="card-title">{{ "contributors.add.title" | sqxTranslate }}</h5>
<form [formGroup]="assignContributorForm.form" (ngSubmit)="assignContributor()">

2
frontend/src/app/features/settings/pages/jobs/jobs-page.component.html

@ -1,5 +1,5 @@
<sqx-title message="i18n:common.jobsBackups" />
<sqx-layout innerWidth="50" layout="main" titleIcon="jobs" titleText="i18n:common.jobsBackups">
<sqx-layout innerWidth="50" layout="main" titleIcon="backups" titleText="i18n:common.jobsBackups">
<ng-container menu>
<button class="btn btn-text-secondary me-2" (click)="reload()" shortcut="CTRL + B" title="i18n:jobs.refreshTooltip" type="button">
<i class="icon-reset"></i> {{ "common.refresh" | sqxTranslate }}

2
frontend/src/app/features/settings/pages/languages/language-add-form.component.html

@ -1,4 +1,4 @@
<div class="card card-body mb-4">
<div class="card card-body card-create mb-4">
<h5 class="card-title">{{ "languages.add.title" | sqxTranslate }}</h5>
<form [formGroup]="addLanguageForm.form" (ngSubmit)="addLanguage()">

2
frontend/src/app/features/settings/pages/plans/plans-page.component.html

@ -25,9 +25,7 @@
<h5 class="card-title">Squidex Referal Program</h5>
<sqx-form-hint>
<div [sqxMarkdown]="'plans.referralHint' | sqxTranslate: referral"></div>
<div [sqxMarkdown]="referral.condition"></div>
<div [sqxMarkdown]="'plans.referralEarned' | sqxTranslate: referral"></div>
</sqx-form-hint>
</div>

48
frontend/src/app/features/settings/pages/roles/role-add-form.component.html

@ -1,30 +1,28 @@
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ "roles.add.title" | sqxTranslate }}</h5>
<div class="card card-body card-create mb-4">
<h5 class="card-title">{{ "roles.add.title" | sqxTranslate }}</h5>
<form [formGroup]="addRoleForm.form" (ngSubmit)="addRole()">
<div class="row gx-2">
<div class="col">
<sqx-control-errors for="name" />
<input
class="form-control"
autocomplete="off"
formControlName="name"
maxlength="40"
placeholder="{{ 'roles.roleNamePlaceholder' | sqxTranslate }}" />
</div>
<form [formGroup]="addRoleForm.form" (ngSubmit)="addRole()">
<div class="row gx-2">
<div class="col">
<sqx-control-errors for="name" />
<input
class="form-control"
autocomplete="off"
formControlName="name"
maxlength="40"
placeholder="{{ 'roles.roleNamePlaceholder' | sqxTranslate }}" />
</div>
<div class="col-auto">
<button class="btn btn-success" [disabled]="addRoleForm.hasNoName | async" type="submit">
{{ "roles.add" | sqxTranslate }}
</button>
</div>
<div class="col-auto">
<button class="btn btn-success" [disabled]="addRoleForm.hasNoName | async" type="submit">
{{ "roles.add" | sqxTranslate }}
</button>
</div>
<div class="col-auto">
<button class="btn btn-text-secondary" (click)="cancel()" type="button">{{ "common.cancel" | sqxTranslate }}</button>
</div>
<div class="col-auto">
<button class="btn btn-text-secondary" (click)="cancel()" type="button">{{ "common.cancel" | sqxTranslate }}</button>
</div>
</form>
<sqx-form-hint> {{ "roles.add.description" | sqxTranslate }} </sqx-form-hint>
</div>
</div>
</form>
<sqx-form-hint> {{ "roles.add.description" | sqxTranslate }} </sqx-form-hint>
</div>

2
frontend/src/app/features/settings/pages/roles/roles-page.component.html

@ -15,7 +15,7 @@
@if (rolesState.defaultRoles | async; as defaultRoles) {
@if (defaultRoles.length > 0) {
<div>
<h5 class="mt-4">{{ "roles.defaultRoles" | sqxTranslate }}</h5>
<h5>{{ "roles.defaultRoles" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "roles.defaultRoles.hint" | sqxTranslate }} </sqx-form-hint>
@for (role of defaultRoles; track role.name) {
<sqx-role [allPermissions]="allPermissions" [role]="role" [schemas]="(schemasState.schemas | async)!" />

2
frontend/src/app/features/settings/pages/templates/templates-page.component.html

@ -1,5 +1,5 @@
<sqx-title message="i18n:common.templates" />
<sqx-layout innerWidth="50" layout="main" titleIcon="templates" titleText="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 }}

48
frontend/src/app/features/settings/pages/workflows/workflow-add-form.component.html

@ -1,30 +1,28 @@
<div class="card mt-4">
<div class="card-body">
<h5 class="card-title">{{ "workflows.add.title" | sqxTranslate }}</h5>
<div class="card card-body card-create mb-4">
<h5 class="card-title">{{ "workflows.add.title" | sqxTranslate }}</h5>
<form [formGroup]="addWorkflowForm.form" (ngSubmit)="addWorkflow()">
<div class="row gx-2">
<div class="col">
<sqx-control-errors for="name" />
<input
class="form-control"
autocomplete="off"
formControlName="name"
maxlength="40"
placeholder="{{ 'workflows.workflowNamePlaceholder' | sqxTranslate }}" />
</div>
<form [formGroup]="addWorkflowForm.form" (ngSubmit)="addWorkflow()">
<div class="row gx-2">
<div class="col">
<sqx-control-errors for="name" />
<input
class="form-control"
autocomplete="off"
formControlName="name"
maxlength="40"
placeholder="{{ 'workflows.workflowNamePlaceholder' | sqxTranslate }}" />
</div>
<div class="col-auto">
<button class="btn btn-success" [disabled]="addWorkflowForm.hasNoName | async" type="submit">
{{ "workflows.add" | sqxTranslate }}
</button>
</div>
<div class="col-auto">
<button class="btn btn-success" [disabled]="addWorkflowForm.hasNoName | async" type="submit">
{{ "workflows.add" | sqxTranslate }}
</button>
</div>
<div class="col-auto">
<button class="btn btn-text-secondary" (click)="cancel()" type="reset">{{ "common.cancel" | sqxTranslate }}</button>
</div>
<div class="col-auto">
<button class="btn btn-text-secondary" (click)="cancel()" type="reset">{{ "common.cancel" | sqxTranslate }}</button>
</div>
</form>
<sqx-form-hint> {{ "workflows.add.description" | sqxTranslate }} </sqx-form-hint>
</div>
</div>
</form>
<sqx-form-hint> {{ "workflows.add.description" | sqxTranslate }} </sqx-form-hint>
</div>

8
frontend/src/app/features/settings/pages/workflows/workflows-page.component.html

@ -24,6 +24,10 @@
}
}
@if (workflowsState.canCreate | async) {
<sqx-workflow-add-form />
}
@if (schemasSource && (workflowsState.isLoaded | async) && (workflowsState.workflows | async); as workflows) {
@for (workflow of workflows; track workflow.id) {
<sqx-workflow [roles]="roles" [schemasSource]="schemasSource" [workflow]="workflow" />
@ -32,10 +36,6 @@
{{ "workflows.empty" | sqxTranslate }}
</div>
}
@if (workflowsState.canCreate | async) {
<sqx-workflow-add-form />
}
}
</ng-container>
</sqx-list-view>

2
frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.html

@ -1,4 +1,4 @@
<div class="card card-body mb-4">
<div class="card card-body card-create mb-4">
<h5 class="card-title">{{ "contributors.add.title" | sqxTranslate }}</h5>
<form [formGroup]="assignContributorForm.form" (ngSubmit)="assignContributor()">

7
frontend/src/app/framework/angular/modals/dialog-renderer.component.html

@ -19,12 +19,13 @@
</sqx-modal-dialog>
}
</ng-container>
<div class="notification-container notification-container-bottom-right">
@for (notification of snapshot.notifications; track notification) {
<div class="alert alert-dismissible alert-{{ notification.messageType }} shadowed" @fade (click)="close(notification)" role="alert">
<div class="alert alert-light alert-dismissible border shadow-sm timed" @fade (click)="close(notification)" role="alert">
<button class="btn-close" data-dismiss="alert" (dialogClose)="close(notification)" type="button"></button>
<div class="shadow"></div>
<div class="shadowed" [sqxMarkdown]="notification.message | sqxTranslate"></div>
<div class="timer timer-{{ notification.messageType }}"></div>
<div [sqxMarkdown]="notification.message | sqxTranslate"></div>
</div>
}
</div>

47
frontend/src/app/framework/angular/modals/dialog-renderer.component.scss

@ -34,25 +34,31 @@
&-bottom-left {
@include fixed(auto, auto, 0, 0);
}
}
.shadow,
.shadowed {
position: relative;
}
.alert {
overflow-x: hidden;
overflow-y: hidden;
}
.timed {
position: relative;
}
.timer {
@include absolute(0, auto, 0, 0);
animation: width 10s 1 linear;
border: 0;
border-radius: 0;
height: 4px;
width: 100%;
.shadowed {
z-index: 2;
&-danger {
background-color: $color-theme-error;
}
.shadow {
@include absolute(0, auto, 0, 0);
animation: width 10s 1 linear;
background: $color-black;
border: 0;
opacity: .1;
overflow: hidden;
width: 100%;
z-index: 1;
&-primary {
background-color: $color-theme-brand;
}
}
@ -71,12 +77,13 @@ $caret-size: 6px;
color: $color-white;
font-size: $font-small;
font-weight: normal;
padding: .5rem;
padding: .25rem .5rem;
pointer-events: none;
white-space: nowrap;
&-left {
&::after {
@include caret-right($color-tooltip, $caret-size);
@include caret-right($color-tooltip, $caret-size + 1px);
@include absolute(50%, -$caret-size * 2, auto, auto);
margin-top: -$caret-size;
}
@ -84,7 +91,7 @@ $caret-size: 6px;
&-right {
&::after {
@include caret-left($color-tooltip, $caret-size);
@include caret-left($color-tooltip, $caret-size + 1px);
@include absolute(50%, auto, auto, -$caret-size * 2);
margin-top: -$caret-size;
}
@ -92,7 +99,7 @@ $caret-size: 6px;
&-top {
&::after {
@include caret-bottom($color-tooltip, $caret-size);
@include caret-bottom($color-tooltip, $caret-size + 1px);
@include absolute(auto, auto, -$caret-size * 2, 50%);
margin-left: -$caret-size;
}
@ -100,7 +107,7 @@ $caret-size: 6px;
&-bottom {
&::after {
@include caret-top($color-tooltip, $caret-size);
@include caret-top($color-tooltip, $caret-size + 1px);
@include absolute(-$caret-size * 2, auto, auto, 50%);
margin-left: -$caret-size;
}

139
frontend/src/app/framework/angular/modals/dialog-renderer.stories.ts

@ -0,0 +1,139 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, inject, Input } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Meta, moduleMetadata, StoryObj } from '@storybook/angular';
import { ConfirmClickDirective, DialogRendererComponent, DialogService, ErrorDto, LocalizerService, RootViewComponent, TooltipDirective } from '@app/framework';
type TestMode = 'ErrorText' | 'ErrorDetails' | 'Info';
@Component({
selector: 'sqx-test',
template: `
<button class="btn btn-primary" (click)="test()">
Show {{ mode }}
</button>
`,
imports: [
DialogRendererComponent,
],
})
class TestComponent {
public readonly dialogs = inject(DialogService);
@Input()
public mode: TestMode = 'Info';
public test() {
if (this.mode === 'ErrorDetails') {
const error = new ErrorDto(500,
'Error in Server',
'Error Code',
[
'Details 1',
'Details 2',
'Details 3',
'Details 4',
],
);
this.dialogs.notifyError(error);
} else if (this.mode === 'ErrorText') {
this.dialogs.notifyError('Error');
} else {
this.dialogs.notifyInfo('Info');
}
}
}
export default {
title: 'Framework/Dialogs',
component: DialogRendererComponent,
decorators: [
moduleMetadata({
imports: [
BrowserAnimationsModule,
ConfirmClickDirective,
RootViewComponent,
TestComponent,
TooltipDirective,
],
providers: [
DialogService,
{
provide: LocalizerService,
useValue: new LocalizerService({
'common.no': 'No',
'common.remember': 'Remember',
'common.yes': 'Yes',
}),
},
],
}),
],
render: args => ({
props: args,
template: `
<sqx-root-view>
<div class="p-4">
<h3>Dialogs</h3>
<div class="p-2 d-flex gap-2">
<sqx-test [mode]="'ErrorText'" />
<sqx-test [mode]="'ErrorDetails'" />
<sqx-test [mode]="'Info'" />
</div>
<h3 class="mt-4">Tooltips</h3>
<div class="p-2 d-flex gap-2">
<button class="btn btn-secondary" title="Tooltip" titlePosition="top">Top</button>
<button class="btn btn-secondary" title="Tooltip" titlePosition="left">Left</button>
<button class="btn btn-secondary" title="Tooltip" titlePosition="right">Right</button>
<button class="btn btn-secondary" title="Tooltip" titlePosition="bottom">Bottom</button>
</div>
<h3 class="mt-4">Immediate Tooltips</h3>
<div class="p-2 d-flex gap-2">
<button class="btn btn-secondary" [titleDelay]="0" title="Tooltip" titlePosition="top">Top</button>
<button class="btn btn-secondary" [titleDelay]="0" title="Tooltip" titlePosition="left">Left</button>
<button class="btn btn-secondary" [titleDelay]="0" title="Tooltip" titlePosition="right">Right</button>
<button class="btn btn-secondary" [titleDelay]="0" title="Tooltip" titlePosition="bottom">Bottom</button>
</div>
<h3 class="mt-4">Confirm</h3>
<div class="p-2 d-flex gap-2">
<button
class="btn btn-secondary"
confirmTitle="Show alert?"
confirmText="Really?"
(sqxConfirmClick)="alert('Click')">
Confirm
</button>
<button
class="btn btn-secondary"
confirmTitle="Show alert?"
confirmText="Really?"
confirmRememberKey="test"
(sqxConfirmClick)="alert('Click')">
Confirm Remember
</button>
</div>
</div>
<sqx-dialog-renderer />
</sqx-root-view>
`,
}),
} as Meta;
type Story = StoryObj<DialogRendererComponent & { mode: TestMode }>;
export const Primary: Story = {
args: {
},
};

4
frontend/src/app/theme/_bootstrap.scss

@ -669,6 +669,10 @@ $icon-size: 4.5rem;
&-title {
margin-bottom: 1rem;
}
&-create {
padding: 1.5rem 2rem;
}
}
//

Loading…
Cancel
Save