Compare commits

..

No commits in common. "ai-dev" and "master" have entirely different histories.

26 changed files with 149 additions and 2497 deletions

View File

@ -1,74 +0,0 @@
<?php
require_once 'includes/header.php';
require_once 'db/config.php';
// Security check: only admins can access this page
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'admin') {
// You can redirect them to the home page or show an error
header('HTTP/1.0 403 Forbidden');
echo "<h1>403 Forbidden</h1><p>You do not have permission to access this page.</p>";
exit();
}
// Fetch all users for display
try {
$pdo = db();
$stmt = $pdo->query("SELECT id, email, role, subscription_plan, subscription_expires_at, created_at FROM users ORDER BY created_at DESC");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$error = "Database error: " . $e->getMessage();
$users = [];
}
?>
<main class="container">
<div class="page-header">
<h1><?= t('admin_panel_title') ?></h1>
<p><?= t('admin_panel_subtitle') ?></p>
</div>
<?php if (!empty($error)): ?>
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<div class="card">
<div class="card-header">
<h3><?= t('registered_users') ?></h3>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th><?= t('email') ?></th>
<th><?= t('role') ?></th>
<th><?= t('subscription_plan') ?></th>
<th><?= t('subscription_expires_at') ?></th>
<th><?= t('registered_on') ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($users)): ?>
<tr>
<td colspan="6" class="text-center"><?= t('no_users_found') ?></td>
</tr>
<?php else: ?>
<?php foreach ($users as $user): ?>
<tr>
<td><?= htmlspecialchars($user['id']) ?></td>
<td><?= htmlspecialchars($user['email']) ?></td>
<td><?= htmlspecialchars($user['role']) ?></td>
<td><?= htmlspecialchars($user['subscription_plan'] ?? 'N/A') ?></td>
<td><?= htmlspecialchars($user['subscription_expires_at'] ?? 'N/A') ?></td>
<td><?= date("Y-m-d", strtotime($user['created_at'])) ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</main>
<?php require_once 'includes/footer.php'; ?>

View File

