Compare commits

..

6 Commits

Author SHA1 Message Date
Flatlogic Bot
893c62c0ac 77 2025-11-22 14:20:01 +00:00
Flatlogic Bot
dc54701498 N7 finsh finsh 2025-11-22 14:03:37 +00:00
Flatlogic Bot
166c8cbdda N7 finsh 2025-11-22 13:55:59 +00:00
Flatlogic Bot
9f05f404f3 Auto commit: 2025-11-22T13:52:11.419Z 2025-11-22 13:52:11 +00:00
Flatlogic Bot
ca7cc5dbed Revert to version 1757816 2025-11-22 13:29:42 +00:00
Flatlogic Bot
dd6845856c n7 2025-11-22 13:26:41 +00:00
26 changed files with 1923 additions and 146 deletions

67
accept-application.php Normal file
View File

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
session_start();
require_once 'db/config.php';
if (!isset($_SESSION['user_id'])) {
header("Location: /login.php");
exit;
}
$application_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$user_id = $_SESSION['user_id'];
if (!$application_id) {
header("Location: /index.php");
exit;
}
$pdo = db();
// Begin a transaction
$pdo->beginTransaction();
try {
// Get application and task details, and ensure the current user owns the task
$stmt = $pdo->prepare(
"SELECT a.id as application_id, a.task_id, t.user_id as task_owner_id
FROM applications a
JOIN tasks t ON a.task_id = t.id
WHERE a.id = ? AND t.user_id = ?"
);
$stmt->execute([$application_id, $user_id]);
$application_info = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$application_info) {
// If no result, either application doesn't exist or user doesn't own the task.
throw new Exception("Authorization failed or application not found.");
}
$task_id = $application_info['task_id'];
// 1. Update the accepted application's status to 'accepted'
$stmt = $pdo->prepare("UPDATE applications SET status = 'accepted' WHERE id = ?");
$stmt->execute([$application_id]);
// 2. Update the task's status to 'assigned'
$stmt = $pdo->prepare("UPDATE tasks SET status = 'assigned' WHERE id = ?");
$stmt->execute([$task_id]);
// 3. Reject all other pending applications for this task
$stmt = $pdo->prepare("UPDATE applications SET status = 'rejected' WHERE task_id = ? AND id != ? AND status = 'pending'");
$stmt->execute([$task_id, $application_id]);
// If all queries were successful, commit the transaction
$pdo->commit();
header("Location: /task-details.php?id=" . $task_id . "&message=application_accepted");
exit;
} catch (Exception $e) {
// If any query fails, roll back the transaction
$pdo->rollBack();
error_log($e->getMessage());
// Redirect with a generic error. Avoid exposing specific DB errors.
header("Location: /task-details.php?id=" . ($task_id ?? 0) . "&message=acceptance_failed");
exit;
}

47
apply.php Normal file
View File

@ -0,0 +1,47 @@
<?php
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/shared/header.php';
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['task_id'])) {
$task_id = $_POST['task_id'];
$user_id = $_SESSION['user_id'];
// Check if user has already applied
$stmt = db()->prepare("SELECT id FROM applications WHERE task_id = ? AND user_id = ?");
$stmt->execute([$task_id, $user_id]);
if ($stmt->fetch()) {
// User has already applied
header('Location: index.php?message=already_applied');
exit();
}
// Check if user is the task owner
$stmt = db()->prepare("SELECT id FROM tasks WHERE id = ? AND user_id = ?");
$stmt->execute([$task_id, $user_id]);
if ($stmt->fetch()) {
// User is the task owner
header('Location: index.php?message=owner_cannot_apply');
exit();
}
try {
$stmt = db()->prepare("INSERT INTO applications (task_id, user_id) VALUES (?, ?)");
$stmt->execute([$task_id, $user_id]);
header('Location: index.php?message=applied_successfully');
exit();
} catch (PDOException $e) {
error_log($e->getMessage());
header('Location: index.php?message=application_failed');
exit();
}
}
}
// Redirect to index if accessed directly without POST
header('Location: index.php');

410
assets/css/main.css Normal file
View File

