Skip to content

Commit d14fc72

Browse files
add support for deny/expired status
1 parent 19355eb commit d14fc72

3 files changed

Lines changed: 71 additions & 12 deletions

File tree

packages/cli/src/commands/spend-request/create.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const CreateSpendRequest: React.FC<CreateSpendRequestProps> = ({
8080
setApprovalUrl(approval.approval_link);
8181
setStatus('waiting');
8282

83-
const { approved } = await server.waitForCallback();
83+
const { status: callbackStatus } = await server.waitForCallback();
8484
if (cancelled) return;
8585

8686
const final = await repository.retrieve(result.id);
@@ -93,7 +93,7 @@ export const CreateSpendRequest: React.FC<CreateSpendRequestProps> = ({
9393
return;
9494
}
9595

96-
if (approved && final.status === 'approved') {
96+
if (callbackStatus === 'approved') {
9797
setRequest(final);
9898
setStatus('success');
9999
setTimeout(() => onComplete(final), DISPLAY_DELAY_MS);

packages/cli/src/commands/spend-request/request-approval.tsx

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const RequestApproval: React.FC<RequestApprovalProps> = ({
2020
onComplete,
2121
}) => {
2222
const [status, setStatus] = useState<
23-
'requesting' | 'waiting' | 'success' | 'error'
23+
'requesting' | 'waiting' | 'success' | 'denied' | 'expired' | 'error'
2424
>('requesting');
2525
const [approvalUrl, setApprovalUrl] = useState<string>('');
2626
const [result, setResult] = useState<SpendRequest | null>(null);
@@ -50,7 +50,7 @@ export const RequestApproval: React.FC<RequestApprovalProps> = ({
5050
setApprovalUrl(res.approval_link);
5151
setStatus('waiting');
5252

53-
const { approved } = await server.waitForCallback();
53+
const { status: callbackStatus } = await server.waitForCallback();
5454
if (cancelled) return;
5555

5656
const final = await repository.retrieve(id);
@@ -62,14 +62,20 @@ export const RequestApproval: React.FC<RequestApprovalProps> = ({
6262
return;
6363
}
6464

65-
if (approved && final.status === 'approved') {
65+
if (callbackStatus === 'approved') {
6666
setResult(final);
6767
setStatus('success');
6868
setTimeout(() => onComplete(final), DISPLAY_DELAY_MS);
69+
} else if (callbackStatus === 'denied') {
70+
setResult(final);
71+
setStatus('denied');
72+
setTimeout(() => onComplete(final), DISPLAY_DELAY_MS);
73+
} else if (callbackStatus === 'expired') {
74+
setResult(final);
75+
setStatus('expired');
76+
setTimeout(() => onComplete(final), DISPLAY_DELAY_MS);
6977
} else {
70-
setError(
71-
`Spend request did not reach approved (status: ${final.status})`,
72-
);
78+
setError('An error occurred during approval');
7379
setStatus('error');
7480
setTimeout(() => onComplete(final), DISPLAY_DELAY_MS);
7581
}
@@ -109,6 +115,38 @@ export const RequestApproval: React.FC<RequestApprovalProps> = ({
109115
);
110116
}
111117

118+
if (status === 'denied') {
119+
return (
120+
<Box flexDirection="column">
121+
<Text color="red">✗ Spend request denied</Text>
122+
<Box flexDirection="column" marginTop={1} paddingX={2}>
123+
<Text>
124+
ID: <Text bold>{result?.id}</Text>
125+
</Text>
126+
<Text>
127+
Status: <Text bold color="red">{result?.status}</Text>
128+
</Text>
129+
</Box>
130+
</Box>
131+
);
132+
}
133+
134+
if (status === 'expired') {
135+
return (
136+
<Box flexDirection="column">
137+
<Text color="yellow">✗ Spend request expired</Text>
138+
<Box flexDirection="column" marginTop={1} paddingX={2}>
139+
<Text>
140+
ID: <Text bold>{result?.id}</Text>
141+
</Text>
142+
<Text>
143+
Status: <Text bold>{result?.status}</Text>
144+
</Text>
145+
</Box>
146+
</Box>
147+
);
148+
}
149+
112150
if (status === 'success') {
113151
return (
114152
<Box flexDirection="column">

packages/cli/src/utils/local-callback-server.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { createServer } from 'node:http';
22
import type { AddressInfo } from 'node:net';
33

4+
export type CallbackStatus = 'approved' | 'denied' | 'expired' | 'error';
5+
46
export interface CallbackResult {
5-
approved: boolean;
7+
status: CallbackStatus;
68
}
79

810
export interface CallbackServer {
@@ -11,6 +13,19 @@ export interface CallbackServer {
1113
close: () => void;
1214
}
1315

16+
const VALID_STATUSES: ReadonlySet<string> = new Set([
17+
'approved',
18+
'denied',
19+
'expired',
20+
'error',
21+
]);
22+
23+
const STATUS_MESSAGES: Record<string, string> = {
24+
approved: 'Spend request approved. You can close this tab.',
25+
denied: 'Spend request denied. You can close this tab.',
26+
expired: 'Spend request expired. You can close this tab.',
27+
};
28+
1429
export function startCallbackServer(): Promise<CallbackServer> {
1530
return new Promise((resolve, reject) => {
1631
let resolveCallback: ((result: CallbackResult) => void) | undefined;
@@ -20,9 +35,15 @@ export function startCallbackServer(): Promise<CallbackServer> {
2035

2136
const server = createServer((req, res) => {
2237
const url = new URL(req.url ?? '/', 'http://localhost');
23-
const approved = url.searchParams.get('status') === 'approved';
24-
res.writeHead(200).end();
25-
resolveCallback?.({ approved });
38+
const raw = url.searchParams.get('status') ?? '';
39+
const status: CallbackStatus = VALID_STATUSES.has(raw)
40+
? (raw as CallbackStatus)
41+
: 'error';
42+
const message =
43+
STATUS_MESSAGES[raw] ?? 'Something went wrong. You can close this tab.';
44+
res.writeHead(200, { 'Content-Type': 'text/html' });
45+
res.end(`<html><body><p>${message}</p></body></html>`);
46+
resolveCallback?.({ status });
2647
});
2748

2849
server.listen(0, '127.0.0.1', () => {

0 commit comments

Comments
 (0)