@ -1,793 +0,0 @@
/* --- Base & Variables --- */
:root {
--color-primary: #6C63FF;
--color-secondary: #FF6584;
--font-family-headings: 'Poppins', sans-serif;
--font-family-body: 'Roboto', sans-serif;
--base-spacing: 1rem;
--border-radius: 0.5rem;
/* Light Mode */
--bg-color: #F8F9FA;
--surface-color: #FFFFFF;
--text-color: #212529;
--subtle-text-color: #6c757d;
--border-color: #E0E0E0;
--gradient: linear-gradient(45deg, var(--color-primary), var(--color-secondary));
}
[data-theme="dark"] {
--bg-color: #121212;
--surface-color: #1E1E1E;
--text-color: #E0E0E0;
--subtle-text-color: #adb5bd;
--border-color: #333;
}
/* --- Reset & Base Styles --- */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family-body);
background-color: var(--bg-color);
color: var(--text-color);
line-height: 1.6;
transition: background-color 0.3s, color 0.3s;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 calc(var(--base-spacing) * 1.5);
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-family-headings);
font-weight: 600;
color: var(--text-color);
}
h1 { font-size: 3rem; line-height: 1.2; }
h2 { font-size: 2.25rem; margin-bottom: var(--base-spacing); }
h3 { font-size: 1.5rem; }
a {
color: var(--color-primary);
text-decoration: none;
transition: color 0.2s;
}
a:hover { color: var(--color-secondary); }
.page-header {
text-align: center;
padding: calc(var(--base-spacing) * 3) 0;
background-color: var(--surface-color);
border-bottom: 1px solid var(--border-color);
margin-bottom: calc(var(--base-spacing) * 3);
}
.page-header h1 {
font-size: 2.75rem;
}
.page-header p {
font-size: 1.1rem;
color: var(--subtle-text-color);
max-width: 600px;
margin: 0.5rem auto 0;
}
/* --- Header --- */
.header {
background-color: var(--surface-color);
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
padding: var(--base-spacing) 0;
position: sticky;
top: 0;
z-index: 100;
transition: background-color 0.3s;
}
[data-theme="dark"] .header { box-shadow: 0 2px 4px rgba(0,0,0,0.2); }
.header .container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-family: var(--font-family-headings);
font-size: 1.5rem;
font-weight: 700;
color: var(--color-primary);
}
.logo:hover { color: var(--color-primary); }
.main-nav {
display: flex;
gap: calc(var(--base-spacing) * 1.5);
}
.main-nav a {
font-weight: 500;
color: var(--text-color);
}
.main-nav a.login-btn {
background: var(--gradient);
color: white;
padding: 0.5rem 1.5rem;
border-radius: var(--border-radius);
font-weight: 600;
}
.header-actions {
display: flex;
align-items: center;
gap: var(--base-spacing);
}
/* --- Language Switcher --- */
.language-switcher {
position: relative;
cursor: pointer;
}
.selected-lang {
display: flex;
align-items: center;
gap: 0.5rem;
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;
height: 15px;
object-fit: cover;
border-radius: 2px;
}
.selected-lang span {
font-weight: 500;
}
.icon-sm {
width: 16px;
height: 16px;
transition: transform 0.2s ease-in-out;
}
.language-switcher:hover .icon-sm {
transform: rotate(180deg);
}
.lang-dropdown {
display: block; /* Use opacity and visibility for animation */
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: 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 {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.lang-dropdown a {
display: flex;
align-items: center;
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;
height: 15px;
object-fit: cover;
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; }
.theme-switch input { opacity: 0; width: 0; height: 0; }
.slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; }
.slider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 3px; bottom: 3px; background-color: white; transition: .4s; }
input:checked + .slider { background-color: var(--color-primary); }
input:focus + .slider { box-shadow: 0 0 1px var(--color-primary); }
input:checked + .slider:before { transform: translateX(24px); }
.slider.round { border-radius: 34px; }
.slider.round:before { border-radius: 50%; }
/* --- Footer --- */
.footer {
background-color: var(--surface-color);
text-align: center;
padding: calc(var(--base-spacing) * 2) 0;
margin-top: calc(var(--base-spacing) * 3);
border-top: 1px solid var(--border-color);
color: var(--subtle-text-color);
transition: background-color 0.3s, border-color 0.3s;
}
/* --- Buttons --- */
.btn {
padding: 0.75rem 1.5rem;
border-radius: var(--border-radius);
font-weight: 600;
font-family: var(--font-family-headings);
border: none;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
transition: all 0.2s;
text-align: center;
}
.btn:hover { opacity: 0.9; transform: translateY(-2px); }
.btn-primary {
background: var(--gradient);
color: white;
}
.btn-primary:hover { color: white; }
.btn-secondary {
background-color: var(--surface-color);
color: var(--text-color);
border: 1px solid var(--border-color);
}
.btn-outline-primary {
background-color: transparent;
color: var(--color-primary);
border: 2px solid var(--color-primary);
}
.btn-outline-primary:hover {
background-color: var(--color-primary);
color: white;
}
/* --- Cards --- */
.card {
background-color: var(--surface-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
overflow: hidden;
transition: all 0.3s;
}
.card-body {
padding: calc(var(--base-spacing) * 1.5);
}
.card-header {
padding: var(--base-spacing) calc(var(--base-spacing) * 1.5);
border-bottom: 1px solid var(--border-color);
background-color: var(--bg-color);
}
/* --- Forms --- */
.form-group {
margin-bottom: var(--base-spacing);
}
.form-group label {
display: block;
font-weight: 500;
margin-bottom: 0.5rem;
}
.form-control {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-family-body);
font-size: 1rem;
transition: border-color 0.2s, background-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.15);
}
/* --- Auth Pages (Login/Register) --- */
.auth-container {
max-width: 450px;
margin: calc(var(--base-spacing) * 3) auto;
}
.auth-container .card-header h2 {
font-size: 1.75rem;
text-align: center;
margin: 0;
}
.auth-container .btn {
width: 100%;
}
.auth-container .text-center {
margin-top: var(--base-spacing);
color: var(--subtle-text-color);
}
/* --- History Page --- */
.history-list {
display: flex;
flex-direction: column;
gap: calc(var(--base-spacing) * 1.5);
}
.history-item .card-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.9rem;
color: var(--subtle-text-color);
}
.history-item .card-body p {
white-space: pre-wrap; /* Keep formatting */
}
/* --- Pricing Page --- */
.pricing-toggle {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: calc(var(--base-spacing) * 2.5);
gap: var(--base-spacing);
}
.pricing-toggle span {
font-weight: 500;
color: var(--subtle-text-color);
}
.pricing-toggle .toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 26px;
}
.pricing-toggle .toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.pricing-toggle .toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--color-primary);
transition: .4s;
border-radius: 34px;
}
.pricing-toggle .toggle-slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
.pricing-toggle input:checked + .toggle-slider:before {
transform: translateX(24px);
}
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: calc(var(--base-spacing) * 2);
align-items: stretch;
}
.pricing-card {
display: flex;
flex-direction: column;
text-align: center;
padding: calc(var(--base-spacing) * 2);
border: 2px solid var(--border-color);
transition: all 0.3s ease;
}
.pricing-card.popular {
border-color: var(--color-primary);
transform: scale(1.05);
box-shadow: 0 10px 30px rgba(108, 99, 255, 0.15);
}
.pricing-card h3 {
font-size: 1.5rem;
color: var(--color-primary);
}
.pricing-card .price {
font-size: 3rem;
font-weight: 700;
font-family: var(--font-family-headings);
margin: var(--base-spacing) 0;
}
.pricing-card .price .period {
font-size: 1rem;
font-weight: 400;
color: var(--subtle-text-color);
}
.pricing-card .features {
list-style: none;
padding: 0;
margin: var(--base-spacing) 0;
flex-grow: 1;
}
.pricing-card .features li {
margin-bottom: 0.75rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.pricing-card .features li svg {
width: 20px;
height: 20px;
color: var(--color-primary);
}
.pricing-card .btn {
width: 100%;
margin-top: auto;
}
.annual-price {
display: none;
}
/* --- FAQ Page --- */
.faq-container {
max-width: 800px;
margin: 0 auto;
}
.accordion-item {
background-color: var(--surface-color);
border: 1px solid var(--border-color);
margin-bottom: var(--base-spacing);
border-radius: var(--border-radius);
}
.accordion-header {
padding: 0;
border: none;
}
.accordion-button {
width: 100%;
text-align: left;
padding: var(--base-spacing) calc(var(--base-spacing) * 1.5);
font-weight: 600;
font-family: var(--font-family-headings);
font-size: 1.1rem;
background-color: var(--surface-color);
color: var(--text-color);
border: none;
cursor: pointer;
position: relative;
border-radius: var(--border-radius);
}
.accordion-button:not(.collapsed) {
color: var(--color-primary);
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.accordion-button::after {
content: '+';
position: absolute;
right: calc(var(--base-spacing) * 1.5);
top: 50%;
transform: translateY(-50%);
font-size: 1.5rem;
font-weight: 300;
transition: transform 0.2s ease-in-out;
}
.accordion-button:not(.collapsed)::after {
content: '';
transform: translateY(-50%) rotate(45deg);
}
.accordion-collapse {
display: none;
}
.accordion-collapse.show {
display: block;
}
.accordion-body {
padding: calc(var(--base-spacing) * 1.5);
background-color: var(--bg-color);
border-bottom-left-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
/* --- Terms Page --- */
.terms-content {
max-width: 800px;
margin: 0 auto;
background-color: var(--surface-color);
padding: calc(var(--base-spacing) * 2);
border-radius: var(--border-radius);
}
.terms-content h2 {
font-size: 1.75rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--border-color);
}
.terms-content h3 {
font-size: 1.25rem;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
color: var(--color-primary);
}
.terms-content p, .terms-content ul, .terms-content ol {
margin-bottom: 1rem;
line-height: 1.7;
}
.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);
}
/* --- How It Works Section --- */
.how-it-works {
padding: calc(var(--base-spacing) * 4) 0;
background-color: var(--bg-color);
}
.how-it-works .section-header {
text-align: center;
margin-bottom: calc(var(--base-spacing) * 3);
}
.how-it-works .section-header h2 {
font-size: 2.5rem;
}
.how-it-works .section-header p {
font-size: 1.1rem;
color: var(--subtle-text-color);
max-width: 500px;
margin: 0.5rem auto 0;
}
.steps-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: calc(var(--base-spacing) * 2.5);
}
.step-card {
background-color: var(--surface-color);
border-radius: var(--border-radius);
padding: calc(var(--base-spacing) * 2);
text-align: center;
border: 1px solid var(--border-color);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.step-card:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0,0,0,0.08);
}
.step-icon-wrapper {
display: inline-flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
border-radius: 50%;
background: var(--gradient);
margin-bottom: calc(var(--base-spacing) * 1.5);
color: white;
}
.step-icon-wrapper svg {
width: 36px;
height: 36px;
}
.step-content h3 {
font-size: 1.3rem;
margin-bottom: 0.5rem;
}
.step-content p {
color: var(--subtle-text-color);
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 3"><path d="M0 0h5v3H0z"/><path d="M0 1h5v2H0z" fill="#D00"/><path d="M0 2h5v1H0z" fill="#FFCE00"/></svg>

Before

Width:  |  Height:  |  Size: 160 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30"><clipPath id="a"><path d="M0 0v30h60V0z"/></clipPath><clipPath id="b"><path d="M30 15h30v15zv15H0zH0V0h30z"/></clipPath><g clip-path="url(#a)"><path d="M0 0v30h60V0z" fill="#00247d"/><path d="M0 0l60 30m0-30L0 30" stroke="#fff" stroke-width="6"/><path d="M0 0l60 30m0-30L0 30" clip-path="url(#b)" stroke="#cf142b" stroke-width="4"/><path d="M30 0v30M0 15h60" stroke="#fff" stroke-width="10"/><path d="M30 0v30M0 15h60" stroke="#cf142b" stroke-width="6"/></g></svg>

Before

Width:  |  Height:  |  Size: 524 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><path d="M0 0h3v2H0z" fill="#c60b1e"/><path d="M0 .5h3v1H0z" fill="#ffc400"/></svg>

Before

Width:  |  Height:  |  Size: 141 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><path d="M0 0h1v2H0z" fill="#0055a4"/><path d="M1 0h1v2H1z" fill="#fff"/><path d="M2 0h1v2H2z" fill="#ef4135"/></svg>

Before

Width:  |  Height:  |  Size: 175 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 5"><path fill="#fff" d="M0 0h8v5H0z"/><path fill="#dc143c" d="M0 2.5h8V5H0z"/></svg>

Before

Width:  |  Height:  |  Size: 139 B

View File

@ -1,132 +0,0 @@
document.addEventListener('DOMContentLoaded', function () {
// --- Feather Icons ---
feather.replace();
// --- Theme Switcher ---
const themeSwitch = document.getElementById('theme-checkbox');
const currentTheme = localStorage.getItem('theme');
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark') {
themeSwitch.checked = true;
}
}
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
if (themeSwitch) {
themeSwitch.addEventListener('change', switchTheme, false);
}
// --- Generator Form: Photo Upload ---
const uploadArea = document.getElementById('upload-area');
const fileInput = document.getElementById('photo-upload');
const photoPreview = document.getElementById('photo-preview');
const MAX_FILES = 5;
let uploadedFiles = [];
if (uploadArea) {
uploadArea.addEventListener('click', () => fileInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.style.borderColor = 'var(--color-primary)';
});
uploadArea.addEventListener('dragleave', (e) => {
e.preventDefault();
uploadArea.style.borderColor = 'var(--border-color)';
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.style.borderColor = 'var(--border-color)';
const files = e.dataTransfer.files;
handleFiles(files);
});
fileInput.addEventListener('change', (e) => {
const files = e.target.files;
handleFiles(files);
});
}
function handleFiles(files) {
for (const file of files) {
if (uploadedFiles.length < MAX_FILES && file.type.startsWith('image/')) {
uploadedFiles.push(file);
}
}
updatePreview();
}
function updatePreview() {
photoPreview.innerHTML = '';
uploadedFiles.forEach((file, index) => {
const reader = new FileReader();
reader.onload = (e) => {
const previewItem = document.createElement('div');
previewItem.classList.add('preview-item');
const img = document.createElement('img');
img.src = e.target.result;
previewItem.appendChild(img);
const removeBtn = document.createElement('button');
removeBtn.classList.add('remove-img-btn');
removeBtn.innerHTML = '&times;';
removeBtn.addEventListener('click', () => {
uploadedFiles.splice(index, 1);
updatePreview();
});
previewItem.appendChild(removeBtn);
photoPreview.appendChild(previewItem);
};
reader.readAsDataURL(file);
});
}
// --- Copy to Clipboard ---
const copyBtn = document.getElementById('copy-btn');
if(copyBtn) {
copyBtn.addEventListener('click', () => {
const title = document.getElementById('result-title')?.innerText || '';
const shortDesc = document.getElementById('result-short-desc')?.innerText || '';
const desc = document.getElementById('result-desc')?.innerText || '';
const measurements = document.getElementById('result-measurements')?.innerText || '';
const hashtags = document.getElementById('result-hashtags')?.innerText || '';
const fullText = `
${title}
${shortDesc}
${desc}
Measurements: ${measurements}
${hashtags}
`;
navigator.clipboard.writeText(fullText.trim()).then(() => {
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = 'Copied!';
setTimeout(() => {
copyBtn.innerHTML = originalText;
}, 2000);
}).catch(err => {
console.error('Failed to copy: ', err);
});
});
}
});

