@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
|
@@ -24,11 +24,11 @@ import ArrowDownLeft from "lucide-solid/icons/arrow-down-left";
|
|
|
24
24
|
import ArrowUpRight from "lucide-solid/icons/arrow-up-right";
|
|
25
25
|
import ArrowRightLeft from "lucide-solid/icons/arrow-right-left";
|
|
26
26
|
|
|
27
|
-
import AccountRadioPicker from "./AccountRadioPicker";
|
|
28
27
|
import FormAdvancedSection from "./FormAdvancedSection";
|
|
29
28
|
import SalesBodyEditor, { type SalesLine } from "./SalesBodyEditor";
|
|
30
29
|
import TransferFeeChip from "./TransferFeeChip";
|
|
31
|
-
import
|
|
30
|
+
import TransactionAccountFields from "./TransactionAccountFields";
|
|
31
|
+
import TransactionPayableFields from "./TransactionPayableFields";
|
|
32
32
|
import ComboBox from "./ComboBox";
|
|
33
33
|
import type { ClientOption, PayeeOption, PayeeKind } from "./picker-types";
|
|
34
34
|
import VoucherPicker, { type VoucherOption } from "./VoucherPicker";
|
|
@@ -43,7 +43,6 @@ import ExistingAttachmentTile, {
|
|
|
43
43
|
import FormField from "../base/FormField";
|
|
44
44
|
import DatePicker from "../base/DatePicker";
|
|
45
45
|
import Button from "../base/Button";
|
|
46
|
-
import SegmentedFilter from "../base/SegmentedFilter";
|
|
47
46
|
import {
|
|
48
47
|
type PendingFile,
|
|
49
48
|
createPendingFile,
|
|
@@ -137,35 +136,27 @@ const TONE_CLASSES: Record<
|
|
|
137
136
|
{ bg: string; text: string; border: string }
|
|
138
137
|
> = {
|
|
139
138
|
emerald: {
|
|
140
|
-
bg: "bg-
|
|
141
|
-
text: "text-
|
|
142
|
-
border: "border-
|
|
139
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-success,#10b981)_10%,transparent)]",
|
|
140
|
+
text: "text-[var(--ks-success-fg,#34d399)]",
|
|
141
|
+
border: "border-[color-mix(in_srgb,var(--ks-success,#10b981)_30%,transparent)]",
|
|
142
|
+
},
|
|
143
|
+
red: {
|
|
144
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-danger,#ef4444)_10%,transparent)]",
|
|
145
|
+
text: "text-[var(--ks-danger-fg,#f87171)]",
|
|
146
|
+
border: "border-[color-mix(in_srgb,var(--ks-danger,#ef4444)_30%,transparent)]",
|
|
147
|
+
},
|
|
148
|
+
blue: {
|
|
149
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-info,#38bdf8)_10%,transparent)]",
|
|
150
|
+
text: "text-[var(--ks-info,#38bdf8)]",
|
|
151
|
+
border: "border-[color-mix(in_srgb,var(--ks-info,#38bdf8)_30%,transparent)]",
|
|
143
152
|
},
|
|
144
|
-
red: { bg: "bg-red-500/10", text: "text-red-400", border: "border-red-500/30" },
|
|
145
|
-
blue: { bg: "bg-blue-500/10", text: "text-blue-400", border: "border-blue-500/30" },
|
|
146
153
|
amber: {
|
|
147
|
-
bg: "bg-
|
|
148
|
-
text: "text-
|
|
149
|
-
border: "border-
|
|
154
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-accent,#fbbf24)_10%,transparent)]",
|
|
155
|
+
text: "text-[var(--ks-accent,#fbbf24)]",
|
|
156
|
+
border: "border-[color-mix(in_srgb,var(--ks-accent,#fbbf24)_30%,transparent)]",
|
|
150
157
|
},
|
|
151
158
|
};
|
|
152
159
|
|
|
153
|
-
const PAYABLE_KIND_OPTIONS: { id: string; label: string }[] = [
|
|
154
|
-
{ id: "subscription", label: "Subscription" },
|
|
155
|
-
{ id: "utility", label: "Utility" },
|
|
156
|
-
{ id: "rent", label: "Rent / Lease" },
|
|
157
|
-
{ id: "loan", label: "Loan" },
|
|
158
|
-
{ id: "tax", label: "Tax" },
|
|
159
|
-
{ id: "other", label: "Other" },
|
|
160
|
-
];
|
|
161
|
-
|
|
162
|
-
const PDC_OPTIONS: { id: string; label: string; dot: string }[] = [
|
|
163
|
-
{ id: "issued", label: "PDC issued", dot: "bg-amber-400" },
|
|
164
|
-
{ id: "presented", label: "PDC presented", dot: "bg-blue-400" },
|
|
165
|
-
{ id: "cleared", label: "PDC cleared", dot: "bg-emerald-400" },
|
|
166
|
-
{ id: "bounced", label: "PDC bounced", dot: "bg-red-400" },
|
|
167
|
-
];
|
|
168
|
-
|
|
169
160
|
const CATEGORY_FORM: Record<
|
|
170
161
|
string,
|
|
171
162
|
{
|
|
@@ -438,12 +429,12 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
438
429
|
onPaste={handlePaste}
|
|
439
430
|
>
|
|
440
431
|
<Show when={dragging()}>
|
|
441
|
-
<div class="absolute inset-0 z-30 border-2 border-dashed border-
|
|
442
|
-
<Upload size={32} class="text-
|
|
443
|
-
<span class="text-sm text-
|
|
432
|
+
<div class="absolute inset-0 z-30 border-2 border-dashed border-[color-mix(in_srgb,var(--ks-accent,#fbbf24)_60%,transparent)] bg-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_10%,transparent)] backdrop-blur-sm flex flex-col items-center justify-center pointer-events-none">
|
|
433
|
+
<Upload size={32} class="text-[var(--ks-accent,#fbbf24)] mb-2" />
|
|
434
|
+
<span class="text-sm text-[var(--ks-accent,#fbbf24)] font-medium">
|
|
444
435
|
Drop files to attach
|
|
445
436
|
</span>
|
|
446
|
-
<span class="text-[10px] text-
|
|
437
|
+
<span class="text-[10px] text-[color-mix(in_srgb,var(--ks-accent,#fbbf24)_60%,transparent)] mt-1">Images or PDFs</span>
|
|
447
438
|
</div>
|
|
448
439
|
</Show>
|
|
449
440
|
|
|
@@ -467,7 +458,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
467
458
|
<div class="flex-1 overflow-x-hidden overflow-y-auto px-5 sm:px-6 py-5 space-y-4">
|
|
468
459
|
<Show when={props.error}>
|
|
469
460
|
<div
|
|
470
|
-
class="rounded-lg border border-
|
|
461
|
+
class="rounded-lg border border-[color-mix(in_srgb,var(--ks-danger,#ef4444)_30%,transparent)] bg-[color-mix(in_srgb,var(--ks-danger,#ef4444)_10%,transparent)] px-3 py-2 text-sm text-[var(--ks-danger-fg,#f87171)]"
|
|
471
462
|
data-testid="transactions-form-error"
|
|
472
463
|
>
|
|
473
464
|
{props.error}
|
|
@@ -476,7 +467,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
476
467
|
|
|
477
468
|
<Show when={!props.simpleMode}>
|
|
478
469
|
<div>
|
|
479
|
-
<div class="text-[10px] uppercase tracking-widest text-
|
|
470
|
+
<div class="text-[10px] uppercase tracking-widest text-[var(--ks-fg-subtle,#71717a)] font-semibold mb-2">
|
|
480
471
|
Type
|
|
481
472
|
</div>
|
|
482
473
|
<div
|
|
@@ -509,7 +500,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
509
500
|
classList={{
|
|
510
501
|
[`${tc.bg} ${tc.border} ${tc.text}`]:
|
|
511
502
|
props.category === cat,
|
|
512
|
-
"border-
|
|
503
|
+
"border-[var(--ks-border,rgba(39,39,42,0.5))] bg-transparent text-[var(--ks-fg-subtle,#71717a)] hover:border-[var(--ks-input-border,#3f3f46)] hover:text-[var(--ks-fg,#ffffff)]":
|
|
513
504
|
props.category !== cat,
|
|
514
505
|
}}
|
|
515
506
|
>
|
|
@@ -520,7 +511,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
520
511
|
}}
|
|
521
512
|
</For>
|
|
522
513
|
</div>
|
|
523
|
-
<p class="text-[11px] text-
|
|
514
|
+
<p class="text-[11px] text-[var(--ks-fg-subtle,#71717a)] mt-2">{catConfig().hint}</p>
|
|
524
515
|
</div>
|
|
525
516
|
</Show>
|
|
526
517
|
|
|
@@ -541,8 +532,8 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
541
532
|
when={!(props.category === "sale" && props.saleItems.length > 0)}
|
|
542
533
|
>
|
|
543
534
|
<FormField label="Amount *">
|
|
544
|
-
<div class="flex items-stretch gap-2 px-4 py-3 border bg-
|
|
545
|
-
<span class="self-center text-3xl font-bold text-
|
|
535
|
+
<div class="flex items-stretch gap-2 px-4 py-3 border bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)] border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] ks-hud-clip-button focus-within:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)] transition-colors">
|
|
536
|
+
<span class="self-center text-3xl font-bold text-[var(--ks-fg-subtle,#71717a)] tabular-nums">
|
|
546
537
|
₱
|
|
547
538
|
</span>
|
|
548
539
|
<input
|
|
@@ -552,7 +543,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
552
543
|
data-testid="transactions-form-amount"
|
|
553
544
|
value={props.amount}
|
|
554
545
|
onInput={(e) => props.setAmount(e.currentTarget.value)}
|
|
555
|
-
class="min-w-0 flex-1 bg-transparent text-2xl sm:text-3xl font-bold tabular-nums text-
|
|
546
|
+
class="min-w-0 flex-1 bg-transparent text-2xl sm:text-3xl font-bold tabular-nums text-[var(--ks-fg,#ffffff)] placeholder-[var(--ks-input-border,#3f3f46)] focus:outline-none"
|
|
556
547
|
placeholder="0.00"
|
|
557
548
|
required
|
|
558
549
|
/>
|
|
@@ -582,8 +573,8 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
582
573
|
data-testid="transactions-form-transfer-fee-field"
|
|
583
574
|
>
|
|
584
575
|
<FormField label="Transfer fee *">
|
|
585
|
-
<div class="flex items-center gap-2 px-3 py-2 border bg-
|
|
586
|
-
<span class="text-lg font-bold text-
|
|
576
|
+
<div class="flex items-center gap-2 px-3 py-2 border bg-[color-mix(in_srgb,var(--ks-bg,#0a0a0a)_50%,transparent)] border-[color-mix(in_srgb,var(--ks-info,#38bdf8)_30%,transparent)] ks-hud-clip-button focus-within:border-[color-mix(in_srgb,var(--ks-info,#38bdf8)_60%,transparent)] transition-colors">
|
|
577
|
+
<span class="text-lg font-bold text-[var(--ks-fg-subtle,#71717a)] tabular-nums">
|
|
587
578
|
₱
|
|
588
579
|
</span>
|
|
589
580
|
<input
|
|
@@ -595,11 +586,11 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
595
586
|
onInput={(e) =>
|
|
596
587
|
props.setTransferFeeAmount(e.currentTarget.value)
|
|
597
588
|
}
|
|
598
|
-
class="min-w-0 flex-1 bg-transparent text-lg font-semibold tabular-nums text-
|
|
589
|
+
class="min-w-0 flex-1 bg-transparent text-lg font-semibold tabular-nums text-[var(--ks-fg,#ffffff)] placeholder-[var(--ks-input-border,#3f3f46)] focus:outline-none"
|
|
599
590
|
placeholder="0.00"
|
|
600
591
|
/>
|
|
601
592
|
</div>
|
|
602
|
-
<p class="mt-1 text-[10px] text-
|
|
593
|
+
<p class="mt-1 text-[10px] text-[var(--ks-fg-subtle,#71717a)]">
|
|
603
594
|
Saved as a separate expense from the source account.
|
|
604
595
|
</p>
|
|
605
596
|
</FormField>
|
|
@@ -614,14 +605,14 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
614
605
|
disabled={!props.isAdmin}
|
|
615
606
|
/>
|
|
616
607
|
<Show when={!props.isAdmin}>
|
|
617
|
-
<p class="text-[10px] text-
|
|
608
|
+
<p class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] mt-0.5">
|
|
618
609
|
Only admins can change the date
|
|
619
610
|
</p>
|
|
620
611
|
</Show>
|
|
621
612
|
</FormField>
|
|
622
613
|
|
|
623
614
|
<Show when={props.isBackdated}>
|
|
624
|
-
<div class="rounded-lg border border-
|
|
615
|
+
<div class="rounded-lg border border-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_20%,transparent)] bg-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_5%,transparent)] px-3 py-2">
|
|
625
616
|
<FormField label="Backdate Reason *">
|
|
626
617
|
<input
|
|
627
618
|
type="text"
|
|
@@ -630,7 +621,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
630
621
|
onInput={(e) =>
|
|
631
622
|
props.setBackdateReason(e.currentTarget.value)
|
|
632
623
|
}
|
|
633
|
-
class="w-full rounded-lg border border-
|
|
624
|
+
class="w-full rounded-lg border border-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_30%,transparent)] bg-[var(--ks-border,rgba(39,39,42,0.5))] px-3 py-2 text-sm text-[var(--ks-fg,#ffffff)] focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)] focus:outline-none"
|
|
634
625
|
placeholder="Why are you backdating this transaction?"
|
|
635
626
|
required
|
|
636
627
|
/>
|
|
@@ -644,7 +635,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
644
635
|
data-testid="transactions-form-description"
|
|
645
636
|
value={props.description}
|
|
646
637
|
onInput={(e) => props.setDescription(e.currentTarget.value)}
|
|
647
|
-
class="w-full bg-
|
|
638
|
+
class="w-full bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)] border border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] px-3 py-3 text-sm text-[var(--ks-fg,#ffffff)] ks-hud-clip-button focus:outline-none focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)]"
|
|
648
639
|
placeholder={catConfig().descPlaceholder}
|
|
649
640
|
required
|
|
650
641
|
/>
|
|
@@ -687,7 +678,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
687
678
|
type="text"
|
|
688
679
|
value={props.refNumber}
|
|
689
680
|
onInput={(e) => props.setRefNumber(e.currentTarget.value)}
|
|
690
|
-
class="w-full bg-
|
|
681
|
+
class="w-full bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)] border border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] px-3 py-3 text-sm text-[var(--ks-fg,#ffffff)] ks-hud-clip-button focus:outline-none focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)]"
|
|
691
682
|
placeholder="OR#, SI#, or ref number"
|
|
692
683
|
/>
|
|
693
684
|
</FormField>
|
|
@@ -699,7 +690,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
699
690
|
type="text"
|
|
700
691
|
value={props.refNumber}
|
|
701
692
|
onInput={(e) => props.setRefNumber(e.currentTarget.value)}
|
|
702
|
-
class="w-full bg-
|
|
693
|
+
class="w-full bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)] border border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] px-3 py-3 text-sm text-[var(--ks-fg,#ffffff)] ks-hud-clip-button focus:outline-none focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)]"
|
|
703
694
|
placeholder="Reference number (optional)"
|
|
704
695
|
/>
|
|
705
696
|
</FormField>
|
|
@@ -713,7 +704,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
713
704
|
<select
|
|
714
705
|
disabled
|
|
715
706
|
data-testid="subcategory-select-loading"
|
|
716
|
-
class="w-full bg-
|
|
707
|
+
class="w-full bg-[color-mix(in_srgb,var(--ks-input-bg,#18181b)_60%,transparent)] border border-[var(--ks-border,rgba(39,39,42,0.5))] px-3 py-3 text-sm text-[var(--ks-fg-subtle,#71717a)] ks-hud-clip-button focus:outline-none"
|
|
717
708
|
>
|
|
718
709
|
<option>Loading…</option>
|
|
719
710
|
</select>
|
|
@@ -745,128 +736,51 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
745
736
|
}
|
|
746
737
|
placeholder="— Uncategorised —"
|
|
747
738
|
searchPlaceholder="Search categories…"
|
|
748
|
-
triggerClass="w-full bg-
|
|
739
|
+
triggerClass="w-full bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)] border border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] px-3 py-3 text-sm text-[var(--ks-fg,#ffffff)] ks-hud-clip-button cursor-pointer focus:outline-none focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)] flex items-center justify-between gap-2"
|
|
749
740
|
triggerLabelClass="truncate text-left flex-1 min-w-0"
|
|
750
741
|
/>
|
|
751
742
|
</Show>
|
|
752
|
-
<p class="text-[10px] text-
|
|
743
|
+
<p class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] mt-0.5">
|
|
753
744
|
Optional. Used for tax-prep classification.
|
|
754
745
|
</p>
|
|
755
746
|
</FormField>
|
|
756
747
|
</Show>
|
|
757
748
|
|
|
758
749
|
<Show when={props.category === "payable"}>
|
|
759
|
-
<
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
onChange={(e) =>
|
|
769
|
-
props.setPayableKind(e.currentTarget.value)
|
|
770
|
-
}
|
|
771
|
-
class="w-full bg-zinc-900/60 border border-zinc-800/60 px-3 py-3 text-sm text-zinc-200 ks-hud-clip-button cursor-pointer focus:outline-none focus:border-amber-500/50"
|
|
772
|
-
>
|
|
773
|
-
<For each={PAYABLE_KIND_OPTIONS}>
|
|
774
|
-
{(opt) => <option value={opt.id}>{opt.label}</option>}
|
|
775
|
-
</For>
|
|
776
|
-
</select>
|
|
777
|
-
</FormField>
|
|
778
|
-
<FormField label="Due date">
|
|
779
|
-
<DatePicker
|
|
780
|
-
value={props.dueDate}
|
|
781
|
-
onChange={(d: string | null) => props.setDueDate(d || "")}
|
|
782
|
-
/>
|
|
783
|
-
<p class="text-[10px] text-zinc-600 mt-0.5">
|
|
784
|
-
When payment is owed. Past-due payables show in the Payables
|
|
785
|
-
tab.
|
|
786
|
-
</p>
|
|
787
|
-
</FormField>
|
|
788
|
-
</div>
|
|
789
|
-
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
790
|
-
<FormField label="Cheque number">
|
|
791
|
-
<input
|
|
792
|
-
type="text"
|
|
793
|
-
value={props.chequeNumber}
|
|
794
|
-
onInput={(e) =>
|
|
795
|
-
props.setChequeNumber(e.currentTarget.value)
|
|
796
|
-
}
|
|
797
|
-
class="w-full bg-zinc-900/60 border border-zinc-800/60 px-3 py-3 text-sm text-zinc-200 ks-hud-clip-button focus:outline-none focus:border-amber-500/50"
|
|
798
|
-
placeholder="e.g. 0004429-007"
|
|
799
|
-
/>
|
|
800
|
-
<p class="text-[10px] text-zinc-600 mt-0.5">
|
|
801
|
-
For post-dated cheques (PDC). Leave blank for direct
|
|
802
|
-
payments.
|
|
803
|
-
</p>
|
|
804
|
-
</FormField>
|
|
805
|
-
<Show when={props.chequeNumber.trim()}>
|
|
806
|
-
<FormField label="PDC status">
|
|
807
|
-
<SegmentedFilter
|
|
808
|
-
options={PDC_OPTIONS.map((opt) => ({
|
|
809
|
-
value: opt.id,
|
|
810
|
-
label: opt.label.replace("PDC ", ""),
|
|
811
|
-
}))}
|
|
812
|
-
value={props.pdcStatus}
|
|
813
|
-
onChange={props.setPdcStatus}
|
|
814
|
-
/>
|
|
815
|
-
</FormField>
|
|
816
|
-
</Show>
|
|
817
|
-
</div>
|
|
818
|
-
</div>
|
|
819
|
-
</Show>
|
|
820
|
-
|
|
821
|
-
<Show
|
|
822
|
-
when={catConfig().showSecondAccount}
|
|
823
|
-
fallback={
|
|
824
|
-
<FormField label={catConfig().accountLabel}>
|
|
825
|
-
<AccountRadioPicker
|
|
826
|
-
accounts={props.accounts}
|
|
827
|
-
ariaLabel={catConfig().accountLabel}
|
|
828
|
-
value={
|
|
829
|
-
props.category === "sale"
|
|
830
|
-
? props.destAccount
|
|
831
|
-
: props.sourceAccount
|
|
832
|
-
}
|
|
833
|
-
onChange={(v) => {
|
|
834
|
-
if (props.category === "sale") {
|
|
835
|
-
props.setDestAccount(v);
|
|
836
|
-
props.setSourceAccount("");
|
|
837
|
-
} else {
|
|
838
|
-
props.setSourceAccount(v);
|
|
839
|
-
props.setDestAccount("");
|
|
840
|
-
}
|
|
841
|
-
}}
|
|
842
|
-
/>
|
|
843
|
-
<Show when={catConfig().accountHint}>
|
|
844
|
-
<p class="text-[10px] text-zinc-600 mt-0.5">
|
|
845
|
-
{catConfig().accountHint}
|
|
846
|
-
</p>
|
|
847
|
-
</Show>
|
|
848
|
-
</FormField>
|
|
849
|
-
}
|
|
850
|
-
>
|
|
851
|
-
<TransferAccountsPicker
|
|
852
|
-
accounts={props.accounts}
|
|
853
|
-
sourceAccount={props.sourceAccount}
|
|
854
|
-
setSourceAccount={props.setSourceAccount}
|
|
855
|
-
destAccount={props.destAccount}
|
|
856
|
-
setDestAccount={props.setDestAccount}
|
|
857
|
-
sourceLabel={catConfig().accountLabel}
|
|
858
|
-
destLabel={catConfig().secondAccountLabel!}
|
|
859
|
-
amount={props.amount}
|
|
860
|
-
feeAmount={props.transferFeeAmount}
|
|
861
|
-
feeEnabled={props.transferFeeEnabled && props.allowTransferFee}
|
|
750
|
+
<TransactionPayableFields
|
|
751
|
+
payableKind={props.payableKind}
|
|
752
|
+
setPayableKind={props.setPayableKind}
|
|
753
|
+
dueDate={props.dueDate}
|
|
754
|
+
setDueDate={props.setDueDate}
|
|
755
|
+
chequeNumber={props.chequeNumber}
|
|
756
|
+
setChequeNumber={props.setChequeNumber}
|
|
757
|
+
pdcStatus={props.pdcStatus}
|
|
758
|
+
setPdcStatus={props.setPdcStatus}
|
|
862
759
|
/>
|
|
863
760
|
</Show>
|
|
864
761
|
|
|
762
|
+
<TransactionAccountFields
|
|
763
|
+
category={props.category}
|
|
764
|
+
accounts={props.accounts}
|
|
765
|
+
accountLabel={catConfig().accountLabel}
|
|
766
|
+
accountHint={catConfig().accountHint}
|
|
767
|
+
showSecondAccount={catConfig().showSecondAccount}
|
|
768
|
+
secondAccountLabel={catConfig().secondAccountLabel}
|
|
769
|
+
sourceAccount={props.sourceAccount}
|
|
770
|
+
setSourceAccount={props.setSourceAccount}
|
|
771
|
+
destAccount={props.destAccount}
|
|
772
|
+
setDestAccount={props.setDestAccount}
|
|
773
|
+
amount={props.amount}
|
|
774
|
+
transferFeeAmount={props.transferFeeAmount}
|
|
775
|
+
transferFeeEnabled={props.transferFeeEnabled}
|
|
776
|
+
allowTransferFee={props.allowTransferFee}
|
|
777
|
+
/>
|
|
778
|
+
|
|
865
779
|
<FormField label="Notes">
|
|
866
780
|
<MentionTextarea
|
|
867
781
|
value={props.notes}
|
|
868
782
|
setValue={props.setNotes}
|
|
869
|
-
class="w-full bg-
|
|
783
|
+
class="w-full bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)] border border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] px-3 py-2 text-sm text-[var(--ks-fg,#ffffff)] ks-hud-clip-button focus:outline-none focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)] resize-none"
|
|
870
784
|
rows={2}
|
|
871
785
|
placeholder="Optional notes... (type @ to mention a client)"
|
|
872
786
|
ariaLabel="Notes"
|
|
@@ -874,7 +788,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
874
788
|
</FormField>
|
|
875
789
|
|
|
876
790
|
<div>
|
|
877
|
-
<div class="flex items-center gap-1 mb-2 text-xs text-
|
|
791
|
+
<div class="flex items-center gap-1 mb-2 text-xs text-[var(--ks-fg-subtle,#71717a)]">
|
|
878
792
|
<Paperclip size={12} /> Attachments
|
|
879
793
|
<Show
|
|
880
794
|
when={
|
|
@@ -883,7 +797,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
883
797
|
0
|
|
884
798
|
}
|
|
885
799
|
>
|
|
886
|
-
<span class="text-
|
|
800
|
+
<span class="text-[var(--ks-fg-subtle,#71717a)]">
|
|
887
801
|
(
|
|
888
802
|
{(props.existingAttachments?.length ?? 0) +
|
|
889
803
|
props.pendingFiles.length}
|
|
@@ -908,7 +822,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
908
822
|
<Show
|
|
909
823
|
when={pf.previewUrl}
|
|
910
824
|
fallback={
|
|
911
|
-
<div class="flex w-24 h-24 flex-col items-center justify-center gap-1 rounded-lg border border-
|
|
825
|
+
<div class="flex w-24 h-24 flex-col items-center justify-center gap-1 rounded-lg border border-[var(--ks-input-border,#3f3f46)] bg-[var(--ks-border,rgba(39,39,42,0.5))] px-2 text-xs text-[var(--ks-fg-muted,#a1a1aa)]">
|
|
912
826
|
<FileIcon size={20} />
|
|
913
827
|
<span class="truncate max-w-full text-[10px]">
|
|
914
828
|
{pf.file.name}
|
|
@@ -916,7 +830,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
916
830
|
</div>
|
|
917
831
|
}
|
|
918
832
|
>
|
|
919
|
-
<div class="block rounded-lg border border-
|
|
833
|
+
<div class="block rounded-lg border border-[var(--ks-input-border,#3f3f46)] overflow-hidden">
|
|
920
834
|
<img
|
|
921
835
|
src={pf.previewUrl!}
|
|
922
836
|
alt={pf.file.name}
|
|
@@ -932,7 +846,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
932
846
|
props.pendingFiles.filter((f) => f.id !== pf.id)
|
|
933
847
|
);
|
|
934
848
|
}}
|
|
935
|
-
class="absolute -top-2 -right-2 flex w-7 h-7 items-center justify-center rounded-full bg-
|
|
849
|
+
class="absolute -top-2 -right-2 flex w-7 h-7 items-center justify-center rounded-full bg-[color-mix(in_srgb,var(--ks-danger,#ef4444)_90%,transparent)] border border-[color-mix(in_srgb,var(--ks-danger-fg,#f87171)_60%,transparent)] text-[var(--ks-fg,#ffffff)] cursor-pointer hover:bg-[var(--ks-danger,#ef4444)] active:bg-[var(--ks-danger,#ef4444)] shadow-lg"
|
|
936
850
|
aria-label={`Remove ${pf.file.name}`}
|
|
937
851
|
>
|
|
938
852
|
<X size={12} />
|
|
@@ -965,7 +879,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
965
879
|
/>
|
|
966
880
|
|
|
967
881
|
<Show when={props.pendingFiles.length === 0}>
|
|
968
|
-
<p class="text-[10px] text-
|
|
882
|
+
<p class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] mt-1">
|
|
969
883
|
Drop files here or paste from clipboard.
|
|
970
884
|
</p>
|
|
971
885
|
</Show>
|
|
@@ -978,7 +892,7 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
978
892
|
onClick={() =>
|
|
979
893
|
setViewMode(viewMode() === "default" ? "advanced" : "default")
|
|
980
894
|
}
|
|
981
|
-
class="text-xs text-
|
|
895
|
+
class="text-xs text-[var(--ks-fg-subtle,#71717a)] hover:text-[var(--ks-accent,#fbbf24)] px-3 py-1.5 transition-colors cursor-pointer"
|
|
982
896
|
>
|
|
983
897
|
{viewMode() === "default"
|
|
984
898
|
? "Show advanced fields"
|
|
@@ -1010,16 +924,16 @@ export default function TransactionForm(props: TransactionFormProps) {
|
|
|
1010
924
|
</Show>
|
|
1011
925
|
</div>
|
|
1012
926
|
|
|
1013
|
-
<div class="px-5 sm:px-6 py-4 border-t border-
|
|
1014
|
-
<div class="text-xs text-
|
|
1015
|
-
<span class="text-
|
|
927
|
+
<div class="px-5 sm:px-6 py-4 border-t border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] bg-[var(--ks-bg,#0a0a0a)] flex flex-col-reverse sm:flex-row sm:items-center sm:justify-between gap-3 shrink-0">
|
|
928
|
+
<div class="text-xs text-[var(--ks-fg-subtle,#71717a)]">
|
|
929
|
+
<span class="text-[var(--ks-fg-subtle,#71717a)]">Will record as </span>
|
|
1016
930
|
<span
|
|
1017
931
|
class="font-bold"
|
|
1018
932
|
classList={{
|
|
1019
|
-
"text-
|
|
1020
|
-
"text-
|
|
1021
|
-
"text-
|
|
1022
|
-
"text-
|
|
933
|
+
"text-[var(--ks-success-fg,#34d399)]": props.category === "sale",
|
|
934
|
+
"text-[var(--ks-danger-fg,#f87171)]": props.category === "expense",
|
|
935
|
+
"text-[var(--ks-accent,#fbbf24)]": props.category === "payable",
|
|
936
|
+
"text-[var(--ks-info,#38bdf8)]": props.category === "business",
|
|
1023
937
|
}}
|
|
1024
938
|
>
|
|
1025
939
|
{(CATEGORY_FORM[props.category] || CATEGORY_FORM.expense).label}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { For, Show } from "solid-js";
|
|
2
|
+
import CalendarDays from "lucide-solid/icons/calendar-days";
|
|
3
|
+
import FormField from "../base/FormField";
|
|
4
|
+
import DatePicker from "../base/DatePicker";
|
|
5
|
+
import SegmentedFilter from "../base/SegmentedFilter";
|
|
6
|
+
|
|
7
|
+
const PAYABLE_KIND_OPTIONS: { id: string; label: string }[] = [
|
|
8
|
+
{ id: "subscription", label: "Subscription" },
|
|
9
|
+
{ id: "utility", label: "Utility" },
|
|
10
|
+
{ id: "rent", label: "Rent / Lease" },
|
|
11
|
+
{ id: "loan", label: "Loan" },
|
|
12
|
+
{ id: "tax", label: "Tax" },
|
|
13
|
+
{ id: "other", label: "Other" },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const PDC_OPTIONS: { id: string; label: string; dot: string }[] = [
|
|
17
|
+
{ id: "issued", label: "PDC issued", dot: "bg-[var(--ks-accent,#fbbf24)]" },
|
|
18
|
+
{ id: "presented", label: "PDC presented", dot: "bg-[var(--ks-info,#38bdf8)]" },
|
|
19
|
+
{ id: "cleared", label: "PDC cleared", dot: "bg-[var(--ks-success-fg,#34d399)]" },
|
|
20
|
+
{ id: "bounced", label: "PDC bounced", dot: "bg-[var(--ks-danger-fg,#f87171)]" },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export interface TransactionPayableFieldsProps {
|
|
24
|
+
payableKind: string;
|
|
25
|
+
setPayableKind: (v: string) => void;
|
|
26
|
+
dueDate: string;
|
|
27
|
+
setDueDate: (v: string) => void;
|
|
28
|
+
chequeNumber: string;
|
|
29
|
+
setChequeNumber: (v: string) => void;
|
|
30
|
+
pdcStatus: string;
|
|
31
|
+
setPdcStatus: (v: string) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The "payable" category's extra fields, split out of TransactionForm to keep
|
|
36
|
+
* that file under the file-size budget. Kind/due-date/cheque/PDC-status only
|
|
37
|
+
* ever apply to the payable category, so this stays a pure sub-form.
|
|
38
|
+
*/
|
|
39
|
+
export default function TransactionPayableFields(props: TransactionPayableFieldsProps) {
|
|
40
|
+
return (
|
|
41
|
+
<div class="rounded-lg border border-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_20%,transparent)] bg-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_5%,transparent)] p-3 space-y-3">
|
|
42
|
+
<div class="flex items-center gap-2 text-[10px] uppercase tracking-widest text-[var(--ks-accent,#fbbf24)] font-semibold">
|
|
43
|
+
<CalendarDays size={12} />
|
|
44
|
+
<span>Payable details</span>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
47
|
+
<FormField label="Kind *">
|
|
48
|
+
<select
|
|
49
|
+
value={props.payableKind}
|
|
50
|
+
onChange={(e) => props.setPayableKind(e.currentTarget.value)}
|
|
51
|
+
class="w-full bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)] border border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] px-3 py-3 text-sm text-[var(--ks-fg,#ffffff)] ks-hud-clip-button cursor-pointer focus:outline-none focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)]"
|
|
52
|
+
>
|
|
53
|
+
<For each={PAYABLE_KIND_OPTIONS}>
|
|
54
|
+
{(opt) => <option value={opt.id}>{opt.label}</option>}
|
|
55
|
+
</For>
|
|
56
|
+
</select>
|
|
57
|
+
</FormField>
|
|
58
|
+
<FormField label="Due date">
|
|
59
|
+
<DatePicker
|
|
60
|
+
value={props.dueDate}
|
|
61
|
+
onChange={(d: string | null) => props.setDueDate(d || "")}
|
|
62
|
+
/>
|
|
63
|
+
<p class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] mt-0.5">
|
|
64
|
+
When payment is owed. Past-due payables show in the Payables tab.
|
|
65
|
+
</p>
|
|
66
|
+
</FormField>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
69
|
+
<FormField label="Cheque number">
|
|
70
|
+
<input
|
|
71
|
+
type="text"
|
|
72
|
+
value={props.chequeNumber}
|
|
73
|
+
onInput={(e) => props.setChequeNumber(e.currentTarget.value)}
|
|
74
|
+
class="w-full bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)] border border-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_60%,transparent)] px-3 py-3 text-sm text-[var(--ks-fg,#ffffff)] ks-hud-clip-button focus:outline-none focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)]"
|
|
75
|
+
placeholder="e.g. 0004429-007"
|
|
76
|
+
/>
|
|
77
|
+
<p class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] mt-0.5">
|
|
78
|
+
For post-dated cheques (PDC). Leave blank for direct payments.
|
|
79
|
+
</p>
|
|
80
|
+
</FormField>
|
|
81
|
+
<Show when={props.chequeNumber.trim()}>
|
|
82
|
+
<FormField label="PDC status">
|
|
83
|
+
<SegmentedFilter
|
|
84
|
+
options={PDC_OPTIONS.map((opt) => ({
|
|
85
|
+
value: opt.id,
|
|
86
|
+
label: opt.label.replace("PDC ", ""),
|
|
87
|
+
}))}
|
|
88
|
+
value={props.pdcStatus}
|
|
89
|
+
onChange={props.setPdcStatus}
|
|
90
|
+
ariaLabel="PDC status"
|
|
91
|
+
/>
|
|
92
|
+
</FormField>
|
|
93
|
+
</Show>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|