Compare commits

..

1 Commits

Author SHA1 Message Date
Flatlogic Bot
3be446013d Initialv1 2025-12-09 00:59:40 +00:00
16 changed files with 1244 additions and 142 deletions

80
assets/css/custom.css Normal file
View File

@ -0,0 +1,80 @@
@import url('https://fonts.googleapis.com/css2?family=Georgia&display=swap');
body {
background-color: #F8FAFC;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: #1F2937;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Georgia', serif;
}
.theme-card {
border-radius: 12px;
border: 1px solid #E5E7EB;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.theme-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.theme-card .card-body {
padding: 1.5rem;
}
.btn-primary {
background-color: #6366F1;
border-color: #6366F1;
border-radius: 9999px;
padding: 0.75rem 1.5rem;
font-weight: 600;
}
.btn-primary:hover {
background-color: #4F46E5;
border-color: #4F46E5;
}
.btn-secondary {
background-color: #EC4899;
border-color: #EC4899;
border-radius: 9999px;
padding: 0.75rem 1.5rem;
font-weight: 600;
}
.btn-secondary:hover {
background-color: #DB2777;
border-color: #DB2777;
}
.ritual-step {
display: none;
}
.ritual-step.active {
display: block;
}
.progress-bar-container {
height: 4px;
background-color: #E5E7EB;
border-radius: 9999px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background-color: #6366F1;
width: 0%;
transition: width 0.5s ease-in-out;
}
.timer {
font-size: 3rem;
font-weight: bold;
}

91
assets/js/main.js Normal file
View File

@ -0,0 +1,91 @@
document.addEventListener('DOMContentLoaded', () => {
const ritualSteps = document.querySelectorAll('.ritual-step');
const nextButtons = document.querySelectorAll('.next-step');
const progressBar = document.querySelector('.progress-bar-fill');
const timerElement = document.getElementById('timer');
const beginButton = document.getElementById('begin-ritual');
let currentStep = 0;
let timerInterval;
function showStep(stepIndex) {
ritualSteps.forEach((step, index) => {
step.classList.toggle('active', index === stepIndex);
});
const progress = ((stepIndex) / (ritualSteps.length - 2)) * 100;
if(progressBar) {
progressBar.style.width = `${progress}%`;
}
}
function startTimer(duration) {
let timer = duration, minutes, seconds;
if(!timerElement) return;
const updateTimer = () => {
minutes = parseInt(timer / 60, 10);
seconds = parseInt(timer % 60, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
timerElement.textContent = minutes + ":" + seconds;
if (--timer < 0) {
clearInterval(timerInterval);
// Optionally, move to next step automatically
if(currentStep < ritualSteps.length - 1) {
currentStep++;
showStep(currentStep);
} else {
// Handle completion
}
}
};
updateTimer();
timerInterval = setInterval(updateTimer, 1000);
}
if(beginButton) {
beginButton.addEventListener('click', () => {
currentStep = 1; // Move to the first ritual step
showStep(currentStep);
startTimer(90); // 90 second timer
});
}
nextButtons.forEach(button => {
button.addEventListener('click', () => {
if (currentStep < ritualSteps.length - 1) {
currentStep++;
showStep(currentStep);
// If this is the last step, log progress
if (currentStep === ritualSteps.length - 1) {
const theme = new URLSearchParams(window.location.search).get('theme') || 'gratitude';
fetch('log_progress.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ theme: theme })
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error('Failed to log progress.');
}
})
.catch(error => console.error('Error logging progress:', error));
}
}
});
});
// Initial setup
if(ritualSteps.length > 0) {
showStep(0);
}
});

79
checkout.php Normal file
View File

