import { useState, useEffect } from “react”;
// ============================================================================
// SHARED CONFIG
// ============================================================================
const C = { navy: “#1a1a2e”, navyLight: “#2d2d4a”, navyDark: “#0a0f1e”, red: “#cc0000”, gold: “#d4af37”, bronze: “#c9a23a”, cream: “#f5f0e1”, white: “#fff” };
// Blog tab gets its own hunter green palette
const B = { green: “#1b3a2d”, greenLight: “#264d3b”, greenDark: “#0f2218”, greenAccent: “#3d8b5e”, mint: “#a8e6c3”, lime: “#d4f5a0”, cream: “#f0f7e6”, white: “#fff”, gold: “#d4af37″ };
const INTERNAL_LINKS = {
home:”https://newjerseyangermanagementgroup.com/”,contact:”https://newjerseyangermanagementgroup.com/contact/”,services:”https://newjerseyangermanagementgroup.com/services/”,classes:”https://newjerseyangermanagementgroup.com/anger-management-classes/”,
bergen:”https://newjerseyangermanagementgroup.com/online-anger-management-classes-for-bergen-county-nj-certified-one-on-one-and-court-approved/”,bergenCounty:”https://newjerseyangermanagementgroup.com/bergen-county-new-jersey-court-accepted-anger-management/”,hudson:”https://newjerseyangermanagementgroup.com/court-approved-anger-management-in-hudson-bergen-union-counties-acomprehensive-guide-to-effective-strategies-and-local-resources/”,essex:”https://newjerseyangermanagementgroup.com/greater-essex-counseling-for-anger-management-in-new-jersey-or/”,monmouth:”https://newjerseyangermanagementgroup.com/8-hour-anger-management-course-for-monmouth-county-new-jersey/”,union:”https://newjerseyangermanagementgroup.com/%e2%ad%90-court-approved-anger-management-in-union-new-jersey/”,
jerseyCity:”https://newjerseyangermanagementgroup.com/anger-management-in-jersey-city-new-jersey/”,jerseyCityCourts:”https://newjerseyangermanagementgroup.com/anger-management-jersey-city-courts/”,fortLee:”https://newjerseyangermanagementgroup.com/court-approved-anger-management-classes-in-fort-lee-nj-live-virtual-one-on-one-sessions-for-court-or-self-improvement/”,hackensack:”https://newjerseyangermanagementgroup.com/hackensack-municipal-court-anger-management-215-state-st/”,newBrunswick:”https://newjerseyangermanagementgroup.com/new-brunswick-municipal-court-anger-management-court-approved-judge-wright/”,bergenfield:”https://newjerseyangermanagementgroup.com/bergenfield-municipal-court-anger-management-judge-montero-room-308/”,unionMunicipal:”https://newjerseyangermanagementgroup.com/just-left-the-union-municipal-court-and-need-to-enroll-in-anger-management/”,freehold:”https://newjerseyangermanagementgroup.com/freehold-new-jersey-anger-management-program/”,newBrunswickMunicipal:”https://newjerseyangermanagementgroup.com/when-you-need-anger-management-for-new-brunswick-municipal-court/”,ridgefieldPark:”https://newjerseyangermanagementgroup.com/small-charges-that-can-get-out-of-hand-without-anger-management-ridgefield-park-municipal-court/”,
importance:”https://newjerseyangermanagementgroup.com/why-anger-management-is-more-important-than-you-think-new-jersey/”,fro:”https://newjerseyangermanagementgroup.com/anger-management-and-dismissing-a-final-restraining-order-in-jersey-city-nj/”,hudsonCountyNeeded:”https://newjerseyangermanagementgroup.com/when-you-need-anger-management-and-want-a-better-outcome-in-your-case-court-ordered-anger-management-in-hudson-county-new-jersey/”,instigators:”https://newjerseyangermanagementgroup.com/everyone-has-a-plan-until-they-get-punched-in-the-mouth-the-new-jersey-anger-management-guide-to-dealing-with-instigators/”,didntStartIt:”https://newjerseyangermanagementgroup.com/i-didnt-start-it-but-im-the-one-stuck-wasting-my-time-in-court-new-jersey-anger-management-group/”,pricing:”https://newjerseyangermanagementgroup.com/299-2/”,
};
const LINKS_TEXT = Object.entries(INTERNAL_LINKS).map(([k,v])=>`${k}: ${v}`).join(“\n”);
const COUNTIES = [“Atlantic”,”Bergen”,”Burlington”,”Camden”,”Cape May”,”Cumberland”,”Essex”,”Gloucester”,”Hudson”,”Hunterdon”,”Mercer”,”Middlesex”,”Monmouth”,”Morris”,”Ocean”,”Passaic”,”Salem”,”Somerset”,”Sussex”,”Union”,”Warren”];
const NJ_TOWNS = {
Atlantic:[“Absecon”,”Atlantic City”,”Brigantine”,”Egg Harbor City”,”Egg Harbor Township”,”Galloway”,”Hamilton Township”,”Hammonton”,”Linwood”,”Margate City”,”Northfield”,”Pleasantville”,”Somers Point”,”Ventnor City”],
Bergen:[“Bergenfield”,”Bogota”,”Carlstadt”,”Cliffside Park”,”Closter”,”Dumont”,”East Rutherford”,”Edgewater”,”Elmwood Park”,”Englewood”,”Englewood Cliffs”,”Fair Lawn”,”Fairview”,”Fort Lee”,”Franklin Lakes”,”Garfield”,”Glen Rock”,”Hackensack”,”Hasbrouck Heights”,”Hillsdale”,”Leonia”,”Little Ferry”,”Lodi”,”Lyndhurst”,”Mahwah”,”Maywood”,”New Milford”,”North Arlington”,”Oradell”,”Palisades Park”,”Paramus”,”Park Ridge”,”Ramsey”,”Ridgefield”,”Ridgefield Park”,”Ridgewood”,”River Edge”,”Rochelle Park”,”Rutherford”,”Saddle Brook”,”Teaneck”,”Tenafly”,”Upper Saddle River”,”Waldwick”,”Wallington”,”Westwood”,”Wood-Ridge”,”Wyckoff”],
Burlington:[“Beverly”,”Bordentown”,”Burlington City”,”Burlington Township”,”Cinnaminson”,”Delran”,”Evesham”,”Florence”,”Maple Shade”,”Medford”,”Moorestown”,”Mount Holly”,”Mount Laurel”,”Pemberton”,”Willingboro”],
Camden:[“Audubon”,”Bellmawr”,”Camden”,”Cherry Hill”,”Collingswood”,”Gloucester City”,”Haddonfield”,”Haddon Township”,”Lindenwold”,”Pennsauken”,”Voorhees”,”Winslow”],
“Cape May”:[“Cape May”,”North Wildwood”,”Ocean City”,”Sea Isle City”,”Wildwood”],
Cumberland:[“Bridgeton”,”Millville”,”Vineland”],
Essex:[“Belleville”,”Bloomfield”,”Caldwell”,”East Orange”,”Irvington”,”Livingston”,”Maplewood”,”Millburn”,”Montclair”,”Newark”,”Nutley”,”Orange”,”South Orange”,”Verona”,”West Caldwell”,”West Orange”],
Gloucester:[“Clayton”,”Deptford”,”Glassboro”,”Monroe Township”,”Washington Township”,”Woodbury”],
Hudson:[“Bayonne”,”Guttenberg”,”Harrison”,”Hoboken”,”Jersey City”,”Kearny”,”North Bergen”,”Secaucus”,”Union City”,”Weehawken”,”West New York”],
Hunterdon:[“Clinton”,”Flemington”,”Lambertville”,”Raritan Township”,”Readington”],
Mercer:[“East Windsor”,”Ewing”,”Hamilton”,”Hightstown”,”Lawrence”,”Princeton”,”Robbinsville”,”Trenton”,”West Windsor”],
Middlesex:[“Carteret”,”East Brunswick”,”Edison”,”Highland Park”,”Metuchen”,”New Brunswick”,”North Brunswick”,”Old Bridge”,”Perth Amboy”,”Piscataway”,”Sayreville”,”South Brunswick”,”South Plainfield”,”Woodbridge”],
Monmouth:[“Asbury Park”,”Eatontown”,”Freehold Borough”,”Freehold Township”,”Hazlet”,”Holmdel”,”Howell”,”Keansburg”,”Long Branch”,”Manalapan”,”Marlboro”,”Matawan”,”Middletown”,”Neptune”,”Red Bank”,”Tinton Falls”,”Wall”],
Morris:[“Boonton”,”Chatham”,”Denville”,”Dover”,”East Hanover”,”Florham Park”,”Madison”,”Montville”,”Morris Township”,”Morristown”,”Mount Olive”,”Parsippany-Troy Hills”,”Randolph”,”Rockaway”,”Roxbury”],
Ocean:[“Barnegat”,”Berkeley”,”Brick”,”Jackson”,”Lacey”,”Lakewood”,”Manchester”,”Point Pleasant”,”Seaside Heights”,”Stafford”,”Toms River”],
Passaic:[“Clifton”,”Hawthorne”,”Little Falls”,”Passaic”,”Paterson”,”Pompton Lakes”,”Totowa”,”Wayne”,”Woodland Park”],
Salem:[“Carneys Point”,”Pennsville”,”Salem”,”Woodstown”],
Somerset:[“Bernards”,”Bernardsville”,”Bound Brook”,”Branchburg”,”Bridgewater”,”Franklin Township”,”Hillsborough”,”Manville”,”North Plainfield”,”Somerville”,”Warren”],
Sussex:[“Byram”,”Hopatcong”,”Newton”,”Sparta”,”Vernon”],
Union:[“Clark”,”Cranford”,”Elizabeth”,”Hillside”,”Kenilworth”,”Linden”,”Plainfield”,”Rahway”,”Roselle”,”Scotch Plains”,”Springfield”,”Summit”,”Union”,”Westfield”],
Warren:[“Hackettstown”,”Lopatcong”,”Phillipsburg”,”Washington”]
};
// Shared business/program prompt block
const BUSINESS_PROMPT = `BUSINESS INFO (use exactly): Name: New Jersey Anger Management Group | Phone: 201-205-3201 | Address: 121 Newark Avenue, Jersey City, NJ 07302 | Website: https://newjerseyangermanagementgroup.com | Founded: 2012 | Director: Santo V. Artusa Jr., Esq. — Rutgers Law Graduate | Geo: 40.7178,-74.0431
RULES: NEVER mention pricing/dollar amounts. NEVER mention copay amounts. Say “we accept most major insurance plans” and “many clients pay little to nothing out of pocket”. Include 2 fictional case studies (marked composite/illustrative). Write 3,000-5,000+ words. Deep local NJ details.
PROGRAM BENEFITS (no prices): Court-approved all 21 NJ counties. Rutgers Law graduate, 15+ yrs NJ legal experience. Hybrid: live-facilitated + online. 7 days/week. AM and BIP. Insurance accepted. English & Spanish. Certificates accepted all NJ courts. 2-52 session programs. Voluntary welcome. Virtual & in-person. Same-day enrollment letters. 100% completion guarantee. Private 1-on-1. SAMHSA-aligned.
INTERNAL LINKS (use 15-25): ${LINKS_TEXT}
OUTBOUND: 2-4 authority links (NJ Courts, SAMHSA, APA, NJ statutes, local .gov)
OUTPUT: ONLY clean WordPress HTML. No markdown, no code fences, no preamble/postamble. No conversational text like “I’ll search for…” or “Here’s the blog post:”. Start with < end with >. Ready for WordPress code editor.
CSS SYSTEM — unique prefix #njamg-[slug]:
.blog-body:max-width:820px,margin:0 auto,font-family:’Georgia’,serif | .hero-section:bg gradient(135deg,#1a1a2e,#2d2d4a),color:#fff,radius:16px,pad:45px | h1:#fff 32px 800 | h2:#1a1a2e 24px border-bottom:3px #cc0000 | h3:#cc0000 20px | p:#333 16.5px line-height:2 | Internal links:#cc0000 underline bold | External:#1a1a2e underline
Blocks: .pullquote(border-left:5px #cc0000) .info-box(#f8f9fa) .alert-box(#fff5f5 border #cc0000) .scenario-card(.card-label #cc0000) .strategy-block(gradient navy,white text) .court-card(#f8f9fa) .cta-section(gradient #cc0000,white,phone 36px 900) .faq-section(collapsible JS)
Badges: Court-Accepted(#1a1a2e/#fff) Insurance(#e8f5e9/#2e7d32) Guarantee(#fff8e1/#f57f17) Bilingual(#e3f2fd/#1565c0) SAMHSA(#f3e5f5/#7b1fa2) Private(#fafafa/#333)
REQUIRED: Hero, badges, 2 case studies, 3-4 techniques, legal perspective, insurance info-box, FAQ 8-12 collapsible, internal links footer, CTA with phone+address, disclaimer, JSON-LD (LocalBusiness+FAQPage+BlogPosting), FAQ toggle JS.`;
const BLOG_SYSTEM = `You are a blog content writer for NJAMG. Create SEO-optimized, empathetic blog posts based on news stories about violence/anger incidents.\n\n${BUSINESS_PROMPT}\n\nADDITIONAL BLOG SECTIONS: Introduction with outbound link to news source, escalation analysis (triggers, amygdala hijack, decision window), community impact section.\n\nOUT-OF-STATE STORIES: When a story occurred outside New Jersey, acknowledge the original location briefly, then pivot the entire post to how this type of incident plays out in New Jersey. Use NJ-specific statutes, courts, penalties, and community context. Frame it as “If this happened in [NJ County], here’s what would happen…” Reference specific NJ municipal courts and superior courts for the target county. The post should read as a NJ-focused resource that uses the out-of-state incident as the hook.\n\nPOLICE VIOLENCE CATEGORY: When the category is police violence/use of force, focus on anger management from BOTH perspectives — officers who need to manage authority-related stress, and civilians who face charges after encounters with police. Discuss de-escalation from both sides. Reference NJ Attorney General use-of-force policies, NJ Internal Affairs guidelines, and how anger management helps people avoid escalating encounters with law enforcement. Be balanced and empathetic to all parties.`;
const PAGE_SYSTEM = `You are a premium page writer for NJAMG. Create SEO-optimized localized WordPress pages with strong LOCAL angles for specific NJ towns.\n\n${BUSINESS_PROMPT}\n\nADDITIONAL PAGE SECTIONS: Town-by-town .town-card sections (court details, community context, localized CTA for each), escalation scale graphic (CSS 1-10), comparison table (Without AM vs With NJAMG), process timeline, stats callout boxes. Suggested title format: “[Topic] in [Towns], [County] County NJ”`;
const DEFAULT_TOPICS = [
{value:”general_am”,label:”Court-Approved Anger Management Classes”},{value:”road_rage”,label:”Road Rage Prevention & Anger Management”},{value:”domestic_violence”,label:”Domestic Violence & Batterers Intervention”},{value:”assault_charges”,label:”Anger Management After Assault Charges”},{value:”restraining_order”,label:”Anger Management for Restraining Orders (TRO/FRO)”},{value:”court_ordered”,label:”Court-Ordered Anger Management Programs”},{value:”one_on_one”,label:”Private One-on-One Anger Management Sessions”},{value:”online_classes”,label:”Online Anger Management Classes”},{value:”spanish”,label:”Anger Management in Spanish (Manejo de la Ira)”},{value:”youth”,label:”Youth & Teen Anger Management”},{value:”workplace”,label:”Workplace Anger Management”},{value:”bip”,label:”Batterers Intervention Program (BIP) — 52 Weeks”},{value:”dv_classes”,label:”Domestic Violence Classes for Court”},{value:”municipal_court”,label:”Municipal Court Anger Management”},{value:”superior_court”,label:”Superior Court Anger Management”},{value:”insurance_am”,label:”Insurance-Covered Anger Management”},{value:”voluntary”,label:”Voluntary Anger Management (Not Court-Ordered)”},{value:”couples”,label:”Anger Management for Couples & Relationships”},{value:”bar_fight”,label:”Anger Management After a Bar Fight”},{value:”disorderly_conduct”,label:”Anger Management for Disorderly Conduct”},{value:”harassment”,label:”Anger Management for Harassment Charges”},{value:”probation”,label:”Anger Management Required by Probation”},
];
const BLOG_CATEGORIES = [
{value:”general”,label:”General Assault / Violence”},{value:”road_rage”,label:”Road Rage”},{value:”domestic_violence”,label:”Domestic Violence”},{value:”bar_fight”,label:”Bar / Alcohol Altercation”},{value:”workplace”,label:”Workplace Violence”},{value:”youth”,label:”Youth / School”},{value:”neighbor”,label:”Neighbor / Community Dispute”},{value:”criminal”,label:”Criminal Charges / Arrest”},{value:”restraining_order”,label:”Restraining Order / TRO / FRO”},{value:”police_violence”,label:”Police Violence / Use of Force”},
];
// ============================================================================
// SHARED API CALLER
// ============================================================================
async function callAPI(system, userMsg, onProgress) {
const res = await fetch(“https://api.anthropic.com/v1/messages”, {
method:”POST”, headers:{“Content-Type”:”application/json”},
body: JSON.stringify({model:”claude-sonnet-4-20250514″,max_tokens:16000,system,tools:[{type:”web_search_20250305″,name:”web_search”}],messages:[{role:”user”,content:userMsg}]})
});
let data = await res.json();
if (data.error) throw new Error(data.error.message);
let history = [{role:”user”,content:userMsg}];
let iters = 0;
while (data.stop_reason === “tool_use” && iters < 6) {
iters++;
if (onProgress) onProgress(`Researching (${iters}/6)...`);
history.push({role:"assistant",content:data.content});
history.push({role:"user",content:data.content.filter(b=>b.type===”tool_use”).map(tu=>({type:”tool_result”,tool_use_id:tu.id,content:”Search completed.”}))});
const next = await fetch(“https://api.anthropic.com/v1/messages”,{method:”POST”,headers:{“Content-Type”:”application/json”},
body:JSON.stringify({model:”claude-sonnet-4-20250514″,max_tokens:16000,system,tools:[{type:”web_search_20250305″,name:”web_search”}],messages:history})});
data = await next.json();
if (data.error) throw new Error(data.error.message);
}
let finalText = [];
if (data.content) data.content.filter(b=>b.type===”text”&&b.text).forEach(b=>finalText.push(b.text));
let html = finalText.join(“”).replace(/“`html?\s*/gi,””).replace(/“`\s*/g,””).trim();
const s = html.indexOf(“<"); if (s>0) html=html.substring(s);
const e = html.lastIndexOf(“>”); if (e>0&&e]+>/g,” “).replace(/\s+/g,” “).trim().split(” “).filter(w=>w).length; }
// ============================================================================
// SHARED UI COMPONENTS
// ============================================================================
function OutputPanel({output,wordCount,copied,onCopy,extraInfo}) {
if (!output) return null;
return (
{extraInfo && (
{extraInfo.label}
{extraInfo.title}
{extraInfo.slug &&
Slug: /{extraInfo.slug}
}
)}
Generated HTML~{wordCount.toLocaleString()} words
{output}
);
}
// ============================================================================
// BLOG TAB
// ============================================================================
function BlogTab() {
const [input,setInput]=useState(“”); const [inputType,setInputType]=useState(“text”);
const [county,setCounty]=useState(“”); const [blogTowns,setBlogTowns]=useState([]);
const [category,setCategory]=useState(“general”);
const [outOfState,setOutOfState]=useState(false); const [njCounty,setNjCounty]=useState(“”); const [njTowns,setNjTowns]=useState([]);
const [loading,setLoading]=useState(false); const [output,setOutput]=useState(“”);
const [error,setError]=useState(“”); const [copied,setCopied]=useState(false);
const [wordCount,setWordCount]=useState(0); const [progress,setProgress]=useState(“”);
// Get available towns for whichever county is active
const activeCounty = outOfState ? njCounty : county;
const activeTowns = outOfState ? njTowns : blogTowns;
const setActiveTowns = outOfState ? setNjTowns : setBlogTowns;
const availTowns = NJ_TOWNS[activeCounty] || [];
const pickCounty = (val) => {
if (outOfState) { setNjCounty(val); setNjTowns([]); }
else { setCounty(val); setBlogTowns([]); }
};
const toggleTown = (t) => {
if (activeTowns.includes(t)) setActiveTowns(activeTowns.filter(x=>x!==t));
else if (activeTowns.length < 3) setActiveTowns([...activeTowns, t]);
};
const generate = async()=>{
setLoading(true);setError(“”);setOutput(“”);setCopied(false);setProgress(“Analyzing story…”);
const townText = activeTowns.length ? `\nTARGET TOWNS: ${activeTowns.join(“, “)} — reference these specific towns/cities in the post with local court details and community context.` : “”;
const outOfStateNote = outOfState ? `\n\nIMPORTANT — OUT-OF-STATE STORY: This incident occurred OUTSIDE New Jersey. Acknowledge the original location briefly, then PIVOT the entire post to New Jersey. Apply all analysis as if this incident occurred in ${njCounty?njCounty+” County, NJ”:”New Jersey”}${njTowns.length?”, specifically in/near “+njTowns.join(“, “):””} . Use NJ statutes, NJ courts, NJ penalties, NJ community context. Frame it as “What if this happened here in New Jersey?” The post must be a NJ-focused resource.` : “”;
const msg=`Generate a comprehensive NJAMG blog post.\n\nCATEGORY: ${BLOG_CATEGORIES.find(c=>c.value===category)?.label}\nCOUNTY: ${outOfState?(njCounty?njCounty+” County, NJ”:”A relevant NJ county”):(county?county+” County, NJ”:”Determine from story”)}${townText}\nINPUT: ${inputType===”url”?”URL — search and summarize, link as outbound”:”Pasted text”}${outOfStateNote}\n\nSTORY:\n${input}\n\nRequirements: 3,000-5,000+ words. 2 case studies. 8-12 FAQ. JSON-LD. 15-25 internal links. Outbound to source + authorities. Phone 201-205-3201. Address 121 Newark Ave JC NJ 07302. NO pricing/copays. ONLY HTML.`;
try {
const html = await callAPI(BLOG_SYSTEM,msg,setProgress);
if(!html){setError(“No content generated. Try pasting full text.”);setLoading(false);return;}
setOutput(html); setWordCount(countWords(html));
} catch(err){setError(err.message);}
setLoading(false);setProgress(“”);
};
const copy=async()=>{try{await navigator.clipboard.writeText(output)}catch{const t=document.createElement(“textarea”);t.value=output;document.body.appendChild(t);t.select();document.execCommand(“copy”);document.body.removeChild(t)}setCopied(true);setTimeout(()=>setCopied(false),3000)};
return (
{/* Input type toggle */}
{[[“text”,”📋 Paste Story Text”],[“url”,”🔗 Paste URL”]].map(([t,l])=>(
))}
{/* Story input */}
{error&&
⚠️ {error}
}
{/* Blog output panel — green themed */}
{output && (
Generated Blog Post~{wordCount.toLocaleString()} words
{outOfState&&NJ Localized}
Leave a Reply