Split invoice history table into two tables for paid and open (#11459)
This commit is contained in:
parent
0c2d2ada34
commit
49b26db27e
|
@ -11,8 +11,12 @@
|
|||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="invoices || transactions">
|
||||
<app-billing-history [invoices]="invoices" [transactions]="transactions"></app-billing-history>
|
||||
<ng-container *ngIf="openInvoices || paidInvoices || transactions">
|
||||
<app-billing-history
|
||||
[openInvoices]="openInvoices"
|
||||
[paidInvoices]="paidInvoices"
|
||||
[transactions]="transactions"
|
||||
></app-billing-history>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
|
|
|
@ -14,7 +14,8 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
|||
export class BillingHistoryViewComponent implements OnInit {
|
||||
loading = false;
|
||||
firstLoaded = false;
|
||||
invoices: BillingInvoiceResponse[] = [];
|
||||
openInvoices: BillingInvoiceResponse[] = [];
|
||||
paidInvoices: BillingInvoiceResponse[] = [];
|
||||
transactions: BillingTransactionResponse[] = [];
|
||||
hasAdditionalHistory: boolean = false;
|
||||
|
||||
|
@ -41,8 +42,14 @@ export class BillingHistoryViewComponent implements OnInit {
|
|||
}
|
||||
this.loading = true;
|
||||
|
||||
const invoicesPromise = this.accountBillingApiService.getBillingInvoices(
|
||||
this.invoices.length > 0 ? this.invoices[this.invoices.length - 1].id : null,
|
||||
const openInvoicesPromise = this.accountBillingApiService.getBillingInvoices(
|
||||
"open",
|
||||
this.openInvoices.length > 0 ? this.openInvoices[this.openInvoices.length - 1].id : null,
|
||||
);
|
||||
|
||||
const paidInvoicesPromise = this.accountBillingApiService.getBillingInvoices(
|
||||
"paid",
|
||||
this.paidInvoices.length > 0 ? this.paidInvoices[this.paidInvoices.length - 1].id : null,
|
||||
);
|
||||
|
||||
const transactionsPromise = this.accountBillingApiService.getBillingTransactions(
|
||||
|
@ -51,15 +58,20 @@ export class BillingHistoryViewComponent implements OnInit {
|
|||
: null,
|
||||
);
|
||||
|
||||
const accountInvoices = await invoicesPromise;
|
||||
const accountTransactions = await transactionsPromise;
|
||||
const openInvoices = await openInvoicesPromise;
|
||||
const paidInvoices = await paidInvoicesPromise;
|
||||
const transactions = await transactionsPromise;
|
||||
|
||||
const pageSize = 5;
|
||||
|
||||
this.invoices = [...this.invoices, ...accountInvoices];
|
||||
this.transactions = [...this.transactions, ...accountTransactions];
|
||||
this.hasAdditionalHistory = !(
|
||||
accountInvoices.length < pageSize && accountTransactions.length < pageSize
|
||||
);
|
||||
this.openInvoices = [...this.openInvoices, ...openInvoices];
|
||||
this.paidInvoices = [...this.paidInvoices, ...paidInvoices];
|
||||
this.transactions = [...this.transactions, ...transactions];
|
||||
|
||||
this.hasAdditionalHistory =
|
||||
openInvoices.length >= pageSize ||
|
||||
paidInvoices.length >= pageSize ||
|
||||
transactions.length >= pageSize;
|
||||
|
||||
this.loading = false;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,12 @@
|
|||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="invoices || transactions">
|
||||
<app-billing-history [invoices]="invoices" [transactions]="transactions"></app-billing-history>
|
||||
<ng-container *ngIf="openInvoices || paidInvoices || transactions">
|
||||
<app-billing-history
|
||||
[openInvoices]="openInvoices"
|
||||
[paidInvoices]="paidInvoices"
|
||||
[transactions]="transactions"
|
||||
></app-billing-history>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
|
|
|
@ -14,7 +14,8 @@ import {
|
|||
export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy {
|
||||
loading = false;
|
||||
firstLoaded = false;
|
||||
invoices: BillingInvoiceResponse[] = [];
|
||||
openInvoices: BillingInvoiceResponse[] = [];
|
||||
paidInvoices: BillingInvoiceResponse[] = [];
|
||||
transactions: BillingTransactionResponse[] = [];
|
||||
organizationId: string;
|
||||
hasAdditionalHistory: boolean = false;
|
||||
|
@ -51,9 +52,16 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy {
|
|||
|
||||
this.loading = true;
|
||||
|
||||
const invoicesPromise = this.organizationBillingApiService.getBillingInvoices(
|
||||
const openInvoicesPromise = this.organizationBillingApiService.getBillingInvoices(
|
||||
this.organizationId,
|
||||
this.invoices.length > 0 ? this.invoices[this.invoices.length - 1].id : null,
|
||||
"open",
|
||||
this.openInvoices.length > 0 ? this.openInvoices[this.openInvoices.length - 1].id : null,
|
||||
);
|
||||
|
||||
const paidInvoicesPromise = this.organizationBillingApiService.getBillingInvoices(
|
||||
this.organizationId,
|
||||
"paid",
|
||||
this.paidInvoices.length > 0 ? this.paidInvoices[this.paidInvoices.length - 1].id : null,
|
||||
);
|
||||
|
||||
const transactionsPromise = this.organizationBillingApiService.getBillingTransactions(
|
||||
|
@ -63,13 +71,21 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy {
|
|||
: null,
|
||||
);
|
||||
|
||||
const invoices = await invoicesPromise;
|
||||
const openInvoices = await openInvoicesPromise;
|
||||
const paidInvoices = await paidInvoicesPromise;
|
||||
const transactions = await transactionsPromise;
|
||||
|
||||
const pageSize = 5;
|
||||
|
||||
this.invoices = [...this.invoices, ...invoices];
|
||||
this.openInvoices = [...this.openInvoices, ...openInvoices];
|
||||
this.paidInvoices = [...this.paidInvoices, ...paidInvoices];
|
||||
this.transactions = [...this.transactions, ...transactions];
|
||||
this.hasAdditionalHistory = !(invoices.length < pageSize && transactions.length < pageSize);
|
||||
|
||||
this.hasAdditionalHistory =
|
||||
openInvoices.length <= pageSize ||
|
||||
paidInvoices.length <= pageSize ||
|
||||
transactions.length <= pageSize;
|
||||
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<bit-section>
|
||||
<h3 bitTypography="h3">{{ "invoices" | i18n }}</h3>
|
||||
<p bitTypography="body1" *ngIf="!invoices || !invoices.length">{{ "noInvoices" | i18n }}</p>
|
||||
<h3 bitTypography="h3">{{ "unpaid" | i18n }} {{ "invoices" | i18n }}</h3>
|
||||
<p bitTypography="body1" *ngIf="!openInvoices || !openInvoices.length">
|
||||
{{ "noUnpaidInvoices" | i18n }}
|
||||
</p>
|
||||
<bit-table>
|
||||
<ng-template body>
|
||||
<tr bitRow *ngFor="let i of invoices">
|
||||
<tr bitRow *ngFor="let i of openInvoices">
|
||||
<td bitCell>{{ i.date | date: "mediumDate" }}</td>
|
||||
<td bitCell>
|
||||
<a
|
||||
|
@ -26,7 +28,51 @@
|
|||
>
|
||||
</td>
|
||||
<td bitCell>{{ i.amount | currency: "$" }}</td>
|
||||
<td bitCell class="tw-w-28">
|
||||
<span *ngIf="i.paid">
|
||||
<i class="bwi bwi-check tw-text-success" aria-hidden="true"></i>
|
||||
{{ "paid" | i18n }}
|
||||
</span>
|
||||
<span *ngIf="!i.paid">
|
||||
<i class="bwi bwi-exclamation-circle tw-text-muted" aria-hidden="true"></i>
|
||||
{{ "unpaid" | i18n }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
</bit-section>
|
||||
<bit-section>
|
||||
<h3 bitTypography="h3">{{ "paid" | i18n }} {{ "invoices" | i18n }}</h3>
|
||||
<p bitTypography="body1" *ngIf="!paidInvoices || !paidInvoices.length">
|
||||
{{ "noPaidInvoices" | i18n }}
|
||||
</p>
|
||||
<bit-table>
|
||||
<ng-template body>
|
||||
<tr bitRow *ngFor="let i of paidInvoices">
|
||||
<td bitCell>{{ i.date | date: "mediumDate" }}</td>
|
||||
<td bitCell>
|
||||
<a
|
||||
href="{{ i.pdfUrl }}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
class="tw-mr-2"
|
||||
appA11yTitle="{{ 'downloadInvoice' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-file-pdf" aria-hidden="true"></i
|
||||
></a>
|
||||
<a
|
||||
bitLink
|
||||
href="{{ i.url }}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title="{{ 'viewInvoice' | i18n }}"
|
||||
>
|
||||
{{ "invoiceNumber" | i18n: i.number }}</a
|
||||
>
|
||||
</td>
|
||||
<td bitCell>{{ i.amount | currency: "$" }}</td>
|
||||
<td bitCell class="tw-w-28">
|
||||
<span *ngIf="i.paid">
|
||||
<i class="bwi bwi-check tw-text-success" aria-hidden="true"></i>
|
||||
{{ "paid" | i18n }}
|
||||
|
|
|
@ -12,7 +12,10 @@ import {
|
|||
})
|
||||
export class BillingHistoryComponent {
|
||||
@Input()
|
||||
invoices: BillingInvoiceResponse[];
|
||||
openInvoices: BillingInvoiceResponse[];
|
||||
|
||||
@Input()
|
||||
paidInvoices: BillingInvoiceResponse[];
|
||||
|
||||
@Input()
|
||||
transactions: BillingTransactionResponse[];
|
||||
|
|
|
@ -2616,8 +2616,11 @@
|
|||
"invoices": {
|
||||
"message": "Invoices"
|
||||
},
|
||||
"noInvoices": {
|
||||
"message": "No invoices."
|
||||
"noUnpaidInvoices": {
|
||||
"message": "No unpaid invoices."
|
||||
},
|
||||
"noPaidInvoices": {
|
||||
"message": "No paid invoices."
|
||||
},
|
||||
"paid": {
|
||||
"message": "Paid",
|
||||
|
|
|
@ -4,9 +4,6 @@ import {
|
|||
} from "@bitwarden/common/billing/models/response/billing.response";
|
||||
|
||||
export class AccountBillingApiServiceAbstraction {
|
||||
getBillingInvoices: (id: string, startAfter?: string) => Promise<BillingInvoiceResponse[]>;
|
||||
getBillingTransactions: (
|
||||
id: string,
|
||||
startAfter?: string,
|
||||
) => Promise<BillingTransactionResponse[]>;
|
||||
getBillingInvoices: (status?: string, startAfter?: string) => Promise<BillingInvoiceResponse[]>;
|
||||
getBillingTransactions: (startAfter?: string) => Promise<BillingTransactionResponse[]>;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,12 @@ import {
|
|||
} from "@bitwarden/common/billing/models/response/billing.response";
|
||||
|
||||
export class OrganizationBillingApiServiceAbstraction {
|
||||
getBillingInvoices: (id: string, startAfter?: string) => Promise<BillingInvoiceResponse[]>;
|
||||
getBillingInvoices: (
|
||||
id: string,
|
||||
status?: string,
|
||||
startAfter?: string,
|
||||
) => Promise<BillingInvoiceResponse[]>;
|
||||
|
||||
getBillingTransactions: (
|
||||
id: string,
|
||||
startAfter?: string,
|
||||
|
|
|
@ -8,11 +8,25 @@ import {
|
|||
export class AccountBillingApiService implements AccountBillingApiServiceAbstraction {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
async getBillingInvoices(startAfter?: string): Promise<BillingInvoiceResponse[]> {
|
||||
const queryParams = startAfter ? `?startAfter=${startAfter}` : "";
|
||||
async getBillingInvoices(
|
||||
status?: string,
|
||||
startAfter?: string,
|
||||
): Promise<BillingInvoiceResponse[]> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (status) {
|
||||
params.append("status", status);
|
||||
}
|
||||
|
||||
if (startAfter) {
|
||||
params.append("startAfter", startAfter);
|
||||
}
|
||||
|
||||
const queryString = `?${params.toString()}`;
|
||||
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
`/accounts/billing/invoices${queryParams}`,
|
||||
`/accounts/billing/invoices${queryString}`,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
|
|
|
@ -8,11 +8,26 @@ import {
|
|||
export class OrganizationBillingApiService implements OrganizationBillingApiServiceAbstraction {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
async getBillingInvoices(id: string, startAfter?: string): Promise<BillingInvoiceResponse[]> {
|
||||
const queryParams = startAfter ? `?startAfter=${startAfter}` : "";
|
||||
async getBillingInvoices(
|
||||
id: string,
|
||||
status?: string,
|
||||
startAfter?: string,
|
||||
): Promise<BillingInvoiceResponse[]> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (status) {
|
||||
params.append("status", status);
|
||||
}
|
||||
|
||||
if (startAfter) {
|
||||
params.append("startAfter", startAfter);
|
||||
}
|
||||
|
||||
const queryString = `?${params.toString()}`;
|
||||
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
`/organizations/${id}/billing/invoices${queryParams}`,
|
||||
`/organizations/${id}/billing/invoices${queryString}`,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
|
|
Loading…
Reference in New Issue