535 lines
28 KiB
PHP
535 lines
28 KiB
PHP
<?php
|
||
/**
|
||
* WBF_Abo — Abo-Verwaltung im Forum-Profil
|
||
* Fly-Abo: WordPress DB (wp_wis_fly_abo_subs)
|
||
* Plot-Abo/Slots: Spigot MySQL (optional)
|
||
*/
|
||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||
|
||
class WBF_Abo {
|
||
|
||
private static function get_spigot_db(): ?mysqli {
|
||
$s = function_exists('wbf_get_settings') ? wbf_get_settings() : [];
|
||
$host = $s['spigot_mysql_host'] ?? '';
|
||
$port = (int)($s['spigot_mysql_port'] ?? 3306);
|
||
$db = $s['spigot_mysql_database'] ?? '';
|
||
$user = $s['spigot_mysql_username'] ?? '';
|
||
$pass = $s['spigot_mysql_password'] ?? '';
|
||
if ( empty($db) || empty($user) ) return null;
|
||
$conn = @new mysqli($host, $user, $pass, $db, $port);
|
||
if ( $conn->connect_errno ) return null;
|
||
$conn->set_charset('utf8mb4');
|
||
return $conn;
|
||
}
|
||
|
||
public static function get_all_abos( string $mc_name ): array {
|
||
global $wpdb;
|
||
$result = ['fly_used_sec' => 0, 'plot_extra' => 0];
|
||
|
||
// Fly-Abo aus WordPress DB
|
||
$fly = $wpdb->get_row( $wpdb->prepare(
|
||
"SELECT label, price, status, cancelled, cancelled_at, expires_at, renewal_count
|
||
FROM {$wpdb->prefix}wis_fly_abo_subs
|
||
WHERE player_name = %s ORDER BY id DESC LIMIT 1",
|
||
$mc_name
|
||
), ARRAY_A );
|
||
|
||
if ( $fly ) {
|
||
$expires = new DateTime( $fly['expires_at'] );
|
||
$cancelled = (int)$fly['cancelled'] === 1;
|
||
$is_active = $fly['status'] === 'active' && $expires > new DateTime();
|
||
$result['fly_abo'] = [
|
||
'label' => $fly['label'],
|
||
'monthly_price' => (int)$fly['price'],
|
||
'cancelled' => $cancelled ? 1 : 0,
|
||
'cancellation_reason' => $cancelled ? 'user' : null,
|
||
'next_billing' => ( new DateTime('first day of next month') )->format('d.m.Y'),
|
||
'period_end' => $expires->format('d.m.Y'),
|
||
'is_active' => $is_active,
|
||
];
|
||
}
|
||
|
||
// Item-Abos aus WordPress DB (wis_item_abo_subs)
|
||
$item_abos = $wpdb->get_results( $wpdb->prepare(
|
||
"SELECT id, label, item_id, daily_qty, cancelled,
|
||
DATE_FORMAT(expires_at, '%%d.%%m.%%Y') AS expires_at_fmt
|
||
FROM {$wpdb->prefix}wis_item_abo_subs
|
||
WHERE player_name = %s
|
||
AND status = 'active'
|
||
AND expires_at > NOW()
|
||
ORDER BY created_at ASC",
|
||
$mc_name
|
||
), ARRAY_A );
|
||
|
||
if ( $item_abos ) {
|
||
foreach ( $item_abos as &$row ) {
|
||
$row['id'] = (int) $row['id'];
|
||
$row['daily_qty'] = (int) $row['daily_qty'];
|
||
$row['cancelled'] = (bool) $row['cancelled'];
|
||
}
|
||
unset($row);
|
||
$result['item_abos'] = $item_abos;
|
||
} else {
|
||
$result['item_abos'] = [];
|
||
}
|
||
|
||
// Spigot MySQL für Fly-Usage + Plot-Daten
|
||
$conn = self::get_spigot_db();
|
||
if ( $conn ) {
|
||
foreach ([
|
||
["SELECT used_sec FROM wis_fly_abo_usage WHERE player_name = ? AND usage_date = CURDATE()", 'fly_used_sec', 'used_sec'],
|
||
] as [$sql, $key, $field]) {
|
||
$stmt = $conn->prepare($sql);
|
||
if ($stmt) { $stmt->bind_param('s', $mc_name); $stmt->execute();
|
||
$r = $stmt->get_result()->fetch_assoc();
|
||
$result[$key] = $r ? (int)$r[$field] : 0; $stmt->close(); }
|
||
}
|
||
$stmt = $conn->prepare(
|
||
"SELECT label, abo_slots, monthly_price, cancelled, cancellation_reason,
|
||
DATE_FORMAT(next_billing_date,'%d.%m.%Y') AS next_billing,
|
||
DATE_FORMAT(period_end,'%d.%m.%Y') AS period_end
|
||
FROM wis_plot_abos WHERE player_name = ? AND period_end >= CURDATE()"
|
||
);
|
||
if ($stmt) { $stmt->bind_param('s', $mc_name); $stmt->execute();
|
||
$r = $stmt->get_result()->fetch_assoc();
|
||
if ($r) $result['plot_abo'] = $r; $stmt->close(); }
|
||
$stmt = $conn->prepare("SELECT extra_slots FROM wis_plot_extra_slots WHERE player_name = ?");
|
||
if ($stmt) { $stmt->bind_param('s', $mc_name); $stmt->execute();
|
||
$r = $stmt->get_result()->fetch_assoc();
|
||
$result['plot_extra'] = $r ? (int)$r['extra_slots'] : 0; $stmt->close(); }
|
||
$conn->close();
|
||
}
|
||
return $result;
|
||
}
|
||
|
||
public static function cancel_abo( string $mc_name, string $type, int $abo_id = 0 ): bool {
|
||
global $wpdb;
|
||
if ( $type === 'fly_abo' ) {
|
||
$rows = $wpdb->update(
|
||
$wpdb->prefix . 'wis_fly_abo_subs',
|
||
['cancelled' => 1, 'cancelled_at' => current_time('mysql')],
|
||
['player_name' => $mc_name, 'status' => 'active', 'cancelled' => 0]
|
||
);
|
||
return $rows > 0;
|
||
}
|
||
if ( $type === 'plot_abo' ) {
|
||
$conn = self::get_spigot_db();
|
||
if (!$conn) return false;
|
||
$stmt = $conn->prepare("UPDATE wis_plot_abos SET cancelled=1, cancellation_reason='user', period_end=LAST_DAY(CURDATE()) WHERE player_name=? AND period_end>=CURDATE() AND cancelled=0");
|
||
if (!$stmt) { $conn->close(); return false; }
|
||
$stmt->bind_param('s', $mc_name); $stmt->execute();
|
||
$a = $stmt->affected_rows; $stmt->close(); $conn->close();
|
||
return $a > 0;
|
||
}
|
||
if ( $type === 'item_abo' ) {
|
||
if ( $abo_id <= 0 ) return false;
|
||
// Sicherheitscheck: Abo muss dem Spieler gehören
|
||
$existing = $wpdb->get_row( $wpdb->prepare(
|
||
"SELECT id, label FROM {$wpdb->prefix}wis_item_abo_subs
|
||
WHERE id = %d AND player_name = %s AND status = 'active' AND cancelled = 0",
|
||
$abo_id, $mc_name
|
||
) );
|
||
if ( ! $existing ) return false;
|
||
$rows = $wpdb->update(
|
||
$wpdb->prefix . 'wis_item_abo_subs',
|
||
['cancelled' => 1, 'cancelled_at' => current_time('mysql')],
|
||
['id' => $abo_id]
|
||
);
|
||
return $rows > 0;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
public static function format_seconds( int $sec ): string {
|
||
if ($sec >= 3600) { $h = intdiv($sec,3600); $m = intdiv($sec%3600,60); return $h.'h'.($m>0?' '.$m.'min':''); }
|
||
if ($sec >= 60) { return intdiv($sec,60).'min'; }
|
||
return $sec.'s';
|
||
}
|
||
|
||
public static function render_tab( string $mc_name, int $profile_id ): string {
|
||
$abos = self::get_all_abos($mc_name);
|
||
$fly_abo = $abos['fly_abo'] ?? null;
|
||
$plot_abo = $abos['plot_abo'] ?? null;
|
||
$item_abos = $abos['item_abos'] ?? [];
|
||
$plot_extra = (int)($abos['plot_extra'] ?? 0);
|
||
$fly_used = (int)($abos['fly_used_sec'] ?? 0);
|
||
$fly_max = (int) get_option('wis_fly_abo_max_daily_hours', 6) * 3600;
|
||
$currency = esc_html(get_option('wis_currency_name', '$'));
|
||
$shop_url = esc_url(get_option('wis_shop_url', home_url('/ingame-shop/')));
|
||
$nonce = wp_create_nonce('wbf_nonce');
|
||
$ajax_url = admin_url('admin-ajax.php');
|
||
$has_any = $fly_abo || $plot_abo || $plot_extra > 0 || !empty($item_abos);
|
||
|
||
ob_start(); ?>
|
||
<style>
|
||
.wbf-abo-wrap { display:flex; flex-direction:column; gap:1rem; }
|
||
.wbf-abo-empty { text-align:center; padding:2.5rem 1rem; color:var(--c-muted); }
|
||
.wbf-abo-empty__icon { font-size:2.5rem; margin-bottom:.5rem; }
|
||
.wbf-abo-item { border-radius:12px; overflow:hidden; border:1px solid var(--c-border); background:var(--c-bg); }
|
||
.wbf-abo-item--active { border-left:4px solid var(--c-success); }
|
||
.wbf-abo-item--cancelled { border-left:4px solid var(--c-danger); opacity:.9; }
|
||
.wbf-abo-item--info { border-left:4px solid var(--c-primary); }
|
||
.wbf-abo-item__head { display:flex; align-items:center; gap:.9rem; padding:.85rem 1.1rem; background:var(--c-bg-alt); border-bottom:1px solid var(--c-border); }
|
||
.wbf-abo-item__icon-wrap { flex-shrink:0; width:2.4rem; height:2.4rem; border-radius:50%; background:var(--c-border); display:flex; align-items:center; justify-content:center; font-size:1.15rem; }
|
||
.wbf-abo-item__info { flex:1; min-width:0; }
|
||
.wbf-abo-item__name { font-weight:700; font-size:.95rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||
.wbf-abo-item__price { font-size:.8rem; color:var(--c-muted); margin-top:.1rem; }
|
||
.wbf-abo-badge { display:inline-flex; align-items:center; gap:.3rem; font-size:.75rem; font-weight:600; padding:.22rem .65rem; border-radius:20px; white-space:nowrap; }
|
||
.wbf-abo-badge--active { background:rgba(34,197,94,.15); color:var(--c-success); }
|
||
.wbf-abo-badge--cancelled { background:rgba(239,68,68,.13); color:var(--c-danger); }
|
||
.wbf-abo-badge--expired { background:rgba(100,100,100,.15); color:var(--c-muted); }
|
||
.wbf-abo-item__body { padding:.85rem 1.1rem; }
|
||
.wbf-abo-grid { display:grid; grid-template-columns:1fr 1fr; gap:.45rem .9rem; }
|
||
.wbf-abo-grid__item--full { grid-column:1/-1; }
|
||
.wbf-abo-grid__label { display:block; font-size:.78rem; color:var(--c-muted); margin-bottom:.15rem; }
|
||
.wbf-abo-grid__value { font-size:.88rem; font-weight:500; }
|
||
.wbf-abo-grid__value--warn { color:var(--c-danger); }
|
||
.wbf-abo-progress { margin-top:.75rem; }
|
||
.wbf-abo-progress__track { height:6px; background:var(--c-border); border-radius:3px; overflow:hidden; }
|
||
.wbf-abo-progress__fill { height:100%; border-radius:3px; transition:width .4s; }
|
||
.wbf-abo-progress__labels { display:flex; justify-content:space-between; font-size:.73rem; color:var(--c-muted); margin-top:.25rem; }
|
||
.wbf-abo-item__foot { display:flex; align-items:center; gap:.75rem; flex-wrap:wrap; padding:.7rem 1.1rem; border-top:1px solid var(--c-border); background:var(--c-bg-alt); }
|
||
.wbf-abo-item__foot--cancelled { background:transparent; }
|
||
.wbf-abo-item__hint { font-size:.78rem; color:var(--c-muted); }
|
||
.wbf-abo-notice { display:flex; align-items:flex-start; gap:.45rem; font-size:.83rem; padding:.5rem .75rem; border-radius:8px; width:100%; }
|
||
.wbf-abo-notice--info { background:rgba(99,102,241,.1); color:var(--c-primary); }
|
||
/* Modal */
|
||
.wbf-abo-modal-overlay { position:fixed; inset:0; z-index:9999; display:flex; align-items:center; justify-content:center; background:rgba(0,0,0,.55); backdrop-filter:blur(3px); }
|
||
.wbf-abo-modal { background:var(--c-bg); border:1px solid var(--c-border); border-radius:14px; width:min(420px,90vw); box-shadow:0 12px 40px rgba(0,0,0,.35); overflow:hidden; }
|
||
.wbf-abo-modal__head { display:flex; align-items:center; gap:.5rem; padding:.9rem 1.2rem; background:var(--c-bg-alt); border-bottom:1px solid var(--c-border); font-weight:700; color:var(--c-danger); }
|
||
.wbf-abo-modal__body { padding:1.1rem 1.2rem; line-height:1.6; }
|
||
.wbf-abo-modal__sub { font-size:.87rem; color:var(--c-muted); margin-top:.5rem; }
|
||
.wbf-abo-modal__foot { display:flex; justify-content:flex-end; gap:.6rem; padding:.8rem 1.2rem; background:var(--c-bg-alt); border-top:1px solid var(--c-border); }
|
||
@media(max-width:540px) { .wbf-abo-grid { grid-template-columns:1fr; } .wbf-abo-grid__item--full { grid-column:1; } }
|
||
</style>
|
||
|
||
<div class="wbf-abo-wrap">
|
||
<?php if (!$has_any): ?>
|
||
<div class="wbf-abo-empty">
|
||
<div class="wbf-abo-empty__icon">📋</div>
|
||
<p>Du hast derzeit keine aktiven Abonnements.</p>
|
||
<a href="<?php echo $shop_url; ?>" target="_blank" class="wbf-btn wbf-btn--primary wbf-btn--sm">
|
||
<i class="fas fa-shopping-cart"></i> Zum Shop
|
||
</a>
|
||
</div>
|
||
<?php else: ?>
|
||
|
||
<?php /* FLY-ABO */ if ($fly_abo):
|
||
$fa = $fly_abo;
|
||
$fcancelled = (int)($fa['cancelled'] ?? 0) === 1;
|
||
$factive = !empty($fa['is_active']) && !$fcancelled;
|
||
$fly_rem = max(0, $fly_max - $fly_used);
|
||
$fly_pct = $fly_max > 0 ? min(100, round(($fly_used / $fly_max) * 100)) : 0;
|
||
$pbar_col = $fly_pct >= 90 ? 'var(--c-danger)' : ($fly_pct >= 60 ? '#f59e0b' : 'var(--c-success)');
|
||
?>
|
||
<div class="wbf-abo-item <?php echo $fcancelled ? 'wbf-abo-item--cancelled' : 'wbf-abo-item--active'; ?>" id="wbf-abo-fly">
|
||
<div class="wbf-abo-item__head">
|
||
<div class="wbf-abo-item__icon-wrap">✈</div>
|
||
<div class="wbf-abo-item__info">
|
||
<div class="wbf-abo-item__name"><?php echo esc_html($fa['label']); ?></div>
|
||
<div class="wbf-abo-item__price"><?php echo number_format((int)$fa['monthly_price']); ?> <?php echo $currency; ?> / Monat</div>
|
||
</div>
|
||
<div class="wbf-abo-item__status">
|
||
<?php if ($fcancelled): ?>
|
||
<span class="wbf-abo-badge wbf-abo-badge--cancelled"><i class="fas fa-clock"></i> Gekündigt</span>
|
||
<?php elseif ($factive): ?>
|
||
<span class="wbf-abo-badge wbf-abo-badge--active"><i class="fas fa-circle-check"></i> Aktiv</span>
|
||
<?php else: ?>
|
||
<span class="wbf-abo-badge wbf-abo-badge--expired"><i class="fas fa-ban"></i> Abgelaufen</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="wbf-abo-item__body">
|
||
<div class="wbf-abo-grid">
|
||
<div class="wbf-abo-grid__item">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-calendar-day"></i> Aktiv bis</span>
|
||
<span class="wbf-abo-grid__value <?php echo $fcancelled ? 'wbf-abo-grid__value--warn' : ''; ?>"><?php echo esc_html($fa['period_end']); ?></span>
|
||
</div>
|
||
<?php if ($factive): ?>
|
||
<div class="wbf-abo-grid__item">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-rotate"></i> Nächste Abbuchung</span>
|
||
<span class="wbf-abo-grid__value"><?php echo esc_html($fa['next_billing']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
<div class="wbf-abo-grid__item wbf-abo-grid__item--full">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-clock"></i> Fly heute</span>
|
||
<span class="wbf-abo-grid__value">
|
||
<?php echo self::format_seconds($fly_used); ?> / <?php echo self::format_seconds($fly_max); ?>
|
||
<span style="color:<?php echo $fly_rem > 0 ? 'var(--c-success)' : 'var(--c-danger)'; ?>;font-weight:600">(noch <?php echo self::format_seconds($fly_rem); ?>)</span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="wbf-abo-progress">
|
||
<div class="wbf-abo-progress__track">
|
||
<div class="wbf-abo-progress__fill" style="width:<?php echo $fly_pct; ?>%;background:<?php echo $pbar_col; ?>"></div>
|
||
</div>
|
||
<div class="wbf-abo-progress__labels"><span><?php echo $fly_pct; ?>% verbraucht</span><span>Limit: <?php echo self::format_seconds($fly_max); ?>/Tag</span></div>
|
||
</div>
|
||
</div>
|
||
<?php if ($factive): ?>
|
||
<div class="wbf-abo-item__foot">
|
||
<button class="wbf-btn wbf-btn--danger wbf-btn--sm wbf-abo-cancel-btn"
|
||
data-type="fly_abo" data-nonce="<?php echo $nonce; ?>"
|
||
data-label="<?php echo esc_attr($fa['label']); ?>"
|
||
data-period="<?php echo esc_attr($fa['period_end']); ?>">
|
||
<i class="fas fa-xmark"></i> Zum Monatsende kündigen
|
||
</button>
|
||
<span class="wbf-abo-item__hint">Bleibt bis <?php echo esc_html($fa['period_end']); ?> aktiv</span>
|
||
</div>
|
||
<?php elseif ($fcancelled): ?>
|
||
<div class="wbf-abo-item__foot wbf-abo-item__foot--cancelled">
|
||
<div class="wbf-abo-notice wbf-abo-notice--info">
|
||
<i class="fas fa-circle-info"></i>
|
||
Kündigung vorgemerkt – Fly-Abo bleibt bis <strong><?php echo esc_html($fa['period_end']); ?></strong> aktiv.
|
||
</div>
|
||
<a href="<?php echo $shop_url; ?>" target="_blank" class="wbf-btn wbf-btn--primary wbf-btn--sm" style="white-space:nowrap">
|
||
<i class="fas fa-rotate-right"></i> Erneut abonnieren
|
||
</a>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; // fly_abo ?>
|
||
|
||
<?php /* PLOT-ABO */ if ($plot_abo):
|
||
$pa = $plot_abo;
|
||
$pcancelled = (int)($pa['cancelled'] ?? 0) === 1;
|
||
$ppayfail = ($pa['cancellation_reason'] ?? '') === 'payment_failed';
|
||
?>
|
||
<div class="wbf-abo-item <?php echo $pcancelled ? 'wbf-abo-item--cancelled' : 'wbf-abo-item--active'; ?>" id="wbf-abo-plot">
|
||
<div class="wbf-abo-item__head">
|
||
<div class="wbf-abo-item__icon-wrap">📦</div>
|
||
<div class="wbf-abo-item__info">
|
||
<div class="wbf-abo-item__name"><?php echo esc_html($pa['label']); ?></div>
|
||
<div class="wbf-abo-item__price">+<?php echo (int)$pa['abo_slots']; ?> Slots · <?php echo (int)$pa['monthly_price']; ?> <?php echo $currency; ?>/Monat</div>
|
||
</div>
|
||
<div class="wbf-abo-item__status">
|
||
<?php if ($pcancelled && $ppayfail): ?>
|
||
<span class="wbf-abo-badge wbf-abo-badge--cancelled"><i class="fas fa-exclamation-triangle"></i> Zahlung fehlgeschlagen</span>
|
||
<?php elseif ($pcancelled): ?>
|
||
<span class="wbf-abo-badge wbf-abo-badge--cancelled"><i class="fas fa-clock"></i> Gekündigt</span>
|
||
<?php else: ?>
|
||
<span class="wbf-abo-badge wbf-abo-badge--active"><i class="fas fa-circle-check"></i> Aktiv</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="wbf-abo-item__body">
|
||
<div class="wbf-abo-grid">
|
||
<div class="wbf-abo-grid__item">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-calendar-day"></i> Aktiv bis</span>
|
||
<span class="wbf-abo-grid__value"><?php echo esc_html($pa['period_end']); ?></span>
|
||
</div>
|
||
<?php if (!$pcancelled): ?>
|
||
<div class="wbf-abo-grid__item">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-rotate"></i> Nächste Abbuchung</span>
|
||
<span class="wbf-abo-grid__value"><?php echo esc_html($pa['next_billing']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php if (!$pcancelled): ?>
|
||
<div class="wbf-abo-item__foot">
|
||
<button class="wbf-btn wbf-btn--danger wbf-btn--sm wbf-abo-cancel-btn"
|
||
data-type="plot_abo" data-nonce="<?php echo $nonce; ?>"
|
||
data-label="<?php echo esc_attr($pa['label']); ?>"
|
||
data-period="<?php echo esc_attr($pa['period_end']); ?>">
|
||
<i class="fas fa-xmark"></i> Zum Monatsende kündigen
|
||
</button>
|
||
<span class="wbf-abo-item__hint">Bleibt bis <?php echo esc_html($pa['period_end']); ?> aktiv</span>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="wbf-abo-item__foot wbf-abo-item__foot--cancelled">
|
||
<div class="wbf-abo-notice wbf-abo-notice--info">
|
||
<i class="fas fa-circle-info"></i>
|
||
Kündigung vorgemerkt – Plot-Abo bleibt bis <strong><?php echo esc_html($pa['period_end']); ?></strong> aktiv.
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; // plot_abo ?>
|
||
|
||
<?php /* ITEM-ABOS */ foreach ($item_abos as $idx => $ia):
|
||
$ia_nr = $idx + 1;
|
||
$ia_id = (int) $ia['id'];
|
||
$ia_label = $ia['label'] ?? 'Item-Abo';
|
||
$ia_item = $ia['item_id'] ?? '?';
|
||
$ia_qty = (int)($ia['daily_qty'] ?? 1);
|
||
$ia_expires = $ia['expires_at_fmt'] ?? '?';
|
||
$ia_cancelled = !empty($ia['cancelled']);
|
||
?>
|
||
<div class="wbf-abo-item <?php echo $ia_cancelled ? 'wbf-abo-item--cancelled' : 'wbf-abo-item--active'; ?>" id="wbf-abo-item-<?php echo $ia_id; ?>">
|
||
<div class="wbf-abo-item__head">
|
||
<div class="wbf-abo-item__icon-wrap">📦</div>
|
||
<div class="wbf-abo-item__info">
|
||
<div class="wbf-abo-item__name"><?php echo esc_html($ia_label); ?></div>
|
||
<div class="wbf-abo-item__price"><?php echo $ia_qty; ?>× täglich · <code style="font-size:.78rem;opacity:.8"><?php echo esc_html($ia_item); ?></code></div>
|
||
</div>
|
||
<div class="wbf-abo-item__status">
|
||
<?php if ($ia_cancelled): ?>
|
||
<span class="wbf-abo-badge wbf-abo-badge--cancelled"><i class="fas fa-clock"></i> Gekündigt</span>
|
||
<?php else: ?>
|
||
<span class="wbf-abo-badge wbf-abo-badge--active"><i class="fas fa-circle-check"></i> Aktiv</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="wbf-abo-item__body">
|
||
<div class="wbf-abo-grid">
|
||
<div class="wbf-abo-grid__item">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-calendar-day"></i> Läuft bis</span>
|
||
<span class="wbf-abo-grid__value <?php echo $ia_cancelled ? 'wbf-abo-grid__value--warn' : ''; ?>"><?php echo esc_html($ia_expires); ?></span>
|
||
</div>
|
||
<div class="wbf-abo-grid__item">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-box-open"></i> Tageslieferung</span>
|
||
<span class="wbf-abo-grid__value"><?php echo $ia_qty; ?>× <?php echo esc_html($ia_item); ?></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php if (!$ia_cancelled): ?>
|
||
<div class="wbf-abo-item__foot">
|
||
<button class="wbf-btn wbf-btn--danger wbf-btn--sm wbf-abo-cancel-btn"
|
||
data-type="item_abo"
|
||
data-abo-id="<?php echo $ia_id; ?>"
|
||
data-nonce="<?php echo $nonce; ?>"
|
||
data-label="<?php echo esc_attr($ia_label); ?>"
|
||
data-period="<?php echo esc_attr($ia_expires); ?>">
|
||
<i class="fas fa-xmark"></i> Kündigen
|
||
</button>
|
||
<span class="wbf-abo-item__hint">Bleibt bis <?php echo esc_html($ia_expires); ?> aktiv</span>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="wbf-abo-item__foot wbf-abo-item__foot--cancelled">
|
||
<div class="wbf-abo-notice wbf-abo-notice--info">
|
||
<i class="fas fa-circle-info"></i>
|
||
Kündigung vorgemerkt – Lieferungen laufen bis <strong><?php echo esc_html($ia_expires); ?></strong> weiter.
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endforeach; // item_abos ?>
|
||
|
||
<?php if ($plot_extra > 0 || $plot_abo): ?>
|
||
<div class="wbf-abo-item__head">
|
||
<div class="wbf-abo-item__icon-wrap">🗺️</div>
|
||
<div class="wbf-abo-item__info">
|
||
<div class="wbf-abo-item__name">Plot-Slots Übersicht</div>
|
||
<div class="wbf-abo-item__price">Citybuild</div>
|
||
</div>
|
||
</div>
|
||
<div class="wbf-abo-item__body">
|
||
<div class="wbf-abo-grid">
|
||
<?php if ($plot_extra > 0): ?>
|
||
<div class="wbf-abo-grid__item">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-infinity"></i> Permanent gekauft</span>
|
||
<span class="wbf-abo-grid__value">+<?php echo $plot_extra; ?> Slots</span>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php if ($plot_abo): ?>
|
||
<div class="wbf-abo-grid__item">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-rotate"></i> Abo-Slots</span>
|
||
<span class="wbf-abo-grid__value <?php echo $pcancelled ? 'wbf-abo-grid__value--warn' : ''; ?>">
|
||
+<?php echo (int)$pa['abo_slots']; ?> Slots<?php echo $pcancelled ? ' (läuft aus)' : ''; ?>
|
||
</span>
|
||
</div>
|
||
<?php endif; ?>
|
||
<div class="wbf-abo-grid__item wbf-abo-grid__item--full" style="border-top:1px solid var(--c-border);margin-top:.4em;padding-top:.6em">
|
||
<span class="wbf-abo-grid__label"><i class="fas fa-layer-group"></i> Gesamt Zusatz-Slots</span>
|
||
<span class="wbf-abo-grid__value" style="color:var(--c-success);font-weight:700;font-size:1.05em">
|
||
+<?php echo $plot_extra + ($plot_abo ? (int)$pa['abo_slots'] : 0); ?> Slots
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php endif; // has_any ?>
|
||
</div><!-- .wbf-abo-wrap -->
|
||
|
||
<!-- Bestätigungs-Modal -->
|
||
<div id="wbf-abo-modal" class="wbf-abo-modal-overlay" style="display:none" role="dialog" aria-modal="true">
|
||
<div class="wbf-abo-modal">
|
||
<div class="wbf-abo-modal__head"><i class="fas fa-triangle-exclamation"></i> Abo kündigen</div>
|
||
<div class="wbf-abo-modal__body">
|
||
<p>Möchtest du <strong id="wbf-abo-modal-label"></strong> wirklich zum Monatsende kündigen?</p>
|
||
<p class="wbf-abo-modal__sub">Das Abo bleibt bis <strong id="wbf-abo-modal-period"></strong> aktiv. Danach werden die Vorteile automatisch deaktiviert.</p>
|
||
</div>
|
||
<div class="wbf-abo-modal__foot">
|
||
<button class="wbf-btn wbf-btn--ghost wbf-btn--sm" id="wbf-abo-modal-no">Abbrechen</button>
|
||
<button class="wbf-btn wbf-btn--danger wbf-btn--sm" id="wbf-abo-modal-yes"><i class="fas fa-xmark"></i> Ja, kündigen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var AJAX = <?php echo json_encode($ajax_url); ?>;
|
||
var p = {type:null, nonce:null, btn:null, aboId:0};
|
||
var modal = document.getElementById('wbf-abo-modal');
|
||
var yesBtn = document.getElementById('wbf-abo-modal-yes');
|
||
var noBtn = document.getElementById('wbf-abo-modal-no');
|
||
|
||
document.querySelectorAll('.wbf-abo-cancel-btn').forEach(function(b) {
|
||
b.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
p = {type:b.dataset.type, nonce:b.dataset.nonce, btn:b, aboId:parseInt(b.dataset.aboId||'0',10)};
|
||
document.getElementById('wbf-abo-modal-label').textContent = b.dataset.label || 'dieses Abo';
|
||
document.getElementById('wbf-abo-modal-period').textContent = b.dataset.period || '';
|
||
modal.style.display = 'flex';
|
||
yesBtn.disabled = false;
|
||
yesBtn.innerHTML = '<i class="fas fa-xmark"></i> Ja, kündigen';
|
||
});
|
||
});
|
||
|
||
function closeModal() { modal.style.display = 'none'; }
|
||
noBtn.addEventListener('click', closeModal);
|
||
modal.addEventListener('click', function(e) { if(e.target===modal) closeModal(); });
|
||
|
||
yesBtn.addEventListener('click', function() {
|
||
yesBtn.disabled = true;
|
||
yesBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Kündigen…';
|
||
closeModal();
|
||
|
||
var params = {action:'wbf_cancel_abo', nonce:p.nonce, type:p.type};
|
||
if (p.type === 'item_abo' && p.aboId > 0) params.abo_id = p.aboId;
|
||
|
||
fetch(AJAX, {
|
||
method:'POST',
|
||
headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
||
body: new URLSearchParams(params).toString()
|
||
})
|
||
.then(function(r){return r.json();})
|
||
.then(function(res) {
|
||
if (res.success) {
|
||
var card;
|
||
if (p.type === 'item_abo') {
|
||
card = p.aboId > 0 ? document.getElementById('wbf-abo-item-'+p.aboId) : null;
|
||
} else {
|
||
card = document.getElementById(p.type==='fly_abo'?'wbf-abo-fly':'wbf-abo-plot');
|
||
}
|
||
if (card) {
|
||
card.classList.replace('wbf-abo-item--active','wbf-abo-item--cancelled');
|
||
var badge = card.querySelector('.wbf-abo-badge');
|
||
if (badge) { badge.className='wbf-abo-badge wbf-abo-badge--cancelled'; badge.innerHTML='<i class="fas fa-clock"></i> Gekündigt'; }
|
||
var foot = card.querySelector('.wbf-abo-item__foot');
|
||
if (foot) {
|
||
foot.className = 'wbf-abo-item__foot wbf-abo-item__foot--cancelled';
|
||
foot.innerHTML = '<div class="wbf-abo-notice wbf-abo-notice--info"><i class="fas fa-circle-info"></i> Kündigung vorgemerkt – Abo bleibt bis <strong>'+(p.btn?p.btn.dataset.period:'')+'</strong> aktiv.</div>';
|
||
}
|
||
card.querySelectorAll('.wbf-abo-grid__item').forEach(function(row){
|
||
if(row.textContent.includes('Abbuchung')) row.style.display='none';
|
||
});
|
||
}
|
||
} else {
|
||
alert((res.data&&res.data.message)?res.data.message:'Fehler beim Kündigen.');
|
||
}
|
||
yesBtn.disabled=false;
|
||
yesBtn.innerHTML='<i class="fas fa-xmark"></i> Ja, kündigen';
|
||
p={type:null,nonce:null,btn:null,aboId:0};
|
||
})
|
||
.catch(function(){ alert('Verbindungsfehler. Bitte ingame kündigen.'); yesBtn.disabled=false; p={type:null,nonce:null,btn:null,aboId:0}; });
|
||
});
|
||
});
|
||
</script>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
}
|