diff --git a/templates/corporate/sponsorship.html b/templates/corporate/sponsorship.html index 6960e1c1c3..f931668aa1 100644 --- a/templates/corporate/sponsorship.html +++ b/templates/corporate/sponsorship.html @@ -4,12 +4,12 @@ {% set PAGE_TITLE = "💚 Request sponsorship" %} {% block portico_content %} -
+

💚 Request sponsorship

-
+
@@ -20,7 +20,7 @@
- {% for org_type in sorted_org_types %} {% if not org_type[1].hidden %}
- +
- +
@@ -46,8 +46,8 @@
diff --git a/tools/test-js-with-node b/tools/test-js-with-node index 86634ef755..79146e2432 100755 --- a/tools/test-js-with-node +++ b/tools/test-js-with-node @@ -58,7 +58,6 @@ EXEMPT_FILES = make_set( "web/src/avatar.ts", "web/src/billing/event_status.ts", "web/src/billing/helpers.ts", - "web/src/billing/upgrade.ts", "web/src/blueslip.ts", "web/src/blueslip_stacktrace.ts", "web/src/browser_history.ts", diff --git a/web/src/billing/helpers.ts b/web/src/billing/helpers.ts index 7bfacc8c0a..cce336896f 100644 --- a/web/src/billing/helpers.ts +++ b/web/src/billing/helpers.ts @@ -5,7 +5,7 @@ import * as loading from "../loading"; import {page_params} from "./page_params"; -type FormDataObject = Record; +export type FormDataObject = Record; export const schedule_schema = z.enum(["monthly", "annual"]); export type Prices = Record, number>; @@ -108,7 +108,7 @@ export function update_charged_amount(prices: Prices, schedule: keyof Prices): v $("#charged_amount").text(format_money(page_params.seat_count * prices[schedule])); } -export function update_discount_details(organization_type: keyof DiscountDetails): void { +export function update_discount_details(organization_type: string): void { let discount_notice = "Your organization may be eligible for a discount on Zulip Cloud Standard. Organizations whose members are not employees are generally eligible."; const discount_details: DiscountDetails = { @@ -120,9 +120,20 @@ export function update_discount_details(organization_type: keyof DiscountDetails education_nonprofit: "Zulip Cloud Standard is discounted 90% for education non-profits with online purchase.", }; - if (discount_details[organization_type]) { - discount_notice = discount_details[organization_type]; + + try { + const parsed_organization_type = organization_type_schema.parse(organization_type); + discount_notice = discount_details[parsed_organization_type]; + } catch { + // This will likely fail if organization_type is not in organization_type_schema or + // parsed_organization_type is not preset in discount_details. In either case, we will + // fallback to the default discount_notice. + // + // Why use try / catch? + // Because organization_type_schema.options.includes wants organization_type to be of type + // opensource | research | ... and defining a type like that is not useful. } + $("#sponsorship-discount-details").text(discount_notice); } @@ -161,18 +172,6 @@ export function set_tab(page: string): void { window.addEventListener("hashchange", handle_hashchange); } -export function set_sponsorship_form(): void { - $("#sponsorship-button").on("click", (e) => { - if (!is_valid_input($("#sponsorship-form"))) { - return; - } - e.preventDefault(); - create_ajax_request("/json/billing/sponsorship", "sponsorship", [], "POST", () => - window.location.replace("/"), - ); - }); -} - export function is_valid_input(elem: JQuery): boolean { return elem[0].checkValidity(); } diff --git a/web/src/billing/sponsorship.ts b/web/src/billing/sponsorship.ts index 79f3e48456..080eedca79 100644 --- a/web/src/billing/sponsorship.ts +++ b/web/src/billing/sponsorship.ts @@ -2,8 +2,56 @@ import $ from "jquery"; import * as helpers from "./helpers"; +function show_submit_loading_indicator(): void { + $("#sponsorship-button .sponsorship-button-loader").css("display", "inline-block"); + $("#sponsorship-button").prop("disabled", true); + $("#sponsorship-button .sponsorship-button-text").hide(); +} + +function hide_submit_loading_indicator(): void { + $("#sponsorship-button .sponsorship-button-loader").css("display", "none"); + $("#sponsorship-button").prop("disabled", false); + $("#sponsorship-button .sponsorship-button-text").show(); +} + +function create_ajax_request(): void { + show_submit_loading_indicator(); + const $form = $("#sponsorship-form"); + const data: helpers.FormDataObject = {}; + + for (const item of $form.serializeArray()) { + data[item.name] = item.value; + } + + void $.ajax({ + type: "post", + url: "/json/billing/sponsorship", + data, + success() { + window.location.reload(); + }, + error(xhr) { + hide_submit_loading_indicator(); + if (xhr.responseJSON?.msg) { + $("#sponsorship-error").show().text(xhr.responseJSON.msg); + } + }, + }); +} + export function initialize(): void { - helpers.set_sponsorship_form(); + $("#sponsorship-button").on("click", (e) => { + if (!helpers.is_valid_input($("#sponsorship-form"))) { + return; + } + e.preventDefault(); + create_ajax_request(); + }); + + $("#organization-type").on("change", (e) => { + const string_value = $(e.currentTarget.selectedOptions).attr("data-string-value") ?? ""; + helpers.update_discount_details(string_value); + }); } $(() => { diff --git a/web/src/billing/upgrade.ts b/web/src/billing/upgrade.ts index c44c601fb9..1106d4e4a5 100644 --- a/web/src/billing/upgrade.ts +++ b/web/src/billing/upgrade.ts @@ -44,11 +44,6 @@ export const initialize = (): void => { helpers.update_charged_amount(prices, helpers.schedule_schema.parse(this.value)); }); - $("select[name=organization-type]").on("change", (e) => { - const string_value = $(e.currentTarget.selectedOptions).attr("data-string-value"); - helpers.update_discount_details(helpers.organization_type_schema.parse(string_value)); - }); - $("#autopay_annual_price").text(helpers.format_money(prices.annual)); $("#autopay_annual_price_per_month").text(helpers.format_money(prices.annual / 12)); $("#autopay_monthly_price").text(helpers.format_money(prices.monthly)); diff --git a/web/styles/portico/billing.css b/web/styles/portico/billing.css index 74a8096ef9..81e4c1aee6 100644 --- a/web/styles/portico/billing.css +++ b/web/styles/portico/billing.css @@ -371,6 +371,33 @@ input[name="licenses"] { background-color: hsl(240deg 96% 68%); } -#sponsorship-form { +.sponsorship-page .white-box { margin: 30px; } + +.sponsorship-page #sponsorship-form { + margin: 0; +} + +#sponsorship-discount-details { + font-weight: normal; + margin: 2px; + padding-top: 25px; + text-align: left; + overflow-wrap: break-word; + width: 450px; +} + +@media (width < 600px) { + .sponsorship-page { + transform: scale(0.8); + margin: -50px; + } +} + +@media (width < 460px) { + .sponsorship-page { + transform: scale(0.6); + margin: -150px -100px; + } +} diff --git a/web/styles/portico/portico_signin.css b/web/styles/portico/portico_signin.css index 1ce45fdcb4..0d4be2ed86 100644 --- a/web/styles/portico/portico_signin.css +++ b/web/styles/portico/portico_signin.css @@ -916,6 +916,7 @@ button#register_auth_button_gitlab { } } +#sponsorship-form, #registration, #new-realm-creation { width: auto; @@ -1012,7 +1013,8 @@ button#register_auth_button_gitlab { width: 450px; } - .register-button .loader { + .register-button .loader, + .sponsorship-button-loader { display: none; vertical-align: top; position: relative;