split html's

This commit is contained in:
Mateusz Gruszczyński
2026-04-08 11:08:41 +02:00
parent f0f20e416e
commit 57cc30427a
38 changed files with 1625 additions and 1643 deletions

View File

@@ -22,221 +22,7 @@ const monthRange = (period: string) => {
selector: 'app-integrations',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, CurrencyPipe, DatePipe],
template: `
<div class="page-header d-print-none mb-3 ec-page-header">
<div class="row align-items-center g-3">
<div class="col">
<h2 class="page-title mb-1">{{ ui.t('integrations.title') }}</h2>
<div class="text-secondary">{{ ui.t('integrations.subtitle') }}</div>
</div>
</div>
</div>
<div class="row row-cards mb-3">
<div class="col-lg-5">
<div class="card overflow-hidden ec-accent-card ec-accent-card-primary h-100">
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
<h3 class="card-title mb-0">{{ ui.t('integrations.shoppingList') }}</h3>
<a class="btn btn-outline-secondary btn-sm" href="https://git.linuxiarz.pl/gru/lista_zakupowa_live" target="_blank" rel="noreferrer">{{ ui.t('integrations.projectLink') }}</a>
</div>
<div class="card-body d-grid gap-3">
<div class="alert alert-info mb-0">
<div class="fw-semibold mb-1">{{ ui.t('integrations.selfHostedTitle') }}</div>
<div>{{ ui.t('integrations.selfHostedHint') }}</div>
</div>
<form [formGroup]="form" (ngSubmit)="save()" class="d-grid gap-3">
<label class="form-check">
<input class="form-check-input" type="checkbox" formControlName="enabled" />
<span class="form-check-label">{{ ui.t('integrations.enabled') }}</span>
</label>
<div>
<label class="form-label">{{ ui.t('integrations.baseUrl') }}</label>
<input class="form-control" formControlName="baseUrl" placeholder="https://host.example.com" />
</div>
<div>
<label class="form-label">{{ ui.t('integrations.apiToken') }}</label>
<input class="form-control" formControlName="apiToken" type="password" />
<div class="form-hint">{{ ui.t('integrations.keepToken') }}</div>
</div>
<div>
<label class="form-label">{{ ui.t('integrations.authMode') }}</label>
<select class="form-select" formControlName="authMode">
<option value="both">Bearer + X-API-Token</option>
<option value="bearer">Bearer</option>
<option value="x-api-token">X-API-Token</option>
</select>
</div>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">{{ ui.t('integrations.ownerId') }}</label>
<input class="form-control" formControlName="ownerId" />
</div>
<div class="col-md-6">
<label class="form-label">{{ ui.t('integrations.defaultListId') }}</label>
<input class="form-control" formControlName="defaultListId" />
</div>
</div>
<div class="btn-list flex-wrap">
<button class="btn btn-success" [disabled]="form.invalid">{{ ui.t('action.save') }}</button>
<button class="btn btn-outline-info" type="button" (click)="test()">{{ ui.t('action.testConnection') }}</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-7">
<div class="card overflow-hidden h-100">
<div class="card-header"><h3 class="card-title">{{ ui.t('integrations.history') }}</h3></div>
<div class="card-body d-grid gap-3">
<form [formGroup]="historyForm" class="row g-3 align-items-end">
<div class="col-md-4">
<label class="form-label">{{ ui.t('integrations.period') }}</label>
<input class="form-control" type="month" formControlName="period" />
</div>
<div class="col-md-3">
<label class="form-label">{{ ui.t('integrations.limit') }}</label>
<input class="form-control" type="number" min="1" max="300" formControlName="limit" />
</div>
<div class="col-md-5">
<div class="btn-list justify-content-md-end">
<button class="btn btn-primary" type="button" (click)="refresh()">{{ ui.t('action.refresh') }}</button>
</div>
</div>
</form>
<div class="row row-cards">
<div class="col-md-4"><div class="ec-stat-tile ec-stat-tile-primary"><div class="ec-stat-label">{{ ui.t('integrations.externalLists') }}</div><div class="ec-stat-value">{{ summaryListCount() }}</div></div></div>
<div class="col-md-4"><div class="ec-stat-tile ec-stat-tile-success"><div class="ec-stat-label">{{ ui.t('integrations.externalSpend') }}</div><div class="ec-stat-value">{{ summaryAmount() | currency:'PLN':'symbol':'1.2-2' }}</div></div></div>
<div class="col-md-4"><div class="ec-stat-tile"><div class="ec-stat-label">{{ ui.t('integrations.externalCount') }}</div><div class="ec-stat-value">{{ summaryCount() }}</div></div></div>
</div>
<div class="border rounded-3 p-3 bg-body-tertiary">
@if (configured()) {
<div class="d-flex justify-content-between gap-2 flex-wrap align-items-center">
<div>
<div class="fw-semibold">{{ ui.t('integrations.summary') }}</div>
<div class="text-secondary small">{{ historyForm.controls.period.value }}</div>
</div>
<div class="text-end small text-secondary">
{{ ui.t('integrations.summaryLists') }}: <strong>{{ summaryListCount() }}</strong> ·
{{ ui.t('integrations.summarySpend') }}: <strong>{{ summaryAmount() | number:'1.2-2' }} PLN</strong>
</div>
</div>
} @else {
<div class="text-secondary">{{ ui.t('integrations.notConfigured') }}</div>
}
</div>
</div>
</div>
</div>
</div>
<div class="row row-cards mb-3">
<div class="col-lg-4">
<div class="card overflow-hidden">
<div class="card-header d-flex justify-content-between align-items-center gap-2">
<h3 class="card-title mb-0">{{ ui.t('integrations.lists') }}</h3>
<span class="badge text-bg-secondary">{{ visibleLists().length }}</span>
</div>
<div class="list-group list-group-flush ec-scroll-list">
@for (item of visibleLists(); track item.id) {
<button class="list-group-item list-group-item-action text-start" type="button" [class.active]="isSelectedList(item)" (click)="selectList(item)">
<div class="fw-semibold">{{ listTitle(item) }}</div>
<div class="small text-secondary">{{ listCreatedAt(item) | date:'yyyy-MM-dd' }} · {{ listOwner(item) || ui.t('common.none') }}</div>
</button>
} @empty {
<div class="list-group-item text-secondary">{{ ui.t('common.noData') }}</div>
}
</div>
</div>
</div>
<div class="col-lg-8">
<div class="card overflow-hidden ec-accent-card ec-accent-card-success">
<div class="card-header"><h3 class="card-title">{{ ui.t('integrations.importTitle') }}</h3></div>
<div class="card-body d-grid gap-3">
<div class="alert alert-warning mb-0">
<div class="fw-semibold mb-1">{{ ui.t('integrations.importExplainTitle') }}</div>
<div>{{ ui.t('integrations.importExplainBodySimple') }}</div>
</div>
<form [formGroup]="importForm" class="row g-3">
<div class="col-md-5">
<label class="form-label">{{ ui.t('expenses.field.category') }}</label>
<select class="form-select" formControlName="categoryId">
<option value="">{{ ui.t('common.select') }}</option>
@for (item of categories(); track item.id) {
<option [value]="item.id">{{ item.name }}</option>
}
</select>
</div>
<div class="col-md-3">
<label class="form-label">{{ ui.t('expenses.field.status') }}</label>
<select class="form-select" formControlName="status">
<option value="DRAFT">{{ ui.t('status.draft') }}</option>
<option value="PENDING">{{ ui.t('status.pending') }}</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label">{{ ui.t('table.merchant') }}</label>
<input class="form-control" formControlName="merchant" />
</div>
</form>
<div class="row g-3">
<div class="col-md-6">
<div class="border rounded-3 p-3 h-100 bg-body-tertiary">
<div class="fw-semibold mb-1">{{ ui.t('integrations.importMonthTitle') }}</div>
<div class="text-secondary small mb-3">{{ ui.t('integrations.importMonthHint') }}</div>
<button class="btn btn-primary" type="button" [disabled]="importForm.invalid || !configured()" (click)="importPeriod()">{{ ui.t('integrations.importPeriod') }}</button>
</div>
</div>
<div class="col-md-6">
<div class="border rounded-3 p-3 h-100 bg-body-tertiary">
<div class="fw-semibold mb-1">{{ ui.t('integrations.importListTitle') }}</div>
<div class="text-secondary small mb-3">{{ selectedList() ? listTitle(selectedList()) : ui.t('integrations.selectListHintSimple') }}</div>
<button class="btn btn-success" type="button" [disabled]="importForm.invalid || !selectedList()" (click)="importSelectedList()">{{ ui.t('integrations.importSelectedList') }}</button>
</div>
</div>
</div>
@if (selectedList()) {
<div class="border rounded-3 p-3 bg-body-tertiary">
<div class="d-flex justify-content-between gap-2 flex-wrap align-items-start mb-2">
<div>
<div class="fw-semibold">{{ listTitle(selectedList()) }}</div>
<div class="small text-secondary">{{ listCreatedAt(selectedList()) | date:'yyyy-MM-dd' }} · {{ listOwner(selectedList()) || ui.t('common.none') }}</div>
</div>
<div class="text-end">
<div class="small text-secondary">{{ ui.t('integrations.selectedListSummary') }}</div>
<div class="fw-semibold">{{ selectedListCount() }} / {{ selectedListTotal() | currency:'PLN':'symbol':'1.2-2' }}</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-sm table-vcenter mb-0">
<thead><tr><th>{{ ui.t('table.title') }}</th><th>{{ ui.t('table.date') }}</th><th class="text-end">{{ ui.t('table.amount') }}</th></tr></thead>
<tbody>
@for (item of selectedListExpenses(); track $index) {
<tr>
<td>{{ itemTitle(item) }}</td>
<td>{{ itemDate(item) }}</td>
<td class="text-end">{{ itemAmount(item) | currency:'PLN':'symbol':'1.2-2' }}</td>
</tr>
} @empty {
<tr><td colspan="3" class="text-secondary">{{ ui.t('common.noData') }}</td></tr>
}
</tbody>
</table>
</div>
</div>
}
</div>
</div>
</div>
</div>
`
templateUrl: './integrations.component.html'
})
export class IntegrationsComponent implements OnInit {
readonly ui = inject(UiService);