diff --git a/public/sitemap-0.xml b/public/sitemap-0.xml
index 9a1f304..d2dccee 100644
--- a/public/sitemap-0.xml
+++ b/public/sitemap-0.xml
@@ -1,33 +1,36 @@
-https://roboticspec.com2026-08-10T09:07:57.626Zdaily0.7
-https://roboticspec.com/activities2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/ActivitiesEditor2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/BlogsEditor2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/BubbleMenu2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/EventsEditor2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/FormatButton2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/HeroEditor2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/MarkdownEditor2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/MenuBar2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/ProjectsEditor2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/ResourceEditor2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/Role2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/SelectSearch2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/TeamEditor2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/role/RoleAccordion2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/role/RolesPanel2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/role/UserRow2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/components/role/UsersPanel2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/admin/page2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/contact2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/events2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/events/components/EventCalendar2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/login2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/login/callback2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/project2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/project/components/ProjectCard2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/resources2026-08-10T09:07:57.627Zdaily0.7
-https://roboticspec.com/resources/components/ResourceCard2026-08-10T09:07:57.627Zdaily0.7
+https://roboticspec.com2026-08-14T11:39:37.785Zdaily0.7
+https://roboticspec.com/activities2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/ActivitiesEditor2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/BlogsEditor2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/BubbleMenu2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/EventsEditor2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/FormatButton2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/HeroEditor2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/MarkdownEditor2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/MenuBar2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/ProjectsEditor2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/ResourceEditor2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/Role2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/SelectSearch2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/TeamEditor2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/role/RoleAccordion2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/role/RolesPanel2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/role/UserRow2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/components/role/UsersPanel2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/admin/page2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/apply2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/contact2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/events2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/events/components/EventCalendar2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/login2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/login/callback2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/panelist2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/panelist/dashboard2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/project2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/project/components/ProjectCard2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/resources2026-08-14T11:39:37.786Zdaily0.7
+https://roboticspec.com/resources/components/ResourceCard2026-08-14T11:39:37.786Zdaily0.7
\ No newline at end of file
diff --git a/src/lib/supabase/actions/applicants.actions.ts b/src/lib/supabase/actions/applicants.actions.ts
index 67f930c..6af5306 100644
--- a/src/lib/supabase/actions/applicants.actions.ts
+++ b/src/lib/supabase/actions/applicants.actions.ts
@@ -15,45 +15,71 @@ export const fetchApplicants = async (): Promise => {
const { data, error } = await client
.from("applicants")
.select("*, applicant_response(branch, responses)")
- .order("created_at", { ascending: false });
+ .order("created_at", {
+ ascending: false,
+ });
if (error) {
- console.error("Error fetching applicants:", error);
+ console.error(
+ "Error fetching applicants:",
+ error
+ );
+
return [];
}
return (data as any[]).map((item) => {
const response = item.applicant_response;
- const responseData = Array.isArray(response) ? response[0] : response;
+
+ const responseData = Array.isArray(response)
+ ? response[0]
+ : response;
+
return {
...item,
status: item.status?.toLowerCase(),
- createdAt: item.createdAt || item.created_at,
+ createdAt:
+ item.createdAt ||
+ item.created_at,
branch: responseData?.branch,
responses: responseData?.responses,
};
}) as ApplicantType[];
};
-export const fetchApplicantWithResponses = async (id: string): Promise => {
+export const fetchApplicantWithResponses = async (
+ id: string
+): Promise => {
const { data, error } = await client
.from("applicants")
- .select("*, applicant_response(branch, responses)")
+ .select(
+ "*, applicant_response(branch, responses)"
+ )
.eq("id", id)
.single();
if (error) {
- console.error("Error fetching applicant with responses:", error);
+ console.error(
+ "Error fetching applicant with responses:",
+ error
+ );
+
return null;
}
- const response = (data as any).applicant_response;
- const responseData = Array.isArray(response) ? response[0] : response;
+ const response =
+ (data as any).applicant_response;
+
+ const responseData = Array.isArray(response)
+ ? response[0]
+ : response;
return {
...data,
status: data.status?.toLowerCase(),
- createdAt: data.createdAt || data.created_at,
+ createdAt:
+ data.createdAt ||
+ data.created_at,
branch: responseData?.branch,
responses: responseData?.responses,
} as ApplicantType;
@@ -79,14 +105,20 @@ export const createWalkIn = async (
.single();
if (error) {
- console.error("Error creating walk-in:", error);
+ console.error(
+ "Error creating walk-in:",
+ error
+ );
+
return null;
}
return {
...data,
status: data.status?.toLowerCase(),
- createdAt: data.createdAt || data.created_at,
+ createdAt:
+ data.createdAt ||
+ data.created_at,
} as ApplicantType;
};
@@ -96,7 +128,16 @@ export const createApplicant = async (
branch: string,
responses: Record
): Promise => {
- const { data: applicantData, error: applicantError } = await client
+ /*
+ * ---------------------------------------------------------
+ * 1. Create applicant in Supabase
+ * ---------------------------------------------------------
+ */
+
+ const {
+ data: applicantData,
+ error: applicantError,
+ } = await client
.from("applicants")
.insert([
{
@@ -111,14 +152,22 @@ export const createApplicant = async (
if (applicantError) {
if (applicantError.code === "23505") {
- console.error("Duplicate application:", applicantError);
+ console.error(
+ "Duplicate application:",
+ applicantError
+ );
+
return {
success: false,
reason: "duplicate",
};
}
- console.error("Error creating applicant:", applicantError);
+ console.error(
+ "Error creating applicant:",
+ applicantError
+ );
+
console.error("Error details:", {
message: applicantError.message,
details: applicantError.details,
@@ -132,6 +181,12 @@ export const createApplicant = async (
};
}
+ /*
+ * ---------------------------------------------------------
+ * 2. Store responses in Supabase
+ * ---------------------------------------------------------
+ */
+
const { error: responseError } = await client
.from("applicant_response")
.insert([
@@ -143,21 +198,92 @@ export const createApplicant = async (
]);
if (responseError) {
- console.error("Error creating applicant response:", responseError);
- await client.from("applicants").delete().eq("id", applicantData.id);
-
+ console.error(
+ "Error creating applicant response:",
+ responseError
+ );
+
+ /*
+ * Roll back applicant if its responses could not
+ * be stored.
+ */
+ await client
+ .from("applicants")
+ .delete()
+ .eq("id", applicantData.id);
+
return {
success: false,
reason: "error",
};
}
+ /*
+ * ---------------------------------------------------------
+ * 3. Sync application to Google Sheets
+ * ---------------------------------------------------------
+ *
+ * Supabase remains the source of truth.
+ * A Google Sheets failure does NOT invalidate the
+ * application.
+ */
+
+ const {
+ data: sheetsData,
+ error: sheetsError,
+ } = await client.functions.invoke(
+ "sync-application-to-sheets",
+ {
+ body: {
+ operation: "application",
+
+ applicationId: applicantData.id,
+
+ name,
+ sid,
+ branch,
+
+ q1: responses.Q1 || "",
+ q2: responses.Q2 || "",
+ q3: responses.Q3 || "",
+ q4: responses.Q4 || "",
+ },
+ }
+ );
+
+ if (sheetsError) {
+ console.error(
+ "Application saved to Supabase, but Google Sheets synchronization failed:",
+ sheetsError
+ );
+ } else if (
+ sheetsData?.success === false
+ ) {
+ console.error(
+ "Google Sheets synchronization failed:",
+ sheetsData
+ );
+ }
+
+ /*
+ * ---------------------------------------------------------
+ * 4. Return successful application
+ * ---------------------------------------------------------
+ */
+
return {
success: true,
+
applicant: {
...applicantData,
- status: applicantData.status?.toLowerCase(),
- createdAt: applicantData.createdAt || applicantData.created_at,
+
+ status:
+ applicantData.status?.toLowerCase(),
+
+ createdAt:
+ applicantData.createdAt ||
+ applicantData.created_at,
+
branch,
responses,
} as ApplicantType,
@@ -170,27 +296,97 @@ export const updateApplicantDecision = async (
remarks: string,
reviewedBy: string
): Promise => {
+ /*
+ * Use one timestamp for both Supabase and Google Sheets.
+ */
+ const reviewedAt = new Date().toISOString();
+
+ /*
+ * ---------------------------------------------------------
+ * 1. Update the existing applicant record
+ * ---------------------------------------------------------
+ */
+
const { error } = await client
.from("applicants")
.update({
status: status.toUpperCase(),
remarks,
- reviewedBy: reviewedBy,
- reviewedAt: new Date().toISOString(),
+ reviewedBy,
+ reviewedAt,
})
.eq("id", applicantId);
if (error) {
- console.error("Error updating applicant decision:", error);
+ console.error(
+ "Error updating applicant decision:",
+ error
+ );
+
return false;
}
+ /*
+ * ---------------------------------------------------------
+ * 2. Sync decision to Results Google Sheet
+ * ---------------------------------------------------------
+ *
+ * We send only data already present in the
+ * existing application architecture.
+ *
+ * The Edge Function uses applicantId to obtain
+ * the applicant's name, SID and branch.
+ */
+
+ const {
+ data: sheetsData,
+ error: sheetsError,
+ } = await client.functions.invoke(
+ "sync-application-to-sheets",
+ {
+ body: {
+ operation: "result",
+
+ applicationId: applicantId,
+
+ status,
+ remarks,
+ reviewedBy,
+ reviewedAt,
+ },
+ }
+ );
+
+ /*
+ * Supabase remains the source of truth.
+ * Do NOT undo a valid decision because Sheets
+ * synchronization failed.
+ */
+
+ if (sheetsError) {
+ console.error(
+ "Decision saved to Supabase, but Results Sheet synchronization failed:",
+ sheetsError
+ );
+ } else if (
+ sheetsData?.success === false
+ ) {
+ console.error(
+ "Results Sheet synchronization failed:",
+ sheetsData
+ );
+ }
+
return true;
};
export const subscribeToApplicantUpdates = (
- onUpdate: (applicant: ApplicantType) => void,
- onInsert: (applicant: ApplicantType) => void
+ onUpdate: (
+ applicant: ApplicantType
+ ) => void,
+ onInsert: (
+ applicant: ApplicantType
+ ) => void
) => {
const channel = client
.channel("applicants-status")
@@ -202,11 +398,16 @@ export const subscribeToApplicantUpdates = (
table: "applicants",
},
(payload) => {
- const item = payload.new as any;
+ const item =
+ payload.new as any;
+
onUpdate({
...item,
- status: item.status?.toLowerCase(),
- createdAt: item.createdAt || item.created_at,
+ status:
+ item.status?.toLowerCase(),
+ createdAt:
+ item.createdAt ||
+ item.created_at,
} as ApplicantType);
}
)
@@ -218,11 +419,16 @@ export const subscribeToApplicantUpdates = (
table: "applicants",
},
(payload) => {
- const item = payload.new as any;
+ const item =
+ payload.new as any;
+
onInsert({
...item,
- status: item.status?.toLowerCase(),
- createdAt: item.createdAt || item.created_at,
+ status:
+ item.status?.toLowerCase(),
+ createdAt:
+ item.createdAt ||
+ item.created_at,
} as ApplicantType);
}
)
diff --git a/supabase/functions/sync-application-to-sheets/deno.json b/supabase/functions/sync-application-to-sheets/deno.json
new file mode 100644
index 0000000..d002339
--- /dev/null
+++ b/supabase/functions/sync-application-to-sheets/deno.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "strict": true
+ }
+}
\ No newline at end of file
diff --git a/supabase/functions/sync-application-to-sheets/index.ts b/supabase/functions/sync-application-to-sheets/index.ts
new file mode 100644
index 0000000..2ab70d9
--- /dev/null
+++ b/supabase/functions/sync-application-to-sheets/index.ts
@@ -0,0 +1,514 @@
+import { createClient } from "npm:@supabase/supabase-js@2";
+
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Headers":
+ "authorization, x-client-info, apikey, content-type",
+ "Access-Control-Allow-Methods":
+ "POST, OPTIONS",
+};
+
+type ApplicationOperation = {
+ operation: "application";
+
+ applicationId: string;
+ name: string;
+ sid: string;
+ branch: string;
+ q1: string;
+ q2: string;
+ q3: string;
+ q4: string;
+};
+
+type ResultOperation = {
+ operation: "result";
+
+ applicationId: string;
+ status: "accepted" | "rejected";
+ remarks: string;
+ reviewedBy: string;
+ reviewedAt: string;
+};
+
+type RequestData =
+ | ApplicationOperation
+ | ResultOperation;
+
+Deno.serve(async (req: Request) => {
+ /*
+ * ---------------------------------------------------------
+ * CORS preflight
+ * ---------------------------------------------------------
+ */
+
+ if (req.method === "OPTIONS") {
+ return new Response("ok", {
+ headers: corsHeaders,
+ });
+ }
+
+ if (req.method !== "POST") {
+ return new Response(
+ JSON.stringify({
+ success: false,
+ error: "Method not allowed",
+ }),
+ {
+ status: 405,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ }
+
+ try {
+ /*
+ * ---------------------------------------------------------
+ * 1. Verify authenticated Supabase user
+ * ---------------------------------------------------------
+ */
+
+ const authorization =
+ req.headers.get("Authorization");
+
+ if (!authorization) {
+ return new Response(
+ JSON.stringify({
+ success: false,
+ error: "Authentication required",
+ }),
+ {
+ status: 401,
+ headers: {
+ ...corsHeaders,
+ "Content-Type":
+ "application/json",
+ },
+ }
+ );
+ }
+
+ const supabaseUrl =
+ Deno.env.get("SUPABASE_URL");
+
+ const supabaseAnonKey =
+ Deno.env.get("SUPABASE_ANON_KEY");
+
+ if (!supabaseUrl || !supabaseAnonKey) {
+ throw new Error(
+ "Supabase environment variables are missing"
+ );
+ }
+
+ const supabase = createClient(
+ supabaseUrl,
+ supabaseAnonKey,
+ {
+ global: {
+ headers: {
+ Authorization:
+ authorization,
+ },
+ },
+ }
+ );
+
+ const {
+ data: { user },
+ error: userError,
+ } = await supabase.auth.getUser();
+
+ if (userError || !user) {
+ return new Response(
+ JSON.stringify({
+ success: false,
+ error:
+ "Invalid or expired Supabase session",
+ }),
+ {
+ status: 401,
+ headers: {
+ ...corsHeaders,
+ "Content-Type":
+ "application/json",
+ },
+ }
+ );
+ }
+
+ /*
+ * ---------------------------------------------------------
+ * 2. Parse request
+ * ---------------------------------------------------------
+ */
+
+ const data =
+ (await req.json()) as RequestData;
+
+ if (!data.operation) {
+ return new Response(
+ JSON.stringify({
+ success: false,
+ error:
+ "Missing operation",
+ }),
+ {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type":
+ "application/json",
+ },
+ }
+ );
+ }
+
+ /*
+ * ---------------------------------------------------------
+ * 3. Read Google configuration
+ * ---------------------------------------------------------
+ */
+
+ const googleScriptUrl =
+ Deno.env.get(
+ "GOOGLE_SHEETS_SCRIPT_URL"
+ );
+
+ const googleSheetsSecret =
+ Deno.env.get(
+ "GOOGLE_SHEETS_SECRET"
+ );
+
+ if (
+ !googleScriptUrl ||
+ !googleSheetsSecret
+ ) {
+ throw new Error(
+ "Google Sheets configuration is missing"
+ );
+ }
+
+ /*
+ * ---------------------------------------------------------
+ * 4. APPLICATION OPERATION
+ * ---------------------------------------------------------
+ *
+ * Existing behavior:
+ * write the application to the current academic-year
+ * application sheet.
+ */
+
+ if (data.operation === "application") {
+ const application =
+ data as ApplicationOperation;
+
+ const requiredFields: (
+ keyof ApplicationOperation
+ )[] = [
+ "applicationId",
+ "name",
+ "sid",
+ "branch",
+ "q1",
+ "q2",
+ "q3",
+ "q4",
+ ];
+
+ for (const field of requiredFields) {
+ if (
+ application[field] === undefined ||
+ application[field] === null ||
+ String(application[field])
+ .trim() === ""
+ ) {
+ return new Response(
+ JSON.stringify({
+ success: false,
+ error:
+ `Missing required field: ${field}`,
+ }),
+ {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type":
+ "application/json",
+ },
+ }
+ );
+ }
+ }
+
+ const googleResponse =
+ await fetch(
+ googleScriptUrl,
+ {
+ method: "POST",
+ headers: {
+ "Content-Type":
+ "application/json",
+ },
+ body: JSON.stringify({
+ operation:
+ "application",
+
+ applicationId:
+ application.applicationId,
+
+ name:
+ application.name,
+
+ sid:
+ application.sid,
+
+ branch:
+ application.branch,
+
+ q1:
+ application.q1,
+
+ q2:
+ application.q2,
+
+ q3:
+ application.q3,
+
+ q4:
+ application.q4,
+
+ secret:
+ googleSheetsSecret,
+
+ userId: user.id,
+ }),
+ }
+ );
+
+ let googleResult: {
+ success?: boolean;
+ duplicate?: boolean;
+ error?: string;
+ };
+
+ try {
+ googleResult =
+ await googleResponse.json();
+ } catch {
+ throw new Error(
+ `Google Apps Script returned an invalid response (HTTP ${googleResponse.status})`
+ );
+ }
+
+ if (
+ !googleResponse.ok ||
+ !googleResult.success
+ ) {
+ throw new Error(
+ googleResult.error ||
+ `Google Sheets synchronization failed (HTTP ${googleResponse.status})`
+ );
+ }
+
+ return new Response(
+ JSON.stringify({
+ success: true,
+ operation: "application",
+ duplicate:
+ googleResult.duplicate ??
+ false,
+ }),
+ {
+ status: 200,
+ headers: {
+ ...corsHeaders,
+ "Content-Type":
+ "application/json",
+ },
+ }
+ );
+ }
+
+ /*
+ * ---------------------------------------------------------
+ * 5. RESULT OPERATION
+ * ---------------------------------------------------------
+ *
+ * We do NOT ask the frontend to send name/SID/branch.
+ *
+ * We fetch those from the existing Supabase data using
+ * the existing application ID.
+ *
+ * This avoids duplicating data sources.
+ */
+
+ if (data.operation === "result") {
+ const result =
+ data as ResultOperation;
+
+ const { data: applicant, error } =
+ await supabase
+ .from("applicants")
+ .select(
+ "id, name, sid, isWalkin, applicant_response(branch)"
+ )
+ .eq(
+ "id",
+ result.applicationId
+ )
+ .single();
+
+ if (error || !applicant) {
+ throw new Error(
+ "Could not fetch applicant for result sheet"
+ );
+ }
+
+ const response =
+ (applicant as any)
+ .applicant_response;
+
+ const responseData =
+ Array.isArray(response)
+ ? response[0]
+ : response;
+
+ const branch =
+ responseData?.branch || "";
+
+ /*
+ * Send the existing decision data + existing
+ * applicant information to Apps Script.
+ */
+
+ const googleResponse =
+ await fetch(
+ googleScriptUrl,
+ {
+ method: "POST",
+ headers: {
+ "Content-Type":
+ "application/json",
+ },
+ body: JSON.stringify({
+ operation: "result",
+
+ applicationId:
+ result.applicationId,
+
+ name:
+ applicant.name,
+
+ sid:
+ applicant.sid,
+
+ branch,
+
+ result:
+ result.status,
+
+ remarks:
+ result.remarks || "",
+
+ reviewedBy:
+ result.reviewedBy,
+
+ reviewedAt:
+ result.reviewedAt,
+
+ secret:
+ googleSheetsSecret,
+
+ userId: user.id,
+ }),
+ }
+ );
+
+ let googleResult: {
+ success?: boolean;
+ error?: string;
+ };
+
+ try {
+ googleResult =
+ await googleResponse.json();
+ } catch {
+ throw new Error(
+ `Google Apps Script returned an invalid response (HTTP ${googleResponse.status})`
+ );
+ }
+
+ if (
+ !googleResponse.ok ||
+ !googleResult.success
+ ) {
+ throw new Error(
+ googleResult.error ||
+ `Results Sheet synchronization failed (HTTP ${googleResponse.status})`
+ );
+ }
+
+ return new Response(
+ JSON.stringify({
+ success: true,
+ operation: "result",
+ }),
+ {
+ status: 200,
+ headers: {
+ ...corsHeaders,
+ "Content-Type":
+ "application/json",
+ },
+ }
+ );
+ }
+
+ /*
+ * Unknown operation
+ */
+
+ return new Response(
+ JSON.stringify({
+ success: false,
+ error:
+ "Unsupported operation",
+ }),
+ {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type":
+ "application/json",
+ },
+ }
+ );
+
+ } catch (error) {
+ console.error(
+ "sync-application-to-sheets error:",
+ error
+ );
+
+ return new Response(
+ JSON.stringify({
+ success: false,
+ error:
+ error instanceof Error
+ ? error.message
+ : "Unknown error",
+ }),
+ {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type":
+ "application/json",
+ },
+ }
+ );
+ }
+});
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index e4918ec..10c0559 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -19,5 +19,5 @@
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next-sitemap.config.js"],
- "exclude": ["node_modules"]
+ "exclude": ["node_modules", "supabase"]
}