Update repo
This commit is contained in:
+26
-26
@@ -1,27 +1,27 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up admin-specific hooks.
|
||||
*/
|
||||
function mystat_admin_init_setup() {
|
||||
add_filter( 'upload_mimes', 'mystat_add_gpx_mime_type' );
|
||||
add_filter( 'wp_check_filetype_and_ext', 'mystat_fix_gpx_upload_permission', 10, 4 );
|
||||
mystat_register_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin-specific CSS.
|
||||
*
|
||||
* @param string $hook The current admin page hook.
|
||||
*/
|
||||
function mystat_enqueue_admin_styles( $hook ) {
|
||||
global $mystat_plugin_hooks;
|
||||
|
||||
if ( in_array( $hook, $mystat_plugin_hooks, true ) ) {
|
||||
$plugin_version = '1.0'; // You can use filemtime() for cache-busting in development
|
||||
wp_enqueue_style( 'mystat-admin-styles', MYSTAT_PLUGIN_URL . 'assets/css/admin.css', array(), $plugin_version );
|
||||
}
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up admin-specific hooks.
|
||||
*/
|
||||
function statpress_admin_init_setup() {
|
||||
add_filter( 'upload_mimes', 'statpress_add_gpx_mime_type' );
|
||||
add_filter( 'wp_check_filetype_and_ext', 'statpress_fix_gpx_upload_permission', 10, 4 );
|
||||
statpress_register_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin-specific CSS.
|
||||
*
|
||||
* @param string $hook The current admin page hook.
|
||||
*/
|
||||
function statpress_enqueue_admin_styles( $hook ) {
|
||||
global $statpress_plugin_hooks;
|
||||
|
||||
if ( is_array( $statpress_plugin_hooks ) && in_array( $hook, $statpress_plugin_hooks, true ) ) {
|
||||
$plugin_version = '1.0'; // You can use filemtime() for cache-busting in development
|
||||
wp_enqueue_style( 'statpress-admin-styles', STATPRESS_PLUGIN_URL . 'assets/css/admin.css', array(), $plugin_version );
|
||||
}
|
||||
}
|
||||
+122
-122
@@ -1,123 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin Menu setup for the plugin.
|
||||
*
|
||||
* @package WordPress Activity Stats
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the main menu and sub-menu pages for the plugin.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function mystat_add_admin_menu() {
|
||||
global $mystat_plugin_hooks;
|
||||
|
||||
$mystat_plugin_hooks[] = add_menu_page(
|
||||
'Moje Statystyki', // Tytuł strony
|
||||
'Statystyki', // Tytuł w menu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'moje-statystyki', // Slug menu
|
||||
'mystat_dashboard_page', // Funkcja renderująca stronę główną (dashboard)
|
||||
'dashicons-chart-line', // Ikona
|
||||
6 // Pozycja
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
'moje-statystyki', // Slug rodzica
|
||||
'Dodaj Nowy Trening', // Tytuł strony
|
||||
'Nowy trening', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'mystat-nowy-trening', // Slug podmenu
|
||||
'mystat_add_new_page' // Funkcja renderująca stronę dodawania
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
'moje-statystyki',
|
||||
'Typy Wydarzeń',
|
||||
'Typy wydarzeń',
|
||||
'manage_options',
|
||||
'mystat-event-types',
|
||||
'mystat_event_types_page'
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
'moje-statystyki',
|
||||
'Sprzęt',
|
||||
'Sprzęt',
|
||||
'manage_options',
|
||||
'mystat-equipment',
|
||||
'mystat_equipment_page'
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
'moje-statystyki',
|
||||
'Cele',
|
||||
'Cele',
|
||||
'manage_options',
|
||||
'mystat-goals',
|
||||
'mystat_goals_page'
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
null, 'Dziennik Serwisowy', 'Dziennik Serwisowy', 'manage_options', 'mystat-equipment-details', 'mystat_equipment_details_page'
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
null, // Ukryta strona, nie pojawia się w menu
|
||||
'Szczegóły Treningu', // Tytuł strony
|
||||
'Szczegóły Treningu', // Tytuł w menu (nieistotny)
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'mystat-view-activity', // Slug podmenu
|
||||
'mystat_view_activity_page' // Funkcja renderująca
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
null, // Ukryta strona
|
||||
'Edytuj Trening', // Tytuł strony
|
||||
'Edytuj Trening', // Tytuł w menu (nieistotny)
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'mystat-edit-activity', // Slug podmenu
|
||||
'mystat_edit_activity_page' // Funkcja renderująca
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
'moje-statystyki', // Slug rodzica
|
||||
'Podsumowanie Roczne', // Tytuł strony
|
||||
'Podsumowanie Roczne', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'mystat-yearly-summary', // Slug podmenu
|
||||
'mystat_yearly_summary_page'// Funkcja renderująca
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
'moje-statystyki', // Slug rodzica
|
||||
'Infografika', // Tytuł strony
|
||||
'Infografika', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'mystat-infographic', // Slug podmenu
|
||||
'mystat_infographic_page' // Funkcja renderująca
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
'moje-statystyki', // Slug rodzica
|
||||
'Import CSV', // Tytuł strony
|
||||
'Import CSV', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'mystat-import-csv', // Slug podmenu
|
||||
'mystat_import_csv_page' // Funkcja renderująca
|
||||
);
|
||||
|
||||
$mystat_plugin_hooks[] = add_submenu_page(
|
||||
'moje-statystyki', // Slug rodzica
|
||||
'Ustawienia', // Tytuł strony
|
||||
'Ustawienia', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'mystat-settings', // Slug podmenu
|
||||
'mystat_settings_page' // Funkcja renderująca
|
||||
);
|
||||
<?php
|
||||
/**
|
||||
* Admin Menu setup for the plugin.
|
||||
*
|
||||
* @package WordPress Activity Stats
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the main menu and sub-menu pages for the plugin.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function statpress_add_admin_menu() {
|
||||
global $statpress_plugin_hooks;
|
||||
|
||||
$statpress_plugin_hooks[] = add_menu_page(
|
||||
'StatPress Dashboard', // Tytuł strony
|
||||
'StatPress', // Tytuł w menu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'statpress-dashboard', // Slug menu
|
||||
'statpress_dashboard_page', // Funkcja renderująca stronę główną (dashboard)
|
||||
'dashicons-chart-line', // Ikona
|
||||
6 // Pozycja
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
'statpress-dashboard', // Slug rodzica
|
||||
'Dodaj Nowy Trening', // Tytuł strony
|
||||
'Nowy trening', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'statpress-add-new', // Slug podmenu
|
||||
'statpress_add_new_page' // Funkcja renderująca stronę dodawania
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
'statpress-dashboard',
|
||||
'Typy Wydarzeń',
|
||||
'Typy wydarzeń',
|
||||
'manage_options',
|
||||
'statpress-event-types',
|
||||
'statpress_event_types_page'
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
'statpress-dashboard',
|
||||
'Sprzęt',
|
||||
'Sprzęt',
|
||||
'manage_options',
|
||||
'statpress-equipment',
|
||||
'statpress_equipment_page'
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
'statpress-dashboard',
|
||||
'Cele',
|
||||
'Cele',
|
||||
'manage_options',
|
||||
'statpress-goals',
|
||||
'statpress_goals_page'
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
null, 'Dziennik Serwisowy', 'Dziennik Serwisowy', 'manage_options', 'statpress-equipment-details', 'statpress_equipment_details_page'
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
null, // Ukryta strona, nie pojawia się w menu
|
||||
'Szczegóły Treningu', // Tytuł strony
|
||||
'Szczegóły Treningu', // Tytuł w menu (nieistotny)
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'statpress-view-activity', // Slug podmenu
|
||||
'statpress_view_activity_page' // Funkcja renderująca
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
null, // Ukryta strona
|
||||
'Edytuj Trening', // Tytuł strony
|
||||
'Edytuj Trening', // Tytuł w menu (nieistotny)
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'statpress-edit-activity', // Slug podmenu
|
||||
'statpress_edit_activity_page' // Funkcja renderująca
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
'statpress-dashboard', // Slug rodzica
|
||||
'Podsumowanie Roczne', // Tytuł strony
|
||||
'Podsumowanie Roczne', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'statpress-yearly-summary', // Slug podmenu
|
||||
'statpress_yearly_summary_page'// Funkcja renderująca
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
'statpress-dashboard', // Slug rodzica
|
||||
'Infografika', // Tytuł strony
|
||||
'Infografika', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'statpress-infographic', // Slug podmenu
|
||||
'statpress_infographic_page' // Funkcja renderująca
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
'statpress-dashboard', // Slug rodzica
|
||||
'Import CSV', // Tytuł strony
|
||||
'Import CSV', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'statpress-import-csv', // Slug podmenu
|
||||
'statpress_import_csv_page' // Funkcja renderująca
|
||||
);
|
||||
|
||||
$statpress_plugin_hooks[] = add_submenu_page(
|
||||
'statpress-dashboard', // Slug rodzica
|
||||
'Ustawienia', // Tytuł strony
|
||||
'Ustawienia', // Tytuł w podmenu
|
||||
'manage_options', // Wymagane uprawnienia
|
||||
'statpress-settings', // Slug podmenu
|
||||
'statpress_settings_page' // Funkcja renderująca
|
||||
);
|
||||
}
|
||||
@@ -1,244 +1,304 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_add_new_page() {
|
||||
echo '<div class="wrap"><h1>Dodaj Nowy Trening</h1>';
|
||||
// Obsługa zapisu formularza (musi być przed renderowaniem, aby wyświetlić komunikat)
|
||||
statpress_handle_activity_form_submission();
|
||||
// Formularz dodawania
|
||||
statpress_render_add_form();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
function statpress_edit_activity_page() {
|
||||
global $wpdb;
|
||||
$activity_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
|
||||
|
||||
if ( $activity_id === 0 ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie podano ID aktywności do edycji.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle form submission for update
|
||||
statpress_handle_activity_form_submission();
|
||||
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$activity = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_activities WHERE id = %d", $activity_id ) );
|
||||
|
||||
if ( ! $activity ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie znaleziono aktywności o podanym ID.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div class="wrap"><h1>Edytuj Trening</h1>';
|
||||
statpress_render_add_form( $activity );
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Obsługa zapisu nowego lub edytowanego wpisu do bazy danych
|
||||
*/
|
||||
function statpress_handle_activity_form_submission() {
|
||||
global $wpdb;
|
||||
|
||||
// Sprawdź czy formularz został wysłany
|
||||
if ( ! isset( $_POST['statpress_submit_activity'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$activity_id = isset( $_POST['activity_id'] ) ? intval( $_POST['activity_id'] ) : 0;
|
||||
$nonce_action = $activity_id > 0 ? 'statpress_edit_entry_' . $activity_id : 'statpress_add_entry';
|
||||
|
||||
// Weryfikacja bezpieczeństwa (Nonce)
|
||||
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], $nonce_action ) ) {
|
||||
echo '<div class="notice notice-error"><p>Błąd weryfikacji bezpieczeństwa formularza.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the refactored function to save data.
|
||||
// We can pass $_POST directly as the function will sanitize it.
|
||||
$result = statpress_save_activity_data( $_POST, $activity_id );
|
||||
|
||||
if ( $activity_id > 0 ) {
|
||||
$message = 'Trening zaktualizowany pomyślnie!';
|
||||
} else {
|
||||
$message = 'Trening dodany pomyślnie!';
|
||||
}
|
||||
|
||||
if ( $result ) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $message ) . '</p></div>';
|
||||
} else {
|
||||
echo '<div class="notice notice-error is-dismissible"><p>Wystąpił błąd podczas zapisu do bazy.</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderowanie formularza HTML
|
||||
*/
|
||||
function statpress_render_add_form( $activity = null ) {
|
||||
// Enqueue media scripts for the uploader
|
||||
wp_enqueue_media();
|
||||
|
||||
global $wpdb;
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
$table_event_types = $wpdb->prefix . 'statpress_event_types';
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
$categories = $wpdb->get_results( "SELECT * FROM $table_categories ORDER BY name ASC" );
|
||||
$event_types = $wpdb->get_results( "SELECT * FROM $table_event_types ORDER BY name ASC" );
|
||||
$equipment_list = $wpdb->get_results( "SELECT * FROM $table_equipment ORDER BY name ASC" );
|
||||
|
||||
$is_edit_mode = ! is_null( $activity );
|
||||
$nonce_action = $is_edit_mode ? 'statpress_edit_entry_' . $activity->id : 'statpress_add_entry';
|
||||
$form_title = $is_edit_mode ? 'Edytuj Aktywność' : 'Dodaj Nową Aktywność';
|
||||
$button_text = $is_edit_mode ? 'Zaktualizuj Trening' : 'Zapisz Trening';
|
||||
|
||||
?>
|
||||
<div class="postbox">
|
||||
<div class="postbox-header"><h2 class="hndle"><?php echo esc_html( $form_title ); ?></h2></div>
|
||||
<div class="inside">
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="activity_id" value="<?php echo $is_edit_mode ? esc_attr( $activity->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( $nonce_action, '_wpnonce' ); ?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><label for="title">Tytuł</label></th>
|
||||
<td><input type="text" name="title" id="title" class="large-text" placeholder="np. Poranny trening w lesie" value="<?php echo $is_edit_mode ? esc_attr( $activity->title ) : ''; ?>" required></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="category_id">Kategoria</label></th>
|
||||
<td>
|
||||
<select name="category_id" id="category_id" required>
|
||||
<?php foreach ( $categories as $cat ) : ?>
|
||||
<option value="<?php echo esc_attr( $cat->id ); ?>" <?php echo $is_edit_mode ? selected( $activity->category_id, $cat->id, false ) : selected( $cat->name, 'Rower', false ); ?>><?php echo esc_html( $cat->name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="date">Data</label></th>
|
||||
<td><input type="date" name="date" id="date" value="<?php echo $is_edit_mode ? esc_attr( $activity->date ) : current_time( 'Y-m-d' ); ?>" class="regular-text" required></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="distance">Dystans (km)</label></th>
|
||||
<td><input type="text" name="distance" id="distance" class="regular-text" placeholder="0,00" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->distance, 2, ',', '' ) ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="duration">Czas (HH:MM:SS)</label></th>
|
||||
<td><input type="time" name="duration" id="duration" step="1" value="<?php echo $is_edit_mode ? esc_attr( $activity->duration ) : '00:00:00'; ?>" class="regular-text"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="calories">Kalorie (kcal)</label></th>
|
||||
<td><input type="number" name="calories" id="calories" class="regular-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->calories ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="event_type_id">Typ wydarzenia</label></th>
|
||||
<td>
|
||||
<select name="event_type_id" id="event_type_id">
|
||||
<option value="">-- Wybierz --</option>
|
||||
<?php foreach ( $event_types as $type ) : ?>
|
||||
<option value="<?php echo esc_attr( $type->id ); ?>" <?php echo $is_edit_mode ? selected( $activity->event_type_id, $type->id, false ) : selected( $type->name, 'Trening', false ); ?>><?php echo esc_html( $type->name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="equipment_id">Sprzęt</label></th>
|
||||
<td>
|
||||
<select name="equipment_id" id="equipment_id">
|
||||
<option value="">-- Wybierz --</option>
|
||||
<?php foreach ( $equipment_list as $item ) : ?>
|
||||
<option value="<?php echo esc_attr( $item->id ); ?>" <?php if ( $is_edit_mode ) {
|
||||
selected( $activity->equipment_id, $item->id );} ?>><?php echo esc_html( $item->name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="form-field">
|
||||
<td colspan="2"><hr><h4>Dane szczegółowe (opcjonalne)</h4></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="avg_speed">Śr. prędkość (km/h)</label></th>
|
||||
<td><input type="text" name="avg_speed" id="avg_speed" class="small-text" placeholder="0,0" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->avg_speed, 2, ',', '' ) ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="max_speed">Maks. prędkość (km/h)</label></th>
|
||||
<td><input type="text" name="max_speed" id="max_speed" class="small-text" placeholder="0,0" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->max_speed, 2, ',', '' ) ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="avg_heart_rate">Śr. tętno</label></th>
|
||||
<td><input type="number" name="avg_heart_rate" id="avg_heart_rate" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->avg_heart_rate ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="max_heart_rate">Maks. tętno</label></th>
|
||||
<td><input type="number" name="max_heart_rate" id="max_heart_rate" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_heart_rate ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="avg_cadence">Śr. rytm pedałowania</label></th>
|
||||
<td><input type="number" name="avg_cadence" id="avg_cadence" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->avg_cadence ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="max_cadence">Maks. rytm pedałowania</label></th>
|
||||
<td><input type="number" name="max_cadence" id="max_cadence" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_cadence ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="total_elevation_gain">Całkowity wznios (m)</label></th>
|
||||
<td><input type="number" name="total_elevation_gain" id="total_elevation_gain" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->total_elevation_gain ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="total_elevation_loss">Całkowity spadek (m)</label></th>
|
||||
<td><input type="number" name="total_elevation_loss" id="total_elevation_loss" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->total_elevation_loss ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="min_altitude">Min. wysokość (m)</labe></th>
|
||||
<td><input type="number" name="min_altitude" id="min_altitude" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->min_altitude ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="max_altitude">Maks. wysokość (m)</label></th>
|
||||
<td><input type="number" name="max_altitude" id="max_altitude" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_altitude ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="comment">Komentarz</label></th>
|
||||
<td><textarea name="comment" id="comment" rows="3" class="large-text"><?php echo $is_edit_mode ? esc_textarea( $activity->comment ) : ''; ?></textarea></td>
|
||||
</tr>
|
||||
<tr class="form-field">
|
||||
<td colspan="2"><hr><h4>Linki zewnętrzne (opcjonalne)</h4></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="strava_url">Link do Strava</label></th>
|
||||
<td><input type="url" name="strava_url" id="strava_url" class="large-text" placeholder="https://www.strava.com/activities/..." value="<?php echo $is_edit_mode ? esc_attr( $activity->strava_url ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="gpx_url">Link do pliku GPX</label></th>
|
||||
<td>
|
||||
<input type="text" name="gpx_url" id="gpx_url" class="large-text" placeholder="Wklej URL lub wgraj plik..." value="<?php echo $is_edit_mode ? esc_attr( $activity->gpx_url ) : ''; ?>">
|
||||
<button type="button" class="button" id="upload_gpx_button" style="margin-top: 5px;">Wgraj lub wybierz plik</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="submit">
|
||||
<input type="submit" name="statpress_submit_activity" id="submit" class="button button-primary" value="<?php echo esc_attr( $button_text ); ?>">
|
||||
</p>
|
||||
</form>
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
$('#upload_gpx_button').click(function(e) {
|
||||
e.preventDefault();
|
||||
var gpx_uploader = wp.media({
|
||||
title: 'Wybierz plik GPX',
|
||||
button: { text: 'Użyj tego pliku' },
|
||||
multiple: false,
|
||||
library: { type: ['application/gpx+xml', 'application/xml', 'text/plain'] }
|
||||
})
|
||||
.on('select', function() {
|
||||
var attachment = gpx_uploader.state().get('selection').first().toJSON();
|
||||
$('#gpx_url').val(attachment.url).trigger('change');
|
||||
}).open();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_add_new_page() {
|
||||
echo '<div class="wrap"><h1>Dodaj Nowy Trening</h1>';
|
||||
// Obsługa zapisu formularza (musi być przed renderowaniem, aby wyświetlić komunikat)
|
||||
statpress_handle_activity_form_submission();
|
||||
// Formularz dodawania
|
||||
statpress_render_add_form();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
function statpress_edit_activity_page() {
|
||||
global $wpdb;
|
||||
$activity_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
|
||||
|
||||
if ( $activity_id === 0 ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie podano ID aktywności do edycji.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle form submission for update
|
||||
statpress_handle_activity_form_submission();
|
||||
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$activity = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_activities WHERE id = %d", $activity_id ) );
|
||||
|
||||
if ( ! $activity ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie znaleziono aktywności o podanym ID.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div class="wrap"><h1>Edytuj Trening</h1>';
|
||||
statpress_render_add_form( $activity );
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Obsługa zapisu nowego lub edytowanego wpisu do bazy danych
|
||||
*/
|
||||
function statpress_handle_activity_form_submission() {
|
||||
global $wpdb;
|
||||
|
||||
// Sprawdź czy formularz został wysłany
|
||||
if ( ! isset( $_POST['statpress_submit_activity'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$activity_id = isset( $_POST['activity_id'] ) ? intval( $_POST['activity_id'] ) : 0;
|
||||
$nonce_action = $activity_id > 0 ? 'statpress_edit_entry_' . $activity_id : 'statpress_add_entry';
|
||||
|
||||
// Weryfikacja bezpieczeństwa (Nonce)
|
||||
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], $nonce_action ) ) {
|
||||
echo '<div class="notice notice-error"><p>Błąd weryfikacji bezpieczeństwa formularza.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the refactored function to save data.
|
||||
// We can pass $_POST directly as the function will sanitize it.
|
||||
$result = statpress_save_activity_data( $_POST, $activity_id );
|
||||
|
||||
if ( $activity_id > 0 ) {
|
||||
$message = 'Trening zaktualizowany pomyślnie!';
|
||||
} else {
|
||||
$message = 'Trening dodany pomyślnie!';
|
||||
}
|
||||
|
||||
if ( $result ) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $message ) . '</p></div>';
|
||||
} else {
|
||||
echo '<div class="notice notice-error is-dismissible"><p>Wystąpił błąd podczas zapisu do bazy.</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderowanie formularza HTML
|
||||
*/
|
||||
function statpress_render_add_form( $activity = null ) {
|
||||
// Enqueue media scripts for the uploader
|
||||
wp_enqueue_media();
|
||||
|
||||
global $wpdb;
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
$table_event_types = $wpdb->prefix . 'statpress_event_types';
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
$categories = $wpdb->get_results( "SELECT * FROM $table_categories ORDER BY name ASC" );
|
||||
$event_types = $wpdb->get_results( "SELECT * FROM $table_event_types ORDER BY name ASC" );
|
||||
$equipment_list = $wpdb->get_results( "SELECT * FROM $table_equipment ORDER BY name ASC" );
|
||||
|
||||
$is_edit_mode = ! is_null( $activity );
|
||||
$nonce_action = $is_edit_mode ? 'statpress_edit_entry_' . $activity->id : 'statpress_add_entry';
|
||||
$form_title = $is_edit_mode ? 'Edytuj Aktywność' : 'Dodaj Nową Aktywność';
|
||||
$button_text = $is_edit_mode ? 'Zaktualizuj Trening' : 'Zapisz Trening';
|
||||
|
||||
?>
|
||||
<div class="postbox">
|
||||
<div class="postbox-header"><h2 class="hndle"><?php echo esc_html( $form_title ); ?></h2></div>
|
||||
<div class="inside">
|
||||
<form method="post" action="">
|
||||
<input type="hidden" name="activity_id" value="<?php echo $is_edit_mode ? esc_attr( $activity->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( $nonce_action, '_wpnonce' ); ?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><label for="title">Tytuł</label></th>
|
||||
<td><input type="text" name="title" id="title" class="large-text" placeholder="np. Poranny trening w lesie" value="<?php echo $is_edit_mode ? esc_attr( $activity->title ) : ''; ?>" required></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="gpx_url">Link do pliku GPX</label></th>
|
||||
<td>
|
||||
<input type="text" name="gpx_url" id="gpx_url" class="large-text" placeholder="Wklej URL lub wgraj plik..." value="<?php echo $is_edit_mode ? esc_attr( $activity->gpx_url ) : ''; ?>" style="width: calc(100% - 150px); margin-right: 10px;">
|
||||
<span id="gpx_parse_status"></span>
|
||||
<button type="button" class="button" id="upload_gpx_button" style="margin-top: 5px;">Wgraj lub wybierz plik</button>
|
||||
<p class="description">Wklej link lub wgraj plik GPX, aby automatycznie uzupełnić pola poniżej.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="padding: 15px 0 5px;"><hr></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="category_id">Kategoria</label></th>
|
||||
<td>
|
||||
<select name="category_id" id="category_id" required>
|
||||
<?php foreach ( $categories as $cat ) : ?>
|
||||
<option value="<?php echo esc_attr( $cat->id ); ?>" <?php echo $is_edit_mode ? selected( $activity->category_id, $cat->id, false ) : selected( $cat->name, 'Rower', false ); ?>><?php echo esc_html( $cat->name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="date">Data</label></th>
|
||||
<td><input type="date" name="date" id="date" value="<?php echo $is_edit_mode ? esc_attr( $activity->date ) : current_time( 'Y-m-d' ); ?>" class="regular-text" required></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="distance">Dystans (km)</label></th>
|
||||
<td><input type="text" name="distance" id="distance" class="regular-text" placeholder="0,00" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->distance, 2, ',', '' ) ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="duration">Czas (HH:MM:SS)</label></th>
|
||||
<td><input type="time" name="duration" id="duration" step="1" value="<?php echo $is_edit_mode ? esc_attr( $activity->duration ) : '00:00:00'; ?>" class="regular-text"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="calories">Kalorie (kcal)</label></th>
|
||||
<td><input type="number" name="calories" id="calories" class="regular-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->calories ) : ''; ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="event_type_id">Typ wydarzenia</label></th>
|
||||
<td>
|
||||
<select name="event_type_id" id="event_type_id">
|
||||
<option value="">-- Wybierz --</option>
|
||||
<?php foreach ( $event_types as $type ) : ?>
|
||||
<option value="<?php echo esc_attr( $type->id ); ?>" <?php echo $is_edit_mode ? selected( $activity->event_type_id, $type->id, false ) : selected( $type->name, 'Trening', false ); ?>><?php echo esc_html( $type->name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="equipment_id">Sprzęt</label></th>
|
||||
<td>
|
||||
<select name="equipment_id" id="equipment_id">
|
||||
<option value="">-- Wybierz --</option>
|
||||
<?php foreach ( $equipment_list as $item ) : ?>
|
||||
<option value="<?php echo esc_attr( $item->id ); ?>" <?php if ( $is_edit_mode ) {
|
||||
selected( $activity->equipment_id, $item->id );} ?>><?php echo esc_html( $item->name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="padding: 20px 0;">
|
||||
<hr>
|
||||
<h3 style="font-size: 1.2em; margin: 1em 0;">Dane szczegółowe (opcjonalne)</h3>
|
||||
<div class="statpress-form-grid">
|
||||
<div class="statpress-form-group">
|
||||
<h3><span class="dashicons dashicons-dashboard" style="vertical-align: middle;"></span> Prędkość</h3>
|
||||
<p><label for="avg_speed">Średnia (km/h)</label><input type="text" name="avg_speed" id="avg_speed" placeholder="0,0" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->avg_speed, 2, ',', '' ) ) : ''; ?>"></p>
|
||||
<p><label for="max_speed">Maksymalna (km/h)</label><input type="text" name="max_speed" id="max_speed" placeholder="0,0" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->max_speed, 2, ',', '' ) ) : ''; ?>"></p>
|
||||
</div>
|
||||
<div class="statpress-form-group">
|
||||
<h3><span class="dashicons dashicons-heart" style="vertical-align: middle;"></span> Tętno</h3>
|
||||
<p><label for="avg_heart_rate">Średnie (bpm)</label><input type="number" name="avg_heart_rate" id="avg_heart_rate" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->avg_heart_rate ) : ''; ?>"></p>
|
||||
<p><label for="max_heart_rate">Maksymalne (bpm)</label><input type="number" name="max_heart_rate" id="max_heart_rate" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_heart_rate ) : ''; ?>"></p>
|
||||
</div>
|
||||
<div class="statpress-form-group">
|
||||
<h3><span class="dashicons dashicons-update" style="vertical-align: middle;"></span> Rytm</h3>
|
||||
<p><label for="avg_cadence">Średni (rpm)</label><input type="number" name="avg_cadence" id="avg_cadence" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->avg_cadence ) : ''; ?>"></p>
|
||||
<p><label for="max_cadence">Maksymalny (rpm)</label><input type="number" name="max_cadence" id="max_cadence" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_cadence ) : ''; ?>"></p>
|
||||
</div>
|
||||
<div class="statpress-form-group">
|
||||
<h3><span class="dashicons dashicons-chart-area" style="vertical-align: middle;"></span> Wysokość</h3>
|
||||
<p><label for="total_elevation_gain">Suma wzniosów (m)</label><input type="number" name="total_elevation_gain" id="total_elevation_gain" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->total_elevation_gain ) : ''; ?>"></p>
|
||||
<p><label for="total_elevation_loss">Suma spadków (m)</label><input type="number" name="total_elevation_loss" id="total_elevation_loss" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->total_elevation_loss ) : ''; ?>"></p>
|
||||
<p><label for="min_altitude">Min. wysokość (m n.p.m.)</label><input type="number" name="min_altitude" id="min_altitude" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->min_altitude ) : ''; ?>"></p>
|
||||
<p><label for="max_altitude">Maks. wysokość (m n.p.m.)</label><input type="number" name="max_altitude" id="max_altitude" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_altitude ) : ''; ?>"></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr style="margin-top: 2em;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="comment">Komentarz</label></th>
|
||||
<td><textarea name="comment" id="comment" rows="3" class="large-text"><?php echo $is_edit_mode ? esc_textarea( $activity->comment ) : ''; ?></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="strava_url">Link do Strava</label></th>
|
||||
<td><input type="url" name="strava_url" id="strava_url" class="large-text" placeholder="https://www.strava.com/activities/..." value="<?php echo $is_edit_mode ? esc_attr( $activity->strava_url ) : ''; ?>"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="submit">
|
||||
<input type="submit" name="statpress_submit_activity" id="submit" class="button button-primary" value="<?php echo esc_attr( $button_text ); ?>">
|
||||
</p>
|
||||
</form>
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
$('#upload_gpx_button').click(function(e) {
|
||||
e.preventDefault();
|
||||
var gpx_uploader = wp.media({
|
||||
title: 'Wybierz plik GPX',
|
||||
button: { text: 'Użyj tego pliku' },
|
||||
multiple: false,
|
||||
library: { type: ['application/gpx+xml', 'application/xml', 'text/plain'] }
|
||||
})
|
||||
.on('select', function() {
|
||||
var attachment = gpx_uploader.state().get('selection').first().toJSON();
|
||||
$('#gpx_url').val(attachment.url).trigger('change');
|
||||
}).open();
|
||||
});
|
||||
|
||||
// --- GPX Auto-fill Feature ---
|
||||
const gpxUrlInput = $('#gpx_url');
|
||||
const statusEl = $('#gpx_parse_status');
|
||||
let debounceTimer;
|
||||
|
||||
gpxUrlInput.on('input change', function() {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(function() {
|
||||
const url = gpxUrlInput.val();
|
||||
if (url && url.toLowerCase().endsWith('.gpx')) {
|
||||
parseGpx(url);
|
||||
}
|
||||
}, 500); // 500ms delay to avoid firing on every keystroke
|
||||
});
|
||||
|
||||
function parseGpx(url) {
|
||||
statusEl.html('<span class="spinner is-active" style="float:none; vertical-align: middle;"></span> Analizuję plik...');
|
||||
|
||||
wp.apiFetch({
|
||||
path: '/statpress/v1/gpx/parse-summary',
|
||||
method: 'POST',
|
||||
data: { gpx_url: url }
|
||||
})
|
||||
.then(function(data) {
|
||||
statusEl.html('<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> Dane wczytane!');
|
||||
|
||||
const fieldsToCheck = [
|
||||
'#distance', '#duration', '#calories', '#avg_speed', '#max_speed',
|
||||
'#avg_heart_rate', '#max_heart_rate', '#avg_cadence', '#max_cadence',
|
||||
'#total_elevation_gain', '#total_elevation_loss', '#min_altitude', '#max_altitude'
|
||||
];
|
||||
|
||||
let hasExistingData = false;
|
||||
fieldsToCheck.forEach(function(selector) {
|
||||
const field = $(selector);
|
||||
if (field.val() && field.val() !== '00:00:00' && field.val() !== '0' && field.val() !== '0,00') {
|
||||
hasExistingData = true;
|
||||
}
|
||||
});
|
||||
|
||||
let proceed = true;
|
||||
if (hasExistingData) {
|
||||
proceed = confirm('Niektóre pola formularza zawierają już dane. Czy na pewno chcesz je nadpisać danymi z pliku GPX?');
|
||||
}
|
||||
|
||||
if (proceed) {
|
||||
const formatNum = (num) => String(num).replace('.', ',');
|
||||
|
||||
if (data.distance) $('#distance').val(formatNum(data.distance));
|
||||
if (data.duration) $('#duration').val(data.duration);
|
||||
if (data.avg_speed) $('#avg_speed').val(formatNum(data.avg_speed));
|
||||
if (data.max_speed) $('#max_speed').val(formatNum(data.max_speed));
|
||||
if (data.avg_heart_rate) $('#avg_heart_rate').val(data.avg_heart_rate);
|
||||
if (data.max_heart_rate) $('#max_heart_rate').val(data.max_heart_rate);
|
||||
if (data.avg_cadence) $('#avg_cadence').val(data.avg_cadence);
|
||||
if (data.max_cadence) $('#max_cadence').val(data.max_cadence);
|
||||
if (data.total_elevation_gain) $('#total_elevation_gain').val(data.total_elevation_gain);
|
||||
if (data.total_elevation_loss) $('#total_elevation_loss').val(data.total_elevation_loss);
|
||||
if (data.min_altitude) $('#min_altitude').val(data.min_altitude);
|
||||
if (data.max_altitude) $('#max_altitude').val(data.max_altitude);
|
||||
}
|
||||
|
||||
setTimeout(() => statusEl.html(''), 4000);
|
||||
})
|
||||
.catch(function(error) {
|
||||
const errorMsg = error.message || 'Nieznany błąd.';
|
||||
statusEl.html('<span class="dashicons dashicons-warning" style="color: #d63638;"></span> Błąd: ' + errorMsg);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -1,263 +1,263 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_view_activity_page() {
|
||||
global $wpdb;
|
||||
|
||||
$activity_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
|
||||
|
||||
if ( $activity_id === 0 ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie podano ID aktywności.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
$table_event_types = $wpdb->prefix . 'statpress_event_types';
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"
|
||||
SELECT a.*, c.name as category_name, c.icon, c.color, et.name as event_type_name, eq.name as equipment_name
|
||||
FROM $table_activities a
|
||||
LEFT JOIN $table_categories c ON a.category_id = c.id
|
||||
LEFT JOIN $table_event_types et ON a.event_type_id = et.id
|
||||
LEFT JOIN $table_equipment eq ON a.equipment_id = eq.id
|
||||
WHERE a.id = %d
|
||||
",
|
||||
$activity_id
|
||||
);
|
||||
|
||||
$activity = $wpdb->get_row( $sql );
|
||||
|
||||
if ( ! $activity ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie znaleziono aktywności o podanym ID.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Funkcja pomocnicza do wyświetlania wiersza, jeśli wartość istnieje
|
||||
$render_row = function( $label, $value, $unit = '' ) {
|
||||
if ( ! is_null( $value ) && '' !== $value ) {
|
||||
echo '<tr>';
|
||||
echo '<th scope="row">' . esc_html( $label ) . '</th>';
|
||||
echo '<td>' . esc_html( $value ) . ( $unit ? ' ' . esc_html( $unit ) : '' ) . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare map and chart data if GPX exists
|
||||
$gpx_data = array();
|
||||
$has_gpx_data = false;
|
||||
if ( ! empty( $activity->gpx_url ) ) {
|
||||
$gpx_data = statpress_parse_gpx_data( $activity->gpx_url );
|
||||
$has_gpx_data = ! empty( $gpx_data['points'] );
|
||||
}
|
||||
|
||||
if ( $has_gpx_data ) {
|
||||
wp_enqueue_style( 'leaflet-css', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' );
|
||||
wp_enqueue_script( 'leaflet-js', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', array(), '1.9.4', true );
|
||||
wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), null, true );
|
||||
|
||||
wp_register_script( 'statpress-details-loader', false );
|
||||
wp_enqueue_script( 'statpress-details-loader' );
|
||||
|
||||
// Check which profiles have data
|
||||
$available_profiles = array();
|
||||
if ( ! empty( array_filter( $gpx_data['profiles']['elevation'] ) ) ) {
|
||||
$available_profiles['elevation'] = 'Wysokość';}
|
||||
if ( ! empty( array_filter( $gpx_data['profiles']['speed'] ) ) ) {
|
||||
$available_profiles['speed'] = 'Prędkość';}
|
||||
if ( ! empty( array_filter( $gpx_data['profiles']['hr'] ) ) ) {
|
||||
$available_profiles['hr'] = 'Tętno';}
|
||||
if ( ! empty( array_filter( $gpx_data['profiles']['cadence'] ) ) ) {
|
||||
$available_profiles['cadence'] = 'Kadencja';}
|
||||
|
||||
$has_time_data = ! empty( array_filter( $gpx_data['profiles']['time'], fn( $t ) => ! is_null( $t ) ) );
|
||||
|
||||
$chart_js = '
|
||||
const track_points = ' . json_encode( $gpx_data['points'] ) . ';
|
||||
const profiles = ' . json_encode( $gpx_data['profiles'] ) . ';
|
||||
let activeChart = null;
|
||||
|
||||
if (typeof L !== "undefined" && track_points.length > 0) {
|
||||
const map = L.map("statpress-activity-map");
|
||||
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: \'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>\' }).addTo(map);
|
||||
const polyline = L.polyline(track_points, {color: "' . esc_js( $activity->color ) . '"}).addTo(map);
|
||||
map.fitBounds(polyline.getBounds().pad(0.1));
|
||||
L.marker(track_points[0]).addTo(map).bindPopup("Start");
|
||||
L.marker(track_points[track_points.length - 1]).addTo(map).bindPopup("Koniec");
|
||||
}
|
||||
|
||||
const chartConfigs = {
|
||||
elevation: { label: "Wysokość", unit: "m n.p.m.", color: "#8e44ad" },
|
||||
speed: { label: "Prędkość", unit: "km/h", color: "#2980b9" },
|
||||
hr: { label: "Tętno", unit: "bpm", color: "#c0392b" },
|
||||
cadence: { label: "Kadencja", unit: "rpm", color: "#27ae60" }
|
||||
};
|
||||
const xAxisConfigs = {
|
||||
distance: { label: "Dystans (km)", data: profiles.distance },
|
||||
time: {
|
||||
label: "Czas", data: profiles.time,
|
||||
formatter: (s) => s === null ? "" : new Date(s * 1000).toISOString().substr(11, 8)
|
||||
}
|
||||
};
|
||||
|
||||
function renderChart() {
|
||||
if (activeChart) activeChart.destroy();
|
||||
const chartType = document.querySelector(".statpress-chart-tabs .nav-tab-active").getAttribute("href").substring(1);
|
||||
const xAxisType = document.querySelector(\'input[name="statpress_xaxis"]:checked\').value;
|
||||
|
||||
const yData = profiles[chartType], xData = xAxisConfigs[xAxisType].data;
|
||||
const filteredY = [], filteredX = [];
|
||||
if(yData) {
|
||||
for(let i=0; i<yData.length; i++) {
|
||||
if (yData[i] !== null) {
|
||||
filteredY.push(yData[i]);
|
||||
filteredX.push(xData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ctx = document.getElementById("statpress-details-chart").getContext("2d");
|
||||
activeChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: filteredX,
|
||||
datasets: [{
|
||||
label: chartConfigs[chartType].label, data: filteredY,
|
||||
borderColor: chartConfigs[chartType].color, backgroundColor: chartConfigs[chartType].color + "20",
|
||||
fill: true, pointRadius: 0, tension: 0.1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: { title: { display: true, text: xAxisConfigs[xAxisType].label }, ticks: { callback: xAxisType === "time" ? xAxisConfigs.time.formatter : (v) => v, maxRotation: 0, autoSkip: true, maxTicksLimit: 10 } },
|
||||
y: { title: { display: true, text: chartConfigs[chartType].unit } }
|
||||
},
|
||||
plugins: { legend: { display: false } },
|
||||
interaction: { intersect: false, mode: "index" },
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll(".statpress-chart-tabs .nav-tab").forEach(t => t.addEventListener("click", e => {
|
||||
e.preventDefault();
|
||||
document.querySelector(".statpress-chart-tabs .nav-tab-active").classList.remove("nav-tab-active");
|
||||
e.target.classList.add("nav-tab-active");
|
||||
renderChart();
|
||||
}));
|
||||
document.querySelectorAll(\'input[name="statpress_xaxis"]\').forEach(r => r.addEventListener("change", renderChart));
|
||||
if (document.querySelector(".statpress-chart-tabs .nav-tab")) renderChart();
|
||||
';
|
||||
wp_add_inline_script( 'statpress-details-loader', 'document.addEventListener("DOMContentLoaded", function() {' . $chart_js . '});' );
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>
|
||||
Szczegóły treningu: <?php echo esc_html( $activity->title ); ?>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'page' => 'statpress-edit-activity', 'id' => $activity->id ), admin_url( 'admin.php' ) ) ); ?>" class="page-title-action">
|
||||
Edytuj
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
<p><a href="<?php echo esc_url( admin_url( 'admin.php?page=statpress-dashboard' ) ); ?>">← Powrót do listy aktywności</a></p>
|
||||
|
||||
<div class="postbox" style="margin-top: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Podsumowanie</h2></div>
|
||||
<div class="inside">
|
||||
<div id="statpress-details-container">
|
||||
<div class="statpress-details-col">
|
||||
<h3>Główne dane</h3>
|
||||
<table class="form-table">
|
||||
<?php $render_row( 'Kategoria', $activity->category_name ); ?>
|
||||
<?php $render_row( 'Data', $activity->date ); ?>
|
||||
<?php $render_row( 'Dystans', number_format( $activity->distance, 2, ',', ' ' ), 'km' ); ?>
|
||||
<?php $render_row( 'Czas trwania', $activity->duration ); ?>
|
||||
<?php $render_row( 'Spalone kalorie', $activity->calories, 'kcal' ); ?>
|
||||
<?php $render_row( 'Typ wydarzenia', $activity->event_type_name ); ?>
|
||||
<?php $render_row( 'Sprzęt', $activity->equipment_name ); ?>
|
||||
</table>
|
||||
</div>
|
||||
<div class="statpress-details-col">
|
||||
<h3>Dane szczegółowe</h3>
|
||||
<table class="form-table">
|
||||
<?php $render_row( 'Średnia prędkość', number_format( $activity->avg_speed, 1, ',', ' ' ), 'km/h' ); ?>
|
||||
<?php $render_row( 'Maksymalna prędkość', number_format( $activity->max_speed, 1, ',', ' ' ), 'km/h' ); ?>
|
||||
<?php $render_row( 'Średnie tętno', $activity->avg_heart_rate, 'bpm' ); ?>
|
||||
<?php $render_row( 'Maksymalne tętno', $activity->max_heart_rate, 'bpm' ); ?>
|
||||
<?php $render_row( 'Średni rytm', $activity->avg_cadence, 'rpm' ); ?>
|
||||
<?php $render_row( 'Maksymalny rytm', $activity->max_cadence, 'rpm' ); ?>
|
||||
<?php $render_row( 'Suma wzniosów', $activity->total_elevation_gain, 'm' ); ?>
|
||||
<?php $render_row( 'Suma spadków', $activity->total_elevation_loss, 'm' ); ?>
|
||||
<?php $render_row( 'Minimalna wysokość', $activity->min_altitude, 'm n.p.m.' ); ?>
|
||||
<?php $render_row( 'Maksymalna wysokość', $activity->max_altitude, 'm n.p.m.' ); ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h3>Notatki i linki</h3>
|
||||
<table class="form-table">
|
||||
<?php $render_row( 'Komentarz', nl2br( $activity->comment ) ); ?>
|
||||
<?php if ( ! empty( $activity->strava_url ) ) : ?>
|
||||
<tr><th scope="row">Strava</th><td><a href="<?php echo esc_url( $activity->strava_url ); ?>" target="_blank" rel="noopener noreferrer">Zobacz aktywność na Strava</a></td></tr>
|
||||
<?php endif; ?>
|
||||
<?php if ( ! empty( $activity->gpx_url ) ) : ?>
|
||||
<tr><th scope="row">Plik GPX</th><td><a href="<?php echo esc_url( $activity->gpx_url ); ?>" target="_blank">Pobierz plik GPX</a></td></tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
|
||||
<?php if ( $has_gpx_data ) : ?>
|
||||
<hr>
|
||||
<h3>Mapa Trasy</h3>
|
||||
<div id="statpress-activity-map" style="height: 450px; width: 100%; border: 1px solid #ddd; margin-bottom: 20px;"></div>
|
||||
|
||||
<?php if ( ! empty( $available_profiles ) ) : ?>
|
||||
<h3>Wykresy</h3>
|
||||
<div class="statpress-charts-container">
|
||||
<div class="statpress-chart-controls">
|
||||
<nav class="nav-tab-wrapper statpress-chart-tabs">
|
||||
<?php
|
||||
$is_first = true;
|
||||
foreach ( $available_profiles as $key => $label ) :
|
||||
?>
|
||||
<a href="#<?php echo esc_attr( $key ); ?>" class="nav-tab <?php
|
||||
if ( $is_first ) {
|
||||
echo 'nav-tab-active';
|
||||
$is_first = false; }
|
||||
?>
|
||||
"><?php echo esc_html( $label ); ?></a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
<?php if ( $has_time_data ) : ?>
|
||||
<div class="statpress-xaxis-switcher">
|
||||
<strong>Oś X:</strong>
|
||||
<label><input type="radio" name="statpress_xaxis" value="distance" checked> Dystans</label>
|
||||
|
||||
<label><input type="radio" name="statpress_xaxis" value="time"> Czas</label>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<input type="hidden" name="statpress_xaxis" value="distance" checked>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div style="position: relative; height:250px; width:100%;">
|
||||
<canvas id="statpress-details-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php elseif ( ! empty( $activity->gpx_url ) ) : ?>
|
||||
<hr>
|
||||
<div class="notice notice-warning inline">
|
||||
<p>Nie udało się wczytać danych z pliku GPX lub plik jest uszkodzony/pusty. Brak danych do wyświetlenia mapy i wykresów.</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_view_activity_page() {
|
||||
global $wpdb;
|
||||
|
||||
$activity_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
|
||||
|
||||
if ( $activity_id === 0 ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie podano ID aktywności.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
$table_event_types = $wpdb->prefix . 'statpress_event_types';
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"
|
||||
SELECT a.*, c.name as category_name, c.icon, c.color, et.name as event_type_name, eq.name as equipment_name
|
||||
FROM $table_activities a
|
||||
LEFT JOIN $table_categories c ON a.category_id = c.id
|
||||
LEFT JOIN $table_event_types et ON a.event_type_id = et.id
|
||||
LEFT JOIN $table_equipment eq ON a.equipment_id = eq.id
|
||||
WHERE a.id = %d
|
||||
",
|
||||
$activity_id
|
||||
);
|
||||
|
||||
$activity = $wpdb->get_row( $sql );
|
||||
|
||||
if ( ! $activity ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie znaleziono aktywności o podanym ID.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Funkcja pomocnicza do wyświetlania wiersza, jeśli wartość istnieje
|
||||
$render_row = function( $label, $value, $unit = '' ) {
|
||||
if ( ! is_null( $value ) && '' !== $value ) {
|
||||
echo '<tr>';
|
||||
echo '<th scope="row">' . esc_html( $label ) . '</th>';
|
||||
echo '<td>' . esc_html( $value ) . ( $unit ? ' ' . esc_html( $unit ) : '' ) . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare map and chart data if GPX exists
|
||||
$gpx_data = array();
|
||||
$has_gpx_data = false;
|
||||
if ( ! empty( $activity->gpx_url ) ) {
|
||||
$gpx_data = statpress_parse_gpx_data( $activity->gpx_url );
|
||||
$has_gpx_data = ! empty( $gpx_data['points'] );
|
||||
}
|
||||
|
||||
if ( $has_gpx_data ) {
|
||||
wp_enqueue_style( 'leaflet-css', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' );
|
||||
wp_enqueue_script( 'leaflet-js', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', array(), '1.9.4', true );
|
||||
wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), null, true );
|
||||
|
||||
wp_register_script( 'statpress-details-loader', false );
|
||||
wp_enqueue_script( 'statpress-details-loader' );
|
||||
|
||||
// Check which profiles have data
|
||||
$available_profiles = array();
|
||||
if ( ! empty( array_filter( $gpx_data['profiles']['elevation'] ) ) ) {
|
||||
$available_profiles['elevation'] = 'Wysokość';}
|
||||
if ( ! empty( array_filter( $gpx_data['profiles']['speed'] ) ) ) {
|
||||
$available_profiles['speed'] = 'Prędkość';}
|
||||
if ( ! empty( array_filter( $gpx_data['profiles']['hr'] ) ) ) {
|
||||
$available_profiles['hr'] = 'Tętno';}
|
||||
if ( ! empty( array_filter( $gpx_data['profiles']['cadence'] ) ) ) {
|
||||
$available_profiles['cadence'] = 'Kadencja';}
|
||||
|
||||
$has_time_data = ! empty( array_filter( $gpx_data['profiles']['time'], fn( $t ) => ! is_null( $t ) ) );
|
||||
|
||||
$chart_js = '
|
||||
const track_points = ' . json_encode( $gpx_data['points'] ) . ';
|
||||
const profiles = ' . json_encode( $gpx_data['profiles'] ) . ';
|
||||
let activeChart = null;
|
||||
|
||||
if (typeof L !== "undefined" && track_points.length > 0) {
|
||||
const map = L.map("statpress-activity-map");
|
||||
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: \'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>\' }).addTo(map);
|
||||
const polyline = L.polyline(track_points, {color: "' . esc_js( $activity->color ) . '"}).addTo(map);
|
||||
map.fitBounds(polyline.getBounds().pad(0.1));
|
||||
L.marker(track_points[0]).addTo(map).bindPopup("Start");
|
||||
L.marker(track_points[track_points.length - 1]).addTo(map).bindPopup("Koniec");
|
||||
}
|
||||
|
||||
const chartConfigs = {
|
||||
elevation: { label: "Wysokość", unit: "m n.p.m.", color: "#8e44ad" },
|
||||
speed: { label: "Prędkość", unit: "km/h", color: "#2980b9" },
|
||||
hr: { label: "Tętno", unit: "bpm", color: "#c0392b" },
|
||||
cadence: { label: "Kadencja", unit: "rpm", color: "#27ae60" }
|
||||
};
|
||||
const xAxisConfigs = {
|
||||
distance: { label: "Dystans (km)", data: profiles.distance },
|
||||
time: {
|
||||
label: "Czas", data: profiles.time,
|
||||
formatter: (s) => s === null ? "" : new Date(s * 1000).toISOString().substr(11, 8)
|
||||
}
|
||||
};
|
||||
|
||||
function renderChart() {
|
||||
if (activeChart) activeChart.destroy();
|
||||
const chartType = document.querySelector(".statpress-chart-tabs .nav-tab-active").getAttribute("href").substring(1);
|
||||
const xAxisType = document.querySelector(\'input[name="statpress_xaxis"]:checked\').value;
|
||||
|
||||
const yData = profiles[chartType], xData = xAxisConfigs[xAxisType].data;
|
||||
const filteredY = [], filteredX = [];
|
||||
if(yData) {
|
||||
for(let i=0; i<yData.length; i++) {
|
||||
if (yData[i] !== null) {
|
||||
filteredY.push(yData[i]);
|
||||
filteredX.push(xData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ctx = document.getElementById("statpress-details-chart").getContext("2d");
|
||||
activeChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: filteredX,
|
||||
datasets: [{
|
||||
label: chartConfigs[chartType].label, data: filteredY,
|
||||
borderColor: chartConfigs[chartType].color, backgroundColor: chartConfigs[chartType].color + "20",
|
||||
fill: true, pointRadius: 0, tension: 0.1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: { title: { display: true, text: xAxisConfigs[xAxisType].label }, ticks: { callback: xAxisType === "time" ? xAxisConfigs.time.formatter : (v) => v, maxRotation: 0, autoSkip: true, maxTicksLimit: 10 } },
|
||||
y: { title: { display: true, text: chartConfigs[chartType].unit } }
|
||||
},
|
||||
plugins: { legend: { display: false } },
|
||||
interaction: { intersect: false, mode: "index" },
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll(".statpress-chart-tabs .nav-tab").forEach(t => t.addEventListener("click", e => {
|
||||
e.preventDefault();
|
||||
document.querySelector(".statpress-chart-tabs .nav-tab-active").classList.remove("nav-tab-active");
|
||||
e.target.classList.add("nav-tab-active");
|
||||
renderChart();
|
||||
}));
|
||||
document.querySelectorAll(\'input[name="statpress_xaxis"]\').forEach(r => r.addEventListener("change", renderChart));
|
||||
if (document.querySelector(".statpress-chart-tabs .nav-tab")) renderChart();
|
||||
';
|
||||
wp_add_inline_script( 'statpress-details-loader', 'document.addEventListener("DOMContentLoaded", function() {' . $chart_js . '});' );
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>
|
||||
Szczegóły treningu: <?php echo esc_html( $activity->title ); ?>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'page' => 'statpress-edit-activity', 'id' => $activity->id ), admin_url( 'admin.php' ) ) ); ?>" class="page-title-action">
|
||||
Edytuj
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
<p><a href="<?php echo esc_url( admin_url( 'admin.php?page=statpress-dashboard' ) ); ?>">← Powrót do listy aktywności</a></p>
|
||||
|
||||
<div class="postbox" style="margin-top: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Podsumowanie</h2></div>
|
||||
<div class="inside">
|
||||
<div id="statpress-details-container">
|
||||
<div class="statpress-details-col">
|
||||
<h3>Główne dane</h3>
|
||||
<table class="form-table">
|
||||
<?php $render_row( 'Kategoria', $activity->category_name ); ?>
|
||||
<?php $render_row( 'Data', $activity->date ); ?>
|
||||
<?php $render_row( 'Dystans', number_format( $activity->distance, 2, ',', ' ' ), 'km' ); ?>
|
||||
<?php $render_row( 'Czas trwania', $activity->duration ); ?>
|
||||
<?php $render_row( 'Spalone kalorie', $activity->calories, 'kcal' ); ?>
|
||||
<?php $render_row( 'Typ wydarzenia', $activity->event_type_name ); ?>
|
||||
<?php $render_row( 'Sprzęt', $activity->equipment_name ); ?>
|
||||
</table>
|
||||
</div>
|
||||
<div class="statpress-details-col">
|
||||
<h3>Dane szczegółowe</h3>
|
||||
<table class="form-table">
|
||||
<?php $render_row( 'Średnia prędkość', number_format( $activity->avg_speed, 1, ',', ' ' ), 'km/h' ); ?>
|
||||
<?php $render_row( 'Maksymalna prędkość', number_format( $activity->max_speed, 1, ',', ' ' ), 'km/h' ); ?>
|
||||
<?php $render_row( 'Średnie tętno', $activity->avg_heart_rate, 'bpm' ); ?>
|
||||
<?php $render_row( 'Maksymalne tętno', $activity->max_heart_rate, 'bpm' ); ?>
|
||||
<?php $render_row( 'Średni rytm', $activity->avg_cadence, 'rpm' ); ?>
|
||||
<?php $render_row( 'Maksymalny rytm', $activity->max_cadence, 'rpm' ); ?>
|
||||
<?php $render_row( 'Suma wzniosów', $activity->total_elevation_gain, 'm' ); ?>
|
||||
<?php $render_row( 'Suma spadków', $activity->total_elevation_loss, 'm' ); ?>
|
||||
<?php $render_row( 'Minimalna wysokość', $activity->min_altitude, 'm n.p.m.' ); ?>
|
||||
<?php $render_row( 'Maksymalna wysokość', $activity->max_altitude, 'm n.p.m.' ); ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h3>Notatki i linki</h3>
|
||||
<table class="form-table">
|
||||
<?php $render_row( 'Komentarz', nl2br( $activity->comment ) ); ?>
|
||||
<?php if ( ! empty( $activity->strava_url ) ) : ?>
|
||||
<tr><th scope="row">Strava</th><td><a href="<?php echo esc_url( $activity->strava_url ); ?>" target="_blank" rel="noopener noreferrer">Zobacz aktywność na Strava</a></td></tr>
|
||||
<?php endif; ?>
|
||||
<?php if ( ! empty( $activity->gpx_url ) ) : ?>
|
||||
<tr><th scope="row">Plik GPX</th><td><a href="<?php echo esc_url( $activity->gpx_url ); ?>" target="_blank">Pobierz plik GPX</a></td></tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
|
||||
<?php if ( $has_gpx_data ) : ?>
|
||||
<hr>
|
||||
<h3>Mapa Trasy</h3>
|
||||
<div id="statpress-activity-map" style="height: 450px; width: 100%; border: 1px solid #ddd; margin-bottom: 20px;"></div>
|
||||
|
||||
<?php if ( ! empty( $available_profiles ) ) : ?>
|
||||
<h3>Wykresy</h3>
|
||||
<div class="statpress-charts-container">
|
||||
<div class="statpress-chart-controls">
|
||||
<nav class="nav-tab-wrapper statpress-chart-tabs">
|
||||
<?php
|
||||
$is_first = true;
|
||||
foreach ( $available_profiles as $key => $label ) :
|
||||
?>
|
||||
<a href="#<?php echo esc_attr( $key ); ?>" class="nav-tab <?php
|
||||
if ( $is_first ) {
|
||||
echo 'nav-tab-active';
|
||||
$is_first = false; }
|
||||
?>
|
||||
"><?php echo esc_html( $label ); ?></a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
<?php if ( $has_time_data ) : ?>
|
||||
<div class="statpress-xaxis-switcher">
|
||||
<strong>Oś X:</strong>
|
||||
<label><input type="radio" name="statpress_xaxis" value="distance" checked> Dystans</label>
|
||||
|
||||
<label><input type="radio" name="statpress_xaxis" value="time"> Czas</label>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<input type="hidden" name="statpress_xaxis" value="distance" checked>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div style="position: relative; height:250px; width:100%;">
|
||||
<canvas id="statpress-details-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php elseif ( ! empty( $activity->gpx_url ) ) : ?>
|
||||
<hr>
|
||||
<div class="notice notice-warning inline">
|
||||
<p>Nie udało się wczytać danych z pliku GPX lub plik jest uszkodzony/pusty. Brak danych do wyświetlenia mapy i wykresów.</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -1,195 +1,233 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_dashboard_page() {
|
||||
echo '<div class="wrap"><h1>StatPress Dashboard</h1>';
|
||||
statpress_render_history_table();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
function statpress_render_history_table() {
|
||||
global $wpdb;
|
||||
|
||||
// Definicje nazw tabel (z uwzględnieniem prefixu WP, jeśli był użyty przy tworzeniu)
|
||||
// Zakładam, że tabele nazywają się dokładnie tak jak w dokumentacji, ale dobrą praktyką jest $wpdb->prefix
|
||||
// Jeśli tabele są "sztywne" (bez prefixu wp_), usuń $wpdb->prefix.
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
|
||||
// --- 1. OBSŁUGA USUWANIA (DELETE) ---
|
||||
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'statpress_delete' === $_GET['action'] ) {
|
||||
$activity_id = intval( $_GET['id'] );
|
||||
|
||||
// Weryfikacja bezpieczeństwa (Nonce)
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_' . $activity_id ) ) {
|
||||
$result = $wpdb->delete(
|
||||
$table_activities,
|
||||
array( 'id' => $activity_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
if ( $result ) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>Aktywność została usunięta.</p></div>';
|
||||
} else {
|
||||
echo '<div class="notice notice-error is-dismissible"><p>Wystąpił błąd podczas usuwania.</p></div>';
|
||||
}
|
||||
} else {
|
||||
echo '<div class="notice notice-error is-dismissible"><p>Błąd weryfikacji bezpieczeństwa (Nonce).</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. USTAWIENIA PAGINACJI ---
|
||||
$items_per_page = 20; // Ile wpisów na stronę
|
||||
$current_page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
|
||||
$offset = ( $current_page - 1 ) * $items_per_page;
|
||||
|
||||
// --- 3. POBIERANIE DANYCH (SELECT) ---
|
||||
// Pobranie całkowitej liczby wpisów do paginacji
|
||||
$total_items = $wpdb->get_var( "SELECT COUNT(id) FROM $table_activities" );
|
||||
$total_pages = ceil( $total_items / $items_per_page );
|
||||
|
||||
// Pobieramy wpisy dla bieżącej strony
|
||||
$sql = $wpdb->prepare(
|
||||
"
|
||||
SELECT a.*, c.name as category_name, c.icon, c.color, et.name as event_type_name, eq.name as equipment_name
|
||||
FROM $table_activities a
|
||||
LEFT JOIN $table_categories c ON a.category_id = c.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_event_types et ON a.event_type_id = et.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_equipment eq ON a.equipment_id = eq.id
|
||||
ORDER BY a.date DESC, a.id DESC
|
||||
LIMIT %d OFFSET %d
|
||||
",
|
||||
$items_per_page,
|
||||
$offset
|
||||
);
|
||||
|
||||
$activities = $wpdb->get_results( $sql );
|
||||
|
||||
// --- 4. WIDOK TABELI (HTML) ---
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h2>Historia Aktywności</h2>
|
||||
|
||||
<?php if ( $total_pages > 1 ) : ?>
|
||||
<div class="tablenav top">
|
||||
<div class="tablenav-pages">
|
||||
<span class="displaying-num"><?php echo esc_html( $total_items ); ?> aktywności</span>
|
||||
<?php
|
||||
echo paginate_links(
|
||||
array(
|
||||
'base' => add_query_arg( 'paged', '%#%' ),
|
||||
'format' => '',
|
||||
'total' => $total_pages,
|
||||
'current' => $current_page,
|
||||
'prev_text' => '« Poprzednia',
|
||||
'next_text' => 'Następna »',
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<table class="wp-list-table widefat fixed striped table-view-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width: 50px;">Ikona</th>
|
||||
<th scope="col">Data</th>
|
||||
<th scope="col">Tytuł</th>
|
||||
<th scope="col" style="width: 120px;">Kategoria</th>
|
||||
<th scope="col">Typ</th>
|
||||
<th scope="col">Sprzęt</th>
|
||||
<th scope="col">Dystans (km)</th>
|
||||
<th scope="col">Czas</th>
|
||||
<th scope="col">Śr. prędkość</th>
|
||||
<th scope="col" style="width: 180px;">Akcja</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if ( ! empty( $activities ) ) : ?>
|
||||
<?php foreach ( $activities as $row ) : ?>
|
||||
<?php
|
||||
// Generowanie URL-i akcji z zachowaniem paginacji
|
||||
$delete_url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'action' => 'statpress_delete',
|
||||
'id' => $row->id,
|
||||
)
|
||||
),
|
||||
'statpress_delete_' . $row->id
|
||||
);
|
||||
|
||||
$edit_url = add_query_arg(
|
||||
array(
|
||||
'page' => 'statpress-edit-activity',
|
||||
'id' => $row->id,
|
||||
),
|
||||
admin_url( 'admin.php' )
|
||||
);
|
||||
|
||||
$details_url = add_query_arg(
|
||||
array(
|
||||
'page' => 'statpress-view-activity',
|
||||
'id' => $row->id,
|
||||
),
|
||||
admin_url( 'admin.php' )
|
||||
);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php if ( ! empty( $row->icon ) ) : ?>
|
||||
<span class="dashicons <?php echo esc_attr( $row->icon ); ?>" style="color: <?php echo esc_attr( $row->color ); ?>;"></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo esc_html( $row->date ); ?></td>
|
||||
<td><strong><a href="<?php echo esc_url( $details_url ); ?>"><?php echo esc_html( $row->title ? wp_trim_words( $row->title, 6 ) : '(bez tytułu)' ); ?></a></strong></td>
|
||||
<td><?php echo esc_html( $row->category_name ); ?></td>
|
||||
<td><?php echo esc_html( $row->event_type_name ); ?></td>
|
||||
<td><?php echo esc_html( $row->equipment_name ); ?></td>
|
||||
<td><?php echo number_format( $row->distance, 2, ',', ' ' ); ?></td>
|
||||
<td><?php echo esc_html( $row->duration ); ?></td>
|
||||
<td><?php echo $row->avg_speed ? number_format( $row->avg_speed, 1, ',', ' ' ) . ' km/h' : '-'; ?></td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( $edit_url ); ?>" class="button button-small">Edytuj</a>
|
||||
<a href="<?php echo esc_url( $details_url ); ?>" class="button button-small">Szczegóły</a>
|
||||
<a href="<?php echo esc_url( $delete_url ); ?>"
|
||||
class="button button-small button-link-delete"
|
||||
onclick="return confirm('Czy na pewno chcesz usunąć ten wpis?');">
|
||||
Usuń
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else : ?>
|
||||
<tr>
|
||||
<td colspan="10">Brak zarejestrowanych aktywności. Dodaj pierwszy trening powyżej!</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php if ( $total_pages > 1 ) : ?>
|
||||
<div class="tablenav bottom">
|
||||
<div class="tablenav-pages">
|
||||
<span class="displaying-num"><?php echo esc_html( $total_items ); ?> aktywności</span>
|
||||
<?php
|
||||
echo paginate_links(
|
||||
array(
|
||||
'base' => add_query_arg( 'paged', '%#%' ),
|
||||
'format' => '',
|
||||
'total' => $total_pages,
|
||||
'current' => $current_page,
|
||||
'prev_text' => '« Poprzednia',
|
||||
'next_text' => 'Następna »',
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_dashboard_page() {
|
||||
echo '<div class="wrap"><h1>StatPress Dashboard</h1>';
|
||||
|
||||
// --- MIGRATION NOTICE ---
|
||||
// Show the migration button if it hasn't been completed yet.
|
||||
if ( ! get_option( 'statpress_migration_complete' ) ) {
|
||||
$migration_url = wp_nonce_url(
|
||||
admin_url( 'admin.php?page=statpress-dashboard&action=statpress_migrate_data' ),
|
||||
'statpress_migration_nonce'
|
||||
);
|
||||
echo '<div class="notice notice-warning is-dismissible" style="padding-bottom: 10px;">';
|
||||
echo '<h4>Migracja danych StatPress</h4>';
|
||||
echo '<p>Wygląda na to, że istnieją dane w starych tabelach (<code>mystat_*</code>), które można przenieść. Kliknij przycisk poniżej, aby rozpocząć proces.</p>';
|
||||
echo '<p><strong>Ważne:</strong> Ta operacja jest jednorazowa i powinna być wykonana tylko raz, na pustej instalacji wtyczki StatPress.</p>';
|
||||
echo '<a href="' . esc_url( $migration_url ) . '" class="button button-primary">Rozpocznij migrację danych</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
// Show the results of the migration after it's done.
|
||||
$migration_results = get_transient( 'statpress_migration_results' );
|
||||
if ( $migration_results ) {
|
||||
echo '<div class="notice notice-success is-dismissible">';
|
||||
echo '<h4>Migracja zakończona!</h4>';
|
||||
echo '<ul>';
|
||||
foreach ( $migration_results as $table => $result ) {
|
||||
if ( 'success' === $result['status'] ) {
|
||||
echo '<li>Tabela <strong>' . esc_html( $table ) . '</strong>: Przeniesiono <strong>' . esc_html( $result['count'] ) . '</strong> wierszy.</li>';
|
||||
} elseif ( 'skipped' === $result['status'] ) {
|
||||
echo '<li>Tabela <strong>' . esc_html( $table ) . '</strong>: Pominięto, ponieważ nowa tabela zawiera już dane (' . esc_html( $result['count'] ) . ' wierszy).</li>';
|
||||
} elseif ( 'failure' === $result['status'] ) {
|
||||
echo '<li>Tabela <strong>' . esc_html( $table ) . '</strong>: <strong style="color:red;">Migracja nieudana.</strong> Błąd bazy danych: <pre style="display:inline;white-space:pre-wrap;">' . esc_html( $result['error'] ) . '</pre></li>';
|
||||
}
|
||||
}
|
||||
echo '</ul>';
|
||||
echo '<p>Twoje dane powinny być teraz widoczne. Stare tabele (<code>' . esc_html( $GLOBALS['wpdb']->prefix ) . 'mystat_*</code>) wciąż istnieją w bazie danych, ale nie są już używane. Możesz je usunąć ręcznie (np. przez phpMyAdmin), jeśli wszystko działa poprawnie.</p>';
|
||||
echo '</div>';
|
||||
delete_transient( 'statpress_migration_results' );
|
||||
}
|
||||
// --- END MIGRATION NOTICE ---
|
||||
|
||||
statpress_render_history_table();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
function statpress_render_history_table() {
|
||||
global $wpdb;
|
||||
|
||||
// Definicje nazw tabel (z uwzględnieniem prefixu WP, jeśli był użyty przy tworzeniu)
|
||||
// Zakładam, że tabele nazywają się dokładnie tak jak w dokumentacji, ale dobrą praktyką jest $wpdb->prefix
|
||||
// Jeśli tabele są "sztywne" (bez prefixu wp_), usuń $wpdb->prefix.
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
|
||||
// --- 1. OBSŁUGA USUWANIA (DELETE) ---
|
||||
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'statpress_delete' === $_GET['action'] ) {
|
||||
$activity_id = intval( $_GET['id'] );
|
||||
|
||||
// Weryfikacja bezpieczeństwa (Nonce)
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_' . $activity_id ) ) {
|
||||
$result = $wpdb->delete(
|
||||
$table_activities,
|
||||
array( 'id' => $activity_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
if ( $result ) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>Aktywność została usunięta.</p></div>';
|
||||
} else {
|
||||
echo '<div class="notice notice-error is-dismissible"><p>Wystąpił błąd podczas usuwania.</p></div>';
|
||||
}
|
||||
} else {
|
||||
echo '<div class="notice notice-error is-dismissible"><p>Błąd weryfikacji bezpieczeństwa (Nonce).</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. USTAWIENIA PAGINACJI ---
|
||||
$items_per_page = 20; // Ile wpisów na stronę
|
||||
$current_page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
|
||||
$offset = ( $current_page - 1 ) * $items_per_page;
|
||||
|
||||
// --- 3. POBIERANIE DANYCH (SELECT) ---
|
||||
// Pobranie całkowitej liczby wpisów do paginacji
|
||||
$total_items = $wpdb->get_var( "SELECT COUNT(id) FROM $table_activities" );
|
||||
$total_pages = ceil( $total_items / $items_per_page );
|
||||
|
||||
// Pobieramy wpisy dla bieżącej strony
|
||||
$sql = $wpdb->prepare(
|
||||
"
|
||||
SELECT a.*, c.name as category_name, c.icon, c.color, et.name as event_type_name, eq.name as equipment_name
|
||||
FROM $table_activities a
|
||||
LEFT JOIN $table_categories c ON a.category_id = c.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_event_types et ON a.event_type_id = et.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_equipment eq ON a.equipment_id = eq.id
|
||||
ORDER BY a.date DESC, a.id DESC
|
||||
LIMIT %d OFFSET %d
|
||||
",
|
||||
$items_per_page,
|
||||
$offset
|
||||
);
|
||||
|
||||
$activities = $wpdb->get_results( $sql );
|
||||
|
||||
// --- 4. WIDOK TABELI (HTML) ---
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h2>Historia Aktywności</h2>
|
||||
|
||||
<?php if ( $total_pages > 1 ) : ?>
|
||||
<div class="tablenav top">
|
||||
<div class="tablenav-pages">
|
||||
<span class="displaying-num"><?php echo esc_html( $total_items ); ?> aktywności</span>
|
||||
<?php
|
||||
echo paginate_links(
|
||||
array(
|
||||
'base' => add_query_arg( 'paged', '%#%' ),
|
||||
'format' => '',
|
||||
'total' => $total_pages,
|
||||
'current' => $current_page,
|
||||
'prev_text' => '« Poprzednia',
|
||||
'next_text' => 'Następna »',
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<table class="wp-list-table widefat fixed striped table-view-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width: 50px;">Ikona</th>
|
||||
<th scope="col">Data</th>
|
||||
<th scope="col">Tytuł</th>
|
||||
<th scope="col" style="width: 120px;">Kategoria</th>
|
||||
<th scope="col">Typ</th>
|
||||
<th scope="col">Sprzęt</th>
|
||||
<th scope="col">Dystans (km)</th>
|
||||
<th scope="col">Czas</th>
|
||||
<th scope="col">Śr. prędkość</th>
|
||||
<th scope="col" style="width: 180px;">Akcja</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if ( ! empty( $activities ) ) : ?>
|
||||
<?php foreach ( $activities as $row ) : ?>
|
||||
<?php
|
||||
// Generowanie URL-i akcji z zachowaniem paginacji
|
||||
$delete_url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'action' => 'statpress_delete',
|
||||
'id' => $row->id,
|
||||
)
|
||||
),
|
||||
'statpress_delete_' . $row->id
|
||||
);
|
||||
|
||||
$edit_url = add_query_arg(
|
||||
array(
|
||||
'page' => 'statpress-edit-activity',
|
||||
'id' => $row->id,
|
||||
),
|
||||
admin_url( 'admin.php' )
|
||||
);
|
||||
|
||||
$details_url = add_query_arg(
|
||||
array(
|
||||
'page' => 'statpress-view-activity',
|
||||
'id' => $row->id,
|
||||
),
|
||||
admin_url( 'admin.php' )
|
||||
);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php if ( ! empty( $row->icon ) ) : ?>
|
||||
<span class="dashicons <?php echo esc_attr( $row->icon ); ?>" style="color: <?php echo esc_attr( $row->color ); ?>;"></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo esc_html( $row->date ); ?></td>
|
||||
<td><strong><a href="<?php echo esc_url( $details_url ); ?>"><?php echo esc_html( $row->title ? wp_trim_words( $row->title, 6 ) : '(bez tytułu)' ); ?></a></strong></td>
|
||||
<td><?php echo esc_html( $row->category_name ); ?></td>
|
||||
<td><?php echo esc_html( $row->event_type_name ); ?></td>
|
||||
<td><?php echo esc_html( $row->equipment_name ); ?></td>
|
||||
<td><?php echo number_format( $row->distance, 2, ',', ' ' ); ?></td>
|
||||
<td><?php echo esc_html( $row->duration ); ?></td>
|
||||
<td><?php echo $row->avg_speed ? number_format( $row->avg_speed, 1, ',', ' ' ) . ' km/h' : '-'; ?></td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( $edit_url ); ?>" class="button button-small">Edytuj</a>
|
||||
<a href="<?php echo esc_url( $details_url ); ?>" class="button button-small">Szczegóły</a>
|
||||
<a href="<?php echo esc_url( $delete_url ); ?>"
|
||||
class="button button-small button-link-delete"
|
||||
onclick="return confirm('Czy na pewno chcesz usunąć ten wpis?');">
|
||||
Usuń
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else : ?>
|
||||
<tr>
|
||||
<td colspan="10">Brak zarejestrowanych aktywności. Dodaj pierwszy trening powyżej!</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php if ( $total_pages > 1 ) : ?>
|
||||
<div class="tablenav bottom">
|
||||
<div class="tablenav-pages">
|
||||
<span class="displaying-num"><?php echo esc_html( $total_items ); ?> aktywności</span>
|
||||
<?php
|
||||
echo paginate_links(
|
||||
array(
|
||||
'base' => add_query_arg( 'paged', '%#%' ),
|
||||
'format' => '',
|
||||
'total' => $total_pages,
|
||||
'current' => $current_page,
|
||||
'prev_text' => '« Poprzednia',
|
||||
'next_text' => 'Następna »',
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -1,297 +1,297 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_equipment_page() {
|
||||
global $wpdb;
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
$message = '';
|
||||
$notice_class = '';
|
||||
|
||||
// Handle POST requests (add/update)
|
||||
if ( isset( $_POST['submit'] ) && check_admin_referer( 'statpress_manage_equipment' ) ) {
|
||||
$item_id = isset( $_POST['equipment_id'] ) ? intval( $_POST['equipment_id'] ) : 0;
|
||||
$data = array(
|
||||
'name' => sanitize_text_field( $_POST['equipment_name'] ),
|
||||
'type' => sanitize_text_field( $_POST['equipment_type'] ),
|
||||
'purchase_date' => empty( $_POST['purchase_date'] ) ? null : sanitize_text_field( $_POST['purchase_date'] ),
|
||||
'initial_cost' => empty( $_POST['initial_cost'] ) ? null : floatval( str_replace( ',', '.', $_POST['initial_cost'] ) ),
|
||||
'status' => sanitize_text_field( $_POST['status'] ),
|
||||
'notes' => sanitize_textarea_field( $_POST['notes'] ),
|
||||
);
|
||||
|
||||
if ( ! empty( $data['name'] ) ) {
|
||||
if ( $item_id > 0 ) { // Update
|
||||
$result = $wpdb->update( $table_equipment, $data, array( 'id' => $item_id ) );
|
||||
if ( false !== $result ) {
|
||||
$message = 'Sprzęt zaktualizowany.';
|
||||
$notice_class = 'notice-success';
|
||||
} else {
|
||||
$message = 'Błąd podczas aktualizacji sprzętu: ' . $wpdb->last_error;
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
} else { // Insert
|
||||
$result = $wpdb->insert( $table_equipment, $data );
|
||||
if ( false !== $result ) {
|
||||
$message = 'Sprzęt dodany.';
|
||||
$notice_class = 'notice-success';
|
||||
} else {
|
||||
$message = 'Błąd podczas dodawania sprzętu: ' . $wpdb->last_error;
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$message = 'Nazwa sprzętu nie może być pusta.';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle GET requests (delete)
|
||||
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'delete' === $_GET['action'] ) {
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_equipment_' . $_GET['id'] ) ) {
|
||||
// Sprawdź, czy sprzęt nie jest używany
|
||||
$usage_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}statpress_activities WHERE equipment_id = %d", intval( $_GET['id'] ) ) );
|
||||
if ( 0 == $usage_count ) {
|
||||
$wpdb->delete( $table_equipment, array( 'id' => intval( $_GET['id'] ) ) );
|
||||
// Usuń również powiązane wpisy w dzienniku
|
||||
$wpdb->delete( $wpdb->prefix . 'statpress_equipment_log', array( 'equipment_id' => intval( $_GET['id'] ) ) );
|
||||
$message = 'Sprzęt usunięty.';
|
||||
$notice_class = 'notice-success';
|
||||
} else {
|
||||
$message = 'Nie można usunąć sprzętu, ponieważ jest przypisany do ' . $usage_count . ' aktywności. Zmień jego status na "wycofany".';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for form (for editing)
|
||||
$item_to_edit = null;
|
||||
$statuses = array( 'aktywny', 'sprzedany', 'wycofany' );
|
||||
if ( isset( $_GET['action'], $_GET['id'] ) && 'edit' === $_GET['action'] ) {
|
||||
$item_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_equipment WHERE id = %d", intval( $_GET['id'] ) ) );
|
||||
}
|
||||
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$equipment_list = $wpdb->get_results("
|
||||
SELECT
|
||||
eq.id,
|
||||
eq.name,
|
||||
eq.type,
|
||||
eq.status,
|
||||
SUM(a.distance) as total_distance,
|
||||
COUNT(a.id) as activity_count
|
||||
FROM
|
||||
{$table_equipment} eq
|
||||
LEFT JOIN
|
||||
{$table_activities} a ON eq.id = a.equipment_id
|
||||
GROUP BY
|
||||
eq.id, eq.name, eq.type, eq.status
|
||||
ORDER BY
|
||||
eq.status ASC, eq.name ASC"
|
||||
);
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Zarządzaj Sprzętem</h1>
|
||||
<?php if ( ! empty( $message ) ) : ?>
|
||||
<div class="notice <?php echo esc_attr( $notice_class ); ?> is-dismissible"><p><?php echo esc_html( $message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="col-container" class="wp-clearfix">
|
||||
<div id="col-left">
|
||||
<div class="col-wrap">
|
||||
<div class="form-wrap">
|
||||
<h2><?php echo $item_to_edit ? 'Edytuj pozycję' : 'Dodaj nowy sprzęt'; ?></h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="equipment_id" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( 'statpress_manage_equipment' ); ?>
|
||||
<div class="form-field form-required"><label for="equipment_name">Nazwa</label><input type="text" name="equipment_name" id="equipment_name" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->name ) : ''; ?>" required></div>
|
||||
<div class="form-field"><label for="equipment_type">Typ</label><input type="text" name="equipment_type" id="equipment_type" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->type ) : 'Rower'; ?>"></div>
|
||||
<div class="form-field"><label for="purchase_date">Data zakupu</label><input type="date" name="purchase_date" id="purchase_date" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->purchase_date ) : ''; ?>"></div>
|
||||
<div class="form-field"><label for="initial_cost">Koszt zakupu</label><input type="text" name="initial_cost" id="initial_cost" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->initial_cost ) : ''; ?>" placeholder="0.00"></div>
|
||||
<div class="form-field"><label for="status">Status</label><select name="status" id="status"><?php foreach ( $statuses as $s ) : ?><option value="<?php echo esc_attr( $s ); ?>" <?php if ( $item_to_edit ) {
|
||||
selected( $item_to_edit->status, $s );} ?>><?php echo esc_html( ucfirst( $s ) ); ?></option><?php endforeach; ?></select></div>
|
||||
<div class="form-field"><label for="notes">Notatki</label><textarea name="notes" id="notes" rows="3"><?php echo $item_to_edit ? esc_textarea( $item_to_edit->notes ) : ''; ?></textarea></div>
|
||||
<?php submit_button( $item_to_edit ? 'Zaktualizuj' : 'Dodaj' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-right">
|
||||
<div class="col-wrap">
|
||||
<table class="wp-list-table widefat striped">
|
||||
<thead><tr><th>Nazwa</th><th>Przebieg</th><th>Liczba aktywności</th><th>Status</th><th style="width: 220px;">Akcje</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ( $equipment_list as $item ) : ?>
|
||||
<?php
|
||||
$details_url = admin_url( 'admin.php?page=statpress-equipment-details&id=' . $item->id );
|
||||
?>
|
||||
<tr>
|
||||
<td><strong><a href="<?php echo esc_url( $details_url ); ?>"><?php echo esc_html( $item->name ); ?></a></strong><br><small><?php echo esc_html( $item->type ); ?></small></td>
|
||||
<td><?php echo $item->total_distance ? number_format( $item->total_distance, 2, ',', ' ' ) . ' km' : '0 km'; ?></td>
|
||||
<td><?php echo (int) $item->activity_count; ?></td>
|
||||
<td><?php echo esc_html( ucfirst( $item->status ) ); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( $details_url ); ?>" class="button button-small">Dziennik / Serwis</a>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'edit', 'id' => $item->id ) ) ); ?>" class="button button-small">Edytuj</a>
|
||||
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'delete', 'id' => $item->id ) ), 'statpress_delete_equipment_' . $item->id ) ); ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten sprzęt?')" class="button button-small button-link-delete">Usuń</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
function statpress_equipment_details_page() {
|
||||
global $wpdb;
|
||||
$equipment_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
|
||||
|
||||
if ( 0 === $equipment_id ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie podano ID sprzętu.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
$table_equipment_log = $wpdb->prefix . 'statpress_equipment_log';
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$message = '';
|
||||
$notice_class = '';
|
||||
|
||||
// --- Handle Service Log form submissions (add/update/delete) ---
|
||||
if ( isset( $_POST['submit_log'] ) && check_admin_referer( 'statpress_manage_equipment_log' ) ) {
|
||||
$log_id = isset( $_POST['log_id'] ) ? intval( $_POST['log_id'] ) : 0;
|
||||
$log_data = array(
|
||||
'equipment_id' => $equipment_id,
|
||||
'log_date' => sanitize_text_field( $_POST['log_date'] ),
|
||||
'log_type' => sanitize_text_field( $_POST['log_type'] ),
|
||||
'description' => sanitize_textarea_field( $_POST['description'] ),
|
||||
'cost' => empty( $_POST['cost'] ) ? null : floatval( str_replace( ',', '.', $_POST['cost'] ) ),
|
||||
'mileage' => empty( $_POST['mileage'] ) ? null : intval( $_POST['mileage'] ),
|
||||
);
|
||||
|
||||
if ( ! empty( $log_data['log_date'] ) && ! empty( $log_data['log_type'] ) && ! empty( $log_data['description'] ) ) {
|
||||
if ( $log_id > 0 ) {
|
||||
$wpdb->update( $table_equipment_log, $log_data, array( 'id' => $log_id ) );
|
||||
$message = 'Wpis w dzienniku zaktualizowany.';
|
||||
} else {
|
||||
$wpdb->insert( $table_equipment_log, $log_data );
|
||||
$message = 'Wpis dodany do dziennika.';
|
||||
}
|
||||
$notice_class = 'notice-success';
|
||||
} else {
|
||||
$message = 'Wypełnij wymagane pola (Data, Typ, Opis).';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $_GET['action'], $_GET['log_id'], $_GET['_wpnonce'] ) && 'delete_log' === $_GET['action'] ) {
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_log_' . $_GET['log_id'] ) ) {
|
||||
$wpdb->delete( $table_equipment_log, array( 'id' => intval( $_GET['log_id'] ) ) );
|
||||
$message = 'Wpis z dziennika usunięty.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
}
|
||||
|
||||
// --- Get Data ---
|
||||
$equipment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_equipment WHERE id = %d", $equipment_id ) );
|
||||
if ( ! $equipment ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie znaleziono sprzętu o podanym ID.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$total_mileage = $wpdb->get_var( $wpdb->prepare( "SELECT SUM(distance) FROM $table_activities WHERE equipment_id = %d", $equipment_id ) );
|
||||
$total_service_cost = $wpdb->get_var( $wpdb->prepare( "SELECT SUM(cost) FROM $table_equipment_log WHERE equipment_id = %d", $equipment_id ) );
|
||||
$service_log = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_equipment_log WHERE equipment_id = %d ORDER BY log_date DESC, id DESC", $equipment_id ) );
|
||||
|
||||
$log_to_edit = null;
|
||||
if ( isset( $_GET['action'], $_GET['log_id'] ) && 'edit_log' === $_GET['action'] ) {
|
||||
$log_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_equipment_log WHERE id = %d", intval( $_GET['log_id'] ) ) );
|
||||
}
|
||||
$log_types = array( 'Naprawa', 'Zakup części', 'Przegląd', 'Modyfikacja', 'Inne' );
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Dziennik serwisowy: <?php echo esc_html( $equipment->name ); ?></h1>
|
||||
<p><a href="<?php echo esc_url( admin_url( 'admin.php?page=statpress-equipment' ) ); ?>">← Powrót do listy sprzętu</a></p>
|
||||
|
||||
<?php if ( ! empty( $message ) ) : ?>
|
||||
<div class="notice <?php echo esc_attr( $notice_class ); ?> is-dismissible"><p><?php echo esc_html( $message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="postbox">
|
||||
<div class="postbox-header"><h2 class="hndle">Podsumowanie Sprzętu</h2></div>
|
||||
<div class="inside">
|
||||
<div class="main">
|
||||
<p><strong>Całkowity przebieg:</strong> <?php echo number_format( (float) $total_mileage, 2, ',', ' ' ); ?> km</p>
|
||||
<?php if ( $total_service_cost > 0 ) : ?>
|
||||
<p><strong>Całkowity koszt serwisu:</strong> <?php echo number_format( $total_service_cost, 2, ',', ' ' ); ?> zł</p>
|
||||
<?php endif; ?>
|
||||
<p><strong>Status:</strong> <?php echo esc_html( ucfirst( $equipment->status ) ); ?></p>
|
||||
<?php if ( $equipment->purchase_date ) : ?><p><strong>Data zakupu:</strong> <?php echo esc_html( $equipment->purchase_date ); ?></p><?php endif; ?>
|
||||
<?php if ( $equipment->initial_cost ) : ?><p><strong>Koszt zakupu:</strong> <?php echo number_format( $equipment->initial_cost, 2, ',', ' ' ); ?> zł</p><?php endif; ?>
|
||||
<?php if ( $equipment->notes ) : ?><p><strong>Notatki:</strong><br><?php echo nl2br( esc_html( $equipment->notes ) ); ?></p><?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="col-container" class="wp-clearfix">
|
||||
<div id="col-left">
|
||||
<div class="col-wrap">
|
||||
<div class="form-wrap">
|
||||
<h2><?php echo $log_to_edit ? 'Edytuj wpis' : 'Dodaj wpis do dziennika'; ?></h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="log_id" value="<?php echo $log_to_edit ? esc_attr( $log_to_edit->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( 'statpress_manage_equipment_log' ); ?>
|
||||
<div class="form-field form-required"><label for="log_date">Data</label><input type="date" name="log_date" id="log_date" value="<?php echo $log_to_edit ? esc_attr( $log_to_edit->log_date ) : current_time( 'Y-m-d' ); ?>" required></div>
|
||||
<div class="form-field form-required"><label for="log_type">Typ</label><select name="log_type" id="log_type" required><?php foreach ( $log_types as $type ) : ?><option value="<?php echo esc_attr( $type ); ?>" <?php if ( $log_to_edit ) {
|
||||
selected( $log_to_edit->log_type, $type );} ?>><?php echo esc_html( $type ); ?></option><?php endforeach; ?></select></div>
|
||||
<div class="form-field form-required"><label for="description">Opis</label><textarea name="description" id="description" rows="4" required><?php echo $log_to_edit ? esc_textarea( $log_to_edit->description ) : ''; ?></textarea></div>
|
||||
<div class="form-field"><label for="cost">Koszt (zł)</label><input type="text" name="cost" id="cost" value="<?php echo $log_to_edit ? esc_attr( $log_to_edit->cost ) : ''; ?>" placeholder="0.00"></div>
|
||||
<div class="form-field"><label for="mileage">Przebieg (km)</label><input type="number" name="mileage" id="mileage" value="<?php echo $log_to_edit ? esc_attr( $log_to_edit->mileage ) : round( (float) $total_mileage ); ?>"><p class="description">Przebieg sprzętu w momencie serwisu. Domyślnie aktualny całkowity przebieg.</p></div>
|
||||
<?php submit_button( $log_to_edit ? 'Zaktualizuj wpis' : 'Dodaj wpis', 'primary', 'submit_log' ); ?>
|
||||
<?php if ( $log_to_edit ) : ?><a href="<?php echo esc_url( remove_query_arg( array( 'action', 'log_id' ) ) ); ?>" class="button">Anuluj edycję</a><?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-right">
|
||||
<div class="col-wrap">
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th colspan="3" style="text-align:right;">Suma kosztów:</th>
|
||||
<th colspan="3"><strong><?php echo number_format( (float) $total_service_cost, 2, ',', ' ' ); ?> zł</strong></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<thead><tr><th>Data</th><th>Typ</th><th>Opis</th><th>Koszt</th><th>Przebieg</th><th style="width: 100px;">Akcje</th></tr></thead>
|
||||
<tbody>
|
||||
<?php if ( empty( $service_log ) ) : ?>
|
||||
<tr><td colspan="6">Brak wpisów w dzienniku.</td></tr>
|
||||
<?php else : ?>
|
||||
<?php foreach ( $service_log as $log ) : ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html( $log->log_date ); ?></td>
|
||||
<td><?php echo esc_html( $log->log_type ); ?></td>
|
||||
<td><?php echo nl2br( esc_html( $log->description ) ); ?></td>
|
||||
<td><?php echo $log->cost ? number_format( $log->cost, 2, ',', ' ' ) . ' zł' : '-'; ?></td>
|
||||
<td><?php echo $log->mileage ? number_format( $log->mileage, 0, '', ' ' ) . ' km' : '-'; ?></td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'edit_log', 'log_id' => $log->id ) ) ); ?>">Edytuj</a> |
|
||||
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'delete_log', 'log_id' => $log->id ) ), 'statpress_delete_log_' . $log->id ) ); ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten wpis?')" style="color: #a00;">Usuń</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_equipment_page() {
|
||||
global $wpdb;
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
$message = '';
|
||||
$notice_class = '';
|
||||
|
||||
// Handle POST requests (add/update)
|
||||
if ( isset( $_POST['submit'] ) && check_admin_referer( 'statpress_manage_equipment' ) ) {
|
||||
$item_id = isset( $_POST['equipment_id'] ) ? intval( $_POST['equipment_id'] ) : 0;
|
||||
$data = array(
|
||||
'name' => sanitize_text_field( $_POST['equipment_name'] ),
|
||||
'type' => sanitize_text_field( $_POST['equipment_type'] ),
|
||||
'purchase_date' => empty( $_POST['purchase_date'] ) ? null : sanitize_text_field( $_POST['purchase_date'] ),
|
||||
'initial_cost' => empty( $_POST['initial_cost'] ) ? null : floatval( str_replace( ',', '.', $_POST['initial_cost'] ) ),
|
||||
'status' => sanitize_text_field( $_POST['status'] ),
|
||||
'notes' => sanitize_textarea_field( $_POST['notes'] ),
|
||||
);
|
||||
|
||||
if ( ! empty( $data['name'] ) ) {
|
||||
if ( $item_id > 0 ) { // Update
|
||||
$result = $wpdb->update( $table_equipment, $data, array( 'id' => $item_id ) );
|
||||
if ( false !== $result ) {
|
||||
$message = 'Sprzęt zaktualizowany.';
|
||||
$notice_class = 'notice-success';
|
||||
} else {
|
||||
$message = 'Błąd podczas aktualizacji sprzętu: ' . $wpdb->last_error;
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
} else { // Insert
|
||||
$result = $wpdb->insert( $table_equipment, $data );
|
||||
if ( false !== $result ) {
|
||||
$message = 'Sprzęt dodany.';
|
||||
$notice_class = 'notice-success';
|
||||
} else {
|
||||
$message = 'Błąd podczas dodawania sprzętu: ' . $wpdb->last_error;
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$message = 'Nazwa sprzętu nie może być pusta.';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle GET requests (delete)
|
||||
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'delete' === $_GET['action'] ) {
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_equipment_' . $_GET['id'] ) ) {
|
||||
// Sprawdź, czy sprzęt nie jest używany
|
||||
$usage_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}statpress_activities WHERE equipment_id = %d", intval( $_GET['id'] ) ) );
|
||||
if ( 0 == $usage_count ) {
|
||||
$wpdb->delete( $table_equipment, array( 'id' => intval( $_GET['id'] ) ) );
|
||||
// Usuń również powiązane wpisy w dzienniku
|
||||
$wpdb->delete( $wpdb->prefix . 'statpress_equipment_log', array( 'equipment_id' => intval( $_GET['id'] ) ) );
|
||||
$message = 'Sprzęt usunięty.';
|
||||
$notice_class = 'notice-success';
|
||||
} else {
|
||||
$message = 'Nie można usunąć sprzętu, ponieważ jest przypisany do ' . $usage_count . ' aktywności. Zmień jego status na "wycofany".';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for form (for editing)
|
||||
$item_to_edit = null;
|
||||
$statuses = array( 'aktywny', 'sprzedany', 'wycofany' );
|
||||
if ( isset( $_GET['action'], $_GET['id'] ) && 'edit' === $_GET['action'] ) {
|
||||
$item_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_equipment WHERE id = %d", intval( $_GET['id'] ) ) );
|
||||
}
|
||||
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$equipment_list = $wpdb->get_results("
|
||||
SELECT
|
||||
eq.id,
|
||||
eq.name,
|
||||
eq.type,
|
||||
eq.status,
|
||||
SUM(a.distance) as total_distance,
|
||||
COUNT(a.id) as activity_count
|
||||
FROM
|
||||
{$table_equipment} eq
|
||||
LEFT JOIN
|
||||
{$table_activities} a ON eq.id = a.equipment_id
|
||||
GROUP BY
|
||||
eq.id, eq.name, eq.type, eq.status
|
||||
ORDER BY
|
||||
eq.status ASC, eq.name ASC"
|
||||
);
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Zarządzaj Sprzętem</h1>
|
||||
<?php if ( ! empty( $message ) ) : ?>
|
||||
<div class="notice <?php echo esc_attr( $notice_class ); ?> is-dismissible"><p><?php echo esc_html( $message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="col-container" class="wp-clearfix">
|
||||
<div id="col-left">
|
||||
<div class="col-wrap">
|
||||
<div class="form-wrap">
|
||||
<h2><?php echo $item_to_edit ? 'Edytuj pozycję' : 'Dodaj nowy sprzęt'; ?></h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="equipment_id" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( 'statpress_manage_equipment' ); ?>
|
||||
<div class="form-field form-required"><label for="equipment_name">Nazwa</label><input type="text" name="equipment_name" id="equipment_name" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->name ) : ''; ?>" required></div>
|
||||
<div class="form-field"><label for="equipment_type">Typ</label><input type="text" name="equipment_type" id="equipment_type" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->type ) : 'Rower'; ?>"></div>
|
||||
<div class="form-field"><label for="purchase_date">Data zakupu</label><input type="date" name="purchase_date" id="purchase_date" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->purchase_date ) : ''; ?>"></div>
|
||||
<div class="form-field"><label for="initial_cost">Koszt zakupu</label><input type="text" name="initial_cost" id="initial_cost" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->initial_cost ) : ''; ?>" placeholder="0.00"></div>
|
||||
<div class="form-field"><label for="status">Status</label><select name="status" id="status"><?php foreach ( $statuses as $s ) : ?><option value="<?php echo esc_attr( $s ); ?>" <?php if ( $item_to_edit ) {
|
||||
selected( $item_to_edit->status, $s );} ?>><?php echo esc_html( ucfirst( $s ) ); ?></option><?php endforeach; ?></select></div>
|
||||
<div class="form-field"><label for="notes">Notatki</label><textarea name="notes" id="notes" rows="3"><?php echo $item_to_edit ? esc_textarea( $item_to_edit->notes ) : ''; ?></textarea></div>
|
||||
<?php submit_button( $item_to_edit ? 'Zaktualizuj' : 'Dodaj' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-right">
|
||||
<div class="col-wrap">
|
||||
<table class="wp-list-table widefat striped">
|
||||
<thead><tr><th>Nazwa</th><th>Przebieg</th><th>Liczba aktywności</th><th>Status</th><th style="width: 220px;">Akcje</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ( $equipment_list as $item ) : ?>
|
||||
<?php
|
||||
$details_url = admin_url( 'admin.php?page=statpress-equipment-details&id=' . $item->id );
|
||||
?>
|
||||
<tr>
|
||||
<td><strong><a href="<?php echo esc_url( $details_url ); ?>"><?php echo esc_html( $item->name ); ?></a></strong><br><small><?php echo esc_html( $item->type ); ?></small></td>
|
||||
<td><?php echo $item->total_distance ? number_format( $item->total_distance, 2, ',', ' ' ) . ' km' : '0 km'; ?></td>
|
||||
<td><?php echo (int) $item->activity_count; ?></td>
|
||||
<td><?php echo esc_html( ucfirst( $item->status ) ); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( $details_url ); ?>" class="button button-small">Dziennik / Serwis</a>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'edit', 'id' => $item->id ) ) ); ?>" class="button button-small">Edytuj</a>
|
||||
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'delete', 'id' => $item->id ) ), 'statpress_delete_equipment_' . $item->id ) ); ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten sprzęt?')" class="button button-small button-link-delete">Usuń</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
function statpress_equipment_details_page() {
|
||||
global $wpdb;
|
||||
$equipment_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
|
||||
|
||||
if ( 0 === $equipment_id ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie podano ID sprzętu.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
$table_equipment_log = $wpdb->prefix . 'statpress_equipment_log';
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$message = '';
|
||||
$notice_class = '';
|
||||
|
||||
// --- Handle Service Log form submissions (add/update/delete) ---
|
||||
if ( isset( $_POST['submit_log'] ) && check_admin_referer( 'statpress_manage_equipment_log' ) ) {
|
||||
$log_id = isset( $_POST['log_id'] ) ? intval( $_POST['log_id'] ) : 0;
|
||||
$log_data = array(
|
||||
'equipment_id' => $equipment_id,
|
||||
'log_date' => sanitize_text_field( $_POST['log_date'] ),
|
||||
'log_type' => sanitize_text_field( $_POST['log_type'] ),
|
||||
'description' => sanitize_textarea_field( $_POST['description'] ),
|
||||
'cost' => empty( $_POST['cost'] ) ? null : floatval( str_replace( ',', '.', $_POST['cost'] ) ),
|
||||
'mileage' => empty( $_POST['mileage'] ) ? null : intval( $_POST['mileage'] ),
|
||||
);
|
||||
|
||||
if ( ! empty( $log_data['log_date'] ) && ! empty( $log_data['log_type'] ) && ! empty( $log_data['description'] ) ) {
|
||||
if ( $log_id > 0 ) {
|
||||
$wpdb->update( $table_equipment_log, $log_data, array( 'id' => $log_id ) );
|
||||
$message = 'Wpis w dzienniku zaktualizowany.';
|
||||
} else {
|
||||
$wpdb->insert( $table_equipment_log, $log_data );
|
||||
$message = 'Wpis dodany do dziennika.';
|
||||
}
|
||||
$notice_class = 'notice-success';
|
||||
} else {
|
||||
$message = 'Wypełnij wymagane pola (Data, Typ, Opis).';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $_GET['action'], $_GET['log_id'], $_GET['_wpnonce'] ) && 'delete_log' === $_GET['action'] ) {
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_log_' . $_GET['log_id'] ) ) {
|
||||
$wpdb->delete( $table_equipment_log, array( 'id' => intval( $_GET['log_id'] ) ) );
|
||||
$message = 'Wpis z dziennika usunięty.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
}
|
||||
|
||||
// --- Get Data ---
|
||||
$equipment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_equipment WHERE id = %d", $equipment_id ) );
|
||||
if ( ! $equipment ) {
|
||||
echo '<div class="wrap"><h1>Błąd</h1><p>Nie znaleziono sprzętu o podanym ID.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$total_mileage = $wpdb->get_var( $wpdb->prepare( "SELECT SUM(distance) FROM $table_activities WHERE equipment_id = %d", $equipment_id ) );
|
||||
$total_service_cost = $wpdb->get_var( $wpdb->prepare( "SELECT SUM(cost) FROM $table_equipment_log WHERE equipment_id = %d", $equipment_id ) );
|
||||
$service_log = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_equipment_log WHERE equipment_id = %d ORDER BY log_date DESC, id DESC", $equipment_id ) );
|
||||
|
||||
$log_to_edit = null;
|
||||
if ( isset( $_GET['action'], $_GET['log_id'] ) && 'edit_log' === $_GET['action'] ) {
|
||||
$log_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_equipment_log WHERE id = %d", intval( $_GET['log_id'] ) ) );
|
||||
}
|
||||
$log_types = array( 'Naprawa', 'Zakup części', 'Przegląd', 'Modyfikacja', 'Inne' );
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Dziennik serwisowy: <?php echo esc_html( $equipment->name ); ?></h1>
|
||||
<p><a href="<?php echo esc_url( admin_url( 'admin.php?page=statpress-equipment' ) ); ?>">← Powrót do listy sprzętu</a></p>
|
||||
|
||||
<?php if ( ! empty( $message ) ) : ?>
|
||||
<div class="notice <?php echo esc_attr( $notice_class ); ?> is-dismissible"><p><?php echo esc_html( $message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="postbox">
|
||||
<div class="postbox-header"><h2 class="hndle">Podsumowanie Sprzętu</h2></div>
|
||||
<div class="inside">
|
||||
<div class="main">
|
||||
<p><strong>Całkowity przebieg:</strong> <?php echo number_format( (float) $total_mileage, 2, ',', ' ' ); ?> km</p>
|
||||
<?php if ( $total_service_cost > 0 ) : ?>
|
||||
<p><strong>Całkowity koszt serwisu:</strong> <?php echo number_format( $total_service_cost, 2, ',', ' ' ); ?> zł</p>
|
||||
<?php endif; ?>
|
||||
<p><strong>Status:</strong> <?php echo esc_html( ucfirst( $equipment->status ) ); ?></p>
|
||||
<?php if ( $equipment->purchase_date ) : ?><p><strong>Data zakupu:</strong> <?php echo esc_html( $equipment->purchase_date ); ?></p><?php endif; ?>
|
||||
<?php if ( $equipment->initial_cost ) : ?><p><strong>Koszt zakupu:</strong> <?php echo number_format( $equipment->initial_cost, 2, ',', ' ' ); ?> zł</p><?php endif; ?>
|
||||
<?php if ( $equipment->notes ) : ?><p><strong>Notatki:</strong><br><?php echo nl2br( esc_html( $equipment->notes ) ); ?></p><?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="col-container" class="wp-clearfix">
|
||||
<div id="col-left">
|
||||
<div class="col-wrap">
|
||||
<div class="form-wrap">
|
||||
<h2><?php echo $log_to_edit ? 'Edytuj wpis' : 'Dodaj wpis do dziennika'; ?></h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="log_id" value="<?php echo $log_to_edit ? esc_attr( $log_to_edit->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( 'statpress_manage_equipment_log' ); ?>
|
||||
<div class="form-field form-required"><label for="log_date">Data</label><input type="date" name="log_date" id="log_date" value="<?php echo $log_to_edit ? esc_attr( $log_to_edit->log_date ) : current_time( 'Y-m-d' ); ?>" required></div>
|
||||
<div class="form-field form-required"><label for="log_type">Typ</label><select name="log_type" id="log_type" required><?php foreach ( $log_types as $type ) : ?><option value="<?php echo esc_attr( $type ); ?>" <?php if ( $log_to_edit ) {
|
||||
selected( $log_to_edit->log_type, $type );} ?>><?php echo esc_html( $type ); ?></option><?php endforeach; ?></select></div>
|
||||
<div class="form-field form-required"><label for="description">Opis</label><textarea name="description" id="description" rows="4" required><?php echo $log_to_edit ? esc_textarea( $log_to_edit->description ) : ''; ?></textarea></div>
|
||||
<div class="form-field"><label for="cost">Koszt (zł)</label><input type="text" name="cost" id="cost" value="<?php echo $log_to_edit ? esc_attr( $log_to_edit->cost ) : ''; ?>" placeholder="0.00"></div>
|
||||
<div class="form-field"><label for="mileage">Przebieg (km)</label><input type="number" name="mileage" id="mileage" value="<?php echo $log_to_edit ? esc_attr( $log_to_edit->mileage ) : round( (float) $total_mileage ); ?>"><p class="description">Przebieg sprzętu w momencie serwisu. Domyślnie aktualny całkowity przebieg.</p></div>
|
||||
<?php submit_button( $log_to_edit ? 'Zaktualizuj wpis' : 'Dodaj wpis', 'primary', 'submit_log' ); ?>
|
||||
<?php if ( $log_to_edit ) : ?><a href="<?php echo esc_url( remove_query_arg( array( 'action', 'log_id' ) ) ); ?>" class="button">Anuluj edycję</a><?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-right">
|
||||
<div class="col-wrap">
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th colspan="3" style="text-align:right;">Suma kosztów:</th>
|
||||
<th colspan="3"><strong><?php echo number_format( (float) $total_service_cost, 2, ',', ' ' ); ?> zł</strong></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<thead><tr><th>Data</th><th>Typ</th><th>Opis</th><th>Koszt</th><th>Przebieg</th><th style="width: 100px;">Akcje</th></tr></thead>
|
||||
<tbody>
|
||||
<?php if ( empty( $service_log ) ) : ?>
|
||||
<tr><td colspan="6">Brak wpisów w dzienniku.</td></tr>
|
||||
<?php else : ?>
|
||||
<?php foreach ( $service_log as $log ) : ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html( $log->log_date ); ?></td>
|
||||
<td><?php echo esc_html( $log->log_type ); ?></td>
|
||||
<td><?php echo nl2br( esc_html( $log->description ) ); ?></td>
|
||||
<td><?php echo $log->cost ? number_format( $log->cost, 2, ',', ' ' ) . ' zł' : '-'; ?></td>
|
||||
<td><?php echo $log->mileage ? number_format( $log->mileage, 0, '', ' ' ) . ' km' : '-'; ?></td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'edit_log', 'log_id' => $log->id ) ) ); ?>">Edytuj</a> |
|
||||
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'delete_log', 'log_id' => $log->id ) ), 'statpress_delete_log_' . $log->id ) ); ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten wpis?')" style="color: #a00;">Usuń</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -1,88 +1,88 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_event_types_page() {
|
||||
global $wpdb;
|
||||
$table_event_types = $wpdb->prefix . 'statpress_event_types';
|
||||
$message = '';
|
||||
$notice_class = '';
|
||||
|
||||
// Handle POST requests (add/update)
|
||||
if ( isset( $_POST['submit'] ) && check_admin_referer( 'statpress_manage_event_type' ) ) {
|
||||
$name = sanitize_text_field( $_POST['event_type_name'] );
|
||||
$type_id = isset( $_POST['event_type_id'] ) ? intval( $_POST['event_type_id'] ) : 0;
|
||||
|
||||
if ( ! empty( $name ) ) {
|
||||
if ( $type_id > 0 ) { // Update
|
||||
$wpdb->update( $table_event_types, array( 'name' => $name ), array( 'id' => $type_id ) );
|
||||
$message = 'Typ wydarzenia zaktualizowany.';
|
||||
$notice_class = 'notice-success';
|
||||
} else { // Insert
|
||||
$wpdb->insert( $table_event_types, array( 'name' => $name ) );
|
||||
$message = 'Typ wydarzenia dodany.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
} else {
|
||||
$message = 'Nazwa typu wydarzenia nie może być pusta.';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle GET requests (delete)
|
||||
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'delete' === $_GET['action'] ) {
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_event_type_' . $_GET['id'] ) ) {
|
||||
$wpdb->delete( $table_event_types, array( 'id' => intval( $_GET['id'] ) ) );
|
||||
$message = 'Typ wydarzenia usunięty.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for form (for editing)
|
||||
$item_to_edit = null;
|
||||
if ( isset( $_GET['action'], $_GET['id'] ) && 'edit' === $_GET['action'] ) {
|
||||
$item_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_event_types WHERE id = %d", intval( $_GET['id'] ) ) );
|
||||
}
|
||||
|
||||
$event_types = $wpdb->get_results( "SELECT * FROM $table_event_types ORDER BY name ASC" );
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Typy Wydarzeń</h1>
|
||||
<?php if ( ! empty( $message ) ) : ?>
|
||||
<div class="notice <?php echo esc_attr( $notice_class ); ?> is-dismissible"><p><?php echo esc_html( $message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="col-container" class="wp-clearfix">
|
||||
<div id="col-left">
|
||||
<div class="col-wrap">
|
||||
<div class="form-wrap">
|
||||
<h2><?php echo $item_to_edit ? 'Edytuj typ wydarzenia' : 'Dodaj nowy typ wydarzenia'; ?></h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="event_type_id" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( 'statpress_manage_event_type' ); ?>
|
||||
<div class="form-field">
|
||||
<label for="event_type_name">Nazwa</label>
|
||||
<input type="text" name="event_type_name" id="event_type_name" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->name ) : ''; ?>" required>
|
||||
</div>
|
||||
<?php submit_button( $item_to_edit ? 'Zaktualizuj' : 'Dodaj' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-right">
|
||||
<div class="col-wrap">
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead><tr><th>Nazwa</th><th style="width: 100px;">Akcje</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ( $event_types as $type ) : ?>
|
||||
<tr><td><?php echo esc_html( $type->name ); ?></td><td><a href="<?php echo esc_url( add_query_arg( array( 'action' => 'edit', 'id' => $type->id ) ) ); ?>">Edytuj</a> | <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'delete', 'id' => $type->id ) ), 'statpress_delete_event_type_' . $type->id ) ); ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten typ?')" style="color: #a00;">Usuń</a></td></tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_event_types_page() {
|
||||
global $wpdb;
|
||||
$table_event_types = $wpdb->prefix . 'statpress_event_types';
|
||||
$message = '';
|
||||
$notice_class = '';
|
||||
|
||||
// Handle POST requests (add/update)
|
||||
if ( isset( $_POST['submit'] ) && check_admin_referer( 'statpress_manage_event_type' ) ) {
|
||||
$name = sanitize_text_field( $_POST['event_type_name'] );
|
||||
$type_id = isset( $_POST['event_type_id'] ) ? intval( $_POST['event_type_id'] ) : 0;
|
||||
|
||||
if ( ! empty( $name ) ) {
|
||||
if ( $type_id > 0 ) { // Update
|
||||
$wpdb->update( $table_event_types, array( 'name' => $name ), array( 'id' => $type_id ) );
|
||||
$message = 'Typ wydarzenia zaktualizowany.';
|
||||
$notice_class = 'notice-success';
|
||||
} else { // Insert
|
||||
$wpdb->insert( $table_event_types, array( 'name' => $name ) );
|
||||
$message = 'Typ wydarzenia dodany.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
} else {
|
||||
$message = 'Nazwa typu wydarzenia nie może być pusta.';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle GET requests (delete)
|
||||
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'delete' === $_GET['action'] ) {
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_event_type_' . $_GET['id'] ) ) {
|
||||
$wpdb->delete( $table_event_types, array( 'id' => intval( $_GET['id'] ) ) );
|
||||
$message = 'Typ wydarzenia usunięty.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for form (for editing)
|
||||
$item_to_edit = null;
|
||||
if ( isset( $_GET['action'], $_GET['id'] ) && 'edit' === $_GET['action'] ) {
|
||||
$item_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_event_types WHERE id = %d", intval( $_GET['id'] ) ) );
|
||||
}
|
||||
|
||||
$event_types = $wpdb->get_results( "SELECT * FROM $table_event_types ORDER BY name ASC" );
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Typy Wydarzeń</h1>
|
||||
<?php if ( ! empty( $message ) ) : ?>
|
||||
<div class="notice <?php echo esc_attr( $notice_class ); ?> is-dismissible"><p><?php echo esc_html( $message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="col-container" class="wp-clearfix">
|
||||
<div id="col-left">
|
||||
<div class="col-wrap">
|
||||
<div class="form-wrap">
|
||||
<h2><?php echo $item_to_edit ? 'Edytuj typ wydarzenia' : 'Dodaj nowy typ wydarzenia'; ?></h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="event_type_id" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( 'statpress_manage_event_type' ); ?>
|
||||
<div class="form-field">
|
||||
<label for="event_type_name">Nazwa</label>
|
||||
<input type="text" name="event_type_name" id="event_type_name" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->name ) : ''; ?>" required>
|
||||
</div>
|
||||
<?php submit_button( $item_to_edit ? 'Zaktualizuj' : 'Dodaj' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-right">
|
||||
<div class="col-wrap">
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead><tr><th>Nazwa</th><th style="width: 100px;">Akcje</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ( $event_types as $type ) : ?>
|
||||
<tr><td><?php echo esc_html( $type->name ); ?></td><td><a href="<?php echo esc_url( add_query_arg( array( 'action' => 'edit', 'id' => $type->id ) ) ); ?>">Edytuj</a> | <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'delete', 'id' => $type->id ) ), 'statpress_delete_event_type_' . $type->id ) ); ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten typ?')" style="color: #a00;">Usuń</a></td></tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
+198
-198
@@ -1,199 +1,199 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the current progress for a given goal.
|
||||
*
|
||||
* @param object $goal The goal object from the database.
|
||||
* @return array An array containing 'current_value' and 'percentage'.
|
||||
*/
|
||||
function statpress_get_goal_progress( $goal ) {
|
||||
global $wpdb;
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
|
||||
$sql_select = '';
|
||||
switch ( $goal->goal_type ) {
|
||||
case 'distance':
|
||||
$sql_select = 'SUM(distance)';
|
||||
break;
|
||||
case 'duration_sec':
|
||||
$sql_select = 'SUM(TIME_TO_SEC(duration))';
|
||||
break;
|
||||
case 'count':
|
||||
$sql_select = 'COUNT(id)';
|
||||
break;
|
||||
default:
|
||||
return array(
|
||||
'current_value' => 0,
|
||||
'percentage' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$where_clauses = array();
|
||||
$where_clauses[] = $wpdb->prepare( 'YEAR(date) = %d', $goal->year );
|
||||
|
||||
if ( ! empty( $goal->month ) ) {
|
||||
$where_clauses[] = $wpdb->prepare( 'MONTH(date) = %d', $goal->month );
|
||||
}
|
||||
if ( ! empty( $goal->category_id ) ) {
|
||||
$where_clauses[] = $wpdb->prepare( 'category_id = %d', $goal->category_id );
|
||||
}
|
||||
|
||||
$sql = "SELECT {$sql_select} FROM {$table_activities} WHERE " . implode( ' AND ', $where_clauses );
|
||||
|
||||
$current_value = (float) $wpdb->get_var( $sql );
|
||||
$percentage = ( $goal->target_value > 0 ) ? ( $current_value / $goal->target_value ) * 100 : 0;
|
||||
|
||||
return array(
|
||||
'current_value' => $current_value,
|
||||
'percentage' => $percentage,
|
||||
);
|
||||
}
|
||||
|
||||
function statpress_goals_page() {
|
||||
global $wpdb;
|
||||
$table_goals = $wpdb->prefix . 'statpress_goals';
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
$message = '';
|
||||
$notice_class = '';
|
||||
|
||||
// Handle POST requests (add/update)
|
||||
if ( isset( $_POST['submit'] ) && check_admin_referer( 'statpress_manage_goal' ) ) {
|
||||
$goal_id = isset( $_POST['goal_id'] ) ? intval( $_POST['goal_id'] ) : 0;
|
||||
|
||||
// Convert hours to seconds for duration goal type before saving
|
||||
$target_value = floatval( str_replace( ',', '.', $_POST['target_value'] ) );
|
||||
if ( 'duration_sec' === $_POST['goal_type'] ) {
|
||||
$target_value *= 3600;
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'name' => sanitize_text_field( $_POST['goal_name'] ),
|
||||
'goal_type' => sanitize_text_field( $_POST['goal_type'] ),
|
||||
'target_value' => $target_value,
|
||||
'year' => intval( $_POST['year'] ),
|
||||
'month' => 'all' === $_POST['month'] ? null : intval( $_POST['month'] ),
|
||||
'category_id' => 'all' === $_POST['category_id'] ? null : intval( $_POST['category_id'] ),
|
||||
);
|
||||
|
||||
if ( ! empty( $data['name'] ) && ! empty( $data['goal_type'] ) && $data['target_value'] > 0 && $data['year'] > 2000 ) {
|
||||
if ( $goal_id > 0 ) { // Update
|
||||
$wpdb->update( $table_goals, $data, array( 'id' => $goal_id ) );
|
||||
$message = 'Cel zaktualizowany.';
|
||||
$notice_class = 'notice-success';
|
||||
} else { // Insert
|
||||
$wpdb->insert( $table_goals, $data );
|
||||
$message = 'Cel dodany.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
} else {
|
||||
$message = 'Wypełnij poprawnie wszystkie wymagane pola (Nazwa, Typ, Cel, Rok).';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle GET requests (delete)
|
||||
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'delete' === $_GET['action'] ) {
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_goal_' . $_GET['id'] ) ) {
|
||||
$wpdb->delete( $table_goals, array( 'id' => intval( $_GET['id'] ) ) );
|
||||
$message = 'Cel usunięty.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for form (for editing)
|
||||
$item_to_edit = null;
|
||||
if ( isset( $_GET['action'], $_GET['id'] ) && 'edit' === $_GET['action'] ) {
|
||||
$item_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_goals WHERE id = %d", intval( $_GET['id'] ) ) );
|
||||
}
|
||||
|
||||
$goals = $wpdb->get_results( "SELECT g.*, c.name as category_name FROM $table_goals g LEFT JOIN $table_categories c ON g.category_id = c.id ORDER BY g.year DESC, g.name ASC" );
|
||||
$categories = $wpdb->get_results( "SELECT * FROM $table_categories ORDER BY name ASC" );
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Zarządzaj Celami</h1>
|
||||
<?php if ( ! empty( $message ) ) : ?>
|
||||
<div class="notice <?php echo esc_attr( $notice_class ); ?> is-dismissible"><p><?php echo esc_html( $message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="col-container" class="wp-clearfix">
|
||||
<div id="col-left">
|
||||
<div class="col-wrap">
|
||||
<div class="form-wrap">
|
||||
<h2><?php echo $item_to_edit ? 'Edytuj cel' : 'Dodaj nowy cel'; ?></h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="goal_id" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( 'statpress_manage_goal' ); ?>
|
||||
<div class="form-field form-required"><label for="goal_name">Nazwa celu</label><input type="text" name="goal_name" id="goal_name" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->name ) : ''; ?>" required></div>
|
||||
<div class="form-field form-required"><label for="goal_type">Typ celu</label><select name="goal_type" id="goal_type" required><option value="distance" <?php if ( $item_to_edit ) { selected( $item_to_edit->goal_type, 'distance' ); } ?>>Dystans (km)</option><option value="duration_sec" <?php if ( $item_to_edit ) { selected( $item_to_edit->goal_type, 'duration_sec' ); } ?>>Czas (w godzinach)</option><option value="count" <?php if ( $item_to_edit ) { selected( $item_to_edit->goal_type, 'count' ); } ?>>Liczba aktywności</option></select></div>
|
||||
<div class="form-field form-required"><label for="target_value">Wartość docelowa</label><input type="text" name="target_value" id="target_value" value="<?php echo $item_to_edit ? esc_attr( 'duration_sec' === $item_to_edit->goal_type ? $item_to_edit->target_value / 3600 : $item_to_edit->target_value ) : ''; ?>" required><p>Dla czasu podaj wartość w godzinach (np. 100.5).</p></div>
|
||||
<div class="form-field form-required"><label for="year">Rok</label><input type="number" name="year" id="year" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->year ) : current_time( 'Y' ); ?>" required></div>
|
||||
<div class="form-field"><label for="month">Miesiąc</label><select name="month" id="month"><option value="all">Cały rok</option><?php for ( $i = 1; $i <= 12; $i++ ) : ?><option value="<?php echo $i; ?>" <?php if ( $item_to_edit ) { selected( $item_to_edit->month, $i ); } ?>><?php echo date_i18n( 'F', mktime( 0, 0, 0, $i, 10 ) ); ?></option><?php endfor; ?></select></div>
|
||||
<div class="form-field"><label for="category_id">Kategoria</label><select name="category_id" id="category_id"><option value="all">Wszystkie</option><?php foreach ( $categories as $cat ) : ?><option value="<?php echo esc_attr( $cat->id ); ?>" <?php if ( $item_to_edit ) { selected( $item_to_edit->category_id, $cat->id ); } ?>><?php echo esc_html( $cat->name ); ?></option><?php endforeach; ?></select></div>
|
||||
<?php submit_button( $item_to_edit ? 'Zaktualizuj' : 'Dodaj' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-right">
|
||||
<div class="col-wrap">
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead><tr><th>Cel</th><th>Postęp</th><th style="width: 100px;">Akcje</th></tr></thead>
|
||||
<tbody>
|
||||
<?php if ( empty( $goals ) ) : ?>
|
||||
<tr><td colspan="3">Brak zdefiniowanych celów.</td></tr>
|
||||
<?php else : ?>
|
||||
<?php foreach ( $goals as $goal ) : ?>
|
||||
<?php
|
||||
$progress = statpress_get_goal_progress( $goal );
|
||||
$percentage = min( 100, $progress['percentage'] );
|
||||
|
||||
$target_formatted = '';
|
||||
$current_formatted = '';
|
||||
if ( 'duration_sec' === $goal->goal_type ) {
|
||||
$target_formatted = round( $goal->target_value / 3600 ) . ' godz.';
|
||||
$current_formatted = sprintf( '%d godz. %d min.', floor( $progress['current_value'] / 3600 ), floor( ( $progress['current_value'] % 3600 ) / 60 ) );
|
||||
} elseif ( 'distance' === $goal->goal_type ) {
|
||||
$target_formatted = number_format( $goal->target_value, 0, ',', ' ' ) . ' km';
|
||||
$current_formatted = number_format( $progress['current_value'], 2, ',', ' ' ) . ' km';
|
||||
} else { // count
|
||||
$target_formatted = (int) $goal->target_value;
|
||||
$current_formatted = (int) $progress['current_value'];
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?php echo esc_html( $goal->name ); ?></strong><br>
|
||||
<small>
|
||||
<?php echo esc_html( $goal->year ); ?>
|
||||
<?php if ( $goal->month ) { echo ' / ' . date_i18n( 'F', mktime( 0, 0, 0, $goal->month, 10 ) ); } ?>
|
||||
<?php if ( $goal->category_name ) { echo ' / ' . esc_html( $goal->category_name ); } ?>
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<div class="statpress-progress-bar-container">
|
||||
<div class="statpress-progress-bar" style="width: <?php echo esc_attr( $percentage ); ?>%;"></div>
|
||||
</div>
|
||||
<small><?php echo $current_formatted; ?> z <?php echo $target_formatted; ?> (<?php echo round( $percentage, 1 ); ?>%)</small>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'edit', 'id' => $goal->id ) ) ); ?>">Edytuj</a> |
|
||||
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'delete', 'id' => $goal->id ) ), 'statpress_delete_goal_' . $goal->id ) ); ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten cel?')" style="color: #a00;">Usuń</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.statpress-progress-bar-container { background: #eee; border-radius: 4px; height: 12px; width: 100%; overflow: hidden; margin-bottom: 4px; }
|
||||
.statpress-progress-bar { background: #3498db; height: 100%; border-radius: 4px; transition: width 0.5s ease-in-out; }
|
||||
</style>
|
||||
<?php
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the current progress for a given goal.
|
||||
*
|
||||
* @param object $goal The goal object from the database.
|
||||
* @return array An array containing 'current_value' and 'percentage'.
|
||||
*/
|
||||
function statpress_get_goal_progress( $goal ) {
|
||||
global $wpdb;
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
|
||||
$sql_select = '';
|
||||
switch ( $goal->goal_type ) {
|
||||
case 'distance':
|
||||
$sql_select = 'SUM(distance)';
|
||||
break;
|
||||
case 'duration_sec':
|
||||
$sql_select = 'SUM(TIME_TO_SEC(duration))';
|
||||
break;
|
||||
case 'count':
|
||||
$sql_select = 'COUNT(id)';
|
||||
break;
|
||||
default:
|
||||
return array(
|
||||
'current_value' => 0,
|
||||
'percentage' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$where_clauses = array();
|
||||
$where_clauses[] = $wpdb->prepare( 'YEAR(date) = %d', $goal->year );
|
||||
|
||||
if ( ! empty( $goal->month ) ) {
|
||||
$where_clauses[] = $wpdb->prepare( 'MONTH(date) = %d', $goal->month );
|
||||
}
|
||||
if ( ! empty( $goal->category_id ) ) {
|
||||
$where_clauses[] = $wpdb->prepare( 'category_id = %d', $goal->category_id );
|
||||
}
|
||||
|
||||
$sql = "SELECT {$sql_select} FROM {$table_activities} WHERE " . implode( ' AND ', $where_clauses );
|
||||
|
||||
$current_value = (float) $wpdb->get_var( $sql );
|
||||
$percentage = ( $goal->target_value > 0 ) ? ( $current_value / $goal->target_value ) * 100 : 0;
|
||||
|
||||
return array(
|
||||
'current_value' => $current_value,
|
||||
'percentage' => $percentage,
|
||||
);
|
||||
}
|
||||
|
||||
function statpress_goals_page() {
|
||||
global $wpdb;
|
||||
$table_goals = $wpdb->prefix . 'statpress_goals';
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
$message = '';
|
||||
$notice_class = '';
|
||||
|
||||
// Handle POST requests (add/update)
|
||||
if ( isset( $_POST['submit'] ) && check_admin_referer( 'statpress_manage_goal' ) ) {
|
||||
$goal_id = isset( $_POST['goal_id'] ) ? intval( $_POST['goal_id'] ) : 0;
|
||||
|
||||
// Convert hours to seconds for duration goal type before saving
|
||||
$target_value = floatval( str_replace( ',', '.', $_POST['target_value'] ) );
|
||||
if ( 'duration_sec' === $_POST['goal_type'] ) {
|
||||
$target_value *= 3600;
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'name' => sanitize_text_field( $_POST['goal_name'] ),
|
||||
'goal_type' => sanitize_text_field( $_POST['goal_type'] ),
|
||||
'target_value' => $target_value,
|
||||
'year' => intval( $_POST['year'] ),
|
||||
'month' => 'all' === $_POST['month'] ? null : intval( $_POST['month'] ),
|
||||
'category_id' => 'all' === $_POST['category_id'] ? null : intval( $_POST['category_id'] ),
|
||||
);
|
||||
|
||||
if ( ! empty( $data['name'] ) && ! empty( $data['goal_type'] ) && $data['target_value'] > 0 && $data['year'] > 2000 ) {
|
||||
if ( $goal_id > 0 ) { // Update
|
||||
$wpdb->update( $table_goals, $data, array( 'id' => $goal_id ) );
|
||||
$message = 'Cel zaktualizowany.';
|
||||
$notice_class = 'notice-success';
|
||||
} else { // Insert
|
||||
$wpdb->insert( $table_goals, $data );
|
||||
$message = 'Cel dodany.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
} else {
|
||||
$message = 'Wypełnij poprawnie wszystkie wymagane pola (Nazwa, Typ, Cel, Rok).';
|
||||
$notice_class = 'notice-error';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle GET requests (delete)
|
||||
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'delete' === $_GET['action'] ) {
|
||||
if ( wp_verify_nonce( $_GET['_wpnonce'], 'statpress_delete_goal_' . $_GET['id'] ) ) {
|
||||
$wpdb->delete( $table_goals, array( 'id' => intval( $_GET['id'] ) ) );
|
||||
$message = 'Cel usunięty.';
|
||||
$notice_class = 'notice-success';
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for form (for editing)
|
||||
$item_to_edit = null;
|
||||
if ( isset( $_GET['action'], $_GET['id'] ) && 'edit' === $_GET['action'] ) {
|
||||
$item_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_goals WHERE id = %d", intval( $_GET['id'] ) ) );
|
||||
}
|
||||
|
||||
$goals = $wpdb->get_results( "SELECT g.*, c.name as category_name FROM $table_goals g LEFT JOIN $table_categories c ON g.category_id = c.id ORDER BY g.year DESC, g.name ASC" );
|
||||
$categories = $wpdb->get_results( "SELECT * FROM $table_categories ORDER BY name ASC" );
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Zarządzaj Celami</h1>
|
||||
<?php if ( ! empty( $message ) ) : ?>
|
||||
<div class="notice <?php echo esc_attr( $notice_class ); ?> is-dismissible"><p><?php echo esc_html( $message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="col-container" class="wp-clearfix">
|
||||
<div id="col-left">
|
||||
<div class="col-wrap">
|
||||
<div class="form-wrap">
|
||||
<h2><?php echo $item_to_edit ? 'Edytuj cel' : 'Dodaj nowy cel'; ?></h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="goal_id" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->id ) : '0'; ?>">
|
||||
<?php wp_nonce_field( 'statpress_manage_goal' ); ?>
|
||||
<div class="form-field form-required"><label for="goal_name">Nazwa celu</label><input type="text" name="goal_name" id="goal_name" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->name ) : ''; ?>" required></div>
|
||||
<div class="form-field form-required"><label for="goal_type">Typ celu</label><select name="goal_type" id="goal_type" required><option value="distance" <?php if ( $item_to_edit ) { selected( $item_to_edit->goal_type, 'distance' ); } ?>>Dystans (km)</option><option value="duration_sec" <?php if ( $item_to_edit ) { selected( $item_to_edit->goal_type, 'duration_sec' ); } ?>>Czas (w godzinach)</option><option value="count" <?php if ( $item_to_edit ) { selected( $item_to_edit->goal_type, 'count' ); } ?>>Liczba aktywności</option></select></div>
|
||||
<div class="form-field form-required"><label for="target_value">Wartość docelowa</label><input type="text" name="target_value" id="target_value" value="<?php echo $item_to_edit ? esc_attr( 'duration_sec' === $item_to_edit->goal_type ? $item_to_edit->target_value / 3600 : $item_to_edit->target_value ) : ''; ?>" required><p>Dla czasu podaj wartość w godzinach (np. 100.5).</p></div>
|
||||
<div class="form-field form-required"><label for="year">Rok</label><input type="number" name="year" id="year" value="<?php echo $item_to_edit ? esc_attr( $item_to_edit->year ) : current_time( 'Y' ); ?>" required></div>
|
||||
<div class="form-field"><label for="month">Miesiąc</label><select name="month" id="month"><option value="all">Cały rok</option><?php for ( $i = 1; $i <= 12; $i++ ) : ?><option value="<?php echo $i; ?>" <?php if ( $item_to_edit ) { selected( $item_to_edit->month, $i ); } ?>><?php echo date_i18n( 'F', mktime( 0, 0, 0, $i, 10 ) ); ?></option><?php endfor; ?></select></div>
|
||||
<div class="form-field"><label for="category_id">Kategoria</label><select name="category_id" id="category_id"><option value="all">Wszystkie</option><?php foreach ( $categories as $cat ) : ?><option value="<?php echo esc_attr( $cat->id ); ?>" <?php if ( $item_to_edit ) { selected( $item_to_edit->category_id, $cat->id ); } ?>><?php echo esc_html( $cat->name ); ?></option><?php endforeach; ?></select></div>
|
||||
<?php submit_button( $item_to_edit ? 'Zaktualizuj' : 'Dodaj' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-right">
|
||||
<div class="col-wrap">
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead><tr><th>Cel</th><th>Postęp</th><th style="width: 100px;">Akcje</th></tr></thead>
|
||||
<tbody>
|
||||
<?php if ( empty( $goals ) ) : ?>
|
||||
<tr><td colspan="3">Brak zdefiniowanych celów.</td></tr>
|
||||
<?php else : ?>
|
||||
<?php foreach ( $goals as $goal ) : ?>
|
||||
<?php
|
||||
$progress = statpress_get_goal_progress( $goal );
|
||||
$percentage = min( 100, $progress['percentage'] );
|
||||
|
||||
$target_formatted = '';
|
||||
$current_formatted = '';
|
||||
if ( 'duration_sec' === $goal->goal_type ) {
|
||||
$target_formatted = round( $goal->target_value / 3600 ) . ' godz.';
|
||||
$current_formatted = sprintf( '%d godz. %d min.', floor( $progress['current_value'] / 3600 ), floor( ( $progress['current_value'] % 3600 ) / 60 ) );
|
||||
} elseif ( 'distance' === $goal->goal_type ) {
|
||||
$target_formatted = number_format( $goal->target_value, 0, ',', ' ' ) . ' km';
|
||||
$current_formatted = number_format( $progress['current_value'], 2, ',', ' ' ) . ' km';
|
||||
} else { // count
|
||||
$target_formatted = (int) $goal->target_value;
|
||||
$current_formatted = (int) $progress['current_value'];
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?php echo esc_html( $goal->name ); ?></strong><br>
|
||||
<small>
|
||||
<?php echo esc_html( $goal->year ); ?>
|
||||
<?php if ( $goal->month ) { echo ' / ' . date_i18n( 'F', mktime( 0, 0, 0, $goal->month, 10 ) ); } ?>
|
||||
<?php if ( $goal->category_name ) { echo ' / ' . esc_html( $goal->category_name ); } ?>
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<div class="statpress-progress-bar-container">
|
||||
<div class="statpress-progress-bar" style="width: <?php echo esc_attr( $percentage ); ?>%;"></div>
|
||||
</div>
|
||||
<small><?php echo $current_formatted; ?> z <?php echo $target_formatted; ?> (<?php echo round( $percentage, 1 ); ?>%)</small>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'edit', 'id' => $goal->id ) ) ); ?>">Edytuj</a> |
|
||||
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'delete', 'id' => $goal->id ) ), 'statpress_delete_goal_' . $goal->id ) ); ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten cel?')" style="color: #a00;">Usuń</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.statpress-progress-bar-container { background: #eee; border-radius: 4px; height: 12px; width: 100%; overflow: hidden; margin-bottom: 4px; }
|
||||
.statpress-progress-bar { background: #3498db; height: 100%; border-radius: 4px; transition: width 0.5s ease-in-out; }
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
@@ -1,309 +1,309 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_import_csv_page() {
|
||||
echo '<div class="wrap"><h1>Importuj aktywności z pliku CSV</h1>';
|
||||
|
||||
// Handle the form submission
|
||||
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['statpress_csv_import_nonce_field'] ) ) {
|
||||
statpress_handle_csv_import();
|
||||
}
|
||||
|
||||
// Display the form
|
||||
?>
|
||||
<div class="postbox">
|
||||
<div class="postbox-header"><h2 class="hndle">Instrukcje i formularz importu</h2></div>
|
||||
<div class="inside">
|
||||
<p>Aby zaimportować dane, możesz wgrać plik CSV <strong>LUB</strong> wkleić dane bezpośrednio w pole tekstowe poniżej. Ta druga opcja jest zalecana, jeśli napotykasz błędy z plikiem.</p>
|
||||
<p><strong>Wymagane kolumny:</strong> <code>Data</code>, <code>Tytuł</code>, <code>Dystans</code> oraz <code>Typ aktywności</code> (lub <code>Kategoria</code>).</p>
|
||||
<p><strong>Opcjonalne kolumny:</strong> <code>Czas</code> (format HH:MM:SS), <code>Kalorie</code>, <code>Średnie tętno</code>, <code>Maksymalne tętno</code>, <code>Średnia prędkość</code>, <code>Maksymalna prędkość</code>, <code>Średni rytm pedałowania</code>, <code>Maksymalny rytm pedałowania</code>, <code>Całkowity wznios</code>, <code>Całkowity spadek</code>, <code>Minimalna wysokość</code>, <code>Maksymalna wysokość</code>, <code>Sprzęt</code>, <code>Typ wydarzenia</code>.</p>
|
||||
<p><strong>Ważne:</strong>
|
||||
<ul>
|
||||
<li>Data musi być w formacie <code>YYYY-MM-DD</code>.</li>
|
||||
<li>Dystans i prędkość: użyj kropki jako separatora dziesiętnego (np. <code>10.5</code>).</li>
|
||||
<li>Nazwy w kolumnach <code>Typ aktywności</code>, <code>Sprzęt</code>, <code>Typ wydarzenia</code> muszą dokładnie odpowiadać nazwom zdefiniowanym w ustawieniach wtyczki. Jeśli nazwa nie zostanie znaleziona, wiersz zostanie pominięty.</li>
|
||||
</ul>
|
||||
</p>
|
||||
<hr>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<?php wp_nonce_field( 'statpress_csv_import_nonce', 'statpress_csv_import_nonce_field' ); ?>
|
||||
<table class="form-table">
|
||||
<tr valign="top">
|
||||
<th scope="row"><label for="statpress_csv_file">Opcja 1: Wgraj plik CSV</label></th>
|
||||
<td><input type="file" id="statpress_csv_file" name="statpress_csv_file" accept=".csv,text/csv" /></td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<th scope="row"><label for="statpress_csv_data">Opcja 2: Wklej dane CSV</label></th>
|
||||
<td><textarea name="statpress_csv_data" id="statpress_csv_data" rows="15" class="large-text" placeholder="Wklej tutaj zawartość swojego pliku CSV... Typ aktywności,Data,Tytuł,Dystans,... Rower,2025-01-01,Nowy Rok,10.5,..."></textarea></td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php submit_button( 'Importuj' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
function statpress_handle_csv_import() {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! isset( $_POST['statpress_csv_import_nonce_field'] ) || ! wp_verify_nonce( $_POST['statpress_csv_import_nonce_field'], 'statpress_csv_import_nonce' ) ) {
|
||||
echo '<div class="notice notice-error"><p>Błąd weryfikacji bezpieczeństwa.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
echo '<div class="notice notice-error"><p>Nie masz wystarczających uprawnień.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Unify input source: prefer textarea, fall back to file upload.
|
||||
$csv_content = '';
|
||||
if ( ! empty( $_POST['statpress_csv_data'] ) ) {
|
||||
$csv_content = stripslashes( $_POST['statpress_csv_data'] );
|
||||
} elseif ( ! empty( $_FILES['statpress_csv_file']['tmp_name'] ) && UPLOAD_ERR_OK === $_FILES['statpress_csv_file']['error'] ) {
|
||||
$csv_content = file_get_contents( $_FILES['statpress_csv_file']['tmp_name'] );
|
||||
}
|
||||
|
||||
if ( empty( trim( $csv_content ) ) ) {
|
||||
echo '<div class="notice notice-error"><p>Nie podano danych do importu. Wgraj plik lub wklej dane w pole tekstowe.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Mapowanie polskich i angielskich nazw kolumn na wewnętrzne klucze
|
||||
$column_map = array(
|
||||
// Polish => English
|
||||
'typ aktywności' => 'category_name',
|
||||
'data' => 'date',
|
||||
'tytuł' => 'title',
|
||||
'dystans' => 'distance',
|
||||
'kalorie' => 'calories',
|
||||
'czas' => 'duration',
|
||||
'średnie tętno' => 'avg_heart_rate',
|
||||
'maksymalne tętno' => 'max_heart_rate',
|
||||
'średnia prędkość' => 'avg_speed',
|
||||
'maksymalna prędkość' => 'max_speed',
|
||||
'średni rytm pedałowania' => 'avg_cadence',
|
||||
'maksymalny rytm pedałowania' => 'max_cadence',
|
||||
'całkowity wznios' => 'total_elevation_gain',
|
||||
'całkowity spadek' => 'total_elevation_loss',
|
||||
'minimalna wysokość' => 'min_altitude',
|
||||
'maksymalna wysokość' => 'max_altitude',
|
||||
'sprzęt' => 'equipment_name',
|
||||
'typ wydarzenia' => 'event_type_name',
|
||||
'komentarz' => 'comment',
|
||||
'link do strava' => 'strava_url',
|
||||
// English keys for compatibility
|
||||
'category_name' => 'category_name',
|
||||
'date' => 'date',
|
||||
'title' => 'title',
|
||||
'distance' => 'distance',
|
||||
'calories' => 'calories',
|
||||
'duration' => 'duration',
|
||||
'avg_heart_rate' => 'avg_heart_rate',
|
||||
'max_heart_rate' => 'max_heart_rate',
|
||||
'avg_speed' => 'avg_speed',
|
||||
'max_speed' => 'max_speed',
|
||||
'avg_cadence' => 'avg_cadence',
|
||||
'max_cadence' => 'max_cadence',
|
||||
'total_elevation_gain' => 'total_elevation_gain',
|
||||
'total_elevation_loss' => 'total_elevation_loss',
|
||||
'min_altitude' => 'min_altitude',
|
||||
'max_altitude' => 'max_altitude',
|
||||
'equipment_name' => 'equipment_name',
|
||||
'event_type_name' => 'event_type_name',
|
||||
'comment' => 'comment',
|
||||
'strava_url' => 'strava_url',
|
||||
);
|
||||
|
||||
// --- START: Robust, case-insensitive lookup ---
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
$table_event_types = $wpdb->prefix . 'statpress_event_types';
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
|
||||
$create_lookup = function( $table_name ) use ( $wpdb ) {
|
||||
$items = $wpdb->get_results( "SELECT id, name FROM {$table_name}" );
|
||||
$lookup = array();
|
||||
if ( is_array( $items ) ) {
|
||||
foreach ( $items as $item ) {
|
||||
// Use a robust trim to handle various whitespace characters and make it case-insensitive
|
||||
$clean_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $item->name );
|
||||
$lookup[ mb_strtolower( $clean_name, 'UTF-8' ) ] = $item->id;
|
||||
}
|
||||
}
|
||||
return $lookup;
|
||||
};
|
||||
|
||||
$categories_lookup = $create_lookup( $table_categories );
|
||||
$event_types_lookup = $create_lookup( $table_event_types );
|
||||
$equipment_lookup = $create_lookup( $table_equipment );
|
||||
// --- END: Robust, case-insensitive lookup ---
|
||||
|
||||
// Process the CSV file
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$imported_count = 0;
|
||||
$skipped_details = array();
|
||||
$row_number = 1; // Header is row 1
|
||||
|
||||
// Normalize line endings and split into lines
|
||||
$lines = str_replace( array( "\r\n", "\r" ), "\n", $csv_content );
|
||||
$lines = explode( "\n", $lines );
|
||||
|
||||
if ( empty( $lines ) || empty( trim( $lines[0] ) ) ) {
|
||||
echo '<div class="notice notice-info"><p>Podane dane CSV są puste.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Delimiter and BOM detection ---
|
||||
$first_line = $lines[0];
|
||||
$delimiter = ( substr_count( $first_line, ';' ) > substr_count( $first_line, ',' ) ) ? ';' : ',';
|
||||
|
||||
// --- BOM removal from first header element ---
|
||||
$bom = "\xEF\xBB\xBF";
|
||||
if ( substr( $first_line, 0, 3 ) === $bom ) {
|
||||
$lines[0] = substr( $first_line, 3 );
|
||||
}
|
||||
|
||||
$header_raw = str_getcsv( array_shift( $lines ), $delimiter );
|
||||
$header_raw = array_map( 'trim', $header_raw );
|
||||
|
||||
// Translate header from Polish/English to internal keys
|
||||
$header = array();
|
||||
foreach ( $header_raw as $col ) {
|
||||
$header[] = $column_map[ strtolower( $col ) ] ?? 'ignored_' . uniqid();
|
||||
}
|
||||
|
||||
$required_internal_keys = array( 'date', 'title', 'category_name', 'distance' );
|
||||
$missing_keys = array_diff( $required_internal_keys, $header );
|
||||
if ( ! empty( $missing_keys ) ) {
|
||||
$key_to_polish_map = array(
|
||||
'date' => 'Data',
|
||||
'title' => 'Tytuł',
|
||||
'category_name' => 'Kategoria / Typ aktywności',
|
||||
'distance' => 'Dystans',
|
||||
);
|
||||
$missing_polish_names = array_map( fn( $key ) => $key_to_polish_map[ $key ] ?? $key, $missing_keys );
|
||||
echo '<div class="notice notice-error"><p>Brak wymaganych kolumn w danych CSV: ' . esc_html( implode( ', ', $missing_polish_names ) ) . '</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$parse_and_round_int = fn( $val ) => round( floatval( str_replace( ',', '.', $val ) ) );
|
||||
$null_if_empty = fn( $value ) => '' !== $value ? $value : null;
|
||||
|
||||
foreach ( $lines as $line ) {
|
||||
$row_number++;
|
||||
if ( empty( trim( $line ) ) ) {
|
||||
continue; // Skip empty lines
|
||||
}
|
||||
$data = str_getcsv( $line, $delimiter );
|
||||
|
||||
if ( count( $data ) !== count( $header ) ) {
|
||||
$skipped_details[] = array(
|
||||
'row' => $row_number,
|
||||
'reason' => 'Nieprawidłowa liczba kolumn (oczekiwano ' . count( $header ) . ', otrzymano ' . count( $data ) . ').',
|
||||
'data' => $line,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$row_data = array_combine( $header, $data );
|
||||
|
||||
// Detailed validation
|
||||
$validation_errors = array();
|
||||
if ( empty( $row_data['date'] ) ) {
|
||||
$validation_errors[] = 'brak daty';
|
||||
}
|
||||
if ( empty( $row_data['title'] ) ) {
|
||||
$validation_errors[] = 'brak tytułu';
|
||||
}
|
||||
if ( ! isset( $row_data['distance'] ) || '' === $row_data['distance'] ) {
|
||||
$validation_errors[] = 'brak dystansu';
|
||||
}
|
||||
|
||||
$category_name = $row_data['category_name'] ?? '';
|
||||
$clean_category_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $category_name );
|
||||
$category_id = $categories_lookup[ mb_strtolower( $clean_category_name, 'UTF-8' ) ] ?? null;
|
||||
|
||||
if ( empty( $clean_category_name ) ) {
|
||||
$validation_errors[] = 'brak nazwy kategorii';
|
||||
} elseif ( is_null( $category_id ) ) {
|
||||
$available_categories_from_db = $wpdb->get_col( "SELECT name FROM $table_categories ORDER BY name" );
|
||||
$validation_errors[] = 'nieznana kategoria: "' . esc_html( $category_name ) . '". Sprawdź, czy nazwa jest poprawna. Dostępne w bazie: "' . esc_html( implode( '", "', $available_categories_from_db ) ) . '".';
|
||||
}
|
||||
|
||||
if ( ! empty( $validation_errors ) ) {
|
||||
$skipped_details[] = array(
|
||||
'row' => $row_number,
|
||||
'reason' => ucfirst( implode( ', ', $validation_errors ) ) . '.',
|
||||
'data' => $line,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get IDs for optional fields using the same robust method
|
||||
$get_id = function( $name, $lookup_table ) {
|
||||
$clean_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $name );
|
||||
return $lookup_table[ mb_strtolower( $clean_name, 'UTF-8' ) ] ?? null;
|
||||
};
|
||||
|
||||
$equipment_id = $get_id( $row_data['equipment_name'] ?? '', $equipment_lookup );
|
||||
$event_type_id = $get_id( $row_data['event_type_name'] ?? '', $event_types_lookup );
|
||||
|
||||
$insert_data = array(
|
||||
'date' => sanitize_text_field( $row_data['date'] ),
|
||||
'title' => sanitize_text_field( $row_data['title'] ),
|
||||
'category_id' => $category_id,
|
||||
'distance' => floatval( str_replace( ',', '.', $row_data['distance'] ) ),
|
||||
'duration' => isset( $row_data['duration'] ) ? sanitize_text_field( $row_data['duration'] ) : '00:00:00',
|
||||
'calories' => isset( $row_data['calories'] ) ? intval( str_replace( ',', '.', $row_data['calories'] ) ) : 0,
|
||||
'comment' => isset( $row_data['comment'] ) ? sanitize_textarea_field( $row_data['comment'] ) : null,
|
||||
'strava_url' => isset( $row_data['strava_url'] ) ? $null_if_empty( esc_url_raw( $row_data['strava_url'] ) ) : null,
|
||||
'avg_heart_rate' => isset( $row_data['avg_heart_rate'] ) ? $null_if_empty( $parse_and_round_int( $row_data['avg_heart_rate'] ) ) : null,
|
||||
'max_heart_rate' => isset( $row_data['max_heart_rate'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_heart_rate'] ) ) : null,
|
||||
'avg_speed' => isset( $row_data['avg_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $row_data['avg_speed'] ) ) ) : null,
|
||||
'max_speed' => isset( $row_data['max_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $row_data['max_speed'] ) ) ) : null,
|
||||
'avg_cadence' => isset( $row_data['avg_cadence'] ) ? $null_if_empty( $parse_and_round_int( $row_data['avg_cadence'] ) ) : null,
|
||||
'max_cadence' => isset( $row_data['max_cadence'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_cadence'] ) ) : null,
|
||||
'total_elevation_gain' => isset( $row_data['total_elevation_gain'] ) ? $null_if_empty( $parse_and_round_int( $row_data['total_elevation_gain'] ) ) : null,
|
||||
'total_elevation_loss' => isset( $row_data['total_elevation_loss'] ) ? $null_if_empty( $parse_and_round_int( $row_data['total_elevation_loss'] ) ) : null,
|
||||
'min_altitude' => isset( $row_data['min_altitude'] ) ? $null_if_empty( $parse_and_round_int( $row_data['min_altitude'] ) ) : null,
|
||||
'max_altitude' => isset( $row_data['max_altitude'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_altitude'] ) ) : null,
|
||||
'equipment_id' => $equipment_id,
|
||||
'event_type_id' => $event_type_id,
|
||||
);
|
||||
|
||||
if ( $wpdb->insert( $table_activities, $insert_data ) ) {
|
||||
$imported_count++;
|
||||
} else {
|
||||
$skipped_details[] = array(
|
||||
'row' => $row_number,
|
||||
'reason' => 'Błąd zapisu do bazy danych. (' . esc_html( $wpdb->last_error ) . ')',
|
||||
'data' => $line,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $imported_count > 0 ) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>Pomyślnie zaimportowano ' . esc_html( $imported_count ) . ' aktywności.</p></div>';}
|
||||
if ( ! empty( $skipped_details ) ) {
|
||||
echo '<div class="notice notice-warning">';
|
||||
echo '<h4>Pominięto ' . count( $skipped_details ) . ' wierszy z powodu błędów</h4>';
|
||||
echo '<div style="max-height: 300px; overflow-y: auto; border: 1px solid #c3c4c7; padding: 10px; background: #fff; margin-top: 10px; font-size: 12px;">';
|
||||
echo '<table class="wp-list-table widefat striped" style="margin:0;"><thead><tr><th style="width:80px">Wiersz</th><th>Powód pominięcia</th><th>Dane wiersza</th></tr></thead><tbody>';
|
||||
foreach ( $skipped_details as $error ) {
|
||||
echo '<tr>';
|
||||
echo '<td>' . esc_html( $error['row'] ) . '</td>';
|
||||
echo '<td>' . esc_html( $error['reason'] ) . '</td>';
|
||||
echo '<td><small>' . esc_html( wp_trim_words( $error['data'], 25, '...' ) ) . '</small></td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
echo '</div></div>';
|
||||
}
|
||||
if ( 0 === $imported_count && empty( $skipped_details ) && $row_number > 1 ) {
|
||||
echo '<div class="notice notice-info"><p>Dane CSV nie zawierały żadnych poprawnych wierszy do importu.</p></div>';
|
||||
} elseif ( 1 === $row_number ) {
|
||||
echo '<div class="notice notice-info"><p>Dane CSV były puste lub zawierały tylko nagłówek.</p></div>';}
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_import_csv_page() {
|
||||
echo '<div class="wrap"><h1>Importuj aktywności z pliku CSV</h1>';
|
||||
|
||||
// Handle the form submission
|
||||
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['statpress_csv_import_nonce_field'] ) ) {
|
||||
statpress_handle_csv_import();
|
||||
}
|
||||
|
||||
// Display the form
|
||||
?>
|
||||
<div class="postbox">
|
||||
<div class="postbox-header"><h2 class="hndle">Instrukcje i formularz importu</h2></div>
|
||||
<div class="inside">
|
||||
<p>Aby zaimportować dane, możesz wgrać plik CSV <strong>LUB</strong> wkleić dane bezpośrednio w pole tekstowe poniżej. Ta druga opcja jest zalecana, jeśli napotykasz błędy z plikiem.</p>
|
||||
<p><strong>Wymagane kolumny:</strong> <code>Data</code>, <code>Tytuł</code>, <code>Dystans</code> oraz <code>Typ aktywności</code> (lub <code>Kategoria</code>).</p>
|
||||
<p><strong>Opcjonalne kolumny:</strong> <code>Czas</code> (format HH:MM:SS), <code>Kalorie</code>, <code>Średnie tętno</code>, <code>Maksymalne tętno</code>, <code>Średnia prędkość</code>, <code>Maksymalna prędkość</code>, <code>Średni rytm pedałowania</code>, <code>Maksymalny rytm pedałowania</code>, <code>Całkowity wznios</code>, <code>Całkowity spadek</code>, <code>Minimalna wysokość</code>, <code>Maksymalna wysokość</code>, <code>Sprzęt</code>, <code>Typ wydarzenia</code>.</p>
|
||||
<p><strong>Ważne:</strong>
|
||||
<ul>
|
||||
<li>Data musi być w formacie <code>YYYY-MM-DD</code>.</li>
|
||||
<li>Dystans i prędkość: użyj kropki jako separatora dziesiętnego (np. <code>10.5</code>).</li>
|
||||
<li>Nazwy w kolumnach <code>Typ aktywności</code>, <code>Sprzęt</code>, <code>Typ wydarzenia</code> muszą dokładnie odpowiadać nazwom zdefiniowanym w ustawieniach wtyczki. Jeśli nazwa nie zostanie znaleziona, wiersz zostanie pominięty.</li>
|
||||
</ul>
|
||||
</p>
|
||||
<hr>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<?php wp_nonce_field( 'statpress_csv_import_nonce', 'statpress_csv_import_nonce_field' ); ?>
|
||||
<table class="form-table">
|
||||
<tr valign="top">
|
||||
<th scope="row"><label for="statpress_csv_file">Opcja 1: Wgraj plik CSV</label></th>
|
||||
<td><input type="file" id="statpress_csv_file" name="statpress_csv_file" accept=".csv,text/csv" /></td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<th scope="row"><label for="statpress_csv_data">Opcja 2: Wklej dane CSV</label></th>
|
||||
<td><textarea name="statpress_csv_data" id="statpress_csv_data" rows="15" class="large-text" placeholder="Wklej tutaj zawartość swojego pliku CSV... Typ aktywności,Data,Tytuł,Dystans,... Rower,2025-01-01,Nowy Rok,10.5,..."></textarea></td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php submit_button( 'Importuj' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
function statpress_handle_csv_import() {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! isset( $_POST['statpress_csv_import_nonce_field'] ) || ! wp_verify_nonce( $_POST['statpress_csv_import_nonce_field'], 'statpress_csv_import_nonce' ) ) {
|
||||
echo '<div class="notice notice-error"><p>Błąd weryfikacji bezpieczeństwa.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
echo '<div class="notice notice-error"><p>Nie masz wystarczających uprawnień.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Unify input source: prefer textarea, fall back to file upload.
|
||||
$csv_content = '';
|
||||
if ( ! empty( $_POST['statpress_csv_data'] ) ) {
|
||||
$csv_content = stripslashes( $_POST['statpress_csv_data'] );
|
||||
} elseif ( ! empty( $_FILES['statpress_csv_file']['tmp_name'] ) && UPLOAD_ERR_OK === $_FILES['statpress_csv_file']['error'] ) {
|
||||
$csv_content = file_get_contents( $_FILES['statpress_csv_file']['tmp_name'] );
|
||||
}
|
||||
|
||||
if ( empty( trim( $csv_content ) ) ) {
|
||||
echo '<div class="notice notice-error"><p>Nie podano danych do importu. Wgraj plik lub wklej dane w pole tekstowe.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Mapowanie polskich i angielskich nazw kolumn na wewnętrzne klucze
|
||||
$column_map = array(
|
||||
// Polish => English
|
||||
'typ aktywności' => 'category_name',
|
||||
'data' => 'date',
|
||||
'tytuł' => 'title',
|
||||
'dystans' => 'distance',
|
||||
'kalorie' => 'calories',
|
||||
'czas' => 'duration',
|
||||
'średnie tętno' => 'avg_heart_rate',
|
||||
'maksymalne tętno' => 'max_heart_rate',
|
||||
'średnia prędkość' => 'avg_speed',
|
||||
'maksymalna prędkość' => 'max_speed',
|
||||
'średni rytm pedałowania' => 'avg_cadence',
|
||||
'maksymalny rytm pedałowania' => 'max_cadence',
|
||||
'całkowity wznios' => 'total_elevation_gain',
|
||||
'całkowity spadek' => 'total_elevation_loss',
|
||||
'minimalna wysokość' => 'min_altitude',
|
||||
'maksymalna wysokość' => 'max_altitude',
|
||||
'sprzęt' => 'equipment_name',
|
||||
'typ wydarzenia' => 'event_type_name',
|
||||
'komentarz' => 'comment',
|
||||
'link do strava' => 'strava_url',
|
||||
// English keys for compatibility
|
||||
'category_name' => 'category_name',
|
||||
'date' => 'date',
|
||||
'title' => 'title',
|
||||
'distance' => 'distance',
|
||||
'calories' => 'calories',
|
||||
'duration' => 'duration',
|
||||
'avg_heart_rate' => 'avg_heart_rate',
|
||||
'max_heart_rate' => 'max_heart_rate',
|
||||
'avg_speed' => 'avg_speed',
|
||||
'max_speed' => 'max_speed',
|
||||
'avg_cadence' => 'avg_cadence',
|
||||
'max_cadence' => 'max_cadence',
|
||||
'total_elevation_gain' => 'total_elevation_gain',
|
||||
'total_elevation_loss' => 'total_elevation_loss',
|
||||
'min_altitude' => 'min_altitude',
|
||||
'max_altitude' => 'max_altitude',
|
||||
'equipment_name' => 'equipment_name',
|
||||
'event_type_name' => 'event_type_name',
|
||||
'comment' => 'comment',
|
||||
'strava_url' => 'strava_url',
|
||||
);
|
||||
|
||||
// --- START: Robust, case-insensitive lookup ---
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
$table_event_types = $wpdb->prefix . 'statpress_event_types';
|
||||
$table_equipment = $wpdb->prefix . 'statpress_equipment';
|
||||
|
||||
$create_lookup = function( $table_name ) use ( $wpdb ) {
|
||||
$items = $wpdb->get_results( "SELECT id, name FROM {$table_name}" );
|
||||
$lookup = array();
|
||||
if ( is_array( $items ) ) {
|
||||
foreach ( $items as $item ) {
|
||||
// Use a robust trim to handle various whitespace characters and make it case-insensitive
|
||||
$clean_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $item->name );
|
||||
$lookup[ mb_strtolower( $clean_name, 'UTF-8' ) ] = $item->id;
|
||||
}
|
||||
}
|
||||
return $lookup;
|
||||
};
|
||||
|
||||
$categories_lookup = $create_lookup( $table_categories );
|
||||
$event_types_lookup = $create_lookup( $table_event_types );
|
||||
$equipment_lookup = $create_lookup( $table_equipment );
|
||||
// --- END: Robust, case-insensitive lookup ---
|
||||
|
||||
// Process the CSV file
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$imported_count = 0;
|
||||
$skipped_details = array();
|
||||
$row_number = 1; // Header is row 1
|
||||
|
||||
// Normalize line endings and split into lines
|
||||
$lines = str_replace( array( "\r\n", "\r" ), "\n", $csv_content );
|
||||
$lines = explode( "\n", $lines );
|
||||
|
||||
if ( empty( $lines ) || empty( trim( $lines[0] ) ) ) {
|
||||
echo '<div class="notice notice-info"><p>Podane dane CSV są puste.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Delimiter and BOM detection ---
|
||||
$first_line = $lines[0];
|
||||
$delimiter = ( substr_count( $first_line, ';' ) > substr_count( $first_line, ',' ) ) ? ';' : ',';
|
||||
|
||||
// --- BOM removal from first header element ---
|
||||
$bom = "\xEF\xBB\xBF";
|
||||
if ( substr( $first_line, 0, 3 ) === $bom ) {
|
||||
$lines[0] = substr( $first_line, 3 );
|
||||
}
|
||||
|
||||
$header_raw = str_getcsv( array_shift( $lines ), $delimiter );
|
||||
$header_raw = array_map( 'trim', $header_raw );
|
||||
|
||||
// Translate header from Polish/English to internal keys
|
||||
$header = array();
|
||||
foreach ( $header_raw as $col ) {
|
||||
$header[] = $column_map[ strtolower( $col ) ] ?? 'ignored_' . uniqid();
|
||||
}
|
||||
|
||||
$required_internal_keys = array( 'date', 'title', 'category_name', 'distance' );
|
||||
$missing_keys = array_diff( $required_internal_keys, $header );
|
||||
if ( ! empty( $missing_keys ) ) {
|
||||
$key_to_polish_map = array(
|
||||
'date' => 'Data',
|
||||
'title' => 'Tytuł',
|
||||
'category_name' => 'Kategoria / Typ aktywności',
|
||||
'distance' => 'Dystans',
|
||||
);
|
||||
$missing_polish_names = array_map( fn( $key ) => $key_to_polish_map[ $key ] ?? $key, $missing_keys );
|
||||
echo '<div class="notice notice-error"><p>Brak wymaganych kolumn w danych CSV: ' . esc_html( implode( ', ', $missing_polish_names ) ) . '</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$parse_and_round_int = fn( $val ) => round( floatval( str_replace( ',', '.', $val ) ) );
|
||||
$null_if_empty = fn( $value ) => '' !== $value ? $value : null;
|
||||
|
||||
foreach ( $lines as $line ) {
|
||||
$row_number++;
|
||||
if ( empty( trim( $line ) ) ) {
|
||||
continue; // Skip empty lines
|
||||
}
|
||||
$data = str_getcsv( $line, $delimiter );
|
||||
|
||||
if ( count( $data ) !== count( $header ) ) {
|
||||
$skipped_details[] = array(
|
||||
'row' => $row_number,
|
||||
'reason' => 'Nieprawidłowa liczba kolumn (oczekiwano ' . count( $header ) . ', otrzymano ' . count( $data ) . ').',
|
||||
'data' => $line,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$row_data = array_combine( $header, $data );
|
||||
|
||||
// Detailed validation
|
||||
$validation_errors = array();
|
||||
if ( empty( $row_data['date'] ) ) {
|
||||
$validation_errors[] = 'brak daty';
|
||||
}
|
||||
if ( empty( $row_data['title'] ) ) {
|
||||
$validation_errors[] = 'brak tytułu';
|
||||
}
|
||||
if ( ! isset( $row_data['distance'] ) || '' === $row_data['distance'] ) {
|
||||
$validation_errors[] = 'brak dystansu';
|
||||
}
|
||||
|
||||
$category_name = $row_data['category_name'] ?? '';
|
||||
$clean_category_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $category_name );
|
||||
$category_id = $categories_lookup[ mb_strtolower( $clean_category_name, 'UTF-8' ) ] ?? null;
|
||||
|
||||
if ( empty( $clean_category_name ) ) {
|
||||
$validation_errors[] = 'brak nazwy kategorii';
|
||||
} elseif ( is_null( $category_id ) ) {
|
||||
$available_categories_from_db = $wpdb->get_col( "SELECT name FROM $table_categories ORDER BY name" );
|
||||
$validation_errors[] = 'nieznana kategoria: "' . esc_html( $category_name ) . '". Sprawdź, czy nazwa jest poprawna. Dostępne w bazie: "' . esc_html( implode( '", "', $available_categories_from_db ) ) . '".';
|
||||
}
|
||||
|
||||
if ( ! empty( $validation_errors ) ) {
|
||||
$skipped_details[] = array(
|
||||
'row' => $row_number,
|
||||
'reason' => ucfirst( implode( ', ', $validation_errors ) ) . '.',
|
||||
'data' => $line,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get IDs for optional fields using the same robust method
|
||||
$get_id = function( $name, $lookup_table ) {
|
||||
$clean_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $name );
|
||||
return $lookup_table[ mb_strtolower( $clean_name, 'UTF-8' ) ] ?? null;
|
||||
};
|
||||
|
||||
$equipment_id = $get_id( $row_data['equipment_name'] ?? '', $equipment_lookup );
|
||||
$event_type_id = $get_id( $row_data['event_type_name'] ?? '', $event_types_lookup );
|
||||
|
||||
$insert_data = array(
|
||||
'date' => sanitize_text_field( $row_data['date'] ),
|
||||
'title' => sanitize_text_field( $row_data['title'] ),
|
||||
'category_id' => $category_id,
|
||||
'distance' => floatval( str_replace( ',', '.', $row_data['distance'] ) ),
|
||||
'duration' => isset( $row_data['duration'] ) ? sanitize_text_field( $row_data['duration'] ) : '00:00:00',
|
||||
'calories' => isset( $row_data['calories'] ) ? intval( str_replace( ',', '.', $row_data['calories'] ) ) : 0,
|
||||
'comment' => isset( $row_data['comment'] ) ? sanitize_textarea_field( $row_data['comment'] ) : null,
|
||||
'strava_url' => isset( $row_data['strava_url'] ) ? $null_if_empty( esc_url_raw( $row_data['strava_url'] ) ) : null,
|
||||
'avg_heart_rate' => isset( $row_data['avg_heart_rate'] ) ? $null_if_empty( $parse_and_round_int( $row_data['avg_heart_rate'] ) ) : null,
|
||||
'max_heart_rate' => isset( $row_data['max_heart_rate'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_heart_rate'] ) ) : null,
|
||||
'avg_speed' => isset( $row_data['avg_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $row_data['avg_speed'] ) ) ) : null,
|
||||
'max_speed' => isset( $row_data['max_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $row_data['max_speed'] ) ) ) : null,
|
||||
'avg_cadence' => isset( $row_data['avg_cadence'] ) ? $null_if_empty( $parse_and_round_int( $row_data['avg_cadence'] ) ) : null,
|
||||
'max_cadence' => isset( $row_data['max_cadence'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_cadence'] ) ) : null,
|
||||
'total_elevation_gain' => isset( $row_data['total_elevation_gain'] ) ? $null_if_empty( $parse_and_round_int( $row_data['total_elevation_gain'] ) ) : null,
|
||||
'total_elevation_loss' => isset( $row_data['total_elevation_loss'] ) ? $null_if_empty( $parse_and_round_int( $row_data['total_elevation_loss'] ) ) : null,
|
||||
'min_altitude' => isset( $row_data['min_altitude'] ) ? $null_if_empty( $parse_and_round_int( $row_data['min_altitude'] ) ) : null,
|
||||
'max_altitude' => isset( $row_data['max_altitude'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_altitude'] ) ) : null,
|
||||
'equipment_id' => $equipment_id,
|
||||
'event_type_id' => $event_type_id,
|
||||
);
|
||||
|
||||
if ( $wpdb->insert( $table_activities, $insert_data ) ) {
|
||||
$imported_count++;
|
||||
} else {
|
||||
$skipped_details[] = array(
|
||||
'row' => $row_number,
|
||||
'reason' => 'Błąd zapisu do bazy danych. (' . esc_html( $wpdb->last_error ) . ')',
|
||||
'data' => $line,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $imported_count > 0 ) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>Pomyślnie zaimportowano ' . esc_html( $imported_count ) . ' aktywności.</p></div>';}
|
||||
if ( ! empty( $skipped_details ) ) {
|
||||
echo '<div class="notice notice-warning">';
|
||||
echo '<h4>Pominięto ' . count( $skipped_details ) . ' wierszy z powodu błędów</h4>';
|
||||
echo '<div style="max-height: 300px; overflow-y: auto; border: 1px solid #c3c4c7; padding: 10px; background: #fff; margin-top: 10px; font-size: 12px;">';
|
||||
echo '<table class="wp-list-table widefat striped" style="margin:0;"><thead><tr><th style="width:80px">Wiersz</th><th>Powód pominięcia</th><th>Dane wiersza</th></tr></thead><tbody>';
|
||||
foreach ( $skipped_details as $error ) {
|
||||
echo '<tr>';
|
||||
echo '<td>' . esc_html( $error['row'] ) . '</td>';
|
||||
echo '<td>' . esc_html( $error['reason'] ) . '</td>';
|
||||
echo '<td><small>' . esc_html( wp_trim_words( $error['data'], 25, '...' ) ) . '</small></td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
echo '</div></div>';
|
||||
}
|
||||
if ( 0 === $imported_count && empty( $skipped_details ) && $row_number > 1 ) {
|
||||
echo '<div class="notice notice-info"><p>Dane CSV nie zawierały żadnych poprawnych wierszy do importu.</p></div>';
|
||||
} elseif ( 1 === $row_number ) {
|
||||
echo '<div class="notice notice-info"><p>Dane CSV były puste lub zawierały tylko nagłówek.</p></div>';}
|
||||
}
|
||||
@@ -1,159 +1,159 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_infographic_page() {
|
||||
global $wpdb;
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
|
||||
$current_year = isset( $_GET['year'] ) ? intval( $_GET['year'] ) : current_time( 'Y' );
|
||||
|
||||
// Pobierz dostępne lata z bazy danych
|
||||
$available_years = $wpdb->get_col( "SELECT DISTINCT YEAR(date) FROM $table_activities ORDER BY YEAR(date) DESC" );
|
||||
if ( empty( $available_years ) ) {
|
||||
$available_years = array( current_time( 'Y' ) ); // Domyślny rok, jeśli brak danych
|
||||
}
|
||||
|
||||
// --- 1. Statystyki ogólne (wszystkie lata) ---
|
||||
$overall_stats = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
SUM(distance) as total_distance,
|
||||
SEC_TO_TIME(SUM(TIME_TO_SEC(duration))) as total_duration,
|
||||
SUM(total_elevation_gain) as total_elevation_gain,
|
||||
COUNT(id) as total_activities
|
||||
FROM $table_activities
|
||||
"
|
||||
);
|
||||
|
||||
// --- 2. Statystyki dla wybranego roku ---
|
||||
$yearly_stats = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT
|
||||
SUM(distance) as total_distance,
|
||||
SEC_TO_TIME(SUM(TIME_TO_SEC(duration))) as total_duration,
|
||||
SUM(total_elevation_gain) as total_elevation_gain,
|
||||
COUNT(id) as total_activities
|
||||
FROM $table_activities
|
||||
WHERE YEAR(date) = %d
|
||||
",
|
||||
$current_year
|
||||
)
|
||||
);
|
||||
|
||||
// --- 3. Dane dla wykresu kołowego (dystans per kategoria dla wybranego roku) ---
|
||||
$category_distance_data = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT c.name as category_name, c.color, SUM(a.distance) as total_distance
|
||||
FROM $table_activities a
|
||||
LEFT JOIN $table_categories c ON a.category_id = c.id
|
||||
WHERE YEAR(a.date) = %d
|
||||
GROUP BY c.name, c.color
|
||||
HAVING SUM(a.distance) > 0
|
||||
ORDER BY total_distance DESC
|
||||
",
|
||||
$current_year
|
||||
)
|
||||
);
|
||||
|
||||
$chart_labels = array();
|
||||
$chart_data = array();
|
||||
$chart_colors = array();
|
||||
foreach ( $category_distance_data as $data ) {
|
||||
$chart_labels[] = $data->category_name;
|
||||
$chart_data[] = round( (float) $data->total_distance, 2 );
|
||||
$chart_colors[] = $data->color;
|
||||
}
|
||||
|
||||
// Włączenie skryptów dla Chart.js
|
||||
wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), null, true );
|
||||
wp_register_script( 'statpress-infographic-chart-loader', false );
|
||||
wp_enqueue_script( 'statpress-infographic-chart-loader' );
|
||||
wp_add_inline_script(
|
||||
'statpress-infographic-chart-loader',
|
||||
'
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const ctx = document.getElementById("statpressCategoryPieChart");
|
||||
if (!ctx) return;
|
||||
new Chart(ctx, {
|
||||
type: "pie",
|
||||
data: {
|
||||
labels: ' . json_encode( $chart_labels ) . ',
|
||||
datasets: [{
|
||||
data: ' . json_encode( $chart_data ) . ',
|
||||
backgroundColor: ' . json_encode( $chart_colors ) . ',
|
||||
hoverOffset: 4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "right",
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: "Dystans wg kategorii w ' . esc_js( $current_year ) . '"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
'
|
||||
);
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Infografika Statystyk Sportowych</h1>
|
||||
|
||||
<div class="tablenav top">
|
||||
<div class="alignleft actions">
|
||||
<form method="get" style="display: flex; gap: 5px; align-items: center;">
|
||||
<input type="hidden" name="page" value="statpress-infographic">
|
||||
<label for="filter-by-year" class="screen-reader-text">Filtruj według roku</label>
|
||||
<select name="year" id="filter-by-year">
|
||||
<?php foreach ( $available_years as $year_option ) : ?>
|
||||
<option value="<?php echo esc_attr( $year_option ); ?>" <?php selected( $current_year, $year_option ); ?>><?php echo esc_html( $year_option ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php submit_button( 'Filtruj', 'secondary', 'filter_action', false ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Statystyki Ogólne (wszystkie lata)</h2></div>
|
||||
<div class="inside statpress-infographic-grid">
|
||||
<div class="statpress-infographic-card"><h3>Dystans</h3><p><?php echo number_format( $overall_stats->total_distance, 2, ',', ' ' ); ?> km</p></div>
|
||||
<div class="statpress-infographic-card"><h3>Czas</h3><p><?php echo esc_html( $overall_stats->total_duration ); ?></p></div>
|
||||
<div class="statpress-infographic-card"><h3>Wznios</h3><p><?php echo number_format( $overall_stats->total_elevation_gain, 0, ',', ' ' ); ?> m</p></div>
|
||||
<div class="statpress-infographic-card"><h3>Aktywności</h3><p><?php echo number_format( $overall_stats->total_activities, 0, ',', ' ' ); ?></p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Statystyki dla <?php echo esc_html( $current_year ); ?></h2></div>
|
||||
<div class="inside statpress-infographic-grid">
|
||||
<div class="statpress-infographic-card"><h3>Dystans</h3><p><?php echo number_format( $yearly_stats->total_distance, 2, ',', ' ' ); ?> km</p></div>
|
||||
<div class="statpress-infographic-card"><h3>Czas</h3><p><?php echo esc_html( $yearly_stats->total_duration ); ?></p></div>
|
||||
<div class="statpress-infographic-card"><h3>Wznios</h3><p><?php echo number_format( $yearly_stats->total_elevation_gain, 0, ',', ' ' ); ?> m</p></div>
|
||||
<div class="statpress-infographic-card"><h3>Aktywności</h3><p><?php echo number_format( $yearly_stats->total_activities, 0, ',', ' ' ); ?></p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Rozkład Dystansu wg Kategorii w <?php echo esc_html( $current_year ); ?></h2></div>
|
||||
<div class="inside">
|
||||
<div style="position: relative; height:40vh; width:100%; max-width: 600px; margin: 0 auto;">
|
||||
<canvas id="statpressCategoryPieChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_infographic_page() {
|
||||
global $wpdb;
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
$table_categories = $wpdb->prefix . 'statpress_categories';
|
||||
|
||||
$current_year = isset( $_GET['year'] ) ? intval( $_GET['year'] ) : current_time( 'Y' );
|
||||
|
||||
// Pobierz dostępne lata z bazy danych
|
||||
$available_years = $wpdb->get_col( "SELECT DISTINCT YEAR(date) FROM $table_activities ORDER BY YEAR(date) DESC" );
|
||||
if ( empty( $available_years ) ) {
|
||||
$available_years = array( current_time( 'Y' ) ); // Domyślny rok, jeśli brak danych
|
||||
}
|
||||
|
||||
// --- 1. Statystyki ogólne (wszystkie lata) ---
|
||||
$overall_stats = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
SUM(distance) as total_distance,
|
||||
SEC_TO_TIME(SUM(TIME_TO_SEC(duration))) as total_duration,
|
||||
SUM(total_elevation_gain) as total_elevation_gain,
|
||||
COUNT(id) as total_activities
|
||||
FROM $table_activities
|
||||
"
|
||||
);
|
||||
|
||||
// --- 2. Statystyki dla wybranego roku ---
|
||||
$yearly_stats = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT
|
||||
SUM(distance) as total_distance,
|
||||
SEC_TO_TIME(SUM(TIME_TO_SEC(duration))) as total_duration,
|
||||
SUM(total_elevation_gain) as total_elevation_gain,
|
||||
COUNT(id) as total_activities
|
||||
FROM $table_activities
|
||||
WHERE YEAR(date) = %d
|
||||
",
|
||||
$current_year
|
||||
)
|
||||
);
|
||||
|
||||
// --- 3. Dane dla wykresu kołowego (dystans per kategoria dla wybranego roku) ---
|
||||
$category_distance_data = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT c.name as category_name, c.color, SUM(a.distance) as total_distance
|
||||
FROM $table_activities a
|
||||
LEFT JOIN $table_categories c ON a.category_id = c.id
|
||||
WHERE YEAR(a.date) = %d
|
||||
GROUP BY c.name, c.color
|
||||
HAVING SUM(a.distance) > 0
|
||||
ORDER BY total_distance DESC
|
||||
",
|
||||
$current_year
|
||||
)
|
||||
);
|
||||
|
||||
$chart_labels = array();
|
||||
$chart_data = array();
|
||||
$chart_colors = array();
|
||||
foreach ( $category_distance_data as $data ) {
|
||||
$chart_labels[] = $data->category_name;
|
||||
$chart_data[] = round( (float) $data->total_distance, 2 );
|
||||
$chart_colors[] = $data->color;
|
||||
}
|
||||
|
||||
// Włączenie skryptów dla Chart.js
|
||||
wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), null, true );
|
||||
wp_register_script( 'statpress-infographic-chart-loader', false );
|
||||
wp_enqueue_script( 'statpress-infographic-chart-loader' );
|
||||
wp_add_inline_script(
|
||||
'statpress-infographic-chart-loader',
|
||||
'
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const ctx = document.getElementById("statpressCategoryPieChart");
|
||||
if (!ctx) return;
|
||||
new Chart(ctx, {
|
||||
type: "pie",
|
||||
data: {
|
||||
labels: ' . json_encode( $chart_labels ) . ',
|
||||
datasets: [{
|
||||
data: ' . json_encode( $chart_data ) . ',
|
||||
backgroundColor: ' . json_encode( $chart_colors ) . ',
|
||||
hoverOffset: 4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "right",
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: "Dystans wg kategorii w ' . esc_js( $current_year ) . '"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
'
|
||||
);
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Infografika Statystyk Sportowych</h1>
|
||||
|
||||
<div class="tablenav top">
|
||||
<div class="alignleft actions">
|
||||
<form method="get" style="display: flex; gap: 5px; align-items: center;">
|
||||
<input type="hidden" name="page" value="statpress-infographic">
|
||||
<label for="filter-by-year" class="screen-reader-text">Filtruj według roku</label>
|
||||
<select name="year" id="filter-by-year">
|
||||
<?php foreach ( $available_years as $year_option ) : ?>
|
||||
<option value="<?php echo esc_attr( $year_option ); ?>" <?php selected( $current_year, $year_option ); ?>><?php echo esc_html( $year_option ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php submit_button( 'Filtruj', 'secondary', 'filter_action', false ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Statystyki Ogólne (wszystkie lata)</h2></div>
|
||||
<div class="inside statpress-infographic-grid">
|
||||
<div class="statpress-infographic-card"><h3>Dystans</h3><p><?php echo number_format( $overall_stats->total_distance, 2, ',', ' ' ); ?> km</p></div>
|
||||
<div class="statpress-infographic-card"><h3>Czas</h3><p><?php echo esc_html( $overall_stats->total_duration ); ?></p></div>
|
||||
<div class="statpress-infographic-card"><h3>Wznios</h3><p><?php echo number_format( $overall_stats->total_elevation_gain, 0, ',', ' ' ); ?> m</p></div>
|
||||
<div class="statpress-infographic-card"><h3>Aktywności</h3><p><?php echo number_format( $overall_stats->total_activities, 0, ',', ' ' ); ?></p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Statystyki dla <?php echo esc_html( $current_year ); ?></h2></div>
|
||||
<div class="inside statpress-infographic-grid">
|
||||
<div class="statpress-infographic-card"><h3>Dystans</h3><p><?php echo number_format( $yearly_stats->total_distance, 2, ',', ' ' ); ?> km</p></div>
|
||||
<div class="statpress-infographic-card"><h3>Czas</h3><p><?php echo esc_html( $yearly_stats->total_duration ); ?></p></div>
|
||||
<div class="statpress-infographic-card"><h3>Wznios</h3><p><?php echo number_format( $yearly_stats->total_elevation_gain, 0, ',', ' ' ); ?> m</p></div>
|
||||
<div class="statpress-infographic-card"><h3>Aktywności</h3><p><?php echo number_format( $yearly_stats->total_activities, 0, ',', ' ' ); ?></p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Rozkład Dystansu wg Kategorii w <?php echo esc_html( $current_year ); ?></h2></div>
|
||||
<div class="inside">
|
||||
<div style="position: relative; height:40vh; width:100%; max-width: 600px; margin: 0 auto;">
|
||||
<canvas id="statpressCategoryPieChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -1,138 +1,161 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_settings_page() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Ustawienia Wtyczki Statystyk</h1>
|
||||
<form method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields( 'statpress_privacy_settings' );
|
||||
do_settings_sections( 'statpress-privacy-section' );
|
||||
settings_fields( 'statpress_api_settings' );
|
||||
do_settings_sections( 'statpress-api-section' );
|
||||
submit_button();
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
function statpress_register_settings() {
|
||||
register_setting(
|
||||
'statpress_privacy_settings',
|
||||
'statpress_privacy_options',
|
||||
'statpress_sanitize_privacy_options'
|
||||
);
|
||||
|
||||
add_settings_section(
|
||||
'statpress_privacy_zone_section',
|
||||
'Strefa Prywatności GPX',
|
||||
'statpress_privacy_section_callback',
|
||||
'statpress-privacy-section'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'statpress_privacy_latitude',
|
||||
'Szerokość geograficzna (Latitude)',
|
||||
'statpress_render_lat_field',
|
||||
'statpress-privacy-section',
|
||||
'statpress_privacy_zone_section'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'statpress_privacy_longitude',
|
||||
'Długość geograficzna (Longitude)',
|
||||
'statpress_render_lon_field',
|
||||
'statpress-privacy-section',
|
||||
'statpress_privacy_zone_section'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'statpress_privacy_radius',
|
||||
'Promień strefy (w metrach)',
|
||||
'statpress_render_radius_field',
|
||||
'statpress-privacy-section',
|
||||
'statpress_privacy_zone_section'
|
||||
);
|
||||
|
||||
// API Settings
|
||||
register_setting(
|
||||
'statpress_api_settings',
|
||||
'statpress_api_options',
|
||||
'statpress_sanitize_api_options'
|
||||
);
|
||||
|
||||
add_settings_section(
|
||||
'statpress_api_section',
|
||||
'Ustawienia API',
|
||||
'statpress_api_section_callback',
|
||||
'statpress-api-section'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'statpress_enable_api',
|
||||
'REST API',
|
||||
'statpress_render_enable_api_field',
|
||||
'statpress-privacy-section',
|
||||
'statpress_privacy_zone_section'
|
||||
);
|
||||
}
|
||||
|
||||
function statpress_privacy_section_callback() {
|
||||
echo '<p>Zdefiniuj strefę prywatności, aby ukryć początek i koniec swoich tras GPX. Punkty wewnątrz tego okręgu nie będą wyświetlane na mapie.</p>';
|
||||
echo '<p>Aby znaleźć swoje współrzędne, kliknij prawym przyciskiem myszy na mapie Google w wybranym miejscu - współrzędne pojawią się jako pierwsza pozycja w menu.</p>';
|
||||
}
|
||||
|
||||
function statpress_render_lat_field() {
|
||||
$options = get_option( 'statpress_privacy_options' );
|
||||
$latitude = isset( $options['latitude'] ) ? esc_attr( $options['latitude'] ) : '';
|
||||
echo "<input type='text' name='statpress_privacy_options[latitude]' value='{$latitude}' placeholder='np. 52.2297' class='regular-text' />";
|
||||
}
|
||||
|
||||
function statpress_render_lon_field() {
|
||||
$options = get_option( 'statpress_privacy_options' );
|
||||
$longitude = isset( $options['longitude'] ) ? esc_attr( $options['longitude'] ) : '';
|
||||
echo "<input type='text' name='statpress_privacy_options[longitude]' value='{$longitude}' placeholder='np. 21.0122' class='regular-text' />";
|
||||
}
|
||||
|
||||
function statpress_render_radius_field() {
|
||||
$options = get_option( 'statpress_privacy_options' );
|
||||
$radius = isset( $options['radius'] ) ? esc_attr( $options['radius'] ) : '500';
|
||||
echo "<input type='number' name='statpress_privacy_options[radius]' value='{$radius}' class='small-text' /> metrów";
|
||||
}
|
||||
|
||||
function statpress_sanitize_privacy_options( $input ) {
|
||||
$sanitized_input = array();
|
||||
if ( isset( $input['latitude'] ) ) {
|
||||
$sanitized_input['latitude'] = floatval( str_replace( ',', '.', $input['latitude'] ) );
|
||||
}
|
||||
if ( isset( $input['longitude'] ) ) {
|
||||
$sanitized_input['longitude'] = floatval( str_replace( ',', '.', $input['longitude'] ) );
|
||||
}
|
||||
if ( isset( $input['radius'] ) ) {
|
||||
$sanitized_input['radius'] = abs( intval( $input['radius'] ) );
|
||||
}
|
||||
return $sanitized_input;
|
||||
}
|
||||
|
||||
function statpress_api_section_callback() {
|
||||
echo '<p>Ustawienia związane z integracją wtyczki z zewnętrznymi aplikacjami, np. mobilnymi.</p>';
|
||||
}
|
||||
|
||||
function statpress_render_enable_api_field() {
|
||||
$options = get_option( 'statpress_api_options' );
|
||||
$checked = isset( $options['enable_api'] ) && $options['enable_api'] ? 'checked' : '';
|
||||
echo "<label><input type='checkbox' name='statpress_api_options[enable_api]' value='1' {$checked} /> Włącz REST API</label>";
|
||||
echo '<p class="description">Umożliwia zewnętrznym aplikacjom (np. na Androida) komunikację z wtyczką w celu dodawania i odczytywania aktywności. Jeśli nie korzystasz z takich integracji, możesz to wyłączyć dla większego bezpieczeństwa.</p>';
|
||||
}
|
||||
|
||||
function statpress_sanitize_api_options( $input ) {
|
||||
$sanitized_input = array();
|
||||
// If the checkbox is not checked, it won't be in the $input array.
|
||||
// So we check for its existence to determine if it's on or off.
|
||||
$sanitized_input['enable_api'] = isset( $input['enable_api'] ) ? 1 : 0;
|
||||
return $sanitized_input;
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_settings_page() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<?php
|
||||
if ( get_transient( 'statpress_migration_reset_notice' ) ) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>Status migracji został zresetowany. Wróć do <a href="' . esc_url( admin_url( 'admin.php?page=statpress-dashboard' ) ) . '">głównego panelu</a>, aby ponownie uruchomić migrację.</p></div>';
|
||||
delete_transient( 'statpress_migration_reset_notice' );
|
||||
}
|
||||
?>
|
||||
<h1>Ustawienia Wtyczki Statystyk</h1>
|
||||
<form method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields( 'statpress_settings_group' );
|
||||
do_settings_sections( 'statpress_settings_page' );
|
||||
submit_button();
|
||||
?>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
<h2>Narzędzia deweloperskie</h2>
|
||||
<div class="postbox">
|
||||
<div class="postbox-header"><h3 class="hndle">Resetowanie migracji</h3></div>
|
||||
<div class="inside">
|
||||
<p>Jeśli migracja danych nie powiodła się, a przycisk do jej ponownego uruchomienia zniknął, możesz użyć tego narzędzia, aby zresetować status migracji. Po kliknięciu, przycisk w głównym panelu wtyczki pojawi się ponownie.</p>
|
||||
<form method="post">
|
||||
<input type="hidden" name="statpress_action" value="reset_migration"><?php wp_nonce_field( 'statpress_reset_migration_nonce', '_wpnonce' ); ?><?php submit_button( 'Zresetuj status migracji', 'delete' ); ?></form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
function statpress_register_settings() {
|
||||
// Define a single group and page for all settings on this form.
|
||||
$option_group = 'statpress_settings_group';
|
||||
$page_slug = 'statpress_settings_page';
|
||||
|
||||
// Register Privacy settings under the main group.
|
||||
register_setting(
|
||||
$option_group,
|
||||
'statpress_privacy_options',
|
||||
'statpress_sanitize_privacy_options'
|
||||
);
|
||||
|
||||
// Register API settings under the same main group.
|
||||
register_setting(
|
||||
$option_group,
|
||||
'statpress_api_options',
|
||||
'statpress_sanitize_api_options'
|
||||
);
|
||||
|
||||
// Add the Privacy section to the main page.
|
||||
add_settings_section(
|
||||
'statpress_privacy_zone_section',
|
||||
'Strefa Prywatności GPX',
|
||||
'statpress_privacy_section_callback',
|
||||
$page_slug
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'statpress_privacy_latitude',
|
||||
'Szerokość geograficzna (Latitude)',
|
||||
'statpress_render_lat_field',
|
||||
$page_slug,
|
||||
'statpress_privacy_zone_section'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'statpress_privacy_longitude',
|
||||
'Długość geograficzna (Longitude)',
|
||||
'statpress_render_lon_field',
|
||||
$page_slug,
|
||||
'statpress_privacy_zone_section'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'statpress_privacy_radius',
|
||||
'Promień strefy (w metrach)',
|
||||
'statpress_render_radius_field',
|
||||
$page_slug,
|
||||
'statpress_privacy_zone_section'
|
||||
);
|
||||
|
||||
// Add the API section to the same main page.
|
||||
add_settings_section(
|
||||
'statpress_api_section',
|
||||
'Ustawienia API',
|
||||
'statpress_api_section_callback',
|
||||
$page_slug
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'statpress_enable_api',
|
||||
'REST API',
|
||||
'statpress_render_enable_api_field',
|
||||
$page_slug,
|
||||
'statpress_api_section'
|
||||
);
|
||||
}
|
||||
|
||||
function statpress_privacy_section_callback() {
|
||||
echo '<p>Zdefiniuj strefę prywatności, aby ukryć początek i koniec swoich tras GPX. Punkty wewnątrz tego okręgu nie będą wyświetlane na mapie.</p>';
|
||||
echo '<p>Aby znaleźć swoje współrzędne, kliknij prawym przyciskiem myszy na mapie Google w wybranym miejscu - współrzędne pojawią się jako pierwsza pozycja w menu.</p>';
|
||||
}
|
||||
|
||||
function statpress_render_lat_field() {
|
||||
$options = get_option( 'statpress_privacy_options' );
|
||||
$latitude = isset( $options['latitude'] ) ? esc_attr( $options['latitude'] ) : '';
|
||||
echo "<input type='text' name='statpress_privacy_options[latitude]' value='{$latitude}' placeholder='np. 52.2297' class='regular-text' />";
|
||||
}
|
||||
|
||||
function statpress_render_lon_field() {
|
||||
$options = get_option( 'statpress_privacy_options' );
|
||||
$longitude = isset( $options['longitude'] ) ? esc_attr( $options['longitude'] ) : '';
|
||||
echo "<input type='text' name='statpress_privacy_options[longitude]' value='{$longitude}' placeholder='np. 21.0122' class='regular-text' />";
|
||||
}
|
||||
|
||||
function statpress_render_radius_field() {
|
||||
$options = get_option( 'statpress_privacy_options' );
|
||||
$radius = isset( $options['radius'] ) ? esc_attr( $options['radius'] ) : '500';
|
||||
echo "<input type='number' name='statpress_privacy_options[radius]' value='{$radius}' class='small-text' /> metrów";
|
||||
}
|
||||
|
||||
function statpress_sanitize_privacy_options( $input ) {
|
||||
$sanitized_input = array();
|
||||
if ( isset( $input['latitude'] ) ) {
|
||||
$sanitized_input['latitude'] = floatval( str_replace( ',', '.', $input['latitude'] ) );
|
||||
}
|
||||
if ( isset( $input['longitude'] ) ) {
|
||||
$sanitized_input['longitude'] = floatval( str_replace( ',', '.', $input['longitude'] ) );
|
||||
}
|
||||
if ( isset( $input['radius'] ) ) {
|
||||
$sanitized_input['radius'] = abs( intval( $input['radius'] ) );
|
||||
}
|
||||
return $sanitized_input;
|
||||
}
|
||||
|
||||
function statpress_api_section_callback() {
|
||||
echo '<p>Ustawienia związane z integracją wtyczki z zewnętrznymi aplikacjami, np. mobilnymi.</p>';
|
||||
}
|
||||
|
||||
function statpress_render_enable_api_field() {
|
||||
$options = get_option( 'statpress_api_options' );
|
||||
$checked = isset( $options['enable_api'] ) && $options['enable_api'] ? 'checked' : '';
|
||||
echo "<label><input type='checkbox' name='statpress_api_options[enable_api]' value='1' {$checked} /> Włącz REST API</label>";
|
||||
echo '<p class="description">Umożliwia zewnętrznym aplikacjom (np. na Androida) komunikację z wtyczką w celu dodawania i odczytywania aktywności. Jeśli nie korzystasz z takich integracji, możesz to wyłączyć dla większego bezpieczeństwa.</p>';
|
||||
}
|
||||
|
||||
function statpress_sanitize_api_options( $input ) {
|
||||
$sanitized_input = array();
|
||||
// If the checkbox is not checked, it won't be in the $input array.
|
||||
// So we check for its existence to determine if it's on or off.
|
||||
$sanitized_input['enable_api'] = isset( $input['enable_api'] ) ? 1 : 0;
|
||||
return $sanitized_input;
|
||||
}
|
||||
@@ -1,292 +1,292 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_yearly_summary_page() {
|
||||
global $wpdb;
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
|
||||
$current_year = isset( $_GET['year'] ) ? intval( $_GET['year'] ) : current_time( 'Y' );
|
||||
|
||||
// Pobierz dostępne lata z bazy danych
|
||||
$available_years = $wpdb->get_col( "SELECT DISTINCT YEAR(date) FROM $table_activities ORDER BY YEAR(date) DESC" );
|
||||
if ( empty( $available_years ) ) {
|
||||
$available_years = array( current_time( 'Y' ) ); // Domyślny rok, jeśli brak danych
|
||||
}
|
||||
|
||||
// --- GOALS SECTION ---
|
||||
$table_goals = $wpdb->prefix . 'statpress_goals';
|
||||
$goals_for_year = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM {$table_goals} WHERE year = %d ORDER BY name ASC",
|
||||
$current_year
|
||||
)
|
||||
);
|
||||
|
||||
// Zapytanie SQL do grupowania danych miesięcznie
|
||||
$sql = $wpdb->prepare(
|
||||
"
|
||||
SELECT
|
||||
MONTH(date) as month_num,
|
||||
SUM(distance) as total_distance,
|
||||
SUM(calories) as total_calories,
|
||||
SUM(TIME_TO_SEC(duration)) as total_seconds,
|
||||
COUNT(id) as activity_count
|
||||
FROM $table_activities
|
||||
WHERE YEAR(date) = %d
|
||||
GROUP BY month_num
|
||||
ORDER BY month_num ASC
|
||||
",
|
||||
$current_year
|
||||
);
|
||||
|
||||
$monthly_summary = $wpdb->get_results( $sql, OBJECT_K ); // OBJECT_K zwróci tablicę z kluczami będącymi numerami miesięcy
|
||||
|
||||
// Przygotowanie danych dla wszystkich 12 miesięcy
|
||||
$full_year_summary = array();
|
||||
$total_year_distance = 0;
|
||||
$total_year_calories = 0;
|
||||
$total_year_seconds = 0;
|
||||
|
||||
// Określ, ile miesięcy pokazać, aby uniknąć zer dla przyszłych miesięcy
|
||||
$this_year = (int) current_time( 'Y' );
|
||||
$this_month = (int) current_time( 'n' );
|
||||
$loop_until_month = 12; // Domyślnie dla lat ubiegłych
|
||||
|
||||
if ( $current_year === $this_year ) {
|
||||
// Dla bieżącego roku, pokaż miesiące do bieżącego miesiąca
|
||||
$loop_until_month = $this_month;
|
||||
} elseif ( $current_year > $this_year ) {
|
||||
// Dla przyszłych lat, pokaż miesiące tylko do ostatniego, w którym są dane
|
||||
$last_month_with_data = $wpdb->get_var( $wpdb->prepare( "SELECT MAX(MONTH(date)) FROM $table_activities WHERE YEAR(date) = %d", $current_year ) );
|
||||
$loop_until_month = $last_month_with_data ? (int) $last_month_with_data : 0;
|
||||
}
|
||||
|
||||
for ( $i = 1; $i <= $loop_until_month; $i++ ) {
|
||||
$month_name = date_i18n( 'F', mktime( 0, 0, 0, $i, 10 ) ); // Nazwa miesiąca
|
||||
$data = isset( $monthly_summary[ $i ] ) ? $monthly_summary[ $i ] : null;
|
||||
|
||||
$full_year_summary[ $i ] = (object) array(
|
||||
'month_name' => $month_name,
|
||||
'total_distance' => $data ? $data->total_distance : 0,
|
||||
'total_calories' => $data ? (int) $data->total_calories : 0,
|
||||
'total_seconds' => $data ? (int) $data->total_seconds : 0,
|
||||
'activity_count' => $data ? (int) $data->activity_count : 0,
|
||||
);
|
||||
|
||||
$total_year_distance += $full_year_summary[ $i ]->total_distance;
|
||||
$total_year_seconds += $full_year_summary[ $i ]->total_seconds;
|
||||
$total_year_calories += $full_year_summary[ $i ]->total_calories;
|
||||
}
|
||||
|
||||
$total_year_hours = floor( $total_year_seconds / 3600 );
|
||||
$total_year_minutes = floor( ( $total_year_seconds % 3600 ) / 60 );
|
||||
$total_year_duration_formatted = sprintf( '%d godz. %d min.', $total_year_hours, $total_year_minutes );
|
||||
|
||||
// Przygotowanie danych dla wykresu
|
||||
$chart_labels_js = array();
|
||||
$chart_datasets = array(
|
||||
'distance' => array(),
|
||||
'duration' => array(),
|
||||
'calories' => array(),
|
||||
'activities' => array(),
|
||||
);
|
||||
|
||||
foreach ( $full_year_summary as $month_data ) {
|
||||
$chart_labels_js[] = $month_data->month_name;
|
||||
$chart_datasets['distance'][] = round( (float) $month_data->total_distance, 2 );
|
||||
$chart_datasets['duration'][] = round( $month_data->total_seconds / 3600, 2 ); // w godzinach
|
||||
$chart_datasets['calories'][] = $month_data->total_calories;
|
||||
$chart_datasets['activities'][] = $month_data->activity_count;
|
||||
}
|
||||
|
||||
// Włączenie skryptów dla Chart.js
|
||||
wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), null, true );
|
||||
wp_register_script( 'statpress-chart-loader', false );
|
||||
wp_enqueue_script( 'statpress-chart-loader' );
|
||||
|
||||
$chart_configs = array(
|
||||
'distance' => array(
|
||||
'label' => 'Dystans (km)',
|
||||
'data' => $chart_datasets['distance'],
|
||||
'backgroundColor' => 'rgba(52, 152, 219, 0.5)',
|
||||
'borderColor' => 'rgba(52, 152, 219, 1)',
|
||||
'yAxisLabel' => 'Kilometry',
|
||||
),
|
||||
'duration' => array(
|
||||
'label' => 'Czas trwania (godz.)',
|
||||
'data' => $chart_datasets['duration'],
|
||||
'backgroundColor' => 'rgba(26, 188, 156, 0.5)',
|
||||
'borderColor' => 'rgba(26, 188, 156, 1)',
|
||||
'yAxisLabel' => 'Godziny',
|
||||
),
|
||||
'calories' => array(
|
||||
'label' => 'Kalorie (kcal)',
|
||||
'data' => $chart_datasets['calories'],
|
||||
'backgroundColor' => 'rgba(231, 76, 60, 0.5)',
|
||||
'borderColor' => 'rgba(231, 76, 60, 1)',
|
||||
'yAxisLabel' => 'kcal',
|
||||
),
|
||||
'activities' => array(
|
||||
'label' => 'Liczba aktywności',
|
||||
'data' => $chart_datasets['activities'],
|
||||
'backgroundColor' => 'rgba(241, 196, 15, 0.5)',
|
||||
'borderColor' => 'rgba(241, 196, 15, 1)',
|
||||
'yAxisLabel' => 'Ilość',
|
||||
),
|
||||
);
|
||||
|
||||
wp_add_inline_script(
|
||||
'statpress-chart-loader',
|
||||
'
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const chartLabels = ' . json_encode( $chart_labels_js ) . ';
|
||||
const chartConfigs = ' . json_encode( $chart_configs ) . ';
|
||||
let activeChart = null;
|
||||
|
||||
const tabs = document.querySelectorAll(".nav-tab");
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
tabs.forEach(t => t.classList.remove("nav-tab-active"));
|
||||
this.classList.add("nav-tab-active");
|
||||
|
||||
const chartType = this.getAttribute("href").substring(1);
|
||||
renderChart(chartType);
|
||||
});
|
||||
});
|
||||
|
||||
function renderChart(type) {
|
||||
if (activeChart) {
|
||||
activeChart.destroy();
|
||||
}
|
||||
const config = chartConfigs[type];
|
||||
const ctx = document.getElementById("statpressYearlyChart").getContext("2d");
|
||||
activeChart = new Chart(ctx, {
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: chartLabels,
|
||||
datasets: [{
|
||||
label: config.label,
|
||||
data: config.data,
|
||||
backgroundColor: config.backgroundColor,
|
||||
borderColor: config.borderColor,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
scales: { y: { beginAtZero: true, title: { display: true, text: config.yAxisLabel } } }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Render initial chart
|
||||
if (tabs.length > 0) {
|
||||
renderChart(tabs[0].getAttribute("href").substring(1));
|
||||
}
|
||||
});
|
||||
'
|
||||
);
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Podsumowanie Roczne</h1>
|
||||
|
||||
<div class="tablenav top">
|
||||
<div class="alignleft actions">
|
||||
<label for="filter-by-year" class="screen-reader-text">Filtruj według roku</label>
|
||||
<form method="get" style="display: flex; gap: 5px; align-items: center;">
|
||||
<input type="hidden" name="page" value="statpress-yearly-summary">
|
||||
<select name="year" id="filter-by-year">
|
||||
<?php foreach ( $available_years as $year_option ) : ?>
|
||||
<option value="<?php echo esc_attr( $year_option ); ?>" <?php selected( $current_year, $year_option ); ?>><?php echo esc_html( $year_option ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php submit_button( 'Filtruj', 'secondary', 'filter_action', false ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ( ! empty( $goals_for_year ) ) : ?>
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Cele na <?php echo esc_html( $current_year ); ?></h2></div>
|
||||
<div class="inside">
|
||||
<ul class="statpress-goals-list">
|
||||
<?php
|
||||
foreach ( $goals_for_year as $goal ) :
|
||||
$progress = statpress_get_goal_progress( $goal );
|
||||
$percentage = min( 100, $progress['percentage'] );
|
||||
|
||||
$target_formatted = '';
|
||||
$current_formatted = '';
|
||||
if ( 'duration_sec' === $goal->goal_type ) {
|
||||
$target_formatted = round( $goal->target_value / 3600 ) . ' godz.';
|
||||
$current_formatted = sprintf( '%d godz. %d min.', floor( $progress['current_value'] / 3600 ), floor( ( $progress['current_value'] % 3600 ) / 60 ) );
|
||||
} elseif ( 'distance' === $goal->goal_type ) {
|
||||
$target_formatted = number_format( $goal->target_value, 0, ',', ' ' ) . ' km';
|
||||
$current_formatted = number_format( $progress['current_value'], 2, ',', ' ' ) . ' km';
|
||||
} else { // count
|
||||
$target_formatted = (int) $goal->target_value;
|
||||
$current_formatted = (int) $progress['current_value'];
|
||||
}
|
||||
?>
|
||||
<li class="statpress-goal-item">
|
||||
<div class="statpress-goal-info">
|
||||
<strong><?php echo esc_html( $goal->name ); ?></strong>
|
||||
<small><?php echo $current_formatted; ?> / <?php echo $target_formatted; ?> (<?php echo round( $percentage, 1 ); ?>%)</small>
|
||||
</div>
|
||||
<div class="statpress-progress-bar-container">
|
||||
<div class="statpress-progress-bar" style="width: <?php echo esc_attr( $percentage ); ?>%;"></div>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.statpress-goals-list { list-style: none; margin: 0; padding: 0; } .statpress-goal-item { margin-bottom: 15px; } .statpress-goal-info { display: flex; justify-content: space-between; margin-bottom: 5px; }
|
||||
.statpress-progress-bar-container { background: #eee; border-radius: 4px; height: 18px; width: 100%; overflow: hidden; } .statpress-progress-bar { background: #3498db; height: 100%; border-radius: 4px; transition: width 0.5s ease-in-out; text-align: center; color: white; font-size: 11px; line-height: 18px; }
|
||||
</style>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Wykresy dla <?php echo esc_html( $current_year ); ?></h2></div>
|
||||
<div class="inside">
|
||||
<nav class="nav-tab-wrapper" style="margin-bottom: 20px;">
|
||||
<a href="#distance" class="nav-tab nav-tab-active">Dystans</a>
|
||||
<a href="#duration" class="nav-tab">Czas</a>
|
||||
<a href="#calories" class="nav-tab">Kalorie</a>
|
||||
<a href="#activities" class="nav-tab">Aktywności</a>
|
||||
</nav>
|
||||
<div style="position: relative; height:40vh; width:100%;">
|
||||
<canvas id="statpressYearlyChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr><th>Miesiąc</th><th>Dystans (km)</th><th>Kalorie (kcal)</th><th>Czas</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ( $full_year_summary as $month_data ) : ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html( $month_data->month_name ); ?></td>
|
||||
<td><?php echo number_format( $month_data->total_distance, 2, ',', ' ' ); ?></td>
|
||||
<td><?php echo number_format( $month_data->total_calories, 0, ',', ' ' ); ?></td>
|
||||
<td><?php echo esc_html( gmdate( 'H:i:s', $month_data->total_seconds ) ); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<tr class="alternate">
|
||||
<th>SUMA ROCZNA</th>
|
||||
<th><?php echo number_format( $total_year_distance, 2, ',', ' ' ); ?></th>
|
||||
<th><?php echo number_format( $total_year_calories, 0, ',', ' ' ); ?></th>
|
||||
<th><?php echo esc_html( $total_year_duration_formatted ); ?></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function statpress_yearly_summary_page() {
|
||||
global $wpdb;
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
|
||||
$current_year = isset( $_GET['year'] ) ? intval( $_GET['year'] ) : current_time( 'Y' );
|
||||
|
||||
// Pobierz dostępne lata z bazy danych
|
||||
$available_years = $wpdb->get_col( "SELECT DISTINCT YEAR(date) FROM $table_activities ORDER BY YEAR(date) DESC" );
|
||||
if ( empty( $available_years ) ) {
|
||||
$available_years = array( current_time( 'Y' ) ); // Domyślny rok, jeśli brak danych
|
||||
}
|
||||
|
||||
// --- GOALS SECTION ---
|
||||
$table_goals = $wpdb->prefix . 'statpress_goals';
|
||||
$goals_for_year = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM {$table_goals} WHERE year = %d ORDER BY name ASC",
|
||||
$current_year
|
||||
)
|
||||
);
|
||||
|
||||
// Zapytanie SQL do grupowania danych miesięcznie
|
||||
$sql = $wpdb->prepare(
|
||||
"
|
||||
SELECT
|
||||
MONTH(date) as month_num,
|
||||
SUM(distance) as total_distance,
|
||||
SUM(calories) as total_calories,
|
||||
SUM(TIME_TO_SEC(duration)) as total_seconds,
|
||||
COUNT(id) as activity_count
|
||||
FROM $table_activities
|
||||
WHERE YEAR(date) = %d
|
||||
GROUP BY month_num
|
||||
ORDER BY month_num ASC
|
||||
",
|
||||
$current_year
|
||||
);
|
||||
|
||||
$monthly_summary = $wpdb->get_results( $sql, OBJECT_K ); // OBJECT_K zwróci tablicę z kluczami będącymi numerami miesięcy
|
||||
|
||||
// Przygotowanie danych dla wszystkich 12 miesięcy
|
||||
$full_year_summary = array();
|
||||
$total_year_distance = 0;
|
||||
$total_year_calories = 0;
|
||||
$total_year_seconds = 0;
|
||||
|
||||
// Określ, ile miesięcy pokazać, aby uniknąć zer dla przyszłych miesięcy
|
||||
$this_year = (int) current_time( 'Y' );
|
||||
$this_month = (int) current_time( 'n' );
|
||||
$loop_until_month = 12; // Domyślnie dla lat ubiegłych
|
||||
|
||||
if ( $current_year === $this_year ) {
|
||||
// Dla bieżącego roku, pokaż miesiące do bieżącego miesiąca
|
||||
$loop_until_month = $this_month;
|
||||
} elseif ( $current_year > $this_year ) {
|
||||
// Dla przyszłych lat, pokaż miesiące tylko do ostatniego, w którym są dane
|
||||
$last_month_with_data = $wpdb->get_var( $wpdb->prepare( "SELECT MAX(MONTH(date)) FROM $table_activities WHERE YEAR(date) = %d", $current_year ) );
|
||||
$loop_until_month = $last_month_with_data ? (int) $last_month_with_data : 0;
|
||||
}
|
||||
|
||||
for ( $i = 1; $i <= $loop_until_month; $i++ ) {
|
||||
$month_name = date_i18n( 'F', mktime( 0, 0, 0, $i, 10 ) ); // Nazwa miesiąca
|
||||
$data = isset( $monthly_summary[ $i ] ) ? $monthly_summary[ $i ] : null;
|
||||
|
||||
$full_year_summary[ $i ] = (object) array(
|
||||
'month_name' => $month_name,
|
||||
'total_distance' => $data ? $data->total_distance : 0,
|
||||
'total_calories' => $data ? (int) $data->total_calories : 0,
|
||||
'total_seconds' => $data ? (int) $data->total_seconds : 0,
|
||||
'activity_count' => $data ? (int) $data->activity_count : 0,
|
||||
);
|
||||
|
||||
$total_year_distance += $full_year_summary[ $i ]->total_distance;
|
||||
$total_year_seconds += $full_year_summary[ $i ]->total_seconds;
|
||||
$total_year_calories += $full_year_summary[ $i ]->total_calories;
|
||||
}
|
||||
|
||||
$total_year_hours = floor( $total_year_seconds / 3600 );
|
||||
$total_year_minutes = floor( ( $total_year_seconds % 3600 ) / 60 );
|
||||
$total_year_duration_formatted = sprintf( '%d godz. %d min.', $total_year_hours, $total_year_minutes );
|
||||
|
||||
// Przygotowanie danych dla wykresu
|
||||
$chart_labels_js = array();
|
||||
$chart_datasets = array(
|
||||
'distance' => array(),
|
||||
'duration' => array(),
|
||||
'calories' => array(),
|
||||
'activities' => array(),
|
||||
);
|
||||
|
||||
foreach ( $full_year_summary as $month_data ) {
|
||||
$chart_labels_js[] = $month_data->month_name;
|
||||
$chart_datasets['distance'][] = round( (float) $month_data->total_distance, 2 );
|
||||
$chart_datasets['duration'][] = round( $month_data->total_seconds / 3600, 2 ); // w godzinach
|
||||
$chart_datasets['calories'][] = $month_data->total_calories;
|
||||
$chart_datasets['activities'][] = $month_data->activity_count;
|
||||
}
|
||||
|
||||
// Włączenie skryptów dla Chart.js
|
||||
wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), null, true );
|
||||
wp_register_script( 'statpress-chart-loader', false );
|
||||
wp_enqueue_script( 'statpress-chart-loader' );
|
||||
|
||||
$chart_configs = array(
|
||||
'distance' => array(
|
||||
'label' => 'Dystans (km)',
|
||||
'data' => $chart_datasets['distance'],
|
||||
'backgroundColor' => 'rgba(52, 152, 219, 0.5)',
|
||||
'borderColor' => 'rgba(52, 152, 219, 1)',
|
||||
'yAxisLabel' => 'Kilometry',
|
||||
),
|
||||
'duration' => array(
|
||||
'label' => 'Czas trwania (godz.)',
|
||||
'data' => $chart_datasets['duration'],
|
||||
'backgroundColor' => 'rgba(26, 188, 156, 0.5)',
|
||||
'borderColor' => 'rgba(26, 188, 156, 1)',
|
||||
'yAxisLabel' => 'Godziny',
|
||||
),
|
||||
'calories' => array(
|
||||
'label' => 'Kalorie (kcal)',
|
||||
'data' => $chart_datasets['calories'],
|
||||
'backgroundColor' => 'rgba(231, 76, 60, 0.5)',
|
||||
'borderColor' => 'rgba(231, 76, 60, 1)',
|
||||
'yAxisLabel' => 'kcal',
|
||||
),
|
||||
'activities' => array(
|
||||
'label' => 'Liczba aktywności',
|
||||
'data' => $chart_datasets['activities'],
|
||||
'backgroundColor' => 'rgba(241, 196, 15, 0.5)',
|
||||
'borderColor' => 'rgba(241, 196, 15, 1)',
|
||||
'yAxisLabel' => 'Ilość',
|
||||
),
|
||||
);
|
||||
|
||||
wp_add_inline_script(
|
||||
'statpress-chart-loader',
|
||||
'
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const chartLabels = ' . json_encode( $chart_labels_js ) . ';
|
||||
const chartConfigs = ' . json_encode( $chart_configs ) . ';
|
||||
let activeChart = null;
|
||||
|
||||
const tabs = document.querySelectorAll(".nav-tab");
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
tabs.forEach(t => t.classList.remove("nav-tab-active"));
|
||||
this.classList.add("nav-tab-active");
|
||||
|
||||
const chartType = this.getAttribute("href").substring(1);
|
||||
renderChart(chartType);
|
||||
});
|
||||
});
|
||||
|
||||
function renderChart(type) {
|
||||
if (activeChart) {
|
||||
activeChart.destroy();
|
||||
}
|
||||
const config = chartConfigs[type];
|
||||
const ctx = document.getElementById("statpressYearlyChart").getContext("2d");
|
||||
activeChart = new Chart(ctx, {
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: chartLabels,
|
||||
datasets: [{
|
||||
label: config.label,
|
||||
data: config.data,
|
||||
backgroundColor: config.backgroundColor,
|
||||
borderColor: config.borderColor,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
scales: { y: { beginAtZero: true, title: { display: true, text: config.yAxisLabel } } }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Render initial chart
|
||||
if (tabs.length > 0) {
|
||||
renderChart(tabs[0].getAttribute("href").substring(1));
|
||||
}
|
||||
});
|
||||
'
|
||||
);
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Podsumowanie Roczne</h1>
|
||||
|
||||
<div class="tablenav top">
|
||||
<div class="alignleft actions">
|
||||
<label for="filter-by-year" class="screen-reader-text">Filtruj według roku</label>
|
||||
<form method="get" style="display: flex; gap: 5px; align-items: center;">
|
||||
<input type="hidden" name="page" value="statpress-yearly-summary">
|
||||
<select name="year" id="filter-by-year">
|
||||
<?php foreach ( $available_years as $year_option ) : ?>
|
||||
<option value="<?php echo esc_attr( $year_option ); ?>" <?php selected( $current_year, $year_option ); ?>><?php echo esc_html( $year_option ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php submit_button( 'Filtruj', 'secondary', 'filter_action', false ); ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ( ! empty( $goals_for_year ) ) : ?>
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Cele na <?php echo esc_html( $current_year ); ?></h2></div>
|
||||
<div class="inside">
|
||||
<ul class="statpress-goals-list">
|
||||
<?php
|
||||
foreach ( $goals_for_year as $goal ) :
|
||||
$progress = statpress_get_goal_progress( $goal );
|
||||
$percentage = min( 100, $progress['percentage'] );
|
||||
|
||||
$target_formatted = '';
|
||||
$current_formatted = '';
|
||||
if ( 'duration_sec' === $goal->goal_type ) {
|
||||
$target_formatted = round( $goal->target_value / 3600 ) . ' godz.';
|
||||
$current_formatted = sprintf( '%d godz. %d min.', floor( $progress['current_value'] / 3600 ), floor( ( $progress['current_value'] % 3600 ) / 60 ) );
|
||||
} elseif ( 'distance' === $goal->goal_type ) {
|
||||
$target_formatted = number_format( $goal->target_value, 0, ',', ' ' ) . ' km';
|
||||
$current_formatted = number_format( $progress['current_value'], 2, ',', ' ' ) . ' km';
|
||||
} else { // count
|
||||
$target_formatted = (int) $goal->target_value;
|
||||
$current_formatted = (int) $progress['current_value'];
|
||||
}
|
||||
?>
|
||||
<li class="statpress-goal-item">
|
||||
<div class="statpress-goal-info">
|
||||
<strong><?php echo esc_html( $goal->name ); ?></strong>
|
||||
<small><?php echo $current_formatted; ?> / <?php echo $target_formatted; ?> (<?php echo round( $percentage, 1 ); ?>%)</small>
|
||||
</div>
|
||||
<div class="statpress-progress-bar-container">
|
||||
<div class="statpress-progress-bar" style="width: <?php echo esc_attr( $percentage ); ?>%;"></div>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.statpress-goals-list { list-style: none; margin: 0; padding: 0; } .statpress-goal-item { margin-bottom: 15px; } .statpress-goal-info { display: flex; justify-content: space-between; margin-bottom: 5px; }
|
||||
.statpress-progress-bar-container { background: #eee; border-radius: 4px; height: 18px; width: 100%; overflow: hidden; } .statpress-progress-bar { background: #3498db; height: 100%; border-radius: 4px; transition: width 0.5s ease-in-out; text-align: center; color: white; font-size: 11px; line-height: 18px; }
|
||||
</style>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="postbox" style="margin-bottom: 20px;">
|
||||
<div class="postbox-header"><h2 class="hndle">Wykresy dla <?php echo esc_html( $current_year ); ?></h2></div>
|
||||
<div class="inside">
|
||||
<nav class="nav-tab-wrapper" style="margin-bottom: 20px;">
|
||||
<a href="#distance" class="nav-tab nav-tab-active">Dystans</a>
|
||||
<a href="#duration" class="nav-tab">Czas</a>
|
||||
<a href="#calories" class="nav-tab">Kalorie</a>
|
||||
<a href="#activities" class="nav-tab">Aktywności</a>
|
||||
</nav>
|
||||
<div style="position: relative; height:40vh; width:100%;">
|
||||
<canvas id="statpressYearlyChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr><th>Miesiąc</th><th>Dystans (km)</th><th>Kalorie (kcal)</th><th>Czas</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ( $full_year_summary as $month_data ) : ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html( $month_data->month_name ); ?></td>
|
||||
<td><?php echo number_format( $month_data->total_distance, 2, ',', ' ' ); ?></td>
|
||||
<td><?php echo number_format( $month_data->total_calories, 0, ',', ' ' ); ?></td>
|
||||
<td><?php echo esc_html( gmdate( 'H:i:s', $month_data->total_seconds ) ); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<tr class="alternate">
|
||||
<th>SUMA ROCZNA</th>
|
||||
<th><?php echo number_format( $total_year_distance, 2, ',', ' ' ); ?></th>
|
||||
<th><?php echo number_format( $total_year_calories, 0, ',', ' ' ); ?></th>
|
||||
<th><?php echo esc_html( $total_year_duration_formatted ); ?></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
+242
-181
@@ -1,182 +1,243 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API routes for the plugin.
|
||||
*
|
||||
* @package WordPress Activity Stats
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST API routes.
|
||||
*/
|
||||
function statpress_register_rest_routes() {
|
||||
$namespace = 'statpress/v1';
|
||||
|
||||
// Route for getting a collection of activities
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/activities',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => 'statpress_get_activities_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
'args' => array(
|
||||
'page' => array(
|
||||
'validate_callback' => 'is_numeric',
|
||||
),
|
||||
'per_page' => array(
|
||||
'validate_callback' => 'is_numeric',
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => 'statpress_create_activity_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Route for a single activity
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/activities/(?P<id>[\d]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => 'statpress_get_activity_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => 'statpress_update_activity_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => 'statpress_delete_activity_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission check for API endpoints.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function statpress_api_permissions_check() {
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of activities.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_get_activities_api( WP_REST_Request $request ) {
|
||||
global $wpdb;
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
|
||||
$per_page = $request->get_param( 'per_page' ) ? (int) $request->get_param( 'per_page' ) : 20;
|
||||
$page = $request->get_param( 'page' ) ? (int) $request->get_param( 'page' ) : 1;
|
||||
$offset = ( $page - 1 ) * $per_page;
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT a.*, c.name as category_name, et.name as event_type_name, eq.name as equipment_name
|
||||
FROM $table_activities a
|
||||
LEFT JOIN {$wpdb->prefix}statpress_categories c ON a.category_id = c.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_event_types et ON a.event_type_id = et.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_equipment eq ON a.equipment_id = eq.id
|
||||
ORDER BY a.date DESC, a.id DESC
|
||||
LIMIT %d OFFSET %d",
|
||||
$per_page,
|
||||
$offset
|
||||
);
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
return new WP_REST_Response( $results, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single activity.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_get_activity_api( WP_REST_Request $request ) {
|
||||
global $wpdb;
|
||||
$id = (int) $request['id'];
|
||||
|
||||
$sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}statpress_activities WHERE id = %d", $id );
|
||||
$activity = $wpdb->get_row( $sql );
|
||||
|
||||
if ( ! $activity ) {
|
||||
return new WP_Error( 'not_found', 'Activity not found', array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $activity, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new activity.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_create_activity_api( WP_REST_Request $request ) {
|
||||
$params = $request->get_json_params();
|
||||
$activity_id = statpress_save_activity_data( $params );
|
||||
|
||||
if ( ! $activity_id ) {
|
||||
return new WP_Error( 'cant-create', 'Error creating activity', array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
$response = statpress_get_activity_api( new WP_REST_Request( 'GET', "/statpress/v1/activities/{$activity_id}" ) );
|
||||
$response->set_status( 201 ); // 201 Created
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing activity.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_update_activity_api( WP_REST_Request $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$params = $request->get_json_params();
|
||||
$activity_id = statpress_save_activity_data( $params, $id );
|
||||
|
||||
if ( ! $activity_id ) {
|
||||
return new WP_Error( 'cant-update', 'Error updating activity', array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
return statpress_get_activity_api( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an activity.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_delete_activity_api( WP_REST_Request $request ) {
|
||||
global $wpdb;
|
||||
$id = (int) $request['id'];
|
||||
|
||||
$result = $wpdb->delete( $wpdb->prefix . 'statpress_activities', array( 'id' => $id ), array( '%d' ) );
|
||||
|
||||
if ( ! $result ) {
|
||||
return new WP_Error( 'cant-delete', 'Error deleting activity', array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( array( 'message' => 'Activity deleted successfully.' ), 200 );
|
||||
<?php
|
||||
/**
|
||||
* REST API routes for the plugin.
|
||||
*
|
||||
* @package WordPress Activity Stats
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST API routes.
|
||||
*/
|
||||
function statpress_register_rest_routes() {
|
||||
$namespace = 'statpress/v1';
|
||||
|
||||
// Route for getting a collection of activities
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/activities',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => 'statpress_get_activities_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
'args' => array(
|
||||
'page' => array(
|
||||
'validate_callback' => 'is_numeric',
|
||||
),
|
||||
'per_page' => array(
|
||||
'validate_callback' => 'is_numeric',
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => 'statpress_create_activity_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Route for a single activity
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/activities/(?P<id>[\d]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => 'statpress_get_activity_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => 'statpress_update_activity_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => 'statpress_delete_activity_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Route for parsing GPX file summary
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/gpx/parse-summary',
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE, // Use POST to send URL in the body
|
||||
'callback' => 'statpress_parse_gpx_summary_api',
|
||||
'permission_callback' => 'statpress_api_permissions_check',
|
||||
'args' => array(
|
||||
'gpx_url' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => 'esc_url_raw',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission check for API endpoints.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function statpress_api_permissions_check() {
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of activities.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_get_activities_api( WP_REST_Request $request ) {
|
||||
global $wpdb;
|
||||
$table_activities = $wpdb->prefix . 'statpress_activities';
|
||||
|
||||
$per_page = $request->get_param( 'per_page' ) ? (int) $request->get_param( 'per_page' ) : 20;
|
||||
$page = $request->get_param( 'page' ) ? (int) $request->get_param( 'page' ) : 1;
|
||||
$offset = ( $page - 1 ) * $per_page;
|
||||
|
||||
// Get total items for pagination headers
|
||||
$total_items = (int) $wpdb->get_var( "SELECT COUNT(id) FROM $table_activities" );
|
||||
$total_pages = ceil( $total_items / $per_page );
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT a.*, c.name as category_name, et.name as event_type_name, eq.name as equipment_name
|
||||
FROM $table_activities a
|
||||
LEFT JOIN {$wpdb->prefix}statpress_categories c ON a.category_id = c.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_event_types et ON a.event_type_id = et.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_equipment eq ON a.equipment_id = eq.id
|
||||
ORDER BY a.date DESC, a.id DESC
|
||||
LIMIT %d OFFSET %d",
|
||||
$per_page,
|
||||
$offset
|
||||
);
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
$response = new WP_REST_Response( $results, 200 );
|
||||
$response->header( 'X-WP-Total', $total_items );
|
||||
$response->header( 'X-WP-TotalPages', $total_pages );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single activity.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_get_activity_api( WP_REST_Request $request ) {
|
||||
global $wpdb;
|
||||
$id = (int) $request['id'];
|
||||
|
||||
// Use the same rich query as the collection endpoint for consistency.
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT a.*, c.name as category_name, et.name as event_type_name, eq.name as equipment_name
|
||||
FROM {$wpdb->prefix}statpress_activities a
|
||||
LEFT JOIN {$wpdb->prefix}statpress_categories c ON a.category_id = c.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_event_types et ON a.event_type_id = et.id
|
||||
LEFT JOIN {$wpdb->prefix}statpress_equipment eq ON a.equipment_id = eq.id
|
||||
WHERE a.id = %d",
|
||||
$id
|
||||
);
|
||||
$activity = $wpdb->get_row( $sql );
|
||||
|
||||
if ( ! $activity ) {
|
||||
return new WP_Error( 'not_found', 'Activity not found', array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $activity, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new activity.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_create_activity_api( WP_REST_Request $request ) {
|
||||
$params = $request->get_json_params();
|
||||
$activity_id = statpress_save_activity_data( $params );
|
||||
|
||||
if ( ! $activity_id ) {
|
||||
return new WP_Error( 'cant-create', 'Error creating activity', array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
// Create a new request object to fetch the newly created activity.
|
||||
$new_request = new WP_REST_Request();
|
||||
$new_request->set_param( 'id', $activity_id );
|
||||
$response = statpress_get_activity_api( $new_request );
|
||||
$response->set_status( 201 ); // 201 Created
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing activity.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_update_activity_api( WP_REST_Request $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$params = $request->get_json_params();
|
||||
$activity_id = statpress_save_activity_data( $params, $id );
|
||||
|
||||
if ( ! $activity_id ) {
|
||||
return new WP_Error( 'cant-update', 'Error updating activity', array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
return statpress_get_activity_api( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an activity.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
function statpress_delete_activity_api( WP_REST_Request $request ) {
|
||||
global $wpdb;
|
||||
$id = (int) $request['id'];
|
||||
|
||||
$result = $wpdb->delete( $wpdb->prefix . 'statpress_activities', array( 'id' => $id ), array( '%d' ) );
|
||||
|
||||
if ( ! $result ) {
|
||||
return new WP_Error( 'cant-delete', 'Error deleting activity', array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( array( 'message' => 'Activity deleted successfully.' ), 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* API callback to parse a GPX file and return its summary data.
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return WP_REST_Response|WP_Error A response object with summary data or an error.
|
||||
*/
|
||||
function statpress_parse_gpx_summary_api( WP_REST_Request $request ) {
|
||||
$params = $request->get_json_params();
|
||||
$gpx_url = $params['gpx_url'] ?? '';
|
||||
|
||||
if ( empty( $gpx_url ) ) {
|
||||
return new WP_Error( 'no_url_provided', 'Nie podano adresu URL do pliku GPX.', array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// This new function will do the heavy lifting of calculation.
|
||||
$summary = statpress_calculate_gpx_summary( $gpx_url );
|
||||
|
||||
if ( empty( $summary ) || ! $summary['distance'] > 0 ) {
|
||||
return new WP_Error( 'gpx_parse_error', 'Nie udało się przetworzyć pliku GPX. Sprawdź, czy plik jest poprawny i zawiera dane trasy.', array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $summary, 200 );
|
||||
}
|
||||
Reference in New Issue
Block a user