Find My Rep
A WordPress plugin that will search for your local representative and provide a templated letter to send via email service
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/bones5/find-my-rep-plugin/archive/refs/heads/main.zipA WordPress plugin that creates a Gutenberg block for contacting local representatives via templated letters sent through Resend.
Features
- Gutenberg Block: Easy-to-use block that can be inserted on any page
- Representative Lookup: Search for local representatives by postcode via API
- Multiple Representative Types: Supports MPs, MSs, local councillors, and PCCs
- Customizable Campaigns: Per-block recipients, an optional question, and an editable letter template
- Multi-recipient: Editors choose which representative types each block contacts
- Email Integration: Sends letters via Resend API
- Abuse Protection: Blocks abusive language, excessive link spam, and repeated rapid submissions
- Sender Confirmation: Requires users to confirm they are personally sending the message
Installation
- Upload the plugin folder to
/wp-content/plugins/find-my-rep-plugin/ - Activate the plugin through the 'Plugins' menu in WordPress
- Configure the plugin settings under Settings > Find My Rep
Configuration
Admin Settings
Navigate to Settings > Find My Rep to configure:
-
Representatives API URL: Enter the API endpoint that returns representative data based on postcode
- The API should accept a
postcodequery parameter - Should return JSON with representative details including:
name,email,titleortype
- The API should accept a
-
Resend API Key: Enter your Resend API key for sending emails
-
Letter Template: Define the default letter template
- Use
{{representative_name}}as a placeholder for the representative's name - Use
{{representative_title}}as a placeholder for the representative's title
- Use
- Use
{{question_response}}wherever the optional visitor response should appear
- Test Postcode: Configure a postcode handled entirely by the plugin without calling the representatives API
- Set the postcode to use for testing, such as
ZZ999ZZ - Enter separate email addresses for the generated test MP, MS, PCC, and councillor
- Each populated email creates one representative of that type with the required fields filled with test information
- Leave a representative email blank to omit that type from the test results
Example Letter Template
Dear {{representative_name}},
I am writing to you as your constituent regarding [issue].
[Your message here]
I would appreciate your support on this matter.
Sincerely,
[User's name will be added automatically]
Usage
Adding the Block
- Edit any page or post
- Click the '+' button to add a new block
- Search for "Find My Rep Contact Form"
- Insert the block
- In the block, choose the representative types to contact, optionally add a required question prompt, and customize the letter template
User Experience
- Enter Postcode: Users enter their postcode to find representatives
- Answer Question: Optionally answer the campaign question when the editor has enabled one
- Edit Letter: Review and customize the letter template
- Send: Submit to all representative types configured for the block
Development
Technology Stack
- TypeScript: Type-safe development with full type definitions
- React: Modern component-based UI framework
- WordPress Blocks API: Gutenberg block integration
- @wordpress/scripts: Build tooling and linting
Requirements
- Node.js and npm
- WordPress 5.0+
- PHP 7.0+
- Docker (for running wp-env)
Building from Source
The plugin is written in TypeScript and React. To build:
npm install
npm run build
This compiles TypeScript/React code to JavaScript bundles in the build/ directory.
Creating a Plugin Release
package.json is the canonical plugin version. To increment it, synchronize the
WordPress plugin header and constant, build the assets, and create an installable
ZIP in one command:
npm run release -- patch
Use minor, major, or an exact version such as 1.2.3 instead of patch when
needed. The command updates package.json and package-lock.json, then creates
dist/find-my-rep-plugin-<version>.zip. It does not create a Git commit or tag.
To rebuild a ZIP without changing the version:
npm run package
Upload the ZIP through Plugins > Add Plugin > Upload Plugin in WordPress and confirm replacing the existing plugin. Review and commit the version changes, then create a matching Git tag after testing the ZIP.
Development Mode
For hot-reloading during development:
npm start
Code Architecture
The plugin follows a modern React/TypeScript architecture:
Source Files (src/):
index.tsx- Gutenberg block registration (editor view)frontend.tsx- Frontend React app initializationtypes.ts- TypeScript type definitionscomponents/- React components for the frontend UIFindMyRepApp.tsx- Main app container with state managementPostcodeStep.tsx- Postcode input stepQuestionStep.tsx- Optional campaign question stepLetterStep.tsx- Letter editing and submission stepLoadingSpinner.tsx- Loading indicator component
Build Output (build/):
index.tsx.js- Compiled block editor scriptfrontend.tsx.js- Compiled frontend React app*.asset.php- WordPress asset files with dependenciesstyle.css- Plugin styles
Linting and Formatting
Run linting checks:
npm run lint:js
Auto-fix linting issues:
npm run lint:js -- --fix
Email Transport Modes
The plugin supports multiple email transport modes for development and production:
- Resend API (default for production): Uses the Resend API to send emails
- SMTP (wp_mail): Uses WordPress's built-in wp_mail() function (useful with MailHog for local development)
- Test (Log to File): Logs emails to a file for testing without sending (stored in
wp-content/uploads/find-my-rep-test-mails.log)
Configure the transport mode in Settings > Find My Rep > Email Transport.
For local development with MailHog, see docs/MAILHOG-DEV.md.
Testing
The plugin includes three types of tests:
PHPUnit Tests
Run PHP unit tests for the email service:
npm run test:phpunit
# or directly: vendor/bin/phpunit
Tests include:
- Template rendering and placeholder replacement
- Email transport behavior (resend, smtp, test)
- Error handling and edge cases
Jest Tests
Run React component tests:
npm run test:jest
Tests include:
- LetterStep component validation
- User interaction flows
- Button states and loading indicators
E2E Tests (Playwright)
Run end-to-end tests using wp-env and Playwright.
Setup
- Install dependencies:
npm install
- Install Playwright browsers:
npx playwright install
Running Tests
- Start the WordPress environment:
npm run env:start
- Run the tests:
npm run test:e2e
- Run tests in headed mode (see browser):
npm run test:e2e:headed
- Debug tests:
npm run test:e2e:debug
- Stop the WordPress environment when done:
npm run env:stop
Other wp-env Commands
- Clean all data:
npm run env:clean - Access WordPress at:
http://localhost:8888 - Admin credentials:
admin/password
API Response Format
The Representatives API should return data in the following format:
{
"postcode": "CF10 1EP",
"councillors": [
{
"id": 1,
"name": "John Smith",
"party": "Conservative",
"ward": "St James's",
"council": "Westminster City Council",
"email": "john.smith@westminster.gov.uk"
}
],
"pcc": {
"id": 1,
"name": "Sir Mark Rowley",
"force": "Metropolitan Police",
"area": "Greater London",
"email": "mopac@london.gov.uk"
},
"mp": {
"id": 1,
"name": "Nickie Aiken",
"party": "Conservative",
"constituency": "Cities of London and Westminster",
"email": "nickie.aiken.mp@parliament.uk"
},
"mss": [
{
"id": 1,
"name": "Joel James",
"party": "Conservative",
"constituency": "Cardiff Central",
"email": "joel.james@senedd.wales"
}
],
"areaInfo": {
"constituency": {
"id": 65659,
"name": "Cities of London and Westminster",
"code": "E14000639"
},
"localAuthority": {
"id": 2247,
"name": "Westminster",
"type": "London borough",
"code": "E09000033"
},
"ward": {
"id": 144393,
"name": "St James's",
"type": "London borough ward",
"code": "E05000644"
},
"region": {
"id": 2247,
"name": "London",
"type": "Government Office Region",
"code": "E12000007"
}
}
}
The plugin automatically transforms this response into an internal format for display. Geographic information is extracted from areaInfo and displayed to users before the list of representatives. See API-INTEGRATION.md for detailed documentation.
License
GPL v2 or later