This commit is contained in:
Flatlogic Bot 2025-11-13 09:20:24 +00:00
parent bfa71e661d
commit 97cbe8f10f
12 changed files with 526 additions and 756 deletions

View File

@ -142,6 +142,10 @@ a:hover { color: var(--color-secondary); }
padding: 0.5rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
transition: background-color 0.2s;
}
.language-switcher:hover .selected-lang {
background-color: var(--bg-color);
}
.selected-lang img {
width: 20px;
@ -155,32 +159,50 @@ a:hover { color: var(--color-secondary); }
.icon-sm {
width: 16px;
height: 16px;
transition: transform 0.2s ease-in-out;
}
.language-switcher:hover .icon-sm {
transform: rotate(180deg);
}
.lang-dropdown {
display: none;
display: block; /* Use opacity and visibility for animation */
position: absolute;
top: 100%;
top: calc(100% + 0.5rem);
right: 0;
background-color: var(--surface-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 0.5rem;
margin-top: 0.5rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
min-width: 120px;
box-shadow: 0 8px 24px rgba(0,0,0,0.1);
min-width: 150px;
z-index: 10;
/* Animation */
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}
.language-switcher:hover .lang-dropdown {
display: block;
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.lang-dropdown a {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
gap: 0.75rem;
padding: 0.6rem 0.8rem;
border-radius: calc(var(--border-radius) / 2);
color: var(--text-color);
font-weight: 500;
}
.lang-dropdown a:hover, .lang-dropdown a.active {
background-color: var(--bg-color);
color: var(--color-primary);
}
.lang-dropdown a.active {
font-weight: 600;
}
.lang-dropdown a img {
width: 20px;
@ -189,6 +211,67 @@ a:hover { color: var(--color-secondary); }
border-radius: 2px;
}
/* --- User Menu --- */
.user-menu {
position: relative;
}
.user-menu-toggle {
background: none;
border: none;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.25rem;
border-radius: 50px;
transition: background-color 0.2s;
}
.user-menu:hover .user-menu-toggle {
background-color: var(--bg-color);
}
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
object-fit: cover;
border: 2px solid var(--border-color);
}
.user-menu .icon-sm {
color: var(--text-color);
}
.user-dropdown {
display: block;
position: absolute;
top: calc(100% + 0.5rem);
right: 0;
background-color: var(--surface-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 0.5rem;
box-shadow: 0 8px 24px rgba(0,0,0,0.1);
min-width: 160px;
z-index: 10;
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}
.user-menu:hover .user-dropdown {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.user-dropdown a {
display: block;
padding: 0.6rem 1rem;
color: var(--text-color);
border-radius: calc(var(--border-radius) / 2);
}
.user-dropdown a:hover {
background-color: var(--bg-color);
color: var(--color-primary);
}
/* --- Theme Switch --- */
.theme-switch-wrapper { display: flex; align-items: center; }
.theme-switch { position: relative; display: inline-block; width: 50px; height: 26px; }
@ -532,4 +615,111 @@ input:checked + .slider:before { transform: translateX(24px); }
}
.terms-content ul, .terms-content ol {
padding-left: 1.5rem;
}
}
/* --- Generator Form --- */
.generator-section {
padding: calc(var(--base-spacing) * 3) 0;
background-color: var(--surface-color);
}
.generator-form {
background-color: var(--bg-color);
padding: calc(var(--base-spacing) * 2);
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
}
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: calc(var(--base-spacing) * 2);
margin-bottom: calc(var(--base-spacing) * 2);
}
.form-column-wide {
grid-column: 1 / -1;
}
.generator-form .form-group label {
font-size: 0.9rem;
font-weight: 600;
color: var(--subtle-text-color);
text-transform: uppercase;
}
.generator-form input[type="text"],
.generator-form input[type="number"],
.generator-form textarea,
.generator-form select {
width: 100%;
padding: 0.8rem 1rem;
border: 2px solid var(--border-color);
border-radius: var(--border-radius);
background-color: var(--surface-color);
color: var(--text-color);
font-family: var(--font-family-body);
font-size: 1rem;
transition: border-color 0.2s, box-shadow 0.2s;
}
.generator-form input:focus,
.generator-form textarea:focus,
.generator-form select:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 4px rgba(108, 99, 255, 0.2);
}
.generator-form select {
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%236c757d' class='bi bi-chevron-down' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 1rem center;
background-size: 1em;
}
.form-submit-group {
text-align: center;
margin-top: calc(var(--base-spacing) * 2);
}
.cta-button {
background: var(--gradient);
color: white;
padding: 1rem 2.5rem;
border-radius: 50px;
font-weight: 700;
font-family: var(--font-family-headings);
text-transform: uppercase;
letter-spacing: 1px;
border: none;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.cta-button:hover {
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
color: white;
}
/* --- Hero Section --- */
.hero {
background: var(--gradient);
padding: 6rem 0;
text-align: center;
margin-top: 2rem;
border-radius: var(--border-radius);
}
.hero h1, .hero p {
color: white;
}
.hero h1 {
font-size: 3.5rem;
font-weight: 700;
}
.hero p {
font-size: 1.25rem;
margin-top: 1rem;
margin-bottom: 2rem;
opacity: 0.9;
}
.hero .cta-button {
background: white;
color: var(--color-primary);
}
.hero .cta-button:hover {
background: var(--surface-color);
color: var(--color-primary);
}

View File

@ -3,7 +3,7 @@ document.addEventListener('DOMContentLoaded', function () {
feather.replace();
// --- Theme Switcher ---
const themeSwitch = document.getElementById('checkbox');
const themeSwitch = document.getElementById('theme-checkbox');
const currentTheme = localStorage.getItem('theme');
if (currentTheme) {
@ -23,7 +23,9 @@ document.addEventListener('DOMContentLoaded', function () {
}
}
themeSwitch.addEventListener('change', switchTheme, false);
if (themeSwitch) {
themeSwitch.addEventListener('change', switchTheme, false);
}
// --- Generator Form: Photo Upload ---
const uploadArea = document.getElementById('upload-area');

View File

@ -19,6 +19,7 @@ try {
$sql_users = "
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
@ -26,6 +27,12 @@ try {
$pdo->exec($sql_users);
echo "Table 'users' is ready.\n";
// Add username column to users table
if (!columnExists($pdo, 'users', 'username')) {
$pdo->exec("ALTER TABLE users ADD COLUMN username VARCHAR(255) NOT NULL AFTER id");
echo "Column 'username' added to 'users' table.\n";
}
// Add role column to users table
if (!columnExists($pdo, 'users', 'role')) {
$pdo->exec("ALTER TABLE users ADD COLUMN role VARCHAR(50) NOT NULL DEFAULT 'user'");

View File

@ -1,10 +1,9 @@
<?php
session_start();
require_once 'includes/translations.php';
require_once 'includes/header.php';
?>
<main class="container my-5">
<h1 class="text-center mb-5"><?php echo t('faq_title'); ?></h1>
<div class="accordion" id="faqAccordion">

View File

@ -1,5 +1,12 @@
<footer>
<div class="container">
<p><?= t('footer_copyright') ?></p>
<p>&copy; <?php echo date('Y'); ?> <?php echo t('app_name'); ?>. All rights reserved.</p>
</div>
</footer>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

View File

@ -22,13 +22,7 @@ if (isset($_GET['lang'])) {
exit();
}
$lang = $_SESSION['lang'] ?? 'en';
$translations = get_translations();
function t($key) {
global $translations, $lang;
return $translations[$lang][$key] ?? $key;
}
$lang = get_current_language();
$is_logged_in = isset($_SESSION['user_id']);
?>
@ -42,7 +36,8 @@ $is_logged_in = isset($_SESSION['user_id']);
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&family=Roboto:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<script src="https://unpkg.com/phosphor-icons"></script>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body data-theme="light">
@ -67,10 +62,10 @@ $is_logged_in = isset($_SESSION['user_id']);
<div class="language-switcher">
<div class="selected-lang">
<img src="assets/images/flags/<?php echo $lang; ?>.svg" alt="<?php echo $lang; ?>">
<i class="ph-bold ph-caret-down icon-sm"></i>
<i data-feather="chevron-down"></i>
</div>
<div class="lang-dropdown">
<?php foreach (array_keys($translations) as $lang_code): ?>
<?php foreach (array_keys(get_translations()) as $lang_code): ?>
<a href="?lang=<?php echo $lang_code; ?>" class="<?php echo $lang === $lang_code ? 'active' : ''; ?>">
<img src="assets/images/flags/<?php echo $lang_code; ?>.svg" alt="<?php echo $lang_code; ?>">
<span><?php echo strtoupper($lang_code); ?></span>
@ -87,8 +82,16 @@ $is_logged_in = isset($_SESSION['user_id']);
</div>
<?php if ($is_logged_in): ?>
<a href="profile.php" class="nav-link"><?php echo t('nav_profile'); ?></a>
<a href="logout.php" class="nav-link"><?php echo t('nav_logout'); ?></a>
<div class="user-menu">
<button class="user-menu-toggle">
<img src="https://i.pravatar.cc/40?u=<?php echo $_SESSION['user_id']; ?>" alt="User Avatar" class="avatar">
<i data-feather="chevron-down"></i>
</button>
<div class="user-dropdown">
<a href="profile.php"><?php echo t('nav_profile'); ?></a>
<a href="logout.php"><?php echo t('nav_logout'); ?></a>
</div>
</div>
<?php else: ?>
<a href="register.php" class="nav-link"><?php echo t('nav_register'); ?></a>
<a href="login.php" class="login-btn"><?php echo t('nav_login'); ?></a>

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
<?php require_once 'includes/header.php'; ?>
<?php
require_once 'includes/header.php';
?>
<main>
<section class="hero">

11
list_users.php Normal file
View File

@ -0,0 +1,11 @@
<?php
require_once __DIR__ . '/db/config.php';
try {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($users);
} catch (PDOException $e) {
die("Database error: " . $e->getMessage() . "\n");
}

View File

@ -1,6 +1,4 @@
<?php
session_start();
require_once 'includes/translations.php';
require_once 'includes/header.php';
// Default to monthly

View File

@ -8,11 +8,12 @@ $success = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = db();
$username = $_POST['username'] ?? '';
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
$password_confirm = $_POST['password_confirm'] ?? '';
if (empty($email) || empty($password) || empty($password_confirm)) {
if (empty($username) || empty($email) || empty($password) || empty($password_confirm)) {
$error = t('fill_all_fields');
} elseif ($password !== $password_confirm) {
$error = t('passwords_do_not_match');
@ -26,8 +27,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$error = t('email_already_registered');
} else {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('INSERT INTO users (email, password) VALUES (?, ?)');
$stmt->execute([$email, $hashed_password]);
$stmt = $pdo->prepare('INSERT INTO users (username, email, password) VALUES (?, ?, ?)');
$stmt->execute([$username, $email, $hashed_password]);
$success = t('registration_successful');
}
} catch (PDOException $e) {
@ -53,15 +54,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<form action="register.php" method="POST" id="register-form" novalidate>
<div class="form-group">
<label for="email"><?= t('email') ?></label>
<label for="username"><?= t('registration_form_username') ?></label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="email"><?= t('registration_form_email') ?></label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="password"><?= t('password') ?></label>
<label for="password"><?= t('registration_form_password') ?></label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label for="password_confirm"><?= t('confirm_password') ?></label>
<label for="password_confirm"><?= t('registration_form_password_confirm') ?></label>
<input type="password" id="password_confirm" name="password_confirm" required>
</div>
<div class="form-group">

View File

@ -1,6 +1,4 @@
<?php
session_start();
require_once 'includes/translations.php';
require_once 'includes/header.php';
?>