@spotsdev/sdk 1.2.0 → 1.3.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/dist/api/mutations/index.d.ts +1 -0
- package/dist/api/mutations/index.js +3 -1
- package/dist/api/mutations/redemptions.d.ts +33 -0
- package/dist/api/mutations/redemptions.js +63 -0
- package/dist/api/queries/index.d.ts +2 -0
- package/dist/api/queries/index.js +6 -2
- package/dist/api/queries/wallet.d.ts +109 -0
- package/dist/api/queries/wallet.js +136 -0
- package/package.json +2 -2
- package/src/api/mutations/index.ts +3 -0
- package/src/api/mutations/redemptions.ts +91 -0
- package/src/api/queries/index.ts +4 -0
- package/src/api/queries/wallet.ts +240 -0
|
@@ -35,4 +35,6 @@ __exportStar(require("./notifications"), exports);
|
|
|
35
35
|
__exportStar(require("./products"), exports);
|
|
36
36
|
// Orders (marketplace)
|
|
37
37
|
__exportStar(require("./orders"), exports);
|
|
38
|
-
|
|
38
|
+
// Redemptions (wallet)
|
|
39
|
+
__exportStar(require("./redemptions"), exports);
|
|
40
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL211dGF0aW9ucy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7R0FJRzs7Ozs7Ozs7Ozs7Ozs7OztBQUVILFFBQVE7QUFDUiwwQ0FBdUI7QUFFdkIsUUFBUTtBQUNSLDBDQUF1QjtBQUV2QixRQUFRO0FBQ1IsMENBQXVCO0FBRXZCLGdCQUFnQjtBQUNoQixrREFBK0I7QUFFL0IsUUFBUTtBQUNSLDBDQUF1QjtBQUV2QixnQkFBZ0I7QUFDaEIsa0RBQStCO0FBRS9CLHlCQUF5QjtBQUN6Qiw2Q0FBMEI7QUFFMUIsdUJBQXVCO0FBQ3ZCLDJDQUF3QjtBQUV4Qix1QkFBdUI7QUFDdkIsZ0RBQTZCIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBTcG90cyBTREsgTXV0YXRpb24gSG9va3MgSW5kZXhcbiAqXG4gKiBSZS1leHBvcnRzIGFsbCBtdXRhdGlvbiBob29rcy5cbiAqL1xuXG4vLyBVc2Vyc1xuZXhwb3J0ICogZnJvbSAnLi91c2VycydcblxuLy8gUG9zdHNcbmV4cG9ydCAqIGZyb20gJy4vcG9zdHMnXG5cbi8vIFNwb3RzXG5leHBvcnQgKiBmcm9tICcuL3Nwb3RzJ1xuXG4vLyBDb252ZXJzYXRpb25zXG5leHBvcnQgKiBmcm9tICcuL2NvbnZlcnNhdGlvbnMnXG5cbi8vIENsdWJzXG5leHBvcnQgKiBmcm9tICcuL2NsdWJzJ1xuXG4vLyBOb3RpZmljYXRpb25zXG5leHBvcnQgKiBmcm9tICcuL25vdGlmaWNhdGlvbnMnXG5cbi8vIFByb2R1Y3RzIChtYXJrZXRwbGFjZSlcbmV4cG9ydCAqIGZyb20gJy4vcHJvZHVjdHMnXG5cbi8vIE9yZGVycyAobWFya2V0cGxhY2UpXG5leHBvcnQgKiBmcm9tICcuL29yZGVycydcblxuLy8gUmVkZW1wdGlvbnMgKHdhbGxldClcbmV4cG9ydCAqIGZyb20gJy4vcmVkZW1wdGlvbnMnXG4iXX0=
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redemption Mutation Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query mutations for redemption operations.
|
|
5
|
+
*/
|
|
6
|
+
import { type UseMutationResult } from '@tanstack/react-query';
|
|
7
|
+
import { type WalletRedemption } from '../queries/wallet';
|
|
8
|
+
export interface RedeemRequest {
|
|
9
|
+
qrCode: string;
|
|
10
|
+
spotId?: string;
|
|
11
|
+
deviceInfo?: string;
|
|
12
|
+
latitude?: number;
|
|
13
|
+
longitude?: number;
|
|
14
|
+
notes?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface VoidRedemptionRequest {
|
|
17
|
+
reason: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Redeem a QR code (spot owner/staff action)
|
|
21
|
+
*
|
|
22
|
+
* @endpoint POST /redemptions/redeem
|
|
23
|
+
*/
|
|
24
|
+
export declare function useRedeem(): UseMutationResult<WalletRedemption, Error, RedeemRequest>;
|
|
25
|
+
/**
|
|
26
|
+
* Void a redemption (spot owner action)
|
|
27
|
+
*
|
|
28
|
+
* @endpoint PUT /redemptions/:id/void
|
|
29
|
+
*/
|
|
30
|
+
export declare function useVoidRedemption(): UseMutationResult<WalletRedemption, Error, {
|
|
31
|
+
redemptionId: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Redemption Mutation Hooks
|
|
4
|
+
*
|
|
5
|
+
* TanStack Query mutations for redemption operations.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.useRedeem = useRedeem;
|
|
9
|
+
exports.useVoidRedemption = useVoidRedemption;
|
|
10
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
11
|
+
const client_1 = require("../client");
|
|
12
|
+
const wallet_1 = require("../queries/wallet");
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// MUTATION HOOKS
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Redeem a QR code (spot owner/staff action)
|
|
18
|
+
*
|
|
19
|
+
* @endpoint POST /redemptions/redeem
|
|
20
|
+
*/
|
|
21
|
+
function useRedeem() {
|
|
22
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
23
|
+
return (0, react_query_1.useMutation)({
|
|
24
|
+
mutationFn: async (request) => {
|
|
25
|
+
const client = (0, client_1.getApiClient)();
|
|
26
|
+
const response = await client.post('/redemptions/redeem', request);
|
|
27
|
+
return response.data.data;
|
|
28
|
+
},
|
|
29
|
+
onSuccess: (_data, variables) => {
|
|
30
|
+
// Invalidate redemption lookup for this QR code
|
|
31
|
+
queryClient.invalidateQueries({
|
|
32
|
+
queryKey: wallet_1.walletKeys.lookup(variables.qrCode),
|
|
33
|
+
});
|
|
34
|
+
// Invalidate spot redemptions if spotId provided
|
|
35
|
+
if (variables.spotId) {
|
|
36
|
+
queryClient.invalidateQueries({
|
|
37
|
+
queryKey: wallet_1.walletKeys.spotRedemptions(variables.spotId),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Void a redemption (spot owner action)
|
|
45
|
+
*
|
|
46
|
+
* @endpoint PUT /redemptions/:id/void
|
|
47
|
+
*/
|
|
48
|
+
function useVoidRedemption() {
|
|
49
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
50
|
+
return (0, react_query_1.useMutation)({
|
|
51
|
+
mutationFn: async ({ redemptionId, reason, }) => {
|
|
52
|
+
const client = (0, client_1.getApiClient)();
|
|
53
|
+
const response = await client.put(`/redemptions/${redemptionId}/void`, { reason });
|
|
54
|
+
return response.data.data;
|
|
55
|
+
},
|
|
56
|
+
onSuccess: () => {
|
|
57
|
+
// Invalidate wallet queries
|
|
58
|
+
queryClient.invalidateQueries({ queryKey: wallet_1.walletKeys.all });
|
|
59
|
+
queryClient.invalidateQueries({ queryKey: wallet_1.walletKeys.redemptions() });
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkZW1wdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL211dGF0aW9ucy9yZWRlbXB0aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7R0FJRzs7QUFnQ0gsOEJBc0JDO0FBT0QsOENBeUJDO0FBcEZELHVEQUF5RjtBQUN6RixzQ0FBc0M7QUFDdEMsOENBQW1FO0FBbUJuRSwrRUFBK0U7QUFDL0UsaUJBQWlCO0FBQ2pCLCtFQUErRTtBQUUvRTs7OztHQUlHO0FBQ0gsU0FBZ0IsU0FBUztJQUN2QixNQUFNLFdBQVcsR0FBRyxJQUFBLDRCQUFjLEdBQUUsQ0FBQTtJQUVwQyxPQUFPLElBQUEseUJBQVcsRUFBQztRQUNqQixVQUFVLEVBQUUsS0FBSyxFQUFFLE9BQXNCLEVBQTZCLEVBQUU7WUFDdEUsTUFBTSxNQUFNLEdBQUcsSUFBQSxxQkFBWSxHQUFFLENBQUE7WUFDN0IsTUFBTSxRQUFRLEdBQUcsTUFBTSxNQUFNLENBQUMsSUFBSSxDQUFDLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFBO1lBQ2xFLE9BQU8sUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUE7UUFDM0IsQ0FBQztRQUNELFNBQVMsRUFBRSxDQUFDLEtBQUssRUFBRSxTQUFTLEVBQUUsRUFBRTtZQUM5QixnREFBZ0Q7WUFDaEQsV0FBVyxDQUFDLGlCQUFpQixDQUFDO2dCQUM1QixRQUFRLEVBQUUsbUJBQVUsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQzthQUM5QyxDQUFDLENBQUE7WUFDRixpREFBaUQ7WUFDakQsSUFBSSxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQ3JCLFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQztvQkFDNUIsUUFBUSxFQUFFLG1CQUFVLENBQUMsZUFBZSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUM7aUJBQ3ZELENBQUMsQ0FBQTtZQUNKLENBQUM7UUFDSCxDQUFDO0tBQ0YsQ0FBQyxDQUFBO0FBQ0osQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFnQixpQkFBaUI7SUFLL0IsTUFBTSxXQUFXLEdBQUcsSUFBQSw0QkFBYyxHQUFFLENBQUE7SUFFcEMsT0FBTyxJQUFBLHlCQUFXLEVBQUM7UUFDakIsVUFBVSxFQUFFLEtBQUssRUFBRSxFQUNqQixZQUFZLEVBQ1osTUFBTSxHQUlQLEVBQTZCLEVBQUU7WUFDOUIsTUFBTSxNQUFNLEdBQUcsSUFBQSxxQkFBWSxHQUFFLENBQUE7WUFDN0IsTUFBTSxRQUFRLEdBQUcsTUFBTSxNQUFNLENBQUMsR0FBRyxDQUFDLGdCQUFnQixZQUFZLE9BQU8sRUFBRSxFQUFDLE1BQU0sRUFBQyxDQUFDLENBQUE7WUFDaEYsT0FBTyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQTtRQUMzQixDQUFDO1FBQ0QsU0FBUyxFQUFFLEdBQUcsRUFBRTtZQUNkLDRCQUE0QjtZQUM1QixXQUFXLENBQUMsaUJBQWlCLENBQUMsRUFBQyxRQUFRLEVBQUUsbUJBQVUsQ0FBQyxHQUFHLEVBQUMsQ0FBQyxDQUFBO1lBQ3pELFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFDLFFBQVEsRUFBRSxtQkFBVSxDQUFDLFdBQVcsRUFBRSxFQUFDLENBQUMsQ0FBQTtRQUNyRSxDQUFDO0tBQ0YsQ0FBQyxDQUFBO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmVkZW1wdGlvbiBNdXRhdGlvbiBIb29rc1xuICpcbiAqIFRhblN0YWNrIFF1ZXJ5IG11dGF0aW9ucyBmb3IgcmVkZW1wdGlvbiBvcGVyYXRpb25zLlxuICovXG5cbmltcG9ydCB7dXNlTXV0YXRpb24sIHVzZVF1ZXJ5Q2xpZW50LCB0eXBlIFVzZU11dGF0aW9uUmVzdWx0fSBmcm9tICdAdGFuc3RhY2svcmVhY3QtcXVlcnknXG5pbXBvcnQge2dldEFwaUNsaWVudH0gZnJvbSAnLi4vY2xpZW50J1xuaW1wb3J0IHt3YWxsZXRLZXlzLCB0eXBlIFdhbGxldFJlZGVtcHRpb259IGZyb20gJy4uL3F1ZXJpZXMvd2FsbGV0J1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBUWVBFU1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlZGVlbVJlcXVlc3Qge1xuICBxckNvZGU6IHN0cmluZ1xuICBzcG90SWQ/OiBzdHJpbmdcbiAgZGV2aWNlSW5mbz86IHN0cmluZ1xuICBsYXRpdHVkZT86IG51bWJlclxuICBsb25naXR1ZGU/OiBudW1iZXJcbiAgbm90ZXM/OiBzdHJpbmdcbn1cblxuZXhwb3J0IGludGVyZmFjZSBWb2lkUmVkZW1wdGlvblJlcXVlc3Qge1xuICByZWFzb246IHN0cmluZ1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBNVVRBVElPTiBIT09LU1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIFJlZGVlbSBhIFFSIGNvZGUgKHNwb3Qgb3duZXIvc3RhZmYgYWN0aW9uKVxuICpcbiAqIEBlbmRwb2ludCBQT1NUIC9yZWRlbXB0aW9ucy9yZWRlZW1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZVJlZGVlbSgpOiBVc2VNdXRhdGlvblJlc3VsdDxXYWxsZXRSZWRlbXB0aW9uLCBFcnJvciwgUmVkZWVtUmVxdWVzdD4ge1xuICBjb25zdCBxdWVyeUNsaWVudCA9IHVzZVF1ZXJ5Q2xpZW50KClcblxuICByZXR1cm4gdXNlTXV0YXRpb24oe1xuICAgIG11dGF0aW9uRm46IGFzeW5jIChyZXF1ZXN0OiBSZWRlZW1SZXF1ZXN0KTogUHJvbWlzZTxXYWxsZXRSZWRlbXB0aW9uPiA9PiB7XG4gICAgICBjb25zdCBjbGllbnQgPSBnZXRBcGlDbGllbnQoKVxuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjbGllbnQucG9zdCgnL3JlZGVtcHRpb25zL3JlZGVlbScsIHJlcXVlc3QpXG4gICAgICByZXR1cm4gcmVzcG9uc2UuZGF0YS5kYXRhXG4gICAgfSxcbiAgICBvblN1Y2Nlc3M6IChfZGF0YSwgdmFyaWFibGVzKSA9PiB7XG4gICAgICAvLyBJbnZhbGlkYXRlIHJlZGVtcHRpb24gbG9va3VwIGZvciB0aGlzIFFSIGNvZGVcbiAgICAgIHF1ZXJ5Q2xpZW50LmludmFsaWRhdGVRdWVyaWVzKHtcbiAgICAgICAgcXVlcnlLZXk6IHdhbGxldEtleXMubG9va3VwKHZhcmlhYmxlcy5xckNvZGUpLFxuICAgICAgfSlcbiAgICAgIC8vIEludmFsaWRhdGUgc3BvdCByZWRlbXB0aW9ucyBpZiBzcG90SWQgcHJvdmlkZWRcbiAgICAgIGlmICh2YXJpYWJsZXMuc3BvdElkKSB7XG4gICAgICAgIHF1ZXJ5Q2xpZW50LmludmFsaWRhdGVRdWVyaWVzKHtcbiAgICAgICAgICBxdWVyeUtleTogd2FsbGV0S2V5cy5zcG90UmVkZW1wdGlvbnModmFyaWFibGVzLnNwb3RJZCksXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfSxcbiAgfSlcbn1cblxuLyoqXG4gKiBWb2lkIGEgcmVkZW1wdGlvbiAoc3BvdCBvd25lciBhY3Rpb24pXG4gKlxuICogQGVuZHBvaW50IFBVVCAvcmVkZW1wdGlvbnMvOmlkL3ZvaWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZVZvaWRSZWRlbXB0aW9uKCk6IFVzZU11dGF0aW9uUmVzdWx0PFxuICBXYWxsZXRSZWRlbXB0aW9uLFxuICBFcnJvcixcbiAge3JlZGVtcHRpb25JZDogc3RyaW5nOyByZWFzb246IHN0cmluZ31cbj4ge1xuICBjb25zdCBxdWVyeUNsaWVudCA9IHVzZVF1ZXJ5Q2xpZW50KClcblxuICByZXR1cm4gdXNlTXV0YXRpb24oe1xuICAgIG11dGF0aW9uRm46IGFzeW5jICh7XG4gICAgICByZWRlbXB0aW9uSWQsXG4gICAgICByZWFzb24sXG4gICAgfToge1xuICAgICAgcmVkZW1wdGlvbklkOiBzdHJpbmdcbiAgICAgIHJlYXNvbjogc3RyaW5nXG4gICAgfSk6IFByb21pc2U8V2FsbGV0UmVkZW1wdGlvbj4gPT4ge1xuICAgICAgY29uc3QgY2xpZW50ID0gZ2V0QXBpQ2xpZW50KClcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xpZW50LnB1dChgL3JlZGVtcHRpb25zLyR7cmVkZW1wdGlvbklkfS92b2lkYCwge3JlYXNvbn0pXG4gICAgICByZXR1cm4gcmVzcG9uc2UuZGF0YS5kYXRhXG4gICAgfSxcbiAgICBvblN1Y2Nlc3M6ICgpID0+IHtcbiAgICAgIC8vIEludmFsaWRhdGUgd2FsbGV0IHF1ZXJpZXNcbiAgICAgIHF1ZXJ5Q2xpZW50LmludmFsaWRhdGVRdWVyaWVzKHtxdWVyeUtleTogd2FsbGV0S2V5cy5hbGx9KVxuICAgICAgcXVlcnlDbGllbnQuaW52YWxpZGF0ZVF1ZXJpZXMoe3F1ZXJ5S2V5OiB3YWxsZXRLZXlzLnJlZGVtcHRpb25zKCl9KVxuICAgIH0sXG4gIH0pXG59XG4iXX0=
|
|
@@ -19,7 +19,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
19
19
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.orderKeys = exports.productKeys = exports.miscKeys = exports.notificationKeys = exports.templateKeys = exports.clubKeys = exports.conversationKeys = exports.postKeys = exports.spotKeys = exports.userKeys = void 0;
|
|
22
|
+
exports.walletKeys = exports.orderKeys = exports.productKeys = exports.miscKeys = exports.notificationKeys = exports.templateKeys = exports.clubKeys = exports.conversationKeys = exports.postKeys = exports.spotKeys = exports.userKeys = void 0;
|
|
23
23
|
// Auth (mutations, but grouped with queries for convenience)
|
|
24
24
|
__exportStar(require("./auth"), exports);
|
|
25
25
|
// Users
|
|
@@ -62,4 +62,8 @@ Object.defineProperty(exports, "productKeys", { enumerable: true, get: function
|
|
|
62
62
|
__exportStar(require("./orders"), exports);
|
|
63
63
|
var orders_1 = require("./orders");
|
|
64
64
|
Object.defineProperty(exports, "orderKeys", { enumerable: true, get: function () { return orders_1.orderKeys; } });
|
|
65
|
-
|
|
65
|
+
// Wallet (redemptions)
|
|
66
|
+
__exportStar(require("./wallet"), exports);
|
|
67
|
+
var wallet_1 = require("./wallet");
|
|
68
|
+
Object.defineProperty(exports, "walletKeys", { enumerable: true, get: function () { return wallet_1.walletKeys; } });
|
|
69
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL3F1ZXJpZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0dBSUc7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRUgsNkRBQTZEO0FBQzdELHlDQUFzQjtBQUV0QixRQUFRO0FBQ1IsMENBQXVCO0FBQ3ZCLGlDQUFnQztBQUF4QixpR0FBQSxRQUFRLE9BQUE7QUFFaEIsUUFBUTtBQUNSLDBDQUF1QjtBQUN2QixpQ0FBZ0M7QUFBeEIsaUdBQUEsUUFBUSxPQUFBO0FBRWhCLFFBQVE7QUFDUiwwQ0FBdUI7QUFDdkIsaUNBQWdDO0FBQXhCLGlHQUFBLFFBQVEsT0FBQTtBQUVoQixnQkFBZ0I7QUFDaEIsa0RBQStCO0FBQy9CLGlEQUFnRDtBQUF4QyxpSEFBQSxnQkFBZ0IsT0FBQTtBQUV4QixRQUFRO0FBQ1IsMENBQXVCO0FBQ3ZCLGlDQUFnQztBQUF4QixpR0FBQSxRQUFRLE9BQUE7QUFFaEIsWUFBWTtBQUNaLDhDQUEyQjtBQUMzQix5Q0FBd0M7QUFBaEMseUdBQUEsWUFBWSxPQUFBO0FBRXBCLGdCQUFnQjtBQUNoQixrREFBK0I7QUFDL0IsaURBQWdEO0FBQXhDLGlIQUFBLGdCQUFnQixPQUFBO0FBRXhCLCtCQUErQjtBQUMvQix5Q0FBc0I7QUFDdEIsK0JBQStCO0FBQXZCLGdHQUFBLFFBQVEsT0FBQTtBQUVoQix5QkFBeUI7QUFDekIsNkNBQTBCO0FBQzFCLHVDQUFzQztBQUE5Qix1R0FBQSxXQUFXLE9BQUE7QUFFbkIsdUJBQXVCO0FBQ3ZCLDJDQUF3QjtBQUN4QixtQ0FBa0M7QUFBMUIsbUdBQUEsU0FBUyxPQUFBO0FBRWpCLHVCQUF1QjtBQUN2QiwyQ0FBd0I7QUFDeEIsbUNBQW1DO0FBQTNCLG9HQUFBLFVBQVUsT0FBQSIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU3BvdHMgU0RLIFF1ZXJ5IEhvb2tzIEluZGV4XG4gKlxuICogUmUtZXhwb3J0cyBhbGwgcXVlcnkgaG9va3MgYW5kIGtleXMuXG4gKi9cblxuLy8gQXV0aCAobXV0YXRpb25zLCBidXQgZ3JvdXBlZCB3aXRoIHF1ZXJpZXMgZm9yIGNvbnZlbmllbmNlKVxuZXhwb3J0ICogZnJvbSAnLi9hdXRoJ1xuXG4vLyBVc2Vyc1xuZXhwb3J0ICogZnJvbSAnLi91c2VycydcbmV4cG9ydCB7dXNlcktleXN9IGZyb20gJy4vdXNlcnMnXG5cbi8vIFNwb3RzXG5leHBvcnQgKiBmcm9tICcuL3Nwb3RzJ1xuZXhwb3J0IHtzcG90S2V5c30gZnJvbSAnLi9zcG90cydcblxuLy8gUG9zdHNcbmV4cG9ydCAqIGZyb20gJy4vcG9zdHMnXG5leHBvcnQge3Bvc3RLZXlzfSBmcm9tICcuL3Bvc3RzJ1xuXG4vLyBDb252ZXJzYXRpb25zXG5leHBvcnQgKiBmcm9tICcuL2NvbnZlcnNhdGlvbnMnXG5leHBvcnQge2NvbnZlcnNhdGlvbktleXN9IGZyb20gJy4vY29udmVyc2F0aW9ucydcblxuLy8gQ2x1YnNcbmV4cG9ydCAqIGZyb20gJy4vY2x1YnMnXG5leHBvcnQge2NsdWJLZXlzfSBmcm9tICcuL2NsdWJzJ1xuXG4vLyBUZW1wbGF0ZXNcbmV4cG9ydCAqIGZyb20gJy4vdGVtcGxhdGVzJ1xuZXhwb3J0IHt0ZW1wbGF0ZUtleXN9IGZyb20gJy4vdGVtcGxhdGVzJ1xuXG4vLyBOb3RpZmljYXRpb25zXG5leHBvcnQgKiBmcm9tICcuL25vdGlmaWNhdGlvbnMnXG5leHBvcnQge25vdGlmaWNhdGlvbktleXN9IGZyb20gJy4vbm90aWZpY2F0aW9ucydcblxuLy8gTWlzYyAoY2l0aWVzLCB2aWJlcywgZXZlbnRzKVxuZXhwb3J0ICogZnJvbSAnLi9taXNjJ1xuZXhwb3J0IHttaXNjS2V5c30gZnJvbSAnLi9taXNjJ1xuXG4vLyBQcm9kdWN0cyAobWFya2V0cGxhY2UpXG5leHBvcnQgKiBmcm9tICcuL3Byb2R1Y3RzJ1xuZXhwb3J0IHtwcm9kdWN0S2V5c30gZnJvbSAnLi9wcm9kdWN0cydcblxuLy8gT3JkZXJzIChtYXJrZXRwbGFjZSlcbmV4cG9ydCAqIGZyb20gJy4vb3JkZXJzJ1xuZXhwb3J0IHtvcmRlcktleXN9IGZyb20gJy4vb3JkZXJzJ1xuXG4vLyBXYWxsZXQgKHJlZGVtcHRpb25zKVxuZXhwb3J0ICogZnJvbSAnLi93YWxsZXQnXG5leHBvcnQge3dhbGxldEtleXN9IGZyb20gJy4vd2FsbGV0J1xuIl19
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for wallet and redemption operations.
|
|
5
|
+
*/
|
|
6
|
+
import { type UseQueryOptions, type UseQueryResult } from '@tanstack/react-query';
|
|
7
|
+
export type RedemptionStatus = 'PENDING' | 'REDEEMED' | 'EXPIRED' | 'CANCELLED' | 'VOID';
|
|
8
|
+
export interface WalletProduct {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
type: string;
|
|
12
|
+
imageUrl: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface WalletOrderItem {
|
|
15
|
+
product: WalletProduct;
|
|
16
|
+
quantity: number;
|
|
17
|
+
unitPrice: number;
|
|
18
|
+
}
|
|
19
|
+
export interface WalletOrder {
|
|
20
|
+
id: string;
|
|
21
|
+
orderNumber: string;
|
|
22
|
+
spotId: string;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
spot: {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
slug: string;
|
|
28
|
+
address: string | null;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface WalletRedemption {
|
|
32
|
+
id: string;
|
|
33
|
+
qrCode: string;
|
|
34
|
+
qrCodeUrl: string | null;
|
|
35
|
+
status: RedemptionStatus;
|
|
36
|
+
validFrom: string;
|
|
37
|
+
validUntil: string;
|
|
38
|
+
redeemedAt: string | null;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
orderItem: WalletOrderItem & {
|
|
41
|
+
order: WalletOrder;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface RedemptionLookup extends WalletRedemption {
|
|
45
|
+
redeemedBy: {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
} | null;
|
|
49
|
+
spot: {
|
|
50
|
+
id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
} | null;
|
|
53
|
+
orderItem: WalletOrderItem & {
|
|
54
|
+
order: WalletOrder & {
|
|
55
|
+
user: {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string | null;
|
|
58
|
+
avatarUrl: string | null;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export interface WalletHistoryResponse {
|
|
64
|
+
data: WalletRedemption[];
|
|
65
|
+
meta: {
|
|
66
|
+
total: number;
|
|
67
|
+
page: number;
|
|
68
|
+
limit: number;
|
|
69
|
+
totalPages: number;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export declare const walletKeys: {
|
|
73
|
+
all: readonly ["wallet"];
|
|
74
|
+
active: () => readonly ["wallet", "active"];
|
|
75
|
+
history: (page?: number) => readonly ["wallet", "history", number | undefined];
|
|
76
|
+
redemptions: () => readonly ["redemptions"];
|
|
77
|
+
lookup: (qrCode: string) => readonly ["redemptions", "lookup", string];
|
|
78
|
+
spotRedemptions: (spotId: string, page?: number) => readonly ["redemptions", "spot", string, number | undefined];
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Get user's active wallet items (pending redemptions)
|
|
82
|
+
*
|
|
83
|
+
* @endpoint GET /wallet
|
|
84
|
+
*/
|
|
85
|
+
export declare function useWallet(options?: Omit<UseQueryOptions<WalletRedemption[]>, 'queryKey' | 'queryFn'>): UseQueryResult<WalletRedemption[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Get user's wallet history (all redemptions)
|
|
88
|
+
*
|
|
89
|
+
* @endpoint GET /wallet/history
|
|
90
|
+
*/
|
|
91
|
+
export declare function useWalletHistory(params?: {
|
|
92
|
+
page?: number;
|
|
93
|
+
limit?: number;
|
|
94
|
+
}, options?: Omit<UseQueryOptions<WalletHistoryResponse>, 'queryKey' | 'queryFn'>): UseQueryResult<WalletHistoryResponse>;
|
|
95
|
+
/**
|
|
96
|
+
* Lookup redemption by QR code (for scanner)
|
|
97
|
+
*
|
|
98
|
+
* @endpoint GET /redemptions/lookup/:qrCode
|
|
99
|
+
*/
|
|
100
|
+
export declare function useRedemptionLookup(qrCode: string, options?: Omit<UseQueryOptions<RedemptionLookup>, 'queryKey' | 'queryFn'>): UseQueryResult<RedemptionLookup>;
|
|
101
|
+
/**
|
|
102
|
+
* Get spot's redemption history (for spot owner)
|
|
103
|
+
*
|
|
104
|
+
* @endpoint GET /spots/:spotId/redemptions
|
|
105
|
+
*/
|
|
106
|
+
export declare function useSpotRedemptions(spotId: string, params?: {
|
|
107
|
+
page?: number;
|
|
108
|
+
limit?: number;
|
|
109
|
+
}, options?: Omit<UseQueryOptions<WalletHistoryResponse>, 'queryKey' | 'queryFn'>): UseQueryResult<WalletHistoryResponse>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Wallet Query Hooks
|
|
4
|
+
*
|
|
5
|
+
* TanStack Query hooks for wallet and redemption operations.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.walletKeys = void 0;
|
|
9
|
+
exports.useWallet = useWallet;
|
|
10
|
+
exports.useWalletHistory = useWalletHistory;
|
|
11
|
+
exports.useRedemptionLookup = useRedemptionLookup;
|
|
12
|
+
exports.useSpotRedemptions = useSpotRedemptions;
|
|
13
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
14
|
+
const client_1 = require("../client");
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// HELPER FUNCTIONS
|
|
17
|
+
// ============================================================================
|
|
18
|
+
function extractArrayData(data) {
|
|
19
|
+
if (Array.isArray(data)) {
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
22
|
+
if (data && typeof data === 'object' && 'data' in data) {
|
|
23
|
+
const nested = data.data;
|
|
24
|
+
if (Array.isArray(nested)) {
|
|
25
|
+
return nested;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
function extractObjectData(data) {
|
|
31
|
+
if (data &&
|
|
32
|
+
typeof data === 'object' &&
|
|
33
|
+
'data' in data &&
|
|
34
|
+
!Array.isArray(data)) {
|
|
35
|
+
const nested = data.data;
|
|
36
|
+
if (nested &&
|
|
37
|
+
typeof nested === 'object' &&
|
|
38
|
+
'data' in nested &&
|
|
39
|
+
!Array.isArray(nested)) {
|
|
40
|
+
return nested.data;
|
|
41
|
+
}
|
|
42
|
+
return nested;
|
|
43
|
+
}
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// QUERY KEYS
|
|
48
|
+
// ============================================================================
|
|
49
|
+
exports.walletKeys = {
|
|
50
|
+
all: ['wallet'],
|
|
51
|
+
active: () => [...exports.walletKeys.all, 'active'],
|
|
52
|
+
history: (page) => [...exports.walletKeys.all, 'history', page],
|
|
53
|
+
redemptions: () => ['redemptions'],
|
|
54
|
+
lookup: (qrCode) => [...exports.walletKeys.redemptions(), 'lookup', qrCode],
|
|
55
|
+
spotRedemptions: (spotId, page) => [...exports.walletKeys.redemptions(), 'spot', spotId, page],
|
|
56
|
+
};
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// QUERY HOOKS
|
|
59
|
+
// ============================================================================
|
|
60
|
+
/**
|
|
61
|
+
* Get user's active wallet items (pending redemptions)
|
|
62
|
+
*
|
|
63
|
+
* @endpoint GET /wallet
|
|
64
|
+
*/
|
|
65
|
+
function useWallet(options) {
|
|
66
|
+
return (0, react_query_1.useQuery)({
|
|
67
|
+
queryKey: exports.walletKeys.active(),
|
|
68
|
+
queryFn: async () => {
|
|
69
|
+
const client = (0, client_1.getApiClient)();
|
|
70
|
+
const response = await client.get('/wallet');
|
|
71
|
+
return extractArrayData(response.data.data);
|
|
72
|
+
},
|
|
73
|
+
...options,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get user's wallet history (all redemptions)
|
|
78
|
+
*
|
|
79
|
+
* @endpoint GET /wallet/history
|
|
80
|
+
*/
|
|
81
|
+
function useWalletHistory(params, options) {
|
|
82
|
+
return (0, react_query_1.useQuery)({
|
|
83
|
+
queryKey: exports.walletKeys.history(params?.page),
|
|
84
|
+
queryFn: async () => {
|
|
85
|
+
const client = (0, client_1.getApiClient)();
|
|
86
|
+
const queryParams = new URLSearchParams();
|
|
87
|
+
if (params?.page)
|
|
88
|
+
queryParams.set('page', String(params.page));
|
|
89
|
+
if (params?.limit)
|
|
90
|
+
queryParams.set('limit', String(params.limit));
|
|
91
|
+
const response = await client.get(`/wallet/history?${queryParams}`);
|
|
92
|
+
return extractObjectData(response.data.data);
|
|
93
|
+
},
|
|
94
|
+
...options,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Lookup redemption by QR code (for scanner)
|
|
99
|
+
*
|
|
100
|
+
* @endpoint GET /redemptions/lookup/:qrCode
|
|
101
|
+
*/
|
|
102
|
+
function useRedemptionLookup(qrCode, options) {
|
|
103
|
+
return (0, react_query_1.useQuery)({
|
|
104
|
+
queryKey: exports.walletKeys.lookup(qrCode),
|
|
105
|
+
queryFn: async () => {
|
|
106
|
+
const client = (0, client_1.getApiClient)();
|
|
107
|
+
const response = await client.get(`/redemptions/lookup/${qrCode}`);
|
|
108
|
+
return extractObjectData(response.data.data);
|
|
109
|
+
},
|
|
110
|
+
enabled: !!qrCode && qrCode.length > 0,
|
|
111
|
+
...options,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get spot's redemption history (for spot owner)
|
|
116
|
+
*
|
|
117
|
+
* @endpoint GET /spots/:spotId/redemptions
|
|
118
|
+
*/
|
|
119
|
+
function useSpotRedemptions(spotId, params, options) {
|
|
120
|
+
return (0, react_query_1.useQuery)({
|
|
121
|
+
queryKey: exports.walletKeys.spotRedemptions(spotId, params?.page),
|
|
122
|
+
queryFn: async () => {
|
|
123
|
+
const client = (0, client_1.getApiClient)();
|
|
124
|
+
const queryParams = new URLSearchParams();
|
|
125
|
+
if (params?.page)
|
|
126
|
+
queryParams.set('page', String(params.page));
|
|
127
|
+
if (params?.limit)
|
|
128
|
+
queryParams.set('limit', String(params.limit));
|
|
129
|
+
const response = await client.get(`/spots/${spotId}/redemptions?${queryParams}`);
|
|
130
|
+
return extractObjectData(response.data.data);
|
|
131
|
+
},
|
|
132
|
+
enabled: !!spotId,
|
|
133
|
+
...options,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2FsbGV0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9xdWVyaWVzL3dhbGxldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7R0FJRzs7O0FBb0pILDhCQVlDO0FBT0QsNENBa0JDO0FBT0Qsa0RBZ0JDO0FBT0QsZ0RBb0JDO0FBek9ELHVEQUk4QjtBQUU5QixzQ0FBc0M7QUErRXRDLCtFQUErRTtBQUMvRSxtQkFBbUI7QUFDbkIsK0VBQStFO0FBRS9FLFNBQVMsZ0JBQWdCLENBQUksSUFBYTtJQUN4QyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUN4QixPQUFPLElBQUksQ0FBQTtJQUNiLENBQUM7SUFDRCxJQUFJLElBQUksSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksTUFBTSxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ3ZELE1BQU0sTUFBTSxHQUFJLElBQXdCLENBQUMsSUFBSSxDQUFBO1FBQzdDLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQzFCLE9BQU8sTUFBTSxDQUFBO1FBQ2YsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLEVBQUUsQ0FBQTtBQUNYLENBQUM7QUFFRCxTQUFTLGlCQUFpQixDQUFJLElBQWE7SUFDekMsSUFDRSxJQUFJO1FBQ0osT0FBTyxJQUFJLEtBQUssUUFBUTtRQUN4QixNQUFNLElBQUksSUFBSTtRQUNkLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFDcEIsQ0FBQztRQUNELE1BQU0sTUFBTSxHQUFJLElBQXdCLENBQUMsSUFBSSxDQUFBO1FBQzdDLElBQ0UsTUFBTTtZQUNOLE9BQU8sTUFBTSxLQUFLLFFBQVE7WUFDMUIsTUFBTSxJQUFJLE1BQU07WUFDaEIsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUN0QixDQUFDO1lBQ0QsT0FBUSxN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QUMsQ0FBQTtRQUNyRSxDQUFDO1FBQ0QsT0FBTyxFQUFFLENBQUMsQ0FBQyxNQUFNO1FBQ2pCLEdBQUcsT0FBTztLQUNYLENBQUMsQ0FBQTtBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFdhbGxldCBRdWVyeSBIb29rc1xuICpcbiAqIFRhblN0YWNrIFF1ZXJ5IGhvb2tzIGZvciB3YWxsZXQgYW5kIHJlZGVtcHRpb24gb3BlcmF0aW9ucy5cbiAqL1xuXG5pbXBvcnQge1xuICB1c2VRdWVyeSxcbiAgdHlwZSBVc2VRdWVyeU9wdGlvbnMsXG4gIHR5cGUgVXNlUXVlcnlSZXN1bHQsXG59IGZyb20gJ0B0YW5zdGFjay9yZWFjdC1xdWVyeSdcblxuaW1wb3J0IHtnZXRBcGlDbGllbnR9IGZyb20gJy4uL2NsaWVudCdcbmltcG9ydCB7dHlwZSBBcGlSZXNwb25zZX0gZnJvbSAnLi4vdHlwZXMnXG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFRZUEVTXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCB0eXBlIFJlZGVtcHRpb25TdGF0dXMgPSAnUEVORElORycgfCAnUkVERUVNRUQnIHwgJ0VYUElSRUQnIHwgJ0NBTkNFTExFRCcgfCAnVk9JRCdcblxuZXhwb3J0IGludGVyZmFjZSBXYWxsZXRQcm9kdWN0IHtcbiAgaWQ6IHN0cmluZ1xuICBuYW1lOiBzdHJpbmdcbiAgdHlwZTogc3RyaW5nXG4gIGltYWdlVXJsOiBzdHJpbmcgfCBudWxsXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgV2FsbGV0T3JkZXJJdGVtIHtcbiAgcHJvZHVjdDogV2FsbGV0UHJvZHVjdFxuICBxdWFudGl0eTogbnVtYmVyXG4gIHVuaXRQcmljZTogbnVtYmVyXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgV2FsbGV0T3JkZXIge1xuICBpZDogc3RyaW5nXG4gIG9yZGVyTnVtYmVyOiBzdHJpbmdcbiAgc3BvdElkOiBzdHJpbmdcbiAgY3JlYXRlZEF0OiBzdHJpbmdcbiAgc3BvdDoge1xuICAgIGlkOiBzdHJpbmdcbiAgICBuYW1lOiBzdHJpbmdcbiAgICBzbHVnOiBzdHJpbmdcbiAgICBhZGRyZXNzOiBzdHJpbmcgfCBudWxsXG4gIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBXYWxsZXRSZWRlbXB0aW9uIHtcbiAgaWQ6IHN0cmluZ1xuICBxckNvZGU6IHN0cmluZ1xuICBxckNvZGVVcmw6IHN0cmluZyB8IG51bGxcbiAgc3RhdHVzOiBSZWRlbXB0aW9uU3RhdHVzXG4gIHZhbGlkRnJvbTogc3RyaW5nXG4gIHZhbGlkVW50aWw6IHN0cmluZ1xuICByZWRlZW1lZEF0OiBzdHJpbmcgfCBudWxsXG4gIGNyZWF0ZWRBdDogc3RyaW5nXG4gIG9yZGVySXRlbTogV2FsbGV0T3JkZXJJdGVtICYge1xuICAgIG9yZGVyOiBXYWxsZXRPcmRlclxuICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVkZW1wdGlvbkxvb2t1cCBleHRlbmRzIFdhbGxldFJlZGVtcHRpb24ge1xuICByZWRlZW1lZEJ5OiB7XG4gICAgaWQ6IHN0cmluZ1xuICAgIG5hbWU6IHN0cmluZ1xuICB9IHwgbnVsbFxuICBzcG90OiB7XG4gICAgaWQ6IHN0cmluZ1xuICAgIG5hbWU6IHN0cmluZ1xuICB9IHwgbnVsbFxuICBvcmRlckl0ZW06IFdhbGxldE9yZGVySXRlbSAmIHtcbiAgICBvcmRlcjogV2FsbGV0T3JkZXIgJiB7XG4gICAgICB1c2VyOiB7XG4gICAgICAgIGlkOiBzdHJpbmdcbiAgICAgICAgbmFtZTogc3RyaW5nIHwgbnVsbFxuICAgICAgICBhdmF0YXJVcmw6IHN0cmluZyB8IG51bGxcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBXYWxsZXRIaXN0b3J5UmVzcG9uc2Uge1xuICBkYXRhOiBXYWxsZXRSZWRlbXB0aW9uW11cbiAgbWV0YToge1xuICAgIHRvdGFsOiBudW1iZXJcbiAgICBwYWdlOiBudW1iZXJcbiAgICBsaW1pdDogbnVtYmVyXG4gICAgdG90YWxQYWdlczogbnVtYmVyXG4gIH1cbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gSEVMUEVSIEZVTkNUSU9OU1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5mdW5jdGlvbiBleHRyYWN0QXJyYXlEYXRhPFQ+KGRhdGE6IHVua25vd24pOiBUW10ge1xuICBpZiAoQXJyYXkuaXNBcnJheShkYXRhKSkge1xuICAgIHJldHVybiBkYXRhXG4gIH1cbiAgaWYgKGRhdGEgJiYgdHlwZW9mIGRhdGEgPT09ICdvYmplY3QnICYmICdkYXRhJyBpbiBkYXRhKSB7XG4gICAgY29uc3QgbmVzdGVkID0gKGRhdGEgYXMge2RhdGE6IHVua25vd259KS5kYXRhXG4gICAgaWYgKEFycmF5LmlzQXJyYXkobmVzdGVkKSkge1xuICAgICAgcmV0dXJuIG5lc3RlZFxuICAgIH1cbiAgfVxuICByZXR1cm4gW11cbn1cblxuZnVuY3Rpb24gZXh0cmFjdE9iamVjdERhdGE8VD4oZGF0YTogdW5rbm93bik6IFQge1xuICBpZiAoXG4gICAgZGF0YSAmJlxuICAgIHR5cGVvZiBkYXRhID09PSAnb2JqZWN0JyAmJlxuICAgICdkYXRhJyBpbiBkYXRhICYmXG4gICAgIUFycmF5LmlzQXJyYXkoZGF0YSlcbiAgKSB7XG4gICAgY29uc3QgbmVzdGVkID0gKGRhdGEgYXMge2RhdGE6IHVua25vd259KS5kYXRhXG4gICAgaWYgKFxuICAgICAgbmVzdGVkICYmXG4gICAgICB0eXBlb2YgbmVzdGVkID09PSAnb2JqZWN0JyAmJlxuICAgICAgJ2RhdGEnIGluIG5lc3RlZCAmJlxuICAgICAgIUFycmF5LmlzQXJyYXkobmVzdGVkKVxuICAgICkge1xuICAgICAgcmV0dXJuIChuZXN0ZWQgYXMge2RhdGE6IFR9KS5kYXRhXG4gICAgfVxuICAgIHJldHVybiBuZXN0ZWQgYXMgVFxuICB9XG4gIHJldHVybiBkYXRhIGFzIFRcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gUVVFUlkgS0VZU1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5leHBvcnQgY29uc3Qgd2FsbGV0S2V5cyA9IHtcbiAgYWxsOiBbJ3dhbGxldCddIGFzIGNvbnN0LFxuICBhY3RpdmU6ICgpID0+IFsuLi53YWxsZXRLZXlzLmFsbCwgJ2FjdGl2ZSddIGFzIGNvbnN0LFxuICBoaXN0b3J5OiAocGFnZT86IG51bWJlcikgPT4gWy4uLndhbGxldEtleXMuYWxsLCAnaGlzdG9yeScsIHBhZ2VdIGFzIGNvbnN0LFxuICByZWRlbXB0aW9uczogKCkgPT4gWydyZWRlbXB0aW9ucyddIGFzIGNvbnN0LFxuICBsb29rdXA6IChxckNvZGU6IHN0cmluZykgPT4gWy4uLndhbGxldEtleXMucmVkZW1wdGlvbnMoKSwgJ2xvb2t1cCcsIHFyQ29kZV0gYXMgY29uc3QsXG4gIHNwb3RSZWRlbXB0aW9uczogKHNwb3RJZDogc3RyaW5nLCBwYWdlPzogbnVtYmVyKSA9PlxuICAgIFsuLi53YWxsZXRLZXlzLnJlZGVtcHRpb25zKCksICdzcG90Jywgc3BvdElkLCBwYWdlXSBhcyBjb25zdCxcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gUVVFUlkgSE9PS1Ncbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBHZXQgdXNlcidzIGFjdGl2ZSB3YWxsZXQgaXRlbXMgKHBlbmRpbmcgcmVkZW1wdGlvbnMpXG4gKlxuICogQGVuZHBvaW50IEdFVCAvd2FsbGV0XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1c2VXYWxsZXQoXG4gIG9wdGlvbnM/OiBPbWl0PFVzZVF1ZXJ5T3B0aW9uczxXYWxsZXRSZWRlbXB0aW9uW10+LCAncXVlcnlLZXknIHwgJ3F1ZXJ5Rm4nPixcbik6IFVzZVF1ZXJ5UmVzdWx0PFdhbGxldFJlZGVtcHRpb25bXT4ge1xuICByZXR1cm4gdXNlUXVlcnkoe1xuICAgIHF1ZXJ5S2V5OiB3YWxsZXRLZXlzLmFjdGl2ZSgpLFxuICAgIHF1ZXJ5Rm46IGFzeW5jICgpOiBQcm9taXNlPFdhbGxldFJlZGVtcHRpb25bXT4gPT4ge1xuICAgICAgY29uc3QgY2xpZW50ID0gZ2V0QXBpQ2xpZW50KClcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xpZW50LmdldDxBcGlSZXNwb25zZTx1bmtub3duPj4oJy93YWxsZXQnKVxuICAgICAgcmV0dXJuIGV4dHJhY3RBcnJheURhdGE8V2FsbGV0UmVkZW1wdGlvbj4ocmVzcG9uc2UuZGF0YS5kYXRhKVxuICAgIH0sXG4gICAgLi4ub3B0aW9ucyxcbiAgfSlcbn1cblxuLyoqXG4gKiBHZXQgdXNlcidzIHdhbGxldCBoaXN0b3J5IChhbGwgcmVkZW1wdGlvbnMpXG4gKlxuICogQGVuZHBvaW50IEdFVCAvd2FsbGV0L2hpc3RvcnlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZVdhbGxldEhpc3RvcnkoXG4gIHBhcmFtcz86IHtwYWdlPzogbnVtYmVyOyBsaW1pdD86IG51bWJlcn0sXG4gIG9wdGlvbnM/OiBPbWl0PFVzZVF1ZXJ5T3B0aW9uczxXYWxsZXRIaXN0b3J5UmVzcG9uc2U+LCAncXVlcnlLZXknIHwgJ3F1ZXJ5Rm4nPixcbik6IFVzZVF1ZXJ5UmVzdWx0PFdhbGxldEhpc3RvcnlSZXNwb25zZT4ge1xuICByZXR1cm4gdXNlUXVlcnkoe1xuICAgIHF1ZXJ5S2V5OiB3YWxsZXRLZXlzLmhpc3RvcnkocGFyYW1zPy5wYWdlKSxcbiAgICBxdWVyeUZuOiBhc3luYyAoKTogUHJvbWlzZTxXYWxsZXRIaXN0b3J5UmVzcG9uc2U+ID0+IHtcbiAgICAgIGNvbnN0IGNsaWVudCA9IGdldEFwaUNsaWVudCgpXG4gICAgICBjb25zdCBxdWVyeVBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKVxuICAgICAgaWYgKHBhcmFtcz8ucGFnZSkgcXVlcnlQYXJhbXMuc2V0KCdwYWdlJywgU3RyaW5nKHBhcmFtcy5wYWdlKSlcbiAgICAgIGlmIChwYXJhbXM/LmxpbWl0KSBxdWVyeVBhcmFtcy5zZXQoJ2xpbWl0JywgU3RyaW5nKHBhcmFtcy5saW1pdCkpXG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5nZXQ8QXBpUmVzcG9uc2U8V2FsbGV0SGlzdG9yeVJlc3BvbnNlPj4oXG4gICAgICAgIGAvd2FsbGV0L2hpc3Rvcnk/JHtxdWVyeVBhcmFtc31gLFxuICAgICAgKVxuICAgICAgcmV0dXJuIGV4dHJhY3RPYmplY3REYXRhPFdhbGxldEhpc3RvcnlSZXNwb25zZT4ocmVzcG9uc2UuZGF0YS5kYXRhKVxuICAgIH0sXG4gICAgLi4ub3B0aW9ucyxcbiAgfSlcbn1cblxuLyoqXG4gKiBMb29rdXAgcmVkZW1wdGlvbiBieSBRUiBjb2RlIChmb3Igc2Nhbm5lcilcbiAqXG4gKiBAZW5kcG9pbnQgR0VUIC9yZWRlbXB0aW9ucy9sb29rdXAvOnFyQ29kZVxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlUmVkZW1wdGlvbkxvb2t1cChcbiAgcXJDb2RlOiBzdHJpbmcsXG4gIG9wdGlvbnM/OiBPbWl0PFVzZVF1ZXJ5T3B0aW9uczxSZWRlbXB0aW9uTG9va3VwPiwgJ3F1ZXJ5S2V5JyB8ICdxdWVyeUZuJz4sXG4pOiBVc2VRdWVyeVJlc3VsdDxSZWRlbXB0aW9uTG9va3VwPiB7XG4gIHJldHVybiB1c2VRdWVyeSh7XG4gICAgcXVlcnlLZXk6IHdhbGxldEtleXMubG9va3VwKHFyQ29kZSksXG4gICAgcXVlcnlGbjogYXN5bmMgKCk6IFByb21pc2U8UmVkZW1wdGlvbkxvb2t1cD4gPT4ge1xuICAgICAgY29uc3QgY2xpZW50ID0gZ2V0QXBpQ2xpZW50KClcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xpZW50LmdldDxBcGlSZXNwb25zZTx1bmtub3duPj4oXG4gICAgICAgIGAvcmVkZW1wdGlvbnMvbG9va3VwLyR7cXJDb2RlfWAsXG4gICAgICApXG4gICAgICByZXR1cm4gZXh0cmFjdE9iamVjdERhdGE8UmVkZW1wdGlvbkxvb2t1cD4ocmVzcG9uc2UuZGF0YS5kYXRhKVxuICAgIH0sXG4gICAgZW5hYmxlZDogISFxckNvZGUgJiYgcXJDb2RlLmxlbmd0aCA+IDAsXG4gICAgLi4ub3B0aW9ucyxcbiAgfSlcbn1cblxuLyoqXG4gKiBHZXQgc3BvdCdzIHJlZGVtcHRpb24gaGlzdG9yeSAoZm9yIHNwb3Qgb3duZXIpXG4gKlxuICogQGVuZHBvaW50IEdFVCAvc3BvdHMvOnNwb3RJZC9yZWRlbXB0aW9uc1xuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlU3BvdFJlZGVtcHRpb25zKFxuICBzcG90SWQ6IHN0cmluZyxcbiAgcGFyYW1zPzoge3BhZ2U/OiBudW1iZXI7IGxpbWl0PzogbnVtYmVyfSxcbiAgb3B0aW9ucz86IE9taXQ8VXNlUXVlcnlPcHRpb25zPFdhbGxldEhpc3RvcnlSZXNwb25zZT4sICdxdWVyeUtleScgfCAncXVlcnlGbic+LFxuKTogVXNlUXVlcnlSZXN1bHQ8V2FsbGV0SGlzdG9yeVJlc3BvbnNlPiB7XG4gIHJldHVybiB1c2VRdWVyeSh7XG4gICAgcXVlcnlLZXk6IHdhbGxldEtleXMuc3BvdFJlZGVtcHRpb25zKHNwb3RJZCwgcGFyYW1zPy5wYWdlKSxcbiAgICBxdWVyeUZuOiBhc3luYyAoKTogUHJvbWlzZTxXYWxsZXRIaXN0b3J5UmVzcG9uc2U+ID0+IHtcbiAgICAgIGNvbnN0IGNsaWVudCA9IGdldEFwaUNsaWVudCgpXG4gICAgICBjb25zdCBxdWVyeVBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKVxuICAgICAgaWYgKHBhcmFtcz8ucGFnZSkgcXVlcnlQYXJhbXMuc2V0KCdwYWdlJywgU3RyaW5nKHBhcmFtcy5wYWdlKSlcbiAgICAgIGlmIChwYXJhbXM/LmxpbWl0KSBxdWVyeVBhcmFtcy5zZXQoJ2xpbWl0JywgU3RyaW5nKHBhcmFtcy5saW1pdCkpXG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5nZXQ8QXBpUmVzcG9uc2U8V2FsbGV0SGlzdG9yeVJlc3BvbnNlPj4oXG4gICAgICAgIGAvc3BvdHMvJHtzcG90SWR9L3JlZGVtcHRpb25zPyR7cXVlcnlQYXJhbXN9YCxcbiAgICAgIClcbiAgICAgIHJldHVybiBleHRyYWN0T2JqZWN0RGF0YTxXYWxsZXRIaXN0b3J5UmVzcG9uc2U+KHJlc3BvbnNlLmRhdGEuZGF0YSlcbiAgICB9LFxuICAgIGVuYWJsZWQ6ICEhc3BvdElkLFxuICAgIC4uLm9wdGlvbnMsXG4gIH0pXG59XG4iXX0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spotsdev/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Shared TypeScript SDK for Spots API - TanStack Query hooks, API client, and utilities",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"react": ">=18.0.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@spotsdev/types": "^1.
|
|
35
|
+
"@spotsdev/types": "^1.1.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^20.0.0",
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redemption Mutation Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query mutations for redemption operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {useMutation, useQueryClient, type UseMutationResult} from '@tanstack/react-query'
|
|
8
|
+
import {getApiClient} from '../client'
|
|
9
|
+
import {walletKeys, type WalletRedemption} from '../queries/wallet'
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// TYPES
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export interface RedeemRequest {
|
|
16
|
+
qrCode: string
|
|
17
|
+
spotId?: string
|
|
18
|
+
deviceInfo?: string
|
|
19
|
+
latitude?: number
|
|
20
|
+
longitude?: number
|
|
21
|
+
notes?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface VoidRedemptionRequest {
|
|
25
|
+
reason: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// MUTATION HOOKS
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Redeem a QR code (spot owner/staff action)
|
|
34
|
+
*
|
|
35
|
+
* @endpoint POST /redemptions/redeem
|
|
36
|
+
*/
|
|
37
|
+
export function useRedeem(): UseMutationResult<WalletRedemption, Error, RedeemRequest> {
|
|
38
|
+
const queryClient = useQueryClient()
|
|
39
|
+
|
|
40
|
+
return useMutation({
|
|
41
|
+
mutationFn: async (request: RedeemRequest): Promise<WalletRedemption> => {
|
|
42
|
+
const client = getApiClient()
|
|
43
|
+
const response = await client.post('/redemptions/redeem', request)
|
|
44
|
+
return response.data.data
|
|
45
|
+
},
|
|
46
|
+
onSuccess: (_data, variables) => {
|
|
47
|
+
// Invalidate redemption lookup for this QR code
|
|
48
|
+
queryClient.invalidateQueries({
|
|
49
|
+
queryKey: walletKeys.lookup(variables.qrCode),
|
|
50
|
+
})
|
|
51
|
+
// Invalidate spot redemptions if spotId provided
|
|
52
|
+
if (variables.spotId) {
|
|
53
|
+
queryClient.invalidateQueries({
|
|
54
|
+
queryKey: walletKeys.spotRedemptions(variables.spotId),
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Void a redemption (spot owner action)
|
|
63
|
+
*
|
|
64
|
+
* @endpoint PUT /redemptions/:id/void
|
|
65
|
+
*/
|
|
66
|
+
export function useVoidRedemption(): UseMutationResult<
|
|
67
|
+
WalletRedemption,
|
|
68
|
+
Error,
|
|
69
|
+
{redemptionId: string; reason: string}
|
|
70
|
+
> {
|
|
71
|
+
const queryClient = useQueryClient()
|
|
72
|
+
|
|
73
|
+
return useMutation({
|
|
74
|
+
mutationFn: async ({
|
|
75
|
+
redemptionId,
|
|
76
|
+
reason,
|
|
77
|
+
}: {
|
|
78
|
+
redemptionId: string
|
|
79
|
+
reason: string
|
|
80
|
+
}): Promise<WalletRedemption> => {
|
|
81
|
+
const client = getApiClient()
|
|
82
|
+
const response = await client.put(`/redemptions/${redemptionId}/void`, {reason})
|
|
83
|
+
return response.data.data
|
|
84
|
+
},
|
|
85
|
+
onSuccess: () => {
|
|
86
|
+
// Invalidate wallet queries
|
|
87
|
+
queryClient.invalidateQueries({queryKey: walletKeys.all})
|
|
88
|
+
queryClient.invalidateQueries({queryKey: walletKeys.redemptions()})
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
}
|
package/src/api/queries/index.ts
CHANGED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for wallet and redemption operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
useQuery,
|
|
9
|
+
type UseQueryOptions,
|
|
10
|
+
type UseQueryResult,
|
|
11
|
+
} from '@tanstack/react-query'
|
|
12
|
+
|
|
13
|
+
import {getApiClient} from '../client'
|
|
14
|
+
import {type ApiResponse} from '../types'
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// TYPES
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export type RedemptionStatus = 'PENDING' | 'REDEEMED' | 'EXPIRED' | 'CANCELLED' | 'VOID'
|
|
21
|
+
|
|
22
|
+
export interface WalletProduct {
|
|
23
|
+
id: string
|
|
24
|
+
name: string
|
|
25
|
+
type: string
|
|
26
|
+
imageUrl: string | null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface WalletOrderItem {
|
|
30
|
+
product: WalletProduct
|
|
31
|
+
quantity: number
|
|
32
|
+
unitPrice: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface WalletOrder {
|
|
36
|
+
id: string
|
|
37
|
+
orderNumber: string
|
|
38
|
+
spotId: string
|
|
39
|
+
createdAt: string
|
|
40
|
+
spot: {
|
|
41
|
+
id: string
|
|
42
|
+
name: string
|
|
43
|
+
slug: string
|
|
44
|
+
address: string | null
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface WalletRedemption {
|
|
49
|
+
id: string
|
|
50
|
+
qrCode: string
|
|
51
|
+
qrCodeUrl: string | null
|
|
52
|
+
status: RedemptionStatus
|
|
53
|
+
validFrom: string
|
|
54
|
+
validUntil: string
|
|
55
|
+
redeemedAt: string | null
|
|
56
|
+
createdAt: string
|
|
57
|
+
orderItem: WalletOrderItem & {
|
|
58
|
+
order: WalletOrder
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface RedemptionLookup extends WalletRedemption {
|
|
63
|
+
redeemedBy: {
|
|
64
|
+
id: string
|
|
65
|
+
name: string
|
|
66
|
+
} | null
|
|
67
|
+
spot: {
|
|
68
|
+
id: string
|
|
69
|
+
name: string
|
|
70
|
+
} | null
|
|
71
|
+
orderItem: WalletOrderItem & {
|
|
72
|
+
order: WalletOrder & {
|
|
73
|
+
user: {
|
|
74
|
+
id: string
|
|
75
|
+
name: string | null
|
|
76
|
+
avatarUrl: string | null
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface WalletHistoryResponse {
|
|
83
|
+
data: WalletRedemption[]
|
|
84
|
+
meta: {
|
|
85
|
+
total: number
|
|
86
|
+
page: number
|
|
87
|
+
limit: number
|
|
88
|
+
totalPages: number
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// HELPER FUNCTIONS
|
|
94
|
+
// ============================================================================
|
|
95
|
+
|
|
96
|
+
function extractArrayData<T>(data: unknown): T[] {
|
|
97
|
+
if (Array.isArray(data)) {
|
|
98
|
+
return data
|
|
99
|
+
}
|
|
100
|
+
if (data && typeof data === 'object' && 'data' in data) {
|
|
101
|
+
const nested = (data as {data: unknown}).data
|
|
102
|
+
if (Array.isArray(nested)) {
|
|
103
|
+
return nested
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return []
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function extractObjectData<T>(data: unknown): T {
|
|
110
|
+
if (
|
|
111
|
+
data &&
|
|
112
|
+
typeof data === 'object' &&
|
|
113
|
+
'data' in data &&
|
|
114
|
+
!Array.isArray(data)
|
|
115
|
+
) {
|
|
116
|
+
const nested = (data as {data: unknown}).data
|
|
117
|
+
if (
|
|
118
|
+
nested &&
|
|
119
|
+
typeof nested === 'object' &&
|
|
120
|
+
'data' in nested &&
|
|
121
|
+
!Array.isArray(nested)
|
|
122
|
+
) {
|
|
123
|
+
return (nested as {data: T}).data
|
|
124
|
+
}
|
|
125
|
+
return nested as T
|
|
126
|
+
}
|
|
127
|
+
return data as T
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// QUERY KEYS
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
export const walletKeys = {
|
|
135
|
+
all: ['wallet'] as const,
|
|
136
|
+
active: () => [...walletKeys.all, 'active'] as const,
|
|
137
|
+
history: (page?: number) => [...walletKeys.all, 'history', page] as const,
|
|
138
|
+
redemptions: () => ['redemptions'] as const,
|
|
139
|
+
lookup: (qrCode: string) => [...walletKeys.redemptions(), 'lookup', qrCode] as const,
|
|
140
|
+
spotRedemptions: (spotId: string, page?: number) =>
|
|
141
|
+
[...walletKeys.redemptions(), 'spot', spotId, page] as const,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// QUERY HOOKS
|
|
146
|
+
// ============================================================================
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get user's active wallet items (pending redemptions)
|
|
150
|
+
*
|
|
151
|
+
* @endpoint GET /wallet
|
|
152
|
+
*/
|
|
153
|
+
export function useWallet(
|
|
154
|
+
options?: Omit<UseQueryOptions<WalletRedemption[]>, 'queryKey' | 'queryFn'>,
|
|
155
|
+
): UseQueryResult<WalletRedemption[]> {
|
|
156
|
+
return useQuery({
|
|
157
|
+
queryKey: walletKeys.active(),
|
|
158
|
+
queryFn: async (): Promise<WalletRedemption[]> => {
|
|
159
|
+
const client = getApiClient()
|
|
160
|
+
const response = await client.get<ApiResponse<unknown>>('/wallet')
|
|
161
|
+
return extractArrayData<WalletRedemption>(response.data.data)
|
|
162
|
+
},
|
|
163
|
+
...options,
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get user's wallet history (all redemptions)
|
|
169
|
+
*
|
|
170
|
+
* @endpoint GET /wallet/history
|
|
171
|
+
*/
|
|
172
|
+
export function useWalletHistory(
|
|
173
|
+
params?: {page?: number; limit?: number},
|
|
174
|
+
options?: Omit<UseQueryOptions<WalletHistoryResponse>, 'queryKey' | 'queryFn'>,
|
|
175
|
+
): UseQueryResult<WalletHistoryResponse> {
|
|
176
|
+
return useQuery({
|
|
177
|
+
queryKey: walletKeys.history(params?.page),
|
|
178
|
+
queryFn: async (): Promise<WalletHistoryResponse> => {
|
|
179
|
+
const client = getApiClient()
|
|
180
|
+
const queryParams = new URLSearchParams()
|
|
181
|
+
if (params?.page) queryParams.set('page', String(params.page))
|
|
182
|
+
if (params?.limit) queryParams.set('limit', String(params.limit))
|
|
183
|
+
const response = await client.get<ApiResponse<WalletHistoryResponse>>(
|
|
184
|
+
`/wallet/history?${queryParams}`,
|
|
185
|
+
)
|
|
186
|
+
return extractObjectData<WalletHistoryResponse>(response.data.data)
|
|
187
|
+
},
|
|
188
|
+
...options,
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Lookup redemption by QR code (for scanner)
|
|
194
|
+
*
|
|
195
|
+
* @endpoint GET /redemptions/lookup/:qrCode
|
|
196
|
+
*/
|
|
197
|
+
export function useRedemptionLookup(
|
|
198
|
+
qrCode: string,
|
|
199
|
+
options?: Omit<UseQueryOptions<RedemptionLookup>, 'queryKey' | 'queryFn'>,
|
|
200
|
+
): UseQueryResult<RedemptionLookup> {
|
|
201
|
+
return useQuery({
|
|
202
|
+
queryKey: walletKeys.lookup(qrCode),
|
|
203
|
+
queryFn: async (): Promise<RedemptionLookup> => {
|
|
204
|
+
const client = getApiClient()
|
|
205
|
+
const response = await client.get<ApiResponse<unknown>>(
|
|
206
|
+
`/redemptions/lookup/${qrCode}`,
|
|
207
|
+
)
|
|
208
|
+
return extractObjectData<RedemptionLookup>(response.data.data)
|
|
209
|
+
},
|
|
210
|
+
enabled: !!qrCode && qrCode.length > 0,
|
|
211
|
+
...options,
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get spot's redemption history (for spot owner)
|
|
217
|
+
*
|
|
218
|
+
* @endpoint GET /spots/:spotId/redemptions
|
|
219
|
+
*/
|
|
220
|
+
export function useSpotRedemptions(
|
|
221
|
+
spotId: string,
|
|
222
|
+
params?: {page?: number; limit?: number},
|
|
223
|
+
options?: Omit<UseQueryOptions<WalletHistoryResponse>, 'queryKey' | 'queryFn'>,
|
|
224
|
+
): UseQueryResult<WalletHistoryResponse> {
|
|
225
|
+
return useQuery({
|
|
226
|
+
queryKey: walletKeys.spotRedemptions(spotId, params?.page),
|
|
227
|
+
queryFn: async (): Promise<WalletHistoryResponse> => {
|
|
228
|
+
const client = getApiClient()
|
|
229
|
+
const queryParams = new URLSearchParams()
|
|
230
|
+
if (params?.page) queryParams.set('page', String(params.page))
|
|
231
|
+
if (params?.limit) queryParams.set('limit', String(params.limit))
|
|
232
|
+
const response = await client.get<ApiResponse<WalletHistoryResponse>>(
|
|
233
|
+
`/spots/${spotId}/redemptions?${queryParams}`,
|
|
234
|
+
)
|
|
235
|
+
return extractObjectData<WalletHistoryResponse>(response.data.data)
|
|
236
|
+
},
|
|
237
|
+
enabled: !!spotId,
|
|
238
|
+
...options,
|
|
239
|
+
})
|
|
240
|
+
}
|