sponsorsihp: Make submitting the form work.

Includes various changes to make submitting the sponsorship form work.
This commit is contained in:
Aman Agrawal 2023-11-02 15:03:52 +00:00 committed by Tim Abbott
parent a75c8ee6f0
commit ff5e1c3aee
7 changed files with 102 additions and 32 deletions

View File

@ -4,12 +4,12 @@
{% set PAGE_TITLE = "💚 Request sponsorship" %} {% set PAGE_TITLE = "💚 Request sponsorship" %}
{% block portico_content %} {% block portico_content %}
<div class="register-account flex full-page"> <div class="register-account flex full-page sponsorship-page">
<div class="center-block new-style"> <div class="center-block new-style">
<div class="pitch"> <div class="pitch">
<h1>💚 Request sponsorship</h1> <h1>💚 Request sponsorship</h1>
</div> </div>
<div id="registration" class="white-box"> <div class="white-box">
<div id="sponsorship-error" class="alert alert-danger"></div> <div id="sponsorship-error" class="alert alert-danger"></div>
<div id="sponsorship-input-section"> <div id="sponsorship-input-section">
<form id="sponsorship-form" method="post"> <form id="sponsorship-form" method="post">
@ -20,7 +20,7 @@
</div> </div>
<div class="input-box sponsorship-form-field"> <div class="input-box sponsorship-form-field">
<div class="inline-block relative"> <div class="inline-block relative">
<select name="realm_type" id="realm_type" class="sponsorship-form-select"> <select name="organization-type" id="organization-type" class="sponsorship-form-select">
{% for org_type in sorted_org_types %} {% for org_type in sorted_org_types %}
{% if not org_type[1].hidden %} {% if not org_type[1].hidden %}
<option data-string-value="{{ org_type[0] }}" <option data-string-value="{{ org_type[0] }}"
@ -32,11 +32,11 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<label for="realm_type" class="inline-block label-title">{{ _('Organization type') }}</label> <label for="organization-type" class="inline-block label-title">{{ _('Organization type') }}</label>
</div> </div>
<div class="input-box sponsorship-form-field no-validation"> <div class="input-box sponsorship-form-field no-validation">
<label for="org-website" class="inline-block label-title">{{ _('Organization website') }}</label> <label for="org-website" class="inline-block label-title">{{ _('Organization website') }}</label>
<input id="org-website" name="website" type="text" class="input-large" placeholder="{{ _('Leave blank if your organization does not have a website.') }}"/> <input id="org-website" name="website" type="text" placeholder="{{ _('Leave blank if your organization does not have a website.') }}"/>
</div> </div>
<div class="input-box sponsorship-form-field"> <div class="input-box sponsorship-form-field">
<label for="description" class="inline-block label-title">{{ _('Describe your organization briefly') }}</label> <label for="description" class="inline-block label-title">{{ _('Describe your organization briefly') }}</label>
@ -46,8 +46,8 @@
<!-- Disabled buttons do not fire any events, so we need a container div that isn't disabled for tippyjs to work --> <!-- Disabled buttons do not fire any events, so we need a container div that isn't disabled for tippyjs to work -->
<div class="upgrade-button-container" {% if is_demo_organization %}data-tippy-content="{% trans %}Convert demo organization before upgrading.{% endtrans %}"{% endif %}> <div class="upgrade-button-container" {% if is_demo_organization %}data-tippy-content="{% trans %}Convert demo organization before upgrading.{% endtrans %}"{% endif %}>
<button type="submit" id="sponsorship-button" class="stripe-button-el invoice-button" {% if is_demo_organization %}disabled{% endif %}> <button type="submit" id="sponsorship-button" class="stripe-button-el invoice-button" {% if is_demo_organization %}disabled{% endif %}>
Submit <span class="sponsorship-button-text">Submit</span>
<object class="loader" type="image/svg+xml" data="{{ static('images/loading/loader-white.svg') }}"></object> <object class="loader sponsorship-button-loader" type="image/svg+xml" data="{{ static('images/loading/loader-white.svg') }}"></object>
</button> </button>
</div> </div>
</form> </form>