@ -0,0 +1,410 @@
@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:wght@400;500;700&display=swap');
/* General Body & Typography */
body {
font-family: 'Be Vietnam Pro', sans-serif;
background-color: #f8f9fa;
color: #212529;
margin: 0;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
/* Header & Navigation */
.header {
background-color: #ffffff;
padding: 1rem 2rem;
border-bottom: 1px solid #dee2e6;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.04);
position: sticky;
top: 0;
z-index: 1000;
}
.logo {
font-size: 1.75rem;
font-weight: 700;
color: #0d6efd;
text-decoration: none;
}
.nav-buttons .btn {
margin-left: 1rem;
}
.welcome-message {
margin-right: 1rem;
color: #495057;
font-weight: 500;
}
/* Buttons */
.btn {
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
text-decoration: none;
font-weight: 700;
font-size: 1rem;
transition: all 0.3s ease;
border: none;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.btn-primary {
background: linear-gradient(45deg, #0d6efd, #0a58ca);
color: #ffffff;
}
.btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 6px 12px rgba(13, 110, 253, 0.25);
}
.btn-secondary {
background-color: #6c757d;
color: #fff;
}
.btn-secondary:hover {
background-color: #5a6268;
}
.btn-success {
background-color: #198754;
color: white;
}
.btn-danger {
background-color: #dc3545;
color: white;
}
/* Forms */
.form-container {
max-width: 500px;
margin: 2rem auto;
padding: 2.5rem;
background-color: #ffffff;
border-radius: 1rem;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}
.form-container h1 {
text-align: center;
margin-bottom: 2rem;
font-size: 2.25rem;
color: #0d6efd;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 700;
color: #495057;
}
.form-group input, .form-group textarea {
width: 100%;
padding: 0.85rem;
border: 1px solid #ced4da;
border-radius: 0.5rem;
font-size: 1rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.form-group input:focus, .form-group textarea:focus {
outline: none;
border-color: #0d6efd;
box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.15);
}
/* Search Form */
.search-form {
display: flex;
gap: 1rem;
margin: 2rem 0;
padding: 1.5rem;
background-color: #fff;
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.search-form input {
flex-grow: 1;
padding: 0.75rem;
border: 1px solid #ced4da;
border-radius: 0.5rem;
}
/* Task Cards */
.task-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.75rem;
}
.task-card {
background-color: #ffffff;
border-radius: 1rem;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
padding: 1.75rem;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
border: 1px solid transparent;
}
.task-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
border-color: #0d6efd;
}
.task-card h3 {
margin-top: 0;
font-size: 1.35rem;
color: #0d6efd;
}
.task-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1rem;
color: #6c757d;
font-size: 0.9rem;
}
.task-meta a {
color: #0d6efd;
text-decoration: none;
font-weight: 600;
}
.task-payout {
font-size: 1.75rem;
font-weight: 700;
color: #198754;
margin-top: auto;
padding-top: 1rem;
}
/* Page Headers */
.page-header {
margin-bottom: 2rem;
border-bottom: 1px solid #dee2e6;
padding-bottom: 1rem;
}
.page-header h1 {
font-size: 2.75rem;
color: #0d6efd;
}
/* Alerts & Status Badges */
.alert {
padding: 1rem;
margin-bottom: 1rem;
border-radius: 0.5rem;
border: 1px solid transparent;
}
.alert-success {
color: #0f5132;
background-color: #d1e7dd;
border-color: #badbcc;
}
.alert-warning {
color: #664d03;
background-color: #fff3cd;
border-color: #ffecb5;
}
.alert-danger {
color: #842029;
background-color: #f8d7da;
border-color: #f5c2c7;
}
.status-badge {
padding: 0.3em 0.7em;
font-size: 0.85em;
font-weight: 700;
border-radius: 0.5rem;
text-transform: capitalize;
}
.status-open {
background-color: #cfe2ff;
color: #052c65;
}
.status-pending {
background-color: #fff3cd;
color: #664d03;
}
.status-assigned, .status-accepted {
background-color: #d1e7dd;
color: #0f5132;
}
.status-rejected {
background-color: #f8d7da;
color: #842029;
}
/* Management & Profile Pages */
.task-manage-card, .application-card, .applicant-card, .task-card-profile {
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.applicant-card {
display: flex;
justify-content: space-between;
align-items: center;
}
.profile-page .page-header {
text-align: center;
}
.profile-section {
margin-top: 3rem;
}
.profile-section h2 {
font-size: 1.75rem;
margin-bottom: 1.5rem;
border-bottom: 2px solid #dee2e6;
padding-bottom: 0.5rem;
color: #0d6efd;
}
/* Footer */
.footer {
text-align: center;
padding: 2rem;
margin-top: 2rem;
background-color: #e9ecef;
border-top: 1px solid #dee2e6;
color: #6c757d;
}
/* Manage Task Card Improvements */
.task-manage-card {
display: flex;
justify-content: space-between;
align-items: center;
transition: background-color 0.3s;
}
.task-manage-card:hover {
background-color: #f8f9fa;
}
.task-info h2 {
margin-top: 0;
margin-bottom: 0.25rem;
}
.task-info h2 a {
text-decoration: none;
color: #0d6efd;
}
.task-info p {
margin-bottom: 0;
color: #6c757d;
}
.task-actions {
display: flex;
gap: 0.5rem;
}
.btn-sm {
padding: 0.25rem 0.75rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
.task-card h3 a {
text-decoration: none;
color: inherit;
}
/* Language Switcher */
.lang-switcher {
margin-right: 1rem;
}
.lang-switcher a {
color: #6c757d;
text-decoration: none;
font-weight: 500;
}
.lang-switcher a.active {
color: #0d6efd;
font-weight: 700;
}
/* RTL Support */
[dir="rtl"] {
text-align: right;
}
[dir="rtl"] .header {
flex-direction: row-reverse;
}
[dir="rtl"] .nav-buttons .btn {
margin-left: 0;
margin-right: 1rem;
}
[dir="rtl"] .lang-switcher {
margin-right: 0;
margin-left: 1rem;
}
[dir="rtl"] .task-meta {
flex-direction: row-reverse;
}
[dir="rtl"] .search-form {
flex-direction: row-reverse;
}
[dir="rtl"] .form-group label {
text-align: right;
}
[dir="rtl"] .task-manage-card {
flex-direction: row-reverse;
}
[dir="rtl"] .task-actions {
flex-direction: row-reverse;
}
[dir="rtl"] .applicant-card {
flex-direction: row-reverse;
}

40
db/migrate.php Normal file
View File

@ -0,0 +1,40 @@
<?php
require_once __DIR__ . '/config.php';
function run_migrations() {
$pdo = db();
$log_file = __DIR__ . '/migrations.log';
$migrations_dir = __DIR__ . '/migrations/';
if (!file_exists($log_file)) {
touch($log_file);
}
$executed_migrations = file($log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$all_migration_files = scandir($migrations_dir);
echo "Checking for new migrations...\n";
foreach ($all_migration_files as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) === 'sql') {
if (!in_array($file, $executed_migrations)) {
echo "Executing migration: $file...\n";
$sql = file_get_contents($migrations_dir . $file);
try {
$pdo->exec($sql);
file_put_contents($log_file, $file . PHP_EOL, FILE_APPEND);
echo "Migration $file executed successfully.\n";
} catch (PDOException $e) {
echo "Error executing migration $file: " . $e->getMessage() . "\n";
// Stop on error
return;
}
}
}
}
echo "All migrations are up to date.\n";
}
run_migrations();

4
db/migrations.log Normal file
View File

@ -0,0 +1,4 @@
001_create_users_table.sql
002_create_tasks_table.sql
003_create_applications_table.sql
004_add_whatsapp_to_users.sql

View File

@ -0,0 +1,9 @@
-- Create the users table
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
phone VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS tasks (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
category VARCHAR(100),
location VARCHAR(255),
payout DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS applications (
id INT AUTO_INCREMENT PRIMARY KEY,
task_id INT NOT NULL,
user_id INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'applied',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN whatsapp_number VARCHAR(255) DEFAULT NULL;

View File

@ -0,0 +1 @@
ALTER TABLE applications ADD COLUMN status VARCHAR(50) NOT NULL DEFAULT 'pending';

50
delete-task.php Normal file
View File

@ -0,0 +1,50 @@
<?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: manage-tasks.php?error=invalid_task_id");
exit();
}
$taskId = (int)$_GET['id'];
$userId = $_SESSION['user_id'];
try {
$pdo = db();
// First, verify the task belongs to the user
$stmt = $pdo->prepare('SELECT user_id FROM tasks WHERE id = :task_id');
$stmt->bindParam(':task_id', $taskId, PDO::PARAM_INT);
$stmt->execute();
$task = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$task || $task['user_id'] != $userId) {
header("Location: manage-tasks.php?error=unauthorized");
exit();
}
// Delete applications for the task
$stmt = $pdo->prepare('DELETE FROM applications WHERE task_id = :task_id');
$stmt->bindParam(':task_id', $taskId, PDO::PARAM_INT);
$stmt->execute();
// Delete the task
$stmt = $pdo->prepare('DELETE FROM tasks WHERE id = :task_id');
$stmt->bindParam(':task_id', $taskId, PDO::PARAM_INT);
$stmt->execute();
header("Location: manage-tasks.php?success=task_deleted");
exit();
} catch (PDOException $e) {
// Log the error and redirect
error_log("Delete task failed: " . $e->getMessage());
header("Location: manage-tasks.php?error=db_error");
exit();
}

304
index.php
View File

@ -1,150 +1,180 @@
<?php <?php
declare(strict_types=1); $pageTitle = 'Welcome to CashTask';
@ini_set('display_errors', '1'); require_once __DIR__ . '/shared/header.php';
@error_reporting(E_ALL); require_once __DIR__ . '/db/config.php';
@date_default_timezone_set('UTC');
$message = $_GET['message'] ?? null;
$message_map = [
'already_applied' => [
'text' => 'You have already applied for this task.',
'type' => 'warning'
],
'owner_cannot_apply' => [
'text' => 'You cannot apply for your own task.',
'type' => 'warning'
],
'applied_successfully' => [
'text' => 'You have successfully applied for the task.',
'type' => 'success'
],
'application_failed' => [
'text' => 'There was an error submitting your application.',
'type' => 'danger'
]
];
// Fetch tasks from the database
try {
$search = $_GET['search'] ?? '';
$category = $_GET['category'] ?? '';
$location = $_GET['location'] ?? '';
$sql = "SELECT t.*, u.id as user_id_poster, u.full_name as user_name FROM tasks t JOIN users u ON t.user_id = u.id WHERE t.status = 'open'";
$params = [];
if (!empty($search)) {
$sql .= " AND (t.title LIKE :search OR t.description LIKE :search)";
$params[':search'] = '%' . $search . '%';
}
if (!empty($category)) {
$sql .= " AND t.category LIKE :category";
$params[':category'] = '%' . $category . '%';
}
if (!empty($location)) {
$sql .= " AND t.location LIKE :location";
$params[':location'] = '%' . $location . '%';
}
$sql .= " ORDER BY t.created_at DESC";
$stmt = db()->prepare($sql);
$stmt->execute($params);
$tasks = $stmt->fetchAll();
} catch (PDOException $e) {
// Handle database error gracefully
error_log($e->getMessage());
$tasks = [];
}
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?> ?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style> <style>
:root { .alert {
--bg-color-start: #6a11cb; padding: 1rem;
--bg-color-end: #2575fc; margin-bottom: 1rem;
--text-color: #ffffff; border-radius: 0.25rem;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
} }
body { .alert-success {
margin: 0; color: #155724;
font-family: 'Inter', sans-serif; background-color: #d4edda;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end)); border-color: #c3e6cb;
color: var(--text-color); }
.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeeba;
}
.alert-danger {
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
.search-form {
display: flex; display: flex;
justify-content: center; gap: 1rem;
margin-bottom: 2rem;
}
.search-form input {
flex-grow: 1;
padding: 0.75rem;
border: 1px solid #ced4da;
border-radius: 0.25rem;
}
.task-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
}
.task-card {
background-color: #ffffff;
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
padding: 1.5rem;
transition: all 0.2s ease-in-out;
}
.task-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
.task-card h3 {
margin-top: 0;
font-size: 1.25rem;
}
.task-meta {
display: flex;
justify-content: space-between;
align-items: center; align-items: center;
min-height: 100vh; margin-top: 1rem;
text-align: center; color: #6c757d;
overflow: hidden;
position: relative;
} }
body::before {
content: ''; .task-payout {
position: absolute; font-size: 1.5rem;
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; font-weight: 700;
margin: 0 0 1rem; color: #14b8a6;
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> </style>
</head>
<body> <?php if (isset($message_map[$message])): ?>
<main> <div class="alert alert-<?= $message_map[$message]['type'] ?>">
<div class="card"> <?= htmlspecialchars($message_map[$message]['text']) ?>
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div> </div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> <?php endif; ?>
<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 class="hero-section" style="text-align: center; padding: 4rem 0;">
<h1 style="font-size: 3rem; font-weight: 700;"><?= __('hero_title') ?></h1>
<p style="font-size: 1.25rem; color: #6c757d; margin-bottom: 2rem;"><?= __('hero_subtitle') ?></p>
<a href="/signup.php" class="btn btn-primary"><?= __('post_task') ?></a>
</div> </div>
</main>
<footer> <h2><?= __('available_tasks') ?></h2>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer> <form action="index.php" method="GET" class="search-form">
</body> <input type="text" name="search" placeholder="<?= __('search_keywords') ?>" value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
</html> <input type="text" name="category" placeholder="<?= __('search_category') ?>" value="<?= htmlspecialchars($_GET['category'] ?? '') ?>">
<input type="text" name="location" placeholder="<?= __('search_location') ?>" value="<?= htmlspecialchars($_GET['location'] ?? '') ?>">
<button type="submit" class="btn"><?= __('search') ?></button>
</form>
<div class="task-grid">
<?php if (empty($tasks)): ?>
<p><?= __('no_tasks_found_criteria') ?></p>
<?php else: ?>
<?php foreach ($tasks as $task): ?>
<div class="task-card">
<h3><a href="task-details.php?id=<?= $task['id'] ?>"><?= htmlspecialchars($task['title']) ?></a></h3>
<div class="task-meta">
<span><?= htmlspecialchars($task['category']) ?> &bull; <?= htmlspecialchars($task['location']) ?></span>
<span><?= __('posted_by') ?>: <a href="profile.php?id=<?= $task['user_id_poster'] ?>"><strong><?= htmlspecialchars($task['user_name']) ?></strong></a></span>
</div>
<div class="task-payout">$<?= htmlspecialchars((string)$task['payout']) ?></div>
<?php if (isset($_SESSION['user_id']) && $_SESSION['user_id'] != $task['user_id']): ?>
<form action="apply.php" method="POST" style="margin-top: 1rem;">
<input type="hidden" name="task_id" value="<?= $task['id'] ?>">
<button type="submit" class="btn btn-primary"><?= __('apply') ?></button>
</form>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php
require_once __DIR__ . '/shared/footer.php';
?>

114
lang/ar.php Normal file
View File

@ -0,0 +1,114 @@
<?php
$translations = [
'app_title' => 'سوق قائم على المهام',
'home' => 'الرئيسية',
'post_task' => 'انشر مهمة',
'manage_tasks' => 'إدارة مهامك',
'my_applications' => 'طلباتي',
'my_profile' => 'ملفي الشخصي',
'logout' => 'تسجيل الخروج',
'login' => 'تسجيل الدخول',
'signup' => 'إنشاء حساب',
'search_tasks' => 'ابحث عن مهام...',
'search' => 'بحث',
'task_title' => 'العنوان',
'task_description' => 'الوصف',
'task_budget' => 'الميزانية',
'posted_by' => 'نشر بواسطة',
'apply' => 'قدم الآن',
'task_details' => 'تفاصيل المهمة',
'all_tasks' => 'كل المهام',
'delete' => 'حذف',
'whatsapp_number' => 'رقم الواتساب',
'submit_application' => 'إرسال الطلب',
'application_submitted' => 'تم إرسال الطلب',
'accept' => 'قبول',
'accepted' => 'مقبول',
'rejected' => 'مرفوض',
'pending' => 'قيد الانتظار',
'view_details' => 'عرض التفاصيل',
'no_tasks' => 'لم يتم العثور على مهام.',
'no_applications' => 'لم يتقدم أحد لهذه المهمة بعد.',
'no_tasks_posted' => 'لم تقم بنشر أي مهام بعد.',
'no_applications_submitted' => 'لم تتقدم لأي مهام بعد.',
'english' => 'English',
'arabic' => 'العربية',
'hero_title' => 'أنجز أي شيء، اليوم.',
'hero_subtitle' => 'أفضل مكان للعثور على مساعدة سريعة لمهامك اليومية.',
'available_tasks' => 'المهام المتاحة',
'search_keywords' => 'كلمات مفتاحية...',
'search_category' => 'فئة...',
'search_location' => 'موقع...',
'no_tasks_found_criteria' => 'لم يتم العثور على مهام تطابق معايير البحث. حاول توسيع نطاق البحث!',
'login_title' => 'تسجيل الدخول',
'email_label' => 'البريد الإلكتروني',
'password_label' => 'كلمة المرور',
'login_button' => 'تسجيل الدخول',
'no_account_prompt' => 'ليس لديك حساب؟',
'signup_link' => 'إنشاء حساب',
'error_all_fields' => 'يرجى ملء جميع الحقول.',
'error_invalid_credentials' => 'البريد الإلكتروني أو كلمة المرور غير صالحة.',
'signup_title' => 'إنشاء حساب',
'full_name_label' => 'الاسم الكامل',
'email_address_label' => 'عنوان البريد الإلكتروني',
'phone_number_label' => 'رقم الهاتف',
'confirm_password_label' => 'تأكيد كلمة المرور',
'signup_button' => 'إنشاء حساب',
'error_full_name_required' => 'الاسم الكامل مطلوب.',
'error_email_required' => 'البريد الإلكتروني مطلوب.',
'error_invalid_email' => 'صيغة البريد الإلكتروني غير صالحة.',
'error_phone_required' => 'رقم الهاتف مطلوب.',
'error_password_required' => 'كلمة المرور مطلوبة.',
'error_passwords_no_match' => 'كلمات المرور غير متطابقة.',
'error_email_exists' => 'مستخدم بهذا البريد الإلكتروني موجود بالفعل.',
'error_database' => 'خطأ في قاعدة البيانات. يرجى المحاولة مرة أخرى في وقت لاحق.',
'profile_title' => 'ملف %s الشخصي',
'member_since' => 'عضو منذ: %s',
'whatsapp_updated_success' => 'تم تحديث رقم الواتساب الخاص بك بنجاح.',
'whatsapp_update_failed' => 'فشل تحديث رقم الواتساب.',
'my_contact_info' => 'معلومات الاتصال الخاصة بي',
'whatsapp_number_label' => 'رقم الواتساب',
'whatsapp_placeholder' => 'أدخل رقم الواتساب الخاص بك...',
'whatsapp_help_text' => 'سيتم مشاركة هذا فقط مع المستخدمين الذين تقبل طلباتهم.',
'save_button' => 'حفظ',
'posted_tasks' => 'المهام المنشورة',
'no_posted_tasks' => 'لم يقم %s بنشر أي مهام بعد.',
'completed_tasks' => 'المهام المكتملة',
'no_completed_tasks' => 'لم يكمل %s أي مهام بعد.',
'task_status' => 'الحالة',
'task_payout' => 'المكافأة',
'post_new_task_title' => 'انشر مهمة جديدة',
'task_title_label' => 'عنوان المهمة',
'description_label' => 'الوصف',
'category_label' => 'الفئة',
'location_label' => 'الموقع',
'payout_label' => 'المكافأة',
'post_task_button' => 'انشر المهمة',
'error_task_required_fields' => 'العنوان والوصف والمكافأة حقول مطلوبة.',
'manage_tasks_title' => 'إدارة مهامك',
'manage_tasks_description' => 'هذه هي المهام التي قمت بنشرها. انقر على مهمة لعرض وإدارة الطلبات.',
'no_posted_tasks_link' => 'لم تقم بنشر أي مهام بعد. <a href="post-task.php">انشر واحدة الآن!</a>',
'task_status_label' => 'الحالة',
'delete_task_confirm' => 'هل أنت متأكد أنك تريد حذف هذه المهمة؟',
'my_applications_title' => 'طلباتي',
'my_applications_description' => 'هذه هي المهام التي تقدمت إليها وحالتها الحالية.',
'no_applications_yet' => 'لم تتقدم لأي مهام بعد.',
'posted_by_label' => 'نشر بواسطة',
'task_details_title' => 'تفاصيل المهمة',
'budget_label' => 'الميزانية',
'posted_on_label' => 'نشر في',
'applicants_title' => 'المتقدمون',
'add_whatsapp_prompt' => 'يرجى <a href="/profile.php?id={$_SESSION['user_id']}">إضافة رقم WhatsApp الخاص بك</a> إلى ملفك الشخصي قبل قبول الطلبات.',
'no_applicants_yet' => 'لا يوجد متقدمون بعد.',
'accept_button' => 'قبول',
'my_application_title' => 'طلبي',
'application_accepted_message' => 'تهانينا! تم قبول طلبك.',
'contact_owner_whatsapp' => 'تواصل مع صاحب المهمة على WhatsApp: <strong>%s</strong>',
'application_status_label' => 'حالة طلبك هي',
'apply_now_button' => 'قدم الآن',
'manage_task_title' => 'إدارة المهمة',
'reject_button' => 'رفض',
'back_to_my_tasks' => 'العودة إلى كل مهامي',
'application_rejected_message' => 'تم رفض الطلب.',
'applicant_awarded' => 'تم منح هذا المتقدم المهمة.',
];

114
lang/en.php Normal file
View File

@ -0,0 +1,114 @@
<?php
$translations = [
'app_title' => 'Task-Based Marketplace',
'home' => 'Home',
'post_task' => 'Post a Task',
'manage_tasks' => 'Manage Your Tasks',
'my_applications' => 'My Applications',
'my_profile' => 'My Profile',
'logout' => 'Logout',
'login' => 'Login',
'signup' => 'Sign Up',
'search_tasks' => 'Search for tasks...',
'search' => 'Search',
'task_title' => 'Title',
'task_description' => 'Description',
'task_budget' => 'Budget',
'posted_by' => 'Posted by',
'apply' => 'Apply',
'task_details' => 'Task Details',
'all_tasks' => 'All Tasks',
'delete' => 'Delete',
'whatsapp_number' => 'WhatsApp Number',
'submit_application' => 'Submit Application',
'application_submitted' => 'Application Submitted',
'accept' => 'Accept',
'accepted' => 'Accepted',
'rejected' => 'Rejected',
'pending' => 'Pending',
'view_details' => 'View Details',
'no_tasks' => 'No tasks found.',
'no_applications' => 'No one has applied to this task yet.',
'no_tasks_posted' => 'You have not posted any tasks yet.',
'no_applications_submitted' => 'You have not applied to any tasks yet.',
'english' => 'English',
'arabic' => 'Arabic',
'hero_title' => 'Get anything done, today.',
'hero_subtitle' => 'The best place to find quick help for your daily tasks.',
'available_tasks' => 'Available Tasks',
'search_keywords' => 'Keywords...',
'search_category' => 'Category...',
'search_location' => 'Location...',
'no_tasks_found_criteria' => 'No tasks found matching your criteria. Try broadening your search!',
'login_title' => 'Login',
'email_label' => 'Email',
'password_label' => 'Password',
'login_button' => 'Login',
'no_account_prompt' => "Don't have an account?",
'signup_link' => 'Sign up',
'error_all_fields' => 'Please fill in all fields.',
'error_invalid_credentials' => 'Invalid email or password.',
'signup_title' => 'Create an Account',
'full_name_label' => 'Full Name',
'email_address_label' => 'Email Address',
'phone_number_label' => 'Phone Number',
'confirm_password_label' => 'Confirm Password',
'signup_button' => 'Sign Up',
'error_full_name_required' => 'Full name is required.',
'error_email_required' => 'Email is required.',
'error_invalid_email' => 'Invalid email format.',
'error_phone_required' => 'Phone number is required.',
'error_password_required' => 'Password is required.',
'error_passwords_no_match' => 'Passwords do not match.',
'error_email_exists' => 'A user with this email already exists.',
'error_database' => 'Database error. Please try again later.',
'profile_title' => "%s's Profile",
'member_since' => 'Member Since: %s',
'whatsapp_updated_success' => 'Your WhatsApp number has been updated successfully.',
'whatsapp_update_failed' => 'Failed to update WhatsApp number.',
'my_contact_info' => 'My Contact Information',
'whatsapp_number_label' => 'WhatsApp Number',
'whatsapp_placeholder' => 'Enter your WhatsApp number...',
'whatsapp_help_text' => 'This will only be shared with users whose applications you accept.',
'save_button' => 'Save',
'posted_tasks' => 'Posted Tasks',
'no_posted_tasks' => '%s has not posted any tasks yet.',
'completed_tasks' => 'Completed Tasks',
'no_completed_tasks' => '%s has not completed any tasks yet.',
'task_status' => 'Status',
'task_payout' => 'Payout',
'post_new_task_title' => 'Post a New Task',
'task_title_label' => 'Task Title',
'description_label' => 'Description',
'category_label' => 'Category',
'location_label' => 'Location',
'payout_label' => 'Payout',
'post_task_button' => 'Post Task',
'error_task_required_fields' => 'Title, description, and payout are required.',
'manage_tasks_title' => 'Manage Your Tasks',
'manage_tasks_description' => "Here are the tasks you've posted. Click on a task to view and manage applications.",
'no_posted_tasks_link' => 'You have not posted any tasks yet. <a href="post-task.php">Post one now!</a>',
'task_status_label' => 'Status',
'delete_task_confirm' => 'Are you sure you want to delete this task?',
'my_applications_title' => 'My Applications',
'my_applications_description' => "Here are the tasks you've applied for and their current status.",
'no_applications_yet' => 'You have not applied for any tasks yet.',
'posted_by_label' => 'Posted by',
'task_details_title' => 'Task Details',
'budget_label' => 'Budget',
'posted_on_label' => 'Posted on',
'applicants_title' => 'Applicants',
'add_whatsapp_prompt' => 'Please <a href="/profile.php?id={$_SESSION['user_id']}">add your WhatsApp number</a> to your profile before accepting applications.',
'no_applicants_yet' => 'No applications yet.',
'accept_button' => 'Accept',
'my_application_title' => 'My Application',
'application_accepted_message' => 'Congratulations! Your application was accepted.',
'contact_owner_whatsapp' => 'Contact the task owner on WhatsApp: <strong>%s</strong>',
'application_status_label' => 'Your application status is',
'apply_now_button' => 'Apply Now',
'manage_task_title' => 'Manage Task',
'reject_button' => 'Reject',
'back_to_my_tasks' => 'Back to All My Tasks',
'application_rejected_message' => 'Application rejected.',
'applicant_awarded' => 'This applicant was awarded the task.',
];

56
login.php Normal file
View File

@ -0,0 +1,56 @@
<?php
require_once 'shared/header.php';
require_once 'db/config.php';
$error = '';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($email) || empty($password)) {
$error = 'error_all_fields';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['full_name'];
header("Location: index.php");
exit;
} else {
$error = 'error_invalid_credentials';
}
} catch (PDOException $e) {
$error = "Database error: " . $e->getMessage();
}
}
}
?>
<div class="container">
<div class="card">
<h2><?= __('login_title') ?></h2>
<form action="login.php" method="post">
<?php if ($error): ?>
<div class="alert error"><?php echo htmlspecialchars(__($error)); ?></div>
<?php endif; ?>
<div class="form-group">
<label for="email"><?= __('email_label') ?></label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="password"><?= __('password_label') ?></label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" class="btn"><?= __('login_button') ?></button>
</form>
<p class="switch-form"><?= __('no_account_prompt') ?> <a href="signup.php"><?= __('signup_link') ?></a></p>
</div>
</div>
<?php require_once 'shared/footer.php'; ?>