View File

@ -1,29 +0,0 @@
<?php
// This script is meant to be run from the command line.
if (php_sapi_name() !== 'cli') {
die("This script can only be run from the command line.");
}
require_once __DIR__ . '/config.php';
if ($argc < 2) {
echo "Usage: php promote_user.php <email>\n";
exit(1);
}
$email = $argv[1];
try {
$pdo = db();
$stmt = $pdo->prepare("UPDATE users SET role = 'admin' WHERE email = ?");
$stmt->execute([$email]);
if ($stmt->rowCount() > 0) {
echo "User '{$email}' has been promoted to admin.\n";
} else {
echo "Could not find a user with email '{$email}'.\n";
}
} catch (PDOException $e) {
die("Database error: " . $e->getMessage() . "\n");
}

View File

@ -1,71 +0,0 @@
<?php
require_once __DIR__ . '/config.php';
function columnExists($pdo, $table, $column) {
try {
$stmt = $pdo->prepare("SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?");
$stmt->execute([$table, $column]);
return $stmt->fetchColumn() !== false;
} catch (PDOException $e) {
// If the query fails, we can assume the column doesn't exist or there's a bigger issue.
return false;
}
}
try {
$pdo = db();
// Create users table
$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
);";
$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'");
echo "Column 'role' added to 'users' table.\n";
}
// Add subscription_plan column to users table
if (!columnExists($pdo, 'users', 'subscription_plan')) {
$pdo->exec("ALTER TABLE users ADD COLUMN subscription_plan VARCHAR(50) DEFAULT NULL");
echo "Column 'subscription_plan' added to 'users' table.\n";
}
// Add subscription_expires_at column to users table
if (!columnExists($pdo, 'users', 'subscription_expires_at')) {
$pdo->exec("ALTER TABLE users ADD COLUMN subscription_expires_at DATE DEFAULT NULL");
echo "Column 'subscription_expires_at' added to 'users' table.\n";
}
// Create generations table
$sql_generations = "
CREATE TABLE IF NOT EXISTS generations (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
prompt TEXT,
description TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);";
$pdo->exec($sql_generations);
echo "Table 'generations' is ready.\n";
echo "\nDatabase setup completed successfully!\n";
} catch (PDOException $e) {
die("DB ERROR: " . $e->getMessage());
}

65
faq.php
View File

@ -1,65 +0,0 @@
<?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">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
<?php echo t('faq_q1_title'); ?>
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#faqAccordion">
<div class="accordion-body">
<?php echo t('faq_q1_text'); ?>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingTwo">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
<?php echo t('faq_q2_title'); ?>
</button>
</h2>
<div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#faqAccordion">
<div class="accordion-body">
<?php echo t('faq_q2_text'); ?>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingThree">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
<?php echo t('faq_q3_title'); ?>
</button>
</h2>
<div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#faqAccordion">
<div class="accordion-body">
<?php echo t('faq_q3_text'); ?>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingFour">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
<?php echo t('faq_q4_title'); ?>
</button>
</h2>
<div id="collapseFour" class="accordion-collapse collapse" aria-labelledby="headingFour" data-bs-parent="#faqAccordion">
<div class="accordion-body">
<?php echo t('faq_q4_text'); ?>
</div>
</div>
</div>
</div>
</main>
<?php require_once 'includes/footer.php'; ?>

View File

@ -1,208 +0,0 @@
<?php
session_start();
require_once __DIR__ . '/includes/translations.php';
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/ai/LocalAIApi.php';
// Set language from session or default to English
$lang = $_SESSION['lang'] ?? 'en';
// --- Helper Functions (omitted for brevity, same as before) ---
function translate_to_english($text, $source_lang) { if ($source_lang === 'en') return $text; $prompt = "Translate the following text from '{$source_lang}' to English. Only return the translated text, with no extra commentary:\n\n{$text}"; $response = LocalAIApi::createResponse(['input' => [['role' => 'system', 'content' => 'You are a translation assistant.'],['role' => 'user', 'content' => $prompt]]]); if (!empty($response['success'])) { $decoded = LocalAIApi::decodeJsonFromResponse($response); return $decoded['choices'][0]['message']['content'] ?? $text; } return $text; }
function translate_from_english($text, $target_lang) { if ($target_lang === 'en') return $text; $prompt = "Translate the following text from English to '{$target_lang}'. Only return the translated text, with no extra commentary:\n\n{$text}"; $response = LocalAIApi::createResponse(['input' => [['role' => 'system', 'content' => 'You are a translation assistant.'],['role' => 'user', 'content' => $prompt]]]); if (!empty($response['success'])) { $decoded = LocalAIApi::decodeJsonFromResponse($response); return $decoded['choices'][0]['message']['content'] ?? $text; } return $text; }
// --- Main Logic ---
$result_html = '';
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// --- 0. Subscription & Limit Check ---
if (isset($_SESSION['user_id'])) {
$user_id = $_SESSION['user_id'];
$pdo = db();
// Fetch user subscription details
$stmt = $pdo->prepare("SELECT subscription_plan, subscription_expires_at FROM users WHERE id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();
$is_active = $user && $user['subscription_expires_at'] && new DateTime() < new DateTime($user['subscription_expires_at']);
if (!$is_active) {
$_SESSION['generation_error'] = t('subscription_expired_error');
header('Location: pricing.php');
exit();
}
if ($user['subscription_plan'] === 'basic') {
// Count generations in the last 30 days
$stmt = $pdo->prepare("SELECT COUNT(*) FROM generations WHERE user_id = ? AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)");
$stmt->execute([$user_id]);
$generation_count = $stmt->fetchColumn();
if ($generation_count >= 150) {
$_SESSION['generation_error'] = t('generation_limit_exceeded_error');
header('Location: pricing.php');
exit();
}
}
} else {
// If user is not logged in, redirect to login page
$_SESSION['generation_error'] = t('login_to_generate_error');
header('Location: login.php');
exit();
}
// --- 1. Collect and Sanitize Inputs ---
$state = htmlspecialchars($_POST['state'] ?? '');
$size = htmlspecialchars($_POST['size'] ?? '');
$material = htmlspecialchars($_POST['material'] ?? '');
$brand = htmlspecialchars($_POST['brand'] ?? '');
$length = htmlspecialchars($_POST['length'] ?? '');
$chest_width = htmlspecialchars($_POST['chest_width'] ?? '');
$waist_width = htmlspecialchars($_POST['waist_width'] ?? '');
$hips_width = htmlspecialchars($_POST['hips_width'] ?? '');
$additional_info = htmlspecialchars($_POST['additional_info'] ?? '');
// --- 2. Validation ---
if (empty($state) || empty($size)) {
$_SESSION['generation_error'] = t('error_required_fields');
header('Location: index.php#generator');
exit();
}
// --- 3. Translation Pipeline (Input) ---
$state_en = translate_to_english($state, $lang);
$size_en = translate_to_english($size, $lang);
$material_en = translate_to_english($material, $lang);
$brand_en = translate_to_english($brand, $lang);
$additional_info_en = translate_to_english($additional_info, $lang);
// --- 4. Construct AI Prompt ---
$prompt_parts = [
"Generate a product listing for an online clothing store. The target audience is women aged 22-60.",
"The output must be a JSON object with the following keys: 'title', 'short_description', 'description', 'measurements', 'hashtags'.",
"The tone should be engaging, concise, and SEO-aware, incorporating trending keywords naturally.",
"Do not add any extra commentary or editorializing. Stick to the facts provided."
];
if ($state_en === 'used') {
$prompt_parts[] = "Condition: Used (perfect condition).";
} elseif ($state_en === 'visibly_used') {
$prompt_parts[] = "Condition: Visibly used (may have some signs of use).";
} else {
$prompt_parts[] = "Condition: New.";
}
$prompt_parts[] = "Size: {$size_en}";
if (!empty($material_en)) $prompt_parts[] = "Material: {$material_en}";
if (!empty($brand_en)) $prompt_parts[] = "Brand: {$brand_en}";
$measurements_parts = [];
if (!empty($length)) $measurements_parts[] = "Length: {$length} cm";
if (!empty($chest_width)) $measurements_parts[] = "Chest width: {$chest_width} cm";
if (!empty($waist_width)) $measurements_parts[] = "Waist width: {$waist_width} cm";
if (!empty($hips_width)) $measurements_parts[] = "Hips width: {$hips_width} cm";
if (!empty($measurements_parts)) {
$prompt_parts[] = "Measurements: " . implode(', ', $measurements_parts);
}
if (!empty($additional_info_en)) {
$prompt_parts[] = "Additional Details: {$additional_info_en}";
}
$prompt_parts[] = "Generate the output in English.";
$prompt = implode("\n", $prompt_parts);
// --- 4. Call AI API ---
$ai_response = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => 'You are an expert copywriter for e-commerce fashion brands.'],
['role' => 'user', 'content' => $prompt],
],
'model' => 'gpt-3.5-turbo', // Using a cost-effective model
]);
if (!empty($ai_response['success'])) {
$decoded_response = LocalAIApi::decodeJsonFromResponse($ai_response);
$generated_text = $decoded_response['choices'][0]['message']['content'] ?? '';
// Find the JSON part of the response
$json_start = strpos($generated_text, '{');
$json_end = strrpos($generated_text, '}');
if ($json_start !== false && $json_end !== false) {
$json_str = substr($generated_text, $json_start, $json_end - $json_start + 1);
$output_data = json_decode($json_str, true);
if ($output_data && json_last_error() === JSON_ERROR_NONE) {
// --- 5. Translation Pipeline (Output) ---
$title = translate_from_english(htmlspecialchars($output_data['title'] ?? ''), $lang);
$short_desc = translate_from_english(htmlspecialchars($output_data['short_description'] ?? ''), $lang);
$description = nl2br(translate_from_english(htmlspecialchars($output_data['description'] ?? ''), $lang));
$measurements = translate_from_english(htmlspecialchars($output_data['measurements'] ?? ''), $lang);
$hashtags = translate_from_english(htmlspecialchars($output_data['hashtags'] ?? ''), $lang);
// --- 6. Save to DB if user is logged in ---
if (isset($_SESSION['user_id'])) {
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO generations (user_id, prompt, description) VALUES (?, ?, ?)");
$stmt->execute([$_SESSION['user_id'], $prompt, $json_str]);
} catch (PDOException $e) {
// Optional: log error to a file
error_log("DB Error: " . $e->getMessage());
}
}
// --- 7. Format Output ---
$result_html = "
<div class='result-container'>
<h3>" . t('generated_result') . "</h3>
<div class='result-item'>
<label>" . t('output_title') . "</label>
<p id='result-title'>{$title}</p>
</div>
<div class='result-item'>
<label>" . t('output_short_description') . "</label>
<p id='result-short-desc'>{$short_desc}</p>
</div>
<div class='result-item'>
<label>" . t('output_description') . "</label>
<p id='result-desc'>{$description}</p>
</div>
<div class='result-item'>
<label>" . t('output_measurements') . "</label>
<p id='result-measurements'>{$measurements}</p>
</div>
<div class='result-item'>
<label>" . t('output_hashtags') . "</label>
<p id='result-hashtags'>{$hashtags}</p>
</div>
<div class='result-actions'>
<button class='btn btn-secondary' id='copy-btn'><i class='ph ph-copy'></i> " . t('copy_to_clipboard') . "</button>
<button class='btn btn-primary' id='approve-btn'><i class='ph ph-check'></i> " . t('approve') . "</button>
</div>
</div>
";
} else {
$error_message = t('error_parsing_ai_response');
}
} else {
$error_message = t('error_parsing_ai_response');
}
} else {
$error_message = t('error_calling_ai_api') . ': ' . htmlspecialchars($ai_response['error'] ?? 'Unknown error');
}
}
// Store result in session to display on the main page
$_SESSION['generation_result'] = $result_html;
$_SESSION['generation_error'] = $error_message;
// Redirect back to the main page
header('Location: index.php#generator');
exit();

