Release 1.16.0

Arbeitsstunden-Modul hinzugefuegt

- Pflichtstunden pro Jahr inkl. Preis je fehlender Stunde

- Arbeitsarten, Arbeitseintraege und Mehrfachzuordnung von Mitgliedern

- Mitgliederuebersicht mit Berechnung fehlender Stunden und Aufschlag

- Datenbankschema fuer Arbeitsstunden erweitert

- Stable Tag und Changelog in README/readme.txt aktualisiert
This commit is contained in:
2026-04-16 21:34:09 +02:00
parent f399cc30bc
commit 7d3d543954
7 changed files with 992 additions and 5 deletions

View File

@@ -17,6 +17,7 @@ use KGV\VereinManager\Repositories\MeterRepository;
use KGV\VereinManager\Repositories\ParcelRepository;
use KGV\VereinManager\Repositories\SectionRepository;
use KGV\VereinManager\Repositories\TenantRepository;
use KGV\VereinManager\Repositories\WorkRepository;
use KGV\VereinManager\Services\ParcelService;
if ( ! defined( 'ABSPATH' ) ) {
@@ -43,6 +44,7 @@ class Admin {
private $assignments;
private $chat;
private $costs;
private $work;
private $parcel_service;
/**
@@ -58,6 +60,7 @@ class Admin {
$this->assignments = new AssignmentRepository();
$this->chat = new ChatRepository();
$this->costs = new CostRepository();
$this->work = new WorkRepository();
$this->parcel_service = new ParcelService();
}
@@ -101,6 +104,7 @@ class Admin {
add_submenu_page( 'kgvvm-dashboard', __( 'Zähler', KGVVM_TEXT_DOMAIN ), __( 'Zähler', KGVVM_TEXT_DOMAIN ), 'edit_zaehler', 'kgvvm-zaehler', array( $this, 'render_meters_page' ) );
add_submenu_page( 'kgvvm-dashboard', __( 'Verbrauch', KGVVM_TEXT_DOMAIN ), __( 'Verbrauch', KGVVM_TEXT_DOMAIN ), $cap, 'kgvvm-consumption', array( $this, 'render_consumption_page' ) );
add_submenu_page( 'kgvvm-dashboard', __( 'Kosten', KGVVM_TEXT_DOMAIN ), __( 'Kosten', KGVVM_TEXT_DOMAIN ), $cap, 'kgvvm-costs', array( $this, 'render_costs_page' ) );
add_submenu_page( 'kgvvm-dashboard', __( 'Arbeitsstunden', KGVVM_TEXT_DOMAIN ), __( 'Arbeitsstunden', KGVVM_TEXT_DOMAIN ), $cap, 'kgvvm-arbeit', array( $this, 'render_work_page' ) );
add_submenu_page( 'kgvvm-dashboard', __( 'Pächter', KGVVM_TEXT_DOMAIN ), __( 'Pächter', KGVVM_TEXT_DOMAIN ), 'edit_paechter', 'kgvvm-paechter', array( $this, 'render_tenants_page' ) );
add_submenu_page( 'kgvvm-dashboard', __( 'Einstellungen', KGVVM_TEXT_DOMAIN ), __( 'Einstellungen', KGVVM_TEXT_DOMAIN ), Roles::SETTINGS_CAP, 'kgvvm-settings', array( $this, 'render_settings_page' ) );
}
@@ -229,6 +233,15 @@ class Admin {
case 'save_settings':
$this->save_settings();
break;
case 'save_work_year_config':
$this->save_work_year_config();
break;
case 'save_work_job':
$this->save_work_job();
break;
case 'save_work_log':
$this->save_work_log();
break;
}
}
@@ -299,6 +312,23 @@ class Admin {
case 'export_readings_csv':
$this->export_readings_csv();
break;
case 'delete_work_job':
$this->require_cap( 'manage_kleingarten' );
if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_work_job_' . $id ) ) {
$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Der Löschvorgang wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
}
$this->work->delete_job( $id );
$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Arbeitsart wurde gelöscht.', KGVVM_TEXT_DOMAIN ), array( 'tab' => 'jobs' ) );
break;
case 'delete_work_log':
$this->require_cap( 'manage_kleingarten' );
$year = absint( isset( $_GET['year'] ) ? $_GET['year'] : current_time( 'Y' ) );
if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_work_log_' . $id ) ) {
$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Der Löschvorgang wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
}
$this->work->delete_log( $id );
$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Arbeitseintrag wurde gelöscht.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
break;
}
}
@@ -4050,4 +4080,401 @@ class Admin {
return add_query_arg( $args, admin_url( 'admin.php' ) );
}
// =========================================================================
// ARBEITSSTUNDEN
// =========================================================================
/**
* Save work year configuration.
*
* @return void
*/
private function save_work_year_config() {
$this->require_cap( 'manage_kleingarten' );
check_admin_referer( 'kgvvm_save_work_year_config' );
$year = absint( isset( $_POST['entry_year'] ) ? $_POST['entry_year'] : 0 );
$required_hours = isset( $_POST['required_hours'] ) ? (float) $_POST['required_hours'] : 0;
$price_per_missing_hour = isset( $_POST['price_per_missing_hour'] ) ? (float) $_POST['price_per_missing_hour'] : 0;
if ( $year < 2000 || $year > 2100 ) {
$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Bitte ein gültiges Jahr eingeben.', KGVVM_TEXT_DOMAIN ) );
}
$this->work->save_year_config( $year, $required_hours, $price_per_missing_hour );
$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Jahreseinstellungen wurden gespeichert.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
}
/**
* Save work job (add/update).
*
* @return void
*/
private function save_work_job() {
$this->require_cap( 'manage_kleingarten' );
check_admin_referer( 'kgvvm_save_work_job' );
$id = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
$name = sanitize_text_field( wp_unslash( isset( $_POST['job_name'] ) ? $_POST['job_name'] : '' ) );
if ( '' === $name ) {
$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Bitte einen Namen für die Arbeitsart eingeben.', KGVVM_TEXT_DOMAIN ), array( 'tab' => 'jobs' ) );
}
if ( $this->work->job_name_exists( $name, $id ) ) {
$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Eine Arbeitsart mit diesem Namen existiert bereits.', KGVVM_TEXT_DOMAIN ), array( 'tab' => 'jobs' ) );
}
$this->work->save_job(
array(
'name' => $name,
'description' => isset( $_POST['job_description'] ) ? wp_unslash( $_POST['job_description'] ) : '',
),
$id
);
$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Arbeitsart wurde gespeichert.', KGVVM_TEXT_DOMAIN ), array( 'tab' => 'jobs' ) );
}
/**
* Save work log entry (add/update).
*
* @return void
*/
private function save_work_log() {
$this->require_cap( 'manage_kleingarten' );
check_admin_referer( 'kgvvm_save_work_log' );
$id = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
$job_id = absint( isset( $_POST['job_id'] ) ? $_POST['job_id'] : 0 );
$work_date = sanitize_text_field( wp_unslash( isset( $_POST['work_date'] ) ? $_POST['work_date'] : '' ) );
$year = $work_date ? (int) substr( $work_date, 0, 4 ) : (int) current_time( 'Y' );
if ( '' === $work_date ) {
$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Bitte ein gültiges Datum eingeben.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
}
$raw_members = isset( $_POST['member_hours'] ) ? (array) wp_unslash( $_POST['member_hours'] ) : array();
$members = array();
foreach ( $raw_members as $uid => $hours ) {
$uid = absint( $uid );
$hours = (float) $hours;
if ( $uid > 0 && $hours > 0 ) {
$members[ $uid ] = $hours;
}
}
if ( empty( $members ) ) {
$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Bitte mindestens ein Mitglied mit Stundenzahl zuordnen.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
}
$this->work->save_log(
array(
'job_id' => $job_id,
'work_date' => $work_date,
'note' => isset( $_POST['note'] ) ? wp_unslash( $_POST['note'] ) : '',
),
$id,
$members
);
$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Arbeitseintrag wurde gespeichert.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
}
/**
* Render the Arbeitsstunden admin page.
*
* @return void
*/
public function render_work_page() {
$this->require_cap( 'manage_kleingarten' );
$tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'logs';
$selected_year = isset( $_GET['year'] ) ? absint( $_GET['year'] ) : (int) current_time( 'Y' );
$selected_year = $selected_year > 0 ? $selected_year : (int) current_time( 'Y' );
$edit_log_id = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
$edit_job_id = absint( isset( $_GET['job_id'] ) ? $_GET['job_id'] : 0 );
$years = $this->work->get_years( $selected_year );
$year_config = $this->work->get_year_config( $selected_year );
$jobs = $this->work->get_jobs();
$all_members = $this->assignments->get_member_users();
$edit_log = null;
$edit_job = null;
if ( $edit_log_id ) {
$edit_log = $this->work->find_log( $edit_log_id );
}
if ( $edit_job_id ) {
$edit_job = $this->work->find_job( $edit_job_id );
}
$logs = $this->work->search_logs(
array(
'year' => $selected_year,
'orderby' => isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : 'work_date',
'order' => isset( $_GET['order'] ) ? sanitize_key( wp_unslash( $_GET['order'] ) ) : 'DESC',
's' => isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '',
)
);
$summary = $this->work->get_member_summary( $selected_year, $all_members );
?>
<div class='wrap'>
<h1 class='wp-heading-inline'><?php echo esc_html__( 'Arbeitsstunden', KGVVM_TEXT_DOMAIN ); ?></h1>
<?php $this->render_notice(); ?>
<?php /* Year filter */ ?>
<form method='get' class='kgvvm-toolbar' style='margin-top:12px;'>
<input type='hidden' name='page' value='kgvvm-arbeit' />
<input type='hidden' name='tab' value='<?php echo esc_attr( $tab ); ?>' />
<div class='kgvvm-filters'>
<label for='kgvvm-work-year'><?php echo esc_html__( 'Jahr', KGVVM_TEXT_DOMAIN ); ?></label>
<select name='year' id='kgvvm-work-year'>
<?php foreach ( $years as $year ) : ?>
<option value='<?php echo esc_attr( $year ); ?>' <?php selected( $selected_year, $year ); ?>><?php echo esc_html( $year ); ?></option>
<?php endforeach; ?>
</select>
<button class='button'><?php echo esc_html__( 'Filtern', KGVVM_TEXT_DOMAIN ); ?></button>
</div>
</form>
<?php /* Year config card */ ?>
<div class='kgvvm-card' style='max-width:560px;margin-top:16px;'>
<h2><?php echo esc_html( sprintf( __( 'Jahreseinstellungen %d', KGVVM_TEXT_DOMAIN ), $selected_year ) ); ?></h2>
<form method='post'>
<?php wp_nonce_field( 'kgvvm_save_work_year_config' ); ?>
<input type='hidden' name='kgvvm_action' value='save_work_year_config' />
<input type='hidden' name='entry_year' value='<?php echo esc_attr( $selected_year ); ?>' />
<table class='form-table kgvvm-form-table'>
<tr>
<th scope='row'><label for='kgvvm-work-required-hours'><?php echo esc_html__( 'Pflichtstunden je Mitglied', KGVVM_TEXT_DOMAIN ); ?></label></th>
<td><input type='number' min='0' step='0.5' name='required_hours' id='kgvvm-work-required-hours'
value='<?php echo esc_attr( $year_config ? (float) $year_config->required_hours : 0 ); ?>' /> <?php echo esc_html__( 'Std.', KGVVM_TEXT_DOMAIN ); ?></td>
</tr>
<tr>
<th scope='row'><label for='kgvvm-work-price'><?php echo esc_html__( 'Preis je fehlender Stunde', KGVVM_TEXT_DOMAIN ); ?></label></th>
<td><input type='number' min='0' step='0.01' name='price_per_missing_hour' id='kgvvm-work-price'
value='<?php echo esc_attr( $year_config ? (float) $year_config->price_per_missing_hour : 0 ); ?>' /> € / Std.</td>
</tr>
</table>
<?php submit_button( __( 'Einstellungen speichern', KGVVM_TEXT_DOMAIN ), 'secondary' ); ?>
</form>
</div>
<?php /* Tabs */ ?>
<nav class='nav-tab-wrapper' style='margin-top:20px;'>
<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'tab' => 'logs' ) ) ); ?>'
class='nav-tab <?php echo 'logs' === $tab ? 'nav-tab-active' : ''; ?>'><?php echo esc_html__( 'Geleistete Arbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'tab' => 'summary' ) ) ); ?>'
class='nav-tab <?php echo 'summary' === $tab ? 'nav-tab-active' : ''; ?>'><?php echo esc_html__( 'Mitgliederübersicht', KGVVM_TEXT_DOMAIN ); ?></a>
<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'tab' => 'jobs' ) ) ); ?>'
class='nav-tab <?php echo 'jobs' === $tab ? 'nav-tab-active' : ''; ?>'><?php echo esc_html__( 'Arbeitsarten', KGVVM_TEXT_DOMAIN ); ?></a>
</nav>
<?php if ( 'jobs' === $tab ) : ?>
<?php /* ---- ARBEITSARTEN ---- */ ?>
<div class='kgvvm-card' style='margin-top:0;border-top:none;padding-top:20px;'>
<h2><?php echo esc_html( $edit_job ? __( 'Arbeitsart bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Neue Arbeitsart', KGVVM_TEXT_DOMAIN ) ); ?></h2>
<form method='post'>
<?php wp_nonce_field( 'kgvvm_save_work_job' ); ?>
<input type='hidden' name='kgvvm_action' value='save_work_job' />
<input type='hidden' name='id' value='<?php echo esc_attr( $edit_job ? $edit_job->id : 0 ); ?>' />
<table class='form-table kgvvm-form-table'>
<tr>
<th scope='row'><label for='kgvvm-job-name'><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></label></th>
<td><input type='text' class='regular-text' name='job_name' id='kgvvm-job-name' required
value='<?php echo esc_attr( $edit_job ? $edit_job->name : '' ); ?>' /></td>
</tr>
<tr>
<th scope='row'><label for='kgvvm-job-description'><?php echo esc_html__( 'Beschreibung', KGVVM_TEXT_DOMAIN ); ?></label></th>
<td><textarea name='job_description' id='kgvvm-job-description' rows='3' class='large-text'><?php echo esc_textarea( $edit_job ? $edit_job->description : '' ); ?></textarea></td>
</tr>
</table>
<?php submit_button( $edit_job ? __( 'Arbeitsart aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Arbeitsart anlegen', KGVVM_TEXT_DOMAIN ) ); ?>
<?php if ( $edit_job ) : ?>
<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'tab' => 'jobs' ) ) ); ?>' class='button'><?php echo esc_html__( 'Abbrechen', KGVVM_TEXT_DOMAIN ); ?></a>
<?php endif; ?>
</form>
</div>
<?php if ( ! empty( $jobs ) ) : ?>
<table class='wp-list-table widefat fixed striped' style='margin-top:16px;'>
<thead>
<tr>
<th><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></th>
<th><?php echo esc_html__( 'Beschreibung', KGVVM_TEXT_DOMAIN ); ?></th>
<th style='width:120px;'><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $jobs as $job ) : ?>
<tr>
<td><strong><?php echo esc_html( $job->name ); ?></strong></td>
<td><?php echo esc_html( $job->description ); ?></td>
<td>
<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'tab' => 'jobs', 'year' => $selected_year, 'job_id' => $job->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
&nbsp;|&nbsp;
<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-arbeit', array( 'kgvvm_action' => 'delete_work_job', 'id' => $job->id, 'year' => $selected_year, 'tab' => 'jobs' ) ), 'kgvvm_delete_work_job_' . $job->id ) ); ?>'
class='kgvvm-delete-link' onclick='return confirm("<?php echo esc_attr__( 'Diese Arbeitsart und alle zugehörigen Einträge wirklich löschen?', KGVVM_TEXT_DOMAIN ); ?>")'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else : ?>
<p class='kgvvm-help'><?php echo esc_html__( 'Noch keine Arbeitsarten angelegt.', KGVVM_TEXT_DOMAIN ); ?></p>
<?php endif; ?>
<?php elseif ( 'summary' === $tab ) : ?>
<?php /* ---- MITGLIEDERÜBERSICHT ---- */ ?>
<div style='margin-top:0;'>
<?php if ( ! $year_config ) : ?>
<p class='notice notice-warning inline'><?php echo esc_html__( 'Für dieses Jahr wurden noch keine Jahreseinstellungen hinterlegt.', KGVVM_TEXT_DOMAIN ); ?></p>
<?php endif; ?>
<table class='wp-list-table widefat fixed striped' style='margin-top:16px;'>
<thead>
<tr>
<th><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></th>
<th><?php echo esc_html__( 'Geleistet (Std.)', KGVVM_TEXT_DOMAIN ); ?></th>
<th><?php echo esc_html__( 'Pflicht (Std.)', KGVVM_TEXT_DOMAIN ); ?></th>
<th><?php echo esc_html__( 'Fehlend (Std.)', KGVVM_TEXT_DOMAIN ); ?></th>
<th><?php echo esc_html__( 'Aufschlag (€)', KGVVM_TEXT_DOMAIN ); ?></th>
</tr>
</thead>
<tbody>
<?php if ( empty( $summary ) ) : ?>
<tr><td colspan='5'><?php echo esc_html__( 'Keine Mitglieder gefunden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
<?php else : ?>
<?php foreach ( $summary as $row ) : ?>
<tr>
<td><?php echo esc_html( $row->display_name ); ?></td>
<td><?php echo esc_html( number_format_i18n( $row->completed_hours, 2 ) ); ?></td>
<td><?php echo esc_html( number_format_i18n( $row->required_hours, 2 ) ); ?></td>
<td><?php echo $row->missing_hours > 0 ? '<strong style="color:#c00">' . esc_html( number_format_i18n( $row->missing_hours, 2 ) ) . '</strong>' : esc_html( number_format_i18n( 0, 2 ) ); ?></td>
<td><?php echo $row->surcharge > 0 ? '<strong style="color:#c00">' . esc_html( $this->format_currency( $row->surcharge ) ) . '</strong>' : '—'; ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<?php else : ?>
<?php /* ---- GELEISTETE ARBEITEN (logs) ---- */ ?>
<?php /* New/edit log form */ ?>
<div class='kgvvm-card' style='margin-top:0;border-top:none;padding-top:20px;'>
<h2><?php echo esc_html( $edit_log ? __( 'Arbeitseintrag bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Neuer Arbeitseintrag', KGVVM_TEXT_DOMAIN ) ); ?></h2>
<form method='post'>
<?php wp_nonce_field( 'kgvvm_save_work_log' ); ?>
<input type='hidden' name='kgvvm_action' value='save_work_log' />
<input type='hidden' name='id' value='<?php echo esc_attr( $edit_log ? $edit_log->id : 0 ); ?>' />
<table class='form-table kgvvm-form-table'>
<tr>
<th scope='row'><label for='kgvvm-log-date'><?php echo esc_html__( 'Datum', KGVVM_TEXT_DOMAIN ); ?></label></th>
<td><input type='date' name='work_date' id='kgvvm-log-date' required
value='<?php echo esc_attr( $edit_log ? esc_attr( $edit_log->work_date ) : current_time( 'Y-m-d' ) ); ?>' /></td>
</tr>
<tr>
<th scope='row'><label for='kgvvm-log-job'><?php echo esc_html__( 'Arbeitsart', KGVVM_TEXT_DOMAIN ); ?></label></th>
<td>
<select name='job_id' id='kgvvm-log-job'>
<option value='0'><?php echo esc_html__( '— keine Zuordnung —', KGVVM_TEXT_DOMAIN ); ?></option>
<?php foreach ( $jobs as $job ) : ?>
<option value='<?php echo esc_attr( $job->id ); ?>' <?php selected( $edit_log ? (int) $edit_log->job_id : 0, (int) $job->id ); ?>><?php echo esc_html( $job->name ); ?></option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope='row'><label><?php echo esc_html__( 'Mitglieder & Stunden', KGVVM_TEXT_DOMAIN ); ?></label></th>
<td>
<?php
$existing_member_hours = array();
if ( $edit_log && ! empty( $edit_log->members ) ) {
foreach ( $edit_log->members as $m ) {
$existing_member_hours[ (int) $m->user_id ] = (float) $m->hours;
}
}
?>
<table class='kgvvm-subtable'>
<thead><tr><th><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></th><th><?php echo esc_html__( 'Stunden', KGVVM_TEXT_DOMAIN ); ?></th></tr></thead>
<tbody>
<?php foreach ( $all_members as $member ) : ?>
<?php
$uid = (int) $member->ID;
$hours_val = isset( $existing_member_hours[ $uid ] ) ? $existing_member_hours[ $uid ] : '';
?>
<tr>
<td><?php echo esc_html( $member->display_name ); ?></td>
<td><input type='number' min='0' step='0.25' style='width:80px;'
name='member_hours[<?php echo esc_attr( $uid ); ?>]'
value='<?php echo esc_attr( $hours_val ); ?>'
placeholder='0' /></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p class='description'><?php echo esc_html__( 'Nur Mitglieder mit eingetragenen Stunden werden gespeichert.', KGVVM_TEXT_DOMAIN ); ?></p>
</td>
</tr>
<tr>
<th scope='row'><label for='kgvvm-log-note'><?php echo esc_html__( 'Notiz', KGVVM_TEXT_DOMAIN ); ?></label></th>
<td><textarea name='note' id='kgvvm-log-note' rows='3' class='large-text'><?php echo esc_textarea( $edit_log ? $edit_log->note : '' ); ?></textarea></td>
</tr>
</table>
<?php submit_button( $edit_log ? __( 'Arbeitseintrag aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Arbeitseintrag speichern', KGVVM_TEXT_DOMAIN ) ); ?>
<?php if ( $edit_log ) : ?>
<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year ) ) ); ?>' class='button'><?php echo esc_html__( 'Abbrechen', KGVVM_TEXT_DOMAIN ); ?></a>
<?php endif; ?>
</form>
</div>
<?php /* Log list */ ?>
<?php if ( ! empty( $logs ) ) : ?>
<table class='wp-list-table widefat fixed striped' style='margin-top:16px;'>
<thead>
<tr>
<th><?php echo esc_html__( 'Datum', KGVVM_TEXT_DOMAIN ); ?></th>
<th><?php echo esc_html__( 'Arbeitsart', KGVVM_TEXT_DOMAIN ); ?></th>
<th><?php echo esc_html__( 'Mitglieder', KGVVM_TEXT_DOMAIN ); ?></th>
<th><?php echo esc_html__( 'Notiz', KGVVM_TEXT_DOMAIN ); ?></th>
<th style='width:120px;'><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $logs as $log ) : ?>
<?php $log_members = $this->work->get_log_members( $log->id ); ?>
<tr>
<td><?php echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $log->work_date ) ) ); ?></td>
<td><?php echo esc_html( $log->job_name ?: '—' ); ?></td>
<td>
<?php foreach ( $log_members as $lm ) :
$u = get_userdata( $lm->user_id );
if ( $u ) : ?>
<?php echo esc_html( $u->display_name ); ?>: <strong><?php echo esc_html( number_format_i18n( (float) $lm->hours, 2 ) ); ?> Std.</strong><br>
<?php endif; ?>
<?php endforeach; ?>
</td>
<td><?php echo esc_html( $log->note ); ?></td>
<td>
<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'id' => $log->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
&nbsp;|&nbsp;
<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-arbeit', array( 'kgvvm_action' => 'delete_work_log', 'id' => $log->id, 'year' => $selected_year ) ), 'kgvvm_delete_work_log_' . $log->id ) ); ?>'
class='kgvvm-delete-link' onclick='return confirm("<?php echo esc_attr__( 'Diesen Arbeitseintrag wirklich löschen?', KGVVM_TEXT_DOMAIN ); ?>")'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else : ?>
<p class='kgvvm-help' style='margin-top:12px;'><?php echo esc_html( sprintf( __( 'Keine Arbeitseinträge für %d vorhanden.', KGVVM_TEXT_DOMAIN ), $selected_year ) ); ?></p>
<?php endif; ?>
<?php endif; ?>
</div>
<?php
}
}