6
logout.php Normal file
View File

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

155
manage-task.php Normal file
View File

@ -0,0 +1,155 @@
<?php
session_start();
require_once 'db/config.php';
// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
$userId = $_SESSION['user_id'];
$taskId = $_GET['id'] ?? null;
$message = $_GET['message'] ?? '';
$message_type = $_GET['type'] ?? 'info';
if (!$taskId) {
header("Location: manage-tasks.php");
exit();
}
$pdo = db();
// Verify task ownership
try {
$stmt = $pdo->prepare('SELECT * FROM tasks WHERE id = :task_id AND user_id = :user_id');
$stmt->execute([':task_id' => $taskId, ':user_id' => $userId]);
$task = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$task) {
// If the user does not own this task, redirect them
header("Location: manage-tasks.php?message=access_denied");
exit();
}
} catch (PDOException $e) {
die("Database error: Could not verify task ownership.");
}
// Handle POST request to update application status
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['application_id']) && isset($_POST['action'])) {
$applicationId = $_POST['application_id'];
$action = $_POST['action'];
try {
$pdo->beginTransaction();
if ($action === 'accept') {
// 1. Set this application to 'accepted'
$stmt = $pdo->prepare('UPDATE applications SET status = 'accepted' WHERE id = :application_id AND task_id = :task_id');
$stmt->execute([':application_id' => $applicationId, ':task_id' => $taskId]);
// 2. Set the task status to 'assigned'
$stmt = $pdo->prepare('UPDATE tasks SET status = 'assigned' WHERE id = :task_id');
$stmt->execute([':task_id' => $taskId]);
// 3. Set all other pending applications for this task to 'rejected'
$stmt = $pdo->prepare('UPDATE applications SET status = 'rejected' WHERE task_id = :task_id AND id != :application_id AND status = 'pending'');
$stmt->execute([':task_id' => $taskId, ':application_id' => $applicationId]);
$message = 'Application accepted! The task is now assigned.';
$message_type = 'success';
} elseif ($action === 'reject') {
// Just reject this single application
$stmt = $pdo->prepare('UPDATE applications SET status = 'rejected' WHERE id = :application_id AND task_id = :task_id');
$stmt->execute([':application_id' => $applicationId, ':task_id' => $taskId]);
$message = 'Application rejected.';
$message_type = 'info';
}
$pdo->commit();
header("Location: manage-task.php?id=$taskId&message=" . urlencode($message) . "&type=" . $message_type);
exit();
} catch (PDOException $e) {
$pdo->rollBack();
// Log error instead of dying in production
die("Database error: Could not update application status. " . $e->getMessage());
}
}
// Fetch all applications for this task
try {
$stmt = $pdo->prepare(
'SELECT a.id, a.status, u.username, u.email
FROM applications a
JOIN users u ON a.user_id = u.id
WHERE a.task_id = :task_id
ORDER BY a.created_at DESC'
);
$stmt->execute([':task_id' => $taskId]);
$applications = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die("Database error: Could not retrieve applications.");
}
$pageTitle = "Manage Task: " . htmlspecialchars($task['title']);
include 'shared/header.php';
?>
<div class="container">
<div class="page-header">
<h1>Manage Task</h1>
<h2><?php echo htmlspecialchars($task['title']); ?></h2>
<p>Posted on: <?php echo date('F j, Y', strtotime($task['created_at'])); ?></p>
<p class="task-status-manage">Status: <span class="status-badge status-<?php echo strtolower(htmlspecialchars($task['status'])); ?>"><?php echo htmlspecialchars($task['status']); ?></span></p>
</div>
<?php if ($message): ?>
<div class="alert alert-<?php echo htmlspecialchars($message_type); ?>"><?php echo htmlspecialchars($message); ?></div>
<?php endif; ?>
<div class="applicants-section">
<h3>Applicants</h3>
<?php if (empty($applications)): ?>
<div class="alert alert-info">There are no applicants for this task yet.</div>
<?php else: ?>
<div class="applicants-list">
<?php foreach ($applications as $app): ?>
<div class="applicant-card">
<div class="applicant-info">
<strong><?php echo htmlspecialchars($app['username']); ?></strong>
(<?php echo htmlspecialchars($app['email']); ?>)
</div>
<div class="applicant-status">
Status: <span class="status-badge status-<?php echo strtolower(htmlspecialchars($app['status'])); ?>"><?php echo htmlspecialchars($app['status']); ?></span>
</div>
<?php if ($app['status'] === 'accepted'): ?>
<div class="applicant-actions">
<p class="text-success">This applicant was awarded the task.</p>
</div>
<?php elseif ($task['status'] === 'open' && $app['status'] === 'pending'): ?>
<div class="applicant-actions">
<form action="manage-task.php?id=<?php echo $taskId; ?>" method="POST" style="display: inline;">
<input type="hidden" name="application_id" value="<?php echo $app['id']; ?>">
<button type="submit" name="action" value="accept" class="btn btn-success">Accept</button>
</form>
<form action="manage-task.php?id=<?php echo $taskId; ?>" method="POST" style="display: inline;">
<input type="hidden" name="application_id" value="<?php echo $app['id']; ?>">
<button type="submit" name="action" value="reject" class="btn btn-danger">Reject</button>
</form>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div class="back-link">
<a href="manage-tasks.php">Back to All My Tasks</a>
</div>
</div>
<?php include 'shared/footer.php'; ?>

