diff --git a/framer/Newsletter_Form.tsx b/framer/Newsletter_Form.tsx
new file mode 100644
index 0000000..e3d3e47
--- /dev/null
+++ b/framer/Newsletter_Form.tsx
@@ -0,0 +1,118 @@
+// @ts-nocheck
+import type { ComponentType } from "react";
+import { useState, useEffect } from "react";
+// @ts-ignore
+import { createStore } from "https://framer.com/m/framer/store.js@^1.0.0";
+
+const emptyStore = {
+ email: "",
+ env: "prod",
+ isLoading: false,
+ isSubscribed: false,
+ isFailed: false,
+};
+const useStore = createStore(emptyStore);
+
+// Buttons
+
+export const submitNewsletterForm = (Component: any): ComponentType => {
+ return (props: any) => {
+ const [store, setStore] = useStore();
+
+ useEffect(() => {
+ setStore({
+ env: location.host === "giveffektivt.dk" ? "prod" : "dev",
+ });
+ }, [setStore]);
+
+ const onTap = async () => submitNewsletter(store, setStore);
+
+ return store.email.includes("@") &&
+ !store.isLoading &&
+ !store.isSubscribed &&
+ !store.isFailed ? (
+
+ ) : null;
+ };
+};
+
+export const submitNewsletterForm_isDisabled = (
+ Component: any,
+): ComponentType => {
+ return (props: any) => {
+ const [store] = useStore();
+ return !store.email.includes("@") &&
+ !store.isLoading &&
+ !store.isSubscribed &&
+ !store.isFailed ? (
+
+ ) : null;
+ };
+};
+
+export const submitNewsletterForm_isLoading = (
+ Component: any,
+): ComponentType => {
+ return (props: any) => {
+ const [store] = useStore();
+ return store.isLoading ? : null;
+ };
+};
+
+export const submitNewsletterForm_isSubscribed = (
+ Component: any,
+): ComponentType => {
+ return (props: any) => {
+ const [store] = useStore();
+ return store.isSubscribed ? : null;
+ };
+};
+
+export const submitNewsletterForm_isFailed = (
+ Component: any,
+): ComponentType => {
+ return (props: any) => {
+ const [store] = useStore();
+ return store.isFailed ? : null;
+ };
+};
+
+// Inputs
+
+const setInput = (Component: any, field: string, isValid): ComponentType => {
+ return (props: any) => {
+ const [store, setStore] = useStore();
+
+ const onValueChange = (value: string) => setStore({ [field]: value });
+
+ return (
+
+ );
+ };
+};
+
+export const inputEmail = (Component: any): ComponentType => {
+ return setInput(Component, "email", (value) => value.includes("@"));
+};
+
+// Debug
+
+export const showDebug = (Component: any): ComponentType => {
+ return (props) => {
+ const [store] = useStore();
+
+ return store.env === "prod" ? null : (
+
+ );
+ };
+};
diff --git a/framer/helpers-newsletter.ts b/framer/helpers-newsletter.ts
new file mode 100644
index 0000000..faaa2a9
--- /dev/null
+++ b/framer/helpers-newsletter.ts
@@ -0,0 +1,18 @@
+const prepareNewsletterPayload = (store: any) => ({ email: store.email });
+
+const submitNewsletter = async (store: any, setStore: any) => {
+ try {
+ setStore({ isLoading: true, isSubscribed: false, isFailed: false });
+ track("Newsletter form submitted", 20);
+
+ await submitForm(store.env, "newsletter", prepareNewsletterPayload(store));
+ setStore({ isSubscribed: true });
+ setTimeout(() => setStore({ email: "" }), 5000);
+ } catch (err) {
+ setStore({ isFailed: true });
+ notifyAboutClientSideError("submitNewsletter", err?.toString());
+ }
+
+ setStore({ isLoading: false });
+ setTimeout(() => setStore({ isSubscribed: false, isFailed: false }), 5000);
+};
diff --git a/framer/justfile b/framer/justfile
index 0d82f51..d6beffd 100644
--- a/framer/justfile
+++ b/framer/justfile
@@ -7,6 +7,9 @@ donation:
membership:
@cat Membership_Form.tsx helpers.ts helpers-membership.ts
+newsletter:
+ @cat Newsletter_Form.tsx helpers.ts helpers-newsletter.ts
+
fundraiser-admin:
@cat Fundraiser_Admin.tsx helpers.ts