@nauticana/sail 0.8.0 → 0.8.1

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 (47) hide show
  1. package/README.md +120 -15
  2. package/package.json +2 -2
  3. package/src/component/abstract/base_async.ts +8 -2
  4. package/src/component/abstract/base_table.ts +94 -35
  5. package/src/component/abstract/base_view.ts +28 -41
  6. package/src/component/billing/checkout_button.ts +1 -1
  7. package/src/component/billing/payment_methods.ts +1 -1
  8. package/src/component/billing/plan_selector.ts +1 -1
  9. package/src/component/form/form_field.ts +8 -11
  10. package/src/component/form/form_record.html +2 -2
  11. package/src/component/form/form_record.ts +1 -1
  12. package/src/component/form/form_table.html +1 -1
  13. package/src/component/form/form_table.ts +1 -1
  14. package/src/component/login/account_deletion.ts +1 -1
  15. package/src/component/login/chpass_component.ts +11 -16
  16. package/src/component/login/confirm_chpass_component.ts +9 -15
  17. package/src/component/login/confirm_register_component.ts +9 -15
  18. package/src/component/login/consent_gate.ts +1 -1
  19. package/src/component/login/login_component.html +1 -1
  20. package/src/component/login/login_component.ts +1 -1
  21. package/src/component/login/otp_input.ts +1 -1
  22. package/src/component/login/register_component.ts +1 -1
  23. package/src/component/login/social_login.ts +1 -1
  24. package/src/component/navigation/navigation.ts +10 -11
  25. package/src/component/security/trusted_devices.ts +1 -1
  26. package/src/component/security/twofactor_setup.ts +18 -26
  27. package/src/component/security/twofactor_verify.ts +10 -12
  28. package/src/component/table/table_detail.html +4 -4
  29. package/src/component/table/table_detail.ts +22 -34
  30. package/src/component/table/table_edit.html +13 -3
  31. package/src/component/table/table_edit.ts +17 -17
  32. package/src/component/table/table_list.ts +18 -52
  33. package/src/component/table/table_lookup.html +1 -1
  34. package/src/component/table/table_lookup.ts +1 -3
  35. package/src/component/table/table_report.ts +11 -14
  36. package/src/component/table/table_search.html +1 -1
  37. package/src/component/table/table_search.ts +14 -38
  38. package/src/index.ts +2 -0
  39. package/src/model/appdata.ts +2 -2
  40. package/src/model/common.ts +39 -1
  41. package/src/service/auth.service.ts +26 -26
  42. package/src/service/base_rest.service.ts +20 -0
  43. package/src/service/billing.service.ts +3 -9
  44. package/src/service/payout.service.ts +3 -9
  45. package/src/service/rest_service.ts +5 -5
  46. package/src/service/user_payment_method.service.ts +4 -8
  47. package/src/util/text.ts +19 -0
@@ -1,5 +1,5 @@
1
1
  import { Injectable, WritableSignal, inject, signal } from '@angular/core';
2
- import { HttpClient, HttpParams } from '@angular/common/http';
2
+ import { HttpParams } from '@angular/common/http';
3
3
  import { map, take, tap } from 'rxjs/operators';
4
4
  import { ApplicationData, AuthSummary, ConfirmRegisterResponse, DictionaryPath, LoginResponse2FA, PartnerRegistration, RestReport, TableDefinition, TrustedDevice, TwoFactorSetupResponse, TwoFactorVerifyRequest, TwoFactorVerifyResponse } from '../model/appdata';