59
manage-tasks.php Normal file
View File

@ -0,0 +1,59 @@
<?php
session_start();
require_once 'db/config.php';
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
$userId = $_SESSION['user_id'];
try {
$pdo = db();
$stmt = $pdo->prepare(
'SELECT id, title, description, status, created_at
FROM tasks
WHERE user_id = :user_id
ORDER BY created_at DESC'
);
$stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
$stmt->execute();
$tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die("Database error: Could not retrieve your tasks.");
}
$pageTitle = __('manage_tasks_title');
include 'shared/header.php';
?>
<div class="container">
<div class="page-header">
<h1><?= __('manage_tasks_title') ?></h1>
<p><?= __('manage_tasks_description') ?></p>
</div>
<?php if (empty($tasks)): ?>
<div class="alert alert-info"><?= __('no_posted_tasks_link') ?></div>
<?php else: ?>
<div class="tasks-list">
<?php foreach ($tasks as $task): ?>
<div class="task-manage-card">
<div class="task-info">
<h2><a href="manage-task.php?id=<?php echo $task['id']; ?>"><?php echo htmlspecialchars($task['title']); ?></a></h2>
<p><?php echo htmlspecialchars(substr($task['description'], 0, 100)); ?>...</p>
</div>
<div class="task-status-manage">
<?= __('task_status_label') ?>: <span class="status-badge status-<?php echo strtolower(htmlspecialchars($task['status'])); ?>"><?php echo htmlspecialchars($task['status']); ?></span>
</div>
<div class="task-actions">
<a href="delete-task.php?id=<?php echo $task['id']; ?>" class="btn btn-danger btn-sm" onclick="return confirm('<?= __('delete_task_confirm') ?>');"><?= __('delete') ?></a>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php include 'shared/footer.php'; ?>