View File

@ -1,105 +0,0 @@
<?php
require_once 'includes/header.php';
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/ai/LocalAIApi.php';
// Redirect to login if not logged in
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit();
}
// Helper function for translation
function translate_from_english($text, $target_lang) {
if ($target_lang === 'en') {
return $text;
}
// Simulate translation
$prompt = "Translate the following text from English to '{$target_lang}'. Only return the translated text, with no extra commentary:\n\n{$text}";
$response = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => 'You are a translation assistant.'],
['role' => 'user', 'content' => $prompt],
],
]);
if (!empty($response['success'])) {
$decoded = LocalAIApi::decodeJsonFromResponse($response);
return $decoded['choices'][0]['message']['content'] ?? $text;
}
return $text; // Fallback to original text on error
}
$user_id = $_SESSION['user_id'];
// Fetch history from the database
$generations = [];
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM generations WHERE user_id = ? ORDER BY created_at DESC");
$stmt->execute([$user_id]);
$generations = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// Optional: handle error
error_log("DB Error: " . $e->getMessage());
}
?>
<main class="container">
<h1 class="page-title"><?php echo t('history_title'); ?></h1>
<?php if (empty($generations)): ?>
<div class="card text-center">
<div class="card-body">
<p><?php echo t('history_empty'); ?></p>
<a href="index.php#generator" class="btn btn-primary"><?php echo t('history_generate_now'); ?></a>
</div>
</div>
<?php else: ?>
<div class="history-list">
<?php foreach ($generations as $index => $generation): ?>
<?php
$output_data = json_decode($generation['description'], true);
if (!$output_data) continue; // Skip if JSON is invalid
// Translate the fields for display
$title = translate_from_english(htmlspecialchars($output_data['title'] ?? ''), $lang);
$short_desc = translate_from_english(htmlspecialchars($output_data['short_description'] ?? ''), $lang);
$description = nl2br(translate_from_english(htmlspecialchars($output_data['description'] ?? ''), $lang));
$measurements = translate_from_english(htmlspecialchars($output_data['measurements'] ?? ''), $lang);
$hashtags = translate_from_english(htmlspecialchars($output_data['hashtags'] ?? ''), $lang);
?>
<div class="card history-item">
<div class="card-header">
<span><?php echo t('generation_date'); ?>: <?php echo date('F j, Y, g:i a', strtotime($generation['created_at'])); ?></span>
<button class="btn btn-sm btn-secondary copy-btn" data-clipboard-target="#history-content-<?php echo $index; ?>"><i class="ph ph-copy"></i> <?php echo t('copy_to_clipboard'); ?></button>
</div>
<div class="card-body" id="history-content-<?php echo $index; ?>">
<div class="result-item">
<label><?php echo t('output_title'); ?></label>
<p><?php echo $title; ?></p>
</div>
<div class="result-item">
<label><?php echo t('output_short_description'); ?></label>
<p><?php echo $short_desc; ?></p>
</div>
<div class="result-item">
<label><?php echo t('output_description'); ?></label>
<p><?php echo $description; ?></p>
</div>
<div class="result-item">
<label><?php echo t('output_measurements'); ?></label>
<p><?php echo $measurements; ?></p>
</div>
<div class="result-item">
<label><?php echo t('output_hashtags'); ?></label>
<p><?php echo $hashtags; ?></p>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</main>
<?php include __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,12 +0,0 @@
<footer>
<div class="container">
<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

