refactor: improve CSS specificity and add vertical spacing
- Add 1rem vertical padding to .ddhh-provider-dashboard for proper spacing - Remove all !important declarations from button styles - Use proper CSS specificity (.ddhh-jobs-table .button) instead - Document CSS best practices in CLAUDE.md (avoid !important) CSS specificity approach is more maintainable and prevents conflicts with Elementor and other theme styles. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
225
CLAUDE.md
225
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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user