Skip to content

Commit 61a3e8c

Browse files
nodrel-devnoctisreusclaude
authored
fix: NDV UX polish — Create billing hint, deduped notices, Validate country dropdown (#24)
Follow-up to the cognitive-load and UX audits: - Add node-level output-pane warning hint for Create so the billing caution shows even when params are collapsed or the node runs via usableAsTool - Fold the credential-fit reminder into createCostNotice and gate shippingCredentialNotice to Get Rates + Validate, so Create shows one yellow block - Give phone + dimension fields distinct inline hint text vs. hover description - Swap Validate's Country to the COUNTRY_OPTIONS dropdown (matches Get Rates) - Add harness pnpm scripts (harness, harness:reset, harness:login) Co-authored-by: Kyle Tully <kytully@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1fd1360 commit 61a3e8c

5 files changed

Lines changed: 58 additions & 9 deletions

File tree

nodes/Fedex/Fedex.node.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ export class Fedex implements INodeType {
1919
// configured account, so gate it behind human approval in agentic workflows; the
2020
// credential defaults to sandbox to keep an unattended agent off a live account.
2121
usableAsTool: true,
22+
// Node-level warning surfaced in the output pane before execution (in addition to the
23+
// field-level createCostNotice) so the billing caution is visible even when the user —
24+
// or an AI agent on the usableAsTool path — runs Create without expanding its parameters.
25+
hints: [
26+
{
27+
message: 'Create books a real, billable FedEx shipment (free in Sandbox).',
28+
type: 'warning',
29+
location: 'outputPane',
30+
whenToDisplay: 'beforeExecution',
31+
displayCondition: '={{ $parameter["operation"] === "create" }}',
32+
},
33+
],
2234
inputs: [NodeConnectionTypes.Main],
2335
outputs: [NodeConnectionTypes.Main],
2436
// Each resource mirrors a FedEx dev-portal project and binds that project's credential:
@@ -82,13 +94,16 @@ export class Fedex implements INodeType {
8294
default: '',
8395
displayOptions: { show: { resource: ['tracking'] } },
8496
},
97+
// Shown for Get Rates + Validate only. Create gets the same credential-fit reminder
98+
// folded into its cost notice (createCostNotice) so its panel carries a single yellow
99+
// block instead of two stacked notices (UX audit, Recommended #1).
85100
{
86101
displayName:
87102
'These operations use a <b>FedEx Shipping OAuth2 API</b> credential. If the credential field above is empty or shows a red mark, create or select one of that type — Shipping and Track require separate FedEx project keys, so a Track credential cannot be used here.',
88103
name: 'shippingCredentialNotice',
89104
type: 'notice',
90105
default: '',
91-
displayOptions: { show: { resource: ['shipping'] } },
106+
displayOptions: { show: { resource: ['shipping'], operation: ['getRates', 'validate'] } },
92107
},
93108
...trackingDescription,
94109
...shippingDescription,

nodes/Fedex/fields.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ export function contactFields(role: Role, show: Show): INodeProperties[] {
102102
default: '',
103103
required: true,
104104
displayOptions: { show },
105-
description: 'FedEx requires a phone number for the shipment contact',
105+
// description = hover tooltip (longer, explains why); hint = persistent inline text
106+
// (terse). Kept distinct so the two layers don't render identical copy (UX audit, Minor #2).
107+
description:
108+
'FedEx requires a phone number for the shipment contact. It is used by FedEx and the delivering carrier to reach the sender or recipient about delivery exceptions, and for customs contact on international shipments.',
109+
hint: 'FedEx requires a phone number for the shipment contact',
106110
},
107111
];
108112
}
@@ -180,6 +184,7 @@ const dimensionEntries: INodeProperties[] = [
180184
typeOptions: { minValue: 0 },
181185
description:
182186
'Dimensions are sent only when length, width, and height are all greater than zero',
187+
hint: 'All three dimensions must be > 0 or none are sent',
183188
},
184189
{
185190
displayName: 'Length',
@@ -189,6 +194,7 @@ const dimensionEntries: INodeProperties[] = [
189194
typeOptions: { minValue: 0 },
190195
description:
191196
'Dimensions are sent only when length, width, and height are all greater than zero',
197+
hint: 'All three dimensions must be > 0 or none are sent',
192198
},
193199
{
194200
displayName: 'Width',
@@ -198,6 +204,7 @@ const dimensionEntries: INodeProperties[] = [
198204
typeOptions: { minValue: 0 },
199205
description:
200206
'Dimensions are sent only when length, width, and height are all greater than zero',
207+
hint: 'All three dimensions must be > 0 or none are sent',
201208
},
202209
];
203210

nodes/Fedex/resources/shipping/create.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ export const createFields: INodeProperties[] = [
3434
// Honest friction (cognitive-load audit, "What NOT to touch" #2): Create buys a real
3535
// shipment and bills the configured account, and the node is usableAsTool, so an AI agent
3636
// can invoke it. Surface the cost up front rather than burying it in docs.
37+
//
38+
// The credential-fit reminder is merged in here (rather than rendered as a second standalone
39+
// notice like shippingCredentialNotice on the other shipping ops) so the Create panel shows a
40+
// single yellow block, keeping the high-stakes billing message from being diluted by a stacked
41+
// callout (UX audit, Recommended #1).
3742
{
3843
displayName:
39-
'Running this operation books a real FedEx shipment and bills the Shipping Account below. In Sandbox it is free; in Production it incurs charges.',
44+
'Running this operation books a real FedEx shipment and bills the Shipping Account below — free in Sandbox, charged in Production. It needs a <b>FedEx Shipping OAuth2 API</b> credential; a Track credential will not work here.',
4045
name: 'createCostNotice',
4146
type: 'notice',
4247
default: '',

nodes/Fedex/resources/shipping/validate.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
import type { IDataObject, IExecuteSingleFunctions, IHttpRequestOptions, INodeProperties } from 'n8n-workflow';
1+
import type {
2+
IDataObject,
3+
IExecuteSingleFunctions,
4+
IHttpRequestOptions,
5+
INodeProperties,
6+
} from 'n8n-workflow';
27
import { toFedexAddress } from '../../cores/toFedexAddress';
8+
import { COUNTRY_OPTIONS } from '../../countries';
39

410
const show = { resource: ['shipping'], operation: ['validate'] };
511

@@ -15,7 +21,13 @@ export const validateFields: INodeProperties[] = [
1521
displayOptions: { show },
1622
description: 'Street address. Put each additional line on its own line; up to three are sent.',
1723
},
18-
{ displayName: 'City', name: 'addressCity', type: 'string', default: '', displayOptions: { show } },
24+
{
25+
displayName: 'City',
26+
name: 'addressCity',
27+
type: 'string',
28+
default: '',
29+
displayOptions: { show },
30+
},
1931
{
2032
displayName: 'State/Province Code',
2133
name: 'addressStateOrProvinceCode',
@@ -24,13 +36,20 @@ export const validateFields: INodeProperties[] = [
2436
placeholder: 'TN',
2537
displayOptions: { show },
2638
},
27-
{ displayName: 'Postal Code', name: 'addressPostalCode', type: 'string', default: '', displayOptions: { show } },
2839
{
29-
displayName: 'Country Code',
30-
name: 'addressCountryCode',
40+
displayName: 'Postal Code',
41+
name: 'addressPostalCode',
3142
type: 'string',
43+
default: '',
44+
displayOptions: { show },
45+
},
46+
{
47+
displayName: 'Country',
48+
name: 'addressCountryCode',
49+
type: 'options',
50+
options: COUNTRY_OPTIONS,
3251
default: 'US',
33-
description: 'Two-letter ISO country code',
52+
description: 'Country of the address; the two-letter ISO code is what gets sent to FedEx',
3453
displayOptions: { show },
3554
},
3655
];

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
"lint:fix": "n8n-node lint --fix",
2727
"test": "vitest run",
2828
"test:watch": "vitest",
29+
"harness": "./scripts/fedex-test-harness.sh up",
30+
"harness:reset": "./scripts/fedex-test-harness.sh reset",
31+
"harness:login": "./scripts/fedex-test-harness.sh login",
2932
"release": "n8n-node release",
3033
"prepublishOnly": "n8n-node prerelease"
3134
},

0 commit comments

Comments
 (0)