Version 2
This commit is contained in:
parent
e7d7877d01
commit
23be5a5236
29
api/pexels.php
Normal file
29
api/pexels.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once __DIR__.'/../includes/pexels.php';
|
||||
$qs = isset($_GET['queries']) ? explode(',', $_GET['queries']) : ['home','apple','pizza','mountains','cat'];
|
||||
$out = [];
|
||||
foreach ($qs as $q) {
|
||||
$u = 'https://api.pexels.com/v1/search?query=' . urlencode(trim($q)) . '&orientation=landscape&per_page=1&page=1';
|
||||
$d = pexels_get($u);
|
||||
if ($d && !empty($d['photos'])) {
|
||||
$p = $d['photos'][0];
|
||||
$src = $p['src']['large2x'] ?? ($p['src']['large'] ?? $p['src']['original']);
|
||||
$dest = __DIR__.'/../assets/images/pexels/'.$p['id'].'.jpg';
|
||||
if ($src && !file_exists($dest)) download_to($src, $dest);
|
||||
$out[] = [
|
||||
'src' => 'assets/images/pexels/'.$p['id'].'.jpg',
|
||||
'photographer' => $p['photographer'] ?? 'Unknown',
|
||||
'photographer_url' => $p['photographer_url'] ?? '',
|
||||
];
|
||||
} else {
|
||||
// Fallback: Picsum
|
||||
$out[] = [
|
||||
'src' => 'https://picsum.photos/1200/800?random='.rand(),
|
||||
'photographer' => 'Random Picsum',
|
||||
'photographer_url' => 'https://picsum.photos/'
|
||||
];
|
||||
}
|
||||
}
|
||||
echo json_encode($out);
|
||||
?>
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
/* General Body Styles */
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
@ -59,8 +58,7 @@ h2 {
|
||||
|
||||
/* Hero Section */
|
||||
#hero {
|
||||
background: linear-gradient(180deg, #FFFFFF 0%, #F2F2F7 100%);
|
||||
padding: 120px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Card Styles */
|
||||
@ -92,3 +90,72 @@ footer {
|
||||
background-color: #FFFFFF;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
/* Pricing Table */
|
||||
#pricing .card {
|
||||
border: 1px solid #e5e5e5;
|
||||
transition: all .2s;
|
||||
}
|
||||
|
||||
#pricing .card:hover {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#pricing .card-popular {
|
||||
border-color: #007AFF;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
#pricing .card-price {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#pricing .period {
|
||||
font-size: 0.8rem;
|
||||
font-weight: normal;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
#pricing .fa-ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#pricing .fa-li {
|
||||
position: relative;
|
||||
left: 0;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-left: 2em; /* Add padding to align text */
|
||||
}
|
||||
|
||||
#pricing .fa-li .fas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2em;
|
||||
text-align: center;
|
||||
color: #007AFF;
|
||||
}
|
||||
|
||||
#pricing .text-muted .fa-li .fas {
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
|
||||
/* Hero Overlay */
|
||||
#hero {
|
||||
position: relative;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
color: white;
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.hero-overlay {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 120px 0;
|
||||
}
|
||||
37
assets/css/portal.css
Normal file
37
assets/css/portal.css
Normal file
@ -0,0 +1,37 @@
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.portal-nav {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.portal-main {
|
||||
min-height: calc(100vh - 56px);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.sidebar .nav-link {
|
||||
color: #333;
|
||||
padding: 1rem;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar .nav-link.active,
|
||||
.sidebar .nav-link:hover {
|
||||
color: #007bff;
|
||||
background-color: #e9ecef;
|
||||
border-left-color: #007bff;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
padding: 2rem;
|
||||
}
|
||||
BIN
assets/images/pexels/185576.jpg
Normal file
BIN
assets/images/pexels/185576.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 246 KiB |
BIN
assets/images/pexels/256514.jpg
Normal file
BIN
assets/images/pexels/256514.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
BIN
assets/images/pexels/607812.jpg
Normal file
BIN
assets/images/pexels/607812.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
assets/images/pexels/669612.jpg
Normal file
BIN
assets/images/pexels/669612.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 216 KiB |
BIN
assets/images/pexels/887843.jpg
Normal file
BIN
assets/images/pexels/887843.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 198 KiB |
72
auth_handler.php
Normal file
72
auth_handler.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
require_once 'includes/auth.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'register') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($name) || empty($email) || empty($password)) {
|
||||
header('Location: register.php?error=missing_fields');
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$stmt = $db->prepare('SELECT id FROM users WHERE email = ?');
|
||||
$stmt->execute([$email]);
|
||||
if ($stmt->fetch()) {
|
||||
header('Location: register.php?error=email_taken');
|
||||
exit();
|
||||
}
|
||||
|
||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// Default new registrations to the 'Client' role
|
||||
$stmt = $db->prepare("SELECT id FROM roles WHERE name = 'Client'");
|
||||
$stmt->execute();
|
||||
$client_role = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$stmt = $db->prepare('INSERT INTO users (name, email, password, role_id) VALUES (?, ?, ?, ?)');
|
||||
if ($stmt->execute([$name, $email, $hashed_password, $client_role['id'] ?? null])) {
|
||||
$user_id = $db->lastInsertId();
|
||||
$_SESSION['user_id'] = $user_id;
|
||||
header('Location: portal/');
|
||||
exit();
|
||||
} else {
|
||||
header('Location: register.php?error=registration_failed');
|
||||
exit();
|
||||
}
|
||||
|
||||
} elseif ($action === 'login') {
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($email) || empty($password)) {
|
||||
header('Location: login.php?error=missing_fields');
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$stmt = $db->prepare('SELECT * FROM users WHERE email = ?');
|
||||
$stmt->execute([$email]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
header('Location: portal/');
|
||||
exit();
|
||||
} else {
|
||||
header('Location: login.php?error=invalid_credentials');
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
26
db/migrations/002_create_users_and_roles.sql
Normal file
26
db/migrations/002_create_users_and_roles.sql
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `roles` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(50) NOT NULL UNIQUE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Insert default roles
|
||||
INSERT IGNORE INTO `roles` (`name`) VALUES
|
||||
('Super Admin'),
|
||||
('Agency Admin'),
|
||||
('Account Manager'),
|
||||
('Client'),
|
||||
('Content Creator'),
|
||||
('Analyst'),
|
||||
('Billing'),
|
||||
('Support');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`email` VARCHAR(100) NOT NULL UNIQUE,
|
||||
`password` VARCHAR(255) NOT NULL,
|
||||
`role_id` INT,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
61
db/migrations/003_create_portal_tables.sql
Normal file
61
db/migrations/003_create_portal_tables.sql
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
-- Projects table to store project information
|
||||
CREATE TABLE IF NOT EXISTS `projects` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`client_id` INT NOT NULL,
|
||||
`status` VARCHAR(50) DEFAULT 'Pending',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`client_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Assets table for creative assets linked to projects
|
||||
CREATE TABLE IF NOT EXISTS `assets` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`project_id` INT NOT NULL,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`file_path` VARCHAR(255) NOT NULL,
|
||||
`status` VARCHAR(50) DEFAULT 'Pending Approval',
|
||||
`uploaded_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Comments on assets
|
||||
CREATE TABLE IF NOT EXISTS `asset_comments` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`asset_id` INT NOT NULL,
|
||||
`user_id` INT NOT NULL,
|
||||
`comment` TEXT NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`asset_id`) REFERENCES `assets`(`id`) ON DELETE CASCADE,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Courses available for purchase
|
||||
CREATE TABLE IF NOT EXISTS `courses` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`description` TEXT,
|
||||
`price` DECIMAL(10, 2) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Link table for users who have access to courses
|
||||
CREATE TABLE IF NOT EXISTS `user_courses` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`user_id` INT NOT NULL,
|
||||
`course_id` INT NOT NULL,
|
||||
`purchased_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
|
||||
FOREIGN KEY (`course_id`) REFERENCES `courses`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Invoices for billing
|
||||
CREATE TABLE IF NOT EXISTS `invoices` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`client_id` INT NOT NULL,
|
||||
`amount` DECIMAL(10, 2) NOT NULL,
|
||||
`status` VARCHAR(50) DEFAULT 'Unpaid',
|
||||
`due_date` DATE,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`client_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
63
db/migrations/004_create_extended_portal_tables.sql
Normal file
63
db/migrations/004_create_extended_portal_tables.sql
Normal file
@ -0,0 +1,63 @@
|
||||
-- Messaging system tied to projects
|
||||
CREATE TABLE IF NOT EXISTS `messages` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`project_id` INT NOT NULL,
|
||||
`user_id` INT NOT NULL,
|
||||
`message` TEXT NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Calendar events
|
||||
CREATE TABLE IF NOT EXISTS `calendar_events` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`start_event` DATETIME NOT NULL,
|
||||
`end_event` DATETIME NOT NULL,
|
||||
`project_id` INT,
|
||||
`user_id` INT,
|
||||
`description` TEXT,
|
||||
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Approvals for assets
|
||||
CREATE TABLE IF NOT EXISTS `approvals` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`asset_id` INT NOT NULL,
|
||||
`user_id` INT NOT NULL,
|
||||
`status` ENUM('Pending', 'Approved', 'Rejected') NOT NULL DEFAULT 'Pending',
|
||||
`comments` TEXT,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`asset_id`) REFERENCES `assets`(`id`) ON DELETE CASCADE,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Metrics from ad platforms
|
||||
CREATE TABLE IF NOT EXISTS `metrics` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`project_id` INT NOT NULL,
|
||||
`platform` VARCHAR(50) NOT NULL, -- e.g., 'Facebook', 'Google Ads'
|
||||
`date` DATE NOT NULL,
|
||||
`impressions` INT,
|
||||
`clicks` INT,
|
||||
`conversions` INT,
|
||||
`spend` DECIMAL(10, 2),
|
||||
`engagement` INT,
|
||||
`reach` INT,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `project_platform_date` (`project_id`, `platform`, `date`),
|
||||
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Secure storage for API tokens
|
||||
CREATE TABLE IF NOT EXISTS `api_tokens` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`service` VARCHAR(50) NOT NULL UNIQUE, -- e.g., 'meta_ads', 'google_ads'
|
||||
`access_token` TEXT NOT NULL,
|
||||
`refresh_token` TEXT,
|
||||
`expires_at` TIMESTAMP,
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
102
includes/auth.php
Normal file
102
includes/auth.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// Function to check if a user is logged in
|
||||
function is_logged_in() {
|
||||
return isset($_SESSION['user_id']);
|
||||
}
|
||||
|
||||
// Function to require a user to be logged in to access a page
|
||||
function require_login() {
|
||||
if (!is_logged_in()) {
|
||||
header('Location: /login.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check if the current user is an admin
|
||||
function is_admin() {
|
||||
if (!is_logged_in()) {
|
||||
return false;
|
||||
}
|
||||
// If impersonating, the original user must be an admin
|
||||
$user_id = $_SESSION['original_user_id'] ?? $_SESSION['user_id'];
|
||||
|
||||
$db = db();
|
||||
$stmt = $db->prepare('SELECT roles.name FROM users JOIN roles ON users.role_id = roles.id WHERE users.id = ?');
|
||||
$stmt->execute([$user_id]);
|
||||
$role = $stmt->fetchColumn();
|
||||
|
||||
return in_array($role, ['Super Admin', 'Agency Admin']);
|
||||
}
|
||||
|
||||
// Function to require admin privileges
|
||||
function require_admin() {
|
||||
if (!is_admin()) {
|
||||
// Redirect to portal index if not an admin
|
||||
header('Location: /portal');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Function to start impersonating a user
|
||||
function impersonate_user($user_id_to_impersonate) {
|
||||
if (!is_admin()) {
|
||||
return false; // Only admins can impersonate
|
||||
}
|
||||
// Prevent impersonating another admin for security
|
||||
$db = db();
|
||||
$stmt = $db->prepare('SELECT roles.name FROM users JOIN roles ON users.role_id = roles.id WHERE users.id = ?');
|
||||
$stmt->execute([$user_id_to_impersonate]);
|
||||
$role_to_impersonate = $stmt->fetchColumn();
|
||||
|
||||
if (in_array($role_to_impersonate, ['Super Admin', 'Agency Admin']) && $_SESSION['user_id'] != $user_id_to_impersonate) {
|
||||
// Allow admins to view their own profile without triggering this rule
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['original_user_id'])) {
|
||||
$_SESSION['original_user_id'] = $_SESSION['user_id'];
|
||||
}
|
||||
$_SESSION['user_id'] = $user_id_to_impersonate;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to stop impersonating
|
||||
function stop_impersonating() {
|
||||
if (isset($_SESSION['original_user_id'])) {
|
||||
$_SESSION['user_id'] = $_SESSION['original_user_id'];
|
||||
unset($_SESSION['original_user_id']);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to check if currently impersonating
|
||||
function is_impersonating() {
|
||||
return isset($_SESSION['original_user_id']);
|
||||
}
|
||||
|
||||
// Function to get the current user's data (handles impersonation)
|
||||
function current_user() {
|
||||
if (!is_logged_in()) {
|
||||
return null;
|
||||
}
|
||||
$db = db();
|
||||
$stmt = $db->prepare('SELECT users.*, roles.name AS role_name FROM users LEFT JOIN roles ON users.role_id = roles.id WHERE users.id = ?');
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// Function to get the original admin user if impersonating
|
||||
function original_user() {
|
||||
if (!is_impersonating()) {
|
||||
return null;
|
||||
}
|
||||
$db = db();
|
||||
$stmt = $db->prepare('SELECT users.*, roles.name AS role_name FROM users LEFT JOIN roles ON users.role_id = roles.id WHERE users.id = ?');
|
||||
$stmt->execute([$_SESSION['original_user_id']]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
26
includes/pexels.php
Normal file
26
includes/pexels.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
function pexels_key() {
|
||||
$k = getenv('PEXELS_KEY');
|
||||
return $k && strlen($k) > 0 ? $k : 'Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18';
|
||||
}
|
||||
function pexels_get($url) {
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [ 'Authorization: '. pexels_key() ],
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
]);
|
||||
$resp = curl_exec($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($code >= 200 && $code < 300 && $resp) return json_decode($resp, true);
|
||||
return null;
|
||||
}
|
||||
function download_to($srcUrl, $destPath) {
|
||||
$data = file_get_contents($srcUrl);
|
||||
if ($data === false) return false;
|
||||
if (!is_dir(dirname($destPath))) mkdir(dirname($destPath), 0775, true);
|
||||
return file_put_contents($destPath, $data) !== false;
|
||||
}
|
||||
?>
|
||||
132
index.php
132
index.php
@ -1,3 +1,22 @@
|
||||
<?php
|
||||
// Fetch images from Pexels API
|
||||
$api_url = 'http://' . $_SERVER['HTTP_HOST'] . '/api/pexels.php?action=multiple&queries=marketing,social%20media,analytics,creative,dashboard';
|
||||
$images_json = @file_get_contents($api_url);
|
||||
$images = $images_json ? json_decode($images_json, true) : [];
|
||||
|
||||
function get_image($index, $fallback_url) {
|
||||
global $images;
|
||||
return htmlspecialchars($images[$index]['src'] ?? $fallback_url);
|
||||
}
|
||||
|
||||
$hero_image = get_image(0, 'https://picsum.photos/1200/800');
|
||||
$about_image = get_image(1, 'https://picsum.photos/600/400');
|
||||
$portfolio_images = [
|
||||
get_image(2, 'https://picsum.photos/600/400?random=1'),
|
||||
get_image(3, 'https://picsum.photos/600/400?random=2'),
|
||||
get_image(4, 'https://picsum.photos/600/400?random=3'),
|
||||
];
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -23,10 +42,26 @@
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
|
||||
<!-- Icons -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
||||
<style>
|
||||
#hero {
|
||||
position: relative;
|
||||
background-image: url('<?php echo $hero_image; ?>');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
color: white;
|
||||
}
|
||||
.hero-overlay {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 6rem 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@ -41,9 +76,14 @@
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#portfolio">Portfolio</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#pricing">Pricing</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#testimonials">Testimonials</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
|
||||
</ul>
|
||||
<div class="d-flex align-items-center ms-lg-3">
|
||||
<a href="/login.php" class="btn btn-outline-secondary me-2">Login</a>
|
||||
<a href="/register.php" class="btn btn-primary">Register</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@ -52,10 +92,12 @@
|
||||
<main>
|
||||
<!-- Hero Section -->
|
||||
<section id="hero" class="text-center">
|
||||
<div class="container">
|
||||
<h1 class="display-3">Automate Your Marketing with AI</h1>
|
||||
<p class="lead col-lg-8 mx-auto">We build intelligent systems to scale your social media, content creation, and client management. Focus on strategy, not busywork.</p>
|
||||
<a href="#contact" class="btn btn-primary btn-lg mt-4">Get a Demo</a>
|
||||
<div class="hero-overlay">
|
||||
<div class="container">
|
||||
<h1 class="display-3">Automate Your Marketing with AI</h1>
|
||||
<p class="lead col-lg-8 mx-auto">We build intelligent systems to scale your social media, content creation, and client management. Focus on strategy, not busywork.</p>
|
||||
<a href="#contact" class="btn btn-primary btn-lg mt-4">Get a Demo</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -69,8 +111,7 @@
|
||||
<p>Our platform provides a unified portal for your team and clients. Manage projects, approve content, and track performance—all in one place. We integrate with the tools you already love, like Stripe, Calendly, and Meta Ads, to create seamless, automated workflows.</p>
|
||||
</div>
|
||||
<div class="col-lg-6 text-center">
|
||||
<!-- Placeholder for an illustration or screenshot -->
|
||||
<img src="https://via.placeholder.com/500x350/007AFF/FFFFFF?text=Portal+Screenshot" class="img-fluid rounded shadow-sm" alt="Marketing Portal Screenshot">
|
||||
<img src="<?php echo $about_image; ?>" class="img-fluid rounded shadow-sm" alt="Marketing Professionals Collaborating">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -81,9 +122,82 @@
|
||||
<div class="container">
|
||||
<h2 class="text-center">Our Work</h2>
|
||||
<div class="row g-4 mt-4">
|
||||
<div class="col-md-4"><div class="card"><img src="https://via.placeholder.com/600x400/EFEFEF/1D1D1F?text=Project+One" class="card-img-top" alt="Project One"><div class="card-body"><h5 class="card-title">Client A</h5><p class="card-text">Social Media Growth</p></div></div></div>
|
||||
<div class="col-md-4"><div class="card"><img src="https://via.placeholder.com/600x400/EFEFEF/1D1D1F?text=Project+Two" class="card-img-top" alt="Project Two"><div class="card-body"><h5 class="card-title">Client B</h5><p class="card-text">Content Automation</p></div></div></div>
|
||||
<div class="col-md-4"><div class="card"><img src="https://via.placeholder.com/600x400/EFEFEF/1D1D1F?text=Project+Three" class="card-img-top" alt="Project Three"><div class="card-body"><h5 class="card-title">Client C</h5><p class="card-text">Ad Campaign Management</p></div></div></div>
|
||||
<div class="col-md-4"><div class="card h-100"><img src="<?php echo $portfolio_images[0]; ?>" class="card-img-top" alt="Social Media Growth Project"><div class="card-body"><h5 class="card-title">Client A</h5><p class="card-text">Social Media Growth</p></div></div></div>
|
||||
<div class="col-md-4"><div class="card h-100"><img src="<?php echo $portfolio_images[1]; ?>" class="card-img-top" alt="Content Automation Project"><div class="card-body"><h5 class="card-title">Client B</h5><p class="card-text">Content Automation</p></div></div></div>
|
||||
<div class="col-md-4"><div class="card h-100"><img src="<?php echo $portfolio_images[2]; ?>" class="card-img-top" alt="Ad Campaign Management Project"><div class="card-body"><h5 class="card-title">Client C</h5><p class="card-text">Ad Campaign Management</p></div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Pricing Section -->
|
||||
<section id="pricing">
|
||||
<div class="container">
|
||||
<h2 class="text-center">Simple, Transparent Pricing</h2>
|
||||
<p class="text-center text-muted mb-5">Choose the plan that's right for your business. All prices are placeholders.</p>
|
||||
<div class="row">
|
||||
<!-- Plan 1 -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card mb-5 mb-lg-0 h-100">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title text-muted text-uppercase text-center">Starter</h5>
|
||||
<h6 class="card-price text-center">$99<span class="period">/month</span></h6>
|
||||
<hr>
|
||||
<ul class="fa-ul">
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>1 Client Portal</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Content Calendar</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Basic Reporting</li>
|
||||
<li class="text-muted"><span class="fa-li"><i class="fas fa-times"></i></span>AI Content Generation</li>
|
||||
<li class="text-muted"><span class="fa-li"><i class="fas fa-times"></i></span>Ad Integration</li>
|
||||
<li class="text-muted"><span class="fa-li"><i class="fas fa-times"></i></span>Dedicated Support</li>
|
||||
</ul>
|
||||
<div class="d-grid mt-auto">
|
||||
<a href="#" class="btn btn-outline-primary text-uppercase">Choose Plan</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Plan 2 -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card mb-5 mb-lg-0 card-popular h-100">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title text-muted text-uppercase text-center">Pro</h5>
|
||||
<h6 class="card-price text-center">$299<span class="period">/month</span></h6>
|
||||
<hr>
|
||||
<ul class="fa-ul">
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>10 Client Portals</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Content Calendar</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Advanced Reporting</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>AI Content Generation</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Ad Integration</li>
|
||||
<li class="text-muted"><span class="fa-li"><i class="fas fa-times"></i></span>Dedicated Support</li>
|
||||
</ul>
|
||||
<div class="d-grid mt-auto">
|
||||
<a href="#" class="btn btn-primary text-uppercase">Choose Plan</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Plan 3 -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title text-muted text-uppercase text-center">Enterprise</h5>
|
||||
<h6 class="card-price text-center">$599+<span class="period">/month</span></h6>
|
||||
<hr>
|
||||
<ul class="fa-ul">
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Unlimited Client Portals</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Content Calendar</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Advanced Reporting</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>AI Content Generation</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Ad Integration</li>
|
||||
<li><span class="fa-li"><i class="fas fa-check"></i></span>Dedicated Support</li>
|
||||
</ul>
|
||||
<div class="d-grid mt-auto">
|
||||
<a href="#" class="btn btn-outline-primary text-uppercase">Choose Plan</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
44
login.php
Normal file
44
login.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
require_once 'includes/auth.php';
|
||||
if (is_logged_in()) {
|
||||
header('Location: /portal');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - AI-Powered Marketing Portal</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>
|
||||
|
||||
<div class="container d-flex justify-content-center align-items-center vh-100">
|
||||
<div class="card shadow-sm" style="width: 100%; max-width: 400px;">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="card-title text-center mb-4">Client Login</h2>
|
||||
<form action="auth_handler.php" method="POST">
|
||||
<input type="hidden" name="action" value="login">
|
||||
<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>
|
||||
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||
</form>
|
||||
<div class="text-center mt-3">
|
||||
<p>Don't have an account? <a href="register.php">Register here</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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
7
logout.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
require_once 'includes/auth.php';
|
||||
|
||||
session_destroy();
|
||||
|
||||
header('Location: /login.php?logged_out=true');
|
||||
exit();
|
||||
28
portal/admin/impersonate_handler.php
Normal file
28
portal/admin/impersonate_handler.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
|
||||
// Stop impersonation if requested
|
||||
if (isset($_GET['stop'])) {
|
||||
stop_impersonating();
|
||||
header('Location: /portal/admin/users.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Start impersonation if user_id is provided
|
||||
if (isset($_GET['user_id'])) {
|
||||
require_admin(); // Ensure only admins can initiate
|
||||
$user_id_to_impersonate = (int)$_GET['user_id'];
|
||||
|
||||
if (impersonate_user($user_id_to_impersonate)) {
|
||||
header('Location: /portal'); // Redirect to the portal dashboard as the impersonated user
|
||||
exit();
|
||||
} else {
|
||||
// Handle failed impersonation (e.g., trying to impersonate an admin)
|
||||
header('Location: /portal/admin/users.php?error=impersonation_failed');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Default redirect if accessed directly without parameters
|
||||
header('Location: /portal/admin/users.php');
|
||||
exit();
|
||||
48
portal/admin/users.php
Normal file
48
portal/admin/users.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
require_admin(); // Only admins can access this page
|
||||
|
||||
$db = db();
|
||||
$stmt = $db->query('SELECT users.id, users.name, users.email, roles.name AS role_name FROM users LEFT JOIN roles ON users.role_id = roles.id ORDER BY users.name');
|
||||
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$page_title = 'Manage Users';
|
||||
include __DIR__ . '/../includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid p-4">
|
||||
<h1 class="h3 mb-4">Manage Users</h1>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($user['name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['email']); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['role_name']); ?></td>
|
||||
<td>
|
||||
<?php if (!in_array($user['role_name'], ['Super Admin', 'Agency Admin']) || $user['id'] == ($_SESSION['original_user_id'] ?? $_SESSION['user_id'])): ?>
|
||||
<a href="/portal/admin/impersonate_handler.php?user_id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-secondary">Impersonate</a>
|
||||
<?php else: ?>
|
||||
<button class="btn btn-sm btn-outline-secondary" disabled>Impersonate</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include __DIR__ . '/../includes/footer.php'; ?>
|
||||
12
portal/approvals.php
Normal file
12
portal/approvals.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
$page_title = 'Approvals';
|
||||
include __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid p-4">
|
||||
<h1 class="h3 mb-4">Approval Queue</h1>
|
||||
<p>This page will display a list of creative assets and other items pending approval. Clients and managers will be able to review and approve or reject work from this screen.</p>
|
||||
<!-- Approval list will go here -->
|
||||
</div>
|
||||
|
||||
<?php include __DIR__ . '/includes/footer.php'; ?>
|
||||
14
portal/assets.php
Normal file
14
portal/assets.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php require_once __DIR__ . '/includes/header.php'; ?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="h2">Creative Assets</h1>
|
||||
<p>Review and approve creative assets for your projects.</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="text-center">There are no assets available for review at this time.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
14
portal/billing.php
Normal file
14
portal/billing.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php require_once __DIR__ . '/includes/header.php'; ?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="h2">Billing</h1>
|
||||
<p>View your invoices and manage your subscription.</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="text-center">You have no invoices.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
12
portal/calendar.php
Normal file
12
portal/calendar.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
$page_title = 'Calendar';
|
||||
include __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid p-4">
|
||||
<h1 class="h3 mb-4">Project Calendar</h1>
|
||||
<p>This page will contain a calendar view of project milestones, deadlines, and other important events. The data will be dynamically loaded based on project and user roles.</p>
|
||||
<!-- Calendar integration will go here -->
|
||||
</div>
|
||||
|
||||
<?php include __DIR__ . '/includes/footer.php'; ?>
|
||||
14
portal/courses.php
Normal file
14
portal/courses.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php require_once __DIR__ . '/includes/header.php'; ?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="h2">My Courses</h1>
|
||||
<p>Access your purchased courses here.</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="text-center">You have not purchased any courses yet.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
6
portal/includes/footer.php
Normal file
6
portal/includes/footer.php
Normal file
@ -0,0 +1,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
55
portal/includes/header.php
Normal file
55
portal/includes/header.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
require_login();
|
||||
$current_user = current_user();
|
||||
$original_user = original_user(); // Get original user if impersonating
|
||||
|
||||
$page = basename($_SERVER['PHP_SELF']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Client Portal</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="/assets/css/portal.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php if (is_impersonating()): ?>
|
||||
<div class="impersonation-banner bg-warning text-dark text-center p-2">
|
||||
You are currently viewing as <strong><?php echo htmlspecialchars($current_user['name']); ?></strong>.
|
||||
<a href="/portal/admin/impersonate_handler.php?stop=true" class="fw-bold">Return to your admin view</a>.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<header class="portal-nav p-3 d-flex justify-content-between align-items-center">
|
||||
<a href="/portal" class="h4 text-dark text-decoration-none">Client Portal</a>
|
||||
<div>
|
||||
<span class="me-3">Welcome, <?php echo htmlspecialchars($original_user ? $original_user['name'] : $current_user['name']); ?>!</span>
|
||||
<a href="/logout.php" class="btn btn-outline-primary">Logout</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="portal-main">
|
||||
<aside class="sidebar p-3">
|
||||
<nav class="nav flex-column">
|
||||
<a class="nav-link <?php echo ($page == 'index.php') ? 'active' : ''; ?>" href="/portal">Dashboard</a>
|
||||
<a class="nav-link <?php echo ($page == 'projects.php') ? 'active' : ''; ?>" href="/portal/projects.php">Projects</a>
|
||||
<a class="nav-link <?php echo ($page == 'calendar.php') ? 'active' : ''; ?>" href="/portal/calendar.php">Calendar</a>
|
||||
<a class="nav-link <?php echo ($page == 'assets.php') ? 'active' : ''; ?>" href="/portal/assets.php">Creative Assets</a>
|
||||
<a class="nav-link <?php echo ($page == 'approvals.php') ? 'active' : ''; ?>" href="/portal/approvals.php">Approvals</a>
|
||||
<a class="nav-link <?php echo ($page == 'metrics.php') ? 'active' : ''; ?>" href="/portal/metrics.php">Metrics</a>
|
||||
<a class="nav-link <?php echo ($page == 'courses.php') ? 'active' : ''; ?>" href="/portal/courses.php">Courses</a>
|
||||
<a class="nav-link <?php echo ($page == 'billing.php') ? 'active' : ''; ?>" href="/portal/billing.php">Billing</a>
|
||||
<a class="nav-link <?php echo ($page == 'profile.php') ? 'active' : ''; ?>" href="/portal/profile.php">Profile</a>
|
||||
<?php if (is_admin()): ?>
|
||||
<hr>
|
||||
<h6 class="nav-link-heading text-muted">Admin</h6>
|
||||
<a class="nav-link <?php echo ($page == 'users.php') ? 'active' : ''; ?>" href="/portal/admin/users.php">Manage Users</a>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
</aside>
|
||||
<main class="content">
|
||||
31
portal/index.php
Normal file
31
portal/index.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="h2">Dashboard</h1>
|
||||
<p>Welcome to your client portal. Here you can manage your projects, view creative assets, access your courses, and handle billing.</p>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Recent Projects</h5>
|
||||
<p class="card-text">You have no active projects.</p>
|
||||
<a href="/portal/projects.php" class="btn btn-primary">View Projects</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">My Courses</h5>
|
||||
<p class="card-text">You have not enrolled in any courses.</p>
|
||||
<a href="/portal/courses.php" class="btn btn-primary">View Courses</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
16
portal/profile.php
Normal file
16
portal/profile.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php require_once __DIR__ . '/includes/header.php'; ?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="h2">My Profile</h1>
|
||||
<p>Your account information.</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p><strong>Name:</strong> <?php echo htmlspecialchars($current_user['name']); ?></p>
|
||||
<p><strong>Email:</strong> <?php echo htmlspecialchars($current_user['email']); ?></p>
|
||||
<p><strong>Role:</strong> <?php echo htmlspecialchars($current_user['role_name']); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
14
portal/projects.php
Normal file
14
portal/projects.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php require_once __DIR__ . '/includes/header.php'; ?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="h2">My Projects</h1>
|
||||
<p>Here is a list of your projects and their current status.</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="text-center">No projects have been assigned to you yet.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
48
register.php
Normal file
48
register.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
require_once 'includes/auth.php';
|
||||
if (is_logged_in()) {
|
||||
header('Location: /portal');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Register - AI-Powered Marketing Portal</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>
|
||||
|
||||
<div class="container d-flex justify-content-center align-items-center vh-100">
|
||||
<div class="card shadow-sm" style="width: 100%; max-width: 400px;">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="card-title text-center mb-4">Create Account</h2>
|
||||
<form action="auth_handler.php" method="POST">
|
||||
<input type="hidden" name="action" value="register">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Full 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>
|
||||
<button type="submit" class="btn btn-primary w-100">Register</button>
|
||||
</form>
|
||||
<div class="text-center mt-3">
|
||||
<p>Already have an account? <a href="login.php">Login here</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user