@ -1,101 +0,0 @@
<?php
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
require_once __DIR__ . '/translations.php';
// Handle language switching
if (isset($_GET['lang'])) {
$_SESSION['lang'] = $_GET['lang'];
// Remove the lang parameter from the URL
$url = strtok($_SERVER["REQUEST_URI"], '?');
// Re-add other query parameters if they exist
$query = parse_url($_SERVER["REQUEST_URI"], PHP_URL_QUERY);
if ($query) {
parse_str($query, $params);
unset($params['lang']);
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
}
header('Location: ' . $url);
exit();
}
$lang = get_current_language();
$is_logged_in = isset($_SESSION['user_id']);
?>
<!DOCTYPE html>
<html lang="<?php echo $lang; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo t('app_name'); ?></title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<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://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body data-theme="light">
<header class="header">
<div class="container">
<a href="index.php" class="logo"><?php echo t('app_name'); ?></a>
<nav class="main-nav">
<a href="index.php#generator"><?php echo t('nav_generator'); ?></a>
<a href="pricing.php"><?php echo t('nav_pricing'); ?></a>
<a href="faq.php"><?php echo t('nav_faq'); ?></a>
<a href="terms.php"><?php echo t('nav_terms'); ?></a>
<?php if ($is_logged_in): ?>
<a href="history.php"><?php echo t('nav_history'); ?></a>
<?php if (isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin'): ?>
<a href="admin.php"><?= t('admin_panel_title') ?></a>
<?php endif; ?>
<?php endif; ?>
</nav>
<div class="header-actions">
<div class="language-switcher">
<div class="selected-lang">
<img src="assets/images/flags/<?php echo $lang; ?>.svg" alt="<?php echo $lang; ?>">
<i data-feather="chevron-down"></i>
</div>
<div class="lang-dropdown">
<?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>
</a>
<?php endforeach; ?>
</div>
</div>
<div class="theme-switch-wrapper">
<label class="theme-switch" for="theme-checkbox">
<input type="checkbox" id="theme-checkbox" />
<div class="slider round"></div>
</label>
</div>
<?php if ($is_logged_in): ?>
<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>
<?php endif; ?>
</div>
</div>
</header>

View File

@ -1,307 +0,0 @@
<?php
// Function to get the current language
function get_current_language() {
if (isset($_SESSION['lang']) && array_key_exists($_SESSION['lang'], get_translations())) {
return $_SESSION['lang'];
}
return 'en'; // Default language
}
// Function to translate a key
function t($key) {
$translations = get_translations();
$lang = get_current_language();
return $translations[$lang][$key] ?? $key;
}
function get_translations() {
return [
'en' => [
'app_name' => 'AI Content Generator',
'nav_generator' => 'Generator',
'nav_pricing' => 'Pricing',
'nav_faq' => 'FAQ',
'nav_terms' => 'Terms',
'nav_history' => 'History',
'nav_profile' => 'Profile',
'nav_logout' => 'Logout',
'nav_register' => 'Register',
'nav_login' => 'Login',
'admin_panel_title' => 'Admin Panel',
'auth_form_title' => 'Login to your account',
'auth_form_email' => 'Email',
'auth_form_password' => 'Password',
'auth_form_submit' => 'Login',
'auth_form_no_account' => "Don\'t have an account?",
'auth_form_register_link' => 'Register here',
'registration_form_title' => 'Create a new account',
'registration_form_username' => 'Username',
'registration_form_email' => 'Email',
'registration_form_password' => 'Password',
'registration_form_password_confirm' => 'Confirm Password',
'registration_form_submit' => 'Register',
'registration_form_has_account' => 'Already have an account?',
'registration_form_login_link' => 'Login here',
'password_reset_title' => 'Reset Password',
'password_reset_email' => 'Email',
'password_reset_submit' => 'Send Password Reset Link',
'password_reset_return_to_login' => 'Return to Login',
'profile_title' => 'Profile',
'profile_username' => 'Username',
'profile_email' => 'Email',
'profile_created_at' => 'Registration Date',
'profile_credits' => 'Credits',
'profile_subscription_status' => 'Subscription Status',
'profile_subscription_none' => 'None',
'profile_subscription_active' => 'Active',
'profile_subscription_expires' => 'Expires on',
'profile_manage_subscription_btn' => 'Manage Subscription',
'pricing_title' => 'Choose Your Plan',
'pricing_subtitle' => 'Unlock premium features and get more credits.',
'pricing_free_title' => 'Free',
'pricing_free_price' => '$0 / month',
'pricing_free_feature_1' => '10 credits',
'pricing_free_feature_2' => 'Basic content generation',
'pricing_free_feature_3' => 'Access to history',
'pricing_free_btn' => 'Current Plan',
'pricing_pro_title' => 'Pro',
'pricing_pro_price' => '$10 / month',
'pricing_pro_feature_1' => '100 credits per month',
'pricing_pro_feature_2' => 'Advanced content generation',
'pricing_pro_feature_3' => 'Priority support',
'pricing_pro_btn' => 'Subscribe',
'faq_title' => 'Frequently Asked Questions',
'faq_q1_title' => 'What is AI Content Generator?',
'faq_q1_text' => 'It is a tool that uses artificial intelligence to generate content for various purposes.',
'faq_q2_title' => 'How do credits work?',
'faq_q2_text' => 'Each generation consumes a certain amount of credits. You can get more by subscribing.',
'faq_q3_title' => 'Can I use the generated content for commercial purposes?',
'faq_q3_text' => 'Yes, you can use the generated content for any purpose, including commercial projects.',
'faq_q4_title' => 'What languages are supported?',
'faq_q4_text' => 'Currently, we support English and Polish. More languages will be added in the future.',
'terms_title' => 'Terms of Service',
'terms_last_updated' => 'Last Updated: 2025-11-13',
'terms_section1_title' => 'Acceptance of Terms',
'terms_section1_text' => 'By accessing and using our service, you accept and agree to be bound by the terms and provision of this agreement.',
'terms_section2_title' => 'Service Description',
'terms_section2_text' => 'Our service provides AI-generated content based on user inputs. The service is provided "as is" without any warranties.',
'terms_section3_title' => 'User Conduct',
'terms_section3_text' => 'You agree not to use the service for any unlawful purpose or any purpose prohibited under this clause.',
'terms_section4_title' => 'Intellectual Property',
'terms_section4_text' => 'The Service and its original content, features, and functionality are and will remain the exclusive property of the service provider.',
'terms_section5_title' => 'Termination',
'terms_section5_text' => 'We may terminate or suspend your access to our service immediately, without prior notice or liability, for any reason whatsoever.',
'history_title' => 'Generation History',
'history_no_generations' => 'You have no generations yet.',
'generation_placeholder' => 'Enter your topic or keywords here...',
'generation_btn' => 'Generate',
'error_title' => 'Error',
'error_message' => 'An unexpected error occurred.',
'insufficient_credits_error' => 'Insufficient credits to proceed with the operation',
'subscription_expired_error' => 'Your subscription has expired.',
'generation_limit_exceeded_error' => 'You have exceeded your generation limit for today.',
'login_to_generate_error' => 'You need to be logged in to generate content.',
'create_account' => 'Create a new account',
'register_subtitle' => 'Join our platform to start generating content.',
'email' => 'Email',
'password' => 'Password',
'register_button' => 'Register',
'already_have_account' => 'Already have an account?',
'login_link' => 'Login here',
'fill_all_fields' => 'Please fill in all fields.',
'invalid_email' => 'Invalid email format.',
'email_already_registered' => 'This email is already registered.',
'registration_successful' => 'Registration successful!',
'login_now' => 'Login now',
'passwords_do_not_match' => 'Passwords do not match.',
'hero_title' => 'Create Unique Descriptions in Seconds',
'hero_subtitle' => 'Our AI will help you create engaging content for your products.',
'hero_cta' => 'Try it now',
'generator_form_title' => 'Generate a Description',
'upload_label' => 'Upload Photos',
'state_label' => 'State',
'state_new' => 'New',
'state_used' => 'Used',
'state_visibly_used' => 'Visibly Used',
'material_label' => 'Material',
'brand_label' => 'Brand',
'size_label' => 'Size',
'length_label' => 'Length (cm)',
'width_chest_label' => 'Chest Width (cm)',
'width_waist_label' => 'Waist Width (cm)',
'width_hips_label' => 'Hips Width (cm)',
'additional_info_label' => 'Additional Info',
'additional_info_placeholder' => 'e.g., any defects, special features',
'generate_button' => 'Generate',
'how_it_works_title' => 'How It Works',
'step1_title' => 'Upload Photos',
'step1_desc' => 'Add images of your product.',
'step2_title' => 'Add Details',
'step2_desc' => 'Provide some basic information.',
'step3_title' => 'Generate Content',
'step3_desc' => 'Let our AI do the magic.',
'basic_plan_name' => 'Basic',
'premium_plan_name' => 'Premium',
'basic_plan_limit' => 'Up to 150 generations per month',
'premium_plan_limit' => 'Unlimited generations',
'feature_150_generations' => '150 generations',
'feature_standard_support' => 'Standard support',
'feature_history_access' => 'Access to generation history',
'feature_unlimited_generations' => 'Unlimited generations',
'feature_priority_support' => 'Priority support',
'monthly' => 'Monthly',
'annually' => 'Annually',
'save_15_percent' => 'Save 15%',
'month_short' => 'mo',
'billed_annually_at' => 'Billed annually at',
'choose_plan' => 'Choose Plan',
'pricing_footer_text' => 'Prices are in USD. You can cancel at any time.',
'history_empty' => 'You have no generation history yet. Create your first one!',
'history_generate_now' => 'Generate Now',
],
'pl' => [
'app_name' => 'Generator Treści AI',
'nav_generator' => 'Generator',
'nav_pricing' => 'Cennik',
'nav_faq' => 'FAQ',
'nav_terms' => 'Regulamin',
'nav_history' => 'Historia',
'nav_profile' => 'Profil',
'nav_logout' => 'Wyloguj',
'nav_register' => 'Rejestracja',
'nav_login' => 'Login',
'admin_panel_title' => 'Panel administratora',
'auth_form_title' => 'Zaloguj się na swoje konto',
'auth_form_email' => 'Email',
'auth_form_password' => 'Hasło',
'auth_form_submit' => 'Zaloguj',
'auth_form_no_account' => "Nie masz konta?",
'auth_form_register_link' => 'Zarejestruj się tutaj',
'registration_form_title' => 'Utwórz nowe konto',
'registration_form_username' => 'Nazwa użytkownika',
'registration_form_email' => 'Email',
'registration_form_password' => 'Hasło',
'registration_form_password_confirm' => 'Potwierdź hasło',
'registration_form_submit' => 'Zarejestruj',
'registration_form_has_account' => 'Masz już konto?',
'registration_form_login_link' => 'Zaloguj się tutaj',
'password_reset_title' => 'Resetuj hasło',
'password_reset_email' => 'Email',
'password_reset_submit' => 'Wyślij link do resetowania hasła',
'password_reset_return_to_login' => 'Wróć do logowania',
'profile_title' => 'Profil',
'profile_username' => 'Nazwa użytkownika',
'profile_email' => 'Email',
'profile_created_at' => 'Data rejestracji',
'profile_credits' => 'Kredyty',
'profile_subscription_status' => 'Status subskrypcji',
'profile_subscription_none' => 'Brak',
'profile_subscription_active' => 'Aktywna',
'profile_subscription_expires' => 'Wygasa dnia',
'profile_manage_subscription_btn' => 'Zarządzaj subskrypcją',
'pricing_title' => 'Wybierz swój plan',
'pricing_subtitle' => 'Odblokuj funkcje premium i zdobądź więcej kredytów.',
'pricing_free_title' => 'Darmowy',
'pricing_free_price' => '0 zł / miesiąc',
'pricing_free_feature_1' => '10 kredytów',
'pricing_free_feature_2' => 'Podstawowe generowanie treści',
'pricing_free_feature_3' => 'Dostęp do historii',
'pricing_free_btn' => 'Obecny plan',
'pricing_pro_title' => 'Pro',
'pricing_pro_price' => '40 zł / miesiąc',
'pricing_pro_feature_1' => '100 kredytów miesięcznie',
'pricing_pro_feature_2' => 'Zaawansowane generowanie treści',
'pricing_pro_feature_3' => 'Priorytetowe wsparcie',
'pricing_pro_btn' => 'Subskrybuj',
'faq_title' => 'Najczęściej zadawane pytania',
'faq_q1_title' => 'Czym jest Generator Treści AI?',
'faq_q1_text' => 'Jest to narzędzie, które wykorzystuje sztuczną inteligencję do generowania treści w różnych celach.',
'faq_q2_title' => 'Jak działają kredyty?',
'faq_q2_text' => 'Każde generowanie zużywa określoną ilość kredytów. Możesz uzyskać więcej, subskrybując.',
'faq_q3_title' => 'Czy mogę używać wygenerowanych treści w celach komercyjnych?',
'faq_q3_text' => 'Tak, możesz używać wygenerowanych treści w dowolnym celu, w tym w projektach komercyjnych.',
'faq_q4_title' => 'Jakie języki są obsługiwane?',
'faq_q4_text' => 'Obecnie obsługujemy język angielski i polski. W przyszłości zostanie dodanych więcej języków.',
'terms_title' => 'Regulamin usługi',
'terms_last_updated' => 'Ostatnia aktualizacja: 2025-11-13',
'terms_section1_title' => 'Akceptacja warunków',
'terms_section1_text' => 'Korzystając z naszej usługi, akceptujesz i zgadzasz się na przestrzeganie warunków niniejszej umowy.',
'terms_section2_title' => 'Opis usługi',
'terms_section2_text' => 'Nasza usługa dostarcza treści generowane przez sztuczną inteligencję na podstawie danych wprowadzonych przez użytkownika. Usługa jest świadczona w stanie, w jakim się znajduje, bez żadnych gwarancji.',
'terms_section3_title' => 'Postępowanie użytkownika',
'terms_section3_text' => 'Zgadzasz się nie używać usługi w żadnym celu niezgodnym z prawem lub zabronionym na mocy niniejszej klauzuli.',
'terms_section4_title' => 'Własność intelektualna',
'terms_section4_text' => 'Usługa i jej oryginalna treść, funkcje i funkcjonalność są i pozostaną wyłączną własnością dostawcy usługi.',
'terms_section5_title' => 'Zakończenie świadczenia usług',
'terms_section5_text' => 'Możemy natychmiast zakończyć lub zawiesić Twój dostęp do naszej usługi, bez wcześniejszego powiadomienia lub odpowiedzialności, z dowolnego powodu.',
'history_title' => 'Historia generowania',
'history_no_generations' => 'Nie masz jeszcze żadnych generacji.',
'generation_placeholder' => 'Wpisz tutaj swój temat lub słowa kluczowe...',
'generation_btn' => 'Generuj',
'error_title' => 'Błąd',
'error_message' => 'Wystąpił nieoczekiwany błąd.',
'insufficient_credits_error' => 'Niewystarczająca ilość kredytów, aby kontynuować operację',
'subscription_expired_error' => 'Twoja subskrypcja wygasła.',
'generation_limit_exceeded_error' => 'Przekroczyłeś limit generowania na dziś.',
'login_to_generate_error' => 'Musisz być zalogowany, aby generować treści.',
'create_account' => 'Utwórz nowe konto',
'register_subtitle' => 'Dołącz do naszej platformy, aby zacząć generować treści.',
'email' => 'Email',
'password' => 'Hasło',
'register_button' => 'Zarejestruj',
'already_have_account' => 'Masz już konto?',
'login_link' => 'Zaloguj się tutaj',
'fill_all_fields' => 'Proszę wypełnić wszystkie pola.',
'invalid_email' => 'Nieprawidłowy format email.',
'email_already_registered' => 'Ten email jest już zarejestrowany.',
'registration_successful' => 'Rejestracja udana!',
'login_now' => 'Zaloguj się teraz',
'passwords_do_not_match' => 'Hasła nie pasują do siebie.',
'hero_title' => 'Twórz unikalne opisy w kilka sekund',
'hero_subtitle' => 'Nasza SI pomoże Ci stworzyć angażujące treści dla Twoich produktów.',
'hero_cta' => 'Wypróbuj teraz',
'generator_form_title' => 'Wygeneruj opis',
'upload_label' => 'Wgraj zdjęcia',
'state_label' => 'Stan',
'state_new' => 'Nowy',
'state_used' => 'Używany',
'state_visibly_used' => 'Widoczne ślady użytkowania',
'material_label' => 'Materiał',
'brand_label' => 'Marka',
'size_label' => 'Rozmiar',
'length_label' => 'Długość (cm)',
'width_chest_label' => 'Szerokość w klatce (cm)',
'width_waist_label' => 'Szerokość w pasie (cm)',
'width_hips_label' => 'Szerokość w biodrach (cm)',
'additional_info_label' => 'Dodatkowe informacje',
'additional_info_placeholder' => 'np. wady, cechy szczególne',
'generate_button' => 'Generuj',
'how_it_works_title' => 'Jak to działa',
'step1_title' => 'Wgraj zdjęcia',
'step1_desc' => 'Dodaj zdjęcia swojego produktu.',
'step2_title' => 'Dodaj szczegóły',
'step2_desc' => 'Podaj kilka podstawowych informacji.',
'step3_title' => 'Generuj treść',
'step3_desc' => 'Pozwól naszej SI działać.',
'basic_plan_name' => 'Podstawowy',
'premium_plan_name' => 'Premium',
'basic_plan_limit' => 'Do 150 generacji miesięcznie',
'premium_plan_limit' => 'Nielimitowane generacje',
'feature_150_generations' => '150 generacji',
'feature_standard_support' => 'Standardowe wsparcie',
'feature_history_access' => 'Dostęp do historii generacji',
'feature_unlimited_generations' => 'Nielimitowane generacje',
'feature_priority_support' => 'Priorytetowe wsparcie',
'monthly' => 'Miesięcznie',
'annually' => 'Rocznie',
'save_15_percent' => 'Zaoszczędź 15%',
'month_short' => 'mc',
'billed_annually_at' => 'Płatne rocznie w kwocie',
'choose_plan' => 'Wybierz plan',
'pricing_footer_text' => 'Ceny podane są w USD. Możesz anulować w dowolnym momencie.',
'history_empty' => 'Nie masz jeszcze historii generacji. Utwórz swoją pierwszą!',
'history_generate_now' => 'Generuj teraz',
],
];
}

273
index.php
View File

@ -1,135 +1,150 @@
<?php <?php
require_once 'includes/header.php'; declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?> ?>
<!doctype html>
<main> <html lang="en">
<section class="hero"> <head>
<div class="container"> <meta charset="utf-8" />
<h1><?php echo t('hero_title'); ?></h1> <meta name="viewport" content="width=device-width, initial-scale=1" />
<p><?php echo t('hero_subtitle'); ?></p> <title>New Style</title>
<a href="#generator" class="cta-button"><?php echo t('hero_cta'); ?></a> <?php
</div> // Read project preview data from environment
</section> $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<section id="generator" class="generator-section"> ?>
<div class="container"> <?php if ($projectDescription): ?>
<h2><?php echo t('generator_form_title'); ?></h2> <!-- Meta description -->
<form action="generate.php" method="POST" class="generator-form" enctype="multipart/form-data"> <meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<div class="form-grid"> <!-- Open Graph meta tags -->
<div class="form-column-wide"> <meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<div class="form-group"> <!-- Twitter meta tags -->
<label for="photo-upload"><?php echo t('upload_label'); ?></label> <meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<div class="photo-upload-container"> <?php endif; ?>
<input type="file" id="photo-upload" name="photos[]" multiple accept="image/*" style="display: none;"> <?php if ($projectImageUrl): ?>
<div class="upload-area" id="upload-area"> <!-- Open Graph image -->
<i data-feather="upload-cloud"></i> <meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<p>Click to browse or drag & drop your images here</p> <!-- Twitter image -->
</div> <meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<div class="photo-preview" id="photo-preview"></div> <?php endif; ?>
</div> <link rel="preconnect" href="https://fonts.googleapis.com">
</div> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
</div> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<div class="form-column"> <style>
<div class="form-group"> :root {
<label for="state"><?php echo t('state_label'); ?></label> --bg-color-start: #6a11cb;
<select id="state" name="state" required> --bg-color-end: #2575fc;
<option value="new"><?php echo t('state_new'); ?></option> --text-color: #ffffff;
<option value="used"><?php echo t('state_used'); ?></option> --card-bg-color: rgba(255, 255, 255, 0.01);
<option value="visibly_used"><?php echo t('state_visibly_used'); ?></option> --card-border-color: rgba(255, 255, 255, 0.1);
</select>
</div>
<div class="form-group">
<label for="material"><?php echo t('material_label'); ?></label>
<input type="text" id="material" name="material" placeholder="e.g., Cotton, Polyester">
</div>
<div class="form-group">
<label for="brand"><?php echo t('brand_label'); ?></label>
<input type="text" id="brand" name="brand" placeholder="e.g., Zara, Nike">
</div>
<div class="form-group">
<label for="size"><?php echo t('size_label'); ?></label>
<input type="text" id="size" name="size" placeholder="e.g., M, 38, 10" required>
</div>
</div>
<div class="form-column">
<div class="form-group">
<label for="length"><?php echo t('length_label'); ?></label>
<input type="number" id="length" name="length" placeholder="e.g., 88">
</div>
<div class="form-group">
<label for="chest-width"><?php echo t('width_chest_label'); ?></label>
<input type="number" id="chest-width" name="chest_width" placeholder="e.g., 45">
</div>
<div class="form-group">
<label for="waist-width"><?php echo t('width_waist_label'); ?></label>
<input type="number" id="waist-width" name="waist_width" placeholder="e.g., 35">
</div>
<div class="form-group">
<label for="hips-width"><?php echo t('width_hips_label'); ?></label>
<input type="number" id="hips-width" name="hips_width" placeholder="e.g., 50">
</div>
</div>
</div>
<div class="form-group">
<label for="additional-info"><?php echo t('additional_info_label'); ?></label>
<textarea id="additional-info" name="additional_info" rows="3" placeholder="<?php echo t('additional_info_placeholder'); ?>"></textarea>
</div>
<div class="form-group form-submit-group">
<button type="submit" class="cta-button"><?php echo t('generate_button'); ?></button>
</div>
</form>
<?php
if (isset($_SESSION['generation_result'])) {
echo $_SESSION['generation_result'];
unset($_SESSION['generation_result']);
} }
if (isset($_SESSION['generation_error'])) { body {
echo '<div class="error-message">' . $_SESSION['generation_error'] . '</div>'; margin: 0;
unset($_SESSION['generation_error']); font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
} }
?> body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div> </div>
</section> <p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<section class="how-it-works"> <p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
<div class="container">
<div class="section-header">
<h2><?php echo t('how_it_works_title'); ?></h2>
<p>Our platform simplifies content creation into three easy steps.</p>
</div> </div>
<div class="steps-container"> </main>
<div class="step-card"> <footer>
<div class="step-icon-wrapper"> Page updated: <?= htmlspecialchars($now) ?> (UTC)
<i data-feather="upload-cloud"></i> </footer>
</div> </body>
<div class="step-content"> </html>
<h3><?php echo t('step1_title'); ?></h3>
<p><?php echo t('step1_desc'); ?></p>
</div>
</div>
<div class="step-card">
<div class="step-icon-wrapper">
<i data-feather="edit-3"></i>
</div>
<div class="step-content">
<h3><?php echo t('step2_title'); ?></h3>
<p><?php echo t('step2_desc'); ?></p>
</div>
</div>
<div class="step-card">
<div class="step-icon-wrapper">
<i data-feather="zap"></i>
</div>
<div class="step-content">
<h3><?php echo t('step3_title'); ?></h3>
<p><?php echo t('step3_desc'); ?></p>
</div>
</div>
</div>
</div>
</section>
</main>
<?php include 'includes/footer.php'; ?>

View File

@ -1,11 +0,0 @@
<?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,62 +0,0 @@
<?php
require_once 'db/config.php';
require_once 'includes/header.php';
$error = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = db();
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($email) || empty($password)) {
$error = t('fill_all_fields');
} else {
try {
$stmt = $pdo->prepare('SELECT id, password, role FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_email'] = $email;
$_SESSION['user_role'] = $user['role'];
header('Location: index.php');
exit();
} else {
$error = t('invalid_credentials');
}
} catch (PDOException $e) {
$error = "Database error: " . $e->getMessage();
}
}
}
?>
<main class="container">
<section class="auth-form">
<h1><?= t('login_heading') ?></h1>
<p><?= t('login_subtitle') ?></p>
<?php if ($error): ?>
<div class="error-message"><?= $error ?></div>
<?php endif; ?>
<form action="login.php" method="POST" id="login-form" novalidate>
<div class="form-group">
<label for="email"><?= t('email') ?></label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="password"><?= t('password') ?></label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary"><?= t('login_button') ?></button>
</div>
</form>
<p class="auth-switch"><?= t('no_account') ?> <a href="register.php"><?= t('register_link') ?></a></p>
</section>
</main>
<?php include 'includes/footer.php'; ?>

