diff --git a/.gitignore b/.gitignore index 3cd4ffe..5e35be5 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ npm-debug.log yarn-error.log testem.log /typings +apps/wordpress-plugin/fcc-donation/assets/ # System Files .DS_Store diff --git a/apps/frontend/project.json b/apps/frontend/project.json index 40241a1..fc27e01 100644 --- a/apps/frontend/project.json +++ b/apps/frontend/project.json @@ -75,6 +75,14 @@ "buildTarget": "frontend:build" } }, + "build-embed": { + "executor": "@nx/vite:build", + "outputs": ["{workspaceRoot}/apps/wordpress-plugin/fcc-donation/assets"], + "options": { + "outputPath": "apps/wordpress-plugin/fcc-donation/assets", + "configFile": "apps/frontend/vite.embed.config.mts" + } + }, "typecheck": { "executor": "nx:run-commands", "options": { diff --git a/apps/frontend/src/components/testimonials/testimonials.css b/apps/frontend/src/components/testimonials/testimonials.css index e7578e5..c2f2821 100644 --- a/apps/frontend/src/components/testimonials/testimonials.css +++ b/apps/frontend/src/components/testimonials/testimonials.css @@ -1,6 +1,7 @@ /* Outer card */ .testimonial-carousel { - width: 1200px; + width: 100%; + max-width: 1200px; height: 395px; border-radius: 10px; overflow: hidden; @@ -67,8 +68,8 @@ font-size: 20px; font-weight: 500; line-height: 100%; - color: #ffffff; - text-decoration-line: underline; + color: #ffffff !important; + text-decoration-line: underline !important; text-decoration-style: solid; text-decoration-skip-ink: auto; text-decoration-color: rgba(255, 255, 255, 0.58); diff --git a/apps/frontend/src/containers/donations/donations.css b/apps/frontend/src/containers/donations/donations.css index be5f6e2..5e7c55b 100644 --- a/apps/frontend/src/containers/donations/donations.css +++ b/apps/frontend/src/containers/donations/donations.css @@ -13,6 +13,14 @@ text-align: center; } +#fcc-donation-embed input#amount { + padding-left: 1.5rem !important; +} + +#fcc-donation-embed button { + min-width: 0 !important; +} + .form-group { display: flex; flex-direction: column; @@ -328,7 +336,7 @@ width: 31%; aspect-ratio: 14 / 1; border-radius: 10px; - background: #3D3E6E; + background: #3d3e6e; } .step2-container { diff --git a/apps/frontend/src/containers/donations/steps/DonationAmount.tsx b/apps/frontend/src/containers/donations/steps/DonationAmount.tsx index d4046ab..d06e162 100644 --- a/apps/frontend/src/containers/donations/steps/DonationAmount.tsx +++ b/apps/frontend/src/containers/donations/steps/DonationAmount.tsx @@ -71,7 +71,7 @@ export const DonationAmount = ({ onChange={onChange} onBlur={onAmountBlur} className={cn( - 'pl-7 pr-12 h-10 text-base font-normal border-[#4E4E4E] border-[1.5px] shadow-none focus-visible:ring-0 rounded-lg', + 'pl-8 pr-12 h-10 text-base font-normal border-[#4E4E4E] border-[1.5px] shadow-none focus-visible:ring-0 rounded-lg', error ? 'border-[#d93025] bg-[#fff6f6]' : '', )} disabled={isSubmitting} diff --git a/apps/frontend/src/containers/donations/steps/DonationRecurrence.tsx b/apps/frontend/src/containers/donations/steps/DonationRecurrence.tsx index 507c40a..441a8b9 100644 --- a/apps/frontend/src/containers/donations/steps/DonationRecurrence.tsx +++ b/apps/frontend/src/containers/donations/steps/DonationRecurrence.tsx @@ -33,7 +33,7 @@ export const DonationRecurrence = ({ key={option.label} type="button" className={cn( - 'flex-1 h-12 px-4 whitespace-nowrap rounded-lg border-[1.5px] text-base cursor-pointer transition-colors duration-150 ease-in-out font-semibold disabled:opacity-60 disabled:cursor-not-allowed', + 'flex-1 h-12 px-4 rounded-lg border-[1.5px] text-base cursor-pointer transition-colors duration-150 ease-in-out font-semibold disabled:opacity-60 disabled:cursor-not-allowed', isRecurrenceSelected(option) ? 'bg-[#007b64] text-white border-[#007b64]' : 'bg-white text-black border-[#4E4E4E]', diff --git a/apps/frontend/src/containers/root.css b/apps/frontend/src/containers/root.css index 6785bb3..581e1fd 100644 --- a/apps/frontend/src/containers/root.css +++ b/apps/frontend/src/containers/root.css @@ -3,7 +3,7 @@ display: flex; justify-content: center; background: #ffffff; - padding: 3rem 1rem 4rem; + padding: 3rem 1rem 6rem; } .root-container { diff --git a/apps/frontend/src/embed.tsx b/apps/frontend/src/embed.tsx new file mode 100644 index 0000000..6d24909 --- /dev/null +++ b/apps/frontend/src/embed.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { MemoryRouter } from 'react-router-dom'; +import { DonationForm } from './containers/donations/DonationForm'; +import { + GrowingGoal, + type SampleDonation, +} from '@components/GrowingGoal/GrowingGoal'; +import { TestimonialCarousel } from '@components/testimonials/TestimonialCarousel'; +import { useActiveGoal } from './hooks/useActiveGoal'; +import './containers/root.css'; +import './styles.css'; + +const SAMPLE_DONATION: SampleDonation = { + name: 'C4C', + amount: 500, + profile: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAALElEQVR42mNgGAWjgLGB4T8DGjBgFiMDw38GphCjEopFY1GNRg0Y1GgAAAD9YB5WfVii1AAAAABJRU5ErkJggg==', +}; + +const EmbedApp: React.FC = () => { + const { data } = useActiveGoal(); + const donationTotal = data?.amountRaised ?? 3000; + const targetGoal = data?.goal?.targetAmount ?? 10000; + const label = data?.goal?.title || 'Grow your community with FCC'; + + return ( +
+
+
+ +
+ +
+
+ +
+ +
+ console.log('[FCC Donation] Submitted:', id)} + onError={(err) => console.error('[FCC Donation] Error:', err)} + /> +
+
+
+
+ ); +}; + +const mountId = 'fcc-donation-embed'; +const container = document.getElementById(mountId); + +if (container) { + ReactDOM.createRoot(container).render( + + + + + , + ); +} else { + console.warn(`[FCC Donation] Mount element #${mountId} not found.`); +} diff --git a/apps/frontend/vite.embed.config.mts b/apps/frontend/vite.embed.config.mts new file mode 100644 index 0000000..589d3b9 --- /dev/null +++ b/apps/frontend/vite.embed.config.mts @@ -0,0 +1,44 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import path from 'path'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/frontend-embed', + + plugins: [react(), nxViteTsPaths()], + publicDir: false, + + define: { + // Allow WordPress pages to set window.__FCC_DONATION_API_URL__ before loading + // the script to point at the backend (e.g. 'https://api.example.com'). + // Falls back to same-origin via the ?? '' in apiClient.ts if not set. + 'import.meta.env.VITE_API_BASE_URL': 'window.__FCC_DONATION_API_URL__', + // Required in IIFE lib mode — React and other deps reference this Node global. + 'process.env.NODE_ENV': JSON.stringify('production'), + }, + + build: { + lib: { + entry: path.resolve(__dirname, 'src/embed.tsx'), + name: 'FCCDonation', + fileName: 'fcc-donation', + formats: ['iife'], + }, + outDir: '../../apps/wordpress-plugin/fcc-donation/assets', + emptyOutDir: true, + }, + + resolve: { + alias: { + '@api': path.resolve(__dirname, './src/api'), + '@components': path.resolve(__dirname, './src/components'), + '@containers': path.resolve(__dirname, './src/containers'), + '@lib': path.resolve(__dirname, './src/lib'), + '@public': path.resolve(__dirname, './public'), + '@shared': path.resolve(__dirname, '../../shared'), + '@utils': path.resolve(__dirname, './src/utils'), + }, + }, +}); diff --git a/apps/wordpress-plugin/fcc-donation/README.md b/apps/wordpress-plugin/fcc-donation/README.md new file mode 100644 index 0000000..7925ca0 --- /dev/null +++ b/apps/wordpress-plugin/fcc-donation/README.md @@ -0,0 +1,52 @@ +# FCC Donation Embed — WordPress Plugin + +Embeds the FCC donation form and growing goal widget into any WordPress page via a shortcode. + +## How the shortcode works + +Add `[fcc_donation]` to any WordPress page or post body. + +When the page renders, WordPress replaces the shortcode with a `
` mount point and automatically enqueues the JS and CSS — only on pages where the shortcode is present, not sitewide. + +### What gets rendered + +The shortcode renders the same layout as the main app's home page: +- "Make a Difference" banner at the top +- Growing goal widget and donation form side by side below + +The growing goal data (amount raised, target, title) is fetched live from the backend's `/api/donations/goal/active` endpoint on page load. + +--- + +## Where assets live + +``` +apps/wordpress-plugin/fcc-donation/ +├── fcc-donation.php # Plugin registration and shortcode handler +├── README.md # This file +└── assets/ # Built output — regenerated by nx build-embed frontend + ├── fcc-donation.iife.js + └── fcc-donation.css +``` + +The `assets/` folder is excluded from git (see `.gitignore`) because it is always regenerated from source. The PHP file is the only thing in this directory that is version-controlled. + +--- + +## How to update the build + +After making changes to any frontend code (donation form, growing goal, styles, etc.), rebuild the embed bundle from the repo root: + +```bash +nx build-embed frontend +``` + +This outputs updated files to `apps/wordpress-plugin/fcc-donation/assets/`. Copy the new file contents into WordPress using the Plugin File Editor: + +1. In the WordPress admin, go to **Plugins → Plugin File Editor**. +2. In the top-right dropdown labeled **"Select plugin to edit:"**, choose **FCC Donation Embed** and click **Select**. +3. Expand the `assets/` folder in the file tree. +4. Click **fcc-donation.css** and replace its contents with those of the newly-generated `fcc-donation.css`. +5. Click **fcc-donation.iife.js** and replace its contents with those of the newly-generated `fcc-donation.iife.js`. + +No changes to the PHP file or WordPress plugin settings are needed unless the shortcode interface itself changes. \ No newline at end of file diff --git a/apps/wordpress-plugin/fcc-donation/fcc-donation.php b/apps/wordpress-plugin/fcc-donation/fcc-donation.php new file mode 100644 index 0000000..a62aae4 --- /dev/null +++ b/apps/wordpress-plugin/fcc-donation/fcc-donation.php @@ -0,0 +1,51 @@ + '' ], + $atts, + 'fcc_donation' + ); + + wp_enqueue_style( + 'fcc-donation', + plugins_url( 'assets/fcc-donation.css', __FILE__ ), + [], + '1.0.0' + ); + + wp_enqueue_script( + 'fcc-donation', + plugins_url( 'assets/fcc-donation.iife.js', __FILE__ ), + [], + '1.0.0', + true // load in footer so the DOM is ready + ); + + if ( ! empty( $atts['api_url'] ) ) { + wp_add_inline_script( + 'fcc-donation', + 'window.__FCC_DONATION_API_URL__ = ' . wp_json_encode( $atts['api_url'] ) . ';', + 'before' + ); + } + + return '
'; +} + +add_shortcode( 'fcc_donation', 'fcc_donation_shortcode' ); diff --git a/fcc-donation.zip b/fcc-donation.zip new file mode 100644 index 0000000..ec3e404 Binary files /dev/null and b/fcc-donation.zip differ