@wealthx/shadcn 1.5.3 → 1.5.5
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/.turbo/turbo-build.log +120 -120
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-G2EWIP2N.mjs → chunk-CKPEFZNH.mjs} +96 -158
- package/dist/{chunk-PX4M67XQ.mjs → chunk-HXU5JGLQ.mjs} +4 -2
- package/dist/{chunk-CUSHAIUL.mjs → chunk-QBPNYXTI.mjs} +2 -1
- package/dist/components/ui/contact-alert-dialog/index.js +95 -157
- package/dist/components/ui/contact-alert-dialog/index.mjs +1 -1
- package/dist/components/ui/csv-import-modal.js +2 -1
- package/dist/components/ui/csv-import-modal.mjs +1 -1
- package/dist/components/ui/file-preview-dialog.js +4 -2
- package/dist/components/ui/file-preview-dialog.mjs +1 -1
- package/dist/index.js +101 -160
- package/dist/index.mjs +3 -3
- package/package.json +1 -1
- package/src/components/ui/contact-alert-dialog/builder-ui.tsx +25 -19
- package/src/components/ui/contact-alert-dialog/config.ts +47 -112
- package/src/components/ui/contact-alert-dialog/contact-alert-dialog.tsx +10 -2
- package/src/components/ui/contact-alert-dialog/types.ts +8 -4
- package/src/components/ui/contact-alert-dialog/utils.ts +0 -4
- package/src/components/ui/csv-import-modal.tsx +4 -1
- package/src/components/ui/file-preview-dialog.tsx +43 -32
|
@@ -8,46 +8,39 @@ import {
|
|
|
8
8
|
} from "@react-awesome-query-builder/ui";
|
|
9
9
|
import type { AlertQueryField, AlertQueryOperator } from "./types";
|
|
10
10
|
|
|
11
|
+
const NUMERIC_OPERATORS_DEFAULT: AlertQueryOperator[] = [
|
|
12
|
+
"equal",
|
|
13
|
+
"less",
|
|
14
|
+
"less_or_equal",
|
|
15
|
+
"greater",
|
|
16
|
+
"greater_or_equal",
|
|
17
|
+
];
|
|
18
|
+
|
|
11
19
|
export const ALERT_QUERY_FIELDS: AlertQueryField[] = [
|
|
12
20
|
{
|
|
13
21
|
key: "userMetric.max_loan_amount",
|
|
14
22
|
label: "Borrowing Capacity",
|
|
15
23
|
type: "number",
|
|
16
24
|
unit: "dollar",
|
|
17
|
-
operators:
|
|
18
|
-
|
|
19
|
-
"less",
|
|
20
|
-
"less_or_equal",
|
|
21
|
-
"greater",
|
|
22
|
-
"greater_or_equal",
|
|
23
|
-
"between",
|
|
24
|
-
],
|
|
25
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
26
|
+
min: 0,
|
|
25
27
|
},
|
|
26
28
|
{
|
|
27
29
|
key: "userMetric.debt_outstanding",
|
|
28
30
|
label: "Outstanding Debt",
|
|
29
31
|
type: "number",
|
|
30
32
|
unit: "dollar",
|
|
31
|
-
operators:
|
|
32
|
-
|
|
33
|
-
"less",
|
|
34
|
-
"less_or_equal",
|
|
35
|
-
"greater",
|
|
36
|
-
"greater_or_equal",
|
|
37
|
-
],
|
|
33
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
34
|
+
min: 0,
|
|
38
35
|
},
|
|
39
36
|
{
|
|
40
37
|
key: "userMetric.lvr",
|
|
41
38
|
label: "Current LVR",
|
|
42
39
|
type: "number",
|
|
43
40
|
unit: "percent",
|
|
44
|
-
operators:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"less_or_equal",
|
|
48
|
-
"greater",
|
|
49
|
-
"greater_or_equal",
|
|
50
|
-
],
|
|
41
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
42
|
+
min: 0,
|
|
43
|
+
max: 100,
|
|
51
44
|
},
|
|
52
45
|
{
|
|
53
46
|
key: "userMetric.has_met_buying_goal",
|
|
@@ -59,79 +52,52 @@ export const ALERT_QUERY_FIELDS: AlertQueryField[] = [
|
|
|
59
52
|
label: "Excess Monthly Surplus",
|
|
60
53
|
type: "number",
|
|
61
54
|
unit: "dollar",
|
|
62
|
-
operators:
|
|
63
|
-
|
|
64
|
-
"less",
|
|
65
|
-
"less_or_equal",
|
|
66
|
-
"greater",
|
|
67
|
-
"greater_or_equal",
|
|
68
|
-
],
|
|
55
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
56
|
+
min: 0,
|
|
69
57
|
},
|
|
70
58
|
{
|
|
71
59
|
key: "userMetric.equity",
|
|
72
60
|
label: "Equity Amount",
|
|
73
61
|
type: "number",
|
|
74
62
|
unit: "dollar",
|
|
75
|
-
operators:
|
|
76
|
-
|
|
77
|
-
"less",
|
|
78
|
-
"less_or_equal",
|
|
79
|
-
"greater",
|
|
80
|
-
"greater_or_equal",
|
|
81
|
-
],
|
|
63
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
64
|
+
min: 0,
|
|
82
65
|
},
|
|
83
66
|
{
|
|
84
67
|
key: "userMetric.max_debt_interest_rate",
|
|
85
68
|
label: "Max Debt Interest Rate",
|
|
86
69
|
type: "number",
|
|
87
70
|
unit: "percent",
|
|
88
|
-
operators:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"less_or_equal",
|
|
92
|
-
"greater",
|
|
93
|
-
"greater_or_equal",
|
|
94
|
-
],
|
|
71
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
72
|
+
min: 0,
|
|
73
|
+
max: 100,
|
|
95
74
|
},
|
|
96
75
|
{
|
|
97
76
|
key: "userMetric.min_debt_interest_rate",
|
|
98
77
|
label: "Min Debt Interest Rate",
|
|
99
78
|
type: "number",
|
|
100
79
|
unit: "percent",
|
|
101
|
-
operators:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"less_or_equal",
|
|
105
|
-
"greater",
|
|
106
|
-
"greater_or_equal",
|
|
107
|
-
],
|
|
80
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
81
|
+
min: 0,
|
|
82
|
+
max: 100,
|
|
108
83
|
},
|
|
109
84
|
];
|
|
110
85
|
|
|
111
|
-
export const ALL_NUMERIC_OPERATORS: AlertQueryOperator[] =
|
|
112
|
-
|
|
113
|
-
"not_equal",
|
|
114
|
-
"less",
|
|
115
|
-
"less_or_equal",
|
|
116
|
-
"greater",
|
|
117
|
-
"greater_or_equal",
|
|
118
|
-
"between",
|
|
119
|
-
];
|
|
86
|
+
export const ALL_NUMERIC_OPERATORS: AlertQueryOperator[] =
|
|
87
|
+
NUMERIC_OPERATORS_DEFAULT;
|
|
120
88
|
|
|
121
89
|
export const BOOLEAN_OPERATORS: AlertQueryOperator[] = ["equal"];
|
|
122
90
|
|
|
123
91
|
export const OPERATOR_LABELS: Record<AlertQueryOperator, string> = {
|
|
124
92
|
equal: "=",
|
|
125
|
-
not_equal: "≠",
|
|
126
93
|
less: "<",
|
|
127
94
|
less_or_equal: "≤",
|
|
128
95
|
greater: ">",
|
|
129
96
|
greater_or_equal: "≥",
|
|
130
|
-
between: "between",
|
|
131
97
|
};
|
|
132
98
|
|
|
133
99
|
export const SEVERITY_LABELS = {
|
|
134
|
-
|
|
100
|
+
HEALTHY: "Healthy",
|
|
135
101
|
WATCH: "Watch",
|
|
136
102
|
NEED_ACTION: "Need Action",
|
|
137
103
|
} as const;
|
|
@@ -139,43 +105,32 @@ export const SEVERITY_LABELS = {
|
|
|
139
105
|
// react-awesome-query-builder config — drives ImmutableTree state.
|
|
140
106
|
export const QB_CONFIG: Config = {
|
|
141
107
|
...BasicConfig,
|
|
108
|
+
settings: {
|
|
109
|
+
...BasicConfig.settings,
|
|
110
|
+
maxNesting: 3,
|
|
111
|
+
maxNumberOfRules: 5,
|
|
112
|
+
},
|
|
142
113
|
fields: {
|
|
143
114
|
"userMetric.max_loan_amount": {
|
|
144
115
|
label: "Borrowing Capacity",
|
|
145
116
|
type: "number",
|
|
146
|
-
operators:
|
|
147
|
-
"equal",
|
|
148
|
-
"less",
|
|
149
|
-
"less_or_equal",
|
|
150
|
-
"greater",
|
|
151
|
-
"greater_or_equal",
|
|
152
|
-
"between",
|
|
153
|
-
],
|
|
117
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
154
118
|
valueSources: ["value"],
|
|
119
|
+
fieldSettings: { min: 0 },
|
|
155
120
|
},
|
|
156
121
|
"userMetric.debt_outstanding": {
|
|
157
122
|
label: "Outstanding Debt",
|
|
158
123
|
type: "number",
|
|
159
|
-
operators:
|
|
160
|
-
"equal",
|
|
161
|
-
"less",
|
|
162
|
-
"less_or_equal",
|
|
163
|
-
"greater",
|
|
164
|
-
"greater_or_equal",
|
|
165
|
-
],
|
|
124
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
166
125
|
valueSources: ["value"],
|
|
126
|
+
fieldSettings: { min: 0 },
|
|
167
127
|
},
|
|
168
128
|
"userMetric.lvr": {
|
|
169
129
|
label: "Current LVR",
|
|
170
130
|
type: "number",
|
|
171
|
-
operators:
|
|
172
|
-
"equal",
|
|
173
|
-
"less",
|
|
174
|
-
"less_or_equal",
|
|
175
|
-
"greater",
|
|
176
|
-
"greater_or_equal",
|
|
177
|
-
],
|
|
131
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
178
132
|
valueSources: ["value"],
|
|
133
|
+
fieldSettings: { min: 0, max: 100 },
|
|
179
134
|
},
|
|
180
135
|
"userMetric.has_met_buying_goal": {
|
|
181
136
|
label: "Has Met Buying Goal",
|
|
@@ -186,50 +141,30 @@ export const QB_CONFIG: Config = {
|
|
|
186
141
|
"userMetric.excess_monthly_surplus": {
|
|
187
142
|
label: "Excess Monthly Surplus",
|
|
188
143
|
type: "number",
|
|
189
|
-
operators:
|
|
190
|
-
"equal",
|
|
191
|
-
"less",
|
|
192
|
-
"less_or_equal",
|
|
193
|
-
"greater",
|
|
194
|
-
"greater_or_equal",
|
|
195
|
-
],
|
|
144
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
196
145
|
valueSources: ["value"],
|
|
146
|
+
fieldSettings: { min: 0 },
|
|
197
147
|
},
|
|
198
148
|
"userMetric.equity": {
|
|
199
149
|
label: "Equity Amount",
|
|
200
150
|
type: "number",
|
|
201
|
-
operators:
|
|
202
|
-
"equal",
|
|
203
|
-
"less",
|
|
204
|
-
"less_or_equal",
|
|
205
|
-
"greater",
|
|
206
|
-
"greater_or_equal",
|
|
207
|
-
],
|
|
151
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
208
152
|
valueSources: ["value"],
|
|
153
|
+
fieldSettings: { min: 0 },
|
|
209
154
|
},
|
|
210
155
|
"userMetric.max_debt_interest_rate": {
|
|
211
156
|
label: "Max Debt Interest Rate",
|
|
212
157
|
type: "number",
|
|
213
|
-
operators:
|
|
214
|
-
"equal",
|
|
215
|
-
"less",
|
|
216
|
-
"less_or_equal",
|
|
217
|
-
"greater",
|
|
218
|
-
"greater_or_equal",
|
|
219
|
-
],
|
|
158
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
220
159
|
valueSources: ["value"],
|
|
160
|
+
fieldSettings: { min: 0, max: 100 },
|
|
221
161
|
},
|
|
222
162
|
"userMetric.min_debt_interest_rate": {
|
|
223
163
|
label: "Min Debt Interest Rate",
|
|
224
164
|
type: "number",
|
|
225
|
-
operators:
|
|
226
|
-
"equal",
|
|
227
|
-
"less",
|
|
228
|
-
"less_or_equal",
|
|
229
|
-
"greater",
|
|
230
|
-
"greater_or_equal",
|
|
231
|
-
],
|
|
165
|
+
operators: NUMERIC_OPERATORS_DEFAULT,
|
|
232
166
|
valueSources: ["value"],
|
|
167
|
+
fieldSettings: { min: 0, max: 100 },
|
|
233
168
|
},
|
|
234
169
|
},
|
|
235
170
|
};
|
|
@@ -88,6 +88,7 @@ export function ContactAlertDialog({
|
|
|
88
88
|
isCompanyAdmin = false,
|
|
89
89
|
initialShareAcrossCompany = false,
|
|
90
90
|
onSave,
|
|
91
|
+
onError,
|
|
91
92
|
isLoading = false,
|
|
92
93
|
className,
|
|
93
94
|
}: ContactAlertDialogProps) {
|
|
@@ -116,10 +117,17 @@ export function ContactAlertDialog({
|
|
|
116
117
|
|
|
117
118
|
function handleSave() {
|
|
118
119
|
if (!canSave) return;
|
|
120
|
+
let filterSegment: ImmutableTree;
|
|
121
|
+
try {
|
|
122
|
+
filterSegment = QbUtils.sanitizeTree(tree, QB_CONFIG).fixedTree;
|
|
123
|
+
} catch (e) {
|
|
124
|
+
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
119
127
|
onSave({
|
|
120
128
|
name: name.trim(),
|
|
121
129
|
severity,
|
|
122
|
-
filterSegment
|
|
130
|
+
filterSegment,
|
|
123
131
|
sharingType: shareAcrossCompany
|
|
124
132
|
? AlertSharingType.COMPANY
|
|
125
133
|
: AlertSharingType.PRIVATE,
|
|
@@ -151,7 +159,7 @@ export function ContactAlertDialog({
|
|
|
151
159
|
}
|
|
152
160
|
>
|
|
153
161
|
{(
|
|
154
|
-
["NEED_ACTION", "WATCH", "
|
|
162
|
+
["NEED_ACTION", "WATCH", "HEALTHY"] as ContactAlertSeverity[]
|
|
155
163
|
).map((s) => (
|
|
156
164
|
<ToggleGroupItem key={s} value={s}>
|
|
157
165
|
{SEVERITY_LABELS[s]}
|
|
@@ -4,16 +4,14 @@ export type AlertQueryCombinator = "AND" | "OR";
|
|
|
4
4
|
|
|
5
5
|
export type AlertQueryOperator =
|
|
6
6
|
| "equal"
|
|
7
|
-
| "not_equal"
|
|
8
7
|
| "less"
|
|
9
8
|
| "less_or_equal"
|
|
10
9
|
| "greater"
|
|
11
|
-
| "greater_or_equal"
|
|
12
|
-
| "between";
|
|
10
|
+
| "greater_or_equal";
|
|
13
11
|
|
|
14
12
|
export type AlertQueryFieldType = "number" | "boolean";
|
|
15
13
|
|
|
16
|
-
export type ContactAlertSeverity = "
|
|
14
|
+
export type ContactAlertSeverity = "HEALTHY" | "WATCH" | "NEED_ACTION";
|
|
17
15
|
|
|
18
16
|
export enum AlertSharingType {
|
|
19
17
|
PRIVATE = "PRIVATE",
|
|
@@ -28,6 +26,10 @@ export interface AlertQueryField {
|
|
|
28
26
|
unit?: "dollar" | "percent";
|
|
29
27
|
/** Allowed operators (defaults to all numeric operators). */
|
|
30
28
|
operators?: AlertQueryOperator[];
|
|
29
|
+
/** Lower bound for numeric value (inclusive). Enforced at input time. */
|
|
30
|
+
min?: number;
|
|
31
|
+
/** Upper bound for numeric value (inclusive). Enforced at input time. */
|
|
32
|
+
max?: number;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export interface ContactAlertQueryBuilderProps {
|
|
@@ -57,5 +59,7 @@ export interface ContactAlertDialogProps {
|
|
|
57
59
|
sharingType: AlertSharingType;
|
|
58
60
|
}) => void;
|
|
59
61
|
isLoading?: boolean;
|
|
62
|
+
/** Fired when the underlying QB sanitizeTree throws while saving. */
|
|
63
|
+
onError?: (error: Error) => void;
|
|
60
64
|
className?: string;
|
|
61
65
|
}
|
|
@@ -45,7 +45,6 @@ export function ruleSummary(
|
|
|
45
45
|
const field: string = ruleProps?.field ?? "";
|
|
46
46
|
const operator: string = ruleProps?.operator ?? "equal";
|
|
47
47
|
const value0 = ruleProps?.value?.[0];
|
|
48
|
-
const value1 = ruleProps?.value?.[1];
|
|
49
48
|
|
|
50
49
|
const fieldDef = fields.find((f) => f.key === field);
|
|
51
50
|
const fieldLabel = fieldDef?.label ?? field;
|
|
@@ -62,9 +61,6 @@ export function ruleSummary(
|
|
|
62
61
|
return String(v);
|
|
63
62
|
};
|
|
64
63
|
|
|
65
|
-
if (operator === "between") {
|
|
66
|
-
return `${fieldLabel} between ${formatVal(value0)} and ${formatVal(value1)}`;
|
|
67
|
-
}
|
|
68
64
|
return `${fieldLabel} ${opLabel} ${formatVal(value0)}`;
|
|
69
65
|
}
|
|
70
66
|
|
|
@@ -29,6 +29,8 @@ export interface CsvImportModalProps {
|
|
|
29
29
|
onNext: () => void;
|
|
30
30
|
/** Called when the user clicks the CSV template download link. */
|
|
31
31
|
onDownloadTemplate?: () => void;
|
|
32
|
+
/** Override the upload zone description (e.g. for a stricter file-size cap). */
|
|
33
|
+
uploadDescription?: string;
|
|
32
34
|
isLoading?: boolean;
|
|
33
35
|
className?: string;
|
|
34
36
|
}
|
|
@@ -55,6 +57,7 @@ export function CsvImportModal({
|
|
|
55
57
|
onFileClear,
|
|
56
58
|
onNext,
|
|
57
59
|
onDownloadTemplate,
|
|
60
|
+
uploadDescription,
|
|
58
61
|
isLoading = false,
|
|
59
62
|
className,
|
|
60
63
|
}: CsvImportModalProps) {
|
|
@@ -93,7 +96,7 @@ export function CsvImportModal({
|
|
|
93
96
|
<UploadCard
|
|
94
97
|
size="lg"
|
|
95
98
|
label="Drag & drop or click to upload"
|
|
96
|
-
description="Supports .csv files only. Max 10 MB."
|
|
99
|
+
description={uploadDescription ?? "Supports .csv files only. Max 10 MB."}
|
|
97
100
|
accept=".csv"
|
|
98
101
|
onFileChange={(file) => file && onFileSelect?.(file)}
|
|
99
102
|
/>
|
|
@@ -99,14 +99,21 @@ export interface FilePreviewDialogProps {
|
|
|
99
99
|
pageSize?: number;
|
|
100
100
|
/**
|
|
101
101
|
* List of staff members available for assignment.
|
|
102
|
-
* When provided, a staff selector is rendered above the table.
|
|
102
|
+
* When provided, a basic staff selector is rendered above the table.
|
|
103
103
|
* Import is blocked until a staff member is selected.
|
|
104
|
+
* Ignored when `staffSelector` is provided.
|
|
104
105
|
*/
|
|
105
106
|
staffOptions?: StaffOption[];
|
|
106
107
|
/** Currently selected staff ID. */
|
|
107
108
|
selectedStaffId?: string;
|
|
108
109
|
/** Called when the user picks a staff member. */
|
|
109
110
|
onStaffSelect?: (staffId: string) => void;
|
|
111
|
+
/**
|
|
112
|
+
* Custom staff-selector block rendered above the table. When provided, replaces
|
|
113
|
+
* the entire built-in "Assign staff" block (label + select + helper text).
|
|
114
|
+
* The caller still drives `selectedStaffId` so the Import button gating works.
|
|
115
|
+
*/
|
|
116
|
+
staffSelector?: React.ReactNode;
|
|
110
117
|
className?: string;
|
|
111
118
|
}
|
|
112
119
|
|
|
@@ -250,6 +257,7 @@ export function FilePreviewDialog({
|
|
|
250
257
|
staffOptions,
|
|
251
258
|
selectedStaffId,
|
|
252
259
|
onStaffSelect,
|
|
260
|
+
staffSelector,
|
|
253
261
|
className,
|
|
254
262
|
}: FilePreviewDialogProps) {
|
|
255
263
|
const [page, setPage] = React.useState(0);
|
|
@@ -264,7 +272,8 @@ export function FilePreviewDialog({
|
|
|
264
272
|
const pageStart = page * pageSize; // used for row index display
|
|
265
273
|
|
|
266
274
|
const isImporting = state === "importing";
|
|
267
|
-
const
|
|
275
|
+
const hasBuiltInStaffSelector = !!staffOptions && staffOptions.length > 0;
|
|
276
|
+
const hasStaffSelector = !!staffSelector || hasBuiltInStaffSelector;
|
|
268
277
|
// Import is blocked when staff selection is required but none is chosen yet
|
|
269
278
|
const canImport =
|
|
270
279
|
state === "preview" &&
|
|
@@ -315,36 +324,38 @@ export function FilePreviewDialog({
|
|
|
315
324
|
|
|
316
325
|
{state === "preview" && (
|
|
317
326
|
<>
|
|
318
|
-
{/* Staff assignment —
|
|
319
|
-
{
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
<
|
|
337
|
-
{s
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
327
|
+
{/* Staff assignment — custom slot wins; otherwise fall back to built-in select */}
|
|
328
|
+
{staffSelector
|
|
329
|
+
? staffSelector
|
|
330
|
+
: hasBuiltInStaffSelector && (
|
|
331
|
+
<div className="flex flex-col gap-1">
|
|
332
|
+
<label className="text-label-medium text-foreground">
|
|
333
|
+
Assign staff{" "}
|
|
334
|
+
<span className="text-destructive" aria-hidden="true">
|
|
335
|
+
*
|
|
336
|
+
</span>
|
|
337
|
+
</label>
|
|
338
|
+
<Select
|
|
339
|
+
value={selectedStaffId ?? ""}
|
|
340
|
+
onValueChange={(id) => onStaffSelect?.(id)}
|
|
341
|
+
>
|
|
342
|
+
<SelectTrigger className="w-full">
|
|
343
|
+
<SelectValue placeholder="Select a staff member" />
|
|
344
|
+
</SelectTrigger>
|
|
345
|
+
<SelectContent>
|
|
346
|
+
{staffOptions!.map((s) => (
|
|
347
|
+
<SelectItem key={s.id} value={s.id}>
|
|
348
|
+
{s.name}
|
|
349
|
+
</SelectItem>
|
|
350
|
+
))}
|
|
351
|
+
</SelectContent>
|
|
352
|
+
</Select>
|
|
353
|
+
<p className="text-xs text-muted-foreground">
|
|
354
|
+
All contacts in this import will be assigned to the
|
|
355
|
+
selected staff member.
|
|
356
|
+
</p>
|
|
357
|
+
</div>
|
|
358
|
+
)}
|
|
348
359
|
|
|
349
360
|
<div className="max-h-[360px] overflow-auto border border-border">
|
|
350
361
|
<Table>
|