View File

@ -1,6 +0,0 @@
<?php
session_start();
session_unset();
session_destroy();
header('Location: index.php');
exit();

View File

@ -1,82 +0,0 @@
<?php
require_once 'includes/header.php';
// Default to monthly
$billing_cycle = isset($_GET['billing_cycle']) && $_GET['billing_cycle'] === 'annually' ? 'annually' : 'monthly';
$plans = [
'basic' => [
'name' => t('basic_plan_name'),
'price_monthly' => 10,
'price_annually' => 10 * 12 * 0.85,
'limit' => t('basic_plan_limit'),
'features' => [
t('feature_150_generations'),
t('feature_standard_support'),
t('feature_history_access')
]
],
'premium' => [
'name' => t('premium_plan_name'),
'price_monthly' => 20,
'price_annually' => 20 * 12 * 0.85,
'limit' => t('premium_plan_limit'),
'features' => [
t('feature_unlimited_generations'),
t('feature_priority_support'),
t('feature_history_access')
]
]
];
?>
<main class="container my-5">
<div class="text-center mb-5">
<h1><?php echo t('pricing_title'); ?></h1>
<p class="lead"><?php echo t('pricing_subtitle'); ?></p>
<div class="btn-group" role="group" aria-label="Billing cycle toggle">
<a href="?billing_cycle=monthly" class="btn <?php echo $billing_cycle === 'monthly' ? 'btn-primary' : 'btn-outline-primary'; ?>"><?php echo t('monthly'); ?></a>
<a href="?billing_cycle=annually" class="btn <?php echo $billing_cycle === 'annually' ? 'btn-primary' : 'btn-outline-primary'; ?>">
<?php echo t('annually'); ?>
<span class="badge bg-success ms-2"><?php echo t('save_15_percent'); ?></span>
</a>
</div>
</div>
<div class="row">
<?php foreach ($plans as $plan_key => $plan): ?>
<div class="col-lg-6 mb-4">
<div class="card h-100 shadow-sm">
<div class="card-header text-center">
<h4 class="my-0 fw-normal"><?php echo $plan['name']; ?></h4>
</div>
<div class="card-body">
<h1 class="card-title pricing-card-title text-center">
$<?php echo $billing_cycle === 'monthly' ? number_format($plan['price_monthly'], 2) : number_format($plan['price_annually'] / 12, 2); ?>
<small class="text-muted fw-light">/<?php echo t('month_short'); ?></small>
</h1>
<?php if ($billing_cycle === 'annually'): ?>
<p class="text-center text-muted"><?php echo t('billed_annually_at'); ?> $<?php echo number_format($plan['price_annually'], 2); ?></p>
<?php endif; ?>
<ul class="list-unstyled mt-3 mb-4">
<?php foreach ($plan['features'] as $feature): ?>
<li class="mb-2"><i class="fas fa-check text-success me-2"></i><?php echo $feature; ?></li>
<?php endforeach; ?>
</ul>
<div class="d-grid">
<a href="subscription.php?plan=<?php echo $plan_key; ?>&billing_cycle=<?php echo $billing_cycle; ?>" class="btn btn-lg btn-primary"><?php echo t('choose_plan'); ?></a>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="mt-5 text-center">
<p><?php echo t('pricing_footer_text'); ?></p>
</div>
</main>
<?php require_once 'includes/footer.php'; ?>

