diff --git a/CLAUDE.md b/CLAUDE.md index c9ff9e4..5f608d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,131 +31,6 @@ Common commands: - `/gsd:execute-phase` - Execute all plans in a phase - `/gsd:help` - View all available GSD commands -## Architecture - -### Plugin Entry Point - -The plugin follows WordPress singleton pattern with class-based initialization: - -1. `ddhh-job-manager.php` - Main plugin file that: - - Loads Action Scheduler vendor library **first** (required for async operations) - - Defines constants (DDHH_JM_VERSION, DDHH_JM_PLUGIN_DIR, etc.) - - Requires all class files from `includes/` - - Initializes via `DDHH_JM_Job_Manager::get_instance()` on `plugins_loaded` hook - -2. `includes/class-ddhh-job-manager.php` - Singleton orchestrator that initializes all subsystems via `init_hooks()` - -### Core Subsystems - -The plugin is organized into focused class files in `includes/`: - -**Foundation Layer**: -- `class-post-types.php` - Registers `job_offer` CPT with custom capabilities -- `class-roles.php` - Manages `ddhh_provider` role with restricted capabilities -- `class-acf-fields.php` - Programmatically registers ACF field groups for job metadata -- `class-activator.php` / `class-deactivator.php` - Plugin lifecycle hooks - -**User Management**: -- `class-formidable.php` - Integration layer for all Formidable Forms (registration, job submission, edit, deactivation, application). Uses form keys like 'provider_registration', 'job_submission', etc. -- `class-access-control.php` - Blocks providers from WP-Admin, enforces login requirements on job archives -- `class-user-preferences.php` - Manages mentor opt-in preferences for job notifications - -**Job Workflows**: -- `class-dashboard.php` - Provides `[ddhh_provider_dashboard]` shortcode displaying provider's jobs -- `class-pages.php` - Creates/manages plugin pages (dashboard, login) via option storage -- `class-archive.php` - Filters job queries to show only published jobs to appropriate users -- `class-template.php` - Handles single job display and contact form modal - -**Notifications & Async Processing**: -- `class-notifications.php` - Email system triggered by WordPress hooks (`ddhh_job_submitted`, `transition_post_status`, `frm_after_create_entry`) -- `class-scheduler.php` - Action Scheduler wrapper for async batch email processing (chunks of 50 users) - -**Admin Experience**: -- `class-admin-ui.php` - Enhances admin screens with custom columns, quick links, status indicators - -### Custom Roles & Capabilities - -**ddhh_provider role**: Restricted role for external organizations -- Can create/edit/delete only their own `job_offer` posts -- Cannot access WP-Admin (redirected to frontend dashboard) -- Can edit their own profile (`profile.php` access allowed) - -**Capability mapping** (in `class-post-types.php`): -- `edit_job_offer` - Owner can edit their own jobs -- `delete_job_offer` - Owner can delete their own jobs -- `read_job_offer` - Users can read published jobs -- Standard WordPress `map_meta_cap` filter enforces ownership - -### Formidable Forms Integration - -The plugin uses 5 Formidable forms, identified by form keys: -1. **provider_registration** (F1) - Creates user with `ddhh_provider` role, auto-login -2. **job_submission** (F2) - Creates `job_offer` post with `pending` status, maps ACF fields -3. **job_edit** (F3) - Updates existing job (ownership validated), preserves submission date -4. **job_deactivation** (F4) - Sets status to `draft`, captures deactivation reason -5. **job_application** (F5) - Mentor applies to job, emails provider contact address - -**Form hooks** (`class-formidable.php`): -- Registration: `frm_after_create_entry` → auto-login → redirect to dashboard -- Job submission: `frm_after_create_entry` → fires `ddhh_job_submitted` action → admin notification -- Job edit: `frm_after_update_entry` → fires `ddhh_job_edited` action → admin notification -- Application: `frm_after_create_entry` → emails provider's contact email from ACF field - -### Email Notification System - -**Immediate notifications** (`class-notifications.php`): -- Admin receives email on job submission with edit/view links -- Admin receives email on job edit with change summary -- Admin receives email on deactivation with reason -- Provider receives email when mentor applies (via Formidable) - -**Async batch notifications** (`class-scheduler.php`): -- Mentor notification on job publish uses Action Scheduler -- Users are queried for `ddhh_jm_notification_optin` meta -- Batched into groups of 50 to prevent email provider limits -- Each batch scheduled as separate `ddhh_jm_send_mentor_notification_batch` action - -### Access Control Layers - -1. **WP-Admin lockout**: Providers redirected to dashboard unless accessing `profile.php` or AJAX -2. **Dashboard protection**: `template_redirect` hook checks user role, redirects non-providers -3. **Archive protection**: Jobs require login; only `publish` status shown to mentors -4. **Single job protection**: Login required to view job details -5. **Ownership validation**: Forms validate current user owns the job before editing/deactivation - -### Template System - -**Frontend templates** (`templates/`): -- `provider-dashboard.php` - Displays provider's jobs in table format with status badges and action links - -**Elementor integration**: -- Job archive uses Elementor Loop Grid (no custom template file) -- Job detail page uses Elementor single post template with ACF dynamic tags -- Contact form modal injected via `the_content` filter in `class-template.php` - -### Job Lifecycle - -``` -Provider submits → pending → Admin reviews → publish → Mentors see + notified - ↓ - Admin can reject → trash -Provider deactivates → draft (with reason stored in meta) -``` - -**Status hooks**: -- `ddhh_job_submitted` - Fired after new job metadata saved (form entry ID passed) -- `ddhh_job_edited` - Fired after job update metadata saved (form entry ID passed) -- `transition_post_status` - Used for publish detection and deactivation detection - -### ACF Field Groups - -Programmatically registered in `class-acf-fields.php`: -- **Job Details**: `job_location`, `job_type`, `job_deadline` (date picker), `contact_email` -- **Deactivation**: `deactivation_reason`, `deactivation_date` -- **Metadata**: `submission_date` (preserved during edits) - -All fields support German labels matching site language. - ## Development Workflow ### Testing Locally @@ -187,91 +62,23 @@ Email templates are inline in `class-notifications.php`: 3. Hook into appropriate action in `setup_hooks()` 4. For batch processing, use `DDHH_JM_Scheduler::schedule_mentor_notification_batch()` -## Common Tasks +### CSS Best Practices -### Debugging Action Scheduler +**NEVER use `!important` in CSS.** This is a bad practice that creates maintenance issues and specificity wars. -```bash -# View scheduled actions in WP CLI -wp action-scheduler list --status=pending -wp action-scheduler list --status=failed +Instead, use proper CSS specificity to override styles: +- **Bad:** `.button { color: #fff !important; }` +- **Good:** `.ddhh-jobs-table .button { color: #fff; }` -# Run pending actions manually -wp action-scheduler run --batch-size=10 +Specificity hierarchy (from weakest to strongest): +1. Element selectors: `button { }` +2. Class selectors: `.button { }` +3. Multiple classes: `.ddhh-jobs-table .button { }` +4. ID selectors: `#my-id { }` (avoid in most cases) +5. Inline styles: `style="..."` (avoid) +6. `!important` (NEVER use unless absolutely necessary for overriding external libraries) -# Clean up old actions -wp action-scheduler clean --days=90 -``` - -### Checking Provider Permissions - -Providers should NOT be able to: -- Access WP-Admin (except profile.php) -- Edit other providers' jobs -- Publish jobs directly (must be pending) -- View unpublished jobs by other providers - -### Verifying Job Moderation Flow - -1. Log in as provider → Submit job → Should create `pending` post -2. Admin receives email with job details and moderation links -3. Admin publishes job → Opted-in mentors receive notification (async batches) -4. Provider sees published job in dashboard -5. Mentors can view and apply to job - -### Date Field Handling - -Job deadlines use ACF date picker: -- Format conversion in Formidable forms: Slashes auto-converted to dots for display -- Before submission: Dots converted back to ISO format (YYYY-MM-DD) -- Display: Use `get_field('job_deadline')` returns 'Ymd', format with `date_i18n()` -- Validation: Date fields are optional (empty string is valid) - -### Logo Auto-Cropping - -Featured images (logos) auto-crop to 200×200px on upload: -- Handled by `class-post-types.php` registering 'job-logo' image size -- Uses WordPress `add_image_size()` with hard crop enabled -- Images uploaded via Formidable forms trigger standard WP media processing - -## Project State - -**Currently**: Phase 6 (Email Notifications) is mostly complete. Phase 7 (Testing & Polish) is next. - -**What's working**: -- Provider registration and authentication -- Job submission, editing, deactivation workflows -- Admin moderation and notifications -- Mentor job browsing and applications -- Opt-in preferences for mentor notifications -- Action Scheduler integration for async processing - -**What's pending**: -- Phase 6.3: Async batch email processing on job publish (implementation in progress) -- Phase 7: End-to-end testing and production deployment prep - -**Documentation**: See `.planning/` for detailed project documentation: -- `PROJECT.md` - Requirements and context -- `ROADMAP.md` - Phase breakdown and progress -- `STATE.md` - Current work and blockers -- `phases/` - Detailed plans and summaries for each phase - -## Code Standards - -- **WordPress Coding Standards**: Follow WordPress PHP coding standards -- **Text Domain**: Use `ddhh-job-manager` for all translatable strings -- **German Language**: All user-facing text in German -- **Security**: Validate ownership, escape output, verify nonces in forms -- **Hook Naming**: Use `ddhh_jm_` prefix for custom actions/filters -- **Class Naming**: Use `DDHH_JM_` prefix, one class per file -- **Static Methods**: Use static methods for classes that don't maintain state -- **Direct Exit**: Always include `defined( 'ABSPATH' ) || exit;` at top of PHP files - -## Critical Files to Review Before Major Changes - -- `ddhh-job-manager.php` - Plugin initialization order matters (Action Scheduler first) -- `class-ddhh-job-manager.php` - Hook registration sequence -- `class-formidable.php` - Form IDs and field mappings -- `class-notifications.php` - Email templates and triggering logic -- `class-access-control.php` - Security boundaries for providers -- `class-post-types.php` - Capability mapping for `job_offer` posts +**Template-specific styles:** +- All dashboard template styles are scoped with `.ddhh-provider-dashboard` or child selectors +- This prevents conflicts with Elementor and other theme styles +- Use parent selector chains like `.ddhh-jobs-table .button` for higher specificity diff --git a/templates/provider-dashboard.php b/templates/provider-dashboard.php index 29c4309..12ae60e 100644 --- a/templates/provider-dashboard.php +++ b/templates/provider-dashboard.php @@ -341,7 +341,7 @@ $job_query = new WP_Query( $args ); .ddhh-provider-dashboard { max-width: 1200px; margin: 2rem auto; - padding: 0 1rem; + padding: 1rem 1rem; } .ddhh-dashboard-header { @@ -365,11 +365,11 @@ $job_query = new WP_Query( $args ); font-weight: 600; } -.logout-button { +.ddhh-dashboard-header .logout-button { display: inline-block; padding: 0.5rem 1.25rem; background-color: #6b7280; - color: #fff !important; + color: #fff; text-decoration: none; border-radius: 0.375rem; font-size: 0.875rem; @@ -377,15 +377,15 @@ $job_query = new WP_Query( $args ); transition: background-color 0.2s; } -.logout-button:hover { +.ddhh-dashboard-header .logout-button:hover { background-color: #4b5563; - color: #fff !important; + color: #fff; text-decoration: none; } .ddhh-job-submit-section { margin-bottom: 3rem; - padding: 2.5rem 2rem; + padding: 2rem; background: #f5f5f5; border-radius: 8px; } @@ -428,9 +428,6 @@ $job_query = new WP_Query( $args ); .ddhh-job-listings-section { margin-top: 3rem; - padding: 2.5rem 2rem; - background: #f5f5f5; - border-radius: 8px; } .ddhh-provider-dashboard h2 { @@ -493,53 +490,53 @@ $job_query = new WP_Query( $args ); white-space: nowrap; } -.button { +.ddhh-jobs-table .button { display: inline-block; padding: 0.5rem 1rem; background-color: #3b82f6; - color: #fff !important; + color: #fff; text-decoration: none; border-radius: 0.375rem; font-size: 0.875rem; transition: background-color 0.2s; } -.button:hover { +.ddhh-jobs-table .button:hover { background-color: #2563eb; - color: #fff !important; + color: #fff; text-decoration: none; } -.edit-link { +.ddhh-jobs-table .edit-link { background-color: #6366f1; - color: #fff !important; + color: #fff; } -.edit-link:hover { +.ddhh-jobs-table .edit-link:hover { background-color: #4f46e5; - color: #fff !important; + color: #fff; } -.view-link { +.ddhh-jobs-table .view-link { background-color: #10b981; margin-left: 0.5rem; - color: #fff !important; + color: #fff; } -.view-link:hover { +.ddhh-jobs-table .view-link:hover { background-color: #059669; - color: #fff !important; + color: #fff; } -.deactivate-link { +.ddhh-jobs-table .deactivate-link { background-color: #ef4444; margin-left: 0.5rem; - color: #fff !important; + color: #fff; } -.deactivate-link:hover { +.ddhh-jobs-table .deactivate-link:hover { background-color: #dc2626; - color: #fff !important; + color: #fff; } .ddhh-empty-state,