58
my-applications.php Normal file
View File

@ -0,0 +1,58 @@
<?php
session_start();
require_once 'db/config.php';
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
$userId = $_SESSION['user_id'];
try {
$pdo = db();
$stmt = $pdo->prepare(
'SELECT t.title, t.description, a.status, u.full_name AS poster_username
FROM applications a
JOIN tasks t ON a.task_id = t.id
JOIN users u ON t.user_id = u.id
WHERE a.user_id = :user_id
ORDER BY a.created_at DESC'
);
$stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
$stmt->execute();
$applications = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// In a real application, you would log this error
die("Database error: Could not retrieve your applications.");
}
$pageTitle = __('my_applications_title');
include 'shared/header.php';
?>
<div class="container">
<div class="page-header">
<h1><?= __('my_applications_title') ?></h1>
<p><?= __('my_applications_description') ?></p>
</div>
<?php if (empty($applications)): ?>
<div class="alert alert-info"><?= __('no_applications_yet') ?></div>
<?php else: ?>
<div class="applications-list">
<?php foreach ($applications as $app): ?>
<div class="application-card">
<h2><?php echo htmlspecialchars($app['title']); ?></h2>
<p class="task-poster"><?= __('posted_by_label') ?>: <strong><?php echo htmlspecialchars($app['poster_username']); ?></strong></p>
<p><?php echo nl2br(htmlspecialchars($app['description'])); ?></p>
<div class="application-status">
<?= __('task_status_label') ?>: <span class="status-badge status-<?php echo strtolower(htmlspecialchars($app['status'])); ?>"><?php echo htmlspecialchars($app['status']); ?></span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php include 'shared/footer.php'; ?>

