// wp-config.jsx — WordPress REST API integration for RoCo Arts Council // // ─── How to connect to WordPress ───────────────────────────────────── // // 1. Install WordPress at your chosen URL // (e.g. https://admin.rocoarts.org or a subdirectory of your main domain) // // 2. Install these plugins: // • Advanced Custom Fields (ACF) — for custom fields on artists & events // • Custom Post Type UI — to register roco_artist and roco_event, OR // add register_post_type() calls to your theme's functions.php // • WP REST API Controller (optional) — fine-tunes REST API exposure // • WPForms / Contact Form 7 — for the contact form and newsletter // • Push Notifications for WordPress (Xtreme One) or OneSignal — // for browser push notifications // // 3. Register custom post types. In functions.php (or a plugin): // // add_action('init', function() { // register_post_type('artist', [ // 'labels' => ['name' => 'Artists', 'singular_name' => 'Artist'], // 'public' => true, // 'show_in_rest' => true, // required for REST API // 'rest_base' => 'artist', // 'supports' => ['title', 'editor', 'thumbnail', 'custom-fields'], // 'has_archive' => true, // ]); // register_post_type('event', [ // 'labels' => ['name' => 'Events', 'singular_name' => 'Event'], // 'public' => true, // 'show_in_rest' => true, // 'rest_base' => 'event', // 'supports' => ['title', 'editor', 'thumbnail', 'custom-fields'], // 'has_archive' => true, // ]); // }); // // 4. Define ACF field groups: // // roco_artist fields: // pronouns (text), location (text — town name), // medium (repeater or checkbox), // member_since (number — year), // is_student (true/false), student_school (text), // student_advisor (text), student_grade (text), // student_graduation_year (number), // student_senior_project (text), // after_graduation (text), // open_studios_participant (true/false), // commissions_open (true/false), // quilt_wall_artist (true/false), // new_this_year (true/false), // studio_address (text), studio_hours (text), // commission_note (textarea), // portrait_url (image — returns URL), // gallery_images (gallery), // website (url), // primary_cta_label (text), primary_cta_url (url) // // roco_event fields: // event_type (select: class|concert|exhibition|open_studios|workshop), // start_date (date picker — YYYY-MM-DD), // end_date (date picker), start_time (time), end_time (time), // location (text), address (text), // cost (text — e.g. "Free", "$12 · $6 students"), // rsvp_url (url), suggested_donation (text), // is_recurring (true/false), // stream (select: events|council|artists|news), // featured_artists (relationship → roco_artist), // photo_url (image) // // Standard posts (news/stories) — additional ACF fields: // post_type_variant (select: story|announcement|open-call|recap|press), // summary (text — the dek / short description), // closes_date (date picker — for open calls), // sidebar_cta_label (text), sidebar_cta_url (url), // related_links (repeater: label + url) // // 5. Set ROCO_WP.baseUrl to your WordPress URL and set useMockData: false // // 6. For the newsletter, set ROCO_WP.newsletterUrl to your form endpoint. // Works with Mailchimp, ConvertKit, WPForms webhook, etc. const ROCO_WP = { // ← Your WordPress site URL (no trailing slash) baseUrl: '', // ← Set to false when WordPress is live and custom post types are registered useMockData: true, // ← Newsletter signup endpoint (Mailchimp, ConvertKit, WPForms webhook, etc.) newsletterUrl: '', }; // ─── Core fetch helper ──────────────────────────────────────────── async function wpFetch(path, params = {}) { if (ROCO_WP.useMockData || !ROCO_WP.baseUrl) { return wpMockFetch(path, params); } const url = new URL(ROCO_WP.baseUrl + '/wp-json/wp/v2' + path); Object.entries(params).forEach(([k, v]) => { if (v != null) url.searchParams.set(k, String(v)); }); const res = await fetch(url.toString(), { headers: { Accept: 'application/json' } }); if (!res.ok) throw new Error(`WP API ${res.status}: ${url}`); const data = await res.json(); // Attach pagination totals from response headers if (Array.isArray(data)) { data._total = parseInt(res.headers.get('X-WP-Total') || '0', 10); data._totalPages = parseInt(res.headers.get('X-WP-TotalPages') || '0', 10); } return data; } // ─── Mock data bridge ───────────────────────────────────────────── // Falls back to the sample data defined in artists-data.jsx / events-data.jsx. // When you add real content to WordPress, set useMockData: false above. function wpMockFetch(path, params) { if (path.startsWith('/artist')) { const all = window.ARTISTS || []; if (params.slug) return Promise.resolve(all.filter(a => a.slug === params.slug)); if (params.search) { const q = params.search.toLowerCase(); return Promise.resolve(all.filter(a => a.name.toLowerCase().includes(q))); } if (params.medium) return Promise.resolve(all.filter(a => (a.mediums || []).includes(params.medium))); if (params.town) return Promise.resolve(all.filter(a => a.town === params.town)); return Promise.resolve(all); } if (path.startsWith('/event')) { const all = window.EVENTS || []; if (params.slug) return Promise.resolve(all.filter(e => e.slug === params.slug)); if (params.type) return Promise.resolve(all.filter(e => e.kind === params.type)); return Promise.resolve(all); } if (path.startsWith('/posts')) { const all = window.ROCO_NEWS_POSTS || []; if (params.slug) return Promise.resolve(all.filter(p => p.slug === params.slug)); if (params.categories) return Promise.resolve(all.filter(p => p.category === params.categories)); return Promise.resolve(all); } return Promise.resolve([]); } // ─── Public API ─────────────────────────────────────────────────── const API = { // Artists artists: (params = {}) => wpFetch('/artist', { per_page: 100, ...params }), artist: (slug) => wpFetch('/artist', { slug }), // Events events: (params = {}) => wpFetch('/event', { per_page: 50, orderby: 'meta_value', meta_key: 'start_date', order: 'asc', ...params, }), event: (slug) => wpFetch('/event', { slug }), // News / posts posts: (params = {}) => wpFetch('/posts', { per_page: 20, ...params }), post: (slug) => wpFetch('/posts', { slug }), // Newsletter signup subscribeNewsletter: async (email) => { if (!ROCO_WP.newsletterUrl) { // Development mock — pretend it succeeded await new Promise(r => setTimeout(r, 700)); return { success: true }; } const res = await fetch(ROCO_WP.newsletterUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }), }); return res.json(); }, // RSVP / contact form (submits to WordPress via WPForms or CF7) rsvp: async (eventId, data) => { if (ROCO_WP.useMockData) { await new Promise(r => setTimeout(r, 600)); return { success: true }; } const url = ROCO_WP.baseUrl + '/wp-json/contact-form-7/v1/contact-forms/rsvp/feedback'; const body = new FormData(); Object.entries({ event_id: eventId, ...data }).forEach(([k, v]) => body.append(k, v)); const res = await fetch(url, { method: 'POST', body }); return res.json(); }, }; Object.assign(window, { ROCO_WP, API, wpFetch });