@kahitsan/ksui 0.32.0 → 0.34.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.
- package/package.json +4 -1
- package/src/components/base/AccountAvatar.tsx +1 -1
- package/src/components/base/AddAttachmentTile.tsx +4 -4
- package/src/components/base/BadgeSelect.tsx +10 -10
- package/src/components/base/Button.tsx +70 -38
- package/src/components/base/CameraCapture.tsx +8 -8
- package/src/components/base/ChartLegend.tsx +3 -3
- package/src/components/base/CopyButton.tsx +1 -1
- package/src/components/base/DataTable.tsx +68 -72
- package/src/components/base/DatePicker.tsx +77 -82
- package/src/components/base/DateTile.tsx +7 -7
- package/src/components/base/DetailRow.tsx +2 -2
- package/src/components/base/Dropdown.tsx +15 -15
- package/src/components/base/ExistingAttachmentTile.tsx +6 -6
- package/src/components/base/EyebrowBadge.tsx +15 -15
- package/src/components/base/FormErrorBanner.tsx +1 -1
- package/src/components/base/FormField.tsx +2 -2
- package/src/components/base/ImageCropper.tsx +19 -19
- package/src/components/base/ImageViewer.tsx +7 -11
- package/src/components/base/KpiCard.tsx +7 -7
- package/src/components/base/Modal.tsx +10 -14
- package/src/components/base/ProgressBar.tsx +63 -59
- package/src/components/base/RadioCardGroup.tsx +13 -6
- package/src/components/base/SectionHeading.tsx +4 -4
- package/src/components/base/SegmentedFilter.tsx +60 -4
- package/src/components/base/SocialLinksBar.tsx +3 -15
- package/src/components/base/StatusIndicator.tsx +11 -11
- package/src/components/base/StatusPill.tsx +15 -15
- package/src/components/base/TagPill.tsx +1 -1
- package/src/components/base/ThemeToggle.tsx +12 -20
- package/src/components/base/Tooltip.tsx +2 -2
- package/src/components/composite/AccountRadioPicker.tsx +8 -4
- package/src/components/composite/ComboBox.tsx +51 -44
- package/src/components/composite/CustomRenderer.tsx +6 -13
- package/src/components/composite/FileField.tsx +10 -17
- package/src/components/composite/FlowGraph.tsx +29 -36
- package/src/components/composite/FlowRunner.tsx +11 -18
- package/src/components/composite/FormAdvancedSection.tsx +31 -31
- package/src/components/composite/LiveTimer.tsx +3 -3
- package/src/components/composite/MarkdownNotes.tsx +11 -11
- package/src/components/composite/MentionTextarea.tsx +10 -10
- package/src/components/composite/NotFound.tsx +7 -7
- package/src/components/composite/PaymentAccountPicker.tsx +19 -19
- package/src/components/composite/SalesBodyEditor.tsx +26 -26
- package/src/components/composite/SearchableSelect.tsx +14 -14
- package/src/components/composite/SecretReveal.tsx +5 -5
- package/src/components/composite/TransactionAccountFields.tsx +78 -0
- package/src/components/composite/TransactionForm.tsx +83 -169
- package/src/components/composite/TransactionPayableFields.tsx +97 -0
- package/src/components/composite/TransferAccountsPicker.tsx +20 -20
- package/src/components/composite/TransferFeeChip.tsx +2 -2
- package/src/components/composite/VoucherPicker.tsx +25 -25
- package/src/components/composite/resource/ResourceForm.tsx +1 -1
- package/src/components/composite/resource/ResourcePage.tsx +8 -8
- package/src/components/composite/resource/cells.tsx +6 -3
- package/src/components/composite/resource/form-spec.ts +141 -0
- package/src/components/composite/resource/route-adapter.ts +63 -0
- package/src/components/composite/resource/route-spec.test.ts +125 -0
- package/src/components/composite/resource/route-spec.ts +158 -0
- package/src/index.ts +8 -0
- package/src/utils/INPUT_CLASS.ts +6 -3
- package/src/utils/account-icons.ts +29 -7
- package/src/utils/highlight.tsx +5 -6
- package/src/utils/inject-css.ts +13 -0
- package/tailwind.js +11 -7
|
@@ -222,8 +222,8 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
222
222
|
});
|
|
223
223
|
|
|
224
224
|
return (
|
|
225
|
-
<div class="rounded-lg border border-
|
|
226
|
-
<div class="flex items-center justify-between gap-2 text-[10px] uppercase tracking-widest text-
|
|
225
|
+
<div class="rounded-lg border border-[color-mix(in_srgb,var(--ks-success,#10b981)_20%,transparent)] bg-[color-mix(in_srgb,var(--ks-success,#10b981)_5%,transparent)] p-3 space-y-3">
|
|
226
|
+
<div class="flex items-center justify-between gap-2 text-[10px] uppercase tracking-widest text-[var(--ks-success-fg,#34d399)] font-semibold">
|
|
227
227
|
<span class="flex items-center gap-1.5">
|
|
228
228
|
<PackageIcon size={12} />
|
|
229
229
|
Packages availed
|
|
@@ -231,7 +231,7 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
231
231
|
<button
|
|
232
232
|
type="button"
|
|
233
233
|
onClick={() => setPickerOpen((v) => !v)}
|
|
234
|
-
class="ks-interactive inline-flex items-center gap-1 rounded-md border border-
|
|
234
|
+
class="ks-interactive inline-flex items-center gap-1 rounded-md border border-[color-mix(in_srgb,var(--ks-success,#10b981)_30%,transparent)] bg-[color-mix(in_srgb,var(--ks-success,#10b981)_10%,transparent)] px-2 py-0.5 text-[10px] uppercase tracking-widest text-[var(--ks-success-fg,#34d399)] hover:bg-[color-mix(in_srgb,var(--ks-success,#10b981)_20%,transparent)]"
|
|
235
235
|
>
|
|
236
236
|
<Plus size={10} /> Add package
|
|
237
237
|
</button>
|
|
@@ -240,23 +240,23 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
240
240
|
<Show when={pickerOpen()}>
|
|
241
241
|
<Show
|
|
242
242
|
when={!loading()}
|
|
243
|
-
fallback={<div class="text-xs text-
|
|
243
|
+
fallback={<div class="text-xs text-[var(--ks-fg-subtle,#71717a)]">Loading packages…</div>}
|
|
244
244
|
>
|
|
245
245
|
<Show
|
|
246
246
|
when={!loadError()}
|
|
247
|
-
fallback={<div class="text-xs text-
|
|
247
|
+
fallback={<div class="text-xs text-[var(--ks-danger-fg,#f87171)]">{loadError()}</div>}
|
|
248
248
|
>
|
|
249
249
|
<Show
|
|
250
250
|
when={packages().length > 0}
|
|
251
251
|
fallback={
|
|
252
|
-
<div class="text-xs text-
|
|
252
|
+
<div class="text-xs text-[var(--ks-fg-subtle,#71717a)]">No active packages.</div>
|
|
253
253
|
}
|
|
254
254
|
>
|
|
255
|
-
<div class="space-y-1.5 max-h-56 overflow-y-auto rounded-md border border-
|
|
255
|
+
<div class="space-y-1.5 max-h-56 overflow-y-auto rounded-md border border-[color-mix(in_srgb,var(--ks-success,#10b981)_15%,transparent)] p-2 bg-[color-mix(in_srgb,var(--ks-bg,#0a0a0a)_40%,transparent)]">
|
|
256
256
|
<For each={packages()}>
|
|
257
257
|
{(pkg) => (
|
|
258
258
|
<div class="space-y-1">
|
|
259
|
-
<div class="text-[11px] uppercase tracking-widest text-
|
|
259
|
+
<div class="text-[11px] uppercase tracking-widest text-[var(--ks-fg-muted,#a1a1aa)]">
|
|
260
260
|
{pkg.name}
|
|
261
261
|
</div>
|
|
262
262
|
<div class="flex flex-wrap gap-1.5">
|
|
@@ -265,7 +265,7 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
265
265
|
<button
|
|
266
266
|
type="button"
|
|
267
267
|
onClick={() => addVariant(pkg, v)}
|
|
268
|
-
class="ks-interactive inline-flex items-center gap-1 rounded border border-
|
|
268
|
+
class="ks-interactive inline-flex items-center gap-1 rounded border border-[var(--ks-input-border,#3f3f46)] bg-[var(--ks-input-bg,#18181b)] px-2 py-0.5 text-xs text-[var(--ks-fg,#ffffff)] hover:border-[color-mix(in_srgb,var(--ks-success,#10b981)_40%,transparent)] hover:text-[var(--ks-success-fg,#34d399)]"
|
|
269
269
|
>
|
|
270
270
|
{v.name} · {formatPHP(Number(v.price))}
|
|
271
271
|
</button>
|
|
@@ -284,7 +284,7 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
284
284
|
<Show
|
|
285
285
|
when={props.items.length > 0}
|
|
286
286
|
fallback={
|
|
287
|
-
<div class="text-xs text-
|
|
287
|
+
<div class="text-xs text-[var(--ks-fg-subtle,#71717a)]">No packages added yet.</div>
|
|
288
288
|
}
|
|
289
289
|
>
|
|
290
290
|
<div class="space-y-1.5">
|
|
@@ -292,11 +292,11 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
292
292
|
{(line) => (
|
|
293
293
|
<div class="flex items-center gap-2 text-sm">
|
|
294
294
|
<div class="min-w-0 flex-1">
|
|
295
|
-
<div class="text-
|
|
295
|
+
<div class="text-[var(--ks-fg,#ffffff)] truncate">
|
|
296
296
|
{line.package_name}{" "}
|
|
297
|
-
<span class="text-
|
|
297
|
+
<span class="text-[var(--ks-fg-subtle,#71717a)]">· {line.variant_name}</span>
|
|
298
298
|
</div>
|
|
299
|
-
<div class="text-[11px] text-
|
|
299
|
+
<div class="text-[11px] text-[var(--ks-fg-subtle,#71717a)] tabular-nums">
|
|
300
300
|
{formatPHP(line.unit_price)} · {line.duration_value}{" "}
|
|
301
301
|
{line.duration_unit}
|
|
302
302
|
{line.duration_value !== 1 ? "s" : ""}
|
|
@@ -306,30 +306,30 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
306
306
|
<button
|
|
307
307
|
type="button"
|
|
308
308
|
onClick={() => adjust(line.key, -1)}
|
|
309
|
-
class="ks-interactive flex h-6 w-6 items-center justify-center rounded border border-
|
|
309
|
+
class="ks-interactive flex h-6 w-6 items-center justify-center rounded border border-[var(--ks-input-border,#3f3f46)] bg-[var(--ks-input-bg,#18181b)] text-[var(--ks-fg,#ffffff)] hover:border-[color-mix(in_srgb,var(--ks-success,#10b981)_40%,transparent)]"
|
|
310
310
|
aria-label="Decrease quantity"
|
|
311
311
|
>
|
|
312
312
|
<Minus size={12} />
|
|
313
313
|
</button>
|
|
314
|
-
<span class="w-6 text-center text-sm text-
|
|
314
|
+
<span class="w-6 text-center text-sm text-[var(--ks-fg,#ffffff)] tabular-nums">
|
|
315
315
|
{line.quantity}
|
|
316
316
|
</span>
|
|
317
317
|
<button
|
|
318
318
|
type="button"
|
|
319
319
|
onClick={() => adjust(line.key, 1)}
|
|
320
|
-
class="ks-interactive flex h-6 w-6 items-center justify-center rounded border border-
|
|
320
|
+
class="ks-interactive flex h-6 w-6 items-center justify-center rounded border border-[var(--ks-input-border,#3f3f46)] bg-[var(--ks-input-bg,#18181b)] text-[var(--ks-fg,#ffffff)] hover:border-[color-mix(in_srgb,var(--ks-success,#10b981)_40%,transparent)]"
|
|
321
321
|
aria-label="Increase quantity"
|
|
322
322
|
>
|
|
323
323
|
<Plus size={12} />
|
|
324
324
|
</button>
|
|
325
325
|
</div>
|
|
326
|
-
<div class="w-20 text-right text-
|
|
326
|
+
<div class="w-20 text-right text-[var(--ks-fg,#ffffff)] tabular-nums whitespace-nowrap">
|
|
327
327
|
{formatPHP(line.unit_price * line.quantity)}
|
|
328
328
|
</div>
|
|
329
329
|
<button
|
|
330
330
|
type="button"
|
|
331
331
|
onClick={() => remove(line.key)}
|
|
332
|
-
class="ks-interactive flex h-6 w-6 items-center justify-center rounded text-
|
|
332
|
+
class="ks-interactive flex h-6 w-6 items-center justify-center rounded text-[var(--ks-fg-subtle,#71717a)] hover:text-[var(--ks-danger-fg,#f87171)]"
|
|
333
333
|
aria-label="Remove line"
|
|
334
334
|
>
|
|
335
335
|
<Trash2 size={12} />
|
|
@@ -342,7 +342,7 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
342
342
|
|
|
343
343
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
|
344
344
|
<div>
|
|
345
|
-
<label class="text-[10px] uppercase tracking-widest text-
|
|
345
|
+
<label class="text-[10px] uppercase tracking-widest text-[var(--ks-fg-subtle,#71717a)] font-semibold">
|
|
346
346
|
Billed to
|
|
347
347
|
</label>
|
|
348
348
|
<ComboBox<ClientOption>
|
|
@@ -360,7 +360,7 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
360
360
|
/>
|
|
361
361
|
</div>
|
|
362
362
|
<div>
|
|
363
|
-
<label class="text-[10px] uppercase tracking-widest text-
|
|
363
|
+
<label class="text-[10px] uppercase tracking-widest text-[var(--ks-fg-subtle,#71717a)] font-semibold">
|
|
364
364
|
Voucher
|
|
365
365
|
</label>
|
|
366
366
|
<VoucherPicker
|
|
@@ -377,7 +377,7 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
377
377
|
|
|
378
378
|
<Show when={!props.voucher}>
|
|
379
379
|
<div>
|
|
380
|
-
<label class="text-[10px] uppercase tracking-widest text-
|
|
380
|
+
<label class="text-[10px] uppercase tracking-widest text-[var(--ks-fg-subtle,#71717a)] font-semibold">
|
|
381
381
|
Manual discount (₱)
|
|
382
382
|
</label>
|
|
383
383
|
<input
|
|
@@ -386,19 +386,19 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
386
386
|
step="0.01"
|
|
387
387
|
value={props.manualDiscount}
|
|
388
388
|
onInput={(e) => props.setManualDiscount(e.currentTarget.value)}
|
|
389
|
-
class="mt-0.5 w-full rounded-md border border-
|
|
389
|
+
class="mt-0.5 w-full rounded-md border border-[var(--ks-input-border,#3f3f46)] bg-[var(--ks-input-bg,#18181b)] px-2 py-1 text-sm text-[var(--ks-fg,#ffffff)] tabular-nums focus:border-[color-mix(in_srgb,var(--ks-success,#10b981)_50%,transparent)] focus:outline-none"
|
|
390
390
|
/>
|
|
391
391
|
</div>
|
|
392
392
|
</Show>
|
|
393
393
|
|
|
394
394
|
<Show when={props.items.length > 0}>
|
|
395
|
-
<div class="border-t border-
|
|
396
|
-
<div class="flex items-center justify-between text-
|
|
395
|
+
<div class="border-t border-[color-mix(in_srgb,var(--ks-success,#10b981)_15%,transparent)] pt-2 text-xs space-y-0.5 tabular-nums">
|
|
396
|
+
<div class="flex items-center justify-between text-[var(--ks-fg-muted,#a1a1aa)]">
|
|
397
397
|
<span>Subtotal</span>
|
|
398
398
|
<span>{formatPHP(subtotal())}</span>
|
|
399
399
|
</div>
|
|
400
400
|
<Show when={effectiveDiscount() > 0}>
|
|
401
|
-
<div class="flex items-center justify-between text-
|
|
401
|
+
<div class="flex items-center justify-between text-[var(--ks-fg-muted,#a1a1aa)]">
|
|
402
402
|
<span>
|
|
403
403
|
{props.voucher
|
|
404
404
|
? `Voucher ${props.voucher.code}`
|
|
@@ -407,7 +407,7 @@ export default function SalesBodyEditor(props: SalesBodyEditorProps) {
|
|
|
407
407
|
<span>− {formatPHP(effectiveDiscount())}</span>
|
|
408
408
|
</div>
|
|
409
409
|
</Show>
|
|
410
|
-
<div class="flex items-center justify-between text-
|
|
410
|
+
<div class="flex items-center justify-between text-[var(--ks-fg,#ffffff)] font-semibold">
|
|
411
411
|
<span>Total</span>
|
|
412
412
|
<span>{formatPHP(total())}</span>
|
|
413
413
|
</div>
|
|
@@ -151,7 +151,7 @@ export default function SearchableSelect(props: SearchableSelectProps): JSX.Elem
|
|
|
151
151
|
onClick={() => !props.disabled && setOpen((o) => !o)}
|
|
152
152
|
class={
|
|
153
153
|
props.triggerClass ??
|
|
154
|
-
"inline-flex items-center gap-1.5 text-xs text-
|
|
154
|
+
"inline-flex items-center gap-1.5 text-xs text-[var(--ks-fg,#ffffff)] bg-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] hover:bg-[var(--ks-surface-raised,#1a1a1a)] border border-[color-mix(in_srgb,var(--ks-border-strong,#3f3f46)_60%,transparent)] rounded px-2 py-1 cursor-pointer transition-colors"
|
|
155
155
|
}
|
|
156
156
|
classList={{ "cursor-not-allowed opacity-60": props.disabled }}
|
|
157
157
|
title={props.disabled ? undefined : "Click to select"}
|
|
@@ -160,33 +160,33 @@ export default function SearchableSelect(props: SearchableSelectProps): JSX.Elem
|
|
|
160
160
|
{busy()
|
|
161
161
|
? "…"
|
|
162
162
|
: (currentLabel() ?? (
|
|
163
|
-
<span class="text-
|
|
163
|
+
<span class="text-[var(--ks-fg-subtle,#71717a)] italic">{props.placeholder ?? "Select…"}</span>
|
|
164
164
|
))}
|
|
165
165
|
</span>
|
|
166
|
-
<ChevronsUpDown size={12} class="text-
|
|
166
|
+
<ChevronsUpDown size={12} class="text-[var(--ks-fg-subtle,#71717a)] shrink-0" />
|
|
167
167
|
</button>
|
|
168
168
|
<Show when={open()}>
|
|
169
169
|
<Portal>
|
|
170
170
|
<div
|
|
171
171
|
ref={popupRef}
|
|
172
|
-
class="z-[10000] rounded-md border border-
|
|
172
|
+
class="z-[10000] rounded-md border border-[var(--ks-input-border,#3f3f46)] bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_95%,transparent)] backdrop-blur shadow-xl overflow-hidden flex flex-col"
|
|
173
173
|
style={popupStyle()}
|
|
174
174
|
>
|
|
175
|
-
<div class="px-2 py-1.5 border-b border-
|
|
175
|
+
<div class="px-2 py-1.5 border-b border-[var(--ks-border-strong,#3f3f46)]">
|
|
176
176
|
<input
|
|
177
177
|
ref={inputRef}
|
|
178
178
|
type="text"
|
|
179
179
|
value={query()}
|
|
180
180
|
onInput={(e) => setQuery(e.currentTarget.value)}
|
|
181
181
|
placeholder={props.searchPlaceholder ?? "Search…"}
|
|
182
|
-
class="w-full px-2 py-1 text-xs bg-
|
|
182
|
+
class="w-full px-2 py-1 text-xs bg-[var(--ks-bg,#0a0a0a)] border border-[var(--ks-border-strong,#3f3f46)] rounded text-[var(--ks-fg,#ffffff)] placeholder:text-[var(--ks-input-placeholder,#71717a)] focus:outline-none focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)]"
|
|
183
183
|
/>
|
|
184
184
|
</div>
|
|
185
185
|
<div class="flex-1 overflow-y-auto">
|
|
186
186
|
<Show
|
|
187
187
|
when={!props.loading && filtered().length > 0}
|
|
188
188
|
fallback={
|
|
189
|
-
<div class="px-3 py-3 text-xs text-
|
|
189
|
+
<div class="px-3 py-3 text-xs text-[var(--ks-fg-subtle,#71717a)] text-center">{emptyStateLabel()}</div>
|
|
190
190
|
}
|
|
191
191
|
>
|
|
192
192
|
<For each={filtered()}>
|
|
@@ -196,22 +196,22 @@ export default function SearchableSelect(props: SearchableSelectProps): JSX.Elem
|
|
|
196
196
|
<button
|
|
197
197
|
type="button"
|
|
198
198
|
onClick={() => select(opt)}
|
|
199
|
-
class="w-full text-left px-3 py-2 text-xs hover:bg-
|
|
199
|
+
class="w-full text-left px-3 py-2 text-xs hover:bg-[color-mix(in_srgb,var(--ks-primary,#c9a961)_10%,transparent)] transition-colors flex items-center justify-between gap-2"
|
|
200
200
|
classList={{
|
|
201
|
-
"text-
|
|
202
|
-
"text-
|
|
201
|
+
"text-[var(--ks-accent,#fbbf24)]": selected(),
|
|
202
|
+
"text-[var(--ks-fg,#ffffff)]": !selected(),
|
|
203
203
|
}}
|
|
204
204
|
>
|
|
205
205
|
<span class="flex flex-col min-w-0">
|
|
206
206
|
<span class="font-medium truncate">{opt.label}</span>
|
|
207
207
|
<Show when={opt.description}>
|
|
208
|
-
<span class="text-[10px] text-
|
|
208
|
+
<span class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] truncate">
|
|
209
209
|
{opt.description}
|
|
210
210
|
</span>
|
|
211
211
|
</Show>
|
|
212
212
|
</span>
|
|
213
213
|
<Show when={selected()}>
|
|
214
|
-
<span class="text-
|
|
214
|
+
<span class="text-[var(--ks-accent,#fbbf24)] shrink-0">✓</span>
|
|
215
215
|
</Show>
|
|
216
216
|
</button>
|
|
217
217
|
);
|
|
@@ -220,11 +220,11 @@ export default function SearchableSelect(props: SearchableSelectProps): JSX.Elem
|
|
|
220
220
|
</Show>
|
|
221
221
|
</div>
|
|
222
222
|
<Show when={props.allowClear && props.value != null && props.value !== ""}>
|
|
223
|
-
<div class="border-t border-
|
|
223
|
+
<div class="border-t border-[var(--ks-border-strong,#3f3f46)]">
|
|
224
224
|
<button
|
|
225
225
|
type="button"
|
|
226
226
|
onClick={() => select(null)}
|
|
227
|
-
class="w-full text-left px-3 py-2 text-xs text-
|
|
227
|
+
class="w-full text-left px-3 py-2 text-xs text-[var(--ks-danger-fg,#f87171)] hover:bg-[color-mix(in_srgb,var(--ks-danger,#ef4444)_10%,transparent)] transition-colors flex items-center gap-2"
|
|
228
228
|
>
|
|
229
229
|
<X size={12} />
|
|
230
230
|
<span>Clear selection</span>
|
|
@@ -34,19 +34,19 @@ const SecretReveal: Component<SecretRevealProps> = (props) => {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
|
-
<div class="mb-4 p-3 rounded-lg bg-
|
|
38
|
-
<p class="text-xs text-
|
|
37
|
+
<div class="mb-4 p-3 rounded-lg bg-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_10%,transparent)] border border-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_20%,transparent)]">
|
|
38
|
+
<p class="text-xs text-[var(--ks-accent-hover,#fcd34d)] font-medium mb-2">{local.warning ?? DEFAULT_WARNING}</p>
|
|
39
39
|
<div class="flex items-center gap-2">
|
|
40
40
|
<code
|
|
41
41
|
data-testid={local.testId}
|
|
42
|
-
class="flex-1 px-2 py-1.5 rounded bg-
|
|
42
|
+
class="flex-1 px-2 py-1.5 rounded bg-[var(--ks-input-bg,#18181b)] border border-[var(--ks-input-border,#3f3f46)] text-[var(--ks-accent-hover,#fcd34d)] text-xs font-mono break-all"
|
|
43
43
|
>
|
|
44
44
|
{local.secret}
|
|
45
45
|
</code>
|
|
46
46
|
<button
|
|
47
47
|
type="button"
|
|
48
48
|
onClick={copy}
|
|
49
|
-
class="ks-interactive px-2 py-1.5 rounded bg-
|
|
49
|
+
class="ks-interactive px-2 py-1.5 rounded bg-[var(--ks-surface-raised,#1a1a1a)] border border-[var(--ks-input-border,#3f3f46)] text-[var(--ks-fg-muted,#a1a1aa)] hover:text-[var(--ks-fg,#ffffff)] text-xs flex items-center gap-1"
|
|
50
50
|
aria-label="Copy secret"
|
|
51
51
|
>
|
|
52
52
|
<Copy size={12} />
|
|
@@ -54,7 +54,7 @@ const SecretReveal: Component<SecretRevealProps> = (props) => {
|
|
|
54
54
|
</button>
|
|
55
55
|
</div>
|
|
56
56
|
<Show when={local.caption}>
|
|
57
|
-
<p class="text-[10px] text-
|
|
57
|
+
<p class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] mt-2">{local.caption}</p>
|
|
58
58
|
</Show>
|
|
59
59
|
</div>
|
|
60
60
|
);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Show } from "solid-js";
|
|
2
|
+
import type { PaymentAccountOption } from "./PaymentAccountPicker";
|
|
3
|
+
import FormField from "../base/FormField";
|
|
4
|
+
import AccountRadioPicker from "./AccountRadioPicker";
|
|
5
|
+
import TransferAccountsPicker from "./TransferAccountsPicker";
|
|
6
|
+
|
|
7
|
+
export type TransactionFormAccount = PaymentAccountOption & {
|
|
8
|
+
balance?: number | string | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface TransactionAccountFieldsProps {
|
|
12
|
+
category: string;
|
|
13
|
+
accounts: TransactionFormAccount[];
|
|
14
|
+
accountLabel: string;
|
|
15
|
+
accountHint: string;
|
|
16
|
+
showSecondAccount: boolean;
|
|
17
|
+
secondAccountLabel?: string;
|
|
18
|
+
sourceAccount: string;
|
|
19
|
+
setSourceAccount: (v: string) => void;
|
|
20
|
+
destAccount: string;
|
|
21
|
+
setDestAccount: (v: string) => void;
|
|
22
|
+
amount: string;
|
|
23
|
+
transferFeeAmount: string;
|
|
24
|
+
transferFeeEnabled: boolean;
|
|
25
|
+
allowTransferFee: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The single-account-picker vs transfer-accounts-picker branch, split out of
|
|
30
|
+
* TransactionForm to keep that file under the file-size budget. `sale` writes
|
|
31
|
+
* to destAccount, every other category writes to sourceAccount — that
|
|
32
|
+
* category-driven single/second-account split is the only domain logic here.
|
|
33
|
+
*/
|
|
34
|
+
export default function TransactionAccountFields(props: TransactionAccountFieldsProps) {
|
|
35
|
+
return (
|
|
36
|
+
<Show
|
|
37
|
+
when={props.showSecondAccount}
|
|
38
|
+
fallback={
|
|
39
|
+
<FormField label={props.accountLabel}>
|
|
40
|
+
<AccountRadioPicker
|
|
41
|
+
accounts={props.accounts}
|
|
42
|
+
ariaLabel={props.accountLabel}
|
|
43
|
+
value={
|
|
44
|
+
props.category === "sale" ? props.destAccount : props.sourceAccount
|
|
45
|
+
}
|
|
46
|
+
onChange={(v) => {
|
|
47
|
+
if (props.category === "sale") {
|
|
48
|
+
props.setDestAccount(v);
|
|
49
|
+
props.setSourceAccount("");
|
|
50
|
+
} else {
|
|
51
|
+
props.setSourceAccount(v);
|
|
52
|
+
props.setDestAccount("");
|
|
53
|
+
}
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
<Show when={props.accountHint}>
|
|
57
|
+
<p class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] mt-0.5">
|
|
58
|
+
{props.accountHint}
|
|
59
|
+
</p>
|
|
60
|
+
</Show>
|
|
61
|
+
</FormField>
|
|
62
|
+
}
|
|
63
|
+
>
|
|
64
|
+
<TransferAccountsPicker
|
|
65
|
+
accounts={props.accounts}
|
|
66
|
+
sourceAccount={props.sourceAccount}
|
|
67
|
+
setSourceAccount={props.setSourceAccount}
|
|
68
|
+
destAccount={props.destAccount}
|
|
69
|
+
setDestAccount={props.setDestAccount}
|
|
70
|
+
sourceLabel={props.accountLabel}
|
|
71
|
+
destLabel={props.secondAccountLabel!}
|
|
72
|
+
amount={props.amount}
|
|
73
|
+
feeAmount={props.transferFeeAmount}
|
|
74
|
+
feeEnabled={props.transferFeeEnabled && props.allowTransferFee}
|
|
75
|
+
/>
|
|
76
|
+
</Show>
|
|
77
|
+
);
|
|
78
|
+
}
|