From c517efab4cdcec7f62391690887cf5d6062eb682 Mon Sep 17 00:00:00 2001 From: r-southworth Date: Tue, 12 Mar 2024 17:54:45 -0400 Subject: [PATCH 1/6] start building createaccount logic, issues with database --- src/components/users/ClientRegistration.tsx | 93 ----- src/components/users/ProviderRegistration.tsx | 384 ++++-------------- src/i18n/UI/English.ts | 6 +- src/i18n/UI/French.ts | 2 +- src/i18n/UI/Spanish.ts | 2 +- src/pages/api/providerProfileSubmit.ts | 72 +--- src/pages/api/updateAccountStripe.ts | 103 +++++ 7 files changed, 205 insertions(+), 457 deletions(-) create mode 100644 src/pages/api/updateAccountStripe.ts diff --git a/src/components/users/ClientRegistration.tsx b/src/components/users/ClientRegistration.tsx index 46645ade..5db07637 100644 --- a/src/components/users/ClientRegistration.tsx +++ b/src/components/users/ClientRegistration.tsx @@ -38,8 +38,6 @@ export const ClientRegistration: Component = () => { const [firstName, setFirstName] = createSignal(""); const [lastName, setLastName] = createSignal(""); - const regularExpressionPhone = new RegExp("^[0-9]{8}$"); - createEffect(async () => { const { data, error } = await supabase.auth.getSession(); setSession(data.session); @@ -116,97 +114,6 @@ export const ClientRegistration: Component = () => { console.log("Other error: " + error); } - // //Minor Municipality - // try { - // const { data: minorMunicipality, error: errorMinorMunicipality } = - // await supabase.from("minor_municipality").select("*"); - // if (errorMinorMunicipality) { - // console.log("supabase error: " + errorMinorMunicipality.message); - // } else { - // document - // .getElementById("MajorMunicipality") - // ?.addEventListener("change", () => { - // let municipalitySelect = document.getElementById( - // "MinorMunicipality" - // ) as HTMLSelectElement; - // - // let length = municipalitySelect?.length; - // - // for (let i = length - 1; i > -1; i--) { - // if (municipalitySelect.options[i].value !== "") { - // municipalitySelect.remove(i); - // } - // } - // - // let filteredMunicipality = minorMunicipality.filter( - // (municipality) => - // municipality.major_municipality == - // ( - // document.getElementById( - // "MajorMunicipality" - // ) as HTMLSelectElement - // )?.value - // ); - // filteredMunicipality.forEach((municipality) => { - // let municipalityOption = new Option( - // municipality.minor_municipality, - // municipality.id - // ); - // document - // .getElementById("MinorMunicipality") - // ?.append(municipalityOption); - // }); - // }); - // } - // } catch (error) { - // console.log("Other error: " + error); - // } - // - // //Governing District - // try { - // const { data: governingDistrict, error: errorGoverningDistrict } = - // await supabase.from("governing_district").select("*"); - // if (errorGoverningDistrict) { - // console.log("supabase error: " + errorGoverningDistrict.message); - // } else { - // document - // .getElementById("MinorMunicipality") - // ?.addEventListener("change", () => { - // let districtSelect = document.getElementById( - // "GoverningDistrict" - // ) as HTMLSelectElement; - // - // let length = districtSelect?.length; - // - // for (let i = length - 1; i > -1; i--) { - // if (districtSelect.options[i].value !== "") { - // districtSelect.remove(i); - // } - // } - // - // let filteredDistrict = governingDistrict.filter( - // (district) => - // district.minor_municipality == - // ( - // document.getElementById( - // "MinorMunicipality" - // ) as HTMLSelectElement - // )?.value - // ); - // filteredDistrict.forEach((district) => { - // let districtOption = new Option( - // district.governing_district, - // district.id - // ); - // document - // .getElementById("GoverningDistrict") - // ?.append(districtOption); - // }); - // }); - // } - // } catch (error) { - // console.log("Other error: " + error); - // } } else { alert(t("messages.createClientAccount")); location.href = `/${lang}/login`; diff --git a/src/components/users/ProviderRegistration.tsx b/src/components/users/ProviderRegistration.tsx index 23c4ff03..b9472afc 100644 --- a/src/components/users/ProviderRegistration.tsx +++ b/src/components/users/ProviderRegistration.tsx @@ -6,18 +6,32 @@ import { createSignal, onMount, For, + Show, } from "solid-js"; import supabase from "../../lib/supabaseClient"; import type { AuthSession } from "@supabase/supabase-js"; import UserImage from "./UserImage"; import { getLangFromUrl, useTranslations } from "../../i18n/utils"; +import stripe from "src/lib/stripe"; import Phone from "./forms/Phone"; import { check } from "prettier"; +import { CreateStripeProductPrice } from "@components/posts/CreateStripeProductPrice"; const lang = getLangFromUrl(new URL(window.location.href)); const t = useTranslations(lang); +async function createStripeAccount(formData: FormData) { + const account = await stripe.accounts.create({ + type: "express", + country: "US", + //TODO: Prefill email + email: formData.get("email") as string, + }) + formData.append("account_id", account.id); + postStripeAccount(formData); +} + //Send the data to the APIRoute and wait for a JSON response see src/pages/api for APIRoute async function postFormData(formData: FormData) { const response = await fetch("/api/providerProfileSubmit", { @@ -27,13 +41,26 @@ async function postFormData(formData: FormData) { const data = await response.json(); console.log(data.message); //Checks the API response for the redirect and sends them to the redirect page if there is one - if (data.redirect) { - alert(data.message); - window.location.href = `/${lang}` + data.redirect; + if (response.status === 200) { + createStripeAccount(formData); } return data; } +async function postStripeAccount(stripeData: FormData) { + const response = await fetch("/api/updateAccountStripe", { + method: "POST", + body: stripeData, + }); + const data = await response.json(); + console.log(data.url) + // if (data.redirect) { + // alert(data.message); //TODO: Not sure how to internationalize these + // window.location.href = `/${lang}` + data.redirect; + // } + return data; +} + //Component that creates the form and collects the data export const ProviderRegistration: Component = () => { const [session, setSession] = createSignal(null); @@ -43,6 +70,7 @@ export const ProviderRegistration: Component = () => { const [phone, setPhone] = createSignal(""); const [firstName, setFirstName] = createSignal(""); const [lastName, setLastName] = createSignal(""); + const [email, setEmail] = createSignal(""); const [providerName, setProviderName] = createSignal(""); const [languages, setLanguages] = createSignal>(); @@ -67,24 +95,13 @@ export const ProviderRegistration: Component = () => { setFirstName(profile[0].first_name); setLastName(profile[0].last_name); setProviderName(firstName() + " " + lastName()); + setEmail(profile[0].email); } } catch (error) { console.log("Other error: " + error); } - //Will create a dropdown of all the languages in the database - try { - const { data, error } = await supabase.from("language").select("*"); - if (error) { - console.log("supabase error: " + error.message); - } else if (data!) { - setLanguages(data); - } - } catch (error) { - console.log("Language error: " + error); - } - - //Will create a dropdown of all the countries in the database (Currently only Costa Rica) + //Will create a dropdown of all the countries in the database (Currently only United States) try { const { data: countries, error } = await supabase .from("country") @@ -101,209 +118,21 @@ export const ProviderRegistration: Component = () => { console.log("Other error: " + error); } - //Will create a list of Major Municipalities based on the selected country try { - const { data: majorMunicipality, error: errorMajorMunicipality } = - await supabase.from("major_municipality").select("*"); - if (errorMajorMunicipality) { - console.log("supabase error: " + errorMajorMunicipality.message); + const { data: majorMunicipality, error: errorMajorMunicipality } = await supabase + .from("major_municipality") + .select("*"); + if (error) { + console.log("supabase error: " + error.message); } else { - document.getElementById("country")?.addEventListener("change", () => { - let municipalitySelect = document.getElementById( - "MajorMunicipality" - ) as HTMLSelectElement; - - let length = municipalitySelect?.length; - - for (let i = length - 1; i > -1; i--) { - if (municipalitySelect.options[i].value !== "") { - municipalitySelect.remove(i); - } - } - let filteredMunicipality = majorMunicipality.filter( - (municipality) => - municipality.country == - (document.getElementById("country") as HTMLSelectElement)?.value - ); - filteredMunicipality.forEach((municipality) => { - let municipalityOption = new Option( - municipality.major_municipality, - municipality.id - ); - document - .getElementById("MajorMunicipality") - ?.append(municipalityOption); - }); + majorMunicipality?.forEach((state) => { + let muniOption = new Option(state.major_municipality, state.id); + document.getElementById("MajorMunicipality")?.append(muniOption); }); } } catch (error) { console.log("Other error: " + error); } - - //Old code for minor and governing - //Creates drop down options for Minor Municipality based on selected Major Municipality - // try { - // const { data: minorMunicipality, error: errorMinorMunicipality } = - // await supabase.from("minor_municipality").select("*"); - // if (errorMinorMunicipality) { - // console.log("supabase error: " + errorMinorMunicipality.message); - // } else { - // document - // .getElementById("MajorMunicipality") - // ?.addEventListener("change", () => { - // let municipalitySelect = document.getElementById( - // "MinorMunicipality" - // ) as HTMLSelectElement; - - // let length = municipalitySelect?.length; - // let length = municipalitySelect?.length; - - // for (let i = length - 1; i > -1; i--) { - // if (municipalitySelect.options[i].value !== "") { - // municipalitySelect.remove(i); - // } - // } - // for (let i = length - 1; i > -1; i--) { - // if (municipalitySelect.options[i].value !== "") { - // municipalitySelect.remove(i); - // } - // } - - // let filteredMunicipality = minorMunicipality.filter( - // (municipality) => - // municipality.major_municipality == - // ( - // document.getElementById( - // "MajorMunicipality" - // ) as HTMLSelectElement - // )?.value - // ); - // filteredMunicipality.forEach((municipality) => { - // let municipalityOption = new Option( - // municipality.minor_municipality, - // municipality.id - // ); - // document - // .getElementById("MinorMunicipality") - // ?.append(municipalityOption); - // }); - // }); - // } - // } catch (error) { - // console.log("Other error: " + error); - // } - // let filteredMunicipality = minorMunicipality.filter( - // (municipality) => - // municipality.major_municipality == - // ( - // document.getElementById( - // "MajorMunicipality" - // ) as HTMLSelectElement - // )?.value - // ); - // filteredMunicipality.forEach((municipality) => { - // let municipalityOption = new Option( - // municipality.minor_municipality, - // municipality.id - // ); - // document - // .getElementById("MinorMunicipality") - // ?.append(municipalityOption); - // }); - // }); - // } - // } catch (error) { - // console.log("Other error: " + error); - // } - - // //Creates filtered drop down options for Governing District base on selected Minor Municipality - // try { - // const { data: governingDistrict, error: errorGoverningDistrict } = - // await supabase.from("governing_district").select("*"); - // if (errorGoverningDistrict) { - // console.log("supabase error: " + errorGoverningDistrict.message); - // } else { - // document - // .getElementById("MinorMunicipality") - // ?.addEventListener("change", () => { - // let districtSelect = document.getElementById( - // "GoverningDistrict" - // ) as HTMLSelectElement; - // //Creates filtered drop down options for Governing District base on selected Minor Municipality - // try { - // const { data: governingDistrict, error: errorGoverningDistrict } = - // await supabase.from("governing_district").select("*"); - // if (errorGoverningDistrict) { - // console.log("supabase error: " + errorGoverningDistrict.message); - // } else { - // document - // .getElementById("MinorMunicipality") - // ?.addEventListener("change", () => { - // let districtSelect = document.getElementById( - // "GoverningDistrict" - // ) as HTMLSelectElement; - - // let length = districtSelect?.length; - // let length = districtSelect?.length; - - // for (let i = length - 1; i > -1; i--) { - // if (districtSelect.options[i].value !== "") { - // districtSelect.remove(i); - // } - // } - // for (let i = length - 1; i > -1; i--) { - // if (districtSelect.options[i].value !== "") { - // districtSelect.remove(i); - // } - // } - - // let filteredDistrict = governingDistrict.filter( - // (district) => - // district.minor_municipality == - // ( - // document.getElementById( - // "MinorMunicipality" - // ) as HTMLSelectElement - // )?.value - // ); - // filteredDistrict.forEach((district) => { - // let districtOption = new Option( - // district.governing_district, - // district.id - // ); - // document - // .getElementById("GoverningDistrict") - // ?.append(districtOption); - // }); - // }); - // } - // } catch (error) { - // console.log("Other error: " + error); - // } - // let filteredDistrict = governingDistrict.filter( - // (district) => - // district.minor_municipality == - // ( - // document.getElementById( - // "MinorMunicipality" - // ) as HTMLSelectElement - // )?.value - // ); - // filteredDistrict.forEach((district) => { - // let districtOption = new Option( - // district.governing_district, - // district.id - // ); - // document - // .getElementById("GoverningDistrict") - // ?.append(districtOption); - // }); - // }); - // } - // } catch (error) { - // console.log("Other error: " + error); - // } - //If the user is not signed in then tell them to sign in and send them to the login page } else { alert(t("messages.createProviderAccount")); @@ -333,8 +162,11 @@ export const ProviderRegistration: Component = () => { formData.append("Phone", phone()); formData.append("access_token", session()?.access_token!); formData.append("refresh_token", session()?.refresh_token!); + formData.append("email", email()); formData.append("lang", lang); formData.append("languageArray", JSON.stringify(languagePick())); + //Remove when more countries are added + formData.set("country", "1"); // formData.append("language", languageS()); if (imageUrl() !== null) { @@ -367,30 +199,30 @@ export const ProviderRegistration: Component = () => { } } - function setLanguageArray(e: Event) { - if ((e.target as HTMLInputElement).checked) { - setLanguagePick([ - ...languagePick(), - (e.target as HTMLInputElement).value, - ]); - } else if ((e.target as HTMLInputElement).checked === false) { - if (languagePick().includes((e.target as HTMLInputElement).value)) { - setLanguagePick( - languagePick().filter( - (value) => value !== (e.target as HTMLInputElement).value - ) - ); - } - } - if (languagePick().length > 0) { - document.getElementById("isValid")?.classList.remove("hidden"); - document.getElementById("languageToolTip")?.classList.add("hidden"); - } else if (languagePick().length === 0) { - document.getElementById("isValid")?.classList.add("hidden"); - document.getElementById("languageToolTip")?.classList.remove("hidden"); - } - console.log(languagePick()); - } + // function setLanguageArray(e: Event) { + // if ((e.target as HTMLInputElement).checked) { + // setLanguagePick([ + // ...languagePick(), + // (e.target as HTMLInputElement).value, + // ]); + // } else if ((e.target as HTMLInputElement).checked === false) { + // if (languagePick().includes((e.target as HTMLInputElement).value)) { + // setLanguagePick( + // languagePick().filter( + // (value) => value !== (e.target as HTMLInputElement).value + // ) + // ); + // } + // } + // if (languagePick().length > 0) { + // document.getElementById("isValid")?.classList.remove("hidden"); + // document.getElementById("languageToolTip")?.classList.add("hidden"); + // } else if (languagePick().length === 0) { + // document.getElementById("isValid")?.classList.add("hidden"); + // document.getElementById("languageToolTip")?.classList.remove("hidden"); + // } + // console.log(languagePick()); + // } //Actual Form that gets displayed for users to fill return ( @@ -575,22 +407,16 @@ export const ProviderRegistration: Component = () => { -
+ {/*
+ */} + {/* Creates a list of checkboxes that drop down to multiple select */} -
+ {/*
languageCheckboxes()}> - {/* */}

-
- +
*/} +
- + {/* TODO: Un-hide by removing Show when allowing countries outside US */}
-
+
- -
- - {/*
- - - - - - - -
*/} -
diff --git a/src/i18n/UI/English.ts b/src/i18n/UI/English.ts index 6e5c9444..98720fef 100644 --- a/src/i18n/UI/English.ts +++ b/src/i18n/UI/English.ts @@ -84,7 +84,7 @@ export const English = { passwordLength: `Password must be \n - at least 6 characters long \n - contain at least one number \n - contain at least one uppercase letter \n - contain at least one lowercase letter \n - contain at least one special character: \n ! @ # $ % ^ & *`, passwordValid: 'Valid password', passwordLackRequirements: 'Password does not meet requirements', - phoneLackRequirements: 'Phone number must be 8 digits long', + phoneLackRequirements: 'Please enter a valid phone number.', phoneValid: 'Valid phone number', passwordMatch: 'Passwords do not match', passwordReset: 'Password Reset', @@ -120,7 +120,7 @@ export const English = { serviceCategory: 'Service Category', postContent: 'Post Content', country: 'Country', - majorMunicipality: 'Major Municipality', + majorMunicipality: 'State', minorMunicipality: 'Minor Municipality', governingDistrict: 'Governing District', search: 'Search', @@ -191,7 +191,7 @@ export const English = { profileEditError: "Error updating profile", noDistrict: "District not found", noMinorMunicipality: "Minor Municipality not found", - noMajorMunicipality: "Major Municipality not found", + noMajorMunicipality: "State not found", noCountry: "Country not found", locationError: "Location not submitted", providerCreateProfileError: "Error creating provider profile", diff --git a/src/i18n/UI/French.ts b/src/i18n/UI/French.ts index 2aa8c05a..fd3025d6 100644 --- a/src/i18n/UI/French.ts +++ b/src/i18n/UI/French.ts @@ -84,7 +84,7 @@ export const French = { passwordLength: "Le mot de passe doit être \n - au moins 6 caractères \n - contenir au moins un chiffre \n - contient au moins une lettre majuscule \n - contenir au moins une lettre minuscule \n - ccontenir au moins un caractère spécial: \n ! @ # $ % ^ & *", passwordValid: 'Mot de passe valide', passwordLackRequirements: 'Le mot de passe ne répond pas aux exigences', - phoneLackRequirements: 'Le numéro de téléphone doit comporter 8 chiffres', + phoneLackRequirements: "S'il vous plaît entrer un numéro de téléphone valide.", phoneValid: 'Numéro de téléphone valide', passwordMatch: "Les mots de passe ne correspondent pas", passwordReset: 'Réinitialisation du mot de passe', diff --git a/src/i18n/UI/Spanish.ts b/src/i18n/UI/Spanish.ts index 28e3a5e1..4b5b9084 100644 --- a/src/i18n/UI/Spanish.ts +++ b/src/i18n/UI/Spanish.ts @@ -84,7 +84,7 @@ export const Spanish = { passwordLength: 'La contraseña debe ser \n - al menos 6 caracteres de largo \n - contener al menos un número \n - contener una letra mayúscula \n - contener 1 letra minúscula \n - contener al menos carácter especial: \n ! @ # $ % ^ & *', passwordValid: 'Contraseña valida', passwordLackRequirements: 'Contraseña no cumple con los requisitos', - phoneLackRequirements: 'El número de teléfono tiene que tener 8 digitos', + phoneLackRequirements: 'Por favor ingrese un número de teléfono válido.', phoneValid: 'Número de teléfono válido', passwordMatch: 'Las contraseñas no coinciden', passwordReset: 'Restablecimiento de contraseña', diff --git a/src/pages/api/providerProfileSubmit.ts b/src/pages/api/providerProfileSubmit.ts index 6dcdc0f6..37a0f4cf 100644 --- a/src/pages/api/providerProfileSubmit.ts +++ b/src/pages/api/providerProfileSubmit.ts @@ -25,23 +25,17 @@ export const POST: APIRoute = async ({ request, redirect }) => { const phone = formData.get("Phone"); const country = formData.get("country"); const majorMunicipality = formData.get("MajorMunicipality"); - // const minorMunicipality = formData.get("MinorMunicipality"); - // const governingDistrict = formData.get("GoverningDistrict"); - const postalArea = formData.get("PostalArea"); const imageUrl = formData.get("image_url") ? formData.get("image_url") : null; - const language = JSON.parse(formData.get("languageArray")! as string); console.log("imageURL: " + imageUrl); - console.log("language: " + language); - console.log(language?.length); // Validate the formData makes sure none of the fields are blank. Could probably do more than this like check for invalid phone numbers, blank strings, unselected location info etc. if ( !phone || !country || - // !majorMunicipality || + !majorMunicipality // !minorMunicipality || // !governingDistrict || - language?.length === 0 + // language?.length === 0 ) { return new Response( JSON.stringify({ @@ -112,46 +106,20 @@ export const POST: APIRoute = async ({ request, redirect }) => { /*Each of these retrieves the appropriate id from the database for the area level (governing district, minor municipality, major municipality, country) in order to make a proper submission to the location table */ - // const { data: districtId, error: districtError } = await supabase - // .from("governing_district") - // .select("id") - // .eq("id", governingDistrict); - // if (districtError) { - // return new Response( - // JSON.stringify({ - // message: (t("apiErrors.noDistrict")), - // }), - // { status: 500 } - // ); - // } - // - // const { data: minorMunicipalityId, error: minorMunicipalityError } = - // await supabase - // .from("minor_municipality") - // .select("id") - // .eq("id", minorMunicipality); - // if (minorMunicipalityError) { - // return new Response( - // JSON.stringify({ - // message: (t("apiErrors.noMinorMunicipality")), - // }), - // { status: 500 } - // ); - // } - - // const { data: majorMunicipalityId, error: majorMunicipalityError } = - // await supabase - // .from("major_municipality") - // .select("id") - // .eq("id", majorMunicipality); - // if (majorMunicipalityError) { - // return new Response( - // JSON.stringify({ - // message: t("apiErrors.noMajorMunicipality"), - // }), - // { status: 500 }, - // ); - // } + + const { data: majorMunicipalityId, error: majorMunicipalityError } = + await supabase + .from("major_municipality") + .select("id") + .eq("id", majorMunicipality); + if (majorMunicipalityError) { + return new Response( + JSON.stringify({ + message: t("apiErrors.noMajorMunicipality"), + }), + { status: 500 }, + ); + } const { data: countryId, error: countryError } = await supabase .from("country") @@ -168,9 +136,7 @@ export const POST: APIRoute = async ({ request, redirect }) => { //Build our submission to the location table keys need to match the field in the database you are trying to fill. let locationSubmission = { - // minor_municipality: minorMunicipalityId[0].id, - // major_municipality: majorMunicipalityId[0].id, - // governing_district: districtId[0].id, + major_municipality: majorMunicipalityId[0].id, country: countryId[0].id, user_id: user.id, }; @@ -196,8 +162,7 @@ export const POST: APIRoute = async ({ request, redirect }) => { seller_phone: phone, location: location[0].id, user_id: user.id, - image_url: imageUrl, - language_spoken: language, + image_url: imageUrl }; //submit to the providers table and select it back @@ -229,7 +194,6 @@ export const POST: APIRoute = async ({ request, redirect }) => { return new Response( JSON.stringify({ message: t("apiErrors.success"), - redirect: "/provider/profile", }), { status: 200 }, ); diff --git a/src/pages/api/updateAccountStripe.ts b/src/pages/api/updateAccountStripe.ts new file mode 100644 index 00000000..01237d01 --- /dev/null +++ b/src/pages/api/updateAccountStripe.ts @@ -0,0 +1,103 @@ +import supabase from "../../lib/supabaseClientServer"; +import type { APIRoute } from "astro"; +import type { APIContext } from "astro"; +import { useTranslations } from "@i18n/utils"; + +export const POST: APIRoute = async ({ request, redirect }) => { + const formData = await request.formData(); + + for (let pair of formData.entries()) { + console.log(pair[0] + ", " + pair[1]); + } + + //Set internationalization values + const lang = formData.get("lang"); + //@ts-ignore + const t = useTranslations(lang); + + const stripeAccountId = formData.get("account_id"); + const access_token = formData.get("access_token"); + const refresh_token = formData.get("refresh_token"); + + // Validate the formData - you'll probably want to do more than this + if ( + !stripeAccountId + ) { + return new Response( + JSON.stringify({ + message: t("apiErrors.missingFields"), + }), + { status: 400 }, + ); + } + + const { data: sessionData, error: sessionError } = + await supabase.auth.setSession({ + refresh_token: refresh_token!.toString(), + access_token: access_token!.toString(), + }); + if (sessionError) { + return new Response( + JSON.stringify({ + message: t("apiErrors.noSession"), + }), + { status: 500 }, + ); + } + + // console.log(sessionData) + + if (!sessionData?.session) { + return new Response( + JSON.stringify({ + message: t("apiErrors.noSession"), + }), + { status: 500 }, + ); + } + + const user = sessionData?.session.user; + + if (!user) { + return new Response( + JSON.stringify({ + message: t("apiErrors.noUser"), + }), + { status: 500 }, + ); + } + + const { error, data } = await supabase + .from("sellers") + .update({stripe_account_id: stripeAccountId}) + .eq("user_id", user.id) + .select(); + + if (error) { + console.log(error); + return new Response( + JSON.stringify({ + message: t("apiErrors.providerCreateProfileError"), + }), + { status: 500 }, + ); + } else if (!data) { + return new Response( + JSON.stringify({ + message: t("apiErrors.noProfileData"), + }), + { status: 500 }, + ); + } else { + console.log("Post Data: " + JSON.stringify(data)); + } + + // Do something with the formData, then return a success response + return new Response( + JSON.stringify({ + message: t("apiErrors.success"), + redirect: "/provider/profile", + }), + { status: 200 }, + ); +}; From 1e86a2b8733b25abf81508d99adae4210cd24188 Mon Sep 17 00:00:00 2001 From: r-southworth Date: Wed, 13 Mar 2024 13:36:51 -0400 Subject: [PATCH 2/6] fix database for stripe accounts --- src/components/users/ProviderRegistration.tsx | 1 - src/config.ts | 2 +- src/lib/stripeServer.tsx | 17 +++++++ src/pages/api/providerProfileSubmit.ts | 2 +- src/pages/api/stripeAccountLinkReauth.ts | 19 ++++++++ src/pages/api/updateAccountStripe.ts | 46 +++++++++++++------ src/pages/provider/stripereauth.astro | 14 ++++++ .../20240313143103_addStripeAccount.sql | 23 ++++++++++ 8 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 src/lib/stripeServer.tsx create mode 100644 src/pages/api/stripeAccountLinkReauth.ts create mode 100644 src/pages/provider/stripereauth.astro create mode 100644 supabase/migrations/20240313143103_addStripeAccount.sql diff --git a/src/components/users/ProviderRegistration.tsx b/src/components/users/ProviderRegistration.tsx index b9472afc..b1f3ff10 100644 --- a/src/components/users/ProviderRegistration.tsx +++ b/src/components/users/ProviderRegistration.tsx @@ -53,7 +53,6 @@ async function postStripeAccount(stripeData: FormData) { body: stripeData, }); const data = await response.json(); - console.log(data.url) // if (data.redirect) { // alert(data.message); //TODO: Not sure how to internationalize these // window.location.href = `/${lang}` + data.redirect; diff --git a/src/config.ts b/src/config.ts index 3e0c178e..7e4d449e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -9,7 +9,7 @@ const CONFIG = { title: "LearnGrove", description: "HomeSchool description", //NEED TO ADD NEW URL - url: "https://NEWURL.com/", + url: "https://learngrove.co/", devUrl: "http://localhost:4321", //NEED TO ADD NEW URL pagesDevUrl: "https://pwa.NEWURL.pages.dev", diff --git a/src/lib/stripeServer.tsx b/src/lib/stripeServer.tsx new file mode 100644 index 00000000..5401ffeb --- /dev/null +++ b/src/lib/stripeServer.tsx @@ -0,0 +1,17 @@ +import Stripe from "stripe" + +const stripeKey = import.meta.env.PUBLIC_VITE_STRIPE_PRIVATE_KEY +let stripe = null; + +try { + stripe = new Stripe(stripeKey); + if (stripe === null) { + console.log("Stripe is null"); + } +} catch (error) { + if(error instanceof Error){ + console.error(error.message); + } +} + +export default stripe!; \ No newline at end of file diff --git a/src/pages/api/providerProfileSubmit.ts b/src/pages/api/providerProfileSubmit.ts index 37a0f4cf..5c14d12c 100644 --- a/src/pages/api/providerProfileSubmit.ts +++ b/src/pages/api/providerProfileSubmit.ts @@ -193,7 +193,7 @@ export const POST: APIRoute = async ({ request, redirect }) => { // If everything works send a success response return new Response( JSON.stringify({ - message: t("apiErrors.success"), + // message: t("apiErrors.success"), }), { status: 200 }, ); diff --git a/src/pages/api/stripeAccountLinkReauth.ts b/src/pages/api/stripeAccountLinkReauth.ts new file mode 100644 index 00000000..d76e0237 --- /dev/null +++ b/src/pages/api/stripeAccountLinkReauth.ts @@ -0,0 +1,19 @@ +import stripe from "../../lib/stripe"; +import type { APIRoute } from "astro"; +import { useTranslations } from "@i18n/utils"; +import { SITE } from "../../config"; + +export const POST: APIRoute = async ({ request, redirect }) => { + const formData = await request.formData(); + const lang = formData.get("lang"); + //@ts-ignore + const t = useTranslations(lang); + + return new Response( + JSON.stringify({ + message: t("apiErrors.success"), + redirect: "/provider/profile", + }), + { status: 200 } + ); +} \ No newline at end of file diff --git a/src/pages/api/updateAccountStripe.ts b/src/pages/api/updateAccountStripe.ts index 01237d01..cbf803f7 100644 --- a/src/pages/api/updateAccountStripe.ts +++ b/src/pages/api/updateAccountStripe.ts @@ -1,7 +1,9 @@ import supabase from "../../lib/supabaseClientServer"; +import stripe from "../../lib/stripe"; import type { APIRoute } from "astro"; import type { APIContext } from "astro"; import { useTranslations } from "@i18n/utils"; +import { SITE } from "../../config"; export const POST: APIRoute = async ({ request, redirect }) => { const formData = await request.formData(); @@ -14,23 +16,41 @@ export const POST: APIRoute = async ({ request, redirect }) => { const lang = formData.get("lang"); //@ts-ignore const t = useTranslations(lang); - + const stripeAccountId = formData.get("account_id"); const access_token = formData.get("access_token"); const refresh_token = formData.get("refresh_token"); - + // Validate the formData - you'll probably want to do more than this - if ( - !stripeAccountId - ) { + if (!stripeAccountId) { return new Response( JSON.stringify({ message: t("apiErrors.missingFields"), }), - { status: 400 }, + { status: 400 } ); } + async function getAccountLink() { + if (stripeAccountId === null) { + return new Response( + JSON.stringify({ + message: t("apiErrors.missingFields"), + }), + { status: 500 } + ); + } else { + const accountLink = await stripe.accountLinks.create({ + account: stripeAccountId?.toString()!, + refresh_url: SITE.url + "/provider/profile", + return_url: SITE.url + "/provider/profile", + type: "account_onboarding", + }); + } + } + + const accountLink = getAccountLink(); + const { data: sessionData, error: sessionError } = await supabase.auth.setSession({ refresh_token: refresh_token!.toString(), @@ -41,7 +61,7 @@ export const POST: APIRoute = async ({ request, redirect }) => { JSON.stringify({ message: t("apiErrors.noSession"), }), - { status: 500 }, + { status: 500 } ); } @@ -52,7 +72,7 @@ export const POST: APIRoute = async ({ request, redirect }) => { JSON.stringify({ message: t("apiErrors.noSession"), }), - { status: 500 }, + { status: 500 } ); } @@ -63,13 +83,13 @@ export const POST: APIRoute = async ({ request, redirect }) => { JSON.stringify({ message: t("apiErrors.noUser"), }), - { status: 500 }, + { status: 500 } ); } const { error, data } = await supabase .from("sellers") - .update({stripe_account_id: stripeAccountId}) + .update({ stripe_connected_account_id: stripeAccountId }) .eq("user_id", user.id) .select(); @@ -79,14 +99,14 @@ export const POST: APIRoute = async ({ request, redirect }) => { JSON.stringify({ message: t("apiErrors.providerCreateProfileError"), }), - { status: 500 }, + { status: 500 } ); } else if (!data) { return new Response( JSON.stringify({ message: t("apiErrors.noProfileData"), }), - { status: 500 }, + { status: 500 } ); } else { console.log("Post Data: " + JSON.stringify(data)); @@ -98,6 +118,6 @@ export const POST: APIRoute = async ({ request, redirect }) => { message: t("apiErrors.success"), redirect: "/provider/profile", }), - { status: 200 }, + { status: 200 } ); }; diff --git a/src/pages/provider/stripereauth.astro b/src/pages/provider/stripereauth.astro new file mode 100644 index 00000000..592f93cf --- /dev/null +++ b/src/pages/provider/stripereauth.astro @@ -0,0 +1,14 @@ +--- +import { responseHasCookies } from "astro/dist/core/cookies"; +import { POST } from "../api/stripeAccountLinkReauth"; + +let response = await POST(Astro); + +const data = await response.json(); + +if (data.redirect) { + window.location.href = data.redirect; +} else { + window.location.href = "/404"; +} +--- diff --git a/supabase/migrations/20240313143103_addStripeAccount.sql b/supabase/migrations/20240313143103_addStripeAccount.sql new file mode 100644 index 00000000..f8394a63 --- /dev/null +++ b/supabase/migrations/20240313143103_addStripeAccount.sql @@ -0,0 +1,23 @@ +drop view if exists "public"."sellerview"; + +alter table "public"."sellers" alter column "language_spoken" drop not null; + +create or replace view "public"."sellerview" as SELECT sellers.user_id, + sellers.created_at, + sellers.seller_name, + sellers.seller_phone, + sellers.image_url, + sellers.seller_id, + locationview.major_municipality, + profiles.first_name, + profiles.last_name, + profiles.email + FROM ((sellers + LEFT JOIN profiles ON ((sellers.user_id = profiles.user_id))) + LEFT JOIN locationview ON ((sellers.location = locationview.id))); + +alter table "public"."sellers" add column "stripe_connected_account_id" text; + +CREATE UNIQUE INDEX sellers_stripe_connected_account_id_key ON public.sellers USING btree (stripe_connected_account_id); + +alter table "public"."sellers" add constraint "sellers_stripe_connected_account_id_key" UNIQUE using index "sellers_stripe_connected_account_id_key"; From b3b3910aeaad74f0c4edff44e4e8f0b9570ba3fd Mon Sep 17 00:00:00 2001 From: r-southworth Date: Wed, 13 Mar 2024 13:48:21 -0400 Subject: [PATCH 3/6] require a stripe account to post resources --- src/components/posts/CreateNewPost.tsx | 4 +++- src/i18n/UI/English.ts | 1 + src/i18n/UI/French.ts | 1 + src/i18n/UI/Spanish.ts | 1 + src/i18n/uiType.ts | 1 + src/pages/api/stripeAccountLinkReauth.ts | 19 ------------------- src/pages/provider/stripereauth.astro | 14 -------------- 7 files changed, 7 insertions(+), 34 deletions(-) delete mode 100644 src/pages/api/stripeAccountLinkReauth.ts delete mode 100644 src/pages/provider/stripereauth.astro diff --git a/src/components/posts/CreateNewPost.tsx b/src/components/posts/CreateNewPost.tsx index 92a906b2..9a2b105e 100644 --- a/src/components/posts/CreateNewPost.tsx +++ b/src/components/posts/CreateNewPost.tsx @@ -170,7 +170,9 @@ export const CreateNewPost: Component = () => { if (providers.length === 0) { alert(t("messages.onlyProvider")); window.location.href = `/${lang}/provider/createaccount`; - } else { + } else if (providers[0].stripe_connected_account_id === null) { + alert(t("messages.noStripeAccount")); + window.location.href = `/${lang}/provider/profile`; } } } catch (error) { diff --git a/src/i18n/UI/English.ts b/src/i18n/UI/English.ts index 98720fef..f23b03f6 100644 --- a/src/i18n/UI/English.ts +++ b/src/i18n/UI/English.ts @@ -113,6 +113,7 @@ export const English = { profileEdits: 'Profile edits will only be saved after clicking the Save Profile button!', noClient: "No Client found.", noPostsSearch: "No posts match this specific search - please try a different filter or key word", + noStripeAccount: "You must complete enrollment with Stripe before posting resources.", }, formLabels: { diff --git a/src/i18n/UI/French.ts b/src/i18n/UI/French.ts index fd3025d6..1445647f 100644 --- a/src/i18n/UI/French.ts +++ b/src/i18n/UI/French.ts @@ -112,6 +112,7 @@ export const French = { mustSignIn: 'Vous devez être connecté pour voir les services disponibles.', profileEdits: "Les modifications du profil ne seront enregistrées qu'après avoir cliqué sur le bouton Enregistrer le profil !", noPostsSearch: "Aucun article ne correspond à cette recherche spécifique - veuillez essayer un autre filtre ou un autre mot clé", + noStripeAccount: "Vous devez terminer votre inscription auprès de Stripe avant de publier des ressources.", }, formLabels: { diff --git a/src/i18n/UI/Spanish.ts b/src/i18n/UI/Spanish.ts index 4b5b9084..f23668f9 100644 --- a/src/i18n/UI/Spanish.ts +++ b/src/i18n/UI/Spanish.ts @@ -114,6 +114,7 @@ export const Spanish = { profileEdits: '¡Las ediciones del perfil solo se guardarán después de hacer clic en el botón Guardar perfil!', noClient: "No se encontró ningún cliente.", noPostsSearch: "Ninguna publicación coincide con esta búsqueda específica - pruebe con un filtro o palabra clave diferente", + noStripeAccount: "Debes completar la inscripción en Stripe antes de publicar recursos.", }, formLabels: { diff --git a/src/i18n/uiType.ts b/src/i18n/uiType.ts index 07a1be62..2c92be08 100644 --- a/src/i18n/uiType.ts +++ b/src/i18n/uiType.ts @@ -113,6 +113,7 @@ export interface uiObject { profileEdits: string, noClient: string, noPostsSearch: string, + noStripeAccount: string, }, formLabels: { diff --git a/src/pages/api/stripeAccountLinkReauth.ts b/src/pages/api/stripeAccountLinkReauth.ts deleted file mode 100644 index d76e0237..00000000 --- a/src/pages/api/stripeAccountLinkReauth.ts +++ /dev/null @@ -1,19 +0,0 @@ -import stripe from "../../lib/stripe"; -import type { APIRoute } from "astro"; -import { useTranslations } from "@i18n/utils"; -import { SITE } from "../../config"; - -export const POST: APIRoute = async ({ request, redirect }) => { - const formData = await request.formData(); - const lang = formData.get("lang"); - //@ts-ignore - const t = useTranslations(lang); - - return new Response( - JSON.stringify({ - message: t("apiErrors.success"), - redirect: "/provider/profile", - }), - { status: 200 } - ); -} \ No newline at end of file diff --git a/src/pages/provider/stripereauth.astro b/src/pages/provider/stripereauth.astro deleted file mode 100644 index 592f93cf..00000000 --- a/src/pages/provider/stripereauth.astro +++ /dev/null @@ -1,14 +0,0 @@ ---- -import { responseHasCookies } from "astro/dist/core/cookies"; -import { POST } from "../api/stripeAccountLinkReauth"; - -let response = await POST(Astro); - -const data = await response.json(); - -if (data.redirect) { - window.location.href = data.redirect; -} else { - window.location.href = "/404"; -} ---- From 8df49b345acde17e3b47b921673454f8d4184a79 Mon Sep 17 00:00:00 2001 From: r-southworth Date: Wed, 13 Mar 2024 16:47:45 -0400 Subject: [PATCH 4/6] Create a stripe button for provider profile page --- src/components/users/ProviderProfileView.tsx | 397 +++++++++--------- src/components/users/ProviderRegistration.tsx | 14 +- .../users/provider/StripeButton.tsx | 105 +++++ src/config.ts | 2 +- src/pages/api/updateAccountStripe.ts | 18 +- tsconfig.json | 1 + 6 files changed, 322 insertions(+), 215 deletions(-) create mode 100644 src/components/users/provider/StripeButton.tsx diff --git a/src/components/users/ProviderProfileView.tsx b/src/components/users/ProviderProfileView.tsx index 5d1d2113..57386995 100644 --- a/src/components/users/ProviderProfileView.tsx +++ b/src/components/users/ProviderProfileView.tsx @@ -16,6 +16,7 @@ import UserImage from "./UserImage"; import { ui } from "../../i18n/ui"; import type { uiObject } from "../../i18n/uiType"; import { getLangFromUrl, useTranslations } from "../../i18n/utils"; +import { StripeButton } from "./provider/StripeButton"; const lang = getLangFromUrl(new URL(window.location.href)); const t = useTranslations(lang); @@ -118,7 +119,7 @@ export const ProviderProfileView: Component = () => { if (session()) { try { const { data, error } = await supabase - .from("providerview") + .from("sellerview") .select("*") .eq("user_id", user_id); console.log(data); @@ -129,27 +130,27 @@ export const ProviderProfileView: Component = () => { alert(t("messages.noProvider")); location.href = `/${lang}/services`; } else { - let languageArray = data[0].language_spoken; - console.log("Languages Array: " + languageArray); - languageArray?.map((language: number) => { - if (language == 1) { - setLanguageSpoken([...languageSpoken(), "English"]); - } + // let languageArray = data[0].language_spoken; + // console.log("Languages Array: " + languageArray); + // languageArray?.map((language: number) => { + // if (language == 1) { + // setLanguageSpoken([...languageSpoken(), "English"]); + // } - if (language == 2) { - setLanguageSpoken([...languageSpoken(), "Español"]); - } + // if (language == 2) { + // setLanguageSpoken([...languageSpoken(), "Español"]); + // } - if (language == 3) { - setLanguageSpoken([...languageSpoken(), "Français"]); - } - }); + // if (language == 3) { + // setLanguageSpoken([...languageSpoken(), "Français"]); + // } + // }); - //set initial list of languages for provider - setLanguagePick(data[0].language_spoken); + // //set initial list of languages for provider + // setLanguagePick(data[0].language_spoken); - //set display list of languages for provider - data[0].languages = languageSpoken().join(", "); + // //set display list of languages for provider + // data[0].languages = languageSpoken().join(", "); setProvider(data[0]); } @@ -217,30 +218,30 @@ export const ProviderProfileView: Component = () => { } //Will create a list of Languages in the database - try { - const { data, error } = await supabase.from("language").select("*"); - if (error) { - console.log("supabase error: " + error.message); - } else if (data!) { - console.log("DB data"); - console.log(data); - data.forEach((item) => { - item.checked = false; - if (provider()?.language_spoken && provider()?.language_spoken.length > 0){ - provider()?.language_spoken.forEach((language) => { - console.log(language); - if (language === item.id.toString()) { - item.checked = true; - } - }); - } - }); - console.log(data); - setLanguages(data); - } - } catch (error) { - console.log("Language error: " + error); - } + // try { + // const { data, error } = await supabase.from("language").select("*"); + // if (error) { + // console.log("supabase error: " + error.message); + // } else if (data!) { + // console.log("DB data"); + // console.log(data); + // data.forEach((item) => { + // item.checked = false; + // if (provider()?.language_spoken && provider()?.language_spoken.length > 0){ + // provider()?.language_spoken.forEach((language) => { + // console.log(language); + // if (language === item.id.toString()) { + // item.checked = true; + // } + // }); + // } + // }); + // console.log(data); + // setLanguages(data); + // } + // } catch (error) { + // console.log("Language error: " + error); + // } //Will create a list of Major Municipalities based on the selected country try { @@ -283,96 +284,96 @@ export const ProviderProfileView: Component = () => { } //Creates drop down options for Minor Municipality based on selected Major Municipality - try { - const { data: minorMunicipality, error: errorMinorMunicipality } = - await supabase.from("minor_municipality").select("*"); - if (errorMinorMunicipality) { - console.log("supabase error: " + errorMinorMunicipality.message); - } else { - document - .getElementById("MajorMunicipality") - ?.addEventListener("change", () => { - let municipalitySelect = document.getElementById( - "MinorMunicipality" - ) as HTMLSelectElement; - - let length = municipalitySelect?.length; - - for (let i = length - 1; i > -1; i--) { - if (municipalitySelect.options[i].value !== "") { - municipalitySelect.remove(i); - } - } - - let filteredMunicipality = minorMunicipality.filter( - (municipality) => - municipality.major_municipality == - ( - document.getElementById( - "MajorMunicipality" - ) as HTMLSelectElement - )?.value - ); - filteredMunicipality.forEach((municipality) => { - let municipalityOption = new Option( - municipality.minor_municipality, - municipality.id - ); - document - .getElementById("MinorMunicipality") - ?.append(municipalityOption); - }); - }); - } - } catch (error) { - console.log("Other error: " + error); - } + // try { + // const { data: minorMunicipality, error: errorMinorMunicipality } = + // await supabase.from("minor_municipality").select("*"); + // if (errorMinorMunicipality) { + // console.log("supabase error: " + errorMinorMunicipality.message); + // } else { + // document + // .getElementById("MajorMunicipality") + // ?.addEventListener("change", () => { + // let municipalitySelect = document.getElementById( + // "MinorMunicipality" + // ) as HTMLSelectElement; + + // let length = municipalitySelect?.length; + + // for (let i = length - 1; i > -1; i--) { + // if (municipalitySelect.options[i].value !== "") { + // municipalitySelect.remove(i); + // } + // } + + // let filteredMunicipality = minorMunicipality.filter( + // (municipality) => + // municipality.major_municipality == + // ( + // document.getElementById( + // "MajorMunicipality" + // ) as HTMLSelectElement + // )?.value + // ); + // filteredMunicipality.forEach((municipality) => { + // let municipalityOption = new Option( + // municipality.minor_municipality, + // municipality.id + // ); + // document + // .getElementById("MinorMunicipality") + // ?.append(municipalityOption); + // }); + // }); + // } + // } catch (error) { + // console.log("Other error: " + error); + // } //Creates filtered drop down options for Governing District base on selected Minor Municipality - try { - const { data: governingDistrict, error: errorGoverningDistrict } = - await supabase.from("governing_district").select("*"); - if (errorGoverningDistrict) { - console.log("supabase error: " + errorGoverningDistrict.message); - } else { - document - .getElementById("MinorMunicipality") - ?.addEventListener("change", () => { - let districtSelect = document.getElementById( - "GoverningDistrict" - ) as HTMLSelectElement; - - let length = districtSelect?.length; - - for (let i = length - 1; i > -1; i--) { - if (districtSelect.options[i].value !== "") { - districtSelect.remove(i); - } - } - - let filteredDistrict = governingDistrict.filter( - (district) => - district.minor_municipality == - ( - document.getElementById( - "MinorMunicipality" - ) as HTMLSelectElement - )?.value - ); - filteredDistrict.forEach((district) => { - let districtOption = new Option( - district.governing_district, - district.id - ); - document - .getElementById("GoverningDistrict") - ?.append(districtOption); - }); - }); - } - } catch (error) { - console.log("Other error: " + error); - } + // try { + // const { data: governingDistrict, error: errorGoverningDistrict } = + // await supabase.from("governing_district").select("*"); + // if (errorGoverningDistrict) { + // console.log("supabase error: " + errorGoverningDistrict.message); + // } else { + // document + // .getElementById("MinorMunicipality") + // ?.addEventListener("change", () => { + // let districtSelect = document.getElementById( + // "GoverningDistrict" + // ) as HTMLSelectElement; + + // let length = districtSelect?.length; + + // for (let i = length - 1; i > -1; i--) { + // if (districtSelect.options[i].value !== "") { + // districtSelect.remove(i); + // } + // } + + // let filteredDistrict = governingDistrict.filter( + // (district) => + // district.minor_municipality == + // ( + // document.getElementById( + // "MinorMunicipality" + // ) as HTMLSelectElement + // )?.value + // ); + // filteredDistrict.forEach((district) => { + // let districtOption = new Option( + // district.governing_district, + // district.id + // ); + // document + // .getElementById("GoverningDistrict") + // ?.append(districtOption); + // }); + // }); + // } + // } catch (error) { + // console.log("Other error: " + error); + // } //If the user is not signed in then tell them to sign in and send them to the login page } @@ -385,33 +386,33 @@ export const ProviderProfileView: Component = () => { const majorMunicipality = document.getElementById( "MajorMunicipality" ) as HTMLSelectElement; - const minorMunicipality = document.getElementById( - "MinorMunicipality" - ) as HTMLSelectElement; - const governingDistrict = document.getElementById( - "GoverningDistrict" - ) as HTMLSelectElement; + // const minorMunicipality = document.getElementById( + // "MinorMunicipality" + // ) as HTMLSelectElement; + // const governingDistrict = document.getElementById( + // "GoverningDistrict" + // ) as HTMLSelectElement; if ( country.value !== "" || - majorMunicipality.value !== "" || - minorMunicipality.value !== "" || - governingDistrict.value !== "" + majorMunicipality.value !== "" + // minorMunicipality.value !== "" || + // governingDistrict.value !== "" ) { country.required = true; majorMunicipality.required = true; - minorMunicipality.required = true; - governingDistrict.required = true; + // minorMunicipality.required = true; + // governingDistrict.required = true; } else if ( country.value === "" && - majorMunicipality.value === "" && - minorMunicipality.value === "" && - governingDistrict.value === "" + majorMunicipality.value === "" + // minorMunicipality.value === "" && + // governingDistrict.value === "" ) { country.required = false; majorMunicipality.required = false; - minorMunicipality.required = false; - governingDistrict.required = false; + // minorMunicipality.required = false; + // governingDistrict.required = false; } }; @@ -432,48 +433,49 @@ export const ProviderProfileView: Component = () => { setFormData(formData); } - let expanded = false; - function languageCheckboxes() { - let checkboxes = document.getElementById("checkboxes"); - if (!expanded) { - checkboxes?.classList.remove("hidden"); - checkboxes?.classList.add("block"); - expanded = true; - } else { - checkboxes?.classList.remove("block"); - checkboxes?.classList.add("hidden"); - expanded = false; - } - } - - function setLanguageArray(e: Event) { - if ((e.target as HTMLInputElement).checked) { - setLanguagePick([ - ...languagePick(), - (e.target as HTMLInputElement).value, - ]); - } else if ((e.target as HTMLInputElement).checked === false) { - if (languagePick().includes((e.target as HTMLInputElement).value)) { - setLanguagePick( - languagePick().filter( - (value) => value !== (e.target as HTMLInputElement).value - ) - ); - } - } - if (languagePick().length > 0) { - document.getElementById("isValid")?.classList.remove("hidden"); - } else if (languagePick().length === 0) { - document.getElementById("isValid")?.classList.add("hidden"); - } - console.log(languagePick()); - } + // let expanded = false; + // function languageCheckboxes() { + // let checkboxes = document.getElementById("checkboxes"); + // if (!expanded) { + // checkboxes?.classList.remove("hidden"); + // checkboxes?.classList.add("block"); + // expanded = true; + // } else { + // checkboxes?.classList.remove("block"); + // checkboxes?.classList.add("hidden"); + // expanded = false; + // } + // } + + // function setLanguageArray(e: Event) { + // if ((e.target as HTMLInputElement).checked) { + // setLanguagePick([ + // ...languagePick(), + // (e.target as HTMLInputElement).value, + // ]); + // } else if ((e.target as HTMLInputElement).checked === false) { + // if (languagePick().includes((e.target as HTMLInputElement).value)) { + // setLanguagePick( + // languagePick().filter( + // (value) => value !== (e.target as HTMLInputElement).value + // ) + // ); + // } + // } + // if (languagePick().length > 0) { + // document.getElementById("isValid")?.classList.remove("hidden"); + // } else if (languagePick().length === 0) { + // document.getElementById("isValid")?.classList.add("hidden"); + // } + // console.log(languagePick()); + // } //TODO: Style improvement - when posts section is opened in mobile view, it takes up full screen width some margin might be nice not sure but this might be due to current card styling //TODO: Style improvement - when boxes are collapsed in mobile view they are narrower than when they are expanded might be nice to keep it the same size return (
+

@@ -759,19 +761,10 @@ export const ProviderProfileView: Component = () => {
{/* Creates a list of checkboxes that drop down to multiple select */}
-
languageCheckboxes()} > - {/* */}

{

-
-
*/} + {/* +
*/}

@@ -1385,21 +1378,11 @@ export const ProviderProfileView: Component = () => {
{/* Creates a list of checkboxes that drop down to multiple select */} -
+ {/*
languageCheckboxes()} > - {/* */} -

{ + return res.charges_enabled; +}); + +export const StripeButton = () => { + const [isUser, setIsUser] = createSignal(false); + const [accountSetup, setAccountSetup] = createSignal(stripeAcctSetup); + + if (User.session === null) { + // console.log("User don't exist"); + } else { + setIsUser(User.session!.user.role === "authenticated"); + } + + async function stripeSetup() { + const accountLink = await stripe.accountLinks.create({ + account: stripeId, + refresh_url: SITE.url + "/provider/profile", + return_url: SITE.url + "/provider/profile", + type: "account_onboarding", + }); + window.open(accountLink.url, "_blank"); + } + + async function stripeLogin() { + const loginLink = await stripe.accounts.createLoginLink(stripeId); + window.open(loginLink.url, "_blank"); + } + + if (UserError) { + console.log("User Error: " + UserError.message); + } + + function renderWhenFalse() { + if (accountSetup() === false) { + return ( +

+ +
+ ); + } + } + + function renderWhenTrue() { + if (accountSetup() === true) { + return ( +
+ +
+ ); + } + } + + return ( +
+ {renderWhenFalse()} + {renderWhenTrue()} +
+ ); +}; diff --git a/src/config.ts b/src/config.ts index 7e4d449e..6baa4a80 100644 --- a/src/config.ts +++ b/src/config.ts @@ -9,7 +9,7 @@ const CONFIG = { title: "LearnGrove", description: "HomeSchool description", //NEED TO ADD NEW URL - url: "https://learngrove.co/", + url: "https://learngrove.co", devUrl: "http://localhost:4321", //NEED TO ADD NEW URL pagesDevUrl: "https://pwa.NEWURL.pages.dev", diff --git a/src/pages/api/updateAccountStripe.ts b/src/pages/api/updateAccountStripe.ts index cbf803f7..5671e8c3 100644 --- a/src/pages/api/updateAccountStripe.ts +++ b/src/pages/api/updateAccountStripe.ts @@ -46,11 +46,21 @@ export const POST: APIRoute = async ({ request, redirect }) => { return_url: SITE.url + "/provider/profile", type: "account_onboarding", }); + + if (!accountLink) { + return new Response( + JSON.stringify({ + //TODO: Change this error to be more specific like "error creating account link" + message: t("apiErrors.providerCreateProfileError"), + }), + { status: 500 } + ); + } + + return accountLink; } } - const accountLink = getAccountLink(); - const { data: sessionData, error: sessionError } = await supabase.auth.setSession({ refresh_token: refresh_token!.toString(), @@ -112,11 +122,13 @@ export const POST: APIRoute = async ({ request, redirect }) => { console.log("Post Data: " + JSON.stringify(data)); } + const accountLink = await getAccountLink(); + // Do something with the formData, then return a success response return new Response( JSON.stringify({ message: t("apiErrors.success"), - redirect: "/provider/profile", + redirect: accountLink.url, }), { status: 200 } ); diff --git a/tsconfig.json b/tsconfig.json index 06c788c0..f17ff3c1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "@i18n/*" : ["src/i18n/*"], "@utils/*" : ["src/utils/*"], "@assets/*" : ["src/assets/*"], + "@lib/*" : ["src/lib/*"] }, "types": ["vite-plugin-pwa/client"], } From c66ede6554796d1bfe58f1a3efbf57838439990f Mon Sep 17 00:00:00 2001 From: r-southworth Date: Wed, 13 Mar 2024 17:41:41 -0400 Subject: [PATCH 5/6] fix stripe button to redirect to create account if no provider account and try to access the provider/profile page --- src/components/posts/CreateNewPost.tsx | 10 +++++----- src/components/users/ProviderProfileView.tsx | 2 +- src/components/users/ProviderRegistration.tsx | 3 --- src/components/users/provider/StripeButton.tsx | 12 +++++++++--- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/posts/CreateNewPost.tsx b/src/components/posts/CreateNewPost.tsx index 9a2b105e..4bd82280 100644 --- a/src/components/posts/CreateNewPost.tsx +++ b/src/components/posts/CreateNewPost.tsx @@ -147,11 +147,11 @@ export const CreateNewPost: Component = () => { }); }); - createEffect(() => { - if(selectedTaxCode() !== undefined) { - console.log("Tax Code: " + selectedTaxCode()!.value); - } - }); + // createEffect(() => { + // if(selectedTaxCode() !== undefined) { + // console.log("Tax Code: " + selectedTaxCode()!.value); + // } + // }); createEffect(async () => { const { data, error } = await supabase.auth.getSession(); diff --git a/src/components/users/ProviderProfileView.tsx b/src/components/users/ProviderProfileView.tsx index 57386995..01518336 100644 --- a/src/components/users/ProviderProfileView.tsx +++ b/src/components/users/ProviderProfileView.tsx @@ -128,7 +128,7 @@ export const ProviderProfileView: Component = () => { console.log(error); } else if (data[0] === undefined) { alert(t("messages.noProvider")); - location.href = `/${lang}/services`; + location.href = `/${lang}/provider/createaccount`; } else { // let languageArray = data[0].language_spoken; // console.log("Languages Array: " + languageArray); diff --git a/src/components/users/ProviderRegistration.tsx b/src/components/users/ProviderRegistration.tsx index 28601f1c..44bd112a 100644 --- a/src/components/users/ProviderRegistration.tsx +++ b/src/components/users/ProviderRegistration.tsx @@ -46,8 +46,6 @@ async function postFormData(formData: FormData) { body: formData, }); const data = await response.json(); - console.log(data.message); - //Checks the API response for the redirect and sends them to the redirect page if there is one if (response.status === 200) { createStripeAccount(formData); } @@ -155,7 +153,6 @@ export const ProviderRegistration: Component = () => { //Must send the access_token and refresh_token to the APIRoute because the server can't see the local session function submit(e: SubmitEvent) { e.preventDefault(); - console.log(languages()); const formData = new FormData(e.target as HTMLFormElement); diff --git a/src/components/users/provider/StripeButton.tsx b/src/components/users/provider/StripeButton.tsx index 8a7a42cd..a88efe3a 100644 --- a/src/components/users/provider/StripeButton.tsx +++ b/src/components/users/provider/StripeButton.tsx @@ -19,12 +19,18 @@ const { data: stripeData, error: stripeError } = await supabase if (stripeError) { console.log("Stripe Error: " + stripeError.message); -} +} if (!stripeData) { console.log("No Stripe ID found"); -} - +} else { + console.log(stripeData) +} +if (stripeData === null ||stripeData.length === 0) { + alert(t("messages.noProvider")); + location.href = `/${lang}/provider/createaccount`; +} const stripeId = stripeData![0].stripe_connected_account_id; + const stripeAcctSetup = await stripe.accounts.retrieve(stripeId).then((res) => { From 0c2680e81d2369ec6509652ee856a9fc7b58a698 Mon Sep 17 00:00:00 2001 From: r-southworth Date: Wed, 13 Mar 2024 17:42:47 -0400 Subject: [PATCH 6/6] add improvement idea comment --- src/components/users/provider/StripeButton.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/users/provider/StripeButton.tsx b/src/components/users/provider/StripeButton.tsx index a88efe3a..b808c78a 100644 --- a/src/components/users/provider/StripeButton.tsx +++ b/src/components/users/provider/StripeButton.tsx @@ -25,6 +25,8 @@ if (!stripeData) { } else { console.log(stripeData) } + +// Improvement: This probably shouldn't be needed in the button like this the page should check that before loading the button at all. But this works for now. if (stripeData === null ||stripeData.length === 0) { alert(t("messages.noProvider")); location.href = `/${lang}/provider/createaccount`;