65
post-task.php Normal file
View File

@ -0,0 +1,65 @@
<?php
require_once 'db/config.php';
require_once 'shared/header.php';
// Redirect if not logged in
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit();
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$title = $_POST['title'] ?? '';
$description = $_POST['description'] ?? '';
$category = $_POST['category'] ?? '';
$location = $_POST['location'] ?? '';
$payout = $_POST['payout'] ?? '';
$user_id = $_SESSION['user_id'];
if (empty($title) || empty($description) || empty($payout)) {
$error = 'error_task_required_fields';
} else {
try {
$stmt = db()->prepare("INSERT INTO tasks (user_id, title, description, category, location, payout) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $title, $description, $category, $location, $payout]);
header('Location: index.php');
exit();
} catch (PDOException $e) {
$error = "Database error: " . $e->getMessage();
}
}
}
?>
<div class="container">
<h1 class="text-center mt-5"><?= __('post_new_task_title') ?></h1>
<form action="post-task.php" method="POST" class="mt-4 p-4 border rounded bg-light">
<?php if ($error): ?>
<div class="alert alert-danger"><?= __($error) ?></div>
<?php endif; ?>
<div class="mb-3">
<label for="title" class="form-label"><?= __('task_title_label') ?></label>
<input type="text" class="form-control" id="title" name="title" required>
</div>
<div class="mb-3">
<label for="description" class="form-label"><?= __('description_label') ?></label>
<textarea class="form-control" id="description" name="description" rows="3" required></textarea>
</div>
<div class="mb-3">
<label for="category" class="form-label"><?= __('category_label') ?></label>
<input type="text" class="form-control" id="category" name="category">
</div>
<div class="mb-3">
<label for="location" class="form-label"><?= __('location_label') ?></label>
<input type="text" class="form-control" id="location" name="location">
</div>
<div class="mb-3">
<label for="payout" class="form-label"><?= __('payout_label') ?></label>
<input type="number" class="form-control" id="payout" name="payout" step="0.01" required>
</div>
<button type="submit" class="btn btn-primary"><?= __('post_task_button') ?></button>
</form>
</div>
<?php require_once 'shared/footer.php'; ?>

139
profile.php Normal file
View File

