Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93410c3389 | ||
|
|
97cbe8f10f | ||
|
|
bfa71e661d | ||
|
|
062a594521 |
74
admin.php
Normal file
74
admin.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?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'; ?>
|
||||
793
assets/css/custom.css
Normal file
793
assets/css/custom.css
Normal file
@ -0,0 +1,793 @@
|
||||
/* --- 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);
|
||||
}
|
||||
1
assets/images/flags/de.svg
Normal file
1
assets/images/flags/de.svg
Normal file
@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 160 B |
1
assets/images/flags/en.svg
Normal file
1
assets/images/flags/en.svg
Normal file
@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 524 B |
1
assets/images/flags/es.svg
Normal file
1
assets/images/flags/es.svg
Normal file
@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 141 B |
1
assets/images/flags/fr.svg
Normal file
1
assets/images/flags/fr.svg
Normal file
@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 175 B |
1
assets/images/flags/pl.svg
Normal file
1
assets/images/flags/pl.svg
Normal file
@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 139 B |
132
assets/js/main.js
Normal file
132
assets/js/main.js
Normal file
@ -0,0 +1,132 @@
|
||||
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 = '×';
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
29
db/promote_user.php
Normal file
29
db/promote_user.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?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");
|
||||
}
|
||||
|
||||
71
db/setup.php
Normal file
71
db/setup.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?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
Normal file
65
faq.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?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'; ?>
|
||||
208
generate.php
Normal file
208
generate.php
Normal file
@ -0,0 +1,208 @@
|
||||
<?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();
|
||||
105
history.php
Normal file
105
history.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?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'; ?>
|
||||
12
includes/footer.php
Normal file
12
includes/footer.php
Normal file
@ -0,0 +1,12 @@
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© <?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>
|
||||
101
includes/header.php
Normal file
101
includes/header.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?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>
|
||||
307
includes/translations.php
Normal file
307
includes/translations.php
Normal file
@ -0,0 +1,307 @@
|
||||
<?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',
|
||||
],
|
||||
];
|
||||
}
|
||||
283
index.php
283
index.php
@ -1,150 +1,135 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
?>
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<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=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
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>
|
||||
<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>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
<main>
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<h1><?php echo t('hero_title'); ?></h1>
|
||||
<p><?php echo t('hero_subtitle'); ?></p>
|
||||
<a href="#generator" class="cta-button"><?php echo t('hero_cta'); ?></a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="generator" class="generator-section">
|
||||
<div class="container">
|
||||
<h2><?php echo t('generator_form_title'); ?></h2>
|
||||
<form action="generate.php" method="POST" class="generator-form" enctype="multipart/form-data">
|
||||
<div class="form-grid">
|
||||
<div class="form-column-wide">
|
||||
<div class="form-group">
|
||||
<label for="photo-upload"><?php echo t('upload_label'); ?></label>
|
||||
<div class="photo-upload-container">
|
||||
<input type="file" id="photo-upload" name="photos[]" multiple accept="image/*" style="display: none;">
|
||||
<div class="upload-area" id="upload-area">
|
||||
<i data-feather="upload-cloud"></i>
|
||||
<p>Click to browse or drag & drop your images here</p>
|
||||
</div>
|
||||
<div class="photo-preview" id="photo-preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-column">
|
||||
<div class="form-group">
|
||||
<label for="state"><?php echo t('state_label'); ?></label>
|
||||
<select id="state" name="state" required>
|
||||
<option value="new"><?php echo t('state_new'); ?></option>
|
||||
<option value="used"><?php echo t('state_used'); ?></option>
|
||||
<option value="visibly_used"><?php echo t('state_visibly_used'); ?></option>
|
||||
</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'])) {
|
||||
echo '<div class="error-message">' . $_SESSION['generation_error'] . '</div>';
|
||||
unset($_SESSION['generation_error']);
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="how-it-works">
|
||||
<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 class="steps-container">
|
||||
<div class="step-card">
|
||||
<div class="step-icon-wrapper">
|
||||
<i data-feather="upload-cloud"></i>
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<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'; ?>
|
||||
|
||||
11
list_users.php
Normal file
11
list_users.php
Normal 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");
|
||||
}
|
||||
|
||||
62
login.php
Normal file
62
login.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?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'; ?>
|
||||
6
logout.php
Normal file
6
logout.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
82
pricing.php
Normal file
82
pricing.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?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'; ?>
|
||||
101
profile.php
Normal file
101
profile.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?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'; ?>
|
||||
80
register.php
Normal file
80
register.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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'; ?>
|
||||
54
subscribe.php
Normal file
54
subscribe.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?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());
|
||||
}
|
||||
38
subscription.php
Normal file
38
subscription.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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>
|
||||
27
terms.php
Normal file
27
terms.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?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'; ?>
|
||||
Loading…
x
Reference in New Issue
Block a user