@ -0,0 +1,79 @@
<?php
session_start();
// Ensure user is logged in
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
// Include the Stripe PHP library (assuming it's installed via Composer)
// require_once 'vendor/autoload.php';
// Set your Stripe API key
// \Stripe\Stripe::setApiKey('YOUR_STRIPE_SECRET_KEY');
$plan = $_GET['plan'] ?? 'basic';
$line_items = [];
if ($plan === 'basic') {
$line_items[] = [
'price_data' => [
'currency' => 'usd',
'product_data' => [
'name' => 'Basic Plan',
],
'unit_amount' => 500, // $5.00
'recurring' => [
'interval' => 'month',
],
],
'quantity' => 1,
];
} elseif ($plan === 'pro') {
$line_items[] = [
'price_data' => [
'currency' => 'usd',
'product_data' => [
'name' => 'Pro Plan',
],
'unit_amount' => 1500, // $15.00
'recurring' => [
'interval' => 'month',
],
],
'quantity' => 1,
];
} else {
// Redirect to pricing page if plan is not recognized
header('Location: pricing.php');
exit;
}
/*
try {
$checkout_session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => $line_items,
'mode' => 'subscription',
'success_url' => 'http://' . $_SERVER['HTTP_HOST'] . '/ritual.php',
'cancel_url' => 'http://' . $_SERVER['HTTP_HOST'] . '/pricing.php',
'customer_email' => $_SESSION['user_email'], // Pre-fill email
'client_reference_id' => $_SESSION['user_id'],
]);
header("Location: " . $checkout_session->url);
exit;
} catch (Exception $e) {
// Handle any errors that occur
error_log('Stripe Error: ' . $e->getMessage());
header('Location: pricing.php?error=1');
exit;
}
*/
// For now, we will simulate a successful payment and redirect to the ritual page.
header('Location: ritual.php?payment=success');
exit;
?>

41
db/setup.php Normal file
View File

@ -0,0 +1,41 @@
<?php
require_once 'config.php';
try {
$pdo = db();
// Create affirmations table
$pdo->exec("CREATE TABLE IF NOT EXISTS affirmations (
id INT AUTO_INCREMENT PRIMARY KEY,
theme VARCHAR(255) NOT NULL,
level VARCHAR(50) NOT NULL DEFAULT 'beginner',
affirmation_text TEXT NOT NULL
)");
// Clear existing affirmations to avoid duplicates on re-run
$pdo->exec("TRUNCATE TABLE affirmations");
$affirmations = [
'gratitude' => 'I am grateful for the abundance in my life.',
'letting-go' => 'I release what no longer serves me.',
'self-love' => 'I love and accept myself unconditionally.',
'forgiveness' => 'I forgive myself and others, freeing myself from the past.',
'abundance' => 'I am a magnet for success and good fortune.',
'clarity' => 'My mind is clear and my path is unfolding perfectly.',
'healing' => 'I am healing, whole, and strong.',
'resilience' => 'I bounce back from challenges with ease.',
'joy' => 'I choose joy and find it in every moment.',
'peace' => 'I cultivate peace within and around me.'
];
$stmt = $pdo->prepare("INSERT INTO affirmations (theme, affirmation_text) VALUES (:theme, :affirmation_text)");
foreach ($affirmations as $theme => $text) {
$stmt->execute(['theme' => $theme, 'affirmation_text' => $text]);
}
echo "Database setup complete. 'affirmations' table created and seeded." . PHP_EOL;
} catch (PDOException $e) {
die("Database setup failed: " . $e->getMessage());
}

19
db/setup_progress.php Normal file
View File

