mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Add ACMCC app source, Supabase backend, and project config
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import { serve } from "https://deno.land/std@0.190.0/http/server.ts";
|
||||
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.39.3";
|
||||
|
||||
const corsHeaders = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers":
|
||||
"authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version",
|
||||
};
|
||||
|
||||
serve(async (req) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
return new Response(null, { headers: corsHeaders });
|
||||
}
|
||||
|
||||
try {
|
||||
const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
|
||||
const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
||||
const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
||||
|
||||
const { booking_id, session_id } = await req.json();
|
||||
|
||||
if (!booking_id) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Missing booking_id" }),
|
||||
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
// Fetch booking
|
||||
const { data: booking, error: bookingErr } = await supabase
|
||||
.from("amenity_bookings")
|
||||
.select("*")
|
||||
.eq("id", booking_id)
|
||||
.single();
|
||||
|
||||
if (bookingErr || !booking) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Booking not found" }),
|
||||
{ status: 404, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
// Only process if still pending_payment
|
||||
if (booking.status !== "pending_payment") {
|
||||
return new Response(
|
||||
JSON.stringify({ success: true, message: "Already processed" }),
|
||||
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
// Update booking status to confirmed
|
||||
await supabase
|
||||
.from("amenity_bookings")
|
||||
.update({ status: "confirmed" })
|
||||
.eq("id", booking_id);
|
||||
|
||||
// Extract pin_id from notes
|
||||
let pinId: string | null = null;
|
||||
try {
|
||||
const notes = typeof booking.notes === "string" ? JSON.parse(booking.notes) : booking.notes;
|
||||
pinId = notes?.pin_id || null;
|
||||
} catch { /* ignore */ }
|
||||
|
||||
// Mark pin as rented
|
||||
if (pinId && booking.amenity_id) {
|
||||
const { data: amenity } = await supabase
|
||||
.from("amenities")
|
||||
.select("map_config")
|
||||
.eq("id", booking.amenity_id)
|
||||
.single();
|
||||
|
||||
if (amenity?.map_config?.pins) {
|
||||
const updatedPins = amenity.map_config.pins.map((pin: any) =>
|
||||
pin.id === pinId ? { ...pin, status: "rented" } : pin
|
||||
);
|
||||
|
||||
await supabase
|
||||
.from("amenities")
|
||||
.update({
|
||||
map_config: { ...amenity.map_config, pins: updatedPins },
|
||||
updated_at: new Date().toISOString(),
|
||||
})
|
||||
.eq("id", booking.amenity_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Send confirmation email
|
||||
if (booking.guest_email) {
|
||||
try {
|
||||
const { data: amenityRow } = await supabase
|
||||
.from("amenities").select("name, association_id").eq("id", booking.amenity_id).single();
|
||||
const { data: assocRow } = amenityRow?.association_id
|
||||
? await supabase.from("associations").select("name").eq("id", amenityRow.association_id).single()
|
||||
: { data: null };
|
||||
const { data: pageRow } = amenityRow?.association_id
|
||||
? await supabase.from("association_public_pages").select("slug").eq("association_id", amenityRow.association_id).maybeSingle()
|
||||
: { data: null };
|
||||
const origin = req.headers.get("origin") || "https://avria.cloud";
|
||||
const dateObj = booking.booking_date ? new Date(`${booking.booking_date}T12:00:00`) : null;
|
||||
const formattedDate = dateObj
|
||||
? dateObj.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" })
|
||||
: "";
|
||||
await supabase.functions.invoke("send-transactional-email", {
|
||||
body: {
|
||||
templateName: "amenity-booking-confirmation",
|
||||
recipientEmail: booking.guest_email,
|
||||
idempotencyKey: `amenity-booking-confirmed-${booking.id}`,
|
||||
templateData: {
|
||||
guestName: booking.guest_name,
|
||||
amenityName: amenityRow?.name,
|
||||
associationName: (assocRow as any)?.name,
|
||||
bookingDate: formattedDate,
|
||||
startTime: booking.start_time || "",
|
||||
status: "confirmed",
|
||||
confirmationLink: `${origin}/booking/${booking.id}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to send confirmation email:", err);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({ success: true }),
|
||||
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("Error in confirm-reservation-payment:", err);
|
||||
return new Response(
|
||||
JSON.stringify({ error: err.message || "Internal server error" }),
|
||||
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user