Import activites, shortcodes in two columns
This commit is contained in:
+391
-25
@@ -220,6 +220,15 @@ function mystat_add_admin_menu() {
|
|||||||
'mystat-infographic', // Slug podmenu
|
'mystat-infographic', // Slug podmenu
|
||||||
'mystat_infographic_page' // Funkcja renderująca
|
'mystat_infographic_page' // Funkcja renderująca
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mystat_dashboard_page() {
|
function mystat_dashboard_page() {
|
||||||
@@ -438,7 +447,21 @@ function mystat_yearly_summary_page() {
|
|||||||
$total_year_calories = 0;
|
$total_year_calories = 0;
|
||||||
$total_year_seconds = 0;
|
$total_year_seconds = 0;
|
||||||
|
|
||||||
for ( $i = 1; $i <= 12; $i++ ) {
|
// 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
|
$month_name = date_i18n( 'F', mktime( 0, 0, 0, $i, 10 ) ); // Nazwa miesiąca
|
||||||
$data = isset( $monthly_summary[$i] ) ? $monthly_summary[$i] : null;
|
$data = isset( $monthly_summary[$i] ) ? $monthly_summary[$i] : null;
|
||||||
|
|
||||||
@@ -746,6 +769,294 @@ function mystat_infographic_page() {
|
|||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mystat_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['mystat_csv_import_nonce_field'] ) ) {
|
||||||
|
mystat_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( 'mystat_csv_import_nonce', 'mystat_csv_import_nonce_field' ); ?>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row"><label for="mystat_csv_file">Opcja 1: Wgraj plik CSV</label></th>
|
||||||
|
<td><input type="file" id="mystat_csv_file" name="mystat_csv_file" accept=".csv,text/csv" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row"><label for="mystat_csv_data">Opcja 2: Wklej dane CSV</label></th>
|
||||||
|
<td><textarea name="mystat_csv_data" id="mystat_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 mystat_handle_csv_import() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
if ( ! isset( $_POST['mystat_csv_import_nonce_field'] ) || ! wp_verify_nonce( $_POST['mystat_csv_import_nonce_field'], 'mystat_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['mystat_csv_data'] ) ) {
|
||||||
|
$csv_content = stripslashes( $_POST['mystat_csv_data'] );
|
||||||
|
} elseif ( ! empty( $_FILES['mystat_csv_file']['tmp_name'] ) && UPLOAD_ERR_OK === $_FILES['mystat_csv_file']['error'] ) {
|
||||||
|
$csv_content = file_get_contents( $_FILES['mystat_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 = [
|
||||||
|
// 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 . 'mystat_categories';
|
||||||
|
$table_event_types = $wpdb->prefix . 'mystat_event_types';
|
||||||
|
$table_equipment = $wpdb->prefix . 'mystat_equipment';
|
||||||
|
|
||||||
|
$create_lookup = function( $table_name ) use ( $wpdb ) {
|
||||||
|
$items = $wpdb->get_results( "SELECT id, name FROM {$table_name}" );
|
||||||
|
$lookup = [];
|
||||||
|
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 . 'mystat_activities';
|
||||||
|
$imported_count = 0;
|
||||||
|
$skipped_details = [];
|
||||||
|
$row_number = 1; // Header is row 1
|
||||||
|
|
||||||
|
// Normalize line endings and split into lines
|
||||||
|
$lines = str_replace( ["\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 = [];
|
||||||
|
foreach ($header_raw as $col) {
|
||||||
|
$header[] = $column_map[strtolower($col)] ?? 'ignored_' . uniqid();
|
||||||
|
}
|
||||||
|
|
||||||
|
$required_internal_keys = ['date', 'title', 'category_name', 'distance'];
|
||||||
|
$missing_keys = array_diff($required_internal_keys, $header);
|
||||||
|
if (!empty($missing_keys)) {
|
||||||
|
$key_to_polish_map = [
|
||||||
|
'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 ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
|
||||||
|
$row_number++;
|
||||||
|
if ( empty( trim( $line ) ) ) {
|
||||||
|
continue; // Skip empty lines
|
||||||
|
}
|
||||||
|
$data = str_getcsv( $line, $delimiter );
|
||||||
|
|
||||||
|
if (count($data) !== count($header)) {
|
||||||
|
$skipped_details[] = [
|
||||||
|
'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 = [];
|
||||||
|
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'] ?? '';
|
||||||
|
// Use a robust trim to handle various whitespace characters and make it case-insensitive
|
||||||
|
$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[] = [
|
||||||
|
'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 = [
|
||||||
|
'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[] = [
|
||||||
|
'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 ( $imported_count === 0 && 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 ($row_number === 1) echo '<div class="notice notice-info"><p>Dane CSV były puste lub zawierały tylko nagłówek.</p></div>';
|
||||||
|
}
|
||||||
|
|
||||||
function mystat_edit_activity_page() {
|
function mystat_edit_activity_page() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
$activity_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
|
$activity_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
|
||||||
@@ -1200,8 +1511,17 @@ function mystat_render_history_table() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 2. POBIERANIE DANYCH (SELECT) ---
|
// --- 2. USTAWIENIA PAGINACJI ---
|
||||||
// Pobieramy ostatnie 10 wpisów, łącząc z tabelą kategorii, aby mieć nazwę i ikonę
|
$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("
|
$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
|
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
|
FROM $table_activities a
|
||||||
@@ -1209,16 +1529,34 @@ function mystat_render_history_table() {
|
|||||||
LEFT JOIN {$wpdb->prefix}mystat_event_types et ON a.event_type_id = et.id
|
LEFT JOIN {$wpdb->prefix}mystat_event_types et ON a.event_type_id = et.id
|
||||||
LEFT JOIN {$wpdb->prefix}mystat_equipment eq ON a.equipment_id = eq.id
|
LEFT JOIN {$wpdb->prefix}mystat_equipment eq ON a.equipment_id = eq.id
|
||||||
ORDER BY a.date DESC, a.id DESC
|
ORDER BY a.date DESC, a.id DESC
|
||||||
LIMIT %d
|
LIMIT %d OFFSET %d
|
||||||
", 10);
|
", $items_per_page, $offset);
|
||||||
|
|
||||||
$activities = $wpdb->get_results( $sql );
|
$activities = $wpdb->get_results( $sql );
|
||||||
|
|
||||||
// --- 3. WIDOK TABELI (HTML) ---
|
// --- 4. WIDOK TABELI (HTML) ---
|
||||||
?>
|
?>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h2>Ostatnie Aktywności</h2>
|
<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">
|
<table class="wp-list-table widefat fixed striped table-view-list">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1238,13 +1576,11 @@ function mystat_render_history_table() {
|
|||||||
<?php if ( ! empty( $activities ) ) : ?>
|
<?php if ( ! empty( $activities ) ) : ?>
|
||||||
<?php foreach ( $activities as $row ) : ?>
|
<?php foreach ( $activities as $row ) : ?>
|
||||||
<?php
|
<?php
|
||||||
// Generowanie URL-i akcji
|
// Generowanie URL-i akcji z zachowaniem paginacji
|
||||||
$delete_url = add_query_arg( array(
|
$delete_url = wp_nonce_url( add_query_arg( array(
|
||||||
'page' => $_REQUEST['page'], // Zachowaj obecną stronę admina
|
|
||||||
'action' => 'mystat_delete',
|
'action' => 'mystat_delete',
|
||||||
'id' => $row->id,
|
'id' => $row->id,
|
||||||
'_wpnonce' => wp_create_nonce( 'mystat_delete_' . $row->id )
|
) ), 'mystat_delete_' . $row->id );
|
||||||
), admin_url( 'admin.php' ) );
|
|
||||||
|
|
||||||
$edit_url = add_query_arg( array(
|
$edit_url = add_query_arg( array(
|
||||||
'page' => 'mystat-edit-activity',
|
'page' => 'mystat-edit-activity',
|
||||||
@@ -1288,6 +1624,24 @@ function mystat_render_history_table() {
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</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>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
@@ -1505,19 +1859,29 @@ function mystat_single_activity_shortcode_handler( $atts ) {
|
|||||||
<h4><?php echo esc_html( $activity->title ); ?></h4>
|
<h4><?php echo esc_html( $activity->title ); ?></h4>
|
||||||
<p><em><?php echo esc_html( date_i18n( 'j F Y', strtotime( $activity->date ) ) ); ?></em></p>
|
<p><em><?php echo esc_html( date_i18n( 'j F Y', strtotime( $activity->date ) ) ); ?></em></p>
|
||||||
|
|
||||||
<table class="mystat-single-summary-table">
|
<div class="mystat-single-columns-container">
|
||||||
<tbody>
|
<div class="mystat-single-col">
|
||||||
<?php $render_row('Dystans', number_format($activity->distance, 2, ',', ' '), 'km'); ?>
|
<table class="mystat-single-summary-table">
|
||||||
<?php $render_row('Czas trwania', $activity->duration); ?>
|
<tbody>
|
||||||
<?php $render_row('Średnia prędkość', number_format($activity->avg_speed, 1, ',', ' '), 'km/h'); ?>
|
<?php $render_row('Dystans', number_format($activity->distance, 2, ',', ' '), 'km'); ?>
|
||||||
<?php $render_row('Suma wzniosów', $activity->total_elevation_gain, 'm'); ?>
|
<?php $render_row('Czas trwania', $activity->duration); ?>
|
||||||
<?php $render_row('Kategoria', $activity->category_name); ?>
|
<?php $render_row('Średnia prędkość', number_format($activity->avg_speed, 1, ',', ' '), 'km/h'); ?>
|
||||||
<?php $render_row('Sprzęt', $activity->equipment_name); ?>
|
<?php $render_row('Suma wzniosów', $activity->total_elevation_gain, 'm'); ?>
|
||||||
<?php if ( ! empty( $activity->strava_url ) ) : ?>
|
</tbody>
|
||||||
<tr><th>Strava</th><td><a href="<?php echo esc_url( $activity->strava_url ); ?>" target="_blank" rel="noopener noreferrer">Zobacz aktywność</a></td></tr>
|
</table>
|
||||||
<?php endif; ?>
|
</div>
|
||||||
</tbody>
|
<div class="mystat-single-col">
|
||||||
</table>
|
<table class="mystat-single-summary-table">
|
||||||
|
<tbody>
|
||||||
|
<?php $render_row('Kategoria', $activity->category_name); ?>
|
||||||
|
<?php $render_row('Sprzęt', $activity->equipment_name); ?>
|
||||||
|
<?php if ( ! empty( $activity->strava_url ) ) : ?>
|
||||||
|
<tr><th>Strava</th><td><a href="<?php echo esc_url( $activity->strava_url ); ?>" target="_blank" rel="noopener noreferrer">Zobacz aktywność</a></td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php if ( ! empty( $track_points ) ) : ?>
|
<?php if ( ! empty( $track_points ) ) : ?>
|
||||||
<div id="<?php echo $map_id; ?>" style="height: 350px; width: 100%; margin-top: 15px; border-radius: 5px;"></div>
|
<div id="<?php echo $map_id; ?>" style="height: 350px; width: 100%; margin-top: 15px; border-radius: 5px;"></div>
|
||||||
@@ -1527,6 +1891,8 @@ function mystat_single_activity_shortcode_handler( $atts ) {
|
|||||||
.mystat-single-activity-shortcode { border: 1px solid #eee; padding: 15px; margin-bottom: 1.5em; border-radius: 5px; background: #f9f9f9; }
|
.mystat-single-activity-shortcode { border: 1px solid #eee; padding: 15px; margin-bottom: 1.5em; border-radius: 5px; background: #f9f9f9; }
|
||||||
.mystat-single-activity-shortcode h4 { margin-top: 0; }
|
.mystat-single-activity-shortcode h4 { margin-top: 0; }
|
||||||
.mystat-single-activity-shortcode p em { color: #777; font-size: 0.9em; }
|
.mystat-single-activity-shortcode p em { color: #777; font-size: 0.9em; }
|
||||||
|
.mystat-single-columns-container { display: flex; flex-wrap: wrap; gap: 30px; margin-bottom: 15px; }
|
||||||
|
.mystat-single-col { flex: 1; min-width: 240px; }
|
||||||
.mystat-single-summary-table { width: 100%; border-collapse: collapse; }
|
.mystat-single-summary-table { width: 100%; border-collapse: collapse; }
|
||||||
.mystat-single-summary-table th, .mystat-single-summary-table td { padding: 4px 0; border: none; text-align: left; vertical-align: top; }
|
.mystat-single-summary-table th, .mystat-single-summary-table td { padding: 4px 0; border: none; text-align: left; vertical-align: top; }
|
||||||
.mystat-single-summary-table th { font-weight: bold; width: 150px; }
|
.mystat-single-summary-table th { font-weight: bold; width: 150px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user