Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ npm-debug.log
yarn-error.log
testem.log
/typings
apps/wordpress-plugin/fcc-donation/assets/

# System Files
.DS_Store
Expand Down
8 changes: 8 additions & 0 deletions apps/frontend/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
7 changes: 4 additions & 3 deletions apps/frontend/src/components/testimonials/testimonials.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Outer card */
.testimonial-carousel {
width: 1200px;
width: 100%;
max-width: 1200px;
height: 395px;
border-radius: 10px;
overflow: hidden;
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion apps/frontend/src/containers/donations/donations.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -328,7 +336,7 @@
width: 31%;
aspect-ratio: 14 / 1;
border-radius: 10px;
background: #3D3E6E;
background: #3d3e6e;
}

.step2-container {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]',
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/containers/root.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
display: flex;
justify-content: center;
background: #ffffff;
padding: 3rem 1rem 4rem;
padding: 3rem 1rem 6rem;
}

.root-container {
Expand Down
69 changes: 69 additions & 0 deletions apps/frontend/src/embed.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="root-page">
<div className="root-container">
<section className="root-testimonial">
<TestimonialCarousel />
</section>

<section className="root-content-grid">
<div className="root-goal-panel">
<GrowingGoal
message={label}
total={donationTotal}
goal={targetGoal}
sampleDonation={SAMPLE_DONATION}
/>
</div>

<div className="root-form-panel">
<DonationForm
onSuccess={(id) => console.log('[FCC Donation] Submitted:', id)}
onError={(err) => console.error('[FCC Donation] Error:', err)}
/>
</div>
</section>
</div>
</div>
);
};

const mountId = 'fcc-donation-embed';
const container = document.getElementById(mountId);

if (container) {
ReactDOM.createRoot(container).render(
<React.StrictMode>
<MemoryRouter>
<EmbedApp />
</MemoryRouter>
</React.StrictMode>,
);
} else {
console.warn(`[FCC Donation] Mount element #${mountId} not found.`);
}
44 changes: 44 additions & 0 deletions apps/frontend/vite.embed.config.mts
Original file line number Diff line number Diff line change
@@ -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'),
},
},
});
52 changes: 52 additions & 0 deletions apps/wordpress-plugin/fcc-donation/README.md
Original file line number Diff line number Diff line change
@@ -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 `<div>` 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.
51 changes: 51 additions & 0 deletions apps/wordpress-plugin/fcc-donation/fcc-donation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* Plugin Name: FCC Donation Embed
* Description: Embeds the FCC donation form and growing goal via the [fcc_donation] shortcode.
* Version: 1.0.0
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Shortcode: [fcc_donation api_url="https://your-backend.com"]
*
* The api_url attribute is optional. If omitted, the embed defaults to
* same-origin (i.e. the backend must be served from the same host as WordPress).
*/
function fcc_donation_shortcode( $atts ) {
$atts = shortcode_atts(
[ 'api_url' => '' ],
$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 '<div id="fcc-donation-embed"></div>';
}

add_shortcode( 'fcc_donation', 'fcc_donation_shortcode' );
Binary file added fcc-donation.zip
Binary file not shown.
Loading