💼 Freelance Marketplace IN HTML CSS AND JAVASCRIPT WITH PHP AND MY SQL

Project Introduction

A comprehensive freelance marketplace platform that connects clients with freelancers. This system allows clients to post projects, freelancers to bid on them, and provides tools for project management, messaging, and payments. Perfect for building your own freelancing platform similar to Upwork or Fiverr but with core essential features.


✨ Features

For Clients

  • Post Projects: Create detailed project listings with budgets and deadlines
  • Browse Freelancers: Search and filter freelancers by skills, rating, and hourly rate
  • Hire Freelancers: Direct hiring or review proposals
  • Project Management: Track project progress and milestones
  • Escrow Payments: Secure payment system with milestone releases
  • Reviews & Ratings: Rate freelancers after project completion

For Freelancers

  • Professional Profile: Showcase skills, portfolio, experience, and certifications
  • Find Work: Browse and search for relevant projects
  • Submit Proposals: Bid on projects with custom proposals and pricing
  • Time Tracking: Optional hourly work logging
  • Earnings Dashboard: Track income and payment history
  • Portfolio Showcase: Display previous work samples

Core Marketplace Features

  • Search & Discovery: Advanced filtering by category, budget, skills, location
  • Messaging System: Real-time chat between clients and freelancers
  • Dispute Resolution: Mediation system for conflicts
  • Skill Verification: Skill tests and badges
  • Saved Searches: Alert for new matching projects
  • Favorites: Save projects and freelancers for later

Payment & Escrow

  • Milestone Payments: Release payments upon completion
  • Hourly Protection: Weekly billing with screenshots (optional)
  • Multiple Currencies: Support for different currencies
  • Payment Gateways: Stripe, PayPal, Razorpay integration
  • Invoice Generation: Automatic invoice creation

Admin Features

  • User Management: Approve, suspend, or ban users
  • Commission Settings: Set platform fees
  • Dispute Management: Mediate conflicts
  • Analytics Dashboard: Monitor platform growth
  • Content Moderation: Review projects and proposals
  • Payout Processing: Handle freelancer withdrawals

📁 File Structure

blog-website/
│
├── marketplace/                      # Main marketplace directory
│   ├── index.php                      # Marketplace homepage
│   ├── projects.php                     # Browse projects
│   ├── project.php                         # Single project view
│   ├── post-project.php                      # Post a new project
│   ├── freelancers.php                          # Browse freelancers
│   ├── freelancer.php                              # Freelancer profile
│   ├── dashboard/                                      # User dashboard
│   │   ├── client.php                                      # Client dashboard
│   │   ├── freelancer.php                                    # Freelancer dashboard
│   │   ├── proposals.php                                       # Manage proposals
│   │   ├── contracts.php                                         # Active contracts
│   │   ├── earnings.php                                            # Earnings & payouts
│   │   └── settings.php                                              # Profile settings
│   │
│   ├── messages/                                      # Messaging system
│   │   ├── inbox.php                                      # Message inbox
│   │   ├── conversation.php                                 # Chat interface
│   │   └── ajax.php                                           # Real-time messaging
│   │
│   ├── payments/                                      # Payment processing
│   │   ├── escrow.php                                      # Escrow system
│   │   ├── release.php                                       # Release payment
│   │   ├── withdraw.php                                        # Withdraw funds
│   │   └── webhook.php                                          # Payment webhooks
│   │
│   ├── admin/                                         # Admin panel
│   │   ├── dashboard.php                                  # Admin dashboard
│   │   ├── users.php                                         # User management
│   │   ├── projects.php                                         # Project moderation
│   │   ├── disputes.php                                            # Dispute management
│   │   ├── payments.php                                              # Payment oversight
│   │   └── settings.php                                                # Platform settings
│   │
│   ├── includes/                                      # Core includes
│   │   ├── marketplace-config.php                         # Configuration
│   │   ├── marketplace-functions.php                        # Core functions
│   │   ├── auth.php                                             # Authentication
│   │   ├── messaging.php                                           # Messaging functions
│   │   ├── payments.php                                               # Payment processing
│   │   └── notifications.php                                             # Notification system
│   │
│   ├── css/                                           # Stylesheets
│   │   ├── marketplace.css                                # Main marketplace styles
│   │   └── messages.css                                      # Messaging styles
│   │
│   ├── js/                                            # JavaScript
│   │   ├── marketplace.js                                 # Core marketplace JS
│   │   ├── messaging.js                                      # Real-time chat
│   │   ├── proposal.js                                          # Proposal submission
│   │   └── dashboard.js                                           # Dashboard interactions
│   │
│   └── uploads/                                        # User uploads
│       ├── portfolios/                                     # Freelancer portfolio
│       ├── attachments/                                        # Project attachments
│       └── avatars/                                                # Profile pictures
│
├── .env                                                    # Environment variables
├── composer.json                                              # Composer dependencies
│
└── database/
└── marketplace.sql                                           # Database schema

🗄️ Database Schema (database/marketplace.sql)