View File

@ -1,101 +0,0 @@
<?php
require_once 'includes/header.php';
require_once 'db/config.php';
// Redirect to login if not authenticated
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit();
}
$error = null;
$success = null;
// Check for flash messages
if (isset($_SESSION['flash_message'])) {
$success = $_SESSION['flash_message'];
unset($_SESSION['flash_message']);
}
$user_id = $_SESSION['user_id'];
// Fetch user subscription data
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT subscription_plan, subscription_expires_at FROM users WHERE id = ?');
$stmt->execute([$user_id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$error = "Database error: " . $e->getMessage();
$user = ['subscription_plan' => null, 'subscription_expires_at' => null];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$password = $_POST['password'] ?? '';
$password_confirm = $_POST['password_confirm'] ?? '';
if (!empty($password) && $password === $password_confirm) {
try {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('UPDATE users SET password = ? WHERE id = ?');
$stmt->execute([$hashed_password, $user_id]);
$success = t('profile_updated_successfully');
} catch (PDOException $e) {
$error = "Database error: " . $e->getMessage();
}
} elseif (!empty($password)) {
$error = t('passwords_do_not_match');
}
}
?>
<main class="container auth-container">
<div class="card">
<div class="card-header">
<h2><?= t('profile_heading') ?></h2>
</div>
<div class="card-body">
<p class="text-center"><?= t('profile_subtitle') ?></p>
<?php if ($error): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success"><?= $success ?></div>
<?php endif; ?>
<div class="card-section">
<h4><?= t('nav_subscription') ?></h4>
<?php if ($user && $user['subscription_plan']): ?>
<p><strong><?= t('plan') ?>:</strong> <?= htmlspecialchars(ucfirst($user['subscription_plan'])) ?></p>
<p><strong><?= t('expires_on') ?>:</strong> <?= date("F j, Y", strtotime($user['subscription_expires_at'])) ?></p>
<a href="pricing.php" class="btn btn-secondary btn-sm"><?= t('change_plan') ?></a>
<?php else: ?>
<p><?= t('no_active_subscription') ?></p>
<a href="pricing.php" class="btn btn-primary"><?= t('choose_plan_to_start') ?></a>
<?php endif; ?>
</div>
<hr>
<form action="profile.php" method="POST" id="profile-form" novalidate>
<div class="form-group">
<label for="email"><?= t('email') ?></label>
<input type="email" id="email" name="email" class="form-control" value="<?= htmlspecialchars($_SESSION['user_email'] ?? '') ?>" disabled>
<small class="form-text text-muted"><?= t('email_cannot_be_changed') ?></small>
</div>
<div class="form-group">
<label for="password"><?= t('new_password') ?></label>
<input type="password" id="password" name="password" class="form-control">
</div>
<div class="form-group">
<label for="password_confirm"><?= t('confirm_new_password') ?></label>
<input type="password" id="password_confirm" name="password_confirm" class="form-control">
</div>
<button type="submit" class="btn btn-primary"><?= t('update_profile_button') ?></button>
</form>
</div>
</div>
</main>
<?php require_once 'includes/footer.php'; ?>

View File

@ -1,80 +0,0 @@
<?php
require_once 'db/config.php';
require_once 'includes/header.php';
$error = null;
$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($username) || empty($email) || empty($password) || empty($password_confirm)) {
$error = t('fill_all_fields');
} elseif ($password !== $password_confirm) {
$error = t('passwords_do_not_match');
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = t('invalid_email');
} else {
try {
$stmt = $pdo->prepare('SELECT id FROM users WHERE email = ?');
$stmt->execute([$email]);
if ($stmt->fetch()) {
$error = t('email_already_registered');
} else {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('INSERT INTO users (username, email, password) VALUES (?, ?, ?)');
$stmt->execute([$username, $email, $hashed_password]);
$success = t('registration_successful');
}
} catch (PDOException $e) {
$error = "Database error: " . $e->getMessage();
}
}
}
?>
<main class="container">
<section class="auth-form">
<h1><?= t('create_account') ?></h1>
<p><?= t('register_subtitle') ?></p>
<?php if ($error): ?>
<div class="error-message"><?= $error ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="success-message">
<?= $success ?>
<a href="login.php"><?= t('login_now') ?></a>
</div>
<?php endif; ?>
<form action="register.php" method="POST" id="register-form" novalidate>
<div class="form-group">
<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('registration_form_password') ?></label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<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">
<button type="submit" class="btn btn-primary"><?= t('register_button') ?></button>
</div>
</form>
<p class="auth-switch"><?= t('already_have_account') ?> <a href="login.php"><?= t('login_link') ?></a></p>
</section>
</main>
<?php include 'includes/footer.php'; ?>

