WP Manifestindependent plugin directory
manifest / utilities / rh-cover-letter-generator

RH - Cover Letter Generator

A cover letter generator for Ronald Huereca

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/ronalfy/rh-cover-letter-generator/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

A WordPress plugin that provides an interactive cover letter generator with JSX and Webpack. Users answer tone-specific questions (formal, casual, awkward) and see a live preview they can copy.

https://github.com/user-attachments/assets/b76ffe5b-cb62-4ddd-a44b-0356088491b1


1. Project Description

This app was initially AI-coded, with the answers written by Ronald Huereca.

  • Frontend: React app (tone tabs, question forms, live preview) built with Webpack, Tanstack Router, and @wordpress/data.
  • Content: Questions and preview copy are driven by a single JSON config (src/js/data/questions.json) so you can change prompts and templates without editing PHP or JSX.
  • Features: Multiple tones, conditional preview sections, inline conditionals in templates (e.g. “show company name when provided, otherwise ‘your company’”), and copy-to-clipboard for the generated text.

2. Installation & Shortcode Usage

WordPress installation

  1. Install the plugin (copy the plugin folder into wp-content/plugins/ or install via ZIP).
  2. Activate RH - Cover Letter Generator in the WordPress admin (Plugins screen).
  3. (Optional) Run the build step so the app bundle exists — see Setup & build below.

Shortcode

The generator is rendered by a shortcode. Scripts and styles are only enqueued on pages that contain it.

Shortcode: rh_cover_letter_generator

Example:

  • [rh_cover_letter_generator]

The shortcode outputs a single div: <div id="rh-cover-letter-generator-app"></div>. The React app mounts there and handles the rest (tabs, forms, preview, copy).


3. Setup & Build (CLI)

You need Node.js and npm to build the JS and CSS.

One-time setup

cd wp-content/plugins/rh-cover-letter-generator
npm install

Build commands

Command Description
npm run build Production build. Outputs dist/js/app.js, dist/js/app.asset.php, and dist/css/app.css.
npm run dev or npm start Development build with watch; recompiles on file changes.

After the first npm run build, the plugin will work in WordPress. Re-run npm run build whenever you change anything under src/ (JS, JSX, or CSS).

Grunt (distribution zip)

Grunt is used to package the plugin for distribution. Run a production build first, then:

npx grunt

or, with Grunt installed globally, grunt. The default task creates rh-cover-letter-generator.zip in the plugin root, containing the main plugin file, includes/, dist/, and vendor/. You can upload that zip in the WordPress admin (Plugins → Add New → Upload Plugin) or use it for deployment.


4. Question Schema & Instructions

All questions and preview content live in one file:

src/js/data/questions.json

The root is keyed by tone (e.g. formal, casual, awkward). You can add or rename tones. Each tone object has:

Property Required Description
tone Yes Same as the key (e.g. "formal").
label Yes Tab label (e.g. "Formal").
description Yes Short description under the tab.
questions Yes Array of question objects.
previewTemplate Yes Object with a sections array (see below).

Example root:

{
  "formal": { "tone": "formal", "label": "Formal", "description": "...", "questions": [], "previewTemplate": { "sections": [] } },
  "casual": { ... }
}

Question object

Each item in questions must have:

Property Required Description
id Yes Unique ID within this tone. Used in form state and in templates as {id}.
label Yes Label shown above the field.
type Yes "text" or "choice".
placeholder For text Placeholder for the input.
options For choice Array of { "value": "...", "label": "..." }. value is stored and used in templates (use snake_case). label is shown to the user.
validation Yes Object. Use { "required": true } or { "required": false }.

Text question example:

{
  "id": "company",
  "label": "Company Name",
  "type": "text",
  "placeholder": "Acme Corp",
  "validation": { "required": false }
}

Choice question example:

{
  "id": "position",
  "label": "What type of position?",
  "type": "choice",
  "options": [
    { "value": "engineer", "label": "Engineer" },
    { "value": "software_developer", "label": "Software Developer" }
  ],
  "validation": { "required": true }
}

Preview template and sections

previewTemplate has one property:

  • sections (array): List of section objects. They are evaluated in order; all sections that pass their dependencies are concatenated to form the preview text.

Each section is either static or dynamic.

Static section

Always included. No variables.

Property Required Description
type Yes "static"
content Yes String. Use \n\n for paragraph breaks.
{
  "type": "static",
  "content": "Thank you for reading my application.\n\n"
}

Dynamic section

Included only when all of its dependencies are satisfied. Supports placeholders and inline conditions in the template.

Property Required Description
type Yes "dynamic"
template Yes String with {questionId} placeholders and/or inline conditions (see below).
dependencies Yes Array of dependency objects.

Dependency object:

Property Required Description
questionId Yes Question whose answer is used in the template and/or evaluated by rules.
defaultValue No Used when the dependency fails validation (optional).
rules No Array of rule objects. If any rule fails, the entire section is skipped.

Rule object — one of:

Rule Description Example
not_empty Answer must be non-empty. { "type": "not_empty" }
empty Answer must be empty. { "type": "empty" }
equals Answer must equal value. { "type": "equals", "value": "startup" }
not_equals Answer must not equal value. { "type": "not_equals", "value": "agency" }

Example dynamic section:

{
  "type": "dynamic",
  "template": "For {company}, this role clearly values {priority}.\n\nThe experience below reflects a steady approach.\n\n",
  "dependencies": [
    {
      "questionId": "company",
      "rules": [ { "type": "not_empty" } ]
    },
    {
      "questionId": "priority",
      "rules": [ { "type": "not_empty" } ]
    }
  ]
}

If either company or priority is empty, this section is skipped. Otherwise {company} and {priority} are replaced by the user’s answers (for choice questions, the value is used, not the label).


Template placeholders

In a dynamic section’s template:

  • {questionId} — Replaced by that question’s answer. Must match an id in questions. For choice questions, the option’s value is used (e.g. engineer), not the label. Case-sensitive.

Inline conditions

Inside a template you can conditionally show one value or a fallback using:

Format:
{condition:questionId:ruleType:ruleValue? trueValue|falseValue}

  • questionId — Which question to evaluate.
  • ruleType — not_empty, empty, equals, or not_equals.
  • ruleValue — Only for equals / not_equals (e.g. engineer). Omit for not_empty / empty.
  • trueValue — Used when the condition passes. Can be:
    • A question ID → replaced by that question’s answer (or its label for choice questions).
    • Literal text (e.g. your company).
    • Empty → nothing inserted (leave a space before the pipe).
  • falseValue — Literal text when the condition fails. No question ID resolution.

There must be a space after ruleType (or ruleValue) before trueValue. The two alternatives are separated by a pipe |.

Examples:

Inline condition Meaning
{condition:company:not_empty company|your company} If company not empty → company answer; else → “your company”.
{condition:position:equals:engineer company|your team} If position is “engineer” → company answer; else → “your team”.
{condition:company:not_empty »|your team} (use a space where » is) If company not empty → nothing; else → “your team”. (Empty trueValue = space before the pipe.)

Full example in a section:

{
  "type": "dynamic",
  "template": "I have worked at several startups. I can adapt to help {condition:company:not_empty company|your company} succeed.\n\n",
  "dependencies": [
    {
      "questionId": "company_type",
      "rules": [ { "type": "equals", "value": "startup" } ]
    }
  ]
}

This section only appears when company_type equals startup. The sentence uses the company name when provided, otherwise “your company”.