@sneat/extension-splitus-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.
Files changed (25) hide show
  1. package/fesm2022/{sneat-extension-splitus-ui-new-split-page.component-DkP-7W5H.mjs → sneat-extension-splitus-ui-new-split-page.component-DqIE_0pp.mjs} +8 -8
  2. package/fesm2022/sneat-extension-splitus-ui-new-split-page.component-DqIE_0pp.mjs.map +1 -0
  3. package/fesm2022/sneat-extension-splitus-ui-sneat-extension-splitus-ui-CX1eiAAR.mjs +558 -0
  4. package/fesm2022/sneat-extension-splitus-ui-sneat-extension-splitus-ui-CX1eiAAR.mjs.map +1 -0
  5. package/fesm2022/sneat-extension-splitus-ui-split-details-page.component-fkoVkmXD.mjs +105 -0
  6. package/fesm2022/sneat-extension-splitus-ui-split-details-page.component-fkoVkmXD.mjs.map +1 -0
  7. package/fesm2022/sneat-extension-splitus-ui-split-status-view-CRt2FKg1.mjs +7 -0
  8. package/fesm2022/sneat-extension-splitus-ui-split-status-view-CRt2FKg1.mjs.map +1 -0
  9. package/fesm2022/sneat-extension-splitus-ui-splitus-home-page.component-tUSn27LG.mjs +100 -0
  10. package/fesm2022/sneat-extension-splitus-ui-splitus-home-page.component-tUSn27LG.mjs.map +1 -0
  11. package/fesm2022/sneat-extension-splitus-ui-start-split-page.component-Br8thaKs.mjs +74 -0
  12. package/fesm2022/sneat-extension-splitus-ui-start-split-page.component-Br8thaKs.mjs.map +1 -0
  13. package/fesm2022/sneat-extension-splitus-ui.mjs +1 -468
  14. package/fesm2022/sneat-extension-splitus-ui.mjs.map +1 -1
  15. package/package.json +4 -3
  16. package/types/sneat-extension-splitus-ui.d.ts +45 -30
  17. package/fesm2022/sneat-extension-splitus-ui-new-split-page.component-DkP-7W5H.mjs.map +0 -1
  18. package/fesm2022/sneat-extension-splitus-ui-split-details-page.component-kzwMFwkF.mjs +0 -87
  19. package/fesm2022/sneat-extension-splitus-ui-split-details-page.component-kzwMFwkF.mjs.map +0 -1
  20. package/fesm2022/sneat-extension-splitus-ui-split-status-view-Dk3SLPhh.mjs +0 -44
  21. package/fesm2022/sneat-extension-splitus-ui-split-status-view-Dk3SLPhh.mjs.map +0 -1
  22. package/fesm2022/sneat-extension-splitus-ui-splitus-home-page.component-Dp5oLR86.mjs +0 -99
  23. package/fesm2022/sneat-extension-splitus-ui-splitus-home-page.component-Dp5oLR86.mjs.map +0 -1
  24. package/fesm2022/sneat-extension-splitus-ui-start-split-page.component-BHsYZbKT.mjs +0 -67
  25. package/fesm2022/sneat-extension-splitus-ui-start-split-page.component-BHsYZbKT.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sneat-extension-splitus-ui-sneat-extension-splitus-ui-CX1eiAAR.mjs","sources":["../../../../../../libs/extensions/splitus/ui/src/lib/services/draft-split-store.service.ts","../../../../../../libs/extensions/splitus/ui/src/lib/services/exact-decimal.ts","../../../../../../libs/extensions/splitus/ui/src/lib/components/new-split-form/split-shares.ts","../../../../../../libs/extensions/splitus/ui/src/lib/components/new-split-form/new-split-form.component.ts","../../../../../../libs/extensions/splitus/ui/src/lib/components/new-split-form/new-split-form.component.html","../../../../../../libs/extensions/splitus/ui/src/lib/pages/space-pages-routing.ts","../../../../../../libs/extensions/splitus/ui/src/lib/pages/start-split/start-split.routes.ts","../../../../../../libs/extensions/splitus/ui/src/lib/services/splitus-settlement-url.ts","../../../../../../libs/extensions/splitus/ui/src/lib/services/splitus-start-continuation-url.ts","../../../../../../libs/extensions/splitus/ui/src/sneat-extension-splitus-ui.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { SplitusCurrencyCode } from '@sneat/extension-splitus-contract';\n\n/**\n * A single in-progress \"start a split\" draft — captured before the user has\n * a space or is even signed in (see StartSplitPageComponent), resumed into\n * NewSplitFormComponent once they are. The canonical journey requirements are\n * maintained in the Backstage SpecScore Splitus features.\n */\nexport interface IDraftSplit {\n readonly billID: string;\n readonly title: string;\n readonly amount: string;\n readonly currencyCode: SplitusCurrencyCode;\n readonly createdAt: number; // epoch ms\n}\n\nexport const DRAFT_SPLIT_STORAGE_KEY = 'sneat.splitus.draftSplit';\n\nconst MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24h\nconst CURRENCY_CODES: readonly SplitusCurrencyCode[] = ['EUR', 'GBP', 'USD'];\n\nfunction isDraftSplit(value: unknown): value is IDraftSplit {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const v = value as Record<string, unknown>;\n return (\n typeof v['billID'] === 'string' &&\n v['billID'].length > 0 &&\n typeof v['title'] === 'string' &&\n typeof v['amount'] === 'string' &&\n CURRENCY_CODES.includes(v['currencyCode'] as SplitusCurrencyCode) &&\n typeof v['createdAt'] === 'number'\n );\n}\n\n@Injectable({ providedIn: 'root' })\nexport class DraftSplitStore {\n save(draft: Omit<IDraftSplit, 'createdAt'>): void {\n const full: IDraftSplit = { ...draft, createdAt: Date.now() };\n try {\n localStorage.setItem(DRAFT_SPLIT_STORAGE_KEY, JSON.stringify(full));\n } catch {\n // Storage unavailable (private mode, quota) — fail open, never block\n // navigation over a lost draft.\n }\n }\n\n read(): IDraftSplit | undefined {\n let raw: string | null;\n try {\n raw = localStorage.getItem(DRAFT_SPLIT_STORAGE_KEY);\n } catch {\n return undefined;\n }\n if (!raw) {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (!isDraftSplit(parsed) || Date.now() - parsed.createdAt > MAX_AGE_MS) {\n return undefined;\n }\n return parsed;\n }\n\n hasPending(): boolean {\n return this.read() !== undefined;\n }\n\n clear(): void {\n try {\n localStorage.removeItem(DRAFT_SPLIT_STORAGE_KEY);\n } catch {\n // ignore\n }\n }\n}\n","import {\n AbstractControl,\n ValidationErrors,\n ValidatorFn,\n} from '@angular/forms';\n\nconst DECIMAL_PATTERN = /^(?:0|[1-9]\\d*)(?:\\.(\\d{1,2}))?$/;\nconst ZERO = BigInt('0');\nconst HUNDRED = BigInt('100');\n\n/** Parses a non-negative decimal string into exact integer hundredths. */\nexport function parseDecimal(\n raw: string | null | undefined,\n): bigint | undefined {\n const value = raw?.trim();\n if (!value) {\n return undefined;\n }\n const match = DECIMAL_PATTERN.exec(value);\n if (!match) {\n return undefined;\n }\n const [whole = '', fraction = ''] = value.split('.');\n return BigInt(whole) * HUNDRED + BigInt(fraction.padEnd(2, '0') || '0');\n}\n\n/** Formats exact integer hundredths as a two-decimal wire string. */\nexport function formatHundredths(value: bigint): string {\n const sign = value < ZERO ? '-' : '';\n const absolute = value < ZERO ? -value : value;\n const whole = absolute / HUNDRED;\n const fraction = (absolute % HUNDRED).toString().padStart(2, '0');\n return `${sign}${whole.toString()}.${fraction}`;\n}\n\n/** Formats basis points as a trimmed percent value. */\nexport function formatPercentValue(value: bigint): string {\n const formatted = formatHundredths(value);\n if (formatted.endsWith('.00')) {\n return formatted.slice(0, -3);\n }\n return formatted.endsWith('0') ? formatted.slice(0, -1) : formatted;\n}\n\nexport const positiveExactDecimalValidator: ValidatorFn = (\n control: AbstractControl<string | null>,\n): ValidationErrors | null => {\n const value = parseDecimal(control.value);\n return value !== undefined && value > ZERO ? null : { exactDecimal: true };\n};\n","import {\n formatHundredths,\n formatPercentValue,\n parseDecimal,\n} from '../../services/exact-decimal';\n\nexport type SplitMode = 'equally' | 'exact-amount' | 'percentage';\n\nexport {\n formatHundredths,\n formatPercentValue,\n parseDecimal,\n} from '../../services/exact-decimal';\n\n// Pure client-side share reconciliation for the create-split form.\n// All arithmetic is done in integer hundredths (cents for amounts, basis\n// points for percentages) so \"0.10 + 0.20 == 0.30\" holds exactly — the same\n// fixed-point discipline the backend applies (decimal.Decimal64p2).\n\nexport interface IShareReconciliation {\n readonly ok: boolean;\n /** Explanatory, user-facing message when `ok` is false. */\n readonly error?: string;\n}\n\n/**\n * Live reconciliation of custom shares against the expense total.\n *\n * - `equally`: always ok — there are no share inputs; the client calculates\n * exact allocations before submitting the bill.\n * - `exact-amount`: every share must be entered and sum exactly to the total.\n * - `percentage`: every percentage must be entered and sum exactly to 100%.\n *\n * @param totalHundredths the expense total in hundredths (cents), if entered.\n * @param shares raw share inputs, one per participant (payer included).\n */\nexport function reconcileShares(\n mode: SplitMode,\n totalHundredths: bigint | undefined,\n shares: readonly (string | null | undefined)[],\n): IShareReconciliation {\n if (mode === 'equally') {\n return { ok: true };\n }\n\n const parsed = shares.map(parseDecimal);\n if (parsed.some((v) => v === undefined)) {\n return {\n ok: false,\n error:\n mode === 'percentage'\n ? 'Enter a percentage for every participant.'\n : 'Enter a share for every participant.',\n };\n }\n const entered = (parsed as bigint[]).reduce(\n (sum, value) => sum + value,\n BigInt('0'),\n );\n\n if (mode === 'percentage') {\n if (entered === BigInt('10000')) {\n return { ok: true };\n }\n return {\n ok: false,\n error: `Percentages sum to ${formatPercentValue(entered)}% — they must sum to exactly 100%.`,\n };\n }\n\n // exact-amount\n if (totalHundredths === undefined) {\n return { ok: false, error: 'Enter the expense total first.' };\n }\n if (entered === totalHundredths) {\n return { ok: true };\n }\n const delta: bigint = entered - totalHundredths;\n const direction =\n delta < BigInt('0')\n ? `${formatHundredths(-delta)} short of`\n : `${formatHundredths(delta)} over`;\n return {\n ok: false,\n error: `Shares sum to ${formatHundredths(entered)} — ${direction} the ${formatHundredths(totalHundredths)} total.`,\n };\n}\n","import {\n Component,\n DestroyRef,\n Input,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport {\n IonButton,\n IonCard,\n IonCardContent,\n IonCardHeader,\n IonCardTitle,\n IonCheckbox,\n IonInput,\n IonItem,\n IonLabel,\n IonNote,\n IonSegment,\n IonSegmentButton,\n IonSelect,\n IonSelectOption,\n IonSpinner,\n} from '@ionic/angular';\nimport { ErrorLogger, IErrorLogger, IIdAndBrief } from '@sneat/core';\nimport {\n CONTACTUS_SPACE_SERVICE,\n IContactBrief,\n} from '@sneat/extension-contactus-contract';\nimport {\n ICreateSplitusBillV1Request,\n ISplitusBillAllocationV1,\n SPLITUS_BILL_CONTRACT_VERSION,\n SPLITUS_BILL_SERVICE_V1,\n SplitusBillKind,\n SplitusCurrencyCode,\n SplitusUtilityKind,\n} from '@sneat/extension-splitus-contract';\nimport { newRandomId } from '@sneat/random';\nimport { ISpaceContext } from '@sneat/space-models';\nimport { SpaceNavService } from '@sneat/space-services';\nimport { Subscription } from 'rxjs';\nimport { DraftSplitStore } from '../../services/draft-split-store.service';\nimport { positiveExactDecimalValidator } from '../../services/exact-decimal';\nimport {\n SplitMode,\n formatHundredths,\n parseDecimal,\n reconcileShares,\n} from './split-shares';\n\ninterface IParticipantChoice {\n readonly id: string;\n readonly title: string;\n}\n\ntype IShareRow = IParticipantChoice;\n\nfunction extractServerMessage(err: unknown): string | undefined {\n if (typeof err !== 'object' || !err) {\n return typeof err === 'string' && err.trim() ? err.trim() : undefined;\n }\n const e = err as { error?: unknown; message?: unknown };\n if (typeof e.error === 'string' && e.error.trim()) {\n return e.error.trim();\n }\n if (typeof e.error === 'object' && e.error) {\n const message = (e.error as { message?: unknown }).message;\n if (typeof message === 'string' && message.trim()) {\n return message.trim();\n }\n }\n return typeof e.message === 'string' && e.message.trim()\n ? e.message.trim()\n : undefined;\n}\n\nfunction distribute(total: bigint, weights: readonly bigint[]): bigint[] {\n const weightTotal = weights.reduce((sum, value) => sum + value, 0n);\n if (weightTotal <= 0n) {\n return [];\n }\n const amounts = weights.map((weight) => (total * weight) / weightTotal);\n let remainder = total - amounts.reduce((sum, value) => sum + value, 0n);\n for (let index = 0; remainder > 0n; index = (index + 1) % amounts.length) {\n amounts[index] += 1n;\n remainder -= 1n;\n }\n return amounts;\n}\n\n@Component({\n selector: 'sneat-splitus-new-split-form',\n templateUrl: './new-split-form.component.html',\n imports: [\n ReactiveFormsModule,\n IonCard,\n IonCardHeader,\n IonCardTitle,\n IonCardContent,\n IonItem,\n IonLabel,\n IonNote,\n IonSelect,\n IonSelectOption,\n IonInput,\n IonCheckbox,\n IonSegment,\n IonSegmentButton,\n IonButton,\n IonSpinner,\n ],\n})\nexport class NewSplitFormComponent {\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n private readonly splitusService = inject(SPLITUS_BILL_SERVICE_V1);\n private readonly contactusSpaceService = inject(CONTACTUS_SPACE_SERVICE);\n private readonly spaceNavService = inject(SpaceNavService);\n private readonly destroyRef = inject(DestroyRef);\n private readonly draftSplitStore = inject(DraftSplitStore);\n\n protected readonly $resumedFromDraft = signal(false);\n private readonly $billID = signal(newRandomId({ len: 20 }));\n private draftChecked = false;\n\n protected readonly $space = signal<ISpaceContext | undefined>(undefined);\n @Input({ required: true }) public set space(value: ISpaceContext | undefined) {\n this.$space.set(value);\n this.watchContacts(value?.id);\n this.tryResumeDraft();\n }\n\n protected readonly $currentUserID = signal<string | undefined>(undefined);\n @Input() public set currentUserID(value: string | undefined) {\n this.$currentUserID.set(value);\n }\n\n private tryResumeDraft(): void {\n if (this.draftChecked) {\n return;\n }\n this.draftChecked = true;\n const draft = this.draftSplitStore.read();\n if (!draft) {\n return;\n }\n this.$billID.set(draft.billID);\n this.title.setValue(draft.title);\n this.currency.setValue(draft.currencyCode);\n this.amount.setValue(draft.amount);\n this.$resumedFromDraft.set(true);\n }\n\n protected dismissDraftBanner(): void {\n this.$resumedFromDraft.set(false);\n this.draftSplitStore.clear();\n }\n\n private contactsSubscription?: Subscription;\n private watchedSpaceID?: string;\n protected readonly $contacts = signal<\n IIdAndBrief<IContactBrief>[] | undefined\n >(undefined);\n protected readonly $contactsError = signal<string | undefined>(undefined);\n\n private watchContacts(spaceID: string | undefined): void {\n if (spaceID === this.watchedSpaceID) {\n return;\n }\n this.watchedSpaceID = spaceID;\n this.contactsSubscription?.unsubscribe();\n this.$contacts.set(undefined);\n this.$contactsError.set(undefined);\n if (!spaceID) {\n return;\n }\n this.contactsSubscription = this.contactusSpaceService\n .watchContactBriefs(spaceID)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (contacts) => this.$contacts.set(contacts),\n error: (err) => {\n this.$contactsError.set('Failed to load contacts.');\n this.errorLogger.logError(err, 'Failed to load space contacts', {\n show: false,\n });\n },\n });\n }\n\n protected readonly $payer = computed<IParticipantChoice | undefined>(() => {\n const userID = this.$currentUserID();\n const contact = this.$contacts()?.find((item) => item.brief?.userID === userID);\n return contact\n ? {\n id: contact.id,\n title: contact.brief?.title || contact.brief?.shortTitle || 'Your contact',\n }\n : undefined;\n });\n\n protected readonly $participantChoices = computed<\n readonly IParticipantChoice[] | undefined\n >(() => {\n const payerID = this.$payer()?.id;\n return this.$contacts()\n ?.filter((contact) => contact.id !== payerID)\n .map((contact) => ({\n id: contact.id,\n title:\n contact.brief?.title || contact.brief?.shortTitle || 'Unnamed contact',\n }));\n });\n\n protected readonly $selectedContactIDs = signal<readonly string[]>([]);\n\n protected onParticipantToggle(contactID: string, checked: boolean): void {\n this.$selectedContactIDs.update((ids) => {\n const without = ids.filter((id) => id !== contactID);\n return checked ? [...without, contactID] : without;\n });\n }\n\n protected onParticipantChecked(contactID: string, event: Event): void {\n const checked = !!(event as CustomEvent<{ checked?: boolean }>).detail?.checked;\n this.onParticipantToggle(contactID, checked);\n }\n\n protected readonly title = new FormControl('', { nonNullable: true });\n protected readonly currency = new FormControl<SplitusCurrencyCode>('EUR', {\n nonNullable: true,\n });\n protected readonly amount = new FormControl('', {\n nonNullable: true,\n validators: [positiveExactDecimalValidator],\n });\n protected readonly billKind = new FormControl<SplitusBillKind>('general', {\n nonNullable: true,\n });\n protected readonly utilityKind = new FormControl<SplitusUtilityKind>(\n 'electricity',\n { nonNullable: true },\n );\n protected readonly providerName = new FormControl('', { nonNullable: true });\n protected readonly periodStart = new FormControl('', { nonNullable: true });\n protected readonly periodEnd = new FormControl('', { nonNullable: true });\n private readonly $amount = toSignal(this.amount.valueChanges, {\n initialValue: this.amount.value,\n });\n protected readonly $billKind = toSignal(this.billKind.valueChanges, {\n initialValue: this.billKind.value,\n });\n private readonly $periodStart = toSignal(this.periodStart.valueChanges, {\n initialValue: this.periodStart.value,\n });\n private readonly $periodEnd = toSignal(this.periodEnd.valueChanges, {\n initialValue: this.periodEnd.value,\n });\n\n protected readonly currencies: readonly SplitusCurrencyCode[] = [\n 'EUR',\n 'GBP',\n 'USD',\n ];\n protected readonly utilityKinds: readonly {\n value: SplitusUtilityKind;\n title: string;\n }[] = [\n { value: 'electricity', title: 'Electricity' },\n { value: 'gas', title: 'Gas' },\n { value: 'water', title: 'Water' },\n { value: 'internet', title: 'Internet' },\n { value: 'other', title: 'Other' },\n ];\n\n protected readonly $splitMode = signal<SplitMode>('equally');\n\n protected onSplitModeChange(event: Event): void {\n const value = (event as CustomEvent<{ value?: SplitMode }>).detail?.value;\n if (value === 'equally' || value === 'exact-amount' || value === 'percentage') {\n this.$splitMode.set(value);\n }\n }\n\n protected readonly $shareValues = signal<Readonly<Record<string, string>>>({});\n\n protected shareValue(contactID: string): string {\n return this.$shareValues()[contactID] ?? '';\n }\n\n protected onShareInput(contactID: string, event: Event): void {\n const value = (event as CustomEvent<{ value?: string | null }>).detail?.value;\n this.$shareValues.update((values) => ({\n ...values,\n [contactID]: value ?? '',\n }));\n }\n\n protected readonly $shareRows = computed<readonly IShareRow[]>(() => {\n const payer = this.$payer();\n if (!payer) {\n return [];\n }\n const choices = this.$participantChoices() ?? [];\n const selected = this.$selectedContactIDs();\n return [payer, ...choices.filter((choice) => selected.includes(choice.id))];\n });\n\n protected readonly $reconciliation = computed(() =>\n reconcileShares(\n this.$splitMode(),\n parseDecimal(this.$amount()),\n this.$shareRows().map((row) => this.$shareValues()[row.id]),\n ),\n );\n\n private readonly $utilityValid = computed(() => {\n if (this.$billKind() !== 'utility') {\n return true;\n }\n const periodStart = this.$periodStart();\n const periodEnd = this.$periodEnd();\n return !!periodStart && !!periodEnd && periodEnd >= periodStart;\n });\n\n protected readonly $canSubmit = computed(() => {\n const total = parseDecimal(this.$amount());\n return (\n !!this.$currentUserID() &&\n !!this.$payer() &&\n total !== undefined &&\n total > 0n &&\n this.$selectedContactIDs().length > 0 &&\n this.$reconciliation().ok &&\n this.$utilityValid()\n );\n });\n\n protected readonly $submitting = signal(false);\n protected readonly $submitError = signal<string | undefined>(undefined);\n\n private owedAllocations(total: bigint): readonly ISplitusBillAllocationV1[] {\n const rows = this.$shareRows();\n const mode = this.$splitMode();\n let amounts: bigint[];\n if (mode === 'exact-amount') {\n amounts = rows.map(\n (row) => parseDecimal(this.$shareValues()[row.id]) ?? 0n,\n );\n } else if (mode === 'percentage') {\n amounts = distribute(\n total,\n rows.map((row) => parseDecimal(this.$shareValues()[row.id]) ?? 0n),\n );\n } else {\n amounts = distribute(total, rows.map(() => 1n));\n }\n const billID = this.$billID();\n return rows.map((row, index) => ({\n allocationID: `owed-${billID}-${index + 1}`,\n contactID: row.id,\n amount: formatHundredths(amounts[index]),\n }));\n }\n\n protected submit(): void {\n this.amount.markAsTouched();\n if (this.$submitting() || !this.$canSubmit()) {\n return;\n }\n const space = this.$space();\n const userID = this.$currentUserID();\n const payer = this.$payer();\n const total = parseDecimal(this.$amount());\n if (!space?.id || !userID || !payer || total === undefined) {\n return;\n }\n const billID = this.$billID();\n const billKind = this.billKind.value;\n const request: ICreateSplitusBillV1Request = {\n contractVersion: SPLITUS_BILL_CONTRACT_VERSION,\n spaceID: space.id,\n billID,\n recorderUserID: userID,\n title: this.title.value.trim() || undefined,\n billKind,\n currency: this.currency.value,\n actualAmount: formatHundredths(total),\n paidAllocations: [\n {\n allocationID: `paid-${billID}-1`,\n contactID: payer.id,\n amount: formatHundredths(total),\n },\n ],\n owedAllocations: this.owedAllocations(total),\n utility:\n billKind === 'utility'\n ? {\n utilityKind: this.utilityKind.value,\n providerName: this.providerName.value.trim() || undefined,\n period: {\n startDate: this.periodStart.value,\n endDate: this.periodEnd.value,\n },\n }\n : undefined,\n };\n\n this.$submitting.set(true);\n this.$submitError.set(undefined);\n this.splitusService.createBill(request).subscribe({\n next: (response) => {\n this.draftSplitStore.clear();\n this.spaceNavService.navigateForwardToSpacePage(\n space,\n `bill/${response.bill.billID}`,\n { replaceUrl: true },\n );\n },\n error: (err) => {\n this.$submitting.set(false);\n const serverMessage = extractServerMessage(err);\n this.$submitError.set(\n serverMessage\n ? `Failed to create bill: ${serverMessage}`\n : 'Failed to create bill. Please try again.',\n );\n this.errorLogger.logError(err, 'Failed to create Splitus bill', {\n show: false,\n });\n },\n });\n }\n}\n","@if ($resumedFromDraft()) {\n <ion-card color=\"light\">\n <ion-card-content style=\"display: flex; align-items: center; justify-content: space-between; gap: 8px\">\n <ion-note color=\"medium\">Picking up your bill from earlier — edit anything below.</ion-note>\n <ion-button fill=\"clear\" size=\"small\" (click)=\"dismissDraftBanner()\">Dismiss</ion-button>\n </ion-card-content>\n </ion-card>\n}\n\n<ion-card>\n <ion-card-header>\n <ion-card-title>Bill</ion-card-title>\n </ion-card-header>\n <ion-item>\n <ion-input\n label=\"Title (optional)\"\n labelPlacement=\"stacked\"\n [formControl]=\"title\"\n placeholder=\"e.g. August electricity\"\n autocapitalize=\"sentences\"\n />\n </ion-item>\n <ion-item>\n <ion-select data-testid=\"bill-kind\" label=\"Bill type\" interface=\"popover\" [formControl]=\"billKind\">\n <ion-select-option value=\"general\">General expense</ion-select-option>\n <ion-select-option value=\"utility\">Utility bill</ion-select-option>\n </ion-select>\n </ion-item>\n <ion-item>\n <ion-select label=\"Currency\" interface=\"popover\" [formControl]=\"currency\">\n @for (currencyCode of currencies; track currencyCode) {\n <ion-select-option [value]=\"currencyCode\">{{ currencyCode }}</ion-select-option>\n }\n </ion-select>\n </ion-item>\n <ion-item>\n <ion-input\n label=\"Amount paid\"\n labelPlacement=\"stacked\"\n type=\"text\"\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\">Enter an amount greater than zero.</ion-note>\n }\n</ion-card>\n\n@if ($billKind() === 'utility') {\n <ion-card>\n <ion-card-header>\n <ion-card-title>Utility details</ion-card-title>\n </ion-card-header>\n <ion-item>\n <ion-select data-testid=\"utility-kind\" label=\"Utility\" interface=\"popover\" [formControl]=\"utilityKind\">\n @for (kind of utilityKinds; track kind.value) {\n <ion-select-option [value]=\"kind.value\">{{ kind.title }}</ion-select-option>\n }\n </ion-select>\n </ion-item>\n <ion-item>\n <ion-input\n data-testid=\"provider-name\"\n label=\"Provider (optional)\"\n labelPlacement=\"stacked\"\n [formControl]=\"providerName\"\n placeholder=\"e.g. Electric Ireland\"\n />\n </ion-item>\n <ion-item>\n <ion-input data-testid=\"period-start\" label=\"Period starts\" labelPlacement=\"stacked\" type=\"date\" [formControl]=\"periodStart\" />\n </ion-item>\n <ion-item>\n <ion-input data-testid=\"period-end\" label=\"Period ends\" labelPlacement=\"stacked\" type=\"date\" [formControl]=\"periodEnd\" />\n </ion-item>\n @if (periodStart.value && periodEnd.value && periodEnd.value < periodStart.value) {\n <ion-note color=\"danger\" class=\"ion-padding-start\">The period end must be on or after its start.</ion-note>\n }\n <ion-item lines=\"none\">\n <ion-note color=\"medium\" class=\"ion-text-wrap\">\n This bill is recorded as a standalone occurrence. Recurring bill schedules will use Calendarius when that flow is available.\n </ion-note>\n </ion-item>\n </ion-card>\n}\n\n<ion-card>\n <ion-card-header>\n <ion-card-title>Housemates</ion-card-title>\n </ion-card-header>\n @if ($payer(); as payer) {\n <ion-item>\n <ion-label>{{ payer.title }} (you paid)</ion-label>\n <ion-note slot=\"end\" color=\"medium\">always included</ion-note>\n </ion-item>\n } @else if ($contacts()) {\n <ion-item lines=\"none\">\n <ion-label color=\"danger\" class=\"ion-text-wrap\">\n Your Contactus person is missing from this space. Add or link your person before recording a bill.\n </ion-label>\n </ion-item>\n }\n @for (contact of $participantChoices() ?? []; track contact.id) {\n <ion-item>\n <ion-checkbox\n labelPlacement=\"end\"\n justify=\"start\"\n [checked]=\"$selectedContactIDs().includes(contact.id)\"\n (ionChange)=\"onParticipantChecked(contact.id, $event)\"\n >{{ contact.title }}</ion-checkbox>\n </ion-item>\n } @empty {\n <ion-item lines=\"none\">\n @if ($contactsError()) {\n <ion-label color=\"danger\">{{ $contactsError() }}</ion-label>\n } @else if (!$contacts()) {\n <ion-label color=\"medium\">Loading real space contacts…</ion-label>\n } @else {\n <ion-label color=\"medium\" class=\"ion-text-wrap\">\n No other contacts yet. Add your housemates to this space first.\n </ion-label>\n }\n </ion-item>\n }\n</ion-card>\n\n<ion-card>\n <ion-card-header>\n <ion-card-title>Who owes what</ion-card-title>\n </ion-card-header>\n <ion-segment [value]=\"$splitMode()\" (ionChange)=\"onSplitModeChange($event)\">\n <ion-segment-button value=\"equally\"><ion-label>Equally</ion-label></ion-segment-button>\n <ion-segment-button value=\"exact-amount\"><ion-label>Amounts</ion-label></ion-segment-button>\n <ion-segment-button value=\"percentage\"><ion-label>Percentages</ion-label></ion-segment-button>\n </ion-segment>\n\n @if ($splitMode() !== 'equally') {\n @for (row of $shareRows(); track row.id) {\n <ion-item>\n <ion-input\n [label]=\"row.title\"\n labelPlacement=\"stacked\"\n type=\"text\"\n inputmode=\"decimal\"\n [placeholder]=\"$splitMode() === 'percentage' ? '0' : '0.00'\"\n [value]=\"shareValue(row.id)\"\n (ionInput)=\"onShareInput(row.id, $event)\"\n />\n @if ($splitMode() === 'percentage') { <ion-note slot=\"end\">%</ion-note> }\n </ion-item>\n }\n @if ($reconciliation(); as reconciliation) {\n <ion-note [color]=\"reconciliation.ok ? 'success' : 'danger'\" class=\"ion-padding-start\">\n {{ reconciliation.ok ? 'Shares reconcile to the total. ✓' : reconciliation.error }}\n </ion-note>\n }\n } @else {\n <ion-item lines=\"none\">\n <ion-note color=\"medium\">The total is divided exactly between you and the selected housemates.</ion-note>\n </ion-item>\n }\n</ion-card>\n\n<div class=\"ion-padding\">\n <ion-button expand=\"block\" color=\"primary\" [disabled]=\"$submitting() || !$canSubmit()\" (click)=\"submit()\">\n @if ($submitting()) {\n <ion-spinner name=\"dots\" slot=\"start\" />\n Recording…\n } @else {\n Record bill\n }\n </ion-button>\n @if ($submitError()) { <ion-note color=\"danger\">{{ $submitError() }}</ion-note> }\n</div>\n","import { Route } from '@angular/router';\n\n// Splitus bill routes, mounted under space/:spaceType/:spaceID.\nexport const spacePagesRoutes: Route[] = [\n {\n path: 'bills',\n data: { title: 'Bills' },\n loadComponent: () =>\n import('./splitus-home/splitus-home-page.component').then(\n (m) => m.SplitusHomePageComponent,\n ),\n },\n {\n path: 'new-bill',\n data: { title: 'New bill' },\n loadComponent: () =>\n import('./new-split/new-split-page.component').then(\n (m) => m.NewSplitPageComponent,\n ),\n },\n {\n path: 'bill/:billID',\n data: { title: 'Bill' },\n loadComponent: () =>\n import('./split-details/split-details-page.component').then(\n (m) => m.SplitDetailsPageComponent,\n ),\n },\n { path: 'splits', pathMatch: 'full', redirectTo: 'bills' },\n { path: 'new-split', pathMatch: 'full', redirectTo: 'new-bill' },\n { path: 'split/:billID', redirectTo: 'bill/:billID' },\n];\n","import { Route } from '@angular/router';\n\n// Unguarded entry point for a first-time visitor to start a bill before\n// signing in. Deliberately NOT space-scoped — kept separate from\n// spacePagesRoutes, which mounts under space/:spaceType/:spaceID.\nexport const startBillRoute: Route = {\n path: 'start-bill',\n data: { title: 'Split a bill' },\n loadComponent: () =>\n import('./start-split-page.component').then(\n (m) => m.StartSplitPageComponent,\n ),\n};\n\nexport const legacyStartSplitRoute: Route = {\n path: 'start-split',\n pathMatch: 'full',\n redirectTo: 'start-bill',\n};\n","import { InjectionToken } from '@angular/core';\nimport { ISplitusDebtusSettlementTargetV1 } from '@sneat/extension-splitus-contract';\n\nexport interface ISplitusSettlementTarget\n extends ISplitusDebtusSettlementTargetV1 {\n readonly spaceType: string;\n}\n\n/** App-owned cross-product navigation boundary for settling a split share. */\nexport type SplitusSettlementUrlBuilder = (\n target: ISplitusSettlementTarget,\n) => string | undefined;\n\nexport const SPLITUS_SETTLEMENT_URL_BUILDER =\n new InjectionToken<SplitusSettlementUrlBuilder>(\n 'SPLITUS_SETTLEMENT_URL_BUILDER',\n );\n","import { InjectionToken } from '@angular/core';\n\n/** URL opened after an unauthenticated bill draft is saved. */\nexport const SPLITUS_START_CONTINUATION_URL = new InjectionToken<string>(\n 'SPLITUS_START_CONTINUATION_URL',\n);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
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, IonTitle, IonContent, IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonList, IonItem, IonLabel, IonNote, IonBadge, IonButton, IonSpinner } from '@ionic/angular';
5
+ import { CONTACTUS_SPACE_SERVICE } from '@sneat/extension-contactus-contract';
6
+ import { SPLITUS_BILL_SERVICE_V1, SPLITUS_BILL_CONTRACT_VERSION } from '@sneat/extension-splitus-contract';
7
+ import { SpacePageBaseComponent, SpaceComponentBaseParams } from '@sneat/space-components';
8
+ import { SpaceServiceModule } from '@sneat/space-services';
9
+ import { ClassName } from '@sneat/ui';
10
+ import { switchMap, combineLatest } from 'rxjs';
11
+ import { b as SPLITUS_SETTLEMENT_URL_BUILDER } from './sneat-extension-splitus-ui-sneat-extension-splitus-ui-CX1eiAAR.mjs';
12
+ import { s as statusColor } from './sneat-extension-splitus-ui-split-status-view-CRt2FKg1.mjs';
13
+
14
+ class SplitDetailsPageComponent extends SpacePageBaseComponent {
15
+ splitusService = inject(SPLITUS_BILL_SERVICE_V1);
16
+ contactusService = inject(CONTACTUS_SPACE_SERVICE);
17
+ settlementUrlBuilder = inject(SPLITUS_SETTLEMENT_URL_BUILDER, { optional: true });
18
+ $loading = signal(true, /* @ts-ignore */
19
+ ...(ngDevMode ? [{ debugName: "$loading" }] : /* istanbul ignore next */ []));
20
+ $error = signal(undefined, /* @ts-ignore */
21
+ ...(ngDevMode ? [{ debugName: "$error" }] : /* istanbul ignore next */ []));
22
+ $bill = signal(undefined, /* @ts-ignore */
23
+ ...(ngDevMode ? [{ debugName: "$bill" }] : /* istanbul ignore next */ []));
24
+ $contacts = signal([], /* @ts-ignore */
25
+ ...(ngDevMode ? [{ debugName: "$contacts" }] : /* istanbul ignore next */ []));
26
+ statusColor = statusColor;
27
+ constructor() {
28
+ super();
29
+ this.$defaultBackUrlSpacePath.set('bills');
30
+ this.spaceIDChanged$
31
+ .pipe(switchMap((spaceID) => this.contactusService.watchContactBriefs(spaceID ?? '')), takeUntilDestroyed())
32
+ .subscribe({
33
+ next: (contacts) => this.$contacts.set(contacts),
34
+ error: (error) => this.errorLogger.logError(error, 'Failed to load Contactus labels', {
35
+ show: false,
36
+ }),
37
+ });
38
+ combineLatest([this.spaceIDChanged$, this.route.paramMap])
39
+ .pipe(switchMap(([spaceID, params]) => {
40
+ const billID = params.get('billID') ?? '';
41
+ this.$loading.set(true);
42
+ this.$error.set(undefined);
43
+ return this.splitusService.getBill({
44
+ contractVersion: SPLITUS_BILL_CONTRACT_VERSION,
45
+ spaceID: spaceID ?? '',
46
+ billID,
47
+ });
48
+ }), takeUntilDestroyed())
49
+ .subscribe({
50
+ next: (response) => {
51
+ this.$bill.set(response.bill);
52
+ this.$loading.set(false);
53
+ },
54
+ error: (error) => {
55
+ this.$error.set('Failed to load bill');
56
+ this.$loading.set(false);
57
+ this.errorLogger.logError(error, 'Failed to load Splitus bill', {
58
+ show: false,
59
+ });
60
+ },
61
+ });
62
+ }
63
+ contactTitle(contactID) {
64
+ const contact = this.$contacts().find((item) => item.id === contactID);
65
+ return contact?.brief?.title || contact?.brief?.shortTitle || 'Unnamed contact';
66
+ }
67
+ settleUrl(target) {
68
+ const spaceType = this.$spaceType();
69
+ return spaceType && this.settlementUrlBuilder?.({ ...target, spaceType });
70
+ }
71
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SplitDetailsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
72
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SplitDetailsPageComponent, isStandalone: true, selector: "sneat-splitus-split-details-page", providers: [
73
+ { provide: ClassName, useValue: 'SplitDetailsPageComponent' },
74
+ SpaceComponentBaseParams,
75
+ ], usesInheritance: true, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\"><ion-back-button [defaultHref]=\"$defaultBackUrl()\" /></ion-buttons>\n <ion-title>{{ $bill()?.title || 'Bill' }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n @if ($loading()) {\n <ion-card><ion-card-content><ion-spinner name=\"lines\" class=\"ion-margin-end\" />Loading\u2026</ion-card-content></ion-card>\n } @else if ($error()) {\n <ion-card><ion-card-content><ion-note color=\"danger\">{{ $error() }}</ion-note></ion-card-content></ion-card>\n } @else if ($bill(); as bill) {\n <ion-card>\n <ion-card-header><ion-card-title>{{ bill.title || 'Bill' }}</ion-card-title></ion-card-header>\n <ion-card-content>\n <p><strong>{{ bill.actualAmount }} {{ bill.currency }}</strong></p>\n <ion-badge [color]=\"statusColor(bill.debtus?.status || bill.posting.status)\">\n {{ bill.debtus?.status || bill.posting.status }}\n </ion-badge>\n @if (bill.utility; as utility) {\n <p>\n {{ utility.utilityKind }}\n @if (utility.providerName) { \u00B7 {{ utility.providerName }} }\n </p>\n <p>{{ utility.period.startDate }} to {{ utility.period.endDate }}</p>\n }\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-card-header><ion-card-title>Paid by</ion-card-title></ion-card-header>\n <ion-list>\n @for (allocation of bill.paidAllocations; track allocation.allocationID) {\n <ion-item>\n <ion-label><h2>{{ contactTitle(allocation.contactID) }}</h2></ion-label>\n <ion-note slot=\"end\">{{ allocation.amount }} {{ bill.currency }}</ion-note>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n <ion-card>\n <ion-card-header><ion-card-title>Shared by</ion-card-title></ion-card-header>\n <ion-list>\n @for (allocation of bill.owedAllocations; track allocation.allocationID) {\n <ion-item>\n <ion-label><h2>{{ contactTitle(allocation.contactID) }}</h2></ion-label>\n <ion-note slot=\"end\">{{ allocation.amount }} {{ bill.currency }}</ion-note>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n @if (bill.debtus; as debtus) {\n <ion-card>\n <ion-card-header><ion-card-title>Settlement</ion-card-title></ion-card-header>\n <ion-card-content>\n <p>Debtus status: {{ debtus.status }}</p>\n @if (debtus.status !== 'settled' && settleUrl(debtus.settlementTarget); as url) {\n <ion-button [href]=\"url\" target=\"_blank\" rel=\"noopener\">Settle in Debtus</ion-button>\n }\n </ion-card-content>\n <ion-list>\n @for (obligation of debtus.obligations; track obligation.lineID) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <h2>{{ contactTitle(obligation.debtorContactID) }} owes {{ contactTitle(obligation.creditorContactID) }}</h2>\n <p>{{ obligation.outstandingAmount }} {{ obligation.currency }} outstanding</p>\n </ion-label>\n <ion-badge slot=\"end\" [color]=\"statusColor(obligation.status)\">{{ obligation.status }}</ion-badge>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n } @else {\n <ion-card><ion-card-content><ion-note color=\"medium\">Debtus is still applying this bill. Reload to see settlement status.</ion-note></ion-card-content></ion-card>\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: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "ngmodule", type: SpaceServiceModule }] });
76
+ }
77
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SplitDetailsPageComponent, decorators: [{
78
+ type: Component,
79
+ args: [{ selector: 'sneat-splitus-split-details-page', imports: [
80
+ IonHeader,
81
+ IonToolbar,
82
+ IonButtons,
83
+ IonBackButton,
84
+ IonTitle,
85
+ IonContent,
86
+ IonCard,
87
+ IonCardHeader,
88
+ IonCardTitle,
89
+ IonCardContent,
90
+ IonList,
91
+ IonItem,
92
+ IonLabel,
93
+ IonNote,
94
+ IonBadge,
95
+ IonButton,
96
+ IonSpinner,
97
+ SpaceServiceModule,
98
+ ], providers: [
99
+ { provide: ClassName, useValue: 'SplitDetailsPageComponent' },
100
+ SpaceComponentBaseParams,
101
+ ], template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\"><ion-back-button [defaultHref]=\"$defaultBackUrl()\" /></ion-buttons>\n <ion-title>{{ $bill()?.title || 'Bill' }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n @if ($loading()) {\n <ion-card><ion-card-content><ion-spinner name=\"lines\" class=\"ion-margin-end\" />Loading\u2026</ion-card-content></ion-card>\n } @else if ($error()) {\n <ion-card><ion-card-content><ion-note color=\"danger\">{{ $error() }}</ion-note></ion-card-content></ion-card>\n } @else if ($bill(); as bill) {\n <ion-card>\n <ion-card-header><ion-card-title>{{ bill.title || 'Bill' }}</ion-card-title></ion-card-header>\n <ion-card-content>\n <p><strong>{{ bill.actualAmount }} {{ bill.currency }}</strong></p>\n <ion-badge [color]=\"statusColor(bill.debtus?.status || bill.posting.status)\">\n {{ bill.debtus?.status || bill.posting.status }}\n </ion-badge>\n @if (bill.utility; as utility) {\n <p>\n {{ utility.utilityKind }}\n @if (utility.providerName) { \u00B7 {{ utility.providerName }} }\n </p>\n <p>{{ utility.period.startDate }} to {{ utility.period.endDate }}</p>\n }\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-card-header><ion-card-title>Paid by</ion-card-title></ion-card-header>\n <ion-list>\n @for (allocation of bill.paidAllocations; track allocation.allocationID) {\n <ion-item>\n <ion-label><h2>{{ contactTitle(allocation.contactID) }}</h2></ion-label>\n <ion-note slot=\"end\">{{ allocation.amount }} {{ bill.currency }}</ion-note>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n <ion-card>\n <ion-card-header><ion-card-title>Shared by</ion-card-title></ion-card-header>\n <ion-list>\n @for (allocation of bill.owedAllocations; track allocation.allocationID) {\n <ion-item>\n <ion-label><h2>{{ contactTitle(allocation.contactID) }}</h2></ion-label>\n <ion-note slot=\"end\">{{ allocation.amount }} {{ bill.currency }}</ion-note>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n @if (bill.debtus; as debtus) {\n <ion-card>\n <ion-card-header><ion-card-title>Settlement</ion-card-title></ion-card-header>\n <ion-card-content>\n <p>Debtus status: {{ debtus.status }}</p>\n @if (debtus.status !== 'settled' && settleUrl(debtus.settlementTarget); as url) {\n <ion-button [href]=\"url\" target=\"_blank\" rel=\"noopener\">Settle in Debtus</ion-button>\n }\n </ion-card-content>\n <ion-list>\n @for (obligation of debtus.obligations; track obligation.lineID) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <h2>{{ contactTitle(obligation.debtorContactID) }} owes {{ contactTitle(obligation.creditorContactID) }}</h2>\n <p>{{ obligation.outstandingAmount }} {{ obligation.currency }} outstanding</p>\n </ion-label>\n <ion-badge slot=\"end\" [color]=\"statusColor(obligation.status)\">{{ obligation.status }}</ion-badge>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n } @else {\n <ion-card><ion-card-content><ion-note color=\"medium\">Debtus is still applying this bill. Reload to see settlement status.</ion-note></ion-card-content></ion-card>\n }\n }\n</ion-content>\n" }]
102
+ }], ctorParameters: () => [] });
103
+
104
+ export { SplitDetailsPageComponent };
105
+ //# sourceMappingURL=sneat-extension-splitus-ui-split-details-page.component-fkoVkmXD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sneat-extension-splitus-ui-split-details-page.component-fkoVkmXD.mjs","sources":["../../../../../../libs/extensions/splitus/ui/src/lib/pages/split-details/split-details-page.component.ts","../../../../../../libs/extensions/splitus/ui/src/lib/pages/split-details/split-details-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 IonItem,\n IonLabel,\n IonList,\n IonNote,\n IonSpinner,\n IonTitle,\n IonToolbar,\n} from '@ionic/angular';\nimport { IIdAndBrief } from '@sneat/core';\nimport {\n CONTACTUS_SPACE_SERVICE,\n IContactBrief,\n} from '@sneat/extension-contactus-contract';\nimport {\n ISplitusBillV1,\n ISplitusDebtusSettlementTargetV1,\n SPLITUS_BILL_CONTRACT_VERSION,\n SPLITUS_BILL_SERVICE_V1,\n} from '@sneat/extension-splitus-contract';\nimport {\n SpaceComponentBaseParams,\n SpacePageBaseComponent,\n} from '@sneat/space-components';\nimport { SpaceServiceModule } from '@sneat/space-services';\nimport { ClassName } from '@sneat/ui';\nimport { combineLatest, switchMap } from 'rxjs';\nimport { SPLITUS_SETTLEMENT_URL_BUILDER } from '../../services';\nimport { statusColor } from '../split-status-view';\n\n@Component({\n selector: 'sneat-splitus-split-details-page',\n templateUrl: './split-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 IonSpinner,\n SpaceServiceModule,\n ],\n providers: [\n { provide: ClassName, useValue: 'SplitDetailsPageComponent' },\n SpaceComponentBaseParams,\n ],\n})\nexport class SplitDetailsPageComponent extends SpacePageBaseComponent {\n private readonly splitusService = inject(SPLITUS_BILL_SERVICE_V1);\n private readonly contactusService = inject(CONTACTUS_SPACE_SERVICE);\n private readonly settlementUrlBuilder = inject(\n SPLITUS_SETTLEMENT_URL_BUILDER,\n { optional: true },\n );\n\n protected readonly $loading = signal(true);\n protected readonly $error = signal<string | undefined>(undefined);\n protected readonly $bill = signal<ISplitusBillV1 | undefined>(undefined);\n private readonly $contacts = signal<readonly IIdAndBrief<IContactBrief>[]>([]);\n protected readonly statusColor = statusColor;\n\n constructor() {\n super();\n this.$defaultBackUrlSpacePath.set('bills');\n\n this.spaceIDChanged$\n .pipe(\n switchMap((spaceID) => this.contactusService.watchContactBriefs(spaceID ?? '')),\n takeUntilDestroyed(),\n )\n .subscribe({\n next: (contacts) => this.$contacts.set(contacts),\n error: (error) =>\n this.errorLogger.logError(error, 'Failed to load Contactus labels', {\n show: false,\n }),\n });\n\n combineLatest([this.spaceIDChanged$, this.route.paramMap])\n .pipe(\n switchMap(([spaceID, params]) => {\n const billID = params.get('billID') ?? '';\n this.$loading.set(true);\n this.$error.set(undefined);\n return this.splitusService.getBill({\n contractVersion: SPLITUS_BILL_CONTRACT_VERSION,\n spaceID: spaceID ?? '',\n billID,\n });\n }),\n takeUntilDestroyed(),\n )\n .subscribe({\n next: (response) => {\n this.$bill.set(response.bill);\n this.$loading.set(false);\n },\n error: (error) => {\n this.$error.set('Failed to load bill');\n this.$loading.set(false);\n this.errorLogger.logError(error, 'Failed to load Splitus bill', {\n show: false,\n });\n },\n });\n }\n\n protected contactTitle(contactID: string): string {\n const contact = this.$contacts().find((item) => item.id === contactID);\n return contact?.brief?.title || contact?.brief?.shortTitle || 'Unnamed contact';\n }\n\n protected settleUrl(\n target: ISplitusDebtusSettlementTargetV1,\n ): string | undefined {\n const spaceType = this.$spaceType();\n return spaceType && this.settlementUrlBuilder?.({ ...target, spaceType });\n }\n}\n","<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\"><ion-back-button [defaultHref]=\"$defaultBackUrl()\" /></ion-buttons>\n <ion-title>{{ $bill()?.title || 'Bill' }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n @if ($loading()) {\n <ion-card><ion-card-content><ion-spinner name=\"lines\" class=\"ion-margin-end\" />Loading…</ion-card-content></ion-card>\n } @else if ($error()) {\n <ion-card><ion-card-content><ion-note color=\"danger\">{{ $error() }}</ion-note></ion-card-content></ion-card>\n } @else if ($bill(); as bill) {\n <ion-card>\n <ion-card-header><ion-card-title>{{ bill.title || 'Bill' }}</ion-card-title></ion-card-header>\n <ion-card-content>\n <p><strong>{{ bill.actualAmount }} {{ bill.currency }}</strong></p>\n <ion-badge [color]=\"statusColor(bill.debtus?.status || bill.posting.status)\">\n {{ bill.debtus?.status || bill.posting.status }}\n </ion-badge>\n @if (bill.utility; as utility) {\n <p>\n {{ utility.utilityKind }}\n @if (utility.providerName) { · {{ utility.providerName }} }\n </p>\n <p>{{ utility.period.startDate }} to {{ utility.period.endDate }}</p>\n }\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-card-header><ion-card-title>Paid by</ion-card-title></ion-card-header>\n <ion-list>\n @for (allocation of bill.paidAllocations; track allocation.allocationID) {\n <ion-item>\n <ion-label><h2>{{ contactTitle(allocation.contactID) }}</h2></ion-label>\n <ion-note slot=\"end\">{{ allocation.amount }} {{ bill.currency }}</ion-note>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n <ion-card>\n <ion-card-header><ion-card-title>Shared by</ion-card-title></ion-card-header>\n <ion-list>\n @for (allocation of bill.owedAllocations; track allocation.allocationID) {\n <ion-item>\n <ion-label><h2>{{ contactTitle(allocation.contactID) }}</h2></ion-label>\n <ion-note slot=\"end\">{{ allocation.amount }} {{ bill.currency }}</ion-note>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n\n @if (bill.debtus; as debtus) {\n <ion-card>\n <ion-card-header><ion-card-title>Settlement</ion-card-title></ion-card-header>\n <ion-card-content>\n <p>Debtus status: {{ debtus.status }}</p>\n @if (debtus.status !== 'settled' && settleUrl(debtus.settlementTarget); as url) {\n <ion-button [href]=\"url\" target=\"_blank\" rel=\"noopener\">Settle in Debtus</ion-button>\n }\n </ion-card-content>\n <ion-list>\n @for (obligation of debtus.obligations; track obligation.lineID) {\n <ion-item>\n <ion-label class=\"ion-text-wrap\">\n <h2>{{ contactTitle(obligation.debtorContactID) }} owes {{ contactTitle(obligation.creditorContactID) }}</h2>\n <p>{{ obligation.outstandingAmount }} {{ obligation.currency }} outstanding</p>\n </ion-label>\n <ion-badge slot=\"end\" [color]=\"statusColor(obligation.status)\">{{ obligation.status }}</ion-badge>\n </ion-item>\n }\n </ion-list>\n </ion-card>\n } @else {\n <ion-card><ion-card-content><ion-note color=\"medium\">Debtus is still applying this bill. Reload to see settlement status.</ion-note></ion-card-content></ion-card>\n }\n }\n</ion-content>\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /** Badge color for server-owned posting and settlement states. */
2
+ function statusColor(status) {
3
+ return status === 'settled' || status === 'applied' ? 'success' : 'warning';
4
+ }
5
+
6
+ export { statusColor as s };
7
+ //# sourceMappingURL=sneat-extension-splitus-ui-split-status-view-CRt2FKg1.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sneat-extension-splitus-ui-split-status-view-CRt2FKg1.mjs","sources":["../../../../../../libs/extensions/splitus/ui/src/lib/pages/split-status-view.ts"],"sourcesContent":["/** Badge color for server-owned posting and settlement states. */\nexport function statusColor(status: string): 'success' | 'warning' {\n return status === 'settled' || status === 'applied' ? 'success' : 'warning';\n}\n"],"names":[],"mappings":"AAAA;AACM,SAAU,WAAW,CAAC,MAAc,EAAA;AACxC,IAAA,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,GAAG,SAAS,GAAG,SAAS;AAC7E;;;;"}
@@ -0,0 +1,100 @@
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, IonCardContent, IonButton, IonIcon, IonList, IonItem, IonLabel, IonNote, IonBadge, IonSpinner } from '@ionic/angular';
5
+ import { SPLITUS_BILL_SERVICE_V1, SPLITUS_BILL_CONTRACT_VERSION } from '@sneat/extension-splitus-contract';
6
+ import { SpacePageBaseComponent, SpacePageTitleComponent, SpaceComponentBaseParams } from '@sneat/space-components';
7
+ import { SpaceServiceModule } from '@sneat/space-services';
8
+ import { ClassName } from '@sneat/ui';
9
+ import { switchMap, EMPTY } from 'rxjs';
10
+ import { D as DraftSplitStore } from './sneat-extension-splitus-ui-sneat-extension-splitus-ui-CX1eiAAR.mjs';
11
+ import { s as statusColor } from './sneat-extension-splitus-ui-split-status-view-CRt2FKg1.mjs';
12
+
13
+ // Bills home for the current Space. Every row comes from the Splitus backend;
14
+ // an empty response is rendered as an honest empty state.
15
+ class SplitusHomePageComponent extends SpacePageBaseComponent {
16
+ splitusService = inject(SPLITUS_BILL_SERVICE_V1);
17
+ draftSplitStore = inject(DraftSplitStore);
18
+ $loading = signal(true, /* @ts-ignore */
19
+ ...(ngDevMode ? [{ debugName: "$loading" }] : /* istanbul ignore next */ []));
20
+ $error = signal(undefined, /* @ts-ignore */
21
+ ...(ngDevMode ? [{ debugName: "$error" }] : /* istanbul ignore next */ []));
22
+ $bills = signal([], /* @ts-ignore */
23
+ ...(ngDevMode ? [{ debugName: "$bills" }] : /* istanbul ignore next */ []));
24
+ statusColor = statusColor;
25
+ constructor() {
26
+ super();
27
+ this.spaceIDChanged$
28
+ .pipe(switchMap((spaceID) => {
29
+ if (spaceID && this.draftSplitStore.hasPending()) {
30
+ // A bill was started before sign-in (see StartSplitPageComponent)
31
+ // — skip the bills list and go straight to finishing it.
32
+ this.spaceNav.navigateForwardToSpacePage(this.space, 'new-bill');
33
+ return EMPTY;
34
+ }
35
+ this.$loading.set(true);
36
+ this.$error.set(undefined);
37
+ return this.splitusService.listBills({
38
+ contractVersion: SPLITUS_BILL_CONTRACT_VERSION,
39
+ spaceID: spaceID ?? '',
40
+ pageSize: 50,
41
+ });
42
+ }), takeUntilDestroyed())
43
+ .subscribe({
44
+ next: (response) => {
45
+ this.$bills.set(response.items);
46
+ this.$loading.set(false);
47
+ },
48
+ error: (err) => {
49
+ this.$error.set('Failed to load bills');
50
+ this.$loading.set(false);
51
+ this.errorLogger.logError(err, 'Failed to load Splitus bills');
52
+ },
53
+ });
54
+ }
55
+ goNewBill() {
56
+ this.spaceNav.navigateForwardToSpacePage(this.space, 'new-bill');
57
+ }
58
+ openBill(billID) {
59
+ this.spaceNav.navigateForwardToSpacePage(this.space, `bill/${billID}`);
60
+ }
61
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SplitusHomePageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
62
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SplitusHomePageComponent, isStandalone: true, selector: "sneat-splitus-home-page", providers: [
63
+ {
64
+ provide: ClassName,
65
+ useValue: 'SplitusHomePageComponent',
66
+ },
67
+ SpaceComponentBaseParams,
68
+ ], 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\" should leave the space and\n return to the app home (the spaces list), not to the space's own root\n (which redirects straight back here). -->\n <ion-back-button defaultHref=\"/\" />\n </ion-buttons>\n <sneat-space-page-title\n [space]=\"space\"\n generalTitle=\"Bills\"\n icon=\"\uD83D\uDCB8\"\n [titlesBySpaceType]=\"{\n personal: 'Personal bills',\n family: 'Household bills',\n group: 'Shared bills',\n }\"\n />\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <ion-card>\n <ion-card-content>\n <ion-button expand=\"block\" (click)=\"goNewBill()\">\n <ion-icon name=\"add\" slot=\"start\" />\n New bill\n </ion-button>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n @if ($loading()) {\n <ion-card-content>\n <ion-spinner name=\"dots\" />\n </ion-card-content>\n } @else if ($error()) {\n <ion-card-content>\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </ion-card-content>\n } @else if ($bills().length) {\n <ion-list>\n @for (bill of $bills(); track bill.billID) {\n <ion-item button (click)=\"openBill(bill.billID)\">\n <ion-label>\n <h2>{{ bill.title || (bill.utilityKind ? bill.utilityKind + ' bill' : 'Bill') }}</h2>\n <p>\n {{ bill.actualAmount }} {{ bill.currency }}\n @if (bill.period) { \u00B7 {{ bill.period.startDate }} to {{ bill.period.endDate }} }\n </p>\n </ion-label>\n <ion-badge slot=\"end\" [color]=\"statusColor(bill.debtusSettlementStatus || bill.postingStatus)\">{{\n bill.debtusSettlementStatus || bill.postingStatus\n }}</ion-badge>\n </ion-item>\n }\n </ion-list>\n } @else {\n <ion-card-content class=\"ion-text-center\">\n <ion-note color=\"medium\">No bills recorded yet.</ion-note>\n <div class=\"ion-margin-top\">\n <ion-button (click)=\"goNewBill()\">\n <ion-icon name=\"add\" slot=\"start\" />\n Record your first bill\n </ion-button>\n </div>\n </ion-card-content>\n }\n </ion-card>\n</ion-content>\n", dependencies: [{ 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: IonCardContent, selector: "ion-card-content", inputs: ["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: 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: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] });
69
+ }
70
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SplitusHomePageComponent, decorators: [{
71
+ type: Component,
72
+ args: [{ selector: 'sneat-splitus-home-page', imports: [
73
+ SpacePageTitleComponent,
74
+ SpaceServiceModule,
75
+ IonHeader,
76
+ IonToolbar,
77
+ IonButtons,
78
+ IonBackButton,
79
+ IonContent,
80
+ IonCard,
81
+ IonCardContent,
82
+ IonButton,
83
+ IonIcon,
84
+ IonList,
85
+ IonItem,
86
+ IonLabel,
87
+ IonNote,
88
+ IonBadge,
89
+ IonSpinner,
90
+ ], providers: [
91
+ {
92
+ provide: ClassName,
93
+ useValue: 'SplitusHomePageComponent',
94
+ },
95
+ SpaceComponentBaseParams,
96
+ ], 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\" should leave the space and\n return to the app home (the spaces list), not to the space's own root\n (which redirects straight back here). -->\n <ion-back-button defaultHref=\"/\" />\n </ion-buttons>\n <sneat-space-page-title\n [space]=\"space\"\n generalTitle=\"Bills\"\n icon=\"\uD83D\uDCB8\"\n [titlesBySpaceType]=\"{\n personal: 'Personal bills',\n family: 'Household bills',\n group: 'Shared bills',\n }\"\n />\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <ion-card>\n <ion-card-content>\n <ion-button expand=\"block\" (click)=\"goNewBill()\">\n <ion-icon name=\"add\" slot=\"start\" />\n New bill\n </ion-button>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n @if ($loading()) {\n <ion-card-content>\n <ion-spinner name=\"dots\" />\n </ion-card-content>\n } @else if ($error()) {\n <ion-card-content>\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </ion-card-content>\n } @else if ($bills().length) {\n <ion-list>\n @for (bill of $bills(); track bill.billID) {\n <ion-item button (click)=\"openBill(bill.billID)\">\n <ion-label>\n <h2>{{ bill.title || (bill.utilityKind ? bill.utilityKind + ' bill' : 'Bill') }}</h2>\n <p>\n {{ bill.actualAmount }} {{ bill.currency }}\n @if (bill.period) { \u00B7 {{ bill.period.startDate }} to {{ bill.period.endDate }} }\n </p>\n </ion-label>\n <ion-badge slot=\"end\" [color]=\"statusColor(bill.debtusSettlementStatus || bill.postingStatus)\">{{\n bill.debtusSettlementStatus || bill.postingStatus\n }}</ion-badge>\n </ion-item>\n }\n </ion-list>\n } @else {\n <ion-card-content class=\"ion-text-center\">\n <ion-note color=\"medium\">No bills recorded yet.</ion-note>\n <div class=\"ion-margin-top\">\n <ion-button (click)=\"goNewBill()\">\n <ion-icon name=\"add\" slot=\"start\" />\n Record your first bill\n </ion-button>\n </div>\n </ion-card-content>\n }\n </ion-card>\n</ion-content>\n" }]
97
+ }], ctorParameters: () => [] });
98
+
99
+ export { SplitusHomePageComponent };
100
+ //# sourceMappingURL=sneat-extension-splitus-ui-splitus-home-page.component-tUSn27LG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sneat-extension-splitus-ui-splitus-home-page.component-tUSn27LG.mjs","sources":["../../../../../../libs/extensions/splitus/ui/src/lib/pages/splitus-home/splitus-home-page.component.ts","../../../../../../libs/extensions/splitus/ui/src/lib/pages/splitus-home/splitus-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 IonContent,\n IonHeader,\n IonIcon,\n IonItem,\n IonLabel,\n IonList,\n IonNote,\n IonSpinner,\n IonToolbar,\n} from '@ionic/angular';\nimport {\n ISplitusBillListItemV1,\n SPLITUS_BILL_CONTRACT_VERSION,\n SPLITUS_BILL_SERVICE_V1,\n} from '@sneat/extension-splitus-contract';\nimport {\n SpaceComponentBaseParams,\n SpacePageTitleComponent,\n SpacePageBaseComponent,\n} from '@sneat/space-components';\nimport { SpaceServiceModule } from '@sneat/space-services';\nimport { ClassName } from '@sneat/ui';\nimport { EMPTY, switchMap } from 'rxjs';\nimport { DraftSplitStore } from '../../services/draft-split-store.service';\nimport { statusColor } from '../split-status-view';\n\n// Bills home for the current Space. Every row comes from the Splitus backend;\n// an empty response is rendered as an honest empty state.\n@Component({\n selector: 'sneat-splitus-home-page',\n templateUrl: './splitus-home-page.component.html',\n imports: [\n SpacePageTitleComponent,\n SpaceServiceModule,\n IonHeader,\n IonToolbar,\n IonButtons,\n IonBackButton,\n IonContent,\n IonCard,\n IonCardContent,\n IonButton,\n IonIcon,\n IonList,\n IonItem,\n IonLabel,\n IonNote,\n IonBadge,\n IonSpinner,\n ],\n providers: [\n {\n provide: ClassName,\n useValue: 'SplitusHomePageComponent',\n },\n SpaceComponentBaseParams,\n ],\n})\nexport class SplitusHomePageComponent extends SpacePageBaseComponent {\n private readonly splitusService = inject(SPLITUS_BILL_SERVICE_V1);\n private readonly draftSplitStore = inject(DraftSplitStore);\n\n protected readonly $loading = signal(true);\n protected readonly $error = signal<string | undefined>(undefined);\n protected readonly $bills = signal<readonly ISplitusBillListItemV1[]>([]);\n\n protected readonly statusColor = statusColor;\n\n constructor() {\n super();\n this.spaceIDChanged$\n .pipe(\n switchMap((spaceID) => {\n if (spaceID && this.draftSplitStore.hasPending()) {\n // A bill was started before sign-in (see StartSplitPageComponent)\n // — skip the bills list and go straight to finishing it.\n this.spaceNav.navigateForwardToSpacePage(this.space, 'new-bill');\n return EMPTY;\n }\n this.$loading.set(true);\n this.$error.set(undefined);\n return this.splitusService.listBills({\n contractVersion: SPLITUS_BILL_CONTRACT_VERSION,\n spaceID: spaceID ?? '',\n pageSize: 50,\n });\n }),\n takeUntilDestroyed(),\n )\n .subscribe({\n next: (response) => {\n this.$bills.set(response.items);\n this.$loading.set(false);\n },\n error: (err) => {\n this.$error.set('Failed to load bills');\n this.$loading.set(false);\n this.errorLogger.logError(err, 'Failed to load Splitus bills');\n },\n });\n }\n\n protected goNewBill(): void {\n this.spaceNav.navigateForwardToSpacePage(this.space, 'new-bill');\n }\n\n protected openBill(billID: string): void {\n this.spaceNav.navigateForwardToSpacePage(this.space, `bill/${billID}`);\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\" should leave the space and\n return to the app home (the spaces list), not to the space's own root\n (which redirects straight back here). -->\n <ion-back-button defaultHref=\"/\" />\n </ion-buttons>\n <sneat-space-page-title\n [space]=\"space\"\n generalTitle=\"Bills\"\n icon=\"💸\"\n [titlesBySpaceType]=\"{\n personal: 'Personal bills',\n family: 'Household bills',\n group: 'Shared bills',\n }\"\n />\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <ion-card>\n <ion-card-content>\n <ion-button expand=\"block\" (click)=\"goNewBill()\">\n <ion-icon name=\"add\" slot=\"start\" />\n New bill\n </ion-button>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n @if ($loading()) {\n <ion-card-content>\n <ion-spinner name=\"dots\" />\n </ion-card-content>\n } @else if ($error()) {\n <ion-card-content>\n <ion-note color=\"danger\">{{ $error() }}</ion-note>\n </ion-card-content>\n } @else if ($bills().length) {\n <ion-list>\n @for (bill of $bills(); track bill.billID) {\n <ion-item button (click)=\"openBill(bill.billID)\">\n <ion-label>\n <h2>{{ bill.title || (bill.utilityKind ? bill.utilityKind + ' bill' : 'Bill') }}</h2>\n <p>\n {{ bill.actualAmount }} {{ bill.currency }}\n @if (bill.period) { · {{ bill.period.startDate }} to {{ bill.period.endDate }} }\n </p>\n </ion-label>\n <ion-badge slot=\"end\" [color]=\"statusColor(bill.debtusSettlementStatus || bill.postingStatus)\">{{\n bill.debtusSettlementStatus || bill.postingStatus\n }}</ion-badge>\n </ion-item>\n }\n </ion-list>\n } @else {\n <ion-card-content class=\"ion-text-center\">\n <ion-note color=\"medium\">No bills recorded yet.</ion-note>\n <div class=\"ion-margin-top\">\n <ion-button (click)=\"goNewBill()\">\n <ion-icon name=\"add\" slot=\"start\" />\n Record your first bill\n </ion-button>\n </div>\n </ion-card-content>\n }\n </ion-card>\n</ion-content>\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, Component } from '@angular/core';
3
+ import * as i1 from '@angular/forms';
4
+ import { FormControl, ReactiveFormsModule } from '@angular/forms';
5
+ import { Router } from '@angular/router';
6
+ import { IonHeader, IonToolbar, IonTitle, IonContent, IonCard, IonCardHeader, IonCardTitle, IonItem, IonInput, IonSelect, IonSelectOption, IonNote, IonButton } from '@ionic/angular';
7
+ import { newRandomId } from '@sneat/random';
8
+ import { D as DraftSplitStore, S as SPLITUS_START_CONTINUATION_URL, p as positiveExactDecimalValidator, a as parseDecimal, f as formatHundredths } from './sneat-extension-splitus-ui-sneat-extension-splitus-ui-CX1eiAAR.mjs';
9
+
10
+ // Unguarded entry point: capture the bill basics (title/amount/currency)
11
+ // without requiring sign-in or a space. Saves a local draft and sends the
12
+ // user to the host-provided continuation URL. Standalone Splitus.app defaults
13
+ // to '/' and lets its AuthGuard/space-list flow continue the journey. Hosts
14
+ // whose root is public can provide an authenticated entry point instead. The
15
+ // canonical journey requirements are maintained in the Backstage SpecScore
16
+ // Splitus features.
17
+ class StartSplitPageComponent {
18
+ draftSplitStore = inject(DraftSplitStore);
19
+ router = inject(Router);
20
+ continuationUrl = inject(SPLITUS_START_CONTINUATION_URL, { optional: true }) ?? '/';
21
+ title = new FormControl('', {
22
+ nonNullable: true,
23
+ });
24
+ currency = new FormControl('EUR', {
25
+ nonNullable: true,
26
+ });
27
+ amount = new FormControl('', {
28
+ nonNullable: true,
29
+ validators: [positiveExactDecimalValidator],
30
+ });
31
+ currencies = [
32
+ 'EUR',
33
+ 'GBP',
34
+ 'USD',
35
+ ];
36
+ onContinue() {
37
+ this.amount.markAsTouched();
38
+ const amount = parseDecimal(this.amount.value);
39
+ if (this.amount.invalid || amount === undefined) {
40
+ return;
41
+ }
42
+ this.draftSplitStore.save({
43
+ billID: newRandomId({ len: 20 }),
44
+ title: this.title.value.trim(),
45
+ amount: formatHundredths(amount),
46
+ currencyCode: this.currency.value,
47
+ });
48
+ this.router.navigateByUrl(this.continuationUrl);
49
+ }
50
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StartSplitPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
51
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: StartSplitPageComponent, isStandalone: true, selector: "sneat-splitus-start-split-page", ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-title>Split a bill</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>What's the bill?</ion-card-title>\n </ion-card-header>\n <ion-item>\n <ion-input\n label=\"Title (optional)\"\n labelPlacement=\"stacked\"\n [formControl]=\"title\"\n placeholder=\"e.g. Dinner at Luigi's\"\n autocapitalize=\"sentences\"\n />\n </ion-item>\n <ion-item>\n <ion-select\n label=\"Currency\"\n interface=\"popover\"\n [formControl]=\"currency\"\n >\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-input\n label=\"Amount\"\n labelPlacement=\"stacked\"\n type=\"text\"\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-card>\n\n <div class=\"ion-padding\">\n <ion-button expand=\"block\" color=\"primary\" (click)=\"onContinue()\">\n Continue\n </ion-button>\n <ion-note\n color=\"medium\"\n class=\"ion-text-center\"\n style=\"display: block; margin-top: 8px;\"\n >\n You'll sign in next to add who's splitting this.\n </ion-note>\n </div>\n</ion-content>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], 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: 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: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { 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: IonNote, selector: "ion-note", 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"] }] });
52
+ }
53
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StartSplitPageComponent, decorators: [{
54
+ type: Component,
55
+ args: [{ selector: 'sneat-splitus-start-split-page', imports: [
56
+ ReactiveFormsModule,
57
+ IonHeader,
58
+ IonToolbar,
59
+ IonTitle,
60
+ IonContent,
61
+ IonCard,
62
+ IonCardHeader,
63
+ IonCardTitle,
64
+ IonItem,
65
+ IonInput,
66
+ IonSelect,
67
+ IonSelectOption,
68
+ IonNote,
69
+ IonButton,
70
+ ], template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-title>Split a bill</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>What's the bill?</ion-card-title>\n </ion-card-header>\n <ion-item>\n <ion-input\n label=\"Title (optional)\"\n labelPlacement=\"stacked\"\n [formControl]=\"title\"\n placeholder=\"e.g. Dinner at Luigi's\"\n autocapitalize=\"sentences\"\n />\n </ion-item>\n <ion-item>\n <ion-select\n label=\"Currency\"\n interface=\"popover\"\n [formControl]=\"currency\"\n >\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-input\n label=\"Amount\"\n labelPlacement=\"stacked\"\n type=\"text\"\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-card>\n\n <div class=\"ion-padding\">\n <ion-button expand=\"block\" color=\"primary\" (click)=\"onContinue()\">\n Continue\n </ion-button>\n <ion-note\n color=\"medium\"\n class=\"ion-text-center\"\n style=\"display: block; margin-top: 8px;\"\n >\n You'll sign in next to add who's splitting this.\n </ion-note>\n </div>\n</ion-content>\n" }]
71
+ }] });
72
+
73
+ export { StartSplitPageComponent };
74
+ //# sourceMappingURL=sneat-extension-splitus-ui-start-split-page.component-Br8thaKs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sneat-extension-splitus-ui-start-split-page.component-Br8thaKs.mjs","sources":["../../../../../../libs/extensions/splitus/ui/src/lib/pages/start-split/start-split-page.component.ts","../../../../../../libs/extensions/splitus/ui/src/lib/pages/start-split/start-split-page.component.html"],"sourcesContent":["import { Component, inject } from '@angular/core';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { Router } from '@angular/router';\nimport {\n IonButton,\n IonCard,\n IonCardHeader,\n IonCardTitle,\n IonContent,\n IonHeader,\n IonInput,\n IonItem,\n IonNote,\n IonSelect,\n IonSelectOption,\n IonTitle,\n IonToolbar,\n} from '@ionic/angular';\nimport { SplitusCurrencyCode } from '@sneat/extension-splitus-contract';\nimport { newRandomId } from '@sneat/random';\nimport { DraftSplitStore } from '../../services/draft-split-store.service';\nimport {\n formatHundredths,\n parseDecimal,\n positiveExactDecimalValidator,\n} from '../../services/exact-decimal';\nimport { SPLITUS_START_CONTINUATION_URL } from '../../services/splitus-start-continuation-url';\n\n// Unguarded entry point: capture the bill basics (title/amount/currency)\n// without requiring sign-in or a space. Saves a local draft and sends the\n// user to the host-provided continuation URL. Standalone Splitus.app defaults\n// to '/' and lets its AuthGuard/space-list flow continue the journey. Hosts\n// whose root is public can provide an authenticated entry point instead. The\n// canonical journey requirements are maintained in the Backstage SpecScore\n// Splitus features.\n@Component({\n selector: 'sneat-splitus-start-split-page',\n templateUrl: './start-split-page.component.html',\n imports: [\n ReactiveFormsModule,\n IonHeader,\n IonToolbar,\n IonTitle,\n IonContent,\n IonCard,\n IonCardHeader,\n IonCardTitle,\n IonItem,\n IonInput,\n IonSelect,\n IonSelectOption,\n IonNote,\n IonButton,\n ],\n})\nexport class StartSplitPageComponent {\n private readonly draftSplitStore = inject(DraftSplitStore);\n private readonly router = inject(Router);\n private readonly continuationUrl =\n inject(SPLITUS_START_CONTINUATION_URL, { optional: true }) ?? '/';\n\n protected readonly title = new FormControl<string>('', {\n nonNullable: true,\n });\n protected readonly currency = new FormControl<SplitusCurrencyCode>('EUR', {\n nonNullable: true,\n });\n protected readonly amount = new FormControl<string>('', {\n nonNullable: true,\n validators: [positiveExactDecimalValidator],\n });\n\n protected readonly currencies: readonly SplitusCurrencyCode[] = [\n 'EUR',\n 'GBP',\n 'USD',\n ];\n\n protected onContinue(): void {\n this.amount.markAsTouched();\n const amount = parseDecimal(this.amount.value);\n if (this.amount.invalid || amount === undefined) {\n return;\n }\n this.draftSplitStore.save({\n billID: newRandomId({ len: 20 }),\n title: this.title.value.trim(),\n amount: formatHundredths(amount),\n currencyCode: this.currency.value,\n });\n this.router.navigateByUrl(this.continuationUrl);\n }\n}\n","<ion-header>\n <ion-toolbar color=\"light\">\n <ion-title>Split a bill</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"cardy\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>What's the bill?</ion-card-title>\n </ion-card-header>\n <ion-item>\n <ion-input\n label=\"Title (optional)\"\n labelPlacement=\"stacked\"\n [formControl]=\"title\"\n placeholder=\"e.g. Dinner at Luigi's\"\n autocapitalize=\"sentences\"\n />\n </ion-item>\n <ion-item>\n <ion-select\n label=\"Currency\"\n interface=\"popover\"\n [formControl]=\"currency\"\n >\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-input\n label=\"Amount\"\n labelPlacement=\"stacked\"\n type=\"text\"\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-card>\n\n <div class=\"ion-padding\">\n <ion-button expand=\"block\" color=\"primary\" (click)=\"onContinue()\">\n Continue\n </ion-button>\n <ion-note\n color=\"medium\"\n class=\"ion-text-center\"\n style=\"display: block; margin-top: 8px;\"\n >\n You'll sign in next to add who's splitting this.\n </ion-note>\n </div>\n</ion-content>\n"],"names":[],"mappings":"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"}