-- Create marketplace database
CREATE DATABASE IF NOT EXISTS marketplace_db;
USE marketplace_db;
-- Users table (extends blog users)
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
user_type ENUM('client', 'freelancer', 'both', 'admin') DEFAULT 'client',
full_name VARCHAR(255) NOT NULL,
username VARCHAR(100) UNIQUE NOT NULL,
profile_image VARCHAR(500),
bio TEXT,
location VARCHAR(255),
timezone VARCHAR(100),
languages JSON,
skills JSON,
hourly_rate DECIMAL(10,2),
experience_years INT,
education JSON,
certifications JSON,
linkedin_url VARCHAR(500),
github_url VARCHAR(500),
portfolio_url VARCHAR(500),
is_verified BOOLEAN DEFAULT FALSE,
verification_badge BOOLEAN DEFAULT FALSE,
account_status ENUM('active', 'suspended', 'banned') DEFAULT 'active',
email_verified BOOLEAN DEFAULT FALSE,
email_verified_at TIMESTAMP NULL,
last_login TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_type (user_type),
INDEX idx_status (account_status),
INDEX idx_location (location),
FULLTEXT INDEX idx_search (full_name, bio, skills)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- User balances (for escrow and earnings)
CREATE TABLE IF NOT EXISTS user_balances (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL UNIQUE,
available_balance DECIMAL(10,2) DEFAULT 0.00,
pending_balance DECIMAL(10,2) DEFAULT 0.00,
total_earned DECIMAL(10,2) DEFAULT 0.00,
total_withdrawn DECIMAL(10,2) DEFAULT 0.00,
currency VARCHAR(3) DEFAULT 'USD',
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Categories
CREATE TABLE IF NOT EXISTS categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
slug VARCHAR(255) UNIQUE NOT NULL,
description TEXT,
icon VARCHAR(100),
parent_id INT NULL,
sort_order INT DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_parent (parent_id),
INDEX idx_active (is_active),
FOREIGN KEY (parent_id) REFERENCES categories(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Skills
CREATE TABLE IF NOT EXISTS skills (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
slug VARCHAR(255) UNIQUE NOT NULL,
category_id INT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_category (category_id),
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Projects
CREATE TABLE IF NOT EXISTS projects (
id INT AUTO_INCREMENT PRIMARY KEY,
client_id INT NOT NULL,
title VARCHAR(500) NOT NULL,
slug VARCHAR(500) UNIQUE NOT NULL,
description TEXT NOT NULL,
category_id INT NOT NULL,
skills_required JSON,
experience_level ENUM('entry', 'intermediate', 'expert') DEFAULT 'intermediate',
project_type ENUM('fixed', 'hourly') DEFAULT 'fixed',
budget_min DECIMAL(10,2),
budget_max DECIMAL(10,2),
fixed_price DECIMAL(10,2),
hourly_rate_min DECIMAL(10,2),
hourly_rate_max DECIMAL(10,2),
estimated_duration INT, -- in days
attachments JSON,
location_preference ENUM('anywhere', 'same_country', 'specific') DEFAULT 'anywhere',
specific_location VARCHAR(255),
visibility ENUM('public', 'private', 'invite_only') DEFAULT 'public',
status ENUM('draft', 'open', 'in_progress', 'completed', 'cancelled', 'expired') DEFAULT 'open',
featured BOOLEAN DEFAULT FALSE,
urgent BOOLEAN DEFAULT FALSE,
nd_required BOOLEAN DEFAULT FALSE,
views INT DEFAULT 0,
proposals_count INT DEFAULT 0,
hired_freelancer_id INT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NULL,
started_at TIMESTAMP NULL,
completed_at TIMESTAMP NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_client (client_id),
INDEX idx_category (category_id),
INDEX idx_status (status),
INDEX idx_created (created_at),
INDEX idx_featured (featured),
FULLTEXT INDEX idx_search (title, description),
FOREIGN KEY (client_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES categories(id),
FOREIGN KEY (hired_freelancer_id) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Proposals (bids)
CREATE TABLE IF NOT EXISTS proposals (
id INT AUTO_INCREMENT PRIMARY KEY,
project_id INT NOT NULL,
freelancer_id INT NOT NULL,
cover_letter TEXT NOT NULL,
bid_amount DECIMAL(10,2) NOT NULL,
estimated_days INT NOT NULL,
attachments JSON,
status ENUM('pending', 'accepted', 'rejected', 'withdrawn', 'shortlisted') DEFAULT 'pending',
client_ viewed BOOLEAN DEFAULT FALSE,
client_viewed_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_project (project_id),
INDEX idx_freelancer (freelancer_id),
INDEX idx_status (status),
UNIQUE KEY unique_proposal (project_id, freelancer_id),
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY (freelancer_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Contracts (hired projects)
CREATE TABLE IF NOT EXISTS contracts (
id INT AUTO_INCREMENT PRIMARY KEY,
project_id INT NOT NULL UNIQUE,
client_id INT NOT NULL,
freelancer_id INT NOT NULL,
proposal_id INT NULL,
title VARCHAR(500) NOT NULL,
description TEXT,
contract_type ENUM('fixed', 'hourly') NOT NULL,
budget_amount DECIMAL(10,2) NOT NULL,
hourly_rate DECIMAL(10,2) NULL,
estimated_hours INT NULL,
start_date TIMESTAMP NOT NULL,
end_date TIMESTAMP NULL,
status ENUM('active', 'paused', 'completed', 'cancelled', 'disputed') DEFAULT 'active',
completion_percentage INT DEFAULT 0,
client_rated BOOLEAN DEFAULT FALSE,
freelancer_rated BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_client (client_id),
INDEX idx_freelancer (freelancer_id),
INDEX idx_status (status),
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY (client_id) REFERENCES users(id),
FOREIGN KEY (freelancer_id) REFERENCES users(id),
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Milestones (for fixed-price projects)
CREATE TABLE IF NOT EXISTS milestones (
id INT AUTO_INCREMENT PRIMARY KEY,
contract_id INT NOT NULL,
title VARCHAR(500) NOT NULL,
description TEXT,
amount DECIMAL(10,2) NOT NULL,
due_date DATE,
status ENUM('pending', 'in_progress', 'submitted', 'approved', 'rejected', 'paid') DEFAULT 'pending',
submission_notes TEXT,
submission_files JSON,
approved_at TIMESTAMP NULL,
paid_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_contract (contract_id),
INDEX idx_status (status),
FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Timesheets (for hourly projects)
CREATE TABLE IF NOT EXISTS timesheets (
id INT AUTO_INCREMENT PRIMARY KEY,
contract_id INT NOT NULL,
freelancer_id INT NOT NULL,
date DATE NOT NULL,
hours DECIMAL(5,2) NOT NULL,
description TEXT,
status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
screenshots JSON,
approved_by_client TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_contract (contract_id),
INDEX idx_freelancer (freelancer_id),
INDEX idx_date (date),
FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE,
FOREIGN KEY (freelancer_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Escrow transactions
CREATE TABLE IF NOT EXISTS escrow_transactions (
id INT AUTO_INCREMENT PRIMARY KEY,
contract_id INT NOT NULL,
milestone_id INT NULL,
amount DECIMAL(10,2) NOT NULL,
client_id INT NOT NULL,
freelancer_id INT NOT NULL,
type ENUM('deposit', 'release', 'refund', 'fee') NOT NULL,
status ENUM('pending', 'completed', 'failed', 'cancelled') DEFAULT 'pending',
payment_method VARCHAR(50),
transaction_id VARCHAR(255),
gateway_response JSON,
platform_fee DECIMAL(10,2) DEFAULT 0.00,
freelancer_amount DECIMAL(10,2) NOT NULL,
released_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_contract (contract_id),
INDEX idx_client (client_id),
INDEX idx_freelancer (freelancer_id),
INDEX idx_status (status),
FOREIGN KEY (contract_id) REFERENCES contracts(id),
FOREIGN KEY (milestone_id) REFERENCES milestones(id) ON DELETE SET NULL,
FOREIGN KEY (client_id) REFERENCES users(id),
FOREIGN KEY (freelancer_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Withdrawals
CREATE TABLE IF NOT EXISTS withdrawals (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
fee DECIMAL(10,2) DEFAULT 0.00,
net_amount DECIMAL(10,2) NOT NULL,
payment_method ENUM('paypal', 'bank', 'stripe', 'payoneer') NOT NULL,
payment_details JSON,
status ENUM('pending', 'processing', 'completed', 'failed', 'cancelled') DEFAULT 'pending',
processed_at TIMESTAMP NULL,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user (user_id),
INDEX idx_status (status),
FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Reviews
CREATE TABLE IF NOT EXISTS reviews (
id INT AUTO_INCREMENT PRIMARY KEY,
contract_id INT NOT NULL UNIQUE,
reviewer_id INT NOT NULL,
reviewee_id INT NOT NULL,
rating TINYINT NOT NULL CHECK (rating >= 1 AND rating <= 5),
review TEXT NOT NULL,
criteria JSON, -- For detailed ratings (communication, quality, etc.)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_reviewer (reviewer_id),
INDEX idx_reviewee (reviewee_id),
INDEX idx_contract (contract_id),
FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE,
FOREIGN KEY (reviewer_id) REFERENCES users(id),
FOREIGN KEY (reviewee_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Messages
CREATE TABLE IF NOT EXISTS messages (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
sender_id INT NOT NULL,
receiver_id INT NOT NULL,
contract_id INT NULL,
message TEXT NOT NULL,
attachments JSON,
is_read BOOLEAN DEFAULT FALSE,
read_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_sender (sender_id),
INDEX idx_receiver (receiver_id),
INDEX idx_contract (contract_id),
INDEX idx_created (created_at),
FOREIGN KEY (sender_id) REFERENCES users(id),
FOREIGN KEY (receiver_id) REFERENCES users(id),
FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Notifications
CREATE TABLE IF NOT EXISTS notifications (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
type VARCHAR(50) NOT NULL,
title VARCHAR(500) NOT NULL,
message TEXT,
data JSON,
is_read BOOLEAN DEFAULT FALSE,
read_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user (user_id),
INDEX idx_read (is_read),
INDEX idx_created (created_at),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Saved items (favorites)
CREATE TABLE IF NOT EXISTS saved_items (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
item_type ENUM('project', 'freelancer') NOT NULL,
item_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_saved (user_id, item_type, item_id),
INDEX idx_user (user_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Disputes
CREATE TABLE IF NOT EXISTS disputes (
id INT AUTO_INCREMENT PRIMARY KEY,
contract_id INT NOT NULL UNIQUE,
raised_by INT NOT NULL,
reason VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
evidence JSON,
status ENUM('open', 'under_review', 'resolved', 'closed') DEFAULT 'open',
resolution TEXT,
resolved_by INT NULL,
resolved_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_contract (contract_id),
INDEX idx_status (status),
FOREIGN KEY (contract_id) REFERENCES contracts(id),
FOREIGN KEY (raised_by) REFERENCES users(id),
FOREIGN KEY (resolved_by) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Platform settings
CREATE TABLE IF NOT EXISTS platform_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
setting_key VARCHAR(255) UNIQUE NOT NULL,
setting_value TEXT,
setting_type ENUM('text', 'number', 'boolean', 'json') DEFAULT 'text',
description TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Insert default settings
INSERT INTO platform_settings (setting_key, setting_value, setting_type, description) VALUES
('platform_name', 'My Freelance Marketplace', 'text', 'Platform name'),
('platform_fee_percentage', '10', 'number', 'Platform fee percentage'),
('platform_fee_fixed', '0.50', 'number', 'Fixed platform fee per transaction'),
('min_withdrawal_amount', '10', 'number', 'Minimum withdrawal amount'),
('max_withdrawal_amount', '5000', 'number', 'Maximum withdrawal amount per transaction'),
('project_approval_required', 'false', 'boolean', 'Require admin approval for projects'),
('freelancer_verification_required', 'false', 'boolean', 'Require identity verification for freelancers'),
('enable_escrow', 'true', 'boolean', 'Enable escrow payment system'),
('enable_hourly_projects', 'true', 'boolean', 'Allow hourly projects'),
('enable_milestones', 'true', 'boolean', 'Enable milestone-based payments');

🔧 Core Functions

1. includes/marketplace-config.php

<?php
/**
* Marketplace Configuration
*/
require_once __DIR__ . '/../../vendor/autoload.php';
use Dotenv\Dotenv;
// Load environment variables
$dotenv = Dotenv::createImmutable(__DIR__ . '/../../');
$dotenv->load();
// Database configuration
define('DB_HOST', $_ENV['DB_HOST']);
define('DB_USER', $_ENV['DB_USER']);
define('DB_PASS', $_ENV['DB_PASS']);
define('DB_NAME', 'marketplace_db');
// Payment Gateway Keys
define('STRIPE_KEY', $_ENV['STRIPE_KEY'] ?? '');
define('STRIPE_SECRET', $_ENV['STRIPE_SECRET'] ?? '');
define('PAYPAL_CLIENT_ID', $_ENV['PAYPAL_CLIENT_ID'] ?? '');
define('PAYPAL_SECRET', $_ENV['PAYPAL_SECRET'] ?? '');
// Platform Settings
define('PLATFORM_FEE_PERCENTAGE', 10); // 10%
define('PLATFORM_FEE_FIXED', 0.50); // $0.50
define('MIN_WITHDRAWAL', 10);
define('MAX_WITHDRAWAL', 5000);
define('CURRENCY', 'USD');
// Upload Settings
define('MAX_PORTFOLIO_IMAGES', 20);
define('MAX_PROJECT_ATTACHMENTS', 5);
define('MAX_FILE_SIZE', 10 * 1024 * 1024); // 10MB
// Start session
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Database connection (PDO)
function getDB() {
static $pdo = null;
if ($pdo === null) {
try {
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4";
$pdo = new PDO($dsn, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]);
} catch (PDOException $e) {
error_log("Database connection failed: " . $e->getMessage());
die("Database connection failed. Please try again later.");
}
}
return $pdo;
}
// Get current user
function getCurrentUser() {
if (isset($_SESSION['user_id'])) {
$pdo = getDB();
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
return $stmt->fetch();
}
return null;
}
// Check if user is logged in
function isLoggedIn() {
return isset($_SESSION['user_id']);
}
// Require login
function requireLogin() {
if (!isLoggedIn()) {
header('Location: /blog-website/login.php?redirect=' . urlencode($_SERVER['REQUEST_URI']));
exit;
}
}
// Require specific user type
function requireUserType($type) {
$user = getCurrentUser();
if (!$user || ($user['user_type'] !== $type && $user['user_type'] !== 'admin')) {
header('Location: /blog-website/marketplace/index.php');
exit;
}
}

2. marketplace/index.php (Homepage)

<?php
require_once '../includes/config.php';
require_once '../includes/marketplace-config.php';
$page_title = 'Freelance Marketplace - Find Expert Freelancers';
// Get statistics
$pdo = getDB();
$stats = [
'projects' => $pdo->query("SELECT COUNT(*) FROM projects WHERE status = 'open'")->fetchColumn(),
'freelancers' => $pdo->query("SELECT COUNT(*) FROM users WHERE user_type IN ('freelancer', 'both')")->fetchColumn(),
'completed' => $pdo->query("SELECT COUNT(*) FROM contracts WHERE status = 'completed'")->fetchColumn()
];
// Get featured projects
$featured = $pdo->query("
SELECT p.*, u.full_name as client_name, u.username, c.name as category_name
FROM projects p
JOIN users u ON p.client_id = u.id
JOIN categories c ON p.category_id = c.id
WHERE p.status = 'open' AND p.featured = 1
ORDER BY p.created_at DESC
LIMIT 6
")->fetchAll();
// Get top freelancers
$topFreelancers = $pdo->query("
SELECT u.*, 
(SELECT AVG(rating) FROM reviews WHERE reviewee_id = u.id) as avg_rating,
(SELECT COUNT(*) FROM contracts WHERE freelancer_id = u.id AND status = 'completed') as completed_projects
FROM users u
WHERE u.user_type IN ('freelancer', 'both') AND u.account_status = 'active'
HAVING avg_rating >= 4.5 OR completed_projects > 0
ORDER BY avg_rating DESC, completed_projects DESC
LIMIT 8
")->fetchAll();
// Get categories
$categories = $pdo->query("
SELECT c.*, 
(SELECT COUNT(*) FROM projects WHERE category_id = c.id AND status = 'open') as project_count
FROM categories c
WHERE c.parent_id IS NULL AND c.is_active = 1
ORDER BY c.sort_order
LIMIT 12
")->fetchAll();
include '../includes/header.php';
?>
<link rel="stylesheet" href="css/marketplace.css">
<div class="marketplace-container">
<!-- Hero Section -->
<section class="hero-section">
<div class="hero-content">
<h1>Hire Expert Freelancers<br>for Any Job, Online</h1>
<p class="hero-subtitle">Find the perfect freelance services for your business</p>
<div class="search-box">
<form action="projects.php" method="GET" class="search-form">
<input type="text" name="search" placeholder="Try 'logo design', 'web development', 'content writing'...">
<button type="submit">Find Work</button>
</form>
</div>
<div class="stats">
<div class="stat-item">
<span class="stat-number"><?php echo number_format($stats['projects']); ?></span>
<span class="stat-label">Open Projects</span>
</div>
<div class="stat-item">
<span class="stat-number"><?php echo number_format($stats['freelancers']); ?></span>
<span class="stat-label">Expert Freelancers</span>
</div>
<div class="stat-item">
<span class="stat-number"><?php echo number_format($stats['completed']); ?></span>
<span class="stat-label">Completed Jobs</span>
</div>
</div>
</div>
</section>
<!-- Categories -->
<section class="categories-section">
<h2>Browse by Category</h2>
<div class="categories-grid">
<?php foreach ($categories as $category): ?>
<a href="projects.php?category=<?php echo $category['id']; ?>" class="category-card">
<div class="category-icon"><?php echo $category['icon'] ?? '📁'; ?></div>
<h3><?php echo htmlspecialchars($category['name']); ?></h3>
<span class="project-count"><?php echo $category['project_count']; ?> projects</span>
</a>
<?php endforeach; ?>
</div>
</section>
<!-- How It Works -->
<section class="how-it-works">
<h2>How It Works</h2>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<h3>Post a Project</h3>
<p>Tell us what you need done. It's free to post and you'll receive competitive quotes.</p>
</div>
<div class="step">
<div class="step-number">2</div>
<h3>Choose Freelancer</h3>
<p>Review proposals, compare quotes, and select the best freelancer for your project.</p>
</div>
<div class="step">
<div class="step-number">3</div>
<h3>Pay Safely</h3>
<p>Use our escrow system to pay securely. Release payment when you're satisfied.</p>
</div>
<div class="step">
<div class="step-number">4</div>
<h3>Get Results</h3>
<p>Work with your freelancer, track progress, and get amazing results.</p>
</div>
</div>
</section>
<!-- Featured Projects -->
<section class="featured-section">
<div class="section-header">
<h2>Featured Projects</h2>
<a href="projects.php" class="view-all">View All Projects →</a>
</div>
<div class="projects-grid">
<?php foreach ($featured as $project): ?>
<div class="project-card">
<div class="project-header">
<h3><a href="project.php?id=<?php echo $project['id']; ?>">
<?php echo htmlspecialchars($project['title']); ?>
</a></h3>
<span class="budget">
<?php if ($project['project_type'] == 'fixed'): ?>
$<?php echo number_format($project['fixed_price']); ?> Fixed
<?php else: ?>
$<?php echo $project['hourly_rate_min']; ?>-<?php echo $project['hourly_rate_max']; ?>/hr
<?php endif; ?>
</span>
</div>
<p class="project-description">
<?php echo substr(htmlspecialchars($project['description']), 0, 150) . '...'; ?>
</p>
<div class="project-skills">
<?php 
$skills = json_decode($project['skills_required'], true);
if (is_array($skills)):
foreach (array_slice($skills, 0, 3) as $skill): 
?>
<span class="skill-tag"><?php echo htmlspecialchars($skill); ?></span>
<?php 
endforeach;
endif; 
?>
<?php if (count($skills) > 3): ?>
<span class="skill-tag more">+<?php echo count($skills) - 3; ?></span>
<?php endif; ?>
</div>
<div class="project-footer">
<span class="client">By <?php echo htmlspecialchars($project['client_name']); ?></span>
<span class="proposals"><?php echo $project['proposals_count']; ?> proposals</span>
</div>
</div>
<?php endforeach; ?>
</div>
</section>
<!-- Top Freelancers -->
<section class="freelancers-section">
<div class="section-header">
<h2>Top Rated Freelancers</h2>
<a href="freelancers.php" class="view-all">View All Freelancers →</a>
</div>
<div class="freelancers-grid">
<?php foreach ($topFreelancers as $freelancer): ?>
<div class="freelancer-card">
<div class="freelancer-header">
<img src="<?php echo $freelancer['profile_image'] ?? '/blog-website/assets/default-avatar.png'; ?>" 
alt="<?php echo htmlspecialchars($freelancer['full_name']); ?>" class="avatar">
<div class="freelancer-info">
<h4><?php echo htmlspecialchars($freelancer['full_name']); ?></h4>
<div class="rating">
<?php
$avgRating = $freelancer['avg_rating'] ?? 0;
for ($i = 1; $i <= 5; $i++):
?>
<span class="star <?php echo $i <= $avgRating ? 'filled' : ''; ?>">★</span>
<?php endfor; ?>
<span class="rating-value">(<?php echo number_format($avgRating, 1); ?>)</span>
</div>
</div>
</div>
<p class="freelancer-bio">
<?php echo substr(htmlspecialchars($freelancer['bio'] ?? 'No bio yet'), 0, 100) . '...'; ?>
</p>
<div class="freelancer-skills">
<?php 
$skills = json_decode($freelancer['skills'], true);
if (is_array($skills)):
foreach (array_slice($skills, 0, 3) as $skill): 
?>
<span class="skill-tag"><?php echo htmlspecialchars($skill); ?></span>
<?php 
endforeach;
endif; 
?>
</div>
<div class="freelancer-footer">
<span class="hourly-rate">$<?php echo $freelancer['hourly_rate']; ?>/hr</span>
<a href="freelancer.php?id=<?php echo $freelancer['id']; ?>" class="btn-view">View Profile</a>
</div>
</div>
<?php endforeach; ?>
</div>
</section>
<!-- CTA Section -->
<section class="cta-section">
<div class="cta-content">
<h2>Ready to Start Your Project?</h2>
<p>Join thousands of businesses and freelancers already using our platform</p>
<div class="cta-buttons">
<a href="post-project.php" class="btn-primary">Post a Project</a>
<a href="register.php?type=freelancer" class="btn-secondary">Become a Freelancer</a>
</div>
</div>
</section>
</div>
<script src="js/marketplace.js"></script>
<?php include '../includes/footer.php'; ?>

3. marketplace/post-project.php

<?php
require_once '../includes/config.php';
require_once '../includes/marketplace-config.php';
requireLogin();
$user = getCurrentUser();
if ($user['user_type'] == 'freelancer') {
// Allow both types
}
$page_title = 'Post a Project';
// Get categories for dropdown
$pdo = getDB();
$categories = $pdo->query("
SELECT c.*, 
(SELECT COUNT(*) FROM skills WHERE category_id = c.id) as skill_count
FROM categories c
WHERE c.is_active = 1
ORDER BY c.name
")->fetchAll();
// Get skills for each category
$skillsByCategory = [];
foreach ($categories as $category) {
$stmt = $pdo->prepare("SELECT * FROM skills WHERE category_id = ? ORDER BY name");
$stmt->execute([$category['id']]);
$skillsByCategory[$category['id']] = $stmt->fetchAll();
}
// Handle form submission
$errors = [];
$success = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Validate input
$title = trim($_POST['title'] ?? '');
$category = $_POST['category'] ?? '';
$description = trim($_POST['description'] ?? '');
$projectType = $_POST['project_type'] ?? 'fixed';
$skills = $_POST['skills'] ?? [];
$experienceLevel = $_POST['experience_level'] ?? 'intermediate';
$duration = (int)($_POST['duration'] ?? 0);
if (empty($title)) {
$errors['title'] = 'Project title is required';
}
if (empty($category)) {
$errors['category'] = 'Please select a category';
}
if (empty($description)) {
$errors['description'] = 'Project description is required';
} elseif (strlen($description) < 100) {
$errors['description'] = 'Description must be at least 100 characters';
}
if ($projectType === 'fixed') {
$budget = (float)($_POST['budget'] ?? 0);
if ($budget < 10) {
$errors['budget'] = 'Minimum budget is $10';
}
} else {
$minRate = (float)($_POST['min_rate'] ?? 0);
$maxRate = (float)($_POST['max_rate'] ?? 0);
if ($minRate < 5) {
$errors['min_rate'] = 'Minimum hourly rate is $5';
}
if ($maxRate < $minRate) {
$errors['max_rate'] = 'Maximum rate must be greater than minimum';
}
}
if ($duration < 1) {
$errors['duration'] = 'Estimated duration is required';
}
// Handle file uploads
$attachments = [];
if (!empty($_FILES['attachments']['name'][0])) {
$uploadDir = __DIR__ . '/uploads/attachments/';
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
foreach ($_FILES['attachments']['tmp_name'] as $key => $tmpName) {
if ($_FILES['attachments']['error'][$key] === UPLOAD_ERR_OK) {
$fileName = time() . '_' . basename($_FILES['attachments']['name'][$key]);
$filePath = $uploadDir . $fileName;
if (move_uploaded_file($tmpName, $filePath)) {
$attachments[] = [
'name' => $_FILES['attachments']['name'][$key],
'path' => 'uploads/attachments/' . $fileName,
'size' => $_FILES['attachments']['size'][$key]
];
}
}
}
}
if (empty($errors)) {
// Create project
$slug = strtolower(preg_replace('/[^a-z0-9]+/', '-', $title)) . '-' . uniqid();
$sql = "INSERT INTO projects (
client_id, title, slug, description, category_id, skills_required,
experience_level, project_type, estimated_duration, attachments,
budget_min, budget_max, fixed_price, hourly_rate_min, hourly_rate_max
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$params = [
$user['id'],
$title,
$slug,
$description,
$category,
json_encode($skills),
$experienceLevel,
$projectType,
$duration,
json_encode($attachments)
];
if ($projectType === 'fixed') {
$params[] = null; // budget_min
$params[] = null; // budget_max
$params[] = $budget; // fixed_price
$params[] = null; // hourly_rate_min
$params[] = null; // hourly_rate_max
} else {
$params[] = $minRate; // budget_min
$params[] = $maxRate; // budget_max
$params[] = null; // fixed_price
$params[] = $minRate; // hourly_rate_min
$params[] = $maxRate; // hourly_rate_max
}
if ($stmt->execute($params)) {
$projectId = $pdo->lastInsertId();
$success = true;
// Send notifications to matching freelancers
sendProjectNotifications($projectId);
// Redirect to project page
header('Location: project.php?id=' . $projectId . '&success=posted');
exit;
} else {
$errors['general'] = 'Failed to create project. Please try again.';
}
}
}
include '../includes/header.php';
?>
<link rel="stylesheet" href="css/marketplace.css">
<div class="post-project-container">
<div class="post-project-header">
<h1>Post a New Project</h1>
<p>Tell us about your project and get proposals from qualified freelancers</p>
</div>
<?php if (!empty($errors['general'])): ?>
<div class="alert error"><?php echo $errors['general']; ?></div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data" class="post-project-form" id="projectForm">
<!-- Basic Information -->
<div class="form-section">
<h2>Basic Information</h2>
<div class="form-group">
<label for="title">Project Title *</label>
<input type="text" 
id="title" 
name="title" 
class="form-control <?php echo isset($errors['title']) ? 'error' : ''; ?>"
value="<?php echo htmlspecialchars($_POST['title'] ?? ''); ?>"
placeholder="e.g., Need a logo for my new startup"
required>
<div class="error-message"><?php echo $errors['title'] ?? ''; ?></div>
<small class="help-text">Be specific and clear about what you need</small>
</div>
<div class="form-row">
<div class="form-group">
<label for="category">Category *</label>
<select id="category" 
name="category" 
class="form-control <?php echo isset($errors['category']) ? 'error' : ''; ?>"
required>
<option value="">Select a category</option>
<?php foreach ($categories as $cat): ?>
<option value="<?php echo $cat['id']; ?>" 
<?php echo ($_POST['category'] ?? '') == $cat['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($cat['name']); ?>
</option>
<?php endforeach; ?>
</select>
<div class="error-message"><?php echo $errors['category'] ?? ''; ?></div>
</div>
<div class="form-group">
<label for="experience_level">Experience Level</label>
<select id="experience_level" name="experience_level" class="form-control">
<option value="entry">Entry Level</option>
<option value="intermediate" selected>Intermediate</option>
<option value="expert">Expert</option>
</select>
</div>
</div>
<div class="form-group">
<label for="description">Project Description *</label>
<textarea id="description" 
name="description" 
class="form-control <?php echo isset($errors['description']) ? 'error' : ''; ?>"
rows="8"
placeholder="Describe your project in detail..."><?php echo htmlspecialchars($_POST['description'] ?? ''); ?></textarea>
<div class="error-message"><?php echo $errors['description'] ?? ''; ?></div>
<div class="character-counter">
<span id="descLength"><?php echo strlen($_POST['description'] ?? ''); ?></span>/5000
</div>
</div>
</div>
<!-- Skills Required -->
<div class="form-section">
<h2>Skills Required</h2>
<p>Select the skills needed for this project</p>
<div class="skills-container" id="skillsContainer">
<?php foreach ($categories as $category): ?>
<?php if (!empty($skillsByCategory[$category['id']])): ?>
<div class="category-skills" data-category="<?php echo $category['id']; ?>" style="display: none;">
<h4><?php echo htmlspecialchars($category['name']); ?></h4>
<div class="skills-grid">
<?php foreach ($skillsByCategory[$category['id']] as $skill): ?>
<label class="skill-checkbox">
<input type="checkbox" name="skills[]" value="<?php echo $skill['name']; ?>">
<span><?php echo htmlspecialchars($skill['name']); ?></span>
</label>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
<div class="selected-skills" id="selectedSkills"></div>
</div>
<!-- Budget & Timeline -->
<div class="form-section">
<h2>Budget & Timeline</h2>
<div class="form-group">
<label>Project Type</label>
<div class="radio-group">
<label class="radio-label">
<input type="radio" name="project_type" value="fixed" checked>
Fixed Price
</label>
<label class="radio-label">
<input type="radio" name="project_type" value="hourly">
Hourly Rate
</label>
</div>
</div>
<div id="fixedFields" class="budget-fields">
<div class="form-group">
<label for="budget">Budget ($) *</label>
<input type="number" 
id="budget" 
name="budget" 
class="form-control <?php echo isset($errors['budget']) ? 'error' : ''; ?>"
min="10"
step="0.01"
value="<?php echo htmlspecialchars($_POST['budget'] ?? ''); ?>">
<div class="error-message"><?php echo $errors['budget'] ?? ''; ?></div>
</div>
</div>
<div id="hourlyFields" class="budget-fields" style="display: none;">
<div class="form-row">
<div class="form-group">
<label for="min_rate">Minimum Rate ($/hr) *</label>
<input type="number" 
id="min_rate" 
name="min_rate" 
class="form-control <?php echo isset($errors['min_rate']) ? 'error' : ''; ?>"
min="5"
step="0.01"
value="<?php echo htmlspecialchars($_POST['min_rate'] ?? ''); ?>">
<div class="error-message"><?php echo $errors['min_rate'] ?? ''; ?></div>
</div>
<div class="form-group">
<label for="max_rate">Maximum Rate ($/hr) *</label>
<input type="number" 
id="max_rate" 
name="max_rate" 
class="form-control <?php echo isset($errors['max_rate']) ? 'error' : ''; ?>"
min="5"
step="0.01"
value="<?php echo htmlspecialchars($_POST['max_rate'] ?? ''); ?>">
<div class="error-message"><?php echo $errors['max_rate'] ?? ''; ?></div>
</div>
</div>
</div>
<div class="form-group">
<label for="duration">Estimated Duration (days) *</label>
<input type="number" 
id="duration" 
name="duration" 
class="form-control <?php echo isset($errors['duration']) ? 'error' : ''; ?>"
min="1"
value="<?php echo htmlspecialchars($_POST['duration'] ?? ''); ?>">
<div class="error-message"><?php echo $errors['duration'] ?? ''; ?></div>
</div>
</div>
<!-- Attachments -->
<div class="form-section">
<h2>Attachments (Optional)</h2>
<p>Upload any relevant files (max 5 files, 10MB each)</p>
<div class="file-upload-area" id="fileDropArea">
<div class="upload-icon">📎</div>
<p>Drag & drop files here or click to browse</p>
<input type="file" 
id="attachments" 
name="attachments[]" 
multiple 
style="display: none;"
accept=".pdf,.doc,.docx,.txt,.jpg,.png,.zip">
<button type="button" class="btn-browse" onclick="document.getElementById('attachments').click()">
Browse Files
</button>
</div>
<div id="fileList" class="file-list"></div>
</div>
<!-- Submit -->
<div class="form-actions">
<button type="submit" class="btn-submit">Post Project</button>
<a href="index.php" class="btn-cancel">Cancel</a>
</div>
<div class="form-note">
<p>By posting a project, you agree to our <a href="/terms">Terms of Service</a> and 
<a href="/privacy">Privacy Policy</a>.</p>
</div>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Character counter
const desc = document.getElementById('description');
const counter = document.getElementById('descLength');
desc.addEventListener('input', function() {
counter.textContent = this.value.length;
});
// Category change - show relevant skills
const categorySelect = document.getElementById('category');
const skillsContainer = document.getElementById('skillsContainer');
categorySelect.addEventListener('change', function() {
document.querySelectorAll('.category-skills').forEach(el => {
el.style.display = 'none';
});
if (this.value) {
const selected = document.querySelector(`.category-skills[data-category="${this.value}"]`);
if (selected) {
selected.style.display = 'block';
}
}
});
// Toggle budget fields based on project type
const projectTypeRadios = document.querySelectorAll('input[name="project_type"]');
const fixedFields = document.getElementById('fixedFields');
const hourlyFields = document.getElementById('hourlyFields');
projectTypeRadios.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'fixed') {
fixedFields.style.display = 'block';
hourlyFields.style.display = 'none';
} else {
fixedFields.style.display = 'none';
hourlyFields.style.display = 'block';
}
});
});
// File upload handling
const fileInput = document.getElementById('attachments');
const fileList = document.getElementById('fileList');
const dropArea = document.getElementById('fileDropArea');
dropArea.addEventListener('dragover', (e) => {
e.preventDefault();
dropArea.classList.add('dragover');
});
dropArea.addEventListener('dragleave', () => {
dropArea.classList.remove('dragover');
});
dropArea.addEventListener('drop', (e) => {
e.preventDefault();
dropArea.classList.remove('dragover');
handleFiles(e.dataTransfer.files);
});
fileInput.addEventListener('change', function() {
handleFiles(this.files);
});
function handleFiles(files) {
fileList.innerHTML = '';
for (let file of files) {
if (file.size > 10 * 1024 * 1024) {
alert(`File ${file.name} exceeds 10MB limit`);
continue;
}
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.innerHTML = `
<span class="file-name">${file.name}</span>
<span class="file-size">${(file.size / 1024).toFixed(1)} KB</span>
<button type="button" class="file-remove" onclick="this.parentElement.remove()">✕</button>
`;
fileList.appendChild(fileItem);
}
}
// Show selected skills
document.querySelectorAll('input[name="skills[]"]').forEach(checkbox => {
checkbox.addEventListener('change', updateSelectedSkills);
});
function updateSelectedSkills() {
const selected = document.getElementById('selectedSkills');
const checked = document.querySelectorAll('input[name="skills[]"]:checked');
if (checked.length > 0) {
let html = '<h4>Selected Skills:</h4><div class="selected-tags">';
checked.forEach(cb => {
html += `<span class="skill-tag">${cb.value}</span>`;
});
html += '</div>';
selected.innerHTML = html;
} else {
selected.innerHTML = '';
}
}
});
</script>
<style>
.post-project-container {
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
}
.post-project-header {
text-align: center;
margin-bottom: 2rem;
}
.post-project-header h1 {
font-size: 2.5rem;
color: #333;
margin-bottom: 0.5rem;
}
.post-project-header p {
color: #666;
}
.post-project-form {
background: white;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
padding: 2rem;
}
.form-section {
margin-bottom: 2rem;
padding-bottom: 2rem;
border-bottom: 1px solid #eee;
}
.form-section h2 {
color: #333;
font-size: 1.3rem;
margin-bottom: 1.5rem;
}
.form-section p {
color: #666;
margin-bottom: 1rem;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #333;
}
.form-control {
width: 100%;
padding: 0.8rem;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 1rem;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: #667eea;
}
.form-control.error {
border-color: #dc3545;
}
.error-message {
color: #dc3545;
font-size: 0.85rem;
margin-top: 0.3rem;
}
.help-text {
color: #888;
font-size: 0.85rem;
margin-top: 0.3rem;
}
.character-counter {
text-align: right;
color: #888;
font-size: 0.85rem;
margin-top: 0.3rem;
}
/* Skills */
.category-skills {
background: #f8f9fa;
padding: 1rem;
border-radius: 5px;
margin-bottom: 1rem;
}
.category-skills h4 {
margin-bottom: 1rem;
color: #333;
}
.skills-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 0.5rem;
}
.skill-checkbox {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.3rem;
cursor: pointer;
}
.skill-checkbox:hover {
background: #e9ecef;
border-radius: 3px;
}
.selected-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1rem;
}
.skill-tag {
padding: 0.3rem 0.8rem;
background: #e9ecef;
border-radius: 20px;
font-size: 0.9rem;
color: #495057;
}
/* Radio Group */
.radio-group {
display: flex;
gap: 1rem;
margin-top: 0.5rem;
}
.radio-label {
display: flex;
align-items: center;
gap: 0.3rem;
cursor: pointer;
}
/* File Upload */
.file-upload-area {
border: 2px dashed #ddd;
border-radius: 5px;
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.file-upload-area:hover,
.file-upload-area.dragover {
border-color: #667eea;
background: #f8f9fa;
}
.upload-icon {
font-size: 2rem;
margin-bottom: 0.5rem;
}
.btn-browse {
margin-top: 1rem;
padding: 0.5rem 1rem;
background: #667eea;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.file-list {
margin-top: 1rem;
}
.file-item {
display: flex;
align-items: center;
padding: 0.5rem;
background: #f8f9fa;
border-radius: 5px;
margin-bottom: 0.5rem;
}
.file-name {
flex: 1;
font-weight: 500;
}
.file-size {
color: #888;
margin-right: 1rem;
}
.file-remove {
background: none;
border: none;
color: #dc3545;
cursor: pointer;
font-size: 1.2rem;
}
/* Form Actions */
.form-actions {
display: flex;
gap: 1rem;
margin-top: 2rem;
}
.btn-submit {
flex: 1;
padding: 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 5px;
font-size: 1.1rem;
cursor: pointer;
transition: opacity 0.3s;
}
.btn-submit:hover {
opacity: 0.9;
}
.btn-cancel {
padding: 1rem 2rem;
background: #6c757d;
color: white;
text-decoration: none;
border-radius: 5px;
text-align: center;
}
.form-note {
margin-top: 1rem;
text-align: center;
color: #888;
font-size: 0.9rem;
}
.form-note a {
color: #667eea;
text-decoration: none;
}
@media (max-width: 768px) {
.form-row {
grid-template-columns: 1fr;
}
.form-actions {
flex-direction: column;
}
.btn-cancel {
text-align: center;
}
}
</style>
<?php include '../includes/footer.php'; ?>

4. marketplace/project.php (Single Project View)

```php
<?php
require_once '../includes/config.php';
require_once '../includes/marketplace-config.php';

$projectId = $_GET['id'] ?? 0;

$pdo = getDB();

// Get project details
$stmt = $pdo->prepare("
SELECT p., u.full_name as client_name, u.username, u.profile_image as client_image, u.location as client_location, u.is_verified as client_verified, c.name as category_name, (SELECT COUNT() FROM users WHERE user_type IN ('freelancer', 'both') AND
JSON_CONTAINS(skills, JSON_ARRAY(skill))

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper