Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Projects/Hyperlocal Services Platform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Hyperlocal Services Platform

An on-demand platform designed to connect users with verified local service professionals like plumbers, electricians, house cleaners, and more.

## Features
- **Discover Services**: Browse a directory of local service categories and top-rated professionals.
- **My Bookings**: Manage your active appointments and view past service history.
- **Provider Dashboard**: A dedicated interface for service professionals to manage incoming requests, track earnings, and view their schedule.
- **Interactive UI**: Seamlessly switch between the Customer View and the Provider View using the top navigation toggle.
- **Responsive Design**: Built with pure CSS for a clean, accessible layout on mobile and desktop screens.

## Run It
No backend or build process required. Simply open `index.html` in your web browser.

## Tech Stack
- HTML5
- CSS3 (Vanilla, CSS Grid & Flexbox)
- Vanilla JavaScript

## Credits
Developed by MistryVishwa.
162 changes: 162 additions & 0 deletions Projects/Hyperlocal Services Platform/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hyperlocal Services Platform</title>
<link rel="stylesheet" href="style.css">
<script src="https://unpkg.com/@phosphor-icons/web"></script>
</head>
<body>

<!-- Top Navigation -->
<nav class="global-nav">
<div class="brand">
<i class="ph-fill ph-wrench text-primary"></i> <span>QuickFix</span>
</div>

<div class="nav-links">
<button class="nav-tab active" data-tab="discover">Find a Pro</button>
<button class="nav-tab" data-tab="bookings">My Bookings</button>
<button class="nav-tab" data-tab="provider" style="display: none;" id="provider-tab-btn">Provider Dashboard</button>
</div>

<div class="nav-actions">
<!-- Role Switcher -->
<div class="role-selector">
<select id="role-select">
<option value="customer">Customer View</option>
<option value="provider">Provider View</option>
</select>
</div>

<button class="icon-btn"><i class="ph ph-bell"></i></button>
<img src="https://ui-avatars.com/api/?name=Local+Customer&background=10b981&color=fff" alt="Avatar" id="user-avatar" class="avatar">
</div>
</nav>

<!-- Mobile Nav -->
<div class="mobile-nav">
<button class="nav-tab active" data-tab="discover"><i class="ph ph-magnifying-glass"></i></button>
<button class="nav-tab" data-tab="bookings"><i class="ph ph-calendar-check"></i></button>
<button class="nav-tab" data-tab="provider" id="mobile-provider-tab-btn" style="display: none;"><i class="ph ph-briefcase"></i></button>
</div>

<main class="main-content">

<!-- ================= DISCOVER TAB (CUSTOMER) ================= -->
<section id="discover" class="tab-content active">
<div class="hero-banner">
<div>
<h1>Expert Help, Right at Your Doorstep</h1>
<p>Book trusted, background-checked local professionals for any home service.</p>
</div>
<div class="search-box">
<i class="ph ph-map-pin"></i>
<input type="text" placeholder="Enter your zip code or service needed...">
<button class="btn primary-btn">Search</button>
</div>
</div>

<div class="section-header mt-3">
<h2>Top Rated Professionals</h2>
<div class="filter-pills">
<span class="pill active">All Services</span>
<span class="pill">Plumbing</span>
<span class="pill">Electrical</span>
<span class="pill">Cleaning</span>
</div>
</div>

<div class="pro-grid" id="pro-grid">
<!-- Injected via JS -->
</div>
</section>

<!-- ================= MY BOOKINGS TAB (CUSTOMER) ================= -->
<section id="bookings" class="tab-content hidden">
<div class="page-heading">
<h1>My Bookings</h1>
<p class="text-muted">Manage your upcoming service appointments.</p>
</div>

<div class="split-layout mt-3">
<div class="panel col-span-2">
<div class="panel-header">
<h3>Upcoming Appointments</h3>
</div>
<div class="panel-body p-0">
<table class="custom-table">
<thead>
<tr><th>Service</th><th>Professional</th><th>Date & Time</th><th>Status</th><th>Action</th></tr>
</thead>
<tbody id="customer-bookings-tbody">
<!-- Injected via JS -->
</tbody>
</table>
</div>
</div>

