fix: settings cards with the page's padding and compact buttons
The Git, automations, webhooks and import cards had no inner padding and oversized buttons, because the settings page's styles do not reach child components; they now share a stylesheet with the same pills. The template button gets its own column instead of squeezing the type's name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -347,16 +347,6 @@ function idFrom(name: string): string {
|
|||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<code class="ts-id">{{ type.id }}</code>
|
<code class="ts-id">{{ type.id }}</code>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="ts-plain-link ts-template-toggle"
|
|
||||||
[attr.aria-expanded]="openTemplate() === i"
|
|
||||||
(click)="
|
|
||||||
openTemplate.set(openTemplate() === i ? null : i)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
Plantilla{{ hasTemplate(type) ? ' ●' : '' }}
|
|
||||||
</button>
|
|
||||||
</span>
|
</span>
|
||||||
<select
|
<select
|
||||||
class="ts-input"
|
class="ts-input"
|
||||||
@@ -388,6 +378,18 @@ function idFrom(name: string): string {
|
|||||||
</option>
|
</option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="ts-add ts-template-toggle"
|
||||||
|
[class.has-template]="hasTemplate(type)"
|
||||||
|
[attr.aria-expanded]="openTemplate() === i"
|
||||||
|
[attr.aria-label]="'Plantilla de ' + type.name"
|
||||||
|
(click)="
|
||||||
|
openTemplate.set(openTemplate() === i ? null : i)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Plantilla{{ hasTemplate(type) ? ' ●' : '' }}
|
||||||
|
</button>
|
||||||
<ng-container
|
<ng-container
|
||||||
[ngTemplateOutlet]="archive"
|
[ngTemplateOutlet]="archive"
|
||||||
[ngTemplateOutletContext]="{
|
[ngTemplateOutletContext]="{
|
||||||
@@ -804,12 +806,15 @@ function idFrom(name: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ts-template-toggle {
|
.ts-template-toggle {
|
||||||
font-size: 0.75rem;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
&.has-template {
|
||||||
|
border-color: var(--ts-brand) !important;
|
||||||
|
color: var(--ts-ink) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ts-type-grid {
|
.ts-type-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 2rem minmax(8rem, 1fr) 7rem 7rem 6.5rem;
|
grid-template-columns: 2rem minmax(8rem, 1fr) 7rem 7rem auto 6.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -940,6 +945,7 @@ function idFrom(name: string): string {
|
|||||||
/* @pi/ui styles every <button>: these opt back into a compact look. */
|
/* @pi/ui styles every <button>: these opt back into a compact look. */
|
||||||
.ts-add,
|
.ts-add,
|
||||||
.ts-archive,
|
.ts-archive,
|
||||||
|
.ts-template-toggle,
|
||||||
.ts-primary,
|
.ts-primary,
|
||||||
.ts-secondary {
|
.ts-secondary {
|
||||||
min-width: 0 !important;
|
min-width: 0 !important;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import {
|
import {
|
||||||
PiActionButtonComponent,
|
|
||||||
PiSelectFieldComponent,
|
PiSelectFieldComponent,
|
||||||
PiTextFieldComponent,
|
PiTextFieldComponent,
|
||||||
type PiSelectFieldOption,
|
type PiSelectFieldOption,
|
||||||
@@ -51,11 +50,8 @@ const ACTION: Record<AutomationActionKind, string> = {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ts-automations',
|
selector: 'ts-automations',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [PiSelectFieldComponent, PiTextFieldComponent],
|
||||||
PiActionButtonComponent,
|
styleUrl: './settings-card.scss',
|
||||||
PiSelectFieldComponent,
|
|
||||||
PiTextFieldComponent,
|
|
||||||
],
|
|
||||||
template: `
|
template: `
|
||||||
<section class="ts-panel ts-block ts-rules" aria-labelledby="rules-title">
|
<section class="ts-panel ts-block ts-rules" aria-labelledby="rules-title">
|
||||||
<div>
|
<div>
|
||||||
@@ -85,20 +81,20 @@ const ACTION: Record<AutomationActionKind, string> = {
|
|||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
<div class="ts-rules__actions">
|
<div class="ts-rules__actions">
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="grey"
|
class="ts-btn ts-btn--ghost"
|
||||||
appearance="ghost"
|
(click)="toggle(rule)"
|
||||||
[text]="rule.active ? 'Pausar' : 'Reanudar'"
|
>
|
||||||
(execute)="toggle(rule)"
|
{{ rule.active ? 'Pausar' : 'Reanudar' }}
|
||||||
/>
|
</button>
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="grey"
|
class="ts-btn ts-btn--ghost"
|
||||||
appearance="ghost"
|
(click)="remove(rule)"
|
||||||
text="Borrar"
|
>
|
||||||
(execute)="remove(rule)"
|
Borrar
|
||||||
/>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
}
|
}
|
||||||
@@ -150,13 +146,14 @@ const ACTION: Record<AutomationActionKind, string> = {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="primary"
|
class="ts-btn ts-btn--primary"
|
||||||
text="Añadir regla"
|
|
||||||
[disabled]="!canCreate()"
|
[disabled]="!canCreate()"
|
||||||
(execute)="create()"
|
(click)="create()"
|
||||||
/>
|
>
|
||||||
|
Añadir regla
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
untracked,
|
untracked,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { PiActionButtonComponent } from '@pi/ui';
|
|
||||||
import { ToastService } from '@task-stream/web/shared/ui';
|
import { ToastService } from '@task-stream/web/shared/ui';
|
||||||
import { GitGateway, GitIntegration } from '@task-stream/web/tracker/domain';
|
import { GitGateway, GitIntegration } from '@task-stream/web/tracker/domain';
|
||||||
|
|
||||||
@@ -20,7 +19,8 @@ import { GitGateway, GitIntegration } from '@task-stream/web/tracker/domain';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ts-git-integration',
|
selector: 'ts-git-integration',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [PiActionButtonComponent],
|
imports: [],
|
||||||
|
styleUrl: './settings-card.scss',
|
||||||
template: `
|
template: `
|
||||||
<section class="ts-panel ts-block ts-git" aria-labelledby="git-title">
|
<section class="ts-panel ts-block ts-git" aria-labelledby="git-title">
|
||||||
<div>
|
<div>
|
||||||
@@ -74,29 +74,30 @@ import { GitGateway, GitIntegration } from '@task-stream/web/tracker/domain';
|
|||||||
<em>pull/merge request</em>.
|
<em>pull/merge request</em>.
|
||||||
</p>
|
</p>
|
||||||
<div class="ts-git__actions">
|
<div class="ts-git__actions">
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="grey"
|
class="ts-btn ts-btn--secondary"
|
||||||
appearance="outline"
|
(click)="enable()"
|
||||||
text="Cambiar el secreto"
|
>
|
||||||
(execute)="enable()"
|
Cambiar el secreto
|
||||||
/>
|
</button>
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="grey"
|
class="ts-btn ts-btn--ghost"
|
||||||
appearance="ghost"
|
(click)="disable()"
|
||||||
text="Desactivar"
|
>
|
||||||
(execute)="disable()"
|
Desactivar
|
||||||
/>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<div>
|
<div>
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="primary"
|
class="ts-btn ts-btn--primary"
|
||||||
text="Activar"
|
(click)="enable()"
|
||||||
(execute)="enable()"
|
>
|
||||||
/>
|
Activar
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
74
libs/web/tracker/presenters/src/lib/ui/settings-card.scss
Normal file
74
libs/web/tracker/presenters/src/lib/ui/settings-card.scss
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// Shared by the cards of the settings page (Git, automations, webhooks,
|
||||||
|
// import and export): the same padding and the same compact pill buttons as
|
||||||
|
// the rest of that page, whose own styles do not reach child components.
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-block {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 1.25rem;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div > p,
|
||||||
|
> div > h3 + p {
|
||||||
|
margin: 0.25rem 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 2.125rem;
|
||||||
|
padding: 0 0.875rem;
|
||||||
|
border: 1px solid var(--ts-line-strong);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--ts-surface);
|
||||||
|
color: var(--ts-ink-soft);
|
||||||
|
font: inherit;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
border-color: var(--ts-brand);
|
||||||
|
color: var(--ts-ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-btn--primary {
|
||||||
|
border-color: transparent;
|
||||||
|
background: var(--ts-brand);
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
color: #fff;
|
||||||
|
filter: brightness(1.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-btn--ghost {
|
||||||
|
border-color: transparent;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-btn-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
signal,
|
signal,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { PiActionButtonComponent, PiFileUploadComponent } from '@pi/ui';
|
import { PiFileUploadComponent } from '@pi/ui';
|
||||||
import { ToastService } from '@task-stream/web/shared/ui';
|
import { ToastService } from '@task-stream/web/shared/ui';
|
||||||
import { ImportReport, TransferGateway } from '@task-stream/web/tracker/domain';
|
import { ImportReport, TransferGateway } from '@task-stream/web/tracker/domain';
|
||||||
|
|
||||||
@@ -15,7 +15,8 @@ import { ImportReport, TransferGateway } from '@task-stream/web/tracker/domain';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ts-transfer',
|
selector: 'ts-transfer',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [PiActionButtonComponent, PiFileUploadComponent],
|
imports: [PiFileUploadComponent],
|
||||||
|
styleUrl: './settings-card.scss',
|
||||||
template: `
|
template: `
|
||||||
<section
|
<section
|
||||||
class="ts-panel ts-block ts-transfer"
|
class="ts-panel ts-block ts-transfer"
|
||||||
@@ -30,14 +31,14 @@ import { ImportReport, TransferGateway } from '@task-stream/web/tracker/domain';
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="grey"
|
class="ts-btn ts-btn--secondary"
|
||||||
appearance="outline"
|
|
||||||
[text]="exporting() ? 'Exportando…' : 'Exportar CSV'"
|
|
||||||
[disabled]="exporting()"
|
[disabled]="exporting()"
|
||||||
(execute)="exportCsv()"
|
(click)="exportCsv()"
|
||||||
/>
|
>
|
||||||
|
{{ exporting() ? 'Exportando…' : 'Exportar CSV' }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<pi-file-upload
|
<pi-file-upload
|
||||||
@@ -85,17 +86,18 @@ import { ImportReport, TransferGateway } from '@task-stream/web/tracker/domain';
|
|||||||
</details>
|
</details>
|
||||||
}
|
}
|
||||||
@if (plan.dryRun && plan.rows) {
|
@if (plan.dryRun && plan.rows) {
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="primary"
|
class="ts-btn ts-btn--primary"
|
||||||
[text]="
|
[disabled]="importing()"
|
||||||
|
(click)="importCsv()"
|
||||||
|
>
|
||||||
|
{{
|
||||||
importing()
|
importing()
|
||||||
? 'Importando…'
|
? 'Importando…'
|
||||||
: 'Importar ' + plan.rows + ' tickets'
|
: 'Importar ' + plan.rows + ' tickets'
|
||||||
"
|
}}
|
||||||
[disabled]="importing()"
|
</button>
|
||||||
(execute)="importCsv()"
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
untracked,
|
untracked,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { PiActionButtonComponent, PiTextFieldComponent } from '@pi/ui';
|
import { PiTextFieldComponent } from '@pi/ui';
|
||||||
import { ToastService } from '@task-stream/web/shared/ui';
|
import { ToastService } from '@task-stream/web/shared/ui';
|
||||||
import { Webhook, WebhooksGateway } from '@task-stream/web/tracker/domain';
|
import { Webhook, WebhooksGateway } from '@task-stream/web/tracker/domain';
|
||||||
import { timeAgo } from './copy';
|
import { timeAgo } from './copy';
|
||||||
@@ -38,7 +38,8 @@ const EVENT_LABEL: Record<string, string> = {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ts-webhooks',
|
selector: 'ts-webhooks',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [PiActionButtonComponent, PiTextFieldComponent],
|
imports: [PiTextFieldComponent],
|
||||||
|
styleUrl: './settings-card.scss',
|
||||||
template: `
|
template: `
|
||||||
<section class="ts-panel ts-block ts-hooks" aria-labelledby="hooks-title">
|
<section class="ts-panel ts-block ts-hooks" aria-labelledby="hooks-title">
|
||||||
<div>
|
<div>
|
||||||
@@ -79,27 +80,27 @@ const EVENT_LABEL: Record<string, string> = {
|
|||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
<div class="ts-hooks__actions">
|
<div class="ts-hooks__actions">
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="grey"
|
class="ts-btn ts-btn--secondary"
|
||||||
appearance="outline"
|
(click)="test(hook)"
|
||||||
text="Probar"
|
>
|
||||||
(execute)="test(hook)"
|
Probar
|
||||||
/>
|
</button>
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="grey"
|
class="ts-btn ts-btn--ghost"
|
||||||
appearance="ghost"
|
(click)="toggle(hook)"
|
||||||
[text]="hook.active ? 'Pausar' : 'Reanudar'"
|
>
|
||||||
(execute)="toggle(hook)"
|
{{ hook.active ? 'Pausar' : 'Reanudar' }}
|
||||||
/>
|
</button>
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="grey"
|
class="ts-btn ts-btn--ghost"
|
||||||
appearance="ghost"
|
(click)="remove(hook)"
|
||||||
text="Borrar"
|
>
|
||||||
(execute)="remove(hook)"
|
Borrar
|
||||||
/>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
}
|
}
|
||||||
@@ -133,13 +134,14 @@ const EVENT_LABEL: Record<string, string> = {
|
|||||||
}
|
}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div>
|
<div>
|
||||||
<pi-action-button
|
<button
|
||||||
size="small"
|
type="button"
|
||||||
color="primary"
|
class="ts-btn ts-btn--primary"
|
||||||
text="Añadir webhook"
|
|
||||||
[disabled]="!url().trim() || !picked().length"
|
[disabled]="!url().trim() || !picked().length"
|
||||||
(execute)="create()"
|
(click)="create()"
|
||||||
/>
|
>
|
||||||
|
Añadir webhook
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user