Files
Brenda WallaceandDaniel O'Connor 8474bdd416 remove newsletter (#4974)
* Remove unused bluecloth gem

Last released in 2011. It was only required, never called; kramdown
renders the markdown.

* Add specs for Tilt markdown rendering and posts markdown partial

* Remove rails_12factor gem

Last released in 2014. Rails 5+ does this via RAILS_SERVE_STATIC_FILES
and RAILS_LOG_TO_STDOUT, which production.rb already reads.

* Remove memcachier gem from Gemfile and Gemfile.lock

* Remove unused webrat gem

Last released in 2011. Nothing requires it; the specs use Rails'
assert_select and Capybara matchers. Only generator comments mention it.

* Remove crowdin-cli gem

Last released in 2018. Crowdin now ships its own standalone CLI, which
reads crowdin.yml and doesn't need the gem. Also drops the dependencies
only it used (rest-client, gli, crowdin-api and friends).

* Removes the crowdin-cli gem (last released 2018), the dependencies only
it needed, and crowdin.yml. Crowdin was never set up: the config still
has a placeholder project ID and nothing else in the repo mentions it.

Assisted by Claude

* Remove newsletter functionality and associated views and tests

* Remove newsletter functionality and related code across the application

* keep the column

* Remove stale test step

* add back newsletter

* Add migration to update newsletter column comment in members table

---------

Co-authored-by: Daniel O'Connor <daniel.oconnor@gmail.com>
2026-10-26 11:48:36 +10:30

77 lines
4.2 KiB
React

import React, {useState} from 'react';
import {postJson} from '../api';
import {AuthFooter, CheckField, FormErrors, TextField, fieldErrors} from './AuthFields';
import Modal from './Modal';
import useRecaptcha from './useRecaptcha';
const FIELDS = ['login_name', 'email', 'password', 'password_confirmation', 'tos_agreement'];
const FAILED = {byField: {}, other: ['Something went wrong joining. Please try again.']};
// Joining, as a dialog over the page the link was on. The same fields and
// checks as the sign up page: it posts to Devise's registrations#create as
// JSON, shows the server's errors against their fields, and on success goes to
// wherever the server would have sent the new member.
export default function SignUpModal({urls, recaptchaSiteKey, siteName, onClose, onSwitch}) {
const [values, setValues] = useState({login_name: '', email: '', password: '', password_confirmation: '', tos_agreement: false});
const [saving, setSaving] = useState(false);
const [errors, setErrors] = useState({byField: {}, other: []});
const captcha = useRecaptcha(recaptchaSiteKey);
const set = (name) => (event) => setValues({...values, [name]: event.target.type === 'checkbox' ? event.target.checked : event.target.value});
async function submit(event) {
event.preventDefault();
setSaving(true);
setErrors({byField: {}, other: []});
try {
const {ok, status, data} = await postJson(urls.sign_up, {member: values, 'g-recaptcha-response': captcha.response()});
if (ok && data && data.location) {
setTimeout(() => { window.location.href = data.location; }, 0);
return;
}
setErrors(status === 422 ? fieldErrors(data, FIELDS) : FAILED);
} catch (error) {
setErrors(FAILED);
}
captcha.reset();
setSaving(false);
}
const on = errors.byField;
return (
<Modal title={`Join ${siteName}`} titleId="sign-up-title" onClose={onClose}>
<form onSubmit={submit} noValidate>
<div className="modal-body fs-5">
<p>Sign up to track your vegetable garden and connect with other local growers.</p>
<FormErrors messages={errors.other} />
<div className="row">
<div className="col-md-6">
<TextField id="sign-up-login-name" label="Login name" hint="This is the name that will show on the website." autoComplete="username" value={values.login_name} onChange={set('login_name')} errors={on.login_name} required />
</div>
<div className="col-md-6">
<TextField id="sign-up-email" label="Email" type="email" hint="We'll use this address to contact you (we never spam!)" autoComplete="email" value={values.email} onChange={set('email')} errors={on.email} required />
</div>
</div>
<div className="row">
<div className="col-md-6">
<TextField id="sign-up-password" label="Password" type="password" autoComplete="new-password" value={values.password} onChange={set('password')} errors={on.password} required />
</div>
<div className="col-md-6">
<TextField id="sign-up-password-confirmation" label="Password confirmation" type="password" autoComplete="new-password" value={values.password_confirmation} onChange={set('password_confirmation')} errors={on.password_confirmation} required />
</div>
</div>
<div className="row align-items-center">
<div className="col-md-6">
<CheckField id="sign-up-tos" label={<>I agree to the <a href="/policy/tos" target="_blank" rel="noreferrer">Terms of Service</a></>} checked={values.tos_agreement} onChange={set('tos_agreement')} errors={on.tos_agreement} />
</div>
{recaptchaSiteKey ? <div className="col-md-6 mt-3 mt-md-0"><div ref={captcha.ref} /></div> : null}
</div>
{recaptchaSiteKey ? <p className="form-text fs-6 mt-3 mb-0">If you have accessibility issues with the captcha, please contact us via the links in the footer and we will help.</p> : null}
</div>
<AuthFooter busy={saving} label="Sign up" busyLabel="Joining…" prompt="Already a member?" switchLabel="Sign in" switchHref={urls.sign_in_page} onSwitch={onSwitch} />
</form>
</Modal>
);
}