@ -0,0 +1,139 @@
<?php
session_start();
require_once 'db/config.php';
$profileId = $_GET['id'] ?? null;
if (!$profileId) {
header("Location: index.php");
exit();
}
$pdo = db();
// Handle WhatsApp number update
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SESSION['user_id']) && $_SESSION['user_id'] == $profileId) {
$whatsapp_number = $_POST['whatsapp_number'] ?? '';
try {
$stmt = $pdo->prepare('UPDATE users SET whatsapp_number = :whatsapp_number WHERE id = :id');
$stmt->execute([':whatsapp_number' => $whatsapp_number, ':id' => $profileId]);
header("Location: profile.php?id=$profileId&message=whatsapp_updated");
exit();
} catch (PDOException $e) {
$update_error = "whatsapp_update_failed";
}
}
// Fetch user information
try {
$stmt = $pdo->prepare('SELECT id, full_name, email, created_at, whatsapp_number FROM users WHERE id = :id');
$stmt->execute([':id' => $profileId]);
$profileUser = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die("Database error: Could not retrieve user profile.");
}
if (!$profileUser) {
// Handle user not found
header("Location: index.php?message=user_not_found");
exit();
}
// Fetch tasks posted by the user
try {
$stmt = $pdo->prepare('SELECT * FROM tasks WHERE user_id = :user_id ORDER BY created_at DESC');
$stmt->execute([':user_id' => $profileId]);
$postedTasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die("Database error: Could not retrieve posted tasks.");
}
// Fetch tasks the user has completed (i.e., their application was accepted)
try {
$stmt = $pdo->prepare(
'SELECT t.* FROM tasks t JOIN applications a ON t.id = a.task_id WHERE a.user_id = :user_id AND a.status = \'accepted\' ORDER BY t.created_at DESC'
);
$stmt->execute([':user_id' => $profileId]);
$completedTasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die("Database error: Could not retrieve completed tasks.");
}
$pageTitle = sprintf(__('profile_title'), htmlspecialchars($profileUser['full_name']));
include 'shared/header.php';
?>
<div class="container profile-page">
<div class="page-header">
<h1><?php echo htmlspecialchars($profileUser['full_name']); ?></h1>
<p><strong><?= sprintf(__('member_since'), date('F j, Y', strtotime($profileUser['created_at']))) ?></strong></p>
</div>
<?php if (isset($_GET['message']) && $_GET['message'] === 'whatsapp_updated'): ?>
<div class="alert alert-success"><?= __('whatsapp_updated_success') ?></div>
<?php endif; ?>
<?php if (isset($update_error)): ?>
<div class="alert alert-danger"><?= htmlspecialchars(__($update_error)) ?></div>
<?php endif; ?>
<?php if (isset($_SESSION['user_id']) && $_SESSION['user_id'] == $profileUser['id']): ?>
<div class="card mb-4" style="margin-bottom: 2rem; background-color: #fff; border: 1px solid #dee2e6; border-radius: 0.75rem; padding: 1.5rem; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
<div class="card-body">
<h2 class="card-title" style="font-size: 1.75rem; margin-bottom: 1.5rem; border-bottom: 2px solid #dee2e6; padding-bottom: 0.5rem; color: #0d6efd;"><?= __('my_contact_info') ?></h2>
<form action="profile.php?id=<?= $profileUser['id'] ?>" method="POST">
<div class="form-group">
<label for="whatsapp_number"><?= __('whatsapp_number_label') ?></label>
<input type="text" class="form-control" id="whatsapp_number" name="whatsapp_number" value="<?= htmlspecialchars($profileUser['whatsapp_number'] ?? '') ?>" placeholder="<?= __('whatsapp_placeholder') ?>">
<small class="form-text text-muted"><?= __('whatsapp_help_text') ?></small>
</div>
<button type="submit" class="btn btn-primary" style="margin-top: 1rem;"><?= __('save_button') ?></button>
</form>
</div>
</div>
<?php endif; ?>
<div class="profile-section">
<h2><?= __('posted_tasks') ?></h2>
<?php if (empty($postedTasks)): ?>
<p><?= sprintf(__('no_posted_tasks'), htmlspecialchars($profileUser['full_name'])) ?></p>
<?php else: ?>
<div class="task-list">
<?php foreach ($postedTasks as $task):
// Check if task status is not null before using it in strtolower
$taskStatus = $task['status'] ?? 'unknown';
?>
<div class="task-card-profile">
<h4><a href="task-details.php?id=<?php echo $task['id']; ?>"><?php echo htmlspecialchars($task['title']); ?></a></h4>
<p><strong><?= __('task_status') ?>:</strong> <span class="status-badge status-<?php echo strtolower(htmlspecialchars($taskStatus)); ?>"><?php echo htmlspecialchars($taskStatus); ?></span></p>
<p><strong><?= __('task_payout') ?>:</strong> $<?php echo htmlspecialchars($task['payout']); ?></p>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div class="profile-section">
<h2><?= __('completed_tasks') ?></h2>
<?php if (empty($completedTasks)): ?>
<p><?= sprintf(__('no_completed_tasks'), htmlspecialchars($profileUser['full_name'])) ?></p>
<?php else: ?>
<div class="task-list">
<?php foreach ($completedTasks as $task):
// Check if task status is not null before using it in strtolower
$taskStatus = $task['status'] ?? 'unknown';
?>
<div class="task-card-profile">
<h4><?php echo htmlspecialchars($task['title']); ?></h4>
<p><strong><?= __('task_status') ?>:</strong> <span class="status-badge status-<?php echo strtolower(htmlspecialchars($taskStatus)); ?>"><?php echo htmlspecialchars($taskStatus); ?></span></p>
<p><strong><?= __('task_payout') ?>:</strong> $<?php echo htmlspecialchars($task['payout']); ?></p>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php include 'shared/footer.php'; ?>

6
shared/footer.php Normal file
View File

@ -0,0 +1,6 @@
</main>
<footer class="footer">
<p>&copy; <?= date('Y') ?> CashTask. All rights reserved.</p>
</footer>
</body>
</html>

60
shared/header.php Normal file
View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
ini_set('display_errors', '1');
error_reporting(E_ALL);
require_once __DIR__ . '/i18n.php';
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<!doctype html>
<html lang="<?= get_lang() ?>" dir="<?= get_dir() ?>">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><?= htmlspecialchars($pageTitle ?? __('app_title')) ?></title>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css?v=<?= time() ?>">
</head>
<body>
<header class="header">
<a href="/" class="logo"><?= __('app_title') ?></a>
<div class="nav-buttons">
<div class="lang-switcher">
<a href="?lang=en" class="<?= get_lang() === 'en' ? 'active' : '' ?>"><?= __('english') ?></a>
|
<a href="?lang=ar" class="<?= get_lang() === 'ar' ? 'active' : '' ?>"><?= __('arabic') ?></a>
</div>
<?php if (isset($_SESSION['user_id'])): ?>
<a href="/profile.php?id=<?php echo $_SESSION['user_id']; ?>" class="btn btn-secondary"><?= __('my_profile') ?></a>
<a href="/my-applications.php" class="btn btn-secondary"><?= __('my_applications') ?></a>
<a href="/manage-tasks.php" class="btn btn-secondary"><?= __('manage_tasks') ?></a>
<a href="/post-task.php" class="btn btn-primary"><?= __('post_task') ?></a>
<a href="/logout.php" class="btn btn-secondary"><?= __('logout') ?></a>
<?php else: ?>
<a href="/login.php" class="btn btn-secondary"><?= __('login') ?></a>
<a href="/signup.php" class="btn btn-primary"><?= __('signup') ?></a>
<?php endif; ?>
</div>
</header>
<main class="container">

41
shared/i18n.php Normal file
View File