5
5
  import {
@@ -14,39 +14,39 @@ import { Observable, ReplaySubject } from 'rxjs';
14
14
  import { ApplicationMenu, ConstantValue } from '../model/common';
15
15
  import { CanActivateFn, Router, Routes } from '@angular/router';
16
16
  import { SAIL_GUI_CONFIG, SailGuiConfig, DEFAULT_CONFIG } from '../config';
17
+ import { BaseRestService } from './base_rest.service';
17
18
 
18
19
  @Injectable()
19
- export abstract class BaseAuthService {
20
- private readonly http = inject(HttpClient);
20
+ export abstract class BaseAuthService extends BaseRestService {
21
21
  protected readonly router = inject(Router);
22
22
  protected readonly guiConfig: SailGuiConfig = inject(SAIL_GUI_CONFIG, {optional: true}) ?? DEFAULT_CONFIG;
23
23
  protected cache!: ApplicationData;
24
24
  private readonly appData$ = new ReplaySubject<ApplicationData>(1);
25
25
  private readonly authIndex = new Map<string, AuthSummary[]>();
26
26
 
27
- protected get appdataUrl() { return RestURL.httpHost + RestURL.appdataURL; }
28
- protected get loginUrl() { return RestURL.httpHost + RestURL.loginURL; }
29
- protected get registerUrl() { return RestURL.httpHost + RestURL.registerURL; }
30
- protected get chpassUrl() { return RestURL.httpHost + RestURL.chpassURL; }
31
- protected get confirmRegisterUrl() { return RestURL.httpHost + RestURL.confirmRegisterURL; }
32
- protected get confirmChpassUrl() { return RestURL.httpHost + RestURL.confirmChpassURL; }
33
- protected get loginGoogleUrl() { return RestURL.httpHost + RestURL.loginGoogleURL; }
34
- protected get twoFactorSetupUrl() { return RestURL.httpHost + RestURL.twoFactorSetupURL; }
35
- protected get twoFactorVerifyUrl() { return RestURL.httpHost + RestURL.twoFactorVerifyURL; }
36
- protected get twoFactorDisableUrl() { return RestURL.httpHost + RestURL.twoFactorDisableURL; }
37
- protected get twoFactorLoginVerifyUrl() { return RestURL.httpHost + RestURL.twoFactorLoginVerifyURL; }
38
- protected get twoFactorBackupVerifyUrl() { return RestURL.httpHost + RestURL.twoFactorBackupVerifyURL; }
39
- protected get trustedDeviceListUrl() { return RestURL.httpHost + RestURL.trustedDeviceListURL; }
40
- protected get trustedDeviceRegisterUrl() { return RestURL.httpHost + RestURL.trustedDeviceRegisterURL; }
41
- protected get trustedDeviceRevokeUrl() { return RestURL.httpHost + RestURL.trustedDeviceRevokeURL; }
42
- protected get otpSendUrl() { return RestURL.httpHost + RestURL.otpSendURL; }
43
- protected get otpVerifyUrl() { return RestURL.httpHost + RestURL.otpVerifyURL; }
44
- protected get otpResendUrl() { return RestURL.httpHost + RestURL.otpResendURL; }
45
- protected get loginSocialUrl() { return RestURL.httpHost + RestURL.loginSocialURL; }
46
- protected get logoutEverywhereUrl() { return RestURL.httpHost + RestURL.logoutEverywhereURL; }
47
- protected get deleteAccountUrl() { return RestURL.httpHost + RestURL.deleteAccountURL; }
48
- protected get pushRegisterUrl() { return RestURL.httpHost + RestURL.pushRegisterURL; }
49
- protected get pushRevokeUrl() { return RestURL.httpHost + RestURL.pushRevokeURL; }
27
+ protected readonly appdataUrl = this.url(RestURL.appdataURL);
28
+ protected readonly loginUrl = this.url(RestURL.loginURL);
29
+ protected readonly registerUrl = this.url(RestURL.registerURL);
30
+ protected readonly chpassUrl = this.url(RestURL.chpassURL);
31
+ protected readonly confirmRegisterUrl = this.url(RestURL.confirmRegisterURL);
32
+ protected readonly confirmChpassUrl = this.url(RestURL.confirmChpassURL);
33
+ protected readonly loginGoogleUrl = this.url(RestURL.loginGoogleURL);
34
+ protected readonly twoFactorSetupUrl = this.url(RestURL.twoFactorSetupURL);
35
+ protected readonly twoFactorVerifyUrl = this.url(RestURL.twoFactorVerifyURL);
36
+ protected readonly twoFactorDisableUrl = this.url(RestURL.twoFactorDisableURL);
37
+ protected readonly twoFactorLoginVerifyUrl = this.url(RestURL.twoFactorLoginVerifyURL);
38
+ protected readonly twoFactorBackupVerifyUrl = this.url(RestURL.twoFactorBackupVerifyURL);
39
+ protected readonly trustedDeviceListUrl = this.url(RestURL.trustedDeviceListURL);
40
+ protected readonly trustedDeviceRegisterUrl = this.url(RestURL.trustedDeviceRegisterURL);
41
+ protected readonly trustedDeviceRevokeUrl = this.url(RestURL.trustedDeviceRevokeURL);
42
+ protected readonly otpSendUrl = this.url(RestURL.otpSendURL);
43
+ protected readonly otpVerifyUrl = this.url(RestURL.otpVerifyURL);
44
+ protected readonly otpResendUrl = this.url(RestURL.otpResendURL);
45
+ protected readonly loginSocialUrl = this.url(RestURL.loginSocialURL);
46
+ protected readonly logoutEverywhereUrl = this.url(RestURL.logoutEverywhereURL);
47
+ protected readonly deleteAccountUrl = this.url(RestURL.deleteAccountURL);
48
+ protected readonly pushRegisterUrl = this.url(RestURL.pushRegisterURL);
49
+ protected readonly pushRevokeUrl = this.url(RestURL.pushRevokeURL);
50
50
 
51
51
  token: string | null = null;
52
52
  readonly isLoggedIn = signal(false);
@@ -0,0 +1,20 @@
1
+ import { HttpClient } from '@angular/common/http';
2
+ import { inject } from '@angular/core';
3
+ import { RestURL } from './rest_url';
4
+
5
+ /**
6
+ * Foundation for sail's REST API clients. Owns the shared `http` injection
7
+ * and the `host + path` URL builder so subclasses don't repeat them.
8
+ *
9
+ * Subclasses set `providedIn: 'root'` on their own `@Injectable` decorator
10
+ * (this class itself stays unannotated — Angular resolves it through the
11
+ * subclass).
12
+ */
13
+ export abstract class BaseRestService {
14
+ protected readonly http = inject(HttpClient);
15
+
16
+ /** Prefix any `RestURL.*URL` path with the configured backend host. */
17
+ protected url(path: string): string {
18
+ return RestURL.httpHost + path;
19
+ }
20
+ }
@@ -1,7 +1,7 @@
1
- import { Injectable, inject } from '@angular/core';
2
- import { HttpClient } from '@angular/common/http';
1
+ import { Injectable } from '@angular/core';
3
2
  import { Observable } from 'rxjs';
4
3
  import { RestURL } from './rest_url';
4
+ import { BaseRestService } from './base_rest.service';
5
5
  import {
6
6
  CheckoutRequest,
7
7
  CheckoutResponse,
@@ -19,13 +19,7 @@ import {
19
19
  * keel/SHARED_PAYMENT.md for the backend contract.
20
20
  */
21
21
  @Injectable({ providedIn: 'root' })
22
- export class BillingService {
23
- private readonly http = inject(HttpClient);
24
-
25
- private url(path: string): string {
26
- return RestURL.httpHost + path;
27
- }
28
-
22
+ export class BillingService extends BaseRestService {
29
23
  /** Public catalog — safe to call before login. */
30
24
  listPlans(): Observable<PublicPlan[]> {
31
25
  return this.http.get<PublicPlan[]>(this.url(RestURL.plansURL));
@@ -1,7 +1,7 @@
1
- import { inject, Injectable } from '@angular/core';
2
- import { HttpClient } from '@angular/common/http';
1
+ import { Injectable } from '@angular/core';
3
2
  import { Observable } from 'rxjs';
4
3
  import { RestURL } from './rest_url';
4
+ import { BaseRestService } from './base_rest.service';
5
5
  import {
6
6
  PayoutOnboardingSession,
7
7
  ReusableAccount,
@@ -19,13 +19,7 @@ import {
19
19
  * RestURL.payout*URL.
20
20
  */
21
21
  @Injectable({ providedIn: 'root' })
22
- export class PayoutService {
23
- private readonly http = inject(HttpClient);
24
-
25
- private url(path: string): string {
26
- return RestURL.httpHost + path;
27
- }
28
-
22
+ export class PayoutService extends BaseRestService {
29
23
  /** Launch the hosted-KYC flow. Returns a URL to open + provider-side handle. */
30
24
  startOnboarding(): Observable<PayoutOnboardingSession> {
31
25
  return this.http.post<PayoutOnboardingSession>(this.url(RestURL.payoutOnboardStartURL), {});
@@ -1,13 +1,13 @@
1
- import { HttpClient, HttpErrorResponse, HttpParams } from "@angular/common/http";
2
- import { inject, Injectable } from "@angular/core";
1
+ import { HttpErrorResponse, HttpParams } from "@angular/common/http";
2
+ import { Injectable } from "@angular/core";
3
3
  import { catchError, map, Observable, throwError } from "rxjs";
4
4
  import { PaginatedList } from "../model/appdata";
5
5
  import { RestURL } from "./rest_url";
6
+ import { BaseRestService } from "./base_rest.service";
6
7
 
7
8
  @Injectable({providedIn: 'root'})
8
- export class BackendService {
9
- protected readonly prefix = RestURL.httpHost + RestURL.api_prefix;
10
- protected readonly http = inject(HttpClient);
9
+ export class BackendService extends BaseRestService {
10
+ protected readonly prefix = this.url(RestURL.api_prefix);
11
11
 
12
12
  protected handleError(error: HttpErrorResponse) {
13
13
  if (error.status === 0) {
@@ -1,8 +1,8 @@
1
- import { inject, Injectable } from '@angular/core';
2
- import { HttpClient } from '@angular/common/http';
1
+ import { Injectable, inject } from '@angular/core';
3
2
  import { Observable } from 'rxjs';
4
3
  import { RestURL } from './rest_url';
5
4
  import { BackendService } from './rest_service';
5
+ import { BaseRestService } from './base_rest.service';
6
6
  import { UserPaymentMethod } from '../model/appdata';
7
7
 
8
8
  /**
@@ -17,8 +17,7 @@ import { UserPaymentMethod } from '../model/appdata';
17
17
  * single-row CRUD can't express.
18
18
  */
19
19
  @Injectable({ providedIn: 'root' })
20
- export class UserPaymentMethodService {
21
- private readonly http = inject(HttpClient);
20
+ export class UserPaymentMethodService extends BaseRestService {
22
21
  private readonly backend = inject(BackendService);
23
22
 
24
23
  /**
@@ -40,9 +39,6 @@ export class UserPaymentMethodService {
40
39
  * Custom endpoint — see service-class doc for the rationale.
41
40
  */
42
41
  setDefault(id: number): Observable<{ message: string }> {
43
- return this.http.post<{ message: string }>(
44
- RestURL.httpHost + RestURL.paymentMethodSetDefaultURL,
45
- { id },
46
- );
42
+ return this.http.post<{ message: string }>(this.url(RestURL.paymentMethodSetDefaultURL), { id });
47
43
  }
48
44
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Convert a snake_case / lower_case identifier to Title Case.
3
+ *
4
+ * titleCase('user_payment_method') -> 'User Payment Method'
5
+ * titleCase('UserId') -> 'Userid' (no camelCase split)
6
+ *
7
+ * Used as the fallback label for tables and columns when no explicit
8
+ * caption is set in `basis.table_header.Caption` / `basis.column.Caption`.
9
+ * Mirrors keel's `common.PascalCase`-style helpers on the frontend side
10
+ * so caption rendering is consistent across consumers.
11
+ */
12
+ export function titleCase(str: string): string {
13
+ return str
14
+ .replaceAll('_', ' ')
15
+ .toLowerCase()
16
+ .split(' ')
17
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
18
+ .join(' ');
19
+ }