MagiCV
This commit is contained in:
parent
05d192f46f
commit
7a4a20350d
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
session_start();
|
||||||
require_once __DIR__ . '/../db/config.php';
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
@ -24,9 +25,35 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
// Insert new user
|
// Insert new user
|
||||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||||
$stmt = $pdo->prepare("INSERT INTO users (email, password, role) VALUES (?, ?, ?)");
|
$stmt = $pdo->prepare("INSERT INTO users (email, password, role) VALUES (?, ?, ?)");
|
||||||
$stmt->execute([$email, $hashed_password, 'FREE_USER']);
|
$stmt->execute([$email, $hashed_password, 'free']);
|
||||||
|
|
||||||
header('Location: /login.php?success=registered');
|
$new_user_id = $pdo->lastInsertId();
|
||||||
|
|
||||||
|
// Check for guest CV data and save it
|
||||||
|
if (isset($_SESSION['guest_cv_data'])) {
|
||||||
|
$cv_data = $_SESSION['guest_cv_data'];
|
||||||
|
$title = $cv_data['title'] ?? 'My CV';
|
||||||
|
$template_id = $cv_data['template_id'] ?? 1;
|
||||||
|
$content = json_encode([
|
||||||
|
'personal_info' => $cv_data['personal_info'] ?? [],
|
||||||
|
'experience' => array_values($cv_data['experience'] ?? []),
|
||||||
|
'education' => array_values($cv_data['education'] ?? []),
|
||||||
|
'skills' => $cv_data['skills'] ?? ''
|
||||||
|
]);
|
||||||
|
|
||||||
|
$cv_stmt = $pdo->prepare('INSERT INTO cvs (user_id, title, content, template_id) VALUES (?, ?, ?, ?)');
|
||||||
|
$cv_stmt->execute([$new_user_id, $title, $content, $template_id]);
|
||||||
|
|
||||||
|
unset($_SESSION['guest_cv_data']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the user in automatically
|
||||||
|
$_SESSION['user_id'] = $new_user_id;
|
||||||
|
$_SESSION['user_email'] = $email;
|
||||||
|
$_SESSION['user_role'] = 'free'; // Default role
|
||||||
|
|
||||||
|
// Redirect to dashboard
|
||||||
|
header('Location: /dashboard.php');
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
@ -34,4 +61,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
header('Location: /register.php?error=db_error');
|
header('Location: /register.php?error=db_error');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
178
cv_editor.php
Normal file
178
cv_editor.php
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
$is_guest = !isset($_SESSION['user_id']);
|
||||||
|
|
||||||
|
// If a guest tries to edit a CV, redirect them to login.
|
||||||
|
if ($is_guest && isset($_GET['id'])) {
|
||||||
|
$_SESSION['error_message'] = 'You must be logged in to edit a CV.';
|
||||||
|
header('Location: /login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$user_role = $_SESSION['user_role'] ?? 'free';
|
||||||
|
|
||||||
|
// Fetch templates based on user role
|
||||||
|
if ($user_role === 'free') {
|
||||||
|
$templates_stmt = $pdo->query('SELECT id, name, is_premium FROM templates WHERE is_premium = 0');
|
||||||
|
} else {
|
||||||
|
// Pro and Admin users can see all templates
|
||||||
|
$templates_stmt = $pdo->query('SELECT id, name, is_premium FROM templates');
|
||||||
|
}
|
||||||
|
$templates = $templates_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$cv_data = [
|
||||||
|
'id' => null,
|
||||||
|
'title' => 'My CV',
|
||||||
|
'content' => json_encode([
|
||||||
|
'personal_info' => ['name' => '', 'email' => '', 'phone' => '', 'linkedin' => '' ],
|
||||||
|
'experience' => [],
|
||||||
|
'education' => [],
|
||||||
|
'skills' => ''
|
||||||
|
]),
|
||||||
|
'template_id' => null
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$stmt = $pdo->prepare('SELECT * FROM cvs WHERE id = :id AND user_id = :user_id');
|
||||||
|
$stmt->execute(['id' => $_GET['id'], 'user_id' => $_SESSION['user_id']]);
|
||||||
|
$cv = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($cv) {
|
||||||
|
$cv_data = $cv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = json_decode($cv_data['content'], true);
|
||||||
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h2><?php echo $cv_data['id'] ? 'Edit CV' : 'Create New CV'; ?></h2>
|
||||||
|
<form action="/save_cv.php" method="post" id="cv-form">
|
||||||
|
<input type="hidden" name="cv_id" value="<?php echo $cv_data['id']; ?>">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">CV Title</label>
|
||||||
|
<input type="text" name="title" id="title" value="<?php echo htmlspecialchars($cv_data['title']); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="template_id">Template</label>
|
||||||
|
<select name="template_id" id="template_id">
|
||||||
|
<?php foreach ($templates as $template): ?>
|
||||||
|
<option value="<?php echo $template['id']; ?>" <?php echo ($cv_data['template_id'] == $template['id']) ? 'selected' : ''; ?>>
|
||||||
|
<?php
|
||||||
|
echo htmlspecialchars($template['name']);
|
||||||
|
if ($template['is_premium']) {
|
||||||
|
echo ' (Pro)';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Personal Information</legend>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Full Name</label>
|
||||||
|
<input type="text" name="personal_info[name]" id="name" value="<?php echo htmlspecialchars($content['personal_info']['name'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input type="email" name="personal_info[email]" id="email" value="<?php echo htmlspecialchars($content['personal_info']['email'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="phone">Phone</label>
|
||||||
|
<input type="text" name="personal_info[phone]" id="phone" value="<?php echo htmlspecialchars($content['personal_info']['phone'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="linkedin">LinkedIn Profile</label>
|
||||||
|
<input type="text" name="personal_info[linkedin]" id="linkedin" value="<?php echo htmlspecialchars($content['personal_info']['linkedin'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Work Experience</legend>
|
||||||
|
<div id="experience-container">
|
||||||
|
<?php if (!empty($content['experience'])):
|
||||||
|
foreach ($content['experience'] as $index => $exp):
|
||||||
|
?>
|
||||||
|
<div class="experience-item">
|
||||||
|
<input type="text" name="experience[<?php echo $index; ?>][title]" placeholder="Job Title" value="<?php echo htmlspecialchars($exp['title'] ?? ''); ?>">
|
||||||
|
<input type="text" name="experience[<?php echo $index; ?>][company]" placeholder="Company" value="<?php echo htmlspecialchars($exp['company'] ?? ''); ?>">
|
||||||
|
<textarea name="experience[<?php echo $index; ?>][description]" placeholder="Description"><?php echo htmlspecialchars($exp['description'] ?? ''); ?></textarea>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
endforeach;
|
||||||
|
endif;
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="add-experience">Add Experience</button>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Education</legend>
|
||||||
|
<div id="education-container">
|
||||||
|
<?php if (!empty($content['education'])):
|
||||||
|
foreach ($content['education'] as $index => $edu):
|
||||||
|
?>
|
||||||
|
<div class="education-item">
|
||||||
|
<input type="text" name="education[<?php echo $index; ?>][degree]" placeholder="Degree" value="<?php echo htmlspecialchars($edu['degree'] ?? ''); ?>">
|
||||||
|
<input type="text" name="education[<?php echo $index; ?>][institution]" placeholder="Institution" value="<?php echo htmlspecialchars($edu['institution'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
endforeach;
|
||||||
|
endif;
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="add-education">Add Education</button>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Skills</legend>
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea name="skills" id="skills" placeholder="e.g., PHP, JavaScript, Project Management"><?php echo htmlspecialchars($content['skills'] ?? ''); ?></textarea>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<button type="submit" class="button">Save CV</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
let experienceIndex = <?php echo count($content['experience'] ?? []); ?>;
|
||||||
|
document.getElementById('add-experience').addEventListener('click', function() {
|
||||||
|
const container = document.getElementById('experience-container');
|
||||||
|
const newItem = document.createElement('div');
|
||||||
|
newItem.className = 'experience-item';
|
||||||
|
newItem.innerHTML = `
|
||||||
|
<input type="text" name="experience[${experienceIndex}][title]" placeholder="Job Title">
|
||||||
|
<input type="text" name="experience[${experienceIndex}][company]" placeholder="Company">
|
||||||
|
<textarea name="experience[${experienceIndex}][description]" placeholder="Description"></textarea>
|
||||||
|
`;
|
||||||
|
container.appendChild(newItem);
|
||||||
|
experienceIndex++;
|
||||||
|
});
|
||||||
|
|
||||||
|
let educationIndex = <?php echo count($content['education'] ?? []); ?>;
|
||||||
|
document.getElementById('add-education').addEventListener('click', function() {
|
||||||
|
const container = document.getElementById('education-container');
|
||||||
|
const newItem = document.createElement('div');
|
||||||
|
newItem.className = 'education-item';
|
||||||
|
newItem.innerHTML = `
|
||||||
|
<input type="text" name="education[${educationIndex}][degree]" placeholder="Degree">
|
||||||
|
<input type="text" name="education[${educationIndex}][institution]" placeholder="Institution">
|
||||||
|
`;
|
||||||
|
container.appendChild(newItem);
|
||||||
|
educationIndex++;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/includes/footer.php';
|
||||||
|
?>
|
||||||
@ -8,16 +8,54 @@ if (!isset($_SESSION['user_id'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require_once __DIR__ . '/includes/header.php';
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
|
||||||
|
// Check for and display error messages
|
||||||
|
if (isset($_SESSION['error_message'])) {
|
||||||
|
echo '<div class="error-banner"><p>' . htmlspecialchars($_SESSION['error_message']) . '</p></div>';
|
||||||
|
unset($_SESSION['error_message']); // Clear the message after displaying
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for and display success messages
|
||||||
|
if (isset($_SESSION['success_message'])) {
|
||||||
|
echo '<div class="success-banner"><p>' . htmlspecialchars($_SESSION['success_message']) . '</p></div>';
|
||||||
|
unset($_SESSION['success_message']); // Clear the message after displaying
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<h2>Welcome to Your Dashboard</h2>
|
<h2>Welcome to Your Dashboard</h2>
|
||||||
<p>Your role: <?php echo htmlspecialchars($_SESSION['user_role']); ?></p>
|
<p>Your role: <strong><?php echo htmlspecialchars($_SESSION['role']); ?></strong></p>
|
||||||
|
|
||||||
<div class="cv-management">
|
<div class="cv-management">
|
||||||
<h3>Your CVs</h3>
|
<h3>Your CVs</h3>
|
||||||
<p>You don't have any CVs yet.</p>
|
<?php
|
||||||
<a href="/create_cv.php" class="button">Create a New CV</a>
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
$pdoconn = db();
|
||||||
|
$stmt = $pdoconn->prepare('SELECT id, title, updated_at FROM cvs WHERE user_id = :user_id ORDER BY updated_at DESC');
|
||||||
|
$stmt->execute(['user_id' => $_SESSION['user_id']]);
|
||||||
|
$cvs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($cvs):
|
||||||
|
?>
|
||||||
|
<ul class="cv-list">
|
||||||
|
<?php foreach ($cvs as $cv): ?>
|
||||||
|
<li>
|
||||||
|
<div class="cv-item">
|
||||||
|
<strong><?php echo htmlspecialchars($cv['title']); ?></strong>
|
||||||
|
<span>Last updated: <?php echo date('F j, Y', strtotime($cv['updated_at'])); ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="cv-actions">
|
||||||
|
<a href="view_cv.php?id=<?php echo $cv['id']; ?>" class="button-sm">View</a>
|
||||||
|
<a href="cv_editor.php?id=<?php echo $cv['id']; ?>" class="button-sm">Edit</a>
|
||||||
|
<a href="delete_cv.php?id=<?php echo $cv['id']; ?>" class="button-sm button-danger" onclick="return confirm('Are you sure you want to delete this CV?');">Delete</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>You don't have any CVs yet. Let's create one!</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="/cv_editor.php" class="button">Create a New CV</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="/logout.php">Logout</a>
|
<a href="/logout.php">Logout</a>
|
||||||
|
|||||||
14
db/migrations/002_add_templates.sql
Normal file
14
db/migrations/002_add_templates.sql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `templates` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`name` VARCHAR(255) NOT NULL,
|
||||||
|
`description` TEXT,
|
||||||
|
`file_path` VARCHAR(255) NOT NULL,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE `cvs` ADD COLUMN `template_id` INT NULL;
|
||||||
|
|
||||||
|
-- Insert some default templates
|
||||||
|
INSERT INTO `templates` (`name`, `description`, `file_path`) VALUES
|
||||||
|
('Minimalist', 'A clean and simple template.', 'minimalist.php'),
|
||||||
|
('Professional', 'A classic and professional template.', 'professional.php');
|
||||||
8
db/migrations/003_add_roles_and_premium_templates.sql
Normal file
8
db/migrations/003_add_roles_and_premium_templates.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-- Add role to users table
|
||||||
|
ALTER TABLE `users` ADD `role` VARCHAR(50) NOT NULL DEFAULT 'free';
|
||||||
|
|
||||||
|
-- Add is_premium to templates table
|
||||||
|
ALTER TABLE `templates` ADD `is_premium` BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
|
|
||||||
|
-- Set the "Professional" template as premium for demonstration
|
||||||
|
UPDATE `templates` SET `is_premium` = TRUE WHERE `name` = 'Professional';
|
||||||
45
delete_cv.php
Normal file
45
delete_cv.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
header('Location: dashboard.php?error=Invalid CV ID');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cv_id = $_GET['id'];
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// First, verify the CV belongs to the current user
|
||||||
|
$stmt = $pdo->prepare("SELECT user_id FROM cvs WHERE id = ?");
|
||||||
|
$stmt->execute([$cv_id]);
|
||||||
|
$cv = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$cv || $cv['user_id'] != $user_id) {
|
||||||
|
// CV not found or does not belong to the user
|
||||||
|
header('Location: dashboard.php?error=CV not found or permission denied.');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the CV
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM cvs WHERE id = ? AND user_id = ?");
|
||||||
|
$stmt->execute([$cv_id, $user_id]);
|
||||||
|
|
||||||
|
header('Location: dashboard.php?success=CV deleted successfully.');
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Log error and redirect
|
||||||
|
error_log("CV Deletion Error: " . $e->getMessage());
|
||||||
|
header('Location: dashboard.php?error=An error occurred while deleting the CV.');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
||||||
@ -1,21 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() == PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
?>
|
||||||
<!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.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>MagiCV</title>
|
<title><?php echo isset($pageTitle) ? $pageTitle . ' - MagiCV' : 'MagiCV'; ?></title>
|
||||||
<link rel="stylesheet" href="/public/css/style.css?v=<?php echo time(); ?>">
|
<link rel="stylesheet" href="/public/css/style.css?v=<?php echo time(); ?>">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<nav>
|
<nav>
|
||||||
<a href="/" class="logo">MagiCV</a>
|
<a href="/" class="logo">MagiCV</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/#templates">Templates</a></li>
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
<li><a href="/#features">Features</a></li>
|
<li><a href="/dashboard.php">Dashboard</a></li>
|
||||||
<li><a href="/login.php">Login</a></li>
|
<li><a href="/templates_preview.php">Templates</a></li>
|
||||||
<li><a href="/register.php" class="button">Sign Up</a></li>
|
<?php if (isset($_SESSION['role']) && $_SESSION['role'] == 'free'): ?>
|
||||||
|
<li><a href="/upgrade.php" class="button-secondary">Upgrade to PRO</a></li>
|
||||||
|
<?php endif; ?>
|
||||||
|
<li><a href="/logout.php">Logout</a></li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li><a href="/templates_preview.php">Templates</a></li>
|
||||||
|
<li><a href="/#features">Features</a></li>
|
||||||
|
<li><a href="/login.php">Login</a></li>
|
||||||
|
<li><a href="/register.php" class="button">Sign Up</a></li>
|
||||||
|
<?php endif; ?>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
27
payment_handler.php
Normal file
27
payment_handler.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require 'db/config.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
// Simulate a successful payment
|
||||||
|
|
||||||
|
// Update user role to 'pro'
|
||||||
|
$sql = "UPDATE users SET role = 'pro' WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
|
||||||
|
if ($stmt->execute([$user_id])) {
|
||||||
|
// Update role in session
|
||||||
|
$_SESSION['role'] = 'pro';
|
||||||
|
$_SESSION['success_message'] = "Congratulations! You have successfully upgraded to the PRO plan.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['error_message'] = "Something went wrong during the upgrade. Please try again.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: dashboard.php');
|
||||||
|
exit();
|
||||||
@ -16,6 +16,15 @@ main {
|
|||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 2rem;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #eaeaea;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header and Navigation */
|
/* Header and Navigation */
|
||||||
header {
|
header {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -140,22 +149,27 @@ footer {
|
|||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group input {
|
.form-group input, .form-group textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-container .button {
|
.button {
|
||||||
width: 100%;
|
display: inline-block;
|
||||||
padding: 0.75rem;
|
padding: 0.75rem 1.5rem;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: #6c63ff;
|
background-color: #6c63ff;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-container .button {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-container p {
|
.auth-container p {
|
||||||
@ -163,9 +177,135 @@ footer {
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auth-container .error {
|
||||||
|
color: #c53030;
|
||||||
|
background-color: #fed7d7;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
/* Dashboard */
|
/* Dashboard */
|
||||||
.dashboard-container {
|
.dashboard-container {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: 2rem auto;
|
margin: 2rem auto;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CV List */
|
||||||
|
.cv-list {
|
||||||
|
list-style: none;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-list li {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #eaeaea;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: box-shadow 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-list li:hover {
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-item strong {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-item span {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-sm {
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #6c63ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-danger {
|
||||||
|
background-color: #e53e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-list a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* CV Editor */
|
||||||
|
#cv-form fieldset {
|
||||||
|
border: 1px solid #eaeaea;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cv-form legend {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-item, .education-item {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#add-experience, #add-education {
|
||||||
|
margin-top: 1rem;
|
||||||
|
background: none;
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
color: #555;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Banners */
|
||||||
|
.error-banner {
|
||||||
|
background-color: #fed7d7;
|
||||||
|
color: #c53030;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid #fbcaca;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.auth-container .info {
|
||||||
|
color: #004085;
|
||||||
|
background-color: #cce5ff;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-banner {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid #c3e6cb;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
session_start();
|
||||||
require_once __DIR__ . '/includes/header.php';
|
require_once __DIR__ . '/includes/header.php';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="auth-container">
|
<div class="auth-container">
|
||||||
<h2>Create Your Account</h2>
|
<h2>Create Your Account</h2>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['info_message'])): ?>
|
||||||
|
<p class="info"><?php echo $_SESSION['info_message']; ?></p>
|
||||||
|
<?php unset($_SESSION['info_message']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (isset($_GET['error'])): ?>
|
<?php if (isset($_GET['error'])): ?>
|
||||||
<p class="error">
|
<p class="error">
|
||||||
<?php
|
<?php
|
||||||
@ -40,4 +47,4 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/includes/footer.php';
|
require_once __DIR__ . '/includes/footer.php';
|
||||||
?>
|
?>
|
||||||
75
save_cv.php
Normal file
75
save_cv.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
// If user is not logged in, store CV data in session and redirect to register
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$_SESSION['guest_cv_data'] = $_POST;
|
||||||
|
$_SESSION['info_message'] = 'Create an account to save your CV.';
|
||||||
|
header('Location: /register.php');
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
// Not a POST request and not logged in
|
||||||
|
header('Location: /login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$pdo = db();
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
$user_role = $_SESSION['user_role'] ?? 'free';
|
||||||
|
$cv_id = $_POST['cv_id'] ?? null;
|
||||||
|
$title = $_POST['title'] ?? 'My CV';
|
||||||
|
$template_id = $_POST['template_id'] ?? 1; // Default to template 1
|
||||||
|
|
||||||
|
// --- Role-based Limitation ---
|
||||||
|
if (empty($cv_id) && $user_role === 'free') {
|
||||||
|
$stmt = $pdo->prepare("SELECT COUNT(*) FROM cvs WHERE user_id = ?");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$cv_count = $stmt->fetchColumn();
|
||||||
|
|
||||||
|
if ($cv_count >= 2) {
|
||||||
|
// Limit reached for free users
|
||||||
|
$_SESSION['error_message'] = 'You have reached the maximum of 2 CVs for the Free Plan. Please upgrade to create more.';
|
||||||
|
header('Location: /dashboard.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = json_encode([
|
||||||
|
'personal_info' => $_POST['personal_info'] ?? [],
|
||||||
|
'experience' => array_values($_POST['experience'] ?? []),
|
||||||
|
'education' => array_values($_POST['education'] ?? []),
|
||||||
|
'skills' => $_POST['skills'] ?? ''
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($cv_id) {
|
||||||
|
// Update existing CV
|
||||||
|
$stmt = $pdo->prepare('UPDATE cvs SET title = :title, content = :content, template_id = :template_id, updated_at = NOW() WHERE id = :id AND user_id = :user_id');
|
||||||
|
$stmt->execute([
|
||||||
|
'id' => $cv_id,
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'title' => $title,
|
||||||
|
'content' => $content,
|
||||||
|
'template_id' => $template_id
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// Insert new CV
|
||||||
|
$stmt = $pdo->prepare('INSERT INTO cvs (user_id, title, content, template_id) VALUES (:user_id, :title, :content, :template_id)');
|
||||||
|
$stmt->execute([
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'title' => $title,
|
||||||
|
'content' => $content,
|
||||||
|
'template_id' => $template_id
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: /dashboard.php');
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
// Not a POST request
|
||||||
|
header('Location: /dashboard.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
36
templates/minimalist.php
Normal file
36
templates/minimalist.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>CV: <?php echo htmlspecialchars($cv['title']); ?></title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; line-height: 1.6; }
|
||||||
|
.cv-container { max-width: 800px; margin: auto; padding: 20px; }
|
||||||
|
h1, h2, h3 { margin-bottom: 0.5em; }
|
||||||
|
h1 { font-size: 2.5em; }
|
||||||
|
h2 { font-size: 1.8em; border-bottom: 1px solid #eee; padding-bottom: 5px; margin-top: 1.5em;}
|
||||||
|
.section { margin-bottom: 1.5em; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="cv-container">
|
||||||
|
<header class="section">
|
||||||
|
<h1><?php echo htmlspecialchars(json_decode($cv['content'])->name); ?></h1>
|
||||||
|
<p><?php echo htmlspecialchars(json_decode($cv['content'])->email); ?> | <?php echo htmlspecialchars(json_decode($cv['content'])->phone); ?></p>
|
||||||
|
</header>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Work Experience</h2>
|
||||||
|
<p><?php echo nl2br(htmlspecialchars(json_decode($cv['content'])->experience)); ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Education</h2>
|
||||||
|
<p><?php echo nl2br(htmlspecialchars(json_decode($cv['content'])->education)); ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Skills</h2>
|
||||||
|
<p><?php echo nl2br(htmlspecialchars(json_decode($cv['content'])->skills)); ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
39
templates/professional.php
Normal file
39
templates/professional.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>CV: <?php echo htmlspecialchars($cv['title']); ?></title>
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Times New Roman', Times, serif; line-height: 1.4; }
|
||||||
|
.cv-container { max-width: 800px; margin: auto; padding: 30px; border: 1px solid #ccc; }
|
||||||
|
h1, h2, h3 { margin-bottom: 0.5em; }
|
||||||
|
h1 { text-align: center; font-size: 2.8em; text-transform: uppercase; letter-spacing: 2px;}
|
||||||
|
h2 { font-size: 1.5em; border-bottom: 2px solid #333; padding-bottom: 5px; margin-top: 2em;}
|
||||||
|
.contact-info { text-align: center; margin-bottom: 2em; }
|
||||||
|
.section { margin-bottom: 1.5em; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="cv-container">
|
||||||
|
<header class="section">
|
||||||
|
<h1><?php echo htmlspecialchars(json_decode($cv['content'])->name); ?></h1>
|
||||||
|
<div class="contact-info">
|
||||||
|
<p><?php echo htmlspecialchars(json_decode($cv['content'])->email); ?> | <?php echo htmlspecialchars(json_decode($cv['content'])->phone); ?></p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Work Experience</h2>
|
||||||
|
<div><?php echo nl2br(htmlspecialchars(json_decode($cv['content'])->experience)); ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Education</h2>
|
||||||
|
<div><?php echo nl2br(htmlspecialchars(json_decode($cv['content'])->education)); ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Skills</h2>
|
||||||
|
<div><?php echo nl2br(htmlspecialchars(json_decode($cv['content'])->skills)); ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
39
templates_preview.php
Normal file
39
templates_preview.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->query("SELECT * FROM templates ORDER BY name");
|
||||||
|
$templates = $stmt->fetchAll();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
<h1 class="text-center my-4">CV Templates</h1>
|
||||||
|
<p class="text-center text-muted mb-5">Browse our library of professionally designed templates. <a href="/register.php">Sign up</a> to use them!</p>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<?php foreach ($templates as $template): ?>
|
||||||
|
<div class="col-md-6 col-lg-4 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<img src="https://picsum.photos/seed/<?php echo htmlspecialchars($template['file_path']); ?>/400/500" class="card-img-top" alt="<?php echo htmlspecialchars($template['name']); ?>">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title d-flex justify-content-between align-items-center">
|
||||||
|
<?php echo htmlspecialchars($template['name']); ?>
|
||||||
|
<?php if ($template['is_premium']): ?>
|
||||||
|
<span class="badge bg-warning text-dark">PRO</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="badge bg-success">FREE</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</h5>
|
||||||
|
<p class="card-text"><?php echo htmlspecialchars($template['description'] ?? 'A great template for your CV.'); ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-center">
|
||||||
|
<a href="/register.php" class="btn btn-primary">Use this Template</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
33
upgrade.php
Normal file
33
upgrade.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageTitle = "Upgrade to PRO";
|
||||||
|
include 'includes/header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="auth-container">
|
||||||
|
<h2>Upgrade to PRO</h2>
|
||||||
|
<p>Unlock premium features and take your CV to the next level.</p>
|
||||||
|
|
||||||
|
<div class="pro-features">
|
||||||
|
<h3>PRO Plan Benefits:</h3>
|
||||||
|
<ul>
|
||||||
|
<li><i class="fas fa-check-circle"></i> Unlimited CVs</li>
|
||||||
|
<li><i class="fas fa-star"></i> Access to all premium templates</li>
|
||||||
|
<li><i class="fas fa-headset"></i> Priority Support</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="payment_handler.php" method="POST">
|
||||||
|
<p>Click the button below to simulate the upgrade process.</p>
|
||||||
|
<button type="submit" class="button">Upgrade Now for $10 (Simulated)</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include 'includes/footer.php'; ?>
|
||||||
72
view_cv.php
Normal file
72
view_cv.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (!isset($_GET['id'])) {
|
||||||
|
header('Location: dashboard.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cv_id = $_GET['id'];
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM cvs WHERE id = ? AND user_id = ?");
|
||||||
|
$stmt->execute([$cv_id, $user_id]);
|
||||||
|
$cv = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$cv) {
|
||||||
|
header('Location: dashboard.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the template
|
||||||
|
$template_path = null;
|
||||||
|
if (!empty($cv['template_id'])) {
|
||||||
|
$stmt = $pdo->prepare("SELECT file_path FROM templates WHERE id = ?");
|
||||||
|
$stmt->execute([$cv['template_id']]);
|
||||||
|
$template = $stmt->fetch();
|
||||||
|
if ($template && file_exists(__DIR__ . '/templates/' . $template['file_path'])) {
|
||||||
|
$template_path = __DIR__ . '/templates/' . $template['file_path'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a template is found, render it. Otherwise, fall back to a default view.
|
||||||
|
if ($template_path) {
|
||||||
|
// The template file will have access to the $cv variable.
|
||||||
|
require_once $template_path;
|
||||||
|
} else {
|
||||||
|
// Default view if no template is set or found
|
||||||
|
$cv_data = json_decode($cv['content'], true);
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>View CV: <?php echo htmlspecialchars($cv['title']); ?></title>
|
||||||
|
<link rel="stylesheet" href="public/css/style.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1><?php echo htmlspecialchars($cv['title']); ?></h1>
|
||||||
|
<p><strong>Name:</strong> <?php echo htmlspecialchars($cv_data['personal_info']['name'] ?? 'N/A'); ?></p>
|
||||||
|
<p><strong>Email:</strong> <?php echo htmlspecialchars($cv_data['personal_info']['email'] ?? 'N/A'); ?></p>
|
||||||
|
<hr>
|
||||||
|
<h2>Work Experience</h2>
|
||||||
|
<div><?php echo nl2br(htmlspecialchars(json_encode($cv_data['experience'] ?? []))); ?></div>
|
||||||
|
<h2>Education</h2>
|
||||||
|
<div><?php echo nl2br(htmlspecialchars(json_encode($cv_data['education'] ?? []))); ?></div>
|
||||||
|
<h2>Skills</h2>
|
||||||
|
<p><?php echo nl2br(htmlspecialchars($cv_data['skills'] ?? '')); ?></p>
|
||||||
|
<a href="/dashboard.php">Back to Dashboard</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user