{"id":47692960,"url":"https://github.com/abderrahimghazali/sylius-popup-plugin","last_synced_at":"2026-04-03T20:01:17.131Z","repository":{"id":347435874,"uuid":"1194064182","full_name":"abderrahimghazali/sylius-popup-plugin","owner":"abderrahimghazali","description":"Exit-intent \u0026 engagement popup system for Sylius 2.x stores, fully managed from the admin panel.","archived":false,"fork":false,"pushed_at":"2026-04-01T22:35:34.000Z","size":12277,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T02:47:12.525Z","etag":null,"topics":["exit-intent","marketing","php","popup","sylius","sylius-plugin","symfony"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abderrahimghazali.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T22:03:52.000Z","updated_at":"2026-04-01T22:33:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/abderrahimghazali/sylius-popup-plugin","commit_stats":null,"previous_names":["abderrahimghazali/sylius-popup-plugin"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/abderrahimghazali/sylius-popup-plugin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abderrahimghazali%2Fsylius-popup-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abderrahimghazali%2Fsylius-popup-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abderrahimghazali%2Fsylius-popup-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abderrahimghazali%2Fsylius-popup-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abderrahimghazali","download_url":"https://codeload.github.com/abderrahimghazali/sylius-popup-plugin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abderrahimghazali%2Fsylius-popup-plugin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31374051,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["exit-intent","marketing","php","popup","sylius","sylius-plugin","symfony"],"created_at":"2026-04-02T15:56:48.064Z","updated_at":"2026-04-03T20:01:17.106Z","avatar_url":"https://github.com/abderrahimghazali.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://sylius.com\" target=\"_blank\"\u003e\n        \u003cpicture\u003e\n            \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://media.sylius.com/sylius-logo-800-dark.png\"\u003e\n            \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://media.sylius.com/sylius-logo-800.png\"\u003e\n            \u003cimg alt=\"Sylius Logo\" src=\"https://media.sylius.com/sylius-logo-800.png\" width=\"300\"/\u003e\n        \u003c/picture\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eSylius Popup Plugin\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    Exit-intent \u0026 engagement popup system for \u003ca href=\"https://sylius.com\"\u003eSylius 2.x\u003c/a\u003e stores, fully managed from the admin panel.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/abderrahimghazali/sylius-popup-plugin/actions/workflows/ci.yaml\"\u003e\u003cimg src=\"https://github.com/abderrahimghazali/sylius-popup-plugin/actions/workflows/ci.yaml/badge.svg\" alt=\"CI\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/abderrahimghazali/sylius-popup-plugin\"\u003e\u003cimg src=\"https://img.shields.io/packagist/v/abderrahimghazali/sylius-popup-plugin.svg\" alt=\"Latest Version\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/abderrahimghazali/sylius-popup-plugin\"\u003e\u003cimg src=\"https://img.shields.io/packagist/php-v/abderrahimghazali/sylius-popup-plugin.svg\" alt=\"PHP Version\"/\u003e\u003c/a\u003e\n    \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"License\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/abderrahimghazali/sylius-popup-plugin\"\u003e\u003cimg src=\"https://img.shields.io/badge/sylius-2.x-green.svg\" alt=\"Sylius 2.x\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/abderrahimghazali/sylius-popup-plugin\"\u003e\u003cimg src=\"https://img.shields.io/badge/symfony-7.x-black.svg\" alt=\"Symfony 7.x\"/\u003e\u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/badge/PHPStan-level%205-brightgreen.svg\" alt=\"PHPStan Level 5\"/\u003e\n\u003c/p\u003e\n\n---\n\n## Screenshots\n\n### Admin — Campaign List\n![Admin Grid](docs/images/admin-grid.png)\n\n### Admin — Edit Campaign (Tabbed Form)\n![Admin Form](docs/images/admin-form.png)\n\n### Shop — Modal Popup (Exit Intent)\n![Popup Modal](docs/images/popup-modal.png)\n\n## Features\n\n- **Multiple popup campaigns** — create, enable/disable, and prioritize from the admin panel\n- **4 trigger types:**\n  - Exit intent (mouseleave detection)\n  - Time on page (configurable delay in seconds)\n  - Scroll depth (configurable percentage)\n  - Cart abandonment (exit intent only when cart has items)\n- **Smart frequency control** — show once per session, per 24h, or per 7 days (localStorage)\n- **Page targeting** — all pages, product pages, cart, or checkout\n- **Audience targeting** — everyone, guests only, or logged-in users only\n- **2 popup styles** — centered modal with overlay, or fixed bottom bar\n- **Discount codes** — display a copyable code with one-click copy (Clipboard API)\n- **Email capture** — optional email field that POSTs to a dedicated API endpoint and dispatches a `sylius_popup.email_captured` event for 3rd-party integrations\n- **Full design control** — background color, text color, button color (HTML color picker in admin)\n- **Rate-limited API** — email subscribe endpoint limited to 3 requests/IP/hour\n- **No external JS** — pure vanilla JS via Stimulus controller\n- **Twig hooks** — auto-injects into the shop layout, no template overrides needed\n\n## Requirements\n\n- Sylius 2.1+\n- Symfony 7.0+\n- PHP 8.2+\n\n## Installation\n\n1. Require the plugin:\n\n```bash\ncomposer require abderrahimghazali/sylius-popup-plugin\n```\n\n2. Register the bundle in `config/bundles.php` (if not auto-discovered):\n\n```php\nreturn [\n    // ...\n    Abderrahim\\SyliusPopupPlugin\\SyliusPopupPlugin::class =\u003e ['all' =\u003e true],\n];\n```\n\n3. Import routes — create `config/routes/sylius_popup.yaml`:\n\n```yaml\nsylius_popup:\n    resource: '@SyliusPopupPlugin/config/routes.yaml'\n```\n\n4. Generate and run the migration:\n\n```bash\nbin/console doctrine:migrations:diff\nbin/console doctrine:migrations:migrate\n```\n\n5. Register the Stimulus controller in `assets/shop/controllers.json`:\n\n```json\n{\n    \"controllers\": {\n        \"@abderrahimghazali/sylius-popup-plugin\": {\n            \"popup\": {\n                \"enabled\": true,\n                \"fetch\": \"eager\"\n            }\n        }\n    }\n}\n```\n\n6. Symlink the plugin assets and rebuild:\n\n```bash\n# Create the symlink (from your project root)\nmkdir -p node_modules/@abderrahimghazali\nln -s ../../vendor/abderrahimghazali/sylius-popup-plugin/assets node_modules/@abderrahimghazali/sylius-popup-plugin\n\n# Rebuild assets\nyarn encore dev\n```\n\n## Usage\n\n### Admin Panel\n\nNavigate to **Marketing \u003e Popup Campaigns** in the Sylius admin sidebar.\n\nCreate a new campaign with three configuration tabs:\n\n| Tab | Fields |\n|-----|--------|\n| **Content** | Name, title, body text, CTA button label \u0026 URL, discount code, email capture toggle |\n| **Design** | Style (modal/bar), background color, text color, button color |\n| **Targeting** | Trigger type \u0026 params, show frequency, target pages, target audience, priority |\n\nToggle campaigns on/off directly from the grid.\n\n### Trigger Types\n\n| Trigger | Behavior | Config |\n|---------|----------|--------|\n| Exit Intent | Fires when cursor leaves viewport (top edge) | — |\n| Time on Page | Fires after N seconds | `triggerDelay` |\n| Scroll Depth | Fires after X% page scroll | `triggerScrollDepth` |\n| Cart Abandonment | Exit intent, but only when cart has items | — |\n\n### Email Capture Event\n\nWhen a visitor submits their email, the plugin dispatches:\n\n```\nEvent: sylius_popup.email_captured\nPayload: { email, popupId }\n```\n\nListen to this event to integrate with Mailchimp, Sendinblue, or any email service:\n\n```php\nuse Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener;\nuse Symfony\\Component\\EventDispatcher\\GenericEvent;\n\n#[AsEventListener(event: 'sylius_popup.email_captured')]\nfinal class EmailCapturedListener\n{\n    public function __invoke(GenericEvent $event): void\n    {\n        $email = $event-\u003egetArgument('email');\n        $popupId = $event-\u003egetArgument('popupId');\n\n        // Send to your email marketing platform\n    }\n}\n```\n\n### API Endpoint\n\n```\nPOST /api/v2/shop/popup/{id}/subscribe\nContent-Type: application/json\n\n{ \"email\": \"visitor@example.com\" }\n\n→ 200 { \"success\": true }\n→ 400 { \"error\": \"Invalid email address.\" }\n→ 429 { \"error\": \"Too many requests. Please try again later.\" }\n```\n\nRate limited to 3 requests per IP per hour.\n\n## Entity: PopupCampaign\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `name` | string | Admin label (not shown to visitors) |\n| `enabled` | boolean | Active/inactive toggle |\n| `title` | string | Popup heading |\n| `body` | text | Popup body content |\n| `ctaLabel` | string? | CTA button text |\n| `ctaUrl` | string? | CTA button link |\n| `discountCode` | string? | Copyable discount code |\n| `emailCaptureEnabled` | boolean | Show email input field |\n| `style` | enum | `modal` or `bar` |\n| `backgroundColor` | string | Hex color (default `#ffffff`) |\n| `textColor` | string | Hex color (default `#111111`) |\n| `buttonColor` | string | Hex color (default `#000000`) |\n| `triggerType` | enum | `exit_intent`, `time_on_page`, `scroll_depth`, `cart_abandonment` |\n| `triggerDelay` | int | Seconds (for time_on_page) |\n| `triggerScrollDepth` | int | Percentage (for scroll_depth) |\n| `showFrequency` | enum | `session`, `day`, `week` |\n| `targetPages` | enum | `all`, `product`, `cart`, `checkout` |\n| `targetAudience` | enum | `everyone`, `guests`, `logged_in` |\n| `priority` | int | Higher = shown first |\n\n## Architecture\n\n```\nsrc/\n├── Controller/\n│   ├── Admin/PopupCampaignController.php    # CRUD + AJAX toggle\n│   └── Shop/PopupSubscribeController.php    # Email capture API\n├── DependencyInjection/\n│   ├── Configuration.php\n│   └── SyliusPopupExtension.php             # Prepends resources, grids, hooks\n├── Entity/\n│   ├── PopupCampaign.php\n│   └── PopupCampaignInterface.php\n├── Enum/                                     # 5 backed string enums\n├── EventListener/AdminMenuListener.php       # Marketing menu item\n├── Form/Type/PopupCampaignType.php          # Admin form with all fields\n├── Repository/PopupCampaignRepository.php    # Targeting query\n├── Service/PopupRenderer.php                 # Page/audience resolution\n├── Twig/PopupRendererExtension.php          # Twig function bridge\n└── SyliusPopupPlugin.php                    # Bundle class\n\nassets/controllers/popup-controller.js        # Stimulus controller\ntemplates/\n├── admin/popup_campaign/                     # Tabbed create/update forms\n└── shop/                                     # Modal + bar + renderer\n```\n\n## Testing\n\n```bash\nvendor/bin/phpunit\n```\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabderrahimghazali%2Fsylius-popup-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabderrahimghazali%2Fsylius-popup-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabderrahimghazali%2Fsylius-popup-plugin/lists"}