@ -0,0 +1,41 @@
<?php
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Determine language
$supported_langs = ['en', 'ar'];
$default_lang = 'en';
$lang = $default_lang;
if (isset($_GET['lang']) && in_array($_GET['lang'], $supported_langs)) {
$_SESSION['lang'] = $_GET['lang'];
$lang = $_GET['lang'];
} elseif (isset($_SESSION['lang'])) {
$lang = $_SESSION['lang'];
}
// Load translations
$translations = [];
$lang_file = __DIR__ . '/../lang/' . $lang . '.php';
if (file_exists($lang_file)) {
require $lang_file;
}
// Translation function
function __($key) {
global $translations;
return $translations[$key] ?? $key;
}
// Get current language
function get_lang() {
global $lang;
return $lang;
}
// Get page direction
function get_dir() {
return (get_lang() === 'ar') ? 'rtl' : 'ltr';
}

107
signup.php Normal file
View File

@ -0,0 +1,107 @@
<?php
require_once __DIR__ . '/shared/header.php';
require_once __DIR__ . '/db/config.php';
$errors = [];
$full_name = '';
$email = '';
$phone = '';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$full_name = trim($_POST['full_name']);
$email = trim($_POST['email']);
$phone = trim($_POST['phone']);
$password = $_POST['password'];
$confirm_password = $_POST['confirm_password'];
if (empty($full_name)) {
$errors[] = 'error_full_name_required';
}
if (empty($email)) {
$errors[] = 'error_email_required';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'error_invalid_email';
}
if (empty($phone)) {
$errors[] = 'error_phone_required';
}
if (empty($password)) {
$errors[] = 'error_password_required';
}
if ($password !== $confirm_password) {
$errors[] = 'error_passwords_no_match';
}
if (empty($errors)) {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$existing_user = $stmt->fetch();
if ($existing_user) {
$errors[] = 'error_email_exists';
} else {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (full_name, email, phone, password) VALUES (?, ?, ?, ?)");
$stmt->execute([$full_name, $email, $phone, $hashed_password]);
header("Location: login.php?registration=success");
exit;
}
} catch (PDOException $e) {
$errors[] = "error_database";
}
}
}
$pageTitle = __('signup_title');
?>
<div class="form-container">
<h1><?= __('signup_title') ?></h1>
<?php if (!empty($errors)): ?>
<div class="errors">
<?php foreach ($errors as $error): ?>
<p><?php echo htmlspecialchars(__($error)); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form action="signup.php" method="post">
<div class="form-group">
<label for="full_name"><?= __('full_name_label') ?></label>
<input type="text" id="full_name" name="full_name" required value="<?php echo htmlspecialchars($full_name); ?>">
</div>
<div class="form-group">
<label for="email"><?= __('email_address_label') ?></label>
<input type="email" id="email" name="email" required value="<?php echo htmlspecialchars($email); ?>">
</div>
<div class="form-group">
<label for="phone"><?= __('phone_number_label') ?></label>
<input type="tel" id="phone" name="phone" required value="<?php echo htmlspecialchars($phone); ?>">
</div>
<div class="form-group">
<label for="password"><?= __('password_label') ?></label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label for="confirm_password"><?= __('confirm_password_label') ?></label>
<input type="password" id="confirm_password" name="confirm_password" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" style="width: 100%;"><?= __('signup_button') ?></button>
</div>
</form>
</div>
<?php
require_once __DIR__ . '/shared/footer.php';
?>

118
task-details.php Normal file
View File

@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
session_start();
require_once 'db/config.php';
$pdo = db();
$task_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if (!$task_id) {
header("Location: index.php");
exit;
}
// Fetch task details along with owner's full name and whatsapp number
$stmt = $pdo->prepare("SELECT t.*, u.full_name, u.whatsapp_number FROM tasks t JOIN users u ON t.user_id = u.id WHERE t.id = ?");
$stmt->execute([$task_id]);
$task = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$task) {
header("Location: index.php");
exit;
}
$is_owner = isset($_SESSION['user_id']) && $_SESSION['user_id'] == $task['user_id'];
$applications = [];
if ($is_owner) {
$stmt = $pdo->prepare("SELECT a.*, u.full_name FROM applications a JOIN users u ON a.user_id = u.id WHERE a.task_id = ? ORDER BY a.created_at DESC");
$stmt->execute([$task_id]);
$applications = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
$user_application = null;
if (isset($_SESSION['user_id']) && !$is_owner) {
$stmt = $pdo->prepare("SELECT * FROM applications WHERE task_id = ? AND user_id = ?");
$stmt->execute([$task_id, $_SESSION['user_id']]);
$user_application = $stmt->fetch(PDO::FETCH_ASSOC);
}
$pageTitle = __('task_details_title');
require_once 'shared/header.php';
?>
<div class="container mt-5">
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h1 class="card-title h3"><?php echo htmlspecialchars($task['title']); ?></h1>
</div>
<div class="card-body">
<p><strong><?= __('posted_by_label') ?>:</strong> <a href="profile.php?id=<?php echo $task['user_id']; ?>"><?php echo htmlspecialchars($task['full_name']); ?></a></p>
<p><strong><?= __('budget_label') ?>:</strong> $<?php echo htmlspecialchars(number_format((float)$task['budget'], 2)); ?></p>
<hr>
<p class="card-text"><?php echo nl2br(htmlspecialchars($task['description'])); ?></p>
</div>
<div class="card-footer text-muted">
<?= __('posted_on_label') ?>: <?php echo date('F j, Y, g:i a', strtotime($task['created_at'])); ?>
</div>
</div>
<?php if ($is_owner): ?>
<div class="card">
<div class="card-header">
<h3><?= __('applicants_title') ?></h3>
</div>
<div class="card-body">
<?php if (empty($task['whatsapp_number'])): ?>
<div class="alert alert-warning"><?= __('add_whatsapp_prompt') ?></div>
<?php endif; ?>
<?php if (empty($applications)): ?>
<p><?= __('no_applicants_yet') ?></p>
<?php else: ?>
<ul class="list-group">
<?php foreach ($applications as $application): ?>
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
<a href="/profile.php?id=<?= $application['user_id'] ?>"><?= htmlspecialchars($application['full_name']) ?></a>
<span class="badge status-<?= htmlspecialchars($application['status']) ?>"><?= htmlspecialchars($application['status']) ?></span>
</div>
<?php if ($application['status'] === 'pending' && !empty($task['whatsapp_number'])): ?>
<a href="accept-application.php?id=<?= $application['id'] ?>" class="btn btn-success btn-sm"><?= __('accept_button') ?></a>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
</div>
<?php elseif (isset($_SESSION['user_id'])): ?>
<div class="card">
<div class="card-header">
<h3><?= __('my_application_title') ?></h3>
</div>
<div class="card-body">
<?php if ($user_application): ?>
<?php if ($user_application['status'] === 'accepted'): ?>
<div class="alert alert-success">
<h4><?= __('application_accepted_message') ?></h4>
<p><?= sprintf(__('contact_owner_whatsapp'), htmlspecialchars($task['whatsapp_number'])) ?></p>
</div>
<?php else: ?>
<p><?= __('application_status_label') ?>: <span class="badge status-<?= htmlspecialchars($user_application['status']) ?>"><?= htmlspecialchars($user_application['status']) ?></span></p>
<?php endif; ?>
<?php else: ?>
<form action="apply.php" method="POST">
<input type="hidden" name="task_id" value="<?= $task['id'] ?>">
<button type="submit" class="btn btn-primary"><?= __('apply_now_button') ?></button>
</form>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
<?php
require_once 'shared/footer.php';
?>