637 lines
30 KiB
PHP
637 lines
30 KiB
PHP
<?php
|
|
$settings_tab = \S::get( 'settings_tab' ) === 'cron' ? 'cron' : 'general';
|
|
$cron_data = is_array( $this -> cron_data ?? null ) ? $this -> cron_data : [];
|
|
$cron_progress = is_array( $cron_data['progress'] ?? null ) ? $cron_data['progress'] : [];
|
|
$cron_urls = is_array( $cron_data['urls'] ?? null ) ? $cron_data['urls'] : [];
|
|
?>
|
|
|
|
<div class="settings-tabs">
|
|
<a href="/settings?settings_tab=general" class="settings-tab <?= $settings_tab === 'general' ? 'active' : ''; ?>">
|
|
<i class="fa-solid fa-sliders"></i> Ustawienia
|
|
</a>
|
|
<a href="/settings?settings_tab=cron" class="settings-tab <?= $settings_tab === 'cron' ? 'active' : ''; ?>">
|
|
<i class="fa-solid fa-clock-rotate-left"></i> CRON
|
|
</a>
|
|
</div>
|
|
|
|
<?php if ( $settings_tab === 'cron' ): ?>
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="settings-card">
|
|
<div class="settings-card-header">
|
|
<div class="settings-card-icon"><i class="fa-solid fa-clock-rotate-left"></i></div>
|
|
<div>
|
|
<h3>Status CRON</h3>
|
|
<small>Adresy URL do wywołań, postęp pipeline i ostatnie uruchomienia</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="cron-status-overview">
|
|
<div><strong>Ostatnie wywołanie:</strong> <span data-cron-overall-last-invoked><?= htmlspecialchars( (string) ( $cron_data['overall_last_invoked_at'] ?? 'Brak danych' ) ); ?></span></div>
|
|
<div><strong>Klienci z Google Ads ID:</strong> <span data-cron-clients-total><?= (int) ( $cron_data['clients_total'] ?? 0 ); ?></span></div>
|
|
<div><strong>Klienci z Facebook Ads ID:</strong> <span data-cron-fb-clients-total><?= (int) ( $cron_data['facebook_clients_total'] ?? 0 ); ?></span></div>
|
|
</div>
|
|
|
|
<div class="cron-progress-list">
|
|
<?php foreach ( $cron_progress as $progress_index => $item ): ?>
|
|
<?php
|
|
$processed = (int) ( $item['processed'] ?? 0 );
|
|
$total = (int) ( $item['total'] ?? 0 );
|
|
$percent = (int) ( $item['percent'] ?? 0 );
|
|
?>
|
|
<div class="cron-progress-item" data-cron-progress-item="<?= (int) $progress_index; ?>">
|
|
<div class="cron-progress-head">
|
|
<strong><?= htmlspecialchars( (string) ( $item['name'] ?? 'CRON' ) ); ?></strong>
|
|
<span data-cron-progress-summary><?= $processed; ?> / <?= $total; ?> (<?= $percent; ?>%)</span>
|
|
</div>
|
|
<div class="cron-progress-bar">
|
|
<span data-cron-progress-fill style="width: <?= $percent; ?>%;"></span>
|
|
</div>
|
|
<small data-cron-progress-meta><?= htmlspecialchars( (string) ( $item['meta'] ?? '' ) ); ?></small>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<div class="cron-url-list">
|
|
<?php foreach ( $cron_urls as $url_index => $row ): ?>
|
|
<div class="cron-url-item" data-cron-url-item="<?= (int) $url_index; ?>">
|
|
<div class="cron-url-top">
|
|
<strong><?= htmlspecialchars( (string) ( $row['name'] ?? 'Cron' ) ); ?></strong>
|
|
<small>Ostatnio: <span data-cron-url-last-invoked><?= htmlspecialchars( (string) ( $row['last_invoked_at'] ?? 'Brak danych' ) ); ?></span></small>
|
|
</div>
|
|
<small class="cron-url-plan" data-cron-url-plan><?= htmlspecialchars( (string) ( $row['plan'] ?? '' ) ); ?></small>
|
|
<code><?= htmlspecialchars( (string) ( $row['url'] ?? '' ) ); ?></code>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<a href="/settings?settings_tab=cron" class="btn btn-primary btn-sm">
|
|
<i class="fa-solid fa-rotate-right mr5"></i> Odśwież status
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="row">
|
|
<div class="col-12 col-md-6">
|
|
<div class="settings-card">
|
|
<div class="settings-card-header">
|
|
<div class="settings-card-icon"><i class="fa-solid fa-lock"></i></div>
|
|
<div>
|
|
<h3>Zmiana hasła</h3>
|
|
<small>Zmień swoje stare hasło na nowe</small>
|
|
</div>
|
|
</div>
|
|
<form method="POST" id="password-settings" action="/users/password_change/">
|
|
<div class="settings-field">
|
|
<label for="password_old">Stare hasło</label>
|
|
<div class="settings-input-wrap">
|
|
<i class="fa-solid fa-key settings-input-icon"></i>
|
|
<input type="password" id="password_old" name="password_old" class="form-control" required placeholder="Wprowadź stare hasło" />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'password_old' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="password_new">Nowe hasło</label>
|
|
<div class="settings-input-wrap">
|
|
<i class="fa-solid fa-key settings-input-icon"></i>
|
|
<input type="password" id="password_new" name="password_new" class="form-control" required placeholder="Wprowadź nowe hasło" />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'password_new' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-success"><i class="fa-solid fa-check mr5"></i>Zmień hasło</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="margin-top: 25px;">
|
|
<div class="col-12">
|
|
<div class="settings-card">
|
|
<div class="settings-card-header">
|
|
<div class="settings-card-icon"><i class="fa-brands fa-google"></i></div>
|
|
<div>
|
|
<h3>Google Ads API</h3>
|
|
<small>Dane do połączenia z Google Ads REST API (wymagane do synchronizacji kampanii)</small>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
$last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' );
|
|
$last_error_at_raw = \services\GoogleAdsApi::get_setting( 'google_ads_last_error_at' );
|
|
$last_error_at = '';
|
|
if ( $last_error_at_raw )
|
|
{
|
|
$last_error_at_ts = strtotime( (string) $last_error_at_raw );
|
|
if ( $last_error_at_ts )
|
|
{
|
|
$last_error_at = date( 'Y-m-d H:i:s', $last_error_at_ts );
|
|
}
|
|
}
|
|
if ( $last_error ): ?>
|
|
<div class="settings-alert-error">
|
|
<i class="fa-solid fa-triangle-exclamation"></i>
|
|
<div style="min-width:0;">
|
|
<div><strong>Ostatni blad API:</strong></div>
|
|
<div><strong>Data wystapienia:</strong> <?= htmlspecialchars( $last_error_at !== '' ? $last_error_at : 'Brak danych' ); ?></div>
|
|
<div style="margin-top:6px;white-space:pre-wrap;word-break:break-word;"><?= htmlspecialchars( $last_error ); ?></div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<form method="POST" id="google-ads-settings" action="/settings/save_google_ads">
|
|
<div class="settings-field" style="margin-bottom: 16px;">
|
|
<label class="settings-toggle-label">
|
|
<?php $google_ads_debug_enabled = \services\GoogleAdsApi::get_setting( 'google_ads_debug_enabled' ) !== '0'; ?>
|
|
<input type="hidden" name="google_ads_debug_enabled" value="0" />
|
|
<input type="checkbox" name="google_ads_debug_enabled" value="1" class="settings-toggle-checkbox" <?= $google_ads_debug_enabled ? 'checked' : ''; ?> />
|
|
<span class="settings-toggle-switch"></span>
|
|
<span class="settings-toggle-text">Włącz debug Google Ads API</span>
|
|
</label>
|
|
</div>
|
|
<div class="settings-fields-grid">
|
|
<div class="settings-field">
|
|
<label for="google_ads_developer_token">Developer Token</label>
|
|
<input type="text" id="google_ads_developer_token" name="google_ads_developer_token" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'google_ads_developer_token' ) ); ?>" placeholder="np. ABcdEf1234..." />
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="google_ads_client_id">OAuth2 Client ID</label>
|
|
<input type="text" id="google_ads_client_id" name="google_ads_client_id" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'google_ads_client_id' ) ); ?>" placeholder="np. 123456789.apps.googleusercontent.com" />
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="google_ads_client_secret">OAuth2 Client Secret</label>
|
|
<div class="settings-input-wrap">
|
|
<input type="password" id="google_ads_client_secret" name="google_ads_client_secret" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'google_ads_client_secret' ) ); ?>" />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'google_ads_client_secret' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="google_ads_refresh_token">OAuth2 Refresh Token</label>
|
|
<div class="settings-input-wrap">
|
|
<input type="password" id="google_ads_refresh_token" name="google_ads_refresh_token" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'google_ads_refresh_token' ) ); ?>" />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'google_ads_refresh_token' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="google_merchant_refresh_token">Merchant API Refresh Token</label>
|
|
<div class="settings-input-wrap">
|
|
<input type="password" id="google_merchant_refresh_token" name="google_merchant_refresh_token" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'google_merchant_refresh_token' ) ); ?>" placeholder="Refresh token dla scope content" />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'google_merchant_refresh_token' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="google_ads_manager_account_id">Manager Account ID <span class="text-muted">(opcjonalnie, dla MCC)</span></label>
|
|
<input type="text" id="google_ads_manager_account_id" name="google_ads_manager_account_id" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'google_ads_manager_account_id' ) ); ?>" placeholder="np. 123-456-7890" />
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-success" onclick="$( '#google-ads-settings' ).submit();"><i class="fa-solid fa-check mr5"></i>Zapisz ustawienia Google Ads</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="margin-top: 25px;">
|
|
<div class="col-12">
|
|
<div class="settings-card">
|
|
<div class="settings-card-header">
|
|
<div class="settings-card-icon"><i class="fa-brands fa-facebook"></i></div>
|
|
<div>
|
|
<h3>Facebook Ads API</h3>
|
|
<small>Token dostępu i konfiguracja synchronizacji kampanii Facebook Ads</small>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
$fb_last_error = \services\FacebookAdsApi::get_setting( 'facebook_ads_last_error' );
|
|
$fb_last_error_at_raw = \services\FacebookAdsApi::get_setting( 'facebook_ads_last_error_at' );
|
|
$fb_last_error_at = '';
|
|
if ( $fb_last_error_at_raw )
|
|
{
|
|
$fb_ts = strtotime( (string) $fb_last_error_at_raw );
|
|
if ( $fb_ts )
|
|
{
|
|
$fb_last_error_at = date( 'Y-m-d H:i:s', $fb_ts );
|
|
}
|
|
}
|
|
if ( $fb_last_error ): ?>
|
|
<div class="settings-alert-error">
|
|
<i class="fa-solid fa-triangle-exclamation"></i>
|
|
<div style="min-width:0;">
|
|
<div><strong>Ostatni błąd API:</strong></div>
|
|
<div><strong>Data wystąpienia:</strong> <?= htmlspecialchars( $fb_last_error_at !== '' ? $fb_last_error_at : 'Brak danych' ); ?></div>
|
|
<div style="margin-top:6px;white-space:pre-wrap;word-break:break-word;"><?= htmlspecialchars( $fb_last_error ); ?></div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<form method="POST" id="facebook-ads-settings" action="/settings/save_facebook_ads">
|
|
<div class="settings-fields-grid">
|
|
<div class="settings-field">
|
|
<label for="facebook_ads_token">Access Token</label>
|
|
<div class="settings-input-wrap">
|
|
<input type="password" id="facebook_ads_token" name="facebook_ads_token" class="form-control" value="<?= htmlspecialchars( (string) \services\FacebookAdsApi::get_setting( 'facebook_ads_token' ) ); ?>" placeholder="EAAVtp..." />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'facebook_ads_token' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="facebook_ads_conversion_window_days">Okno konwersji (dni)</label>
|
|
<input type="number" id="facebook_ads_conversion_window_days" name="facebook_ads_conversion_window_days" class="form-control" min="1" max="90" value="<?= (int) ( \services\FacebookAdsApi::get_setting( 'facebook_ads_conversion_window_days' ) ?: 7 ); ?>" />
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-success" onclick="$( '#facebook-ads-settings' ).submit();"><i class="fa-solid fa-check mr5"></i>Zapisz ustawienia Facebook Ads</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="margin-top: 25px;">
|
|
<div class="col-12">
|
|
<div class="settings-card">
|
|
<div class="settings-card-header">
|
|
<div class="settings-card-icon"><i class="fa-solid fa-robot"></i></div>
|
|
<div>
|
|
<h3>OpenAI (ChatGPT)</h3>
|
|
<small>Klucz API i model do optymalizacji tytułów i opisów produktów przez AI</small>
|
|
</div>
|
|
</div>
|
|
<form method="POST" id="openai-settings" action="/settings/save_openai">
|
|
<div class="settings-field" style="margin-bottom: 16px;">
|
|
<label class="settings-toggle-label">
|
|
<?php $openai_enabled = \services\GoogleAdsApi::get_setting( 'openai_enabled' ) !== '0'; ?>
|
|
<input type="hidden" name="openai_enabled" value="0" />
|
|
<input type="checkbox" name="openai_enabled" value="1" class="settings-toggle-checkbox" <?= $openai_enabled ? 'checked' : ''; ?> />
|
|
<span class="settings-toggle-switch"></span>
|
|
<span class="settings-toggle-text">Włącz OpenAI (ChatGPT)</span>
|
|
</label>
|
|
</div>
|
|
<div class="settings-fields-grid">
|
|
<div class="settings-field">
|
|
<label for="openai_api_key">API Key</label>
|
|
<div class="settings-input-wrap">
|
|
<input type="password" id="openai_api_key" name="openai_api_key" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'openai_api_key' ) ); ?>" placeholder="sk-..." />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'openai_api_key' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="openai_model">Model</label>
|
|
<?php $current_model = \services\GoogleAdsApi::get_setting( 'openai_model' ) ?: 'gpt-5-mini'; ?>
|
|
<select id="openai_model" name="openai_model" class="form-control">
|
|
<option value="gpt-5.2" <?= $current_model === 'gpt-5.2' ? 'selected' : ''; ?>>GPT-5.2 (najnowszy, $1.75/$14 per 1M)</option>
|
|
<option value="gpt-5-mini" <?= $current_model === 'gpt-5-mini' ? 'selected' : ''; ?>>GPT-5 Mini (szybki, $0.25/$2 per 1M)</option>
|
|
<option value="gpt-4.1" <?= $current_model === 'gpt-4.1' ? 'selected' : ''; ?>>GPT-4.1</option>
|
|
<option value="gpt-4.1-mini" <?= $current_model === 'gpt-4.1-mini' ? 'selected' : ''; ?>>GPT-4.1 Mini</option>
|
|
<option value="gpt-4o" <?= $current_model === 'gpt-4o' ? 'selected' : ''; ?>>GPT-4o (legacy)</option>
|
|
<option value="gpt-4o-mini" <?= $current_model === 'gpt-4o-mini' ? 'selected' : ''; ?>>GPT-4o Mini (legacy)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-success" onclick="$( '#openai-settings' ).submit();"><i class="fa-solid fa-check mr5"></i>Zapisz ustawienia OpenAI</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="margin-top: 25px;">
|
|
<div class="col-12">
|
|
<div class="settings-card">
|
|
<div class="settings-card-header">
|
|
<div class="settings-card-icon"><i class="fa-solid fa-brain"></i></div>
|
|
<div>
|
|
<h3>Claude (Anthropic)</h3>
|
|
<small>Klucz API i model Claude do optymalizacji tytułów i opisów produktów przez AI</small>
|
|
</div>
|
|
</div>
|
|
<form method="POST" id="claude-settings" action="/settings/save_claude">
|
|
<div class="settings-field" style="margin-bottom: 16px;">
|
|
<label class="settings-toggle-label">
|
|
<?php $claude_enabled = \services\GoogleAdsApi::get_setting( 'claude_enabled' ) !== '0'; ?>
|
|
<input type="hidden" name="claude_enabled" value="0" />
|
|
<input type="checkbox" name="claude_enabled" value="1" class="settings-toggle-checkbox" <?= $claude_enabled ? 'checked' : ''; ?> />
|
|
<span class="settings-toggle-switch"></span>
|
|
<span class="settings-toggle-text">Włącz Claude (Anthropic)</span>
|
|
</label>
|
|
</div>
|
|
<div class="settings-fields-grid">
|
|
<div class="settings-field">
|
|
<label for="claude_api_key">API Key</label>
|
|
<div class="settings-input-wrap">
|
|
<input type="password" id="claude_api_key" name="claude_api_key" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'claude_api_key' ) ); ?>" placeholder="sk-ant-..." />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'claude_api_key' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="claude_model">Model</label>
|
|
<?php $current_claude_model = \services\GoogleAdsApi::get_setting( 'claude_model' ) ?: 'claude-sonnet-4-5-20250929'; ?>
|
|
<select id="claude_model" name="claude_model" class="form-control">
|
|
<option value="claude-opus-4-6" <?= $current_claude_model === 'claude-opus-4-6' ? 'selected' : ''; ?>>Claude Opus 4.6 (najpotężniejszy)</option>
|
|
<option value="claude-sonnet-4-5-20250929" <?= $current_claude_model === 'claude-sonnet-4-5-20250929' ? 'selected' : ''; ?>>Claude Sonnet 4.5 (zbalansowany)</option>
|
|
<option value="claude-haiku-4-5-20251001" <?= $current_claude_model === 'claude-haiku-4-5-20251001' ? 'selected' : ''; ?>>Claude Haiku 4.5 (szybki, tani)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-success" onclick="$( '#claude-settings' ).submit();"><i class="fa-solid fa-check mr5"></i>Zapisz ustawienia Claude</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="margin-top: 25px;">
|
|
<div class="col-12">
|
|
<div class="settings-card">
|
|
<div class="settings-card-header">
|
|
<div class="settings-card-icon"><i class="fa-solid fa-diamond"></i></div>
|
|
<div>
|
|
<h3>Gemini (Google)</h3>
|
|
<small>Klucz API i model Gemini do optymalizacji tytułów i opisów produktów przez AI</small>
|
|
</div>
|
|
</div>
|
|
<form method="POST" id="gemini-settings" action="/settings/save_gemini">
|
|
<div class="settings-field" style="margin-bottom: 16px;">
|
|
<label class="settings-toggle-label">
|
|
<?php $gemini_enabled = \services\GoogleAdsApi::get_setting( 'gemini_enabled' ) !== '0'; ?>
|
|
<input type="hidden" name="gemini_enabled" value="0" />
|
|
<input type="checkbox" name="gemini_enabled" value="1" class="settings-toggle-checkbox" <?= $gemini_enabled ? 'checked' : ''; ?> />
|
|
<span class="settings-toggle-switch"></span>
|
|
<span class="settings-toggle-text">Włącz Gemini (Google)</span>
|
|
</label>
|
|
</div>
|
|
<div class="settings-fields-grid">
|
|
<div class="settings-field">
|
|
<label for="gemini_api_key">API Key</label>
|
|
<div class="settings-input-wrap">
|
|
<input type="password" id="gemini_api_key" name="gemini_api_key" class="form-control" value="<?= htmlspecialchars( \services\GoogleAdsApi::get_setting( 'gemini_api_key' ) ); ?>" placeholder="AIza..." />
|
|
<button type="button" class="settings-toggle-pw" onclick="password_toggle( this, 'gemini_api_key' )">
|
|
<i class="fa-solid fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="gemini_model">Model</label>
|
|
<?php $current_gemini_model = \services\GoogleAdsApi::get_setting( 'gemini_model' ) ?: 'gemini-2.5-flash'; ?>
|
|
<select id="gemini_model" name="gemini_model" class="form-control">
|
|
<option value="gemini-2.5-pro" <?= $current_gemini_model === 'gemini-2.5-pro' ? 'selected' : ''; ?>>Gemini 2.5 Pro (najlepszy, $1.25/$10 per 1M)</option>
|
|
<option value="gemini-2.5-flash" <?= $current_gemini_model === 'gemini-2.5-flash' ? 'selected' : ''; ?>>Gemini 2.5 Flash (szybki, $0.30/$2.50 per 1M)</option>
|
|
<option value="gemini-2.5-flash-lite" <?= $current_gemini_model === 'gemini-2.5-flash-lite' ? 'selected' : ''; ?>>Gemini 2.5 Flash Lite (najtańszy)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-success" onclick="$( '#gemini-settings' ).submit();"><i class="fa-solid fa-check mr5"></i>Zapisz ustawienia Gemini</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="margin-top: 25px;">
|
|
<div class="col-12">
|
|
<div class="settings-card">
|
|
<div class="settings-card-header">
|
|
<div class="settings-card-icon"><i class="fa-solid fa-wand-magic-sparkles"></i></div>
|
|
<div>
|
|
<h3>Prompty AI (ogólne)</h3>
|
|
<small>Wspólne prompty używane przez wszystkie modele AI (ChatGPT, Claude, Gemini) do generowania tytułów i opisów produktów</small>
|
|
</div>
|
|
</div>
|
|
<form method="POST" id="ai-prompts-settings" action="/settings/save_ai_prompts">
|
|
<?php
|
|
$ai_title_prompt_saved = trim( (string) \services\GoogleAdsApi::get_setting( 'ai_prompt_title_template' ) );
|
|
$ai_desc_prompt_saved = trim( (string) \services\GoogleAdsApi::get_setting( 'ai_prompt_description_template' ) );
|
|
$ai_title_prompt_value = $ai_title_prompt_saved !== '' ? $ai_title_prompt_saved : \services\OpenAiApi::get_default_title_prompt_template();
|
|
$ai_desc_prompt_value = $ai_desc_prompt_saved !== '' ? $ai_desc_prompt_saved : \services\OpenAiApi::get_default_description_prompt_template();
|
|
?>
|
|
<div class="settings-field">
|
|
<label for="ai_prompt_title_template">Prompt: nazwa produktu</label>
|
|
<textarea id="ai_prompt_title_template" name="ai_prompt_title_template" class="form-control" rows="8" style="height:auto;min-height:170px;" placeholder="Domyslny prompt zostanie uzyty, jesli pole jest puste."><?= htmlspecialchars( $ai_title_prompt_value ); ?></textarea>
|
|
<small class="text-muted">Dostepne placeholdery: <code>{{context}}</code>, <code>{{keyword_terms}}</code>. Gdy pominiesz placeholder, system dopnie dane automatycznie.</small>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="ai_prompt_description_template">Prompt: opis produktu</label>
|
|
<textarea id="ai_prompt_description_template" name="ai_prompt_description_template" class="form-control" rows="10" style="height:auto;min-height:210px;" placeholder="Domyslny prompt zostanie uzyty, jesli pole jest puste."><?= htmlspecialchars( $ai_desc_prompt_value ); ?></textarea>
|
|
<small class="text-muted">Dostepne placeholdery: <code>{{length_guide}}</code>, <code>{{context}}</code>, <code>{{keyword_terms}}</code>. Gdy pominiesz placeholder, system dopnie dane automatycznie.</small>
|
|
</div>
|
|
<button type="button" class="btn btn-success" onclick="$( '#ai-prompts-settings' ).submit();"><i class="fa-solid fa-check mr5"></i>Zapisz prompty AI</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<script type="text/javascript">
|
|
function password_toggle( btn, id )
|
|
{
|
|
var icon = btn.querySelector( 'i' );
|
|
var input = document.getElementById( id );
|
|
if ( !input || !icon )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( input.type === 'password' )
|
|
{
|
|
input.type = 'text';
|
|
icon.classList.remove( 'fa-eye' );
|
|
icon.classList.add( 'fa-eye-slash' );
|
|
}
|
|
else
|
|
{
|
|
input.type = 'password';
|
|
icon.classList.remove( 'fa-eye-slash' );
|
|
icon.classList.add( 'fa-eye' );
|
|
}
|
|
}
|
|
|
|
<?php if ( $settings_tab === 'cron' ): ?>
|
|
(function()
|
|
{
|
|
var refresh_interval_ms = 60000;
|
|
var refresh_timer = null;
|
|
var cron_status_url = '/settings/cron_status';
|
|
|
|
function set_text( selector, value )
|
|
{
|
|
var node = document.querySelector( selector );
|
|
if ( !node )
|
|
{
|
|
return;
|
|
}
|
|
node.textContent = value;
|
|
}
|
|
|
|
function render_progress( progress )
|
|
{
|
|
if ( !Array.isArray( progress ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
progress.forEach( function( item, index )
|
|
{
|
|
var container = document.querySelector( '[data-cron-progress-item="' + index + '"]' );
|
|
if ( !container )
|
|
{
|
|
return;
|
|
}
|
|
|
|
var processed = parseInt( item && item.processed ? item.processed : 0, 10 );
|
|
var total = parseInt( item && item.total ? item.total : 0, 10 );
|
|
var percent = parseInt( item && item.percent ? item.percent : 0, 10 );
|
|
if ( isNaN( processed ) ) processed = 0;
|
|
if ( isNaN( total ) ) total = 0;
|
|
if ( isNaN( percent ) ) percent = 0;
|
|
percent = Math.max( 0, Math.min( 100, percent ) );
|
|
|
|
var summary = container.querySelector( '[data-cron-progress-summary]' );
|
|
if ( summary )
|
|
{
|
|
summary.textContent = processed + ' / ' + total + ' (' + percent + '%)';
|
|
}
|
|
|
|
var fill = container.querySelector( '[data-cron-progress-fill]' );
|
|
if ( fill )
|
|
{
|
|
fill.style.width = percent + '%';
|
|
}
|
|
|
|
var meta = container.querySelector( '[data-cron-progress-meta]' );
|
|
if ( meta )
|
|
{
|
|
meta.textContent = item && item.meta ? item.meta : '';
|
|
}
|
|
} );
|
|
}
|
|
|
|
function render_urls( urls )
|
|
{
|
|
if ( !Array.isArray( urls ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
urls.forEach( function( row, index )
|
|
{
|
|
var container = document.querySelector( '[data-cron-url-item="' + index + '"]' );
|
|
if ( !container )
|
|
{
|
|
return;
|
|
}
|
|
|
|
var last_invoked = container.querySelector( '[data-cron-url-last-invoked]' );
|
|
if ( last_invoked )
|
|
{
|
|
last_invoked.textContent = row && row.last_invoked_at ? row.last_invoked_at : 'Brak danych';
|
|
}
|
|
|
|
var plan = container.querySelector( '[data-cron-url-plan]' );
|
|
if ( plan )
|
|
{
|
|
plan.textContent = row && row.plan ? row.plan : '';
|
|
}
|
|
} );
|
|
}
|
|
|
|
function render_cron_data( data )
|
|
{
|
|
if ( !data || typeof data !== 'object' )
|
|
{
|
|
return;
|
|
}
|
|
|
|
set_text( '[data-cron-overall-last-invoked]', data.overall_last_invoked_at || 'Brak danych' );
|
|
set_text( '[data-cron-clients-total]', data.clients_total || 0 );
|
|
set_text( '[data-cron-fb-clients-total]', data.facebook_clients_total || 0 );
|
|
render_progress( data.progress );
|
|
render_urls( data.urls );
|
|
}
|
|
|
|
function schedule_refresh()
|
|
{
|
|
if ( refresh_timer )
|
|
{
|
|
clearTimeout( refresh_timer );
|
|
}
|
|
refresh_timer = setTimeout( refresh_status, refresh_interval_ms );
|
|
}
|
|
|
|
function refresh_status()
|
|
{
|
|
if ( document.hidden )
|
|
{
|
|
schedule_refresh();
|
|
return;
|
|
}
|
|
|
|
if ( typeof window.fetch !== 'function' )
|
|
{
|
|
window.location.reload();
|
|
return;
|
|
}
|
|
|
|
fetch( cron_status_url + '?_ts=' + Date.now(), {
|
|
method: 'GET',
|
|
credentials: 'same-origin',
|
|
cache: 'no-store',
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
} )
|
|
.then( function( response )
|
|
{
|
|
if ( !response.ok )
|
|
{
|
|
throw new Error( 'HTTP ' + response.status );
|
|
}
|
|
return response.json();
|
|
} )
|
|
.then( function( payload )
|
|
{
|
|
if ( payload && payload.status === 'ok' )
|
|
{
|
|
render_cron_data( payload.data || {} );
|
|
}
|
|
} )
|
|
.catch( function()
|
|
{
|
|
} )
|
|
.finally( function()
|
|
{
|
|
schedule_refresh();
|
|
} );
|
|
}
|
|
|
|
document.addEventListener( 'visibilitychange', function()
|
|
{
|
|
if ( document.hidden )
|
|
{
|
|
if ( refresh_timer )
|
|
{
|
|
clearTimeout( refresh_timer );
|
|
}
|
|
return;
|
|
}
|
|
|
|
refresh_status();
|
|
} );
|
|
|
|
window.addEventListener( 'beforeunload', function()
|
|
{
|
|
if ( refresh_timer )
|
|
{
|
|
clearTimeout( refresh_timer );
|
|
}
|
|
} );
|
|
|
|
schedule_refresh();
|
|
})();
|
|
<?php endif; ?>
|
|
</script>
|
|
|