<div class="panel">
<div class="panel-header">
<h3>Quick Actions</h3>
</div>
<div class="panel-body flex-column gap-1">
<button class="btn outline-btn" style="width:100%"><i class="ph ph-headset"></i> Contact Support</button>
<button class="btn outline-btn" style="width:100%"><i class="ph ph-clock-counter-clockwise"></i> View History</button>
</div>
</div>
</div>
</section>

<!-- ================= PROVIDER DASHBOARD TAB ================= -->
<section id="provider" class="tab-content hidden">
<div class="page-heading flex-between">
<div>
<h1>Provider Dashboard</h1>
<p class="text-muted">Manage incoming service requests and track your earnings.</p>
</div>
<div>
<span class="badge success" style="padding: 0.6rem 1rem; font-size: 0.9rem;"><i class="ph-fill ph-check-circle"></i> Accepting Jobs</span>
</div>
</div>

<div class="metrics-grid mt-2">
<div class="metric-card">
<div class="metric-icon bg-blue-light text-blue"><i class="ph-fill ph-calendar-plus"></i></div>
<div class="metric-data"><h3>New Requests</h3><h2>5</h2></div>
</div>
<div class="metric-card">
<div class="metric-icon bg-green-light text-green"><i class="ph-fill ph-check-square"></i></div>
<div class="metric-data"><h3>Completed Jobs</h3><h2>128</h2></div>
</div>
<div class="metric-card">
<div class="metric-icon bg-purple-light text-purple"><i class="ph-fill ph-currency-circle-dollar"></i></div>
<div class="metric-data"><h3>Earnings (This Week)</h3><h2>$1,450</h2></div>
</div>
</div>

<div class="split-layout mt-3">
<div class="panel col-span-2">
<div class="panel-header">
<h3>Incoming Service Requests</h3>
</div>
<div class="panel-body p-0">
<table class="custom-table">
<thead>
<tr><th>Customer</th><th>Job Type</th><th>Location</th><th>Requested Time</th><th>Action</th></tr>
</thead>
<tbody id="provider-requests-tbody">
<!-- Injected via JS -->
</tbody>
</table>
</div>
</div>
</div>
</section>

</main>