View File

@ -1,54 +0,0 @@
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
require_once 'includes/translations.php';
require_once 'db/config.php';
// Require login
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$plan = $_GET['plan'] ?? null;
$period = $_GET['period'] ?? 'monthly'; // Default to monthly
$allowed_plans = ['basic', 'premium'];
if (!$plan || !in_array($plan, $allowed_plans)) {
header('Location: pricing.php');
exit;
}
$userId = $_SESSION['user_id'];
$expiryDate = '';
// Calculate expiry date
$date = new DateTime();
if ($period === 'annual') {
$date->modify('+1 year');
} else {
$date->modify('+1 month');
}
$expiryDate = $date->format('Y-m-d');
// Update user record in the database
try {
$pdo = db();
$stmt = $pdo->prepare("UPDATE users SET subscription_plan = ?, subscription_expires_at = ? WHERE id = ?");
$stmt->execute([$plan, $expiryDate, $userId]);
// Set a session flash message
$_SESSION['flash_message'] = t('subscription_success_message');
// Redirect to profile page
header('Location: profile.php');
exit;
} catch (PDOException $e) {
// In a real app, you would log this error
die("Database error: " . $e->getMessage());
}

View File

@ -1,38 +0,0 @@
<?php
// The header.php file now handles session_start(), login checks, translations, and language selection.
// We include it at the top of the <body>.
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit();
}
?>
<!DOCTYPE html>
<html lang="<?php echo $_SESSION['lang'] ?? 'en'; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= t('nav_subscription') ?> - Captionista</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/feather-icons/4.29.0/feather.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="light-mode">
<?php include 'includes/header.php'; ?>
<main class="container">
<h1><?= t('nav_subscription') ?></h1>
<p><?= t('pricing_coming_soon') ?></p>
</main>
<?php include 'includes/footer.php'; ?>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

View File

@ -1,27 +0,0 @@
<?php
require_once 'includes/header.php';
?>
<main class="container my-5">
<h1 class="text-center mb-4"><?php echo t('terms_title'); ?></h1>
<p class="text-muted text-center mb-5"><?php echo t('terms_last_updated'); ?></p>
<div class="card card-body">
<h4>1. <?php echo t('terms_section1_title'); ?></h4>
<p><?php echo t('terms_section1_text'); ?></p>
<h4>2. <?php echo t('terms_section2_title'); ?></h4>
<p><?php echo t('terms_section2_text'); ?></p>
<h4>3. <?php echo t('terms_section3_title'); ?></h4>
<p><?php echo t('terms_section3_text'); ?></p>
<h4>4. <?php echo t('terms_section4_title'); ?></h4>
<p><?php echo t('terms_section4_text'); ?></p>
<h4>5. <?php echo t('terms_section5_title'); ?></h4>
<p><?php echo t('terms_section5_text'); ?></p>
</div>
</main>
<?php require_once 'includes/footer.php'; ?>