@block-auth.io/angular 0.1.1 → 0.2.0

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.
@@ -8,6 +8,8 @@ import {
8
8
  output,
9
9
  signal,
10
10
  } from '@angular/core';
11
+ import { $locale, t as translate, type TextKey } from '@block-auth.io/core';
12
+ import { injectStore } from '../../../inject-store';
11
13
  import { injectBlockAuthClient } from '../../../provide-block-auth';
12
14
  import { ArrowIconComponent } from '../../../primitives/arrow-icon.component';
13
15
  import { LifetimeSliderBaseComponent } from '../../../primitives/lifetime-slider-base.component';
@@ -42,16 +44,22 @@ interface TxnPreParams {
42
44
  changeDetection: ChangeDetectionStrategy.OnPush,
43
45
  imports: [ArrowIconComponent, LifetimeSliderBaseComponent],
44
46
  template: `
45
- <div class="bas-flex bas-h-full bas-w-full bas-flex-col bas-items-center bas-justify-between">
46
- <div class="bal-ba-transition-3 bas-flex bas-w-full bas-flex-col bas-gap-8 bas-pt-2">
47
+ <div
48
+ class="bas-flex bas-h-full bas-w-full bas-flex-col bas-items-center bas-justify-between"
49
+ >
50
+ <div
51
+ class="bal-ba-transition-3 bas-flex bas-w-full bas-flex-col bas-gap-8 bas-pt-2"
52
+ >
47
53
  <div
48
54
  class="bal-ba-outline-none bas-grid bas-h-[30px] bas-cursor-pointer bas-grid-cols-[65px_auto_65px] bas-items-center bas-justify-center bas-gap-2"
49
55
  (click)="permissionsRequiredClicked.emit()"
50
56
  >
51
57
  <ba-arrow-icon direction="right" />
52
58
  <div class="bas-flex bas-h-full bas-w-full bas-items-end">
53
- <p class="bas-m-0 bas-text-center bas-text-lg bas-font-medium bas-text-[var(--bal-primary-900)]">
54
- {{ permissionsRequiredTxt() }}
59
+ <p
60
+ class="bas-m-0 bas-text-center bas-text-lg bas-font-medium bas-text-[var(--bal-primary-900)]"
61
+ >
62
+ {{ permissionsRequiredTxtResolved() }}
55
63
  </p>
56
64
  </div>
57
65
  <ba-arrow-icon direction="left" />
@@ -60,12 +68,16 @@ interface TxnPreParams {
60
68
  <div
61
69
  [class]="
62
70
  'bas-grid bas-h-[30px] bas-grid-cols-[65px_auto_65px] bas-items-center bas-justify-center bas-gap-2 ' +
63
- (hasSolicitedData() ? 'bas-cursor-pointer' : 'bas-cursor-not-allowed bas-opacity-40')
71
+ (hasSolicitedData()
72
+ ? 'bas-cursor-pointer'
73
+ : 'bas-cursor-not-allowed bas-opacity-40')
64
74
  "
65
75
  (click)="onSolicitedDataClick()"
66
76
  >
67
77
  <ba-arrow-icon direction="right" />
68
- <div class="bas-flex bas-h-full bas-w-full bas-items-end bas-justify-center">
78
+ <div
79
+ class="bas-flex bas-h-full bas-w-full bas-items-end bas-justify-center"
80
+ >
69
81
  <p
70
82
  [class]="
71
83
  'bas-m-0 bas-text-center bas-text-lg bas-transition-colors ' +
@@ -74,7 +86,7 @@ interface TxnPreParams {
74
86
  : 'bas-text-slate-400')
75
87
  "
76
88
  >
77
- {{ solicitedDataTxt() }}
89
+ {{ solicitedDataTxtResolved() }}
78
90
  </p>
79
91
  </div>
80
92
  <ba-arrow-icon direction="left" />
@@ -82,40 +94,55 @@ interface TxnPreParams {
82
94
  </div>
83
95
 
84
96
  @if (!showLifetime()) {
85
- <button
86
- type="button"
87
- id="btn-configure-session-time"
88
- class="bal-ba-transition-2 bas-flex bas-w-full bas-cursor-pointer bas-flex-row bas-items-end bas-justify-center bas-gap-2 bas-border-0 bas-bg-transparent bas-text-[--bal-primary-700] hover:bas-text-[--bal-primary-900]"
89
- (click)="showLifetime.set(true)"
97
+ <button
98
+ type="button"
99
+ id="btn-configure-session-time"
100
+ class="bal-ba-transition-2 bas-flex bas-w-full bas-cursor-pointer bas-flex-row bas-items-end bas-justify-center bas-gap-2 bas-border-0 bas-bg-transparent bas-text-[--bal-primary-700] hover:bas-text-[--bal-primary-900]"
101
+ (click)="showLifetime.set(true)"
102
+ >
103
+ <p class="bas-m-0 bas-text-center bas-text-base">
104
+ {{ configureSessionTimeTxtResolved() }}
105
+ </p>
106
+ <svg
107
+ class="bas-h-5 bas-w-5"
108
+ viewBox="0 0 24 24"
109
+ fill="none"
110
+ stroke="currentColor"
111
+ stroke-width="1.8"
112
+ aria-hidden="true"
90
113
  >
91
- <p class="bas-m-0 bas-text-center bas-text-base">{{ configureSessionTimeTxt() }}</p>
92
- <svg class="bas-h-5 bas-w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
93
- <circle cx="12" cy="12" r="9" />
94
- <path d="M12 7v5l3 2" stroke-linecap="round" stroke-linejoin="round" />
95
- </svg>
96
- </button>
114
+ <circle cx="12" cy="12" r="9" />
115
+ <path
116
+ d="M12 7v5l3 2"
117
+ stroke-linecap="round"
118
+ stroke-linejoin="round"
119
+ />
120
+ </svg>
121
+ </button>
97
122
  } @else {
98
- <ba-lifetime-slider-base
99
- id="txn-lifetime-slider"
100
- [sliderValue]="lifetimeSign()"
101
- [step]="1"
102
- [min]="minLifetime()"
103
- [max]="maxLifetime()"
104
- [timeInputValue]="lifetimeTimeValue()"
105
- [isOpen]="isTimePickerOpen()"
106
- [isTimePickerInsideModal]="true"
107
- modalId="txns-overview"
108
- timePickerDropdownDirection="up"
109
- classNames="bas-grid bas-h-auto bas-w-full bas-items-center bas-gap-3 bas-grid-cols-[152px_1fr]"
110
- timePickerContainerClassNames="bas-flex bas-w-full bas-items-center bas-justify-center"
111
- (sliderValueChanged)="onLifetimeValueChanged($event)"
112
- (timeChanged)="onLifetimeValueChanged($event)"
113
- (isOpenChange)="isTimePickerOpen.set($event)"
114
- (toggleTimerDropdown)="toggleTimerDropdown()"
115
- />
123
+ <ba-lifetime-slider-base
124
+ id="txn-lifetime-slider"
125
+ [sliderValue]="lifetimeSign()"
126
+ [step]="1"
127
+ [min]="minLifetime()"
128
+ [max]="maxLifetime()"
129
+ [timeInputValue]="lifetimeTimeValue()"
130
+ [isOpen]="isTimePickerOpen()"
131
+ [isTimePickerInsideModal]="true"
132
+ modalId="txns-overview"
133
+ timePickerDropdownDirection="up"
134
+ classNames="bas-grid bas-h-auto bas-w-full bas-items-center bas-gap-3 bas-grid-cols-[152px_1fr]"
135
+ timePickerContainerClassNames="bas-flex bas-w-full bas-items-center bas-justify-center"
136
+ (sliderValueChanged)="onLifetimeValueChanged($event)"
137
+ (timeChanged)="onLifetimeValueChanged($event)"
138
+ (isOpenChange)="isTimePickerOpen.set($event)"
139
+ (toggleTimerDropdown)="toggleTimerDropdown()"
140
+ />
116
141
  }
117
142
 
118
- <div class="bal-ba-transition-3 bas-flex bas-w-full bas-flex-col bas-gap-8 bas-pb-4">
143
+ <div
144
+ class="bal-ba-transition-3 bas-flex bas-w-full bas-flex-col bas-gap-8 bas-pb-4"
145
+ >
119
146
  <div
120
147
  data-testid="txn-pptc-toggle"
121
148
  [class]="
@@ -139,21 +166,29 @@ interface TxnPreParams {
139
166
  class="bal-ba-checkbox-animated-input bal-ba-outline-none bas-peer bas-hidden"
140
167
  (change)="pptcAccepted.set(!pptcAccepted())"
141
168
  />
142
- <span class="bal-ba-input-check bal-ba-input-check-big bal-ba-transition-3 bas-relative bas-top-0 bas-inline-block bas-h-[25px] bas-w-[25px] bas-cursor-pointer bas-rounded-[4px] bas-border-2 bas-border-solid bas-border-[#c2c6c8] group-hover:bas-border-[#61a5c2] peer-checked:bas-border-2 peer-checked:bas-border-solid peer-checked:bas-border-[var(--bal-primary-600)] peer-checked:bas-bg-[var(--bal-primary-600)] peer-disabled:bas-cursor-not-allowed peer-disabled:bas-opacity-50"></span>
169
+ <span
170
+ class="bal-ba-input-check bal-ba-input-check-big bal-ba-transition-3 bas-relative bas-top-0 bas-inline-block bas-h-[25px] bas-w-[25px] bas-cursor-pointer bas-rounded-[4px] bas-border-2 bas-border-solid bas-border-[#c2c6c8] group-hover:bas-border-[#61a5c2] peer-checked:bas-border-2 peer-checked:bas-border-solid peer-checked:bas-border-[var(--bal-primary-600)] peer-checked:bas-bg-[var(--bal-primary-600)] peer-disabled:bas-cursor-not-allowed peer-disabled:bas-opacity-50"
171
+ ></span>
143
172
  </label>
144
173
  </div>
145
- <div class="bas-flex bas-flex-col bas-gap-0.5 bas-text-sm bas-text-[var(--bal-primary-500)] group-hover:bas-text-[var(--bal-primary-800)]">
174
+ <div
175
+ class="bas-flex bas-flex-col bas-gap-0.5 bas-text-sm bas-text-[var(--bal-primary-500)] group-hover:bas-text-[var(--bal-primary-800)]"
176
+ >
146
177
  <div class="bas-flex bas-flex-row bas-items-center bas-gap-1">
147
- <p class="bal-ba-transition-2 bas-m-0 bas-cursor-default">{{ iAuthorizeTheTxt() }}</p>
178
+ <p class="bal-ba-transition-2 bas-m-0 bas-cursor-default">
179
+ {{ iAuthorizeTheTxtResolved() }}
180
+ </p>
148
181
  <button
149
182
  type="button"
150
183
  data-testid="btn-privacy-policy"
151
184
  class="bal-ba-transition-2 bas-flex bas-h-full bas-cursor-pointer bas-items-center bas-border-none bas-bg-transparent bas-p-0 bas-pb-[1px] bas-text-sm bas-text-[var(--bal-primary-700)] bas-underline group-hover:bas-text-[var(--bal-primary-900)]"
152
185
  (click)="openPrivacyPolicy()"
153
186
  >
154
- {{ privacyPolicyTxt() }}
187
+ {{ privacyPolicyTxtResolved() }}
155
188
  </button>
156
- <p class="bal-ba-transition-2 bas-m-0 bas-cursor-default">{{ andTheTxt() }}</p>
189
+ <p class="bal-ba-transition-2 bas-m-0 bas-cursor-default">
190
+ {{ andTheTxtResolved() }}
191
+ </p>
157
192
  </div>
158
193
  <button
159
194
  type="button"
@@ -161,19 +196,23 @@ interface TxnPreParams {
161
196
  class="bal-ba-transition-2 bas-flex bas-h-full bas-w-fit bas-cursor-pointer bas-items-center bas-border-none bas-bg-transparent bas-p-0 bas-pb-[1px] bas-text-sm bas-text-[var(--bal-primary-700)] bas-underline group-hover:bas-text-[var(--bal-primary-900)]"
162
197
  (click)="openTermsAndConditions()"
163
198
  >
164
- {{ termsAndConditionsTxt() }}
199
+ {{ termsAndConditionsTxtResolved() }}
165
200
  </button>
166
201
  </div>
167
202
  </div>
168
- <div class="bas-flex bas-w-full bas-flex-row bas-items-center bas-justify-between bas-gap-2">
203
+ <div
204
+ class="bas-flex bas-w-full bas-flex-row bas-items-center bas-justify-between bas-gap-2"
205
+ >
169
206
  <button
170
207
  type="button"
171
208
  id="btn-txn-reject"
172
209
  class="bal-ba-ring-base bal-ba-ring bal-ba-transition-2 bas-group bas-relative bas-box-border bas-flex bas-flex-row bas-gap-2 bas-items-center bas-overflow-hidden bas-bg-transparent bal-ba-hover-ring-slate-400 bal-ba-focus-ring-slate-400 bas-h-[42px] bas-px-2.5 bas-py-0 disabled:bas-cursor-not-allowed disabled:bas-opacity-50 bal-ba-hover-ring-2 bas-cursor-pointer bal-ba-focus-ring-2 focus:bas-opacity-90 bal-ba-focus-ring-offset-0 bas-w-full bas-justify-center bas-border-2 bas-border-slate-400 bas-rounded-md bas-border-solid"
173
210
  (click)="onTxnRejectClicked()"
174
211
  >
175
- <span class="bal-ba-transition-2 bas-text-center bas-text-base bas-font-medium bas-text-slate-600 group-hover:bas-text-slate-800 bas-flex bas-h-full bas-items-center">
176
- {{ rejectTxt() }}
212
+ <span
213
+ class="bal-ba-transition-2 bas-text-center bas-text-base bas-font-medium bas-text-slate-600 group-hover:bas-text-slate-800 bas-flex bas-h-full bas-items-center"
214
+ >
215
+ {{ rejectTxtResolved() }}
177
216
  </span>
178
217
  </button>
179
218
  <button
@@ -183,8 +222,10 @@ interface TxnPreParams {
183
222
  class="bal-ba-ring-base bal-ba-ring bal-ba-transition-2 bas-group bas-relative bas-box-border bas-flex bas-flex-row bas-gap-2 bas-items-center bas-overflow-hidden bas-bg-[#38b000] hover:bas-bg-[#329e00] bal-ba-hover-ring-green-500 bal-ba-focus-ring-green-500 bas-h-[42px] bas-px-2.5 bas-py-0 disabled:bas-cursor-not-allowed disabled:bas-opacity-50 bal-ba-hover-ring-2 bas-cursor-pointer bal-ba-focus-ring-2 focus:bas-opacity-90 bal-ba-focus-ring-offset-2 bal-ba-hover-ring-offset-2 bas-w-full bas-justify-center bas-border-none bas-rounded-md"
184
223
  (click)="onTxnAcceptClicked()"
185
224
  >
186
- <span class="bal-ba-transition-2 bas-text-white bas-text-center bas-text-base bas-font-medium bas-flex bas-h-full bas-items-center">
187
- {{ acceptTxt() }}
225
+ <span
226
+ class="bal-ba-transition-2 bas-text-white bas-text-center bas-text-base bas-font-medium bas-flex bas-h-full bas-items-center"
227
+ >
228
+ {{ acceptTxtResolved() }}
188
229
  </span>
189
230
  </button>
190
231
  </div>
@@ -193,15 +234,15 @@ interface TxnPreParams {
193
234
  `,
194
235
  })