<script src="script.js"></script>
</body>
</html>
14 changes: 14 additions & 0 deletions Projects/Hyperlocal Services Platform/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"title": "Hyperlocal Services Platform",
"description": "An on-demand platform connecting users with local service professionals like plumbers, electricians, and cleaners.",
"author": {
"name": "MistryVishwa",
"github": "MistryVishwa"
},
"tags": [
"services",
"on-demand",
"vanilla-js"
],
"entry": "index.html"
}
135 changes: 135 additions & 0 deletions Projects/Hyperlocal Services Platform/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// --- Mock Data ---
const professionals = [
{ id: 1, name: "Marcus Webb", category: "Plumbing", rating: 4.9, reviews: 124, price: "$85/hr", img: "https://images.unsplash.com/photo-1621905252507-b35492cc74b4?auto=format&fit=crop&q=80&w=200" },
{ id: 2, name: "Sarah Jenkins", category: "Electrical", rating: 4.8, reviews: 89, price: "$95/hr", img: "https://images.unsplash.com/photo-1581578731548-c64695cc6952?auto=format&fit=crop&q=80&w=200" },
{ id: 3, name: "Elena Gomez", category: "House Cleaning", rating: 5.0, reviews: 210, price: "$40/hr", img: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&q=80&w=200" },
{ id: 4, name: "David Chen", category: "HVAC Repair", rating: 4.7, reviews: 65, price: "$110/hr", img: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&q=80&w=200" }
];

const customerBookings = [
{ service: "Leaky Faucet Repair", pro: "Marcus Webb", date: "Oct 22, 10:00 AM", status: "Upcoming" },
{ service: "Deep House Cleaning", pro: "Elena Gomez", date: "Oct 15, 09:00 AM", status: "Completed" }
];

const providerRequests = [
{ customer: "John Doe", type: "Pipe Installation", loc: "123 Main St", time: "Tomorrow, 2:00 PM", status: "Pending" },
{ customer: "Alice Smith", type: "Water Heater Fix", loc: "456 Oak Ave", time: "Oct 24, 9:00 AM", status: "Accepted" }
];

// --- Core Logic ---
document.addEventListener('DOMContentLoaded', () => {
const navTabs = document.querySelectorAll('.nav-tab');
const views = document.querySelectorAll('.tab-content');
const roleSelect = document.getElementById('role-select');
const providerTabBtn = document.getElementById('provider-tab-btn');
const mobileProviderTabBtn = document.getElementById('mobile-provider-tab-btn');

// Initialization
renderPros();
renderBookings();

// Role Switching Logic
roleSelect.addEventListener('change', (e) => {
const role = e.target.value;

if (role === 'provider') {
providerTabBtn.style.display = 'block';
if(mobileProviderTabBtn) mobileProviderTabBtn.style.display = 'block';
document.querySelector('[data-tab="provider"]').click();

// Update Avatar
document.getElementById('user-avatar').src = "https://ui-avatars.com/api/?name=Service+Provider&background=3b82f6&color=fff";
} else {
providerTabBtn.style.display = 'none';
if(mobileProviderTabBtn) mobileProviderTabBtn.style.display = 'none';
document.querySelector('[data-tab="discover"]').click();

// Update Avatar
document.getElementById('user-avatar').src = "https://ui-avatars.com/api/?name=Local+Customer&background=10b981&color=fff";
}
});

// Tab Switching
navTabs.forEach(tab => {
tab.addEventListener('click', () => {
const targetId = tab.getAttribute('data-tab');

// Update Active Classes
navTabs.forEach(t => t.classList.remove('active'));
document.querySelectorAll(`[data-tab="${targetId}"]`).forEach(t => t.classList.add('active'));

// Switch Content
views.forEach(v => {
v.classList.add('hidden');
if (v.id === targetId) {
v.classList.remove('hidden');
}
});
});
});

function getStatusBadge(status) {
if (status === 'Completed' || status === 'Accepted') return `<span class="badge success">${status}</span>`;
if (status === 'Pending') return `<span class="badge warning">${status}</span>`;
return `<span class="badge primary">${status}</span>`;
}

function renderPros() {
const grid = document.getElementById('pro-grid');
if(!grid) return;

grid.innerHTML = professionals.map(p => `
<div class="pro-card">
<div class="pro-header">
<img src="${p.img}" class="pro-avatar">
<div>
<div class="pro-name">${p.name}</div>
<div class="pro-category">${p.category}</div>
</div>
</div>
<div class="pro-rating">
<i class="ph-fill ph-star"></i> ${p.rating} <span class="text-muted font-normal">(${p.reviews} reviews)</span>
</div>
<div class="pro-footer">
<span class="pro-price">${p.price}</span>
<button class="btn primary-btn btn-sm">Book Now</button>
</div>
</div>
`).join('');
}

function renderBookings() {
// Customer
const cTbody = document.getElementById('customer-bookings-tbody');
if(cTbody) {
cTbody.innerHTML = customerBookings.map(b => `
<tr>
<td><strong>${b.service}</strong></td>
<td>${b.pro}</td>
<td>${b.date}</td>
<td>${getStatusBadge(b.status)}</td>
<td><button class="btn outline-btn btn-sm">View</button></td>
</tr>
`).join('');
}

// Provider
const pTbody = document.getElementById('provider-requests-tbody');
if(pTbody) {
pTbody.innerHTML = providerRequests.map(r => `
<tr>
<td><strong>${r.customer}</strong></td>
<td>${r.type}</td>
<td><i class="ph-fill ph-map-pin text-muted"></i> ${r.loc}</td>
<td>${r.time}</td>
<td>
${r.status === 'Pending'
? `<button class="btn primary-btn btn-sm">Accept</button>`
: getStatusBadge(r.status)
}
</td>
</tr>
`).join('');
}
}
});
Loading
Loading