1100 lines
31 KiB
PHP
1100 lines
31 KiB
PHP
<?php
|
||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/session_bootstrap.php';
|
||
if (empty($_SESSION['logged_in'])) {
|
||
header('Location: https://togethere.cloud/login/');
|
||
exit();
|
||
}
|
||
|
||
$host = "localhost";
|
||
$db = "togethere_cloud";
|
||
$user = "root";
|
||
$pass = "HasloDoSQL";
|
||
|
||
try {
|
||
$pdo = og_session_get_pdo();
|
||
if (!$pdo instanceof PDO) {
|
||
throw new PDOException('Nie udało się zainicjalizować połączenia z bazą danych.');
|
||
}
|
||
} catch (PDOException $e) {
|
||
die("Błąd połączenia z bazą danych: " . $e->getMessage());
|
||
}
|
||
|
||
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
|
||
$stmt->execute([$_SESSION['user_id']]);
|
||
$userData = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
$phoneCountryOptions = [
|
||
'+48' => 'Polska (+48)',
|
||
'+44' => 'Wielka Brytania (+44)',
|
||
'+49' => 'Niemcy (+49)',
|
||
'+33' => 'Francja (+33)',
|
||
'+34' => 'Hiszpania (+34)',
|
||
'+39' => 'Włochy (+39)',
|
||
'+31' => 'Holandia (+31)',
|
||
'+420' => 'Czechy (+420)',
|
||
'+421' => 'Słowacja (+421)',
|
||
'+1' => 'USA/Kanada (+1)'
|
||
];
|
||
$storedPhoneNumber = trim((string)($userData['phone_number'] ?? ''));
|
||
$currentPhoneCountryCode = '';
|
||
$currentPhoneNumber = $storedPhoneNumber;
|
||
if ($storedPhoneNumber !== '' && preg_match('/^(\+\d{1,4})\s*(.*)$/', $storedPhoneNumber, $matches)) {
|
||
$parsedCode = trim((string)$matches[1]);
|
||
$parsedLocal = trim((string)$matches[2]);
|
||
if (array_key_exists($parsedCode, $phoneCountryOptions)) {
|
||
$currentPhoneCountryCode = $parsedCode;
|
||
$currentPhoneNumber = $parsedLocal;
|
||
}
|
||
}
|
||
|
||
if (!$userData) {
|
||
session_destroy();
|
||
header('Location: /login/');
|
||
exit();
|
||
}
|
||
|
||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/account_suspension.php';
|
||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/user_avatar.php';
|
||
$suspensionState = og_is_current_user_suspended($pdo);
|
||
$isSuspended = (bool)($suspensionState['is_suspended'] ?? false);
|
||
$suspendedReason = (string)($suspensionState['reason'] ?? '');
|
||
$suspendedUntil = (string)($suspensionState['suspended_until'] ?? '');
|
||
$profileFormDisabled = $isSuspended ? 'disabled' : '';
|
||
|
||
$avatarFile = og_get_user_avatar_file($pdo, (int)($_SESSION['user_id'] ?? 0));
|
||
if (!$avatarFile && !empty($_SESSION['profile_avatar_file'])) {
|
||
$avatarFile = trim((string)$_SESSION['profile_avatar_file']);
|
||
}
|
||
$avatarUrl = og_avatar_file_to_url($avatarFile);
|
||
$avatarInitial = og_avatar_initial((string)($userData['username'] ?? 'U'));
|
||
$displayFirstName = trim((string)($userData['first_name'] ?? ''));
|
||
$displayLastName = trim((string)($userData['last_name'] ?? ''));
|
||
$displayFullName = trim($displayFirstName . ' ' . $displayLastName);
|
||
if ($displayFullName === '') {
|
||
$displayFullName = (string)($userData['username'] ?? 'Użytkownik');
|
||
}
|
||
$displayNickname = (string)($userData['username'] ?? '');
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>Informacje Profilowe | kontakt: wspolpraca@togethere.cloud</title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta charset="utf-8">
|
||
<meta name="keywords" content="projekty przyszłości"/>
|
||
<link rel="stylesheet" href="/css/header.css" type="text/css" media="all"/>
|
||
<link rel="stylesheet" href="/css/footer.css" type="text/css" media="all"/>
|
||
<link href="//fonts.googleapis.com/css?family=Lato:400,500,600,700,800,900" rel="stylesheet">
|
||
<style>
|
||
body {
|
||
background: linear-gradient(135deg, #e3f2fd 0%, #ffffff 100%);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
h1 {
|
||
color: #1976d2;
|
||
padding: 30px;
|
||
margin-bottom: 20px;
|
||
text-align: center;
|
||
font-size: 2.5em;
|
||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.nav-link {
|
||
display: inline-block;
|
||
margin: 0 auto 30px;
|
||
padding: 12px 30px;
|
||
background: linear-gradient(135deg, #42a5f5, #1976d2);
|
||
color: white;
|
||
text-decoration: none;
|
||
border-radius: 25px;
|
||
font-weight: 600;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 4px 15px rgba(25, 118, 210, 0.3);
|
||
}
|
||
|
||
.nav-link:hover {
|
||
background: linear-gradient(135deg, #1976d2, #0d47a1);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 20px rgba(25, 118, 210, 0.4);
|
||
}
|
||
|
||
.nav-container {
|
||
display: flex;
|
||
width: 100%;
|
||
text-align: center;
|
||
justify-content: center;
|
||
align-items: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.nav-container .box {
|
||
display: flex;
|
||
gap: 15px;
|
||
}
|
||
|
||
nav.navigation {
|
||
margin-top: 0px !important;
|
||
}
|
||
|
||
.settings-container {
|
||
max-width: 100%;
|
||
width: 100%;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
}
|
||
|
||
.settings-section {
|
||
background: white;
|
||
border-radius: 15px;
|
||
padding: 35px;
|
||
margin-bottom: 30px;
|
||
box-shadow: 0 10px 30px rgba(100, 181, 246, 0.2);
|
||
width: 100%;
|
||
max-width: 100%;
|
||
}
|
||
|
||
.settings-section h2 {
|
||
color: #1976d2;
|
||
font-size: 1.8em;
|
||
margin-bottom: 25px;
|
||
padding-bottom: 15px;
|
||
border-bottom: 3px solid #64b5f6;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 25px;
|
||
width: 100% !important;
|
||
}
|
||
|
||
.form-group.highlight-target {
|
||
padding: 16px;
|
||
border-radius: 12px;
|
||
border: 2px solid #ff9800;
|
||
background: linear-gradient(180deg, rgba(255, 152, 0, 0.1) 0%, rgba(255, 152, 0, 0.03) 100%);
|
||
box-shadow: 0 8px 24px rgba(255, 152, 0, 0.12);
|
||
}
|
||
|
||
.form-help-callout {
|
||
margin-top: 10px;
|
||
padding: 12px 14px;
|
||
border-radius: 10px;
|
||
background: #fff3cd;
|
||
border-left: 4px solid #ff9800;
|
||
color: #7a4b00;
|
||
line-height: 1.6;
|
||
font-size: 0.95em;
|
||
}
|
||
|
||
form div label {
|
||
padding-left: 5px !important;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
color: #2c3e50;
|
||
font-weight: 600;
|
||
margin-bottom: 10px;
|
||
font-size: 1.05em;
|
||
}
|
||
|
||
.form-group input[type="text"],
|
||
.form-group input[type="email"],
|
||
.form-group select {
|
||
width: 100% !important;
|
||
max-width: 100% !important;
|
||
padding: 15px;
|
||
border: 2px solid #64b5f6;
|
||
border-radius: 8px;
|
||
font-size: 1em;
|
||
transition: all 0.3s ease;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group select:focus {
|
||
outline: none;
|
||
border-color: #1976d2;
|
||
box-shadow: 0 0 10px rgba(25, 118, 210, 0.2);
|
||
}
|
||
|
||
.form-group.highlight-target input:focus {
|
||
border-color: #ff9800;
|
||
box-shadow: 0 0 0 4px rgba(255, 152, 0, 0.15);
|
||
}
|
||
|
||
.phone-row {
|
||
display: grid;
|
||
grid-template-columns: 220px 1fr;
|
||
gap: 20px;
|
||
}
|
||
|
||
.form-row {
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: 20px;
|
||
}
|
||
|
||
.btn {
|
||
padding: 15px 40px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 1.1em;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
width: 100% !important;
|
||
max-width: 100% !important;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, #42a5f5, #1976d2);
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: linear-gradient(135deg, #1976d2, #0d47a1);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 5px 15px rgba(25, 118, 210, 0.4);
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: #95a5a6;
|
||
color: white;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: #7f8c8d;
|
||
}
|
||
|
||
.button-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
margin-top: 25px;
|
||
}
|
||
|
||
.profile-hero {
|
||
display: grid;
|
||
grid-template-columns: 168px 1fr;
|
||
gap: 26px;
|
||
align-items: center;
|
||
padding: 28px;
|
||
margin-bottom: 30px;
|
||
border-radius: 18px;
|
||
background: linear-gradient(130deg, #f3faff 0%, #e3f2fd 55%, #f8fbff 100%);
|
||
box-shadow: 0 12px 28px rgba(100, 181, 246, 0.2);
|
||
}
|
||
|
||
.profile-avatar-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.profile-avatar-button {
|
||
width: 150px;
|
||
height: 150px;
|
||
border: 0;
|
||
padding: 0;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
background: linear-gradient(135deg, #1e88e5, #42a5f5);
|
||
box-shadow: 0 16px 30px rgba(33, 150, 243, 0.28);
|
||
position: relative;
|
||
}
|
||
|
||
.profile-avatar-button.disabled {
|
||
cursor: not-allowed;
|
||
opacity: 0.65;
|
||
}
|
||
|
||
.profile-avatar-button::after {
|
||
content: 'Zmień';
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
padding: 8px 0;
|
||
font-size: 0.85em;
|
||
font-weight: 700;
|
||
letter-spacing: 0.04em;
|
||
color: #fff;
|
||
background: rgba(0, 0, 0, 0.46);
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
|
||
.profile-avatar-button:hover::after,
|
||
.profile-avatar-button:focus-visible::after {
|
||
opacity: 1;
|
||
}
|
||
|
||
.profile-avatar-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
display: block;
|
||
}
|
||
|
||
.profile-avatar-fallback {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: grid;
|
||
place-items: center;
|
||
font-size: 3.1em;
|
||
font-weight: 800;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.profile-avatar-hint {
|
||
font-size: 0.86em;
|
||
color: #607d8b;
|
||
text-align: center;
|
||
}
|
||
|
||
.profile-identity {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.profile-identity-name {
|
||
font-size: 2em;
|
||
line-height: 1.12;
|
||
font-weight: 800;
|
||
color: #0d47a1;
|
||
margin: 0;
|
||
}
|
||
|
||
.profile-identity-nick {
|
||
font-size: 1.05em;
|
||
font-weight: 700;
|
||
color: #1976d2;
|
||
margin: 0;
|
||
}
|
||
|
||
.profile-identity-email {
|
||
font-size: 0.95em;
|
||
color: #607d8b;
|
||
margin: 0;
|
||
}
|
||
|
||
.avatar-upload-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 12000;
|
||
background: rgba(3, 11, 21, 0.72);
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 20px;
|
||
}
|
||
|
||
.avatar-upload-overlay.active {
|
||
display: flex;
|
||
}
|
||
|
||
.avatar-upload-modal {
|
||
position: relative;
|
||
width: min(680px, 96vw);
|
||
background: #ffffff;
|
||
border-radius: 16px;
|
||
padding: 26px;
|
||
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
|
||
}
|
||
|
||
.avatar-modal-close {
|
||
position: absolute;
|
||
top: 10px;
|
||
right: 10px;
|
||
border: 0;
|
||
width: 34px;
|
||
height: 34px;
|
||
border-radius: 50%;
|
||
font-size: 1.2em;
|
||
cursor: pointer;
|
||
background: #eef4fb;
|
||
color: #355a75;
|
||
}
|
||
|
||
.avatar-modal-title {
|
||
margin: 0 0 8px;
|
||
color: #0d47a1;
|
||
font-size: 1.45em;
|
||
}
|
||
|
||
.avatar-modal-subtitle {
|
||
margin: 0 0 14px;
|
||
color: #607d8b;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.avatar-dropzone {
|
||
position: relative;
|
||
border: 2px dashed #90caf9;
|
||
background: #f5fbff;
|
||
border-radius: 14px;
|
||
padding: 24px;
|
||
text-align: center;
|
||
margin-bottom: 14px;
|
||
transition: border-color 0.2s ease, background 0.2s ease;
|
||
}
|
||
|
||
.avatar-dropzone.dragover {
|
||
border-color: #1976d2;
|
||
background: #e8f4ff;
|
||
}
|
||
|
||
.avatar-dropzone input[type="file"] {
|
||
position: absolute;
|
||
inset: 0;
|
||
opacity: 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.avatar-dropzone strong {
|
||
display: block;
|
||
color: #1565c0;
|
||
font-size: 1.05em;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.avatar-editor {
|
||
display: none;
|
||
grid-template-columns: 320px 1fr;
|
||
gap: 18px;
|
||
align-items: start;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.avatar-editor.active {
|
||
display: grid;
|
||
}
|
||
|
||
.avatar-crop-stage {
|
||
position: relative;
|
||
width: 320px;
|
||
height: 320px;
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
box-shadow: inset 0 0 0 2px rgba(33, 150, 243, 0.25);
|
||
background: repeating-conic-gradient(#f2f8ff 0% 25%, #ebf2fb 0% 50%) 50%/20px 20px;
|
||
}
|
||
|
||
.avatar-crop-canvas {
|
||
width: 320px;
|
||
height: 320px;
|
||
display: block;
|
||
}
|
||
|
||
.avatar-circle-mask {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.avatar-circle-mask::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 270px;
|
||
height: 270px;
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.26);
|
||
border: 2px solid rgba(255, 255, 255, 0.95);
|
||
}
|
||
|
||
.avatar-editor-controls {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 14px;
|
||
}
|
||
|
||
.avatar-editor-controls label {
|
||
margin: 0;
|
||
font-size: 0.95em;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
.avatar-editor-controls input[type="range"] {
|
||
width: 100%;
|
||
}
|
||
|
||
.avatar-upload-hint {
|
||
font-size: 0.9em;
|
||
color: #607d8b;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.avatar-modal-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 10px;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.avatar-modal-actions .btn {
|
||
width: auto !important;
|
||
min-width: 130px;
|
||
padding-inline: 18px;
|
||
}
|
||
|
||
.avatar-modal-message {
|
||
margin-top: 10px;
|
||
font-size: 0.9em;
|
||
color: #607d8b;
|
||
min-height: 20px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
h1 {
|
||
font-size: 2em;
|
||
padding: 20px;
|
||
}
|
||
|
||
.settings-section {
|
||
padding: 25px 20px;
|
||
}
|
||
|
||
.form-row {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.phone-row {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.profile-hero {
|
||
grid-template-columns: 1fr;
|
||
justify-items: center;
|
||
text-align: center;
|
||
padding: 24px 16px;
|
||
}
|
||
|
||
.profile-identity {
|
||
align-items: center;
|
||
}
|
||
|
||
.avatar-upload-modal {
|
||
padding: 18px;
|
||
}
|
||
|
||
.avatar-editor {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.avatar-crop-stage,
|
||
.avatar-crop-canvas {
|
||
width: 280px;
|
||
height: 280px;
|
||
}
|
||
|
||
.avatar-circle-mask::before {
|
||
width: 236px;
|
||
height: 236px;
|
||
}
|
||
|
||
.button-group {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.btn {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.footer-copyright {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 40px;
|
||
}
|
||
|
||
div.polices p {
|
||
color: black !important;
|
||
font-weight: bold !important;
|
||
}
|
||
|
||
div.polices p a {
|
||
text-decoration: none !important;
|
||
font-size: 1rem;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<?php
|
||
if (!empty($_SESSION['logged_in'])) {
|
||
include $_SERVER['DOCUMENT_ROOT'].'/global/navLogined.php';
|
||
} else {
|
||
include $_SERVER['DOCUMENT_ROOT'].'/global/navNoLogined.php';
|
||
}
|
||
?>
|
||
|
||
<main>
|
||
<div class="settings-container">
|
||
<?php
|
||
$focusField = isset($_GET['focus']) ? trim((string) $_GET['focus']) : '';
|
||
$usernameRequired = isset($_GET['username_required']) && $_GET['username_required'] === '1';
|
||
?>
|
||
<h1>⚙️ Ustawienia Konta</h1>
|
||
<div class="nav-container">
|
||
<div class="box">
|
||
<a href="/account/profile/" class="nav-link">👤 Informacje profilowe</a>
|
||
<a href="/account/settings/" class="nav-link">⚙️ Pozostałe ustawienia</a>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if (isset($_GET['success']) && $_GET['success'] === 'personal_data'): ?>
|
||
<div style="background: #d4edda; color: #155724; padding: 15px; border-radius: 8px; margin-bottom: 20px; text-align: center; border-left: 4px solid #28a745;">
|
||
✅ Dane osobowe zostały zaktualizowane!
|
||
</div>
|
||
<?php elseif (isset($_GET['success']) && $_GET['success'] === 'avatar_updated'): ?>
|
||
<div style="background: #d4edda; color: #155724; padding: 15px; border-radius: 8px; margin-bottom: 20px; text-align: center; border-left: 4px solid #28a745;">
|
||
✅ Zdjęcie profilowe zostało zaktualizowane!
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (isset($_GET['error'])): ?>
|
||
<div style="background: #f8d7da; color: #721c24; padding: 15px; border-radius: 8px; margin-bottom: 20px; text-align: center; border-left: 4px solid #dc3545;">
|
||
❌ <?= htmlspecialchars($_GET['error']) ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="profile-hero">
|
||
<div class="profile-avatar-wrap">
|
||
<button type="button" class="profile-avatar-button<?= $isSuspended ? ' disabled' : '' ?>" id="avatarToggleBtn" <?= $isSuspended ? 'disabled' : '' ?> aria-controls="avatarUploadPanel" aria-expanded="false" title="Kliknij, aby zmienić zdjęcie">
|
||
<?php if ($avatarUrl): ?>
|
||
<img class="profile-avatar-img" src="<?= htmlspecialchars($avatarUrl, ENT_QUOTES, 'UTF-8') ?>" alt="Zdjęcie profilowe">
|
||
<?php else: ?>
|
||
<span class="profile-avatar-fallback"><?= htmlspecialchars($avatarInitial, ENT_QUOTES, 'UTF-8') ?></span>
|
||
<?php endif; ?>
|
||
</button>
|
||
<div class="profile-avatar-hint">Kliknij zdjęcie, aby dodać nowe</div>
|
||
</div>
|
||
|
||
<div class="profile-identity">
|
||
<h2 class="profile-identity-name"><?= htmlspecialchars($displayFullName, ENT_QUOTES, 'UTF-8') ?></h2>
|
||
<p class="profile-identity-nick">@<?= htmlspecialchars($displayNickname, ENT_QUOTES, 'UTF-8') ?></p>
|
||
<p class="profile-identity-email"><?= htmlspecialchars((string)($userData['email'] ?? ''), ENT_QUOTES, 'UTF-8') ?></p>
|
||
|
||
<p class="avatar-upload-hint">Avatar jest zapisywany jako kwadrat. W podglądzie widzisz okrągłą strefę docelową.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="avatar-upload-overlay" id="avatarUploadOverlay" aria-hidden="true">
|
||
<div class="avatar-upload-modal" role="dialog" aria-modal="true" aria-labelledby="avatarModalTitle">
|
||
<button type="button" class="avatar-modal-close" id="avatarModalClose" aria-label="Zamknij">×</button>
|
||
<h3 class="avatar-modal-title" id="avatarModalTitle">Przytnij zdjęcie profilowe</h3>
|
||
<p class="avatar-modal-subtitle">Przeciągnij obraz w obszarze kadru i ustaw skalę. Zapisany zostanie kwadrat z widocznym podglądem koła.</p>
|
||
|
||
<div class="avatar-dropzone" id="avatarDropzone">
|
||
<strong>Upuść zdjęcie tutaj albo kliknij, aby wybrać</strong>
|
||
<span>Dozwolone: JPG, PNG, GIF, WEBP. Maksymalny rozmiar wejściowy 10MB.</span>
|
||
<input type="file" id="avatarFileInput" accept="image/png,image/jpeg,image/gif,image/webp" <?= $profileFormDisabled ?>>
|
||
</div>
|
||
|
||
<div class="avatar-editor" id="avatarEditor">
|
||
<div class="avatar-crop-stage" id="avatarCropStage">
|
||
<canvas id="avatarCropCanvas" class="avatar-crop-canvas" width="320" height="320"></canvas>
|
||
<div class="avatar-circle-mask"></div>
|
||
</div>
|
||
<div class="avatar-editor-controls">
|
||
<label for="avatarZoomRange">Skala zdjęcia</label>
|
||
<input type="range" id="avatarZoomRange" min="100" max="300" step="1" value="100">
|
||
<div class="avatar-upload-hint">Przesuwaj zdjęcie myszką lub palcem, aby ustawić centralny kadr.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="avatar-modal-actions">
|
||
<button type="button" class="btn btn-secondary" id="avatarCancelBtn">Anuluj</button>
|
||
<button type="button" class="btn btn-primary" id="avatarAttachBtn" disabled>Załącz</button>
|
||
</div>
|
||
<div class="avatar-modal-message" id="avatarModalMessage">Wybierz plik, aby rozpocząć kadrowanie.</div>
|
||
|
||
<form method="POST" action="/account/settings/update_avatar.php" enctype="multipart/form-data" id="avatarUploadForm" style="display:none;">
|
||
<input type="file" name="avatar_file" id="avatarCroppedInput">
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-section" id="profile">
|
||
<h2>👤 Dane osobowe</h2>
|
||
<form method="POST" action="/account/settings/update_settings.php">
|
||
<input type="hidden" name="action" value="personal_data">
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="firstName">Imię</label>
|
||
<input type="text" id="firstName" name="first_name" value="<?= htmlspecialchars($userData['first_name'] ?? '') ?>" required <?= $profileFormDisabled ?>>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lastName">Nazwisko</label>
|
||
<input type="text" id="lastName" name="last_name" value="<?= htmlspecialchars($userData['last_name'] ?? '') ?>" required <?= $profileFormDisabled ?>>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="email">Adres e-mail</label>
|
||
<input type="email" id="email" value="<?= htmlspecialchars($userData['email']) ?>" disabled>
|
||
<small style="color: #7f8c8d;">
|
||
<a href="<?= $isSuspended ? '#' : '/account/settings/change_email_request.php' ?>" style="color: #2196F3; text-decoration: none; font-weight: 600; pointer-events: <?= $isSuspended ? 'none' : 'auto' ?>; opacity: <?= $isSuspended ? '0.6' : '1' ?>;">
|
||
📧 Zmień adres email
|
||
</a>
|
||
</small>
|
||
</div>
|
||
<div class="form-group<?= $focusField === 'username' ? ' highlight-target' : '' ?>" id="usernameFieldGroup">
|
||
<label for="username">Nazwa użytkownika</label>
|
||
<input type="text" id="username" name="username" value="<?= htmlspecialchars($userData['username']) ?>" required maxlength="20" pattern="[A-Za-z0-9_&!]{1,20}" title="Dozwolone: litery angielskie, cyfry, _, &, ! (max 20 znaków)" <?= $profileFormDisabled ?>>
|
||
<?php if ($usernameRequired): ?>
|
||
<div class="form-help-callout">
|
||
Ustaw tutaj username, żeby wejść do gry. Wymagany format: 1-20 znaków, dozwolone tylko litery angielskie, cyfry oraz znaki <strong>_ & !</strong>.
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="phone-row">
|
||
<div class="form-group">
|
||
<label for="phoneCountryCode">Kierunkowy państwa</label>
|
||
<select id="phoneCountryCode" name="phone_country_code" <?= $profileFormDisabled ?>>
|
||
<option value="">Wybierz kierunkowy</option>
|
||
<?php foreach ($phoneCountryOptions as $code => $label): ?>
|
||
<option value="<?= htmlspecialchars($code, ENT_QUOTES, 'UTF-8') ?>" <?= $currentPhoneCountryCode === $code ? 'selected' : '' ?>>
|
||
<?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="phoneNumber">Numer telefonu</label>
|
||
<input type="text" id="phoneNumber" name="phone_number" value="<?= htmlspecialchars($currentPhoneNumber, ENT_QUOTES, 'UTF-8') ?>" maxlength="20" inputmode="numeric" pattern="[0-9\s\-]{4,20}" title="Dozwolone cyfry, spacje i myślnik" <?= $profileFormDisabled ?>>
|
||
</div>
|
||
</div>
|
||
<div class="button-group">
|
||
<button type="submit" class="btn btn-primary" <?= $profileFormDisabled ?>>Zapisz zmiany</button>
|
||
<button type="button" class="btn btn-secondary" onclick="location.reload()" <?= $profileFormDisabled ?>>Anuluj</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<?php
|
||
if (!empty($_SESSION['logged_in'])) {
|
||
include $_SERVER['DOCUMENT_ROOT'].'/global/footerLogined.php';
|
||
} else {
|
||
include $_SERVER['DOCUMENT_ROOT'].'/global/footerNoLogined.php';
|
||
}
|
||
?>
|
||
<script>
|
||
(function() {
|
||
var toggleBtn = document.getElementById('avatarToggleBtn');
|
||
var overlay = document.getElementById('avatarUploadOverlay');
|
||
var closeBtn = document.getElementById('avatarModalClose');
|
||
var cancelBtn = document.getElementById('avatarCancelBtn');
|
||
var dropzone = document.getElementById('avatarDropzone');
|
||
var fileInput = document.getElementById('avatarFileInput');
|
||
var editor = document.getElementById('avatarEditor');
|
||
var zoomRange = document.getElementById('avatarZoomRange');
|
||
var canvas = document.getElementById('avatarCropCanvas');
|
||
var attachBtn = document.getElementById('avatarAttachBtn');
|
||
var message = document.getElementById('avatarModalMessage');
|
||
var uploadForm = document.getElementById('avatarUploadForm');
|
||
var croppedInput = document.getElementById('avatarCroppedInput');
|
||
|
||
if (!toggleBtn || !overlay || !fileInput || !editor || !zoomRange || !canvas || !attachBtn || !message || !uploadForm || !croppedInput) {
|
||
return;
|
||
}
|
||
|
||
var ctx = canvas.getContext('2d');
|
||
var cropSize = 320;
|
||
var loadedImage = null;
|
||
var baseScale = 1;
|
||
var scale = 1;
|
||
var offsetX = 0;
|
||
var offsetY = 0;
|
||
var dragging = false;
|
||
var dragStartX = 0;
|
||
var dragStartY = 0;
|
||
var startOffsetX = 0;
|
||
var startOffsetY = 0;
|
||
|
||
function setMessage(text, isError) {
|
||
message.textContent = text;
|
||
message.style.color = isError ? '#c62828' : '#607d8b';
|
||
}
|
||
|
||
function openModal() {
|
||
overlay.classList.add('active');
|
||
overlay.setAttribute('aria-hidden', 'false');
|
||
toggleBtn.setAttribute('aria-expanded', 'true');
|
||
}
|
||
|
||
function closeModal() {
|
||
overlay.classList.remove('active');
|
||
overlay.setAttribute('aria-hidden', 'true');
|
||
toggleBtn.setAttribute('aria-expanded', 'false');
|
||
}
|
||
|
||
function clampOffsets() {
|
||
if (!loadedImage) {
|
||
return;
|
||
}
|
||
|
||
var scaledW = loadedImage.width * scale;
|
||
var scaledH = loadedImage.height * scale;
|
||
var minX = cropSize - scaledW;
|
||
var minY = cropSize - scaledH;
|
||
offsetX = Math.min(0, Math.max(minX, offsetX));
|
||
offsetY = Math.min(0, Math.max(minY, offsetY));
|
||
}
|
||
|
||
function drawPreview() {
|
||
ctx.clearRect(0, 0, cropSize, cropSize);
|
||
if (!loadedImage) {
|
||
return;
|
||
}
|
||
|
||
ctx.drawImage(loadedImage, offsetX, offsetY, loadedImage.width * scale, loadedImage.height * scale);
|
||
}
|
||
|
||
function setImage(file) {
|
||
if (!file) {
|
||
return;
|
||
}
|
||
|
||
var allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||
if (allowed.indexOf(file.type) === -1) {
|
||
setMessage('Dozwolone sa tylko obrazy JPG, PNG, GIF, WEBP.', true);
|
||
return;
|
||
}
|
||
|
||
if (file.size > 10 * 1024 * 1024) {
|
||
setMessage('Plik wejsciowy jest za duzy. Maksymalnie 10MB.', true);
|
||
return;
|
||
}
|
||
|
||
var reader = new FileReader();
|
||
reader.onload = function(evt) {
|
||
var img = new Image();
|
||
img.onload = function() {
|
||
loadedImage = img;
|
||
baseScale = Math.max(cropSize / img.width, cropSize / img.height);
|
||
scale = baseScale;
|
||
offsetX = (cropSize - (img.width * scale)) / 2;
|
||
offsetY = (cropSize - (img.height * scale)) / 2;
|
||
zoomRange.value = '100';
|
||
editor.classList.add('active');
|
||
attachBtn.disabled = false;
|
||
setMessage('Ustaw kadr i kliknij "Zalacz".', false);
|
||
drawPreview();
|
||
};
|
||
img.onerror = function() {
|
||
setMessage('Nie mozna odczytac wybranego obrazu.', true);
|
||
};
|
||
img.src = String(evt.target && evt.target.result ? evt.target.result : '');
|
||
};
|
||
reader.readAsDataURL(file);
|
||
}
|
||
|
||
function getPointFromEvent(evt) {
|
||
if (evt.touches && evt.touches.length) {
|
||
return { x: evt.touches[0].clientX, y: evt.touches[0].clientY };
|
||
}
|
||
return { x: evt.clientX, y: evt.clientY };
|
||
}
|
||
|
||
toggleBtn.addEventListener('click', function() {
|
||
openModal();
|
||
});
|
||
|
||
if (closeBtn) {
|
||
closeBtn.addEventListener('click', closeModal);
|
||
}
|
||
|
||
if (cancelBtn) {
|
||
cancelBtn.addEventListener('click', closeModal);
|
||
}
|
||
|
||
overlay.addEventListener('click', function(evt) {
|
||
if (evt.target === overlay) {
|
||
closeModal();
|
||
}
|
||
});
|
||
|
||
document.addEventListener('keydown', function(evt) {
|
||
if (evt.key === 'Escape' && overlay.classList.contains('active')) {
|
||
closeModal();
|
||
}
|
||
});
|
||
|
||
fileInput.addEventListener('change', function() {
|
||
setImage(fileInput.files && fileInput.files.length ? fileInput.files[0] : null);
|
||
});
|
||
|
||
dropzone.addEventListener('dragover', function(evt) {
|
||
evt.preventDefault();
|
||
dropzone.classList.add('dragover');
|
||
});
|
||
|
||
dropzone.addEventListener('dragleave', function() {
|
||
dropzone.classList.remove('dragover');
|
||
});
|
||
|
||
dropzone.addEventListener('drop', function(evt) {
|
||
evt.preventDefault();
|
||
dropzone.classList.remove('dragover');
|
||
if (!evt.dataTransfer || !evt.dataTransfer.files || !evt.dataTransfer.files.length) {
|
||
return;
|
||
}
|
||
setImage(evt.dataTransfer.files[0]);
|
||
});
|
||
|
||
zoomRange.addEventListener('input', function() {
|
||
if (!loadedImage) {
|
||
return;
|
||
}
|
||
|
||
var prevScale = scale;
|
||
var ratio = Number(zoomRange.value) / 100;
|
||
scale = baseScale * ratio;
|
||
var centerX = cropSize / 2;
|
||
var centerY = cropSize / 2;
|
||
offsetX = centerX - ((centerX - offsetX) / prevScale) * scale;
|
||
offsetY = centerY - ((centerY - offsetY) / prevScale) * scale;
|
||
clampOffsets();
|
||
drawPreview();
|
||
});
|
||
|
||
function startDrag(evt) {
|
||
if (!loadedImage) {
|
||
return;
|
||
}
|
||
dragging = true;
|
||
var p = getPointFromEvent(evt);
|
||
dragStartX = p.x;
|
||
dragStartY = p.y;
|
||
startOffsetX = offsetX;
|
||
startOffsetY = offsetY;
|
||
evt.preventDefault();
|
||
}
|
||
|
||
function moveDrag(evt) {
|
||
if (!dragging || !loadedImage) {
|
||
return;
|
||
}
|
||
var p = getPointFromEvent(evt);
|
||
offsetX = startOffsetX + (p.x - dragStartX);
|
||
offsetY = startOffsetY + (p.y - dragStartY);
|
||
clampOffsets();
|
||
drawPreview();
|
||
evt.preventDefault();
|
||
}
|
||
|
||
function endDrag() {
|
||
dragging = false;
|
||
}
|
||
|
||
canvas.addEventListener('mousedown', startDrag);
|
||
canvas.addEventListener('touchstart', startDrag, { passive: false });
|
||
window.addEventListener('mousemove', moveDrag);
|
||
window.addEventListener('touchmove', moveDrag, { passive: false });
|
||
window.addEventListener('mouseup', endDrag);
|
||
window.addEventListener('touchend', endDrag);
|
||
|
||
attachBtn.addEventListener('click', function() {
|
||
if (!loadedImage) {
|
||
setMessage('Najpierw wybierz plik do kadrowania.', true);
|
||
return;
|
||
}
|
||
|
||
var outputCanvas = document.createElement('canvas');
|
||
outputCanvas.width = 512;
|
||
outputCanvas.height = 512;
|
||
var out = outputCanvas.getContext('2d');
|
||
var factor = outputCanvas.width / cropSize;
|
||
out.drawImage(
|
||
loadedImage,
|
||
offsetX * factor,
|
||
offsetY * factor,
|
||
loadedImage.width * scale * factor,
|
||
loadedImage.height * scale * factor
|
||
);
|
||
|
||
function submitCroppedBlob(blob, mimeType, fileName) {
|
||
if (!blob) {
|
||
setMessage('Nie udalo sie przygotowac pliku do wysylki.', true);
|
||
return;
|
||
}
|
||
|
||
if (typeof DataTransfer === 'undefined') {
|
||
setMessage('Przegladarka nie wspiera automatycznego przeslania. Uzyj nowszej wersji.', true);
|
||
return;
|
||
}
|
||
|
||
var file = new File([blob], fileName, { type: mimeType });
|
||
var dt = new DataTransfer();
|
||
dt.items.add(file);
|
||
croppedInput.files = dt.files;
|
||
uploadForm.submit();
|
||
}
|
||
|
||
outputCanvas.toBlob(function(webpBlob) {
|
||
if (webpBlob) {
|
||
submitCroppedBlob(webpBlob, 'image/webp', 'avatar.webp');
|
||
return;
|
||
}
|
||
|
||
// Fallback dla starszych przeglądarek bez wsparcia WebP w canvas.toBlob
|
||
outputCanvas.toBlob(function(jpegBlob) {
|
||
submitCroppedBlob(jpegBlob, 'image/jpeg', 'avatar.jpg');
|
||
}, 'image/jpeg', 0.9);
|
||
}, 'image/webp', 0.86);
|
||
});
|
||
|
||
setMessage('Wybierz plik, aby rozpocząć kadrowanie.', false);
|
||
})();
|
||
|
||
(function() {
|
||
var shouldFocusUsername = <?php echo $focusField === 'username' ? 'true' : 'false'; ?>;
|
||
if (!shouldFocusUsername) {
|
||
return;
|
||
}
|
||
|
||
var input = document.getElementById('username');
|
||
var group = document.getElementById('usernameFieldGroup');
|
||
if (group && typeof group.scrollIntoView === 'function') {
|
||
group.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
}
|
||
if (input && typeof input.focus === 'function') {
|
||
window.setTimeout(function() {
|
||
input.focus();
|
||
if (typeof input.select === 'function') {
|
||
input.select();
|
||
}
|
||
}, 160);
|
||
}
|
||
})();
|
||
|
||
(function() {
|
||
var usernameInput = document.getElementById('username');
|
||
if (!usernameInput) {
|
||
return;
|
||
}
|
||
|
||
usernameInput.addEventListener('invalid', function() {
|
||
if (usernameInput.validity.patternMismatch) {
|
||
usernameInput.setCustomValidity('Nazwa użytkownika może zawierać tylko litery angielskie, cyfry oraz znaki _ & ! (max 20 znaków).');
|
||
} else {
|
||
usernameInput.setCustomValidity('');
|
||
}
|
||
});
|
||
|
||
usernameInput.addEventListener('input', function() {
|
||
usernameInput.setCustomValidity('');
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|