@ -0,0 +1,19 @@
<?php
require_once 'config.php';
try {
$pdo = db();
$sql = "
CREATE TABLE IF NOT EXISTS progress (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
theme VARCHAR(255) NOT NULL,
completion_date DATE NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
";
$pdo->exec($sql);
echo "Progress table created successfully.";
} catch (PDOException $e) {
die("Could not connect to the database $dbname :" . $e->getMessage());
}

View File

@ -0,0 +1,23 @@
<?php
require_once 'config.php';
try {
$pdo = db();
$sql = "
CREATE TABLE IF NOT EXISTS `subscriptions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT NOT NULL,
`stripe_subscription_id` VARCHAR(255) NOT NULL,
`plan` VARCHAR(50) NOT NULL,
`status` VARCHAR(50) NOT NULL,
`start_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`end_date` TIMESTAMP NULL DEFAULT NULL,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
";
$pdo->exec($sql);
echo "Table 'subscriptions' created successfully.";
} catch (PDOException $e) {
die("Could not create table 'subscriptions': " . $e->getMessage());
}
?>

20
db/setup_users.php Normal file
View File

@ -0,0 +1,20 @@
<?php
require_once 'config.php';
try {
$pdo = db();
$sql = "
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
";
$pdo->exec($sql);
echo "Table 'users' created successfully.";
} catch (PDOException $e) {
die("DB ERROR: " . $e->getMessage());
}
?>

250
index.php
View File

@ -1,150 +1,120 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION; <?php
$now = date('Y-m-d H:i:s'); $projectName = $_SERVER['PROJECT_NAME'] ?? 'Sacred Habits';
$projectDesc = $_SERVER['PROJECT_DESCRIPTION'] ?? 'A 90-second daily ritual to reprogram your inner voice.';
$projectImage = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
$themes = [
['name' => 'Safety', 'color' => '#34D399'],
['name' => 'Permission', 'color' => '#FBBF24'],
['name' => 'Possibility', 'color' => '#60A5FA'],
['name' => 'Presence', 'color' => '#A78BFA'],
['name' => 'Enoughness', 'color' => '#F472B6'],
['name' => 'Trust', 'color' => '#2DD4BF'],
['name' => 'Release', 'color' => '#F87171'],
['name' => 'Prosperity', 'color' => '#38B2AC', 'premium' => true],
['name' => 'Wellness', 'color' => '#4ADE80', 'premium' => true],
['name' => 'Spirituality', 'color' => '#818CF8', 'premium' => true],
];
?> ?>
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Style</title> <title><?php echo htmlspecialchars($projectName); ?></title>
<?php <meta name="description" content="<?php echo htmlspecialchars($projectDesc); ?>">
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; <!-- Open Graph / Facebook -->
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; <meta property="og:type" content="website">
?> <meta property="og:url" content="<?php echo (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; ?>">
<?php if ($projectDescription): ?> <meta property="og:title" content="<?php echo htmlspecialchars($projectName); ?>">
<!-- Meta description --> <meta property="og:description" content="<?php echo htmlspecialchars($projectDesc); ?>">
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' /> <meta property="og:image" content="<?php echo htmlspecialchars($projectImage); ?>">
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" /> <!-- Twitter -->
<!-- Twitter meta tags --> <meta property="twitter:card" content="summary_large_image">
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" /> <meta property="twitter:url" content="<?php echo (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; ?>">
<?php endif; ?> <meta property="twitter:title" content="<?php echo htmlspecialchars($projectName); ?>">
<?php if ($projectImageUrl): ?> <meta property="twitter:description" content="<?php echo htmlspecialchars($projectDesc); ?>">
<!-- Open Graph image --> <meta property="twitter:image" content="<?php echo htmlspecialchars($projectImage); ?>">
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" /> <link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<?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> </head>
<body> <?php session_start(); ?>
<main> <body class="d-flex flex-column min-vh-100">
<div class="card"> <div class="container my-5">
<h1>Analyzing your requirements and generating your website…</h1> <nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <div class="container-fluid">
<span class="sr-only">Loading…</span> <a class="navbar-brand" href="index.php"><?php echo htmlspecialchars($projectName); ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<?php if (isset($_SESSION['user_id'])): ?>
<li class="nav-item">
<span class="navbar-text me-3">Welcome, <?php echo htmlspecialchars($_SESSION['user_name']); ?>!</span>
</li>
<li class="nav-item">
<a class="nav-link" href="progress.php">Progress</a>
</li>
<li class="nav-item">
<a class="nav-link" href="pricing.php">Subscribe</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">Logout</a>
</li>
<?php else: ?>
<li class="nav-item">
<a class="nav-link" href="login.php">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="register.php">Register</a>
</li>
<?php endif; ?>
</ul>
</div> </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> </div>
</main> </nav>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC) <?php
// Simulate checking subscription status
$has_subscription = isset($_SESSION['has_subscription']) && $_SESSION['has_subscription'];
?>
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4">
<?php foreach ($themes as $theme): ?>
<?php
$is_premium = isset($theme['premium']) && $theme['premium'];
$can_access = !$is_premium || ($is_premium && $has_subscription);
$link = $can_access ? 'ritual.php?theme=' . urlencode($theme['name']) : 'pricing.php';
$card_class = $can_access ? '' : 'disabled-theme';
?>
<div class="col">
<a href="<?php echo $link; ?>" class="text-decoration-none">
<div class="card h-100 theme-card <?php echo $card_class; ?>" style="border-top: 5px solid <?php echo $theme['color']; ?>;">
<div class="card-body text-center">
<h5 class="card-title mb-0">
<?php echo htmlspecialchars($theme['name']); ?>
<?php if ($is_premium): ?>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-lock-fill" viewBox="0 0 16 16">
<path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z"/>
</svg>
<?php endif; ?>
</h5>
</div>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
</div>
<footer class="text-center mt-5 py-4 text-muted">
<p>&copy; <?php echo date('Y'); ?> <?php echo htmlspecialchars($projectName); ?>. All rights reserved.</p>
</footer> </footer>
</body> </body>
</html> </html>

34
log_progress.php Normal file
View File

@ -0,0 +1,34 @@
<?php
session_start();
require_once 'db/config.php';
if (!isset($_SESSION['user_id'])) {
http_response_code(401); // Unauthorized
exit('You must be logged in to log progress.');
}
$data = json_decode(file_get_contents('php://input'), true);
$theme = $data['theme'] ?? null;
if (empty($theme)) {
http_response_code(400); // Bad Request
exit('Theme is required.');
}
$user_id = $_SESSION['user_id'];
$completion_date = date('Y-m-d');
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO progress (user_id, theme, completion_date) VALUES (?, ?, ?)");
$stmt->execute([$user_id, $theme, $completion_date]);
http_response_code(200); // OK
echo json_encode(['success' => true]);
} catch (PDOException $e) {
http_response_code(500); // Internal Server Error
error_log('Failed to log progress: ' . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Could not save progress.']);
}

112
login.php Normal file
View File

@ -0,0 +1,112 @@
<?php
session_start();
require_once 'db/config.php';
$error = '';
$success = '';
if (isset($_SESSION['success_message'])) {
$success = $_SESSION['success_message'];
unset($_SESSION['success_message']);
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$email = trim($_POST['email']);
$password = $_POST['password'];
if (empty($email) || empty($password)) {
$error = 'Please fill in all fields.';
} else {
try {
$pdo = db();
$sql = 'SELECT * FROM users WHERE email = ?';
$stmt = $pdo->prepare($sql);
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
header('Location: index.php');
exit;
} else {
$error = 'Invalid email or password.';
}
} catch (PDOException $e) {
$error = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Sacred Habits</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body class="d-flex flex-column min-vh-100">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="index.php">Sacred Habits</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="register.php">Register</a>
</li>
</ul>
</div>
</div>
</nav>
<main class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h2 class="card-title text-center">Login to Your Account</h2>
<?php if (!empty($error)): ?>
<div class="alert alert-danger" role="alert">
<?php echo $error; ?>
</div>
<?php endif; ?>
<?php if (!empty($success)): ?>
<div class="alert alert-success" role="alert">
<?php echo $success; ?>
</div>
<?php endif; ?>
<form action="login.php" method="POST">
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Login</button>
</div>
</form>
</div>
</div>
</div>
</div>
</main>
<footer class="footer mt-auto py-3 bg-light">
<div class="container text-center">
<span class="text-muted">© 2025 Sacred Habits</span>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

7
logout.php Normal file
View File

@ -0,0 +1,7 @@
<?php
session_start();
session_unset();
session_destroy();
header("Location: login.php");
exit;
?>

130
pricing.php Normal file
View File

@ -0,0 +1,130 @@
<?php
session_start();
if (isset($_GET['simulate'])) {
$_SESSION['has_subscription'] = $_GET['simulate'] === 'true';
header('Location: pricing.php');
exit;
}
require_once 'db/config.php';
$project_name = getenv('PROJECT_NAME') ?: 'Ritual';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pricing - <?php echo htmlspecialchars($project_name); ?></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="index.php"><?php echo htmlspecialchars($project_name); ?></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<?php if (isset($_SESSION['user_id'])): ?>
<li class="nav-item">
<a class="nav-link" href="#">Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?></a>
</li>
<li class="nav-item">
<a class="nav-link" href="progress.php">Progress</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">Logout</a>
</li>
<?php else: ?>
<li class="nav-item">
<a class="nav-link" href="login.php">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="register.php">Register</a>
</li>
<?php endif; ?>
</ul>
</div>
</nav>
<div class="container mt-5">
<div class="text-center">
<h1>Choose Your Plan</h1>
<p class="lead">Unlock premium features and support the development of this platform.</p>
</div>
<div class="row mt-5">
<div class="col-md-4">
<div class="card mb-4 shadow-sm">
<div class="card-header">
<h4 class="my-0 font-weight-normal">Basic</h4>
</div>
<div class="card-body">
<h1 class="card-title pricing-card-title">$5 <small class="text-muted">/ mo</small></h1>
<ul class="list-unstyled mt-3 mb-4">
<li>Access to all themes</li>
<li>Track your progress</li>
<li>Email support</li>
</ul>
<?php if (isset($_SESSION['user_id'])): ?>
<a href="checkout.php?plan=basic" class="btn btn-lg btn-block btn-outline-primary">Sign up for Basic</a>
<?php else: ?>
<a href="login.php" class="btn btn-lg btn-block btn-primary">Login to Subscribe</a>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 shadow-sm">
<div class="card-header">
<h4 class="my-0 font-weight-normal">Pro</h4>
</div>
<div class="card-body">
<h1 class="card-title pricing-card-title">$15 <small class="text-muted">/ mo</small></h1>
<ul class="list-unstyled mt-3 mb-4">
<li>All Basic features</li>
<li>Access to premium themes</li>
<li>Priority email support</li>
</ul>
<?php if (isset($_SESSION['user_id'])): ?>
<a href="checkout.php?plan=pro" class="btn btn-lg btn-block btn-primary">Get started</a>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 shadow-sm">
<div class="card-header">
<h4 class="my-0 font-weight-normal">Enterprise</h4>
</div>
<div class="card-body">
<h1 class="card-title pricing-card-title">$29 <small class="text-muted">/ mo</small></h1>
<ul class="list-unstyled mt-3 mb-4">
<li>All Pro features</li>
<li>24/7 phone support</li>
<li>Dedicated account manager</li>
</ul>
</div>
</div>
</div>
</div>
<?php if (isset($_SESSION['user_id'])): ?>
<div class="mt-5 p-4 bg-light rounded">
<h3>Subscription Simulation (for testing)</h3>
<p>You can simulate having a subscription to test the premium features.</p>
<a href="pricing.php?simulate=true" class="btn btn-success">Simulate Subscription</a>
<a href="pricing.php?simulate=false" class="btn btn-danger">Cancel Simulation</a>
<p class="mt-2">Current status: <strong><?php echo isset($_SESSION['has_subscription']) && $_SESSION['has_subscription'] ? 'Active' : 'Inactive'; ?></strong></p>
</div>
<?php endif; ?>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

155
progress.php Normal file
View File

@ -0,0 +1,155 @@
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit();
}
require_once 'db/config.php';
$pdo = db();
$user_id = $_SESSION['user_id'];
// Fetch progress data
$stmt = $pdo->prepare("SELECT theme, completion_date FROM progress WHERE user_id = ? ORDER BY completion_date DESC");
$stmt->execute([$user_id]);
$progress_data = $stmt->fetchAll(PDO::FETCH_ASSOC);
// --- Progress Calculation ---
// 1. Last 7 Days Grid
$last_7_days = [];
for ($i = 0; $i < 7; $i++) {
$date = date('Y-m-d', strtotime("-$i days"));
$last_7_days[$date] = false;
}
foreach ($progress_data as $progress) {
if (isset($last_7_days[$progress['completion_date']])) {
$last_7_days[$progress['completion_date']] = true;
}
}
// 2. Total Practices
$total_practices = count($progress_data);
// 3. Current Streak
$current_streak = 0;
if ($total_practices > 0) {
$dates = array_unique(array_column($progress_data, 'completion_date'));
rsort($dates);
$current_streak = 1;
$today = date('Y-m-d');
$yesterday = date('Y-m-d', strtotime('-1 day'));
if ($dates[0] != $today && $dates[0] != $yesterday) {
$current_streak = 0;
} else {
for ($i = 0; $i < count($dates) - 1; $i++) {
$date1 = new DateTime($dates[$i]);
$date2 = new DateTime($dates[$i+1]);
$diff = $date1->diff($date2)->days;
if ($diff == 1) {
$current_streak++;
} else {
break;
}
}
}
}
// 4. Practiced Themes
$practiced_themes = array_unique(array_column($progress_data, 'theme'));
$project_name = getenv('PROJECT_NAME') ?: 'Mindful Moments';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your Progress - <?php echo htmlspecialchars($project_name); ?></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
<style>
.progress-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
}
.day-box {
width: 50px;
height: 50px;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.day-box.completed {
background-color: #d4edda;
}
.day-initial {
font-weight: bold;
}
</style>
</head>
<body>
<?php include 'index.php'; ?>
<div class="container mt-5">
<h2>Your Progress</h2>
<div class="row mt-4">
<div class="col-md-4">
<div class="card">
<div class="card-body text-center">
<h5 class="card-title">Total Practices</h5>
<p class="card-text display-4"><?php echo $total_practices; ?></p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body text-center">
<h5 class="card-title">Current Streak</h5>
<p class="card-text display-4"><?php echo $current_streak; ?> <?php echo ($current_streak == 1) ? 'day' : 'days'; ?></p>
</div>
</div>
</div>
</div>
<div class="mt-5">
<h5>Last 7 Days</h5>
<div class="progress-grid">
<?php foreach ($last_7_days as $date => $completed): ?>
<div class="day-box <?php echo $completed ? 'completed' : ''; ?>">
<span class="day-initial"><?php echo date('D', strtotime($date)); ?></span>
<span><?php echo date('d', strtotime($date)); ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="mt-5">
<h5>Practiced Themes</h5>
<?php if (empty($practiced_themes)): ?>
<p>You haven't completed any rituals yet.</p>
<?php else: ?>
<ul class="list-group">
<?php foreach ($practiced_themes as $theme): ?>
<li class="list-group-item"><?php echo htmlspecialchars($theme); ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

111
register.php Normal file
View File

@ -0,0 +1,111 @@
<?php
session_start();
require_once 'db/config.php';
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$password = $_POST['password'];
if (empty($name) || empty($email) || empty($password)) {
$error = 'Please fill in all fields.';
} else {
try {
$pdo = db();
$sql = 'SELECT id FROM users WHERE email = ?';
$stmt = $pdo->prepare($sql);
$stmt->execute([$email]);
if ($stmt->fetch()) {
$error = 'Email is already registered.';
} else {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$sql = 'INSERT INTO users (name, email, password) VALUES (?, ?, ?)';
$stmt = $pdo->prepare($sql);
if ($stmt->execute([$name, $email, $hashed_password])) {
$_SESSION['success_message'] = 'Registration successful! Please login.';
header('Location: login.php');
exit;
} else {
$error = 'Something went wrong. Please try again.';
}
}
} catch (PDOException $e) {
$error = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register - Sacred Habits</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body class="d-flex flex-column min-vh-100">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="index.php">Sacred Habits</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="login.php">Login</a>
</li>
</ul>
</div>
</div>
</nav>
<main class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h2 class="card-title text-center">Create Your Account</h2>
<?php if (!empty($error)): ?>
<div class="alert alert-danger" role="alert">
<?php echo $error; ?>
</div>
<?php endif; ?>
<form action="register.php" method="POST">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</form>
</div>
</div>
</div>
</div>
</main>
<footer class="footer mt-auto py-3 bg-light">
<div class="container text-center">
<span class="text-muted">© 2025 Sacred Habits</span>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

149
ritual.php Normal file
View File

@ -0,0 +1,149 @@
<?php session_start(); ?>
<?php
require_once 'db/config.php';
// Sanitize theme input
$theme = filter_input(INPUT_GET, 'theme', FILTER_SANITIZE_STRING);
if (empty($theme)) {
$theme = 'gratitude'; // Default theme
}
$level = 'beginner'; // Hardcoded for now, will be dynamic later
$affirmation_text = 'Your default affirmation text goes here. You are capable of amazing things.';
try {
$pdo = db();
// Fetch a random affirmation for the given theme and level
$stmt = $pdo->prepare(
"SELECT affirmation_text FROM affirmations WHERE theme = :theme AND level = :level ORDER BY RAND() LIMIT 1"
);
$stmt->execute(['theme' => $theme, 'level' => $level]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result && !empty($result['affirmation_text'])) {
$affirmation_text = $result['affirmation_text'];
}
} catch (PDOException $e) {
// Log the error instead of displaying it to the user
error_log("Database error in ritual.php: " . $e->getMessage());
// The page will proceed with the default affirmation text
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sacred Habits - Ritual</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
</head>
<body>
<?php $projectName = $_SERVER['PROJECT_NAME'] ?? 'Sacred Habits'; ?>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="index.php"><?php echo htmlspecialchars($projectName); ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<?php if (isset($_SESSION['user_id'])): ?>
<li class="nav-item">
<span class="navbar-text me-3">Welcome, <?php echo htmlspecialchars($_SESSION['user_name']); ?>!</span>
</li>
<li class="nav-item">
<a class="nav-link" href="progress.php">Progress</a>
</li>
<li class="nav-item">
<a class="nav-link" href="pricing.php">Subscribe</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">Logout</a>
</li>
<?php else: ?>
<li class="nav-item">
<a class="nav-link" href="login.php">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="register.php">Register</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
<div class="container my-5">
<div class="progress-bar-container mb-4">
<div class="progress-bar-fill"></div>
</div>
<!-- Step 0: Intro -->
<div class="ritual-step active text-center">
<h1 class="mb-3">Your 90-Second Ritual</h1>
<p class="lead text-muted mb-4">A moment for yourself, inspired by the theme of <strong><?php echo htmlspecialchars(ucfirst($theme)); ?></strong>.</p>
<button id="begin-ritual" class="btn btn-primary btn-lg">Begin Ritual</button>
</div>
<!-- Step 1: Release -->
<div class="ritual-step text-center">
<i data-feather="x-circle" class="mb-3" style="width: 48px; height: 48px; color: #EC4899;"></i>
<h2 class="mb-3">Release</h2>
<p class="lead">Let go of what no longer serves you. Take a deep breath out.</p>
<div id="timer" class="timer text-primary">01:30</div>
<button class="btn btn-secondary next-step mt-4">Next</button>
</div>
<!-- Step 2: Receive -->
<div class="ritual-step text-center">
<i data-feather="arrow-down-circle" class="mb-3" style="width: 48px; height: 48px; color: #6366F1;"></i>
<h2 class="mb-3">Receive</h2>
<p class="lead">Open yourself to new energy and possibility. Breathe in deeply.</p>
<button class="btn btn-primary next-step mt-4">Next</button>
</div>
<!-- Step 3: Practice -->
<div class="ritual-step text-center">
<i data-feather="heart" class="mb-3" style="width: 48px; height: 48px; color: #EC4899;"></i>
<h2 class="mb-3">Practice</h2>
<p class="lead">Repeat the affirmation, feeling its truth resonate within you.</p>
<blockquote class="blockquote fs-4 my-4">"<?php echo htmlspecialchars($affirmation_text); ?>"</blockquote>
<button class="btn btn-secondary next-step mt-4">Next</button>
</div>
<!-- Step 4: Breathe -->
<div class="ritual-step text-center">
<i data-feather="wind" class="mb-3" style="width: 48px; height: 48px; color: #6366F1;"></i>
<h2 class="mb-3">Breathe</h2>
<p class="lead">Integrate the practice with your breath. Inhale peace, exhale gratitude.</p>
<button class="btn btn-primary next-step mt-4">Next</button>
</div>
<!-- Step 5: Affirm -->
<div class="ritual-step text-center">
<i data-feather="check-circle" class="mb-3" style="width: 48px; height: 48px; color: #10B981;"></i>
<h2 class="mb-3">Affirm</h2>
<p class="lead">Lock in the feeling with one final repetition.</p>
<blockquote class="blockquote fs-4 my-4 text-primary">"<?php echo htmlspecialchars($affirmation_text); ?>"</blockquote>
<button class="btn btn-secondary next-step mt-4">Complete</button>
</div>
<!-- Step 6: Celebration -->
<div class="ritual-step text-center">
<h1 class="display-3 text-primary"> Ritual Complete! </h1>
<p class="lead">You have honored your commitment to yourself.</p>
<a href="index.php" class="btn btn-primary mt-4">Back to Themes</a>
</div>
</div>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

81
webhook.php Normal file
View File

@ -0,0 +1,81 @@
<?php
// Include the Stripe PHP library (assuming it's installed via Composer)
// require_once 'vendor/autoload.php';
require_once 'db/config.php';
// Set your Stripe API key and webhook secret
// \Stripe\Stripe::setApiKey('YOUR_STRIPE_SECRET_KEY');
$webhook_secret = 'YOUR_STRIPE_WEBHOOK_SECRET';
$payload = @file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = null;
/*
try {
$event = \Stripe\Webhook::constructEvent(
$payload, $sig_header, $webhook_secret
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
http_response_code(400);
exit();
}
// Handle the event
switch ($event->type) {
case 'checkout.session.completed':
$session = $event->data->object;
$user_id = $session->client_reference_id;
$stripe_subscription_id = $session->subscription;
// Get subscription details
$stripe = new \Stripe\StripeClient('YOUR_STRIPE_SECRET_KEY');
$subscription = $stripe->subscriptions->retrieve($stripe_subscription_id, []);
$plan = $subscription->items->data[0]->price->nickname;
// Store subscription in the database
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO subscriptions (user_id, stripe_subscription_id, plan, status, start_date, end_date) VALUES (?, ?, ?, ?, FROM_UNIXTIME(?), FROM_UNIXTIME(?))");
$stmt->execute([
$user_id,
$stripe_subscription_id,
$plan,
$subscription->status,
$subscription->current_period_start,
$subscription->current_period_end
]);
} catch (PDOException $e) {
// Log error
error_log("Webhook DB Error: " . $e->getMessage());
}
break;
case 'customer.subscription.updated':
$subscription = $event->data->object;
$stripe_subscription_id = $subscription->id;
$status = $subscription->status;
$end_date = $subscription->cancel_at_period_end ? $subscription->current_period_end : null;
try {
$pdo = db();
$stmt = $pdo->prepare("UPDATE subscriptions SET status = ?, end_date = FROM_UNIXTIME(?) WHERE stripe_subscription_id = ?");
$stmt->execute([$status, $end_date, $stripe_subscription_id]);
} catch (PDOException $e) {
// Log error
error_log("Webhook DB Error: " . $e->getMessage());
}
break;
// ... handle other event types
default:
// Unexpected event type
}
*/
http_response_code(200);
?>