175 lines
4.9 KiB
TypeScript
175 lines
4.9 KiB
TypeScript
import {
|
|
getClientInvitationStateFromOrderStatus,
|
|
hashInvitationToken,
|
|
isActiveInvitationState,
|
|
} from "../_shared/delivery-invitations.ts";
|
|
import { createServiceClient } from "../_shared/chatbot.ts";
|
|
|
|
const normalizeOrderItems = (
|
|
items: unknown,
|
|
): Array<{ name: string; quantity?: string }> => {
|
|
if (!Array.isArray(items)) {
|
|
return [];
|
|
}
|
|
|
|
return items
|
|
.map((item) => {
|
|
if (typeof item === "string") {
|
|
const [namePart, quantityPart] = item.split("|").map((part) => part.trim());
|
|
const name = namePart || item.trim();
|
|
if (!name) {
|
|
return null;
|
|
}
|
|
|
|
return quantityPart ? { name, quantity: quantityPart } : { name };
|
|
}
|
|
|
|
if (item && typeof item === "object") {
|
|
const typedItem = item as { name?: unknown; quantity?: unknown; label?: unknown };
|
|
const name = typeof typedItem.name === "string"
|
|
? typedItem.name.trim()
|
|
: typeof typedItem.label === "string"
|
|
? typedItem.label.trim()
|
|
: "";
|
|
const quantity = typeof typedItem.quantity === "string"
|
|
? typedItem.quantity.trim()
|
|
: typeof typedItem.quantity === "number"
|
|
? String(typedItem.quantity)
|
|
: "";
|
|
|
|
if (!name && !quantity) {
|
|
return null;
|
|
}
|
|
|
|
return quantity ? { name: name || "Позиция", quantity } : { name: name || "Позиция" };
|
|
}
|
|
|
|
return null;
|
|
})
|
|
.filter((item): item is { name: string; quantity?: string } => Boolean(item));
|
|
};
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
Deno.serve(async (request) => {
|
|
if (request.method === "OPTIONS") {
|
|
return new Response("ok", { headers: corsHeaders });
|
|
}
|
|
|
|
if (!["GET", "POST"].includes(request.method)) {
|
|
return new Response(JSON.stringify({ error: "Method not allowed" }), {
|
|
status: 405,
|
|
headers: {
|
|
...corsHeaders,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
}
|
|
|
|
try {
|
|
const url = new URL(request.url);
|
|
const token = request.method === "POST"
|
|
? ((await request.json()) as { token?: string })?.token || ""
|
|
: url.searchParams.get("token") || "";
|
|
if (!token) {
|
|
return new Response(JSON.stringify({ error: "token is required" }), {
|
|
status: 400,
|
|
headers: {
|
|
...corsHeaders,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
}
|
|
|
|
const tokenHash = await hashInvitationToken(token);
|
|
const supabase = createServiceClient();
|
|
|
|
const { data: invitation, error: invitationError } = await supabase
|
|
.from("delivery_invitations")
|
|
.select("*")
|
|
.eq("token_hash", tokenHash)
|
|
.single();
|
|
|
|
if (invitationError) {
|
|
if (invitationError.code === "PGRST116") {
|
|
return new Response(
|
|
JSON.stringify({ ok: false, error: "Invitation not found" }),
|
|
{
|
|
status: 404,
|
|
headers: {
|
|
...corsHeaders,
|
|
"Content-Type": "application/json",
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
throw invitationError;
|
|
}
|
|
|
|
const { data: order, error: orderError } = await supabase
|
|
.from("orders")
|
|
.select("id, order_number, status, delivery_agreement_status, customer")
|
|
.eq("id", invitation.order_id)
|
|
.single();
|
|
|
|
if (orderError) {
|
|
throw orderError;
|
|
}
|
|
|
|
const publicState = getClientInvitationStateFromOrderStatus(order.status);
|
|
|
|
if (isActiveInvitationState(publicState) && !invitation.opened_at) {
|
|
await supabase
|
|
.from("delivery_invitations")
|
|
.update({
|
|
opened_at: new Date().toISOString(),
|
|
})
|
|
.eq("id", invitation.id);
|
|
}
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
ok: true,
|
|
invitation: {
|
|
orderId: invitation.order_id,
|
|
state: publicState,
|
|
token: token,
|
|
orderNumber: order.order_number,
|
|
customerName: order.customer?.name || invitation.customer_name || null,
|
|
customerPhone: order.customer?.phone || invitation.customer_phone || null,
|
|
orderItems: normalizeOrderItems(order.customer?.items),
|
|
availableSlots: invitation.available_slots || [],
|
|
deliveryDate: invitation.delivery_date || null,
|
|
deliveryTime: invitation.delivery_time || null,
|
|
orderStatus: order.status,
|
|
deliveryAgreementStatus: order.delivery_agreement_status,
|
|
},
|
|
}),
|
|
{
|
|
headers: {
|
|
...corsHeaders,
|
|
"Content-Type": "application/json",
|
|
},
|
|
},
|
|
);
|
|
} catch (error) {
|
|
return new Response(
|
|
JSON.stringify({
|
|
ok: false,
|
|
error: error instanceof Error ? error.message : "Unexpected error",
|
|
}),
|
|
{
|
|
status: 500,
|
|
headers: {
|
|
...corsHeaders,
|
|
"Content-Type": "application/json",
|
|
},
|
|
},
|
|
);
|
|
}
|
|
});
|