Files
Digital-Dabei-Hamburg-Job-M…/templates/provider-dashboard.php
Viktor Miller 08b9ad24a5 refactor: restructure dashboard UX and migrate logos to provider level
- Move job submission form to separate view (?action=new_job)
- Replace inline form with prominent green button on main dashboard
- Migrate logos from per-job (post thumbnail) to per-provider (user meta)
- Add logo upload/removal functionality to provider dashboard
- Display provider logo on single job pages instead of per-job logo
- Add back link to archive on single job pages
- Remove logo handling from form submission/edit processors
- Improve button styling with proper CSS specificity

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-29 14:03:47 +09:00

782 lines
20 KiB
PHP

<?php
/**
* Provider Dashboard Template
*
* Displays the current provider's job listings in a table format
*
* @package DDHH_Job_Manager
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
// Check if user is logged in and has the ddhh_provider role
if ( ! is_user_logged_in() ) {
echo '<div class="ddhh-error-message">';
echo '<p>Sie müssen angemeldet sein, um diese Seite anzuzeigen.</p>';
echo '</div>';
return;
}
$current_user = wp_get_current_user();
if ( ! in_array( 'ddhh_provider', $current_user->roles, true ) ) {
echo '<div class="ddhh-error-message">';
echo '<p>Sie haben keine Berechtigung, diese Seite anzuzeigen.</p>';
echo '</div>';
return;
}
// Handle logo upload
if ( isset( $_POST['ddhh_upload_logo'] ) && isset( $_POST['ddhh_logo_nonce'] ) && wp_verify_nonce( $_POST['ddhh_logo_nonce'], 'ddhh_logo_upload' ) ) {
if ( ! empty( $_FILES['ddhh_logo_file'] ) && $_FILES['ddhh_logo_file']['error'] === UPLOAD_ERR_OK ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/image.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );
// Validate file type
$allowed_types = array( 'image/jpeg', 'image/png', 'image/jpg' );
$file_type = $_FILES['ddhh_logo_file']['type'];
if ( in_array( $file_type, $allowed_types, true ) ) {
$upload = wp_handle_upload( $_FILES['ddhh_logo_file'], array( 'test_form' => false ) );
if ( isset( $upload['file'] ) && ! isset( $upload['error'] ) ) {
// Insert attachment
$attachment = array(
'post_mime_type' => $upload['type'],
'post_title' => sanitize_file_name( $_FILES['ddhh_logo_file']['name'] ),
'post_content' => '',
'post_status' => 'inherit',
);
$attachment_id = wp_insert_attachment( $attachment, $upload['file'] );
if ( ! is_wp_error( $attachment_id ) ) {
// Generate metadata
$attachment_data = wp_generate_attachment_metadata( $attachment_id, $upload['file'] );
wp_update_attachment_metadata( $attachment_id, $attachment_data );
// Save to user meta
update_user_meta( get_current_user_id(), 'ddhh_provider_logo', $attachment_id );
}
}
}
}
}
// Handle logo removal
if ( isset( $_POST['ddhh_remove_logo'] ) && isset( $_POST['ddhh_logo_nonce'] ) && wp_verify_nonce( $_POST['ddhh_logo_nonce'], 'ddhh_logo_upload' ) ) {
delete_user_meta( get_current_user_id(), 'ddhh_provider_logo' );
}
// Check if we're in edit mode
$is_edit_mode = isset( $_GET['action'] ) && $_GET['action'] === 'edit_job' && isset( $_GET['job_id'] );
// Check if we're in deactivate mode
$is_deactivate_mode = isset( $_GET['action'] ) && $_GET['action'] === 'deactivate_job' && isset( $_GET['job_id'] );
// Check if we're in new job mode
$is_new_job_mode = isset( $_GET['action'] ) && $_GET['action'] === 'new_job';
if ( $is_edit_mode ) {
$job_id = absint( $_GET['job_id'] );
$form_id = DDHH_JM_Formidable::get_job_edit_form_id();
if ( $form_id ) {
// Get post data
$post = get_post( $job_id );
if ( ! $post || 'job_offer' !== $post->post_type || absint( $post->post_author ) !== get_current_user_id() ) {
echo '<div class="ddhh-error-message"><p>Sie haben keine Berechtigung, dieses Stellenangebot zu bearbeiten.</p></div>';
return;
}
// Get field IDs
$fields = FrmField::getAll( array( 'fi.form_id' => $form_id ), 'field_order' );
$field_params = array();
foreach ( $fields as $field ) {
$field_value = '';
switch ( $field->field_key ) {
case 'job_title2':
$field_value = $post->post_title;
break;
case 'job_description2':
$field_value = $post->post_content;
break;
case 'job_location2':
$field_value = get_post_meta( $job_id, 'job_location', true );
break;
case 'job_type2':
$field_value = get_post_meta( $job_id, 'job_type', true );
break;
case 'job_deadline2':
$field_value = get_post_meta( $job_id, 'job_deadline', true );
break;
case 'job_contact_email2':
$field_value = get_post_meta( $job_id, 'job_contact_email', true );
break;
}
if ( ! empty( $field_value ) ) {
$field_params[ $field->id ] = $field_value;
}
}
?>
<div class="ddhh-provider-dashboard">
<div class="ddhh-dashboard-header">
<div class="ddhh-user-info">
<span class="welcome-text">Angemeldet als: <strong><?php echo esc_html( $current_user->display_name ); ?></strong></span>
</div>
<div class="ddhh-logout-link">
<a href="<?php echo esc_url( wp_logout_url( home_url( '/anbieter-login/' ) ) ); ?>" class="logout-button">Abmelden</a>
</div>
</div>
<div class="ddhh-job-edit-section">
<h2>Stellenangebot bearbeiten</h2>
<p><a href="<?php echo esc_url( home_url( '/anbieter-dashboard/' ) ); ?>" class="back-to-dashboard">← Zurück zur Übersicht</a></p>
<?php echo do_shortcode( "[formidable id={$form_id}]" ); ?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Populate form fields with existing data
var formData = <?php echo json_encode( $field_params ); ?>;
// Wait for form to be fully loaded
setTimeout(function() {
$.each(formData, function(fieldId, value) {
var field = $('input[name="item_meta[' + fieldId + ']"], textarea[name="item_meta[' + fieldId + ']"], select[name="item_meta[' + fieldId + ']"]');
if (field.length) {
if (field.is('select')) {
// For select fields, try case-insensitive matching
var options = field.find('option');
var matched = false;
options.each(function() {
if ($(this).val().toLowerCase() === value.toLowerCase()) {
field.val($(this).val()).trigger('change');
matched = true;
console.log('Matched select option:', $(this).val(), 'for value:', value);
return false; // break
}
});
if (!matched) {
console.warn('No matching option found for field', fieldId, 'value:', value, 'Available options:', options.map(function() { return $(this).val(); }).get());
}
} else if (field.is(':checkbox') || field.is(':radio')) {
field.filter('[value="' + value + '"]').prop('checked', true);
} else {
// For date fields, convert YYYY-MM-DD to DD.MM.YYYY format
if (field.attr('type') === 'date' || field.hasClass('frm_date')) {
// Check if value is in YYYY-MM-DD format
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
var parts = value.split('-');
var formattedDate = parts[2] + '.' + parts[1] + '.' + parts[0];
field.val(formattedDate).trigger('change');
console.log('Converted date from', value, 'to', formattedDate);
} else {
field.val(value);
}
} else {
field.val(value);
}
}
console.log('Populated field ' + fieldId + ' with:', value);
} else {
console.warn('Field not found:', fieldId);
}
});
}, 500);
// Fix date format after user selects a date - replace slashes with dots for display
$(document).on('change', 'input[type="date"], input.frm_date', function() {
var field = $(this);
var value = field.val();
// If the value contains slashes, replace them with dots
if (value && value.includes('/')) {
var fixedValue = value.replace(/\//g, '.');
field.val(fixedValue);
// Store the original format in a data attribute for form submission
field.data('original-format', value.replace(/\//g, '-'));
console.log('Fixed date format from', value, 'to', fixedValue);
}
});
// Before form submission, convert dates back to YYYY-MM-DD format
$('form').on('submit', function() {
$(this).find('input[type="date"], input.frm_date').each(function() {
var field = $(this);
var value = field.val();
// Convert DD.MM.YYYY or DD/MM/YYYY back to YYYY-MM-DD
if (value && /^\d{2}[./]\d{2}[./]\d{4}$/.test(value)) {
var parts = value.split(/[./]/);
var isoDate = parts[2] + '-' + parts[1] + '-' + parts[0];
field.val(isoDate);
console.log('Converted date for submission from', value, 'to', isoDate);
}
});
});
});
</script>
</div>
</div>
<?php
return;
}
}
if ( $is_deactivate_mode ) {
$job_id = absint( $_GET['job_id'] );
$form_id = DDHH_JM_Formidable::get_job_deactivation_form_id();
if ( $form_id ) {
?>
<div class="ddhh-provider-dashboard">
<div class="ddhh-dashboard-header">
<div class="ddhh-user-info">
<span class="welcome-text">Angemeldet als: <strong><?php echo esc_html( $current_user->display_name ); ?></strong></span>
</div>
<div class="ddhh-logout-link">
<a href="<?php echo esc_url( wp_logout_url( home_url( '/anbieter-login/' ) ) ); ?>" class="logout-button">Abmelden</a>
</div>
</div>
<div class="ddhh-job-deactivate-section">
<h2>Stellenangebot deaktivieren</h2>
<p><a href="<?php echo esc_url( home_url( '/anbieter-dashboard/' ) ); ?>" class="back-to-dashboard">← Zurück zur Übersicht</a></p>
<?php echo do_shortcode( "[formidable id={$form_id} id_param={$job_id}]" ); ?>
</div>
</div>
<?php
return;
}
}
if ( $is_new_job_mode ) {
$form_id = DDHH_JM_Formidable::get_job_submission_form_id();
if ( $form_id ) {
?>
<div class="ddhh-provider-dashboard">
<div class="ddhh-dashboard-header">
<div class="ddhh-user-info">
<span class="welcome-text">Angemeldet als: <strong><?php echo esc_html( $current_user->display_name ); ?></strong></span>
</div>
<div class="ddhh-logout-link">
<a href="<?php echo esc_url( wp_logout_url( home_url( '/anbieter-login/' ) ) ); ?>" class="logout-button">Abmelden</a>
</div>
</div>
<div class="ddhh-job-submit-section">
<h2>Neues Stellenangebot erstellen</h2>
<p><a href="<?php echo esc_url( home_url( '/anbieter-dashboard/' ) ); ?>" class="back-to-dashboard">← Zurück zur Übersicht</a></p>
<?php echo do_shortcode( "[formidable id={$form_id}]" ); ?>
</div>
</div>
<?php
return;
}
}
// Query current user's job_offer posts
$args = array(
'post_type' => 'job_offer',
'author' => get_current_user_id(),
'post_status' => array( 'publish', 'pending', 'draft' ),
'orderby' => 'date',
'order' => 'DESC',
'posts_per_page' => -1,
);
$job_query = new WP_Query( $args );
?>
<div class="ddhh-provider-dashboard">
<div class="ddhh-dashboard-header">
<div class="ddhh-user-info">
<span class="welcome-text">Angemeldet als: <strong><?php echo esc_html( $current_user->display_name ); ?></strong></span>
</div>
<div class="ddhh-logout-link">
<a href="<?php echo esc_url( wp_logout_url( home_url( '/anbieter-login/' ) ) ); ?>" class="logout-button">Abmelden</a>
</div>
</div>
<div class="ddhh-logo-section">
<h3>Ihr Logo</h3>
<?php
$provider_logo_id = get_user_meta( get_current_user_id(), 'ddhh_provider_logo', true );
if ( $provider_logo_id ) {
echo '<div class="ddhh-current-logo">';
echo wp_get_attachment_image( absint( $provider_logo_id ), 'medium' );
echo '</div>';
}
?>
<form method="post" enctype="multipart/form-data" class="ddhh-logo-upload-form">
<?php wp_nonce_field( 'ddhh_logo_upload', 'ddhh_logo_nonce' ); ?>
<div class="ddhh-logo-controls">
<input type="file" name="ddhh_logo_file" accept="image/png,image/jpeg,image/jpg" class="ddhh-logo-input">
<button type="submit" name="ddhh_upload_logo" class="ddhh-logo-button ddhh-upload-button">
<?php echo $provider_logo_id ? 'Logo ändern' : 'Logo hochladen'; ?>
</button>
<?php if ( $provider_logo_id ) : ?>
<button type="submit" name="ddhh_remove_logo" class="ddhh-logo-button ddhh-remove-button" onclick="return confirm('Möchten Sie das Logo wirklich entfernen?');">Logo entfernen</button>
<?php endif; ?>
</div>
<p class="ddhh-logo-hint">Erlaubte Formate: JPG, PNG (max. 2 MB)</p>
</form>
</div>
<div class="ddhh-dashboard-actions">
<a href="<?php echo esc_url( add_query_arg( 'action', 'new_job', home_url( '/anbieter-dashboard/' ) ) ); ?>" class="ddhh-new-job-button">+ Neues Stellenangebot erstellen</a>
</div>
<div class="ddhh-job-listings-section">
<h2>Meine Stellenangebote</h2>
<?php if ( $job_query->have_posts() ) : ?>
<table class="ddhh-jobs-table">
<thead>
<tr>
<th>Titel</th>
<th>Status</th>
<th>Standort</th>
<th>Art</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<?php while ( $job_query->have_posts() ) : $job_query->the_post(); ?>
<?php
$post_id = get_the_ID();
$post_status = get_post_status();
$location = get_field( 'job_location', $post_id );
$job_type = get_field( 'job_type', $post_id );
// Translate status to German
$status_labels = array(
'publish' => 'Veröffentlicht',
'pending' => 'Ausstehend',
'draft' => 'Entwurf',
);
$status_text = isset( $status_labels[ $post_status ] ) ? $status_labels[ $post_status ] : $post_status;
?>
<tr>
<td class="job-title"><?php the_title(); ?></td>
<td class="job-status">
<span class="status-badge status-<?php echo esc_attr( $post_status ); ?>">
<?php echo esc_html( $status_text ); ?>
</span>
</td>
<td class="job-location"><?php echo esc_html( $location ? $location : '-' ); ?></td>
<td class="job-type"><?php echo esc_html( $job_type ? $job_type : '-' ); ?></td>
<td class="job-actions">
<?php
// Edit link - points to edit form on dashboard
if ( current_user_can( 'edit_job_offer', $post_id ) ) {
$dashboard_url = home_url( '/anbieter-dashboard/' );
$edit_url = add_query_arg(
array(
'action' => 'edit_job',
'job_id' => $post_id,
'id_param' => $post_id, // For Formidable form action
),
$dashboard_url
);
echo '<a href="' . esc_url( $edit_url ) . '" class="button edit-link">Bearbeiten</a>';
}
// View link - only for published posts
if ( 'publish' === $post_status ) {
echo ' <a href="' . esc_url( get_permalink( $post_id ) ) . '" class="button view-link" target="_blank">Ansehen</a>';
}
// Deactivate link - only for published posts
if ( 'publish' === $post_status ) {
$dashboard_url = home_url( '/anbieter-dashboard/' );
$deactivate_url = add_query_arg(
array(
'action' => 'deactivate_job',
'job_id' => $post_id,
),
$dashboard_url
);
echo ' <a href="' . esc_url( $deactivate_url ) . '" class="button deactivate-link">Deaktivieren</a>';
}
?>
</td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
<?php else : ?>
<div class="ddhh-empty-state">
<p>Sie haben noch keine Stellenangebote erstellt.</p>
</div>
<?php endif; ?>
<?php wp_reset_postdata(); ?>
</div>
</div>
<style>
.ddhh-provider-dashboard {
max-width: 1200px;
margin: 2rem auto;
padding: 1rem 1rem;
}
.ddhh-dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.25rem 2rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
}
.ddhh-user-info .welcome-text {
color: #374151;
font-size: 0.95rem;
}
.ddhh-user-info strong {
color: #111827;
font-weight: 600;
}
.ddhh-dashboard-header .logout-button {
display: inline-block;
padding: 0.5rem 1.25rem;
background-color: #6b7280;
color: #fff;
text-decoration: none;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
transition: background-color 0.2s;
}
.ddhh-dashboard-header .logout-button:hover {
background-color: #4b5563;
color: #fff;
text-decoration: none;
}
.ddhh-logo-section {
padding: 1.5rem 2rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
}
.ddhh-logo-section h3 {
margin-top: 0;
margin-bottom: 1rem;
font-size: 1.125rem;
color: #111827;
font-weight: 600;
}
.ddhh-current-logo {
margin-bottom: 1rem;
padding: 1rem;
background: #f9fafb;
border-radius: 4px;
display: inline-block;
}
.ddhh-current-logo img {
max-width: 200px;
height: auto;
display: block;
}
.ddhh-logo-upload-form {
margin: 0;
}
.ddhh-logo-controls {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
margin-bottom: 0.5rem;
}
.ddhh-logo-input {
flex: 1;
min-width: 200px;
padding: 0.5rem;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
font-size: 0.875rem;
}
.ddhh-logo-button {
padding: 0.5rem 1rem;
border: none;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.ddhh-upload-button {
background-color: #3b82f6;
color: #fff;
}
.ddhh-upload-button:hover {
background-color: #2563eb;
}
.ddhh-remove-button {
background-color: #ef4444;
color: #fff;
}
.ddhh-remove-button:hover {
background-color: #dc2626;
}
.ddhh-logo-hint {
margin: 0;
font-size: 0.8125rem;
color: #6b7280;
}
.ddhh-dashboard-actions {
margin-bottom: 2rem;
text-align: left;
}
.ddhh-provider-dashboard .ddhh-new-job-button {
display: inline-block;
padding: 0.875rem 2rem;
background-color: #10b981;
color: #fff;
text-decoration: none;
border-radius: 0.5rem;
font-size: 1rem;
font-weight: 600;
transition: background-color 0.2s;
}
.ddhh-provider-dashboard .ddhh-new-job-button:hover {
background-color: #059669;
color: #fff;
text-decoration: none;
}
.ddhh-job-submit-section {
padding: 2rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.ddhh-job-submit-section h2 {
margin-top: 0;
margin-bottom: 1rem;
font-size: 1.5rem;
color: #333;
}
.ddhh-job-edit-section,
.ddhh-job-deactivate-section {
padding: 2rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.ddhh-job-edit-section h2,
.ddhh-job-deactivate-section h2 {
margin-top: 0;
margin-bottom: 1rem;
font-size: 1.5rem;
color: #333;
}
.back-to-dashboard {
display: inline-block;
margin-bottom: 1.5rem;
color: #3b82f6;
text-decoration: none;
font-weight: 500;
}
.back-to-dashboard:hover {
color: #2563eb;
text-decoration: underline;
}
.ddhh-job-listings-section {
margin-top: 3rem;
}
.ddhh-provider-dashboard h2 {
margin-bottom: 1.5rem;
font-size: 2rem;
color: #333;
}
.ddhh-jobs-table {
width: 100%;
border-collapse: collapse;
background: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.ddhh-jobs-table th,
.ddhh-jobs-table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid #e5e7eb;
}
.ddhh-jobs-table thead th {
background-color: #f9fafb;
font-weight: 600;
color: #374151;
text-transform: uppercase;
font-size: 0.875rem;
letter-spacing: 0.05em;
}
.ddhh-jobs-table tbody tr:hover {
background-color: #f9fafb;
}
.status-badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.875rem;
font-weight: 500;
}
.status-publish {
background-color: #d1fae5;
color: #065f46;
}
.status-pending {
background-color: #fef3c7;
color: #92400e;
}
.status-draft {
background-color: #e5e7eb;
color: #374151;
}
.job-actions {
white-space: nowrap;
}
.ddhh-jobs-table .button {
display: inline-block;
padding: 0.5rem 1rem;
background-color: #3b82f6;
color: #fff;
text-decoration: none;
border-radius: 0.375rem;
font-size: 0.875rem;
transition: background-color 0.2s;
}
.ddhh-jobs-table .button:hover {
background-color: #2563eb;
color: #fff;
text-decoration: none;
}
.ddhh-jobs-table .edit-link {
background-color: #6366f1;
color: #fff;
}
.ddhh-jobs-table .edit-link:hover {
background-color: #4f46e5;
color: #fff;
}
.ddhh-jobs-table .view-link {
background-color: #10b981;
margin-left: 0.5rem;
color: #fff;
}
.ddhh-jobs-table .view-link:hover {
background-color: #059669;
color: #fff;
}
.ddhh-jobs-table .deactivate-link {
background-color: #ef4444;
margin-left: 0.5rem;
color: #fff;
}
.ddhh-jobs-table .deactivate-link:hover {
background-color: #dc2626;
color: #fff;
}
.ddhh-empty-state,
.ddhh-error-message {
padding: 3rem;
text-align: center;
background: #f9fafb;
border-radius: 0.5rem;
color: #6b7280;
}
.ddhh-error-message {
background: #fee2e2;
color: #991b1b;
}
@media (max-width: 768px) {
.ddhh-dashboard-header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
padding: 1rem;
}
.ddhh-logout-link {
width: 100%;
}
.logout-button {
width: 100%;
text-align: center;
}
.ddhh-jobs-table {
font-size: 0.875rem;
}
.ddhh-jobs-table th,
.ddhh-jobs-table td {
padding: 0.75rem 0.5rem;
}
.button {
padding: 0.375rem 0.75rem;
font-size: 0.8125rem;
}
}
</style>