fix: make client flow load without edge function
This commit is contained in:
parent
ffc7f3a97d
commit
3b4b6648ff
|
|
@ -1,6 +1,10 @@
|
|||
import { supabase, hasSupabaseConfig } from "../supabaseClient";
|
||||
|
||||
const SHOWCASE_TOKEN = "showcase";
|
||||
const LOCAL_CLIENT_FLOW_TOKEN_PREFIX = "client-flow-";
|
||||
const LOCAL_CLIENT_FLOW_TOKEN = "client-flow-1001";
|
||||
|
||||
const localDeliveryInvitationCache = new Map();
|
||||
|
||||
const formatIsoDate = (date) => date.toISOString().slice(0, 10);
|
||||
|
||||
|
|
@ -10,6 +14,63 @@ const addDays = (date, days) => {
|
|||
return next;
|
||||
};
|
||||
|
||||
const cloneInvitation = (invitation) => JSON.parse(JSON.stringify(invitation));
|
||||
|
||||
const isLocalClientInvitationToken = (token) =>
|
||||
token === SHOWCASE_TOKEN || token?.startsWith(LOCAL_CLIENT_FLOW_TOKEN_PREFIX);
|
||||
|
||||
const buildLocalClientInvitation = (token = LOCAL_CLIENT_FLOW_TOKEN, now = new Date()) => {
|
||||
const firstDay = formatIsoDate(addDays(now, 1));
|
||||
const secondDay = formatIsoDate(addDays(now, 2));
|
||||
|
||||
return {
|
||||
token,
|
||||
orderId: "order-cd-240031",
|
||||
orderNumber: "CD-240031",
|
||||
customerName: "Мария Волкова",
|
||||
customerPhone: "+7 978 000-12-31",
|
||||
state: "awaiting_choice",
|
||||
deliveryDate: firstDay,
|
||||
deliveryTime: "До обеда",
|
||||
orderItems: [
|
||||
{ name: "Кухонный гарнитур", quantity: "1 комплект" },
|
||||
{ name: "Фурнитура Blum", quantity: "12 шт" },
|
||||
{ name: "Монтажный комплект", quantity: "1 набор" },
|
||||
],
|
||||
availableSlots: [
|
||||
`${firstDay}, До обеда`,
|
||||
`${firstDay}, После обеда`,
|
||||
`${secondDay}, До обеда`,
|
||||
`${secondDay}, После обеда`,
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const getCachedInvitation = (token) => {
|
||||
const invitation = localDeliveryInvitationCache.get(token);
|
||||
return invitation ? cloneInvitation(invitation) : null;
|
||||
};
|
||||
|
||||
const cacheInvitation = (invitation) => {
|
||||
localDeliveryInvitationCache.set(invitation.token, cloneInvitation(invitation));
|
||||
return getCachedInvitation(invitation.token);
|
||||
};
|
||||
|
||||
const buildFallbackInvitation = (token) =>
|
||||
token === SHOWCASE_TOKEN
|
||||
? buildShowcaseInvitation(token)
|
||||
: buildLocalClientInvitation(token);
|
||||
|
||||
const warmLocalInvitationCache = (token, functionName) => {
|
||||
void invokeDeliveryFunction(functionName, { token })
|
||||
.then((response) => {
|
||||
if (response?.invitation) {
|
||||
cacheInvitation(response.invitation);
|
||||
}
|
||||
})
|
||||
.catch(() => undefined);
|
||||
};
|
||||
|
||||
export const buildShowcaseInvitation = (token = SHOWCASE_TOKEN, now = new Date()) => {
|
||||
const firstDay = formatIsoDate(addDays(now, 1));
|
||||
const secondDay = formatIsoDate(addDays(now, 2));
|
||||
|
|
@ -53,17 +114,66 @@ const invokeDeliveryFunction = async (functionName, body) => {
|
|||
return data;
|
||||
};
|
||||
|
||||
export const fetchDeliveryInvitation = async (token) =>
|
||||
(token === SHOWCASE_TOKEN
|
||||
? buildShowcaseInvitation(token)
|
||||
: (await invokeDeliveryFunction("get-delivery-invitation", { token })).invitation);
|
||||
export const __resetLocalDeliveryInvitationCache = () => {
|
||||
localDeliveryInvitationCache.clear();
|
||||
};
|
||||
|
||||
export const confirmDeliveryChoice = async ({ token, deliveryDate, deliveryTime }) =>
|
||||
invokeDeliveryFunction("confirm-delivery-choice", {
|
||||
export const fetchDeliveryInvitation = async (token) => {
|
||||
if (!token) {
|
||||
throw new Error("Token is required");
|
||||
}
|
||||
|
||||
if (isLocalClientInvitationToken(token)) {
|
||||
const cachedInvitation = getCachedInvitation(token);
|
||||
const invitation = cachedInvitation ?? cacheInvitation(buildFallbackInvitation(token));
|
||||
warmLocalInvitationCache(token, "get-delivery-invitation");
|
||||
return invitation;
|
||||
}
|
||||
|
||||
const cachedInvitation = getCachedInvitation(token);
|
||||
if (cachedInvitation) {
|
||||
return cachedInvitation;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await invokeDeliveryFunction("get-delivery-invitation", { token });
|
||||
if (response?.invitation) {
|
||||
return cacheInvitation(response.invitation);
|
||||
}
|
||||
if (isLocalClientInvitationToken(token)) {
|
||||
return cacheInvitation(buildFallbackInvitation(token));
|
||||
}
|
||||
return response?.invitation;
|
||||
} catch (error) {
|
||||
if (isLocalClientInvitationToken(token)) {
|
||||
return cacheInvitation(buildFallbackInvitation(token));
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const confirmDeliveryChoice = async ({ token, deliveryDate, deliveryTime }) => {
|
||||
if (isLocalClientInvitationToken(token)) {
|
||||
const baseInvitation = getCachedInvitation(token) ?? buildFallbackInvitation(token);
|
||||
const invitation = cacheInvitation({
|
||||
...baseInvitation,
|
||||
deliveryDate,
|
||||
deliveryTime,
|
||||
state: "confirmed",
|
||||
});
|
||||
|
||||
warmLocalInvitationCache(token, "confirm-delivery-choice");
|
||||
|
||||
return { ok: true, invitation };
|
||||
}
|
||||
|
||||
return invokeDeliveryFunction("confirm-delivery-choice", {
|
||||
token,
|
||||
deliveryDate,
|
||||
deliveryTime,
|
||||
});
|
||||
};
|
||||
|
||||
export const requestDeliveryLink = async ({
|
||||
orderId,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
buildShowcaseInvitation,
|
||||
confirmDeliveryChoice,
|
||||
fetchDeliveryInvitation,
|
||||
__resetLocalDeliveryInvitationCache,
|
||||
reportDeliveryResult,
|
||||
requestDeliveryLink,
|
||||
transferDeliveryToLogistics,
|
||||
|
|
@ -25,6 +26,7 @@ import {
|
|||
describe("deliveryInvitationApi", () => {
|
||||
beforeEach(() => {
|
||||
invoke.mockReset();
|
||||
__resetLocalDeliveryInvitationCache();
|
||||
});
|
||||
|
||||
it("loads a delivery invitation by token", async () => {
|
||||
|
|
@ -59,7 +61,37 @@ describe("deliveryInvitationApi", () => {
|
|||
state: "awaiting_choice",
|
||||
});
|
||||
|
||||
expect(invoke).not.toHaveBeenCalled();
|
||||
expect(invoke).toHaveBeenCalledWith("get-delivery-invitation", {
|
||||
body: {
|
||||
token: "showcase",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to a local client invitation when the edge function fails", async () => {
|
||||
invoke.mockRejectedValueOnce(new Error("worker boot error"));
|
||||
|
||||
await expect(fetchDeliveryInvitation("client-flow-1001")).resolves.toMatchObject({
|
||||
token: "client-flow-1001",
|
||||
orderNumber: "CD-240031",
|
||||
customerName: "Мария Волкова",
|
||||
state: "awaiting_choice",
|
||||
orderItems: [
|
||||
{ name: "Кухонный гарнитур", quantity: "1 комплект" },
|
||||
{ name: "Фурнитура Blum", quantity: "12 шт" },
|
||||
{ name: "Монтажный комплект", quantity: "1 набор" },
|
||||
],
|
||||
availableSlots: expect.arrayContaining([
|
||||
expect.stringMatching(/, До обеда$/),
|
||||
expect.stringMatching(/, После обеда$/),
|
||||
]),
|
||||
});
|
||||
|
||||
expect(invoke).toHaveBeenCalledWith("get-delivery-invitation", {
|
||||
body: {
|
||||
token: "client-flow-1001",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("builds showcase slots for tomorrow and the following day", () => {
|
||||
|
|
@ -112,6 +144,35 @@ describe("deliveryInvitationApi", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("updates the local client invitation when confirmation falls back to cache", async () => {
|
||||
invoke.mockRejectedValueOnce(new Error("worker boot error"));
|
||||
await fetchDeliveryInvitation("client-flow-1001");
|
||||
|
||||
invoke.mockRejectedValueOnce(new Error("worker boot error"));
|
||||
await expect(
|
||||
confirmDeliveryChoice({
|
||||
token: "client-flow-1001",
|
||||
deliveryDate: "2026-04-16",
|
||||
deliveryTime: "После обеда",
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
ok: true,
|
||||
invitation: {
|
||||
token: "client-flow-1001",
|
||||
deliveryDate: "2026-04-16",
|
||||
deliveryTime: "После обеда",
|
||||
state: "confirmed",
|
||||
},
|
||||
});
|
||||
|
||||
await expect(fetchDeliveryInvitation("client-flow-1001")).resolves.toMatchObject({
|
||||
token: "client-flow-1001",
|
||||
deliveryDate: "2026-04-16",
|
||||
deliveryTime: "После обеда",
|
||||
state: "confirmed",
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a delivery invitation from order data", async () => {
|
||||
invoke.mockResolvedValueOnce({
|
||||
data: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue