@sneat/extension-debtus-ui 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/sneat-extension-debtus-ui-debtus-contact-details-page.component-ygSD0Q-C.mjs +101 -0
- package/fesm2022/sneat-extension-debtus-ui-debtus-contact-details-page.component-ygSD0Q-C.mjs.map +1 -0
- package/fesm2022/{sneat-extension-debtus-ui-debtus-contacts-page.component-DYtFeNsM.mjs → sneat-extension-debtus-ui-debtus-contacts-page.component-B2SicqpG.mjs} +5 -5
- package/fesm2022/sneat-extension-debtus-ui-debtus-contacts-page.component-B2SicqpG.mjs.map +1 -0
- package/fesm2022/sneat-extension-debtus-ui-debtus-home-page.component-Dd5-oQdz.mjs +95 -0
- package/fesm2022/sneat-extension-debtus-ui-debtus-home-page.component-Dd5-oQdz.mjs.map +1 -0
- package/fesm2022/{sneat-extension-debtus-ui-new-transfer-page.component-CT-6X_0e.mjs → sneat-extension-debtus-ui-new-transfer-page.component-YaGgYqKB.mjs} +16 -12
- package/fesm2022/sneat-extension-debtus-ui-new-transfer-page.component-YaGgYqKB.mjs.map +1 -0
- package/fesm2022/sneat-extension-debtus-ui-settle-up-page.component-48l8YFn4.mjs +162 -0
- package/fesm2022/sneat-extension-debtus-ui-settle-up-page.component-48l8YFn4.mjs.map +1 -0
- package/fesm2022/sneat-extension-debtus-ui-source-obligations-page.component-6E9_pHCq.mjs +423 -0
- package/fesm2022/sneat-extension-debtus-ui-source-obligations-page.component-6E9_pHCq.mjs.map +1 -0
- package/fesm2022/sneat-extension-debtus-ui-transfer-details-page.component-B6iQS1YJ.mjs +105 -0
- package/fesm2022/sneat-extension-debtus-ui-transfer-details-page.component-B6iQS1YJ.mjs.map +1 -0
- package/fesm2022/sneat-extension-debtus-ui.mjs +14 -36
- package/fesm2022/sneat-extension-debtus-ui.mjs.map +1 -1
- package/package.json +5 -3
- package/types/sneat-extension-debtus-ui.d.ts +1 -6
- package/fesm2022/sneat-extension-debtus-ui-debtus-contact-details-page.component-DIAVE0D6.mjs +0 -106
- package/fesm2022/sneat-extension-debtus-ui-debtus-contact-details-page.component-DIAVE0D6.mjs.map +0 -1
- package/fesm2022/sneat-extension-debtus-ui-debtus-contacts-page.component-DYtFeNsM.mjs.map +0 -1
- package/fesm2022/sneat-extension-debtus-ui-debtus-home-page.component-BCNMpirX.mjs +0 -97
- package/fesm2022/sneat-extension-debtus-ui-debtus-home-page.component-BCNMpirX.mjs.map +0 -1
- package/fesm2022/sneat-extension-debtus-ui-new-transfer-page.component-CT-6X_0e.mjs.map +0 -1
- package/fesm2022/sneat-extension-debtus-ui-settle-up-page.component-CUoNSuAE.mjs +0 -163
- package/fesm2022/sneat-extension-debtus-ui-settle-up-page.component-CUoNSuAE.mjs.map +0 -1
- package/fesm2022/sneat-extension-debtus-ui-transfer-details-page.component-uo5lP557.mjs +0 -97
- package/fesm2022/sneat-extension-debtus-ui-transfer-details-page.component-uo5lP557.mjs.map +0 -1
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, signal, Component } from '@angular/core';
|
|
3
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
|
-
import { IonHeader, IonToolbar, IonButtons, IonBackButton, IonContent, IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonList, IonItem, IonLabel, IonNote, IonBadge, IonButton, IonIcon, IonSpinner } from '@ionic/angular';
|
|
5
|
-
import { DemoDataBannerComponent } from './sneat-extension-debtus-ui.mjs';
|
|
6
|
-
import { DEBTUS_SERVICE, formatAmount, formatSignedBalance, isZeroBalance, summarizeBalances } from '@sneat/extension-debtus-contract';
|
|
7
|
-
import { SpacePageBaseComponent, SpacePageTitleComponent, SpaceComponentBaseParams } from '@sneat/space-components';
|
|
8
|
-
import { SpaceServiceModule } from '@sneat/space-services';
|
|
9
|
-
import { ClassName } from '@sneat/ui';
|
|
10
|
-
import { switchMap } from 'rxjs';
|
|
11
|
-
|
|
12
|
-
// Home / balances screen — the debtus entry point. Mirrors the bot's
|
|
13
|
-
// `debts_balance` view: a net summary of who owes the user and whom the user
|
|
14
|
-
// owes, plus quick actions into contacts and creating a transfer.
|
|
15
|
-
class DebtusHomePageComponent extends SpacePageBaseComponent {
|
|
16
|
-
debtusService = inject(DEBTUS_SERVICE);
|
|
17
|
-
$loading = signal(true, /* @ts-ignore */
|
|
18
|
-
...(ngDevMode ? [{ debugName: "$loading" }] : /* istanbul ignore next */ []));
|
|
19
|
-
$error = signal(undefined, /* @ts-ignore */
|
|
20
|
-
...(ngDevMode ? [{ debugName: "$error" }] : /* istanbul ignore next */ []));
|
|
21
|
-
$contacts = signal([], /* @ts-ignore */
|
|
22
|
-
...(ngDevMode ? [{ debugName: "$contacts" }] : /* istanbul ignore next */ []));
|
|
23
|
-
$summary = signal({
|
|
24
|
-
theyOweYou: [],
|
|
25
|
-
youOwe: [],
|
|
26
|
-
}, /* @ts-ignore */
|
|
27
|
-
...(ngDevMode ? [{ debugName: "$summary" }] : /* istanbul ignore next */ []));
|
|
28
|
-
formatAmount = formatAmount;
|
|
29
|
-
formatSignedBalance = formatSignedBalance;
|
|
30
|
-
constructor() {
|
|
31
|
-
super();
|
|
32
|
-
this.spaceIDChanged$
|
|
33
|
-
.pipe(switchMap((spaceID) => {
|
|
34
|
-
this.$loading.set(true);
|
|
35
|
-
this.$error.set(undefined);
|
|
36
|
-
return this.debtusService.getContactBalances(spaceID ?? '');
|
|
37
|
-
}), takeUntilDestroyed())
|
|
38
|
-
.subscribe({
|
|
39
|
-
next: (contacts) => {
|
|
40
|
-
const active = contacts.filter((c) => !isZeroBalance(c.balance));
|
|
41
|
-
this.$contacts.set(active);
|
|
42
|
-
this.$summary.set(summarizeBalances(contacts));
|
|
43
|
-
this.$loading.set(false);
|
|
44
|
-
},
|
|
45
|
-
error: (err) => {
|
|
46
|
-
this.$error.set('Failed to load balances');
|
|
47
|
-
this.$loading.set(false);
|
|
48
|
-
this.errorLogger.logError(err, 'Failed to load contact balances');
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
goContacts() {
|
|
53
|
-
this.spaceNav.navigateForwardToSpacePage(this.space, 'debtus-contacts');
|
|
54
|
-
}
|
|
55
|
-
goNewTransfer() {
|
|
56
|
-
this.spaceNav.navigateForwardToSpacePage(this.space, 'new-transfer');
|
|
57
|
-
}
|
|
58
|
-
goContact(contactID) {
|
|
59
|
-
this.spaceNav.navigateForwardToSpacePage(this.space, `debtus-contact/${contactID}`);
|
|
60
|
-
}
|
|
61
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DebtusHomePageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
62
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: DebtusHomePageComponent, isStandalone: true, selector: "sneat-debtus-home-page", providers: [
|
|
63
|
-
{ provide: ClassName, useValue: 'DebtusHomePageComponent' },
|
|
64
|
-
SpaceComponentBaseParams,
|
|
65
|
-
], usesInheritance: true, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"light\" class=\"with-back-button with-end-buttons\">\n <ion-buttons slot=\"start\">\n <!-- This is the top page of a space, so \"back\" leaves the space and\n returns to the app home (the spaces list). -->\n <ion-back-button defaultHref=\"/\" />\n </ion-buttons>\n <sneat-space-page-title\n [space]=\"space\"\n generalTitle=\"Debts\"\n icon=\"\uD83D\uDCB8\"\n [titlesBySpaceType]=\"{\n personal: 'Personal debts',\n family: 'Family debts',\n }\"\n />\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <!-- Reads on this page are still demo fixtures (see DebtusService). -->\n <sneat-debtus-demo-data-banner />\n\n <ion-card>\n <ion-card-header>\n <ion-card-title>Balance</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n @if ($loading()) {\n <ion-spinner name=\"dots\" />\n } @else if ($error()) {\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n } @else {\n <ion-list lines=\"none\">\n <ion-item>\n <ion-label>They owe you</ion-label>\n @if ($summary().theyOweYou.length) {\n @for (a of $summary().theyOweYou; track a.currency) {\n <ion-badge slot=\"end\" color=\"success\">{{\n formatAmount(a)\n }}</ion-badge>\n }\n } @else {\n <ion-note slot=\"end\">\u2014</ion-note>\n }\n </ion-item>\n <ion-item>\n <ion-label>You owe</ion-label>\n @if ($summary().youOwe.length) {\n @for (a of $summary().youOwe; track a.currency) {\n <ion-badge slot=\"end\" color=\"warning\">{{\n formatAmount(a)\n }}</ion-badge>\n }\n } @else {\n <ion-note slot=\"end\">\u2014</ion-note>\n }\n </ion-item>\n </ion-list>\n }\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-card-content>\n <ion-button expand=\"block\" (click)=\"goNewTransfer()\">\n <ion-icon name=\"add\" slot=\"start\" />\n New transfer\n </ion-button>\n <ion-button expand=\"block\" fill=\"outline\" (click)=\"goContacts()\">\n <ion-icon name=\"people-outline\" slot=\"start\" />\n Contacts\n </ion-button>\n </ion-card-content>\n </ion-card>\n\n @if (!$loading() && !$error()) {\n <ion-card>\n <ion-card-header>\n <ion-card-title>By contact</ion-card-title>\n </ion-card-header>\n @if ($contacts().length) {\n <ion-list>\n @for (c of $contacts(); track c.contactID) {\n <ion-item button (click)=\"goContact(c.contactID)\">\n <ion-label>\n <h2>{{ c.title }}</h2>\n <p>{{ formatSignedBalance(c.balance) }}</p>\n @if (c.counterpartySpaceID) {\n <ion-note color=\"medium\">\n cross-space \u00B7 {{ c.counterpartySpaceID }}\n </ion-note>\n }\n </ion-label>\n </ion-item>\n }\n </ion-list>\n } @else {\n <ion-card-content>\n <ion-note>Everyone is settled up. \uD83C\uDF89</ion-note>\n </ion-card-content>\n }\n </ion-card>\n }\n</ion-content>\n", dependencies: [{ kind: "component", type: DemoDataBannerComponent, selector: "sneat-debtus-demo-data-banner" }, { kind: "component", type: SpacePageTitleComponent, selector: "sneat-space-page-title", inputs: ["icon", "generalTitle", "space", "titlesBySpaceType"] }, { kind: "ngmodule", type: SpaceServiceModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonBackButton, selector: "ion-back-button" }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"], outputs: ["ionScrollStart", "ionScroll", "ionScrollEnd"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonBadge, selector: "ion-badge", inputs: ["color", "mode"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"], outputs: ["ionFocus", "ionBlur"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] });
|
|
66
|
-
}
|
|
67
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DebtusHomePageComponent, decorators: [{
|
|
68
|
-
type: Component,
|
|
69
|
-
args: [{ selector: 'sneat-debtus-home-page', imports: [
|
|
70
|
-
DemoDataBannerComponent,
|
|
71
|
-
SpacePageTitleComponent,
|
|
72
|
-
SpaceServiceModule,
|
|
73
|
-
IonHeader,
|
|
74
|
-
IonToolbar,
|
|
75
|
-
IonButtons,
|
|
76
|
-
IonBackButton,
|
|
77
|
-
IonContent,
|
|
78
|
-
IonCard,
|
|
79
|
-
IonCardHeader,
|
|
80
|
-
IonCardTitle,
|
|
81
|
-
IonCardContent,
|
|
82
|
-
IonList,
|
|
83
|
-
IonItem,
|
|
84
|
-
IonLabel,
|
|
85
|
-
IonNote,
|
|
86
|
-
IonBadge,
|
|
87
|
-
IonButton,
|
|
88
|
-
IonIcon,
|
|
89
|
-
IonSpinner,
|
|
90
|
-
], providers: [
|
|
91
|
-
{ provide: ClassName, useValue: 'DebtusHomePageComponent' },
|
|
92
|
-
SpaceComponentBaseParams,
|
|
93
|
-
], template: "<ion-header>\n <ion-toolbar color=\"light\" class=\"with-back-button with-end-buttons\">\n <ion-buttons slot=\"start\">\n <!-- This is the top page of a space, so \"back\" leaves the space and\n returns to the app home (the spaces list). -->\n <ion-back-button defaultHref=\"/\" />\n </ion-buttons>\n <sneat-space-page-title\n [space]=\"space\"\n generalTitle=\"Debts\"\n icon=\"\uD83D\uDCB8\"\n [titlesBySpaceType]=\"{\n personal: 'Personal debts',\n family: 'Family debts',\n }\"\n />\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <!-- Reads on this page are still demo fixtures (see DebtusService). -->\n <sneat-debtus-demo-data-banner />\n\n <ion-card>\n <ion-card-header>\n <ion-card-title>Balance</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n @if ($loading()) {\n <ion-spinner name=\"dots\" />\n } @else if ($error()) {\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n } @else {\n <ion-list lines=\"none\">\n <ion-item>\n <ion-label>They owe you</ion-label>\n @if ($summary().theyOweYou.length) {\n @for (a of $summary().theyOweYou; track a.currency) {\n <ion-badge slot=\"end\" color=\"success\">{{\n formatAmount(a)\n }}</ion-badge>\n }\n } @else {\n <ion-note slot=\"end\">\u2014</ion-note>\n }\n </ion-item>\n <ion-item>\n <ion-label>You owe</ion-label>\n @if ($summary().youOwe.length) {\n @for (a of $summary().youOwe; track a.currency) {\n <ion-badge slot=\"end\" color=\"warning\">{{\n formatAmount(a)\n }}</ion-badge>\n }\n } @else {\n <ion-note slot=\"end\">\u2014</ion-note>\n }\n </ion-item>\n </ion-list>\n }\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-card-content>\n <ion-button expand=\"block\" (click)=\"goNewTransfer()\">\n <ion-icon name=\"add\" slot=\"start\" />\n New transfer\n </ion-button>\n <ion-button expand=\"block\" fill=\"outline\" (click)=\"goContacts()\">\n <ion-icon name=\"people-outline\" slot=\"start\" />\n Contacts\n </ion-button>\n </ion-card-content>\n </ion-card>\n\n @if (!$loading() && !$error()) {\n <ion-card>\n <ion-card-header>\n <ion-card-title>By contact</ion-card-title>\n </ion-card-header>\n @if ($contacts().length) {\n <ion-list>\n @for (c of $contacts(); track c.contactID) {\n <ion-item button (click)=\"goContact(c.contactID)\">\n <ion-label>\n <h2>{{ c.title }}</h2>\n <p>{{ formatSignedBalance(c.balance) }}</p>\n @if (c.counterpartySpaceID) {\n <ion-note color=\"medium\">\n cross-space \u00B7 {{ c.counterpartySpaceID }}\n </ion-note>\n }\n </ion-label>\n </ion-item>\n }\n </ion-list>\n } @else {\n <ion-card-content>\n <ion-note>Everyone is settled up. \uD83C\uDF89</ion-note>\n </ion-card-content>\n }\n </ion-card>\n }\n</ion-content>\n" }]
|
|
94
|
-
}], ctorParameters: () => [] });
|
|
95
|
-
|
|
96
|
-
export { DebtusHomePageComponent };
|
|
97
|
-
//# sourceMappingURL=sneat-extension-debtus-ui-debtus-home-page.component-BCNMpirX.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sneat-extension-debtus-ui-debtus-home-page.component-BCNMpirX.mjs","sources":["../../../../../../libs/extensions/debtus/ui/src/lib/pages/debtus-home/debtus-home-page.component.ts","../../../../../../libs/extensions/debtus/ui/src/lib/pages/debtus-home/debtus-home-page.component.html"],"sourcesContent":["import { Component, inject, signal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport {\n IonBackButton,\n IonBadge,\n IonButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonCardHeader,\n IonCardTitle,\n IonContent,\n IonHeader,\n IonIcon,\n IonItem,\n IonLabel,\n IonList,\n IonNote,\n IonSpinner,\n IonToolbar,\n} from '@ionic/angular';\nimport { DemoDataBannerComponent } from '../../components/demo-data-banner/demo-data-banner.component';\nimport {\n DEBTUS_SERVICE,\n IBalanceSummary,\n IContactBalance,\n formatAmount,\n formatSignedBalance,\n isZeroBalance,\n summarizeBalances,\n} from '@sneat/extension-debtus-contract';\nimport {\n SpaceComponentBaseParams,\n SpacePageBaseComponent,\n SpacePageTitleComponent,\n} from '@sneat/space-components';\nimport { SpaceServiceModule } from '@sneat/space-services';\nimport { ClassName } from '@sneat/ui';\nimport { switchMap } from 'rxjs';\n\n// Home / balances screen — the debtus entry point. Mirrors the bot's\n// `debts_balance` view: a net summary of who owes the user and whom the user\n// owes, plus quick actions into contacts and creating a transfer.\n@Component({\n selector: 'sneat-debtus-home-page',\n templateUrl: './debtus-home-page.component.html',\n imports: [\n DemoDataBannerComponent,\n SpacePageTitleComponent,\n SpaceServiceModule,\n IonHeader,\n IonToolbar,\n IonButtons,\n IonBackButton,\n IonContent,\n IonCard,\n IonCardHeader,\n IonCardTitle,\n IonCardContent,\n IonList,\n IonItem,\n IonLabel,\n IonNote,\n IonBadge,\n IonButton,\n IonIcon,\n IonSpinner,\n ],\n providers: [\n { provide: ClassName, useValue: 'DebtusHomePageComponent' },\n SpaceComponentBaseParams,\n ],\n})\nexport class DebtusHomePageComponent extends SpacePageBaseComponent {\n private readonly debtusService = inject(DEBTUS_SERVICE);\n\n protected readonly $loading = signal(true);\n protected readonly $error = signal<string | undefined>(undefined);\n protected readonly $contacts = signal<IContactBalance[]>([]);\n protected readonly $summary = signal<IBalanceSummary>({\n theyOweYou: [],\n youOwe: [],\n });\n\n protected readonly formatAmount = formatAmount;\n protected readonly formatSignedBalance = formatSignedBalance;\n\n constructor() {\n super();\n this.spaceIDChanged$\n .pipe(\n switchMap((spaceID) => {\n this.$loading.set(true);\n this.$error.set(undefined);\n return this.debtusService.getContactBalances(spaceID ?? '');\n }),\n takeUntilDestroyed(),\n )\n .subscribe({\n next: (contacts) => {\n const active = contacts.filter((c) => !isZeroBalance(c.balance));\n this.$contacts.set(active);\n this.$summary.set(summarizeBalances(contacts));\n this.$loading.set(false);\n },\n error: (err) => {\n this.$error.set('Failed to load balances');\n this.$loading.set(false);\n this.errorLogger.logError(err, 'Failed to load contact balances');\n },\n });\n }\n\n protected goContacts(): void {\n this.spaceNav.navigateForwardToSpacePage(this.space, 'debtus-contacts');\n }\n\n protected goNewTransfer(): void {\n this.spaceNav.navigateForwardToSpacePage(this.space, 'new-transfer');\n }\n\n protected goContact(contactID: string): void {\n this.spaceNav.navigateForwardToSpacePage(\n this.space,\n `debtus-contact/${contactID}`,\n );\n }\n}\n","<ion-header>\n <ion-toolbar color=\"light\" class=\"with-back-button with-end-buttons\">\n <ion-buttons slot=\"start\">\n <!-- This is the top page of a space, so \"back\" leaves the space and\n returns to the app home (the spaces list). -->\n <ion-back-button defaultHref=\"/\" />\n </ion-buttons>\n <sneat-space-page-title\n [space]=\"space\"\n generalTitle=\"Debts\"\n icon=\"💸\"\n [titlesBySpaceType]=\"{\n personal: 'Personal debts',\n family: 'Family debts',\n }\"\n />\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <!-- Reads on this page are still demo fixtures (see DebtusService). -->\n <sneat-debtus-demo-data-banner />\n\n <ion-card>\n <ion-card-header>\n <ion-card-title>Balance</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n @if ($loading()) {\n <ion-spinner name=\"dots\" />\n } @else if ($error()) {\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n } @else {\n <ion-list lines=\"none\">\n <ion-item>\n <ion-label>They owe you</ion-label>\n @if ($summary().theyOweYou.length) {\n @for (a of $summary().theyOweYou; track a.currency) {\n <ion-badge slot=\"end\" color=\"success\">{{\n formatAmount(a)\n }}</ion-badge>\n }\n } @else {\n <ion-note slot=\"end\">—</ion-note>\n }\n </ion-item>\n <ion-item>\n <ion-label>You owe</ion-label>\n @if ($summary().youOwe.length) {\n @for (a of $summary().youOwe; track a.currency) {\n <ion-badge slot=\"end\" color=\"warning\">{{\n formatAmount(a)\n }}</ion-badge>\n }\n } @else {\n <ion-note slot=\"end\">—</ion-note>\n }\n </ion-item>\n </ion-list>\n }\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-card-content>\n <ion-button expand=\"block\" (click)=\"goNewTransfer()\">\n <ion-icon name=\"add\" slot=\"start\" />\n New transfer\n </ion-button>\n <ion-button expand=\"block\" fill=\"outline\" (click)=\"goContacts()\">\n <ion-icon name=\"people-outline\" slot=\"start\" />\n Contacts\n </ion-button>\n </ion-card-content>\n </ion-card>\n\n @if (!$loading() && !$error()) {\n <ion-card>\n <ion-card-header>\n <ion-card-title>By contact</ion-card-title>\n </ion-card-header>\n @if ($contacts().length) {\n <ion-list>\n @for (c of $contacts(); track c.contactID) {\n <ion-item button (click)=\"goContact(c.contactID)\">\n <ion-label>\n <h2>{{ c.title }}</h2>\n <p>{{ formatSignedBalance(c.balance) }}</p>\n @if (c.counterpartySpaceID) {\n <ion-note color=\"medium\">\n cross-space · {{ c.counterpartySpaceID }}\n </ion-note>\n }\n </ion-label>\n </ion-item>\n }\n </ion-list>\n } @else {\n <ion-card-content>\n <ion-note>Everyone is settled up. 🎉</ion-note>\n </ion-card-content>\n }\n </ion-card>\n }\n</ion-content>\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sneat-extension-debtus-ui-new-transfer-page.component-CT-6X_0e.mjs","sources":["../../../../../../libs/extensions/debtus/ui/src/lib/pages/new-transfer/new-transfer-page.component.ts","../../../../../../libs/extensions/debtus/ui/src/lib/pages/new-transfer/new-transfer-page.component.html"],"sourcesContent":["import { Component, inject, signal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport {\n FormControl,\n FormGroup,\n ReactiveFormsModule,\n Validators,\n} from '@angular/forms';\nimport {\n IonBackButton,\n IonButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonContent,\n IonHeader,\n IonInput,\n IonItem,\n IonLabel,\n IonNote,\n IonSegment,\n IonSegmentButton,\n IonSelect,\n IonSelectOption,\n IonSpinner,\n IonTextarea,\n IonTitle,\n IonToolbar,\n ToastController,\n} from '@ionic/angular';\nimport { IContactContext } from '@sneat/extension-contactus-contract';\nimport { ContactInputComponent } from '@sneat/extension-contactus-ui';\nimport {\n CurrencyCode,\n DEBTUS_SERVICE,\n DebtDirection,\n ICreateTransferRequest,\n} from '@sneat/extension-debtus-contract';\nimport {\n SpaceComponentBaseParams,\n SpacePageBaseComponent,\n} from '@sneat/space-components';\nimport { ClassName } from '@sneat/ui';\nimport { first } from 'rxjs';\n\n// Create transfer — the primary write flow. Mirrors the bot's lend/borrow\n// wizard: direction, counterparty (a contactus space contact), amount +\n// currency, optional note. Reuses the contactus `sneat-contact-input` picker\n// so the counterparty is a real space contact, not a debtus-private person.\n@Component({\n selector: 'sneat-debtus-new-transfer-page',\n templateUrl: './new-transfer-page.component.html',\n imports: [\n ReactiveFormsModule,\n IonHeader,\n IonToolbar,\n IonButtons,\n IonBackButton,\n IonTitle,\n IonContent,\n IonCard,\n IonCardContent,\n IonItem,\n IonLabel,\n IonInput,\n IonTextarea,\n IonSelect,\n IonSelectOption,\n IonSegment,\n IonSegmentButton,\n IonButton,\n IonNote,\n IonSpinner,\n ContactInputComponent,\n ],\n providers: [\n { provide: ClassName, useValue: 'NewTransferPageComponent' },\n SpaceComponentBaseParams,\n ],\n})\nexport class NewTransferPageComponent extends SpacePageBaseComponent {\n private readonly debtusService = inject(DEBTUS_SERVICE);\n private readonly toastController = inject(ToastController);\n\n protected readonly $submitting = signal(false);\n protected readonly $pickedContact = signal<IContactContext | undefined>(\n undefined,\n );\n /** Set when arriving from a contact detail page (?contactID=…). */\n protected prefilledContactID?: string;\n\n protected readonly direction = new FormControl<DebtDirection>('lend', {\n nonNullable: true,\n });\n protected readonly currency = new FormControl<CurrencyCode>('EUR', {\n nonNullable: true,\n });\n protected readonly amount = new FormControl<number | null>(null, [\n Validators.required,\n Validators.min(0.01),\n ]);\n protected readonly counterpartyName = new FormControl<string>('', {\n nonNullable: true,\n });\n protected readonly note = new FormControl<string>('', { nonNullable: true });\n\n protected readonly form = new FormGroup({\n direction: this.direction,\n currency: this.currency,\n amount: this.amount,\n counterpartyName: this.counterpartyName,\n note: this.note,\n });\n\n protected readonly currencies: CurrencyCode[] = ['EUR', 'USD'];\n\n constructor() {\n super();\n this.$defaultBackUrlSpacePath.set('debts');\n this.route.queryParamMap.pipe(first(), takeUntilDestroyed()).subscribe({\n next: (params) => {\n const dir = params.get('direction');\n if (dir === 'lend' || dir === 'borrow') {\n this.direction.setValue(dir);\n }\n const contactID = params.get('contactID');\n if (contactID) {\n this.prefilledContactID = contactID;\n // Prefill the name field from the known balance so the user sees who\n // they're recording against even before the contactus picker loads.\n this.spaceIDChanged$.pipe(first()).subscribe((spaceID) => {\n this.debtusService\n .getContactBalance(spaceID ?? '', contactID)\n .pipe(first())\n .subscribe((c) => this.counterpartyName.setValue(c.title));\n });\n }\n },\n });\n }\n\n protected onContactChanged(contact: IContactContext | undefined): void {\n this.$pickedContact.set(contact);\n if (contact) {\n // A picked contact supersedes a typed name / prefilled id.\n this.prefilledContactID = undefined;\n this.counterpartyName.setValue(contact.brief?.title ?? '');\n }\n }\n\n private resolveCounterparty(): { contactID: string; title: string } | null {\n const picked = this.$pickedContact();\n if (picked) {\n return { contactID: picked.id, title: picked.brief?.title ?? picked.id };\n }\n if (this.prefilledContactID) {\n return {\n contactID: this.prefilledContactID,\n title: this.counterpartyName.value,\n };\n }\n // Fable refactoring: the \"new counterparty by name\" path is disabled —\n // the assumption that \"the backend create flow resolves/creates it\" was\n // wrong: POST create-transfer validation requires toContactID/fromContactID\n // (facade4debtus CreateTransferRequest.Validate), so an empty contactID\n // always 400s. Until a create-contact-then-transfer flow is wired, users\n // must pick an existing space contact (the picker above supports adding\n // one). Original branch kept per the no-delete policy:\n // const name = this.counterpartyName.value.trim();\n // if (name) {\n // // New counterparty by name (mirrors the bot's \"new counterparty\"\n // // step). No contactID yet — the backend create flow\n // // resolves/creates it.\n // return { contactID: '', title: name };\n // }\n return null;\n }\n\n protected submit(): void {\n this.form.markAllAsTouched();\n const amount = this.amount.value;\n if (!amount || amount <= 0) {\n return;\n }\n const counterparty = this.resolveCounterparty();\n if (!counterparty) {\n this.showToast(\n 'Pick a counterparty contact. To record against someone new, add them as a space contact first.',\n 'danger',\n );\n return;\n }\n const spaceID = this.$spaceID();\n if (!spaceID) {\n return;\n }\n const request: ICreateTransferRequest = {\n spaceID,\n direction: this.direction.value,\n amount: { currency: this.currency.value, value: amount },\n contactID: counterparty.contactID,\n contactTitle: counterparty.title,\n note: this.note.value.trim() || undefined,\n counterpartySpaceID: this.$pickedContact()?.space?.id,\n };\n\n this.$submitting.set(true);\n this.debtusService.createTransfer(request).subscribe({\n next: (resp) => {\n this.$submitting.set(false);\n // Navigation default: go to the created transfer's detail, replaceUrl\n // so Back doesn't reopen the filled form. The created transfer is\n // handed over via router state — transfer reads are not wired to the\n // live backend yet, so the details page must not fabricate a receipt\n // from fixtures.\n this.spaceNav.navigateForwardToSpacePage(\n this.space,\n `transfer/${resp.transfer.id}`,\n { replaceUrl: true, state: { transfer: resp.transfer } },\n );\n },\n error: (err) => {\n this.$submitting.set(false);\n this.errorLogger.logError(err, 'Failed to create transfer', {\n show: false,\n });\n this.showToast('Failed to record transfer. Please try again.', 'danger');\n },\n });\n }\n\n private async showToast(message: string, color: string): Promise<void> {\n const toast = await this.toastController.create({\n message,\n duration: 3000,\n color,\n });\n await toast.present();\n }\n}\n","<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button [defaultHref]=\"$defaultBackUrl()\" />\n </ion-buttons>\n <ion-title>New transfer</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n <ion-card>\n <ion-card-content>\n <ion-segment [formControl]=\"direction\">\n <ion-segment-button value=\"lend\">\n <ion-label>I lent</ion-label>\n </ion-segment-button>\n <ion-segment-button value=\"borrow\">\n <ion-label>I borrowed</ion-label>\n </ion-segment-button>\n </ion-segment>\n <ion-note class=\"ion-padding-start\" color=\"medium\">\n {{\n direction.value === 'lend'\n ? 'You gave money — they owe you.'\n : 'You got money — you owe them.'\n }}\n </ion-note>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-card-content>\n <!-- Reuse the contactus space-contact picker. The counterparty is a\n real contactus contact, not a debtus-private person. -->\n <sneat-contact-input\n [space]=\"space\"\n label=\"Counterparty\"\n [canChangeContact]=\"true\"\n (contactChange)=\"onContactChanged($event)\"\n />\n @if (prefilledContactID) {\n <ion-note class=\"ion-padding-start\" color=\"medium\">\n Recording against\n {{ counterpartyName.value || prefilledContactID }}\n </ion-note>\n }\n <!-- Fable refactoring: the \"Or new counterparty name\" free-text input\n is disabled — a name-only counterparty always 400s server-side\n (POST create-transfer requires a real contact id; see\n resolveCounterparty() in the component). Re-enable once a\n create-contact-then-transfer flow exists. Kept per the no-delete\n policy:\n <ion-item>\n <ion-label position=\"stacked\">\n Or new counterparty name\n </ion-label>\n <ion-input\n [formControl]=\"counterpartyName\"\n placeholder=\"e.g. Alex\"\n autocapitalize=\"words\"\n />\n </ion-item>\n -->\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-card-content>\n <ion-item>\n <ion-label>Currency</ion-label>\n <ion-select [formControl]=\"currency\" interface=\"popover\">\n @for (c of currencies; track c) {\n <ion-select-option [value]=\"c\">{{ c }}</ion-select-option>\n }\n </ion-select>\n </ion-item>\n <ion-item>\n <ion-label position=\"stacked\">Amount</ion-label>\n <ion-input\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"amount\"\n placeholder=\"0.00\"\n />\n </ion-item>\n @if (amount.touched && amount.invalid) {\n <ion-note color=\"danger\" class=\"ion-padding-start\">\n Enter an amount greater than zero.\n </ion-note>\n }\n <ion-item>\n <ion-label position=\"stacked\">Note (optional)</ion-label>\n <ion-textarea\n [formControl]=\"note\"\n placeholder=\"What is it for?\"\n [autoGrow]=\"true\"\n />\n </ion-item>\n </ion-card-content>\n </ion-card>\n\n <div class=\"ion-padding\">\n <ion-button\n expand=\"block\"\n type=\"submit\"\n [disabled]=\"$submitting() || form.invalid\"\n >\n @if ($submitting()) {\n <ion-spinner name=\"dots\" slot=\"start\" />\n Saving…\n } @else {\n Record transfer\n }\n </ion-button>\n </div>\n </form>\n</ion-content>\n"],"names":[],"mappings":"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"}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, signal, Component } from '@angular/core';
|
|
3
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
|
-
import * as i1 from '@angular/forms';
|
|
5
|
-
import { FormControl, Validators, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
6
|
-
import { ToastController, IonHeader, IonToolbar, IonButtons, IonBackButton, IonTitle, IonContent, IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonItem, IonLabel, IonInput, IonSelect, IonSelectOption, IonButton, IonNote, IonSpinner } from '@ionic/angular';
|
|
7
|
-
import { DemoDataBannerComponent } from './sneat-extension-debtus-ui.mjs';
|
|
8
|
-
import { DEBTUS_SERVICE, formatSignedBalance, round2, settleDirectionForBalance } from '@sneat/extension-debtus-contract';
|
|
9
|
-
import { SpacePageBaseComponent, SpaceComponentBaseParams } from '@sneat/space-components';
|
|
10
|
-
import { ClassName } from '@sneat/ui';
|
|
11
|
-
import { combineLatest, switchMap } from 'rxjs';
|
|
12
|
-
|
|
13
|
-
// Settle up — records a settling (return) transfer against a counterparty
|
|
14
|
-
// balance. Mirrors the bot's "Returned fully/partially" flow: the backend nets
|
|
15
|
-
// the return against outstanding transfers.
|
|
16
|
-
class SettleUpPageComponent extends SpacePageBaseComponent {
|
|
17
|
-
debtusService = inject(DEBTUS_SERVICE);
|
|
18
|
-
toastController = inject(ToastController);
|
|
19
|
-
$loading = signal(true, /* @ts-ignore */
|
|
20
|
-
...(ngDevMode ? [{ debugName: "$loading" }] : /* istanbul ignore next */ []));
|
|
21
|
-
$submitting = signal(false, /* @ts-ignore */
|
|
22
|
-
...(ngDevMode ? [{ debugName: "$submitting" }] : /* istanbul ignore next */ []));
|
|
23
|
-
$error = signal(undefined, /* @ts-ignore */
|
|
24
|
-
...(ngDevMode ? [{ debugName: "$error" }] : /* istanbul ignore next */ []));
|
|
25
|
-
$contact = signal(undefined, /* @ts-ignore */
|
|
26
|
-
...(ngDevMode ? [{ debugName: "$contact" }] : /* istanbul ignore next */ []));
|
|
27
|
-
contactID = '';
|
|
28
|
-
formatSignedBalance = formatSignedBalance;
|
|
29
|
-
currency = new FormControl('EUR', {
|
|
30
|
-
nonNullable: true,
|
|
31
|
-
});
|
|
32
|
-
amount = new FormControl(null, [
|
|
33
|
-
Validators.required,
|
|
34
|
-
Validators.min(0.01),
|
|
35
|
-
]);
|
|
36
|
-
form = new FormGroup({
|
|
37
|
-
currency: this.currency,
|
|
38
|
-
amount: this.amount,
|
|
39
|
-
});
|
|
40
|
-
currencies = ['EUR', 'USD'];
|
|
41
|
-
constructor() {
|
|
42
|
-
super();
|
|
43
|
-
combineLatest([this.spaceIDChanged$, this.route.queryParamMap])
|
|
44
|
-
.pipe(switchMap(([spaceID, params]) => {
|
|
45
|
-
this.contactID = params.get('contactID') ?? '';
|
|
46
|
-
// Back should return to the contact detail this settle-up was
|
|
47
|
-
// opened from, not a fixed page.
|
|
48
|
-
this.$defaultBackUrlSpacePath.set(`debtus-contact/${this.contactID}`);
|
|
49
|
-
this.$loading.set(true);
|
|
50
|
-
this.$error.set(undefined);
|
|
51
|
-
return this.debtusService.getContactBalance(spaceID ?? '', this.contactID);
|
|
52
|
-
}), takeUntilDestroyed())
|
|
53
|
-
.subscribe({
|
|
54
|
-
next: (contact) => {
|
|
55
|
-
this.$contact.set(contact);
|
|
56
|
-
// Default the settle amount/currency to the outstanding balance.
|
|
57
|
-
const entries = Object.entries(contact.balance).filter(([, v]) => v && round2(v) !== 0);
|
|
58
|
-
if (entries.length) {
|
|
59
|
-
const [cur, val] = entries[0];
|
|
60
|
-
this.currency.setValue(cur);
|
|
61
|
-
this.amount.setValue(Math.abs(round2(val)));
|
|
62
|
-
}
|
|
63
|
-
this.$loading.set(false);
|
|
64
|
-
},
|
|
65
|
-
error: (err) => {
|
|
66
|
-
this.$loading.set(false);
|
|
67
|
-
this.$error.set('Failed to load contact');
|
|
68
|
-
this.errorLogger.logError(err, 'Failed to load contact for settle');
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
submit() {
|
|
73
|
-
this.form.markAllAsTouched();
|
|
74
|
-
const amount = this.amount.value;
|
|
75
|
-
if (!amount || amount <= 0) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const spaceID = this.$spaceID();
|
|
79
|
-
if (!spaceID || !this.contactID) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
// Mirrors the bot's "Is it returned in full?" confirmation step
|
|
83
|
-
// (askIfReturnedInFull in transfer_return.go) before recording the
|
|
84
|
-
// settling transfer — this is a financial record, not a reversible
|
|
85
|
-
// toggle, so it needs an explicit yes.
|
|
86
|
-
const title = this.$contact()?.title ?? 'this contact';
|
|
87
|
-
if (!confirm(`Record settlement of ${this.currency.value} ${amount.toFixed(2)} with ${title}?`)) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
// This page shows the signed balance, so it (not the service) derives the
|
|
91
|
-
// direction that moves that balance toward zero for the chosen currency.
|
|
92
|
-
// Deriving it downstream from demo fixtures recorded the wrong direction
|
|
93
|
-
// for contacts the fixtures don't know about.
|
|
94
|
-
const balanceValue = this.$contact()?.balance[this.currency.value] ?? 0;
|
|
95
|
-
const request = {
|
|
96
|
-
spaceID,
|
|
97
|
-
contactID: this.contactID,
|
|
98
|
-
contactTitle: this.$contact()?.title,
|
|
99
|
-
amount: { currency: this.currency.value, value: amount },
|
|
100
|
-
counterpartySpaceID: this.$contact()?.counterpartySpaceID,
|
|
101
|
-
direction: settleDirectionForBalance(balanceValue),
|
|
102
|
-
};
|
|
103
|
-
this.$submitting.set(true);
|
|
104
|
-
this.debtusService.settleUp(request).subscribe({
|
|
105
|
-
next: (resp) => {
|
|
106
|
-
this.$submitting.set(false);
|
|
107
|
-
// Hand the created transfer to the details page via router state —
|
|
108
|
-
// transfer reads are not wired to the live backend yet, so the details
|
|
109
|
-
// page must not fabricate a receipt from fixtures.
|
|
110
|
-
this.spaceNav.navigateForwardToSpacePage(this.space, `transfer/${resp.transfer.id}`, { replaceUrl: true, state: { transfer: resp.transfer } });
|
|
111
|
-
},
|
|
112
|
-
error: (err) => {
|
|
113
|
-
this.$submitting.set(false);
|
|
114
|
-
this.errorLogger.logError(err, 'Failed to settle up');
|
|
115
|
-
this.showToast('Failed to settle up. Please try again.');
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
async showToast(message) {
|
|
120
|
-
const toast = await this.toastController.create({
|
|
121
|
-
message,
|
|
122
|
-
duration: 3000,
|
|
123
|
-
color: 'danger',
|
|
124
|
-
});
|
|
125
|
-
await toast.present();
|
|
126
|
-
}
|
|
127
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SettleUpPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
128
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SettleUpPageComponent, isStandalone: true, selector: "sneat-debtus-settle-up-page", providers: [
|
|
129
|
-
{ provide: ClassName, useValue: 'SettleUpPageComponent' },
|
|
130
|
-
SpaceComponentBaseParams,
|
|
131
|
-
], usesInheritance: true, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button [defaultHref]=\"$defaultBackUrl()\" />\n </ion-buttons>\n <ion-title>Settle up</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <!-- Reads on this page are still demo fixtures (see DebtusService). -->\n <sneat-debtus-demo-data-banner />\n\n @if ($loading()) {\n <div class=\"ion-padding ion-text-center\">\n <ion-spinner name=\"dots\" />\n </div>\n } @else if ($error()) {\n <div class=\"ion-padding\">\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </div>\n } @else {\n <ion-card>\n <ion-card-header>\n <ion-card-title>{{ $contact()?.title ?? 'Contact' }}</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n Current balance: {{ formatSignedBalance($contact()?.balance ?? {}) }}\n </ion-card-content>\n </ion-card>\n\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n <ion-card>\n <ion-card-content>\n <ion-item>\n <ion-label>Currency</ion-label>\n <ion-select [formControl]=\"currency\" interface=\"popover\">\n @for (c of currencies; track c) {\n <ion-select-option [value]=\"c\">{{ c }}</ion-select-option>\n }\n </ion-select>\n </ion-item>\n <ion-item>\n <ion-label position=\"stacked\">Amount to settle</ion-label>\n <ion-input\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"amount\"\n placeholder=\"0.00\"\n />\n </ion-item>\n @if (amount.touched && amount.invalid) {\n <ion-note color=\"danger\" class=\"ion-padding-start\">\n Enter an amount greater than zero.\n </ion-note>\n }\n <ion-note class=\"ion-padding\" color=\"medium\">\n Records a settling (return) transfer. The balance is netted against\n outstanding transfers.\n </ion-note>\n </ion-card-content>\n </ion-card>\n\n <div class=\"ion-padding\">\n <ion-button\n expand=\"block\"\n color=\"success\"\n type=\"submit\"\n [disabled]=\"$submitting() || form.invalid\"\n >\n @if ($submitting()) {\n <ion-spinner name=\"dots\" slot=\"start\" />\n Settling\u2026\n } @else {\n Record settlement\n }\n </ion-button>\n </div>\n </form>\n }\n</ion-content>\n", dependencies: [{ kind: "component", type: DemoDataBannerComponent, selector: "sneat-debtus-demo-data-banner" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonBackButton, selector: "ion-back-button" }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"], outputs: ["ionScrollStart", "ionScroll", "ionScrollEnd"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["description", "disabled", "justify", "labelPlacement", "mode", "value"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"], outputs: ["ionFocus", "ionBlur"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] });
|
|
132
|
-
}
|
|
133
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SettleUpPageComponent, decorators: [{
|
|
134
|
-
type: Component,
|
|
135
|
-
args: [{ selector: 'sneat-debtus-settle-up-page', imports: [
|
|
136
|
-
DemoDataBannerComponent,
|
|
137
|
-
ReactiveFormsModule,
|
|
138
|
-
IonHeader,
|
|
139
|
-
IonToolbar,
|
|
140
|
-
IonButtons,
|
|
141
|
-
IonBackButton,
|
|
142
|
-
IonTitle,
|
|
143
|
-
IonContent,
|
|
144
|
-
IonCard,
|
|
145
|
-
IonCardHeader,
|
|
146
|
-
IonCardTitle,
|
|
147
|
-
IonCardContent,
|
|
148
|
-
IonItem,
|
|
149
|
-
IonLabel,
|
|
150
|
-
IonInput,
|
|
151
|
-
IonSelect,
|
|
152
|
-
IonSelectOption,
|
|
153
|
-
IonButton,
|
|
154
|
-
IonNote,
|
|
155
|
-
IonSpinner,
|
|
156
|
-
], providers: [
|
|
157
|
-
{ provide: ClassName, useValue: 'SettleUpPageComponent' },
|
|
158
|
-
SpaceComponentBaseParams,
|
|
159
|
-
], template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button [defaultHref]=\"$defaultBackUrl()\" />\n </ion-buttons>\n <ion-title>Settle up</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <!-- Reads on this page are still demo fixtures (see DebtusService). -->\n <sneat-debtus-demo-data-banner />\n\n @if ($loading()) {\n <div class=\"ion-padding ion-text-center\">\n <ion-spinner name=\"dots\" />\n </div>\n } @else if ($error()) {\n <div class=\"ion-padding\">\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </div>\n } @else {\n <ion-card>\n <ion-card-header>\n <ion-card-title>{{ $contact()?.title ?? 'Contact' }}</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n Current balance: {{ formatSignedBalance($contact()?.balance ?? {}) }}\n </ion-card-content>\n </ion-card>\n\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n <ion-card>\n <ion-card-content>\n <ion-item>\n <ion-label>Currency</ion-label>\n <ion-select [formControl]=\"currency\" interface=\"popover\">\n @for (c of currencies; track c) {\n <ion-select-option [value]=\"c\">{{ c }}</ion-select-option>\n }\n </ion-select>\n </ion-item>\n <ion-item>\n <ion-label position=\"stacked\">Amount to settle</ion-label>\n <ion-input\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"amount\"\n placeholder=\"0.00\"\n />\n </ion-item>\n @if (amount.touched && amount.invalid) {\n <ion-note color=\"danger\" class=\"ion-padding-start\">\n Enter an amount greater than zero.\n </ion-note>\n }\n <ion-note class=\"ion-padding\" color=\"medium\">\n Records a settling (return) transfer. The balance is netted against\n outstanding transfers.\n </ion-note>\n </ion-card-content>\n </ion-card>\n\n <div class=\"ion-padding\">\n <ion-button\n expand=\"block\"\n color=\"success\"\n type=\"submit\"\n [disabled]=\"$submitting() || form.invalid\"\n >\n @if ($submitting()) {\n <ion-spinner name=\"dots\" slot=\"start\" />\n Settling\u2026\n } @else {\n Record settlement\n }\n </ion-button>\n </div>\n </form>\n }\n</ion-content>\n" }]
|
|
160
|
-
}], ctorParameters: () => [] });
|
|
161
|
-
|
|
162
|
-
export { SettleUpPageComponent };
|
|
163
|
-
//# sourceMappingURL=sneat-extension-debtus-ui-settle-up-page.component-CUoNSuAE.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sneat-extension-debtus-ui-settle-up-page.component-CUoNSuAE.mjs","sources":["../../../../../../libs/extensions/debtus/ui/src/lib/pages/settle-up/settle-up-page.component.ts","../../../../../../libs/extensions/debtus/ui/src/lib/pages/settle-up/settle-up-page.component.html"],"sourcesContent":["import { Component, inject, signal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport {\n FormControl,\n FormGroup,\n ReactiveFormsModule,\n Validators,\n} from '@angular/forms';\nimport {\n IonBackButton,\n IonButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonCardHeader,\n IonCardTitle,\n IonContent,\n IonHeader,\n IonInput,\n IonItem,\n IonLabel,\n IonNote,\n IonSelect,\n IonSelectOption,\n IonSpinner,\n IonTitle,\n IonToolbar,\n ToastController,\n} from '@ionic/angular';\nimport { DemoDataBannerComponent } from '../../components/demo-data-banner/demo-data-banner.component';\nimport {\n CurrencyCode,\n DEBTUS_SERVICE,\n IContactBalance,\n ISettleUpRequest,\n formatSignedBalance,\n round2,\n settleDirectionForBalance,\n} from '@sneat/extension-debtus-contract';\nimport {\n SpaceComponentBaseParams,\n SpacePageBaseComponent,\n} from '@sneat/space-components';\nimport { ClassName } from '@sneat/ui';\nimport { combineLatest, switchMap } from 'rxjs';\n\n// Settle up — records a settling (return) transfer against a counterparty\n// balance. Mirrors the bot's \"Returned fully/partially\" flow: the backend nets\n// the return against outstanding transfers.\n@Component({\n selector: 'sneat-debtus-settle-up-page',\n templateUrl: './settle-up-page.component.html',\n imports: [\n DemoDataBannerComponent,\n ReactiveFormsModule,\n IonHeader,\n IonToolbar,\n IonButtons,\n IonBackButton,\n IonTitle,\n IonContent,\n IonCard,\n IonCardHeader,\n IonCardTitle,\n IonCardContent,\n IonItem,\n IonLabel,\n IonInput,\n IonSelect,\n IonSelectOption,\n IonButton,\n IonNote,\n IonSpinner,\n ],\n providers: [\n { provide: ClassName, useValue: 'SettleUpPageComponent' },\n SpaceComponentBaseParams,\n ],\n})\nexport class SettleUpPageComponent extends SpacePageBaseComponent {\n private readonly debtusService = inject(DEBTUS_SERVICE);\n private readonly toastController = inject(ToastController);\n\n protected readonly $loading = signal(true);\n protected readonly $submitting = signal(false);\n protected readonly $error = signal<string | undefined>(undefined);\n protected readonly $contact = signal<IContactBalance | undefined>(undefined);\n protected contactID = '';\n\n protected readonly formatSignedBalance = formatSignedBalance;\n\n protected readonly currency = new FormControl<CurrencyCode>('EUR', {\n nonNullable: true,\n });\n protected readonly amount = new FormControl<number | null>(null, [\n Validators.required,\n Validators.min(0.01),\n ]);\n protected readonly form = new FormGroup({\n currency: this.currency,\n amount: this.amount,\n });\n\n protected readonly currencies: CurrencyCode[] = ['EUR', 'USD'];\n\n constructor() {\n super();\n combineLatest([this.spaceIDChanged$, this.route.queryParamMap])\n .pipe(\n switchMap(([spaceID, params]) => {\n this.contactID = params.get('contactID') ?? '';\n // Back should return to the contact detail this settle-up was\n // opened from, not a fixed page.\n this.$defaultBackUrlSpacePath.set(\n `debtus-contact/${this.contactID}`,\n );\n this.$loading.set(true);\n this.$error.set(undefined);\n return this.debtusService.getContactBalance(\n spaceID ?? '',\n this.contactID,\n );\n }),\n takeUntilDestroyed(),\n )\n .subscribe({\n next: (contact) => {\n this.$contact.set(contact);\n // Default the settle amount/currency to the outstanding balance.\n const entries = Object.entries(contact.balance).filter(\n ([, v]) => v && round2(v) !== 0,\n );\n if (entries.length) {\n const [cur, val] = entries[0];\n this.currency.setValue(cur as CurrencyCode);\n this.amount.setValue(Math.abs(round2(val as number)));\n }\n this.$loading.set(false);\n },\n error: (err) => {\n this.$loading.set(false);\n this.$error.set('Failed to load contact');\n this.errorLogger.logError(err, 'Failed to load contact for settle');\n },\n });\n }\n\n protected submit(): void {\n this.form.markAllAsTouched();\n const amount = this.amount.value;\n if (!amount || amount <= 0) {\n return;\n }\n const spaceID = this.$spaceID();\n if (!spaceID || !this.contactID) {\n return;\n }\n // Mirrors the bot's \"Is it returned in full?\" confirmation step\n // (askIfReturnedInFull in transfer_return.go) before recording the\n // settling transfer — this is a financial record, not a reversible\n // toggle, so it needs an explicit yes.\n const title = this.$contact()?.title ?? 'this contact';\n if (\n !confirm(\n `Record settlement of ${this.currency.value} ${amount.toFixed(2)} with ${title}?`,\n )\n ) {\n return;\n }\n // This page shows the signed balance, so it (not the service) derives the\n // direction that moves that balance toward zero for the chosen currency.\n // Deriving it downstream from demo fixtures recorded the wrong direction\n // for contacts the fixtures don't know about.\n const balanceValue = this.$contact()?.balance[this.currency.value] ?? 0;\n const request: ISettleUpRequest = {\n spaceID,\n contactID: this.contactID,\n contactTitle: this.$contact()?.title,\n amount: { currency: this.currency.value, value: amount },\n counterpartySpaceID: this.$contact()?.counterpartySpaceID,\n direction: settleDirectionForBalance(balanceValue),\n };\n this.$submitting.set(true);\n this.debtusService.settleUp(request).subscribe({\n next: (resp) => {\n this.$submitting.set(false);\n // Hand the created transfer to the details page via router state —\n // transfer reads are not wired to the live backend yet, so the details\n // page must not fabricate a receipt from fixtures.\n this.spaceNav.navigateForwardToSpacePage(\n this.space,\n `transfer/${resp.transfer.id}`,\n { replaceUrl: true, state: { transfer: resp.transfer } },\n );\n },\n error: (err) => {\n this.$submitting.set(false);\n this.errorLogger.logError(err, 'Failed to settle up');\n this.showToast('Failed to settle up. Please try again.');\n },\n });\n }\n\n private async showToast(message: string): Promise<void> {\n const toast = await this.toastController.create({\n message,\n duration: 3000,\n color: 'danger',\n });\n await toast.present();\n }\n}\n","<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button [defaultHref]=\"$defaultBackUrl()\" />\n </ion-buttons>\n <ion-title>Settle up</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <!-- Reads on this page are still demo fixtures (see DebtusService). -->\n <sneat-debtus-demo-data-banner />\n\n @if ($loading()) {\n <div class=\"ion-padding ion-text-center\">\n <ion-spinner name=\"dots\" />\n </div>\n } @else if ($error()) {\n <div class=\"ion-padding\">\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </div>\n } @else {\n <ion-card>\n <ion-card-header>\n <ion-card-title>{{ $contact()?.title ?? 'Contact' }}</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n Current balance: {{ formatSignedBalance($contact()?.balance ?? {}) }}\n </ion-card-content>\n </ion-card>\n\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n <ion-card>\n <ion-card-content>\n <ion-item>\n <ion-label>Currency</ion-label>\n <ion-select [formControl]=\"currency\" interface=\"popover\">\n @for (c of currencies; track c) {\n <ion-select-option [value]=\"c\">{{ c }}</ion-select-option>\n }\n </ion-select>\n </ion-item>\n <ion-item>\n <ion-label position=\"stacked\">Amount to settle</ion-label>\n <ion-input\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"amount\"\n placeholder=\"0.00\"\n />\n </ion-item>\n @if (amount.touched && amount.invalid) {\n <ion-note color=\"danger\" class=\"ion-padding-start\">\n Enter an amount greater than zero.\n </ion-note>\n }\n <ion-note class=\"ion-padding\" color=\"medium\">\n Records a settling (return) transfer. The balance is netted against\n outstanding transfers.\n </ion-note>\n </ion-card-content>\n </ion-card>\n\n <div class=\"ion-padding\">\n <ion-button\n expand=\"block\"\n color=\"success\"\n type=\"submit\"\n [disabled]=\"$submitting() || form.invalid\"\n >\n @if ($submitting()) {\n <ion-spinner name=\"dots\" slot=\"start\" />\n Settling…\n } @else {\n Record settlement\n }\n </ion-button>\n </div>\n </form>\n }\n</ion-content>\n"],"names":[],"mappings":"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"}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { DatePipe } from '@angular/common';
|
|
2
|
-
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, signal, Component } from '@angular/core';
|
|
4
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
5
|
-
import { Router } from '@angular/router';
|
|
6
|
-
import { IonHeader, IonToolbar, IonButtons, IonBackButton, IonTitle, IonContent, IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonList, IonItem, IonLabel, IonNote, IonBadge, IonButton, IonIcon, IonSpinner } from '@ionic/angular';
|
|
7
|
-
import { DEBTUS_SERVICE, formatAmount } from '@sneat/extension-debtus-contract';
|
|
8
|
-
import { SpacePageBaseComponent, SpaceComponentBaseParams } from '@sneat/space-components';
|
|
9
|
-
import { combineLatest, switchMap, of } from 'rxjs';
|
|
10
|
-
|
|
11
|
-
// Transfer detail / receipt — amount, parties, date, note, and its effect on
|
|
12
|
-
// the balance. Mirrors the bot's receipt. Offers a settle-up entry.
|
|
13
|
-
class TransferDetailsPageComponent extends SpacePageBaseComponent {
|
|
14
|
-
debtusService = inject(DEBTUS_SERVICE);
|
|
15
|
-
router = inject(Router);
|
|
16
|
-
$loading = signal(true, /* @ts-ignore */
|
|
17
|
-
...(ngDevMode ? [{ debugName: "$loading" }] : /* istanbul ignore next */ []));
|
|
18
|
-
$error = signal(undefined, /* @ts-ignore */
|
|
19
|
-
...(ngDevMode ? [{ debugName: "$error" }] : /* istanbul ignore next */ []));
|
|
20
|
-
$transfer = signal(undefined, /* @ts-ignore */
|
|
21
|
-
...(ngDevMode ? [{ debugName: "$transfer" }] : /* istanbul ignore next */ []));
|
|
22
|
-
formatAmount = formatAmount;
|
|
23
|
-
constructor() {
|
|
24
|
-
super();
|
|
25
|
-
this.$defaultBackUrlSpacePath.set('debts');
|
|
26
|
-
// A transfer just created by the new-transfer/settle-up pages is handed
|
|
27
|
-
// over via router `state` (it survives reloads through history.state).
|
|
28
|
-
// Prefer it: transfer reads are not wired to the live backend yet, so
|
|
29
|
-
// fetching a REAL transfer's id would fail — and it must fail rather than
|
|
30
|
-
// fabricate a receipt from fixtures.
|
|
31
|
-
const stateTransfer = (this.router.getCurrentNavigation()?.extras?.state ?? history.state)?.['transfer'];
|
|
32
|
-
combineLatest([this.spaceIDChanged$, this.route.paramMap])
|
|
33
|
-
.pipe(switchMap(([spaceID, params]) => {
|
|
34
|
-
const transferID = params.get('transferID') ?? '';
|
|
35
|
-
this.$loading.set(true);
|
|
36
|
-
this.$error.set(undefined);
|
|
37
|
-
if (stateTransfer?.id === transferID) {
|
|
38
|
-
return of(stateTransfer);
|
|
39
|
-
}
|
|
40
|
-
return this.debtusService.getTransfer(spaceID ?? '', transferID);
|
|
41
|
-
}), takeUntilDestroyed())
|
|
42
|
-
.subscribe({
|
|
43
|
-
next: (transfer) => {
|
|
44
|
-
this.$transfer.set(transfer);
|
|
45
|
-
this.$loading.set(false);
|
|
46
|
-
},
|
|
47
|
-
error: (err) => {
|
|
48
|
-
this.$error.set('Failed to load transfer');
|
|
49
|
-
this.$loading.set(false);
|
|
50
|
-
this.errorLogger.logError(err, 'Failed to load debtus transfer');
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
settleUp() {
|
|
55
|
-
const t = this.$transfer();
|
|
56
|
-
if (!t) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
this.spaceNav.navigateForwardToSpacePage(this.space, `settle-up?contactID=${t.counterpartyContactID}`);
|
|
60
|
-
}
|
|
61
|
-
openContact() {
|
|
62
|
-
const t = this.$transfer();
|
|
63
|
-
if (!t?.counterpartyContactID) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
this.spaceNav.navigateForwardToSpacePage(this.space, `debtus-contact/${t.counterpartyContactID}`);
|
|
67
|
-
}
|
|
68
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: TransferDetailsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
69
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: TransferDetailsPageComponent, isStandalone: true, selector: "sneat-debtus-transfer-details-page", providers: [SpaceComponentBaseParams], usesInheritance: true, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button [defaultHref]=\"$defaultBackUrl()\" />\n </ion-buttons>\n <ion-title>Transfer</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n @if ($loading()) {\n <div class=\"ion-padding ion-text-center\">\n <ion-spinner name=\"dots\" />\n </div>\n } @else if ($error()) {\n <div class=\"ion-padding\">\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </div>\n } @else if ($transfer(); as t) {\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n {{ t.direction === 'lend' ? 'You lent' : 'You borrowed' }}\n @if (t.isReturn) { \u00B7 return }\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-badge\n [color]=\"t.direction === 'lend' ? 'success' : 'warning'\"\n style=\"font-size: 1.2rem\"\n >\n {{ formatAmount(t.amount) }}\n </ion-badge>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-list lines=\"full\">\n <ion-item button (click)=\"openContact()\">\n <ion-label>\n <p>Counterparty</p>\n <h2>{{ t.counterpartyTitle }}</h2>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-label>\n <p>Date</p>\n <h2>{{ t.created | date: 'medium' }}</h2>\n </ion-label>\n </ion-item>\n @if (t.dueOn) {\n <ion-item>\n <ion-label>\n <p>Due</p>\n <h2>{{ t.dueOn | date: 'mediumDate' }}</h2>\n </ion-label>\n </ion-item>\n }\n @if (t.note) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Note</p>\n <h2>{{ t.note }}</h2>\n </ion-label>\n </ion-item>\n }\n <ion-item>\n <ion-label>\n <p>Status</p>\n <h2>{{ t.isOutstanding ? 'Outstanding' : 'Settled' }}</h2>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Effect on balance</p>\n <h2>\n @if (t.direction === 'lend') {\n {{ t.counterpartyTitle }} owes you {{ formatAmount(t.amount) }}\n } @else {\n You owe {{ t.counterpartyTitle }} {{ formatAmount(t.amount) }}\n }\n </h2>\n </ion-label>\n </ion-item>\n @if (t.counterpartySpaceID) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Cross-space</p>\n <h2>Counterparty side is in space {{ t.counterpartySpaceID }}</h2>\n <ion-note color=\"medium\">\n Your side: {{ t.creatorSpaceID }}\n </ion-note>\n </ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n @if (t.isOutstanding) {\n <div class=\"ion-padding\">\n <ion-button expand=\"block\" color=\"success\" (click)=\"settleUp()\">\n <ion-icon name=\"checkmark-done-outline\" slot=\"start\" />\n Settle up\n </ion-button>\n </div>\n }\n }\n</ion-content>\n", dependencies: [{ kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonBackButton, selector: "ion-back-button" }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"], outputs: ["ionScrollStart", "ionScroll", "ionScrollEnd"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonBadge, selector: "ion-badge", inputs: ["color", "mode"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"], outputs: ["ionFocus", "ionBlur"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "pipe", type: DatePipe, name: "date" }] });
|
|
70
|
-
}
|
|
71
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: TransferDetailsPageComponent, decorators: [{
|
|
72
|
-
type: Component,
|
|
73
|
-
args: [{ selector: 'sneat-debtus-transfer-details-page', imports: [
|
|
74
|
-
IonHeader,
|
|
75
|
-
IonToolbar,
|
|
76
|
-
IonButtons,
|
|
77
|
-
IonBackButton,
|
|
78
|
-
IonTitle,
|
|
79
|
-
IonContent,
|
|
80
|
-
IonCard,
|
|
81
|
-
IonCardHeader,
|
|
82
|
-
IonCardTitle,
|
|
83
|
-
IonCardContent,
|
|
84
|
-
IonList,
|
|
85
|
-
IonItem,
|
|
86
|
-
IonLabel,
|
|
87
|
-
IonNote,
|
|
88
|
-
IonBadge,
|
|
89
|
-
IonButton,
|
|
90
|
-
IonIcon,
|
|
91
|
-
IonSpinner,
|
|
92
|
-
DatePipe,
|
|
93
|
-
], providers: [SpaceComponentBaseParams], template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button [defaultHref]=\"$defaultBackUrl()\" />\n </ion-buttons>\n <ion-title>Transfer</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n @if ($loading()) {\n <div class=\"ion-padding ion-text-center\">\n <ion-spinner name=\"dots\" />\n </div>\n } @else if ($error()) {\n <div class=\"ion-padding\">\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </div>\n } @else if ($transfer(); as t) {\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n {{ t.direction === 'lend' ? 'You lent' : 'You borrowed' }}\n @if (t.isReturn) { \u00B7 return }\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-badge\n [color]=\"t.direction === 'lend' ? 'success' : 'warning'\"\n style=\"font-size: 1.2rem\"\n >\n {{ formatAmount(t.amount) }}\n </ion-badge>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-list lines=\"full\">\n <ion-item button (click)=\"openContact()\">\n <ion-label>\n <p>Counterparty</p>\n <h2>{{ t.counterpartyTitle }}</h2>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-label>\n <p>Date</p>\n <h2>{{ t.created | date: 'medium' }}</h2>\n </ion-label>\n </ion-item>\n @if (t.dueOn) {\n <ion-item>\n <ion-label>\n <p>Due</p>\n <h2>{{ t.dueOn | date: 'mediumDate' }}</h2>\n </ion-label>\n </ion-item>\n }\n @if (t.note) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Note</p>\n <h2>{{ t.note }}</h2>\n </ion-label>\n </ion-item>\n }\n <ion-item>\n <ion-label>\n <p>Status</p>\n <h2>{{ t.isOutstanding ? 'Outstanding' : 'Settled' }}</h2>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Effect on balance</p>\n <h2>\n @if (t.direction === 'lend') {\n {{ t.counterpartyTitle }} owes you {{ formatAmount(t.amount) }}\n } @else {\n You owe {{ t.counterpartyTitle }} {{ formatAmount(t.amount) }}\n }\n </h2>\n </ion-label>\n </ion-item>\n @if (t.counterpartySpaceID) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Cross-space</p>\n <h2>Counterparty side is in space {{ t.counterpartySpaceID }}</h2>\n <ion-note color=\"medium\">\n Your side: {{ t.creatorSpaceID }}\n </ion-note>\n </ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n @if (t.isOutstanding) {\n <div class=\"ion-padding\">\n <ion-button expand=\"block\" color=\"success\" (click)=\"settleUp()\">\n <ion-icon name=\"checkmark-done-outline\" slot=\"start\" />\n Settle up\n </ion-button>\n </div>\n }\n }\n</ion-content>\n" }]
|
|
94
|
-
}], ctorParameters: () => [] });
|
|
95
|
-
|
|
96
|
-
export { TransferDetailsPageComponent };
|
|
97
|
-
//# sourceMappingURL=sneat-extension-debtus-ui-transfer-details-page.component-uo5lP557.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sneat-extension-debtus-ui-transfer-details-page.component-uo5lP557.mjs","sources":["../../../../../../libs/extensions/debtus/ui/src/lib/pages/transfer-details/transfer-details-page.component.ts","../../../../../../libs/extensions/debtus/ui/src/lib/pages/transfer-details/transfer-details-page.component.html"],"sourcesContent":["import { DatePipe } from '@angular/common';\nimport { Component, inject, signal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Router } from '@angular/router';\nimport {\n IonBackButton,\n IonBadge,\n IonButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonCardHeader,\n IonCardTitle,\n IonContent,\n IonHeader,\n IonIcon,\n IonItem,\n IonLabel,\n IonList,\n IonNote,\n IonSpinner,\n IonTitle,\n IonToolbar,\n} from '@ionic/angular';\nimport {\n DEBTUS_SERVICE,\n IDebtusTransfer,\n formatAmount,\n} from '@sneat/extension-debtus-contract';\nimport {\n SpaceComponentBaseParams,\n SpacePageBaseComponent,\n} from '@sneat/space-components';\nimport { combineLatest, of, switchMap } from 'rxjs';\n\n// Transfer detail / receipt — amount, parties, date, note, and its effect on\n// the balance. Mirrors the bot's receipt. Offers a settle-up entry.\n@Component({\n selector: 'sneat-debtus-transfer-details-page',\n templateUrl: './transfer-details-page.component.html',\n imports: [\n IonHeader,\n IonToolbar,\n IonButtons,\n IonBackButton,\n IonTitle,\n IonContent,\n IonCard,\n IonCardHeader,\n IonCardTitle,\n IonCardContent,\n IonList,\n IonItem,\n IonLabel,\n IonNote,\n IonBadge,\n IonButton,\n IonIcon,\n IonSpinner,\n DatePipe,\n ],\n providers: [SpaceComponentBaseParams],\n})\nexport class TransferDetailsPageComponent extends SpacePageBaseComponent {\n private readonly debtusService = inject(DEBTUS_SERVICE);\n private readonly router = inject(Router);\n\n protected readonly $loading = signal(true);\n protected readonly $error = signal<string | undefined>(undefined);\n protected readonly $transfer = signal<IDebtusTransfer | undefined>(undefined);\n\n protected readonly formatAmount = formatAmount;\n\n constructor() {\n super();\n this.$defaultBackUrlSpacePath.set('debts');\n // A transfer just created by the new-transfer/settle-up pages is handed\n // over via router `state` (it survives reloads through history.state).\n // Prefer it: transfer reads are not wired to the live backend yet, so\n // fetching a REAL transfer's id would fail — and it must fail rather than\n // fabricate a receipt from fixtures.\n const stateTransfer = (\n this.router.getCurrentNavigation()?.extras?.state ?? history.state\n )?.['transfer'] as IDebtusTransfer | undefined;\n combineLatest([this.spaceIDChanged$, this.route.paramMap])\n .pipe(\n switchMap(([spaceID, params]) => {\n const transferID = params.get('transferID') ?? '';\n this.$loading.set(true);\n this.$error.set(undefined);\n if (stateTransfer?.id === transferID) {\n return of(stateTransfer);\n }\n return this.debtusService.getTransfer(spaceID ?? '', transferID);\n }),\n takeUntilDestroyed(),\n )\n .subscribe({\n next: (transfer) => {\n this.$transfer.set(transfer);\n this.$loading.set(false);\n },\n error: (err) => {\n this.$error.set('Failed to load transfer');\n this.$loading.set(false);\n this.errorLogger.logError(err, 'Failed to load debtus transfer');\n },\n });\n }\n\n protected settleUp(): void {\n const t = this.$transfer();\n if (!t) {\n return;\n }\n this.spaceNav.navigateForwardToSpacePage(\n this.space,\n `settle-up?contactID=${t.counterpartyContactID}`,\n );\n }\n\n protected openContact(): void {\n const t = this.$transfer();\n if (!t?.counterpartyContactID) {\n return;\n }\n this.spaceNav.navigateForwardToSpacePage(\n this.space,\n `debtus-contact/${t.counterpartyContactID}`,\n );\n }\n}\n","<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button [defaultHref]=\"$defaultBackUrl()\" />\n </ion-buttons>\n <ion-title>Transfer</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n @if ($loading()) {\n <div class=\"ion-padding ion-text-center\">\n <ion-spinner name=\"dots\" />\n </div>\n } @else if ($error()) {\n <div class=\"ion-padding\">\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </div>\n } @else if ($transfer(); as t) {\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n {{ t.direction === 'lend' ? 'You lent' : 'You borrowed' }}\n @if (t.isReturn) { · return }\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-badge\n [color]=\"t.direction === 'lend' ? 'success' : 'warning'\"\n style=\"font-size: 1.2rem\"\n >\n {{ formatAmount(t.amount) }}\n </ion-badge>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-list lines=\"full\">\n <ion-item button (click)=\"openContact()\">\n <ion-label>\n <p>Counterparty</p>\n <h2>{{ t.counterpartyTitle }}</h2>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-label>\n <p>Date</p>\n <h2>{{ t.created | date: 'medium' }}</h2>\n </ion-label>\n </ion-item>\n @if (t.dueOn) {\n <ion-item>\n <ion-label>\n <p>Due</p>\n <h2>{{ t.dueOn | date: 'mediumDate' }}</h2>\n </ion-label>\n </ion-item>\n }\n @if (t.note) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Note</p>\n <h2>{{ t.note }}</h2>\n </ion-label>\n </ion-item>\n }\n <ion-item>\n <ion-label>\n <p>Status</p>\n <h2>{{ t.isOutstanding ? 'Outstanding' : 'Settled' }}</h2>\n </ion-label>\n </ion-item>\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Effect on balance</p>\n <h2>\n @if (t.direction === 'lend') {\n {{ t.counterpartyTitle }} owes you {{ formatAmount(t.amount) }}\n } @else {\n You owe {{ t.counterpartyTitle }} {{ formatAmount(t.amount) }}\n }\n </h2>\n </ion-label>\n </ion-item>\n @if (t.counterpartySpaceID) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <p>Cross-space</p>\n <h2>Counterparty side is in space {{ t.counterpartySpaceID }}</h2>\n <ion-note color=\"medium\">\n Your side: {{ t.creatorSpaceID }}\n </ion-note>\n </ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n @if (t.isOutstanding) {\n <div class=\"ion-padding\">\n <ion-button expand=\"block\" color=\"success\" (click)=\"settleUp()\">\n <ion-icon name=\"checkmark-done-outline\" slot=\"start\" />\n Settle up\n </ion-button>\n </div>\n }\n }\n</ion-content>\n"],"names":[],"mappings":"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"}
|