195
236
  export class TxnComponent {
196
- permissionsRequiredTxt = input('Permissions required');
197
- solicitedDataTxt = input('Solicited data');
198
- rejectTxt = input('REJECT');
199
- acceptTxt = input('ACCEPT');
200
- iAuthorizeTheTxt = input('I authorize the');
201
- privacyPolicyTxt = input('Privacy Policy');
202
- andTheTxt = input('and the');
203
- termsAndConditionsTxt = input('Terms and Conditions');
204
- configureSessionTimeTxt = input('I want to configure my session time');
237
+ permissionsRequiredTxt = input<string>();
238
+ solicitedDataTxt = input<string>();
239
+ rejectTxt = input<string>();
240
+ acceptTxt = input<string>();
241
+ iAuthorizeTheTxt = input<string>();
242
+ privacyPolicyTxt = input<string>();
243
+ andTheTxt = input<string>();
244
+ termsAndConditionsTxt = input<string>();
245
+ configureSessionTimeTxt = input<string>();
205
246
  hasSolicitedData = input(false);
206
247
  allRequiredFilled = input(true);
207
248
  closeTxns = input<() => void>(() => {});
@@ -218,13 +259,49 @@ export class TxnComponent {
218
259
  solicitedDataClicked = output<void>();
219
260
 
220
261
  private readonly client = injectBlockAuthClient();
262
+ private readonly locale = injectStore($locale);
263
+ protected t = (key: TextKey): string => translate(key, this.locale());
264
+
265
+ protected readonly permissionsRequiredTxtResolved = computed(
266
+ () => this.permissionsRequiredTxt() ?? this.t('permissionsRequired'),
267
+ );
268
+ protected readonly solicitedDataTxtResolved = computed(
269
+ () => this.solicitedDataTxt() ?? this.t('solicitedData'),
270
+ );
271
+ protected readonly rejectTxtResolved = computed(
272
+ () => this.rejectTxt() ?? this.t('reject'),
273
+ );
274
+ protected readonly acceptTxtResolved = computed(
275
+ () => this.acceptTxt() ?? this.t('accept'),
276
+ );
277
+ protected readonly iAuthorizeTheTxtResolved = computed(
278
+ () => this.iAuthorizeTheTxt() ?? this.t('iAuthorizeThe'),
279
+ );
280
+ protected readonly privacyPolicyTxtResolved = computed(
281
+ () => this.privacyPolicyTxt() ?? this.t('privacyPolicy'),
282
+ );
283
+ protected readonly andTheTxtResolved = computed(
284
+ () => this.andTheTxt() ?? this.t('andThe'),
285
+ );
286
+ protected readonly termsAndConditionsTxtResolved = computed(
287
+ () => this.termsAndConditionsTxt() ?? this.t('termsAndConditions'),
288
+ );
289
+ protected readonly configureSessionTimeTxtResolved = computed(
290
+ () => this.configureSessionTimeTxt() ?? this.t('configureSessionTime'),
291
+ );
221
292
 
222
293
  protected readonly pptcAccepted = signal(false);
223
294
  protected readonly showLifetime = signal(false);
224
295
 
225
- private readonly lifetimeData = computed(() => this.selectedTxnPreParams().lifetime ?? {});
226
- protected readonly minLifetime = computed(() => this.lifetimeData().min ?? 10);
227
- protected readonly maxLifetime = computed(() => this.lifetimeData().max ?? 29100);
296
+ private readonly lifetimeData = computed(
297
+ () => this.selectedTxnPreParams().lifetime ?? {},
298
+ );
299
+ protected readonly minLifetime = computed(
300
+ () => this.lifetimeData().min ?? 10,
301
+ );
302
+ protected readonly maxLifetime = computed(
303
+ () => this.lifetimeData().max ?? 29100,
304
+ );
228
305
  protected readonly lifetimeSign = signal(this.lifetime());
229
306
  // El TimePicker refleja `lifetimeSign` como string HH:MM:SS; tanto el slider
230
307
  // como el picker reportan en segundos, así un único handler mantiene ambos
@@ -288,7 +365,10 @@ export class TxnComponent {
288
365
  if (!preParams.appKey) return this.addError()('no_app_key_found');
289
366
  if (!preParams.to) return this.addError()('no_contract_address_found');
290
367
  this.client.state.$ui.setKey('isAcceptingTxn', true);
291
- this.client.txns.accept({ lifetime: this.lifetimeSign(), userParams: this.userParamValues() });
368
+ this.client.txns.accept({
369
+ lifetime: this.lifetimeSign(),
370
+ userParams: this.userParamValues(),
371
+ });
292
372
  // Auto-close after 4s if txns are cleared (matches original behaviour).
293
373
  this.autoCloseTimeout = setTimeout(() => {
294
374
  const requests = this.client.state.$ws.get().requests;
@@ -6,19 +6,23 @@ import {
6
6
  input,
7
7
  signal,
8
8
  } from '@angular/core';
9
+ import { $locale, t as translate, type TextKey } from '@block-auth.io/core';
9
10
  import { injectBlockAuthClient } from '../../provide-block-auth';
10
11
  import { injectStore } from '../../inject-store';
11
12
  import { TxnComponent } from './internals/txn.component';
12
13
  import { TxnTypeComponent } from './internals/txn-type.component';
13
14
  import { TxnErrorComponent } from './internals/txn-error.component';
14
15
  import { PermissionsRequiredModalComponent } from './internals/permissions-required-modal.component';
15
- import { SolicitedDataModalComponent, type ParamChange } from './internals/solicited-data-modal.component';
16
+ import {
17
+ SolicitedDataModalComponent,
18
+ type ParamChange,
19
+ } from './internals/solicited-data-modal.component';
16
20
 
17
21
  // Orquestador de txns: lee las solicitudes pendientes de `$ws.requests` y la
18
22
  // config de UI de `$ui.uiConfig` (vía injectStore). Se conserva la navegación
19
23
  // prev/next, la composición de overlays permisos/datos-solicitados, la
20
24
  // derivación de required/optional params desde `uiConfig` y las clases `bas-`.
21
- // Se elimina `useLocale` (textos inline) y los wrappers framer-motion.
25
+ // Se elimina los wrappers framer-motion.
22
26
  interface TxnPreParams {
23
27
  iconUrl?: string;
24
28
  domain?: string;
@@ -31,14 +35,16 @@ interface TxnPreParams {
31
35
  }
32
36
 
33
37
  const accountShort = (addr: string | null | undefined): string =>
34
- addr && addr.length > 10 ? `${addr.slice(0, 6)}…${addr.slice(-4)}` : (addr ?? '');
35
-
36
- const TXN_ERROR_TEXTS: Record<string, string> = {
37
- no_transactions_found: 'No transactions found',
38
- no_app_key_found: 'No application key found',
39
- no_contract_address_found: 'No contract address found',
40
- no_transactions_params_found: 'No transaction params found',
41
- unknown: 'Something went wrong',
38
+ addr && addr.length > 10
39
+ ? `${addr.slice(0, 6)}…${addr.slice(-4)}`
40
+ : addr ?? '';
41
+
42
+ const TXN_ERROR_TEXT_KEYS: Record<string, TextKey> = {
43
+ no_transactions_found: 'noTransactionsFound',
44
+ no_app_key_found: 'noAppKeyFound',
45
+ no_contract_address_found: 'noContractAddressFound',
46
+ no_transactions_params_found: 'noTransactionsParamsFound',
47
+ unknown: 'somethingWentWrong',
42
48
  };
43
49
 
44
50
  @Component({
@@ -58,79 +64,80 @@ const TXN_ERROR_TEXTS: Record<string, string> = {
58
64
  class="bas-flex bas-box-border bas-relative bas-h-full bas-w-full bas-gap-6 bas-flex-col bas-items-center bas-p-6 bas-font-medium bas-justify-center"
59
65
  >
60
66
  @if (isEmpty() && isSigning()) {
61
- <ba-txn-error
62
- errorMessageTxt="No transactions found"
63
- errorMessageSlug="no_transactions_found"
64
- [closeTxns]="closeTxns()"
65
- [isCountdown]="false"
66
- />
67
+ <ba-txn-error
68
+ [errorMessageTxt]="t('noTransactionsFound')"
69
+ errorMessageSlug="no_transactions_found"
70
+ [closeTxns]="closeTxns()"
71
+ [isCountdown]="false"
72
+ />
67
73
  } @else if (!isEmpty() && error().value) {
68
- <ba-txn-error
69
- [errorMessageTxt]="errorText()"
70
- [errorMessageSlug]="error().messageSlug"
74
+ <ba-txn-error
75
+ [errorMessageTxt]="errorText()"
76
+ [errorMessageSlug]="error().messageSlug"
77
+ [closeTxns]="closeTxns()"
78
+ [isCountdown]="false"
79
+ />
80
+ } @else if (!isEmpty()) {
81
+ <div class="bas-flex bas-flex-col bas-items-center bas-gap-3">
82
+ <img
83
+ [src]="
84
+ preParams().iconUrl ||
85
+ 'https://dashboard.block-auth.io/img/blockauth_logo.png'
86
+ "
87
+ class="bas-h-16"
88
+ alt="app logo"
89
+ />
90
+ <h2 class="bas-m-0 bas-text-base bas-text-[var(--bal-primary-600)]">
91
+ {{ preParams().domain || 'dashboard.block-auth.io' }}
92
+ </h2>
93
+ <p class="bas-m-0 bas-text-lg bas-text-[var(--bal-primary-800)]">
94
+ {{ preParams().appName || 'Block-Auth' }}
95
+ <span class="bas-text-base bas-text-[var(--bal-primary-500)]">{{
96
+ t('solicitsTo')
97
+ }}</span>
98
+ {{ accountShort(did()) }}
99
+ </p>
100
+ </div>
101
+ <ba-txn-type
102
+ [txnType]="preParams().txnType || t('login')"
103
+ [txnsLength]="txns().length"
104
+ (prevTxnClicked)="onPrevTxnClicked()"
105
+ (nextTxnClicked)="onNextTxnClicked()"
106
+ />
107
+ <div class="bas-h-[334px] bas-w-full bas-max-w-[330px]">
108
+ <ba-txn
109
+ [hasSolicitedData]="hasSolicitedData()"
110
+ [allRequiredFilled]="allRequiredFilled()"
71
111
  [closeTxns]="closeTxns()"
72
- [isCountdown]="false"
112
+ [selectedTxnPreParams]="preParams()"
113
+ [userParamValues]="userParamValues()"
114
+ [addError]="addError"
115
+ (permissionsRequiredClicked)="showPermissions.set(true)"
116
+ (solicitedDataClicked)="showSolicitedData.set(true)"
73
117
  />
74
- } @else if (!isEmpty()) {
75
- <div class="bas-flex bas-flex-col bas-items-center bas-gap-3">
76
- <img
77
- [src]="preParams().iconUrl || 'https://dashboard.block-auth.io/img/blockauth_logo.png'"
78
- class="bas-h-16"
79
- alt="app logo"
80
- />
81
- <h2 class="bas-m-0 bas-text-base bas-text-[var(--bal-primary-600)]">
82
- {{ preParams().domain || 'dashboard.block-auth.io' }}
83
- </h2>
84
- <p class="bas-m-0 bas-text-lg bas-text-[var(--bal-primary-800)]">
85
- {{ preParams().appName || 'Block-Auth' }}
86
- <span class="bas-text-base bas-text-[var(--bal-primary-500)]">solicits to</span>
87
- {{ accountShort(did()) }}
88
- </p>
89
- </div>
90
- <ba-txn-type
91
- [txnType]="preParams().txnType || 'Log In'"
92
- [txnsLength]="txns().length"
93
- (prevTxnClicked)="onPrevTxnClicked()"
94
- (nextTxnClicked)="onNextTxnClicked()"
118
+ </div>
119
+ } @if (showPermissions()) {
120
+ <div
121
+ class="bal-ba-transition-2 bal-ba-backdrop-blur-base bal-ba-backdrop-blur-sm bas-absolute bas-z-30 bas-box-border bas-h-full bas-w-full"
122
+ (click)="showPermissions.set(false)"
123
+ ></div>
124
+ <div class="bas-absolute bas-top-44 bas-z-40">
125
+ <ba-permissions-required-modal />
126
+ </div>
127
+ } @if (showSolicitedData()) {
128
+ <div
129
+ class="bal-ba-transition-2 bal-ba-backdrop-blur-base bal-ba-backdrop-blur-sm bas-absolute bas-z-30 bas-box-border bas-h-full bas-w-full"
130
+ (click)="showSolicitedData.set(false)"
131
+ ></div>
132
+ <div class="bas-absolute bas-top-32 bas-z-40">
133
+ <ba-solicited-data-modal
134
+ [requiredParams]="requiredParams()"
135
+ [optionalParams]="optionalParams()"
136
+ [userParamValues]="userParamValues()"
137
+ (paramChange)="handleParamChange($event)"
138
+ (validationChange)="allRequiredFilled.set($event)"
95
139
  />
96
- <div class="bas-h-[334px] bas-w-full bas-max-w-[330px]">
97
- <ba-txn
98
- [hasSolicitedData]="hasSolicitedData()"
99
- [allRequiredFilled]="allRequiredFilled()"
100
- [closeTxns]="closeTxns()"
101
- [selectedTxnPreParams]="preParams()"
102
- [userParamValues]="userParamValues()"
103
- [addError]="addError"
104
- (permissionsRequiredClicked)="showPermissions.set(true)"
105
- (solicitedDataClicked)="showSolicitedData.set(true)"
106
- />
107
- </div>
108
- }
109
-
110
- @if (showPermissions()) {
111
- <div
112
- class="bal-ba-transition-2 bal-ba-backdrop-blur-base bal-ba-backdrop-blur-sm bas-absolute bas-z-30 bas-box-border bas-h-full bas-w-full"
113
- (click)="showPermissions.set(false)"
114
- ></div>
115
- <div class="bas-absolute bas-top-44 bas-z-40">
116
- <ba-permissions-required-modal />
117
- </div>
118
- }
119
-
120
- @if (showSolicitedData()) {
121
- <div
122
- class="bal-ba-transition-2 bal-ba-backdrop-blur-base bal-ba-backdrop-blur-sm bas-absolute bas-z-30 bas-box-border bas-h-full bas-w-full"
123
- (click)="showSolicitedData.set(false)"
124
- ></div>
125
- <div class="bas-absolute bas-top-32 bas-z-40">
126
- <ba-solicited-data-modal
127
- [requiredParams]="requiredParams()"
128
- [optionalParams]="optionalParams()"
129
- [userParamValues]="userParamValues()"
130
- (paramChange)="handleParamChange($event)"
131
- (validationChange)="allRequiredFilled.set($event)"
132
- />
133
- </div>
140
+ </div>
134
141
  }
135
142
  </div>
136
143
  `,
@@ -141,6 +148,8 @@ export class TxnsOverviewComponent {
141
148
  private readonly client = injectBlockAuthClient();
142
149
  private readonly ws = injectStore(this.client.state.$ws);
143
150
  private readonly ui = injectStore(this.client.state.$ui);
151
+ private readonly locale = injectStore($locale);
152
+ protected t = (key: TextKey): string => translate(key, this.locale());
144
153
 
145
154
  protected readonly accountShort = accountShort;
146
155
 
@@ -158,8 +167,11 @@ export class TxnsOverviewComponent {
158
167
  protected readonly did = computed(() => this.ws().did);
159
168
  protected readonly isSigning = computed(() => this.ui().isSigning);
160
169
  protected readonly isEmpty = computed(() => this.txns().length === 0);
161
- protected readonly errorText = computed(
162
- () => TXN_ERROR_TEXTS[this.error().messageSlug] ?? TXN_ERROR_TEXTS['unknown'],
170
+ protected readonly errorText = computed(() =>
171
+ this.t(
172
+ TXN_ERROR_TEXT_KEYS[this.error().messageSlug] ??
173
+ TXN_ERROR_TEXT_KEYS['unknown'],
174
+ ),
163
175
  );
164
176
 
165
177
  private readonly selectedTxn = computed(
@@ -177,22 +189,28 @@ export class TxnsOverviewComponent {
177
189
 
178
190
  private readonly requiredOptionalParams = computed(() => {
179
191
  const cfg =
180
- (this.ui().uiConfig as { config?: Record<string, unknown> } | null)?.config ?? {};
192
+ (this.ui().uiConfig as { config?: Record<string, unknown> } | null)
193
+ ?.config ?? {};
181
194
  const required: string[] = [];
182
195
  const optional: string[] = [];
183
- const kycMode = (cfg['modules'] as { kyc?: { mode?: string } } | undefined)?.kyc?.mode;
196
+ const kycMode = (cfg['modules'] as { kyc?: { mode?: string } } | undefined)
197
+ ?.kyc?.mode;
184
198
  if (kycMode === 'required') required.push('modules-kyc');
185
199
  else if (kycMode === 'optional') optional.push('modules-kyc');
186
- Object.entries((cfg['params'] as Record<string, { mode?: string }>) || {}).forEach(
187
- ([slug, paramCfg]) => {
188
- if (paramCfg.mode === 'required') required.push(slug);
189
- else if (paramCfg.mode === 'optional') optional.push(slug);
190
- },
191
- );
200
+ Object.entries(
201
+ (cfg['params'] as Record<string, { mode?: string }>) || {},
202
+ ).forEach(([slug, paramCfg]) => {
203
+ if (paramCfg.mode === 'required') required.push(slug);
204
+ else if (paramCfg.mode === 'optional') optional.push(slug);
205
+ });
192
206
  return { requiredParams: required, optionalParams: optional };
193
207
  });
194
- protected readonly requiredParams = computed(() => this.requiredOptionalParams().requiredParams);
195
- protected readonly optionalParams = computed(() => this.requiredOptionalParams().optionalParams);
208
+ protected readonly requiredParams = computed(
209
+ () => this.requiredOptionalParams().requiredParams,
210
+ );
211
+ protected readonly optionalParams = computed(
212
+ () => this.requiredOptionalParams().optionalParams,
213
+ );
196
214
  protected readonly hasSolicitedData = computed(
197
215
  () => this.requiredParams().length > 0 || this.optionalParams().length > 0,
198
216
  );