View File

@ -58,7 +58,6 @@ EXEMPT_FILES = make_set(
"web/src/avatar.ts", "web/src/avatar.ts",
"web/src/billing/event_status.ts", "web/src/billing/event_status.ts",
"web/src/billing/helpers.ts", "web/src/billing/helpers.ts",
"web/src/billing/upgrade.ts",
"web/src/blueslip.ts", "web/src/blueslip.ts",
"web/src/blueslip_stacktrace.ts", "web/src/blueslip_stacktrace.ts",
"web/src/browser_history.ts", "web/src/browser_history.ts",

View File

@ -5,7 +5,7 @@ import * as loading from "../loading";
import {page_params} from "./page_params"; import {page_params} from "./page_params";
type FormDataObject = Record<string, string>; export type FormDataObject = Record<string, string>;
export const schedule_schema = z.enum(["monthly", "annual"]); export const schedule_schema = z.enum(["monthly", "annual"]);
export type Prices = Record<z.infer<typeof schedule_schema>, number>; export type Prices = Record<z.infer<typeof schedule_schema>, 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])); $("#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 = let discount_notice =
"Your organization may be eligible for a discount on Zulip Cloud Standard. Organizations whose members are not employees are generally eligible."; "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 = { const discount_details: DiscountDetails = {
@ -120,9 +120,20 @@ export function update_discount_details(organization_type: keyof DiscountDetails
education_nonprofit: education_nonprofit:
"Zulip Cloud Standard is discounted 90% for education non-profits with online purchase.", "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); $("#sponsorship-discount-details").text(discount_notice);
} }
@ -161,18 +172,6 @@ export function set_tab(page: string): void {
window.addEventListener("hashchange", handle_hashchange); 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<HTMLFormElement>): boolean { export function is_valid_input(elem: JQuery<HTMLFormElement>): boolean {
return elem[0].checkValidity(); return elem[0].checkValidity();
} }

View File

@ -2,8 +2,56 @@ import $ from "jquery";
import * as helpers from "./helpers"; 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 { 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();
});
$<HTMLSelectElement>("#organization-type").on("change", (e) => {
const string_value = $(e.currentTarget.selectedOptions).attr("data-string-value") ?? "";
helpers.update_discount_details(string_value);
});
} }
$(() => { $(() => {

View File

@ -44,11 +44,6 @@ export const initialize = (): void => {
helpers.update_charged_amount(prices, helpers.schedule_schema.parse(this.value)); helpers.update_charged_amount(prices, helpers.schedule_schema.parse(this.value));
}); });
$<HTMLSelectElement>("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").text(helpers.format_money(prices.annual));
$("#autopay_annual_price_per_month").text(helpers.format_money(prices.annual / 12)); $("#autopay_annual_price_per_month").text(helpers.format_money(prices.annual / 12));
$("#autopay_monthly_price").text(helpers.format_money(prices.monthly)); $("#autopay_monthly_price").text(helpers.format_money(prices.monthly));

View File

@ -371,6 +371,33 @@ input[name="licenses"] {
background-color: hsl(240deg 96% 68%); background-color: hsl(240deg 96% 68%);
} }
#sponsorship-form { .sponsorship-page .white-box {
margin: 30px; 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;
}
}

View File

@ -916,6 +916,7 @@ button#register_auth_button_gitlab {
} }
} }
#sponsorship-form,
#registration, #registration,
#new-realm-creation { #new-realm-creation {
width: auto; width: auto;
@ -1012,7 +1013,8 @@ button#register_auth_button_gitlab {
width: 450px; width: 450px;
} }
.register-button .loader { .register-button .loader,
.sponsorship-button-loader {
display: none; display: none;
vertical-align: top; vertical-align: top;
position: relative; position: relative;